Bug 1885337 - Part 1: Implement to/from hex methods. r=dminor
[gecko.git] / js / src / shell / js.cpp
blobf90bd5d6e22955d5dba030f2404cdefa818efd78
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* JS shell. */
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"
29 #include <algorithm>
30 #include <chrono>
31 #ifdef XP_WIN
32 # include <direct.h>
33 # include <process.h>
34 #endif
35 #include <errno.h>
36 #include <fcntl.h>
37 #if defined(XP_WIN)
38 # include <io.h> /* for isatty() */
39 #endif
40 #include <locale.h>
41 #if defined(MALLOC_H)
42 # include MALLOC_H /* for malloc_usable_size, malloc_size, _msize */
43 #endif
44 #include <ctime>
45 #include <math.h>
46 #ifndef __wasi__
47 # include <signal.h>
48 #endif
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <sys/stat.h>
53 #include <sys/types.h>
54 #include <utility>
55 #ifdef XP_UNIX
56 # ifndef __wasi__
57 # include <sys/mman.h>
58 # include <sys/wait.h>
59 # endif
60 # include <sys/stat.h>
61 # include <unistd.h>
62 #endif
63 #ifdef XP_LINUX
64 # include <sys/prctl.h>
65 #endif
67 #include "jsapi.h"
68 #include "jsfriendapi.h"
69 #include "jstypes.h"
70 #ifndef JS_WITHOUT_NSPR
71 # include "prerror.h"
72 # include "prlink.h"
73 #endif
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"
86 #endif
87 #include "frontend/FrontendContext.h" // AutoReportFrontendContext
88 #include "frontend/ModuleSharedContext.h"
89 #include "frontend/Parser.h"
90 #include "frontend/ScopeBindingCache.h" // js::frontend::ScopeBindingCache
91 #include "gc/GC.h"
92 #include "gc/PublicIterators.h"
93 #ifdef DEBUG
94 # include "irregexp/RegExpAPI.h"
95 #endif
97 #ifdef JS_SIMULATOR_ARM
98 # include "jit/arm/Simulator-arm.h"
99 #endif
100 #ifdef JS_SIMULATOR_MIPS32
101 # include "jit/mips32/Simulator-mips32.h"
102 #endif
103 #ifdef JS_SIMULATOR_MIPS64
104 # include "jit/mips64/Simulator-mips64.h"
105 #endif
106 #ifdef JS_SIMULATOR_LOONG64
107 # include "jit/loong64/Simulator-loong64.h"
108 #endif
109 #ifdef JS_SIMULATOR_RISCV64
110 # include "jit/riscv64/Simulator-riscv64.h"
111 #endif
112 #include "jit/CacheIRHealth.h"
113 #include "jit/InlinableNatives.h"
114 #include "jit/Ion.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"
147 #include "js/JSON.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
203 #include "vm/Time.h"
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"
217 #undef compress
219 using namespace js;
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)
258 struct shmem_data {
259 uint32_t num_edges;
260 unsigned char edges[];
263 struct shmem_data* __shmem;
265 uint32_t *__edges_start, *__edges_stop;
266 void __sanitizer_cov_reset_edgeguards() {
267 uint64_t N = 0;
268 for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++)
269 *x = ++N;
272 extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start,
273 uint32_t* stop) {
274 // Avoid duplicate initialization
275 if (start == stop || *start) return;
277 if (__edges_start != NULL || __edges_stop != NULL) {
278 fprintf(stderr,
279 "Coverage instrumentation is only supported for a single module\n");
280 _exit(-1);
283 __edges_start = start;
284 __edges_stop = stop;
286 // Map the shared memory region
287 const char* shm_key = getenv("SHM_ID");
288 if (!shm_key) {
289 puts("[COV] no shared memory bitmap available, skipping");
290 __shmem = (struct shmem_data*)malloc(SHM_SIZE);
291 } else {
292 int fd = shm_open(shm_key, O_RDWR, S_IREAD | S_IWRITE);
293 if (fd <= -1) {
294 fprintf(stderr, "Failed to open shared memory region: %s\n",
295 strerror(errno));
296 _exit(-1);
299 __shmem = (struct shmem_data*)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE,
300 MAP_SHARED, fd, 0);
301 if (__shmem == MAP_FAILED) {
302 fprintf(stderr, "Failed to mmap shared memory region\n");
303 _exit(-1);
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.
323 if (!index) return;
324 __shmem->edges[index / 8] |= 1 << (index % 8);
325 *guard = 0;
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,
333 EXITCODE_TIMEOUT = 6
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
345 #else
346 # define SHARED_MEMORY_DEFAULT 0
347 #endif
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(); }
365 # else
366 void counters_dump(int) { /* Do nothing */
369 void counters_reset(int) { /* Do nothing */
371 # endif
373 static void InstallCoverageSignalHandlers() {
374 # ifndef XP_WIN
375 fprintf(stderr, "[CodeCoverage] Setting handlers for process %d.\n",
376 getpid());
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");
391 # endif
393 #endif
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;
401 enum State {
402 RUNNING, // Working; no stencil.
403 DONE, // Finished; have stencil.
404 CANCELLED // Cancelled due to error.
407 public:
408 enum class Kind {
409 CompileScript,
410 CompileModule,
411 Decode,
414 OffThreadJob(ShellContext* sc, Kind kind, JS::SourceText<char16_t>&& srcBuf);
415 OffThreadJob(ShellContext* sc, Kind kind, JS::TranscodeBuffer&& xdrBuf);
417 ~OffThreadJob();
419 bool init(JSContext* cx, const JS::ReadOnlyCompileOptions& options);
420 bool dispatch();
422 static void OffThreadMain(OffThreadJob* self);
423 void run();
425 void cancel();
426 void waitUntilDone();
428 already_AddRefed<JS::Stencil> stealStencil(JSContext* cx);
430 public:
431 const int32_t id;
433 private:
434 Kind kind_;
435 State state_;
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,
453 T&& source) {
454 ShellContext* sc = GetShellContext(cx);
455 if (sc->isWorker) {
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");
460 return nullptr;
463 UniquePtr<OffThreadJob> job(
464 cx->new_<OffThreadJob>(sc, kind, std::move(source)));
465 if (!job) {
466 return nullptr;
469 if (!job->init(cx, options)) {
470 return nullptr;
473 if (!sc->offThreadJobs.append(job.get())) {
474 job->cancel();
475 JS_ReportErrorASCII(cx, "OOM adding off-thread job");
476 return nullptr;
479 return job.release();
482 static OffThreadJob* GetSingleOffThreadJob(JSContext* cx) {
483 ShellContext* sc = GetShellContext(cx);
484 const auto& jobs = sc->offThreadJobs;
485 if (jobs.empty()) {
486 JS_ReportErrorASCII(cx, "No off-thread jobs are pending");
487 return nullptr;
490 if (jobs.length() > 1) {
491 JS_ReportErrorASCII(
492 cx, "Multiple off-thread jobs are pending: must specify job ID");
493 return nullptr;
496 return jobs[0];
499 static OffThreadJob* LookupOffThreadJobByID(JSContext* cx, int32_t id) {
500 if (id <= 0) {
501 JS_ReportErrorASCII(cx, "Bad off-thread job ID");
502 return nullptr;
505 ShellContext* sc = GetShellContext(cx);
506 const auto& jobs = sc->offThreadJobs;
507 if (jobs.empty()) {
508 JS_ReportErrorASCII(cx, "No off-thread jobs are pending");
509 return nullptr;
512 OffThreadJob* job = nullptr;
513 for (auto someJob : jobs) {
514 if (someJob->id == id) {
515 job = someJob;
516 break;
520 if (!job) {
521 JS_ReportErrorASCII(cx, "Off-thread job not found");
522 return nullptr;
525 return job;
528 static OffThreadJob* LookupOffThreadJobForArgs(JSContext* cx,
529 const CallArgs& args,
530 size_t arg) {
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.
537 int32_t id = 0;
538 RootedValue value(cx, args[arg]);
539 if (!ToInt32(cx, value, &id)) {
540 return nullptr;
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]);
551 js_delete(job);
552 return;
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();
564 js_delete(job);
568 mozilla::Atomic<int32_t> gOffThreadJobSerial(1);
570 OffThreadJob::OffThreadJob(ShellContext* sc, Kind kind,
571 JS::SourceText<char16_t>&& srcBuf)
572 : id(gOffThreadJobSerial++),
573 kind_(kind),
574 state_(RUNNING),
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++),
583 kind_(kind),
584 state_(RUNNING),
585 options_(JS::OwningCompileOptions::ForFrontendContext()),
586 xdrBuf_(std::move(xdrBuf)) {
587 MOZ_RELEASE_ASSERT(id > 0, "Off-thread job IDs exhausted");
590 OffThreadJob::~OffThreadJob() {
591 if (fc_) {
592 JS::DestroyFrontendContext(fc_);
594 MOZ_ASSERT(state_ != RUNNING);
597 bool OffThreadJob::init(JSContext* cx,
598 const JS::ReadOnlyCompileOptions& options) {
599 fc_ = JS::NewFrontendContext();
600 if (!fc_) {
601 ReportOutOfMemory(cx);
602 state_ = CANCELLED;
603 return false;
606 if (!options_.copy(cx, options)) {
607 state_ = CANCELLED;
608 return false;
611 return true;
614 bool OffThreadJob::dispatch() {
615 thread_ =
616 js::MakeUnique<Thread>(Thread::Options().setStackSize(kThreadStackQuota));
617 if (!thread_) {
618 state_ = CANCELLED;
619 return false;
622 if (!thread_->init(OffThreadJob::OffThreadMain, this)) {
623 state_ = CANCELLED;
624 thread_ = nullptr;
625 return false;
628 return true;
631 /* static */ void OffThreadJob::OffThreadMain(OffThreadJob* self) {
632 self->run();
635 void OffThreadJob::run() {
636 MOZ_ASSERT(state_ == RUNNING);
637 MOZ_ASSERT(!stencil_);
639 JS::SetNativeStackQuota(fc_, kCompileStackQuota);
641 switch (kind_) {
642 case Kind::CompileScript: {
643 stencil_ = JS::CompileGlobalScriptToStencil(fc_, options_, srcBuf_);
644 break;
646 case Kind::CompileModule: {
647 stencil_ = JS::CompileModuleScriptToStencil(fc_, options_, srcBuf_);
648 break;
650 case Kind::Decode: {
651 JS::DecodeOptions decodeOptions(options_);
652 JS::TranscodeRange range(xdrBuf_.begin(), xdrBuf_.length());
653 transcodeResult_ = JS::DecodeStencil(fc_, decodeOptions, range,
654 getter_AddRefs(stencil_));
655 break;
659 state_ = DONE;
662 void OffThreadJob::cancel() {
663 MOZ_ASSERT(state_ == RUNNING);
664 MOZ_ASSERT(!stencil_);
665 MOZ_ASSERT(!thread_, "cannot cancel after starting a thread");
667 state_ = CANCELLED;
670 void OffThreadJob::waitUntilDone() {
671 MOZ_ASSERT(state_ != CANCELLED);
672 thread_->join();
675 already_AddRefed<JS::Stencil> OffThreadJob::stealStencil(JSContext* cx) {
676 JS::FrontendContext* fc = fc_;
677 fc_ = nullptr;
678 auto destroyFrontendContext =
679 mozilla::MakeScopeExit([&]() { JS::DestroyFrontendContext(fc); });
681 MOZ_ASSERT(fc);
683 if (JS::HadFrontendErrors(fc)) {
684 (void)JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options_);
685 return nullptr;
688 if (!stencil_ && JS::IsTranscodeFailureResult(transcodeResult_)) {
689 JS_ReportErrorASCII(cx, "failed to decode cache");
690 return nullptr;
693 // Report warnings.
694 if (!JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options_)) {
695 return nullptr;
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;
734 #endif
735 bool shell::enableImportAttributes = false;
736 bool shell::enableImportAttributesAssertSyntax = false;
737 #ifdef JS_GC_ZEAL
738 uint32_t shell::gZealBits = 0;
739 uint32_t shell::gZealFrequency = 0;
740 #endif
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;
749 #ifdef DEBUG
750 bool shell::dumpEntrainedVariables = false;
751 bool shell::OOM_printAllocationCount = false;
752 #endif
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 {
765 GlobalObject,
766 WindowProxy,
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);
788 JSObject* obj =
789 js::Wrapper::New(cx, global, &js::Wrapper::singleton, options);
790 MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
791 return 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 {
807 uint32_t bits;
809 static uint32_t getBits(JSPrincipals* p) {
810 if (!p) {
811 return 0xffff;
813 return static_cast<ShellPrincipals*>(p)->bits;
816 public:
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
824 // fuzzer is happy.
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
850 subsumes};
852 // The fully-trusted principal subsumes all other principals.
853 ShellPrincipals ShellPrincipals::fullyTrusted(-1, 1);
855 #ifdef EDITLINE
856 extern "C" {
857 extern MOZ_EXPORT char* readline(const char* prompt);
858 extern MOZ_EXPORT void add_history(char* line);
859 } // extern "C"
860 #endif
862 ShellContext::ShellContext(JSContext* cx, IsWorkerEnum isWorker_)
863 : cx_(nullptr),
864 isWorker(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),
876 exitCode(0),
877 quitting(false),
878 readLineBufPos(0),
879 errFilePtr(nullptr),
880 outFilePtr(nullptr),
881 offThreadMonitor(mutexid::ShellOffThreadState),
882 finalizationRegistryCleanupCallbacks(cx) {}
884 ShellContext* js::shell::GetShellContext(JSContext* cx) {
885 ShellContext* sc = static_cast<ShellContext*>(JS_GetContextPrivate(cx));
886 MOZ_ASSERT(sc);
887 return sc;
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()));
896 if (!priv) {
897 continue;
900 GCPtr<ArrayObject*>& array =
901 (color == gc::MarkColor::Black) ? priv->blackRoot : priv->grayRoot;
902 TraceNullableEdge(trc, &array, "shell root array");
904 if (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);
921 return true;
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) {
929 #ifdef EDITLINE
931 * Use readline only if file is stdin, because there's no way to specify
932 * another handle. Are other filehandles interactive?
934 if (file == stdin) {
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) {
943 errno = 0;
945 if (!linep) {
946 return nullptr;
948 if (linep[0] != '\0') {
949 add_history(linep.get());
951 return linep;
953 #endif
955 size_t len = 0;
956 if (*prompt != '\0' && gOutFile->isOpen()) {
957 fprintf(gOutFile->fp, "%s", prompt);
958 fflush(gOutFile->fp);
961 size_t size = 80;
962 mozilla::UniqueFreePtr<char[]> buffer(static_cast<char*>(malloc(size)));
963 if (!buffer) {
964 return nullptr;
967 char* current = buffer.get();
968 do {
969 while (true) {
970 if (fgets(current, size - len, file)) {
971 break;
973 if (errno != EINTR) {
974 return nullptr;
978 len += strlen(current);
979 char* t = buffer.get() + len - 1;
980 if (*t == '\n') {
981 /* Line was read. We remove '\n' and exit. */
982 *t = '\0';
983 break;
986 if (len + 1 == size) {
987 size = size * 2;
988 char* raw = buffer.release();
989 char* tmp = static_cast<char*>(realloc(raw, size));
990 if (!tmp) {
991 free(raw);
992 return nullptr;
994 buffer.reset(tmp);
996 current = buffer.get() + len;
997 } while (true);
998 return buffer;
1001 static bool ShellInterruptCallback(JSContext* cx) {
1002 ShellContext* sc = GetShellContext(cx);
1003 if (!sc->serviceInterrupt) {
1004 return true;
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;
1013 bool result;
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
1023 // JSOp::RetRval.
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) {
1030 are.emplace(cx);
1032 result = JS_CallFunctionValue(cx, nullptr, sc->interruptFunc,
1033 JS::HandleValueArray::empty(), &rval);
1035 savedExc.restore();
1037 if (rval.isBoolean()) {
1038 result = rval.toBoolean();
1039 } else {
1040 result = false;
1042 } else {
1043 result = false;
1046 if (!result && sc->exitCode == 0) {
1047 static const char msg[] = "Script terminated by interrupt handler.\n";
1048 fputs(msg, stderr);
1050 sc->exitCode = EXITCODE_TIMEOUT;
1053 return result;
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();
1062 #endif
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);
1076 // Skip the BOM
1077 if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF) {
1078 return;
1081 // No BOM - revert
1082 if (ch3 != EOF) {
1083 ungetc(ch3, file);
1085 if (ch2 != EOF) {
1086 ungetc(ch2, file);
1088 if (ch1 != EOF) {
1089 ungetc(ch1, file);
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);
1101 if (!closure(cx)) {
1102 return;
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));
1114 if (!path) {
1115 return false;
1118 MOZ_ASSERT(JS::GetScriptPrivate(script).isUndefined());
1119 RootedObject infoObject(cx, js::CreateScriptPrivate(cx, path));
1120 if (!infoObject) {
1121 return false;
1124 JS::SetScriptPrivate(script, ObjectValue(*infoObject));
1125 return true;
1128 enum class CompileUtf8 {
1129 InflateToUtf16,
1130 DontInflate,
1133 [[nodiscard]] static bool RunFile(JSContext* cx, const char* filename,
1134 FILE* file, CompileUtf8 compileMethod,
1135 bool compileOnly, bool fullParse) {
1136 SkipUTF8BOM(file);
1138 int64_t t1 = PRMJ_Now();
1139 RootedScript script(cx);
1142 CompileOptions options(cx);
1143 options.setIntroductionType("js shell file")
1144 .setFileAndLine(filename, 1)
1145 .setIsRunOnce(true)
1146 .setNoScriptRval(true);
1148 if (fullParse) {
1149 options.setForceFullParse();
1150 } else {
1151 options.setEagerDelazificationStrategy(defaultDelazificationMode);
1154 if (compileMethod == CompileUtf8::DontInflate) {
1155 script = JS::CompileUtf8File(cx, options, file);
1156 } else {
1157 fprintf(stderr, "(compiling '%s' after inflating to UTF-16)\n", filename);
1159 FileContents buffer(cx);
1160 if (!ReadCompleteFile(cx, file, buffer)) {
1161 return false;
1164 size_t length = buffer.length();
1165 auto chars = UniqueTwoByteChars(
1166 UTF8CharsToNewTwoByteCharsZ(
1168 JS::UTF8Chars(reinterpret_cast<const char*>(buffer.begin()),
1169 buffer.length()),
1170 &length, js::MallocArena)
1171 .get());
1172 if (!chars) {
1173 return false;
1176 JS::SourceText<char16_t> source;
1177 if (!source.init(cx, std::move(chars), length)) {
1178 return false;
1181 script = JS::Compile(cx, options, source);
1184 if (!script) {
1185 return false;
1189 if (!RegisterScriptPathWithModuleLoader(cx, script, filename)) {
1190 return false;
1193 #ifdef DEBUG
1194 if (dumpEntrainedVariables) {
1195 AnalyzeEntrainedVariables(cx, script);
1197 #endif
1198 if (!compileOnly) {
1199 if (!JS_ExecuteScript(cx, script)) {
1200 return false;
1202 int64_t t2 = PRMJ_Now() - t1;
1203 if (printTiming) {
1204 printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC);
1207 return true;
1210 [[nodiscard]] static bool RunModule(JSContext* cx, const char* filename,
1211 bool compileOnly) {
1212 ShellContext* sc = GetShellContext(cx);
1214 RootedString path(cx, NewStringCopyUTF8(cx, filename));
1215 if (!path) {
1216 return false;
1219 path = ResolvePath(cx, path, RootRelative);
1220 if (!path) {
1221 return false;
1224 return sc->moduleLoader->loadRootModule(cx, path);
1227 static void ShellCleanupFinalizationRegistryCallback(JSFunction* doCleanup,
1228 JSObject* incumbentGlobal,
1229 void* data) {
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) {
1253 callback = f;
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(),
1262 &unused);
1265 ranTasks = true;
1267 if (sc->quitting) {
1268 break;
1272 return ranTasks;
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");
1280 return false;
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);
1291 if (sc->quitting) {
1292 return;
1295 while (true) {
1296 // Run microtasks.
1297 js::RunJobs(cx);
1298 if (sc->quitting) {
1299 return;
1302 // Run tasks (only finalization registry clean tasks are possible).
1303 bool ranTasks = MaybeRunFinalizationRegistryCleanupTasks(cx);
1304 if (!ranTasks) {
1305 break;
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");
1316 return false;
1319 RunShellJobs(cx);
1321 if (GetShellContext(cx)->quitting) {
1322 return false;
1325 args.rval().setUndefined();
1326 return true;
1329 static bool GlobalOfFirstJobInQueue(JSContext* cx, unsigned argc, Value* vp) {
1330 CallArgs args = CallArgsFromVp(argc, vp);
1332 RootedObject job(cx, cx->internalJobQueue->maybeFront());
1333 if (!job) {
1334 JS_ReportErrorASCII(cx, "Job queue is empty");
1335 return false;
1338 RootedObject global(cx, &job->nonCCWGlobal());
1339 if (!cx->compartment()->wrap(cx, &global)) {
1340 return false;
1343 args.rval().setObject(*global);
1344 return true;
1347 static bool TrackUnhandledRejections(JSContext* cx, JS::HandleObject promise,
1348 JS::PromiseRejectionHandlingState state) {
1349 ShellContext* sc = GetShellContext(cx);
1350 if (!sc->trackUnhandledRejections) {
1351 return true;
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.");
1363 return false;
1365 #endif
1367 if (!sc->unhandledRejectedPromises) {
1368 sc->unhandledRejectedPromises = SetObject::create(cx);
1369 if (!sc->unhandledRejectedPromises) {
1370 return false;
1374 RootedValue promiseVal(cx, ObjectValue(*promise));
1376 AutoRealm ar(cx, sc->unhandledRejectedPromises);
1377 if (!cx->compartment()->wrap(cx, &promiseVal)) {
1378 return false;
1381 switch (state) {
1382 case JS::PromiseRejectionHandlingState::Unhandled:
1383 if (!SetObject::add(cx, sc->unhandledRejectedPromises, promiseVal)) {
1384 return false;
1386 break;
1387 case JS::PromiseRejectionHandlingState::Handled:
1388 bool deleted = false;
1389 if (!SetObject::delete_(cx, sc->unhandledRejectedPromises, promiseVal,
1390 &deleted)) {
1391 return false;
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.
1395 break;
1398 return true;
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)) {
1407 return;
1410 RootedValue callback(cx,
1411 GetShellContext(cx)->promiseRejectionTrackerCallback);
1412 if (callback.isNull()) {
1413 return;
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])) {
1423 return;
1426 RootedValue rval(cx);
1427 (void)Call(cx, callback, UndefinedHandleValue, args, &rval);
1430 static bool SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc,
1431 Value* vp) {
1432 CallArgs args = CallArgsFromVp(argc, vp);
1434 if (!IsFunctionObject(args.get(0))) {
1435 JS_ReportErrorASCII(
1437 "setPromiseRejectionTrackerCallback expects a function as its sole "
1438 "argument");
1439 return false;
1442 GetShellContext(cx)->promiseRejectionTrackerCallback = args[0];
1444 args.rval().setUndefined();
1445 return true;
1448 // clang-format off
1449 static const char* telemetryNames[static_cast<int>(JSMetric::Count)] = {
1450 #define LIT(NAME, _) #NAME,
1451 FOR_EACH_JS_METRIC(LIT)
1452 #undef LIT
1454 // clang-format on
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>;
1462 public:
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)) {
1473 return;
1477 static void WriteTelemetryDataToDisk(const char* dir) {
1478 const int pathLen = 260;
1479 char fileName[pathLen];
1480 Fprinter output;
1481 auto initOutput = [&](const char* name) -> bool {
1482 if (SprintfLiteral(fileName, "%s%s.csv", dir, name) >= pathLen) {
1483 return false;
1485 FILE* file = fopen(fileName, "a");
1486 if (!file) {
1487 return false;
1489 output.init(file);
1490 return true;
1493 for (size_t id = 0; id < size_t(JSMetric::Count); id++) {
1494 auto clear = MakeScopeExit([&] { telemetryResults[id].clearAndFree(); });
1495 if (!initOutput(telemetryNames[id])) {
1496 continue;
1498 for (uint32_t data : telemetryResults[id]) {
1499 output.printf("%u\n", data);
1501 output.finish();
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);
1515 bool isExplicit;
1517 RootedValue v(cx);
1519 if (!JS_GetProperty(cx, options, "stack", &v)) {
1520 return false;
1522 if (!v.isObject() || !v.toObject().is<SavedFrame>()) {
1523 JS_ReportErrorASCII(cx,
1524 "The 'stack' property must be a SavedFrame object.");
1525 return false;
1527 stack = &v.toObject().as<SavedFrame>();
1529 if (!JS_GetProperty(cx, options, "cause", &v)) {
1530 return false;
1532 RootedString causeString(cx, ToString(cx, v));
1533 if (!causeString) {
1534 return false;
1537 UniqueChars cause = JS_EncodeStringToUTF8(cx, causeString);
1538 if (!cause) {
1539 MOZ_ASSERT(cx->isExceptionPending());
1540 return false;
1543 if (!JS_GetProperty(cx, options, "explicit", &v)) {
1544 return false;
1546 isExplicit = v.isUndefined() ? true : ToBoolean(v);
1548 auto kind =
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(),
1554 args.rval());
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.");
1562 return false;
1565 if (!args[0].isObject() || !IsCallable(args[0])) {
1566 JS_ReportErrorASCII(
1567 cx, "bindToAsyncStack's first argument should be a function.");
1568 return false;
1571 if (!args[1].isObject()) {
1572 JS_ReportErrorASCII(
1573 cx, "bindToAsyncStack's second argument should be an object.");
1574 return false;
1577 RootedFunction bound(cx, NewFunctionWithReserved(cx, BoundToAsyncStack, 0, 0,
1578 "bindToAsyncStack thunk"));
1579 if (!bound) {
1580 return false;
1582 SetFunctionNativeReserved(bound, 0, args[0]);
1583 SetFunctionNativeReserved(bound, 1, args[1]);
1585 args.rval().setObject(*bound);
1586 return true;
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");
1594 return false;
1596 JS::RootedObject intl(cx, &args[0].toObject());
1598 static const JSFunctionSpec funcs[] = {
1599 JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0),
1600 JS_FS_END};
1602 if (!JS_DefineFunctions(cx, intl, funcs)) {
1603 return false;
1606 if (!JS::AddMozDateTimeFormatConstructor(cx, intl)) {
1607 return false;
1610 if (!JS::AddMozDisplayNamesConstructor(cx, intl)) {
1611 return false;
1614 args.rval().setUndefined();
1615 return true;
1617 #endif // JS_HAS_INTL_API
1619 [[nodiscard]] static bool EvalUtf8AndPrint(JSContext* cx, const char* bytes,
1620 size_t length, int lineno,
1621 bool compileOnly) {
1622 // Eval.
1623 JS::CompileOptions options(cx);
1624 options.setIntroductionType("js shell interactive")
1625 .setIsRunOnce(true)
1626 .setFileAndLine("typein", lineno)
1627 .setEagerDelazificationStrategy(defaultDelazificationMode);
1629 JS::SourceText<Utf8Unit> srcBuf;
1630 if (!srcBuf.init(cx, bytes, length, JS::SourceOwnership::Borrowed)) {
1631 return false;
1634 RootedScript script(cx, JS::Compile(cx, options, srcBuf));
1635 if (!script) {
1636 return false;
1638 if (compileOnly) {
1639 return true;
1641 RootedValue result(cx);
1642 if (!JS_ExecuteScript(cx, script, &result)) {
1643 return false;
1646 if (!result.isUndefined() && gOutFile->isOpen()) {
1647 // Print.
1648 RootedString str(cx, JS_ValueToSource(cx, result));
1649 if (!str) {
1650 return false;
1653 UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
1654 if (!utf8chars) {
1655 return false;
1657 fprintf(gOutFile->fp, "%s\n", utf8chars.get());
1659 return true;
1662 [[nodiscard]] static bool ReadEvalPrintLoop(JSContext* cx, FILE* in,
1663 bool compileOnly) {
1664 ShellContext* sc = GetShellContext(cx);
1665 int lineno = 1;
1666 bool hitEOF = false;
1668 do {
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);
1679 do {
1680 ScheduleWatchdog(cx, -1);
1681 sc->serviceInterrupt = false;
1682 errno = 0;
1684 mozilla::UniqueFreePtr<char[]> line =
1685 GetLine(in, startline == lineno ? "js> " : "");
1686 if (!line) {
1687 if (errno) {
1688 if (UniqueChars error = SystemErrorMessage(cx, errno)) {
1689 JS_ReportErrorUTF8(cx, "%s", error.get());
1691 return false;
1693 hitEOF = true;
1694 break;
1697 if (!buffer.append(line.get(), strlen(line.get())) ||
1698 !buffer.append('\n')) {
1699 return false;
1702 lineno++;
1703 if (!ScheduleWatchdog(cx, sc->timeoutInterval)) {
1704 hitEOF = true;
1705 break;
1707 } while (!JS_Utf8BufferIsCompilableUnit(cx, cx->global(), buffer.begin(),
1708 buffer.length()));
1710 if (hitEOF && buffer.empty()) {
1711 break;
1715 // Report exceptions but keep going.
1716 AutoReportException are(cx);
1717 (void)EvalUtf8AndPrint(cx, buffer.begin(), buffer.length(), startline,
1718 compileOnly);
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()) {
1727 fputs(
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",
1732 stderr);
1735 RunShellJobs(cx);
1736 } while (!hitEOF && !sc->quitting);
1738 if (gOutFile->isOpen()) {
1739 fprintf(gOutFile->fp, "\n");
1742 return true;
1745 enum FileKind {
1746 PreludeScript, // UTF-8 script, fully-parsed, to avoid conflicting
1747 // configurations.
1748 FileScript, // UTF-8, directly parsed as such
1749 FileScriptUtf16, // FileScript, but inflate to UTF-16 before parsing
1750 FileModule,
1753 [[nodiscard]] static bool Process(JSContext* cx, const char* filename,
1754 bool forceTTY, FileKind kind) {
1755 FILE* file;
1756 if (forceTTY || !filename || strcmp(filename, "-") == 0) {
1757 file = stdin;
1758 } else {
1759 file = OpenFile(cx, filename, "rb");
1760 if (!file) {
1761 return false;
1764 AutoCloseFile autoClose(file);
1766 bool fullParse = false;
1767 if (!forceTTY && !isatty(fileno(file))) {
1768 // It's not interactive - just execute it.
1769 switch (kind) {
1770 case PreludeScript:
1771 fullParse = true;
1772 if (!RunFile(cx, filename, file, CompileUtf8::DontInflate, compileOnly,
1773 fullParse)) {
1774 return false;
1776 break;
1777 case FileScript:
1778 if (!RunFile(cx, filename, file, CompileUtf8::DontInflate, compileOnly,
1779 fullParse)) {
1780 return false;
1782 break;
1783 case FileScriptUtf16:
1784 if (!RunFile(cx, filename, file, CompileUtf8::InflateToUtf16,
1785 compileOnly, fullParse)) {
1786 return false;
1788 break;
1789 case FileModule:
1790 if (!RunModule(cx, filename, compileOnly)) {
1791 return false;
1793 break;
1794 default:
1795 MOZ_CRASH("Impossible FileKind!");
1797 } else {
1798 // It's an interactive filehandle; drop into read-eval-print loop.
1799 MOZ_ASSERT(kind == FileScript);
1800 if (!ReadEvalPrintLoop(cx, file, compileOnly)) {
1801 return false;
1804 #ifdef FUZZING_JS_FUZZILLI
1805 fprintf(stderr, "executionHash is 0x%x with %d inputs\n", cx->executionHash,
1806 cx->executionHashInputs);
1807 #endif
1808 return true;
1811 #ifdef XP_WIN
1812 # define GET_FD_FROM_FILE(a) int(_get_osfhandle(fileno(a)))
1813 #else
1814 # define GET_FD_FROM_FILE(a) fileno(a)
1815 #endif
1817 static void freeExternalCallback(void* contents, void* userData) {
1818 MOZ_ASSERT(!userData);
1819 js_free(contents);
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");
1829 return false;
1832 int32_t bytes = 0;
1833 if (!ToInt32(cx, args[0], &bytes)) {
1834 return false;
1837 if (bytes < 0) {
1838 JS_ReportErrorASCII(cx, "Size must be non-negative");
1839 return false;
1842 void* buffer = js_calloc(bytes);
1843 if (!buffer) {
1844 JS_ReportOutOfMemory(cx);
1845 return false;
1848 UniquePtr<void, JS::BufferContentsDeleter> ptr{buffer,
1849 {&freeExternalCallback}};
1850 RootedObject arrayBuffer(
1851 cx, JS::NewExternalArrayBuffer(cx, bytes, std::move(ptr)));
1852 if (!arrayBuffer) {
1853 return false;
1856 args.rval().setObject(*arrayBuffer);
1857 return true;
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");
1868 return false;
1871 RootedString rawFilenameStr(cx, JS::ToString(cx, args[0]));
1872 if (!rawFilenameStr) {
1873 return false;
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));
1881 if (!filenameStr) {
1882 return false;
1884 UniqueChars filename = JS_EncodeStringToUTF8(cx, filenameStr);
1885 if (!filename) {
1886 return false;
1889 uint32_t offset = 0;
1890 if (args.length() >= 2) {
1891 if (!JS::ToUint32(cx, args[1], &offset)) {
1892 return false;
1896 bool sizeGiven = false;
1897 uint32_t size;
1898 if (args.length() >= 3) {
1899 if (!JS::ToUint32(cx, args[2], &size)) {
1900 return false;
1902 sizeGiven = true;
1903 if (size == 0) {
1904 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1905 JSMSG_BAD_ARRAY_LENGTH);
1906 return false;
1910 FILE* file = OpenFile(cx, filename.get(), "rb");
1911 if (!file) {
1912 return false;
1914 AutoCloseFile autoClose(file);
1916 struct stat st;
1917 if (fstat(fileno(file), &st) < 0) {
1918 JS_ReportErrorASCII(cx, "Unable to stat file");
1919 return false;
1922 if ((st.st_mode & S_IFMT) != S_IFREG) {
1923 JS_ReportErrorASCII(cx, "Path is not a regular file");
1924 return false;
1927 if (!sizeGiven) {
1928 if (off_t(offset) >= st.st_size) {
1929 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1930 JSMSG_OFFSET_LARGER_THAN_FILESIZE);
1931 return false;
1933 size = st.st_size - offset;
1936 void* contents =
1937 JS::CreateMappedArrayBufferContents(GET_FD_FROM_FILE(file), offset, size);
1938 if (!contents) {
1939 JS_ReportErrorASCII(cx,
1940 "failed to allocate mapped array buffer contents "
1941 "(possibly due to bad alignment)");
1942 return false;
1945 RootedObject obj(cx,
1946 JS::NewMappedArrayBufferWithContents(cx, size, contents));
1947 if (!obj) {
1948 return false;
1951 args.rval().setObject(*obj);
1952 return true;
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);
1966 public:
1967 static const JSClassOps classOps_;
1968 static const JSClass class_;
1970 [[nodiscard]] static UserBufferObject* create(JSContext* cx,
1971 size_t byteLength);
1973 void* buffer() const {
1974 auto& buffer = getReservedSlot(BUFFER_SLOT);
1975 if (buffer.isUndefined()) {
1976 return nullptr;
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
1991 nullptr, // resolve
1992 nullptr, // mayResolve
1993 UserBufferObject::finalize, // finalize
1994 nullptr, // call
1995 nullptr, // construct
1996 nullptr, // trace
1999 const JSClass UserBufferObject::class_ = {
2000 "UserBufferObject",
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);
2008 if (!buffer) {
2009 JS_ReportOutOfMemory(cx);
2010 return nullptr;
2012 UniquePtr<void, JS::FreePolicy> ptr(buffer);
2014 auto* userBuffer = NewObjectWithGivenProto<UserBufferObject>(cx, nullptr);
2015 if (!userBuffer) {
2016 return nullptr;
2019 InitReservedSlot(userBuffer, BUFFER_SLOT, ptr.release(), byteLength,
2020 BufferMemoryUse);
2021 userBuffer->initReservedSlot(BYTE_LENGTH_SLOT, PrivateValue(byteLength));
2023 return userBuffer;
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");
2040 return false;
2043 int32_t bytes = 0;
2044 if (!ToInt32(cx, args[0], &bytes)) {
2045 return false;
2047 if (bytes < 0) {
2048 JS_ReportErrorASCII(cx, "Size must be non-negative");
2049 return false;
2052 Rooted<UserBufferObject*> userBuffer(cx, UserBufferObject::create(cx, bytes));
2053 if (!userBuffer) {
2054 return false;
2057 Rooted<JSObject*> arrayBuffer(
2058 cx, JS::NewArrayBufferWithUserOwnedContents(cx, userBuffer->byteLength(),
2059 userBuffer->buffer()));
2060 if (!arrayBuffer) {
2061 return false;
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());
2071 if (!privateName) {
2072 return false;
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)) {
2078 return false;
2081 args.rval().setObject(*arrayBuffer);
2082 return true;
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");
2093 return false;
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");
2104 return false;
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");
2121 return false;
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();
2134 return true;
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]));
2143 if (!str) {
2144 return false;
2147 Rooted<JSLinearString*> opt(cx, str->ensureLinear(cx));
2148 if (!opt) {
2149 return false;
2152 if (StringEqualsLiteral(opt, "throw_on_asmjs_validation_failure")) {
2153 JS::ContextOptionsRef(cx).toggleThrowOnAsmJSValidationFailure();
2154 } else {
2155 UniqueChars optChars = QuoteString(cx, opt, '"');
2156 if (!optChars) {
2157 return false;
2160 JS_ReportErrorASCII(cx,
2161 "unknown option name %s."
2162 " The valid name is "
2163 "throw_on_asmjs_validation_failure.",
2164 optChars.get());
2165 return false;
2169 UniqueChars names = DuplicateString("");
2170 bool found = false;
2171 if (names && oldContextOptions.throwOnAsmJSValidationFailure()) {
2172 names = JS_sprintf_append(std::move(names), "%s%s", found ? "," : "",
2173 "throw_on_asmjs_validation_failure");
2174 found = true;
2176 if (!names) {
2177 JS_ReportOutOfMemory(cx);
2178 return false;
2181 JSString* str = JS_NewStringCopyZ(cx, names.get());
2182 if (!str) {
2183 return false;
2185 args.rval().setString(str);
2186 return true;
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]);
2196 if (!str) {
2197 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
2198 JSSMSG_INVALID_ARGS, "load");
2199 return false;
2202 str = ResolvePath(cx, str, scriptRelative ? ScriptRelative : RootRelative);
2203 if (!str) {
2204 JS_ReportErrorASCII(cx, "unable to resolve path");
2205 return false;
2208 UniqueChars filename = JS_EncodeStringToUTF8(cx, str);
2209 if (!filename) {
2210 return false;
2213 errno = 0;
2215 CompileOptions opts(cx);
2216 opts.setIntroductionType("js shell load")
2217 .setIsRunOnce(true)
2218 .setNoScriptRval(true)
2219 .setEagerDelazificationStrategy(defaultDelazificationMode);
2221 RootedValue unused(cx);
2222 if (!(compileOnly
2223 ? JS::CompileUtf8Path(cx, opts, filename.get()) != nullptr
2224 : JS::EvaluateUtf8Path(cx, opts, filename.get(), &unused))) {
2225 return false;
2229 args.rval().setUndefined();
2230 return true;
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,
2238 Value* vp) {
2239 return LoadScript(cx, argc, vp, true);
2242 static void my_LargeAllocFailCallback() {
2243 JSContext* cx = TlsContext.get();
2244 if (!cx) {
2245 return;
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 {
2265 IsRunOnce,
2266 NoScriptRval,
2267 Global,
2268 NonSyntactic,
2269 SourceIsLazy,
2270 ForceFullParse,
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.
2302 return a == b;
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");
2314 return false;
2317 RootedObject obj(cx, JS_NewObject(cx, &CacheEntry_class));
2318 if (!obj) {
2319 return false;
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);
2331 return true;
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.");
2344 return nullptr;
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");
2359 return false;
2361 return true;
2364 static uint8_t* CacheEntry_getBytecode(JSContext* cx, HandleObject cache,
2365 size_t* length) {
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.");
2372 return nullptr;
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));
2390 if (!arrayBuffer) {
2391 return false;
2394 JS::SetReservedSlot(cache, CacheEntry_BYTECODE, ObjectValue(*arrayBuffer));
2395 JS::SetReservedSlot(cache, CacheEntry_OPTIONS,
2396 Int32Value(cacheOptions.serialize()));
2397 return true;
2400 static bool ConvertTranscodeResultToJSException(JSContext* cx,
2401 JS::TranscodeResult rv) {
2402 switch (rv) {
2403 case JS::TranscodeResult::Ok:
2404 return true;
2406 default:
2407 [[fallthrough]];
2408 case JS::TranscodeResult::Failure:
2409 MOZ_ASSERT(!cx->isExceptionPending());
2410 JS_ReportErrorASCII(cx, "generic warning");
2411 return false;
2412 case JS::TranscodeResult::Failure_BadBuildId:
2413 MOZ_ASSERT(!cx->isExceptionPending());
2414 JS_ReportErrorASCII(cx, "the build-id does not match");
2415 return false;
2416 case JS::TranscodeResult::Failure_AsmJSNotSupported:
2417 MOZ_ASSERT(!cx->isExceptionPending());
2418 JS_ReportErrorASCII(cx, "Asm.js is not supported by XDR");
2419 return false;
2420 case JS::TranscodeResult::Failure_BadDecode:
2421 MOZ_ASSERT(!cx->isExceptionPending());
2422 JS_ReportErrorASCII(cx, "XDR data corruption");
2423 return false;
2425 case JS::TranscodeResult::Throw:
2426 MOZ_ASSERT(cx->isExceptionPending());
2427 return false;
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);
2441 sc->exitCode = 0;
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,
2452 "evaluate");
2453 return false;
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);
2464 if (!code) {
2465 return false;
2469 if (!code || (args.length() == 2 && args[1].isPrimitive())) {
2470 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
2471 JSSMSG_INVALID_ARGS, "evaluate");
2472 return false;
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");
2479 return false;
2482 opts = &args[1].toObject();
2485 RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
2486 MOZ_ASSERT(global);
2488 // Check "global" property before everything to use the given global's
2489 // option as the default value.
2490 Maybe<CompileOptions> maybeOptions;
2491 if (opts) {
2492 RootedValue v(cx);
2493 if (!JS_GetProperty(cx, opts, "global", &v)) {
2494 return false;
2496 if (!v.isUndefined()) {
2497 if (v.isObject()) {
2498 global = js::CheckedUnwrapDynamic(&v.toObject(), cx,
2499 /* stopAtWindowProxy = */ false);
2500 if (!global) {
2501 return 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");
2508 return false;
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);
2540 if (opts) {
2541 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
2542 return false;
2544 if (!ParseDebugMetadata(cx, opts, &privateValue, &elementAttributeName)) {
2545 return false;
2547 if (!ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) {
2548 return false;
2551 RootedValue v(cx);
2552 if (!JS_GetProperty(cx, opts, "catchTermination", &v)) {
2553 return false;
2555 if (!v.isUndefined()) {
2556 catchTermination = ToBoolean(v);
2559 if (!JS_GetProperty(cx, opts, "loadBytecode", &v)) {
2560 return false;
2562 if (!v.isUndefined()) {
2563 loadBytecode = ToBoolean(v);
2566 if (!JS_GetProperty(cx, opts, "saveIncrementalBytecode", &v)) {
2567 return false;
2569 if (!v.isUndefined()) {
2570 saveIncrementalBytecode = ToBoolean(v);
2573 if (!JS_GetProperty(cx, opts, "execute", &v)) {
2574 return false;
2576 if (!v.isUndefined()) {
2577 execute = ToBoolean(v);
2580 if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v)) {
2581 return false;
2583 if (!v.isUndefined()) {
2584 assertEqBytecode = ToBoolean(v);
2587 if (!JS_GetProperty(cx, opts, "envChainObject", &v)) {
2588 return false;
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");
2595 return false;
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");
2604 return false;
2607 if (!envChain.append(obj)) {
2608 return false;
2612 // We cannot load or save the bytecode if we have no object where the
2613 // bytecode cache is stored.
2614 if (loadBytecode || saveIncrementalBytecode) {
2615 if (!cacheEntry) {
2616 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
2617 JSSMSG_INVALID_ARGS, "evaluate");
2618 return false;
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])) {
2628 return false;
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;
2647 if (loadBytecode) {
2648 size_t loadLength = 0;
2649 uint8_t* loadData = nullptr;
2651 if (!CacheEntry_compatible(cx, cacheEntry, cacheOptions)) {
2652 return false;
2655 loadData = CacheEntry_getBytecode(cx, cacheEntry, &loadLength);
2656 if (!loadData) {
2657 return false;
2659 if (!loadBuffer.append(loadData, loadLength)) {
2660 JS_ReportOutOfMemory(cx);
2661 return false;
2666 JSAutoRealm ar(cx, global);
2667 RefPtr<JS::Stencil> stencil;
2669 if (loadBytecode) {
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");
2677 return false;
2680 if (!ConvertTranscodeResultToJSException(cx, rv)) {
2681 return false;
2683 } else {
2684 AutoStableStringChars linearChars(cx);
2685 if (!linearChars.initTwoByte(cx, code)) {
2686 return false;
2689 JS::SourceText<char16_t> srcBuf;
2690 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
2691 return false;
2694 stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
2695 if (!stencil) {
2696 return false;
2700 if (!js::ValidateLazinessOfStencilAndGlobal(cx, *stencil)) {
2701 return false;
2704 JS::InstantiateOptions instantiateOptions(options);
2705 RootedScript script(
2706 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil));
2707 if (!script) {
2708 return false;
2711 AutoReportFrontendContext fc(cx);
2712 if (!SetSourceOptions(cx, &fc, script->scriptSource(), displayURL,
2713 sourceMapURL)) {
2714 return false;
2717 if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue,
2718 elementAttributeName, nullptr, nullptr)) {
2719 return false;
2722 if (saveIncrementalBytecode) {
2723 if (!JS::StartIncrementalEncoding(cx, std::move(stencil))) {
2724 return false;
2728 if (execute) {
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);
2734 if (sc->quitting) {
2735 UnsetQuitting(cx);
2738 JSAutoRealm ar1(cx, callerGlobal);
2739 JSString* str = JS_NewStringCopyZ(cx, "terminated");
2740 if (!str) {
2741 return false;
2743 args.rval().setString(str);
2744 return true;
2746 return false;
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)) {
2754 return false;
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,
2771 saveLengthStr);
2772 return false;
2775 if (!ArrayEqual(loadBuffer.begin(), saveBuffer.begin(),
2776 loadBuffer.length())) {
2777 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
2778 JSSMSG_CACHE_EQ_CONTENT_FAILED);
2779 return false;
2783 size_t saveLength = saveBuffer.length();
2784 if (saveLength >= INT32_MAX) {
2785 JS_ReportErrorASCII(cx, "Cannot save large cache entry content");
2786 return false;
2788 uint8_t* saveData = saveBuffer.extractOrCopyRawBuffer();
2789 if (!CacheEntry_setBytecode(cx, cacheEntry, cacheOptions, saveData,
2790 saveLength)) {
2791 js_free(saveData);
2792 return false;
2796 return JS_WrapValue(cx, args.rval());
2799 JSString* js::shell::FileAsString(JSContext* cx, JS::HandleString pathnameStr) {
2800 UniqueChars pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
2801 if (!pathname) {
2802 return nullptr;
2805 FILE* file = OpenFile(cx, pathname.get(), "rb");
2806 if (!file) {
2807 return nullptr;
2810 AutoCloseFile autoClose(file);
2812 struct stat st;
2813 if (fstat(fileno(file), &st) != 0) {
2814 JS_ReportErrorUTF8(cx, "can't stat %s", pathname.get());
2815 return nullptr;
2818 if ((st.st_mode & S_IFMT) != S_IFREG) {
2819 JS_ReportErrorUTF8(cx, "can't read non-regular file %s", pathname.get());
2820 return nullptr;
2823 size_t len;
2824 if (!FileSize(cx, pathname.get(), file, &len)) {
2825 return nullptr;
2828 UniqueChars buf(js_pod_malloc<char>(len + 1));
2829 if (!buf) {
2830 JS_ReportErrorUTF8(cx, "out of memory reading %s", pathname.get());
2831 return nullptr;
2834 if (!ReadFile(cx, pathname.get(), file, buf.get(), len)) {
2835 return nullptr;
2838 UniqueTwoByteChars ucbuf(
2839 JS::LossyUTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf.get(), len),
2840 &len, js::MallocArena)
2841 .get());
2842 if (!ucbuf) {
2843 JS_ReportErrorUTF8(cx, "Invalid UTF-8 in file '%s'", pathname.get());
2844 return nullptr;
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");
2860 return false;
2863 RootedString str(cx, JS::ToString(cx, args[0]));
2864 if (!str) {
2865 return false;
2867 args[0].setString(str);
2869 str = FileAsString(cx, str);
2870 if (!str) {
2871 return false;
2874 AutoStableStringChars linearChars(cx);
2875 if (!linearChars.initTwoByte(cx, str)) {
2876 return false;
2879 JS::SourceText<char16_t> srcBuf;
2880 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
2881 return false;
2884 RootedScript script(cx);
2885 int64_t startClock = PRMJ_Now();
2887 UniqueChars filename = JS_EncodeStringToUTF8(cx, str);
2888 if (!filename) {
2889 return false;
2892 JS::CompileOptions options(cx);
2893 options.setIntroductionType("js shell run")
2894 .setFileAndLine(filename.get(), 1)
2895 .setIsRunOnce(true)
2896 .setNoScriptRval(true)
2897 .setEagerDelazificationStrategy(defaultDelazificationMode);
2899 script = JS::Compile(cx, options, srcBuf);
2900 if (!script) {
2901 return false;
2905 if (!JS_ExecuteScript(cx, script)) {
2906 return false;
2909 int64_t endClock = PRMJ_Now();
2911 args.rval().setDouble((endClock - startClock) / double(PRMJ_USEC_PER_MSEC));
2912 return true;
2915 static int js_fgets(char* buf, int size, FILE* file) {
2916 int n, i, c;
2917 bool crflag;
2919 n = size - 1;
2920 if (n < 0) {
2921 return -1;
2924 // Use the fastest available getc.
2925 auto fast_getc =
2926 #if defined(HAVE_GETC_UNLOCKED)
2927 getc_unlocked
2928 #elif defined(HAVE__GETC_NOLOCK)
2929 _getc_nolock
2930 #else
2931 getc
2932 #endif
2935 crflag = false;
2936 for (i = 0; i < n && (c = fast_getc(file)) != EOF; i++) {
2937 buf[i] = c;
2938 if (c == '\n') { // any \n ends a line
2939 i++; // keep the \n; we know there is room for \0
2940 break;
2942 if (crflag) { // \r not followed by \n ends line at the \r
2943 ungetc(c, file);
2944 break; // and overwrite c in buf with \0
2946 crflag = (c == '\r');
2949 buf[i] = '\0';
2950 return i;
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;
2961 FILE* from = stdin;
2962 size_t buflength = 0;
2963 size_t bufsize = BUFSIZE;
2964 char* buf = (char*)JS_malloc(cx, bufsize);
2965 if (!buf) {
2966 return false;
2969 bool sawNewline = false;
2970 size_t gotlength;
2971 while ((gotlength = js_fgets(buf + buflength, bufsize - buflength, from)) >
2972 0) {
2973 buflength += gotlength;
2975 /* Are we done? */
2976 if (buf[buflength - 1] == '\n') {
2977 buf[buflength - 1] = '\0';
2978 sawNewline = true;
2979 break;
2980 } else if (buflength < bufsize - 1) {
2981 break;
2984 /* Else, grow our buffer for another pass. */
2985 char* tmp;
2986 bufsize *= 2;
2987 if (bufsize > buflength) {
2988 tmp = static_cast<char*>(JS_realloc(cx, buf, bufsize / 2, bufsize));
2989 } else {
2990 JS_ReportOutOfMemory(cx);
2991 tmp = nullptr;
2994 if (!tmp) {
2995 JS_free(cx, buf);
2996 return false;
2999 buf = tmp;
3002 /* Treat the empty string specially. */
3003 if (buflength == 0) {
3004 args.rval().set(feof(from) ? NullValue() : JS_GetEmptyStringValue(cx));
3005 JS_free(cx, buf);
3006 return true;
3009 /* Shrink the buffer to the real size. */
3010 char* tmp = static_cast<char*>(JS_realloc(cx, buf, bufsize, buflength));
3011 if (!tmp) {
3012 JS_free(cx, buf);
3013 return false;
3016 buf = tmp;
3019 * Turn buf into a JSString. Note that buflength includes the trailing null
3020 * character.
3022 JSString* str =
3023 JS_NewStringCopyN(cx, buf, sawNewline ? buflength - 1 : buflength);
3024 JS_free(cx, buf);
3025 if (!str) {
3026 return false;
3029 args.rval().setString(str);
3030 return true;
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.");
3046 return false;
3049 char* currentBuf = sc->readLineBuf.get() + sc->readLineBufPos;
3050 size_t buflen = strlen(currentBuf);
3052 if (!buflen) {
3053 args.rval().setNull();
3054 return true;
3057 size_t len = 0;
3058 while (len < buflen) {
3059 if (currentBuf[len] == '\n') {
3060 break;
3062 len++;
3065 JSString* str = JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(currentBuf, len));
3066 if (!str) {
3067 return false;
3070 if (currentBuf[len] == '\0') {
3071 sc->readLineBufPos += len;
3072 } else {
3073 sc->readLineBufPos += len + 1;
3076 args.rval().setString(str);
3077 return true;
3080 if (args.length() == 1) {
3081 sc->readLineBuf = nullptr;
3082 sc->readLineBufPos = 0;
3084 RootedString str(cx, JS::ToString(cx, args[0]));
3085 if (!str) {
3086 return false;
3088 sc->readLineBuf = JS_EncodeStringToUTF8(cx, str);
3089 if (!sc->readLineBuf) {
3090 return false;
3093 args.rval().setUndefined();
3094 return true;
3097 JS_ReportErrorASCII(cx, "Must specify at most one argument");
3098 return false;
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");
3107 return false;
3110 RootedString str(cx, JS::ToString(cx, args[0]));
3111 if (!str) {
3112 return false;
3114 UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
3115 if (!bytes) {
3116 return false;
3118 fputs(bytes.get(), gOutFile->fp);
3119 fflush(gOutFile->fp);
3122 args.rval().setUndefined();
3123 return true;
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);
3130 return true;
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);
3137 return true;
3140 static bool PrintInternal(JSContext* cx, const CallArgs& args, RCFile* file) {
3141 if (!file->isOpen()) {
3142 JS_ReportErrorASCII(cx, "output file is closed");
3143 return false;
3146 for (unsigned i = 0; i < args.length(); i++) {
3147 RootedString str(cx, JS::ToString(cx, args[i]));
3148 if (!str) {
3149 return false;
3151 UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
3152 if (!bytes) {
3153 return false;
3155 fprintf(file->fp, "%s%s", i ? " " : "", bytes.get());
3158 fputc('\n', file->fp);
3159 fflush(file->fp);
3161 args.rval().setUndefined();
3162 return true;
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
3171 // to read.
3172 args.rval().setUndefined();
3173 return true;
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);
3194 int32_t code;
3195 if (!ToInt32(cx, args.get(0), &code)) {
3196 return false;
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");
3206 return false;
3209 SetQuitting(cx, code);
3210 return false;
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");
3218 return false;
3221 if (!cx->runtime()->gc.stats().startTimingMutator()) {
3222 JS_ReportErrorASCII(
3223 cx, "StartTimingMutator should only be called from outside of GC");
3224 return false;
3227 args.rval().setUndefined();
3228 return true;
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");
3236 return false;
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");
3243 return false;
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();
3253 return true;
3256 static const char* ToSource(JSContext* cx, HandleValue vp, UniqueChars* bytes) {
3257 RootedString str(cx, JS_ValueToSource(cx, vp));
3258 if (str) {
3259 *bytes = JS_EncodeStringToUTF8(cx, str);
3260 if (*bytes) {
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,
3275 "assertEq");
3276 return false;
3279 bool same;
3280 if (!JS::SameValue(cx, args[0], args[1], &same)) {
3281 return false;
3283 if (!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);
3290 } else {
3291 RootedString message(cx, args[2].toString());
3292 UniqueChars bytes2 = QuoteString(cx, message);
3293 if (!bytes2) {
3294 return false;
3296 JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr,
3297 JSSMSG_ASSERT_EQ_FAILED_MSG, actual, expected,
3298 bytes2.get());
3300 return false;
3302 args.rval().setUndefined();
3303 return true;
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));
3314 *ip = 0;
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);
3320 if (!script) {
3321 return false;
3323 intarg++;
3325 if (!args.get(intarg).isUndefined()) {
3326 if (!JS::ToInt32(cx, args[intarg], ip)) {
3327 return false;
3329 if ((uint32_t)*ip >= script->length()) {
3330 JS_ReportErrorASCII(cx, "Invalid PC");
3331 return false;
3336 scriptp.set(script);
3338 return true;
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);
3347 return false;
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]);
3354 if (!script) {
3355 return false;
3357 lineArg++;
3360 uint32_t lineno;
3361 if (!ToUint32(cx, args.get(lineArg), &lineno)) {
3362 return false;
3365 jsbytecode* pc = LineNumberToPC(script, lineno);
3366 if (!pc) {
3367 return false;
3369 args.rval().setInt32(script->pcToOffset(pc));
3370 return true;
3373 static bool PCToLine(JSContext* cx, unsigned argc, Value* vp) {
3374 CallArgs args = CallArgsFromVp(argc, vp);
3375 RootedScript script(cx);
3376 int32_t i;
3377 unsigned lineno;
3379 if (!GetScriptAndPCArgs(cx, args, &script, &i)) {
3380 return false;
3382 lineno = PCToLineNumber(script, script->offsetToPC(i));
3383 if (!lineno) {
3384 return false;
3386 args.rval().setInt32(lineno);
3387 return true;
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()) {
3396 return false;
3399 for (unsigned i = 0; i < args.length(); i++) {
3400 RootedScript script(cx, TestingFunctionArgumentToScript(cx, args[i]));
3401 if (!script) {
3402 return false;
3405 if (!JSScript::dumpSrcNotes(cx, script, &sprinter)) {
3406 return false;
3410 JSString* str = sprinter.release(cx);
3411 if (!str) {
3412 return false;
3414 args.rval().setString(str);
3415 return true;
3418 namespace {
3420 struct DisassembleOptionParser {
3421 unsigned argc;
3422 Value* argv;
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);
3435 if (!linearStr) {
3436 return false;
3438 if (JS_LinearStringEqualsLiteral(linearStr, "-r")) {
3439 options.recursive = true;
3440 } else {
3441 break;
3443 argv++;
3444 argc--;
3446 return 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());
3456 if (!p.parse(cx)) {
3457 return false;
3460 if (p.argc == 0) {
3461 /* Without arguments, disassemble the current script. */
3462 RootedScript script(cx, GetTopScript(cx));
3463 if (script) {
3464 JSAutoRealm ar(cx, script);
3465 if (!JSScript::dump(cx, script, p.options, sp)) {
3466 return false;
3469 } else {
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>()
3477 .get()
3478 ->maybeScript();
3479 } else {
3480 script = TestingFunctionArgumentToScript(cx, value, fun.address());
3482 if (!script) {
3483 return false;
3486 if (!JSScript::dump(cx, script, p.options, sp)) {
3487 return false;
3492 return true;
3495 static bool DisassembleToString(JSContext* cx, unsigned argc, Value* vp) {
3496 CallArgs args = CallArgsFromVp(argc, vp);
3497 JSSprinter sprinter(cx);
3498 if (!sprinter.init()) {
3499 return false;
3501 if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) {
3502 return false;
3505 JSString* str = sprinter.release(cx);
3506 if (!str) {
3507 return false;
3509 args.rval().setString(str);
3510 return true;
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");
3518 return false;
3521 Sprinter sprinter(cx);
3522 if (!sprinter.init()) {
3523 return false;
3525 if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) {
3526 return false;
3529 JS::UniqueChars str = sprinter.release();
3530 if (!str) {
3531 return false;
3533 fprintf(gOutFile->fp, "%s\n", str.get());
3534 args.rval().setUndefined();
3535 return true;
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");
3543 return false;
3546 /* Support extra options at the start, just like Disassemble. */
3547 DisassembleOptionParser p(args.length(), args.array());
3548 if (!p.parse(cx)) {
3549 return false;
3552 if (!p.argc) {
3553 args.rval().setUndefined();
3554 return true;
3557 // We should change DisassembleOptionParser to store CallArgs.
3558 Rooted<JSString*> str(
3559 cx, JS::ToString(cx, HandleValue::fromMarkedLocation(&p.argv[0])));
3560 if (!str) {
3561 return false;
3563 UniqueChars filename = JS_EncodeStringToUTF8(cx, str);
3564 if (!filename) {
3565 return false;
3567 RootedScript script(cx);
3570 CompileOptions options(cx);
3571 options.setIntroductionType("js shell disFile")
3572 .setFileAndLine(filename.get(), 1)
3573 .setIsRunOnce(true)
3574 .setNoScriptRval(true)
3575 .setEagerDelazificationStrategy(defaultDelazificationMode);
3577 script = JS::CompileUtf8Path(cx, options, filename.get());
3578 if (!script) {
3579 return false;
3583 Sprinter sprinter(cx);
3584 if (!sprinter.init()) {
3585 return false;
3587 if (JSScript::dump(cx, script, p.options, &sprinter)) {
3588 return false;
3591 JS::UniqueChars chars = sprinter.release();
3592 if (!chars) {
3593 return false;
3595 fprintf(gOutFile->fp, "%s\n", chars.get());
3597 args.rval().setUndefined();
3598 return true;
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");
3606 return false;
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]);
3617 if (!script) {
3618 return false;
3621 if (!script->filename()) {
3622 JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
3623 JSSMSG_FILE_SCRIPTS_ONLY);
3624 return false;
3627 FILE* file = OpenFile(cx, script->filename(), "rb");
3628 if (!file) {
3629 return false;
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()) {
3638 return false;
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);
3645 if (!tmp) {
3646 JS_ReportErrorUTF8(cx, "failed to read %s fully", script->filename());
3647 return false;
3651 bupline = 0;
3652 while (pc < end) {
3653 line2 = PCToLineNumber(script, pc);
3655 if (line2 < line1) {
3656 if (bupline != line2) {
3657 bupline = line2;
3658 sprinter.printf("%s %3u: BACKUP\n", sep, line2);
3660 } else {
3661 if (bupline && line1 == line2) {
3662 sprinter.printf("%s %3u: RESTORE\n", sep, line2);
3664 bupline = 0;
3665 while (line1 < line2) {
3666 if (!fgets(linebuf, lineBufLen, file)) {
3667 JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr,
3668 JSSMSG_UNEXPECTED_EOF, script->filename());
3669 return false;
3671 line1++;
3672 sprinter.printf("%s %3u: %s", sep, line1, linebuf);
3676 len =
3677 Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter);
3678 if (!len) {
3679 return false;
3682 pc += len;
3685 JS::UniqueChars str = sprinter.release();
3686 if (!str) {
3687 return false;
3689 fprintf(gOutFile->fp, "%s\n", str.get());
3692 args.rval().setUndefined();
3693 return true;
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
3708 // a health report.
3709 AutoSpewChannel channel(cx, SpewChannel::CacheIRHealthReport, script);
3710 if (!argc) {
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);
3721 } else {
3722 RootedValue value(cx, args.get(0));
3724 if (value.isObject() && value.toObject().is<ShellModuleObjectWrapper>()) {
3725 script =
3726 value.toObject().as<ShellModuleObjectWrapper>().get()->maybeScript();
3727 } else {
3728 script = TestingFunctionArgumentToScript(cx, args.get(0));
3731 if (!script) {
3732 return false;
3735 cih.healthReportForScript(cx, script, js::jit::SpewContext::Shell);
3738 args.rval().setUndefined();
3739 return true;
3741 #endif /* JS_CACHEIR_SPEW */
3743 /* Pretend we can always preserve wrappers for dummy DOM objects. */
3744 static bool DummyPreserveWrapperCallback(JSContext* cx, HandleObject obj) {
3745 return true;
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();
3755 if (argc != 1) {
3756 return true;
3758 uint32_t hash;
3759 JS::Handle<JS::Value> v = args.get(0);
3760 if (v.isInt32()) {
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);
3779 return true;
3780 } else {
3781 hash = 0;
3784 cx->executionHashInputs += 1;
3785 cx->executionHash = mozilla::RotateLeft(cx->executionHash + hash, 1);
3786 return true;
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
3794 // to perform.
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)));
3799 if (!arg) {
3800 return false;
3802 Rooted<JSLinearString*> operation(cx, StringToLinearString(cx, arg));
3803 if (!operation) {
3804 return false;
3807 if (StringEqualsAscii(operation, "FUZZILLI_CRASH")) {
3808 int type;
3809 if (!ToInt32(cx, args.get(1), &type)) {
3810 return false;
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.
3815 switch (type) {
3816 case 0:
3817 *((int*)0x41414141) = 0x1337;
3818 break;
3819 case 1:
3820 MOZ_RELEASE_ASSERT(false);
3821 break;
3822 case 2:
3823 MOZ_ASSERT(false);
3824 break;
3825 case 3:
3826 __asm__("int3");
3827 break;
3828 default:
3829 exit(1);
3831 } else if (StringEqualsAscii(operation, "FUZZILLI_PRINT")) {
3832 static FILE* fzliout = fdopen(REPRL_DWFD, "w");
3833 if (!fzliout) {
3834 fprintf(
3835 stderr,
3836 "Fuzzer output channel not available, printing to stdout instead\n");
3837 fzliout = stdout;
3840 RootedString str(cx, JS::ToString(cx, args.get(1)));
3841 if (!str) {
3842 return false;
3844 UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
3845 if (!bytes) {
3846 return false;
3848 fprintf(fzliout, "%s\n", bytes.get());
3849 fflush(fzliout);
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
3853 // behavior.
3854 args.rval().setInt32(static_cast<uint32_t>(mozilla::RandomUint64OrDie()));
3855 return true;
3858 args.rval().setUndefined();
3859 return true;
3862 static bool FuzzilliReprlGetAndRun(JSContext* cx) {
3863 size_t scriptSize = 0;
3865 unsigned action;
3866 MOZ_RELEASE_ASSERT(read(REPRL_CRFD, &action, 4) == 4);
3867 if (action == 'cexe') {
3868 MOZ_RELEASE_ASSERT(read(REPRL_CRFD, &scriptSize, 8) == 8);
3869 } else {
3870 fprintf(stderr, "Unknown action: %u\n", action);
3871 _exit(-1);
3874 CompileOptions options(cx);
3875 options.setIntroductionType("reprl")
3876 .setFileAndLine("reprl", 1)
3877 .setIsRunOnce(true)
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);
3887 if (rv <= 0) {
3888 fprintf(stderr, "Failed to load script\n");
3889 _exit(-1);
3891 remaining -= rv;
3892 ptr += rv;
3895 JS::SourceText<Utf8Unit> srcBuf;
3896 if (!srcBuf.init(cx, scriptSrc, scriptSize,
3897 JS::SourceOwnership::TakeOwnership)) {
3898 return false;
3901 RootedScript script(cx, JS::Compile(cx, options, srcBuf));
3902 if (!script) {
3903 return false;
3906 if (!JS_ExecuteScript(cx, script)) {
3907 return false;
3910 return true;
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");
3919 if (reprl_mode) {
3920 // Check in with parent
3921 char helo[] = "HELO";
3922 if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) {
3923 reprl_mode = false;
3926 if (memcmp(helo, "HELO", 4) != 0) {
3927 fprintf(stderr, "Invalid response from parent\n");
3928 _exit(-1);
3931 return reprl_mode;
3932 #else
3933 return false;
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]));
3943 if (!message) {
3944 return false;
3946 UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, message);
3947 if (!utf8chars) {
3948 return false;
3950 if (args.get(1).isObject()) {
3951 RootedValue v(cx);
3952 RootedObject opts(cx, &args[1].toObject());
3953 if (!JS_GetProperty(cx, opts, "suppress_minidump", &v)) {
3954 return false;
3956 if (v.isBoolean() && v.toBoolean()) {
3957 js::NoteIntentionalCrash();
3960 #ifndef DEBUG
3961 MOZ_ReportCrash(utf8chars.get(), __FILE__, __LINE__);
3962 #endif
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));
3971 if (!script) {
3972 return false;
3974 args.rval().setInt32(GetScriptLineExtent(script));
3975 return true;
3978 static bool ThrowError(JSContext* cx, unsigned argc, Value* vp) {
3979 JS_ReportErrorASCII(cx, "This is an error");
3980 return false;
3983 static bool CopyErrorReportToObject(JSContext* cx, JSErrorReport* report,
3984 HandleObject obj) {
3985 RootedString nameStr(cx);
3986 if (report->exnType == JSEXN_WARN) {
3987 nameStr = JS_NewStringCopyZ(cx, "Warning");
3988 if (!nameStr) {
3989 return false;
3991 } else {
3992 nameStr = GetErrorTypeName(cx, report->exnType);
3993 // GetErrorTypeName doesn't set an exception, but
3994 // can fail for InternalError or non-error objects.
3995 if (!nameStr) {
3996 nameStr = cx->runtime()->emptyString;
3999 RootedValue nameVal(cx, StringValue(nameStr));
4000 if (!DefineDataProperty(cx, obj, cx->names().name, nameVal)) {
4001 return false;
4004 RootedString messageStr(cx, report->newMessageString(cx));
4005 if (!messageStr) {
4006 return false;
4008 RootedValue messageVal(cx, StringValue(messageStr));
4009 if (!DefineDataProperty(cx, obj, cx->names().message, messageVal)) {
4010 return false;
4013 RootedValue linenoVal(cx, Int32Value(report->lineno));
4014 if (!DefineDataProperty(cx, obj, cx->names().lineNumber, linenoVal)) {
4015 return false;
4018 RootedValue columnVal(cx, Int32Value(report->column.oneOriginValue()));
4019 if (!DefineDataProperty(cx, obj, cx->names().columnNumber, columnVal)) {
4020 return false;
4023 RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
4024 if (!notesArray) {
4025 return false;
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)) {
4039 return false;
4042 MOZ_ASSERT(!report.report()->isWarning());
4044 RootedObject obj(cx, JS_NewPlainObject(cx));
4045 if (!obj) {
4046 return false;
4049 RootedString toString(cx, NewStringCopyUTF8Z(cx, report.toStringResult()));
4050 if (!toString) {
4051 return false;
4054 if (!JS_DefineProperty(cx, obj, "toStringResult", toString,
4055 JSPROP_ENUMERATE)) {
4056 return false;
4059 if (!CopyErrorReportToObject(cx, report.report(), obj)) {
4060 return false;
4063 args.rval().setObject(*obj);
4064 return true;
4067 #define LAZY_STANDARD_CLASSES
4069 /* A class for easily testing the inner/outer object callbacks. */
4070 typedef struct ComplexObject {
4071 bool isInner;
4072 bool frozen;
4073 JSObject* inner;
4074 JSObject* outer;
4075 } ComplexObject;
4077 static bool sandbox_enumerate(JSContext* cx, JS::HandleObject obj,
4078 JS::MutableHandleIdVector properties,
4079 bool enumerableOnly) {
4080 RootedValue v(cx);
4082 if (!JS_GetProperty(cx, obj, "lazy", &v)) {
4083 return false;
4086 if (!ToBoolean(v)) {
4087 return true;
4090 return JS_NewEnumerateStandardClasses(cx, obj, properties, enumerableOnly);
4093 static bool sandbox_resolve(JSContext* cx, HandleObject obj, HandleId id,
4094 bool* resolvedp) {
4095 RootedValue v(cx);
4096 if (!JS_GetProperty(cx, obj, "lazy", &v)) {
4097 return false;
4100 if (ToBoolean(v)) {
4101 return JS_ResolveStandardClass(cx, obj, id, resolvedp);
4103 return true;
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
4114 nullptr, // call
4115 nullptr, // construct
4116 JS_GlobalObjectTraceHook, // trace
4119 static const JSClass sandbox_class = {"sandbox", JSCLASS_GLOBAL_FLAGS,
4120 &sandbox_classOps};
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)
4129 #endif
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) {
4139 return true;
4142 JS::Compartment* comp = creationOptions.compartment();
4144 // All realms in a compartment must be either system or non-system.
4145 bool isSystem =
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");
4151 return false;
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");
4160 return false;
4163 return true;
4166 static JSObject* NewSandbox(JSContext* cx, bool lazy) {
4167 JS::RealmOptions options;
4168 SetStandardRealmOptions(options);
4170 if (defaultToSameCompartment) {
4171 options.creationOptions().setExistingCompartment(cx->global());
4172 } else {
4173 options.creationOptions().setNewCompartmentAndZone();
4176 JSPrincipals* principals = nullptr;
4177 if (!CheckRealmOptions(cx, options, principals)) {
4178 return nullptr;
4181 RootedObject obj(cx,
4182 JS_NewGlobalObject(cx, &sandbox_class, principals,
4183 JS::DontFireOnNewGlobalHook, options));
4184 if (!obj) {
4185 return nullptr;
4189 JSAutoRealm ar(cx, obj);
4190 if (!lazy && !JS::InitRealmStandardClasses(cx)) {
4191 return nullptr;
4194 RootedValue value(cx, BooleanValue(lazy));
4195 if (!JS_DefineProperty(cx, obj, "lazy", value,
4196 JSPROP_PERMANENT | JSPROP_READONLY)) {
4197 return nullptr;
4200 JS_FireOnNewGlobalObject(cx, obj);
4203 if (!cx->compartment()->wrap(cx, &obj)) {
4204 return nullptr;
4206 return obj;
4209 static bool EvalInContext(JSContext* cx, unsigned argc, Value* vp) {
4210 CallArgs args = CallArgsFromVp(argc, vp);
4211 if (!args.requireAtLeast(cx, "evalcx", 1)) {
4212 return false;
4215 RootedString str(cx, ToString(cx, args[0]));
4216 if (!str) {
4217 return false;
4220 RootedObject sobj(cx);
4221 if (args.hasDefined(1)) {
4222 sobj = ToObject(cx, args[1]);
4223 if (!sobj) {
4224 return false;
4228 AutoStableStringChars strChars(cx);
4229 if (!strChars.initTwoByte(cx, str)) {
4230 return false;
4233 mozilla::Range<const char16_t> chars = strChars.twoByteRange();
4234 size_t srclen = chars.length();
4235 const char16_t* src = chars.begin().get();
4237 bool lazy = false;
4238 if (srclen == 4) {
4239 if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
4240 lazy = true;
4241 srclen = 0;
4245 if (!sobj) {
4246 sobj = NewSandbox(cx, lazy);
4247 if (!sobj) {
4248 return false;
4252 if (srclen == 0) {
4253 args.rval().setObject(*sobj);
4254 return true;
4257 JS::AutoFilename filename;
4258 uint32_t lineno;
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");
4270 return false;
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())) {
4280 return false;
4284 if (!cx->compartment()->wrap(cx, args.rval())) {
4285 return false;
4288 return true;
4291 static bool EnsureGeckoProfilingStackInstalled(JSContext* cx,
4292 ShellContext* sc) {
4293 if (cx->geckoProfiler().infraInstalled()) {
4294 MOZ_ASSERT(sc->geckoProfilingStack);
4295 return true;
4298 MOZ_ASSERT(!sc->geckoProfilingStack);
4299 sc->geckoProfilingStack = MakeUnique<ProfilingStack>();
4300 if (!sc->geckoProfilingStack) {
4301 JS_ReportOutOfMemory(cx);
4302 return false;
4305 SetContextProfilingStack(cx, sc->geckoProfilingStack.get());
4306 return true;
4309 struct WorkerInput {
4310 JSRuntime* parentRuntime;
4311 UniqueTwoByteChars chars;
4312 size_t length;
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));
4322 js_delete(priv);
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);
4334 if (!cx) {
4335 return;
4337 auto destroyContext = MakeScopeExit([cx] { JS_DestroyContext(cx); });
4339 UniquePtr<ShellContext> sc =
4340 MakeUnique<ShellContext>(cx, ShellContext::Worker);
4341 if (!sc || !sc->registerWithCx(cx)) {
4342 return;
4345 if (!JS::InitSelfHostedCode(cx)) {
4346 return;
4349 EnvironmentPreparer environmentPreparer(cx);
4351 do {
4352 JS::RealmOptions realmOptions;
4353 SetStandardRealmOptions(realmOptions);
4355 RootedObject global(cx, NewGlobalObject(cx, realmOptions, nullptr,
4356 ShellGlobalKind::WindowProxy,
4357 /* immutablePrototype = */ true));
4358 if (!global) {
4359 break;
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) {
4368 return;
4370 sc->moduleLoader = js::MakeUnique<ModuleLoader>();
4371 if (!sc->moduleLoader || !sc->moduleLoader->init(cx, moduleLoadPath)) {
4372 return;
4375 JS::CompileOptions options(cx);
4376 options.setFileAndLine("<string>", 1)
4377 .setIsRunOnce(true)
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)) {
4384 break;
4387 RootedScript script(cx, JS::Compile(cx, options, srcBuf));
4388 if (!script) {
4389 break;
4391 RootedValue result(cx);
4392 JS_ExecuteScript(cx, script, &result);
4393 } while (0);
4395 KillWatchdog(cx);
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>;
4405 public:
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");
4414 return false;
4417 CallArgs args = CallArgsFromVp(argc, vp);
4418 if (!args.get(0).isString()) {
4419 JS_ReportErrorASCII(cx, "Invalid arguments");
4420 return false;
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");
4427 return false;
4429 #endif
4431 if (!args[0].toString()->ensureLinear(cx)) {
4432 return false;
4435 if (!workerThreadsLock) {
4436 workerThreadsLock = js_new<Mutex>(mutexid::ShellWorkerThreads);
4437 if (!workerThreadsLock) {
4438 ReportOutOfMemory(cx);
4439 return false;
4443 JSLinearString* str = &args[0].toString()->asLinear();
4445 UniqueTwoByteChars chars(js_pod_malloc<char16_t>(str->length()));
4446 if (!chars) {
4447 ReportOutOfMemory(cx);
4448 return false;
4451 CopyChars(chars.get(), *str);
4453 auto input = js::MakeUnique<WorkerInput>(JS_GetParentRuntime(cx),
4454 std::move(chars), str->length());
4455 if (!input) {
4456 ReportOutOfMemory(cx);
4457 return false;
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);
4473 thread->join();
4474 return false;
4477 args.rval().setUndefined();
4478 return true;
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");
4485 return false;
4487 JSObject* obj = &args[0].toObject();
4488 args.rval().set(JS_NumberValue(double(uintptr_t(obj->shape()) >> 3)));
4489 return true;
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) {
4498 double t_secs;
4499 if (!ToNumber(cx, args[0], &t_secs)) {
4500 return false;
4502 if (std::isnan(t_secs)) {
4503 JS_ReportErrorASCII(cx, "sleep interval is not a number");
4504 return false;
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");
4512 return false;
4516 LockGuard<Mutex> guard(sc->watchdogLock);
4517 TimeStamp toWakeup = TimeStamp::Now() + duration;
4518 for (;;) {
4519 sc->sleepWakeup.wait_for(guard, duration);
4520 if (sc->serviceInterrupt) {
4521 break;
4523 auto now = TimeStamp::Now();
4524 if (now >= toWakeup) {
4525 break;
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);
4541 if (thread) {
4542 // The watchdog thread becoming Nothing is its signal to exit.
4543 sc->watchdogWakeup.notify_one();
4546 if (thread) {
4547 thread->join();
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
4565 * outside the lock.
4567 sc->watchdogTimeout = Nothing();
4569 UnlockGuard<Mutex> unlock(guard);
4570 CancelExecution(cx);
4573 /* Wake up any threads doing sleep. */
4574 sc->sleepWakeup.notify_all();
4575 } else {
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);
4596 if (t <= 0) {
4597 LockGuard<Mutex> guard(sc->watchdogLock);
4598 sc->watchdogTimeout = Nothing();
4599 return true;
4602 #ifdef __wasi__
4603 return false;
4604 #endif
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);
4620 return true;
4623 static void KillWorkerThreads(JSContext* cx) {
4624 MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty());
4626 if (!workerThreadsLock) {
4627 MOZ_ASSERT(workerThreads.empty());
4628 return;
4631 while (true) {
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()) {
4639 break;
4641 thread = std::move(workerThreads.back());
4642 workerThreads.popBack();
4644 thread->join();
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");
4662 return false;
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");
4668 return false;
4670 GetShellContext(cx)->timeoutInterval = t;
4671 if (!ScheduleWatchdog(cx, t)) {
4672 JS_ReportErrorASCII(cx, "Failed to create the watchdog");
4673 return false;
4675 return true;
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);
4684 return true;
4687 if (args.length() > 2) {
4688 JS_ReportErrorASCII(cx, "Wrong number of arguments");
4689 return false;
4692 double t;
4693 if (!ToNumber(cx, args[0], &t)) {
4694 return false;
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");
4701 return false;
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");
4716 return false;
4719 if (ToBoolean(args[0])) {
4720 GetShellContext(cx)->serviceInterrupt = true;
4721 JS_RequestInterruptCallback(cx);
4724 args.rval().setUndefined();
4725 return true;
4728 static bool InvokeInterruptCallbackWrapper(JSContext* cx, unsigned argc,
4729 Value* vp) {
4730 CallArgs args = CallArgsFromVp(argc, vp);
4731 if (args.length() != 1) {
4732 JS_ReportErrorASCII(cx, "Wrong number of arguments");
4733 return false;
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
4743 // return, return.
4744 if (!interruptRv && !cx->isExceptionPending() &&
4745 !cx->isPropagatingForcedReturn()) {
4746 return false;
4749 JS::AutoSaveExceptionState savedExc(cx);
4751 FixedInvokeArgs<1> iargs(cx);
4753 iargs[0].setBoolean(interruptRv);
4755 RootedValue rv(cx);
4756 if (!js::Call(cx, args[0], UndefinedHandleValue, iargs, &rv)) {
4757 return false;
4760 args.rval().setUndefined();
4761 return interruptRv;
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");
4769 return false;
4772 RootedValue value(cx, args[0]);
4773 if (!value.isObject() || !value.toObject().is<JSFunction>()) {
4774 JS_ReportErrorASCII(cx, "Argument must be a function");
4775 return false;
4777 GetShellContext(cx)->interruptFunc = value;
4778 GetShellContext(cx)->haveInterruptFunc = true;
4780 args.rval().setUndefined();
4781 return true;
4784 #ifdef DEBUG
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.");
4796 return false;
4798 if (!(args[0].isObject() && args[0].toObject().is<RegExpObject>())) {
4799 ReportUsageErrorASCII(cx, callee,
4800 "First argument must be a regular expression.");
4801 return false;
4803 if (!args[1].isString()) {
4804 ReportUsageErrorASCII(cx, callee, "Second argument must be a String.");
4805 return false;
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,
4816 args.rval());
4818 #endif
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.");
4826 return false;
4828 if (!args[0].isString()) {
4829 ReportUsageErrorASCII(cx, callee, "First argument must be a string.");
4830 return false;
4833 RootedString string(cx, args[0].toString());
4834 AutoStableStringChars stableChars(cx);
4835 if (!stableChars.initTwoByte(cx, string)) {
4836 return false;
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,
4844 &error)) {
4845 return false;
4848 args.rval().set(error);
4849 return true;
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.");
4858 return false;
4861 if (!args[0].isString()) {
4862 ReportUsageErrorASCII(cx, callee, "First argument must be a String.");
4863 return false;
4866 if (!args[1].isInt32()) {
4867 ReportUsageErrorASCII(cx, callee, "Second argument must be an Int32.");
4868 return false;
4871 // Disallow setting JIT options when there are worker threads, to avoid
4872 // races.
4873 if (workerThreadsLock) {
4874 ReportUsageErrorASCII(
4875 cx, callee, "Can't set JIT options when there are worker threads.");
4876 return false;
4879 JSLinearString* strArg = JS_EnsureLinearString(cx, args[0].toString());
4880 if (!strArg) {
4881 return false;
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;
4889 if (false) {
4891 JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
4892 #undef JIT_COMPILER_MATCH
4894 if (opt == JSJITCOMPILER_NOT_AN_OPTION) {
4895 ReportUsageErrorASCII(
4896 cx, callee,
4897 "First argument does not name a valid option (see jsapi.h).");
4898 return false;
4901 int32_t number = args[1].toInt32();
4902 if (number < 0) {
4903 number = -1;
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
4911 // use shell flags.
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.");
4917 return false;
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) &&
4924 number == 0) {
4925 js::jit::JitActivationIterator iter(cx);
4926 if (!iter.done()) {
4927 JS_ReportErrorASCII(cx,
4928 "Can't turn off JITs with JIT code on the stack.");
4929 return false;
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");
4941 return false;
4943 return true;
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) &&
4955 number == 0) {
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.");
4967 return false;
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();
4983 return true;
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();
4994 return true;
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();
5005 return true;
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.");
5014 return false;
5017 if (!JS_WrapValue(cx, &sc->lastWarning)) {
5018 return false;
5021 args.rval().set(sc->lastWarning);
5022 return true;
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.");
5031 return false;
5034 sc->lastWarning.setNull();
5036 args.rval().setUndefined();
5037 return true;
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");
5046 return false;
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);
5055 if (!buf) {
5056 fputs("Failed to format JavaScript stack for dump\n", gOutFile->fp);
5057 JS_ClearPendingException(cx);
5058 } else {
5059 fputs(buf.get(), gOutFile->fp);
5062 args.rval().setUndefined();
5063 return true;
5065 #endif
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));
5073 return true;
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);
5081 return true;
5083 JS_ReportErrorASCII(cx, "Wrong number of arguments");
5084 return false;
5087 static ShellCompartmentPrivate* EnsureShellCompartmentPrivate(JSContext* cx) {
5088 Compartment* comp = cx->compartment();
5089 auto priv =
5090 static_cast<ShellCompartmentPrivate*>(JS_GetCompartmentPrivate(comp));
5091 if (!priv) {
5092 priv = cx->new_<ShellCompartmentPrivate>();
5093 JS_SetCompartmentPrivate(cx->compartment(), priv);
5095 return priv;
5098 static bool ParseModule(JSContext* cx, unsigned argc, Value* vp) {
5099 CallArgs args = CallArgsFromVp(argc, vp);
5100 if (!args.requireAtLeast(cx, "parseModule", 1)) {
5101 return false;
5104 if (!args[0].isString()) {
5105 const char* typeName = InformalValueTypeName(args[0]);
5106 JS_ReportErrorASCII(cx, "expected string to compile, got %s", typeName);
5107 return false;
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);
5118 return false;
5121 RootedString str(cx, args[1].toString());
5122 filename = JS_EncodeStringToUTF8(cx, str);
5123 if (!filename) {
5124 return false;
5127 options.setFileAndLine(filename.get(), 1);
5128 } else {
5129 options.setFileAndLine("<string>", 1);
5131 options.setModule();
5133 AutoStableStringChars linearChars(cx);
5134 if (!linearChars.initTwoByte(cx, scriptContents)) {
5135 return false;
5138 JS::SourceText<char16_t> srcBuf;
5139 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
5140 return false;
5143 AutoReportFrontendContext fc(cx);
5144 RootedObject module(cx, frontend::CompileModule(cx, &fc, options, srcBuf));
5145 if (!module) {
5146 return false;
5149 Rooted<ShellModuleObjectWrapper*> wrapper(
5150 cx, ShellModuleObjectWrapper::create(cx, module.as<ModuleObject>()));
5151 if (!wrapper) {
5152 return false;
5154 args.rval().setObject(*wrapper);
5155 return true;
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;
5163 public:
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());
5173 MOZ_ASSERT(buf);
5174 return buf;
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
5190 nullptr, // resolve
5191 nullptr, // mayResolve
5192 XDRBufferObject::finalize, // finalize
5193 nullptr, // call
5194 nullptr, // construct
5195 nullptr, // trace
5198 /*static */ const JSClass XDRBufferObject::class_ = {
5199 "XDRBufferObject",
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);
5208 if (!bufObj) {
5209 return nullptr;
5212 auto heapBuf = cx->make_unique<JS::TranscodeBuffer>(std::move(buf));
5213 if (!heapBuf) {
5214 return nullptr;
5217 size_t len = heapBuf->length();
5218 InitReservedSlot(bufObj, VECTOR_SLOT, heapBuf.release(), len,
5219 MemoryUse::XDRBufferElements);
5221 return bufObj;
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)) {
5236 return false;
5239 /* Prepare the input byte array. */
5240 if (!args[0].isObject()) {
5241 JS_ReportErrorASCII(cx,
5242 "instantiateModuleStencil: Stencil object expected");
5243 return false;
5245 Rooted<js::StencilObject*> stencilObj(
5246 cx, args[0].toObject().maybeUnwrapIf<js::StencilObject>());
5247 if (!stencilObj) {
5248 JS_ReportErrorASCII(cx,
5249 "instantiateModuleStencil: Stencil object expected");
5250 return false;
5253 if (!stencilObj->stencil()->isModule()) {
5254 JS_ReportErrorASCII(cx,
5255 "instantiateModuleStencil: Module stencil expected");
5256 return false;
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");
5265 return false;
5268 RootedObject opts(cx, &args[1].toObject());
5269 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
5270 return false;
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)) {
5279 return false;
5282 if (!js::ValidateLazinessOfStencilAndGlobal(cx, *stencilObj->stencil())) {
5283 return false;
5286 /* Instantiate the stencil. */
5287 Rooted<frontend::CompilationGCOutput> output(cx);
5288 if (!frontend::CompilationStencil::instantiateStencils(
5289 cx, input.get(), *stencilObj->stencil(), output.get())) {
5290 return false;
5293 Rooted<ModuleObject*> modObject(cx, output.get().module);
5294 Rooted<ShellModuleObjectWrapper*> wrapper(
5295 cx, ShellModuleObjectWrapper::create(cx, modObject));
5296 if (!wrapper) {
5297 return false;
5299 args.rval().setObject(*wrapper);
5300 return true;
5303 static bool InstantiateModuleStencilXDR(JSContext* cx, uint32_t argc,
5304 Value* vp) {
5305 CallArgs args = CallArgsFromVp(argc, vp);
5307 if (!args.requireAtLeast(cx, "instantiateModuleStencilXDR", 1)) {
5308 return false;
5311 /* Prepare the input byte array. */
5312 if (!args[0].isObject()) {
5313 JS_ReportErrorASCII(
5314 cx, "instantiateModuleStencilXDR: Stencil XDR object expected");
5315 return false;
5317 Rooted<StencilXDRBufferObject*> xdrObj(
5318 cx, args[0].toObject().maybeUnwrapIf<StencilXDRBufferObject>());
5319 if (!xdrObj) {
5320 JS_ReportErrorASCII(
5321 cx, "instantiateModuleStencilXDR: Stencil XDR object expected");
5322 return false;
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");
5333 return false;
5336 RootedObject opts(cx, &args[1].toObject());
5337 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
5338 return false;
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)) {
5347 return false;
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)) {
5355 return false;
5357 if (!succeeded) {
5358 fc.clearAutoReport();
5359 JS_ReportErrorASCII(cx, "Decoding failure");
5360 return false;
5363 if (!stencil.isModule()) {
5364 fc.clearAutoReport();
5365 JS_ReportErrorASCII(cx,
5366 "instantiateModuleStencilXDR: Module stencil expected");
5367 return false;
5370 if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencil)) {
5371 return false;
5374 /* Instantiate the stencil. */
5375 Rooted<frontend::CompilationGCOutput> output(cx);
5376 if (!frontend::CompilationStencil::instantiateStencils(
5377 cx, input.get(), stencil, output.get())) {
5378 return false;
5381 Rooted<ModuleObject*> modObject(cx, output.get().module);
5382 Rooted<ShellModuleObjectWrapper*> wrapper(
5383 cx, ShellModuleObjectWrapper::create(cx, modObject));
5384 if (!wrapper) {
5385 return false;
5387 args.rval().setObject(*wrapper);
5388 return true;
5391 static bool RegisterModule(JSContext* cx, unsigned argc, Value* vp) {
5392 CallArgs args = CallArgsFromVp(argc, vp);
5393 if (!args.requireAtLeast(cx, "registerModule", 2)) {
5394 return false;
5397 if (!args[0].isString()) {
5398 const char* typeName = InformalValueTypeName(args[0]);
5399 JS_ReportErrorASCII(cx, "expected string, got %s", typeName);
5400 return false;
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);
5407 return false;
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()));
5415 if (!specifier) {
5416 return false;
5419 RootedObject moduleRequest(
5420 cx, ModuleRequestObject::create(cx, specifier, nullptr));
5421 if (!moduleRequest) {
5422 return false;
5425 if (!sc->moduleLoader->registerTestModule(cx, moduleRequest, module)) {
5426 return false;
5429 Rooted<ShellModuleObjectWrapper*> wrapper(
5430 cx, ShellModuleObjectWrapper::create(cx, module));
5431 if (!wrapper) {
5432 return false;
5434 args.rval().setObject(*wrapper);
5435 return true;
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();
5443 return true;
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,
5451 "moduleLink");
5452 return false;
5455 RootedObject object(cx, UncheckedUnwrap(&args[0].toObject()));
5456 if (!object->is<ShellModuleObjectWrapper>()) {
5457 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
5458 "moduleLink");
5459 return false;
5462 AutoRealm ar(cx, object);
5464 Rooted<ModuleObject*> module(cx,
5465 object->as<ShellModuleObjectWrapper>().get());
5466 if (!js::ModuleLink(cx, module)) {
5467 return false;
5470 args.rval().setUndefined();
5471 return true;
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,
5479 "moduleEvaluate");
5480 return false;
5483 RootedObject object(cx, UncheckedUnwrap(&args[0].toObject()));
5484 if (!object->is<ShellModuleObjectWrapper>()) {
5485 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
5486 "moduleEvaluate");
5487 return false;
5491 AutoRealm ar(cx, object);
5493 Rooted<ModuleObject*> module(cx,
5494 object->as<ShellModuleObjectWrapper>().get());
5495 if (!js::ModuleEvaluate(cx, module, args.rval())) {
5496 return false;
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());
5508 MOZ_ASSERT(env);
5509 return env;
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");
5516 return false;
5519 if (!args[0].isObject() ||
5520 !args[0].toObject().is<ShellModuleObjectWrapper>()) {
5521 JS_ReportErrorASCII(cx,
5522 "First argument should be a ShellModuleObjectWrapper");
5523 return false;
5526 Rooted<ModuleObject*> module(
5527 cx, args[0].toObject().as<ShellModuleObjectWrapper>().get());
5528 if (module->hadEvaluationError()) {
5529 JS_ReportErrorASCII(cx, "Module environment unavailable");
5530 return false;
5533 Rooted<ModuleEnvironmentObject*> env(cx,
5534 GetModuleInitialEnvironment(cx, module));
5535 Rooted<IdVector> ids(cx, IdVector(cx));
5536 if (!JS_Enumerate(cx, env, &ids)) {
5537 return false;
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));
5546 if (!array) {
5547 return false;
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);
5556 return true;
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");
5563 return false;
5566 if (!args[0].isObject() ||
5567 !args[0].toObject().is<ShellModuleObjectWrapper>()) {
5568 JS_ReportErrorASCII(cx,
5569 "First argument should be a ShellModuleObjectWrapper");
5570 return false;
5573 if (!args[1].isString()) {
5574 JS_ReportErrorASCII(cx, "Second argument should be a string");
5575 return false;
5578 Rooted<ModuleObject*> module(
5579 cx, args[0].toObject().as<ShellModuleObjectWrapper>().get());
5580 if (module->hadEvaluationError()) {
5581 JS_ReportErrorASCII(cx, "Module environment unavailable");
5582 return false;
5585 Rooted<ModuleEnvironmentObject*> env(cx,
5586 GetModuleInitialEnvironment(cx, module));
5587 RootedString name(cx, args[1].toString());
5588 RootedId id(cx);
5589 if (!JS_StringToId(cx, name, &id)) {
5590 return false;
5593 if (!GetProperty(cx, env, env, id, args.rval())) {
5594 return false;
5597 if (args.rval().isMagic(JS_UNINITIALIZED_LEXICAL)) {
5598 ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
5599 return false;
5602 return true;
5605 enum class DumpType {
5606 ParseNode,
5607 Stencil,
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,
5620 compilationState,
5621 /* syntaxParser = */ nullptr);
5622 if (!parser.checkOptions()) {
5623 return false;
5626 // Emplace the top-level stencil.
5627 MOZ_ASSERT(compilationState.scriptData.length() ==
5628 CompilationStencil::TopLevelIndex);
5629 if (!compilationState.appendScriptStencilAndData(&fc)) {
5630 return false;
5633 js::frontend::ParseNode* pn;
5634 if (goal == frontend::ParseGoal::Script) {
5635 pn = parser.parse().unwrapOr(nullptr);
5636 } else {
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);
5644 if (!pn) {
5645 return false;
5648 #if defined(DEBUG)
5649 js::Fprinter out(stderr);
5650 DumpParseTree(&parser, pn, out);
5651 #endif
5653 return true;
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)) {
5666 return false;
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);
5675 } else {
5676 stencil = frontend::ParseModuleToExtensibleStencil(
5677 cx, &fc, cx->tempLifoAlloc(), input.get(), &scopeCache, srcBuf);
5680 if (!stencil) {
5681 return false;
5684 #if defined(DEBUG) || defined(JS_JITSPEW)
5685 stencil->dump();
5686 #endif
5688 return true;
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)) {
5698 return false;
5700 if (!args[0].isString()) {
5701 const char* typeName = InformalValueTypeName(args[0]);
5702 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
5703 return false;
5706 frontend::ParseGoal goal = frontend::ParseGoal::Script;
5707 #ifdef JS_ENABLE_SMOOSH
5708 bool smoosh = false;
5709 #endif
5711 CompileOptions options(cx);
5712 options.setIntroductionType("js shell parse")
5713 .setFileAndLine("<string>", 1)
5714 .setIsRunOnce(true)
5715 .setNoScriptRval(true);
5717 if (args.length() >= 2) {
5718 if (!args[1].isObject()) {
5719 JS_ReportErrorASCII(cx, "The 2nd argument must be an object");
5720 return false;
5723 RootedObject objOptions(cx, &args[1].toObject());
5725 RootedValue optionModule(cx);
5726 if (!JS_GetProperty(cx, objOptions, "module", &optionModule)) {
5727 return false;
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",
5737 typeName);
5738 return false;
5740 if (!js::ParseCompileOptions(cx, options, objOptions, nullptr)) {
5741 return false;
5744 if (goal == frontend::ParseGoal::Module && options.lineno == 0) {
5745 JS_ReportErrorASCII(cx, "Module cannot be compiled with lineNumber == 0");
5746 return false;
5749 #ifdef JS_ENABLE_SMOOSH
5750 bool found = false;
5751 if (!JS_HasProperty(cx, objOptions, "rustFrontend", &found)) {
5752 return false;
5754 if (found) {
5755 JS_ReportErrorASCII(cx, "'rustFrontend' option is renamed to 'smoosh'");
5756 return false;
5759 RootedValue optionSmoosh(cx);
5760 if (!JS_GetProperty(cx, objOptions, "smoosh", &optionSmoosh)) {
5761 return false;
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",
5769 typeName);
5770 return false;
5772 #endif // JS_ENABLE_SMOOSH
5775 JSString* scriptContents = args[0].toString();
5776 Rooted<JSLinearString*> linearString(cx, scriptContents->ensureLinear(cx));
5777 if (!linearString) {
5778 return false;
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);
5790 if (isAscii) {
5791 if (!stableChars.init(cx, scriptContents)) {
5792 return false;
5794 MOZ_ASSERT(stableChars.isLatin1());
5795 } else {
5796 if (!stableChars.initTwoByte(cx, scriptContents)) {
5797 return false;
5801 size_t length = scriptContents->length();
5802 #ifdef JS_ENABLE_SMOOSH
5803 if (dumpType == DumpType::ParseNode) {
5804 if (smoosh) {
5805 if (isAscii) {
5806 const Latin1Char* chars = stableChars.latin1Range().begin().get();
5808 if (goal == frontend::ParseGoal::Script) {
5809 if (!SmooshParseScript(cx, chars, length)) {
5810 return false;
5812 } else {
5813 if (!SmooshParseModule(cx, chars, length)) {
5814 return false;
5817 args.rval().setUndefined();
5818 return true;
5820 JS_ReportErrorASCII(cx,
5821 "SmooshMonkey does not support non-ASCII chars yet");
5822 return false;
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
5835 if (smoosh) {
5836 if (isAscii) {
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)) {
5842 return false;
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)) {
5851 return false;
5853 if (!stencil) {
5854 fc.clearAutoReport();
5855 JS_ReportErrorASCII(cx, "SmooshMonkey failed to parse");
5856 return false;
5859 # ifdef DEBUG
5861 frontend::BorrowingCompilationStencil borrowingStencil(*stencil);
5862 borrowingStencil.dump();
5864 # endif
5865 } else {
5866 JS_ReportErrorASCII(cx,
5867 "SmooshMonkey does not support module stencil");
5868 return false;
5870 args.rval().setUndefined();
5871 return true;
5873 JS_ReportErrorASCII(cx,
5874 "SmooshMonkey does not support non-ASCII chars yet");
5875 return false;
5877 #endif // JS_ENABLE_SMOOSH
5879 if (isAscii) {
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)) {
5883 return false;
5885 } else {
5886 MOZ_ASSERT(stableChars.isTwoByte());
5887 const char16_t* chars = stableChars.twoByteRange().begin().get();
5888 if (!DumpStencil<char16_t>(cx, options, chars, length, goal)) {
5889 return false;
5893 args.rval().setUndefined();
5894 return true;
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)) {
5902 return false;
5904 } else {
5905 if (!input.get().initForModule(&fc)) {
5906 return false;
5910 LifoAllocScope allocScope(&cx->tempLifoAlloc());
5911 frontend::NoScopeBindingCache scopeCache;
5912 frontend::CompilationState compilationState(&fc, allocScope, input.get());
5913 if (!compilationState.init(&fc, &scopeCache)) {
5914 return false;
5917 if (isAscii) {
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,
5921 goal)) {
5922 return false;
5924 } else {
5925 MOZ_ASSERT(stableChars.isTwoByte());
5926 const char16_t* chars = stableChars.twoByteRange().begin().get();
5927 if (!DumpAST<char16_t>(cx, options, chars, length, compilationState,
5928 goal)) {
5929 return false;
5932 args.rval().setUndefined();
5933 return true;
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.");
5946 return false;
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)) {
5958 return false;
5960 if (!args[0].isString()) {
5961 const char* typeName = InformalValueTypeName(args[0]);
5962 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
5963 return false;
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)) {
5974 return false;
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)) {
5984 return false;
5987 LifoAllocScope allocScope(&cx->tempLifoAlloc());
5988 frontend::NoScopeBindingCache scopeCache;
5989 frontend::CompilationState compilationState(&fc, allocScope, input.get());
5990 if (!compilationState.init(&fc, &scopeCache)) {
5991 return false;
5994 Parser<frontend::SyntaxParseHandler, char16_t> parser(
5995 &fc, options, chars, length,
5996 /* foldConstants = */ false, compilationState,
5997 /* syntaxParser = */ nullptr);
5998 if (!parser.checkOptions()) {
5999 return false;
6002 bool succeeded = parser.parse().isOk();
6003 if (fc.hadErrors()) {
6004 return false;
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());
6011 return false;
6014 args.rval().setBoolean(succeeded);
6015 return true;
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");
6022 return false;
6025 CallArgs args = CallArgsFromVp(argc, vp);
6027 if (!args.requireAtLeast(cx, "offThreadCompileToStencil", 1)) {
6028 return false;
6030 if (!args[0].isString()) {
6031 const char* typeName = InformalValueTypeName(args[0]);
6032 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
6033 return false;
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");
6045 return false;
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)) {
6052 return false;
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)) {
6062 return false;
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));
6073 } else {
6074 ownedChars.reset(cx->pod_malloc<char16_t>(length));
6075 if (!ownedChars) {
6076 return false;
6079 mozilla::PodCopy(ownedChars.get(), chars, length);
6082 if (!cx->runtime()->canUseParallelParsing() || !js::CanUseExtraThreads()) {
6083 JS_ReportErrorASCII(cx, "cannot compile code on helper thread");
6084 return false;
6087 JS::SourceText<char16_t> srcBuf;
6088 if (!srcBuf.init(cx, std::move(ownedChars), length)) {
6089 return false;
6092 OffThreadJob* job = NewOffThreadJob(cx, OffThreadJob::Kind::CompileScript,
6093 options, std::move(srcBuf));
6094 if (!job) {
6095 return false;
6098 if (!job->dispatch()) {
6099 ReportOutOfMemory(cx);
6100 DeleteOffThreadJob(cx, job);
6101 return false;
6104 args.rval().setInt32(job->id);
6105 return true;
6108 static bool FinishOffThreadStencil(JSContext* cx, unsigned argc, Value* vp) {
6109 CallArgs args = CallArgsFromVp(argc, vp);
6111 OffThreadJob* job = LookupOffThreadJobForArgs(cx, args, 0);
6112 if (!job) {
6113 return false;
6116 job->waitUntilDone();
6118 RefPtr<JS::Stencil> stencil = job->stealStencil(cx);
6119 DeleteOffThreadJob(cx, job);
6120 if (!stencil) {
6121 return false;
6123 RootedObject stencilObj(cx,
6124 js::StencilObject::create(cx, std::move(stencil)));
6125 if (!stencilObj) {
6126 return false;
6129 args.rval().setObject(*stencilObj);
6130 return true;
6133 static bool OffThreadCompileModuleToStencil(JSContext* cx, unsigned argc,
6134 Value* vp) {
6135 CallArgs args = CallArgsFromVp(argc, vp);
6137 if (!args.requireAtLeast(cx, "offThreadCompileModuleToStencil", 1)) {
6138 return false;
6140 if (!args[0].isString()) {
6141 const char* typeName = InformalValueTypeName(args[0]);
6142 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
6143 return false;
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");
6156 return false;
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)) {
6163 return false;
6166 if (!ValidateModuleCompileOptions(cx, options)) {
6167 return false;
6171 options.setIsRunOnce(true).setSourceIsLazy(false);
6173 JSString* scriptContents = args[0].toString();
6174 AutoStableStringChars stableChars(cx);
6175 if (!stableChars.initTwoByte(cx, scriptContents)) {
6176 return false;
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));
6187 } else {
6188 ownedChars.reset(cx->pod_malloc<char16_t>(length));
6189 if (!ownedChars) {
6190 return false;
6193 mozilla::PodCopy(ownedChars.get(), chars, length);
6196 if (!cx->runtime()->canUseParallelParsing() || !js::CanUseExtraThreads()) {
6197 JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
6198 return false;
6201 JS::SourceText<char16_t> srcBuf;
6202 if (!srcBuf.init(cx, std::move(ownedChars), length)) {
6203 return false;
6206 OffThreadJob* job = NewOffThreadJob(cx, OffThreadJob::Kind::CompileModule,
6207 options, std::move(srcBuf));
6208 if (!job) {
6209 return false;
6212 if (!job->dispatch()) {
6213 ReportOutOfMemory(cx);
6214 DeleteOffThreadJob(cx, job);
6215 return false;
6218 args.rval().setInt32(job->id);
6219 return true;
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");
6226 return false;
6229 CallArgs args = CallArgsFromVp(argc, vp);
6231 if (!args.requireAtLeast(cx, "offThreadDecodeStencil", 1)) {
6232 return false;
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);
6237 return false;
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");
6250 return false;
6253 RootedObject opts(cx, &args[1].toObject());
6254 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
6255 return false;
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);
6267 if (!loadData) {
6268 return false;
6270 if (!loadBuffer.append(loadData, loadLength)) {
6271 JS_ReportOutOfMemory(cx);
6272 return false;
6275 if (!cx->runtime()->canUseParallelParsing() || !js::CanUseExtraThreads()) {
6276 JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
6277 return false;
6280 OffThreadJob* job = NewOffThreadJob(cx, OffThreadJob::Kind::Decode, options,
6281 std::move(loadBuffer));
6282 if (!job) {
6283 return false;
6286 if (!job->dispatch()) {
6287 ReportOutOfMemory(cx);
6288 DeleteOffThreadJob(cx, job);
6289 return false;
6292 args.rval().setInt32(job->id);
6293 return true;
6296 class AutoCStringVector {
6297 Vector<char*> argv_;
6299 public:
6300 explicit AutoCStringVector(JSContext* cx) : argv_(cx) {}
6301 ~AutoCStringVector() {
6302 for (size_t i = 0; i < argv_.length(); i++) {
6303 js_free(argv_[i]);
6306 bool append(UniqueChars&& arg) {
6307 if (!argv_.append(arg.get())) {
6308 return false;
6311 // Now owned by this vector.
6312 (void)arg.release();
6313 return true;
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) {
6319 js_free(argv_[i]);
6320 argv_[i] = arg.release();
6324 #if defined(XP_WIN)
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++) {
6331 if (!argv[i]) {
6332 continue;
6335 size_t newLen = 3; // quotes before and after and null-terminator
6336 for (char* p = argv[i]; *p; p++) {
6337 newLen++;
6338 if (*p == '\"' || *p == '\\') {
6339 newLen++;
6343 auto escaped = cx->make_pod_array<char>(newLen);
6344 if (!escaped) {
6345 return false;
6348 char* src = argv[i];
6349 char* dst = escaped.get();
6350 *dst++ = '\"';
6351 while (*src) {
6352 if (*src == '\"' || *src == '\\') {
6353 *dst++ = '\\';
6355 *dst++ = *src++;
6357 *dst++ = '\"';
6358 *dst++ = '\0';
6359 MOZ_ASSERT(escaped.get() + newLen == dst);
6361 argv.replace(i, std::move(escaped));
6363 return true;
6365 #endif
6367 #ifndef __wasi__
6368 static bool ReadAll(int fd, wasm::Bytes* bytes) {
6369 size_t lastLength = bytes->length();
6370 while (true) {
6371 static const int ChunkSize = 64 * 1024;
6372 if (!bytes->growBy(ChunkSize)) {
6373 return false;
6376 intptr_t readCount;
6377 while (true) {
6378 readCount = read(fd, bytes->begin() + lastLength, ChunkSize);
6379 if (readCount >= 0) {
6380 break;
6382 if (errno != EINTR) {
6383 return false;
6387 if (readCount < ChunkSize) {
6388 bytes->shrinkTo(lastLength + readCount);
6389 if (readCount == 0) {
6390 return true;
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);
6401 if (written < 0) {
6402 if (errno == EINTR) {
6403 continue;
6405 return false;
6407 MOZ_ASSERT(unsigned(written) <= length);
6408 length -= written;
6409 bytes += written;
6412 return true;
6415 class AutoPipe {
6416 int fds_[2];
6418 public:
6419 AutoPipe() {
6420 fds_[0] = -1;
6421 fds_[1] = -1;
6424 ~AutoPipe() {
6425 if (fds_[0] != -1) {
6426 close(fds_[0]);
6428 if (fds_[1] != -1) {
6429 close(fds_[1]);
6433 bool init() {
6434 # ifdef XP_WIN
6435 return !_pipe(fds_, 4096, O_BINARY);
6436 # else
6437 return !pipe(fds_);
6438 # endif
6441 int reader() const {
6442 MOZ_ASSERT(fds_[0] != -1);
6443 return fds_[0];
6446 int writer() const {
6447 MOZ_ASSERT(fds_[1] != -1);
6448 return fds_[1];
6451 void closeReader() {
6452 MOZ_ASSERT(fds_[0] != -1);
6453 close(fds_[0]);
6454 fds_[0] = -1;
6457 void closeWriter() {
6458 MOZ_ASSERT(fds_[1] != -1);
6459 close(fds_[1]);
6460 fds_[1] = -1;
6463 #endif // __wasi__
6465 int shell::sArgc;
6466 char** shell::sArgv;
6468 #ifndef __wasi__
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()) {
6479 return false;
6482 AutoCStringVector argv(cx);
6484 UniqueChars argv0 = DuplicateString(cx, sArgv[0]);
6485 if (!argv0 || !argv.append(std::move(argv0))) {
6486 return false;
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))) {
6494 return false;
6498 UniqueChars arg;
6500 arg = DuplicateString(sWasmCompileAndSerializeFlag);
6501 if (!arg || !argv.append(std::move(arg))) {
6502 return false;
6505 # ifdef XP_WIN
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))) {
6514 return false;
6517 arg = JS_smprintf("%d", stdIn.writer());
6518 if (!arg || !argv.append(std::move(arg))) {
6519 return false;
6522 arg = JS_smprintf("%d", stdOut.reader());
6523 if (!arg || !argv.append(std::move(arg))) {
6524 return false;
6527 arg = JS_smprintf("%d", stdOut.writer());
6528 if (!arg || !argv.append(std::move(arg))) {
6529 return false;
6531 # endif
6533 // Required by both _spawnv and exec.
6534 if (!argv.append(nullptr)) {
6535 return false;
6538 # ifdef XP_WIN
6539 if (!EscapeForShell(cx, argv)) {
6540 return false;
6543 int childPid = _spawnv(P_NOWAIT, sArgv[0], argv.get());
6544 if (childPid == -1) {
6545 return false;
6547 # else
6548 pid_t childPid = fork();
6549 switch (childPid) {
6550 case -1:
6551 return false;
6552 case 0:
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) {
6558 exit(-1);
6560 if (dup2(stdOut.writer(), STDOUT_FILENO) == -1) {
6561 exit(-1);
6563 close(stdIn.reader());
6564 close(stdIn.writer());
6565 close(stdOut.reader());
6566 close(stdOut.writer());
6567 execv(sArgv[0], argv.get());
6568 exit(-1);
6570 # endif
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)) {
6578 return false;
6581 stdIn.closeWriter();
6583 if (!ReadAll(stdOut.reader(), serialized)) {
6584 return false;
6587 stdOut.closeReader();
6589 int status;
6590 # ifdef XP_WIN
6591 if (_cwait(&status, childPid, WAIT_CHILD) == -1) {
6592 return false;
6594 # else
6595 while (true) {
6596 if (waitpid(childPid, &status, 0) >= 0) {
6597 break;
6599 if (errno != EINTR) {
6600 return false;
6603 # endif
6605 return status == 0;
6608 static bool WasmCompileAndSerialize(JSContext* cx) {
6609 MOZ_ASSERT(wasm::CodeCachingAvailable(cx));
6611 # ifdef XP_WIN
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.
6615 int flagIndex = 0;
6616 for (; flagIndex < sArgc; flagIndex++) {
6617 if (!strcmp(sArgv[flagIndex], sWasmCompileAndSerializeFlag)) {
6618 break;
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;
6632 close(stdInWriter);
6633 close(stdOutReader);
6634 int stdOut = stdOutWriter;
6635 # else
6636 int stdIn = STDIN_FILENO;
6637 int stdOut = STDOUT_FILENO;
6638 # endif
6640 wasm::MutableBytes bytecode = js_new<wasm::ShareableBytes>();
6641 if (!ReadAll(stdIn, &bytecode->bytes)) {
6642 return false;
6645 wasm::Bytes serialized;
6646 if (!wasm::CompileAndSerialize(cx, *bytecode, &serialized)) {
6647 return false;
6650 if (!WriteAll(stdOut, serialized.begin(), serialized.length())) {
6651 return false;
6654 return true;
6657 static bool WasmCompileInSeparateProcess(JSContext* cx, unsigned argc,
6658 Value* vp) {
6659 if (!wasm::CodeCachingAvailable(cx)) {
6660 JS_ReportErrorASCII(cx, "WebAssembly caching not supported");
6661 return false;
6664 CallArgs args = CallArgsFromVp(argc, vp);
6665 if (!args.requireAtLeast(cx, "wasmCompileInSeparateProcess", 1)) {
6666 return false;
6669 SharedMem<uint8_t*> bytecode;
6670 size_t numBytes;
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");
6675 return false;
6678 wasm::Bytes serialized;
6679 if (!CompileAndSerializeInSeparateProcess(cx, bytecode.unwrap(), numBytes,
6680 &serialized)) {
6681 if (!cx->isExceptionPending()) {
6682 JS_ReportErrorASCII(cx, "creating and executing child process");
6684 return false;
6687 RootedObject module(cx);
6688 if (!wasm::DeserializeModule(cx, serialized, &module)) {
6689 return false;
6692 args.rval().setObject(*module);
6693 return true;
6695 #endif // __wasi__
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();
6702 return true;
6704 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
6705 JSString* result = JS_DecompileFunction(cx, fun);
6706 if (!result) {
6707 return false;
6709 args.rval().setString(result);
6710 return true;
6713 static bool DecompileThisScript(JSContext* cx, unsigned argc, Value* vp) {
6714 CallArgs args = CallArgsFromVp(argc, vp);
6716 NonBuiltinScriptFrameIter iter(cx);
6717 if (iter.done()) {
6718 args.rval().setString(cx->runtime()->emptyString);
6719 return true;
6723 JSAutoRealm ar(cx, iter.script());
6725 RootedScript script(cx, iter.script());
6726 JSString* result = JS_DecompileScript(cx, script);
6727 if (!result) {
6728 return false;
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));
6741 if (!str) {
6742 return false;
6745 args.rval().setString(str);
6746 return true;
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);
6755 return true;
6758 JSString* str = NewStringCopyUTF8(cx, filename.get());
6759 if (!str) {
6760 return false;
6763 args.rval().setString(str);
6764 return true;
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");
6774 return false;
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");
6781 return false;
6784 WrapperOptions options(cx);
6785 options.setProto(proto.toObjectOrNull());
6786 JSObject* wrapped = Wrapper::New(cx, &obj.toObject(),
6787 &Wrapper::singletonWithPrototype, options);
6788 if (!wrapped) {
6789 return false;
6792 args.rval().setObject(*wrapped);
6793 return true;
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());
6812 } else {
6813 creationOptions.setNewCompartmentAndZone();
6816 JS::AutoHoldPrincipals principals(cx);
6818 if (args.length() == 1 && args[0].isObject()) {
6819 RootedObject opts(cx, &args[0].toObject());
6820 RootedValue v(cx);
6822 if (!JS_GetProperty(cx, opts, "invisibleToDebugger", &v)) {
6823 return false;
6825 if (v.isBoolean()) {
6826 creationOptions.setInvisibleToDebugger(v.toBoolean());
6829 if (!JS_GetProperty(cx, opts, "sameZoneAs", &v)) {
6830 return false;
6832 if (v.isObject()) {
6833 creationOptions.setNewCompartmentInExistingZone(
6834 UncheckedUnwrap(&v.toObject()));
6837 if (!JS_GetProperty(cx, opts, "sameCompartmentAs", &v)) {
6838 return false;
6840 if (v.isObject()) {
6841 creationOptions.setExistingCompartment(UncheckedUnwrap(&v.toObject()));
6844 if (!JS_GetProperty(cx, opts, "newCompartment", &v)) {
6845 return false;
6847 if (v.isBoolean()) {
6848 if (v.toBoolean()) {
6849 creationOptions.setNewCompartmentAndZone();
6850 } else {
6851 creationOptions.setExistingCompartment(cx->global());
6855 if (!JS_GetProperty(cx, opts, "discardSource", &v)) {
6856 return false;
6858 if (v.isBoolean()) {
6859 behaviors.setDiscardSource(v.toBoolean());
6862 if (!JS_GetProperty(cx, opts, "useWindowProxy", &v)) {
6863 return false;
6865 if (v.isBoolean()) {
6866 kind = v.toBoolean() ? ShellGlobalKind::WindowProxy
6867 : ShellGlobalKind::GlobalObject;
6870 if (!JS_GetProperty(cx, opts, "immutablePrototype", &v)) {
6871 return false;
6873 if (v.isBoolean()) {
6874 immutablePrototype = v.toBoolean();
6877 if (!JS_GetProperty(cx, opts, "systemPrincipal", &v)) {
6878 return false;
6880 if (v.isBoolean()) {
6881 principals.reset(&ShellPrincipals::fullyTrusted);
6884 if (!JS_GetProperty(cx, opts, "principal", &v)) {
6885 return false;
6887 if (!v.isUndefined()) {
6888 uint32_t bits;
6889 if (!ToUint32(cx, v, &bits)) {
6890 return false;
6892 JSPrincipals* newPrincipals = cx->new_<ShellPrincipals>(bits);
6893 if (!newPrincipals) {
6894 return false;
6896 principals.reset(newPrincipals);
6899 if (!JS_GetProperty(cx, opts, "enableCoopAndCoep", &v)) {
6900 return false;
6902 if (v.isBoolean()) {
6903 creationOptions.setCoopAndCoepEnabled(v.toBoolean());
6906 if (!JS_GetProperty(cx, opts, "freezeBuiltins", &v)) {
6907 return false;
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)) {
6918 return false;
6920 if (v.isBoolean()) {
6921 creationOptions.setDefineSharedArrayBufferConstructor(v.toBoolean());
6924 if (!JS_GetProperty(cx, opts, "forceUTC", &v)) {
6925 return false;
6927 if (v.isBoolean()) {
6928 creationOptions.setForceUTC(v.toBoolean());
6931 if (!JS_GetProperty(cx, opts, "alwaysUseFdlibm", &v)) {
6932 return false;
6934 if (v.isBoolean()) {
6935 creationOptions.setAlwaysUseFdlibm(v.toBoolean());
6938 if (!JS_GetProperty(cx, opts, "locale", &v)) {
6939 return false;
6941 if (v.isString()) {
6942 RootedString str(cx, v.toString());
6943 UniqueChars locale = StringToLocale(cx, callee, str);
6944 if (!locale) {
6945 return false;
6947 creationOptions.setLocaleCopyZ(locale.get());
6951 if (!CheckRealmOptions(cx, options, principals.get())) {
6952 return false;
6955 RootedObject global(cx, NewGlobalObject(cx, options, principals.get(), kind,
6956 immutablePrototype));
6957 if (!global) {
6958 return false;
6961 RootedObject wrapped(cx, ToWindowProxyIfWindow(global));
6962 if (!JS_WrapObject(cx, &wrapped)) {
6963 return false;
6966 args.rval().setObject(*wrapped);
6967 return true;
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");
6976 return false;
6979 NukeCrossCompartmentWrappers(cx, AllCompartments(), cx->realm(),
6980 NukeWindowReferences, NukeAllReferences);
6981 args.rval().setUndefined();
6982 return true;
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");
6991 return false;
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))) {
7014 return false;
7017 args.rval().setUndefined();
7018 return true;
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;
7044 fprintf(stderr,
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();
7056 return true;
7059 static bool GetMaxArgs(JSContext* cx, unsigned argc, Value* vp) {
7060 CallArgs args = CallArgsFromVp(argc, vp);
7061 args.rval().setInt32(ARGS_LENGTH_MAX);
7062 return true;
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();
7073 return true;
7076 JS_ReportErrorASCII(
7077 cx, "IsHTMLDDA object is being called in an impermissible manner");
7078 return false;
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
7089 nullptr, // resolve
7090 nullptr, // mayResolve
7091 nullptr, // finalize
7092 IsHTMLDDA_Call, // call
7093 nullptr, // construct
7094 nullptr, // trace
7097 static const JSClass cls = {
7098 "IsHTMLDDA",
7099 JSCLASS_EMULATES_UNDEFINED,
7100 &classOps,
7103 JSObject* obj = JS_NewObject(cx, &cls);
7104 if (!obj) {
7105 return false;
7107 args.rval().setObject(*obj);
7108 return true;
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");
7117 return false;
7119 Rooted<JSAtom*> srcAtom(cx, ToAtom<CanGC>(cx, args[0]));
7120 if (!srcAtom) {
7121 return false;
7123 Rooted<PropertyName*> srcName(cx, srcAtom->asPropertyName());
7124 return GlobalObject::getIntrinsicValue(cx, cx->global(), srcName,
7125 args.rval());
7128 class ShellSourceHook : public SourceHook {
7129 // The function we should call to lazily retrieve source code.
7130 PersistentRootedFunction fun;
7132 public:
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);
7141 if (filename) {
7142 str = NewStringCopyUTF8(cx, filename);
7143 if (!str) {
7144 return false;
7146 } else {
7147 str = JS_GetEmptyString(cx);
7149 RootedValue filenameValue(cx, StringValue(str));
7151 RootedValue result(cx);
7152 if (!Call(cx, UndefinedHandleValue, fun, HandleValueArray(filenameValue),
7153 &result)) {
7154 return false;
7157 str = JS::ToString(cx, result);
7158 if (!str) {
7159 return false;
7162 Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
7163 if (!linear) {
7164 return false;
7167 if (twoByteSource) {
7168 *length = JS_GetStringLength(linear);
7170 *twoByteSource = cx->pod_malloc<char16_t>(*length);
7171 if (!*twoByteSource) {
7172 return false;
7175 CopyChars(*twoByteSource, *linear);
7176 } else {
7177 MOZ_ASSERT(utf8Source != nullptr);
7179 *length = JS::GetDeflatedUTF8StringLength(linear);
7181 *utf8Source = cx->pod_malloc<char>(*length);
7182 if (!*utf8Source) {
7183 return false;
7186 mozilla::DebugOnly<size_t> dstLen = JS::DeflateStringToUTF8Buffer(
7187 linear, mozilla::Span(*utf8Source, *length));
7188 MOZ_ASSERT(dstLen == *length);
7191 return true;
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.");
7201 return false;
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.");
7208 return false;
7211 mozilla::UniquePtr<ShellSourceHook> hook =
7212 mozilla::MakeUnique<ShellSourceHook>(cx,
7213 args[0].toObject().as<JSFunction>());
7214 if (!hook) {
7215 return false;
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));
7225 return result;
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();
7238 return true;
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()) {
7247 return;
7250 JS::ProfilingFrameIterator::RegisterState state;
7251 state.pc = pc;
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);
7265 # else
7266 # error "NYI: Single-step profiling support"
7267 # endif
7269 mozilla::DebugOnly<void*> lastStackAddress = nullptr;
7270 StackChars stack;
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);
7282 if (frameNo > 0) {
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");
7290 frameNo++;
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");
7304 #endif
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();
7314 return true;
7315 #else
7316 JS_ReportErrorASCII(cx, "single-step profiling not enabled on this platform");
7317 return false;
7318 #endif
7321 static bool DisableSingleStepProfiling(JSContext* cx, unsigned argc,
7322 Value* vp) {
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++) {
7333 JSString* stack =
7334 JS_NewUCStringCopyN(cx, sc->stacks[i].begin(), sc->stacks[i].length());
7335 if (!stack) {
7336 return false;
7338 if (!elems.append(StringValue(stack))) {
7339 return false;
7343 JSObject* array = JS::NewArrayObject(cx, elems);
7344 if (!array) {
7345 return false;
7348 sc->stacks.clear();
7349 args.rval().setObject(*array);
7350 return true;
7351 #else
7352 JS_ReportErrorASCII(cx, "single-step profiling not enabled on this platform");
7353 return false;
7354 #endif
7357 static bool IsLatin1(JSContext* cx, unsigned argc, Value* vp) {
7358 CallArgs args = CallArgsFromVp(argc, vp);
7359 bool isLatin1 =
7360 args.get(0).isString() && args[0].toString()->hasLatin1Chars();
7361 args.rval().setBoolean(isLatin1);
7362 return true;
7365 static bool EnableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp) {
7366 CallArgs args = CallArgsFromVp(argc, vp);
7368 if (!EnsureGeckoProfilingStackInstalled(cx, GetShellContext(cx))) {
7369 return false;
7372 cx->runtime()->geckoProfiler().enableSlowAssertions(false);
7373 cx->runtime()->geckoProfiler().enable(true);
7375 args.rval().setUndefined();
7376 return true;
7379 static bool EnableGeckoProfilingWithSlowAssertions(JSContext* cx, unsigned argc,
7380 Value* vp) {
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,
7386 // this is a no-op.
7387 if (cx->runtime()->geckoProfiler().slowAssertionsEnabled()) {
7388 return true;
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))) {
7397 return false;
7400 cx->runtime()->geckoProfiler().enableSlowAssertions(true);
7401 cx->runtime()->geckoProfiler().enable(true);
7403 return 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()) {
7411 return true;
7414 cx->runtime()->geckoProfiler().enable(false);
7415 return true;
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 {
7446 Empty,
7447 SharedArrayBuffer,
7448 WasmMemory,
7449 WasmModule,
7450 Number,
7453 struct SharedObjectMailbox {
7454 union Value {
7455 struct {
7456 SharedArrayRawBuffer* buffer;
7457 size_t length;
7458 bool isHugeMemory; // For a WasmMemory tag, otherwise false
7459 bool isGrowable; // For GrowableSharedArrayBuffer, otherwise false
7460 } sarb;
7461 JS::WasmModule* module;
7462 double number;
7464 Value() : number(0.0) {}
7467 MailboxTag tag = MailboxTag::Empty;
7468 Value val;
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();
7486 switch (mbx->tag) {
7487 case MailboxTag::Empty:
7488 case MailboxTag::Number:
7489 break;
7490 case MailboxTag::SharedArrayBuffer:
7491 case MailboxTag::WasmMemory:
7492 mbx->val.sarb.buffer->dropReference();
7493 break;
7494 case MailboxTag::WasmModule:
7495 mbx->val.module->Release();
7496 break;
7497 default:
7498 MOZ_CRASH();
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();
7512 switch (mbx->tag) {
7513 case MailboxTag::Empty: {
7514 break;
7516 case MailboxTag::Number: {
7517 args.rval().setNumber(mbx->val.number);
7518 return true;
7520 case MailboxTag::SharedArrayBuffer:
7521 case MailboxTag::WasmMemory: {
7522 // Flag was set in the sender; ensure it is set in the receiver.
7523 MOZ_ASSERT(
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);
7534 return false;
7537 // If the allocation fails we must decrement the refcount before
7538 // returning.
7540 Rooted<ArrayBufferObjectMaybeShared*> maybesab(cx);
7541 if (!mbx->val.sarb.isGrowable) {
7542 maybesab = SharedArrayBufferObject::New(cx, buf, length);
7543 } else {
7544 maybesab = SharedArrayBufferObject::NewGrowable(cx, buf, length);
7546 if (!maybesab) {
7547 buf->dropReference();
7548 return false;
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) {
7558 newObj = maybesab;
7559 } else {
7560 if (!GlobalObject::ensureConstructor(cx, cx->global(),
7561 JSProto_WebAssembly)) {
7562 return false;
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());
7569 if (!newObj) {
7570 return false;
7574 break;
7576 case MailboxTag::WasmModule: {
7577 // Flag was set in the sender; ensure it is set in the receiver.
7578 MOZ_ASSERT(
7579 cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
7581 if (!GlobalObject::ensureConstructor(cx, cx->global(),
7582 JSProto_WebAssembly)) {
7583 return false;
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);
7589 if (!newObj) {
7590 return false;
7592 break;
7594 default: {
7595 MOZ_CRASH();
7600 args.rval().setObjectOrNull(newObj);
7601 return true;
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");
7626 return false;
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>()
7634 .buffer()
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");
7645 return false;
7647 } else {
7648 JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject");
7649 return false;
7651 } else if (JS::IsWasmModuleObject(obj)) {
7652 tag = MailboxTag::WasmModule;
7653 value.module = JS::GetWasmModule(obj).forget().take();
7654 } else {
7655 JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject");
7656 return false;
7658 } else if (args.get(0).isNumber()) {
7659 tag = MailboxTag::Number;
7660 value.number = args.get(0).toNumber();
7661 // Nothing
7662 } else if (args.get(0).isNullOrUndefined()) {
7663 // Nothing
7664 } else {
7665 JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject");
7666 return false;
7670 auto mbx = sharedObjectMailbox->lock();
7672 switch (mbx->tag) {
7673 case MailboxTag::Empty:
7674 case MailboxTag::Number:
7675 break;
7676 case MailboxTag::SharedArrayBuffer:
7677 case MailboxTag::WasmMemory:
7678 mbx->val.sarb.buffer->dropReference();
7679 break;
7680 case MailboxTag::WasmModule:
7681 mbx->val.module->Release();
7682 break;
7683 default:
7684 MOZ_CRASH();
7687 mbx->tag = tag;
7688 mbx->val = value;
7691 args.rval().setUndefined();
7692 return true;
7695 typedef Vector<uint8_t, 0, SystemAllocPolicy> Uint8Vector;
7697 class StreamCacheEntry : public AtomicRefCounted<StreamCacheEntry>,
7698 public JS::OptimizedEncodingListener {
7699 typedef AtomicRefCounted<StreamCacheEntry> AtomicBase;
7701 Uint8Vector bytes_;
7702 ExclusiveData<Uint8Vector> optimized_;
7704 public:
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();
7713 return 1; // unused
7715 MozExternalRefCountType MOZ_XPCOM_ABI Release() override {
7716 AtomicBase::Release();
7717 return 0; // unused
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) {
7730 return;
7733 if (!dstBytes->resize(srcLength)) {
7734 return;
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>()) {
7760 return false;
7763 StreamCacheEntryObject& obj =
7764 args.thisv().toObject().as<StreamCacheEntryObject>();
7765 args.rval().setBoolean(obj.cache().hasOptimizedEncoding());
7766 return true;
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>()) {
7772 return false;
7775 auto& bytes =
7776 args.thisv().toObject().as<StreamCacheEntryObject>().cache().bytes();
7777 auto* buffer = ArrayBufferObject::createZeroed(cx, bytes.length());
7778 if (!buffer) {
7779 return false;
7782 memcpy(buffer->dataPointer(), bytes.begin(), bytes.length());
7784 args.rval().setObject(*buffer);
7785 return true;
7788 public:
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)) {
7796 return false;
7799 SharedMem<uint8_t*> ptr;
7800 size_t numBytes;
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");
7805 return false;
7808 Uint8Vector bytes;
7809 if (!bytes.resize(numBytes)) {
7810 return false;
7813 memcpy(bytes.begin(), ptr.unwrap(), numBytes);
7815 RefPtr<StreamCacheEntry> cache =
7816 cx->new_<StreamCacheEntry>(std::move(bytes));
7817 if (!cache) {
7818 return false;
7821 Rooted<NativeObject*> obj(
7822 cx, NewObjectWithGivenProto<StreamCacheEntryObject>(cx, nullptr));
7823 if (!obj) {
7824 return false;
7826 obj->initReservedSlot(CACHE_ENTRY_SLOT,
7827 PrivateValue(cache.forget().take()));
7829 if (!JS_DefineProperty(cx, obj, "cached", cachedGetter, nullptr, 0)) {
7830 return false;
7832 if (!JS_DefineFunction(cx, obj, "getBuffer", getBuffer, 0, 0)) {
7833 return false;
7836 args.rval().setObject(*obj);
7837 return true;
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
7850 nullptr, // resolve
7851 nullptr, // mayResolve
7852 StreamCacheEntryObject::finalize, // finalize
7853 nullptr, // call
7854 nullptr, // construct
7855 nullptr, // trace
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;
7866 Thread thread;
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;
7877 size_t delayMillis;
7878 size_t chunkSize;
7879 bool shutdown;
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;
7890 size_t byteLength;
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());
7898 goto done;
7901 bytes = cache.bytes().begin();
7902 byteLength = cache.bytes().length();
7903 listener = &cache;
7904 } else {
7905 bytes = job->source.as<Uint8Vector>().begin();
7906 byteLength = job->source.as<Uint8Vector>().length();
7907 listener = nullptr;
7910 size_t byteOffset;
7911 byteOffset = 0;
7912 while (true) {
7913 if (byteOffset == byteLength) {
7914 job->consumer->streamEnd(listener);
7915 break;
7918 bool shutdown;
7919 size_t delayMillis;
7920 size_t chunkSize;
7922 auto state = bufferStreamState->lock();
7923 shutdown = state->shutdown;
7924 delayMillis = state->delayMillis;
7925 chunkSize = state->chunkSize;
7928 if (shutdown) {
7929 job->consumer->streamError(JSMSG_STREAM_CONSUME_ERROR);
7930 break;
7933 ThisThread::SleepMilliseconds(delayMillis);
7935 chunkSize = std::min(chunkSize, byteLength - byteOffset);
7937 if (!job->consumer->consumeChunk(bytes + byteOffset, chunkSize)) {
7938 break;
7941 byteOffset += chunkSize;
7944 done:
7945 auto state = bufferStreamState->lock();
7946 size_t jobIndex = 0;
7947 while (state->jobs[jobIndex].get() != job) {
7948 jobIndex++;
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)) {
7962 return false;
7964 UniqueChars urlChars;
7965 if (url.isString()) {
7966 Rooted<JSString*> str(cx, url.toString());
7967 urlChars = JS_EncodeStringToUTF8(cx, str);
7968 if (!urlChars) {
7969 return false;
7973 RootedValue mapUrl(cx);
7974 if (!JS_GetProperty(cx, obj, "sourceMappingURL", &mapUrl)) {
7975 return false;
7977 UniqueChars mapUrlChars;
7978 if (mapUrl.isString()) {
7979 Rooted<JSString*> str(cx, mapUrl.toString());
7980 mapUrlChars = JS_EncodeStringToUTF8(cx, str);
7981 if (!mapUrlChars) {
7982 return false;
7986 consumer->noteResponseURLs(urlChars.get(), mapUrlChars.get());
7989 UniquePtr<BufferStreamJob> job;
7991 SharedMem<uint8_t*> dataPointer;
7992 size_t byteLength;
7993 if (IsBufferSource(obj, &dataPointer, &byteLength)) {
7994 Uint8Vector bytes;
7995 if (!bytes.resize(byteLength)) {
7996 JS_ReportOutOfMemory(cx);
7997 return false;
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);
8005 } else {
8006 JS_ReportErrorASCII(
8008 "shell streaming consumes a buffer source (buffer or view) "
8009 "or StreamCacheEntryObject");
8010 return false;
8012 if (!job) {
8013 return false;
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);
8023 return false;
8028 AutoEnterOOMUnsafeRegion oomUnsafe;
8029 if (!jobPtr->thread.init(BufferStreamMain, jobPtr)) {
8030 oomUnsafe.crash("ConsumeBufferSource");
8034 return true;
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)) {
8044 return false;
8047 double delayMillis;
8048 if (!ToNumber(cx, args[0], &delayMillis)) {
8049 return false;
8052 double chunkSize;
8053 if (!ToNumber(cx, args[1], &chunkSize)) {
8054 return false;
8058 auto state = bufferStreamState->lock();
8059 state->delayMillis = delayMillis;
8060 state->chunkSize = chunkSize;
8063 args.rval().setUndefined();
8064 return true;
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.");
8083 return false;
8086 if (args.length() != 1) {
8087 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8088 return false;
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");
8096 return false;
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");
8107 return false;
8109 script = JSFunction::getOrCreateScript(cx, fun);
8110 if (!script) {
8111 return false;
8113 } else {
8114 script = obj->as<ShellModuleObjectWrapper>().get()->maybeScript();
8115 if (!script) {
8116 JS_ReportErrorASCII(cx, "module does not have an associated script");
8117 return false;
8121 script->bodyScope()->dump();
8123 args.rval().setUndefined();
8124 return true;
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
8129 // subsequent GCs.
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,
8146 Value* vp) {
8147 CallArgs args = CallArgsFromVp(argc, vp);
8149 auto priv = EnsureShellCompartmentPrivate(cx);
8150 if (!priv) {
8151 return false;
8154 GCPtr<ArrayObject*>& root =
8155 (color == gc::MarkColor::Black) ? priv->blackRoot : priv->grayRoot;
8157 if (!root && !(root = NewTenuredDenseEmptyArray(cx))) {
8158 return false;
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);
8166 return true;
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) {
8180 auto* observers =
8181 cx->new_<MarkBitObservers>(cx->runtime(), NonshrinkingGCObjectVector());
8182 if (!observers) {
8183 return nullptr;
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) {
8195 return false;
8198 markObservers->get().clear();
8200 args.rval().setUndefined();
8201 return true;
8204 static bool AddMarkObservers(JSContext* cx, unsigned argc, Value* vp) {
8205 CallArgs args = CallArgsFromVp(argc, vp);
8207 auto markObservers = EnsureMarkBitObservers(cx);
8208 if (!markObservers) {
8209 return false;
8212 if (!args.get(0).isObject()) {
8213 JS_ReportErrorASCII(cx, "argument must be an Array of objects");
8214 return false;
8217 RootedObject observersArg(cx, &args[0].toObject());
8218 uint64_t length;
8219 if (!GetLengthProperty(cx, observersArg, &length)) {
8220 return false;
8223 if (length > UINT32_MAX) {
8224 JS_ReportErrorASCII(cx, "Invalid length for observers array");
8225 return false;
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)) {
8232 return false;
8235 if (!value.isObject()) {
8236 JS_ReportErrorASCII(cx, "argument must be an Array of objects");
8237 return false;
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)) {
8251 return false;
8255 args.rval().setInt32(length);
8256 return true;
8259 static bool GetMarks(JSContext* cx, unsigned argc, Value* vp) {
8260 CallArgs args = CallArgsFromVp(argc, vp);
8262 auto& observers = GetShellContext(cx)->markObservers;
8263 if (!observers) {
8264 args.rval().setUndefined();
8265 return true;
8268 size_t length = observers->get().length();
8269 Rooted<ArrayObject*> ret(cx, js::NewDenseEmptyArray(cx));
8270 if (!ret) {
8271 return false;
8274 for (uint32_t i = 0; i < length; i++) {
8275 const char* color;
8276 JSObject* obj = observers->get()[i];
8277 if (!obj) {
8278 color = "dead";
8279 } else if (obj->zone()->isGCPreparing()) {
8280 color = "unmarked";
8281 } else {
8282 gc::TenuredCell* cell = &obj->asTenured();
8283 if (cell->isMarkedGray()) {
8284 color = "gray";
8285 } else if (cell->isMarkedBlack()) {
8286 color = "black";
8287 } else {
8288 color = "unmarked";
8291 JSString* s = JS_NewStringCopyZ(cx, color);
8292 if (!s) {
8293 return false;
8295 if (!NewbornArrayPush(cx, ret, StringValue(s))) {
8296 return false;
8300 args.rval().setObject(*ret);
8301 return true;
8304 namespace js {
8305 namespace shell {
8307 class ShellAutoEntryMonitor : JS::dbg::AutoEntryMonitor {
8308 Vector<UniqueChars, 1, js::SystemAllocPolicy> log;
8309 bool oom;
8310 bool enteredWithoutExit;
8312 public:
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));
8324 if (displayId) {
8325 UniqueChars displayIdStr = JS_EncodeStringToUTF8(cx, displayId);
8326 if (!displayIdStr) {
8327 // We report OOM in buildResult.
8328 cx->recoverFromOutOfMemory();
8329 oom = true;
8330 return;
8332 oom = !log.append(std::move(displayIdStr));
8333 return;
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) {
8354 if (oom) {
8355 JS_ReportOutOfMemory(cx);
8356 return false;
8359 RootedObject result(cx, JS::NewArrayObject(cx, log.length()));
8360 if (!result) {
8361 return false;
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)));
8367 if (!string) {
8368 return false;
8370 RootedValue value(cx, StringValue(string));
8371 if (!JS_SetElement(cx, result, i, value)) {
8372 return false;
8376 resultValue.setObject(*result.get());
8377 return true;
8381 } // namespace shell
8382 } // namespace js
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");
8389 return false;
8392 RootedObject opts(cx, ToObject(cx, args[0]));
8393 if (!opts) {
8394 return false;
8397 // { function: f } --- Call f.
8399 RootedValue fun(cx), dummy(cx);
8401 if (!JS_GetProperty(cx, opts, "function", &fun)) {
8402 return false;
8404 if (!fun.isUndefined()) {
8405 js::shell::ShellAutoEntryMonitor sarep(cx);
8406 if (!Call(cx, UndefinedHandleValue, fun, JS::HandleValueArray::empty(),
8407 &dummy)) {
8408 return false;
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))
8421 return false;
8422 if (!objectv.isUndefined() && !propv.isUndefined()) {
8423 RootedObject object(cx, ToObject(cx, objectv));
8424 if (!object) {
8425 return false;
8428 RootedString string(cx, ToString(cx, propv));
8429 if (!string) {
8430 return false;
8432 RootedId id(cx);
8433 if (!JS_StringToId(cx, string, &id)) {
8434 return false;
8437 if (!JS_GetProperty(cx, opts, "value", &valuev)) {
8438 return false;
8441 js::shell::ShellAutoEntryMonitor sarep(cx);
8443 if (!valuev.isUndefined()) {
8444 if (!JS_SetPropertyById(cx, object, id, valuev)) {
8445 return false;
8447 } else {
8448 if (!JS_GetPropertyById(cx, object, id, &valuev)) {
8449 return false;
8453 return sarep.buildResult(cx, args.rval());
8457 // { ToString: v } --- Apply JS::ToString to v.
8459 RootedValue v(cx);
8461 if (!JS_GetProperty(cx, opts, "ToString", &v)) {
8462 return false;
8464 if (!v.isUndefined()) {
8465 js::shell::ShellAutoEntryMonitor sarep(cx);
8466 if (!JS::ToString(cx, v)) {
8467 return false;
8469 return sarep.buildResult(cx, args.rval());
8473 // { ToNumber: v } --- Apply JS::ToNumber to v.
8475 RootedValue v(cx);
8476 double dummy;
8478 if (!JS_GetProperty(cx, opts, "ToNumber", &v)) {
8479 return false;
8481 if (!v.isUndefined()) {
8482 js::shell::ShellAutoEntryMonitor sarep(cx);
8483 if (!JS::ToNumber(cx, v, &dummy)) {
8484 return false;
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)) {
8495 return false;
8497 if (!code.isUndefined()) {
8498 RootedString codeString(cx, ToString(cx, code));
8499 if (!codeString) {
8500 return false;
8503 AutoStableStringChars linearChars(cx);
8504 if (!linearChars.initTwoByte(cx, codeString)) {
8505 return false;
8507 JS::SourceText<char16_t> srcBuf;
8508 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
8509 return false;
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)) {
8518 return false;
8520 return sarep.buildResult(cx, args.rval());
8524 JS_ReportErrorASCII(cx, "bad 'params' object");
8525 return false;
8528 #ifndef __wasi__
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)) {
8534 return false;
8537 if (!args[0].isString()) {
8538 ReportUsageErrorASCII(cx, callee, "First argument must be a String");
8539 return false;
8542 size_t textLen = args[0].toString()->length();
8544 AutoStableStringChars twoByteChars(cx);
8545 if (!twoByteChars.initTwoByte(cx, args[0].toString())) {
8546 return false;
8549 wasm::Bytes bytes;
8550 UniqueChars error;
8551 if (!wasm::TextToBinary(twoByteChars.twoByteChars(), textLen, &bytes,
8552 &error)) {
8553 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
8554 error.get() ? error.get() : "out of memory");
8555 return false;
8558 RootedObject binary(cx, JS_NewUint8Array(cx, bytes.length()));
8559 if (!binary) {
8560 return false;
8563 memcpy(binary->as<TypedArrayObject>().dataPointerUnshared(), bytes.begin(),
8564 bytes.length());
8566 args.rval().setObject(*binary);
8567 return true;
8570 # ifndef __AFL_HAVE_MANUAL_CONTROL
8571 # define __AFL_LOOP(x) true
8572 # endif
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");
8580 return false;
8583 if (!args[0].isString()) {
8584 ReportUsageErrorASCII(cx, callee, "First argument must be a String");
8585 return false;
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");
8593 return false;
8595 importObj = &args[1].toObject();
8598 RootedString givenPath(cx, args[0].toString());
8599 RootedString filename(cx, ResolvePath(cx, givenPath, RootRelative));
8600 if (!filename) {
8601 return false;
8604 while (__AFL_LOOP(1000)) {
8605 Rooted<JSObject*> ret(cx, FileAsTypedArray(cx, filename));
8606 if (!ret) {
8607 return false;
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
8619 return true;
8620 # endif
8622 #endif // __wasi__
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 {
8640 public:
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()) {
8658 return nullptr;
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);
8669 if (v.isObject()) {
8670 return &v.toObject();
8672 MOZ_ASSERT(v.isUndefined());
8674 JSObject* expando = JS_NewObjectWithGivenProto(cx, nullptr, nullptr);
8675 if (!expando) {
8676 return nullptr;
8678 SetProxyPrivate(obj, ObjectValue(*expando));
8679 return 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");
8696 return false;
8699 // |newGlobal| needs to be a GlobalObject.
8700 RootedObject newGlobal(
8701 cx, js::CheckedUnwrapDynamic(&args[0].toObject(), cx,
8702 /* stopAtWindowProxy = */ false));
8703 if (!newGlobal) {
8704 ReportAccessDenied(cx);
8705 return false;
8707 if (!JS_IsGlobalObject(newGlobal)) {
8708 JS_ReportErrorNumberASCII(
8709 cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
8710 "\"global\" passed to transplant()", "not a global object");
8711 return false;
8714 const Value& reserved =
8715 GetFunctionNativeReserved(callee, TransplantSourceObject);
8716 RootedObject source(cx, CheckedUnwrapStatic(&reserved.toObject()));
8717 if (!source) {
8718 ReportAccessDenied(cx);
8719 return false;
8721 if (JS_IsDeadWrapper(source)) {
8722 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
8723 return false;
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)) {
8744 return false;
8747 bool isProxy = IsProxy(source);
8748 RootedObject expandoObject(cx);
8749 if (isProxy) {
8750 expandoObject =
8751 TransplantableDOMProxyHandler::GetAndClearExpandoObject(source);
8754 JSAutoRealm ar(cx, newGlobal);
8756 RootedObject proto(cx);
8757 if (JS::GetClass(source) == GetDomClass()) {
8758 proto = GetDOMPrototype(cx, newGlobal);
8759 } else {
8760 proto = JS::GetRealmObjectPrototype(cx);
8762 if (!proto) {
8763 return false;
8766 RootedObject target(cx, JS_CloneObject(cx, source, proto));
8767 if (!target) {
8768 return false;
8771 if (JS::GetCompartment(source) != JS::GetCompartment(target) &&
8772 !AllowNewWrapper(JS::GetCompartment(source), target)) {
8773 JS_ReportErrorASCII(cx, "Cannot transplant into nuked compartment");
8774 return false;
8777 RootedObject copyFrom(cx, isProxy ? expandoObject : source);
8778 RootedObject propertyHolder(cx,
8779 JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
8780 if (!propertyHolder) {
8781 return false;
8784 if (!JS_CopyOwnPropertiesAndPrivateFields(cx, propertyHolder, copyFrom)) {
8785 return false;
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);
8798 if (!source) {
8799 return false;
8802 RootedObject copyTo(cx);
8803 if (isProxy) {
8804 copyTo = TransplantableDOMProxyHandler::EnsureExpandoObject(cx, source);
8805 if (!copyTo) {
8806 return false;
8808 } else {
8809 copyTo = source;
8811 if (!JS_CopyOwnPropertiesAndPrivateFields(cx, copyTo, propertyHolder)) {
8812 return false;
8815 args.rval().setUndefined();
8816 return true;
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");
8825 return false;
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");
8833 return false;
8836 RootedObject options(cx, &args[0].toObject());
8837 RootedValue value(cx);
8839 if (!JS_GetProperty(cx, options, "proxy", &value)) {
8840 return false;
8842 createProxy = JS::ToBoolean(value);
8844 if (!JS_GetProperty(cx, options, "object", &value)) {
8845 return false;
8847 if (!value.isUndefined()) {
8848 if (!value.isObject()) {
8849 ReportUsageErrorASCII(cx, callee, "'object' option must be an object");
8850 return false;
8853 source = &value.toObject();
8854 if (JS::GetClass(source) != GetDomClass()) {
8855 ReportUsageErrorASCII(cx, callee, "Object not a FakeDOMObject");
8856 return false;
8859 // |source| must be a tenured object to be transplantable.
8860 if (gc::IsInsideNursery(source)) {
8861 JS_GC(cx);
8863 MOZ_ASSERT(!gc::IsInsideNursery(source),
8864 "Live objects should be tenured after one GC, because "
8865 "the nursery has only a single generation");
8870 if (!source) {
8871 if (!createProxy) {
8872 source = NewBuiltinClassInstance(cx, &TransplantableDOMObjectClass,
8873 TenuredObject);
8874 if (!source) {
8875 return false;
8878 JS::SetReservedSlot(source, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
8879 } else {
8880 JSObject* expando = JS_NewPlainObject(cx);
8881 if (!expando) {
8882 return false;
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);
8892 if (!source) {
8893 return false;
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));
8903 if (!transplant) {
8904 return false;
8907 SetFunctionNativeReserved(transplant, TransplantSourceObject,
8908 ObjectValue(*source));
8910 RootedObject result(cx, JS_NewPlainObject(cx));
8911 if (!result) {
8912 return false;
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)) {
8919 return false;
8922 args.rval().setObject(*result);
8923 return true;
8926 #ifdef DEBUG
8927 static bool DebugGetQueuedJobs(JSContext* cx, unsigned argc, Value* vp) {
8928 CallArgs args = CallArgsFromVp(argc, vp);
8930 JSObject* jobs = js::GetJobsInInternalJobQueue(cx);
8931 if (!jobs) {
8932 return false;
8935 args.rval().setObject(*jobs);
8936 return true;
8938 #endif
8940 #ifdef FUZZING_INTERFACES
8941 extern "C" {
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");
8951 return false;
8954 if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) {
8955 ReportUsageErrorASCII(cx, callee, "Argument must be ArrayBuffer.");
8956 return false;
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);
8967 if (!outSize) {
8968 JS_ReportErrorASCII(cx, "Generated module is too large.");
8969 return false;
8972 JS::Rooted<JSObject*> outArr(cx, JS_NewUint8ClampedArray(cx, outSize));
8973 if (!outArr) {
8974 return false;
8978 JS::AutoCheckCannotGC nogc;
8979 bool isShared;
8980 uint8_t* data = JS_GetUint8ClampedArrayData(outArr, &isShared, nogc);
8981 MOZ_RELEASE_ASSERT(!isShared);
8982 memcpy(data, tmp, outSize);
8985 args.rval().setObject(*outArr);
8986 return true;
8989 #endif
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");
8997 return false;
9000 JS::Rooted<JSLinearString*> input(cx, args[0].toString()->ensureLinear(cx));
9001 if (!input) {
9002 return false;
9005 bool result;
9006 if (input->hasLatin1Chars()) {
9007 JS::AutoCheckCannotGC nogc;
9008 result = JS::IsValidJSON(input->latin1Chars(nogc), input->length());
9009 } else {
9010 JS::AutoCheckCannotGC nogc;
9011 result = JS::IsValidJSON(input->twoByteChars(nogc), input->length());
9014 args.rval().setBoolean(result);
9015 return true;
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");
9031 return false;
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);
9039 return false;
9042 // Create a buffer big enough for the header and the max amount of compressed
9043 // bytes.
9044 size_t outputCapacity =
9045 LZ4HeaderSize + mozilla::Compression::LZ4::maxCompressedSize(byteLength);
9047 mozilla::UniquePtr<void, JS::FreePolicy> output(js_malloc(outputCapacity));
9048 if (!output) {
9049 ReportOutOfMemory(cx);
9050 return false;
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) {
9067 return false;
9070 args.rval().setObject(*outputArrayBuffer);
9071 return true;
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");
9081 return false;
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");
9089 return false;
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");
9097 return false;
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);
9105 return false;
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");
9119 return false;
9122 // Create an ArrayBuffer wrapping the decompressed bytes
9123 JSObject* outputArrayBuffer = NewArrayBufferWithContents(
9124 cx, decompressedBytesLength, std::move(decompressedBytes));
9125 if (!outputArrayBuffer) {
9126 return false;
9129 args.rval().setObject(*outputArrayBuffer);
9130 return true;
9133 // clang-format off
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"
9186 " transcode.\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,
9197 "run('foo.js')",
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,
9202 "readline()",
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,
9212 "print([exp ...])",
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,
9220 "putstr([exp])",
9221 " Evaluate and print expression without newline."),
9223 JS_FN_HELP("dateNow", Now, 0, 0,
9224 "dateNow()",
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,
9232 "quit()",
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,
9249 "throwError()",
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"
9255 " to an object."),
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,
9267 "dis([fun/code])",
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,
9279 "notes([fun])",
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."),
9287 #endif
9289 JS_FN_HELP("getslx", GetSLX, 1, 0,
9290 "getslx(obj)",
9291 " Get script line extent."),
9293 JS_FN_HELP("evalcx", EvalInContext, 1, 0,
9294 "evalcx(s[, o])",
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,
9324 "shapeOf(obj)",
9325 " Get the shape of obj (an implementation detail)."),
9327 #ifdef DEBUG
9328 JS_FN_HELP("arrayInfo", ArrayInfo, 1, 0,
9329 "arrayInfo(a1, a2, ...)",
9330 " Report statistics about arrays."),
9331 #endif
9333 JS_FN_HELP("sleep", Sleep_fn, 1, 0,
9334 "sleep(dt)",
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,
9356 "clearModules()",
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"
9379 " compiled:\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"
9383 " be used."),
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"),
9467 #ifdef DEBUG
9468 JS_FN_HELP("interruptRegexp", InterruptRegexp, 2, 0,
9469 "interruptRegexp(<regexp>, <string>)",
9470 " Interrrupt the execution of regular expression.\n"),
9471 #endif
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,
9485 "getLastWarning()",
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,
9493 "elapsed()",
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,
9501 "decompileThis()",
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,
9509 "thisFilename()",
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,
9547 "nukeAllCCWs()",
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,
9587 "getMaxArgs()",
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,
9601 "cacheEntry(code)",
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"
9623 " are discarded."),
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,
9644 "isLatin1(s)",
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"
9658 "\n"
9659 "{ function }: Call the object |params.function| with no arguments.\n"
9660 "\n"
9661 "{ object, property }: Fetch the property named |params.property| of\n"
9662 "|params.object|.\n"
9663 "\n"
9664 "{ ToString }: Apply JS::ToString to |params.toString|.\n"
9665 "\n"
9666 "{ ToNumber }: Apply JS::ToNumber to |params.toNumber|.\n"
9667 "\n"
9668 "{ eval }: Apply JS::Evaluate to |params.eval|.\n"
9669 "\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"
9674 "similar.\n"),
9676 JS_FN_HELP("enqueueJob", EnqueueJob, 1, 0,
9677 "enqueueJob(fn)",
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,
9686 "drainJobQueue()",
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,
9700 "blackRoot()",
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,
9705 "grayRoot()",
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,
9721 "getMarks()",
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"
9734 "\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"
9739 "\n"
9740 " cause: A string, supplied as the async cause on the top frame of\n"
9741 " captured async stacks.\n"
9742 "\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"
9755 "underneath you."),
9756 #endif // JS_HAS_INTL_API
9758 #ifndef __wasi__
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."),
9768 #endif // __wasi__
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"
9777 "\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"
9781 " FakeDOMObject."),
9783 JS_FN_HELP("cpuNow", CpuNow, /* nargs= */ 0, /* flags = */ 0,
9784 "cpuNow()",
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."),
9792 #endif
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."),
9798 #endif
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."),
9812 JS_FS_HELP_END
9814 // clang-format on
9816 // clang-format off
9817 #ifdef FUZZING_JS_FUZZILLI
9818 static const JSFunctionSpec shell_function_fuzzilli_hash[] = {
9819 JS_INLINABLE_FN("fuzzilli_hash", fuzzilli_hash, 1, 0, FuzzilliHash),
9820 JS_FS_END
9822 #endif
9823 // clang-format on
9825 // clang-format off
9826 static const JSFunctionSpecWithHelp diff_testing_unsafe_functions[] = {
9828 JS_FS_HELP_END
9830 // clang-format on
9832 // clang-format off
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"
9863 "\n"
9864 " Notes:\n"
9865 "\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"
9868 "\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.)"),
9885 #ifndef __wasi__
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."),
9890 #endif // __wasi__
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."),
9901 #endif
9903 #ifdef DEBUG
9904 JS_FN_HELP("debugGetQueuedJobs", DebugGetQueuedJobs, 0, 0,
9905 "debugGetQueuedJobs()",
9906 " Returns an array of queued jobs."),
9907 #endif
9909 JS_FS_HELP_END
9911 // clang-format on
9913 // clang-format off
9914 static const JSFunctionSpecWithHelp performance_functions[] = {
9915 JS_FN_HELP("now", Now, 0, 0,
9916 "now()",
9917 " Return the current time with sub-ms precision.\n"
9918 " This function is an alias of the dateNow() function."),
9919 JS_FS_HELP_END
9921 // clang-format on
9923 // clang-format off
9924 static const JSFunctionSpecWithHelp console_functions[] = {
9925 JS_FN_HELP("log", Print, 0, 0,
9926 "log([exp ...])",
9927 " Evaluate and print expressions to stdout.\n"
9928 " This function is an alias of the print() function."),
9929 JS_FS_HELP_END
9931 // clang-format on
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
9943 # else
9944 # define CALLGRIND_FUNCTION_COUNT 0
9945 # endif
9946 # ifdef MOZ_VTUNE
9947 # define VTUNE_FUNCTION_COUNT 4
9948 # else
9949 # define VTUNE_FUNCTION_COUNT 0
9950 # endif
9951 # define EXTERNAL_FUNCTION_COUNT \
9952 (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT)
9953 #else
9954 # define EXTERNAL_FUNCTION_COUNT 0
9955 #endif
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);
9967 if (!bytes) {
9968 return false;
9971 fprintf(gOutFile->fp, "%s\n", bytes.get());
9972 return true;
9975 static bool PrintHelp(JSContext* cx, HandleObject obj) {
9976 RootedValue usage(cx);
9977 if (!JS_GetProperty(cx, obj, "usage", &usage)) {
9978 return false;
9980 RootedValue help(cx);
9981 if (!JS_GetProperty(cx, obj, "help", &help)) {
9982 return false;
9985 if (!usage.isString() || !help.isString()) {
9986 return true;
9989 return PrintHelpString(cx, usage) && PrintHelpString(cx, help);
9992 struct ExtraGlobalBindingWithHelp {
9993 const char* name;
9994 const char* help;
9997 // clang-format off
9998 static ExtraGlobalBindingWithHelp extraGlobalBindingsWithHelp[] = {
9999 // Defined in BindScriptArgs.
10001 "scriptArgs",
10002 " An array containing the command line arguments passed after the path\n"
10003 " to a JS script."},
10005 "scriptPath",
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.
10011 "console",
10012 " An object with console.log() which aliases print()."},
10014 // Defined in NewGlobalObject.
10016 "performance",
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"
10022 " gcBytes\n"
10023 " gcMaxBytes\n"
10024 " mallocBytes\n"
10025 " gcIsHighFrequencyMode\n"
10026 " gcNumber\n"
10027 " majorGCCount\n"
10028 " minorGCCount\n"
10029 " sliceCount\n"
10030 " compartmentCount\n"
10031 " lastStartReason\n"
10032 " zone.gcBytes\n"
10033 " zone.gcTriggerBytes\n"
10034 " zone.gcAllocTrigger\n"
10035 " zone.mallocBytes\n"
10036 " zone.mallocTriggerBytes\n"
10037 " zone.gcNumber"},
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"},
10051 // clang-format on
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)) {
10057 return false;
10060 // Execute the regular expression in |regex|'s compartment.
10061 JSAutoRealm ar(cx, regex);
10062 if (!cx->compartment()->wrap(cx, &linearInputStr)) {
10063 return false;
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)) {
10069 return false;
10071 *result = !v.isNull();
10072 return true;
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)) {
10079 return false;
10082 Rooted<RegExpObject*> regex(cx);
10083 if (pattern) {
10084 regex = &UncheckedUnwrap(pattern)->as<RegExpObject>();
10087 for (size_t i = 0; i < idv.length(); i++) {
10088 RootedValue v(cx);
10089 RootedId id(cx, idv[i]);
10090 if (!JS_GetPropertyById(cx, obj, id, &v)) {
10091 return false;
10093 if (!v.isObject()) {
10094 continue;
10097 RootedObject funcObj(cx, &v.toObject());
10098 if (regex) {
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)) {
10102 return false;
10104 if (!v.isString()) {
10105 continue;
10108 // For functions, match against the name. For interface objects,
10109 // match against the usage string.
10110 if (!JS_GetProperty(cx, funcObj, "name", &v)) {
10111 return false;
10113 if (!v.isString()) {
10114 if (!JS_GetProperty(cx, funcObj, "usage", &v)) {
10115 return false;
10117 if (!v.isString()) {
10118 continue;
10122 Rooted<JSString*> inputStr(cx, v.toString());
10123 bool result = false;
10124 if (!MatchPattern(cx, regex, inputStr, &result)) {
10125 return false;
10127 if (!result) {
10128 continue;
10132 if (!PrintHelp(cx, funcObj)) {
10133 return false;
10137 return true;
10140 static bool PrintExtraGlobalEnumeratedHelp(JSContext* cx, HandleObject pattern,
10141 bool brief) {
10142 Rooted<RegExpObject*> regex(cx);
10143 if (pattern) {
10144 regex = &UncheckedUnwrap(pattern)->as<RegExpObject>();
10147 for (const auto& item : extraGlobalBindingsWithHelp) {
10148 if (regex) {
10149 JS::Rooted<JSString*> name(cx, JS_NewStringCopyZ(cx, item.name));
10150 if (!name) {
10151 return false;
10154 bool result = false;
10155 if (!MatchPattern(cx, regex, name, &result)) {
10156 return false;
10158 if (!result) {
10159 continue;
10162 fprintf(gOutFile->fp, "%s\n", item.name);
10163 fprintf(gOutFile->fp, "%s\n", item.help);
10166 return true;
10169 static bool Help(JSContext* cx, unsigned argc, Value* vp) {
10170 if (!gOutFile->isOpen()) {
10171 JS_ReportErrorASCII(cx, "output file is closed");
10172 return false;
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
10180 // global.
10181 if (args.length() == 0) {
10182 fprintf(gOutFile->fp, "%s\n", JS_GetImplementationVersion());
10184 if (!PrintEnumeratedHelp(cx, global, nullptr, false)) {
10185 return false;
10187 if (!PrintExtraGlobalEnumeratedHelp(cx, nullptr, false)) {
10188 return false;
10190 return true;
10193 RootedValue v(cx);
10195 if (args[0].isPrimitive()) {
10196 // help("foo")
10197 JS_ReportErrorASCII(cx, "primitive arg");
10198 return false;
10201 RootedObject obj(cx, &args[0].toObject());
10202 if (!obj) {
10203 return true;
10205 bool isRegexp;
10206 if (!JS::ObjectIsRegExp(cx, obj, &isRegexp)) {
10207 return false;
10210 if (isRegexp) {
10211 // help(/pattern/)
10212 if (!PrintEnumeratedHelp(cx, global, obj, false)) {
10213 return false;
10215 if (!PrintExtraGlobalEnumeratedHelp(cx, obj, false)) {
10216 return false;
10218 return true;
10221 // help(function)
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"
10230 #undef MSG_DEF
10233 const JSErrorFormatString* js::shell::my_GetErrorMessage(
10234 void* userRef, const unsigned errorNumber) {
10235 if (errorNumber == 0 || errorNumber >= JSShellErr_Limit) {
10236 return nullptr;
10239 return &jsShell_ErrorFormatString[errorNumber];
10242 static bool CreateLastWarningObject(JSContext* cx, JSErrorReport* report) {
10243 RootedObject warningObj(cx, JS_NewObject(cx, nullptr));
10244 if (!warningObj) {
10245 return false;
10248 if (!CopyErrorReportToObject(cx, report, warningObj)) {
10249 return false;
10252 GetShellContext(cx)->lastWarning.setObject(*warningObj);
10253 return true;
10256 static FILE* ErrorFilePointer() {
10257 if (gErrFile->isOpen()) {
10258 return gErrFile->fp;
10261 fprintf(stderr, "error file is closed; falling back to stderr\n");
10262 return stderr;
10265 bool shell::PrintStackTrace(JSContext* cx, HandleObject stackObj) {
10266 if (!stackObj || !stackObj->is<SavedFrame>()) {
10267 return true;
10270 JSPrincipals* principals = stackObj->nonCCWRealm()->principals();
10271 RootedString stackStr(cx);
10272 if (!BuildStackString(cx, principals, stackObj, &stackStr, 2)) {
10273 return false;
10276 UniqueChars stack = JS_EncodeStringToUTF8(cx, stackStr);
10277 if (!stack) {
10278 return false;
10281 FILE* fp = ErrorFilePointer();
10282 fputs("Stack:\n", fp);
10283 fputs(stack.get(), fp);
10285 return true;
10288 js::shell::AutoReportException::~AutoReportException() {
10289 if (!JS_IsExceptionPending(cx)) {
10290 return;
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");
10297 fflush(stderr);
10298 JS_ClearPendingException(cx);
10299 return false;
10302 MOZ_ASSERT(!report.report()->isWarning());
10304 FILE* fp = ErrorFilePointer();
10305 if (prefix) {
10306 fputs(prefix, fp);
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);
10326 return true;
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");
10333 fflush(stderr);
10334 JS_ClearPendingException(cx);
10335 return;
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.
10342 return;
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
10368 // testing.
10369 if (cx->runningOOMTest) {
10370 return;
10372 #endif
10374 if (report.report()->errorNumber == JSMSG_OUT_OF_MEMORY) {
10375 sc->exitCode = EXITCODE_OUT_OF_MEMORY;
10376 } else {
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",
10391 fp);
10392 fflush(fp);
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);
10406 #else
10407 return true;
10408 #endif
10411 static bool global_resolve(JSContext* cx, HandleObject obj, HandleId id,
10412 bool* resolvedp) {
10413 #ifdef LAZY_STANDARD_CLASSES
10414 if (!JS_ResolveStandardClass(cx, obj, id, resolvedp)) {
10415 return false;
10417 #endif
10418 return true;
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
10434 nullptr, // call
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 = {
10443 "global",
10444 JSCLASS_GLOBAL_FLAGS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS),
10445 &global_classOps};
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)));
10467 return true;
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);
10474 return true;
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);
10485 return true;
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()));
10497 return true;
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");
10510 return false;
10513 return true;
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());
10524 return true;
10527 static const JSJitInfo dom_x_getterinfo = {
10528 {(JSJitGetterOp)dom_get_x},
10529 {0}, /* protoID */
10530 {0}, /* depth */
10531 JSJitInfo::Getter,
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 */
10540 0 /* slotIndex */
10543 static const JSJitInfo dom_x_setterinfo = {
10544 {(JSJitGetterOp)dom_set_x},
10545 {0}, /* protoID */
10546 {0}, /* depth */
10547 JSJitInfo::Setter,
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 */
10556 0 /* slotIndex */
10559 static const JSJitInfo dom_slot_getterinfo = {
10560 {(JSJitGetterOp)dom_get_slot},
10561 {0}, /* protoID */
10562 {0}, /* depth */
10563 JSJitInfo::Getter,
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},
10580 {0}, /* protoID */
10581 {0}, /* depth */
10582 JSJitInfo::Getter,
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 */
10591 0 /* slotIndex */
10594 static const JSJitInfo dom_global_setterinfo = {
10595 {(JSJitGetterOp)dom_set_global},
10596 {0}, /* protoID */
10597 {0}, /* depth */
10598 JSJitInfo::Setter,
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 */
10607 0 /* slotIndex */
10610 static const JSJitInfo doFoo_methodinfo = {
10611 {(JSJitGetterOp)dom_doFoo},
10612 {0}, /* protoID */
10613 {0}, /* depth */
10614 JSJitInfo::Method,
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 */
10623 0 /* slotIndex */
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),
10635 JS_PS_END};
10637 static const JSFunctionSpec dom_methods[] = {
10638 JS_FNINFO("doFoo", dom_genericMethod, &doFoo_methodinfo, 3,
10639 JSPROP_ENUMERATE),
10640 JS_FS_END};
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();
10652 return true;
10655 RootedObject obj(cx, &args.thisv().toObject());
10656 if (JS::GetClass(obj) != &dom_class) {
10657 args.rval().set(UndefinedValue());
10658 return true;
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();
10674 return true;
10677 RootedObject obj(cx, &args.thisv().toObject());
10678 if (JS::GetClass(obj) != &dom_class) {
10679 args.rval().set(UndefinedValue());
10680 return true;
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))) {
10689 return false;
10691 args.rval().set(UndefinedValue());
10692 return true;
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();
10700 return true;
10703 RootedObject obj(cx, &args.thisv().toObject());
10704 if (JS::GetClass(obj) != &dom_class) {
10705 args.rval().set(UndefinedValue());
10706 return true;
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");
10727 return nullptr;
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)) {
10741 return false;
10744 if (!protov.isObject()) {
10745 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_PROTOTYPE,
10746 "FakeDOMObject");
10747 return false;
10750 RootedObject proto(cx, &protov.toObject());
10751 RootedObject domObj(cx, JS_NewObjectWithGivenProto(cx, &dom_class, proto));
10752 if (!domObj) {
10753 return false;
10756 InitDOMObject(domObj);
10758 args.rval().setObject(*domObj);
10759 return true;
10762 static bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID,
10763 uint32_t depth) {
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);
10784 return true;
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));
10796 if (!glob) {
10797 return nullptr;
10801 JSAutoRealm ar(cx, glob);
10803 if (kind == ShellGlobalKind::WindowProxy) {
10804 RootedObject proxy(cx, NewShellWindowProxy(cx, glob));
10805 if (!proxy) {
10806 return nullptr;
10808 js::SetWindowProxy(cx, glob, proxy);
10811 #ifndef LAZY_STANDARD_CLASSES
10812 if (!JS::InitRealmStandardClasses(cx)) {
10813 return nullptr;
10815 #endif
10817 if (immutablePrototype) {
10818 bool succeeded;
10819 if (!JS_SetImmutablePrototype(cx, glob, &succeeded)) {
10820 return nullptr;
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)) {
10829 return nullptr;
10831 #endif
10832 if (!JS_InitReflectParse(cx, glob)) {
10833 return nullptr;
10835 if (!JS_DefineDebuggerObject(cx, glob)) {
10836 return nullptr;
10838 if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) ||
10839 !JS_DefineProfilingFunctions(cx, glob)) {
10840 return nullptr;
10842 #ifdef FUZZING_JS_FUZZILLI
10843 if (!JS_DefineFunctions(cx, glob, shell_function_fuzzilli_hash)) {
10844 return nullptr;
10846 #endif
10847 if (!js::DefineTestingFunctions(cx, glob, fuzzingSafe,
10848 disableOOMFunctions)) {
10849 return nullptr;
10851 if (!JS_DefineProperties(cx, glob, TestingProperties)) {
10852 return nullptr;
10855 if (!fuzzingSafe) {
10856 if (!JS_DefineFunctionsWithHelp(cx, glob, fuzzing_unsafe_functions)) {
10857 return nullptr;
10859 if (!DefineConsole(cx, glob)) {
10860 return nullptr;
10864 if (!DefineOS(cx, glob, fuzzingSafe, &gOutFile, &gErrFile)) {
10865 return nullptr;
10868 if (!js::SupportDifferentialTesting()) {
10869 if (!JS_DefineFunctionsWithHelp(cx, glob,
10870 diff_testing_unsafe_functions)) {
10871 return nullptr;
10874 RootedObject performanceObj(cx, JS_NewObject(cx, nullptr));
10875 if (!performanceObj) {
10876 return nullptr;
10878 if (!JS_DefineFunctionsWithHelp(cx, performanceObj,
10879 performance_functions)) {
10880 return nullptr;
10882 RootedObject mozMemoryObj(cx, JS_NewObject(cx, nullptr));
10883 if (!mozMemoryObj) {
10884 return nullptr;
10886 RootedObject gcObj(cx, gc::NewMemoryInfoObject(cx));
10887 if (!gcObj) {
10888 return nullptr;
10890 if (!JS_DefineProperty(cx, glob, "performance", performanceObj,
10891 JSPROP_ENUMERATE)) {
10892 return nullptr;
10894 if (!JS_DefineProperty(cx, performanceObj, "mozMemory", mozMemoryObj,
10895 JSPROP_ENUMERATE)) {
10896 return nullptr;
10898 if (!JS_DefineProperty(cx, mozMemoryObj, "gc", gcObj, JSPROP_ENUMERATE)) {
10899 return nullptr;
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,
10910 nullptr));
10911 if (!domProto) {
10912 return 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)) {
10924 return nullptr;
10927 JS_FireOnNewGlobalObject(cx, glob);
10930 return 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);
10939 if (!scriptArgs) {
10940 return false;
10943 if (!JS_DefineProperty(cx, cx->global(), "scriptArgs", scriptArgs, 0)) {
10944 return false;
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) {
10951 return false;
10953 RootedString str(cx, NewStringCopyUTF8(cx, scriptArgUtf8.get()));
10954 if (!str || !JS_DefineElement(cx, scriptArgs, i, str, JSPROP_ENUMERATE)) {
10955 return false;
10959 RootedValue scriptPathValue(cx);
10960 if (const char* scriptPath = op->getStringArg("script")) {
10961 UniqueChars scriptPathUtf8 = JS::EncodeNarrowToUtf8(cx, scriptPath);
10962 if (!scriptPathUtf8) {
10963 return false;
10965 RootedString scriptPathString(cx,
10966 NewStringCopyUTF8(cx, scriptPathUtf8.get()));
10967 if (!scriptPathString) {
10968 return false;
10970 scriptPathValue = StringValue(scriptPathString);
10971 } else {
10972 scriptPathValue = UndefinedValue();
10975 if (!JS_DefineProperty(cx, cx->global(), "scriptPath", scriptPathValue, 0)) {
10976 return false;
10979 return true;
10982 static bool OptionFailure(const char* option, const char* str) {
10983 fprintf(stderr, "Unrecognized option for %s: %s\n", option, str);
10984 return false;
10987 template <typename... Ts>
10988 auto minVal(Ts... args);
10989 template <typename T>
10990 auto minVal(T a) {
10991 return a;
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)) {
11004 return false;
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();
11033 size_t ufpArgno =
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());
11041 if (!path) {
11042 return false;
11044 if (!Process(cx, path.get(), false, PreludeScript)) {
11045 return false;
11048 preludePaths.popFront();
11049 continue;
11052 if (fpArgno == minArgno) {
11053 UniqueChars path = JS::EncodeNarrowToUtf8(cx, filePaths.front());
11054 if (!path) {
11055 return false;
11057 if (!Process(cx, path.get(), false, FileScript)) {
11058 return false;
11061 filePaths.popFront();
11062 continue;
11065 if (ufpArgno == minArgno) {
11066 UniqueChars path = JS::EncodeNarrowToUtf8(cx, utf16FilePaths.front());
11067 if (!path) {
11068 return false;
11070 if (!Process(cx, path.get(), false, FileScriptUtf16)) {
11071 return false;
11074 utf16FilePaths.popFront();
11075 continue;
11078 if (ccArgno == minArgno) {
11079 UniqueChars code = JS::EncodeNarrowToUtf8(cx, codeChunks.front());
11080 if (!code) {
11081 return false;
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)) {
11092 return false;
11095 RootedValue rval(cx);
11096 if (!JS::Evaluate(cx, opts, srcBuf, &rval)) {
11097 return false;
11100 codeChunks.popFront();
11101 if (sc->quitting) {
11102 break;
11105 continue;
11108 MOZ_ASSERT(mpArgno == minArgno);
11110 UniqueChars path = JS::EncodeNarrowToUtf8(cx, modulePaths.front());
11111 if (!path) {
11112 return false;
11114 if (!Process(cx, path.get(), false, FileModule)) {
11115 return false;
11118 modulePaths.popFront();
11121 if (sc->quitting) {
11122 return false;
11125 /* The |script| argument is processed after all options. */
11126 if (const char* path = op->getStringArg("script")) {
11127 UniqueChars pathUtf8 = JS::EncodeNarrowToUtf8(cx, path);
11128 if (!pathUtf8) {
11129 return false;
11131 if (!Process(cx, pathUtf8.get(), false, FileScript)) {
11132 return false;
11136 if (op->getBoolOption('i')) {
11137 if (!Process(cx, nullptr, true, FileScript)) {
11138 return false;
11142 return true;
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;
11161 #ifdef JS_GC_ZEAL
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);
11169 #endif
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));
11180 if (!str) {
11181 return false;
11184 UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
11185 if (!utf8chars) {
11186 return false;
11189 FILE* fp = ErrorFilePointer();
11190 fprintf(fp, "Unhandled rejection: %s\n", utf8chars.get());
11192 if (!site) {
11193 fputs("(no stack trace available)\n", stderr);
11194 return true;
11197 JSPrincipals* principals = cx->realm()->principals();
11198 RootedString stackStr(cx);
11199 if (!BuildStackString(cx, principals, site, &stackStr, 2)) {
11200 return false;
11203 UniqueChars stack = JS_EncodeStringToUTF8(cx, stackStr);
11204 if (!stack) {
11205 return false;
11208 fputs("Stack:\n", fp);
11209 fputs(stack.get(), fp);
11211 return true;
11214 [[nodiscard]] static bool ReportUnhandledRejections(JSContext* cx) {
11215 ShellContext* sc = GetShellContext(cx);
11216 if (!sc->trackUnhandledRejections) {
11217 return true;
11220 if (!sc->unhandledRejectedPromises) {
11221 return true;
11224 AutoRealm ar(cx, sc->unhandledRejectedPromises);
11226 if (!SetObject::size(cx, sc->unhandledRejectedPromises)) {
11227 return true;
11230 sc->exitCode = EXITCODE_RUNTIME_ERROR;
11232 RootedValue iter(cx);
11233 if (!SetObject::iterator(cx, SetObject::IteratorKind::Values,
11234 sc->unhandledRejectedPromises, &iter)) {
11235 return false;
11238 Rooted<SetIteratorObject*> iterObj(cx,
11239 &iter.toObject().as<SetIteratorObject>());
11240 JSObject* obj = SetIteratorObject::createResult(cx);
11241 if (!obj) {
11242 return false;
11245 Rooted<ArrayObject*> resultObj(cx, &obj->as<ArrayObject>());
11246 while (true) {
11247 bool done = SetIteratorObject::next(iterObj, resultObj);
11248 if (done) {
11249 break;
11252 RootedObject obj(cx, &resultObj->getDenseElement(0).toObject());
11253 Rooted<PromiseObject*> promise(cx, obj->maybeUnwrapIf<PromiseObject>());
11254 if (!promise) {
11255 FILE* fp = ErrorFilePointer();
11256 fputs(
11257 "Unhandled rejection: dead proxy found in unhandled "
11258 "rejections set\n",
11259 fp);
11260 continue;
11263 AutoRealm ar2(cx, promise);
11265 if (!PrintUnhandledRejection(cx, promise)) {
11266 return false;
11270 sc->unhandledRejectedPromises = nullptr;
11272 return true;
11275 bool ShellContext::registerWithCx(JSContext* cx) {
11276 cx_ = cx;
11277 JS_SetContextPrivate(cx, this);
11279 if (isWorker) {
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);
11299 return true;
11302 ShellContext::~ShellContext() {
11303 markObservers.reset();
11304 if (cx_) {
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();
11316 #endif
11318 auto exitShell = MakeScopeExit([&] {
11319 #ifdef JS_STRUCTURED_SPEW
11320 cx->spewer().disableSpewing();
11321 #endif
11324 #ifdef MOZ_CODE_COVERAGE
11325 InstallCoverageSignalHandlers();
11326 #endif
11328 Maybe<JS::AutoDisableGenerationalGC> noggc;
11329 if (op->getBoolOption("no-ggc")) {
11330 noggc.emplace(cx);
11333 Maybe<AutoDisableCompactingGC> nocgc;
11334 if (op->getBoolOption("no-cgc")) {
11335 nocgc.emplace(cx);
11338 if (op->getBoolOption("fuzzing-safe")) {
11339 fuzzingSafe = true;
11340 } else {
11341 fuzzingSafe =
11342 (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0');
11345 #ifdef DEBUG
11346 if (op->getBoolOption("differential-testing")) {
11347 JS::SetSupportDifferentialTesting(true);
11349 #endif
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;
11363 do {
11364 JS::RealmOptions options;
11365 SetStandardRealmOptions(options);
11366 RootedObject glob(
11367 cx, NewGlobalObject(cx, options, nullptr, ShellGlobalKind::WindowProxy,
11368 /* immutablePrototype = */ true));
11369 if (!glob) {
11370 return 1;
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);
11384 #endif
11386 sc->exitCode = 0;
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.
11400 RunShellJobs(cx);
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
11416 if (reprl_mode) {
11417 fflush(stdout);
11418 fflush(stderr);
11419 // Send return code to parent and reset edge counters.
11420 struct {
11421 int status;
11422 uint32_t execHash;
11423 uint32_t execHashInputs;
11424 } s;
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;
11433 #endif
11435 if (enableDisassemblyDumps) {
11436 AutoReportException are(cx);
11437 if (!js::DumpRealmPCCounts(cx)) {
11438 result = EXITCODE_OUT_OF_MEMORY;
11442 // End REPRL loop
11443 } while (reprl_mode);
11445 return result;
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) {
11453 RCFile* outFile;
11455 const char* outPath = getenv(envVar);
11456 FILE* newfp;
11457 if (outPath && *outPath && (newfp = fopen(outPath, "w"))) {
11458 outFile = SystemAlloc_New<RCFile>(newfp);
11459 } else {
11460 outFile = defaultOut;
11463 if (!outFile) {
11464 MOZ_CRASH("Failed to allocate output file");
11467 outFile->acquire();
11468 *outFileP = outFile;
11471 static void PreInit() {
11472 #ifdef XP_WIN
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);
11481 #endif
11484 #ifndef JS_WITHOUT_NSPR
11485 class AutoLibraryLoader {
11486 Vector<PRLibrary*, 4, SystemAllocPolicy> libraries;
11488 public:
11489 ~AutoLibraryLoader() {
11490 for (auto dll : libraries) {
11491 PR_UnloadLibrary(dll);
11495 PRLibrary* load(const char* path) {
11496 PRLibSpec libSpec;
11497 libSpec.type = PR_LibSpec_Pathname;
11498 libSpec.value.pathname = path;
11499 PRLibrary* dll = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_GLOBAL);
11500 if (!dll) {
11501 fprintf(stderr, "LoadLibrary '%s' failed with code %d\n", path,
11502 PR_GetError());
11503 MOZ_CRASH("Failed to load library");
11506 MOZ_ALWAYS_TRUE(libraries.append(dll));
11507 return dll;
11510 #endif
11512 static bool ReadSelfHostedXDRFile(JSContext* cx, FileContents& buf) {
11513 FILE* file = fopen(selfHostedXDRPath, "rb");
11514 if (!file) {
11515 fprintf(stderr, "Can't open self-hosted stencil XDR file.\n");
11516 return false;
11518 AutoCloseFile autoClose(file);
11520 struct stat st;
11521 if (fstat(fileno(file), &st) < 0) {
11522 fprintf(stderr, "Unable to stat self-hosted stencil XDR file.\n");
11523 return false;
11526 if (st.st_size >= INT32_MAX) {
11527 fprintf(stderr, "self-hosted stencil XDR file too large.\n");
11528 return false;
11530 uint32_t filesize = uint32_t(st.st_size);
11532 if (!buf.growBy(filesize)) {
11533 return false;
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");
11538 return false;
11541 return true;
11544 static bool WriteSelfHostedXDRFile(JSContext* cx, JS::SelfHostedCache buffer) {
11545 FILE* file = fopen(selfHostedXDRPath, "wb");
11546 if (!file) {
11547 JS_ReportErrorUTF8(cx, "Can't open self-hosted stencil XDR file.");
11548 return false;
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.");
11555 return false;
11558 return true;
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) {
11565 *result = true;
11566 return true;
11568 if (strcmp(val, "false") == 0) {
11569 *result = false;
11570 return true;
11572 fprintf(stderr, "Invalid value for boolean pref %s: %s\n", name, val);
11573 return false;
11574 } else {
11575 static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>);
11576 char* end;
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);
11580 return false;
11582 *result = static_cast<T>(v);
11583 return true;
11587 static bool SetJSPrefToTrueForBool(const char* name) {
11588 // Search for a matching pref and try to set it to a default value for the
11589 // type.
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); \
11594 return true; \
11595 } else { \
11596 fprintf(stderr, "Pref %s must have a value specified.\n", name); \
11597 return false; \
11600 FOR_EACH_JS_PREF(CHECK_PREF)
11601 #undef CHECK_PREF
11603 // Nothing matched, return false
11604 fprintf(stderr, "Invalid pref name: %s\n", name);
11605 return false;
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) { \
11613 TYPE v; \
11614 if (!ParsePrefValue<TYPE>(NAME, value, &v)) { \
11615 return false; \
11617 JS::Prefs::SETTER(v); \
11618 return true; \
11620 FOR_EACH_JS_PREF(CHECK_PREF)
11621 #undef CHECK_PREF
11623 // Nothing matched, return false
11624 fprintf(stderr, "Invalid pref name: %s\n", name);
11625 return false;
11628 static bool SetJSPref(const char* pref) {
11629 const char* assign = strchr(pref, '=');
11630 if (!assign) {
11631 if (!SetJSPrefToTrueForBool(pref)) {
11632 return false;
11634 return true;
11637 size_t nameLen = assign - pref;
11638 const char* valStart = assign + 1; // Skip '='.
11640 if (!SetJSPrefToValue(pref, nameLen, valStart)) {
11641 return false;
11643 return true;
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);
11653 } else {
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)
11662 #undef PRINT_PREF
11665 static bool SetGCParameterFromArg(JSContext* cx, char* arg) {
11666 char* c = strchr(arg, '=');
11667 if (!c) {
11668 fprintf(stderr,
11669 "Error: --gc-param argument '%s' must be of the form "
11670 "name=decimalValue\n",
11671 arg);
11672 return false;
11675 *c = '\0';
11676 const char* name = arg;
11677 const char* valueStr = c + 1;
11679 JSGCParamKey key;
11680 bool writable;
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) \
11685 if (writable) { \
11686 fprintf(stderr, " %s\n", name); \
11688 FOR_EACH_GC_PARAM(PRINT_WRITABLE_PARAM_NAME)
11689 #undef PRINT_WRITABLE_PARAM_NAME
11690 return false;
11693 if (!writable) {
11694 fprintf(stderr, "Error: GC parameter '%s' is not writable\n", name);
11695 return false;
11698 char* end = nullptr;
11699 unsigned long int value = strtoul(valueStr, &end, 10);
11700 if (end == valueStr || *end) {
11701 fprintf(stderr,
11702 "Error: Could not parse '%s' as decimal for GC parameter '%s'\n",
11703 valueStr, name);
11704 return false;
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",
11711 valueStr, name);
11712 return false;
11715 return true;
11718 int main(int argc, char** argv) {
11719 PreInit();
11721 sArgc = argc;
11722 sArgv = argv;
11724 int result;
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:
11756 break;
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);
11770 return 1;
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
11787 // closing it.
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);
11805 if (!cx) {
11806 return 1;
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)) {
11819 return 1;
11822 if (!SetContextOptions(cx, op)) {
11823 return 1;
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) {
11836 return 1;
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] {
11850 KillWatchdog(cx);
11851 KillWorkerThreads(cx);
11852 DestructSharedObjectMailbox();
11853 CancelOffThreadJobsForRuntime(cx);
11856 // The file content should stay alive as long as Worker thread can be
11857 // initialized.
11858 JS::SelfHostedCache xdrSpan = nullptr;
11859 JS::SelfHostedWriter xdrWriter = nullptr;
11860 if (selfHostedXDRPath) {
11861 if (encodeSelfHostedCode) {
11862 xdrWriter = WriteSelfHostedXDRFile;
11863 } else {
11864 selfHostedXDRBuffer.emplace(cx);
11865 if (ReadSelfHostedXDRFile(cx, *selfHostedXDRBuffer)) {
11866 MOZ_ASSERT(selfHostedXDRBuffer->length() > 0);
11867 JS::SelfHostedCache span(selfHostedXDRBuffer->begin(),
11868 selfHostedXDRBuffer->end());
11869 xdrSpan = span;
11870 } else {
11871 fprintf(stderr, "Falling back on parsing source.\n");
11872 selfHostedXDRPath = nullptr;
11877 if (!JS::InitSelfHostedCode(cx, xdrSpan, xdrWriter)) {
11878 return 1;
11881 EnvironmentPreparer environmentPreparer(cx);
11883 JS::SetProcessLargeAllocationFailureCallback(my_LargeAllocFailCallback);
11885 if (op.getBoolOption("wasm-compile-and-serialize")) {
11886 #ifdef __wasi__
11887 MOZ_CRASH("WASI doesn't support wasm");
11888 #else
11889 if (!WasmCompileAndSerialize(cx)) {
11890 // Errors have been printed directly to stderr.
11891 MOZ_ASSERT(!cx->isExceptionPending());
11892 return EXIT_FAILURE;
11894 #endif
11895 return EXIT_SUCCESS;
11898 result = Shell(cx, &op);
11900 #ifdef DEBUG
11901 if (OOM_printAllocationCount) {
11902 printf("OOM max count: %" PRIu64 "\n", js::oom::simulator.counter());
11904 #endif
11906 return result;
11909 bool InitOptionParser(OptionParser& op) {
11910 op.setDescription(
11911 "The SpiderMonkey shell provides a command line interface to the "
11912 "JavaScript engine. Code and file options provided via the command line "
11913 "are "
11914 "run left to right. If provided, the optional script argument is run "
11915 "after "
11916 "all options have been processed. Just-In-Time compilation modes may be "
11917 "enabled via "
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 "
11935 "XDR file") ||
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.") ||
11950 !op.addBoolOption(
11951 '\0', "disable-parser-deferred-alloc",
11952 "Disable deferred allocation of GC objects until after parser") ||
11953 #ifdef DEBUG
11954 !op.addBoolOption('O', "print-alloc",
11955 "Print the number of allocations at exit") ||
11956 #endif
11957 !op.addOptionalStringArg("script",
11958 "A script to execute (after all options)") ||
11959 !op.addOptionalMultiStringArg(
11960 "scriptArgs",
11961 "String arguments to bind as |scriptArgs| in the "
11962 "shell's global") ||
11963 !op.addIntOption(
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.",
11968 -1) ||
11969 !op.addIntOption('\0', "thread-count", "COUNT", "Alias for --cpu-count.",
11970 -1) ||
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"
11977 " call",
11978 -1) ||
11979 !op.addIntOption('\0', "small-function-length", "COUNT",
11980 "The maximum bytecode length of a 'small function' for "
11981 "the purpose of inlining.",
11982 -1) ||
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") ||
12000 !op.addIntOption(
12001 '\0', "regexp-warmup-threshold", "COUNT",
12002 "Wait for COUNT invocations before compiling regexps to native code "
12003 "(default 10)",
12004 -1) ||
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") ||
12025 #endif
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") ||
12038 !op.addBoolOption(
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)"
12057 #else
12058 "(default: off, on to enable)"
12059 #endif
12060 ) ||
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 "
12068 "disable)") ||
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)",
12133 -1) ||
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 "
12140 "(default)\n"
12141 " testbed: Backtracking allocator with experimental features\n"
12142 " stupid: Simple block local register allocation") ||
12143 !op.addBoolOption(
12144 '\0', "ion-eager",
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") ||
12165 #endif
12166 !op.addIntOption(
12167 '\0', "baseline-warmup-threshold", "COUNT",
12168 "Wait for COUNT calls or iterations before baseline-compiling "
12169 "(default: 10)",
12170 -1) ||
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.") ||
12176 !op.addBoolOption(
12177 '\0', "emit-interpreter-entry",
12178 "Emit Interpreter entry trampolines (default under --enable-perf)") ||
12179 !op.addBoolOption(
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") ||
12184 !op.addIntOption(
12185 '\0', "blinterp-warmup-threshold", "COUNT",
12186 "Wait for COUNT calls or iterations before Baseline-interpreting "
12187 "(default: 10)",
12188 -1) ||
12189 !op.addIntOption(
12190 '\0', "trial-inlining-warmup-threshold", "COUNT",
12191 "Wait for COUNT calls or iterations before trial-inlining "
12192 "(default: 500)",
12193 -1) ||
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)") ||
12198 !op.addBoolOption(
12199 '\0', "non-writable-jitcode",
12200 "(NOP for fuzzers) Allocate JIT code as non-writable memory.") ||
12201 !op.addBoolOption(
12202 '\0', "no-sse3",
12203 "Pretend CPU does not support SSE3 instructions and above "
12204 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12205 !op.addBoolOption(
12206 '\0', "no-ssse3",
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).") ||
12209 !op.addBoolOption(
12210 '\0', "no-sse41",
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") ||
12214 !op.addBoolOption(
12215 '\0', "no-sse42",
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.") ||
12221 !op.addBoolOption(
12222 '\0', "no-avx",
12223 "Pretend CPU does not support AVX or AVX2 instructions "
12224 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12225 #else
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.") ||
12231 #endif
12232 !op.addBoolOption('\0', "more-compartments",
12233 "Make newGlobal default to creating a new "
12234 "compartment.") ||
12235 !op.addBoolOption('\0', "fuzzing-safe",
12236 "Don't expose functions that aren't safe for "
12237 "fuzzers to call") ||
12238 #ifdef DEBUG
12239 !op.addBoolOption('\0', "differential-testing",
12240 "Avoid random/undefined behavior that disturbs "
12241 "differential testing (correctness fuzzing)") ||
12242 #endif
12243 !op.addBoolOption('\0', "disable-oom-functions",
12244 "Disable functions that cause "
12245 "artificial OOMs") ||
12246 !op.addBoolOption('\0', "no-threads", "Disable helper threads") ||
12247 !op.addBoolOption(
12248 '\0', "no-jit-backend",
12249 "Disable the JIT backend completely for this process") ||
12250 #ifdef DEBUG
12251 !op.addBoolOption('\0', "dump-entrained-variables",
12252 "Print variables which are "
12253 "unnecessarily entrained by inner functions") ||
12254 #endif
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") ||
12262 !op.addIntOption(
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)",
12271 0) ||
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.",
12278 0) ||
12279 !op.addIntOption('\0', "asm-pool-max-offset", "OFFSET",
12280 "The maximum pc relative OFFSET permitted in pool "
12281 "reference instructions.",
12282 1024) ||
12283 !op.addBoolOption('\0', "arm-sim-icache-checks",
12284 "Enable icache flush checks in the ARM "
12285 "simulator.") ||
12286 !op.addIntOption('\0', "arm-sim-stop-at", "NUMBER",
12287 "Stop the ARM simulator after the given "
12288 "NUMBER of instructions.",
12289 -1) ||
12290 !op.addBoolOption('\0', "mips-sim-icache-checks",
12291 "Enable icache flush checks in the MIPS "
12292 "simulator.") ||
12293 !op.addIntOption('\0', "mips-sim-stop-at", "NUMBER",
12294 "Stop the MIPS simulator after the given "
12295 "NUMBER of instructions.",
12296 -1) ||
12297 !op.addBoolOption('\0', "loong64-sim-icache-checks",
12298 "Enable icache flush checks in the LoongArch64 "
12299 "simulator.") ||
12300 !op.addIntOption('\0', "loong64-sim-stop-at", "NUMBER",
12301 "Stop the LoongArch64 simulator after the given "
12302 "NUMBER of instructions.",
12303 -1) ||
12304 #ifdef JS_CODEGEN_RISCV64
12305 !op.addBoolOption('\0', "riscv-debug", "debug print riscv info.") ||
12306 #endif
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.",
12315 -1) ||
12316 #endif
12317 !op.addIntOption('\0', "nursery-size", "SIZE-MB",
12318 "Set the maximum nursery size in MB",
12319 JS::DefaultNurseryMaxBytes / 1024 / 1024) ||
12320 #ifdef JS_GC_ZEAL
12321 !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
12322 gc::ZealModeHelpText) ||
12323 #else
12324 !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
12325 "option ignored in non-gc-zeal builds") ||
12326 #endif
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") ||
12344 #else
12345 !op.addBoolOption('\0', "smoosh", "No-op") ||
12346 #endif
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") ||
12359 #endif
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.") ||
12367 !op.addBoolOption(
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.")) {
12385 return false;
12388 op.setArgTerminatesOptions("script", true);
12389 op.setArgCapturesRest("scriptArgs");
12391 return true;
12394 bool SetGlobalOptionsPreJSInit(const OptionParser& op) {
12395 for (MultiStringRange args = op.getMultiStringOption("setpref");
12396 !args.empty(); args.popFront()) {
12397 if (!SetJSPref(args.front())) {
12398 return false;
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);
12434 #endif
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")) {
12457 fprintf(
12458 stderr,
12459 "Wasm shell flags are now using prefs, use -P wasm_feature instead.\n");
12460 return false;
12463 if (op.getBoolOption("list-prefs")) {
12464 ListJSPrefs();
12465 return false;
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");
12479 if (fill >= 0) {
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;
12487 #endif
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")) {
12509 return false;
12512 if (op.getBoolOption("no-ssse3")) {
12513 js::jit::CPUInfo::SetSSSE3Disabled();
12514 if (!sCompilerProcessFlags.append("--no-ssse3")) {
12515 return false;
12518 if (op.getBoolOption("no-sse4") || op.getBoolOption("no-sse41")) {
12519 js::jit::CPUInfo::SetSSE41Disabled();
12520 if (!sCompilerProcessFlags.append("--no-sse41")) {
12521 return false;
12524 if (op.getBoolOption("no-sse42")) {
12525 js::jit::CPUInfo::SetSSE42Disabled();
12526 if (!sCompilerProcessFlags.append("--no-sse42")) {
12527 return false;
12530 if (op.getBoolOption("no-avx")) {
12531 js::jit::CPUInfo::SetAVXDisabled();
12532 if (!sCompilerProcessFlags.append("--no-avx")) {
12533 return false;
12536 if (op.getBoolOption("enable-avx")) {
12537 js::jit::CPUInfo::SetAVXEnabled();
12538 if (!sCompilerProcessFlags.append("--enable-avx")) {
12539 return false;
12542 #endif
12544 return true;
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) {
12552 return false;
12556 // Allow dumping on Linux with the fuzzing flag set, even when running with
12557 // the suid/sgid flag set on the shell.
12558 #ifdef XP_LINUX
12559 if (op.getBoolOption("fuzzing-safe")) {
12560 prctl(PR_SET_DUMPABLE, 1);
12562 #endif
12564 #ifdef DEBUG
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');
12570 #endif
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;
12604 } else {
12605 MOZ_CRASH(
12606 "invalid option value for --selfhosted-xdr-mode, must be "
12607 "encode/decode");
12611 #ifdef JS_WITHOUT_NSPR
12612 if (!op.getMultiStringOption("dll").empty()) {
12613 fprintf(stderr, "Error: --dll requires NSPR support!\n");
12614 return false;
12616 #else
12617 AutoLibraryLoader loader;
12618 MultiStringRange dllPaths = op.getMultiStringOption("dll");
12619 while (!dllPaths.empty()) {
12620 char* path = dllPaths.front();
12621 loader.load(path);
12622 dllPaths.popFront();
12624 #endif
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)) {
12637 return false;
12640 return true;
12643 bool SetContextOptions(JSContext* cx, const OptionParser& op) {
12644 if (!SetContextWasmOptions(cx, op) || !SetContextJITOptions(cx, op) ||
12645 !SetContextGCOptions(cx, op)) {
12646 return false;
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");
12656 #endif
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;
12674 } else {
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);
12699 #endif
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;
12714 } else {
12715 return OptionFailure("delazification-mode", mode);
12719 return true;
12722 bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) {
12723 enableAsmJS = !op.getBoolOption("no-asmjs");
12725 enableWasm = true;
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;
12749 } else {
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);
12764 #ifndef __wasi__
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")) {
12776 return false;
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)) {
12787 return false;
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) {
12800 size_t n_needed =
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)) {
12812 return false;
12815 #endif // __wasi__
12817 return true;
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;
12836 } else {
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;
12854 } else {
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);
12864 } else {
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;
12877 } else {
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;
12887 } else {
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;
12912 } else {
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;
12922 } else {
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;
12932 } else {
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;
12942 } else {
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;
12952 } else {
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;
12962 } else {
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;
12972 } else {
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;
12982 } else {
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;
13000 } else {
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;
13010 } else {
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;
13020 } else {
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;
13059 #endif
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")) {
13151 fprintf(stderr,
13152 "--ion-parallel-compile is deprecated. Please use "
13153 "--ion-offthread-compile instead.\n");
13154 return false;
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;
13174 } else {
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;
13184 } else {
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");
13195 if (stopAt >= 0) {
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");
13204 if (stopAt >= 0) {
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");
13213 if (stopAt >= 0) {
13214 jit::Simulator::StopSimAt = stopAt;
13216 #endif
13218 #ifdef DEBUG
13219 # ifdef JS_CODEGEN_RISCV64
13220 if (op.getBoolOption("riscv-debug")) {
13221 jit::Assembler::FLAG_riscv_debug = true;
13223 # endif
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");
13235 if (stopAt >= 0) {
13236 jit::Simulator::StopSimAt = stopAt;
13238 # endif
13239 #endif
13241 return true;
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");
13250 fprintf(stderr,
13251 "The nursery can be disabled by passing the --no-ggc option.\n");
13252 return false;
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();
13266 } else {
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();
13276 } else {
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);
13284 #ifndef ANDROID
13285 bool parallelMarking = true;
13286 #else
13287 bool parallelMarking = false;
13288 #endif
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())) {
13309 return false;
13313 #ifdef DEBUG
13314 dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
13315 #endif
13317 #ifdef JS_GC_ZEAL
13318 const char* zealStr = op.getStringOption("gc-zeal");
13319 if (zealStr) {
13320 if (!cx->runtime()->gc.parseAndSetZeal(zealStr)) {
13321 return false;
13323 uint32_t nextScheduled;
13324 cx->runtime()->gc.getZealBits(&gZealBits, &gZealFrequency, &nextScheduled);
13326 #endif
13328 return true;
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);
13335 if (!pathUtf8) {
13336 return false;
13339 Rooted<JSString*> jspath(cx, NewStringCopyUTF8(cx, pathUtf8.get()));
13340 if (!jspath) {
13341 return false;
13344 moduleLoadPath = js::shell::ResolvePath(cx, jspath, RootRelative);
13346 processWideModuleLoadPath = JS_EncodeStringToUTF8(cx, moduleLoadPath);
13347 if (!processWideModuleLoadPath) {
13348 return false;
13350 } else {
13351 processWideModuleLoadPath = js::shell::GetCWD(cx);
13352 if (!processWideModuleLoadPath) {
13353 return false;
13356 moduleLoadPath = NewStringCopyUTF8(cx, processWideModuleLoadPath.get());
13357 if (!moduleLoadPath) {
13358 return false;
13362 ShellContext* sc = GetShellContext(cx);
13363 sc->moduleLoader = js::MakeUnique<ModuleLoader>();
13364 if (!sc->moduleLoader || !sc->moduleLoader->init(cx, moduleLoadPath)) {
13365 return false;
13368 return true;