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/Compression.h"
15 #include "mozilla/DebugOnly.h"
16 #include "mozilla/EnumSet.h"
17 #include "mozilla/IntegerPrintfMacros.h"
18 #include "mozilla/mozalloc.h"
19 #include "mozilla/PodOperations.h"
20 #include "mozilla/RandomNum.h"
21 #include "mozilla/RefPtr.h"
22 #include "mozilla/ScopeExit.h"
23 #include "mozilla/Sprintf.h"
24 #include "mozilla/TimeStamp.h"
25 #include "mozilla/UniquePtrExtensions.h" // UniqueFreePtr
26 #include "mozilla/Utf8.h"
27 #include "mozilla/Variant.h"
38 # include <io.h> /* for isatty() */
42 # include MALLOC_H /* for malloc_usable_size, malloc_size, _msize */
53 #include <sys/types.h>
57 # include <sys/mman.h>
58 # include <sys/wait.h>
60 # include <sys/stat.h>
64 # include <sys/prctl.h>
68 #include "jsfriendapi.h"
70 #ifndef JS_WITHOUT_NSPR
75 #include "builtin/Array.h"
76 #include "builtin/MapObject.h"
77 #include "builtin/ModuleObject.h"
78 #include "builtin/RegExp.h"
79 #include "builtin/TestingFunctions.h"
80 #include "builtin/TestingUtility.h" // js::ParseCompileOptions, js::ParseDebugMetadata, js::CreateScriptPrivate
81 #include "debugger/DebugAPI.h"
82 #include "frontend/BytecodeCompiler.h" // frontend::{CompileGlobalScriptToExtensibleStencil, CompileModule, ParseModuleToExtensibleStencil}
83 #include "frontend/CompilationStencil.h"
84 #ifdef JS_ENABLE_SMOOSH
85 # include "frontend/Frontend2.h"
87 #include "frontend/FrontendContext.h" // AutoReportFrontendContext
88 #include "frontend/ModuleSharedContext.h"
89 #include "frontend/Parser.h"
90 #include "frontend/ScopeBindingCache.h" // js::frontend::ScopeBindingCache
92 #include "gc/PublicIterators.h"
94 # include "irregexp/RegExpAPI.h"
97 #ifdef JS_SIMULATOR_ARM
98 # include "jit/arm/Simulator-arm.h"
100 #ifdef JS_SIMULATOR_MIPS32
101 # include "jit/mips32/Simulator-mips32.h"
103 #ifdef JS_SIMULATOR_MIPS64
104 # include "jit/mips64/Simulator-mips64.h"
106 #ifdef JS_SIMULATOR_LOONG64
107 # include "jit/loong64/Simulator-loong64.h"
109 #ifdef JS_SIMULATOR_RISCV64
110 # include "jit/riscv64/Simulator-riscv64.h"
112 #include "jit/CacheIRHealth.h"
113 #include "jit/InlinableNatives.h"
115 #include "jit/JitcodeMap.h"
116 #include "jit/JitZone.h"
117 #include "jit/shared/CodeGenerator-shared.h"
118 #include "js/Array.h" // JS::NewArrayObject
119 #include "js/ArrayBuffer.h" // JS::{CreateMappedArrayBufferContents,NewMappedArrayBufferWithContents,IsArrayBufferObject,GetArrayBufferLengthAndData}
120 #include "js/BuildId.h" // JS::BuildIdCharVector, JS::SetProcessBuildIdOp
121 #include "js/CallAndConstruct.h" // JS::Call, JS::IsCallable, JS_CallFunction, JS_CallFunctionValue
122 #include "js/CharacterEncoding.h" // JS::StringIsASCII
123 #include "js/CompilationAndEvaluation.h"
124 #include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions, JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::InstantiateOptions
125 #include "js/ContextOptions.h" // JS::ContextOptions{,Ref}
126 #include "js/Debug.h"
127 #include "js/Equality.h" // JS::SameValue
128 #include "js/ErrorReport.h" // JS::PrintError
129 #include "js/Exception.h" // JS::StealPendingExceptionStack
130 #include "js/experimental/CodeCoverage.h" // js::EnableCodeCoverage
131 #include "js/experimental/CompileScript.h" // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::HadFrontendErrors, JS::ConvertFrontendErrorsToRuntimeErrors, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil
132 #include "js/experimental/CTypes.h" // JS::InitCTypesClass
133 #include "js/experimental/Intl.h" // JS::AddMoz{DateTimeFormat,DisplayNames}Constructor
134 #include "js/experimental/JitInfo.h" // JSJit{Getter,Setter,Method}CallArgs, JSJitGetterInfo, JSJit{Getter,Setter}Op, JSJitInfo
135 #include "js/experimental/JSStencil.h" // JS::Stencil, JS::DecodeStencil
136 #include "js/experimental/SourceHook.h" // js::{Set,Forget,}SourceHook
137 #include "js/experimental/TypedData.h" // JS_NewUint8Array
138 #include "js/friend/DumpFunctions.h" // JS::FormatStackDump
139 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
140 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
141 #include "js/friend/WindowProxy.h" // js::IsWindowProxy, js::SetWindowProxyClass, js::ToWindowProxyIfWindow, js::ToWindowIfWindowProxy
142 #include "js/GCAPI.h" // JS::AutoCheckCannotGC
143 #include "js/GCVector.h"
144 #include "js/GlobalObject.h"
145 #include "js/Initialization.h"
146 #include "js/Interrupt.h"
148 #include "js/MemoryCallbacks.h"
149 #include "js/MemoryFunctions.h"
150 #include "js/Modules.h" // JS::GetModulePrivate, JS::SetModule{DynamicImport,Metadata,Resolve}Hook, JS::SetModulePrivate
151 #include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
152 #include "js/Prefs.h"
153 #include "js/Principals.h"
154 #include "js/Printer.h" // QuoteString
155 #include "js/Printf.h"
156 #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
157 #include "js/PropertySpec.h"
158 #include "js/Realm.h"
159 #include "js/RegExp.h" // JS::ObjectIsRegExp
160 #include "js/ScriptPrivate.h"
161 #include "js/SourceText.h" // JS::SourceText
162 #include "js/StableStringChars.h"
163 #include "js/Stack.h"
164 #include "js/StreamConsumer.h"
165 #include "js/StructuredClone.h"
166 #include "js/Transcoding.h" // JS::TranscodeBuffer, JS::TranscodeRange, JS::IsTranscodeFailureResult
167 #include "js/Warnings.h" // JS::SetWarningReporter
168 #include "js/WasmModule.h" // JS::WasmModule
169 #include "js/Wrapper.h"
170 #include "proxy/DeadObjectProxy.h" // js::IsDeadProxyObject
171 #include "shell/jsoptparse.h"
172 #include "shell/jsshell.h"
173 #include "shell/OSObject.h"
174 #include "shell/ShellModuleObjectWrapper.h"
175 #include "shell/WasmTesting.h"
176 #include "threading/ConditionVariable.h"
177 #include "threading/ExclusiveData.h"
178 #include "threading/LockGuard.h"
179 #include "threading/Thread.h"
180 #include "util/CompleteFile.h" // js::FileContents, js::ReadCompleteFile
181 #include "util/DifferentialTesting.h"
182 #include "util/StringBuffer.h"
183 #include "util/Text.h"
184 #include "util/WindowsWrapper.h"
185 #include "vm/ArgumentsObject.h"
186 #include "vm/Compression.h"
187 #include "vm/ErrorObject.h"
188 #include "vm/ErrorReporting.h"
189 #include "vm/HelperThreads.h"
190 #include "vm/JSAtomUtils.h" // AtomizeUTF8Chars, AtomizeString, ToAtom
191 #include "vm/JSContext.h"
192 #include "vm/JSFunction.h"
193 #include "vm/JSObject.h"
194 #include "vm/JSScript.h"
195 #include "vm/ModuleBuilder.h" // js::ModuleBuilder
196 #include "vm/Modules.h"
197 #include "vm/Monitor.h"
198 #include "vm/MutexIDs.h"
199 #include "vm/PromiseObject.h" // js::PromiseObject
200 #include "vm/Shape.h"
201 #include "vm/SharedArrayObject.h"
202 #include "vm/StencilObject.h" // js::StencilObject
204 #include "vm/ToSource.h" // js::ValueToSource
205 #include "vm/TypedArrayObject.h"
206 #include "vm/WrapperObject.h"
207 #include "wasm/WasmFeatures.h"
208 #include "wasm/WasmJS.h"
210 #include "vm/Compartment-inl.h"
211 #include "vm/ErrorObject-inl.h"
212 #include "vm/Interpreter-inl.h"
213 #include "vm/JSObject-inl.h"
214 #include "vm/Realm-inl.h"
215 #include "vm/Stack-inl.h"
220 using namespace js::cli
;
221 using namespace js::shell
;
223 using JS::AutoStableStringChars
;
224 using JS::CompileOptions
;
226 using js::shell::RCFile
;
228 using mozilla::ArrayEqual
;
229 using mozilla::AsVariant
;
230 using mozilla::Atomic
;
231 using mozilla::MakeScopeExit
;
232 using mozilla::Maybe
;
233 using mozilla::Nothing
;
234 using mozilla::NumberEqualsInt32
;
235 using mozilla::TimeDuration
;
236 using mozilla::TimeStamp
;
237 using mozilla::Utf8Unit
;
238 using mozilla::Variant
;
240 bool InitOptionParser(OptionParser
& op
);
241 bool SetGlobalOptionsPreJSInit(const OptionParser
& op
);
242 bool SetGlobalOptionsPostJSInit(const OptionParser
& op
);
243 bool SetContextOptions(JSContext
* cx
, const OptionParser
& op
);
244 bool SetContextWasmOptions(JSContext
* cx
, const OptionParser
& op
);
245 bool SetContextJITOptions(JSContext
* cx
, const OptionParser
& op
);
246 bool SetContextGCOptions(JSContext
* cx
, const OptionParser
& op
);
247 bool InitModuleLoader(JSContext
* cx
, const OptionParser
& op
);
249 #ifdef FUZZING_JS_FUZZILLI
250 # define REPRL_CRFD 100
251 # define REPRL_CWFD 101
252 # define REPRL_DRFD 102
253 # define REPRL_DWFD 103
255 # define SHM_SIZE 0x100000
256 # define MAX_EDGES ((SHM_SIZE - 4) * 8)
260 unsigned char edges
[];
263 struct shmem_data
* __shmem
;
265 uint32_t *__edges_start
, *__edges_stop
;
266 void __sanitizer_cov_reset_edgeguards() {
268 for (uint32_t* x
= __edges_start
; x
< __edges_stop
&& N
< MAX_EDGES
; x
++)
272 extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start
,
274 // Avoid duplicate initialization
275 if (start
== stop
|| *start
) return;
277 if (__edges_start
!= NULL
|| __edges_stop
!= NULL
) {
279 "Coverage instrumentation is only supported for a single module\n");
283 __edges_start
= start
;
286 // Map the shared memory region
287 const char* shm_key
= getenv("SHM_ID");
289 puts("[COV] no shared memory bitmap available, skipping");
290 __shmem
= (struct shmem_data
*)malloc(SHM_SIZE
);
292 int fd
= shm_open(shm_key
, O_RDWR
, S_IREAD
| S_IWRITE
);
294 fprintf(stderr
, "Failed to open shared memory region: %s\n",
299 __shmem
= (struct shmem_data
*)mmap(0, SHM_SIZE
, PROT_READ
| PROT_WRITE
,
301 if (__shmem
== MAP_FAILED
) {
302 fprintf(stderr
, "Failed to mmap shared memory region\n");
307 __sanitizer_cov_reset_edgeguards();
309 __shmem
->num_edges
= stop
- start
;
310 printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n",
311 shm_key
, __shmem
->num_edges
);
314 extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard
) {
315 // There's a small race condition here: if this function executes in two
316 // threads for the same edge at the same time, the first thread might disable
317 // the edge (by setting the guard to zero) before the second thread fetches
318 // the guard value (and thus the index). However, our instrumentation ignores
319 // the first edge (see libcoverage.c) and so the race is unproblematic.
320 uint32_t index
= *guard
;
321 // If this function is called before coverage instrumentation is properly
322 // initialized we want to return early.
324 __shmem
->edges
[index
/ 8] |= 1 << (index
% 8);
327 #endif /* FUZZING_JS_FUZZILLI */
329 enum JSShellExitCode
{
330 EXITCODE_RUNTIME_ERROR
= 3,
331 EXITCODE_FILE_NOT_FOUND
= 4,
332 EXITCODE_OUT_OF_MEMORY
= 5,
337 * Limit the timeout to 30 minutes to prevent an overflow on platfoms
338 * that represent the time internally in microseconds using 32-bit int.
340 static const double MAX_TIMEOUT_SECONDS
= 1800.0;
342 // Not necessarily in sync with the browser
343 #ifdef ENABLE_SHARED_MEMORY
344 # define SHARED_MEMORY_DEFAULT 1
346 # define SHARED_MEMORY_DEFAULT 0
349 // Fuzzing support for JS runtime fuzzing
350 #ifdef FUZZING_INTERFACES
351 # include "shell/jsrtfuzzing/jsrtfuzzing.h"
352 static bool fuzzDoDebug
= !!getenv("MOZ_FUZZ_DEBUG");
353 static bool fuzzHaveModule
= !!getenv("FUZZER");
354 #endif // FUZZING_INTERFACES
356 // Code to support GCOV code coverage measurements on standalone shell
357 #ifdef MOZ_CODE_COVERAGE
358 # if defined(__GNUC__) && !defined(__clang__)
359 extern "C" void __gcov_dump();
360 extern "C" void __gcov_reset();
362 void counters_dump(int) { __gcov_dump(); }
364 void counters_reset(int) { __gcov_reset(); }
366 void counters_dump(int) { /* Do nothing */
369 void counters_reset(int) { /* Do nothing */
373 static void InstallCoverageSignalHandlers() {
375 fprintf(stderr
, "[CodeCoverage] Setting handlers for process %d.\n",
378 struct sigaction dump_sa
;
379 dump_sa
.sa_handler
= counters_dump
;
380 dump_sa
.sa_flags
= SA_RESTART
;
381 sigemptyset(&dump_sa
.sa_mask
);
382 mozilla::DebugOnly
<int> r1
= sigaction(SIGUSR1
, &dump_sa
, nullptr);
383 MOZ_ASSERT(r1
== 0, "Failed to install GCOV SIGUSR1 handler");
385 struct sigaction reset_sa
;
386 reset_sa
.sa_handler
= counters_reset
;
387 reset_sa
.sa_flags
= SA_RESTART
;
388 sigemptyset(&reset_sa
.sa_mask
);
389 mozilla::DebugOnly
<int> r2
= sigaction(SIGUSR2
, &reset_sa
, nullptr);
390 MOZ_ASSERT(r2
== 0, "Failed to install GCOV SIGUSR2 handler");
395 // An off-thread parse or decode job.
396 class js::shell::OffThreadJob
{
397 static constexpr size_t kCompileStackQuota
= 128 * sizeof(size_t) * 1024;
398 static constexpr size_t kThreadStackQuota
=
399 kCompileStackQuota
+ 128 * sizeof(size_t) * 1024;
402 RUNNING
, // Working; no stencil.
403 DONE
, // Finished; have stencil.
404 CANCELLED
// Cancelled due to error.
414 OffThreadJob(ShellContext
* sc
, Kind kind
, JS::SourceText
<char16_t
>&& srcBuf
);
415 OffThreadJob(ShellContext
* sc
, Kind kind
, JS::TranscodeBuffer
&& xdrBuf
);
419 bool init(JSContext
* cx
, const JS::ReadOnlyCompileOptions
& options
);
422 static void OffThreadMain(OffThreadJob
* self
);
426 void waitUntilDone();
428 already_AddRefed
<JS::Stencil
> stealStencil(JSContext
* cx
);
437 JS::FrontendContext
* fc_
= nullptr;
438 JS::OwningCompileOptions options_
;
440 UniquePtr
<Thread
> thread_
;
442 JS::SourceText
<char16_t
> srcBuf_
;
443 JS::TranscodeBuffer xdrBuf_
;
445 RefPtr
<JS::Stencil
> stencil_
;
447 JS::TranscodeResult transcodeResult_
= JS::TranscodeResult::Ok
;
450 template <typename T
>
451 static OffThreadJob
* NewOffThreadJob(JSContext
* cx
, OffThreadJob::Kind kind
,
452 JS::ReadOnlyCompileOptions
& options
,
454 ShellContext
* sc
= GetShellContext(cx
);
456 // Off-thread compilation/decode is used by main-thread, in order to improve
457 // the responsiveness. It's not used by worker in browser, and there's not
458 // much reason to support worker here.
459 JS_ReportErrorASCII(cx
, "Off-thread job is not supported in worker");
463 UniquePtr
<OffThreadJob
> job(
464 cx
->new_
<OffThreadJob
>(sc
, kind
, std::move(source
)));
469 if (!job
->init(cx
, options
)) {
473 if (!sc
->offThreadJobs
.append(job
.get())) {
475 JS_ReportErrorASCII(cx
, "OOM adding off-thread job");
479 return job
.release();
482 static OffThreadJob
* GetSingleOffThreadJob(JSContext
* cx
) {
483 ShellContext
* sc
= GetShellContext(cx
);
484 const auto& jobs
= sc
->offThreadJobs
;
486 JS_ReportErrorASCII(cx
, "No off-thread jobs are pending");
490 if (jobs
.length() > 1) {
492 cx
, "Multiple off-thread jobs are pending: must specify job ID");
499 static OffThreadJob
* LookupOffThreadJobByID(JSContext
* cx
, int32_t id
) {
501 JS_ReportErrorASCII(cx
, "Bad off-thread job ID");
505 ShellContext
* sc
= GetShellContext(cx
);
506 const auto& jobs
= sc
->offThreadJobs
;
508 JS_ReportErrorASCII(cx
, "No off-thread jobs are pending");
512 OffThreadJob
* job
= nullptr;
513 for (auto someJob
: jobs
) {
514 if (someJob
->id
== id
) {
521 JS_ReportErrorASCII(cx
, "Off-thread job not found");
528 static OffThreadJob
* LookupOffThreadJobForArgs(JSContext
* cx
,
529 const CallArgs
& args
,
531 // If the optional ID argument isn't present, get the single pending job.
532 if (args
.length() <= arg
) {
533 return GetSingleOffThreadJob(cx
);
536 // Lookup the job using the specified ID.
538 RootedValue
value(cx
, args
[arg
]);
539 if (!ToInt32(cx
, value
, &id
)) {
543 return LookupOffThreadJobByID(cx
, id
);
546 static void DeleteOffThreadJob(JSContext
* cx
, OffThreadJob
* job
) {
547 ShellContext
* sc
= GetShellContext(cx
);
548 for (size_t i
= 0; i
< sc
->offThreadJobs
.length(); i
++) {
549 if (sc
->offThreadJobs
[i
] == job
) {
550 sc
->offThreadJobs
.erase(&sc
->offThreadJobs
[i
]);
556 MOZ_CRASH("Off-thread job not found");
559 static void CancelOffThreadJobsForRuntime(JSContext
* cx
) {
560 ShellContext
* sc
= GetShellContext(cx
);
561 while (!sc
->offThreadJobs
.empty()) {
562 OffThreadJob
* job
= sc
->offThreadJobs
.popCopy();
563 job
->waitUntilDone();
568 mozilla::Atomic
<int32_t> gOffThreadJobSerial(1);
570 OffThreadJob::OffThreadJob(ShellContext
* sc
, Kind kind
,
571 JS::SourceText
<char16_t
>&& srcBuf
)
572 : id(gOffThreadJobSerial
++),
575 options_(JS::OwningCompileOptions::ForFrontendContext()),
576 srcBuf_(std::move(srcBuf
)) {
577 MOZ_RELEASE_ASSERT(id
> 0, "Off-thread job IDs exhausted");
580 OffThreadJob::OffThreadJob(ShellContext
* sc
, Kind kind
,
581 JS::TranscodeBuffer
&& xdrBuf
)
582 : id(gOffThreadJobSerial
++),
585 options_(JS::OwningCompileOptions::ForFrontendContext()),
586 xdrBuf_(std::move(xdrBuf
)) {
587 MOZ_RELEASE_ASSERT(id
> 0, "Off-thread job IDs exhausted");
590 OffThreadJob::~OffThreadJob() {
592 JS::DestroyFrontendContext(fc_
);
594 MOZ_ASSERT(state_
!= RUNNING
);
597 bool OffThreadJob::init(JSContext
* cx
,
598 const JS::ReadOnlyCompileOptions
& options
) {
599 fc_
= JS::NewFrontendContext();
601 ReportOutOfMemory(cx
);
606 if (!options_
.copy(cx
, options
)) {
614 bool OffThreadJob::dispatch() {
616 js::MakeUnique
<Thread
>(Thread::Options().setStackSize(kThreadStackQuota
));
622 if (!thread_
->init(OffThreadJob::OffThreadMain
, this)) {
631 /* static */ void OffThreadJob::OffThreadMain(OffThreadJob
* self
) {
635 void OffThreadJob::run() {
636 MOZ_ASSERT(state_
== RUNNING
);
637 MOZ_ASSERT(!stencil_
);
639 JS::SetNativeStackQuota(fc_
, kCompileStackQuota
);
642 case Kind::CompileScript
: {
643 stencil_
= JS::CompileGlobalScriptToStencil(fc_
, options_
, srcBuf_
);
646 case Kind::CompileModule
: {
647 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;
726 bool shell::enableWasmVerbose
= false;
727 bool shell::enableTestWasmAwaitTier2
= false;
728 bool shell::enableSourcePragmas
= true;
729 bool shell::enableAsyncStacks
= false;
730 bool shell::enableAsyncStackCaptureDebuggeeOnly
= false;
731 bool shell::enableToSource
= false;
732 #ifdef ENABLE_JSON_PARSE_WITH_SOURCE
733 bool shell::enableJSONParseWithSource
= false;
735 bool shell::enableImportAttributes
= false;
736 bool shell::enableImportAttributesAssertSyntax
= false;
738 uint32_t shell::gZealBits
= 0;
739 uint32_t shell::gZealFrequency
= 0;
741 bool shell::printTiming
= false;
742 RCFile
* shell::gErrFile
= nullptr;
743 RCFile
* shell::gOutFile
= nullptr;
744 bool shell::reportWarnings
= true;
745 bool shell::compileOnly
= false;
746 bool shell::disableOOMFunctions
= false;
747 bool shell::defaultToSameCompartment
= true;
750 bool shell::dumpEntrainedVariables
= false;
751 bool shell::OOM_printAllocationCount
= false;
754 UniqueChars
shell::processWideModuleLoadPath
;
756 static bool SetTimeoutValue(JSContext
* cx
, double t
);
758 static void KillWatchdog(JSContext
* cx
);
760 static bool ScheduleWatchdog(JSContext
* cx
, double t
);
762 static void CancelExecution(JSContext
* cx
);
764 enum class ShellGlobalKind
{
769 static JSObject
* NewGlobalObject(JSContext
* cx
, JS::RealmOptions
& options
,
770 JSPrincipals
* principals
, ShellGlobalKind kind
,
771 bool immutablePrototype
);
774 * A toy WindowProxy class for the shell. This is intended for testing code
775 * where global |this| is a WindowProxy. All requests are forwarded to the
776 * underlying global and no navigation is supported.
778 const JSClass ShellWindowProxyClass
=
779 PROXY_CLASS_DEF("ShellWindowProxy", JSCLASS_HAS_RESERVED_SLOTS(1));
781 JSObject
* NewShellWindowProxy(JSContext
* cx
, JS::HandleObject global
) {
782 MOZ_ASSERT(global
->is
<GlobalObject
>());
784 js::WrapperOptions options
;
785 options
.setClass(&ShellWindowProxyClass
);
787 JSAutoRealm
ar(cx
, global
);
789 js::Wrapper::New(cx
, global
, &js::Wrapper::singleton
, options
);
790 MOZ_ASSERT_IF(obj
, js::IsWindowProxy(obj
));
795 * A toy principals type for the shell.
797 * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the
798 * set bits in P are a superset of those in Q. Thus, the principal 0 is
799 * subsumed by everything, and the principal ~0 subsumes everything.
801 * As a special case, a null pointer as a principal is treated like 0xffff.
803 * The 'newGlobal' function takes an option indicating which principal the
804 * new global should have; 'evaluate' does for the new code.
806 class ShellPrincipals final
: public JSPrincipals
{
809 static uint32_t getBits(JSPrincipals
* p
) {
813 return static_cast<ShellPrincipals
*>(p
)->bits
;
817 explicit ShellPrincipals(uint32_t bits
, int32_t refcount
= 0) : bits(bits
) {
818 this->refcount
= refcount
;
821 bool write(JSContext
* cx
, JSStructuredCloneWriter
* writer
) override
{
822 // The shell doesn't have a read principals hook, so it doesn't really
823 // matter what we write here, but we have to write something so the
825 return JS_WriteUint32Pair(writer
, bits
, 0);
828 bool isSystemOrAddonPrincipal() override
{ return true; }
830 static void destroy(JSPrincipals
* principals
) {
831 MOZ_ASSERT(principals
!= &fullyTrusted
);
832 MOZ_ASSERT(principals
->refcount
== 0);
833 js_delete(static_cast<const ShellPrincipals
*>(principals
));
836 static bool subsumes(JSPrincipals
* first
, JSPrincipals
* second
) {
837 uint32_t firstBits
= getBits(first
);
838 uint32_t secondBits
= getBits(second
);
839 return (firstBits
| secondBits
) == firstBits
;
842 static JSSecurityCallbacks securityCallbacks
;
844 // Fully-trusted principals singleton.
845 static ShellPrincipals fullyTrusted
;
848 JSSecurityCallbacks
ShellPrincipals::securityCallbacks
= {
849 nullptr, // contentSecurityPolicyAllows
852 // The fully-trusted principal subsumes all other principals.
853 ShellPrincipals
ShellPrincipals::fullyTrusted(-1, 1);
857 extern MOZ_EXPORT
char* readline(const char* prompt
);
858 extern MOZ_EXPORT
void add_history(char* line
);
862 ShellContext::ShellContext(JSContext
* cx
, IsWorkerEnum isWorker_
)
865 lastWarningEnabled(false),
866 trackUnhandledRejections(true),
867 timeoutInterval(-1.0),
868 startTime(PRMJ_Now()),
869 serviceInterrupt(false),
870 haveInterruptFunc(false),
871 interruptFunc(cx
, NullValue()),
872 lastWarning(cx
, NullValue()),
873 promiseRejectionTrackerCallback(cx
, NullValue()),
874 unhandledRejectedPromises(cx
),
875 watchdogLock(mutexid::ShellContextWatchdog
),
881 offThreadMonitor(mutexid::ShellOffThreadState
),
882 finalizationRegistryCleanupCallbacks(cx
) {}
884 ShellContext
* js::shell::GetShellContext(JSContext
* cx
) {
885 ShellContext
* sc
= static_cast<ShellContext
*>(JS_GetContextPrivate(cx
));
890 static void TraceRootArrays(JSTracer
* trc
, gc::MarkColor color
) {
891 JSRuntime
* rt
= trc
->runtime();
892 for (ZonesIter
zone(rt
, SkipAtoms
); !zone
.done(); zone
.next()) {
893 for (CompartmentsInZoneIter
comp(zone
); !comp
.done(); comp
.next()) {
894 auto priv
= static_cast<ShellCompartmentPrivate
*>(
895 JS_GetCompartmentPrivate(comp
.get()));
900 GCPtr
<ArrayObject
*>& array
=
901 (color
== gc::MarkColor::Black
) ? priv
->blackRoot
: priv
->grayRoot
;
902 TraceNullableEdge(trc
, &array
, "shell root array");
905 // Trace the array elements as part of root marking.
906 for (uint32_t i
= 0; i
< array
->getDenseInitializedLength(); i
++) {
907 Value
& value
= const_cast<Value
&>(array
->getDenseElement(i
));
908 TraceManuallyBarrieredEdge(trc
, &value
, "shell root array element");
915 static void TraceBlackRoots(JSTracer
* trc
, void* data
) {
916 TraceRootArrays(trc
, gc::MarkColor::Black
);
919 static bool TraceGrayRoots(JSTracer
* trc
, SliceBudget
& budget
, void* data
) {
920 TraceRootArrays(trc
, gc::MarkColor::Gray
);
924 static inline JSString
* NewStringCopyUTF8(JSContext
* cx
, const char* chars
) {
925 return JS_NewStringCopyUTF8N(cx
, JS::UTF8Chars(chars
, strlen(chars
)));
928 static mozilla::UniqueFreePtr
<char[]> GetLine(FILE* file
, const char* prompt
) {
931 * Use readline only if file is stdin, because there's no way to specify
932 * another handle. Are other filehandles interactive?
935 mozilla::UniqueFreePtr
<char[]> linep(readline(prompt
));
937 * We set it to zero to avoid complaining about inappropriate ioctl
938 * for device in the case of EOF. Looks like errno == 251 if line is
939 * finished with EOF and errno == 25 (EINVAL on Mac) if there is
940 * nothing left to read.
942 if (errno
== 251 || errno
== 25 || errno
== EINVAL
) {
948 if (linep
[0] != '\0') {
949 add_history(linep
.get());
956 if (*prompt
!= '\0' && gOutFile
->isOpen()) {
957 fprintf(gOutFile
->fp
, "%s", prompt
);
958 fflush(gOutFile
->fp
);
962 mozilla::UniqueFreePtr
<char[]> buffer(static_cast<char*>(malloc(size
)));
967 char* current
= buffer
.get();
970 if (fgets(current
, size
- len
, file
)) {
973 if (errno
!= EINTR
) {
978 len
+= strlen(current
);
979 char* t
= buffer
.get() + len
- 1;
981 /* Line was read. We remove '\n' and exit. */
986 if (len
+ 1 == size
) {
988 char* raw
= buffer
.release();
989 char* tmp
= static_cast<char*>(realloc(raw
, size
));
996 current
= buffer
.get() + len
;
1001 static bool ShellInterruptCallback(JSContext
* cx
) {
1002 ShellContext
* sc
= GetShellContext(cx
);
1003 if (!sc
->serviceInterrupt
) {
1007 // Reset serviceInterrupt. CancelExecution or InterruptIf will set it to
1008 // true to distinguish watchdog or user triggered interrupts.
1009 // Do this first to prevent other interrupts that may occur while the
1010 // user-supplied callback is executing from re-entering the handler.
1011 sc
->serviceInterrupt
= false;
1014 if (sc
->haveInterruptFunc
) {
1015 bool wasAlreadyThrowing
= cx
->isExceptionPending();
1016 JS::AutoSaveExceptionState
savedExc(cx
);
1017 JSAutoRealm
ar(cx
, &sc
->interruptFunc
.toObject());
1018 RootedValue
rval(cx
);
1020 // Report any exceptions thrown by the JS interrupt callback, but do
1021 // *not* keep it on the cx. The interrupt handler is invoked at points
1022 // that are not expected to throw catchable exceptions, like at
1025 // If the interrupted JS code was already throwing, any exceptions
1026 // thrown by the interrupt handler are silently swallowed.
1028 Maybe
<AutoReportException
> are
;
1029 if (!wasAlreadyThrowing
) {
1032 result
= JS_CallFunctionValue(cx
, nullptr, sc
->interruptFunc
,
1033 JS::HandleValueArray::empty(), &rval
);
1037 if (rval
.isBoolean()) {
1038 result
= rval
.toBoolean();
1046 if (!result
&& sc
->exitCode
== 0) {
1047 static const char msg
[] = "Script terminated by interrupt handler.\n";
1050 sc
->exitCode
= EXITCODE_TIMEOUT
;
1056 static void GCSliceCallback(JSContext
* cx
, JS::GCProgress progress
,
1057 const JS::GCDescription
& desc
) {
1058 if (progress
== JS::GC_CYCLE_END
) {
1059 #if defined(MOZ_MEMORY)
1060 // We call this here to match the browser's DOMGCSliceCallback.
1061 jemalloc_free_dirty_pages();
1067 * Some UTF-8 files, notably those written using Notepad, have a Unicode
1068 * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order
1069 * is meaningless for UTF-8) but causes a syntax error unless we skip it.
1071 static void SkipUTF8BOM(FILE* file
) {
1072 int ch1
= fgetc(file
);
1073 int ch2
= fgetc(file
);
1074 int ch3
= fgetc(file
);
1077 if (ch1
== 0xEF && ch2
== 0xBB && ch3
== 0xBF) {
1093 void EnvironmentPreparer::invoke(HandleObject global
, Closure
& closure
) {
1094 MOZ_ASSERT(JS_IsGlobalObject(global
));
1096 JSContext
* cx
= TlsContext
.get();
1097 MOZ_ASSERT(!JS_IsExceptionPending(cx
));
1099 AutoRealm
ar(cx
, global
);
1100 AutoReportException
are(cx
);
1106 static bool RegisterScriptPathWithModuleLoader(JSContext
* cx
,
1107 HandleScript script
,
1108 const char* filename
) {
1109 // Set the private value associated with a script to a object containing the
1110 // script's filename so that the module loader can use it to resolve
1111 // relative imports.
1113 RootedString
path(cx
, NewStringCopyUTF8(cx
, filename
));
1118 MOZ_ASSERT(JS::GetScriptPrivate(script
).isUndefined());
1119 RootedObject
infoObject(cx
, js::CreateScriptPrivate(cx
, path
));
1124 JS::SetScriptPrivate(script
, ObjectValue(*infoObject
));
1128 enum class CompileUtf8
{
1133 [[nodiscard
]] static bool RunFile(JSContext
* cx
, const char* filename
,
1134 FILE* file
, CompileUtf8 compileMethod
,
1135 bool compileOnly
, bool fullParse
) {
1138 int64_t t1
= PRMJ_Now();
1139 RootedScript
script(cx
);
1142 CompileOptions
options(cx
);
1143 options
.setIntroductionType("js shell file")
1144 .setFileAndLine(filename
, 1)
1146 .setNoScriptRval(true);
1149 options
.setForceFullParse();
1151 options
.setEagerDelazificationStrategy(defaultDelazificationMode
);
1154 if (compileMethod
== CompileUtf8::DontInflate
) {
1155 script
= JS::CompileUtf8File(cx
, options
, file
);
1157 fprintf(stderr
, "(compiling '%s' after inflating to UTF-16)\n", filename
);
1159 FileContents
buffer(cx
);
1160 if (!ReadCompleteFile(cx
, file
, buffer
)) {
1164 size_t length
= buffer
.length();
1165 auto chars
= UniqueTwoByteChars(
1166 UTF8CharsToNewTwoByteCharsZ(
1168 JS::UTF8Chars(reinterpret_cast<const char*>(buffer
.begin()),
1170 &length
, js::MallocArena
)
1176 JS::SourceText
<char16_t
> source
;
1177 if (!source
.init(cx
, std::move(chars
), length
)) {
1181 script
= JS::Compile(cx
, options
, source
);
1189 if (!RegisterScriptPathWithModuleLoader(cx
, script
, filename
)) {
1194 if (dumpEntrainedVariables
) {
1195 AnalyzeEntrainedVariables(cx
, script
);
1199 if (!JS_ExecuteScript(cx
, script
)) {
1202 int64_t t2
= PRMJ_Now() - t1
;
1204 printf("runtime = %.3f ms\n", double(t2
) / PRMJ_USEC_PER_MSEC
);
1210 [[nodiscard
]] static bool RunModule(JSContext
* cx
, const char* filename
,
1212 ShellContext
* sc
= GetShellContext(cx
);
1214 RootedString
path(cx
, NewStringCopyUTF8(cx
, filename
));
1219 path
= ResolvePath(cx
, path
, RootRelative
);
1224 return sc
->moduleLoader
->loadRootModule(cx
, path
);
1227 static void ShellCleanupFinalizationRegistryCallback(JSFunction
* doCleanup
,
1228 JSObject
* incumbentGlobal
,
1230 // In the browser this queues a task. Shell jobs correspond to microtasks so
1231 // we arrange for cleanup to happen after all jobs/microtasks have run. The
1232 // incumbent global is ignored in the shell.
1234 auto sc
= static_cast<ShellContext
*>(data
);
1235 AutoEnterOOMUnsafeRegion oomUnsafe
;
1236 if (!sc
->finalizationRegistryCleanupCallbacks
.append(doCleanup
)) {
1237 oomUnsafe
.crash("ShellCleanupFinalizationRegistryCallback");
1241 // Run any FinalizationRegistry cleanup tasks and return whether any ran.
1242 static bool MaybeRunFinalizationRegistryCleanupTasks(JSContext
* cx
) {
1243 ShellContext
* sc
= GetShellContext(cx
);
1244 MOZ_ASSERT(!sc
->quitting
);
1246 Rooted
<ShellContext::FunctionVector
> callbacks(cx
);
1247 std::swap(callbacks
.get(), sc
->finalizationRegistryCleanupCallbacks
.get());
1249 bool ranTasks
= false;
1251 RootedFunction
callback(cx
);
1252 for (JSFunction
* f
: callbacks
) {
1255 JS::ExposeObjectToActiveJS(callback
);
1256 AutoRealm
ar(cx
, callback
);
1259 AutoReportException
are(cx
);
1260 RootedValue
unused(cx
);
1261 (void)JS_CallFunction(cx
, nullptr, callback
, HandleValueArray::empty(),
1275 static bool EnqueueJob(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1276 CallArgs args
= CallArgsFromVp(argc
, vp
);
1278 if (!IsFunctionObject(args
.get(0))) {
1279 JS_ReportErrorASCII(cx
, "EnqueueJob's first argument must be a function");
1283 args
.rval().setUndefined();
1285 RootedObject
job(cx
, &args
[0].toObject());
1286 return js::EnqueueJob(cx
, job
);
1289 static void RunShellJobs(JSContext
* cx
) {
1290 ShellContext
* sc
= GetShellContext(cx
);
1302 // Run tasks (only finalization registry clean tasks are possible).
1303 bool ranTasks
= MaybeRunFinalizationRegistryCleanupTasks(cx
);
1310 static bool DrainJobQueue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1311 CallArgs args
= CallArgsFromVp(argc
, vp
);
1313 if (GetShellContext(cx
)->quitting
) {
1314 JS_ReportErrorASCII(
1315 cx
, "Mustn't drain the job queue when the shell is quitting");
1321 if (GetShellContext(cx
)->quitting
) {
1325 args
.rval().setUndefined();
1329 static bool GlobalOfFirstJobInQueue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1330 CallArgs args
= CallArgsFromVp(argc
, vp
);
1332 RootedObject
job(cx
, cx
->internalJobQueue
->maybeFront());
1334 JS_ReportErrorASCII(cx
, "Job queue is empty");
1338 RootedObject
global(cx
, &job
->nonCCWGlobal());
1339 if (!cx
->compartment()->wrap(cx
, &global
)) {
1343 args
.rval().setObject(*global
);
1347 static bool TrackUnhandledRejections(JSContext
* cx
, JS::HandleObject promise
,
1348 JS::PromiseRejectionHandlingState state
) {
1349 ShellContext
* sc
= GetShellContext(cx
);
1350 if (!sc
->trackUnhandledRejections
) {
1354 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
1355 if (cx
->runningOOMTest
) {
1356 // When OOM happens, we cannot reliably track the set of unhandled
1357 // promise rejections. Throw error only when simulated OOM is used
1358 // *and* promises are used in the test.
1359 JS_ReportErrorASCII(
1361 "Can't track unhandled rejections while running simulated OOM "
1362 "test. Call ignoreUnhandledRejections before using oomTest etc.");
1367 if (!sc
->unhandledRejectedPromises
) {
1368 sc
->unhandledRejectedPromises
= SetObject::create(cx
);
1369 if (!sc
->unhandledRejectedPromises
) {
1374 RootedValue
promiseVal(cx
, ObjectValue(*promise
));
1376 AutoRealm
ar(cx
, sc
->unhandledRejectedPromises
);
1377 if (!cx
->compartment()->wrap(cx
, &promiseVal
)) {
1382 case JS::PromiseRejectionHandlingState::Unhandled
:
1383 if (!SetObject::add(cx
, sc
->unhandledRejectedPromises
, promiseVal
)) {
1387 case JS::PromiseRejectionHandlingState::Handled
:
1388 bool deleted
= false;
1389 if (!SetObject::delete_(cx
, sc
->unhandledRejectedPromises
, promiseVal
,
1393 // We can't MOZ_ASSERT(deleted) here, because it's possible we failed to
1394 // add the promise in the first place, due to OOM.
1401 static void ForwardingPromiseRejectionTrackerCallback(
1402 JSContext
* cx
, bool mutedErrors
, JS::HandleObject promise
,
1403 JS::PromiseRejectionHandlingState state
, void* data
) {
1404 AutoReportException
are(cx
);
1406 if (!TrackUnhandledRejections(cx
, promise
, state
)) {
1410 RootedValue
callback(cx
,
1411 GetShellContext(cx
)->promiseRejectionTrackerCallback
);
1412 if (callback
.isNull()) {
1416 AutoRealm
ar(cx
, &callback
.toObject());
1418 FixedInvokeArgs
<2> args(cx
);
1419 args
[0].setObject(*promise
);
1420 args
[1].setInt32(static_cast<int32_t>(state
));
1422 if (!JS_WrapValue(cx
, args
[0])) {
1426 RootedValue
rval(cx
);
1427 (void)Call(cx
, callback
, UndefinedHandleValue
, args
, &rval
);
1430 static bool SetPromiseRejectionTrackerCallback(JSContext
* cx
, unsigned argc
,
1432 CallArgs args
= CallArgsFromVp(argc
, vp
);
1434 if (!IsFunctionObject(args
.get(0))) {
1435 JS_ReportErrorASCII(
1437 "setPromiseRejectionTrackerCallback expects a function as its sole "
1442 GetShellContext(cx
)->promiseRejectionTrackerCallback
= args
[0];
1444 args
.rval().setUndefined();
1449 static const char* telemetryNames
[static_cast<int>(JSMetric::Count
)] = {
1450 #define LIT(NAME, _) #NAME,
1451 FOR_EACH_JS_METRIC(LIT
)
1456 // Telemetry can be executed from multiple threads, and the callback is
1457 // responsible to avoid contention on the recorded telemetry data.
1458 static Mutex
* telemetryLock
= nullptr;
1459 class MOZ_RAII AutoLockTelemetry
: public LockGuard
<Mutex
> {
1460 using Base
= LockGuard
<Mutex
>;
1463 AutoLockTelemetry() : Base(*telemetryLock
) { MOZ_ASSERT(telemetryLock
); }
1466 using TelemetryData
= uint32_t;
1467 using TelemetryVec
= Vector
<TelemetryData
, 0, SystemAllocPolicy
>;
1468 static mozilla::Array
<TelemetryVec
, size_t(JSMetric::Count
)> telemetryResults
;
1469 static void AccumulateTelemetryDataCallback(JSMetric id
, uint32_t sample
) {
1470 AutoLockTelemetry alt
;
1471 // We ignore OOMs while writting teleemtry data.
1472 if (telemetryResults
[static_cast<int>(id
)].append(sample
)) {
1477 static void WriteTelemetryDataToDisk(const char* dir
) {
1478 const int pathLen
= 260;
1479 char fileName
[pathLen
];
1481 auto initOutput
= [&](const char* name
) -> bool {
1482 if (SprintfLiteral(fileName
, "%s%s.csv", dir
, name
) >= pathLen
) {
1485 FILE* file
= fopen(fileName
, "a");
1493 for (size_t id
= 0; id
< size_t(JSMetric::Count
); id
++) {
1494 auto clear
= MakeScopeExit([&] { telemetryResults
[id
].clearAndFree(); });
1495 if (!initOutput(telemetryNames
[id
])) {
1498 for (uint32_t data
: telemetryResults
[id
]) {
1499 output
.printf("%u\n", data
);
1505 #undef MAP_TELEMETRY
1507 static bool BoundToAsyncStack(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1508 CallArgs args
= CallArgsFromVp(argc
, vp
);
1510 RootedValue
function(cx
, GetFunctionNativeReserved(&args
.callee(), 0));
1511 RootedObject
options(
1512 cx
, &GetFunctionNativeReserved(&args
.callee(), 1).toObject());
1514 Rooted
<SavedFrame
*> stack(cx
, nullptr);
1519 if (!JS_GetProperty(cx
, options
, "stack", &v
)) {
1522 if (!v
.isObject() || !v
.toObject().is
<SavedFrame
>()) {
1523 JS_ReportErrorASCII(cx
,
1524 "The 'stack' property must be a SavedFrame object.");
1527 stack
= &v
.toObject().as
<SavedFrame
>();
1529 if (!JS_GetProperty(cx
, options
, "cause", &v
)) {
1532 RootedString
causeString(cx
, ToString(cx
, v
));
1537 UniqueChars cause
= JS_EncodeStringToUTF8(cx
, causeString
);
1539 MOZ_ASSERT(cx
->isExceptionPending());
1543 if (!JS_GetProperty(cx
, options
, "explicit", &v
)) {
1546 isExplicit
= v
.isUndefined() ? true : ToBoolean(v
);
1549 (isExplicit
? JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT
1550 : JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::IMPLICIT
);
1552 JS::AutoSetAsyncStackForNewCalls
asasfnckthxbye(cx
, stack
, cause
.get(), kind
);
1553 return Call(cx
, UndefinedHandleValue
, function
, JS::HandleValueArray::empty(),
1557 static bool BindToAsyncStack(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1558 CallArgs args
= CallArgsFromVp(argc
, vp
);
1560 if (args
.length() != 2) {
1561 JS_ReportErrorASCII(cx
, "bindToAsyncStack takes exactly two arguments.");
1565 if (!args
[0].isObject() || !IsCallable(args
[0])) {
1566 JS_ReportErrorASCII(
1567 cx
, "bindToAsyncStack's first argument should be a function.");
1571 if (!args
[1].isObject()) {
1572 JS_ReportErrorASCII(
1573 cx
, "bindToAsyncStack's second argument should be an object.");
1577 RootedFunction
bound(cx
, NewFunctionWithReserved(cx
, BoundToAsyncStack
, 0, 0,
1578 "bindToAsyncStack thunk"));
1582 SetFunctionNativeReserved(bound
, 0, args
[0]);
1583 SetFunctionNativeReserved(bound
, 1, args
[1]);
1585 args
.rval().setObject(*bound
);
1589 #ifdef JS_HAS_INTL_API
1590 static bool AddIntlExtras(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1591 CallArgs args
= CallArgsFromVp(argc
, vp
);
1592 if (!args
.get(0).isObject()) {
1593 JS_ReportErrorASCII(cx
, "addIntlExtras must be passed an object");
1596 JS::RootedObject
intl(cx
, &args
[0].toObject());
1598 static const JSFunctionSpec funcs
[] = {
1599 JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0),
1602 if (!JS_DefineFunctions(cx
, intl
, funcs
)) {
1606 if (!JS::AddMozDateTimeFormatConstructor(cx
, intl
)) {
1610 if (!JS::AddMozDisplayNamesConstructor(cx
, intl
)) {
1614 args
.rval().setUndefined();
1617 #endif // JS_HAS_INTL_API
1619 [[nodiscard
]] static bool EvalUtf8AndPrint(JSContext
* cx
, const char* bytes
,
1620 size_t length
, int lineno
,
1623 JS::CompileOptions
options(cx
);
1624 options
.setIntroductionType("js shell interactive")
1626 .setFileAndLine("typein", lineno
)
1627 .setEagerDelazificationStrategy(defaultDelazificationMode
);
1629 JS::SourceText
<Utf8Unit
> srcBuf
;
1630 if (!srcBuf
.init(cx
, bytes
, length
, JS::SourceOwnership::Borrowed
)) {
1634 RootedScript
script(cx
, JS::Compile(cx
, options
, srcBuf
));
1641 RootedValue
result(cx
);
1642 if (!JS_ExecuteScript(cx
, script
, &result
)) {
1646 if (!result
.isUndefined() && gOutFile
->isOpen()) {
1648 RootedString
str(cx
, JS_ValueToSource(cx
, result
));
1653 UniqueChars utf8chars
= JS_EncodeStringToUTF8(cx
, str
);
1657 fprintf(gOutFile
->fp
, "%s\n", utf8chars
.get());
1662 [[nodiscard
]] static bool ReadEvalPrintLoop(JSContext
* cx
, FILE* in
,
1664 ShellContext
* sc
= GetShellContext(cx
);
1666 bool hitEOF
= false;
1670 * Accumulate lines until we get a 'compilable unit' - one that either
1671 * generates an error (before running out of source) or that compiles
1672 * cleanly. This should be whenever we get a complete statement that
1673 * coincides with the end of a line.
1675 int startline
= lineno
;
1676 typedef Vector
<char, 32> CharBuffer
;
1677 RootedObject
globalLexical(cx
, &cx
->global()->lexicalEnvironment());
1678 CharBuffer
buffer(cx
);
1680 ScheduleWatchdog(cx
, -1);
1681 sc
->serviceInterrupt
= false;
1684 mozilla::UniqueFreePtr
<char[]> line
=
1685 GetLine(in
, startline
== lineno
? "js> " : "");
1688 if (UniqueChars error
= SystemErrorMessage(cx
, errno
)) {
1689 JS_ReportErrorUTF8(cx
, "%s", error
.get());
1697 if (!buffer
.append(line
.get(), strlen(line
.get())) ||
1698 !buffer
.append('\n')) {
1703 if (!ScheduleWatchdog(cx
, sc
->timeoutInterval
)) {
1707 } while (!JS_Utf8BufferIsCompilableUnit(cx
, cx
->global(), buffer
.begin(),
1710 if (hitEOF
&& buffer
.empty()) {
1715 // Report exceptions but keep going.
1716 AutoReportException
are(cx
);
1717 (void)EvalUtf8AndPrint(cx
, buffer
.begin(), buffer
.length(), startline
,
1721 // If a let or const fail to initialize they will remain in an unusable
1722 // without further intervention. This call cleans up the global scope,
1723 // setting uninitialized lexicals to undefined so that they may still
1724 // be used. This behavior is _only_ acceptable in the context of the repl.
1725 if (JS::ForceLexicalInitialization(cx
, globalLexical
) &&
1726 gErrFile
->isOpen()) {
1728 "Warning: According to the standard, after the above exception,\n"
1729 "Warning: the global bindings should be permanently uninitialized.\n"
1730 "Warning: We have non-standard-ly initialized them to `undefined`"
1731 "for you.\nWarning: This nicety only happens in the JS shell.\n",
1736 } while (!hitEOF
&& !sc
->quitting
);
1738 if (gOutFile
->isOpen()) {
1739 fprintf(gOutFile
->fp
, "\n");
1746 PreludeScript
, // UTF-8 script, fully-parsed, to avoid conflicting
1748 FileScript
, // UTF-8, directly parsed as such
1749 FileScriptUtf16
, // FileScript, but inflate to UTF-16 before parsing
1753 [[nodiscard
]] static bool Process(JSContext
* cx
, const char* filename
,
1754 bool forceTTY
, FileKind kind
) {
1756 if (forceTTY
|| !filename
|| strcmp(filename
, "-") == 0) {
1759 file
= OpenFile(cx
, filename
, "rb");
1764 AutoCloseFile
autoClose(file
);
1766 bool fullParse
= false;
1767 if (!forceTTY
&& !isatty(fileno(file
))) {
1768 // It's not interactive - just execute it.
1772 if (!RunFile(cx
, filename
, file
, CompileUtf8::DontInflate
, compileOnly
,
1778 if (!RunFile(cx
, filename
, file
, CompileUtf8::DontInflate
, compileOnly
,
1783 case FileScriptUtf16
:
1784 if (!RunFile(cx
, filename
, file
, CompileUtf8::InflateToUtf16
,
1785 compileOnly
, fullParse
)) {
1790 if (!RunModule(cx
, filename
, compileOnly
)) {
1795 MOZ_CRASH("Impossible FileKind!");
1798 // It's an interactive filehandle; drop into read-eval-print loop.
1799 MOZ_ASSERT(kind
== FileScript
);
1800 if (!ReadEvalPrintLoop(cx
, file
, compileOnly
)) {
1804 #ifdef FUZZING_JS_FUZZILLI
1805 fprintf(stderr
, "executionHash is 0x%x with %d inputs\n", cx
->executionHash
,
1806 cx
->executionHashInputs
);
1812 # define GET_FD_FROM_FILE(a) int(_get_osfhandle(fileno(a)))
1814 # define GET_FD_FROM_FILE(a) fileno(a)
1817 static void freeExternalCallback(void* contents
, void* userData
) {
1818 MOZ_ASSERT(!userData
);
1822 static bool CreateExternalArrayBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1823 CallArgs args
= CallArgsFromVp(argc
, vp
);
1824 if (args
.length() != 1) {
1825 JS_ReportErrorNumberASCII(
1826 cx
, my_GetErrorMessage
, nullptr,
1827 args
.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS
,
1828 "createExternalArrayBuffer");
1833 if (!ToInt32(cx
, args
[0], &bytes
)) {
1838 JS_ReportErrorASCII(cx
, "Size must be non-negative");
1842 void* buffer
= js_calloc(bytes
);
1844 JS_ReportOutOfMemory(cx
);
1848 UniquePtr
<void, JS::BufferContentsDeleter
> ptr
{buffer
,
1849 {&freeExternalCallback
}};
1850 RootedObject
arrayBuffer(
1851 cx
, JS::NewExternalArrayBuffer(cx
, bytes
, std::move(ptr
)));
1856 args
.rval().setObject(*arrayBuffer
);
1860 static bool CreateMappedArrayBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1861 CallArgs args
= CallArgsFromVp(argc
, vp
);
1863 if (args
.length() < 1 || args
.length() > 3) {
1864 JS_ReportErrorNumberASCII(
1865 cx
, my_GetErrorMessage
, nullptr,
1866 args
.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS
,
1867 "createMappedArrayBuffer");
1871 RootedString
rawFilenameStr(cx
, JS::ToString(cx
, args
[0]));
1872 if (!rawFilenameStr
) {
1875 // It's a little bizarre to resolve relative to the script, but for testing
1876 // I need a file at a known location, and the only good way I know of to do
1877 // that right now is to include it in the repo alongside the test script.
1878 // Bug 944164 would introduce an alternative.
1879 Rooted
<JSString
*> filenameStr(
1880 cx
, ResolvePath(cx
, rawFilenameStr
, ScriptRelative
));
1884 UniqueChars filename
= JS_EncodeStringToUTF8(cx
, filenameStr
);
1889 uint32_t offset
= 0;
1890 if (args
.length() >= 2) {
1891 if (!JS::ToUint32(cx
, args
[1], &offset
)) {
1896 bool sizeGiven
= false;
1898 if (args
.length() >= 3) {
1899 if (!JS::ToUint32(cx
, args
[2], &size
)) {
1904 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1905 JSMSG_BAD_ARRAY_LENGTH
);
1910 FILE* file
= OpenFile(cx
, filename
.get(), "rb");
1914 AutoCloseFile
autoClose(file
);
1917 if (fstat(fileno(file
), &st
) < 0) {
1918 JS_ReportErrorASCII(cx
, "Unable to stat file");
1922 if ((st
.st_mode
& S_IFMT
) != S_IFREG
) {
1923 JS_ReportErrorASCII(cx
, "Path is not a regular file");
1928 if (off_t(offset
) >= st
.st_size
) {
1929 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1930 JSMSG_OFFSET_LARGER_THAN_FILESIZE
);
1933 size
= st
.st_size
- offset
;
1937 JS::CreateMappedArrayBufferContents(GET_FD_FROM_FILE(file
), offset
, size
);
1939 JS_ReportErrorASCII(cx
,
1940 "failed to allocate mapped array buffer contents "
1941 "(possibly due to bad alignment)");
1945 RootedObject
obj(cx
,
1946 JS::NewMappedArrayBufferWithContents(cx
, size
, contents
));
1951 args
.rval().setObject(*obj
);
1955 #undef GET_FD_FROM_FILE
1957 class UserBufferObject
: public NativeObject
{
1958 static const uint32_t BUFFER_SLOT
= 0;
1959 static const uint32_t BYTE_LENGTH_SLOT
= 1;
1960 static const uint32_t RESERVED_SLOTS
= 2;
1962 static constexpr auto BufferMemoryUse
= MemoryUse::Embedding1
;
1964 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
);
1967 static const JSClassOps classOps_
;
1968 static const JSClass class_
;
1970 [[nodiscard
]] static UserBufferObject
* create(JSContext
* cx
,
1973 void* buffer() const {
1974 auto& buffer
= getReservedSlot(BUFFER_SLOT
);
1975 if (buffer
.isUndefined()) {
1978 return buffer
.toPrivate();
1981 size_t byteLength() const {
1982 return size_t(getReservedSlot(BYTE_LENGTH_SLOT
).toPrivate());
1986 const JSClassOps
UserBufferObject::classOps_
= {
1987 nullptr, // addProperty
1988 nullptr, // delProperty
1989 nullptr, // enumerate
1990 nullptr, // newEnumerate
1992 nullptr, // mayResolve
1993 UserBufferObject::finalize
, // finalize
1995 nullptr, // construct
1999 const JSClass
UserBufferObject::class_
= {
2001 JSCLASS_HAS_RESERVED_SLOTS(UserBufferObject::RESERVED_SLOTS
) |
2002 JSCLASS_BACKGROUND_FINALIZE
,
2003 &UserBufferObject::classOps_
,
2006 UserBufferObject
* UserBufferObject::create(JSContext
* cx
, size_t byteLength
) {
2007 void* buffer
= js_calloc(byteLength
);
2009 JS_ReportOutOfMemory(cx
);
2012 UniquePtr
<void, JS::FreePolicy
> ptr(buffer
);
2014 auto* userBuffer
= NewObjectWithGivenProto
<UserBufferObject
>(cx
, nullptr);
2019 InitReservedSlot(userBuffer
, BUFFER_SLOT
, ptr
.release(), byteLength
,
2021 userBuffer
->initReservedSlot(BYTE_LENGTH_SLOT
, PrivateValue(byteLength
));
2026 void UserBufferObject::finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
2027 auto* userBuffer
= &obj
->as
<UserBufferObject
>();
2028 if (auto* buffer
= userBuffer
->buffer()) {
2029 gcx
->free_(userBuffer
, buffer
, userBuffer
->byteLength(), BufferMemoryUse
);
2033 static bool CreateUserArrayBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2034 CallArgs args
= CallArgsFromVp(argc
, vp
);
2035 if (args
.length() != 1) {
2036 JS_ReportErrorNumberASCII(
2037 cx
, my_GetErrorMessage
, nullptr,
2038 args
.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS
,
2039 "createUserArrayBuffer");
2044 if (!ToInt32(cx
, args
[0], &bytes
)) {
2048 JS_ReportErrorASCII(cx
, "Size must be non-negative");
2052 Rooted
<UserBufferObject
*> userBuffer(cx
, UserBufferObject::create(cx
, bytes
));
2057 Rooted
<JSObject
*> arrayBuffer(
2058 cx
, JS::NewArrayBufferWithUserOwnedContents(cx
, userBuffer
->byteLength(),
2059 userBuffer
->buffer()));
2064 // Create a strong reference from |arrayBuffer| to |userBuffer|. This ensures
2065 // |userBuffer| can't outlive |arrayBuffer|. That way we don't have to worry
2066 // about detaching the ArrayBuffer object when |userBuffer| gets finalized.
2067 // The reference is made through a private name, because we don't want to
2068 // expose |userBuffer| to user-code.
2070 auto* privateName
= NewPrivateName(cx
, cx
->names().empty_
.toHandle());
2075 Rooted
<PropertyKey
> id(cx
, PropertyKey::Symbol(privateName
));
2076 Rooted
<JS::Value
> userBufferVal(cx
, ObjectValue(*userBuffer
));
2077 if (!js::DefineDataProperty(cx
, arrayBuffer
, id
, userBufferVal
, 0)) {
2081 args
.rval().setObject(*arrayBuffer
);
2085 static bool AddPromiseReactions(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2086 CallArgs args
= CallArgsFromVp(argc
, vp
);
2088 if (args
.length() != 3) {
2089 JS_ReportErrorNumberASCII(
2090 cx
, my_GetErrorMessage
, nullptr,
2091 args
.length() < 3 ? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS
,
2092 "addPromiseReactions");
2096 RootedObject
promise(cx
);
2097 if (args
[0].isObject()) {
2098 promise
= &args
[0].toObject();
2101 if (!promise
|| !JS::IsPromiseObject(promise
)) {
2102 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2103 JSSMSG_INVALID_ARGS
, "addPromiseReactions");
2107 RootedObject
onResolve(cx
);
2108 if (args
[1].isObject()) {
2109 onResolve
= &args
[1].toObject();
2112 RootedObject
onReject(cx
);
2113 if (args
[2].isObject()) {
2114 onReject
= &args
[2].toObject();
2117 if (!onResolve
|| !onResolve
->is
<JSFunction
>() || !onReject
||
2118 !onReject
->is
<JSFunction
>()) {
2119 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2120 JSSMSG_INVALID_ARGS
, "addPromiseReactions");
2124 return JS::AddPromiseReactions(cx
, promise
, onResolve
, onReject
);
2127 static bool IgnoreUnhandledRejections(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2128 CallArgs args
= CallArgsFromVp(argc
, vp
);
2130 ShellContext
* sc
= GetShellContext(cx
);
2131 sc
->trackUnhandledRejections
= false;
2133 args
.rval().setUndefined();
2137 static bool Options(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2138 CallArgs args
= CallArgsFromVp(argc
, vp
);
2140 JS::ContextOptions oldContextOptions
= JS::ContextOptionsRef(cx
);
2141 for (unsigned i
= 0; i
< args
.length(); i
++) {
2142 RootedString
str(cx
, JS::ToString(cx
, args
[i
]));
2147 Rooted
<JSLinearString
*> opt(cx
, str
->ensureLinear(cx
));
2152 if (StringEqualsLiteral(opt
, "throw_on_asmjs_validation_failure")) {
2153 JS::ContextOptionsRef(cx
).toggleThrowOnAsmJSValidationFailure();
2155 UniqueChars optChars
= QuoteString(cx
, opt
, '"');
2160 JS_ReportErrorASCII(cx
,
2161 "unknown option name %s."
2162 " The valid name is "
2163 "throw_on_asmjs_validation_failure.",
2169 UniqueChars names
= DuplicateString("");
2171 if (names
&& oldContextOptions
.throwOnAsmJSValidationFailure()) {
2172 names
= JS_sprintf_append(std::move(names
), "%s%s", found
? "," : "",
2173 "throw_on_asmjs_validation_failure");
2177 JS_ReportOutOfMemory(cx
);
2181 JSString
* str
= JS_NewStringCopyZ(cx
, names
.get());
2185 args
.rval().setString(str
);
2189 static bool LoadScript(JSContext
* cx
, unsigned argc
, Value
* vp
,
2190 bool scriptRelative
) {
2191 CallArgs args
= CallArgsFromVp(argc
, vp
);
2193 RootedString
str(cx
);
2194 for (unsigned i
= 0; i
< args
.length(); i
++) {
2195 str
= JS::ToString(cx
, args
[i
]);
2197 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2198 JSSMSG_INVALID_ARGS
, "load");
2202 str
= ResolvePath(cx
, str
, scriptRelative
? ScriptRelative
: RootRelative
);
2204 JS_ReportErrorASCII(cx
, "unable to resolve path");
2208 UniqueChars filename
= JS_EncodeStringToUTF8(cx
, str
);
2215 CompileOptions
opts(cx
);
2216 opts
.setIntroductionType("js shell load")
2218 .setNoScriptRval(true)
2219 .setEagerDelazificationStrategy(defaultDelazificationMode
);
2221 RootedValue
unused(cx
);
2223 ? JS::CompileUtf8Path(cx
, opts
, filename
.get()) != nullptr
2224 : JS::EvaluateUtf8Path(cx
, opts
, filename
.get(), &unused
))) {
2229 args
.rval().setUndefined();
2233 static bool Load(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2234 return LoadScript(cx
, argc
, vp
, false);
2237 static bool LoadScriptRelativeToScript(JSContext
* cx
, unsigned argc
,
2239 return LoadScript(cx
, argc
, vp
, true);
2242 static void my_LargeAllocFailCallback() {
2243 JSContext
* cx
= TlsContext
.get();
2248 MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
2250 JS::PrepareForFullGC(cx
);
2251 cx
->runtime()->gc
.gc(JS::GCOptions::Shrink
,
2252 JS::GCReason::SHARED_MEMORY_LIMIT
);
2255 static const uint32_t CacheEntry_SOURCE
= 0;
2256 static const uint32_t CacheEntry_BYTECODE
= 1;
2257 static const uint32_t CacheEntry_OPTIONS
= 2;
2259 // Some compile options can't be combined differently between save and load.
2261 // CacheEntries store a CacheOption set, and on load an exception is thrown
2262 // if the entries are incompatible.
2264 enum CacheOptions
: uint32_t {
2273 struct CacheOptionSet
: public mozilla::EnumSet
<CacheOptions
> {
2274 using mozilla::EnumSet
<CacheOptions
>::EnumSet
;
2276 explicit CacheOptionSet(const CompileOptions
& options
) : EnumSet() {
2277 initFromOptions(options
);
2280 void initFromOptions(const CompileOptions
& options
) {
2281 if (options
.noScriptRval
) {
2282 *this += CacheOptions::NoScriptRval
;
2284 if (options
.isRunOnce
) {
2285 *this += CacheOptions::IsRunOnce
;
2287 if (options
.sourceIsLazy
) {
2288 *this += CacheOptions::SourceIsLazy
;
2290 if (options
.forceFullParse()) {
2291 *this += CacheOptions::ForceFullParse
;
2293 if (options
.nonSyntacticScope
) {
2294 *this += CacheOptions::NonSyntactic
;
2299 static bool CacheOptionsCompatible(const CacheOptionSet
& a
,
2300 const CacheOptionSet
& b
) {
2301 // If the options are identical, they are trivially compatible.
2305 static const JSClass CacheEntry_class
= {"CacheEntryObject",
2306 JSCLASS_HAS_RESERVED_SLOTS(3)};
2308 static bool CacheEntry(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
2309 CallArgs args
= CallArgsFromVp(argc
, vp
);
2311 if (args
.length() != 1 || !args
[0].isString()) {
2312 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2313 JSSMSG_INVALID_ARGS
, "CacheEntry");
2317 RootedObject
obj(cx
, JS_NewObject(cx
, &CacheEntry_class
));
2322 JS::SetReservedSlot(obj
, CacheEntry_SOURCE
, args
[0]);
2323 JS::SetReservedSlot(obj
, CacheEntry_BYTECODE
, UndefinedValue());
2325 // Fill in empty option set.
2326 CacheOptionSet defaultOptions
;
2327 JS::SetReservedSlot(obj
, CacheEntry_OPTIONS
,
2328 Int32Value(defaultOptions
.serialize()));
2330 args
.rval().setObject(*obj
);
2334 static bool CacheEntry_isCacheEntry(JSObject
* cache
) {
2335 return cache
->hasClass(&CacheEntry_class
);
2338 static JSString
* CacheEntry_getSource(JSContext
* cx
, HandleObject cache
) {
2339 MOZ_ASSERT(CacheEntry_isCacheEntry(cache
));
2340 Value v
= JS::GetReservedSlot(cache
, CacheEntry_SOURCE
);
2341 if (!v
.isString()) {
2342 JS_ReportErrorASCII(
2343 cx
, "CacheEntry_getSource: Unexpected type of source reserved slot.");
2347 return v
.toString();
2350 static bool CacheEntry_compatible(JSContext
* cx
, HandleObject cache
,
2351 const CacheOptionSet
& currentOptionSet
) {
2352 CacheOptionSet cacheEntryOptions
;
2353 MOZ_ASSERT(CacheEntry_isCacheEntry(cache
));
2354 Value v
= JS::GetReservedSlot(cache
, CacheEntry_OPTIONS
);
2355 cacheEntryOptions
.deserialize(v
.toInt32());
2356 if (!CacheOptionsCompatible(cacheEntryOptions
, currentOptionSet
)) {
2357 JS_ReportErrorASCII(cx
,
2358 "CacheEntry_compatible: Incompatible cache contents");
2364 static uint8_t* CacheEntry_getBytecode(JSContext
* cx
, HandleObject cache
,
2366 MOZ_ASSERT(CacheEntry_isCacheEntry(cache
));
2367 Value v
= JS::GetReservedSlot(cache
, CacheEntry_BYTECODE
);
2368 if (!v
.isObject() || !v
.toObject().is
<ArrayBufferObject
>()) {
2369 JS_ReportErrorASCII(
2371 "CacheEntry_getBytecode: Unexpected type of bytecode reserved slot.");
2375 ArrayBufferObject
* arrayBuffer
= &v
.toObject().as
<ArrayBufferObject
>();
2376 *length
= arrayBuffer
->byteLength();
2377 return arrayBuffer
->dataPointer();
2380 static bool CacheEntry_setBytecode(JSContext
* cx
, HandleObject cache
,
2381 const CacheOptionSet
& cacheOptions
,
2382 uint8_t* buffer
, uint32_t length
) {
2383 MOZ_ASSERT(CacheEntry_isCacheEntry(cache
));
2385 using BufferContents
= ArrayBufferObject::BufferContents
;
2387 BufferContents contents
= BufferContents::createMallocedUnknownArena(buffer
);
2388 Rooted
<ArrayBufferObject
*> arrayBuffer(
2389 cx
, ArrayBufferObject::createForContents(cx
, length
, contents
));
2394 JS::SetReservedSlot(cache
, CacheEntry_BYTECODE
, ObjectValue(*arrayBuffer
));
2395 JS::SetReservedSlot(cache
, CacheEntry_OPTIONS
,
2396 Int32Value(cacheOptions
.serialize()));
2400 static bool ConvertTranscodeResultToJSException(JSContext
* cx
,
2401 JS::TranscodeResult rv
) {
2403 case JS::TranscodeResult::Ok
:
2408 case JS::TranscodeResult::Failure
:
2409 MOZ_ASSERT(!cx
->isExceptionPending());
2410 JS_ReportErrorASCII(cx
, "generic warning");
2412 case JS::TranscodeResult::Failure_BadBuildId
:
2413 MOZ_ASSERT(!cx
->isExceptionPending());
2414 JS_ReportErrorASCII(cx
, "the build-id does not match");
2416 case JS::TranscodeResult::Failure_AsmJSNotSupported
:
2417 MOZ_ASSERT(!cx
->isExceptionPending());
2418 JS_ReportErrorASCII(cx
, "Asm.js is not supported by XDR");
2420 case JS::TranscodeResult::Failure_BadDecode
:
2421 MOZ_ASSERT(!cx
->isExceptionPending());
2422 JS_ReportErrorASCII(cx
, "XDR data corruption");
2425 case JS::TranscodeResult::Throw
:
2426 MOZ_ASSERT(cx
->isExceptionPending());
2431 static void SetQuitting(JSContext
* cx
, int32_t code
) {
2432 ShellContext
* sc
= GetShellContext(cx
);
2433 js::StopDrainingJobQueue(cx
);
2434 sc
->exitCode
= code
;
2435 sc
->quitting
= true;
2438 static void UnsetQuitting(JSContext
* cx
) {
2439 ShellContext
* sc
= GetShellContext(cx
);
2440 js::RestartDrainingJobQueue(cx
);
2442 sc
->quitting
= false;
2445 static bool Evaluate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2446 CallArgs args
= CallArgsFromVp(argc
, vp
);
2448 if (args
.length() < 1 || args
.length() > 2) {
2449 JS_ReportErrorNumberASCII(
2450 cx
, my_GetErrorMessage
, nullptr,
2451 args
.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS
,
2456 RootedString
code(cx
, nullptr);
2457 RootedObject
cacheEntry(cx
, nullptr);
2458 if (args
[0].isString()) {
2459 code
= args
[0].toString();
2460 } else if (args
[0].isObject() &&
2461 CacheEntry_isCacheEntry(&args
[0].toObject())) {
2462 cacheEntry
= &args
[0].toObject();
2463 code
= CacheEntry_getSource(cx
, cacheEntry
);
2469 if (!code
|| (args
.length() == 2 && args
[1].isPrimitive())) {
2470 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2471 JSSMSG_INVALID_ARGS
, "evaluate");
2475 RootedObject
opts(cx
);
2476 if (args
.length() == 2) {
2477 if (!args
[1].isObject()) {
2478 JS_ReportErrorASCII(cx
, "evaluate: The 2nd argument must be an object");
2482 opts
= &args
[1].toObject();
2485 RootedObject
global(cx
, JS::CurrentGlobalOrNull(cx
));
2488 // Check "global" property before everything to use the given global's
2489 // option as the default value.
2490 Maybe
<CompileOptions
> maybeOptions
;
2493 if (!JS_GetProperty(cx
, opts
, "global", &v
)) {
2496 if (!v
.isUndefined()) {
2498 global
= js::CheckedUnwrapDynamic(&v
.toObject(), cx
,
2499 /* stopAtWindowProxy = */ false);
2504 if (!global
|| !(JS::GetClass(global
)->flags
& JSCLASS_IS_GLOBAL
)) {
2505 JS_ReportErrorNumberASCII(
2506 cx
, GetErrorMessage
, nullptr, JSMSG_UNEXPECTED_TYPE
,
2507 "\"global\" passed to evaluate()", "not a global object");
2511 JSAutoRealm
ar(cx
, global
);
2512 maybeOptions
.emplace(cx
);
2515 if (!maybeOptions
) {
2516 // If "global" property is not given, use the current global's option as
2517 // the default value.
2518 maybeOptions
.emplace(cx
);
2521 CompileOptions
& options
= maybeOptions
.ref();
2522 UniqueChars fileNameBytes
;
2523 RootedString
displayURL(cx
);
2524 RootedString
sourceMapURL(cx
);
2525 bool catchTermination
= false;
2526 bool loadBytecode
= false;
2527 bool saveIncrementalBytecode
= false;
2528 bool execute
= true;
2529 bool assertEqBytecode
= false;
2530 JS::RootedObjectVector
envChain(cx
);
2531 RootedObject
callerGlobal(cx
, cx
->global());
2533 options
.setIntroductionType("js shell evaluate")
2534 .setFileAndLine("@evaluate", 1)
2535 .setDeferDebugMetadata();
2537 RootedValue
privateValue(cx
);
2538 RootedString
elementAttributeName(cx
);
2541 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
2544 if (!ParseDebugMetadata(cx
, opts
, &privateValue
, &elementAttributeName
)) {
2547 if (!ParseSourceOptions(cx
, opts
, &displayURL
, &sourceMapURL
)) {
2552 if (!JS_GetProperty(cx
, opts
, "catchTermination", &v
)) {
2555 if (!v
.isUndefined()) {
2556 catchTermination
= ToBoolean(v
);
2559 if (!JS_GetProperty(cx
, opts
, "loadBytecode", &v
)) {
2562 if (!v
.isUndefined()) {
2563 loadBytecode
= ToBoolean(v
);
2566 if (!JS_GetProperty(cx
, opts
, "saveIncrementalBytecode", &v
)) {
2569 if (!v
.isUndefined()) {
2570 saveIncrementalBytecode
= ToBoolean(v
);
2573 if (!JS_GetProperty(cx
, opts
, "execute", &v
)) {
2576 if (!v
.isUndefined()) {
2577 execute
= ToBoolean(v
);
2580 if (!JS_GetProperty(cx
, opts
, "assertEqBytecode", &v
)) {
2583 if (!v
.isUndefined()) {
2584 assertEqBytecode
= ToBoolean(v
);
2587 if (!JS_GetProperty(cx
, opts
, "envChainObject", &v
)) {
2590 if (!v
.isUndefined()) {
2591 if (!v
.isObject()) {
2592 JS_ReportErrorNumberASCII(
2593 cx
, GetErrorMessage
, nullptr, JSMSG_UNEXPECTED_TYPE
,
2594 "\"envChainObject\" passed to evaluate()", "not an object");
2598 JSObject
* obj
= &v
.toObject();
2599 if (obj
->isUnqualifiedVarObj()) {
2600 JS_ReportErrorASCII(
2602 "\"envChainObject\" passed to evaluate() should not be an "
2603 "unqualified variables object");
2607 if (!envChain
.append(obj
)) {
2612 // We cannot load or save the bytecode if we have no object where the
2613 // bytecode cache is stored.
2614 if (loadBytecode
|| saveIncrementalBytecode
) {
2616 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2617 JSSMSG_INVALID_ARGS
, "evaluate");
2623 if (envChain
.length() != 0) {
2624 // Wrap the envChainObject list into target realm.
2625 JSAutoRealm
ar(cx
, global
);
2626 for (size_t i
= 0; i
< envChain
.length(); ++i
) {
2627 if (!JS_WrapObject(cx
, envChain
[i
])) {
2632 options
.setNonSyntacticScope(true);
2635 // The `loadBuffer` we use below outlives the Stencil we generate so we can
2636 // use its contents directly in the Stencil.
2637 options
.borrowBuffer
= true;
2639 // We need to track the options used to generate bytecode for a CacheEntry to
2640 // avoid mismatches. This is primarily a concern when fuzzing the jsshell.
2641 CacheOptionSet cacheOptions
;
2642 cacheOptions
.initFromOptions(options
);
2644 JS::TranscodeBuffer loadBuffer
;
2645 JS::TranscodeBuffer saveBuffer
;
2648 size_t loadLength
= 0;
2649 uint8_t* loadData
= nullptr;
2651 if (!CacheEntry_compatible(cx
, cacheEntry
, cacheOptions
)) {
2655 loadData
= CacheEntry_getBytecode(cx
, cacheEntry
, &loadLength
);
2659 if (!loadBuffer
.append(loadData
, loadLength
)) {
2660 JS_ReportOutOfMemory(cx
);
2666 JSAutoRealm
ar(cx
, global
);
2667 RefPtr
<JS::Stencil
> stencil
;
2670 JS::TranscodeRange
range(loadBuffer
.begin(), loadBuffer
.length());
2671 JS::DecodeOptions
decodeOptions(options
);
2673 JS::TranscodeResult rv
=
2674 JS::DecodeStencil(cx
, decodeOptions
, range
, getter_AddRefs(stencil
));
2675 if (JS::IsTranscodeFailureResult(rv
)) {
2676 JS_ReportErrorASCII(cx
, "failed to decode cache");
2680 if (!ConvertTranscodeResultToJSException(cx
, rv
)) {
2684 AutoStableStringChars
linearChars(cx
);
2685 if (!linearChars
.initTwoByte(cx
, code
)) {
2689 JS::SourceText
<char16_t
> srcBuf
;
2690 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
2694 stencil
= JS::CompileGlobalScriptToStencil(cx
, options
, srcBuf
);
2700 if (!js::ValidateLazinessOfStencilAndGlobal(cx
, *stencil
)) {
2704 JS::InstantiateOptions
instantiateOptions(options
);
2705 RootedScript
script(
2706 cx
, JS::InstantiateGlobalStencil(cx
, instantiateOptions
, stencil
));
2711 AutoReportFrontendContext
fc(cx
);
2712 if (!SetSourceOptions(cx
, &fc
, script
->scriptSource(), displayURL
,
2717 if (!JS::UpdateDebugMetadata(cx
, script
, instantiateOptions
, privateValue
,
2718 elementAttributeName
, nullptr, nullptr)) {
2722 if (saveIncrementalBytecode
) {
2723 if (!JS::StartIncrementalEncoding(cx
, std::move(stencil
))) {
2729 if (!(envChain
.empty()
2730 ? JS_ExecuteScript(cx
, script
, args
.rval())
2731 : JS_ExecuteScript(cx
, envChain
, script
, args
.rval()))) {
2732 if (catchTermination
&& !JS_IsExceptionPending(cx
)) {
2733 ShellContext
* sc
= GetShellContext(cx
);
2738 JSAutoRealm
ar1(cx
, callerGlobal
);
2739 JSString
* str
= JS_NewStringCopyZ(cx
, "terminated");
2743 args
.rval().setString(str
);
2750 // Serialize the encoded bytecode, recorded before the execution, into a
2751 // buffer which can be deserialized linearly.
2752 if (saveIncrementalBytecode
) {
2753 if (!FinishIncrementalEncoding(cx
, script
, saveBuffer
)) {
2759 if (saveIncrementalBytecode
) {
2760 // If we are both loading and saving, we assert that we are going to
2761 // replace the current bytecode by the same stream of bytes.
2762 if (loadBytecode
&& assertEqBytecode
) {
2763 if (saveBuffer
.length() != loadBuffer
.length()) {
2764 char loadLengthStr
[16];
2765 SprintfLiteral(loadLengthStr
, "%zu", loadBuffer
.length());
2766 char saveLengthStr
[16];
2767 SprintfLiteral(saveLengthStr
, "%zu", saveBuffer
.length());
2769 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2770 JSSMSG_CACHE_EQ_SIZE_FAILED
, loadLengthStr
,
2775 if (!ArrayEqual(loadBuffer
.begin(), saveBuffer
.begin(),
2776 loadBuffer
.length())) {
2777 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2778 JSSMSG_CACHE_EQ_CONTENT_FAILED
);
2783 size_t saveLength
= saveBuffer
.length();
2784 if (saveLength
>= INT32_MAX
) {
2785 JS_ReportErrorASCII(cx
, "Cannot save large cache entry content");
2788 uint8_t* saveData
= saveBuffer
.extractOrCopyRawBuffer();
2789 if (!CacheEntry_setBytecode(cx
, cacheEntry
, cacheOptions
, saveData
,
2796 return JS_WrapValue(cx
, args
.rval());
2799 JSString
* js::shell::FileAsString(JSContext
* cx
, JS::HandleString pathnameStr
) {
2800 UniqueChars pathname
= JS_EncodeStringToUTF8(cx
, pathnameStr
);
2805 FILE* file
= OpenFile(cx
, pathname
.get(), "rb");
2810 AutoCloseFile
autoClose(file
);
2813 if (fstat(fileno(file
), &st
) != 0) {
2814 JS_ReportErrorUTF8(cx
, "can't stat %s", pathname
.get());
2818 if ((st
.st_mode
& S_IFMT
) != S_IFREG
) {
2819 JS_ReportErrorUTF8(cx
, "can't read non-regular file %s", pathname
.get());
2824 if (!FileSize(cx
, pathname
.get(), file
, &len
)) {
2828 UniqueChars
buf(js_pod_malloc
<char>(len
+ 1));
2830 JS_ReportErrorUTF8(cx
, "out of memory reading %s", pathname
.get());
2834 if (!ReadFile(cx
, pathname
.get(), file
, buf
.get(), len
)) {
2838 UniqueTwoByteChars
ucbuf(
2839 JS::LossyUTF8CharsToNewTwoByteCharsZ(cx
, JS::UTF8Chars(buf
.get(), len
),
2840 &len
, js::MallocArena
)
2843 JS_ReportErrorUTF8(cx
, "Invalid UTF-8 in file '%s'", pathname
.get());
2847 return JS_NewUCStringCopyN(cx
, ucbuf
.get(), len
);
2851 * Function to run scripts and return compilation + execution time. Semantics
2852 * are closely modelled after the equivalent function in WebKit, as this is used
2853 * to produce benchmark timings by SunSpider.
2855 static bool Run(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2856 CallArgs args
= CallArgsFromVp(argc
, vp
);
2857 if (args
.length() != 1) {
2858 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2859 JSSMSG_INVALID_ARGS
, "run");
2863 RootedString
str(cx
, JS::ToString(cx
, args
[0]));
2867 args
[0].setString(str
);
2869 str
= FileAsString(cx
, str
);
2874 AutoStableStringChars
linearChars(cx
);
2875 if (!linearChars
.initTwoByte(cx
, str
)) {
2879 JS::SourceText
<char16_t
> srcBuf
;
2880 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
2884 RootedScript
script(cx
);
2885 int64_t startClock
= PRMJ_Now();
2887 UniqueChars filename
= JS_EncodeStringToUTF8(cx
, str
);
2892 JS::CompileOptions
options(cx
);
2893 options
.setIntroductionType("js shell run")
2894 .setFileAndLine(filename
.get(), 1)
2896 .setNoScriptRval(true)
2897 .setEagerDelazificationStrategy(defaultDelazificationMode
);
2899 script
= JS::Compile(cx
, options
, srcBuf
);
2905 if (!JS_ExecuteScript(cx
, script
)) {
2909 int64_t endClock
= PRMJ_Now();
2911 args
.rval().setDouble((endClock
- startClock
) / double(PRMJ_USEC_PER_MSEC
));
2915 static int js_fgets(char* buf
, int size
, FILE* file
) {
2924 // Use the fastest available getc.
2926 #if defined(HAVE_GETC_UNLOCKED)
2928 #elif defined(HAVE__GETC_NOLOCK)
2936 for (i
= 0; i
< n
&& (c
= fast_getc(file
)) != EOF
; i
++) {
2938 if (c
== '\n') { // any \n ends a line
2939 i
++; // keep the \n; we know there is room for \0
2942 if (crflag
) { // \r not followed by \n ends line at the \r
2944 break; // and overwrite c in buf with \0
2946 crflag
= (c
== '\r');
2954 * function readline()
2955 * Provides a hook for scripts to read a line from stdin.
2957 static bool ReadLine(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2958 CallArgs args
= CallArgsFromVp(argc
, vp
);
2960 static constexpr size_t BUFSIZE
= 256;
2962 size_t buflength
= 0;
2963 size_t bufsize
= BUFSIZE
;
2964 char* buf
= (char*)JS_malloc(cx
, bufsize
);
2969 bool sawNewline
= false;
2971 while ((gotlength
= js_fgets(buf
+ buflength
, bufsize
- buflength
, from
)) >
2973 buflength
+= gotlength
;
2976 if (buf
[buflength
- 1] == '\n') {
2977 buf
[buflength
- 1] = '\0';
2980 } else if (buflength
< bufsize
- 1) {
2984 /* Else, grow our buffer for another pass. */
2987 if (bufsize
> buflength
) {
2988 tmp
= static_cast<char*>(JS_realloc(cx
, buf
, bufsize
/ 2, bufsize
));
2990 JS_ReportOutOfMemory(cx
);
3002 /* Treat the empty string specially. */
3003 if (buflength
== 0) {
3004 args
.rval().set(feof(from
) ? NullValue() : JS_GetEmptyStringValue(cx
));
3009 /* Shrink the buffer to the real size. */
3010 char* tmp
= static_cast<char*>(JS_realloc(cx
, buf
, bufsize
, buflength
));
3019 * Turn buf into a JSString. Note that buflength includes the trailing null
3023 JS_NewStringCopyN(cx
, buf
, sawNewline
? buflength
- 1 : buflength
);
3029 args
.rval().setString(str
);
3034 * function readlineBuf()
3035 * Provides a hook for scripts to emulate readline() using a string object.
3037 static bool ReadLineBuf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3038 CallArgs args
= CallArgsFromVp(argc
, vp
);
3039 ShellContext
* sc
= GetShellContext(cx
);
3041 if (!args
.length()) {
3042 if (!sc
->readLineBuf
) {
3043 JS_ReportErrorASCII(cx
,
3044 "No source buffer set. You must initially "
3045 "call readlineBuf with an argument.");
3049 char* currentBuf
= sc
->readLineBuf
.get() + sc
->readLineBufPos
;
3050 size_t buflen
= strlen(currentBuf
);
3053 args
.rval().setNull();
3058 while (len
< buflen
) {
3059 if (currentBuf
[len
] == '\n') {
3065 JSString
* str
= JS_NewStringCopyUTF8N(cx
, JS::UTF8Chars(currentBuf
, len
));
3070 if (currentBuf
[len
] == '\0') {
3071 sc
->readLineBufPos
+= len
;
3073 sc
->readLineBufPos
+= len
+ 1;
3076 args
.rval().setString(str
);
3080 if (args
.length() == 1) {
3081 sc
->readLineBuf
= nullptr;
3082 sc
->readLineBufPos
= 0;
3084 RootedString
str(cx
, JS::ToString(cx
, args
[0]));
3088 sc
->readLineBuf
= JS_EncodeStringToUTF8(cx
, str
);
3089 if (!sc
->readLineBuf
) {
3093 args
.rval().setUndefined();
3097 JS_ReportErrorASCII(cx
, "Must specify at most one argument");
3101 static bool PutStr(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3102 CallArgs args
= CallArgsFromVp(argc
, vp
);
3104 if (args
.length() != 0) {
3105 if (!gOutFile
->isOpen()) {
3106 JS_ReportErrorASCII(cx
, "output file is closed");
3110 RootedString
str(cx
, JS::ToString(cx
, args
[0]));
3114 UniqueChars bytes
= JS_EncodeStringToUTF8(cx
, str
);
3118 fputs(bytes
.get(), gOutFile
->fp
);
3119 fflush(gOutFile
->fp
);
3122 args
.rval().setUndefined();
3126 static bool Now(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3127 CallArgs args
= CallArgsFromVp(argc
, vp
);
3128 double now
= PRMJ_Now() / double(PRMJ_USEC_PER_MSEC
);
3129 args
.rval().setDouble(now
);
3133 static bool CpuNow(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3134 CallArgs args
= CallArgsFromVp(argc
, vp
);
3135 double now
= double(std::clock()) / double(CLOCKS_PER_SEC
);
3136 args
.rval().setDouble(now
);
3140 static bool PrintInternal(JSContext
* cx
, const CallArgs
& args
, RCFile
* file
) {
3141 if (!file
->isOpen()) {
3142 JS_ReportErrorASCII(cx
, "output file is closed");
3146 for (unsigned i
= 0; i
< args
.length(); i
++) {
3147 RootedString
str(cx
, JS::ToString(cx
, args
[i
]));
3151 UniqueChars bytes
= JS_EncodeStringToUTF8(cx
, str
);
3155 fprintf(file
->fp
, "%s%s", i
? " " : "", bytes
.get());
3158 fputc('\n', file
->fp
);
3161 args
.rval().setUndefined();
3165 static bool Print(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3166 CallArgs args
= CallArgsFromVp(argc
, vp
);
3167 #ifdef FUZZING_INTERFACES
3168 if (fuzzHaveModule
&& !fuzzDoDebug
) {
3169 // When fuzzing and not debugging, suppress any print() output,
3170 // as it slows down fuzzing and makes libFuzzer's output hard
3172 args
.rval().setUndefined();
3175 #endif // FUZZING_INTERFACES
3176 return PrintInternal(cx
, args
, gOutFile
);
3179 static bool PrintErr(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3180 CallArgs args
= CallArgsFromVp(argc
, vp
);
3181 return PrintInternal(cx
, args
, gErrFile
);
3184 static bool Help(JSContext
* cx
, unsigned argc
, Value
* vp
);
3186 static bool Quit(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3187 // Print a message to stderr in differential testing to help jsfunfuzz
3188 // find uncatchable-exception bugs.
3189 if (js::SupportDifferentialTesting()) {
3190 fprintf(stderr
, "quit called\n");
3193 CallArgs args
= CallArgsFromVp(argc
, vp
);
3195 if (!ToInt32(cx
, args
.get(0), &code
)) {
3199 // The fuzzers check the shell's exit code and assume a value >= 128 means
3200 // the process crashed (for instance, SIGSEGV will result in code 139). On
3201 // POSIX platforms, the exit code is 8-bit and negative values can also
3202 // result in an exit code >= 128. We restrict the value to range [0, 127] to
3203 // avoid false positives.
3204 if (code
< 0 || code
>= 128) {
3205 JS_ReportErrorASCII(cx
, "quit exit code should be in range 0-127");
3209 SetQuitting(cx
, code
);
3213 static bool StartTimingMutator(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3214 CallArgs args
= CallArgsFromVp(argc
, vp
);
3215 if (args
.length() > 0) {
3216 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
3217 JSSMSG_TOO_MANY_ARGS
, "startTimingMutator");
3221 if (!cx
->runtime()->gc
.stats().startTimingMutator()) {
3222 JS_ReportErrorASCII(
3223 cx
, "StartTimingMutator should only be called from outside of GC");
3227 args
.rval().setUndefined();
3231 static bool StopTimingMutator(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3232 CallArgs args
= CallArgsFromVp(argc
, vp
);
3233 if (args
.length() > 0) {
3234 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
3235 JSSMSG_TOO_MANY_ARGS
, "stopTimingMutator");
3239 double mutator_ms
, gc_ms
;
3240 if (!cx
->runtime()->gc
.stats().stopTimingMutator(mutator_ms
, gc_ms
)) {
3241 JS_ReportErrorASCII(cx
,
3242 "stopTimingMutator called when not timing the mutator");
3245 double total_ms
= mutator_ms
+ gc_ms
;
3246 if (total_ms
> 0 && gOutFile
->isOpen()) {
3247 fprintf(gOutFile
->fp
, "Mutator: %.3fms (%.1f%%), GC: %.3fms (%.1f%%)\n",
3248 mutator_ms
, mutator_ms
/ total_ms
* 100.0, gc_ms
,
3249 gc_ms
/ total_ms
* 100.0);
3252 args
.rval().setUndefined();
3256 static const char* ToSource(JSContext
* cx
, HandleValue vp
, UniqueChars
* bytes
) {
3257 RootedString
str(cx
, JS_ValueToSource(cx
, vp
));
3259 *bytes
= JS_EncodeStringToUTF8(cx
, str
);
3261 return bytes
->get();
3264 JS_ClearPendingException(cx
);
3265 return "<<error converting value to string>>";
3268 static bool AssertEq(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3269 CallArgs args
= CallArgsFromVp(argc
, vp
);
3270 if (!(args
.length() == 2 || (args
.length() == 3 && args
[2].isString()))) {
3271 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
3272 (args
.length() < 2) ? JSSMSG_NOT_ENOUGH_ARGS
3273 : (args
.length() == 3) ? JSSMSG_INVALID_ARGS
3274 : JSSMSG_TOO_MANY_ARGS
,
3280 if (!JS::SameValue(cx
, args
[0], args
[1], &same
)) {
3284 UniqueChars bytes0
, bytes1
;
3285 const char* actual
= ToSource(cx
, args
[0], &bytes0
);
3286 const char* expected
= ToSource(cx
, args
[1], &bytes1
);
3287 if (args
.length() == 2) {
3288 JS_ReportErrorNumberUTF8(cx
, my_GetErrorMessage
, nullptr,
3289 JSSMSG_ASSERT_EQ_FAILED
, actual
, expected
);
3291 RootedString
message(cx
, args
[2].toString());
3292 UniqueChars bytes2
= QuoteString(cx
, message
);
3296 JS_ReportErrorNumberUTF8(cx
, my_GetErrorMessage
, nullptr,
3297 JSSMSG_ASSERT_EQ_FAILED_MSG
, actual
, expected
,
3302 args
.rval().setUndefined();
3306 static JSScript
* GetTopScript(JSContext
* cx
) {
3307 NonBuiltinScriptFrameIter
iter(cx
);
3308 return iter
.done() ? nullptr : iter
.script();
3311 static bool GetScriptAndPCArgs(JSContext
* cx
, CallArgs
& args
,
3312 MutableHandleScript scriptp
, int32_t* ip
) {
3313 RootedScript
script(cx
, GetTopScript(cx
));
3315 if (!args
.get(0).isUndefined()) {
3316 HandleValue v
= args
[0];
3317 unsigned intarg
= 0;
3318 if (v
.isObject() && JS::GetClass(&v
.toObject())->isJSFunction()) {
3319 script
= TestingFunctionArgumentToScript(cx
, v
);
3325 if (!args
.get(intarg
).isUndefined()) {
3326 if (!JS::ToInt32(cx
, args
[intarg
], ip
)) {
3329 if ((uint32_t)*ip
>= script
->length()) {
3330 JS_ReportErrorASCII(cx
, "Invalid PC");
3336 scriptp
.set(script
);
3341 static bool LineToPC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3342 CallArgs args
= CallArgsFromVp(argc
, vp
);
3344 if (args
.length() == 0) {
3345 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
3346 JSSMSG_LINE2PC_USAGE
);
3350 RootedScript
script(cx
, GetTopScript(cx
));
3351 int32_t lineArg
= 0;
3352 if (args
[0].isObject() && args
[0].toObject().is
<JSFunction
>()) {
3353 script
= TestingFunctionArgumentToScript(cx
, args
[0]);
3361 if (!ToUint32(cx
, args
.get(lineArg
), &lineno
)) {
3365 jsbytecode
* pc
= LineNumberToPC(script
, lineno
);
3369 args
.rval().setInt32(script
->pcToOffset(pc
));
3373 static bool PCToLine(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3374 CallArgs args
= CallArgsFromVp(argc
, vp
);
3375 RootedScript
script(cx
);
3379 if (!GetScriptAndPCArgs(cx
, args
, &script
, &i
)) {
3382 lineno
= PCToLineNumber(script
, script
->offsetToPC(i
));
3386 args
.rval().setInt32(lineno
);
3390 #if defined(DEBUG) || defined(JS_JITSPEW)
3392 static bool Notes(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3393 CallArgs args
= CallArgsFromVp(argc
, vp
);
3394 JSSprinter
sprinter(cx
);
3395 if (!sprinter
.init()) {
3399 for (unsigned i
= 0; i
< args
.length(); i
++) {
3400 RootedScript
script(cx
, TestingFunctionArgumentToScript(cx
, args
[i
]));
3405 if (!JSScript::dumpSrcNotes(cx
, script
, &sprinter
)) {
3410 JSString
* str
= sprinter
.release(cx
);
3414 args
.rval().setString(str
);
3420 struct DisassembleOptionParser
{
3423 JSScript::DumpOptions options
;
3425 DisassembleOptionParser(unsigned argc
, Value
* argv
)
3426 : argc(argc
), argv(argv
) {}
3428 bool parse(JSContext
* cx
) {
3429 options
.recursive
= false;
3431 /* Read options off early arguments */
3432 while (argc
> 0 && argv
[0].isString()) {
3433 JSString
* str
= argv
[0].toString();
3434 JSLinearString
* linearStr
= JS_EnsureLinearString(cx
, str
);
3438 if (JS_LinearStringEqualsLiteral(linearStr
, "-r")) {
3439 options
.recursive
= true;
3450 } /* anonymous namespace */
3452 static bool DisassembleToSprinter(JSContext
* cx
, unsigned argc
, Value
* vp
,
3453 StringPrinter
* sp
) {
3454 CallArgs args
= CallArgsFromVp(argc
, vp
);
3455 DisassembleOptionParser
p(args
.length(), args
.array());
3461 /* Without arguments, disassemble the current script. */
3462 RootedScript
script(cx
, GetTopScript(cx
));
3464 JSAutoRealm
ar(cx
, script
);
3465 if (!JSScript::dump(cx
, script
, p
.options
, sp
)) {
3470 for (unsigned i
= 0; i
< p
.argc
; i
++) {
3471 RootedFunction
fun(cx
);
3472 RootedScript
script(cx
);
3473 RootedValue
value(cx
, p
.argv
[i
]);
3474 if (value
.isObject() && value
.toObject().is
<ShellModuleObjectWrapper
>()) {
3475 script
= value
.toObject()
3476 .as
<ShellModuleObjectWrapper
>()
3480 script
= TestingFunctionArgumentToScript(cx
, value
, fun
.address());
3486 if (!JSScript::dump(cx
, script
, p
.options
, sp
)) {
3495 static bool DisassembleToString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3496 CallArgs args
= CallArgsFromVp(argc
, vp
);
3497 JSSprinter
sprinter(cx
);
3498 if (!sprinter
.init()) {
3501 if (!DisassembleToSprinter(cx
, args
.length(), vp
, &sprinter
)) {
3505 JSString
* str
= sprinter
.release(cx
);
3509 args
.rval().setString(str
);
3513 static bool Disassemble(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3514 CallArgs args
= CallArgsFromVp(argc
, vp
);
3516 if (!gOutFile
->isOpen()) {
3517 JS_ReportErrorASCII(cx
, "output file is closed");
3521 Sprinter
sprinter(cx
);
3522 if (!sprinter
.init()) {
3525 if (!DisassembleToSprinter(cx
, args
.length(), vp
, &sprinter
)) {
3529 JS::UniqueChars str
= sprinter
.release();
3533 fprintf(gOutFile
->fp
, "%s\n", str
.get());
3534 args
.rval().setUndefined();
3538 static bool DisassFile(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3539 CallArgs args
= CallArgsFromVp(argc
, vp
);
3541 if (!gOutFile
->isOpen()) {
3542 JS_ReportErrorASCII(cx
, "output file is closed");
3546 /* Support extra options at the start, just like Disassemble. */
3547 DisassembleOptionParser
p(args
.length(), args
.array());
3553 args
.rval().setUndefined();
3557 // We should change DisassembleOptionParser to store CallArgs.
3558 Rooted
<JSString
*> str(
3559 cx
, JS::ToString(cx
, HandleValue::fromMarkedLocation(&p
.argv
[0])));
3563 UniqueChars filename
= JS_EncodeStringToUTF8(cx
, str
);
3567 RootedScript
script(cx
);
3570 CompileOptions
options(cx
);
3571 options
.setIntroductionType("js shell disFile")
3572 .setFileAndLine(filename
.get(), 1)
3574 .setNoScriptRval(true)
3575 .setEagerDelazificationStrategy(defaultDelazificationMode
);
3577 script
= JS::CompileUtf8Path(cx
, options
, filename
.get());
3583 Sprinter
sprinter(cx
);
3584 if (!sprinter
.init()) {
3587 if (JSScript::dump(cx
, script
, p
.options
, &sprinter
)) {
3591 JS::UniqueChars chars
= sprinter
.release();
3595 fprintf(gOutFile
->fp
, "%s\n", chars
.get());
3597 args
.rval().setUndefined();
3601 static bool DisassWithSrc(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3602 CallArgs args
= CallArgsFromVp(argc
, vp
);
3604 if (!gOutFile
->isOpen()) {
3605 JS_ReportErrorASCII(cx
, "output file is closed");
3609 const size_t lineBufLen
= 512;
3610 unsigned len
, line1
, line2
, bupline
;
3611 char linebuf
[lineBufLen
];
3612 static const char sep
[] = ";-------------------------";
3614 RootedScript
script(cx
);
3615 for (unsigned i
= 0; i
< args
.length(); i
++) {
3616 script
= TestingFunctionArgumentToScript(cx
, args
[i
]);
3621 if (!script
->filename()) {
3622 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
3623 JSSMSG_FILE_SCRIPTS_ONLY
);
3627 FILE* file
= OpenFile(cx
, script
->filename(), "rb");
3631 auto closeFile
= MakeScopeExit([file
] { fclose(file
); });
3633 jsbytecode
* pc
= script
->code();
3634 jsbytecode
* end
= script
->codeEnd();
3636 Sprinter
sprinter(cx
);
3637 if (!sprinter
.init()) {
3641 /* burn the leading lines */
3642 line2
= PCToLineNumber(script
, pc
);
3643 for (line1
= 0; line1
< line2
- 1; line1
++) {
3644 char* tmp
= fgets(linebuf
, lineBufLen
, file
);
3646 JS_ReportErrorUTF8(cx
, "failed to read %s fully", script
->filename());
3653 line2
= PCToLineNumber(script
, pc
);
3655 if (line2
< line1
) {
3656 if (bupline
!= line2
) {
3658 sprinter
.printf("%s %3u: BACKUP\n", sep
, line2
);
3661 if (bupline
&& line1
== line2
) {
3662 sprinter
.printf("%s %3u: RESTORE\n", sep
, line2
);
3665 while (line1
< line2
) {
3666 if (!fgets(linebuf
, lineBufLen
, file
)) {
3667 JS_ReportErrorNumberUTF8(cx
, my_GetErrorMessage
, nullptr,
3668 JSSMSG_UNEXPECTED_EOF
, script
->filename());
3672 sprinter
.printf("%s %3u: %s", sep
, line1
, linebuf
);
3677 Disassemble1(cx
, script
, pc
, script
->pcToOffset(pc
), true, &sprinter
);
3685 JS::UniqueChars str
= sprinter
.release();
3689 fprintf(gOutFile
->fp
, "%s\n", str
.get());
3692 args
.rval().setUndefined();
3696 #endif /* defined(DEBUG) || defined(JS_JITSPEW) */
3698 #ifdef JS_CACHEIR_SPEW
3699 static bool CacheIRHealthReport(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3700 CallArgs args
= CallArgsFromVp(argc
, vp
);
3702 js::jit::CacheIRHealth cih
;
3703 RootedScript
script(cx
);
3705 // In the case that we are calling this function from the shell and
3706 // the environment variable is not set, AutoSpewChannel automatically
3707 // sets and unsets the proper channel for the duration of spewing
3709 AutoSpewChannel
channel(cx
, SpewChannel::CacheIRHealthReport
, script
);
3711 // Calling CacheIRHealthReport without any arguments will create health
3712 // reports for all scripts in the zone.
3713 if (jit::JitZone
* jitZone
= cx
->zone()->jitZone()) {
3714 jitZone
->forEachJitScript([&](jit::JitScript
* jitScript
) {
3715 script
= jitScript
->owningScript();
3716 if (!script
->selfHosted()) {
3717 cih
.healthReportForScript(cx
, script
, js::jit::SpewContext::Shell
);
3722 RootedValue
value(cx
, args
.get(0));
3724 if (value
.isObject() && value
.toObject().is
<ShellModuleObjectWrapper
>()) {
3726 value
.toObject().as
<ShellModuleObjectWrapper
>().get()->maybeScript();
3728 script
= TestingFunctionArgumentToScript(cx
, args
.get(0));
3735 cih
.healthReportForScript(cx
, script
, js::jit::SpewContext::Shell
);
3738 args
.rval().setUndefined();
3741 #endif /* JS_CACHEIR_SPEW */
3743 /* Pretend we can always preserve wrappers for dummy DOM objects. */
3744 static bool DummyPreserveWrapperCallback(JSContext
* cx
, HandleObject obj
) {
3748 static bool DummyHasReleasedWrapperCallback(HandleObject obj
) { return true; }
3750 #ifdef FUZZING_JS_FUZZILLI
3751 static bool fuzzilli_hash(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3752 CallArgs args
= CallArgsFromVp(argc
, vp
);
3753 args
.rval().setUndefined();
3759 JS::Handle
<JS::Value
> v
= args
.get(0);
3761 int32_t i
= v
.toInt32();
3762 hash
= FuzzilliHashDouble((double)i
);
3763 } else if (v
.isDouble()) {
3764 double d
= v
.toDouble();
3765 d
= JS::CanonicalizeNaN(d
);
3766 hash
= FuzzilliHashDouble(d
);
3767 } else if (v
.isNull()) {
3768 hash
= FuzzilliHashDouble(1.0);
3769 } else if (v
.isUndefined()) {
3770 hash
= FuzzilliHashDouble(2.0);
3771 } else if (v
.isBoolean()) {
3772 hash
= FuzzilliHashDouble(3.0 + v
.toBoolean());
3773 } else if (v
.isBigInt()) {
3774 JS::BigInt
* bigInt
= v
.toBigInt();
3775 hash
= FuzzilliHashBigInt(bigInt
);
3776 } else if (v
.isObject()) {
3777 JSObject
& obj
= v
.toObject();
3778 FuzzilliHashObject(cx
, &obj
);
3784 cx
->executionHashInputs
+= 1;
3785 cx
->executionHash
= mozilla::RotateLeft(cx
->executionHash
+ hash
, 1);
3789 // We have to assume that the fuzzer will be able to call this function e.g. by
3790 // enumerating the properties of the global object and eval'ing them. As such
3791 // this function is implemented in a way that requires passing some magic value
3792 // as first argument (with the idea being that the fuzzer won't be able to
3793 // generate this value) which then also acts as a selector for the operation
3795 static bool Fuzzilli(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3796 CallArgs args
= CallArgsFromVp(argc
, vp
);
3798 RootedString
arg(cx
, JS::ToString(cx
, args
.get(0)));
3802 Rooted
<JSLinearString
*> operation(cx
, StringToLinearString(cx
, arg
));
3807 if (StringEqualsAscii(operation
, "FUZZILLI_CRASH")) {
3809 if (!ToInt32(cx
, args
.get(1), &type
)) {
3813 // With this, we can test the various ways the JS shell can crash and make
3814 // sure that Fuzzilli is able to detect all of these failures properly.
3817 *((int*)0x41414141) = 0x1337;
3820 MOZ_RELEASE_ASSERT(false);
3831 } else if (StringEqualsAscii(operation
, "FUZZILLI_PRINT")) {
3832 static FILE* fzliout
= fdopen(REPRL_DWFD
, "w");
3836 "Fuzzer output channel not available, printing to stdout instead\n");
3840 RootedString
str(cx
, JS::ToString(cx
, args
.get(1)));
3844 UniqueChars bytes
= JS_EncodeStringToUTF8(cx
, str
);
3848 fprintf(fzliout
, "%s\n", bytes
.get());
3850 } else if (StringEqualsAscii(operation
, "FUZZILLI_RANDOM")) {
3851 // This is an entropy source which can be called during fuzzing.
3852 // Its currently used to tests whether Fuzzilli detects non-deterministic
3854 args
.rval().setInt32(static_cast<uint32_t>(mozilla::RandomUint64OrDie()));
3858 args
.rval().setUndefined();
3862 static bool FuzzilliReprlGetAndRun(JSContext
* cx
) {
3863 size_t scriptSize
= 0;
3866 MOZ_RELEASE_ASSERT(read(REPRL_CRFD
, &action
, 4) == 4);
3867 if (action
== 'cexe') {
3868 MOZ_RELEASE_ASSERT(read(REPRL_CRFD
, &scriptSize
, 8) == 8);
3870 fprintf(stderr
, "Unknown action: %u\n", action
);
3874 CompileOptions
options(cx
);
3875 options
.setIntroductionType("reprl")
3876 .setFileAndLine("reprl", 1)
3878 .setNoScriptRval(true)
3879 .setEagerDelazificationStrategy(defaultDelazificationMode
);
3881 char* scriptSrc
= static_cast<char*>(js_malloc(scriptSize
));
3883 char* ptr
= scriptSrc
;
3884 size_t remaining
= scriptSize
;
3885 while (remaining
> 0) {
3886 ssize_t rv
= read(REPRL_DRFD
, ptr
, remaining
);
3888 fprintf(stderr
, "Failed to load script\n");
3895 JS::SourceText
<Utf8Unit
> srcBuf
;
3896 if (!srcBuf
.init(cx
, scriptSrc
, scriptSize
,
3897 JS::SourceOwnership::TakeOwnership
)) {
3901 RootedScript
script(cx
, JS::Compile(cx
, options
, srcBuf
));
3906 if (!JS_ExecuteScript(cx
, script
)) {
3913 #endif /* FUZZING_JS_FUZZILLI */
3915 static bool FuzzilliUseReprlMode(OptionParser
* op
) {
3916 #ifdef FUZZING_JS_FUZZILLI
3917 // Check if we should use REPRL mode
3918 bool reprl_mode
= op
->getBoolOption("reprl");
3920 // Check in with parent
3921 char helo
[] = "HELO";
3922 if (write(REPRL_CWFD
, helo
, 4) != 4 || read(REPRL_CRFD
, helo
, 4) != 4) {
3926 if (memcmp(helo
, "HELO", 4) != 0) {
3927 fprintf(stderr
, "Invalid response from parent\n");
3934 #endif /* FUZZING_JS_FUZZILLI */
3937 static bool Crash(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3938 CallArgs args
= CallArgsFromVp(argc
, vp
);
3939 if (args
.length() == 0) {
3940 MOZ_CRASH("forced crash");
3942 RootedString
message(cx
, JS::ToString(cx
, args
[0]));
3946 UniqueChars utf8chars
= JS_EncodeStringToUTF8(cx
, message
);
3950 if (args
.get(1).isObject()) {
3952 RootedObject
opts(cx
, &args
[1].toObject());
3953 if (!JS_GetProperty(cx
, opts
, "suppress_minidump", &v
)) {
3956 if (v
.isBoolean() && v
.toBoolean()) {
3957 js::NoteIntentionalCrash();
3961 MOZ_ReportCrash(utf8chars
.get(), __FILE__
, __LINE__
);
3963 MOZ_CRASH_UNSAFE(utf8chars
.get());
3966 static bool GetSLX(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3967 CallArgs args
= CallArgsFromVp(argc
, vp
);
3968 RootedScript
script(cx
);
3970 script
= TestingFunctionArgumentToScript(cx
, args
.get(0));
3974 args
.rval().setInt32(GetScriptLineExtent(script
));
3978 static bool ThrowError(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3979 JS_ReportErrorASCII(cx
, "This is an error");
3983 static bool CopyErrorReportToObject(JSContext
* cx
, JSErrorReport
* report
,
3985 RootedString
nameStr(cx
);
3986 if (report
->exnType
== JSEXN_WARN
) {
3987 nameStr
= JS_NewStringCopyZ(cx
, "Warning");
3992 nameStr
= GetErrorTypeName(cx
, report
->exnType
);
3993 // GetErrorTypeName doesn't set an exception, but
3994 // can fail for InternalError or non-error objects.
3996 nameStr
= cx
->runtime()->emptyString
;
3999 RootedValue
nameVal(cx
, StringValue(nameStr
));
4000 if (!DefineDataProperty(cx
, obj
, cx
->names().name
, nameVal
)) {
4004 RootedString
messageStr(cx
, report
->newMessageString(cx
));
4008 RootedValue
messageVal(cx
, StringValue(messageStr
));
4009 if (!DefineDataProperty(cx
, obj
, cx
->names().message
, messageVal
)) {
4013 RootedValue
linenoVal(cx
, Int32Value(report
->lineno
));
4014 if (!DefineDataProperty(cx
, obj
, cx
->names().lineNumber
, linenoVal
)) {
4018 RootedValue
columnVal(cx
, Int32Value(report
->column
.oneOriginValue()));
4019 if (!DefineDataProperty(cx
, obj
, cx
->names().columnNumber
, columnVal
)) {
4023 RootedObject
notesArray(cx
, CreateErrorNotesArray(cx
, report
));
4028 RootedValue
notesArrayVal(cx
, ObjectValue(*notesArray
));
4029 return DefineDataProperty(cx
, obj
, cx
->names().notes
, notesArrayVal
);
4032 static bool CreateErrorReport(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4033 CallArgs args
= CallArgsFromVp(argc
, vp
);
4035 // We don't have a stack here, so just initialize with null.
4036 JS::ExceptionStack
exnStack(cx
, args
.get(0), nullptr);
4037 JS::ErrorReportBuilder
report(cx
);
4038 if (!report
.init(cx
, exnStack
, JS::ErrorReportBuilder::WithSideEffects
)) {
4042 MOZ_ASSERT(!report
.report()->isWarning());
4044 RootedObject
obj(cx
, JS_NewPlainObject(cx
));
4049 RootedString
toString(cx
, NewStringCopyUTF8Z(cx
, report
.toStringResult()));
4054 if (!JS_DefineProperty(cx
, obj
, "toStringResult", toString
,
4055 JSPROP_ENUMERATE
)) {
4059 if (!CopyErrorReportToObject(cx
, report
.report(), obj
)) {
4063 args
.rval().setObject(*obj
);
4067 #define LAZY_STANDARD_CLASSES
4069 /* A class for easily testing the inner/outer object callbacks. */
4070 typedef struct ComplexObject
{
4077 static bool sandbox_enumerate(JSContext
* cx
, JS::HandleObject obj
,
4078 JS::MutableHandleIdVector properties
,
4079 bool enumerableOnly
) {
4082 if (!JS_GetProperty(cx
, obj
, "lazy", &v
)) {
4086 if (!ToBoolean(v
)) {
4090 return JS_NewEnumerateStandardClasses(cx
, obj
, properties
, enumerableOnly
);
4093 static bool sandbox_resolve(JSContext
* cx
, HandleObject obj
, HandleId id
,
4096 if (!JS_GetProperty(cx
, obj
, "lazy", &v
)) {
4101 return JS_ResolveStandardClass(cx
, obj
, id
, resolvedp
);
4106 static const JSClassOps sandbox_classOps
= {
4107 nullptr, // addProperty
4108 nullptr, // delProperty
4109 nullptr, // enumerate
4110 sandbox_enumerate
, // newEnumerate
4111 sandbox_resolve
, // resolve
4112 nullptr, // mayResolve
4113 nullptr, // finalize
4115 nullptr, // construct
4116 JS_GlobalObjectTraceHook
, // trace
4119 static const JSClass sandbox_class
= {"sandbox", JSCLASS_GLOBAL_FLAGS
,
4122 static void SetStandardRealmOptions(JS::RealmOptions
& options
) {
4123 options
.creationOptions()
4124 .setSharedMemoryAndAtomicsEnabled(enableSharedMemory
)
4125 .setCoopAndCoepEnabled(false)
4126 .setToSourceEnabled(enableToSource
)
4127 #ifdef ENABLE_JSON_PARSE_WITH_SOURCE
4128 .setJSONParseWithSource(enableJSONParseWithSource
)
4133 [[nodiscard
]] static bool CheckRealmOptions(JSContext
* cx
,
4134 JS::RealmOptions
& options
,
4135 JSPrincipals
* principals
) {
4136 JS::RealmCreationOptions
& creationOptions
= options
.creationOptions();
4137 if (creationOptions
.compartmentSpecifier() !=
4138 JS::CompartmentSpecifier::ExistingCompartment
) {
4142 JS::Compartment
* comp
= creationOptions
.compartment();
4144 // All realms in a compartment must be either system or non-system.
4146 principals
&& principals
== cx
->runtime()->trustedPrincipals();
4147 if (isSystem
!= IsSystemCompartment(comp
)) {
4148 JS_ReportErrorASCII(cx
,
4149 "Cannot create system and non-system realms in the "
4150 "same compartment");
4154 // Debugger visibility is per-compartment, not per-realm, so make sure the
4155 // requested visibility matches the existing compartment's.
4156 if (creationOptions
.invisibleToDebugger() != comp
->invisibleToDebugger()) {
4157 JS_ReportErrorASCII(cx
,
4158 "All the realms in a compartment must have "
4159 "the same debugger visibility");
4166 static JSObject
* NewSandbox(JSContext
* cx
, bool lazy
) {
4167 JS::RealmOptions options
;
4168 SetStandardRealmOptions(options
);
4170 if (defaultToSameCompartment
) {
4171 options
.creationOptions().setExistingCompartment(cx
->global());
4173 options
.creationOptions().setNewCompartmentAndZone();
4176 JSPrincipals
* principals
= nullptr;
4177 if (!CheckRealmOptions(cx
, options
, principals
)) {
4181 RootedObject
obj(cx
,
4182 JS_NewGlobalObject(cx
, &sandbox_class
, principals
,
4183 JS::DontFireOnNewGlobalHook
, options
));
4189 JSAutoRealm
ar(cx
, obj
);
4190 if (!lazy
&& !JS::InitRealmStandardClasses(cx
)) {
4194 RootedValue
value(cx
, BooleanValue(lazy
));
4195 if (!JS_DefineProperty(cx
, obj
, "lazy", value
,
4196 JSPROP_PERMANENT
| JSPROP_READONLY
)) {
4200 JS_FireOnNewGlobalObject(cx
, obj
);
4203 if (!cx
->compartment()->wrap(cx
, &obj
)) {
4209 static bool EvalInContext(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4210 CallArgs args
= CallArgsFromVp(argc
, vp
);
4211 if (!args
.requireAtLeast(cx
, "evalcx", 1)) {
4215 RootedString
str(cx
, ToString(cx
, args
[0]));
4220 RootedObject
sobj(cx
);
4221 if (args
.hasDefined(1)) {
4222 sobj
= ToObject(cx
, args
[1]);
4228 AutoStableStringChars
strChars(cx
);
4229 if (!strChars
.initTwoByte(cx
, str
)) {
4233 mozilla::Range
<const char16_t
> chars
= strChars
.twoByteRange();
4234 size_t srclen
= chars
.length();
4235 const char16_t
* src
= chars
.begin().get();
4239 if (src
[0] == 'l' && src
[1] == 'a' && src
[2] == 'z' && src
[3] == 'y') {
4246 sobj
= NewSandbox(cx
, lazy
);
4253 args
.rval().setObject(*sobj
);
4257 JS::AutoFilename filename
;
4260 DescribeScriptedCaller(cx
, &filename
, &lineno
);
4262 sobj
= UncheckedUnwrap(sobj
, true);
4264 JSAutoRealm
ar(cx
, sobj
);
4266 sobj
= ToWindowIfWindowProxy(sobj
);
4268 if (!JS_IsGlobalObject(sobj
)) {
4269 JS_ReportErrorASCII(cx
, "Invalid scope argument to evalcx");
4273 JS::CompileOptions
opts(cx
);
4274 opts
.setFileAndLine(filename
.get(), lineno
)
4275 .setEagerDelazificationStrategy(defaultDelazificationMode
);
4277 JS::SourceText
<char16_t
> srcBuf
;
4278 if (!srcBuf
.init(cx
, src
, srclen
, JS::SourceOwnership::Borrowed
) ||
4279 !JS::Evaluate(cx
, opts
, srcBuf
, args
.rval())) {
4284 if (!cx
->compartment()->wrap(cx
, args
.rval())) {
4291 static bool EnsureGeckoProfilingStackInstalled(JSContext
* cx
,
4293 if (cx
->geckoProfiler().infraInstalled()) {
4294 MOZ_ASSERT(sc
->geckoProfilingStack
);
4298 MOZ_ASSERT(!sc
->geckoProfilingStack
);
4299 sc
->geckoProfilingStack
= MakeUnique
<ProfilingStack
>();
4300 if (!sc
->geckoProfilingStack
) {
4301 JS_ReportOutOfMemory(cx
);
4305 SetContextProfilingStack(cx
, sc
->geckoProfilingStack
.get());
4309 struct WorkerInput
{
4310 JSRuntime
* parentRuntime
;
4311 UniqueTwoByteChars chars
;
4314 WorkerInput(JSRuntime
* parentRuntime
, UniqueTwoByteChars chars
, size_t length
)
4315 : parentRuntime(parentRuntime
), chars(std::move(chars
)), length(length
) {}
4318 static void DestroyShellCompartmentPrivate(JS::GCContext
* gcx
,
4319 JS::Compartment
* compartment
) {
4320 auto priv
= static_cast<ShellCompartmentPrivate
*>(
4321 JS_GetCompartmentPrivate(compartment
));
4325 static void SetWorkerContextOptions(JSContext
* cx
);
4326 static bool ShellBuildId(JS::BuildIdCharVector
* buildId
);
4328 static constexpr size_t gWorkerStackSize
= 2 * 128 * sizeof(size_t) * 1024;
4330 static void WorkerMain(UniquePtr
<WorkerInput
> input
) {
4331 MOZ_ASSERT(input
->parentRuntime
);
4333 JSContext
* cx
= JS_NewContext(8L * 1024L * 1024L, input
->parentRuntime
);
4337 auto destroyContext
= MakeScopeExit([cx
] { JS_DestroyContext(cx
); });
4339 UniquePtr
<ShellContext
> sc
=
4340 MakeUnique
<ShellContext
>(cx
, ShellContext::Worker
);
4341 if (!sc
|| !sc
->registerWithCx(cx
)) {
4345 if (!JS::InitSelfHostedCode(cx
)) {
4349 EnvironmentPreparer
environmentPreparer(cx
);
4352 JS::RealmOptions realmOptions
;
4353 SetStandardRealmOptions(realmOptions
);
4355 RootedObject
global(cx
, NewGlobalObject(cx
, realmOptions
, nullptr,
4356 ShellGlobalKind::WindowProxy
,
4357 /* immutablePrototype = */ true));
4362 JSAutoRealm
ar(cx
, global
);
4364 JS::ConstUTF8CharsZ
path(processWideModuleLoadPath
.get(),
4365 strlen(processWideModuleLoadPath
.get()));
4366 RootedString
moduleLoadPath(cx
, JS_NewStringCopyUTF8Z(cx
, path
));
4367 if (!moduleLoadPath
) {
4370 sc
->moduleLoader
= js::MakeUnique
<ModuleLoader
>();
4371 if (!sc
->moduleLoader
|| !sc
->moduleLoader
->init(cx
, moduleLoadPath
)) {
4375 JS::CompileOptions
options(cx
);
4376 options
.setFileAndLine("<string>", 1)
4378 .setEagerDelazificationStrategy(defaultDelazificationMode
);
4380 AutoReportException
are(cx
);
4381 JS::SourceText
<char16_t
> srcBuf
;
4382 if (!srcBuf
.init(cx
, input
->chars
.get(), input
->length
,
4383 JS::SourceOwnership::Borrowed
)) {
4387 RootedScript
script(cx
, JS::Compile(cx
, options
, srcBuf
));
4391 RootedValue
result(cx
);
4392 JS_ExecuteScript(cx
, script
, &result
);
4398 // Workers can spawn other workers, so we need a lock to access workerThreads.
4399 static Mutex
* workerThreadsLock
= nullptr;
4400 static Vector
<UniquePtr
<js::Thread
>, 0, SystemAllocPolicy
> workerThreads
;
4402 class MOZ_RAII AutoLockWorkerThreads
: public LockGuard
<Mutex
> {
4403 using Base
= LockGuard
<Mutex
>;
4406 AutoLockWorkerThreads() : Base(*workerThreadsLock
) {
4407 MOZ_ASSERT(workerThreadsLock
);
4411 static bool EvalInWorker(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4412 if (!CanUseExtraThreads()) {
4413 JS_ReportErrorASCII(cx
, "Can't create threads with --no-threads");
4417 CallArgs args
= CallArgsFromVp(argc
, vp
);
4418 if (!args
.get(0).isString()) {
4419 JS_ReportErrorASCII(cx
, "Invalid arguments");
4423 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
4424 if (cx
->runningOOMTest
) {
4425 JS_ReportErrorASCII(
4426 cx
, "Can't create threads while running simulated OOM test");
4431 if (!args
[0].toString()->ensureLinear(cx
)) {
4435 if (!workerThreadsLock
) {
4436 workerThreadsLock
= js_new
<Mutex
>(mutexid::ShellWorkerThreads
);
4437 if (!workerThreadsLock
) {
4438 ReportOutOfMemory(cx
);
4443 JSLinearString
* str
= &args
[0].toString()->asLinear();
4445 UniqueTwoByteChars
chars(js_pod_malloc
<char16_t
>(str
->length()));
4447 ReportOutOfMemory(cx
);
4451 CopyChars(chars
.get(), *str
);
4453 auto input
= js::MakeUnique
<WorkerInput
>(JS_GetParentRuntime(cx
),
4454 std::move(chars
), str
->length());
4456 ReportOutOfMemory(cx
);
4460 UniquePtr
<Thread
> thread
;
4462 AutoEnterOOMUnsafeRegion oomUnsafe
;
4463 thread
= js::MakeUnique
<Thread
>(
4464 Thread::Options().setStackSize(gWorkerStackSize
+ 512 * 1024));
4465 if (!thread
|| !thread
->init(WorkerMain
, std::move(input
))) {
4466 oomUnsafe
.crash("EvalInWorker");
4470 AutoLockWorkerThreads alwt
;
4471 if (!workerThreads
.append(std::move(thread
))) {
4472 ReportOutOfMemory(cx
);
4477 args
.rval().setUndefined();
4481 static bool ShapeOf(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
4482 CallArgs args
= CallArgsFromVp(argc
, vp
);
4483 if (!args
.get(0).isObject()) {
4484 JS_ReportErrorASCII(cx
, "shapeOf: object expected");
4487 JSObject
* obj
= &args
[0].toObject();
4488 args
.rval().set(JS_NumberValue(double(uintptr_t(obj
->shape()) >> 3)));
4492 static bool Sleep_fn(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4493 ShellContext
* sc
= GetShellContext(cx
);
4494 CallArgs args
= CallArgsFromVp(argc
, vp
);
4496 TimeDuration duration
= TimeDuration::FromSeconds(0.0);
4497 if (args
.length() > 0) {
4499 if (!ToNumber(cx
, args
[0], &t_secs
)) {
4502 if (std::isnan(t_secs
)) {
4503 JS_ReportErrorASCII(cx
, "sleep interval is not a number");
4507 duration
= TimeDuration::FromSeconds(std::max(0.0, t_secs
));
4508 const TimeDuration MAX_TIMEOUT_INTERVAL
=
4509 TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS
);
4510 if (duration
> MAX_TIMEOUT_INTERVAL
) {
4511 JS_ReportErrorASCII(cx
, "Excessive sleep interval");
4516 LockGuard
<Mutex
> guard(sc
->watchdogLock
);
4517 TimeStamp toWakeup
= TimeStamp::Now() + duration
;
4519 sc
->sleepWakeup
.wait_for(guard
, duration
);
4520 if (sc
->serviceInterrupt
) {
4523 auto now
= TimeStamp::Now();
4524 if (now
>= toWakeup
) {
4527 duration
= toWakeup
- now
;
4530 args
.rval().setUndefined();
4531 return !sc
->serviceInterrupt
;
4534 static void KillWatchdog(JSContext
* cx
) {
4535 ShellContext
* sc
= GetShellContext(cx
);
4536 Maybe
<Thread
> thread
;
4539 LockGuard
<Mutex
> guard(sc
->watchdogLock
);
4540 std::swap(sc
->watchdogThread
, thread
);
4542 // The watchdog thread becoming Nothing is its signal to exit.
4543 sc
->watchdogWakeup
.notify_one();
4550 MOZ_ASSERT(!sc
->watchdogThread
);
4553 static void WatchdogMain(JSContext
* cx
) {
4554 ThisThread::SetName("JS Watchdog");
4556 ShellContext
* sc
= GetShellContext(cx
);
4559 LockGuard
<Mutex
> guard(sc
->watchdogLock
);
4560 while (sc
->watchdogThread
) {
4561 auto now
= TimeStamp::Now();
4562 if (sc
->watchdogTimeout
&& now
>= sc
->watchdogTimeout
.value()) {
4564 * The timeout has just expired. Request an interrupt callback
4567 sc
->watchdogTimeout
= Nothing();
4569 UnlockGuard
<Mutex
> unlock(guard
);
4570 CancelExecution(cx
);
4573 /* Wake up any threads doing sleep. */
4574 sc
->sleepWakeup
.notify_all();
4576 if (sc
->watchdogTimeout
) {
4578 * Time hasn't expired yet. Simulate an interrupt callback
4579 * which doesn't abort execution.
4581 JS_RequestInterruptCallback(cx
);
4584 TimeDuration sleepDuration
= sc
->watchdogTimeout
4585 ? TimeDuration::FromSeconds(0.1)
4586 : TimeDuration::Forever();
4587 sc
->watchdogWakeup
.wait_for(guard
, sleepDuration
);
4593 static bool ScheduleWatchdog(JSContext
* cx
, double t
) {
4594 ShellContext
* sc
= GetShellContext(cx
);
4597 LockGuard
<Mutex
> guard(sc
->watchdogLock
);
4598 sc
->watchdogTimeout
= Nothing();
4606 auto interval
= TimeDuration::FromSeconds(t
);
4607 auto timeout
= TimeStamp::Now() + interval
;
4608 LockGuard
<Mutex
> guard(sc
->watchdogLock
);
4609 if (!sc
->watchdogThread
) {
4610 MOZ_ASSERT(!sc
->watchdogTimeout
);
4611 sc
->watchdogThread
.emplace();
4612 AutoEnterOOMUnsafeRegion oomUnsafe
;
4613 if (!sc
->watchdogThread
->init(WatchdogMain
, cx
)) {
4614 oomUnsafe
.crash("watchdogThread.init");
4616 } else if (!sc
->watchdogTimeout
|| timeout
< sc
->watchdogTimeout
.value()) {
4617 sc
->watchdogWakeup
.notify_one();
4619 sc
->watchdogTimeout
= Some(timeout
);
4623 static void KillWorkerThreads(JSContext
* cx
) {
4624 MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads
.empty());
4626 if (!workerThreadsLock
) {
4627 MOZ_ASSERT(workerThreads
.empty());
4632 // We need to leave the AutoLockWorkerThreads scope before we call
4633 // js::Thread::join, to avoid deadlocks when AutoLockWorkerThreads is
4634 // used by the worker thread.
4635 UniquePtr
<Thread
> thread
;
4637 AutoLockWorkerThreads alwt
;
4638 if (workerThreads
.empty()) {
4641 thread
= std::move(workerThreads
.back());
4642 workerThreads
.popBack();
4647 workerThreads
.clearAndFree();
4649 js_delete(workerThreadsLock
);
4650 workerThreadsLock
= nullptr;
4653 static void CancelExecution(JSContext
* cx
) {
4654 ShellContext
* sc
= GetShellContext(cx
);
4655 sc
->serviceInterrupt
= true;
4656 JS_RequestInterruptCallback(cx
);
4659 static bool SetTimeoutValue(JSContext
* cx
, double t
) {
4660 if (std::isnan(t
)) {
4661 JS_ReportErrorASCII(cx
, "timeout is not a number");
4664 const TimeDuration MAX_TIMEOUT_INTERVAL
=
4665 TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS
);
4666 if (TimeDuration::FromSeconds(t
) > MAX_TIMEOUT_INTERVAL
) {
4667 JS_ReportErrorASCII(cx
, "Excessive timeout value");
4670 GetShellContext(cx
)->timeoutInterval
= t
;
4671 if (!ScheduleWatchdog(cx
, t
)) {
4672 JS_ReportErrorASCII(cx
, "Failed to create the watchdog");
4678 static bool Timeout(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4679 ShellContext
* sc
= GetShellContext(cx
);
4680 CallArgs args
= CallArgsFromVp(argc
, vp
);
4682 if (args
.length() == 0) {
4683 args
.rval().setNumber(sc
->timeoutInterval
);
4687 if (args
.length() > 2) {
4688 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
4693 if (!ToNumber(cx
, args
[0], &t
)) {
4697 if (args
.length() > 1) {
4698 RootedValue
value(cx
, args
[1]);
4699 if (!value
.isObject() || !value
.toObject().is
<JSFunction
>()) {
4700 JS_ReportErrorASCII(cx
, "Second argument must be a timeout function");
4703 sc
->interruptFunc
= value
;
4704 sc
->haveInterruptFunc
= true;
4707 args
.rval().setUndefined();
4708 return SetTimeoutValue(cx
, t
);
4711 static bool InterruptIf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4712 CallArgs args
= CallArgsFromVp(argc
, vp
);
4714 if (args
.length() != 1) {
4715 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
4719 if (ToBoolean(args
[0])) {
4720 GetShellContext(cx
)->serviceInterrupt
= true;
4721 JS_RequestInterruptCallback(cx
);
4724 args
.rval().setUndefined();
4728 static bool InvokeInterruptCallbackWrapper(JSContext
* cx
, unsigned argc
,
4730 CallArgs args
= CallArgsFromVp(argc
, vp
);
4731 if (args
.length() != 1) {
4732 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
4736 GetShellContext(cx
)->serviceInterrupt
= true;
4737 JS_RequestInterruptCallback(cx
);
4738 bool interruptRv
= CheckForInterrupt(cx
);
4740 // The interrupt handler could have set a pending exception. Since we call
4741 // back into JS, don't have it see the pending exception. If we have an
4742 // uncatchable exception that's not propagating a debug mode forced
4744 if (!interruptRv
&& !cx
->isExceptionPending() &&
4745 !cx
->isPropagatingForcedReturn()) {
4749 JS::AutoSaveExceptionState
savedExc(cx
);
4751 FixedInvokeArgs
<1> iargs(cx
);
4753 iargs
[0].setBoolean(interruptRv
);
4756 if (!js::Call(cx
, args
[0], UndefinedHandleValue
, iargs
, &rv
)) {
4760 args
.rval().setUndefined();
4764 static bool SetInterruptCallback(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4765 CallArgs args
= CallArgsFromVp(argc
, vp
);
4767 if (args
.length() != 1) {
4768 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
4772 RootedValue
value(cx
, args
[0]);
4773 if (!value
.isObject() || !value
.toObject().is
<JSFunction
>()) {
4774 JS_ReportErrorASCII(cx
, "Argument must be a function");
4777 GetShellContext(cx
)->interruptFunc
= value
;
4778 GetShellContext(cx
)->haveInterruptFunc
= true;
4780 args
.rval().setUndefined();
4785 // var s0 = "A".repeat(10*1024);
4786 // interruptRegexp(/a(bc|bd)/, s0);
4787 // first arg is regexp
4788 // second arg is string
4789 static bool InterruptRegexp(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4790 CallArgs args
= CallArgsFromVp(argc
, vp
);
4791 ShellContext
* sc
= GetShellContext(cx
);
4792 RootedObject
callee(cx
, &args
.callee());
4794 if (args
.length() != 2) {
4795 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments.");
4798 if (!(args
[0].isObject() && args
[0].toObject().is
<RegExpObject
>())) {
4799 ReportUsageErrorASCII(cx
, callee
,
4800 "First argument must be a regular expression.");
4803 if (!args
[1].isString()) {
4804 ReportUsageErrorASCII(cx
, callee
, "Second argument must be a String.");
4807 // Set interrupt flags
4808 sc
->serviceInterrupt
= true;
4809 js::irregexp::IsolateSetShouldSimulateInterrupt(cx
->isolate
);
4811 RootedObject
regexp(cx
, &args
[0].toObject());
4812 RootedString
string(cx
, args
[1].toString());
4813 int32_t lastIndex
= 0;
4815 return js::RegExpMatcherRaw(cx
, regexp
, string
, lastIndex
, nullptr,
4820 static bool CheckRegExpSyntax(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4821 CallArgs args
= CallArgsFromVp(argc
, vp
);
4822 RootedObject
callee(cx
, &args
.callee());
4824 if (args
.length() != 1) {
4825 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments.");
4828 if (!args
[0].isString()) {
4829 ReportUsageErrorASCII(cx
, callee
, "First argument must be a string.");
4833 RootedString
string(cx
, args
[0].toString());
4834 AutoStableStringChars
stableChars(cx
);
4835 if (!stableChars
.initTwoByte(cx
, string
)) {
4839 const char16_t
* chars
= stableChars
.twoByteRange().begin().get();
4840 size_t length
= string
->length();
4842 Rooted
<JS::Value
> error(cx
);
4843 if (!JS::CheckRegExpSyntax(cx
, chars
, length
, JS::RegExpFlag::NoFlags
,
4848 args
.rval().set(error
);
4852 static bool SetJitCompilerOption(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4853 CallArgs args
= CallArgsFromVp(argc
, vp
);
4854 RootedObject
callee(cx
, &args
.callee());
4856 if (args
.length() != 2) {
4857 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments.");
4861 if (!args
[0].isString()) {
4862 ReportUsageErrorASCII(cx
, callee
, "First argument must be a String.");
4866 if (!args
[1].isInt32()) {
4867 ReportUsageErrorASCII(cx
, callee
, "Second argument must be an Int32.");
4871 // Disallow setting JIT options when there are worker threads, to avoid
4873 if (workerThreadsLock
) {
4874 ReportUsageErrorASCII(
4875 cx
, callee
, "Can't set JIT options when there are worker threads.");
4879 JSLinearString
* strArg
= JS_EnsureLinearString(cx
, args
[0].toString());
4884 #define JIT_COMPILER_MATCH(key, string) \
4885 else if (JS_LinearStringEqualsLiteral(strArg, string)) opt = \
4886 JSJITCOMPILER_##key;
4888 JSJitCompilerOption opt
= JSJITCOMPILER_NOT_AN_OPTION
;
4891 JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH
);
4892 #undef JIT_COMPILER_MATCH
4894 if (opt
== JSJITCOMPILER_NOT_AN_OPTION
) {
4895 ReportUsageErrorASCII(
4897 "First argument does not name a valid option (see jsapi.h).");
4901 int32_t number
= args
[1].toInt32();
4906 // Disallow enabling or disabling the Baseline Interpreter at runtime.
4907 // Enabling is a problem because the Baseline Interpreter code is only
4908 // present if the interpreter was enabled when the JitRuntime was created.
4909 // To support disabling we would have to discard all JitScripts. Furthermore,
4910 // we really want JitOptions to be immutable after startup so it's better to
4912 if (opt
== JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE
&&
4913 bool(number
) != jit::IsBaselineInterpreterEnabled()) {
4914 JS_ReportErrorASCII(cx
,
4915 "Enabling or disabling the Baseline Interpreter at "
4916 "runtime is not supported.");
4920 // Throw if disabling the JITs and there's JIT code on the stack, to avoid
4921 // assertion failures.
4922 if ((opt
== JSJITCOMPILER_BASELINE_ENABLE
||
4923 opt
== JSJITCOMPILER_ION_ENABLE
) &&
4925 js::jit::JitActivationIterator
iter(cx
);
4927 JS_ReportErrorASCII(cx
,
4928 "Can't turn off JITs with JIT code on the stack.");
4933 // Changing code memory protection settings at runtime is not supported. Don't
4934 // throw if not changing the setting because some jit-tests depend on that.
4935 if (opt
== JSJITCOMPILER_WRITE_PROTECT_CODE
) {
4936 uint32_t writeProtect
;
4937 MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
4938 cx
, JSJITCOMPILER_WRITE_PROTECT_CODE
, &writeProtect
));
4939 if (bool(number
) != writeProtect
) {
4940 JS_ReportErrorASCII(cx
, "Can't change code write protection at runtime");
4946 // Throw if trying to disable all the Wasm compilers. The logic here is that
4947 // if we're trying to disable a compiler that is currently enabled and that is
4948 // the last compiler enabled then we must throw.
4950 // Note that this check does not prevent an error from being thrown later.
4951 // Actual compiler availability is dynamic and depends on other conditions,
4952 // such as other options set and whether a debugger is present.
4953 if ((opt
== JSJITCOMPILER_WASM_JIT_BASELINE
||
4954 opt
== JSJITCOMPILER_WASM_JIT_OPTIMIZING
) &&
4956 uint32_t baseline
, optimizing
;
4957 MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
4958 cx
, JSJITCOMPILER_WASM_JIT_BASELINE
, &baseline
));
4959 MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
4960 cx
, JSJITCOMPILER_WASM_JIT_OPTIMIZING
, &optimizing
));
4961 if (baseline
+ optimizing
== 1) {
4962 if ((opt
== JSJITCOMPILER_WASM_JIT_BASELINE
&& baseline
) ||
4963 (opt
== JSJITCOMPILER_WASM_JIT_OPTIMIZING
&& optimizing
)) {
4964 JS_ReportErrorASCII(
4966 "Disabling all the Wasm compilers at runtime is not supported.");
4972 // JIT compiler options are process-wide, so we have to stop off-thread
4973 // compilations for all runtimes to avoid races.
4974 WaitForAllHelperThreads();
4976 // Only release JIT code for the current runtime because there's no good
4977 // way to discard code for other runtimes.
4978 ReleaseAllJITCode(cx
->gcContext());
4980 JS_SetGlobalJitCompilerOption(cx
, opt
, uint32_t(number
));
4982 args
.rval().setUndefined();
4986 static bool EnableLastWarning(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4987 ShellContext
* sc
= GetShellContext(cx
);
4988 CallArgs args
= CallArgsFromVp(argc
, vp
);
4990 sc
->lastWarningEnabled
= true;
4991 sc
->lastWarning
.setNull();
4993 args
.rval().setUndefined();
4997 static bool DisableLastWarning(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4998 ShellContext
* sc
= GetShellContext(cx
);
4999 CallArgs args
= CallArgsFromVp(argc
, vp
);
5001 sc
->lastWarningEnabled
= false;
5002 sc
->lastWarning
.setNull();
5004 args
.rval().setUndefined();
5008 static bool GetLastWarning(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5009 ShellContext
* sc
= GetShellContext(cx
);
5010 CallArgs args
= CallArgsFromVp(argc
, vp
);
5012 if (!sc
->lastWarningEnabled
) {
5013 JS_ReportErrorASCII(cx
, "Call enableLastWarning first.");
5017 if (!JS_WrapValue(cx
, &sc
->lastWarning
)) {
5021 args
.rval().set(sc
->lastWarning
);
5025 static bool ClearLastWarning(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5026 ShellContext
* sc
= GetShellContext(cx
);
5027 CallArgs args
= CallArgsFromVp(argc
, vp
);
5029 if (!sc
->lastWarningEnabled
) {
5030 JS_ReportErrorASCII(cx
, "Call enableLastWarning first.");
5034 sc
->lastWarning
.setNull();
5036 args
.rval().setUndefined();
5040 #if defined(DEBUG) || defined(JS_JITSPEW)
5041 static bool StackDump(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5042 CallArgs args
= CallArgsFromVp(argc
, vp
);
5044 if (!gOutFile
->isOpen()) {
5045 JS_ReportErrorASCII(cx
, "output file is closed");
5049 bool showArgs
= ToBoolean(args
.get(0));
5050 bool showLocals
= ToBoolean(args
.get(1));
5051 bool showThisProps
= ToBoolean(args
.get(2));
5053 JS::UniqueChars buf
=
5054 JS::FormatStackDump(cx
, showArgs
, showLocals
, showThisProps
);
5056 fputs("Failed to format JavaScript stack for dump\n", gOutFile
->fp
);
5057 JS_ClearPendingException(cx
);
5059 fputs(buf
.get(), gOutFile
->fp
);
5062 args
.rval().setUndefined();
5067 static bool StackPointerInfo(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5068 CallArgs args
= CallArgsFromVp(argc
, vp
);
5070 // Copy the truncated stack pointer to the result. This value is not used
5071 // as a pointer but as a way to measure frame-size from JS.
5072 args
.rval().setInt32(int32_t(reinterpret_cast<size_t>(&args
) & 0xfffffff));
5076 static bool Elapsed(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5077 CallArgs args
= CallArgsFromVp(argc
, vp
);
5078 if (args
.length() == 0) {
5079 double d
= PRMJ_Now() - GetShellContext(cx
)->startTime
;
5080 args
.rval().setDouble(d
);
5083 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
5087 static ShellCompartmentPrivate
* EnsureShellCompartmentPrivate(JSContext
* cx
) {
5088 Compartment
* comp
= cx
->compartment();
5090 static_cast<ShellCompartmentPrivate
*>(JS_GetCompartmentPrivate(comp
));
5092 priv
= cx
->new_
<ShellCompartmentPrivate
>();
5093 JS_SetCompartmentPrivate(cx
->compartment(), priv
);
5098 static bool ParseModule(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5099 CallArgs args
= CallArgsFromVp(argc
, vp
);
5100 if (!args
.requireAtLeast(cx
, "parseModule", 1)) {
5104 if (!args
[0].isString()) {
5105 const char* typeName
= InformalValueTypeName(args
[0]);
5106 JS_ReportErrorASCII(cx
, "expected string to compile, got %s", typeName
);
5110 JSString
* scriptContents
= args
[0].toString();
5112 UniqueChars filename
;
5113 CompileOptions
options(cx
);
5114 if (args
.length() > 1) {
5115 if (!args
[1].isString()) {
5116 const char* typeName
= InformalValueTypeName(args
[1]);
5117 JS_ReportErrorASCII(cx
, "expected filename string, got %s", typeName
);
5121 RootedString
str(cx
, args
[1].toString());
5122 filename
= JS_EncodeStringToUTF8(cx
, str
);
5127 options
.setFileAndLine(filename
.get(), 1);
5129 options
.setFileAndLine("<string>", 1);
5131 options
.setModule();
5133 AutoStableStringChars
linearChars(cx
);
5134 if (!linearChars
.initTwoByte(cx
, scriptContents
)) {
5138 JS::SourceText
<char16_t
> srcBuf
;
5139 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
5143 AutoReportFrontendContext
fc(cx
);
5144 RootedObject
module(cx
, frontend::CompileModule(cx
, &fc
, options
, srcBuf
));
5149 Rooted
<ShellModuleObjectWrapper
*> wrapper(
5150 cx
, ShellModuleObjectWrapper::create(cx
, module
.as
<ModuleObject
>()));
5154 args
.rval().setObject(*wrapper
);
5158 // A JSObject that holds XDRBuffer.
5159 class XDRBufferObject
: public NativeObject
{
5160 static const size_t VECTOR_SLOT
= 0;
5161 static const unsigned RESERVED_SLOTS
= 1;
5164 static const JSClassOps classOps_
;
5165 static const JSClass class_
;
5167 [[nodiscard
]] inline static XDRBufferObject
* create(
5168 JSContext
* cx
, JS::TranscodeBuffer
&& buf
);
5170 JS::TranscodeBuffer
* data() const {
5171 Value value
= getReservedSlot(VECTOR_SLOT
);
5172 auto buf
= static_cast<JS::TranscodeBuffer
*>(value
.toPrivate());
5177 bool hasData() const {
5178 // Data may not be present if we hit OOM in initialization.
5179 return !getReservedSlot(VECTOR_SLOT
).isUndefined();
5182 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
);
5185 /*static */ const JSClassOps
XDRBufferObject::classOps_
= {
5186 nullptr, // addProperty
5187 nullptr, // delProperty
5188 nullptr, // enumerate
5189 nullptr, // newEnumerate
5191 nullptr, // mayResolve
5192 XDRBufferObject::finalize
, // finalize
5194 nullptr, // construct
5198 /*static */ const JSClass
XDRBufferObject::class_
= {
5200 JSCLASS_HAS_RESERVED_SLOTS(XDRBufferObject::RESERVED_SLOTS
) |
5201 JSCLASS_BACKGROUND_FINALIZE
,
5202 &XDRBufferObject::classOps_
};
5204 XDRBufferObject
* XDRBufferObject::create(JSContext
* cx
,
5205 JS::TranscodeBuffer
&& buf
) {
5206 XDRBufferObject
* bufObj
=
5207 NewObjectWithGivenProto
<XDRBufferObject
>(cx
, nullptr);
5212 auto heapBuf
= cx
->make_unique
<JS::TranscodeBuffer
>(std::move(buf
));
5217 size_t len
= heapBuf
->length();
5218 InitReservedSlot(bufObj
, VECTOR_SLOT
, heapBuf
.release(), len
,
5219 MemoryUse::XDRBufferElements
);
5224 void XDRBufferObject::finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
5225 XDRBufferObject
* buf
= &obj
->as
<XDRBufferObject
>();
5226 if (buf
->hasData()) {
5227 gcx
->delete_(buf
, buf
->data(), buf
->data()->length(),
5228 MemoryUse::XDRBufferElements
);
5232 static bool InstantiateModuleStencil(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
5233 CallArgs args
= CallArgsFromVp(argc
, vp
);
5235 if (!args
.requireAtLeast(cx
, "instantiateModuleStencil", 1)) {
5239 /* Prepare the input byte array. */
5240 if (!args
[0].isObject()) {
5241 JS_ReportErrorASCII(cx
,
5242 "instantiateModuleStencil: Stencil object expected");
5245 Rooted
<js::StencilObject
*> stencilObj(
5246 cx
, args
[0].toObject().maybeUnwrapIf
<js::StencilObject
>());
5248 JS_ReportErrorASCII(cx
,
5249 "instantiateModuleStencil: Stencil object expected");
5253 if (!stencilObj
->stencil()->isModule()) {
5254 JS_ReportErrorASCII(cx
,
5255 "instantiateModuleStencil: Module stencil expected");
5259 CompileOptions
options(cx
);
5260 UniqueChars fileNameBytes
;
5261 if (args
.length() == 2) {
5262 if (!args
[1].isObject()) {
5263 JS_ReportErrorASCII(
5264 cx
, "instantiateModuleStencil: The 2nd argument must be an object");
5268 RootedObject
opts(cx
, &args
[1].toObject());
5269 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
5274 /* Prepare the CompilationStencil for decoding. */
5275 AutoReportFrontendContext
fc(cx
);
5276 Rooted
<frontend::CompilationInput
> input(cx
,
5277 frontend::CompilationInput(options
));
5278 if (!input
.get().initForModule(&fc
)) {
5282 if (!js::ValidateLazinessOfStencilAndGlobal(cx
, *stencilObj
->stencil())) {
5286 /* Instantiate the stencil. */
5287 Rooted
<frontend::CompilationGCOutput
> output(cx
);
5288 if (!frontend::CompilationStencil::instantiateStencils(
5289 cx
, input
.get(), *stencilObj
->stencil(), output
.get())) {
5293 Rooted
<ModuleObject
*> modObject(cx
, output
.get().module
);
5294 Rooted
<ShellModuleObjectWrapper
*> wrapper(
5295 cx
, ShellModuleObjectWrapper::create(cx
, modObject
));
5299 args
.rval().setObject(*wrapper
);
5303 static bool InstantiateModuleStencilXDR(JSContext
* cx
, uint32_t argc
,
5305 CallArgs args
= CallArgsFromVp(argc
, vp
);
5307 if (!args
.requireAtLeast(cx
, "instantiateModuleStencilXDR", 1)) {
5311 /* Prepare the input byte array. */
5312 if (!args
[0].isObject()) {
5313 JS_ReportErrorASCII(
5314 cx
, "instantiateModuleStencilXDR: Stencil XDR object expected");
5317 Rooted
<StencilXDRBufferObject
*> xdrObj(
5318 cx
, args
[0].toObject().maybeUnwrapIf
<StencilXDRBufferObject
>());
5320 JS_ReportErrorASCII(
5321 cx
, "instantiateModuleStencilXDR: Stencil XDR object expected");
5324 MOZ_ASSERT(xdrObj
->hasBuffer());
5326 CompileOptions
options(cx
);
5327 UniqueChars fileNameBytes
;
5328 if (args
.length() == 2) {
5329 if (!args
[1].isObject()) {
5330 JS_ReportErrorASCII(
5332 "instantiateModuleStencilXDR: The 2nd argument must be an object");
5336 RootedObject
opts(cx
, &args
[1].toObject());
5337 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
5342 /* Prepare the CompilationStencil for decoding. */
5343 AutoReportFrontendContext
fc(cx
);
5344 Rooted
<frontend::CompilationInput
> input(cx
,
5345 frontend::CompilationInput(options
));
5346 if (!input
.get().initForModule(&fc
)) {
5349 frontend::CompilationStencil
stencil(nullptr);
5351 /* Deserialize the stencil from XDR. */
5352 JS::TranscodeRange
xdrRange(xdrObj
->buffer(), xdrObj
->bufferLength());
5353 bool succeeded
= false;
5354 if (!stencil
.deserializeStencils(&fc
, options
, xdrRange
, &succeeded
)) {
5358 fc
.clearAutoReport();
5359 JS_ReportErrorASCII(cx
, "Decoding failure");
5363 if (!stencil
.isModule()) {
5364 fc
.clearAutoReport();
5365 JS_ReportErrorASCII(cx
,
5366 "instantiateModuleStencilXDR: Module stencil expected");
5370 if (!js::ValidateLazinessOfStencilAndGlobal(cx
, stencil
)) {
5374 /* Instantiate the stencil. */
5375 Rooted
<frontend::CompilationGCOutput
> output(cx
);
5376 if (!frontend::CompilationStencil::instantiateStencils(
5377 cx
, input
.get(), stencil
, output
.get())) {
5381 Rooted
<ModuleObject
*> modObject(cx
, output
.get().module
);
5382 Rooted
<ShellModuleObjectWrapper
*> wrapper(
5383 cx
, ShellModuleObjectWrapper::create(cx
, modObject
));
5387 args
.rval().setObject(*wrapper
);
5391 static bool RegisterModule(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5392 CallArgs args
= CallArgsFromVp(argc
, vp
);
5393 if (!args
.requireAtLeast(cx
, "registerModule", 2)) {
5397 if (!args
[0].isString()) {
5398 const char* typeName
= InformalValueTypeName(args
[0]);
5399 JS_ReportErrorASCII(cx
, "expected string, got %s", typeName
);
5403 if (!args
[1].isObject() ||
5404 !args
[1].toObject().is
<ShellModuleObjectWrapper
>()) {
5405 const char* typeName
= InformalValueTypeName(args
[1]);
5406 JS_ReportErrorASCII(cx
, "expected module, got %s", typeName
);
5410 ShellContext
* sc
= GetShellContext(cx
);
5411 Rooted
<ModuleObject
*> module(
5412 cx
, args
[1].toObject().as
<ShellModuleObjectWrapper
>().get());
5414 Rooted
<JSAtom
*> specifier(cx
, AtomizeString(cx
, args
[0].toString()));
5419 RootedObject
moduleRequest(
5420 cx
, ModuleRequestObject::create(cx
, specifier
, nullptr));
5421 if (!moduleRequest
) {
5425 if (!sc
->moduleLoader
->registerTestModule(cx
, moduleRequest
, module
)) {
5429 Rooted
<ShellModuleObjectWrapper
*> wrapper(
5430 cx
, ShellModuleObjectWrapper::create(cx
, module
));
5434 args
.rval().setObject(*wrapper
);
5438 static bool ClearModules(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5439 CallArgs args
= CallArgsFromVp(argc
, vp
);
5440 ShellContext
* sc
= GetShellContext(cx
);
5441 sc
->moduleLoader
->clearModules(cx
);
5442 args
.rval().setUndefined();
5446 static bool ModuleLink(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5447 CallArgs args
= CallArgsFromVp(argc
, vp
);
5449 if (args
.length() != 1 || !args
[0].isObject()) {
5450 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
5455 RootedObject
object(cx
, UncheckedUnwrap(&args
[0].toObject()));
5456 if (!object
->is
<ShellModuleObjectWrapper
>()) {
5457 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
5462 AutoRealm
ar(cx
, object
);
5464 Rooted
<ModuleObject
*> module(cx
,
5465 object
->as
<ShellModuleObjectWrapper
>().get());
5466 if (!js::ModuleLink(cx
, module
)) {
5470 args
.rval().setUndefined();
5474 static bool ModuleEvaluate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5475 CallArgs args
= CallArgsFromVp(argc
, vp
);
5477 if (args
.length() != 1 || !args
[0].isObject()) {
5478 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
5483 RootedObject
object(cx
, UncheckedUnwrap(&args
[0].toObject()));
5484 if (!object
->is
<ShellModuleObjectWrapper
>()) {
5485 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
5491 AutoRealm
ar(cx
, object
);
5493 Rooted
<ModuleObject
*> module(cx
,
5494 object
->as
<ShellModuleObjectWrapper
>().get());
5495 if (!js::ModuleEvaluate(cx
, module
, args
.rval())) {
5500 return JS_WrapValue(cx
, args
.rval());
5503 static ModuleEnvironmentObject
* GetModuleInitialEnvironment(
5504 JSContext
* cx
, Handle
<ModuleObject
*> module
) {
5505 // Use the initial environment so that tests can check bindings exists
5506 // before they have been instantiated.
5507 Rooted
<ModuleEnvironmentObject
*> env(cx
, &module
->initialEnvironment());
5512 static bool GetModuleEnvironmentNames(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5513 CallArgs args
= CallArgsFromVp(argc
, vp
);
5514 if (args
.length() != 1) {
5515 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
5519 if (!args
[0].isObject() ||
5520 !args
[0].toObject().is
<ShellModuleObjectWrapper
>()) {
5521 JS_ReportErrorASCII(cx
,
5522 "First argument should be a ShellModuleObjectWrapper");
5526 Rooted
<ModuleObject
*> module(
5527 cx
, args
[0].toObject().as
<ShellModuleObjectWrapper
>().get());
5528 if (module
->hadEvaluationError()) {
5529 JS_ReportErrorASCII(cx
, "Module environment unavailable");
5533 Rooted
<ModuleEnvironmentObject
*> env(cx
,
5534 GetModuleInitialEnvironment(cx
, module
));
5535 Rooted
<IdVector
> ids(cx
, IdVector(cx
));
5536 if (!JS_Enumerate(cx
, env
, &ids
)) {
5540 // The "*namespace*" binding is a detail of current implementation so hide
5541 // it to give stable results in tests.
5542 ids
.eraseIfEqual(NameToId(cx
->names().star_namespace_star_
));
5544 uint32_t length
= ids
.length();
5545 Rooted
<ArrayObject
*> array(cx
, NewDenseFullyAllocatedArray(cx
, length
));
5550 array
->setDenseInitializedLength(length
);
5551 for (uint32_t i
= 0; i
< length
; i
++) {
5552 array
->initDenseElement(i
, StringValue(ids
[i
].toString()));
5555 args
.rval().setObject(*array
);
5559 static bool GetModuleEnvironmentValue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5560 CallArgs args
= CallArgsFromVp(argc
, vp
);
5561 if (args
.length() != 2) {
5562 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
5566 if (!args
[0].isObject() ||
5567 !args
[0].toObject().is
<ShellModuleObjectWrapper
>()) {
5568 JS_ReportErrorASCII(cx
,
5569 "First argument should be a ShellModuleObjectWrapper");
5573 if (!args
[1].isString()) {
5574 JS_ReportErrorASCII(cx
, "Second argument should be a string");
5578 Rooted
<ModuleObject
*> module(
5579 cx
, args
[0].toObject().as
<ShellModuleObjectWrapper
>().get());
5580 if (module
->hadEvaluationError()) {
5581 JS_ReportErrorASCII(cx
, "Module environment unavailable");
5585 Rooted
<ModuleEnvironmentObject
*> env(cx
,
5586 GetModuleInitialEnvironment(cx
, module
));
5587 RootedString
name(cx
, args
[1].toString());
5589 if (!JS_StringToId(cx
, name
, &id
)) {
5593 if (!GetProperty(cx
, env
, env
, id
, args
.rval())) {
5597 if (args
.rval().isMagic(JS_UNINITIALIZED_LEXICAL
)) {
5598 ReportRuntimeLexicalError(cx
, JSMSG_UNINITIALIZED_LEXICAL
, id
);
5605 enum class DumpType
{
5610 template <typename Unit
>
5611 static bool DumpAST(JSContext
* cx
, const JS::ReadOnlyCompileOptions
& options
,
5612 const Unit
* units
, size_t length
,
5613 js::frontend::CompilationState
& compilationState
,
5614 js::frontend::ParseGoal goal
) {
5615 using namespace js::frontend
;
5617 AutoReportFrontendContext
fc(cx
);
5618 Parser
<FullParseHandler
, Unit
> parser(&fc
, options
, units
, length
,
5619 /* foldConstants = */ false,
5621 /* syntaxParser = */ nullptr);
5622 if (!parser
.checkOptions()) {
5626 // Emplace the top-level stencil.
5627 MOZ_ASSERT(compilationState
.scriptData
.length() ==
5628 CompilationStencil::TopLevelIndex
);
5629 if (!compilationState
.appendScriptStencilAndData(&fc
)) {
5633 js::frontend::ParseNode
* pn
;
5634 if (goal
== frontend::ParseGoal::Script
) {
5635 pn
= parser
.parse().unwrapOr(nullptr);
5637 ModuleBuilder
builder(&fc
, &parser
);
5639 SourceExtent extent
= SourceExtent::makeGlobalExtent(length
);
5640 ModuleSharedContext
modulesc(&fc
, options
, builder
, extent
);
5641 pn
= parser
.moduleBody(&modulesc
).unwrapOr(nullptr);
5649 js::Fprinter
out(stderr
);
5650 DumpParseTree(&parser
, pn
, out
);
5656 template <typename Unit
>
5657 [[nodiscard
]] static bool DumpStencil(JSContext
* cx
,
5658 const JS::ReadOnlyCompileOptions
& options
,
5659 const Unit
* units
, size_t length
,
5660 js::frontend::ParseGoal goal
) {
5661 Rooted
<frontend::CompilationInput
> input(cx
,
5662 frontend::CompilationInput(options
));
5664 JS::SourceText
<Unit
> srcBuf
;
5665 if (!srcBuf
.init(cx
, units
, length
, JS::SourceOwnership::Borrowed
)) {
5669 AutoReportFrontendContext
fc(cx
);
5670 js::frontend::NoScopeBindingCache scopeCache
;
5671 UniquePtr
<frontend::ExtensibleCompilationStencil
> stencil
;
5672 if (goal
== frontend::ParseGoal::Script
) {
5673 stencil
= frontend::CompileGlobalScriptToExtensibleStencil(
5674 cx
, &fc
, input
.get(), &scopeCache
, srcBuf
, ScopeKind::Global
);
5676 stencil
= frontend::ParseModuleToExtensibleStencil(
5677 cx
, &fc
, cx
->tempLifoAlloc(), input
.get(), &scopeCache
, srcBuf
);
5684 #if defined(DEBUG) || defined(JS_JITSPEW)
5691 static bool FrontendTest(JSContext
* cx
, unsigned argc
, Value
* vp
,
5692 const char* funcName
, DumpType dumpType
) {
5693 using namespace js::frontend
;
5695 CallArgs args
= CallArgsFromVp(argc
, vp
);
5697 if (!args
.requireAtLeast(cx
, funcName
, 1)) {
5700 if (!args
[0].isString()) {
5701 const char* typeName
= InformalValueTypeName(args
[0]);
5702 JS_ReportErrorASCII(cx
, "expected string to parse, got %s", typeName
);
5706 frontend::ParseGoal goal
= frontend::ParseGoal::Script
;
5707 #ifdef JS_ENABLE_SMOOSH
5708 bool smoosh
= false;
5711 CompileOptions
options(cx
);
5712 options
.setIntroductionType("js shell parse")
5713 .setFileAndLine("<string>", 1)
5715 .setNoScriptRval(true);
5717 if (args
.length() >= 2) {
5718 if (!args
[1].isObject()) {
5719 JS_ReportErrorASCII(cx
, "The 2nd argument must be an object");
5723 RootedObject
objOptions(cx
, &args
[1].toObject());
5725 RootedValue
optionModule(cx
);
5726 if (!JS_GetProperty(cx
, objOptions
, "module", &optionModule
)) {
5730 if (optionModule
.isBoolean()) {
5731 if (optionModule
.toBoolean()) {
5732 goal
= frontend::ParseGoal::Module
;
5734 } else if (!optionModule
.isUndefined()) {
5735 const char* typeName
= InformalValueTypeName(optionModule
);
5736 JS_ReportErrorASCII(cx
, "option `module` should be a boolean, got %s",
5740 if (!js::ParseCompileOptions(cx
, options
, objOptions
, nullptr)) {
5744 if (goal
== frontend::ParseGoal::Module
&& options
.lineno
== 0) {
5745 JS_ReportErrorASCII(cx
, "Module cannot be compiled with lineNumber == 0");
5749 #ifdef JS_ENABLE_SMOOSH
5751 if (!JS_HasProperty(cx
, objOptions
, "rustFrontend", &found
)) {
5755 JS_ReportErrorASCII(cx
, "'rustFrontend' option is renamed to 'smoosh'");
5759 RootedValue
optionSmoosh(cx
);
5760 if (!JS_GetProperty(cx
, objOptions
, "smoosh", &optionSmoosh
)) {
5764 if (optionSmoosh
.isBoolean()) {
5765 smoosh
= optionSmoosh
.toBoolean();
5766 } else if (!optionSmoosh
.isUndefined()) {
5767 const char* typeName
= InformalValueTypeName(optionSmoosh
);
5768 JS_ReportErrorASCII(cx
, "option `smoosh` should be a boolean, got %s",
5772 #endif // JS_ENABLE_SMOOSH
5775 JSString
* scriptContents
= args
[0].toString();
5776 Rooted
<JSLinearString
*> linearString(cx
, scriptContents
->ensureLinear(cx
));
5777 if (!linearString
) {
5781 bool isAscii
= false;
5782 if (linearString
->hasLatin1Chars()) {
5783 JS::AutoCheckCannotGC nogc
;
5784 isAscii
= JS::StringIsASCII(mozilla::Span(
5785 reinterpret_cast<const char*>(linearString
->latin1Chars(nogc
)),
5786 linearString
->length()));
5789 AutoStableStringChars
stableChars(cx
);
5791 if (!stableChars
.init(cx
, scriptContents
)) {
5794 MOZ_ASSERT(stableChars
.isLatin1());
5796 if (!stableChars
.initTwoByte(cx
, scriptContents
)) {
5801 size_t length
= scriptContents
->length();
5802 #ifdef JS_ENABLE_SMOOSH
5803 if (dumpType
== DumpType::ParseNode
) {
5806 const Latin1Char
* chars
= stableChars
.latin1Range().begin().get();
5808 if (goal
== frontend::ParseGoal::Script
) {
5809 if (!SmooshParseScript(cx
, chars
, length
)) {
5813 if (!SmooshParseModule(cx
, chars
, length
)) {
5817 args
.rval().setUndefined();
5820 JS_ReportErrorASCII(cx
,
5821 "SmooshMonkey does not support non-ASCII chars yet");
5825 #endif // JS_ENABLE_SMOOSH
5827 if (goal
== frontend::ParseGoal::Module
) {
5828 // See frontend::CompileModule.
5829 options
.setForceStrictMode();
5830 options
.allowHTMLComments
= false;
5833 if (dumpType
== DumpType::Stencil
) {
5834 #ifdef JS_ENABLE_SMOOSH
5837 if (goal
== frontend::ParseGoal::Script
) {
5838 const Latin1Char
* latin1
= stableChars
.latin1Range().begin().get();
5839 auto utf8
= reinterpret_cast<const mozilla::Utf8Unit
*>(latin1
);
5840 JS::SourceText
<Utf8Unit
> srcBuf
;
5841 if (!srcBuf
.init(cx
, utf8
, length
, JS::SourceOwnership::Borrowed
)) {
5845 AutoReportFrontendContext
fc(cx
);
5846 Rooted
<frontend::CompilationInput
> input(
5847 cx
, frontend::CompilationInput(options
));
5848 UniquePtr
<frontend::ExtensibleCompilationStencil
> stencil
;
5849 if (!Smoosh::tryCompileGlobalScriptToExtensibleStencil(
5850 cx
, &fc
, input
.get(), srcBuf
, stencil
)) {
5854 fc
.clearAutoReport();
5855 JS_ReportErrorASCII(cx
, "SmooshMonkey failed to parse");
5861 frontend::BorrowingCompilationStencil
borrowingStencil(*stencil
);
5862 borrowingStencil
.dump();
5866 JS_ReportErrorASCII(cx
,
5867 "SmooshMonkey does not support module stencil");
5870 args
.rval().setUndefined();
5873 JS_ReportErrorASCII(cx
,
5874 "SmooshMonkey does not support non-ASCII chars yet");
5877 #endif // JS_ENABLE_SMOOSH
5880 const Latin1Char
* latin1
= stableChars
.latin1Range().begin().get();
5881 auto utf8
= reinterpret_cast<const mozilla::Utf8Unit
*>(latin1
);
5882 if (!DumpStencil
<mozilla::Utf8Unit
>(cx
, options
, utf8
, length
, goal
)) {
5886 MOZ_ASSERT(stableChars
.isTwoByte());
5887 const char16_t
* chars
= stableChars
.twoByteRange().begin().get();
5888 if (!DumpStencil
<char16_t
>(cx
, options
, chars
, length
, goal
)) {
5893 args
.rval().setUndefined();
5897 AutoReportFrontendContext
fc(cx
);
5898 Rooted
<frontend::CompilationInput
> input(cx
,
5899 frontend::CompilationInput(options
));
5900 if (goal
== frontend::ParseGoal::Script
) {
5901 if (!input
.get().initForGlobal(&fc
)) {
5905 if (!input
.get().initForModule(&fc
)) {
5910 LifoAllocScope
allocScope(&cx
->tempLifoAlloc());
5911 frontend::NoScopeBindingCache scopeCache
;
5912 frontend::CompilationState
compilationState(&fc
, allocScope
, input
.get());
5913 if (!compilationState
.init(&fc
, &scopeCache
)) {
5918 const Latin1Char
* latin1
= stableChars
.latin1Range().begin().get();
5919 auto utf8
= reinterpret_cast<const mozilla::Utf8Unit
*>(latin1
);
5920 if (!DumpAST
<mozilla::Utf8Unit
>(cx
, options
, utf8
, length
, compilationState
,
5925 MOZ_ASSERT(stableChars
.isTwoByte());
5926 const char16_t
* chars
= stableChars
.twoByteRange().begin().get();
5927 if (!DumpAST
<char16_t
>(cx
, options
, chars
, length
, compilationState
,
5932 args
.rval().setUndefined();
5936 static bool DumpStencil(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5937 return FrontendTest(cx
, argc
, vp
, "dumpStencil", DumpType::Stencil
);
5940 static bool Parse(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5941 // Parse returns local scope information with variables ordered
5942 // differently, depending on the underlying JIT implementation.
5943 if (js::SupportDifferentialTesting()) {
5944 JS_ReportErrorASCII(cx
,
5945 "Function not available in differential testing mode.");
5949 return FrontendTest(cx
, argc
, vp
, "parse", DumpType::ParseNode
);
5952 static bool SyntaxParse(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5953 using namespace js::frontend
;
5955 CallArgs args
= CallArgsFromVp(argc
, vp
);
5957 if (!args
.requireAtLeast(cx
, "syntaxParse", 1)) {
5960 if (!args
[0].isString()) {
5961 const char* typeName
= InformalValueTypeName(args
[0]);
5962 JS_ReportErrorASCII(cx
, "expected string to parse, got %s", typeName
);
5966 JSString
* scriptContents
= args
[0].toString();
5968 CompileOptions
options(cx
);
5969 options
.setIntroductionType("js shell syntaxParse")
5970 .setFileAndLine("<string>", 1);
5972 AutoStableStringChars
stableChars(cx
);
5973 if (!stableChars
.initTwoByte(cx
, scriptContents
)) {
5977 const char16_t
* chars
= stableChars
.twoByteRange().begin().get();
5978 size_t length
= scriptContents
->length();
5980 AutoReportFrontendContext
fc(cx
);
5981 Rooted
<frontend::CompilationInput
> input(cx
,
5982 frontend::CompilationInput(options
));
5983 if (!input
.get().initForGlobal(&fc
)) {
5987 LifoAllocScope
allocScope(&cx
->tempLifoAlloc());
5988 frontend::NoScopeBindingCache scopeCache
;
5989 frontend::CompilationState
compilationState(&fc
, allocScope
, input
.get());
5990 if (!compilationState
.init(&fc
, &scopeCache
)) {
5994 Parser
<frontend::SyntaxParseHandler
, char16_t
> parser(
5995 &fc
, options
, chars
, length
,
5996 /* foldConstants = */ false, compilationState
,
5997 /* syntaxParser = */ nullptr);
5998 if (!parser
.checkOptions()) {
6002 bool succeeded
= parser
.parse().isOk();
6003 if (fc
.hadErrors()) {
6007 if (!succeeded
&& !parser
.hadAbortedSyntaxParse()) {
6008 // If no exception is posted, either there was an OOM or a language
6009 // feature unhandled by the syntax parser was encountered.
6010 MOZ_ASSERT(fc
.hadOutOfMemory());
6014 args
.rval().setBoolean(succeeded
);
6018 static bool OffThreadCompileToStencil(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6019 if (!CanUseExtraThreads()) {
6020 JS_ReportErrorASCII(
6021 cx
, "Can't use offThreadCompileToStencil with --no-threads");
6025 CallArgs args
= CallArgsFromVp(argc
, vp
);
6027 if (!args
.requireAtLeast(cx
, "offThreadCompileToStencil", 1)) {
6030 if (!args
[0].isString()) {
6031 const char* typeName
= InformalValueTypeName(args
[0]);
6032 JS_ReportErrorASCII(cx
, "expected string to parse, got %s", typeName
);
6036 UniqueChars fileNameBytes
;
6037 CompileOptions
options(cx
);
6038 options
.setIntroductionType("js shell offThreadCompileToStencil")
6039 .setFileAndLine("<string>", 1);
6041 if (args
.length() >= 2) {
6042 if (!args
[1].isObject()) {
6043 JS_ReportErrorASCII(
6044 cx
, "offThreadCompileToStencil: The 2nd argument must be an object");
6048 // Offthread compilation requires that the debug metadata be set when the
6049 // script is collected from offthread, rather than when compiled.
6050 RootedObject
opts(cx
, &args
[1].toObject());
6051 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
6056 // This option setting must override whatever the caller requested.
6057 options
.setIsRunOnce(true);
6059 JSString
* scriptContents
= args
[0].toString();
6060 AutoStableStringChars
stableChars(cx
);
6061 if (!stableChars
.initTwoByte(cx
, scriptContents
)) {
6065 size_t length
= scriptContents
->length();
6066 const char16_t
* chars
= stableChars
.twoByteChars();
6068 // Make sure we own the string's chars, so that they are not freed before
6069 // the compilation is finished.
6070 UniqueTwoByteChars ownedChars
;
6071 if (stableChars
.maybeGiveOwnershipToCaller()) {
6072 ownedChars
.reset(const_cast<char16_t
*>(chars
));
6074 ownedChars
.reset(cx
->pod_malloc
<char16_t
>(length
));
6079 mozilla::PodCopy(ownedChars
.get(), chars
, length
);
6082 if (!cx
->runtime()->canUseParallelParsing() || !js::CanUseExtraThreads()) {
6083 JS_ReportErrorASCII(cx
, "cannot compile code on helper thread");
6087 JS::SourceText
<char16_t
> srcBuf
;
6088 if (!srcBuf
.init(cx
, std::move(ownedChars
), length
)) {
6092 OffThreadJob
* job
= NewOffThreadJob(cx
, OffThreadJob::Kind::CompileScript
,
6093 options
, std::move(srcBuf
));
6098 if (!job
->dispatch()) {
6099 ReportOutOfMemory(cx
);
6100 DeleteOffThreadJob(cx
, job
);
6104 args
.rval().setInt32(job
->id
);
6108 static bool FinishOffThreadStencil(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6109 CallArgs args
= CallArgsFromVp(argc
, vp
);
6111 OffThreadJob
* job
= LookupOffThreadJobForArgs(cx
, args
, 0);
6116 job
->waitUntilDone();
6118 RefPtr
<JS::Stencil
> stencil
= job
->stealStencil(cx
);
6119 DeleteOffThreadJob(cx
, job
);
6123 RootedObject
stencilObj(cx
,
6124 js::StencilObject::create(cx
, std::move(stencil
)));
6129 args
.rval().setObject(*stencilObj
);
6133 static bool OffThreadCompileModuleToStencil(JSContext
* cx
, unsigned argc
,
6135 CallArgs args
= CallArgsFromVp(argc
, vp
);
6137 if (!args
.requireAtLeast(cx
, "offThreadCompileModuleToStencil", 1)) {
6140 if (!args
[0].isString()) {
6141 const char* typeName
= InformalValueTypeName(args
[0]);
6142 JS_ReportErrorASCII(cx
, "expected string to parse, got %s", typeName
);
6146 UniqueChars fileNameBytes
;
6147 CompileOptions
options(cx
);
6148 options
.setIntroductionType("js shell offThreadCompileModuleToStencil")
6149 .setFileAndLine("<string>", 1);
6151 if (args
.length() >= 2) {
6152 if (!args
[1].isObject()) {
6153 JS_ReportErrorASCII(cx
,
6154 "offThreadCompileModuleToStencil: The 2nd argument "
6155 "must be an object");
6159 // Offthread compilation requires that the debug metadata be set when the
6160 // script is collected from offthread, rather than when compiled.
6161 RootedObject
opts(cx
, &args
[1].toObject());
6162 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
6166 if (!ValidateModuleCompileOptions(cx
, options
)) {
6171 options
.setIsRunOnce(true).setSourceIsLazy(false);
6173 JSString
* scriptContents
= args
[0].toString();
6174 AutoStableStringChars
stableChars(cx
);
6175 if (!stableChars
.initTwoByte(cx
, scriptContents
)) {
6179 size_t length
= scriptContents
->length();
6180 const char16_t
* chars
= stableChars
.twoByteChars();
6182 // Make sure we own the string's chars, so that they are not freed before
6183 // the compilation is finished.
6184 UniqueTwoByteChars ownedChars
;
6185 if (stableChars
.maybeGiveOwnershipToCaller()) {
6186 ownedChars
.reset(const_cast<char16_t
*>(chars
));
6188 ownedChars
.reset(cx
->pod_malloc
<char16_t
>(length
));
6193 mozilla::PodCopy(ownedChars
.get(), chars
, length
);
6196 if (!cx
->runtime()->canUseParallelParsing() || !js::CanUseExtraThreads()) {
6197 JS_ReportErrorASCII(cx
, "cannot compile code on worker thread");
6201 JS::SourceText
<char16_t
> srcBuf
;
6202 if (!srcBuf
.init(cx
, std::move(ownedChars
), length
)) {
6206 OffThreadJob
* job
= NewOffThreadJob(cx
, OffThreadJob::Kind::CompileModule
,
6207 options
, std::move(srcBuf
));
6212 if (!job
->dispatch()) {
6213 ReportOutOfMemory(cx
);
6214 DeleteOffThreadJob(cx
, job
);
6218 args
.rval().setInt32(job
->id
);
6222 static bool OffThreadDecodeStencil(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6223 if (!CanUseExtraThreads()) {
6224 JS_ReportErrorASCII(cx
,
6225 "Can't use offThreadDecodeStencil with --no-threads");
6229 CallArgs args
= CallArgsFromVp(argc
, vp
);
6231 if (!args
.requireAtLeast(cx
, "offThreadDecodeStencil", 1)) {
6234 if (!args
[0].isObject() || !CacheEntry_isCacheEntry(&args
[0].toObject())) {
6235 const char* typeName
= InformalValueTypeName(args
[0]);
6236 JS_ReportErrorASCII(cx
, "expected cache entry, got %s", typeName
);
6239 RootedObject
cacheEntry(cx
, &args
[0].toObject());
6241 UniqueChars fileNameBytes
;
6242 CompileOptions
options(cx
);
6243 options
.setIntroductionType("js shell offThreadDecodeStencil")
6244 .setFileAndLine("<string>", 1);
6246 if (args
.length() >= 2) {
6247 if (!args
[1].isObject()) {
6248 JS_ReportErrorASCII(
6249 cx
, "offThreadDecodeStencil: The 2nd argument must be an object");
6253 RootedObject
opts(cx
, &args
[1].toObject());
6254 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
6259 // This option setting must override whatever the caller requested, and
6260 // this should match `Evaluate` that encodes the script.
6261 options
.setIsRunOnce(false);
6263 JS::TranscodeBuffer loadBuffer
;
6264 size_t loadLength
= 0;
6265 uint8_t* loadData
= nullptr;
6266 loadData
= CacheEntry_getBytecode(cx
, cacheEntry
, &loadLength
);
6270 if (!loadBuffer
.append(loadData
, loadLength
)) {
6271 JS_ReportOutOfMemory(cx
);
6275 if (!cx
->runtime()->canUseParallelParsing() || !js::CanUseExtraThreads()) {
6276 JS_ReportErrorASCII(cx
, "cannot compile code on worker thread");
6280 OffThreadJob
* job
= NewOffThreadJob(cx
, OffThreadJob::Kind::Decode
, options
,
6281 std::move(loadBuffer
));
6286 if (!job
->dispatch()) {
6287 ReportOutOfMemory(cx
);
6288 DeleteOffThreadJob(cx
, job
);
6292 args
.rval().setInt32(job
->id
);
6296 class AutoCStringVector
{
6297 Vector
<char*> argv_
;
6300 explicit AutoCStringVector(JSContext
* cx
) : argv_(cx
) {}
6301 ~AutoCStringVector() {
6302 for (size_t i
= 0; i
< argv_
.length(); i
++) {
6306 bool append(UniqueChars
&& arg
) {
6307 if (!argv_
.append(arg
.get())) {
6311 // Now owned by this vector.
6312 (void)arg
.release();
6315 char* const* get() const { return argv_
.begin(); }
6316 size_t length() const { return argv_
.length(); }
6317 char* operator[](size_t i
) const { return argv_
[i
]; }
6318 void replace(size_t i
, UniqueChars arg
) {
6320 argv_
[i
] = arg
.release();
6325 static bool EscapeForShell(JSContext
* cx
, AutoCStringVector
& argv
) {
6326 // Windows will break arguments in argv by various spaces, so we wrap each
6327 // argument in quotes and escape quotes within. Even with quotes, \ will be
6328 // treated like an escape character, so inflate each \ to \\.
6330 for (size_t i
= 0; i
< argv
.length(); i
++) {
6335 size_t newLen
= 3; // quotes before and after and null-terminator
6336 for (char* p
= argv
[i
]; *p
; p
++) {
6338 if (*p
== '\"' || *p
== '\\') {
6343 auto escaped
= cx
->make_pod_array
<char>(newLen
);
6348 char* src
= argv
[i
];
6349 char* dst
= escaped
.get();
6352 if (*src
== '\"' || *src
== '\\') {
6359 MOZ_ASSERT(escaped
.get() + newLen
== dst
);
6361 argv
.replace(i
, std::move(escaped
));
6368 static bool ReadAll(int fd
, wasm::Bytes
* bytes
) {
6369 size_t lastLength
= bytes
->length();
6371 static const int ChunkSize
= 64 * 1024;
6372 if (!bytes
->growBy(ChunkSize
)) {
6378 readCount
= read(fd
, bytes
->begin() + lastLength
, ChunkSize
);
6379 if (readCount
>= 0) {
6382 if (errno
!= EINTR
) {
6387 if (readCount
< ChunkSize
) {
6388 bytes
->shrinkTo(lastLength
+ readCount
);
6389 if (readCount
== 0) {
6394 lastLength
= bytes
->length();
6398 static bool WriteAll(int fd
, const uint8_t* bytes
, size_t length
) {
6399 while (length
> 0) {
6400 int written
= write(fd
, bytes
, length
);
6402 if (errno
== EINTR
) {
6407 MOZ_ASSERT(unsigned(written
) <= length
);
6425 if (fds_
[0] != -1) {
6428 if (fds_
[1] != -1) {
6435 return !_pipe(fds_
, 4096, O_BINARY
);
6441 int reader() const {
6442 MOZ_ASSERT(fds_
[0] != -1);
6446 int writer() const {
6447 MOZ_ASSERT(fds_
[1] != -1);
6451 void closeReader() {
6452 MOZ_ASSERT(fds_
[0] != -1);
6457 void closeWriter() {
6458 MOZ_ASSERT(fds_
[1] != -1);
6466 char** shell::sArgv
;
6469 static const char sWasmCompileAndSerializeFlag
[] =
6470 "--wasm-compile-and-serialize";
6471 static Vector
<const char*, 5, js::SystemAllocPolicy
> sCompilerProcessFlags
;
6473 static bool CompileAndSerializeInSeparateProcess(JSContext
* cx
,
6474 const uint8_t* bytecode
,
6475 size_t bytecodeLength
,
6476 wasm::Bytes
* serialized
) {
6477 AutoPipe stdIn
, stdOut
;
6478 if (!stdIn
.init() || !stdOut
.init()) {
6482 AutoCStringVector
argv(cx
);
6484 UniqueChars argv0
= DuplicateString(cx
, sArgv
[0]);
6485 if (!argv0
|| !argv
.append(std::move(argv0
))) {
6489 // Put compiler flags first since they must precede the non-option
6490 // file-descriptor args (passed on Windows, below).
6491 for (unsigned i
= 0; i
< sCompilerProcessFlags
.length(); i
++) {
6492 UniqueChars flags
= DuplicateString(cx
, sCompilerProcessFlags
[i
]);
6493 if (!flags
|| !argv
.append(std::move(flags
))) {
6500 arg
= DuplicateString(sWasmCompileAndSerializeFlag
);
6501 if (!arg
|| !argv
.append(std::move(arg
))) {
6506 // The spawned process will have all the stdIn/stdOut file handles open, but
6507 // without the power of fork, we need some other way to communicate the
6508 // integer fd values so we encode them in argv and WasmCompileAndSerialize()
6509 // has a matching #ifdef XP_WIN to parse them out. Communicate both ends of
6510 // both pipes so the child process can closed the unused ends.
6512 arg
= JS_smprintf("%d", stdIn
.reader());
6513 if (!arg
|| !argv
.append(std::move(arg
))) {
6517 arg
= JS_smprintf("%d", stdIn
.writer());
6518 if (!arg
|| !argv
.append(std::move(arg
))) {
6522 arg
= JS_smprintf("%d", stdOut
.reader());
6523 if (!arg
|| !argv
.append(std::move(arg
))) {
6527 arg
= JS_smprintf("%d", stdOut
.writer());
6528 if (!arg
|| !argv
.append(std::move(arg
))) {
6533 // Required by both _spawnv and exec.
6534 if (!argv
.append(nullptr)) {
6539 if (!EscapeForShell(cx
, argv
)) {
6543 int childPid
= _spawnv(P_NOWAIT
, sArgv
[0], argv
.get());
6544 if (childPid
== -1) {
6548 pid_t childPid
= fork();
6553 // In the child process. Redirect stdin/stdout to the respective ends of
6554 // the pipes. Closing stdIn.writer() is necessary for stdin to hit EOF.
6555 // This case statement must not return before exec() takes over. Rather,
6556 // exit(-1) is used to return failure to the parent process.
6557 if (dup2(stdIn
.reader(), STDIN_FILENO
) == -1) {
6560 if (dup2(stdOut
.writer(), STDOUT_FILENO
) == -1) {
6563 close(stdIn
.reader());
6564 close(stdIn
.writer());
6565 close(stdOut
.reader());
6566 close(stdOut
.writer());
6567 execv(sArgv
[0], argv
.get());
6572 // In the parent process. Closing stdOut.writer() is necessary for
6573 // stdOut.reader() below to hit EOF.
6574 stdIn
.closeReader();
6575 stdOut
.closeWriter();
6577 if (!WriteAll(stdIn
.writer(), bytecode
, bytecodeLength
)) {
6581 stdIn
.closeWriter();
6583 if (!ReadAll(stdOut
.reader(), serialized
)) {
6587 stdOut
.closeReader();
6591 if (_cwait(&status
, childPid
, WAIT_CHILD
) == -1) {
6596 if (waitpid(childPid
, &status
, 0) >= 0) {
6599 if (errno
!= EINTR
) {
6608 static bool WasmCompileAndSerialize(JSContext
* cx
) {
6609 MOZ_ASSERT(wasm::CodeCachingAvailable(cx
));
6612 // See CompileAndSerializeInSeparateProcess for why we've had to smuggle
6613 // these fd values through argv. Closing the writing ends is necessary for
6614 // the reading ends to hit EOF.
6616 for (; flagIndex
< sArgc
; flagIndex
++) {
6617 if (!strcmp(sArgv
[flagIndex
], sWasmCompileAndSerializeFlag
)) {
6621 MOZ_RELEASE_ASSERT(flagIndex
< sArgc
);
6623 int fdsIndex
= flagIndex
+ 1;
6624 MOZ_RELEASE_ASSERT(fdsIndex
+ 4 == sArgc
);
6626 int stdInReader
= atoi(sArgv
[fdsIndex
+ 0]);
6627 int stdInWriter
= atoi(sArgv
[fdsIndex
+ 1]);
6628 int stdOutReader
= atoi(sArgv
[fdsIndex
+ 2]);
6629 int stdOutWriter
= atoi(sArgv
[fdsIndex
+ 3]);
6631 int stdIn
= stdInReader
;
6633 close(stdOutReader
);
6634 int stdOut
= stdOutWriter
;
6636 int stdIn
= STDIN_FILENO
;
6637 int stdOut
= STDOUT_FILENO
;
6640 wasm::MutableBytes bytecode
= js_new
<wasm::ShareableBytes
>();
6641 if (!ReadAll(stdIn
, &bytecode
->bytes
)) {
6645 wasm::Bytes serialized
;
6646 if (!wasm::CompileAndSerialize(cx
, *bytecode
, &serialized
)) {
6650 if (!WriteAll(stdOut
, serialized
.begin(), serialized
.length())) {
6657 static bool WasmCompileInSeparateProcess(JSContext
* cx
, unsigned argc
,
6659 if (!wasm::CodeCachingAvailable(cx
)) {
6660 JS_ReportErrorASCII(cx
, "WebAssembly caching not supported");
6664 CallArgs args
= CallArgsFromVp(argc
, vp
);
6665 if (!args
.requireAtLeast(cx
, "wasmCompileInSeparateProcess", 1)) {
6669 SharedMem
<uint8_t*> bytecode
;
6671 if (!args
[0].isObject() ||
6672 !IsBufferSource(&args
[0].toObject(), &bytecode
, &numBytes
)) {
6673 RootedObject
callee(cx
, &args
.callee());
6674 ReportUsageErrorASCII(cx
, callee
, "Argument must be a buffer source");
6678 wasm::Bytes serialized
;
6679 if (!CompileAndSerializeInSeparateProcess(cx
, bytecode
.unwrap(), numBytes
,
6681 if (!cx
->isExceptionPending()) {
6682 JS_ReportErrorASCII(cx
, "creating and executing child process");
6687 RootedObject
module(cx
);
6688 if (!wasm::DeserializeModule(cx
, serialized
, &module
)) {
6692 args
.rval().setObject(*module
);
6697 static bool DecompileFunction(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6698 CallArgs args
= CallArgsFromVp(argc
, vp
);
6699 if (args
.length() < 1 || !args
[0].isObject() ||
6700 !args
[0].toObject().is
<JSFunction
>()) {
6701 args
.rval().setUndefined();
6704 RootedFunction
fun(cx
, &args
[0].toObject().as
<JSFunction
>());
6705 JSString
* result
= JS_DecompileFunction(cx
, fun
);
6709 args
.rval().setString(result
);
6713 static bool DecompileThisScript(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6714 CallArgs args
= CallArgsFromVp(argc
, vp
);
6716 NonBuiltinScriptFrameIter
iter(cx
);
6718 args
.rval().setString(cx
->runtime()->emptyString
);
6723 JSAutoRealm
ar(cx
, iter
.script());
6725 RootedScript
script(cx
, iter
.script());
6726 JSString
* result
= JS_DecompileScript(cx
, script
);
6731 args
.rval().setString(result
);
6734 return JS_WrapValue(cx
, args
.rval());
6737 static bool ValueToSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6738 CallArgs args
= CallArgsFromVp(argc
, vp
);
6740 JSString
* str
= ValueToSource(cx
, args
.get(0));
6745 args
.rval().setString(str
);
6749 static bool ThisFilename(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6750 CallArgs args
= CallArgsFromVp(argc
, vp
);
6752 JS::AutoFilename filename
;
6753 if (!DescribeScriptedCaller(cx
, &filename
) || !filename
.get()) {
6754 args
.rval().setString(cx
->runtime()->emptyString
);
6758 JSString
* str
= NewStringCopyUTF8(cx
, filename
.get());
6763 args
.rval().setString(str
);
6767 static bool WrapWithProto(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6768 CallArgs args
= CallArgsFromVp(argc
, vp
);
6769 Value obj
= args
.get(0);
6770 Value proto
= args
.get(1);
6771 if (!obj
.isObject() || !proto
.isObjectOrNull()) {
6772 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
6773 JSSMSG_INVALID_ARGS
, "wrapWithProto");
6777 // Disallow constructing (deeply) nested wrapper chains, to avoid running
6778 // out of stack space in isCallable/isConstructor. See bug 1126105.
6779 if (IsWrapper(&obj
.toObject())) {
6780 JS_ReportErrorASCII(cx
, "wrapWithProto cannot wrap a wrapper");
6784 WrapperOptions
options(cx
);
6785 options
.setProto(proto
.toObjectOrNull());
6786 JSObject
* wrapped
= Wrapper::New(cx
, &obj
.toObject(),
6787 &Wrapper::singletonWithPrototype
, options
);
6792 args
.rval().setObject(*wrapped
);
6796 static bool NewGlobal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6797 CallArgs args
= CallArgsFromVp(argc
, vp
);
6798 RootedObject
callee(cx
, &args
.callee());
6800 JS::RealmOptions options
;
6801 JS::RealmCreationOptions
& creationOptions
= options
.creationOptions();
6802 JS::RealmBehaviors
& behaviors
= options
.behaviors();
6803 ShellGlobalKind kind
= ShellGlobalKind::WindowProxy
;
6804 bool immutablePrototype
= true;
6806 SetStandardRealmOptions(options
);
6808 // Default to creating the global in the current compartment unless
6809 // --more-compartments is used.
6810 if (defaultToSameCompartment
) {
6811 creationOptions
.setExistingCompartment(cx
->global());
6813 creationOptions
.setNewCompartmentAndZone();
6816 JS::AutoHoldPrincipals
principals(cx
);
6818 if (args
.length() == 1 && args
[0].isObject()) {
6819 RootedObject
opts(cx
, &args
[0].toObject());
6822 if (!JS_GetProperty(cx
, opts
, "invisibleToDebugger", &v
)) {
6825 if (v
.isBoolean()) {
6826 creationOptions
.setInvisibleToDebugger(v
.toBoolean());
6829 if (!JS_GetProperty(cx
, opts
, "sameZoneAs", &v
)) {
6833 creationOptions
.setNewCompartmentInExistingZone(
6834 UncheckedUnwrap(&v
.toObject()));
6837 if (!JS_GetProperty(cx
, opts
, "sameCompartmentAs", &v
)) {
6841 creationOptions
.setExistingCompartment(UncheckedUnwrap(&v
.toObject()));
6844 if (!JS_GetProperty(cx
, opts
, "newCompartment", &v
)) {
6847 if (v
.isBoolean()) {
6848 if (v
.toBoolean()) {
6849 creationOptions
.setNewCompartmentAndZone();
6851 creationOptions
.setExistingCompartment(cx
->global());
6855 if (!JS_GetProperty(cx
, opts
, "discardSource", &v
)) {
6858 if (v
.isBoolean()) {
6859 behaviors
.setDiscardSource(v
.toBoolean());
6862 if (!JS_GetProperty(cx
, opts
, "useWindowProxy", &v
)) {
6865 if (v
.isBoolean()) {
6866 kind
= v
.toBoolean() ? ShellGlobalKind::WindowProxy
6867 : ShellGlobalKind::GlobalObject
;
6870 if (!JS_GetProperty(cx
, opts
, "immutablePrototype", &v
)) {
6873 if (v
.isBoolean()) {
6874 immutablePrototype
= v
.toBoolean();
6877 if (!JS_GetProperty(cx
, opts
, "systemPrincipal", &v
)) {
6880 if (v
.isBoolean()) {
6881 principals
.reset(&ShellPrincipals::fullyTrusted
);
6884 if (!JS_GetProperty(cx
, opts
, "principal", &v
)) {
6887 if (!v
.isUndefined()) {
6889 if (!ToUint32(cx
, v
, &bits
)) {
6892 JSPrincipals
* newPrincipals
= cx
->new_
<ShellPrincipals
>(bits
);
6893 if (!newPrincipals
) {
6896 principals
.reset(newPrincipals
);
6899 if (!JS_GetProperty(cx
, opts
, "enableCoopAndCoep", &v
)) {
6902 if (v
.isBoolean()) {
6903 creationOptions
.setCoopAndCoepEnabled(v
.toBoolean());
6906 if (!JS_GetProperty(cx
, opts
, "freezeBuiltins", &v
)) {
6909 if (v
.isBoolean()) {
6910 creationOptions
.setFreezeBuiltins(v
.toBoolean());
6913 // On the web, the SharedArrayBuffer constructor is not installed as a
6914 // global property in pages that aren't isolated in a separate process (and
6915 // thus can't allow the structured cloning of shared memory). Specify false
6916 // for this option to reproduce this behavior.
6917 if (!JS_GetProperty(cx
, opts
, "defineSharedArrayBufferConstructor", &v
)) {
6920 if (v
.isBoolean()) {
6921 creationOptions
.setDefineSharedArrayBufferConstructor(v
.toBoolean());
6924 if (!JS_GetProperty(cx
, opts
, "forceUTC", &v
)) {
6927 if (v
.isBoolean()) {
6928 creationOptions
.setForceUTC(v
.toBoolean());
6931 if (!JS_GetProperty(cx
, opts
, "alwaysUseFdlibm", &v
)) {
6934 if (v
.isBoolean()) {
6935 creationOptions
.setAlwaysUseFdlibm(v
.toBoolean());
6938 if (!JS_GetProperty(cx
, opts
, "locale", &v
)) {
6942 RootedString
str(cx
, v
.toString());
6943 UniqueChars locale
= StringToLocale(cx
, callee
, str
);
6947 creationOptions
.setLocaleCopyZ(locale
.get());
6951 if (!CheckRealmOptions(cx
, options
, principals
.get())) {
6955 RootedObject
global(cx
, NewGlobalObject(cx
, options
, principals
.get(), kind
,
6956 immutablePrototype
));
6961 RootedObject
wrapped(cx
, ToWindowProxyIfWindow(global
));
6962 if (!JS_WrapObject(cx
, &wrapped
)) {
6966 args
.rval().setObject(*wrapped
);
6970 static bool NukeAllCCWs(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6971 CallArgs args
= CallArgsFromVp(argc
, vp
);
6973 if (args
.length() != 0) {
6974 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
6975 JSSMSG_INVALID_ARGS
, "nukeAllCCWs");
6979 NukeCrossCompartmentWrappers(cx
, AllCompartments(), cx
->realm(),
6980 NukeWindowReferences
, NukeAllReferences
);
6981 args
.rval().setUndefined();
6985 static bool RecomputeWrappers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6986 CallArgs args
= CallArgsFromVp(argc
, vp
);
6988 if (args
.length() > 2) {
6989 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
6990 JSSMSG_INVALID_ARGS
, "recomputeWrappers");
6994 JS::Compartment
* sourceComp
= nullptr;
6995 if (args
.get(0).isObject()) {
6996 sourceComp
= JS::GetCompartment(UncheckedUnwrap(&args
[0].toObject()));
6999 JS::Compartment
* targetComp
= nullptr;
7000 if (args
.get(1).isObject()) {
7001 targetComp
= JS::GetCompartment(UncheckedUnwrap(&args
[1].toObject()));
7004 struct SingleOrAllCompartments final
: public CompartmentFilter
{
7005 JS::Compartment
* comp
;
7006 explicit SingleOrAllCompartments(JS::Compartment
* c
) : comp(c
) {}
7007 virtual bool match(JS::Compartment
* c
) const override
{
7008 return !comp
|| comp
== c
;
7012 if (!js::RecomputeWrappers(cx
, SingleOrAllCompartments(sourceComp
),
7013 SingleOrAllCompartments(targetComp
))) {
7017 args
.rval().setUndefined();
7021 static bool DumpObjectWrappers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7022 CallArgs args
= CallArgsFromVp(argc
, vp
);
7024 bool printedHeader
= false;
7025 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
7026 bool printedZoneInfo
= false;
7027 for (CompartmentsInZoneIter
comp(zone
); !comp
.done(); comp
.next()) {
7028 bool printedCompartmentInfo
= false;
7029 for (Compartment::ObjectWrapperEnum
e(comp
); !e
.empty(); e
.popFront()) {
7030 JSObject
* wrapper
= e
.front().value().unbarrieredGet();
7031 JSObject
* wrapped
= e
.front().key();
7032 if (!printedHeader
) {
7033 fprintf(stderr
, "Cross-compartment object wrappers:\n");
7034 printedHeader
= true;
7036 if (!printedZoneInfo
) {
7037 fprintf(stderr
, " Zone %p:\n", zone
.get());
7038 printedZoneInfo
= true;
7040 if (!printedCompartmentInfo
) {
7041 fprintf(stderr
, " Compartment %p:\n", comp
.get());
7042 printedCompartmentInfo
= true;
7045 " Object wrapper %p -> %p in zone %p compartment %p\n",
7046 wrapper
, wrapped
, wrapped
->zone(), wrapped
->compartment());
7051 if (!printedHeader
) {
7052 fprintf(stderr
, "No cross-compartment object wrappers.\n");
7055 args
.rval().setUndefined();
7059 static bool GetMaxArgs(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7060 CallArgs args
= CallArgsFromVp(argc
, vp
);
7061 args
.rval().setInt32(ARGS_LENGTH_MAX
);
7065 static bool IsHTMLDDA_Call(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7066 CallArgs args
= CallArgsFromVp(argc
, vp
);
7068 // These are the required conditions under which this object may be called
7069 // by test262 tests, and the required behavior under those conditions.
7070 if (args
.length() == 0 ||
7071 (args
[0].isString() && args
[0].toString()->length() == 0)) {
7072 args
.rval().setNull();
7076 JS_ReportErrorASCII(
7077 cx
, "IsHTMLDDA object is being called in an impermissible manner");
7081 static bool CreateIsHTMLDDA(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7082 CallArgs args
= CallArgsFromVp(argc
, vp
);
7084 static const JSClassOps classOps
= {
7085 nullptr, // addProperty
7086 nullptr, // delProperty
7087 nullptr, // enumerate
7088 nullptr, // newEnumerate
7090 nullptr, // mayResolve
7091 nullptr, // finalize
7092 IsHTMLDDA_Call
, // call
7093 nullptr, // construct
7097 static const JSClass cls
= {
7099 JSCLASS_EMULATES_UNDEFINED
,
7103 JSObject
* obj
= JS_NewObject(cx
, &cls
);
7107 args
.rval().setObject(*obj
);
7111 static bool GetSelfHostedValue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7112 CallArgs args
= CallArgsFromVp(argc
, vp
);
7114 if (args
.length() != 1 || !args
[0].isString()) {
7115 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
7116 JSSMSG_INVALID_ARGS
, "getSelfHostedValue");
7119 Rooted
<JSAtom
*> srcAtom(cx
, ToAtom
<CanGC
>(cx
, args
[0]));
7123 Rooted
<PropertyName
*> srcName(cx
, srcAtom
->asPropertyName());
7124 return GlobalObject::getIntrinsicValue(cx
, cx
->global(), srcName
,
7128 class ShellSourceHook
: public SourceHook
{
7129 // The function we should call to lazily retrieve source code.
7130 PersistentRootedFunction fun
;
7133 ShellSourceHook(JSContext
* cx
, JSFunction
& fun
) : fun(cx
, &fun
) {}
7135 bool load(JSContext
* cx
, const char* filename
, char16_t
** twoByteSource
,
7136 char** utf8Source
, size_t* length
) override
{
7137 MOZ_ASSERT((twoByteSource
!= nullptr) != (utf8Source
!= nullptr),
7138 "must be called requesting only one of UTF-8 or UTF-16 source");
7140 RootedString
str(cx
);
7142 str
= NewStringCopyUTF8(cx
, filename
);
7147 str
= JS_GetEmptyString(cx
);
7149 RootedValue
filenameValue(cx
, StringValue(str
));
7151 RootedValue
result(cx
);
7152 if (!Call(cx
, UndefinedHandleValue
, fun
, HandleValueArray(filenameValue
),
7157 str
= JS::ToString(cx
, result
);
7162 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
7167 if (twoByteSource
) {
7168 *length
= JS_GetStringLength(linear
);
7170 *twoByteSource
= cx
->pod_malloc
<char16_t
>(*length
);
7171 if (!*twoByteSource
) {
7175 CopyChars(*twoByteSource
, *linear
);
7177 MOZ_ASSERT(utf8Source
!= nullptr);
7179 *length
= JS::GetDeflatedUTF8StringLength(linear
);
7181 *utf8Source
= cx
->pod_malloc
<char>(*length
);
7186 mozilla::DebugOnly
<size_t> dstLen
= JS::DeflateStringToUTF8Buffer(
7187 linear
, mozilla::Span(*utf8Source
, *length
));
7188 MOZ_ASSERT(dstLen
== *length
);
7195 static bool WithSourceHook(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7196 CallArgs args
= CallArgsFromVp(argc
, vp
);
7197 RootedObject
callee(cx
, &args
.callee());
7199 if (args
.length() != 2) {
7200 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments.");
7204 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>() ||
7205 !args
[1].isObject() || !args
[1].toObject().is
<JSFunction
>()) {
7206 ReportUsageErrorASCII(cx
, callee
,
7207 "First and second arguments must be functions.");
7211 mozilla::UniquePtr
<ShellSourceHook
> hook
=
7212 mozilla::MakeUnique
<ShellSourceHook
>(cx
,
7213 args
[0].toObject().as
<JSFunction
>());
7218 mozilla::UniquePtr
<SourceHook
> savedHook
= js::ForgetSourceHook(cx
);
7219 js::SetSourceHook(cx
, std::move(hook
));
7221 RootedObject
fun(cx
, &args
[1].toObject());
7222 bool result
= Call(cx
, UndefinedHandleValue
, fun
,
7223 JS::HandleValueArray::empty(), args
.rval());
7224 js::SetSourceHook(cx
, std::move(savedHook
));
7228 static void PrintProfilerEvents_Callback(const char* msg
, const char* details
) {
7229 fprintf(stderr
, "PROFILER EVENT: %s %s\n", msg
, details
);
7232 static bool PrintProfilerEvents(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7233 CallArgs args
= CallArgsFromVp(argc
, vp
);
7234 if (cx
->runtime()->geckoProfiler().enabled()) {
7235 js::RegisterContextProfilingEventMarker(cx
, &PrintProfilerEvents_Callback
);
7237 args
.rval().setUndefined();
7241 #ifdef SINGLESTEP_PROFILING
7242 static void SingleStepCallback(void* arg
, jit::Simulator
* sim
, void* pc
) {
7243 JSContext
* cx
= reinterpret_cast<JSContext
*>(arg
);
7245 // If profiling is not enabled, don't do anything.
7246 if (!cx
->runtime()->geckoProfiler().enabled()) {
7250 JS::ProfilingFrameIterator::RegisterState state
;
7252 # if defined(JS_SIMULATOR_ARM)
7253 state
.sp
= (void*)sim
->get_register(jit::Simulator::sp
);
7254 state
.lr
= (void*)sim
->get_register(jit::Simulator::lr
);
7255 state
.fp
= (void*)sim
->get_register(jit::Simulator::fp
);
7256 state
.tempFP
= (void*)sim
->get_register(jit::Simulator::r7
);
7257 # elif defined(JS_SIMULATOR_MIPS64) || defined(JS_SIMULATOR_MIPS32)
7258 state
.sp
= (void*)sim
->getRegister(jit::Simulator::sp
);
7259 state
.lr
= (void*)sim
->getRegister(jit::Simulator::ra
);
7260 state
.fp
= (void*)sim
->getRegister(jit::Simulator::fp
);
7261 # elif defined(JS_SIMULATOR_LOONG64)
7262 state
.sp
= (void*)sim
->getRegister(jit::Simulator::sp
);
7263 state
.lr
= (void*)sim
->getRegister(jit::Simulator::ra
);
7264 state
.fp
= (void*)sim
->getRegister(jit::Simulator::fp
);
7266 # error "NYI: Single-step profiling support"
7269 mozilla::DebugOnly
<void*> lastStackAddress
= nullptr;
7271 uint32_t frameNo
= 0;
7272 AutoEnterOOMUnsafeRegion oomUnsafe
;
7273 for (JS::ProfilingFrameIterator
i(cx
, state
); !i
.done(); ++i
) {
7274 MOZ_ASSERT(i
.stackAddress() != nullptr);
7275 MOZ_ASSERT(lastStackAddress
<= i
.stackAddress());
7276 lastStackAddress
= i
.stackAddress();
7277 JS::ProfilingFrameIterator::Frame frames
[16];
7278 uint32_t nframes
= i
.extractStack(frames
, 0, 16);
7279 for (uint32_t i
= 0; i
< nframes
; i
++) {
7280 // Assert endStackAddress never exceeds sp (bug 1782188).
7281 MOZ_ASSERT(frames
[i
].endStackAddress
>= state
.sp
);
7283 if (!stack
.append(",", 1)) {
7284 oomUnsafe
.crash("stack.append");
7287 if (!stack
.append(frames
[i
].label
, strlen(frames
[i
].label
))) {
7288 oomUnsafe
.crash("stack.append");
7294 ShellContext
* sc
= GetShellContext(cx
);
7296 // Only append the stack if it differs from the last stack.
7297 if (sc
->stacks
.empty() || sc
->stacks
.back().length() != stack
.length() ||
7298 !ArrayEqual(sc
->stacks
.back().begin(), stack
.begin(), stack
.length())) {
7299 if (!sc
->stacks
.append(std::move(stack
))) {
7300 oomUnsafe
.crash("stacks.append");
7306 static bool EnableSingleStepProfiling(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7307 #ifdef SINGLESTEP_PROFILING
7308 CallArgs args
= CallArgsFromVp(argc
, vp
);
7310 jit::Simulator
* sim
= cx
->simulator();
7311 sim
->enable_single_stepping(SingleStepCallback
, cx
);
7313 args
.rval().setUndefined();
7316 JS_ReportErrorASCII(cx
, "single-step profiling not enabled on this platform");
7321 static bool DisableSingleStepProfiling(JSContext
* cx
, unsigned argc
,
7323 #ifdef SINGLESTEP_PROFILING
7324 CallArgs args
= CallArgsFromVp(argc
, vp
);
7326 jit::Simulator
* sim
= cx
->simulator();
7327 sim
->disable_single_stepping();
7329 ShellContext
* sc
= GetShellContext(cx
);
7331 RootedValueVector
elems(cx
);
7332 for (size_t i
= 0; i
< sc
->stacks
.length(); i
++) {
7334 JS_NewUCStringCopyN(cx
, sc
->stacks
[i
].begin(), sc
->stacks
[i
].length());
7338 if (!elems
.append(StringValue(stack
))) {
7343 JSObject
* array
= JS::NewArrayObject(cx
, elems
);
7349 args
.rval().setObject(*array
);
7352 JS_ReportErrorASCII(cx
, "single-step profiling not enabled on this platform");
7357 static bool IsLatin1(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7358 CallArgs args
= CallArgsFromVp(argc
, vp
);
7360 args
.get(0).isString() && args
[0].toString()->hasLatin1Chars();
7361 args
.rval().setBoolean(isLatin1
);
7365 static bool EnableGeckoProfiling(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7366 CallArgs args
= CallArgsFromVp(argc
, vp
);
7368 if (!EnsureGeckoProfilingStackInstalled(cx
, GetShellContext(cx
))) {
7372 cx
->runtime()->geckoProfiler().enableSlowAssertions(false);
7373 cx
->runtime()->geckoProfiler().enable(true);
7375 args
.rval().setUndefined();
7379 static bool EnableGeckoProfilingWithSlowAssertions(JSContext
* cx
, unsigned argc
,
7381 CallArgs args
= CallArgsFromVp(argc
, vp
);
7382 args
.rval().setUndefined();
7384 if (cx
->runtime()->geckoProfiler().enabled()) {
7385 // If profiling already enabled with slow assertions disabled,
7387 if (cx
->runtime()->geckoProfiler().slowAssertionsEnabled()) {
7391 // Slow assertions are off. Disable profiling before re-enabling
7392 // with slow assertions on.
7393 cx
->runtime()->geckoProfiler().enable(false);
7396 if (!EnsureGeckoProfilingStackInstalled(cx
, GetShellContext(cx
))) {
7400 cx
->runtime()->geckoProfiler().enableSlowAssertions(true);
7401 cx
->runtime()->geckoProfiler().enable(true);
7406 static bool DisableGeckoProfiling(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7407 CallArgs args
= CallArgsFromVp(argc
, vp
);
7408 args
.rval().setUndefined();
7410 if (!cx
->runtime()->geckoProfiler().enabled()) {
7414 cx
->runtime()->geckoProfiler().enable(false);
7418 // Global mailbox that is used to communicate a shareable object value from one
7419 // worker to another.
7421 // These object types are shareable:
7423 // - SharedArrayBuffer
7424 // - WasmMemoryObject (when constructed with shared:true)
7425 // - WasmModuleObject
7427 // For the SharedArrayBuffer and WasmMemoryObject we transmit the underlying
7428 // SharedArrayRawBuffer ("SARB"). For the WasmModuleObject we transmit the
7429 // underlying JS::WasmModule. The transmitted types are refcounted. When they
7430 // are in the mailbox their reference counts are at least 1, accounting for the
7431 // reference from the mailbox.
7433 // The lock guards the mailbox variable and prevents a race where two workers
7434 // try to set the mailbox at the same time to replace an object that is only
7435 // referenced from the mailbox: the workers will both decrement the reference
7436 // count on the old object, and one of those decrements will be on a garbage
7437 // object. We could implement this with atomics and a CAS loop but it's not
7438 // worth the bother.
7440 // Note that if a thread reads the mailbox repeatedly it will get distinct
7441 // objects on each read. The alternatives are to cache created objects locally,
7442 // but this retains storage we don't need to retain, or to somehow clear the
7443 // mailbox locally, but this creates a coordination headache. Buyer beware.
7445 enum class MailboxTag
{
7453 struct SharedObjectMailbox
{
7456 SharedArrayRawBuffer
* buffer
;
7458 bool isHugeMemory
; // For a WasmMemory tag, otherwise false
7459 bool isGrowable
; // For GrowableSharedArrayBuffer, otherwise false
7461 JS::WasmModule
* module
;
7464 Value() : number(0.0) {}
7467 MailboxTag tag
= MailboxTag::Empty
;
7471 typedef ExclusiveData
<SharedObjectMailbox
> SOMailbox
;
7473 // Never null after successful initialization.
7474 static SOMailbox
* sharedObjectMailbox
;
7476 static bool InitSharedObjectMailbox() {
7477 sharedObjectMailbox
= js_new
<SOMailbox
>(mutexid::ShellObjectMailbox
);
7478 return sharedObjectMailbox
!= nullptr;
7481 static void DestructSharedObjectMailbox() {
7482 // All workers need to have terminated at this point.
7485 auto mbx
= sharedObjectMailbox
->lock();
7487 case MailboxTag::Empty
:
7488 case MailboxTag::Number
:
7490 case MailboxTag::SharedArrayBuffer
:
7491 case MailboxTag::WasmMemory
:
7492 mbx
->val
.sarb
.buffer
->dropReference();
7494 case MailboxTag::WasmModule
:
7495 mbx
->val
.module
->Release();
7502 js_delete(sharedObjectMailbox
);
7503 sharedObjectMailbox
= nullptr;
7506 static bool GetSharedObject(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7507 CallArgs args
= CallArgsFromVp(argc
, vp
);
7508 RootedObject
newObj(cx
);
7511 auto mbx
= sharedObjectMailbox
->lock();
7513 case MailboxTag::Empty
: {
7516 case MailboxTag::Number
: {
7517 args
.rval().setNumber(mbx
->val
.number
);
7520 case MailboxTag::SharedArrayBuffer
:
7521 case MailboxTag::WasmMemory
: {
7522 // Flag was set in the sender; ensure it is set in the receiver.
7524 cx
->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
7526 // The protocol for creating a SAB requires the refcount to be
7527 // incremented prior to the SAB creation.
7529 SharedArrayRawBuffer
* buf
= mbx
->val
.sarb
.buffer
;
7530 size_t length
= mbx
->val
.sarb
.length
;
7531 if (!buf
->addReference()) {
7532 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
7533 JSMSG_SC_SAB_REFCNT_OFLO
);
7537 // If the allocation fails we must decrement the refcount before
7540 Rooted
<ArrayBufferObjectMaybeShared
*> maybesab(cx
);
7541 if (!mbx
->val
.sarb
.isGrowable
) {
7542 maybesab
= SharedArrayBufferObject::New(cx
, buf
, length
);
7544 maybesab
= SharedArrayBufferObject::NewGrowable(cx
, buf
, length
);
7547 buf
->dropReference();
7551 // At this point the SAB was created successfully and it owns the
7552 // refcount-increase on the buffer that we performed above. So even
7553 // if we fail to allocate along any path below we must not decrement
7554 // the refcount; the garbage collector must be allowed to handle
7555 // that via finalization of the orphaned SAB object.
7557 if (mbx
->tag
== MailboxTag::SharedArrayBuffer
) {
7560 if (!GlobalObject::ensureConstructor(cx
, cx
->global(),
7561 JSProto_WebAssembly
)) {
7564 RootedObject
proto(cx
,
7565 &cx
->global()->getPrototype(JSProto_WasmMemory
));
7566 newObj
= WasmMemoryObject::create(cx
, maybesab
,
7567 mbx
->val
.sarb
.isHugeMemory
, proto
);
7568 MOZ_ASSERT_IF(newObj
, newObj
->as
<WasmMemoryObject
>().isShared());
7576 case MailboxTag::WasmModule
: {
7577 // Flag was set in the sender; ensure it is set in the receiver.
7579 cx
->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
7581 if (!GlobalObject::ensureConstructor(cx
, cx
->global(),
7582 JSProto_WebAssembly
)) {
7586 // WasmModuleObject::create() increments the refcount on the module
7587 // and signals an error and returns null if that fails.
7588 newObj
= mbx
->val
.module
->createObject(cx
);
7600 args
.rval().setObjectOrNull(newObj
);
7604 static bool SetSharedObject(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7605 CallArgs args
= CallArgsFromVp(argc
, vp
);
7607 MailboxTag tag
= MailboxTag::Empty
;
7608 SharedObjectMailbox::Value value
;
7610 // Increase refcounts when we obtain the value to avoid operating on dead
7611 // storage during self-assignment.
7613 if (args
.get(0).isObject()) {
7614 RootedObject
obj(cx
, &args
[0].toObject());
7615 if (obj
->is
<SharedArrayBufferObject
>()) {
7616 Rooted
<SharedArrayBufferObject
*> sab(cx
,
7617 &obj
->as
<SharedArrayBufferObject
>());
7618 tag
= MailboxTag::SharedArrayBuffer
;
7619 value
.sarb
.buffer
= sab
->rawBufferObject();
7620 value
.sarb
.length
= sab
->byteLengthOrMaxByteLength();
7621 value
.sarb
.isHugeMemory
= false;
7622 value
.sarb
.isGrowable
= sab
->isGrowable();
7623 if (!value
.sarb
.buffer
->addReference()) {
7624 JS_ReportErrorASCII(cx
,
7625 "Reference count overflow on SharedArrayBuffer");
7628 } else if (obj
->is
<WasmMemoryObject
>()) {
7629 // Here we must transmit sab.byteLength() as the length; the SARB has its
7630 // own notion of the length which may be greater, and that's fine.
7631 if (obj
->as
<WasmMemoryObject
>().isShared()) {
7632 Rooted
<SharedArrayBufferObject
*> sab(
7633 cx
, &obj
->as
<WasmMemoryObject
>()
7635 .as
<SharedArrayBufferObject
>());
7636 MOZ_ASSERT(!sab
->isGrowable(), "unexpected growable shared buffer");
7637 tag
= MailboxTag::WasmMemory
;
7638 value
.sarb
.buffer
= sab
->rawBufferObject();
7639 value
.sarb
.length
= sab
->byteLength();
7640 value
.sarb
.isHugeMemory
= obj
->as
<WasmMemoryObject
>().isHuge();
7641 value
.sarb
.isGrowable
= false;
7642 if (!value
.sarb
.buffer
->addReference()) {
7643 JS_ReportErrorASCII(cx
,
7644 "Reference count overflow on SharedArrayBuffer");
7648 JS_ReportErrorASCII(cx
, "Invalid argument to SetSharedObject");
7651 } else if (JS::IsWasmModuleObject(obj
)) {
7652 tag
= MailboxTag::WasmModule
;
7653 value
.module
= JS::GetWasmModule(obj
).forget().take();
7655 JS_ReportErrorASCII(cx
, "Invalid argument to SetSharedObject");
7658 } else if (args
.get(0).isNumber()) {
7659 tag
= MailboxTag::Number
;
7660 value
.number
= args
.get(0).toNumber();
7662 } else if (args
.get(0).isNullOrUndefined()) {
7665 JS_ReportErrorASCII(cx
, "Invalid argument to SetSharedObject");
7670 auto mbx
= sharedObjectMailbox
->lock();
7673 case MailboxTag::Empty
:
7674 case MailboxTag::Number
:
7676 case MailboxTag::SharedArrayBuffer
:
7677 case MailboxTag::WasmMemory
:
7678 mbx
->val
.sarb
.buffer
->dropReference();
7680 case MailboxTag::WasmModule
:
7681 mbx
->val
.module
->Release();
7691 args
.rval().setUndefined();
7695 typedef Vector
<uint8_t, 0, SystemAllocPolicy
> Uint8Vector
;
7697 class StreamCacheEntry
: public AtomicRefCounted
<StreamCacheEntry
>,
7698 public JS::OptimizedEncodingListener
{
7699 typedef AtomicRefCounted
<StreamCacheEntry
> AtomicBase
;
7702 ExclusiveData
<Uint8Vector
> optimized_
;
7705 explicit StreamCacheEntry(Uint8Vector
&& original
)
7706 : bytes_(std::move(original
)),
7707 optimized_(mutexid::ShellStreamCacheEntryState
) {}
7709 // Implement JS::OptimizedEncodingListener:
7711 MozExternalRefCountType MOZ_XPCOM_ABI
AddRef() override
{
7712 AtomicBase::AddRef();
7715 MozExternalRefCountType MOZ_XPCOM_ABI
Release() override
{
7716 AtomicBase::Release();
7720 const Uint8Vector
& bytes() const { return bytes_
; }
7722 void storeOptimizedEncoding(const uint8_t* srcBytes
,
7723 size_t srcLength
) override
{
7724 MOZ_ASSERT(srcLength
> 0);
7726 // Tolerate races since a single StreamCacheEntry object can be used as
7727 // the source of multiple streaming compilations.
7728 auto dstBytes
= optimized_
.lock();
7729 if (dstBytes
->length() > 0) {
7733 if (!dstBytes
->resize(srcLength
)) {
7736 memcpy(dstBytes
->begin(), srcBytes
, srcLength
);
7739 bool hasOptimizedEncoding() const { return !optimized_
.lock()->empty(); }
7740 const Uint8Vector
& optimizedEncoding() const {
7741 return optimized_
.lock().get();
7745 typedef RefPtr
<StreamCacheEntry
> StreamCacheEntryPtr
;
7747 class StreamCacheEntryObject
: public NativeObject
{
7748 static const unsigned CACHE_ENTRY_SLOT
= 0;
7749 static const JSClassOps classOps_
;
7750 static const JSPropertySpec properties_
;
7752 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
7753 obj
->as
<StreamCacheEntryObject
>().cache().Release();
7756 static bool cachedGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7757 CallArgs args
= CallArgsFromVp(argc
, vp
);
7758 if (!args
.thisv().isObject() ||
7759 !args
.thisv().toObject().is
<StreamCacheEntryObject
>()) {
7763 StreamCacheEntryObject
& obj
=
7764 args
.thisv().toObject().as
<StreamCacheEntryObject
>();
7765 args
.rval().setBoolean(obj
.cache().hasOptimizedEncoding());
7768 static bool getBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7769 CallArgs args
= CallArgsFromVp(argc
, vp
);
7770 if (!args
.thisv().isObject() ||
7771 !args
.thisv().toObject().is
<StreamCacheEntryObject
>()) {
7776 args
.thisv().toObject().as
<StreamCacheEntryObject
>().cache().bytes();
7777 auto* buffer
= ArrayBufferObject::createZeroed(cx
, bytes
.length());
7782 memcpy(buffer
->dataPointer(), bytes
.begin(), bytes
.length());
7784 args
.rval().setObject(*buffer
);
7789 static const unsigned RESERVED_SLOTS
= 1;
7790 static const JSClass class_
;
7791 static const JSPropertySpec properties
[];
7793 static bool construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7794 CallArgs args
= CallArgsFromVp(argc
, vp
);
7795 if (!args
.requireAtLeast(cx
, "streamCacheEntry", 1)) {
7799 SharedMem
<uint8_t*> ptr
;
7801 if (!args
[0].isObject() ||
7802 !IsBufferSource(&args
[0].toObject(), &ptr
, &numBytes
)) {
7803 RootedObject
callee(cx
, &args
.callee());
7804 ReportUsageErrorASCII(cx
, callee
, "Argument must be an ArrayBuffer");
7809 if (!bytes
.resize(numBytes
)) {
7813 memcpy(bytes
.begin(), ptr
.unwrap(), numBytes
);
7815 RefPtr
<StreamCacheEntry
> cache
=
7816 cx
->new_
<StreamCacheEntry
>(std::move(bytes
));
7821 Rooted
<NativeObject
*> obj(
7822 cx
, NewObjectWithGivenProto
<StreamCacheEntryObject
>(cx
, nullptr));
7826 obj
->initReservedSlot(CACHE_ENTRY_SLOT
,
7827 PrivateValue(cache
.forget().take()));
7829 if (!JS_DefineProperty(cx
, obj
, "cached", cachedGetter
, nullptr, 0)) {
7832 if (!JS_DefineFunction(cx
, obj
, "getBuffer", getBuffer
, 0, 0)) {
7836 args
.rval().setObject(*obj
);
7840 StreamCacheEntry
& cache() const {
7841 return *(StreamCacheEntry
*)getReservedSlot(CACHE_ENTRY_SLOT
).toPrivate();
7845 const JSClassOps
StreamCacheEntryObject::classOps_
= {
7846 nullptr, // addProperty
7847 nullptr, // delProperty
7848 nullptr, // enumerate
7849 nullptr, // newEnumerate
7851 nullptr, // mayResolve
7852 StreamCacheEntryObject::finalize
, // finalize
7854 nullptr, // construct
7858 const JSClass
StreamCacheEntryObject::class_
= {
7859 "StreamCacheEntryObject",
7860 JSCLASS_HAS_RESERVED_SLOTS(StreamCacheEntryObject::RESERVED_SLOTS
) |
7861 JSCLASS_BACKGROUND_FINALIZE
,
7862 &StreamCacheEntryObject::classOps_
};
7864 struct BufferStreamJob
{
7865 Variant
<Uint8Vector
, StreamCacheEntryPtr
> source
;
7867 JS::StreamConsumer
* consumer
;
7869 BufferStreamJob(Uint8Vector
&& source
, JS::StreamConsumer
* consumer
)
7870 : source(AsVariant
<Uint8Vector
>(std::move(source
))), consumer(consumer
) {}
7871 BufferStreamJob(StreamCacheEntry
& source
, JS::StreamConsumer
* consumer
)
7872 : source(AsVariant
<StreamCacheEntryPtr
>(&source
)), consumer(consumer
) {}
7875 struct BufferStreamState
{
7876 Vector
<UniquePtr
<BufferStreamJob
>, 0, SystemAllocPolicy
> jobs
;
7881 BufferStreamState() : delayMillis(1), chunkSize(10), shutdown(false) {}
7883 ~BufferStreamState() { MOZ_ASSERT(jobs
.empty()); }
7886 static ExclusiveWaitableData
<BufferStreamState
>* bufferStreamState
;
7888 static void BufferStreamMain(BufferStreamJob
* job
) {
7889 const uint8_t* bytes
;
7891 JS::OptimizedEncodingListener
* listener
;
7892 if (job
->source
.is
<StreamCacheEntryPtr
>()) {
7893 StreamCacheEntry
& cache
= *job
->source
.as
<StreamCacheEntryPtr
>();
7894 if (cache
.hasOptimizedEncoding()) {
7895 const Uint8Vector
& optimized
= cache
.optimizedEncoding();
7896 job
->consumer
->consumeOptimizedEncoding(optimized
.begin(),
7897 optimized
.length());
7901 bytes
= cache
.bytes().begin();
7902 byteLength
= cache
.bytes().length();
7905 bytes
= job
->source
.as
<Uint8Vector
>().begin();
7906 byteLength
= job
->source
.as
<Uint8Vector
>().length();
7913 if (byteOffset
== byteLength
) {
7914 job
->consumer
->streamEnd(listener
);
7922 auto state
= bufferStreamState
->lock();
7923 shutdown
= state
->shutdown
;
7924 delayMillis
= state
->delayMillis
;
7925 chunkSize
= state
->chunkSize
;
7929 job
->consumer
->streamError(JSMSG_STREAM_CONSUME_ERROR
);
7933 ThisThread::SleepMilliseconds(delayMillis
);
7935 chunkSize
= std::min(chunkSize
, byteLength
- byteOffset
);
7937 if (!job
->consumer
->consumeChunk(bytes
+ byteOffset
, chunkSize
)) {
7941 byteOffset
+= chunkSize
;
7945 auto state
= bufferStreamState
->lock();
7946 size_t jobIndex
= 0;
7947 while (state
->jobs
[jobIndex
].get() != job
) {
7950 job
->thread
.detach(); // quiet assert in ~Thread() called by erase().
7951 state
->jobs
.erase(state
->jobs
.begin() + jobIndex
);
7952 if (state
->jobs
.empty()) {
7953 state
.notify_all(/* jobs empty */);
7957 static bool ConsumeBufferSource(JSContext
* cx
, JS::HandleObject obj
,
7958 JS::MimeType
, JS::StreamConsumer
* consumer
) {
7960 RootedValue
url(cx
);
7961 if (!JS_GetProperty(cx
, obj
, "url", &url
)) {
7964 UniqueChars urlChars
;
7965 if (url
.isString()) {
7966 Rooted
<JSString
*> str(cx
, url
.toString());
7967 urlChars
= JS_EncodeStringToUTF8(cx
, str
);
7973 RootedValue
mapUrl(cx
);
7974 if (!JS_GetProperty(cx
, obj
, "sourceMappingURL", &mapUrl
)) {
7977 UniqueChars mapUrlChars
;
7978 if (mapUrl
.isString()) {
7979 Rooted
<JSString
*> str(cx
, mapUrl
.toString());
7980 mapUrlChars
= JS_EncodeStringToUTF8(cx
, str
);
7986 consumer
->noteResponseURLs(urlChars
.get(), mapUrlChars
.get());
7989 UniquePtr
<BufferStreamJob
> job
;
7991 SharedMem
<uint8_t*> dataPointer
;
7993 if (IsBufferSource(obj
, &dataPointer
, &byteLength
)) {
7995 if (!bytes
.resize(byteLength
)) {
7996 JS_ReportOutOfMemory(cx
);
8000 memcpy(bytes
.begin(), dataPointer
.unwrap(), byteLength
);
8001 job
= cx
->make_unique
<BufferStreamJob
>(std::move(bytes
), consumer
);
8002 } else if (obj
->is
<StreamCacheEntryObject
>()) {
8003 job
= cx
->make_unique
<BufferStreamJob
>(
8004 obj
->as
<StreamCacheEntryObject
>().cache(), consumer
);
8006 JS_ReportErrorASCII(
8008 "shell streaming consumes a buffer source (buffer or view) "
8009 "or StreamCacheEntryObject");
8016 BufferStreamJob
* jobPtr
= job
.get();
8019 auto state
= bufferStreamState
->lock();
8020 MOZ_ASSERT(!state
->shutdown
);
8021 if (!state
->jobs
.append(std::move(job
))) {
8022 JS_ReportOutOfMemory(cx
);
8028 AutoEnterOOMUnsafeRegion oomUnsafe
;
8029 if (!jobPtr
->thread
.init(BufferStreamMain
, jobPtr
)) {
8030 oomUnsafe
.crash("ConsumeBufferSource");
8037 static void ReportStreamError(JSContext
* cx
, size_t errorNumber
) {
8038 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, errorNumber
);
8041 static bool SetBufferStreamParams(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8042 CallArgs args
= CallArgsFromVp(argc
, vp
);
8043 if (!args
.requireAtLeast(cx
, "setBufferStreamParams", 2)) {
8048 if (!ToNumber(cx
, args
[0], &delayMillis
)) {
8053 if (!ToNumber(cx
, args
[1], &chunkSize
)) {
8058 auto state
= bufferStreamState
->lock();
8059 state
->delayMillis
= delayMillis
;
8060 state
->chunkSize
= chunkSize
;
8063 args
.rval().setUndefined();
8067 static void ShutdownBufferStreams() {
8068 auto state
= bufferStreamState
->lock();
8069 state
->shutdown
= true;
8070 while (!state
->jobs
.empty()) {
8071 state
.wait(/* jobs empty */);
8073 state
->jobs
.clearAndFree();
8076 static bool DumpScopeChain(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8077 CallArgs args
= CallArgsFromVp(argc
, vp
);
8078 RootedObject
callee(cx
, &args
.callee());
8080 if (js::SupportDifferentialTesting()) {
8081 ReportUsageErrorASCII(
8082 cx
, callee
, "Function not available in differential testing mode.");
8086 if (args
.length() != 1) {
8087 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8091 if (!args
[0].isObject() ||
8092 !(args
[0].toObject().is
<JSFunction
>() ||
8093 args
[0].toObject().is
<ShellModuleObjectWrapper
>())) {
8094 ReportUsageErrorASCII(
8095 cx
, callee
, "Argument must be an interpreted function or a module");
8099 RootedObject
obj(cx
, &args
[0].toObject());
8100 RootedScript
script(cx
);
8102 if (obj
->is
<JSFunction
>()) {
8103 RootedFunction
fun(cx
, &obj
->as
<JSFunction
>());
8104 if (!fun
->isInterpreted()) {
8105 ReportUsageErrorASCII(cx
, callee
,
8106 "Argument must be an interpreted function");
8109 script
= JSFunction::getOrCreateScript(cx
, fun
);
8114 script
= obj
->as
<ShellModuleObjectWrapper
>().get()->maybeScript();
8116 JS_ReportErrorASCII(cx
, "module does not have an associated script");
8121 script
->bodyScope()->dump();
8123 args
.rval().setUndefined();
8127 // For testing GC marking, blackRoot() and grayRoot() will heap-allocate an
8128 // array whose elements (as well as the array itself) will be marked as roots in
8131 // Note that EnsureGrayRoot() will blacken the returned object, so it will not
8132 // actually end up marked gray until the following GC clears the black bit
8133 // (assuming nothing is holding onto it.)
8135 // The idea is that you can set up a whole graph of objects to be marked gray,
8136 // hanging off of the object returned from grayRoot(). Then you GC to clear the
8137 // black bits and set the gray bits.
8139 // To test grayness, register the objects of interest with addMarkObservers(),
8140 // which takes an Array of objects (which will be marked black at the time
8141 // they're passed in). Their mark bits may be retrieved at any time with
8142 // getMarks(), in the form of an array of strings with each index corresponding
8143 // to the original objects passed to addMarkObservers().
8145 static bool EnsureRootArray(JSContext
* cx
, gc::MarkColor color
, unsigned argc
,
8147 CallArgs args
= CallArgsFromVp(argc
, vp
);
8149 auto priv
= EnsureShellCompartmentPrivate(cx
);
8154 GCPtr
<ArrayObject
*>& root
=
8155 (color
== gc::MarkColor::Black
) ? priv
->blackRoot
: priv
->grayRoot
;
8157 if (!root
&& !(root
= NewTenuredDenseEmptyArray(cx
))) {
8161 // Barrier to enforce the invariant that JS does not touch gray objects.
8162 JSObject
* obj
= root
;
8163 JS::ExposeObjectToActiveJS(obj
);
8165 args
.rval().setObject(*obj
);
8169 static bool EnsureBlackRoot(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8170 return EnsureRootArray(cx
, gc::MarkColor::Black
, argc
, vp
);
8173 static bool EnsureGrayRoot(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8174 return EnsureRootArray(cx
, gc::MarkColor::Gray
, argc
, vp
);
8177 static MarkBitObservers
* EnsureMarkBitObservers(JSContext
* cx
) {
8178 ShellContext
* sc
= GetShellContext(cx
);
8179 if (!sc
->markObservers
) {
8181 cx
->new_
<MarkBitObservers
>(cx
->runtime(), NonshrinkingGCObjectVector());
8185 sc
->markObservers
.reset(observers
);
8187 return sc
->markObservers
.get();
8190 static bool ClearMarkObservers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8191 CallArgs args
= CallArgsFromVp(argc
, vp
);
8193 auto markObservers
= EnsureMarkBitObservers(cx
);
8194 if (!markObservers
) {
8198 markObservers
->get().clear();
8200 args
.rval().setUndefined();
8204 static bool AddMarkObservers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8205 CallArgs args
= CallArgsFromVp(argc
, vp
);
8207 auto markObservers
= EnsureMarkBitObservers(cx
);
8208 if (!markObservers
) {
8212 if (!args
.get(0).isObject()) {
8213 JS_ReportErrorASCII(cx
, "argument must be an Array of objects");
8217 RootedObject
observersArg(cx
, &args
[0].toObject());
8219 if (!GetLengthProperty(cx
, observersArg
, &length
)) {
8223 if (length
> UINT32_MAX
) {
8224 JS_ReportErrorASCII(cx
, "Invalid length for observers array");
8228 RootedValue
value(cx
);
8229 RootedObject
object(cx
);
8230 for (uint32_t i
= 0; i
< length
; i
++) {
8231 if (!JS_GetElement(cx
, observersArg
, i
, &value
)) {
8235 if (!value
.isObject()) {
8236 JS_ReportErrorASCII(cx
, "argument must be an Array of objects");
8240 object
= &value
.toObject();
8241 if (gc::IsInsideNursery(object
)) {
8242 // WeakCaches are not swept during a minor GC. To prevent
8243 // nursery-allocated contents from having the mark bits be deceptively
8244 // black until the second GC, they would need to be marked weakly (cf
8245 // NurseryAwareHashMap). It is simpler to evict the nursery to prevent
8246 // nursery objects from being observed.
8247 cx
->runtime()->gc
.evictNursery();
8250 if (!markObservers
->get().append(object
)) {
8255 args
.rval().setInt32(length
);
8259 static bool GetMarks(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8260 CallArgs args
= CallArgsFromVp(argc
, vp
);
8262 auto& observers
= GetShellContext(cx
)->markObservers
;
8264 args
.rval().setUndefined();
8268 size_t length
= observers
->get().length();
8269 Rooted
<ArrayObject
*> ret(cx
, js::NewDenseEmptyArray(cx
));
8274 for (uint32_t i
= 0; i
< length
; i
++) {
8276 JSObject
* obj
= observers
->get()[i
];
8279 } else if (obj
->zone()->isGCPreparing()) {
8282 gc::TenuredCell
* cell
= &obj
->asTenured();
8283 if (cell
->isMarkedGray()) {
8285 } else if (cell
->isMarkedBlack()) {
8291 JSString
* s
= JS_NewStringCopyZ(cx
, color
);
8295 if (!NewbornArrayPush(cx
, ret
, StringValue(s
))) {
8300 args
.rval().setObject(*ret
);
8307 class ShellAutoEntryMonitor
: JS::dbg::AutoEntryMonitor
{
8308 Vector
<UniqueChars
, 1, js::SystemAllocPolicy
> log
;
8310 bool enteredWithoutExit
;
8313 explicit ShellAutoEntryMonitor(JSContext
* cx
)
8314 : AutoEntryMonitor(cx
), oom(false), enteredWithoutExit(false) {}
8316 ~ShellAutoEntryMonitor() { MOZ_ASSERT(!enteredWithoutExit
); }
8318 void Entry(JSContext
* cx
, JSFunction
* function
, JS::HandleValue asyncStack
,
8319 const char* asyncCause
) override
{
8320 MOZ_ASSERT(!enteredWithoutExit
);
8321 enteredWithoutExit
= true;
8323 RootedString
displayId(cx
, JS_GetMaybePartialFunctionDisplayId(function
));
8325 UniqueChars displayIdStr
= JS_EncodeStringToUTF8(cx
, displayId
);
8326 if (!displayIdStr
) {
8327 // We report OOM in buildResult.
8328 cx
->recoverFromOutOfMemory();
8332 oom
= !log
.append(std::move(displayIdStr
));
8336 oom
= !log
.append(DuplicateString("anonymous"));
8339 void Entry(JSContext
* cx
, JSScript
* script
, JS::HandleValue asyncStack
,
8340 const char* asyncCause
) override
{
8341 MOZ_ASSERT(!enteredWithoutExit
);
8342 enteredWithoutExit
= true;
8344 UniqueChars
label(JS_smprintf("eval:%s", JS_GetScriptFilename(script
)));
8345 oom
= !label
|| !log
.append(std::move(label
));
8348 void Exit(JSContext
* cx
) override
{
8349 MOZ_ASSERT(enteredWithoutExit
);
8350 enteredWithoutExit
= false;
8353 bool buildResult(JSContext
* cx
, MutableHandleValue resultValue
) {
8355 JS_ReportOutOfMemory(cx
);
8359 RootedObject
result(cx
, JS::NewArrayObject(cx
, log
.length()));
8364 for (size_t i
= 0; i
< log
.length(); i
++) {
8365 char* name
= log
[i
].get();
8366 RootedString
string(cx
, AtomizeUTF8Chars(cx
, name
, strlen(name
)));
8370 RootedValue
value(cx
, StringValue(string
));
8371 if (!JS_SetElement(cx
, result
, i
, value
)) {
8376 resultValue
.setObject(*result
.get());
8381 } // namespace shell
8384 static bool EntryPoints(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8385 CallArgs args
= CallArgsFromVp(argc
, vp
);
8387 if (args
.length() != 1) {
8388 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
8392 RootedObject
opts(cx
, ToObject(cx
, args
[0]));
8397 // { function: f } --- Call f.
8399 RootedValue
fun(cx
), dummy(cx
);
8401 if (!JS_GetProperty(cx
, opts
, "function", &fun
)) {
8404 if (!fun
.isUndefined()) {
8405 js::shell::ShellAutoEntryMonitor
sarep(cx
);
8406 if (!Call(cx
, UndefinedHandleValue
, fun
, JS::HandleValueArray::empty(),
8410 return sarep
.buildResult(cx
, args
.rval());
8414 // { object: o, property: p, value: v } --- Fetch o[p], or if
8415 // v is present, assign o[p] = v.
8417 RootedValue
objectv(cx
), propv(cx
), valuev(cx
);
8419 if (!JS_GetProperty(cx
, opts
, "object", &objectv
) ||
8420 !JS_GetProperty(cx
, opts
, "property", &propv
))
8422 if (!objectv
.isUndefined() && !propv
.isUndefined()) {
8423 RootedObject
object(cx
, ToObject(cx
, objectv
));
8428 RootedString
string(cx
, ToString(cx
, propv
));
8433 if (!JS_StringToId(cx
, string
, &id
)) {
8437 if (!JS_GetProperty(cx
, opts
, "value", &valuev
)) {
8441 js::shell::ShellAutoEntryMonitor
sarep(cx
);
8443 if (!valuev
.isUndefined()) {
8444 if (!JS_SetPropertyById(cx
, object
, id
, valuev
)) {
8448 if (!JS_GetPropertyById(cx
, object
, id
, &valuev
)) {
8453 return sarep
.buildResult(cx
, args
.rval());
8457 // { ToString: v } --- Apply JS::ToString to v.
8461 if (!JS_GetProperty(cx
, opts
, "ToString", &v
)) {
8464 if (!v
.isUndefined()) {
8465 js::shell::ShellAutoEntryMonitor
sarep(cx
);
8466 if (!JS::ToString(cx
, v
)) {
8469 return sarep
.buildResult(cx
, args
.rval());
8473 // { ToNumber: v } --- Apply JS::ToNumber to v.
8478 if (!JS_GetProperty(cx
, opts
, "ToNumber", &v
)) {
8481 if (!v
.isUndefined()) {
8482 js::shell::ShellAutoEntryMonitor
sarep(cx
);
8483 if (!JS::ToNumber(cx
, v
, &dummy
)) {
8486 return sarep
.buildResult(cx
, args
.rval());
8490 // { eval: code } --- Apply ToString and then Evaluate to code.
8492 RootedValue
code(cx
), dummy(cx
);
8494 if (!JS_GetProperty(cx
, opts
, "eval", &code
)) {
8497 if (!code
.isUndefined()) {
8498 RootedString
codeString(cx
, ToString(cx
, code
));
8503 AutoStableStringChars
linearChars(cx
);
8504 if (!linearChars
.initTwoByte(cx
, codeString
)) {
8507 JS::SourceText
<char16_t
> srcBuf
;
8508 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
8512 CompileOptions
options(cx
);
8513 options
.setIntroductionType("entryPoint eval")
8514 .setFileAndLine("entryPoint eval", 1);
8516 js::shell::ShellAutoEntryMonitor
sarep(cx
);
8517 if (!JS::Evaluate(cx
, options
, srcBuf
, &dummy
)) {
8520 return sarep
.buildResult(cx
, args
.rval());
8524 JS_ReportErrorASCII(cx
, "bad 'params' object");
8529 static bool WasmTextToBinary(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8530 CallArgs args
= CallArgsFromVp(argc
, vp
);
8531 RootedObject
callee(cx
, &args
.callee());
8533 if (!args
.requireAtLeast(cx
, "wasmTextToBinary", 1)) {
8537 if (!args
[0].isString()) {
8538 ReportUsageErrorASCII(cx
, callee
, "First argument must be a String");
8542 size_t textLen
= args
[0].toString()->length();
8544 AutoStableStringChars
twoByteChars(cx
);
8545 if (!twoByteChars
.initTwoByte(cx
, args
[0].toString())) {
8551 if (!wasm::TextToBinary(twoByteChars
.twoByteChars(), textLen
, &bytes
,
8553 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_WASM_TEXT_FAIL
,
8554 error
.get() ? error
.get() : "out of memory");
8558 RootedObject
binary(cx
, JS_NewUint8Array(cx
, bytes
.length()));
8563 memcpy(binary
->as
<TypedArrayObject
>().dataPointerUnshared(), bytes
.begin(),
8566 args
.rval().setObject(*binary
);
8570 # ifndef __AFL_HAVE_MANUAL_CONTROL
8571 # define __AFL_LOOP(x) true
8574 static bool WasmLoop(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8575 CallArgs args
= CallArgsFromVp(argc
, vp
);
8576 RootedObject
callee(cx
, &args
.callee());
8578 if (args
.length() < 1 || args
.length() > 2) {
8579 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8583 if (!args
[0].isString()) {
8584 ReportUsageErrorASCII(cx
, callee
, "First argument must be a String");
8588 RootedObject
importObj(cx
);
8589 if (!args
.get(1).isUndefined()) {
8590 if (!args
.get(1).isObject()) {
8591 ReportUsageErrorASCII(cx
, callee
,
8592 "Second argument, if present, must be an Object");
8595 importObj
= &args
[1].toObject();
8598 RootedString
givenPath(cx
, args
[0].toString());
8599 RootedString
filename(cx
, ResolvePath(cx
, givenPath
, RootRelative
));
8604 while (__AFL_LOOP(1000)) {
8605 Rooted
<JSObject
*> ret(cx
, FileAsTypedArray(cx
, filename
));
8610 Rooted
<TypedArrayObject
*> typedArray(cx
, &ret
->as
<TypedArrayObject
>());
8611 Rooted
<WasmInstanceObject
*> instanceObj(cx
);
8612 if (!wasm::Eval(cx
, typedArray
, importObj
, &instanceObj
)) {
8613 // Clear any pending exceptions, we don't care about them
8614 cx
->clearPendingException();
8618 # ifdef __AFL_HAVE_MANUAL_CONTROL // to silence unreachable code warning
8624 static constexpr uint32_t DOM_OBJECT_SLOT
= 0;
8625 static constexpr uint32_t DOM_OBJECT_SLOT2
= 1;
8627 static const JSClass
* GetDomClass();
8629 static JSObject
* GetDOMPrototype(JSContext
* cx
, JSObject
* global
);
8631 static const JSClass TransplantableDOMObjectClass
= {
8632 "TransplantableDOMObject",
8633 JSCLASS_IS_DOMJSCLASS
| JSCLASS_HAS_RESERVED_SLOTS(1)};
8635 static const JSClass TransplantableDOMProxyObjectClass
=
8636 PROXY_CLASS_DEF("TransplantableDOMProxyObject",
8637 JSCLASS_IS_DOMJSCLASS
| JSCLASS_HAS_RESERVED_SLOTS(1));
8639 class TransplantableDOMProxyHandler final
: public ForwardingProxyHandler
{
8641 static const TransplantableDOMProxyHandler singleton
;
8642 static const char family
;
8644 constexpr TransplantableDOMProxyHandler() : ForwardingProxyHandler(&family
) {}
8646 // These two proxy traps are called in |js::DeadProxyTargetValue|, which in
8647 // turn is called when nuking proxies. Because this proxy can temporarily be
8648 // without an object in its private slot, see |EnsureExpandoObject|, the
8649 // default implementation inherited from ForwardingProxyHandler can't be used,
8650 // since it tries to derive the callable/constructible value from the target.
8651 bool isCallable(JSObject
* obj
) const override
{ return false; }
8652 bool isConstructor(JSObject
* obj
) const override
{ return false; }
8654 // Simplified implementation of |DOMProxyHandler::GetAndClearExpandoObject|.
8655 static JSObject
* GetAndClearExpandoObject(JSObject
* obj
) {
8656 const Value
& v
= GetProxyPrivate(obj
);
8657 if (v
.isUndefined()) {
8661 JSObject
* expandoObject
= &v
.toObject();
8662 SetProxyPrivate(obj
, UndefinedValue());
8663 return expandoObject
;
8666 // Simplified implementation of |DOMProxyHandler::EnsureExpandoObject|.
8667 static JSObject
* EnsureExpandoObject(JSContext
* cx
, JS::HandleObject obj
) {
8668 const Value
& v
= GetProxyPrivate(obj
);
8670 return &v
.toObject();
8672 MOZ_ASSERT(v
.isUndefined());
8674 JSObject
* expando
= JS_NewObjectWithGivenProto(cx
, nullptr, nullptr);
8678 SetProxyPrivate(obj
, ObjectValue(*expando
));
8683 const TransplantableDOMProxyHandler
TransplantableDOMProxyHandler::singleton
;
8684 const char TransplantableDOMProxyHandler::family
= 0;
8686 enum TransplantObjectSlots
{
8687 TransplantSourceObject
= 0,
8690 static bool TransplantObject(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8691 CallArgs args
= CallArgsFromVp(argc
, vp
);
8692 RootedFunction
callee(cx
, &args
.callee().as
<JSFunction
>());
8694 if (args
.length() != 1 || !args
[0].isObject()) {
8695 JS_ReportErrorASCII(cx
, "transplant() must be called with an object");
8699 // |newGlobal| needs to be a GlobalObject.
8700 RootedObject
newGlobal(
8701 cx
, js::CheckedUnwrapDynamic(&args
[0].toObject(), cx
,
8702 /* stopAtWindowProxy = */ false));
8704 ReportAccessDenied(cx
);
8707 if (!JS_IsGlobalObject(newGlobal
)) {
8708 JS_ReportErrorNumberASCII(
8709 cx
, GetErrorMessage
, nullptr, JSMSG_UNEXPECTED_TYPE
,
8710 "\"global\" passed to transplant()", "not a global object");
8714 const Value
& reserved
=
8715 GetFunctionNativeReserved(callee
, TransplantSourceObject
);
8716 RootedObject
source(cx
, CheckedUnwrapStatic(&reserved
.toObject()));
8718 ReportAccessDenied(cx
);
8721 if (JS_IsDeadWrapper(source
)) {
8722 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
8725 MOZ_ASSERT(source
->getClass()->isDOMClass());
8727 // The following steps aim to replicate the behavior of UpdateReflectorGlobal
8728 // in dom/bindings/BindingUtils.cpp. In detail:
8729 // 1. Check the recursion depth using checkConservative.
8730 // 2. Enter the target compartment.
8731 // 3. Clone the source object using JS_CloneObject.
8732 // 4. Check if new wrappers can be created if source and target are in
8733 // different compartments.
8734 // 5. Copy all properties from source to a temporary holder object.
8735 // 6. Actually transplant the object.
8736 // 7. And finally copy the properties back to the source object.
8738 // As an extension to the algorithm in UpdateReflectorGlobal, we also allow
8739 // to transplant an object into the same compartment as the source object to
8740 // cover all operations supported by JS_TransplantObject.
8742 AutoCheckRecursionLimit
recursion(cx
);
8743 if (!recursion
.checkConservative(cx
)) {
8747 bool isProxy
= IsProxy(source
);
8748 RootedObject
expandoObject(cx
);
8751 TransplantableDOMProxyHandler::GetAndClearExpandoObject(source
);
8754 JSAutoRealm
ar(cx
, newGlobal
);
8756 RootedObject
proto(cx
);
8757 if (JS::GetClass(source
) == GetDomClass()) {
8758 proto
= GetDOMPrototype(cx
, newGlobal
);
8760 proto
= JS::GetRealmObjectPrototype(cx
);
8766 RootedObject
target(cx
, JS_CloneObject(cx
, source
, proto
));
8771 if (JS::GetCompartment(source
) != JS::GetCompartment(target
) &&
8772 !AllowNewWrapper(JS::GetCompartment(source
), target
)) {
8773 JS_ReportErrorASCII(cx
, "Cannot transplant into nuked compartment");
8777 RootedObject
copyFrom(cx
, isProxy
? expandoObject
: source
);
8778 RootedObject
propertyHolder(cx
,
8779 JS_NewObjectWithGivenProto(cx
, nullptr, nullptr));
8780 if (!propertyHolder
) {
8784 if (!JS_CopyOwnPropertiesAndPrivateFields(cx
, propertyHolder
, copyFrom
)) {
8788 JS::SetReservedSlot(target
, DOM_OBJECT_SLOT
,
8789 JS::GetReservedSlot(source
, DOM_OBJECT_SLOT
));
8790 JS::SetReservedSlot(source
, DOM_OBJECT_SLOT
, JS::PrivateValue(nullptr));
8791 if (JS::GetClass(source
) == GetDomClass()) {
8792 JS::SetReservedSlot(target
, DOM_OBJECT_SLOT2
,
8793 JS::GetReservedSlot(source
, DOM_OBJECT_SLOT2
));
8794 JS::SetReservedSlot(source
, DOM_OBJECT_SLOT2
, UndefinedValue());
8797 source
= JS_TransplantObject(cx
, source
, target
);
8802 RootedObject
copyTo(cx
);
8804 copyTo
= TransplantableDOMProxyHandler::EnsureExpandoObject(cx
, source
);
8811 if (!JS_CopyOwnPropertiesAndPrivateFields(cx
, copyTo
, propertyHolder
)) {
8815 args
.rval().setUndefined();
8819 static bool TransplantableObject(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8820 CallArgs args
= CallArgsFromVp(argc
, vp
);
8821 RootedObject
callee(cx
, &args
.callee());
8823 if (args
.length() > 1) {
8824 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8828 bool createProxy
= false;
8829 RootedObject
source(cx
);
8830 if (args
.length() == 1 && !args
[0].isUndefined()) {
8831 if (!args
[0].isObject()) {
8832 ReportUsageErrorASCII(cx
, callee
, "Argument must be an object");
8836 RootedObject
options(cx
, &args
[0].toObject());
8837 RootedValue
value(cx
);
8839 if (!JS_GetProperty(cx
, options
, "proxy", &value
)) {
8842 createProxy
= JS::ToBoolean(value
);
8844 if (!JS_GetProperty(cx
, options
, "object", &value
)) {
8847 if (!value
.isUndefined()) {
8848 if (!value
.isObject()) {
8849 ReportUsageErrorASCII(cx
, callee
, "'object' option must be an object");
8853 source
= &value
.toObject();
8854 if (JS::GetClass(source
) != GetDomClass()) {
8855 ReportUsageErrorASCII(cx
, callee
, "Object not a FakeDOMObject");
8859 // |source| must be a tenured object to be transplantable.
8860 if (gc::IsInsideNursery(source
)) {
8863 MOZ_ASSERT(!gc::IsInsideNursery(source
),
8864 "Live objects should be tenured after one GC, because "
8865 "the nursery has only a single generation");
8872 source
= NewBuiltinClassInstance(cx
, &TransplantableDOMObjectClass
,
8878 JS::SetReservedSlot(source
, DOM_OBJECT_SLOT
, JS::PrivateValue(nullptr));
8880 JSObject
* expando
= JS_NewPlainObject(cx
);
8884 RootedValue
expandoVal(cx
, ObjectValue(*expando
));
8886 ProxyOptions options
;
8887 options
.setClass(&TransplantableDOMProxyObjectClass
);
8888 options
.setLazyProto(true);
8890 source
= NewProxyObject(cx
, &TransplantableDOMProxyHandler::singleton
,
8891 expandoVal
, nullptr, options
);
8896 SetProxyReservedSlot(source
, DOM_OBJECT_SLOT
, JS::PrivateValue(nullptr));
8900 jsid emptyId
= NameToId(cx
->names().empty_
);
8901 RootedObject
transplant(
8902 cx
, NewFunctionByIdWithReserved(cx
, TransplantObject
, 0, 0, emptyId
));
8907 SetFunctionNativeReserved(transplant
, TransplantSourceObject
,
8908 ObjectValue(*source
));
8910 RootedObject
result(cx
, JS_NewPlainObject(cx
));
8915 RootedValue
sourceVal(cx
, ObjectValue(*source
));
8916 RootedValue
transplantVal(cx
, ObjectValue(*transplant
));
8917 if (!JS_DefineProperty(cx
, result
, "object", sourceVal
, 0) ||
8918 !JS_DefineProperty(cx
, result
, "transplant", transplantVal
, 0)) {
8922 args
.rval().setObject(*result
);
8927 static bool DebugGetQueuedJobs(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8928 CallArgs args
= CallArgsFromVp(argc
, vp
);
8930 JSObject
* jobs
= js::GetJobsInInternalJobQueue(cx
);
8935 args
.rval().setObject(*jobs
);
8940 #ifdef FUZZING_INTERFACES
8942 size_t gluesmith(uint8_t* data
, size_t size
, uint8_t* out
, size_t maxsize
);
8945 static bool GetWasmSmithModule(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8946 CallArgs args
= CallArgsFromVp(argc
, vp
);
8947 RootedObject
callee(cx
, &args
.callee());
8949 if (args
.length() != 1) {
8950 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8954 if (!args
[0].isObject() || !args
[0].toObject().is
<ArrayBufferObject
>()) {
8955 ReportUsageErrorASCII(cx
, callee
, "Argument must be ArrayBuffer.");
8959 ArrayBufferObject
* arrayBuffer
= &args
[0].toObject().as
<ArrayBufferObject
>();
8960 size_t length
= arrayBuffer
->byteLength();
8961 uint8_t* data
= arrayBuffer
->dataPointer();
8963 const size_t maxModuleSize
= 4096;
8964 uint8_t tmp
[maxModuleSize
];
8966 size_t outSize
= gluesmith(data
, length
, tmp
, maxModuleSize
);
8968 JS_ReportErrorASCII(cx
, "Generated module is too large.");
8972 JS::Rooted
<JSObject
*> outArr(cx
, JS_NewUint8ClampedArray(cx
, outSize
));
8978 JS::AutoCheckCannotGC nogc
;
8980 uint8_t* data
= JS_GetUint8ClampedArrayData(outArr
, &isShared
, nogc
);
8981 MOZ_RELEASE_ASSERT(!isShared
);
8982 memcpy(data
, tmp
, outSize
);
8985 args
.rval().setObject(*outArr
);
8991 static bool IsValidJSON(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8992 CallArgs args
= CallArgsFromVp(argc
, vp
);
8993 RootedObject
callee(cx
, &args
.callee());
8995 if (!args
.get(0).isString()) {
8996 ReportUsageErrorASCII(cx
, callee
, "First argument must be a String");
9000 JS::Rooted
<JSLinearString
*> input(cx
, args
[0].toString()->ensureLinear(cx
));
9006 if (input
->hasLatin1Chars()) {
9007 JS::AutoCheckCannotGC nogc
;
9008 result
= JS::IsValidJSON(input
->latin1Chars(nogc
), input
->length());
9010 JS::AutoCheckCannotGC nogc
;
9011 result
= JS::IsValidJSON(input
->twoByteChars(nogc
), input
->length());
9014 args
.rval().setBoolean(result
);
9018 // Quick file format for a LZ4 compressed file
9019 static constexpr uint32_t LZ4MagicHeader
= -1;
9020 // A magic word and a length field
9021 static constexpr size_t LZ4HeaderSize
= sizeof(uint32_t) * 2;
9022 static constexpr size_t LZ4MaxSize
= UINT32_MAX
;
9024 static bool CompressLZ4(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9025 CallArgs args
= CallArgsFromVp(argc
, vp
);
9026 RootedObject
callee(cx
, &args
.callee());
9028 if (!args
.get(0).isObject() ||
9029 !args
.get(0).toObject().is
<ArrayBufferObject
>()) {
9030 ReportUsageErrorASCII(cx
, callee
, "First argument must be an ArrayBuffer");
9034 JS::Rooted
<ArrayBufferObject
*> bytes(
9035 cx
, &args
.get(0).toObject().as
<ArrayBufferObject
>());
9036 size_t byteLength
= bytes
->byteLength();
9037 if (byteLength
> LZ4MaxSize
) {
9038 ReportOutOfMemory(cx
);
9042 // Create a buffer big enough for the header and the max amount of compressed
9044 size_t outputCapacity
=
9045 LZ4HeaderSize
+ mozilla::Compression::LZ4::maxCompressedSize(byteLength
);
9047 mozilla::UniquePtr
<void, JS::FreePolicy
> output(js_malloc(outputCapacity
));
9049 ReportOutOfMemory(cx
);
9053 // Write the magic header word and decompressed size in bytes.
9054 ((uint32_t*)(output
.get()))[0] = LZ4MagicHeader
;
9055 ((uint32_t*)(output
.get()))[1] = byteLength
;
9057 // Compress the bytes into the output
9058 char* compressedBytesStart
= ((char*)output
.get()) + LZ4HeaderSize
;
9059 size_t compressedBytesLength
= mozilla::Compression::LZ4::compress(
9060 (const char*)bytes
->dataPointer(), byteLength
, compressedBytesStart
);
9061 size_t outputLength
= compressedBytesLength
+ LZ4HeaderSize
;
9063 // Create an ArrayBuffer wrapping the compressed bytes
9064 JSObject
* outputArrayBuffer
=
9065 NewArrayBufferWithContents(cx
, outputLength
, std::move(output
));
9066 if (!outputArrayBuffer
) {
9070 args
.rval().setObject(*outputArrayBuffer
);
9074 static bool DecompressLZ4(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9075 CallArgs args
= CallArgsFromVp(argc
, vp
);
9076 RootedObject
callee(cx
, &args
.callee());
9078 if (!args
.get(0).isObject() ||
9079 !args
.get(0).toObject().is
<ArrayBufferObject
>()) {
9080 ReportUsageErrorASCII(cx
, callee
, "First argument must be an ArrayBuffer");
9084 JS::Rooted
<ArrayBufferObject
*> bytes(
9085 cx
, &args
.get(0).toObject().as
<ArrayBufferObject
>());
9086 size_t byteLength
= bytes
->byteLength();
9087 if (byteLength
< LZ4HeaderSize
) {
9088 JS_ReportErrorASCII(cx
, "Invalid LZ4 buffer");
9092 // Check the magic header and get the decompressed byte length.
9093 uint32_t magicHeader
= ((uint32_t*)(bytes
->dataPointer()))[0];
9094 uint32_t decompressedBytesLength
= ((uint32_t*)(bytes
->dataPointer()))[1];
9095 if (magicHeader
!= LZ4MagicHeader
) {
9096 JS_ReportErrorASCII(cx
, "Invalid magic header");
9100 // Allocate a buffer to store the decompressed bytes.
9101 mozilla::UniquePtr
<void, JS::FreePolicy
> decompressedBytes(
9102 js_malloc(decompressedBytesLength
));
9103 if (!decompressedBytes
) {
9104 ReportOutOfMemory(cx
);
9108 // Decompress the bytes into the output
9109 const char* compressedBytesStart
=
9110 ((const char*)bytes
->dataPointer()) + LZ4HeaderSize
;
9111 size_t compressedBytesLength
= byteLength
- LZ4HeaderSize
;
9112 size_t actualDecompressedBytesLength
= 0;
9113 if (!mozilla::Compression::LZ4::decompress(
9114 compressedBytesStart
, compressedBytesLength
,
9115 (char*)decompressedBytes
.get(), decompressedBytesLength
,
9116 &actualDecompressedBytesLength
) ||
9117 actualDecompressedBytesLength
!= decompressedBytesLength
) {
9118 JS_ReportErrorASCII(cx
, "Invalid LZ4 buffer");
9122 // Create an ArrayBuffer wrapping the decompressed bytes
9123 JSObject
* outputArrayBuffer
= NewArrayBufferWithContents(
9124 cx
, decompressedBytesLength
, std::move(decompressedBytes
));
9125 if (!outputArrayBuffer
) {
9129 args
.rval().setObject(*outputArrayBuffer
);
9134 static const JSFunctionSpecWithHelp shell_functions
[] = {
9135 JS_FN_HELP("options", Options
, 0, 0,
9136 "options([option ...])",
9137 " Get or toggle JavaScript options."),
9139 JS_FN_HELP("load", Load
, 1, 0,
9140 "load(['foo.js' ...])",
9141 " Load files named by string arguments. Filename is relative to the\n"
9142 " current working directory."),
9144 JS_FN_HELP("loadRelativeToScript", LoadScriptRelativeToScript
, 1, 0,
9145 "loadRelativeToScript(['foo.js' ...])",
9146 " Load files named by string arguments. Filename is relative to the\n"
9147 " calling script."),
9149 JS_FN_HELP("evaluate", Evaluate
, 2, 0,
9150 "evaluate(code[, options])",
9151 " Evaluate code as though it were the contents of a file.\n"
9152 " options is an optional object that may have these properties:\n"
9153 " isRunOnce: use the isRunOnce compiler option (default: false)\n"
9154 " noScriptRval: use the no-script-rval compiler option (default: false)\n"
9155 " fileName: filename for error messages and debug info\n"
9156 " skipFileNameValidation: skip the filename-validation callback\n"
9157 " lineNumber: starting line number for error messages and debug info\n"
9158 " columnNumber: starting column number for error messages and debug info\n"
9159 " global: global in which to execute the code\n"
9160 " newContext: if true, create and use a new cx (default: false)\n"
9161 " catchTermination: if true, catch termination (failure without\n"
9162 " an exception value, as for slow scripts or out-of-memory)\n"
9163 " and return 'terminated'\n"
9164 " element: if present with value |v|, convert |v| to an object |o| and\n"
9165 " mark the source as being attached to the DOM element |o|. If the\n"
9166 " property is omitted or |v| is null, don't attribute the source to\n"
9167 " any DOM element.\n"
9168 " elementAttributeName: if present and not undefined, the name of\n"
9169 " property of 'element' that holds this code. This is what\n"
9170 " Debugger.Source.prototype.elementAttributeName returns.\n"
9171 " sourceMapURL: if present with value |v|, convert |v| to a string, and\n"
9172 " provide that as the code's source map URL. If omitted, attach no\n"
9173 " source map URL to the code (although the code may provide one itself,\n"
9174 " via a //#sourceMappingURL comment).\n"
9175 " sourceIsLazy: if present and true, indicates that, after compilation, \n"
9176 " script source should not be cached by the JS engine and should be \n"
9177 " lazily loaded from the embedding as-needed.\n"
9178 " forceFullParse: if present and true, disable syntax-parse.\n"
9179 " loadBytecode: if true, and if the source is a CacheEntryObject,\n"
9180 " the bytecode would be loaded and decoded from the cache entry instead\n"
9181 " of being parsed, then it would be executed as usual.\n"
9182 " saveIncrementalBytecode: if true, and if the source is a\n"
9183 " CacheEntryObject, the bytecode would be incrementally encoded and\n"
9184 " saved into the cache entry.\n"
9185 " execute: if false, do not execute the script, but do parse and/or\n"
9187 " assertEqBytecode: if true, and if both loadBytecode and either\n"
9188 " saveIncrementalBytecode is true, then the loaded\n"
9189 " bytecode and the encoded bytecode are compared.\n"
9190 " and an assertion is raised if they differ.\n"
9191 " envChainObject: object to put on the scope chain, with its fields added\n"
9192 " as var bindings, akin to how elements are added to the environment in\n"
9193 " event handlers in Gecko.\n"
9196 JS_FN_HELP("run", Run
, 1, 0,
9198 " Run the file named by the first argument, returning the number of\n"
9199 " of milliseconds spent compiling and executing it."),
9201 JS_FN_HELP("readline", ReadLine
, 0, 0,
9203 " Read a single line from stdin."),
9205 JS_FN_HELP("readlineBuf", ReadLineBuf
, 1, 0,
9206 "readlineBuf([ buf ])",
9207 " Emulate readline() on the specified string. The first call with a string\n"
9208 " argument sets the source buffer. Subsequent calls without an argument\n"
9209 " then read from this buffer line by line.\n"),
9211 JS_FN_HELP("print", Print
, 0, 0,
9213 " Evaluate and print expressions to stdout."),
9215 JS_FN_HELP("printErr", PrintErr
, 0, 0,
9216 "printErr([exp ...])",
9217 " Evaluate and print expressions to stderr."),
9219 JS_FN_HELP("putstr", PutStr
, 0, 0,
9221 " Evaluate and print expression without newline."),
9223 JS_FN_HELP("dateNow", Now
, 0, 0,
9225 " Return the current time with sub-ms precision."),
9227 JS_FN_HELP("help", Help
, 0, 0,
9228 "help([function or interface object or /pattern/])",
9229 " Display usage and help messages."),
9231 JS_FN_HELP("quit", Quit
, 0, 0,
9233 " Quit the shell."),
9235 JS_FN_HELP("assertEq", AssertEq
, 2, 0,
9236 "assertEq(actual, expected[, msg])",
9237 " Throw if the first two arguments are not the same (both +0 or both -0,\n"
9238 " both NaN, or non-zero and ===)."),
9240 JS_FN_HELP("startTimingMutator", StartTimingMutator
, 0, 0,
9241 "startTimingMutator()",
9242 " Start accounting time to mutator vs GC."),
9244 JS_FN_HELP("stopTimingMutator", StopTimingMutator
, 0, 0,
9245 "stopTimingMutator()",
9246 " Stop accounting time to mutator vs GC and dump the results."),
9248 JS_FN_HELP("throwError", ThrowError
, 0, 0,
9250 " Throw an error from JS_ReportError."),
9252 JS_FN_HELP("createErrorReport", CreateErrorReport
, 1, 0,
9253 "createErrorReport(value)",
9254 " Create an JS::ErrorReportBuilder object from the given value and serialize\n"
9257 #if defined(DEBUG) || defined(JS_JITSPEW)
9258 JS_FN_HELP("disassemble", DisassembleToString
, 1, 0,
9259 "disassemble([fun/code])",
9260 " Return the disassembly for the given function or code.\n"
9261 " All disassembly functions take these options as leading string arguments:\n"
9262 " \"-r\" (disassemble recursively)\n"
9263 " \"-l\" (show line numbers)\n"
9264 " \"-S\" (omit source notes)"),
9266 JS_FN_HELP("dis", Disassemble
, 1, 0,
9268 " Disassemble functions into bytecodes."),
9270 JS_FN_HELP("disfile", DisassFile
, 1, 0,
9271 "disfile('foo.js')",
9272 " Disassemble script file into bytecodes.\n"),
9274 JS_FN_HELP("dissrc", DisassWithSrc
, 1, 0,
9275 "dissrc([fun/code])",
9276 " Disassemble functions with source lines."),
9278 JS_FN_HELP("notes", Notes
, 1, 0,
9280 " Show source notes for functions."),
9282 JS_FN_HELP("stackDump", StackDump
, 3, 0,
9283 "stackDump(showArgs, showLocals, showThisProps)",
9284 " Tries to print a lot of information about the current stack. \n"
9285 " Similar to the DumpJSStack() function in the browser."),
9289 JS_FN_HELP("getslx", GetSLX
, 1, 0,
9291 " Get script line extent."),
9293 JS_FN_HELP("evalcx", EvalInContext
, 1, 0,
9295 " Evaluate s in optional sandbox object o.\n"
9296 " if (s == '' && !o) return new o with eager standard classes\n"
9297 " if (s == 'lazy' && !o) return new o with lazy standard classes"),
9299 JS_FN_HELP("evalInWorker", EvalInWorker
, 1, 0,
9300 "evalInWorker(str)",
9301 " Evaluate 'str' in a separate thread with its own runtime.\n"),
9303 JS_FN_HELP("getSharedObject", GetSharedObject
, 0, 0,
9304 "getSharedObject()",
9305 " Retrieve the shared object from the cross-worker mailbox.\n"
9306 " The object retrieved may not be identical to the object that was\n"
9307 " installed, but it references the same shared memory.\n"
9308 " getSharedObject performs an ordering memory barrier.\n"),
9310 JS_FN_HELP("setSharedObject", SetSharedObject
, 0, 0,
9311 "setSharedObject(obj)",
9312 " Install the shared object in the cross-worker mailbox. The object\n"
9313 " may be null. setSharedObject performs an ordering memory barrier.\n"),
9315 JS_FN_HELP("getSharedArrayBuffer", GetSharedObject
, 0, 0,
9316 "getSharedArrayBuffer()",
9317 " Obsolete alias for getSharedObject().\n"),
9319 JS_FN_HELP("setSharedArrayBuffer", SetSharedObject
, 0, 0,
9320 "setSharedArrayBuffer(obj)",
9321 " Obsolete alias for setSharedObject(obj).\n"),
9323 JS_FN_HELP("shapeOf", ShapeOf
, 1, 0,
9325 " Get the shape of obj (an implementation detail)."),
9328 JS_FN_HELP("arrayInfo", ArrayInfo
, 1, 0,
9329 "arrayInfo(a1, a2, ...)",
9330 " Report statistics about arrays."),
9333 JS_FN_HELP("sleep", Sleep_fn
, 1, 0,
9335 " Sleep for dt seconds."),
9337 JS_FN_HELP("parseModule", ParseModule
, 1, 0,
9338 "parseModule(code)",
9339 " Parses source text as a module and returns a ModuleObject wrapper object."),
9341 JS_FN_HELP("instantiateModuleStencil", InstantiateModuleStencil
, 1, 0,
9342 "instantiateModuleStencil(stencil, [options])",
9343 " Instantiates the given stencil as module, and return the module object."),
9345 JS_FN_HELP("instantiateModuleStencilXDR", InstantiateModuleStencilXDR
, 1, 0,
9346 "instantiateModuleStencilXDR(stencil, [options])",
9347 " Reads the given stencil XDR object, instantiates the stencil as module, and"
9348 " return the module object."),
9350 JS_FN_HELP("registerModule", RegisterModule
, 2, 0,
9351 "registerModule(specifier, module)",
9352 " Register a module with the module loader, so that subsequent import from\n"
9353 " |specifier| will resolve to |module|. Returns |module|."),
9355 JS_FN_HELP("clearModules", ClearModules
, 0, 0,
9357 " Clear knowledge of all loaded modules."),
9359 JS_FN_HELP("moduleLink", ModuleLink
, 1, 0,
9360 "moduleLink(moduleOjbect)",
9361 " Link a module graph, performing the spec's Link method."),
9363 JS_FN_HELP("moduleEvaluate", ModuleEvaluate
, 1, 0,
9364 "moduleEvaluate(moduleOjbect)",
9365 " Evaluate a module graph, performing the spec's Evaluate method."),
9367 JS_FN_HELP("getModuleEnvironmentNames", GetModuleEnvironmentNames
, 1, 0,
9368 "getModuleEnvironmentNames(module)",
9369 " Get the list of a module environment's bound names for a specified module.\n"),
9371 JS_FN_HELP("getModuleEnvironmentValue", GetModuleEnvironmentValue
, 2, 0,
9372 "getModuleEnvironmentValue(module, name)",
9373 " Get the value of a bound name in a module environment.\n"),
9375 JS_FN_HELP("dumpStencil", DumpStencil
, 1, 0,
9376 "dumpStencil(code, [options])",
9377 " Parses a string and returns string that represents stencil.\n"
9378 " If present, |options| may have properties saying how the code should be\n"
9380 " module: if present and true, compile the source as module.\n"
9381 " smoosh: if present and true, use SmooshMonkey.\n"
9382 " CompileOptions-related properties of evaluate function's option can also\n"
9385 JS_FN_HELP("parse", Parse
, 1, 0,
9386 "parse(code, [options])",
9387 " Parses a string, potentially throwing. If present, |options| may\n"
9388 " have properties saying how the code should be compiled:\n"
9389 " module: if present and true, compile the source as module.\n"
9390 " smoosh: if present and true, use SmooshMonkey.\n"
9391 " CompileOptions-related properties of evaluate function's option can also\n"
9392 " be used. except forceFullParse. This function always use full parse."),
9394 JS_FN_HELP("syntaxParse", SyntaxParse
, 1, 0,
9395 "syntaxParse(code)",
9396 " Check the syntax of a string, returning success value"),
9398 JS_FN_HELP("offThreadCompileModuleToStencil", OffThreadCompileModuleToStencil
, 1, 0,
9399 "offThreadCompileModuleToStencil(code[, options])",
9400 " Compile |code| on a helper thread, returning a job ID. To wait for the\n"
9401 " compilation to finish and and get the module stencil object call\n"
9402 " |finishOffThreadStencil| passing the job ID."),
9404 JS_FN_HELP("offThreadDecodeStencil", OffThreadDecodeStencil
, 1, 0,
9405 "offThreadDecodeStencil(cacheEntry[, options])",
9406 " Decode |code| on a helper thread, returning a job ID. To wait for the\n"
9407 " decoding to finish and run the code, call |finishOffThreadStencil| passing\n"
9408 " the job ID. If present, |options| may have properties saying how the code\n"
9409 " should be compiled (see also offThreadCompileToStencil)."),
9411 JS_FN_HELP("offThreadCompileToStencil", OffThreadCompileToStencil
, 1, 0,
9412 "offThreadCompileToStencil(code[, options])",
9413 " Compile |code| on a helper thread, returning a job ID. To wait for the\n"
9414 " compilation to finish and get the stencil object, call\n"
9415 " |finishOffThreadStencil| passing the job ID. If present, \n"
9416 " |options| may have properties saying how the code should be compiled:\n"
9417 " noScriptRval: use the no-script-rval compiler option (default: false)\n"
9418 " fileName: filename for error messages and debug info\n"
9419 " lineNumber: starting line number for error messages and debug info\n"
9420 " columnNumber: starting column number for error messages and debug info\n"
9421 " element: if present with value |v|, convert |v| to an object |o| and\n"
9422 " mark the source as being attached to the DOM element |o|. If the\n"
9423 " property is omitted or |v| is null, don't attribute the source to\n"
9424 " any DOM element.\n"
9425 " elementAttributeName: if present and not undefined, the name of\n"
9426 " property of 'element' that holds this code. This is what\n"
9427 " Debugger.Source.prototype.elementAttributeName returns."),
9429 JS_FN_HELP("finishOffThreadStencil", FinishOffThreadStencil
, 0, 0,
9430 "finishOffThreadStencil([jobID])",
9431 " Wait for an off-thread compilation or decode job to complete. The job ID\n"
9432 " can be ommitted if there is only one job pending. If an error occurred,\n"
9433 " throw the appropriate exception; otherwise, return the stencil object,"
9434 " that can be passed to |evalStencil|."),
9436 JS_FN_HELP("timeout", Timeout
, 1, 0,
9437 "timeout([seconds], [func])",
9438 " Get/Set the limit in seconds for the execution time for the current context.\n"
9439 " When the timeout expires the current interrupt callback is invoked.\n"
9440 " The timeout is used just once. If the callback returns a falsy value, the\n"
9441 " script is aborted. A negative value for seconds (this is the default) cancels\n"
9442 " any pending timeout.\n"
9443 " If a second argument is provided, it is installed as the interrupt handler,\n"
9444 " exactly as if by |setInterruptCallback|.\n"),
9446 JS_FN_HELP("interruptIf", InterruptIf
, 1, 0,
9447 "interruptIf(cond)",
9448 " Requests interrupt callback if cond is true. If a callback function is set via\n"
9449 " |timeout| or |setInterruptCallback|, it will be called. No-op otherwise."),
9451 JS_FN_HELP("invokeInterruptCallback", InvokeInterruptCallbackWrapper
, 0, 0,
9452 "invokeInterruptCallback(fun)",
9453 " Forcefully set the interrupt flag and invoke the interrupt handler. If a\n"
9454 " callback function is set via |timeout| or |setInterruptCallback|, it will\n"
9455 " be called. Before returning, fun is called with the return value of the\n"
9456 " interrupt handler."),
9458 JS_FN_HELP("setInterruptCallback", SetInterruptCallback
, 1, 0,
9459 "setInterruptCallback(func)",
9460 " Sets func as the interrupt callback function.\n"
9461 " Calling this function will replace any callback set by |timeout|.\n"
9462 " If the callback returns a falsy value, the script is aborted.\n"),
9464 JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption
, 2, 0,
9465 "setJitCompilerOption(<option>, <number>)",
9466 " Set a compiler option indexed in JSCompileOption enum to a number.\n"),
9468 JS_FN_HELP("interruptRegexp", InterruptRegexp
, 2, 0,
9469 "interruptRegexp(<regexp>, <string>)",
9470 " Interrrupt the execution of regular expression.\n"),
9472 JS_FN_HELP("checkRegExpSyntax", CheckRegExpSyntax
, 1, 0,
9473 "checkRegExpSyntax(<string>)",
9474 " Return undefined if the string parses as a RegExp. If the string does not\n"
9475 " parse correctly, return the SyntaxError that occurred."),
9477 JS_FN_HELP("enableLastWarning", EnableLastWarning
, 0, 0,
9478 "enableLastWarning()",
9479 " Enable storing the last warning."),
9480 JS_FN_HELP("disableLastWarning", DisableLastWarning
, 0, 0,
9481 "disableLastWarning()",
9482 " Disable storing the last warning."),
9484 JS_FN_HELP("getLastWarning", GetLastWarning
, 0, 0,
9486 " Returns an object that represents the last warning."),
9488 JS_FN_HELP("clearLastWarning", ClearLastWarning
, 0, 0,
9489 "clearLastWarning()",
9490 " Clear the last warning."),
9492 JS_FN_HELP("elapsed", Elapsed
, 0, 0,
9494 " Execution time elapsed for the current thread."),
9496 JS_FN_HELP("decompileFunction", DecompileFunction
, 1, 0,
9497 "decompileFunction(func)",
9498 " Decompile a function."),
9500 JS_FN_HELP("decompileThis", DecompileThisScript
, 0, 0,
9502 " Decompile the currently executing script."),
9504 JS_FN_HELP("valueToSource", ValueToSource
, 1, 0,
9505 "valueToSource(value)",
9506 " Format a value for inspection."),
9508 JS_FN_HELP("thisFilename", ThisFilename
, 0, 0,
9510 " Return the filename of the current script"),
9512 JS_FN_HELP("newGlobal", NewGlobal
, 1, 0,
9513 "newGlobal([options])",
9514 " Return a new global object/realm. The new global is created in the\n"
9515 " 'newGlobal' function object's compartment and zone, unless the\n"
9516 " '--more-compartments' command-line flag was given, in which case new\n"
9517 " globals get a fresh compartment and zone. If options is given, it may\n"
9518 " have any of the following properties:\n"
9519 " sameCompartmentAs: If an object, the global will be in the same\n"
9520 " compartment and zone as the given object.\n"
9521 " sameZoneAs: The global will be in a new compartment in the same zone\n"
9522 " as the given object.\n"
9523 " newCompartment: If true, the global will always be created in a new\n"
9524 " compartment and zone.\n"
9525 " invisibleToDebugger: If true, the global will be invisible to the\n"
9526 " debugger (default false)\n"
9527 " discardSource: If true, discard source after compiling a script\n"
9528 " (default false).\n"
9529 " useWindowProxy: the global will be created with a WindowProxy attached. In this\n"
9530 " case, the WindowProxy will be returned.\n"
9531 " freezeBuiltins: certain builtin constructors will be frozen when created and\n"
9532 " their prototypes will be sealed. These constructors will be defined on the\n"
9533 " global as non-configurable and non-writable.\n"
9534 " immutablePrototype: whether the global's prototype is immutable.\n"
9535 " principal: if present, its value converted to a number must be an\n"
9536 " integer that fits in 32 bits; use that as the new realm's\n"
9537 " principal. Shell principals are toys, meant only for testing; one\n"
9538 " shell principal subsumes another if its set bits are a superset of\n"
9539 " the other's. Thus, a principal of 0 subsumes nothing, while a\n"
9540 " principals of ~0 subsumes all other principals. The absence of a\n"
9541 " principal is treated as if its bits were 0xffff, for subsumption\n"
9542 " purposes. If this property is omitted, supply no principal.\n"
9543 " systemPrincipal: If true, use the shell's trusted principals for the\n"
9544 " new realm. This creates a realm that's marked as a 'system' realm."),
9546 JS_FN_HELP("nukeAllCCWs", NukeAllCCWs
, 0, 0,
9548 " Like nukeCCW, but for all CrossCompartmentWrappers targeting the current realm."),
9550 JS_FN_HELP("recomputeWrappers", RecomputeWrappers
, 2, 0,
9551 "recomputeWrappers([src, [target]])",
9552 " Recompute all cross-compartment wrappers. src and target are both optional\n"
9553 " and can be used to filter source or target compartments: the unwrapped\n"
9554 " object's compartment is used as CompartmentFilter.\n"),
9556 JS_FN_HELP("dumpObjectWrappers", DumpObjectWrappers
, 2, 0,
9557 "dumpObjectWrappers()",
9558 " Print information about cross-compartment object wrappers.\n"),
9560 JS_FN_HELP("wrapWithProto", WrapWithProto
, 2, 0,
9561 "wrapWithProto(obj)",
9562 " Wrap an object into a noop wrapper with prototype semantics."),
9564 JS_FN_HELP("createExternalArrayBuffer", CreateExternalArrayBuffer
, 1, 0,
9565 "createExternalArrayBuffer(size)",
9566 " Create an array buffer that has external data of size."),
9568 JS_FN_HELP("createMappedArrayBuffer", CreateMappedArrayBuffer
, 1, 0,
9569 "createMappedArrayBuffer(filename, [offset, [size]])",
9570 " Create an array buffer that mmaps the given file."),
9572 JS_FN_HELP("createUserArrayBuffer", CreateUserArrayBuffer
, 1, 0,
9573 "createUserArrayBuffer(size)",
9574 " Create an array buffer that uses user-controlled memory."),
9576 JS_FN_HELP("addPromiseReactions", AddPromiseReactions
, 3, 0,
9577 "addPromiseReactions(promise, onResolve, onReject)",
9578 " Calls the JS::AddPromiseReactions JSAPI function with the given arguments."),
9580 JS_FN_HELP("ignoreUnhandledRejections", IgnoreUnhandledRejections
, 0, 0,
9581 "ignoreUnhandledRejections()",
9582 " By default, js shell tracks unhandled promise rejections and reports\n"
9583 " them at the end of the exectuion. If a testcase isn't interested\n"
9584 " in those rejections, call this to stop tracking and reporting."),
9586 JS_FN_HELP("getMaxArgs", GetMaxArgs
, 0, 0,
9588 " Return the maximum number of supported args for a call."),
9590 JS_FN_HELP("createIsHTMLDDA", CreateIsHTMLDDA
, 0, 0,
9591 "createIsHTMLDDA()",
9592 " Return an object |obj| that \"looks like\" the |document.all| object in\n"
9593 " browsers in certain ways: |typeof obj === \"undefined\"|, |obj == null|\n"
9594 " and |obj == undefined| (vice versa for !=), |ToBoolean(obj) === false|,\n"
9595 " and when called with no arguments or the single argument \"\" returns\n"
9596 " null. (Calling |obj| any other way crashes or throws an exception.)\n"
9597 " This function implements the exact requirements of the $262.IsHTMLDDA\n"
9598 " property in test262."),
9600 JS_FN_HELP("cacheEntry", CacheEntry
, 1, 0,
9602 " Return a new opaque object which emulates a cache entry of a script. This\n"
9603 " object encapsulates the code and its cached content. The cache entry is filled\n"
9604 " and read by the \"evaluate\" function by using it in place of the source, and\n"
9605 " by setting \"saveIncrementalBytecode\" and \"loadBytecode\" options."),
9607 JS_FN_HELP("streamCacheEntry", StreamCacheEntryObject::construct
, 1, 0,
9608 "streamCacheEntry(buffer)",
9609 " Create a shell-only object that holds wasm bytecode and can be streaming-\n"
9610 " compiled and cached by WebAssembly.{compile,instantiate}Streaming(). On a\n"
9611 " second compilation of the same cache entry, the cached code will be used."),
9613 JS_FN_HELP("printProfilerEvents", PrintProfilerEvents
, 0, 0,
9614 "printProfilerEvents()",
9615 " Register a callback with the profiler that prints javascript profiler events\n"
9616 " to stderr. Callback is only registered if profiling is enabled."),
9618 JS_FN_HELP("enableSingleStepProfiling", EnableSingleStepProfiling
, 0, 0,
9619 "enableSingleStepProfiling()",
9620 " This function will fail on platforms that don't support single-step profiling\n"
9621 " (currently ARM and MIPS64 support it). When enabled, at every instruction a\n"
9622 " backtrace will be recorded and stored in an array. Adjacent duplicate backtraces\n"
9625 JS_FN_HELP("disableSingleStepProfiling", DisableSingleStepProfiling
, 0, 0,
9626 "disableSingleStepProfiling()",
9627 " Return the array of backtraces recorded by enableSingleStepProfiling."),
9629 JS_FN_HELP("enableGeckoProfiling", EnableGeckoProfiling
, 0, 0,
9630 "enableGeckoProfiling()",
9631 " Enables Gecko Profiler instrumentation and corresponding assertions, with slow\n"
9632 " assertions disabled.\n"),
9634 JS_FN_HELP("enableGeckoProfilingWithSlowAssertions", EnableGeckoProfilingWithSlowAssertions
, 0, 0,
9635 "enableGeckoProfilingWithSlowAssertions()",
9636 " Enables Gecko Profiler instrumentation and corresponding assertions, with slow\n"
9637 " assertions enabled.\n"),
9639 JS_FN_HELP("disableGeckoProfiling", DisableGeckoProfiling
, 0, 0,
9640 "disableGeckoProfiling()",
9641 " Disables Gecko Profiler instrumentation"),
9643 JS_FN_HELP("isLatin1", IsLatin1
, 1, 0,
9645 " Return true iff the string's characters are stored as Latin1."),
9647 JS_FN_HELP("stackPointerInfo", StackPointerInfo
, 0, 0,
9648 "stackPointerInfo()",
9649 " Return an int32 value which corresponds to the offset of the latest stack\n"
9650 " pointer, such that one can take the differences of 2 to estimate a frame-size."),
9652 JS_FN_HELP("entryPoints", EntryPoints
, 1, 0,
9653 "entryPoints(params)",
9654 "Carry out some JSAPI operation as directed by |params|, and return an array of\n"
9655 "objects describing which JavaScript entry points were invoked as a result.\n"
9656 "|params| is an object whose properties indicate what operation to perform. Here\n"
9657 "are the recognized groups of properties:\n"
9659 "{ function }: Call the object |params.function| with no arguments.\n"
9661 "{ object, property }: Fetch the property named |params.property| of\n"
9662 "|params.object|.\n"
9664 "{ ToString }: Apply JS::ToString to |params.toString|.\n"
9666 "{ ToNumber }: Apply JS::ToNumber to |params.toNumber|.\n"
9668 "{ eval }: Apply JS::Evaluate to |params.eval|.\n"
9670 "The return value is an array of strings, with one element for each\n"
9671 "JavaScript invocation that occurred as a result of the given\n"
9672 "operation. Each element is the name of the function invoked, or the\n"
9673 "string 'eval:FILENAME' if the code was invoked by 'eval' or something\n"
9676 JS_FN_HELP("enqueueJob", EnqueueJob
, 1, 0,
9678 " Enqueue 'fn' on the shell's job queue."),
9680 JS_FN_HELP("globalOfFirstJobInQueue", GlobalOfFirstJobInQueue
, 0, 0,
9681 "globalOfFirstJobInQueue()",
9682 " Returns the global of the first item in the job queue. Throws an exception\n"
9683 " if the queue is empty.\n"),
9685 JS_FN_HELP("drainJobQueue", DrainJobQueue
, 0, 0,
9687 "Take jobs from the shell's job queue in FIFO order and run them until the\n"
9688 "queue is empty.\n"),
9690 JS_FN_HELP("setPromiseRejectionTrackerCallback", SetPromiseRejectionTrackerCallback
, 1, 0,
9691 "setPromiseRejectionTrackerCallback()",
9692 "Sets the callback to be invoked whenever a Promise rejection is unhandled\n"
9693 "or a previously-unhandled rejection becomes handled."),
9695 JS_FN_HELP("dumpScopeChain", DumpScopeChain
, 1, 0,
9696 "dumpScopeChain(obj)",
9697 " Prints the scope chain of an interpreted function or a module."),
9699 JS_FN_HELP("blackRoot", EnsureBlackRoot
, 0, 0,
9701 " Return an array in the current compartment whose elements will be marked\n"
9702 " as black roots by the GC."),
9704 JS_FN_HELP("grayRoot", EnsureGrayRoot
, 0, 0,
9706 " Return an array in the current compartment whose elements will be marked\n"
9707 " as gray roots by the GC."),
9709 JS_FN_HELP("addMarkObservers", AddMarkObservers
, 1, 0,
9710 "addMarkObservers(array_of_objects)",
9711 " Register an array of objects whose mark bits will be tested by calls to\n"
9712 " getMarks. The objects will be in calling compartment. Objects from\n"
9713 " multiple compartments may be monitored by calling this function in\n"
9714 " different compartments."),
9716 JS_FN_HELP("clearMarkObservers", ClearMarkObservers
, 1, 0,
9717 "clearMarkObservers()",
9718 " Clear out the list of objects whose mark bits will be tested.\n"),
9720 JS_FN_HELP("getMarks", GetMarks
, 0, 0,
9722 " Return an array of strings representing the current state of the mark\n"
9723 " bits ('gray' or 'black', or 'dead' if the object has been collected)\n"
9724 " for the objects registered via addMarkObservers. Note that some of the\n"
9725 " objects tested may be from different compartments than the one in which\n"
9726 " this function runs."),
9728 JS_FN_HELP("bindToAsyncStack", BindToAsyncStack
, 2, 0,
9729 "bindToAsyncStack(fn, { stack, cause, explicit })",
9730 " Returns a new function that calls 'fn' with no arguments, passing\n"
9731 " 'undefined' as the 'this' value, and supplies an async stack for the\n"
9732 " call as described by the second argument, an object with the following\n"
9733 " properties (which are not optional, unless specified otherwise):\n"
9735 " stack: A SavedFrame object, like that returned by 'saveStack'. Stacks\n"
9736 " captured during calls to the returned function capture this as\n"
9737 " their async stack parent, accessible via a SavedFrame's\n"
9738 " 'asyncParent' property.\n"
9740 " cause: A string, supplied as the async cause on the top frame of\n"
9741 " captured async stacks.\n"
9743 " explicit: A boolean value, indicating whether the given 'stack' should\n"
9744 " always supplant the returned function's true callers (true),\n"
9745 " or only when there are no other JavaScript frames on the stack\n"
9746 " below it (false). If omitted, this is treated as 'true'."),
9748 #ifdef JS_HAS_INTL_API
9749 JS_FN_HELP("addIntlExtras", AddIntlExtras
, 1, 0,
9750 "addIntlExtras(obj)",
9751 "Adds various not-yet-standardized Intl functions as properties on the\n"
9752 "provided object (this should generally be Intl itself). The added\n"
9753 "functions and their behavior are experimental: don't depend upon them\n"
9754 "unless you're willing to update your code if these experimental APIs change\n"
9756 #endif // JS_HAS_INTL_API
9759 JS_FN_HELP("wasmCompileInSeparateProcess", WasmCompileInSeparateProcess
, 1, 0,
9760 "wasmCompileInSeparateProcess(buffer)",
9761 " Compile the given buffer in a separate process, serialize the resulting\n"
9762 " wasm::Module into bytes, and deserialize those bytes in the current\n"
9763 " process, returning the resulting WebAssembly.Module."),
9765 JS_FN_HELP("wasmTextToBinary", WasmTextToBinary
, 1, 0,
9766 "wasmTextToBinary(str)",
9767 " Translates the given text wasm module into its binary encoding."),
9770 JS_FN_HELP("transplantableObject", TransplantableObject
, 0, 0,
9771 "transplantableObject([options])",
9772 " Returns the pair {object, transplant}. |object| is an object which can be\n"
9773 " transplanted into a new object when the |transplant| function, which must\n"
9774 " be invoked with a global object, is called.\n"
9775 " |object| is swapped with a cross-compartment wrapper if the global object\n"
9776 " is in a different compartment.\n"
9778 " If options is given, it may have any of the following properties:\n"
9779 " proxy: Create a DOM Proxy object instead of a plain DOM object.\n"
9780 " object: Don't create a new DOM object, but instead use the supplied\n"
9783 JS_FN_HELP("cpuNow", CpuNow
, /* nargs= */ 0, /* flags = */ 0,
9785 " Returns the approximate processor time used by the process since an arbitrary epoch, in seconds.\n"
9786 " Only the difference between two calls to `cpuNow()` is meaningful."),
9788 #ifdef FUZZING_JS_FUZZILLI
9789 JS_FN_HELP("fuzzilli", Fuzzilli
, 0, 0,
9790 "fuzzilli(operation, arg)",
9791 " Exposes functionality used by the Fuzzilli JavaScript fuzzer."),
9794 #ifdef FUZZING_INTERFACES
9795 JS_FN_HELP("getWasmSmithModule", GetWasmSmithModule
, 1, 0,
9796 "getWasmSmithModule(arrayBuffer)",
9797 " Call wasm-smith to generate a random wasm module from the provided data."),
9800 JS_FN_HELP("isValidJSON", IsValidJSON
, 1, 0,
9801 "isValidJSON(source)",
9802 " Returns true if the given source is valid JSON."),
9804 JS_FN_HELP("compressLZ4", CompressLZ4
, 1, 0,
9805 "compressLZ4(bytes)",
9806 " Return a compressed copy of bytes using LZ4."),
9808 JS_FN_HELP("decompressLZ4", DecompressLZ4
, 1, 0,
9809 "decompressLZ4(bytes)",
9810 " Return a decompressed copy of bytes using LZ4."),
9817 #ifdef FUZZING_JS_FUZZILLI
9818 static const JSFunctionSpec shell_function_fuzzilli_hash
[] = {
9819 JS_INLINABLE_FN("fuzzilli_hash", fuzzilli_hash
, 1, 0, FuzzilliHash
),
9826 static const JSFunctionSpecWithHelp diff_testing_unsafe_functions
[] = {
9833 static const JSFunctionSpecWithHelp fuzzing_unsafe_functions
[] = {
9834 JS_FN_HELP("getSelfHostedValue", GetSelfHostedValue
, 1, 0,
9835 "getSelfHostedValue()",
9836 " Get a self-hosted value by its name. Note that these values don't get \n"
9837 " cached, so repeatedly getting the same value creates multiple distinct clones."),
9839 JS_FN_HELP("line2pc", LineToPC
, 0, 0,
9840 "line2pc([fun,] line)",
9841 " Map line number to PC."),
9843 JS_FN_HELP("pc2line", PCToLine
, 0, 0,
9844 "pc2line(fun[, pc])",
9845 " Map PC to line number."),
9847 JS_INLINABLE_FN_HELP("assertFloat32", testingFunc_assertFloat32
, 2, 0, TestAssertFloat32
,
9848 "assertFloat32(value, isFloat32)",
9849 " In IonMonkey only, asserts that value has (resp. hasn't) the MIRType::Float32 if isFloat32 is true (resp. false)."),
9851 JS_INLINABLE_FN_HELP("assertRecoveredOnBailout", testingFunc_assertRecoveredOnBailout
, 2, 0,
9852 TestAssertRecoveredOnBailout
,
9853 "assertRecoveredOnBailout(var)",
9854 " In IonMonkey only, asserts that variable has RecoveredOnBailout flag."),
9856 JS_FN_HELP("withSourceHook", WithSourceHook
, 1, 0,
9857 "withSourceHook(hook, fun)",
9858 " Set this JS runtime's lazy source retrieval hook (that is, the hook\n"
9859 " used to find sources compiled with |CompileOptions::LAZY_SOURCE|) to\n"
9860 " |hook|; call |fun| with no arguments; and then restore the runtime's\n"
9861 " original hook. Return or throw whatever |fun| did. |hook| gets\n"
9862 " passed the requested code's URL, and should return a string.\n"
9866 " 1) SpiderMonkey may assert if the returned code isn't close enough\n"
9867 " to the script's real code, so this function is not fuzzer-safe.\n"
9869 " 2) The runtime can have only one source retrieval hook active at a\n"
9870 " time. If |fun| is not careful, |hook| could be asked to retrieve the\n"
9871 " source code for compilations that occurred long before it was set,\n"
9872 " and that it knows nothing about. The reverse applies as well: the\n"
9873 " original hook, that we reinstate after the call to |fun| completes,\n"
9874 " might be asked for the source code of compilations that |fun|\n"
9875 " performed, and which, presumably, only |hook| knows how to find.\n"),
9877 JS_FN_HELP("crash", Crash
, 0, 0,
9878 "crash([message, [{disable_minidump:true}]])",
9879 " Crashes the process with a MOZ_CRASH, optionally providing a message.\n"
9880 " An options object may be passed as the second argument. If the key\n"
9881 " 'suppress_minidump' is set to true, then a minidump will not be\n"
9882 " generated by the crash (which only has an effect if the breakpad\n"
9883 " dumping library is loaded.)"),
9886 JS_FN_HELP("wasmLoop", WasmLoop
, 2, 0,
9887 "wasmLoop(filename, imports)",
9888 " Performs an AFL-style persistent loop reading data from the given file and passing it\n"
9889 " to the 'wasmEval' function together with the specified imports object."),
9892 JS_FN_HELP("setBufferStreamParams", SetBufferStreamParams
, 2, 0,
9893 "setBufferStreamParams(delayMillis, chunkByteSize)",
9894 " Set the delay time (between calls to StreamConsumer::consumeChunk) and chunk\n"
9895 " size (in bytes)."),
9897 #ifdef JS_CACHEIR_SPEW
9898 JS_FN_HELP("cacheIRHealthReport", CacheIRHealthReport
, 0, 0,
9899 "cacheIRHealthReport()",
9900 " Show health rating of CacheIR stubs."),
9904 JS_FN_HELP("debugGetQueuedJobs", DebugGetQueuedJobs
, 0, 0,
9905 "debugGetQueuedJobs()",
9906 " Returns an array of queued jobs."),
9914 static const JSFunctionSpecWithHelp performance_functions
[] = {
9915 JS_FN_HELP("now", Now
, 0, 0,
9917 " Return the current time with sub-ms precision.\n"
9918 " This function is an alias of the dateNow() function."),
9924 static const JSFunctionSpecWithHelp console_functions
[] = {
9925 JS_FN_HELP("log", Print
, 0, 0,
9927 " Evaluate and print expressions to stdout.\n"
9928 " This function is an alias of the print() function."),
9933 bool DefineConsole(JSContext
* cx
, HandleObject global
) {
9934 RootedObject
obj(cx
, JS_NewPlainObject(cx
));
9935 return obj
&& JS_DefineFunctionsWithHelp(cx
, obj
, console_functions
) &&
9936 JS_DefineProperty(cx
, global
, "console", obj
, 0);
9939 #ifdef MOZ_PROFILING
9940 # define PROFILING_FUNCTION_COUNT 5
9941 # ifdef MOZ_CALLGRIND
9942 # define CALLGRIND_FUNCTION_COUNT 3
9944 # define CALLGRIND_FUNCTION_COUNT 0
9947 # define VTUNE_FUNCTION_COUNT 4
9949 # define VTUNE_FUNCTION_COUNT 0
9951 # define EXTERNAL_FUNCTION_COUNT \
9952 (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT)
9954 # define EXTERNAL_FUNCTION_COUNT 0
9957 #undef PROFILING_FUNCTION_COUNT
9958 #undef CALLGRIND_FUNCTION_COUNT
9959 #undef VTUNE_FUNCTION_COUNT
9960 #undef EXTERNAL_FUNCTION_COUNT
9962 static bool PrintHelpString(JSContext
* cx
, HandleValue v
) {
9963 RootedString
str(cx
, v
.toString());
9964 MOZ_ASSERT(gOutFile
->isOpen());
9966 UniqueChars bytes
= JS_EncodeStringToUTF8(cx
, str
);
9971 fprintf(gOutFile
->fp
, "%s\n", bytes
.get());
9975 static bool PrintHelp(JSContext
* cx
, HandleObject obj
) {
9976 RootedValue
usage(cx
);
9977 if (!JS_GetProperty(cx
, obj
, "usage", &usage
)) {
9980 RootedValue
help(cx
);
9981 if (!JS_GetProperty(cx
, obj
, "help", &help
)) {
9985 if (!usage
.isString() || !help
.isString()) {
9989 return PrintHelpString(cx
, usage
) && PrintHelpString(cx
, help
);
9992 struct ExtraGlobalBindingWithHelp
{
9998 static ExtraGlobalBindingWithHelp extraGlobalBindingsWithHelp
[] = {
9999 // Defined in BindScriptArgs.
10002 " An array containing the command line arguments passed after the path\n"
10003 " to a JS script."},
10006 " The path to the JS script passed to JS shell. This does not reflect\n"
10007 " modules evaluated via -m option."},
10009 // Defined in DefineConsole.
10012 " An object with console.log() which aliases print()."},
10014 // Defined in NewGlobalObject.
10017 " An object with the following properties:\n"
10018 " performance.now()\n"
10019 " See help(performance.now)\n"
10020 " performance.mozMemory.gc\n"
10021 " An object that represents GC statistics with the following properties:\n"
10025 " gcIsHighFrequencyMode\n"
10030 " compartmentCount\n"
10031 " lastStartReason\n"
10033 " zone.gcTriggerBytes\n"
10034 " zone.gcAllocTrigger\n"
10035 " zone.mallocBytes\n"
10036 " zone.mallocTriggerBytes\n"
10039 "new FakeDOMObject()",
10040 " A constructor to test IonMonkey DOM optimizations in JS shell.\n"
10041 " The prototype object has the following properties:\n"
10042 " FakeDOMObject.prototype.x\n"
10043 " Generic getter/setter with JSJitInfo\n"
10044 " FakeDOMObject.prototype.slot\n"
10045 " Getter with JSJitInfo.slotIndex\n"
10046 " FakeDOMObject.prototype.global\n"
10047 " Getter/setter with JSJitInfo::AliasEverything\n"
10048 " FakeDOMObject.prototype.doFoo()\n"
10049 " Method with JSJitInfo"},
10053 static bool MatchPattern(JSContext
* cx
, JS::Handle
<RegExpObject
*> regex
,
10054 JS::Handle
<JSString
*> inputStr
, bool* result
) {
10055 JS::Rooted
<JSString
*> linearInputStr(cx
, inputStr
);
10056 if (!linearInputStr
->ensureLinear(cx
)) {
10060 // Execute the regular expression in |regex|'s compartment.
10061 JSAutoRealm
ar(cx
, regex
);
10062 if (!cx
->compartment()->wrap(cx
, &linearInputStr
)) {
10065 JS::Rooted
<JSLinearString
*> input(cx
, &linearInputStr
->asLinear());
10066 size_t ignored
= 0;
10067 JS::Rooted
<JS::Value
> v(cx
);
10068 if (!ExecuteRegExpLegacy(cx
, nullptr, regex
, input
, &ignored
, true, &v
)) {
10071 *result
= !v
.isNull();
10075 static bool PrintEnumeratedHelp(JSContext
* cx
, HandleObject obj
,
10076 HandleObject pattern
, bool brief
) {
10077 RootedIdVector
idv(cx
);
10078 if (!GetPropertyKeys(cx
, obj
, JSITER_OWNONLY
| JSITER_HIDDEN
, &idv
)) {
10082 Rooted
<RegExpObject
*> regex(cx
);
10084 regex
= &UncheckedUnwrap(pattern
)->as
<RegExpObject
>();
10087 for (size_t i
= 0; i
< idv
.length(); i
++) {
10089 RootedId
id(cx
, idv
[i
]);
10090 if (!JS_GetPropertyById(cx
, obj
, id
, &v
)) {
10093 if (!v
.isObject()) {
10097 RootedObject
funcObj(cx
, &v
.toObject());
10099 // Only pay attention to objects with a 'help' property, which will
10100 // either be documented functions or interface objects.
10101 if (!JS_GetProperty(cx
, funcObj
, "help", &v
)) {
10104 if (!v
.isString()) {
10108 // For functions, match against the name. For interface objects,
10109 // match against the usage string.
10110 if (!JS_GetProperty(cx
, funcObj
, "name", &v
)) {
10113 if (!v
.isString()) {
10114 if (!JS_GetProperty(cx
, funcObj
, "usage", &v
)) {
10117 if (!v
.isString()) {
10122 Rooted
<JSString
*> inputStr(cx
, v
.toString());
10123 bool result
= false;
10124 if (!MatchPattern(cx
, regex
, inputStr
, &result
)) {
10132 if (!PrintHelp(cx
, funcObj
)) {
10140 static bool PrintExtraGlobalEnumeratedHelp(JSContext
* cx
, HandleObject pattern
,
10142 Rooted
<RegExpObject
*> regex(cx
);
10144 regex
= &UncheckedUnwrap(pattern
)->as
<RegExpObject
>();
10147 for (const auto& item
: extraGlobalBindingsWithHelp
) {
10149 JS::Rooted
<JSString
*> name(cx
, JS_NewStringCopyZ(cx
, item
.name
));
10154 bool result
= false;
10155 if (!MatchPattern(cx
, regex
, name
, &result
)) {
10162 fprintf(gOutFile
->fp
, "%s\n", item
.name
);
10163 fprintf(gOutFile
->fp
, "%s\n", item
.help
);
10169 static bool Help(JSContext
* cx
, unsigned argc
, Value
* vp
) {
10170 if (!gOutFile
->isOpen()) {
10171 JS_ReportErrorASCII(cx
, "output file is closed");
10175 CallArgs args
= CallArgsFromVp(argc
, vp
);
10176 args
.rval().setUndefined();
10177 RootedObject
global(cx
, JS::CurrentGlobalOrNull(cx
));
10179 // help() - display the version and dump out help for all functions on the
10181 if (args
.length() == 0) {
10182 fprintf(gOutFile
->fp
, "%s\n", JS_GetImplementationVersion());
10184 if (!PrintEnumeratedHelp(cx
, global
, nullptr, false)) {
10187 if (!PrintExtraGlobalEnumeratedHelp(cx
, nullptr, false)) {
10195 if (args
[0].isPrimitive()) {
10197 JS_ReportErrorASCII(cx
, "primitive arg");
10201 RootedObject
obj(cx
, &args
[0].toObject());
10206 if (!JS::ObjectIsRegExp(cx
, obj
, &isRegexp
)) {
10212 if (!PrintEnumeratedHelp(cx
, global
, obj
, false)) {
10215 if (!PrintExtraGlobalEnumeratedHelp(cx
, obj
, false)) {
10222 // help(namespace_obj)
10223 return PrintHelp(cx
, obj
);
10226 static const JSErrorFormatString jsShell_ErrorFormatString
[JSShellErr_Limit
] = {
10227 #define MSG_DEF(name, count, exception, format) \
10228 {#name, format, count, JSEXN_ERR},
10229 #include "jsshell.msg"
10233 const JSErrorFormatString
* js::shell::my_GetErrorMessage(
10234 void* userRef
, const unsigned errorNumber
) {
10235 if (errorNumber
== 0 || errorNumber
>= JSShellErr_Limit
) {
10239 return &jsShell_ErrorFormatString
[errorNumber
];
10242 static bool CreateLastWarningObject(JSContext
* cx
, JSErrorReport
* report
) {
10243 RootedObject
warningObj(cx
, JS_NewObject(cx
, nullptr));
10248 if (!CopyErrorReportToObject(cx
, report
, warningObj
)) {
10252 GetShellContext(cx
)->lastWarning
.setObject(*warningObj
);
10256 static FILE* ErrorFilePointer() {
10257 if (gErrFile
->isOpen()) {
10258 return gErrFile
->fp
;
10261 fprintf(stderr
, "error file is closed; falling back to stderr\n");
10265 bool shell::PrintStackTrace(JSContext
* cx
, HandleObject stackObj
) {
10266 if (!stackObj
|| !stackObj
->is
<SavedFrame
>()) {
10270 JSPrincipals
* principals
= stackObj
->nonCCWRealm()->principals();
10271 RootedString
stackStr(cx
);
10272 if (!BuildStackString(cx
, principals
, stackObj
, &stackStr
, 2)) {
10276 UniqueChars stack
= JS_EncodeStringToUTF8(cx
, stackStr
);
10281 FILE* fp
= ErrorFilePointer();
10282 fputs("Stack:\n", fp
);
10283 fputs(stack
.get(), fp
);
10288 js::shell::AutoReportException::~AutoReportException() {
10289 if (!JS_IsExceptionPending(cx
)) {
10293 auto printError
= [](JSContext
* cx
, auto& report
, const auto& exnStack
,
10294 const char* prefix
= nullptr) {
10295 if (!report
.init(cx
, exnStack
, JS::ErrorReportBuilder::WithSideEffects
)) {
10296 fprintf(stderr
, "out of memory initializing JS::ErrorReportBuilder\n");
10298 JS_ClearPendingException(cx
);
10302 MOZ_ASSERT(!report
.report()->isWarning());
10304 FILE* fp
= ErrorFilePointer();
10308 JS::PrintError(fp
, report
, reportWarnings
);
10309 JS_ClearPendingException(cx
);
10311 // If possible, use the original error stack as the source of truth, because
10312 // finally block handlers may have overwritten the exception stack.
10313 RootedObject
stack(cx
, exnStack
.stack());
10314 if (exnStack
.exception().isObject()) {
10315 RootedObject
exception(cx
, &exnStack
.exception().toObject());
10316 if (JSObject
* exceptionStack
= JS::ExceptionStackOrNull(exception
)) {
10317 stack
.set(exceptionStack
);
10321 if (!PrintStackTrace(cx
, stack
)) {
10322 fputs("(Unable to print stack trace)\n", fp
);
10323 JS_ClearPendingException(cx
);
10329 // Get exception object and stack before printing and clearing exception.
10330 JS::ExceptionStack
exnStack(cx
);
10331 if (!JS::StealPendingExceptionStack(cx
, &exnStack
)) {
10332 fprintf(stderr
, "out of memory while stealing exception\n");
10334 JS_ClearPendingException(cx
);
10338 ShellContext
* sc
= GetShellContext(cx
);
10339 JS::ErrorReportBuilder
report(cx
);
10340 if (!printError(cx
, report
, exnStack
)) {
10341 // Return if we couldn't initialize the error report.
10345 // Print the error's cause, if available.
10346 if (exnStack
.exception().isObject()) {
10347 JSObject
* exception
= &exnStack
.exception().toObject();
10348 if (exception
->is
<ErrorObject
>()) {
10349 auto* error
= &exception
->as
<ErrorObject
>();
10350 if (auto maybeCause
= error
->getCause()) {
10351 RootedValue
cause(cx
, maybeCause
.value());
10353 RootedObject
causeStack(cx
);
10354 if (cause
.isObject()) {
10355 RootedObject
causeObj(cx
, &cause
.toObject());
10356 causeStack
= JS::ExceptionStackOrNull(causeObj
);
10359 JS::ExceptionStack
exnStack(cx
, cause
, causeStack
);
10360 JS::ErrorReportBuilder
report(cx
);
10361 printError(cx
, report
, exnStack
, "Caused by: ");
10366 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
10367 // Don't quit the shell if an unhandled exception is reported during OOM
10369 if (cx
->runningOOMTest
) {
10374 if (report
.report()->errorNumber
== JSMSG_OUT_OF_MEMORY
) {
10375 sc
->exitCode
= EXITCODE_OUT_OF_MEMORY
;
10377 sc
->exitCode
= EXITCODE_RUNTIME_ERROR
;
10381 void js::shell::WarningReporter(JSContext
* cx
, JSErrorReport
* report
) {
10382 ShellContext
* sc
= GetShellContext(cx
);
10383 FILE* fp
= ErrorFilePointer();
10385 MOZ_ASSERT(report
->isWarning());
10387 if (sc
->lastWarningEnabled
) {
10388 JS::AutoSaveExceptionState
savedExc(cx
);
10389 if (!CreateLastWarningObject(cx
, report
)) {
10390 fputs("Unhandled error happened while creating last warning object.\n",
10394 savedExc
.restore();
10397 // Print the warning.
10398 JS::PrintError(fp
, report
, reportWarnings
);
10401 static bool global_enumerate(JSContext
* cx
, JS::HandleObject obj
,
10402 JS::MutableHandleIdVector properties
,
10403 bool enumerableOnly
) {
10404 #ifdef LAZY_STANDARD_CLASSES
10405 return JS_NewEnumerateStandardClasses(cx
, obj
, properties
, enumerableOnly
);
10411 static bool global_resolve(JSContext
* cx
, HandleObject obj
, HandleId id
,
10413 #ifdef LAZY_STANDARD_CLASSES
10414 if (!JS_ResolveStandardClass(cx
, obj
, id
, resolvedp
)) {
10421 static bool global_mayResolve(const JSAtomState
& names
, jsid id
,
10422 JSObject
* maybeObj
) {
10423 return JS_MayResolveStandardClass(names
, id
, maybeObj
);
10426 static const JSClassOps global_classOps
= {
10427 nullptr, // addProperty
10428 nullptr, // delProperty
10429 nullptr, // enumerate
10430 global_enumerate
, // newEnumerate
10431 global_resolve
, // resolve
10432 global_mayResolve
, // mayResolve
10433 nullptr, // finalize
10435 nullptr, // construct
10436 JS_GlobalObjectTraceHook
, // trace
10439 static constexpr uint32_t DOM_PROTOTYPE_SLOT
= JSCLASS_GLOBAL_SLOT_COUNT
;
10440 static constexpr uint32_t DOM_GLOBAL_SLOTS
= 1;
10442 static const JSClass global_class
= {
10444 JSCLASS_GLOBAL_FLAGS
| JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS
),
10448 * Define a FakeDOMObject constructor. It returns an object with a getter,
10449 * setter and method with attached JitInfo. This object can be used to test
10450 * IonMonkey DOM optimizations in the shell.
10453 /* Fow now just use to a constant we can check. */
10454 static const void* DOM_PRIVATE_VALUE
= (void*)0x1234;
10456 static bool dom_genericGetter(JSContext
* cx
, unsigned argc
, JS::Value
* vp
);
10458 static bool dom_genericSetter(JSContext
* cx
, unsigned argc
, JS::Value
* vp
);
10460 static bool dom_genericMethod(JSContext
* cx
, unsigned argc
, JS::Value
* vp
);
10462 static bool dom_get_x(JSContext
* cx
, HandleObject obj
, void* self
,
10463 JSJitGetterCallArgs args
) {
10464 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10465 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10466 args
.rval().set(JS_NumberValue(double(3.14)));
10470 static bool dom_set_x(JSContext
* cx
, HandleObject obj
, void* self
,
10471 JSJitSetterCallArgs args
) {
10472 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10473 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10477 static bool dom_get_slot(JSContext
* cx
, HandleObject obj
, void* self
,
10478 JSJitGetterCallArgs args
) {
10479 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10480 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10482 Value v
= JS::GetReservedSlot(obj
, DOM_OBJECT_SLOT2
);
10483 MOZ_ASSERT(v
.toInt32() == 42);
10484 args
.rval().set(v
);
10488 static bool dom_get_global(JSContext
* cx
, HandleObject obj
, void* self
,
10489 JSJitGetterCallArgs args
) {
10490 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10491 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10493 // Return the current global (instead of obj->global()) to test cx->realm
10494 // switching in the JIT.
10495 args
.rval().setObject(*ToWindowProxyIfWindow(cx
->global()));
10500 static bool dom_set_global(JSContext
* cx
, HandleObject obj
, void* self
,
10501 JSJitSetterCallArgs args
) {
10502 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10503 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10505 // Throw an exception if our argument is not the current global. This lets
10506 // us test cx->realm switching.
10507 if (!args
[0].isObject() ||
10508 ToWindowIfWindowProxy(&args
[0].toObject()) != cx
->global()) {
10509 JS_ReportErrorASCII(cx
, "Setter not called with matching global argument");
10516 static bool dom_doFoo(JSContext
* cx
, HandleObject obj
, void* self
,
10517 const JSJitMethodCallArgs
& args
) {
10518 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10519 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10520 MOZ_ASSERT(cx
->realm() == args
.callee().as
<JSFunction
>().realm());
10522 /* Just return args.length(). */
10523 args
.rval().setInt32(args
.length());
10527 static const JSJitInfo dom_x_getterinfo
= {
10528 {(JSJitGetterOp
)dom_get_x
},
10532 JSJitInfo::AliasNone
, /* aliasSet */
10533 JSVAL_TYPE_UNKNOWN
, /* returnType */
10534 true, /* isInfallible. False in setters. */
10535 true, /* isMovable */
10536 true, /* isEliminatable */
10537 false, /* isAlwaysInSlot */
10538 false, /* isLazilyCachedInSlot */
10539 false, /* isTypedMethod */
10543 static const JSJitInfo dom_x_setterinfo
= {
10544 {(JSJitGetterOp
)dom_set_x
},
10548 JSJitInfo::AliasEverything
, /* aliasSet */
10549 JSVAL_TYPE_UNKNOWN
, /* returnType */
10550 false, /* isInfallible. False in setters. */
10551 false, /* isMovable. */
10552 false, /* isEliminatable. */
10553 false, /* isAlwaysInSlot */
10554 false, /* isLazilyCachedInSlot */
10555 false, /* isTypedMethod */
10559 static const JSJitInfo dom_slot_getterinfo
= {
10560 {(JSJitGetterOp
)dom_get_slot
},
10564 JSJitInfo::AliasNone
, /* aliasSet */
10565 JSVAL_TYPE_INT32
, /* returnType */
10566 false, /* isInfallible. False in setters. */
10567 true, /* isMovable */
10568 true, /* isEliminatable */
10569 true, /* isAlwaysInSlot */
10570 false, /* isLazilyCachedInSlot */
10571 false, /* isTypedMethod */
10572 DOM_OBJECT_SLOT2
/* slotIndex */
10575 // Note: this getter uses AliasEverything and is marked as fallible and
10576 // non-movable (1) to prevent Ion from getting too clever optimizing it and
10577 // (2) it's nice to have a few different kinds of getters in the shell.
10578 static const JSJitInfo dom_global_getterinfo
= {
10579 {(JSJitGetterOp
)dom_get_global
},
10583 JSJitInfo::AliasEverything
, /* aliasSet */
10584 JSVAL_TYPE_OBJECT
, /* returnType */
10585 false, /* isInfallible. False in setters. */
10586 false, /* isMovable */
10587 false, /* isEliminatable */
10588 false, /* isAlwaysInSlot */
10589 false, /* isLazilyCachedInSlot */
10590 false, /* isTypedMethod */
10594 static const JSJitInfo dom_global_setterinfo
= {
10595 {(JSJitGetterOp
)dom_set_global
},
10599 JSJitInfo::AliasEverything
, /* aliasSet */
10600 JSVAL_TYPE_UNKNOWN
, /* returnType */
10601 false, /* isInfallible. False in setters. */
10602 false, /* isMovable. */
10603 false, /* isEliminatable. */
10604 false, /* isAlwaysInSlot */
10605 false, /* isLazilyCachedInSlot */
10606 false, /* isTypedMethod */
10610 static const JSJitInfo doFoo_methodinfo
= {
10611 {(JSJitGetterOp
)dom_doFoo
},
10615 JSJitInfo::AliasEverything
, /* aliasSet */
10616 JSVAL_TYPE_UNKNOWN
, /* returnType */
10617 false, /* isInfallible. False in setters. */
10618 false, /* isMovable */
10619 false, /* isEliminatable */
10620 false, /* isAlwaysInSlot */
10621 false, /* isLazilyCachedInSlot */
10622 false, /* isTypedMethod */
10626 static const JSPropertySpec dom_props
[] = {
10627 JSPropertySpec::nativeAccessors("x", JSPROP_ENUMERATE
, dom_genericGetter
,
10628 &dom_x_getterinfo
, dom_genericSetter
,
10629 &dom_x_setterinfo
),
10630 JSPropertySpec::nativeAccessors("slot", JSPROP_ENUMERATE
, dom_genericGetter
,
10631 &dom_slot_getterinfo
),
10632 JSPropertySpec::nativeAccessors("global", JSPROP_ENUMERATE
,
10633 dom_genericGetter
, &dom_global_getterinfo
,
10634 dom_genericSetter
, &dom_global_setterinfo
),
10637 static const JSFunctionSpec dom_methods
[] = {
10638 JS_FNINFO("doFoo", dom_genericMethod
, &doFoo_methodinfo
, 3,
10642 static const JSClass dom_class
= {
10643 "FakeDOMObject", JSCLASS_IS_DOMJSCLASS
| JSCLASS_HAS_RESERVED_SLOTS(2)};
10645 static const JSClass
* GetDomClass() { return &dom_class
; }
10647 static bool dom_genericGetter(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
10648 CallArgs args
= CallArgsFromVp(argc
, vp
);
10650 if (!args
.thisv().isObject()) {
10651 args
.rval().setUndefined();
10655 RootedObject
obj(cx
, &args
.thisv().toObject());
10656 if (JS::GetClass(obj
) != &dom_class
) {
10657 args
.rval().set(UndefinedValue());
10661 JS::Value val
= JS::GetReservedSlot(obj
, DOM_OBJECT_SLOT
);
10663 const JSJitInfo
* info
= FUNCTION_VALUE_TO_JITINFO(args
.calleev());
10664 MOZ_ASSERT(info
->type() == JSJitInfo::Getter
);
10665 JSJitGetterOp getter
= info
->getter
;
10666 return getter(cx
, obj
, val
.toPrivate(), JSJitGetterCallArgs(args
));
10669 static bool dom_genericSetter(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
10670 CallArgs args
= CallArgsFromVp(argc
, vp
);
10672 if (args
.length() < 1 || !args
.thisv().isObject()) {
10673 args
.rval().setUndefined();
10677 RootedObject
obj(cx
, &args
.thisv().toObject());
10678 if (JS::GetClass(obj
) != &dom_class
) {
10679 args
.rval().set(UndefinedValue());
10683 JS::Value val
= JS::GetReservedSlot(obj
, DOM_OBJECT_SLOT
);
10685 const JSJitInfo
* info
= FUNCTION_VALUE_TO_JITINFO(args
.calleev());
10686 MOZ_ASSERT(info
->type() == JSJitInfo::Setter
);
10687 JSJitSetterOp setter
= info
->setter
;
10688 if (!setter(cx
, obj
, val
.toPrivate(), JSJitSetterCallArgs(args
))) {
10691 args
.rval().set(UndefinedValue());
10695 static bool dom_genericMethod(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
10696 CallArgs args
= CallArgsFromVp(argc
, vp
);
10698 if (!args
.thisv().isObject()) {
10699 args
.rval().setUndefined();
10703 RootedObject
obj(cx
, &args
.thisv().toObject());
10704 if (JS::GetClass(obj
) != &dom_class
) {
10705 args
.rval().set(UndefinedValue());
10709 JS::Value val
= JS::GetReservedSlot(obj
, DOM_OBJECT_SLOT
);
10711 const JSJitInfo
* info
= FUNCTION_VALUE_TO_JITINFO(args
.calleev());
10712 MOZ_ASSERT(info
->type() == JSJitInfo::Method
);
10713 JSJitMethodOp method
= info
->method
;
10714 return method(cx
, obj
, val
.toPrivate(), JSJitMethodCallArgs(args
));
10717 static void InitDOMObject(HandleObject obj
) {
10718 JS::SetReservedSlot(obj
, DOM_OBJECT_SLOT
,
10719 PrivateValue(const_cast<void*>(DOM_PRIVATE_VALUE
)));
10720 JS::SetReservedSlot(obj
, DOM_OBJECT_SLOT2
, Int32Value(42));
10723 static JSObject
* GetDOMPrototype(JSContext
* cx
, JSObject
* global
) {
10724 MOZ_ASSERT(JS_IsGlobalObject(global
));
10725 if (JS::GetClass(global
) != &global_class
) {
10726 JS_ReportErrorASCII(cx
, "Can't get FakeDOMObject prototype in sandbox");
10730 const JS::Value
& slot
= JS::GetReservedSlot(global
, DOM_PROTOTYPE_SLOT
);
10731 MOZ_ASSERT(slot
.isObject());
10732 return &slot
.toObject();
10735 static bool dom_constructor(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
10736 CallArgs args
= CallArgsFromVp(argc
, vp
);
10738 RootedObject
callee(cx
, &args
.callee());
10739 RootedValue
protov(cx
);
10740 if (!GetProperty(cx
, callee
, callee
, cx
->names().prototype
, &protov
)) {
10744 if (!protov
.isObject()) {
10745 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_BAD_PROTOTYPE
,
10750 RootedObject
proto(cx
, &protov
.toObject());
10751 RootedObject
domObj(cx
, JS_NewObjectWithGivenProto(cx
, &dom_class
, proto
));
10756 InitDOMObject(domObj
);
10758 args
.rval().setObject(*domObj
);
10762 static bool InstanceClassHasProtoAtDepth(const JSClass
* clasp
, uint32_t protoID
,
10764 // Only the (fake) DOM object supports any JIT optimizations.
10765 return clasp
== GetDomClass();
10768 static bool ShellBuildId(JS::BuildIdCharVector
* buildId
) {
10769 // The browser embeds the date into the buildid and the buildid is embedded
10770 // in the binary, so every 'make' necessarily builds a new firefox binary.
10771 // Fortunately, the actual firefox executable is tiny -- all the code is in
10772 // libxul.so and other shared modules -- so this isn't a big deal. Not so
10773 // for the statically-linked JS shell. To avoid recompiling js.cpp and
10774 // re-linking 'js' on every 'make', we use a constant buildid and rely on
10775 // the shell user to manually clear any caches between cache-breaking updates.
10776 const char buildid
[] = "JS-shell";
10777 return buildId
->append(buildid
, sizeof(buildid
));
10780 static bool TimesAccessed(JSContext
* cx
, unsigned argc
, Value
* vp
) {
10781 static int32_t accessed
= 0;
10782 CallArgs args
= CallArgsFromVp(argc
, vp
);
10783 args
.rval().setInt32(++accessed
);
10787 static const JSPropertySpec TestingProperties
[] = {
10788 JS_PSG("timesAccessed", TimesAccessed
, 0), JS_PS_END
};
10790 static JSObject
* NewGlobalObject(JSContext
* cx
, JS::RealmOptions
& options
,
10791 JSPrincipals
* principals
, ShellGlobalKind kind
,
10792 bool immutablePrototype
) {
10793 RootedObject
glob(cx
,
10794 JS_NewGlobalObject(cx
, &global_class
, principals
,
10795 JS::DontFireOnNewGlobalHook
, options
));
10801 JSAutoRealm
ar(cx
, glob
);
10803 if (kind
== ShellGlobalKind::WindowProxy
) {
10804 RootedObject
proxy(cx
, NewShellWindowProxy(cx
, glob
));
10808 js::SetWindowProxy(cx
, glob
, proxy
);
10811 #ifndef LAZY_STANDARD_CLASSES
10812 if (!JS::InitRealmStandardClasses(cx
)) {
10817 if (immutablePrototype
) {
10819 if (!JS_SetImmutablePrototype(cx
, glob
, &succeeded
)) {
10822 MOZ_ASSERT(succeeded
,
10823 "a fresh, unexposed global object is always capable of "
10824 "having its [[Prototype]] be immutable");
10827 #ifdef JS_HAS_CTYPES
10828 if (!fuzzingSafe
&& !JS::InitCTypesClass(cx
, glob
)) {
10832 if (!JS_InitReflectParse(cx
, glob
)) {
10835 if (!JS_DefineDebuggerObject(cx
, glob
)) {
10838 if (!JS_DefineFunctionsWithHelp(cx
, glob
, shell_functions
) ||
10839 !JS_DefineProfilingFunctions(cx
, glob
)) {
10842 #ifdef FUZZING_JS_FUZZILLI
10843 if (!JS_DefineFunctions(cx
, glob
, shell_function_fuzzilli_hash
)) {
10847 if (!js::DefineTestingFunctions(cx
, glob
, fuzzingSafe
,
10848 disableOOMFunctions
)) {
10851 if (!JS_DefineProperties(cx
, glob
, TestingProperties
)) {
10855 if (!fuzzingSafe
) {
10856 if (!JS_DefineFunctionsWithHelp(cx
, glob
, fuzzing_unsafe_functions
)) {
10859 if (!DefineConsole(cx
, glob
)) {
10864 if (!DefineOS(cx
, glob
, fuzzingSafe
, &gOutFile
, &gErrFile
)) {
10868 if (!js::SupportDifferentialTesting()) {
10869 if (!JS_DefineFunctionsWithHelp(cx
, glob
,
10870 diff_testing_unsafe_functions
)) {
10874 RootedObject
performanceObj(cx
, JS_NewObject(cx
, nullptr));
10875 if (!performanceObj
) {
10878 if (!JS_DefineFunctionsWithHelp(cx
, performanceObj
,
10879 performance_functions
)) {
10882 RootedObject
mozMemoryObj(cx
, JS_NewObject(cx
, nullptr));
10883 if (!mozMemoryObj
) {
10886 RootedObject
gcObj(cx
, gc::NewMemoryInfoObject(cx
));
10890 if (!JS_DefineProperty(cx
, glob
, "performance", performanceObj
,
10891 JSPROP_ENUMERATE
)) {
10894 if (!JS_DefineProperty(cx
, performanceObj
, "mozMemory", mozMemoryObj
,
10895 JSPROP_ENUMERATE
)) {
10898 if (!JS_DefineProperty(cx
, mozMemoryObj
, "gc", gcObj
, JSPROP_ENUMERATE
)) {
10903 /* Initialize FakeDOMObject. */
10904 static const js::DOMCallbacks DOMcallbacks
= {InstanceClassHasProtoAtDepth
};
10905 SetDOMCallbacks(cx
, &DOMcallbacks
);
10907 RootedObject
domProto(
10908 cx
, JS_InitClass(cx
, glob
, &dom_class
, nullptr, "FakeDOMObject",
10909 dom_constructor
, 0, dom_props
, dom_methods
, nullptr,
10915 // FakeDOMObject.prototype is the only DOM object which needs to retrieved
10916 // in the shell; store it directly instead of creating a separate layer
10917 // (ProtoAndIfaceCache) as done in the browser.
10918 JS::SetReservedSlot(glob
, DOM_PROTOTYPE_SLOT
, ObjectValue(*domProto
));
10920 /* Initialize FakeDOMObject.prototype */
10921 InitDOMObject(domProto
);
10923 if (!DefineToStringTag(cx
, glob
, cx
->names().global
)) {
10927 JS_FireOnNewGlobalObject(cx
, glob
);
10933 static bool BindScriptArgs(JSContext
* cx
, OptionParser
* op
) {
10934 AutoReportException
are(cx
);
10936 MultiStringRange msr
= op
->getMultiStringArg("scriptArgs");
10937 RootedObject
scriptArgs(cx
);
10938 scriptArgs
= JS::NewArrayObject(cx
, 0);
10943 if (!JS_DefineProperty(cx
, cx
->global(), "scriptArgs", scriptArgs
, 0)) {
10947 for (size_t i
= 0; !msr
.empty(); msr
.popFront(), ++i
) {
10948 const char* scriptArg
= msr
.front();
10949 UniqueChars scriptArgUtf8
= JS::EncodeNarrowToUtf8(cx
, scriptArg
);
10950 if (!scriptArgUtf8
) {
10953 RootedString
str(cx
, NewStringCopyUTF8(cx
, scriptArgUtf8
.get()));
10954 if (!str
|| !JS_DefineElement(cx
, scriptArgs
, i
, str
, JSPROP_ENUMERATE
)) {
10959 RootedValue
scriptPathValue(cx
);
10960 if (const char* scriptPath
= op
->getStringArg("script")) {
10961 UniqueChars scriptPathUtf8
= JS::EncodeNarrowToUtf8(cx
, scriptPath
);
10962 if (!scriptPathUtf8
) {
10965 RootedString
scriptPathString(cx
,
10966 NewStringCopyUTF8(cx
, scriptPathUtf8
.get()));
10967 if (!scriptPathString
) {
10970 scriptPathValue
= StringValue(scriptPathString
);
10972 scriptPathValue
= UndefinedValue();
10975 if (!JS_DefineProperty(cx
, cx
->global(), "scriptPath", scriptPathValue
, 0)) {
10982 static bool OptionFailure(const char* option
, const char* str
) {
10983 fprintf(stderr
, "Unrecognized option for %s: %s\n", option
, str
);
10987 template <typename
... Ts
>
10988 auto minVal(Ts
... args
);
10989 template <typename T
>
10994 template <typename T
, typename
... Ts
>
10995 auto minVal(T a
, Ts
... args
) {
10996 return std::min(a
, minVal(args
...));
10999 [[nodiscard
]] static bool ProcessArgs(JSContext
* cx
, OptionParser
* op
) {
11000 ShellContext
* sc
= GetShellContext(cx
);
11002 /* |scriptArgs| gets bound on the global before any code is run. */
11003 if (!BindScriptArgs(cx
, op
)) {
11007 MultiStringRange filePaths
= op
->getMultiStringOption('f');
11008 MultiStringRange utf16FilePaths
= op
->getMultiStringOption('u');
11009 MultiStringRange preludePaths
= op
->getMultiStringOption('p');
11010 MultiStringRange codeChunks
= op
->getMultiStringOption('e');
11011 MultiStringRange modulePaths
= op
->getMultiStringOption('m');
11013 #ifdef FUZZING_JS_FUZZILLI
11014 // Check for REPRL file source
11015 if (op
->getBoolOption("reprl")) {
11016 return FuzzilliReprlGetAndRun(cx
);
11018 #endif /* FUZZING_JS_FUZZILLI */
11020 if (filePaths
.empty() && utf16FilePaths
.empty() && codeChunks
.empty() &&
11021 modulePaths
.empty() && !op
->getStringArg("script")) {
11022 // Always use the interactive shell when -i is used. Without -i we let
11023 // Process figure it out based on isatty.
11024 bool forceTTY
= op
->getBoolOption('i');
11025 return Process(cx
, nullptr, forceTTY
, FileScript
);
11028 while (!preludePaths
.empty() || !filePaths
.empty() ||
11029 !utf16FilePaths
.empty() || !codeChunks
.empty() ||
11030 !modulePaths
.empty()) {
11031 size_t ppArgno
= preludePaths
.empty() ? SIZE_MAX
: preludePaths
.argno();
11032 size_t fpArgno
= filePaths
.empty() ? SIZE_MAX
: filePaths
.argno();
11034 utf16FilePaths
.empty() ? SIZE_MAX
: utf16FilePaths
.argno();
11035 size_t ccArgno
= codeChunks
.empty() ? SIZE_MAX
: codeChunks
.argno();
11036 size_t mpArgno
= modulePaths
.empty() ? SIZE_MAX
: modulePaths
.argno();
11037 size_t minArgno
= minVal(ppArgno
, fpArgno
, ufpArgno
, ccArgno
, mpArgno
);
11039 if (ppArgno
== minArgno
) {
11040 UniqueChars path
= JS::EncodeNarrowToUtf8(cx
, preludePaths
.front());
11044 if (!Process(cx
, path
.get(), false, PreludeScript
)) {
11048 preludePaths
.popFront();
11052 if (fpArgno
== minArgno
) {
11053 UniqueChars path
= JS::EncodeNarrowToUtf8(cx
, filePaths
.front());
11057 if (!Process(cx
, path
.get(), false, FileScript
)) {
11061 filePaths
.popFront();
11065 if (ufpArgno
== minArgno
) {
11066 UniqueChars path
= JS::EncodeNarrowToUtf8(cx
, utf16FilePaths
.front());
11070 if (!Process(cx
, path
.get(), false, FileScriptUtf16
)) {
11074 utf16FilePaths
.popFront();
11078 if (ccArgno
== minArgno
) {
11079 UniqueChars code
= JS::EncodeNarrowToUtf8(cx
, codeChunks
.front());
11084 // Command line scripts are always parsed with full-parse to evaluate
11085 // conditions which might filter code coverage conditions.
11086 JS::CompileOptions
opts(cx
);
11087 opts
.setFileAndLine("-e", 1).setForceFullParse();
11089 JS::SourceText
<Utf8Unit
> srcBuf
;
11090 if (!srcBuf
.init(cx
, code
.get(), strlen(code
.get()),
11091 JS::SourceOwnership::Borrowed
)) {
11095 RootedValue
rval(cx
);
11096 if (!JS::Evaluate(cx
, opts
, srcBuf
, &rval
)) {
11100 codeChunks
.popFront();
11101 if (sc
->quitting
) {
11108 MOZ_ASSERT(mpArgno
== minArgno
);
11110 UniqueChars path
= JS::EncodeNarrowToUtf8(cx
, modulePaths
.front());
11114 if (!Process(cx
, path
.get(), false, FileModule
)) {
11118 modulePaths
.popFront();
11121 if (sc
->quitting
) {
11125 /* The |script| argument is processed after all options. */
11126 if (const char* path
= op
->getStringArg("script")) {
11127 UniqueChars pathUtf8
= JS::EncodeNarrowToUtf8(cx
, path
);
11131 if (!Process(cx
, pathUtf8
.get(), false, FileScript
)) {
11136 if (op
->getBoolOption('i')) {
11137 if (!Process(cx
, nullptr, true, FileScript
)) {
11145 static void SetWorkerContextOptions(JSContext
* cx
) {
11146 // Copy option values from the main thread.
11147 JS::ContextOptionsRef(cx
)
11148 .setAsmJS(enableAsmJS
)
11149 .setWasm(enableWasm
)
11150 .setWasmBaseline(enableWasmBaseline
)
11151 .setWasmIon(enableWasmOptimizing
)
11153 .setWasmVerbose(enableWasmVerbose
)
11154 .setTestWasmAwaitTier2(enableTestWasmAwaitTier2
)
11155 .setSourcePragmas(enableSourcePragmas
);
11157 cx
->runtime()->setOffthreadIonCompilationEnabled(offthreadCompilation
);
11158 cx
->runtime()->profilingScripts
=
11159 enableCodeCoverage
|| enableDisassemblyDumps
;
11162 if (gZealBits
&& gZealFrequency
) {
11163 for (size_t i
= 0; i
< size_t(gc::ZealMode::Count
); i
++) {
11164 if (gZealBits
& (1 << i
)) {
11165 cx
->runtime()->gc
.setZeal(i
, gZealFrequency
);
11171 JS_SetNativeStackQuota(cx
, gWorkerStackSize
);
11174 [[nodiscard
]] static bool PrintUnhandledRejection(
11175 JSContext
* cx
, Handle
<PromiseObject
*> promise
) {
11176 RootedValue
reason(cx
, promise
->reason());
11177 RootedObject
site(cx
, promise
->resolutionSite());
11179 RootedString
str(cx
, JS_ValueToSource(cx
, reason
));
11184 UniqueChars utf8chars
= JS_EncodeStringToUTF8(cx
, str
);
11189 FILE* fp
= ErrorFilePointer();
11190 fprintf(fp
, "Unhandled rejection: %s\n", utf8chars
.get());
11193 fputs("(no stack trace available)\n", stderr
);
11197 JSPrincipals
* principals
= cx
->realm()->principals();
11198 RootedString
stackStr(cx
);
11199 if (!BuildStackString(cx
, principals
, site
, &stackStr
, 2)) {
11203 UniqueChars stack
= JS_EncodeStringToUTF8(cx
, stackStr
);
11208 fputs("Stack:\n", fp
);
11209 fputs(stack
.get(), fp
);
11214 [[nodiscard
]] static bool ReportUnhandledRejections(JSContext
* cx
) {
11215 ShellContext
* sc
= GetShellContext(cx
);
11216 if (!sc
->trackUnhandledRejections
) {
11220 if (!sc
->unhandledRejectedPromises
) {
11224 AutoRealm
ar(cx
, sc
->unhandledRejectedPromises
);
11226 if (!SetObject::size(cx
, sc
->unhandledRejectedPromises
)) {
11230 sc
->exitCode
= EXITCODE_RUNTIME_ERROR
;
11232 RootedValue
iter(cx
);
11233 if (!SetObject::iterator(cx
, SetObject::IteratorKind::Values
,
11234 sc
->unhandledRejectedPromises
, &iter
)) {
11238 Rooted
<SetIteratorObject
*> iterObj(cx
,
11239 &iter
.toObject().as
<SetIteratorObject
>());
11240 JSObject
* obj
= SetIteratorObject::createResult(cx
);
11245 Rooted
<ArrayObject
*> resultObj(cx
, &obj
->as
<ArrayObject
>());
11247 bool done
= SetIteratorObject::next(iterObj
, resultObj
);
11252 RootedObject
obj(cx
, &resultObj
->getDenseElement(0).toObject());
11253 Rooted
<PromiseObject
*> promise(cx
, obj
->maybeUnwrapIf
<PromiseObject
>());
11255 FILE* fp
= ErrorFilePointer();
11257 "Unhandled rejection: dead proxy found in unhandled "
11258 "rejections set\n",
11263 AutoRealm
ar2(cx
, promise
);
11265 if (!PrintUnhandledRejection(cx
, promise
)) {
11270 sc
->unhandledRejectedPromises
= nullptr;
11275 bool ShellContext::registerWithCx(JSContext
* cx
) {
11277 JS_SetContextPrivate(cx
, this);
11280 SetWorkerContextOptions(cx
);
11283 JS::SetWarningReporter(cx
, WarningReporter
);
11284 JS_SetFutexCanWait(cx
);
11285 JS_InitDestroyPrincipalsCallback(cx
, ShellPrincipals::destroy
);
11286 JS_SetDestroyCompartmentCallback(cx
, DestroyShellCompartmentPrivate
);
11287 js::SetWindowProxyClass(cx
, &ShellWindowProxyClass
);
11289 js::UseInternalJobQueues(cx
);
11291 js::SetPreserveWrapperCallbacks(cx
, DummyPreserveWrapperCallback
,
11292 DummyHasReleasedWrapperCallback
);
11294 JS::SetHostCleanupFinalizationRegistryCallback(
11295 cx
, ShellCleanupFinalizationRegistryCallback
, this);
11296 JS_AddExtraGCRootsTracer(cx
, TraceBlackRoots
, nullptr);
11297 JS_SetGrayGCRootsTracer(cx
, TraceGrayRoots
, nullptr);
11302 ShellContext::~ShellContext() {
11303 markObservers
.reset();
11305 JS_SetContextPrivate(cx_
, nullptr);
11306 JS::SetHostCleanupFinalizationRegistryCallback(cx_
, nullptr, nullptr);
11307 JS_SetGrayGCRootsTracer(cx_
, nullptr, nullptr);
11308 JS_RemoveExtraGCRootsTracer(cx_
, TraceBlackRoots
, nullptr);
11310 MOZ_ASSERT(offThreadJobs
.empty());
11313 static int Shell(JSContext
* cx
, OptionParser
* op
) {
11314 #ifdef JS_STRUCTURED_SPEW
11315 cx
->spewer().enableSpewing();
11318 auto exitShell
= MakeScopeExit([&] {
11319 #ifdef JS_STRUCTURED_SPEW
11320 cx
->spewer().disableSpewing();
11324 #ifdef MOZ_CODE_COVERAGE
11325 InstallCoverageSignalHandlers();
11328 Maybe
<JS::AutoDisableGenerationalGC
> noggc
;
11329 if (op
->getBoolOption("no-ggc")) {
11333 Maybe
<AutoDisableCompactingGC
> nocgc
;
11334 if (op
->getBoolOption("no-cgc")) {
11338 if (op
->getBoolOption("fuzzing-safe")) {
11339 fuzzingSafe
= true;
11342 (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0');
11346 if (op
->getBoolOption("differential-testing")) {
11347 JS::SetSupportDifferentialTesting(true);
11351 if (op
->getBoolOption("disable-oom-functions")) {
11352 disableOOMFunctions
= true;
11355 if (op
->getBoolOption("more-compartments")) {
11356 defaultToSameCompartment
= false;
11359 bool reprl_mode
= FuzzilliUseReprlMode(op
);
11361 // Begin REPRL Loop
11362 int result
= EXIT_SUCCESS
;
11364 JS::RealmOptions options
;
11365 SetStandardRealmOptions(options
);
11367 cx
, NewGlobalObject(cx
, options
, nullptr, ShellGlobalKind::WindowProxy
,
11368 /* immutablePrototype = */ true));
11373 JSAutoRealm
ar(cx
, glob
);
11375 ShellContext
* sc
= GetShellContext(cx
);
11376 if (!sc
->moduleLoader
&& !InitModuleLoader(cx
, *op
)) {
11377 return EXIT_FAILURE
;
11380 #ifdef FUZZING_INTERFACES
11381 if (fuzzHaveModule
) {
11382 return FuzzJSRuntimeStart(cx
, &sArgc
, &sArgv
);
11387 result
= EXIT_SUCCESS
;
11389 AutoReportException
are(cx
);
11390 if (!ProcessArgs(cx
, op
) && !sc
->quitting
) {
11391 result
= EXITCODE_RUNTIME_ERROR
;
11396 * The job queue must be drained even on error to finish outstanding async
11397 * tasks before the main thread JSRuntime is torn down. Drain after
11398 * uncaught exceptions have been reported since draining runs callbacks.
11402 // Only if there's no other error, report unhandled rejections.
11403 if (!result
&& !sc
->exitCode
) {
11404 AutoReportException
are(cx
);
11405 if (!ReportUnhandledRejections(cx
)) {
11406 FILE* fp
= ErrorFilePointer();
11407 fputs("Error while printing unhandled rejection\n", fp
);
11411 if (sc
->exitCode
) {
11412 result
= sc
->exitCode
;
11415 #ifdef FUZZING_JS_FUZZILLI
11419 // Send return code to parent and reset edge counters.
11423 uint32_t execHashInputs
;
11425 s
.status
= (result
& 0xff) << 8;
11426 s
.execHash
= cx
->executionHash
;
11427 s
.execHashInputs
= cx
->executionHashInputs
;
11428 MOZ_RELEASE_ASSERT(write(REPRL_CWFD
, &s
, 12) == 12);
11429 __sanitizer_cov_reset_edgeguards();
11430 cx
->executionHash
= 1;
11431 cx
->executionHashInputs
= 0;
11435 if (enableDisassemblyDumps
) {
11436 AutoReportException
are(cx
);
11437 if (!js::DumpRealmPCCounts(cx
)) {
11438 result
= EXITCODE_OUT_OF_MEMORY
;
11443 } while (reprl_mode
);
11448 // Used to allocate memory when jemalloc isn't yet initialized.
11449 JS_DECLARE_NEW_METHODS(SystemAlloc_New
, malloc
, static)
11451 static void SetOutputFile(const char* const envVar
, RCFile
* defaultOut
,
11452 RCFile
** outFileP
) {
11455 const char* outPath
= getenv(envVar
);
11457 if (outPath
&& *outPath
&& (newfp
= fopen(outPath
, "w"))) {
11458 outFile
= SystemAlloc_New
<RCFile
>(newfp
);
11460 outFile
= defaultOut
;
11464 MOZ_CRASH("Failed to allocate output file");
11467 outFile
->acquire();
11468 *outFileP
= outFile
;
11471 static void PreInit() {
11473 const char* crash_option
= getenv("XRE_NO_WINDOWS_CRASH_DIALOG");
11474 if (crash_option
&& crash_option
[0] == '1') {
11475 // Disable the segfault dialog. We want to fail the tests immediately
11476 // instead of hanging automation.
11477 UINT newMode
= SEM_NOGPFAULTERRORBOX
| SEM_NOOPENFILEERRORBOX
;
11478 UINT prevMode
= SetErrorMode(newMode
);
11479 SetErrorMode(prevMode
| newMode
);
11484 #ifndef JS_WITHOUT_NSPR
11485 class AutoLibraryLoader
{
11486 Vector
<PRLibrary
*, 4, SystemAllocPolicy
> libraries
;
11489 ~AutoLibraryLoader() {
11490 for (auto dll
: libraries
) {
11491 PR_UnloadLibrary(dll
);
11495 PRLibrary
* load(const char* path
) {
11497 libSpec
.type
= PR_LibSpec_Pathname
;
11498 libSpec
.value
.pathname
= path
;
11499 PRLibrary
* dll
= PR_LoadLibraryWithFlags(libSpec
, PR_LD_NOW
| PR_LD_GLOBAL
);
11501 fprintf(stderr
, "LoadLibrary '%s' failed with code %d\n", path
,
11503 MOZ_CRASH("Failed to load library");
11506 MOZ_ALWAYS_TRUE(libraries
.append(dll
));
11512 static bool ReadSelfHostedXDRFile(JSContext
* cx
, FileContents
& buf
) {
11513 FILE* file
= fopen(selfHostedXDRPath
, "rb");
11515 fprintf(stderr
, "Can't open self-hosted stencil XDR file.\n");
11518 AutoCloseFile
autoClose(file
);
11521 if (fstat(fileno(file
), &st
) < 0) {
11522 fprintf(stderr
, "Unable to stat self-hosted stencil XDR file.\n");
11526 if (st
.st_size
>= INT32_MAX
) {
11527 fprintf(stderr
, "self-hosted stencil XDR file too large.\n");
11530 uint32_t filesize
= uint32_t(st
.st_size
);
11532 if (!buf
.growBy(filesize
)) {
11535 size_t cc
= fread(buf
.begin(), 1, filesize
, file
);
11536 if (cc
!= filesize
) {
11537 fprintf(stderr
, "Short read on self-hosted stencil XDR file.\n");
11544 static bool WriteSelfHostedXDRFile(JSContext
* cx
, JS::SelfHostedCache buffer
) {
11545 FILE* file
= fopen(selfHostedXDRPath
, "wb");
11547 JS_ReportErrorUTF8(cx
, "Can't open self-hosted stencil XDR file.");
11550 AutoCloseFile
autoClose(file
);
11552 size_t cc
= fwrite(buffer
.Elements(), 1, buffer
.LengthBytes(), file
);
11553 if (cc
!= buffer
.LengthBytes()) {
11554 JS_ReportErrorUTF8(cx
, "Short write on self-hosted stencil XDR file.");
11561 template <typename T
>
11562 static bool ParsePrefValue(const char* name
, const char* val
, T
* result
) {
11563 if constexpr (std::is_same_v
<T
, bool>) {
11564 if (strcmp(val
, "true") == 0) {
11568 if (strcmp(val
, "false") == 0) {
11572 fprintf(stderr
, "Invalid value for boolean pref %s: %s\n", name
, val
);
11575 static_assert(std::is_same_v
<T
, int32_t> || std::is_same_v
<T
, uint32_t>);
11577 long v
= strtol(val
, &end
, 10);
11578 if (end
!= val
+ strlen(val
) || static_cast<long>(static_cast<T
>(v
)) != v
) {
11579 fprintf(stderr
, "Invalid value for integer pref %s: %s\n", name
, val
);
11582 *result
= static_cast<T
>(v
);
11587 static bool SetJSPrefToTrueForBool(const char* name
) {
11588 // Search for a matching pref and try to set it to a default value for the
11590 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
11591 if (strcmp(name, NAME) == 0) { \
11592 if constexpr (std::is_same_v<TYPE, bool>) { \
11593 JS::Prefs::SETTER(true); \
11596 fprintf(stderr, "Pref %s must have a value specified.\n", name); \
11600 FOR_EACH_JS_PREF(CHECK_PREF
)
11603 // Nothing matched, return false
11604 fprintf(stderr
, "Invalid pref name: %s\n", name
);
11608 static bool SetJSPrefToValue(const char* name
, size_t nameLen
,
11609 const char* value
) {
11610 // Search for a matching pref and try to set it to the provided value.
11611 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
11612 if (nameLen == strlen(NAME) && memcmp(name, NAME, strlen(NAME)) == 0) { \
11614 if (!ParsePrefValue<TYPE>(NAME, value, &v)) { \
11617 JS::Prefs::SETTER(v); \
11620 FOR_EACH_JS_PREF(CHECK_PREF
)
11623 // Nothing matched, return false
11624 fprintf(stderr
, "Invalid pref name: %s\n", name
);
11628 static bool SetJSPref(const char* pref
) {
11629 const char* assign
= strchr(pref
, '=');
11631 if (!SetJSPrefToTrueForBool(pref
)) {
11637 size_t nameLen
= assign
- pref
;
11638 const char* valStart
= assign
+ 1; // Skip '='.
11640 if (!SetJSPrefToValue(pref
, nameLen
, valStart
)) {
11646 static void ListJSPrefs() {
11647 auto printPref
= [](const char* name
, auto defaultVal
) {
11648 using T
= decltype(defaultVal
);
11649 if constexpr (std::is_same_v
<T
, bool>) {
11650 fprintf(stderr
, "%s=%s\n", name
, defaultVal
? "true" : "false");
11651 } else if constexpr (std::is_same_v
<T
, int32_t>) {
11652 fprintf(stderr
, "%s=%d\n", name
, defaultVal
);
11654 static_assert(std::is_same_v
<T
, uint32_t>);
11655 fprintf(stderr
, "%s=%u\n", name
, defaultVal
);
11659 #define PRINT_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
11660 printPref(NAME, JS::Prefs::CPP_NAME());
11661 FOR_EACH_JS_PREF(PRINT_PREF
)
11665 static bool SetGCParameterFromArg(JSContext
* cx
, char* arg
) {
11666 char* c
= strchr(arg
, '=');
11669 "Error: --gc-param argument '%s' must be of the form "
11670 "name=decimalValue\n",
11676 const char* name
= arg
;
11677 const char* valueStr
= c
+ 1;
11681 if (!GetGCParameterInfo(name
, &key
, &writable
)) {
11682 fprintf(stderr
, "Error: Unknown GC parameter name '%s'\n", name
);
11683 fprintf(stderr
, "Writable GC parameter names are:\n");
11684 #define PRINT_WRITABLE_PARAM_NAME(name, _, writable) \
11686 fprintf(stderr, " %s\n", name); \
11688 FOR_EACH_GC_PARAM(PRINT_WRITABLE_PARAM_NAME
)
11689 #undef PRINT_WRITABLE_PARAM_NAME
11694 fprintf(stderr
, "Error: GC parameter '%s' is not writable\n", name
);
11698 char* end
= nullptr;
11699 unsigned long int value
= strtoul(valueStr
, &end
, 10);
11700 if (end
== valueStr
|| *end
) {
11702 "Error: Could not parse '%s' as decimal for GC parameter '%s'\n",
11707 uint32_t paramValue
= uint32_t(value
);
11708 if (value
== ULONG_MAX
|| value
!= paramValue
||
11709 !cx
->runtime()->gc
.setParameter(cx
, key
, paramValue
)) {
11710 fprintf(stderr
, "Error: Value %s is out of range for GC parameter '%s'\n",
11718 int main(int argc
, char** argv
) {
11726 setlocale(LC_ALL
, "");
11728 // Special-case stdout and stderr. We bump their refcounts to prevent them
11729 // from getting closed and then having some printf fail somewhere.
11730 RCFile
rcStdout(stdout
);
11731 rcStdout
.acquire();
11732 RCFile
rcStderr(stderr
);
11733 rcStderr
.acquire();
11735 SetOutputFile("JS_STDOUT", &rcStdout
, &gOutFile
);
11736 SetOutputFile("JS_STDERR", &rcStderr
, &gErrFile
);
11738 // Use a larger jemalloc page cache. This should match the value for browser
11739 // foreground processes in ContentChild::RecvNotifyProcessPriorityChanged.
11740 moz_set_max_dirty_page_modifier(4);
11742 OptionParser
op("Usage: {progname} [options] [[script] scriptArgs*]");
11743 if (!InitOptionParser(op
)) {
11744 return EXIT_FAILURE
;
11747 switch (op
.parseArgs(argc
, argv
)) {
11748 case OptionParser::EarlyExit
:
11749 return EXIT_SUCCESS
;
11750 case OptionParser::ParseError
:
11751 op
.printHelp(argv
[0]);
11752 return EXIT_FAILURE
;
11753 case OptionParser::Fail
:
11754 return EXIT_FAILURE
;
11755 case OptionParser::Okay
:
11759 if (op
.getHelpOption()) {
11760 return EXIT_SUCCESS
;
11763 if (!SetGlobalOptionsPreJSInit(op
)) {
11764 return EXIT_FAILURE
;
11767 // Start the engine.
11768 if (const char* message
= JS_InitWithFailureDiagnostic()) {
11769 fprintf(gErrFile
->fp
, "JS_Init failed: %s\n", message
);
11773 // `selfHostedXDRBuffer` contains XDR buffer of the self-hosted JS.
11774 // A part of it is borrowed by ImmutableScriptData of the self-hosted scripts.
11776 // This buffer should outlive JS_Shutdown.
11777 Maybe
<FileContents
> selfHostedXDRBuffer
;
11779 auto shutdownEngine
= MakeScopeExit([] { JS_ShutDown(); });
11781 if (!SetGlobalOptionsPostJSInit(op
)) {
11782 return EXIT_FAILURE
;
11785 // Record aggregated telemetry data on disk. Do this as early as possible such
11786 // that the telemetry is recording both before starting the context and after
11788 auto writeTelemetryResults
= MakeScopeExit([&op
] {
11789 if (telemetryLock
) {
11790 const char* dir
= op
.getStringOption("telemetry-dir");
11791 WriteTelemetryDataToDisk(dir
);
11792 js_free(telemetryLock
);
11793 telemetryLock
= nullptr;
11797 if (!InitSharedObjectMailbox()) {
11798 return EXIT_FAILURE
;
11801 JS::SetProcessBuildIdOp(ShellBuildId
);
11803 /* Use the same parameters as the browser in xpcjsruntime.cpp. */
11804 JSContext
* const cx
= JS_NewContext(JS::DefaultHeapMaxBytes
);
11809 // Register telemetry callbacks, if needed.
11810 if (telemetryLock
) {
11811 JS_SetAccumulateTelemetryCallback(cx
, AccumulateTelemetryDataCallback
);
11814 auto destroyCx
= MakeScopeExit([cx
] { JS_DestroyContext(cx
); });
11816 UniquePtr
<ShellContext
> sc
=
11817 MakeUnique
<ShellContext
>(cx
, ShellContext::MainThread
);
11818 if (!sc
|| !sc
->registerWithCx(cx
)) {
11822 if (!SetContextOptions(cx
, op
)) {
11826 JS_SetTrustedPrincipals(cx
, &ShellPrincipals::fullyTrusted
);
11827 JS_SetSecurityCallbacks(cx
, &ShellPrincipals::securityCallbacks
);
11829 JS_AddInterruptCallback(cx
, ShellInterruptCallback
);
11831 JS::SetGCSliceCallback(cx
, GCSliceCallback
);
11833 bufferStreamState
= js_new
<ExclusiveWaitableData
<BufferStreamState
>>(
11834 mutexid::BufferStreamState
);
11835 if (!bufferStreamState
) {
11838 auto shutdownBufferStreams
= MakeScopeExit([] {
11839 ShutdownBufferStreams();
11840 js_delete(bufferStreamState
);
11842 JS::InitConsumeStreamCallback(cx
, ConsumeBufferSource
, ReportStreamError
);
11844 JS::SetPromiseRejectionTrackerCallback(
11845 cx
, ForwardingPromiseRejectionTrackerCallback
);
11847 JS::dbg::SetDebuggerMallocSizeOf(cx
, moz_malloc_size_of
);
11849 auto shutdownShellThreads
= MakeScopeExit([cx
] {
11851 KillWorkerThreads(cx
);
11852 DestructSharedObjectMailbox();
11853 CancelOffThreadJobsForRuntime(cx
);
11856 // The file content should stay alive as long as Worker thread can be
11858 JS::SelfHostedCache xdrSpan
= nullptr;
11859 JS::SelfHostedWriter xdrWriter
= nullptr;
11860 if (selfHostedXDRPath
) {
11861 if (encodeSelfHostedCode
) {
11862 xdrWriter
= WriteSelfHostedXDRFile
;
11864 selfHostedXDRBuffer
.emplace(cx
);
11865 if (ReadSelfHostedXDRFile(cx
, *selfHostedXDRBuffer
)) {
11866 MOZ_ASSERT(selfHostedXDRBuffer
->length() > 0);
11867 JS::SelfHostedCache
span(selfHostedXDRBuffer
->begin(),
11868 selfHostedXDRBuffer
->end());
11871 fprintf(stderr
, "Falling back on parsing source.\n");
11872 selfHostedXDRPath
= nullptr;
11877 if (!JS::InitSelfHostedCode(cx
, xdrSpan
, xdrWriter
)) {
11881 EnvironmentPreparer
environmentPreparer(cx
);
11883 JS::SetProcessLargeAllocationFailureCallback(my_LargeAllocFailCallback
);
11885 if (op
.getBoolOption("wasm-compile-and-serialize")) {
11887 MOZ_CRASH("WASI doesn't support wasm");
11889 if (!WasmCompileAndSerialize(cx
)) {
11890 // Errors have been printed directly to stderr.
11891 MOZ_ASSERT(!cx
->isExceptionPending());
11892 return EXIT_FAILURE
;
11895 return EXIT_SUCCESS
;
11898 result
= Shell(cx
, &op
);
11901 if (OOM_printAllocationCount
) {
11902 printf("OOM max count: %" PRIu64
"\n", js::oom::simulator
.counter());
11909 bool InitOptionParser(OptionParser
& op
) {
11911 "The SpiderMonkey shell provides a command line interface to the "
11912 "JavaScript engine. Code and file options provided via the command line "
11914 "run left to right. If provided, the optional script argument is run "
11916 "all options have been processed. Just-In-Time compilation modes may be "
11918 "command line options.");
11919 op
.setDescriptionWidth(72);
11920 op
.setHelpWidth(80);
11921 op
.setVersion(JS_GetImplementationVersion());
11923 if (!op
.addMultiStringOption(
11924 'f', "file", "PATH",
11925 "File path to run, parsing file contents as UTF-8") ||
11926 !op
.addMultiStringOption(
11927 'u', "utf16-file", "PATH",
11928 "File path to run, inflating the file's UTF-8 contents to UTF-16 and "
11929 "then parsing that") ||
11930 !op
.addMultiStringOption('m', "module", "PATH", "Module path to run") ||
11931 !op
.addMultiStringOption('p', "prelude", "PATH", "Prelude path to run") ||
11932 !op
.addMultiStringOption('e', "execute", "CODE", "Inline code to run") ||
11933 !op
.addStringOption('\0', "selfhosted-xdr-path", "[filename]",
11934 "Read/Write selfhosted script data from/to the given "
11936 !op
.addStringOption('\0', "selfhosted-xdr-mode", "(encode,decode,off)",
11937 "Whether to encode/decode data of the file provided"
11938 "with --selfhosted-xdr-path.") ||
11939 !op
.addBoolOption('i', "shell", "Enter prompt after running code") ||
11940 !op
.addBoolOption('c', "compileonly",
11941 "Only compile, don't run (syntax checking mode)") ||
11942 !op
.addBoolOption('w', "warnings", "Emit warnings") ||
11943 !op
.addBoolOption('W', "nowarnings", "Don't emit warnings") ||
11944 !op
.addBoolOption('D', "dump-bytecode",
11945 "Dump bytecode with exec count for all scripts") ||
11946 !op
.addBoolOption('b', "print-timing",
11947 "Print sub-ms runtime for each file that's run") ||
11948 !op
.addBoolOption('\0', "code-coverage",
11949 "Enable code coverage instrumentation.") ||
11951 '\0', "disable-parser-deferred-alloc",
11952 "Disable deferred allocation of GC objects until after parser") ||
11954 !op
.addBoolOption('O', "print-alloc",
11955 "Print the number of allocations at exit") ||
11957 !op
.addOptionalStringArg("script",
11958 "A script to execute (after all options)") ||
11959 !op
.addOptionalMultiStringArg(
11961 "String arguments to bind as |scriptArgs| in the "
11962 "shell's global") ||
11964 '\0', "cpu-count", "COUNT",
11965 "Set the number of CPUs (hardware threads) to COUNT, the "
11966 "default is the actual number of CPUs. The total number of "
11967 "background helper threads is the CPU count plus some constant.",
11969 !op
.addIntOption('\0', "thread-count", "COUNT", "Alias for --cpu-count.",
11971 !op
.addBoolOption('\0', "ion", "Enable IonMonkey (default)") ||
11972 !op
.addBoolOption('\0', "no-ion", "Disable IonMonkey") ||
11973 !op
.addBoolOption('\0', "no-ion-for-main-context",
11974 "Disable IonMonkey for the main context only") ||
11975 !op
.addIntOption('\0', "inlining-entry-threshold", "COUNT",
11976 "The minimum stub entry count before trial-inlining a"
11979 !op
.addIntOption('\0', "small-function-length", "COUNT",
11980 "The maximum bytecode length of a 'small function' for "
11981 "the purpose of inlining.",
11983 !op
.addBoolOption('\0', "only-inline-selfhosted",
11984 "Only inline selfhosted functions") ||
11985 !op
.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") ||
11986 !op
.addStringOption(
11987 '\0', "wasm-compiler", "[option]",
11988 "Choose to enable a subset of the wasm compilers, valid options are "
11989 "'none', 'baseline', 'ion', 'optimizing', "
11990 "'baseline+ion', 'baseline+optimizing'.") ||
11991 !op
.addBoolOption('\0', "wasm-verbose",
11992 "Enable WebAssembly verbose logging") ||
11993 !op
.addBoolOption('\0', "disable-wasm-huge-memory",
11994 "Disable WebAssembly huge memory") ||
11995 !op
.addBoolOption('\0', "test-wasm-await-tier2",
11996 "Forcibly activate tiering and block "
11997 "instantiation on completion of tier2") ||
11998 !op
.addBoolOption('\0', "no-native-regexp",
11999 "Disable native regexp compilation") ||
12001 '\0', "regexp-warmup-threshold", "COUNT",
12002 "Wait for COUNT invocations before compiling regexps to native code "
12005 !op
.addBoolOption('\0', "trace-regexp-parser", "Trace regexp parsing") ||
12006 !op
.addBoolOption('\0', "trace-regexp-assembler",
12007 "Trace regexp assembler") ||
12008 !op
.addBoolOption('\0', "trace-regexp-interpreter",
12009 "Trace regexp interpreter") ||
12010 !op
.addBoolOption('\0', "trace-regexp-peephole",
12011 "Trace regexp peephole optimization") ||
12012 !op
.addBoolOption('\0', "less-debug-code",
12013 "Emit less machine code for "
12014 "checking assertions under DEBUG.") ||
12015 !op
.addBoolOption('\0', "disable-weak-refs", "Disable weak references") ||
12016 !op
.addBoolOption('\0', "disable-tosource", "Disable toSource/uneval") ||
12017 !op
.addBoolOption('\0', "disable-property-error-message-fix",
12018 "Disable fix for the error message when accessing "
12019 "property of null or undefined") ||
12020 !op
.addBoolOption('\0', "enable-iterator-helpers",
12021 "Enable iterator helpers") ||
12022 #ifdef ENABLE_JSON_PARSE_WITH_SOURCE
12023 !op
.addBoolOption('\0', "enable-json-parse-with-source",
12024 "Enable JSON.parse with source") ||
12026 !op
.addBoolOption('\0', "enable-shadow-realms", "Enable ShadowRealms") ||
12027 !op
.addBoolOption('\0', "disable-array-grouping",
12028 "Disable Object.groupBy and Map.groupBy") ||
12029 !op
.addBoolOption('\0', "disable-well-formed-unicode-strings",
12030 "Disable String.prototype.{is,to}WellFormed() methods"
12031 "(Well-Formed Unicode Strings) (default: Enabled)") ||
12032 !op
.addBoolOption('\0', "enable-new-set-methods",
12033 "Enable New Set methods") ||
12034 !op
.addBoolOption('\0', "disable-arraybuffer-transfer",
12035 "Disable ArrayBuffer.prototype.transfer() methods") ||
12036 !op
.addBoolOption('\0', "enable-symbols-as-weakmap-keys",
12037 "Enable Symbols As WeakMap keys") ||
12039 '\0', "enable-arraybuffer-resizable",
12040 "Enable resizable ArrayBuffers and growable SharedArrayBuffers") ||
12041 !op
.addBoolOption('\0', "enable-uint8array-base64",
12042 "Enable Uint8Array base64/hex methods") ||
12043 !op
.addBoolOption('\0', "enable-top-level-await",
12044 "Enable top-level await") ||
12045 !op
.addBoolOption('\0', "enable-class-static-blocks",
12046 "(no-op) Enable class static blocks") ||
12047 !op
.addBoolOption('\0', "enable-import-assertions",
12048 "Enable import attributes with old assert syntax") ||
12049 !op
.addBoolOption('\0', "enable-import-attributes",
12050 "Enable import attributes") ||
12051 !op
.addBoolOption('\0', "disable-destructuring-fuse",
12052 "Disable Destructuring Fuse") ||
12053 !op
.addStringOption('\0', "shared-memory", "on/off",
12054 "SharedArrayBuffer and Atomics "
12055 #if SHARED_MEMORY_DEFAULT
12056 "(default: on, off to disable)"
12058 "(default: off, on to enable)"
12061 !op
.addStringOption('\0', "spectre-mitigations", "on/off",
12062 "Whether Spectre mitigations are enabled (default: "
12063 "off, on to enable)") ||
12064 !op
.addStringOption('\0', "write-protect-code", "on/off",
12065 "Whether the W^X policy is enforced to mark JIT code "
12066 "pages as either writable or executable but never "
12067 "both at the same time (default: on, off to "
12069 !op
.addStringOption('\0', "cache-ir-stubs", "on/off/call",
12070 "Use CacheIR stubs (default: on, off to disable, "
12071 "call to enable work-in-progress call ICs)") ||
12072 !op
.addStringOption('\0', "ion-shared-stubs", "on/off",
12073 "Use shared stubs (default: on, off to disable)") ||
12074 !op
.addStringOption('\0', "ion-scalar-replacement", "on/off",
12075 "Scalar Replacement (default: on, off to disable)") ||
12076 !op
.addStringOption('\0', "ion-gvn", "[mode]",
12077 "Specify Ion global value numbering:\n"
12078 " off: disable GVN\n"
12079 " on: enable GVN (default)\n") ||
12080 !op
.addStringOption(
12081 '\0', "ion-licm", "on/off",
12082 "Loop invariant code motion (default: on, off to disable)") ||
12083 !op
.addStringOption('\0', "ion-edgecase-analysis", "on/off",
12084 "Find edge cases where Ion can avoid bailouts "
12085 "(default: on, off to disable)") ||
12086 !op
.addStringOption('\0', "ion-pruning", "on/off",
12087 "Branch pruning (default: on, off to disable)") ||
12088 !op
.addStringOption('\0', "ion-range-analysis", "on/off",
12089 "Range analysis (default: on, off to disable)") ||
12090 !op
.addStringOption('\0', "ion-sink", "on/off",
12091 "Sink code motion (default: off, on to enable)") ||
12092 !op
.addStringOption('\0', "ion-optimization-levels", "on/off",
12093 "No-op for fuzzing") ||
12094 !op
.addStringOption('\0', "ion-loop-unrolling", "on/off",
12095 "(NOP for fuzzers)") ||
12096 !op
.addStringOption(
12097 '\0', "ion-instruction-reordering", "on/off",
12098 "Instruction reordering (default: off, on to enable)") ||
12099 !op
.addStringOption(
12100 '\0', "ion-optimize-shapeguards", "on/off",
12101 "Eliminate redundant shape guards (default: on, off to disable)") ||
12102 !op
.addStringOption(
12103 '\0', "ion-optimize-gcbarriers", "on/off",
12104 "Eliminate redundant GC barriers (default: on, off to disable)") ||
12105 !op
.addStringOption('\0', "ion-iterator-indices", "on/off",
12106 "Optimize property access in for-in loops "
12107 "(default: on, off to disable)") ||
12108 !op
.addStringOption('\0', "ion-load-keys", "on/off",
12109 "Atomize property loads used as keys "
12110 "(default: on, off to disable)") ||
12111 !op
.addBoolOption('\0', "ion-check-range-analysis",
12112 "Range analysis checking") ||
12113 !op
.addBoolOption('\0', "ion-extra-checks",
12114 "Perform extra dynamic validation checks") ||
12115 !op
.addStringOption(
12116 '\0', "ion-inlining", "on/off",
12117 "Inline methods where possible (default: on, off to disable)") ||
12118 !op
.addStringOption(
12119 '\0', "ion-osr", "on/off",
12120 "On-Stack Replacement (default: on, off to disable)") ||
12121 !op
.addBoolOption('\0', "disable-bailout-loop-check",
12122 "Turn off bailout loop check") ||
12123 !op
.addBoolOption('\0', "enable-ic-frame-pointers",
12124 "Use frame pointers in all IC stubs") ||
12125 !op
.addBoolOption('\0', "scalar-replace-arguments",
12126 "Use scalar replacement to optimize ArgumentsObject") ||
12127 !op
.addStringOption(
12128 '\0', "ion-limit-script-size", "on/off",
12129 "Don't compile very large scripts (default: on, off to disable)") ||
12130 !op
.addIntOption('\0', "ion-warmup-threshold", "COUNT",
12131 "Wait for COUNT calls or iterations before compiling "
12132 "at the normal optimization level (default: 1000)",
12134 !op
.addIntOption('\0', "ion-full-warmup-threshold", "COUNT",
12135 "No-op for fuzzing", -1) ||
12136 !op
.addStringOption(
12137 '\0', "ion-regalloc", "[mode]",
12138 "Specify Ion register allocation:\n"
12139 " backtracking: Priority based backtracking register allocation "
12141 " testbed: Backtracking allocator with experimental features\n"
12142 " stupid: Simple block local register allocation") ||
12145 "Always ion-compile methods (implies --baseline-eager)") ||
12146 !op
.addBoolOption('\0', "fast-warmup",
12147 "Reduce warmup thresholds for each tier.") ||
12148 !op
.addStringOption('\0', "ion-offthread-compile", "on/off",
12149 "Compile scripts off thread (default: on)") ||
12150 !op
.addStringOption('\0', "ion-parallel-compile", "on/off",
12151 "--ion-parallel compile is deprecated. Use "
12152 "--ion-offthread-compile.") ||
12153 !op
.addBoolOption('\0', "baseline",
12154 "Enable baseline compiler (default)") ||
12155 !op
.addBoolOption('\0', "no-baseline", "Disable baseline compiler") ||
12156 !op
.addBoolOption('\0', "baseline-eager",
12157 "Always baseline-compile methods") ||
12158 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
12159 !op
.addBoolOption('\0', "portable-baseline-eager",
12160 "Always use the porbale baseline interpreter") ||
12161 !op
.addBoolOption('\0', "portable-baseline",
12162 "Enable Portable Baseline Interpreter (default)") ||
12163 !op
.addBoolOption('\0', "no-portable-baseline",
12164 "Disable Portable Baseline Interpreter") ||
12167 '\0', "baseline-warmup-threshold", "COUNT",
12168 "Wait for COUNT calls or iterations before baseline-compiling "
12171 !op
.addBoolOption('\0', "blinterp",
12172 "Enable Baseline Interpreter (default)") ||
12173 !op
.addBoolOption('\0', "no-blinterp", "Disable Baseline Interpreter") ||
12174 !op
.addBoolOption('\0', "disable-jithints",
12175 "Disable caching eager baseline compilation hints.") ||
12177 '\0', "emit-interpreter-entry",
12178 "Emit Interpreter entry trampolines (default under --enable-perf)") ||
12180 '\0', "no-emit-interpreter-entry",
12181 "Do not emit Interpreter entry trampolines (default).") ||
12182 !op
.addBoolOption('\0', "blinterp-eager",
12183 "Always Baseline-interpret scripts") ||
12185 '\0', "blinterp-warmup-threshold", "COUNT",
12186 "Wait for COUNT calls or iterations before Baseline-interpreting "
12190 '\0', "trial-inlining-warmup-threshold", "COUNT",
12191 "Wait for COUNT calls or iterations before trial-inlining "
12194 !op
.addStringOption(
12195 '\0', "monomorphic-inlining", "default/always/never",
12196 "Whether monomorphic inlining is used instead of trial inlining "
12197 "always, never, or based on heuristics (default)") ||
12199 '\0', "non-writable-jitcode",
12200 "(NOP for fuzzers) Allocate JIT code as non-writable memory.") ||
12203 "Pretend CPU does not support SSE3 instructions and above "
12204 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12207 "Pretend CPU does not support SSSE3 [sic] instructions and above "
12208 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12211 "Pretend CPU does not support SSE4.1 instructions "
12212 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12213 !op
.addBoolOption('\0', "no-sse4", "Alias for --no-sse41") ||
12216 "Pretend CPU does not support SSE4.2 instructions "
12217 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12218 #ifdef ENABLE_WASM_AVX
12219 !op
.addBoolOption('\0', "enable-avx",
12220 "No-op. AVX is enabled by default, if available.") ||
12223 "Pretend CPU does not support AVX or AVX2 instructions "
12224 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12226 !op
.addBoolOption('\0', "enable-avx",
12227 "AVX is disabled by default. Enable AVX. "
12228 "(no-op on platforms other than x86 and x64).") ||
12229 !op
.addBoolOption('\0', "no-avx",
12230 "No-op. AVX is currently disabled by default.") ||
12232 !op
.addBoolOption('\0', "more-compartments",
12233 "Make newGlobal default to creating a new "
12235 !op
.addBoolOption('\0', "fuzzing-safe",
12236 "Don't expose functions that aren't safe for "
12237 "fuzzers to call") ||
12239 !op
.addBoolOption('\0', "differential-testing",
12240 "Avoid random/undefined behavior that disturbs "
12241 "differential testing (correctness fuzzing)") ||
12243 !op
.addBoolOption('\0', "disable-oom-functions",
12244 "Disable functions that cause "
12245 "artificial OOMs") ||
12246 !op
.addBoolOption('\0', "no-threads", "Disable helper threads") ||
12248 '\0', "no-jit-backend",
12249 "Disable the JIT backend completely for this process") ||
12251 !op
.addBoolOption('\0', "dump-entrained-variables",
12252 "Print variables which are "
12253 "unnecessarily entrained by inner functions") ||
12255 !op
.addBoolOption('\0', "no-ggc", "Disable Generational GC") ||
12256 !op
.addBoolOption('\0', "no-cgc", "Disable Compacting GC") ||
12257 !op
.addBoolOption('\0', "no-incremental-gc", "Disable Incremental GC") ||
12258 !op
.addBoolOption('\0', "no-parallel-marking",
12259 "Disable GC parallel marking") ||
12260 !op
.addBoolOption('\0', "enable-parallel-marking",
12261 "Enable GC parallel marking") ||
12263 '\0', "marking-threads", "COUNT",
12264 "Set the number of threads used for parallel marking to COUNT.", 0) ||
12265 !op
.addStringOption('\0', "nursery-strings", "on/off",
12266 "Allocate strings in the nursery") ||
12267 !op
.addStringOption('\0', "nursery-bigints", "on/off",
12268 "Allocate BigInts in the nursery") ||
12269 !op
.addIntOption('\0', "available-memory", "SIZE",
12270 "Select GC settings based on available memory (MB)",
12272 !op
.addStringOption('\0', "arm-hwcap", "[features]",
12273 "Specify ARM code generation features, or 'help' to "
12274 "list all features.") ||
12275 !op
.addIntOption('\0', "arm-asm-nop-fill", "SIZE",
12276 "Insert the given number of NOP instructions at all "
12277 "possible pool locations.",
12279 !op
.addIntOption('\0', "asm-pool-max-offset", "OFFSET",
12280 "The maximum pc relative OFFSET permitted in pool "
12281 "reference instructions.",
12283 !op
.addBoolOption('\0', "arm-sim-icache-checks",
12284 "Enable icache flush checks in the ARM "
12286 !op
.addIntOption('\0', "arm-sim-stop-at", "NUMBER",
12287 "Stop the ARM simulator after the given "
12288 "NUMBER of instructions.",
12290 !op
.addBoolOption('\0', "mips-sim-icache-checks",
12291 "Enable icache flush checks in the MIPS "
12293 !op
.addIntOption('\0', "mips-sim-stop-at", "NUMBER",
12294 "Stop the MIPS simulator after the given "
12295 "NUMBER of instructions.",
12297 !op
.addBoolOption('\0', "loong64-sim-icache-checks",
12298 "Enable icache flush checks in the LoongArch64 "
12300 !op
.addIntOption('\0', "loong64-sim-stop-at", "NUMBER",
12301 "Stop the LoongArch64 simulator after the given "
12302 "NUMBER of instructions.",
12304 #ifdef JS_CODEGEN_RISCV64
12305 !op
.addBoolOption('\0', "riscv-debug", "debug print riscv info.") ||
12307 #ifdef JS_SIMULATOR_RISCV64
12308 !op
.addBoolOption('\0', "trace-sim", "print simulator info.") ||
12309 !op
.addBoolOption('\0', "debug-sim", "debug simulator.") ||
12310 !op
.addBoolOption('\0', "riscv-trap-to-simulator-debugger",
12311 "trap into simulator debuggger.") ||
12312 !op
.addIntOption('\0', "riscv-sim-stop-at", "NUMBER",
12313 "Stop the riscv simulator after the given "
12314 "NUMBER of instructions.",
12317 !op
.addIntOption('\0', "nursery-size", "SIZE-MB",
12318 "Set the maximum nursery size in MB",
12319 JS::DefaultNurseryMaxBytes
/ 1024 / 1024) ||
12321 !op
.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
12322 gc::ZealModeHelpText
) ||
12324 !op
.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
12325 "option ignored in non-gc-zeal builds") ||
12327 !op
.addMultiStringOption('\0', "gc-param", "NAME=VALUE",
12328 "Set a named GC parameter") ||
12329 !op
.addStringOption('\0', "module-load-path", "DIR",
12330 "Set directory to load modules from") ||
12331 !op
.addBoolOption('\0', "no-source-pragmas",
12332 "Disable source(Mapping)URL pragma parsing") ||
12333 !op
.addBoolOption('\0', "no-async-stacks", "Disable async stacks") ||
12334 !op
.addBoolOption('\0', "async-stacks-capture-debuggee-only",
12335 "Limit async stack capture to only debuggees") ||
12336 !op
.addMultiStringOption('\0', "dll", "LIBRARY",
12337 "Dynamically load LIBRARY") ||
12338 !op
.addBoolOption('\0', "suppress-minidump",
12339 "Suppress crash minidumps") ||
12340 #ifdef JS_ENABLE_SMOOSH
12341 !op
.addBoolOption('\0', "smoosh", "Use SmooshMonkey") ||
12342 !op
.addStringOption('\0', "not-implemented-watchfile", "[filename]",
12343 "Track NotImplemented errors in the new frontend") ||
12345 !op
.addBoolOption('\0', "smoosh", "No-op") ||
12347 !op
.addStringOption(
12348 '\0', "delazification-mode", "[option]",
12349 "Select one of the delazification mode for scripts given on the "
12350 "command line, valid options are: "
12351 "'on-demand', 'concurrent-df', 'eager', 'concurrent-df+on-demand'. "
12352 "Choosing 'concurrent-df+on-demand' will run both concurrent-df and "
12353 "on-demand delazification mode, and compare compilation outcome. ") ||
12354 !op
.addBoolOption('\0', "wasm-compile-and-serialize",
12355 "Compile the wasm bytecode from stdin and serialize "
12356 "the results to stdout") ||
12357 #ifdef FUZZING_JS_FUZZILLI
12358 !op
.addBoolOption('\0', "reprl", "Enable REPRL mode for fuzzing") ||
12360 !op
.addStringOption('\0', "telemetry-dir", "[directory]",
12361 "Output telemetry results in a directory") ||
12362 !op
.addMultiStringOption('P', "setpref", "name[=val]",
12363 "Set the value of a JS pref. The value may "
12364 "be omitted for boolean prefs, in which case "
12365 "they default to true. Use --list-prefs "
12366 "to print all pref names.") ||
12368 '\0', "list-prefs",
12369 "Print list of prefs that can be set with --setpref.") ||
12370 !op
.addBoolOption('\0', "use-fdlibm-for-sin-cos-tan",
12371 "Use fdlibm for Math.sin, Math.cos, and Math.tan") ||
12372 !op
.addBoolOption('\0', "wasm-gc", "Enable WebAssembly gc proposal.") ||
12373 !op
.addBoolOption('\0', "wasm-relaxed-simd",
12374 "Enable WebAssembly relaxed-simd proposal.") ||
12375 !op
.addBoolOption('\0', "wasm-multi-memory",
12376 "Enable WebAssembly multi-memory proposal.") ||
12377 !op
.addBoolOption('\0', "wasm-memory-control",
12378 "Enable WebAssembly memory-control proposal.") ||
12379 !op
.addBoolOption('\0', "wasm-memory64",
12380 "Enable WebAssembly memory64 proposal.") ||
12381 !op
.addBoolOption('\0', "wasm-tail-calls",
12382 "Enable WebAssembly tail-calls proposal.") ||
12383 !op
.addBoolOption('\0', "wasm-js-string-builtins",
12384 "Enable WebAssembly js-string-builtins proposal.")) {
12388 op
.setArgTerminatesOptions("script", true);
12389 op
.setArgCapturesRest("scriptArgs");
12394 bool SetGlobalOptionsPreJSInit(const OptionParser
& op
) {
12395 for (MultiStringRange args
= op
.getMultiStringOption("setpref");
12396 !args
.empty(); args
.popFront()) {
12397 if (!SetJSPref(args
.front())) {
12402 // Override pref values for prefs that have a custom shell flag.
12403 // If you're adding a new feature, consider using --setpref instead.
12405 if (op
.getBoolOption("disable-array-grouping")) {
12406 JS::Prefs::setAtStartup_array_grouping(false);
12408 if (op
.getBoolOption("disable-arraybuffer-transfer")) {
12409 JS::Prefs::setAtStartup_arraybuffer_transfer(false);
12411 if (op
.getBoolOption("enable-shadow-realms")) {
12412 JS::Prefs::set_experimental_shadow_realms(true);
12414 if (op
.getBoolOption("disable-well-formed-unicode-strings")) {
12415 JS::Prefs::setAtStartup_well_formed_unicode_strings(false);
12417 #ifdef NIGHTLY_BUILD
12418 if (op
.getBoolOption("enable-arraybuffer-resizable")) {
12419 JS::Prefs::setAtStartup_experimental_arraybuffer_resizable(true);
12420 JS::Prefs::setAtStartup_experimental_sharedarraybuffer_growable(true);
12422 if (op
.getBoolOption("enable-iterator-helpers")) {
12423 JS::Prefs::setAtStartup_experimental_iterator_helpers(true);
12425 if (op
.getBoolOption("enable-new-set-methods")) {
12426 JS::Prefs::setAtStartup_experimental_new_set_methods(true);
12428 if (op
.getBoolOption("enable-symbols-as-weakmap-keys")) {
12429 JS::Prefs::setAtStartup_experimental_symbols_as_weakmap_keys(true);
12431 if (op
.getBoolOption("enable-uint8array-base64")) {
12432 JS::Prefs::setAtStartup_experimental_uint8array_base64(true);
12436 if (op
.getBoolOption("disable-weak-refs")) {
12437 JS::Prefs::setAtStartup_weakrefs(false);
12439 JS::Prefs::setAtStartup_experimental_weakrefs_expose_cleanupSome(true);
12441 if (op
.getBoolOption("disable-destructuring-fuse")) {
12442 JS::Prefs::setAtStartup_destructuring_fuse(false);
12444 if (op
.getBoolOption("disable-property-error-message-fix")) {
12445 JS::Prefs::setAtStartup_property_error_message_fix(false);
12448 JS::Prefs::set_use_fdlibm_for_sin_cos_tan(
12449 op
.getBoolOption("use-fdlibm-for-sin-cos-tan"));
12451 if (op
.getBoolOption("wasm-gc") || op
.getBoolOption("wasm-relaxed-simd") ||
12452 op
.getBoolOption("wasm-multi-memory") ||
12453 op
.getBoolOption("wasm-memory-control") ||
12454 op
.getBoolOption("wasm-memory64") ||
12455 op
.getBoolOption("wasm-tail-calls") ||
12456 op
.getBoolOption("wasm-js-string-builtins")) {
12459 "Wasm shell flags are now using prefs, use -P wasm_feature instead.\n");
12463 if (op
.getBoolOption("list-prefs")) {
12468 // Note: DisableJitBackend must be called before JS_InitWithFailureDiagnostic.
12469 if (op
.getBoolOption("no-jit-backend")) {
12470 JS::DisableJitBackend();
12473 #if defined(JS_CODEGEN_ARM)
12474 if (const char* str
= op
.getStringOption("arm-hwcap")) {
12475 jit::SetARMHwCapFlagsString(str
);
12478 int32_t fill
= op
.getIntOption("arm-asm-nop-fill");
12480 jit::Assembler::NopFill
= fill
;
12483 int32_t poolMaxOffset
= op
.getIntOption("asm-pool-max-offset");
12484 if (poolMaxOffset
>= 5 && poolMaxOffset
<= 1024) {
12485 jit::Assembler::AsmPoolMaxOffset
= poolMaxOffset
;
12489 // Fish around in `op` for various important compiler-configuration flags
12490 // and make sure they get handed on to any child processes we might create.
12491 // See bug 1700900. Semantically speaking, this is all rather dubious:
12493 // * What set of flags need to be propagated in order to guarantee that the
12494 // child produces code that is "compatible" (in whatever sense) with that
12495 // produced by the parent? This isn't always easy to determine.
12497 // * There's nothing that ensures that flags given to the child are
12498 // presented in the same order that they exist in the parent's `argv[]`.
12499 // That could be a problem in the case where two flags with contradictory
12500 // meanings are given, and they are presented to the child in the opposite
12501 // order. For example: --wasm-compiler=optimizing --wasm-compiler=baseline.
12503 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
12504 MOZ_ASSERT(!js::jit::CPUFlagsHaveBeenComputed());
12506 if (op
.getBoolOption("no-sse3")) {
12507 js::jit::CPUInfo::SetSSE3Disabled();
12508 if (!sCompilerProcessFlags
.append("--no-sse3")) {
12512 if (op
.getBoolOption("no-ssse3")) {
12513 js::jit::CPUInfo::SetSSSE3Disabled();
12514 if (!sCompilerProcessFlags
.append("--no-ssse3")) {
12518 if (op
.getBoolOption("no-sse4") || op
.getBoolOption("no-sse41")) {
12519 js::jit::CPUInfo::SetSSE41Disabled();
12520 if (!sCompilerProcessFlags
.append("--no-sse41")) {
12524 if (op
.getBoolOption("no-sse42")) {
12525 js::jit::CPUInfo::SetSSE42Disabled();
12526 if (!sCompilerProcessFlags
.append("--no-sse42")) {
12530 if (op
.getBoolOption("no-avx")) {
12531 js::jit::CPUInfo::SetAVXDisabled();
12532 if (!sCompilerProcessFlags
.append("--no-avx")) {
12536 if (op
.getBoolOption("enable-avx")) {
12537 js::jit::CPUInfo::SetAVXEnabled();
12538 if (!sCompilerProcessFlags
.append("--enable-avx")) {
12547 bool SetGlobalOptionsPostJSInit(const OptionParser
& op
) {
12548 if (op
.getStringOption("telemetry-dir")) {
12549 MOZ_ASSERT(!telemetryLock
);
12550 telemetryLock
= js_new
<Mutex
>(mutexid::ShellTelemetry
);
12551 if (!telemetryLock
) {
12556 // Allow dumping on Linux with the fuzzing flag set, even when running with
12557 // the suid/sgid flag set on the shell.
12559 if (op
.getBoolOption("fuzzing-safe")) {
12560 prctl(PR_SET_DUMPABLE
, 1);
12566 * Process OOM options as early as possible so that we can observe as many
12567 * allocations as possible.
12569 OOM_printAllocationCount
= op
.getBoolOption('O');
12572 if (op
.getBoolOption("no-threads")) {
12573 js::DisableExtraThreads();
12576 enableCodeCoverage
= op
.getBoolOption("code-coverage");
12577 if (enableCodeCoverage
) {
12578 js::EnableCodeCoverage();
12581 // If LCov is enabled, then the default delazification mode should be changed
12582 // to parse everything eagerly, such that we know the location of every
12583 // instruction, to report them in the LCov summary, even if there is no uses
12584 // of these instructions.
12586 // Note: code coverage can be enabled either using the --code-coverage command
12587 // line, or the JS_CODE_COVERAGE_OUTPUT_DIR environment variable, which is
12588 // processed by JS_InitWithFailureDiagnostic.
12589 if (coverage::IsLCovEnabled()) {
12590 defaultDelazificationMode
=
12591 JS::DelazificationOption::ParseEverythingEagerly
;
12594 if (const char* xdr
= op
.getStringOption("selfhosted-xdr-path")) {
12595 shell::selfHostedXDRPath
= xdr
;
12597 if (const char* opt
= op
.getStringOption("selfhosted-xdr-mode")) {
12598 if (strcmp(opt
, "encode") == 0) {
12599 shell::encodeSelfHostedCode
= true;
12600 } else if (strcmp(opt
, "decode") == 0) {
12601 shell::encodeSelfHostedCode
= false;
12602 } else if (strcmp(opt
, "off") == 0) {
12603 shell::selfHostedXDRPath
= nullptr;
12606 "invalid option value for --selfhosted-xdr-mode, must be "
12611 #ifdef JS_WITHOUT_NSPR
12612 if (!op
.getMultiStringOption("dll").empty()) {
12613 fprintf(stderr
, "Error: --dll requires NSPR support!\n");
12617 AutoLibraryLoader loader
;
12618 MultiStringRange dllPaths
= op
.getMultiStringOption("dll");
12619 while (!dllPaths
.empty()) {
12620 char* path
= dllPaths
.front();
12622 dllPaths
.popFront();
12626 if (op
.getBoolOption("suppress-minidump")) {
12627 js::NoteIntentionalCrash();
12630 // The fake CPU count must be set before initializing the Runtime,
12631 // which spins up the thread pool.
12632 int32_t cpuCount
= op
.getIntOption("cpu-count"); // What we're really setting
12633 if (cpuCount
< 0) {
12634 cpuCount
= op
.getIntOption("thread-count"); // Legacy name
12636 if (cpuCount
>= 0 && !SetFakeCPUCount(cpuCount
)) {
12643 bool SetContextOptions(JSContext
* cx
, const OptionParser
& op
) {
12644 if (!SetContextWasmOptions(cx
, op
) || !SetContextJITOptions(cx
, op
) ||
12645 !SetContextGCOptions(cx
, op
)) {
12649 enableSourcePragmas
= !op
.getBoolOption("no-source-pragmas");
12650 enableAsyncStacks
= !op
.getBoolOption("no-async-stacks");
12651 enableAsyncStackCaptureDebuggeeOnly
=
12652 op
.getBoolOption("async-stacks-capture-debuggee-only");
12653 enableToSource
= !op
.getBoolOption("disable-tosource");
12654 #ifdef ENABLE_JSON_PARSE_WITH_SOURCE
12655 enableJSONParseWithSource
= op
.getBoolOption("enable-json-parse-with-source");
12657 enableImportAttributesAssertSyntax
=
12658 op
.getBoolOption("enable-import-assertions");
12659 enableImportAttributes
= op
.getBoolOption("enable-import-attributes") ||
12660 enableImportAttributesAssertSyntax
;
12662 JS::ContextOptionsRef(cx
)
12663 .setSourcePragmas(enableSourcePragmas
)
12664 .setAsyncStack(enableAsyncStacks
)
12665 .setAsyncStackCaptureDebuggeeOnly(enableAsyncStackCaptureDebuggeeOnly
)
12666 .setImportAttributes(enableImportAttributes
)
12667 .setImportAttributesAssertSyntax(enableImportAttributesAssertSyntax
);
12669 if (const char* str
= op
.getStringOption("shared-memory")) {
12670 if (strcmp(str
, "off") == 0) {
12671 enableSharedMemory
= false;
12672 } else if (strcmp(str
, "on") == 0) {
12673 enableSharedMemory
= true;
12675 return OptionFailure("shared-memory", str
);
12679 reportWarnings
= op
.getBoolOption('w');
12680 compileOnly
= op
.getBoolOption('c');
12681 printTiming
= op
.getBoolOption('b');
12682 enableDisassemblyDumps
= op
.getBoolOption('D');
12683 cx
->runtime()->profilingScripts
=
12684 enableCodeCoverage
|| enableDisassemblyDumps
;
12686 #ifdef JS_ENABLE_SMOOSH
12687 if (op
.getBoolOption("smoosh")) {
12688 JS::ContextOptionsRef(cx
).setTrySmoosh(true);
12689 js::frontend::InitSmoosh();
12692 if (const char* filename
= op
.getStringOption("not-implemented-watchfile")) {
12693 FILE* out
= fopen(filename
, "a");
12694 MOZ_RELEASE_ASSERT(out
);
12695 setbuf(out
, nullptr); // Make unbuffered
12696 cx
->runtime()->parserWatcherFile
.init(out
);
12697 JS::ContextOptionsRef(cx
).setTrackNotImplemented(true);
12701 if (const char* mode
= op
.getStringOption("delazification-mode")) {
12702 if (strcmp(mode
, "on-demand") == 0) {
12703 defaultDelazificationMode
= JS::DelazificationOption::OnDemandOnly
;
12704 } else if (strcmp(mode
, "concurrent-df") == 0) {
12705 defaultDelazificationMode
=
12706 JS::DelazificationOption::ConcurrentDepthFirst
;
12707 } else if (strcmp(mode
, "eager") == 0) {
12708 defaultDelazificationMode
=
12709 JS::DelazificationOption::ParseEverythingEagerly
;
12710 } else if (strcmp(mode
, "concurrent-df+on-demand") == 0 ||
12711 strcmp(mode
, "on-demand+concurrent-df") == 0) {
12712 defaultDelazificationMode
=
12713 JS::DelazificationOption::CheckConcurrentWithOnDemand
;
12715 return OptionFailure("delazification-mode", mode
);
12722 bool SetContextWasmOptions(JSContext
* cx
, const OptionParser
& op
) {
12723 enableAsmJS
= !op
.getBoolOption("no-asmjs");
12726 enableWasmBaseline
= true;
12727 enableWasmOptimizing
= true;
12729 if (const char* str
= op
.getStringOption("wasm-compiler")) {
12730 if (strcmp(str
, "none") == 0) {
12731 enableWasm
= false;
12732 } else if (strcmp(str
, "baseline") == 0) {
12733 MOZ_ASSERT(enableWasmBaseline
);
12734 enableWasmOptimizing
= false;
12735 } else if (strcmp(str
, "optimizing") == 0 ||
12736 strcmp(str
, "optimized") == 0) {
12737 enableWasmBaseline
= false;
12738 MOZ_ASSERT(enableWasmOptimizing
);
12739 } else if (strcmp(str
, "baseline+optimizing") == 0 ||
12740 strcmp(str
, "baseline+optimized") == 0) {
12741 MOZ_ASSERT(enableWasmBaseline
);
12742 MOZ_ASSERT(enableWasmOptimizing
);
12743 } else if (strcmp(str
, "ion") == 0) {
12744 enableWasmBaseline
= false;
12745 enableWasmOptimizing
= true;
12746 } else if (strcmp(str
, "baseline+ion") == 0) {
12747 MOZ_ASSERT(enableWasmBaseline
);
12748 enableWasmOptimizing
= true;
12750 return OptionFailure("wasm-compiler", str
);
12754 enableWasmVerbose
= op
.getBoolOption("wasm-verbose");
12755 enableTestWasmAwaitTier2
= op
.getBoolOption("test-wasm-await-tier2");
12757 JS::ContextOptionsRef(cx
)
12758 .setAsmJS(enableAsmJS
)
12759 .setWasm(enableWasm
)
12760 .setWasmForTrustedPrinciples(enableWasm
)
12761 .setWasmBaseline(enableWasmBaseline
)
12762 .setWasmIon(enableWasmOptimizing
);
12765 // This must be set before self-hosted code is initialized, as self-hosted
12766 // code reads the property and the property may not be changed later.
12767 bool disabledHugeMemory
= false;
12768 if (op
.getBoolOption("disable-wasm-huge-memory")) {
12769 disabledHugeMemory
= JS::DisableWasmHugeMemory();
12770 MOZ_RELEASE_ASSERT(disabledHugeMemory
);
12773 // --disable-wasm-huge-memory needs to be propagated. See bug 1518210.
12774 if (disabledHugeMemory
&&
12775 !sCompilerProcessFlags
.append("--disable-wasm-huge-memory")) {
12779 // Also the following are to be propagated.
12780 const char* to_propagate
[] = {
12781 // Compiler selection options
12782 "--test-wasm-await-tier2",
12784 for (const char* p
: to_propagate
) {
12785 if (op
.getBoolOption(p
+ 2 /* 2 => skip the leading '--' */)) {
12786 if (!sCompilerProcessFlags
.append(p
)) {
12792 // Also --wasm-compiler= is to be propagated. This is tricky because it is
12793 // necessary to reconstitute the --wasm-compiler=<whatever> string from its
12794 // pieces, without causing a leak. Hence it is copied into a static buffer.
12795 // This is thread-unsafe, but we're in `main()` and on the process' root
12796 // thread. Also, we do this only once -- it wouldn't work properly if we
12797 // handled multiple --wasm-compiler= flags in a loop.
12798 const char* wasm_compiler
= op
.getStringOption("wasm-compiler");
12799 if (wasm_compiler
) {
12801 2 + strlen("wasm-compiler") + 1 + strlen(wasm_compiler
) + 1;
12802 const size_t n_avail
= 128;
12803 static char buf
[n_avail
];
12804 // `n_needed` depends on the compiler name specified. However, it can't
12805 // be arbitrarily long, since previous flag-checking should have limited
12806 // it to a set of known possibilities: "baseline", "ion",
12807 // "baseline+ion", Still, assert this for safety.
12808 MOZ_RELEASE_ASSERT(n_needed
< n_avail
);
12809 memset(buf
, 0, sizeof(buf
));
12810 SprintfBuf(buf
, n_avail
, "--%s=%s", "wasm-compiler", wasm_compiler
);
12811 if (!sCompilerProcessFlags
.append(buf
)) {
12820 bool SetContextJITOptions(JSContext
* cx
, const OptionParser
& op
) {
12821 // Check --fast-warmup first because it sets default warm-up thresholds. These
12822 // thresholds can then be overridden below by --ion-eager and other flags.
12823 if (op
.getBoolOption("fast-warmup")) {
12824 jit::JitOptions
.setFastWarmUp();
12827 if (op
.getBoolOption("no-ion-for-main-context")) {
12828 JS::ContextOptionsRef(cx
).setDisableIon();
12831 if (const char* str
= op
.getStringOption("cache-ir-stubs")) {
12832 if (strcmp(str
, "on") == 0) {
12833 jit::JitOptions
.disableCacheIR
= false;
12834 } else if (strcmp(str
, "off") == 0) {
12835 jit::JitOptions
.disableCacheIR
= true;
12837 return OptionFailure("cache-ir-stubs", str
);
12841 if (const char* str
= op
.getStringOption("spectre-mitigations")) {
12842 if (strcmp(str
, "on") == 0) {
12843 jit::JitOptions
.spectreIndexMasking
= true;
12844 jit::JitOptions
.spectreObjectMitigations
= true;
12845 jit::JitOptions
.spectreStringMitigations
= true;
12846 jit::JitOptions
.spectreValueMasking
= true;
12847 jit::JitOptions
.spectreJitToCxxCalls
= true;
12848 } else if (strcmp(str
, "off") == 0) {
12849 jit::JitOptions
.spectreIndexMasking
= false;
12850 jit::JitOptions
.spectreObjectMitigations
= false;
12851 jit::JitOptions
.spectreStringMitigations
= false;
12852 jit::JitOptions
.spectreValueMasking
= false;
12853 jit::JitOptions
.spectreJitToCxxCalls
= false;
12855 return OptionFailure("spectre-mitigations", str
);
12859 if (const char* str
= op
.getStringOption("write-protect-code")) {
12860 if (strcmp(str
, "on") == 0) {
12861 jit::JitOptions
.maybeSetWriteProtectCode(true);
12862 } else if (strcmp(str
, "off") == 0) {
12863 jit::JitOptions
.maybeSetWriteProtectCode(false);
12865 return OptionFailure("write-protect-code", str
);
12869 if (const char* str
= op
.getStringOption("monomorphic-inlining")) {
12870 if (strcmp(str
, "default") == 0) {
12871 jit::JitOptions
.monomorphicInlining
=
12872 jit::UseMonomorphicInlining::Default
;
12873 } else if (strcmp(str
, "always") == 0) {
12874 jit::JitOptions
.monomorphicInlining
= jit::UseMonomorphicInlining::Always
;
12875 } else if (strcmp(str
, "never") == 0) {
12876 jit::JitOptions
.monomorphicInlining
= jit::UseMonomorphicInlining::Never
;
12878 return OptionFailure("monomorphic-inlining", str
);
12882 if (const char* str
= op
.getStringOption("ion-scalar-replacement")) {
12883 if (strcmp(str
, "on") == 0) {
12884 jit::JitOptions
.disableScalarReplacement
= false;
12885 } else if (strcmp(str
, "off") == 0) {
12886 jit::JitOptions
.disableScalarReplacement
= true;
12888 return OptionFailure("ion-scalar-replacement", str
);
12892 if (op
.getStringOption("ion-shared-stubs")) {
12893 // Dead option, preserved for now for potential fuzzer interaction.
12896 if (const char* str
= op
.getStringOption("ion-gvn")) {
12897 if (strcmp(str
, "off") == 0) {
12898 jit::JitOptions
.disableGvn
= true;
12899 } else if (strcmp(str
, "on") != 0 && strcmp(str
, "optimistic") != 0 &&
12900 strcmp(str
, "pessimistic") != 0) {
12901 // We accept "pessimistic" and "optimistic" as synonyms for "on"
12902 // for backwards compatibility.
12903 return OptionFailure("ion-gvn", str
);
12907 if (const char* str
= op
.getStringOption("ion-licm")) {
12908 if (strcmp(str
, "on") == 0) {
12909 jit::JitOptions
.disableLicm
= false;
12910 } else if (strcmp(str
, "off") == 0) {
12911 jit::JitOptions
.disableLicm
= true;
12913 return OptionFailure("ion-licm", str
);
12917 if (const char* str
= op
.getStringOption("ion-edgecase-analysis")) {
12918 if (strcmp(str
, "on") == 0) {
12919 jit::JitOptions
.disableEdgeCaseAnalysis
= false;
12920 } else if (strcmp(str
, "off") == 0) {
12921 jit::JitOptions
.disableEdgeCaseAnalysis
= true;
12923 return OptionFailure("ion-edgecase-analysis", str
);
12927 if (const char* str
= op
.getStringOption("ion-pruning")) {
12928 if (strcmp(str
, "on") == 0) {
12929 jit::JitOptions
.disablePruning
= false;
12930 } else if (strcmp(str
, "off") == 0) {
12931 jit::JitOptions
.disablePruning
= true;
12933 return OptionFailure("ion-pruning", str
);
12937 if (const char* str
= op
.getStringOption("ion-range-analysis")) {
12938 if (strcmp(str
, "on") == 0) {
12939 jit::JitOptions
.disableRangeAnalysis
= false;
12940 } else if (strcmp(str
, "off") == 0) {
12941 jit::JitOptions
.disableRangeAnalysis
= true;
12943 return OptionFailure("ion-range-analysis", str
);
12947 if (const char* str
= op
.getStringOption("ion-sink")) {
12948 if (strcmp(str
, "on") == 0) {
12949 jit::JitOptions
.disableSink
= false;
12950 } else if (strcmp(str
, "off") == 0) {
12951 jit::JitOptions
.disableSink
= true;
12953 return OptionFailure("ion-sink", str
);
12957 if (const char* str
= op
.getStringOption("ion-optimize-shapeguards")) {
12958 if (strcmp(str
, "on") == 0) {
12959 jit::JitOptions
.disableRedundantShapeGuards
= false;
12960 } else if (strcmp(str
, "off") == 0) {
12961 jit::JitOptions
.disableRedundantShapeGuards
= true;
12963 return OptionFailure("ion-optimize-shapeguards", str
);
12967 if (const char* str
= op
.getStringOption("ion-optimize-gcbarriers")) {
12968 if (strcmp(str
, "on") == 0) {
12969 jit::JitOptions
.disableRedundantGCBarriers
= false;
12970 } else if (strcmp(str
, "off") == 0) {
12971 jit::JitOptions
.disableRedundantGCBarriers
= true;
12973 return OptionFailure("ion-optimize-gcbarriers", str
);
12977 if (const char* str
= op
.getStringOption("ion-instruction-reordering")) {
12978 if (strcmp(str
, "on") == 0) {
12979 jit::JitOptions
.disableInstructionReordering
= false;
12980 } else if (strcmp(str
, "off") == 0) {
12981 jit::JitOptions
.disableInstructionReordering
= true;
12983 return OptionFailure("ion-instruction-reordering", str
);
12987 if (op
.getBoolOption("ion-check-range-analysis")) {
12988 jit::JitOptions
.checkRangeAnalysis
= true;
12991 if (op
.getBoolOption("ion-extra-checks")) {
12992 jit::JitOptions
.runExtraChecks
= true;
12995 if (const char* str
= op
.getStringOption("ion-inlining")) {
12996 if (strcmp(str
, "on") == 0) {
12997 jit::JitOptions
.disableInlining
= false;
12998 } else if (strcmp(str
, "off") == 0) {
12999 jit::JitOptions
.disableInlining
= true;
13001 return OptionFailure("ion-inlining", str
);
13005 if (const char* str
= op
.getStringOption("ion-osr")) {
13006 if (strcmp(str
, "on") == 0) {
13007 jit::JitOptions
.osr
= true;
13008 } else if (strcmp(str
, "off") == 0) {
13009 jit::JitOptions
.osr
= false;
13011 return OptionFailure("ion-osr", str
);
13015 if (const char* str
= op
.getStringOption("ion-limit-script-size")) {
13016 if (strcmp(str
, "on") == 0) {
13017 jit::JitOptions
.limitScriptSize
= true;
13018 } else if (strcmp(str
, "off") == 0) {
13019 jit::JitOptions
.limitScriptSize
= false;
13021 return OptionFailure("ion-limit-script-size", str
);
13025 int32_t warmUpThreshold
= op
.getIntOption("ion-warmup-threshold");
13026 if (warmUpThreshold
>= 0) {
13027 jit::JitOptions
.setNormalIonWarmUpThreshold(warmUpThreshold
);
13030 warmUpThreshold
= op
.getIntOption("baseline-warmup-threshold");
13031 if (warmUpThreshold
>= 0) {
13032 jit::JitOptions
.baselineJitWarmUpThreshold
= warmUpThreshold
;
13035 warmUpThreshold
= op
.getIntOption("trial-inlining-warmup-threshold");
13036 if (warmUpThreshold
>= 0) {
13037 jit::JitOptions
.trialInliningWarmUpThreshold
= warmUpThreshold
;
13040 warmUpThreshold
= op
.getIntOption("regexp-warmup-threshold");
13041 if (warmUpThreshold
>= 0) {
13042 jit::JitOptions
.regexpWarmUpThreshold
= warmUpThreshold
;
13045 if (op
.getBoolOption("baseline-eager")) {
13046 jit::JitOptions
.setEagerBaselineCompilation();
13049 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
13050 if (op
.getBoolOption("portable-baseline-eager")) {
13051 jit::JitOptions
.setEagerPortableBaselineInterpreter();
13053 if (op
.getBoolOption("portable-baseline")) {
13054 jit::JitOptions
.portableBaselineInterpreter
= true;
13056 if (op
.getBoolOption("no-portable-baseline")) {
13057 jit::JitOptions
.portableBaselineInterpreter
= false;
13061 if (op
.getBoolOption("blinterp")) {
13062 jit::JitOptions
.baselineInterpreter
= true;
13065 if (op
.getBoolOption("no-blinterp")) {
13066 jit::JitOptions
.baselineInterpreter
= false;
13069 if (op
.getBoolOption("disable-jithints")) {
13070 jit::JitOptions
.disableJitHints
= true;
13073 if (op
.getBoolOption("emit-interpreter-entry")) {
13074 jit::JitOptions
.emitInterpreterEntryTrampoline
= true;
13077 if (op
.getBoolOption("no-emit-interpreter-entry")) {
13078 jit::JitOptions
.emitInterpreterEntryTrampoline
= false;
13081 warmUpThreshold
= op
.getIntOption("blinterp-warmup-threshold");
13082 if (warmUpThreshold
>= 0) {
13083 jit::JitOptions
.baselineInterpreterWarmUpThreshold
= warmUpThreshold
;
13086 if (op
.getBoolOption("blinterp-eager")) {
13087 jit::JitOptions
.baselineInterpreterWarmUpThreshold
= 0;
13090 if (op
.getBoolOption("no-baseline")) {
13091 jit::JitOptions
.baselineJit
= false;
13094 if (op
.getBoolOption("no-ion")) {
13095 jit::JitOptions
.ion
= false;
13098 if (op
.getBoolOption("no-native-regexp")) {
13099 jit::JitOptions
.nativeRegExp
= false;
13102 if (op
.getBoolOption("trace-regexp-parser")) {
13103 jit::JitOptions
.trace_regexp_parser
= true;
13105 if (op
.getBoolOption("trace-regexp-assembler")) {
13106 jit::JitOptions
.trace_regexp_assembler
= true;
13108 if (op
.getBoolOption("trace-regexp-interpreter")) {
13109 jit::JitOptions
.trace_regexp_bytecodes
= true;
13111 if (op
.getBoolOption("trace-regexp-peephole")) {
13112 jit::JitOptions
.trace_regexp_peephole_optimization
= true;
13115 if (op
.getBoolOption("less-debug-code")) {
13116 jit::JitOptions
.lessDebugCode
= true;
13119 int32_t inliningEntryThreshold
= op
.getIntOption("inlining-entry-threshold");
13120 if (inliningEntryThreshold
> 0) {
13121 jit::JitOptions
.inliningEntryThreshold
= inliningEntryThreshold
;
13124 int32_t smallFunctionLength
= op
.getIntOption("small-function-length");
13125 if (smallFunctionLength
> 0) {
13126 jit::JitOptions
.smallFunctionMaxBytecodeLength
= smallFunctionLength
;
13129 if (const char* str
= op
.getStringOption("ion-regalloc")) {
13130 jit::JitOptions
.forcedRegisterAllocator
= jit::LookupRegisterAllocator(str
);
13131 if (!jit::JitOptions
.forcedRegisterAllocator
.isSome()) {
13132 return OptionFailure("ion-regalloc", str
);
13136 if (op
.getBoolOption("ion-eager")) {
13137 jit::JitOptions
.setEagerIonCompilation();
13140 offthreadCompilation
= true;
13141 if (const char* str
= op
.getStringOption("ion-offthread-compile")) {
13142 if (strcmp(str
, "off") == 0) {
13143 offthreadCompilation
= false;
13144 } else if (strcmp(str
, "on") != 0) {
13145 return OptionFailure("ion-offthread-compile", str
);
13148 cx
->runtime()->setOffthreadIonCompilationEnabled(offthreadCompilation
);
13150 if (op
.getStringOption("ion-parallel-compile")) {
13152 "--ion-parallel-compile is deprecated. Please use "
13153 "--ion-offthread-compile instead.\n");
13157 if (op
.getBoolOption("disable-bailout-loop-check")) {
13158 jit::JitOptions
.disableBailoutLoopCheck
= true;
13161 if (op
.getBoolOption("only-inline-selfhosted")) {
13162 jit::JitOptions
.onlyInlineSelfHosted
= true;
13165 if (op
.getBoolOption("enable-ic-frame-pointers")) {
13166 jit::JitOptions
.enableICFramePointers
= true;
13169 if (const char* str
= op
.getStringOption("ion-iterator-indices")) {
13170 if (strcmp(str
, "on") == 0) {
13171 jit::JitOptions
.disableIteratorIndices
= false;
13172 } else if (strcmp(str
, "off") == 0) {
13173 jit::JitOptions
.disableIteratorIndices
= true;
13175 return OptionFailure("ion-iterator-indices", str
);
13179 if (const char* str
= op
.getStringOption("ion-load-keys")) {
13180 if (strcmp(str
, "on") == 0) {
13181 jit::JitOptions
.disableMarkLoadsUsedAsPropertyKeys
= false;
13182 } else if (strcmp(str
, "off") == 0) {
13183 jit::JitOptions
.disableMarkLoadsUsedAsPropertyKeys
= true;
13185 return OptionFailure("ion-load-keys", str
);
13189 #if defined(JS_SIMULATOR_ARM)
13190 if (op
.getBoolOption("arm-sim-icache-checks")) {
13191 jit::SimulatorProcess::ICacheCheckingDisableCount
= 0;
13194 int32_t stopAt
= op
.getIntOption("arm-sim-stop-at");
13196 jit::Simulator::StopSimAt
= stopAt
;
13198 #elif defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
13199 if (op
.getBoolOption("mips-sim-icache-checks")) {
13200 jit::SimulatorProcess::ICacheCheckingDisableCount
= 0;
13203 int32_t stopAt
= op
.getIntOption("mips-sim-stop-at");
13205 jit::Simulator::StopSimAt
= stopAt
;
13207 #elif defined(JS_SIMULATOR_LOONG64)
13208 if (op
.getBoolOption("loong64-sim-icache-checks")) {
13209 jit::SimulatorProcess::ICacheCheckingDisableCount
= 0;
13212 int32_t stopAt
= op
.getIntOption("loong64-sim-stop-at");
13214 jit::Simulator::StopSimAt
= stopAt
;
13219 # ifdef JS_CODEGEN_RISCV64
13220 if (op
.getBoolOption("riscv-debug")) {
13221 jit::Assembler::FLAG_riscv_debug
= true;
13224 # ifdef JS_SIMULATOR_RISCV64
13225 if (op
.getBoolOption("trace-sim")) {
13226 jit::Simulator::FLAG_trace_sim
= true;
13228 if (op
.getBoolOption("debug-sim")) {
13229 jit::Simulator::FLAG_debug_sim
= true;
13231 if (op
.getBoolOption("riscv-trap-to-simulator-debugger")) {
13232 jit::Simulator::FLAG_riscv_trap_to_simulator_debugger
= true;
13234 int32_t stopAt
= op
.getIntOption("riscv-sim-stop-at");
13236 jit::Simulator::StopSimAt
= stopAt
;
13244 bool SetContextGCOptions(JSContext
* cx
, const OptionParser
& op
) {
13245 JS_SetGCParameter(cx
, JSGC_MAX_BYTES
, 0xffffffff);
13247 size_t nurseryBytes
= op
.getIntOption("nursery-size") * 1024L * 1024L;
13248 if (nurseryBytes
== 0) {
13249 fprintf(stderr
, "Error: --nursery-size parameter must be non-zero.\n");
13251 "The nursery can be disabled by passing the --no-ggc option.\n");
13254 JS_SetGCParameter(cx
, JSGC_MAX_NURSERY_BYTES
, nurseryBytes
);
13256 size_t availMemMB
= op
.getIntOption("available-memory");
13257 if (availMemMB
> 0) {
13258 JS_SetGCParametersBasedOnAvailableMemory(cx
, availMemMB
);
13261 if (const char* opt
= op
.getStringOption("nursery-strings")) {
13262 if (strcmp(opt
, "on") == 0) {
13263 cx
->runtime()->gc
.nursery().enableStrings();
13264 } else if (strcmp(opt
, "off") == 0) {
13265 cx
->runtime()->gc
.nursery().disableStrings();
13267 MOZ_CRASH("invalid option value for --nursery-strings, must be on/off");
13271 if (const char* opt
= op
.getStringOption("nursery-bigints")) {
13272 if (strcmp(opt
, "on") == 0) {
13273 cx
->runtime()->gc
.nursery().enableBigInts();
13274 } else if (strcmp(opt
, "off") == 0) {
13275 cx
->runtime()->gc
.nursery().disableBigInts();
13277 MOZ_CRASH("invalid option value for --nursery-bigints, must be on/off");
13281 bool incrementalGC
= !op
.getBoolOption("no-incremental-gc");
13282 JS_SetGCParameter(cx
, JSGC_INCREMENTAL_GC_ENABLED
, incrementalGC
);
13285 bool parallelMarking
= true;
13287 bool parallelMarking
= false;
13289 if (op
.getBoolOption("enable-parallel-marking")) {
13290 parallelMarking
= true;
13292 if (op
.getBoolOption("no-parallel-marking")) {
13293 parallelMarking
= false;
13295 JS_SetGCParameter(cx
, JSGC_PARALLEL_MARKING_ENABLED
, parallelMarking
);
13297 int32_t markingThreads
= op
.getIntOption("marking-threads");
13298 if (markingThreads
> 0) {
13299 JS_SetGCParameter(cx
, JSGC_MARKING_THREAD_COUNT
, markingThreads
);
13302 JS_SetGCParameter(cx
, JSGC_SLICE_TIME_BUDGET_MS
, 5);
13304 JS_SetGCParameter(cx
, JSGC_PER_ZONE_GC_ENABLED
, true);
13306 for (MultiStringRange args
= op
.getMultiStringOption("gc-param");
13307 !args
.empty(); args
.popFront()) {
13308 if (!SetGCParameterFromArg(cx
, args
.front())) {
13314 dumpEntrainedVariables
= op
.getBoolOption("dump-entrained-variables");
13318 const char* zealStr
= op
.getStringOption("gc-zeal");
13320 if (!cx
->runtime()->gc
.parseAndSetZeal(zealStr
)) {
13323 uint32_t nextScheduled
;
13324 cx
->runtime()->gc
.getZealBits(&gZealBits
, &gZealFrequency
, &nextScheduled
);
13331 bool InitModuleLoader(JSContext
* cx
, const OptionParser
& op
) {
13332 RootedString
moduleLoadPath(cx
);
13333 if (const char* option
= op
.getStringOption("module-load-path")) {
13334 UniqueChars pathUtf8
= JS::EncodeNarrowToUtf8(cx
, option
);
13339 Rooted
<JSString
*> jspath(cx
, NewStringCopyUTF8(cx
, pathUtf8
.get()));
13344 moduleLoadPath
= js::shell::ResolvePath(cx
, jspath
, RootRelative
);
13346 processWideModuleLoadPath
= JS_EncodeStringToUTF8(cx
, moduleLoadPath
);
13347 if (!processWideModuleLoadPath
) {
13351 processWideModuleLoadPath
= js::shell::GetCWD(cx
);
13352 if (!processWideModuleLoadPath
) {
13356 moduleLoadPath
= NewStringCopyUTF8(cx
, processWideModuleLoadPath
.get());
13357 if (!moduleLoadPath
) {
13362 ShellContext
* sc
= GetShellContext(cx
);
13363 sc
->moduleLoader
= js::MakeUnique
<ModuleLoader
>();
13364 if (!sc
->moduleLoader
|| !sc
->moduleLoader
->init(cx
, moduleLoadPath
)) {