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/. */
6 
7 /* JS shell. */
8 
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/Atomics.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/EnumSet.h"
14 #include "mozilla/IntegerPrintfMacros.h"
15 #include "mozilla/mozalloc.h"
16 #include "mozilla/PodOperations.h"
17 #include "mozilla/ScopeExit.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/TimeStamp.h"
20 #include "mozilla/UniquePtrExtensions.h"  // UniqueFreePtr
21 #include "mozilla/Utf8.h"
22 #include "mozilla/Variant.h"
23 
24 #include <algorithm>
25 #include <chrono>
26 #ifdef XP_WIN
27 #  include <direct.h>
28 #  include <process.h>
29 #endif
30 #include <errno.h>
31 #include <fcntl.h>
32 #if defined(XP_WIN)
33 #  include <io.h> /* for isatty() */
34 #endif
35 #include <locale.h>
36 #if defined(MALLOC_H)
37 #  include MALLOC_H /* for malloc_usable_size, malloc_size, _msize */
38 #endif
39 #include <ctime>
40 #include <math.h>
41 #ifndef __wasi__
42 #  include <signal.h>
43 #endif
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <sys/stat.h>
48 #include <sys/types.h>
49 #include <utility>
50 #ifdef XP_UNIX
51 #  ifndef __wasi__
52 #    include <sys/mman.h>
53 #    include <sys/wait.h>
54 #  endif
55 #  include <sys/stat.h>
56 #  include <unistd.h>
57 #endif
58 #ifdef XP_LINUX
59 #  include <sys/prctl.h>
60 #endif
61 
62 #include "jsapi.h"
63 #include "jsfriendapi.h"
64 #include "jstypes.h"
65 #ifndef JS_WITHOUT_NSPR
66 #  include "prerror.h"
67 #  include "prlink.h"
68 #endif
69 
70 #include "builtin/Array.h"
71 #include "builtin/MapObject.h"
72 #include "builtin/ModuleObject.h"
73 #include "builtin/RegExp.h"
74 #include "builtin/TestingFunctions.h"
75 #include "builtin/TestingUtility.h"  // js::ParseCompileOptions
76 #include "debugger/DebugAPI.h"
77 #include "frontend/BytecodeCompilation.h"
78 #include "frontend/BytecodeCompiler.h"
79 #include "frontend/BytecodeEmitter.h"
80 #include "frontend/CompilationStencil.h"
81 #ifdef JS_ENABLE_SMOOSH
82 #  include "frontend/Frontend2.h"
83 #endif
84 #include "frontend/ModuleSharedContext.h"
85 #include "frontend/Parser.h"
86 #include "frontend/SourceNotes.h"  // SrcNote, SrcNoteType, SrcNoteIterator
87 #include "gc/PublicIterators.h"
88 #ifdef DEBUG
89 #  include "irregexp/RegExpAPI.h"
90 #endif
91 #include "gc/GC-inl.h"  // ZoneCellIter
92 
93 #ifdef JS_SIMULATOR_ARM
94 #  include "jit/arm/Simulator-arm.h"
95 #endif
96 #ifdef JS_SIMULATOR_MIPS32
97 #  include "jit/mips32/Simulator-mips32.h"
98 #endif
99 #ifdef JS_SIMULATOR_MIPS64
100 #  include "jit/mips64/Simulator-mips64.h"
101 #endif
102 #include "jit/CacheIRHealth.h"
103 #include "jit/InlinableNatives.h"
104 #include "jit/Ion.h"
105 #include "jit/JitcodeMap.h"
106 #include "jit/shared/CodeGenerator-shared.h"
107 #include "js/Array.h"        // JS::NewArrayObject
108 #include "js/ArrayBuffer.h"  // JS::{CreateMappedArrayBufferContents,NewMappedArrayBufferWithContents,IsArrayBufferObject,GetArrayBufferLengthAndData}
109 #include "js/BuildId.h"      // JS::BuildIdCharVector, JS::SetProcessBuildIdOp
110 #include "js/CharacterEncoding.h"  // JS::StringIsASCII
111 #include "js/CompilationAndEvaluation.h"
112 #include "js/CompileOptions.h"
113 #include "js/ContextOptions.h"  // JS::ContextOptions{,Ref}
114 #include "js/Debug.h"
115 #include "js/Equality.h"                   // JS::SameValue
116 #include "js/ErrorReport.h"                // JS::PrintError
117 #include "js/Exception.h"                  // JS::StealPendingExceptionStack
118 #include "js/experimental/CodeCoverage.h"  // js::EnableCodeCoverage
119 #include "js/experimental/CTypes.h"        // JS::InitCTypesClass
120 #include "js/experimental/Intl.h"  // JS::AddMoz{DateTimeFormat,DisplayNames}Constructor
121 #include "js/experimental/JitInfo.h"  // JSJit{Getter,Setter,Method}CallArgs, JSJitGetterInfo, JSJit{Getter,Setter}Op, JSJitInfo
122 #include "js/experimental/JSStencil.h"  // JS::Stencil, JS::CompileToStencilOffThread, JS::FinishOffThreadCompileToStencil
123 #include "js/experimental/SourceHook.h"  // js::{Set,Forget,}SourceHook
124 #include "js/experimental/TypedData.h"   // JS_NewUint8Array
125 #include "js/friend/DumpFunctions.h"     // JS::FormatStackDump
126 #include "js/friend/ErrorMessages.h"     // js::GetErrorMessage, JSMSG_*
127 #include "js/friend/StackLimits.h"       // js::AutoCheckRecursionLimit
128 #include "js/friend/WindowProxy.h"  // js::IsWindowProxy, js::SetWindowProxyClass, js::ToWindowProxyIfWindow, js::ToWindowIfWindowProxy
129 #include "js/GCAPI.h"               // JS::AutoCheckCannotGC
130 #include "js/GCVector.h"
131 #include "js/Initialization.h"
132 #include "js/JSON.h"
133 #include "js/MemoryFunctions.h"
134 #include "js/Modules.h"  // JS::GetModulePrivate, JS::SetModule{DynamicImport,Metadata,Resolve}Hook, JS::SetModulePrivate
135 #include "js/Object.h"  // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
136 #include "js/OffThreadScriptCompilation.h"  // JS::SetUseOffThreadParseGlobal, js::UseOffThreadParseGlobal
137 #include "js/Printf.h"
138 #include "js/PropertySpec.h"
139 #include "js/Realm.h"
140 #include "js/RegExp.h"  // JS::ObjectIsRegExp
141 #include "js/SourceText.h"
142 #include "js/StableStringChars.h"
143 #include "js/StructuredClone.h"
144 #include "js/SweepingAPI.h"
145 #include "js/Transcoding.h"  // JS::TranscodeBuffer, JS::TranscodeRange
146 #include "js/Warnings.h"     // JS::SetWarningReporter
147 #include "js/WasmModule.h"   // JS::WasmModule
148 #include "js/Wrapper.h"
149 #include "proxy/DeadObjectProxy.h"  // js::IsDeadProxyObject
150 #include "shell/jsoptparse.h"
151 #include "shell/jsshell.h"
152 #include "shell/OSObject.h"
153 #include "shell/ShellModuleObjectWrapper.h"
154 #include "shell/WasmTesting.h"
155 #include "threading/ConditionVariable.h"
156 #include "threading/ExclusiveData.h"
157 #include "threading/LockGuard.h"
158 #include "threading/Thread.h"
159 #include "util/CompleteFile.h"  // js::FileContents, js::ReadCompleteFile
160 #include "util/DifferentialTesting.h"
161 #include "util/StringBuffer.h"
162 #include "util/Text.h"
163 #include "util/Windows.h"
164 #include "vm/ArgumentsObject.h"
165 #include "vm/Compression.h"
166 #include "vm/ErrorObject.h"
167 #include "vm/HelperThreads.h"
168 #include "vm/JSAtom.h"
169 #include "vm/JSContext.h"
170 #include "vm/JSFunction.h"
171 #include "vm/JSObject.h"
172 #include "vm/JSScript.h"
173 #include "vm/ModuleBuilder.h"  // js::ModuleBuilder
174 #include "vm/Monitor.h"
175 #include "vm/MutexIDs.h"
176 #include "vm/Printer.h"        // QuoteString
177 #include "vm/PromiseObject.h"  // js::PromiseObject
178 #include "vm/Shape.h"
179 #include "vm/SharedArrayObject.h"
180 #include "vm/StencilObject.h"  // js::StencilObject
181 #include "vm/Time.h"
182 #include "vm/ToSource.h"  // js::ValueToSource
183 #include "vm/TypedArrayObject.h"
184 #include "vm/WrapperObject.h"
185 #include "wasm/WasmJS.h"
186 
187 #include "vm/Compartment-inl.h"
188 #include "vm/ErrorObject-inl.h"
189 #include "vm/Interpreter-inl.h"
190 #include "vm/JSObject-inl.h"
191 #include "vm/Realm-inl.h"
192 #include "vm/Stack-inl.h"
193 
194 using namespace js;
195 using namespace js::cli;
196 using namespace js::shell;
197 
198 using JS::AutoStableStringChars;
199 using JS::CompileOptions;
200 
201 using js::shell::RCFile;
202 
203 using mozilla::ArrayEqual;
204 using mozilla::AsVariant;
205 using mozilla::Atomic;
206 using mozilla::MakeScopeExit;
207 using mozilla::Maybe;
208 using mozilla::Nothing;
209 using mozilla::NumberEqualsInt32;
210 using mozilla::TimeDuration;
211 using mozilla::TimeStamp;
212 using mozilla::Utf8Unit;
213 using mozilla::Variant;
214 
215 #ifdef FUZZING_JS_FUZZILLI
216 #  define REPRL_CRFD 100
217 #  define REPRL_CWFD 101
218 #  define REPRL_DRFD 102
219 #  define REPRL_DWFD 103
220 
221 #  define SHM_SIZE 0x100000
222 #  define MAX_EDGES ((SHM_SIZE - 4) * 8)
223 
224 struct shmem_data {
225   uint32_t num_edges;
226   unsigned char edges[];
227 };
228 
229 struct shmem_data* __shmem;
230 
231 uint32_t *__edges_start, *__edges_stop;
__sanitizer_cov_reset_edgeguards()232 void __sanitizer_cov_reset_edgeguards() {
233   uint64_t N = 0;
234   for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++)
235     *x = ++N;
236 }
237 
__sanitizer_cov_trace_pc_guard_init(uint32_t * start,uint32_t * stop)238 extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start,
239                                                     uint32_t* stop) {
240   // Avoid duplicate initialization
241   if (start == stop || *start) return;
242 
243   if (__edges_start != NULL || __edges_stop != NULL) {
244     fprintf(stderr,
245             "Coverage instrumentation is only supported for a single module\n");
246     _exit(-1);
247   }
248 
249   __edges_start = start;
250   __edges_stop = stop;
251 
252   // Map the shared memory region
253   const char* shm_key = getenv("SHM_ID");
254   if (!shm_key) {
255     puts("[COV] no shared memory bitmap available, skipping");
256     __shmem = (struct shmem_data*)malloc(SHM_SIZE);
257   } else {
258     int fd = shm_open(shm_key, O_RDWR, S_IREAD | S_IWRITE);
259     if (fd <= -1) {
260       fprintf(stderr, "Failed to open shared memory region: %s\n",
261               strerror(errno));
262       _exit(-1);
263     }
264 
265     __shmem = (struct shmem_data*)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE,
266                                        MAP_SHARED, fd, 0);
267     if (__shmem == MAP_FAILED) {
268       fprintf(stderr, "Failed to mmap shared memory region\n");
269       _exit(-1);
270     }
271   }
272 
273   __sanitizer_cov_reset_edgeguards();
274 
275   __shmem->num_edges = stop - start;
276   printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n",
277          shm_key, __shmem->num_edges);
278 }
279 
__sanitizer_cov_trace_pc_guard(uint32_t * guard)280 extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
281   // There's a small race condition here: if this function executes in two
282   // threads for the same edge at the same time, the first thread might disable
283   // the edge (by setting the guard to zero) before the second thread fetches
284   // the guard value (and thus the index). However, our instrumentation ignores
285   // the first edge (see libcoverage.c) and so the race is unproblematic.
286   uint32_t index = *guard;
287   // If this function is called before coverage instrumentation is properly
288   // initialized we want to return early.
289   if (!index) return;
290   __shmem->edges[index / 8] |= 1 << (index % 8);
291   *guard = 0;
292 }
293 #endif /* FUZZING_JS_FUZZILLI */
294 
295 enum JSShellExitCode {
296   EXITCODE_RUNTIME_ERROR = 3,
297   EXITCODE_FILE_NOT_FOUND = 4,
298   EXITCODE_OUT_OF_MEMORY = 5,
299   EXITCODE_TIMEOUT = 6
300 };
301 
302 /*
303  * Limit the timeout to 30 minutes to prevent an overflow on platfoms
304  * that represent the time internally in microseconds using 32-bit int.
305  */
306 static const double MAX_TIMEOUT_SECONDS = 1800.0;
307 
308 // Not necessarily in sync with the browser
309 #ifdef ENABLE_SHARED_MEMORY
310 #  define SHARED_MEMORY_DEFAULT 1
311 #else
312 #  define SHARED_MEMORY_DEFAULT 0
313 #endif
314 
315 // Fuzzing support for JS runtime fuzzing
316 #ifdef FUZZING_INTERFACES
317 #  include "shell/jsrtfuzzing/jsrtfuzzing.h"
318 static bool fuzzDoDebug = !!getenv("MOZ_FUZZ_DEBUG");
319 static bool fuzzHaveModule = !!getenv("FUZZER");
320 #endif  // FUZZING_INTERFACES
321 
322 // Code to support GCOV code coverage measurements on standalone shell
323 #ifdef MOZ_CODE_COVERAGE
324 #  if defined(__GNUC__) && !defined(__clang__)
325 extern "C" void __gcov_dump();
326 extern "C" void __gcov_reset();
327 
counters_dump(int)328 void counters_dump(int) { __gcov_dump(); }
329 
counters_reset(int)330 void counters_reset(int) { __gcov_reset(); }
331 #  else
counters_dump(int)332 void counters_dump(int) { /* Do nothing */
333 }
334 
counters_reset(int)335 void counters_reset(int) { /* Do nothing */
336 }
337 #  endif
338 
InstallCoverageSignalHandlers()339 static void InstallCoverageSignalHandlers() {
340 #  ifndef XP_WIN
341   fprintf(stderr, "[CodeCoverage] Setting handlers for process %d.\n",
342           getpid());
343 
344   struct sigaction dump_sa;
345   dump_sa.sa_handler = counters_dump;
346   dump_sa.sa_flags = SA_RESTART;
347   sigemptyset(&dump_sa.sa_mask);
348   mozilla::DebugOnly<int> r1 = sigaction(SIGUSR1, &dump_sa, nullptr);
349   MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler");
350 
351   struct sigaction reset_sa;
352   reset_sa.sa_handler = counters_reset;
353   reset_sa.sa_flags = SA_RESTART;
354   sigemptyset(&reset_sa.sa_mask);
355   mozilla::DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr);
356   MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler");
357 #  endif
358 }
359 #endif
360 
361 // An off-thread parse or decode job.
362 class js::shell::OffThreadJob {
363   enum State {
364     RUNNING,   // Working; no token.
365     DONE,      // Finished; have token.
366     CANCELLED  // Cancelled due to error.
367   };
368 
369  public:
370   using Source = mozilla::Variant<JS::UniqueTwoByteChars, JS::TranscodeBuffer>;
371 
372   OffThreadJob(ShellContext* sc, ScriptKind kind, bool useOffThreadParseGlobal,
373                Source&& source);
374   ~OffThreadJob();
375 
376   void cancel();
377   void markDone(JS::OffThreadToken* newToken);
378   JS::OffThreadToken* waitUntilDone(JSContext* cx);
379 
sourceChars()380   char16_t* sourceChars() { return source.as<UniqueTwoByteChars>().get(); }
xdrBuffer()381   JS::TranscodeBuffer& xdrBuffer() { return source.as<JS::TranscodeBuffer>(); }
382 
383  public:
384   const int32_t id;
385   const ScriptKind kind;
386   const bool useOffThreadParseGlobal;
387 
388  private:
389   js::Monitor& monitor;
390   State state;
391   JS::OffThreadToken* token;
392   Source source;
393 };
394 
NewOffThreadJob(JSContext * cx,ScriptKind kind,CompileOptions & options,OffThreadJob::Source && source)395 static OffThreadJob* NewOffThreadJob(JSContext* cx, ScriptKind kind,
396                                      CompileOptions& options,
397                                      OffThreadJob::Source&& source) {
398   ShellContext* sc = GetShellContext(cx);
399   UniquePtr<OffThreadJob> job(cx->new_<OffThreadJob>(
400       sc, kind, options.useOffThreadParseGlobal, std::move(source)));
401   if (!job) {
402     return nullptr;
403   }
404 
405   if (!sc->offThreadJobs.append(job.get())) {
406     job->cancel();
407     JS_ReportErrorASCII(cx, "OOM adding off-thread job");
408     return nullptr;
409   }
410 
411   return job.release();
412 }
413 
GetSingleOffThreadJob(JSContext * cx,ScriptKind kind)414 static OffThreadJob* GetSingleOffThreadJob(JSContext* cx, ScriptKind kind) {
415   ShellContext* sc = GetShellContext(cx);
416   const auto& jobs = sc->offThreadJobs;
417   if (jobs.empty()) {
418     JS_ReportErrorASCII(cx, "No off-thread jobs are pending");
419     return nullptr;
420   }
421 
422   if (jobs.length() > 1) {
423     JS_ReportErrorASCII(
424         cx, "Multiple off-thread jobs are pending: must specify job ID");
425     return nullptr;
426   }
427 
428   OffThreadJob* job = jobs[0];
429   if (job->kind != kind) {
430     JS_ReportErrorASCII(cx, "Off-thread job is the wrong kind");
431     return nullptr;
432   }
433 
434   return job;
435 }
436 
LookupOffThreadJobByID(JSContext * cx,ScriptKind kind,int32_t id)437 static OffThreadJob* LookupOffThreadJobByID(JSContext* cx, ScriptKind kind,
438                                             int32_t id) {
439   if (id <= 0) {
440     JS_ReportErrorASCII(cx, "Bad off-thread job ID");
441     return nullptr;
442   }
443 
444   ShellContext* sc = GetShellContext(cx);
445   const auto& jobs = sc->offThreadJobs;
446   if (jobs.empty()) {
447     JS_ReportErrorASCII(cx, "No off-thread jobs are pending");
448     return nullptr;
449   }
450 
451   OffThreadJob* job = nullptr;
452   for (auto someJob : jobs) {
453     if (someJob->id == id) {
454       job = someJob;
455       break;
456     }
457   }
458 
459   if (!job) {
460     JS_ReportErrorASCII(cx, "Off-thread job not found");
461     return nullptr;
462   }
463 
464   if (job->kind != kind) {
465     JS_ReportErrorASCII(cx, "Off-thread job is the wrong kind");
466     return nullptr;
467   }
468 
469   return job;
470 }
471 
LookupOffThreadJobForArgs(JSContext * cx,ScriptKind kind,const CallArgs & args,size_t arg)472 static OffThreadJob* LookupOffThreadJobForArgs(JSContext* cx, ScriptKind kind,
473                                                const CallArgs& args,
474                                                size_t arg) {
475   // If the optional ID argument isn't present, get the single pending job.
476   if (args.length() <= arg) {
477     return GetSingleOffThreadJob(cx, kind);
478   }
479 
480   // Lookup the job using the specified ID.
481   int32_t id = 0;
482   RootedValue value(cx, args[arg]);
483   if (!ToInt32(cx, value, &id)) {
484     return nullptr;
485   }
486 
487   return LookupOffThreadJobByID(cx, kind, id);
488 }
489 
DeleteOffThreadJob(JSContext * cx,OffThreadJob * job)490 static void DeleteOffThreadJob(JSContext* cx, OffThreadJob* job) {
491   ShellContext* sc = GetShellContext(cx);
492   for (size_t i = 0; i < sc->offThreadJobs.length(); i++) {
493     if (sc->offThreadJobs[i] == job) {
494       sc->offThreadJobs.erase(&sc->offThreadJobs[i]);
495       js_delete(job);
496       return;
497     }
498   }
499 
500   MOZ_CRASH("Off-thread job not found");
501 }
502 
CancelOffThreadJobsForContext(JSContext * cx)503 static void CancelOffThreadJobsForContext(JSContext* cx) {
504   // Parse jobs may be blocked waiting on GC.
505   gc::FinishGC(cx);
506 
507   // Wait for jobs belonging to this context.
508   ShellContext* sc = GetShellContext(cx);
509   while (!sc->offThreadJobs.empty()) {
510     OffThreadJob* job = sc->offThreadJobs.popCopy();
511     job->waitUntilDone(cx);
512     js_delete(job);
513   }
514 }
515 
CancelOffThreadJobsForRuntime(JSContext * cx)516 static void CancelOffThreadJobsForRuntime(JSContext* cx) {
517   // Parse jobs may be blocked waiting on GC.
518   gc::FinishGC(cx);
519 
520   // Cancel jobs belonging to this runtime.
521   CancelOffThreadParses(cx->runtime());
522   ShellContext* sc = GetShellContext(cx);
523   while (!sc->offThreadJobs.empty()) {
524     js_delete(sc->offThreadJobs.popCopy());
525   }
526 }
527 
528 mozilla::Atomic<int32_t> gOffThreadJobSerial(1);
529 
OffThreadJob(ShellContext * sc,ScriptKind kind,bool useOffThreadParseGlobal,Source && source)530 OffThreadJob::OffThreadJob(ShellContext* sc, ScriptKind kind,
531                            bool useOffThreadParseGlobal, Source&& source)
532     : id(gOffThreadJobSerial++),
533       kind(kind),
534       useOffThreadParseGlobal(useOffThreadParseGlobal),
535       monitor(sc->offThreadMonitor),
536       state(RUNNING),
537       token(nullptr),
538       source(std::move(source)) {
539   MOZ_RELEASE_ASSERT(id > 0, "Off-thread job IDs exhausted");
540 }
541 
~OffThreadJob()542 OffThreadJob::~OffThreadJob() { MOZ_ASSERT(state != RUNNING); }
543 
cancel()544 void OffThreadJob::cancel() {
545   MOZ_ASSERT(state == RUNNING);
546   MOZ_ASSERT(!token);
547 
548   state = CANCELLED;
549 }
550 
markDone(JS::OffThreadToken * newToken)551 void OffThreadJob::markDone(JS::OffThreadToken* newToken) {
552   AutoLockMonitor alm(monitor);
553   MOZ_ASSERT(state == RUNNING);
554   MOZ_ASSERT(!token);
555   MOZ_ASSERT(newToken);
556 
557   token = newToken;
558   state = DONE;
559   alm.notifyAll();
560 }
561 
waitUntilDone(JSContext * cx)562 JS::OffThreadToken* OffThreadJob::waitUntilDone(JSContext* cx) {
563   AutoLockMonitor alm(monitor);
564   MOZ_ASSERT(state != CANCELLED);
565 
566   while (state != DONE) {
567     alm.wait();
568   }
569 
570   MOZ_ASSERT(token);
571   return token;
572 }
573 
574 struct ShellCompartmentPrivate {
575   GCPtrObject grayRoot;
576 };
577 
578 struct MOZ_STACK_CLASS EnvironmentPreparer
579     : public js::ScriptEnvironmentPreparer {
EnvironmentPreparerEnvironmentPreparer580   explicit EnvironmentPreparer(JSContext* cx) {
581     js::SetScriptEnvironmentPreparer(cx, this);
582   }
583   void invoke(JS::HandleObject global, Closure& closure) override;
584 };
585 
586 const char* shell::selfHostedXDRPath = nullptr;
587 bool shell::encodeSelfHostedCode = false;
588 bool shell::enableCodeCoverage = false;
589 bool shell::enableDisassemblyDumps = false;
590 bool shell::offthreadCompilation = false;
591 bool shell::enableAsmJS = false;
592 bool shell::enableWasm = false;
593 bool shell::enableSharedMemory = SHARED_MEMORY_DEFAULT;
594 bool shell::enableWasmBaseline = false;
595 bool shell::enableWasmOptimizing = false;
596 
597 #define WASM_DEFAULT_FEATURE(NAME, ...) bool shell::enableWasm##NAME = true;
598 #define WASM_EXPERIMENTAL_FEATURE(NAME, ...) \
599   bool shell::enableWasm##NAME = false;
600 JS_FOR_WASM_FEATURES(WASM_DEFAULT_FEATURE, WASM_EXPERIMENTAL_FEATURE);
601 #undef WASM_DEFAULT_FEATURE
602 #undef WASM_EXPERIMENTAL_FEATURE
603 
604 #ifdef ENABLE_WASM_SIMD_WORMHOLE
605 bool shell::enableWasmSimdWormhole = false;
606 #endif
607 bool shell::enableWasmVerbose = false;
608 bool shell::enableTestWasmAwaitTier2 = false;
609 bool shell::enableSourcePragmas = true;
610 bool shell::enableAsyncStacks = false;
611 bool shell::enableAsyncStackCaptureDebuggeeOnly = false;
612 bool shell::enableStreams = false;
613 bool shell::enableReadableByteStreams = false;
614 bool shell::enableBYOBStreamReaders = false;
615 bool shell::enableWritableStreams = false;
616 bool shell::enableReadableStreamPipeTo = false;
617 bool shell::enableWeakRefs = false;
618 bool shell::enableToSource = false;
619 bool shell::enablePropertyErrorMessageFix = false;
620 bool shell::enableIteratorHelpers = false;
621 bool shell::enablePrivateClassFields = false;
622 bool shell::enablePrivateClassMethods = false;
623 bool shell::enableTopLevelAwait = true;
624 bool shell::enableErgonomicBrandChecks = true;
625 bool shell::useOffThreadParseGlobal = true;
626 bool shell::enableClassStaticBlocks = false;
627 #ifdef JS_GC_ZEAL
628 uint32_t shell::gZealBits = 0;
629 uint32_t shell::gZealFrequency = 0;
630 #endif
631 bool shell::printTiming = false;
632 RCFile* shell::gErrFile = nullptr;
633 RCFile* shell::gOutFile = nullptr;
634 bool shell::reportWarnings = true;
635 bool shell::compileOnly = false;
636 bool shell::disableOOMFunctions = false;
637 bool shell::defaultToSameCompartment = true;
638 
639 bool shell::useFdlibmForSinCosTan = false;
640 
641 #ifdef DEBUG
642 bool shell::dumpEntrainedVariables = false;
643 bool shell::OOM_printAllocationCount = false;
644 #endif
645 
646 UniqueChars shell::processWideModuleLoadPath;
647 
648 static bool SetTimeoutValue(JSContext* cx, double t);
649 
650 static void KillWatchdog(JSContext* cx);
651 
652 static bool ScheduleWatchdog(JSContext* cx, double t);
653 
654 static void CancelExecution(JSContext* cx);
655 
656 enum class ShellGlobalKind {
657   GlobalObject,
658   WindowProxy,
659 };
660 
661 static JSObject* NewGlobalObject(JSContext* cx, JS::RealmOptions& options,
662                                  JSPrincipals* principals, ShellGlobalKind kind,
663                                  bool immutablePrototype);
664 
665 /*
666  * A toy WindowProxy class for the shell. This is intended for testing code
667  * where global |this| is a WindowProxy. All requests are forwarded to the
668  * underlying global and no navigation is supported.
669  */
670 const JSClass ShellWindowProxyClass =
671     PROXY_CLASS_DEF("ShellWindowProxy", JSCLASS_HAS_RESERVED_SLOTS(1));
672 
NewShellWindowProxy(JSContext * cx,JS::HandleObject global)673 JSObject* NewShellWindowProxy(JSContext* cx, JS::HandleObject global) {
674   MOZ_ASSERT(global->is<GlobalObject>());
675 
676   js::WrapperOptions options;
677   options.setClass(&ShellWindowProxyClass);
678 
679   JSAutoRealm ar(cx, global);
680   JSObject* obj =
681       js::Wrapper::New(cx, global, &js::Wrapper::singleton, options);
682   MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
683   return obj;
684 }
685 
686 /*
687  * A toy principals type for the shell.
688  *
689  * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the
690  * set bits in P are a superset of those in Q. Thus, the principal 0 is
691  * subsumed by everything, and the principal ~0 subsumes everything.
692  *
693  * As a special case, a null pointer as a principal is treated like 0xffff.
694  *
695  * The 'newGlobal' function takes an option indicating which principal the
696  * new global should have; 'evaluate' does for the new code.
697  */
698 class ShellPrincipals final : public JSPrincipals {
699   uint32_t bits;
700 
getBits(JSPrincipals * p)701   static uint32_t getBits(JSPrincipals* p) {
702     if (!p) {
703       return 0xffff;
704     }
705     return static_cast<ShellPrincipals*>(p)->bits;
706   }
707 
708  public:
ShellPrincipals(uint32_t bits,int32_t refcount=0)709   explicit ShellPrincipals(uint32_t bits, int32_t refcount = 0) : bits(bits) {
710     this->refcount = refcount;
711   }
712 
write(JSContext * cx,JSStructuredCloneWriter * writer)713   bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
714     // The shell doesn't have a read principals hook, so it doesn't really
715     // matter what we write here, but we have to write something so the
716     // fuzzer is happy.
717     return JS_WriteUint32Pair(writer, bits, 0);
718   }
719 
isSystemOrAddonPrincipal()720   bool isSystemOrAddonPrincipal() override { return true; }
721 
destroy(JSPrincipals * principals)722   static void destroy(JSPrincipals* principals) {
723     MOZ_ASSERT(principals != &fullyTrusted);
724     MOZ_ASSERT(principals->refcount == 0);
725     js_delete(static_cast<const ShellPrincipals*>(principals));
726   }
727 
subsumes(JSPrincipals * first,JSPrincipals * second)728   static bool subsumes(JSPrincipals* first, JSPrincipals* second) {
729     uint32_t firstBits = getBits(first);
730     uint32_t secondBits = getBits(second);
731     return (firstBits | secondBits) == firstBits;
732   }
733 
734   static JSSecurityCallbacks securityCallbacks;
735 
736   // Fully-trusted principals singleton.
737   static ShellPrincipals fullyTrusted;
738 };
739 
740 JSSecurityCallbacks ShellPrincipals::securityCallbacks = {
741     nullptr,  // contentSecurityPolicyAllows
742     subsumes};
743 
744 // The fully-trusted principal subsumes all other principals.
745 ShellPrincipals ShellPrincipals::fullyTrusted(-1, 1);
746 
747 #ifdef EDITLINE
748 extern "C" {
749 extern MOZ_EXPORT char* readline(const char* prompt);
750 extern MOZ_EXPORT void add_history(char* line);
751 }  // extern "C"
752 #endif
753 
ShellContext(JSContext * cx)754 ShellContext::ShellContext(JSContext* cx)
755     : isWorker(false),
756       lastWarningEnabled(false),
757       trackUnhandledRejections(true),
758       timeoutInterval(-1.0),
759       startTime(PRMJ_Now()),
760       serviceInterrupt(false),
761       haveInterruptFunc(false),
762       interruptFunc(cx, NullValue()),
763       lastWarning(cx, NullValue()),
764       promiseRejectionTrackerCallback(cx, NullValue()),
765       unhandledRejectedPromises(cx),
766       watchdogLock(mutexid::ShellContextWatchdog),
767       exitCode(0),
768       quitting(false),
769       readLineBufPos(0),
770       errFilePtr(nullptr),
771       outFilePtr(nullptr),
772       offThreadMonitor(mutexid::ShellOffThreadState),
773       finalizationRegistryCleanupCallbacks(cx) {}
774 
~ShellContext()775 ShellContext::~ShellContext() { MOZ_ASSERT(offThreadJobs.empty()); }
776 
GetShellContext(JSContext * cx)777 ShellContext* js::shell::GetShellContext(JSContext* cx) {
778   ShellContext* sc = static_cast<ShellContext*>(JS_GetContextPrivate(cx));
779   MOZ_ASSERT(sc);
780   return sc;
781 }
782 
TraceGrayRoots(JSTracer * trc,void * data)783 static void TraceGrayRoots(JSTracer* trc, void* data) {
784   JSRuntime* rt = trc->runtime();
785   for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
786     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
787       auto priv = static_cast<ShellCompartmentPrivate*>(
788           JS_GetCompartmentPrivate(comp.get()));
789       if (priv) {
790         TraceNullableEdge(trc, &priv->grayRoot, "test gray root");
791       }
792     }
793   }
794 }
795 
GetLine(FILE * file,const char * prompt)796 static mozilla::UniqueFreePtr<char[]> GetLine(FILE* file, const char* prompt) {
797 #ifdef EDITLINE
798   /*
799    * Use readline only if file is stdin, because there's no way to specify
800    * another handle.  Are other filehandles interactive?
801    */
802   if (file == stdin) {
803     mozilla::UniqueFreePtr<char[]> linep(readline(prompt));
804     /*
805      * We set it to zero to avoid complaining about inappropriate ioctl
806      * for device in the case of EOF. Looks like errno == 251 if line is
807      * finished with EOF and errno == 25 (EINVAL on Mac) if there is
808      * nothing left to read.
809      */
810     if (errno == 251 || errno == 25 || errno == EINVAL) {
811       errno = 0;
812     }
813     if (!linep) {
814       return nullptr;
815     }
816     if (linep[0] != '\0') {
817       add_history(linep.get());
818     }
819     return linep;
820   }
821 #endif
822 
823   size_t len = 0;
824   if (*prompt != '\0' && gOutFile->isOpen()) {
825     fprintf(gOutFile->fp, "%s", prompt);
826     fflush(gOutFile->fp);
827   }
828 
829   size_t size = 80;
830   mozilla::UniqueFreePtr<char[]> buffer(static_cast<char*>(malloc(size)));
831   if (!buffer) {
832     return nullptr;
833   }
834 
835   char* current = buffer.get();
836   do {
837     while (true) {
838       if (fgets(current, size - len, file)) {
839         break;
840       }
841       if (errno != EINTR) {
842         return nullptr;
843       }
844     }
845 
846     len += strlen(current);
847     char* t = buffer.get() + len - 1;
848     if (*t == '\n') {
849       /* Line was read. We remove '\n' and exit. */
850       *t = '\0';
851       break;
852     }
853 
854     if (len + 1 == size) {
855       size = size * 2;
856       char* raw = buffer.release();
857       char* tmp = static_cast<char*>(realloc(raw, size));
858       if (!tmp) {
859         free(raw);
860         return nullptr;
861       }
862       buffer.reset(tmp);
863     }
864     current = buffer.get() + len;
865   } while (true);
866   return buffer;
867 }
868 
ShellInterruptCallback(JSContext * cx)869 static bool ShellInterruptCallback(JSContext* cx) {
870   ShellContext* sc = GetShellContext(cx);
871   if (!sc->serviceInterrupt) {
872     return true;
873   }
874 
875   // Reset serviceInterrupt. CancelExecution or InterruptIf will set it to
876   // true to distinguish watchdog or user triggered interrupts.
877   // Do this first to prevent other interrupts that may occur while the
878   // user-supplied callback is executing from re-entering the handler.
879   sc->serviceInterrupt = false;
880 
881   bool result;
882   if (sc->haveInterruptFunc) {
883     bool wasAlreadyThrowing = cx->isExceptionPending();
884     JS::AutoSaveExceptionState savedExc(cx);
885     JSAutoRealm ar(cx, &sc->interruptFunc.toObject());
886     RootedValue rval(cx);
887 
888     // Report any exceptions thrown by the JS interrupt callback, but do
889     // *not* keep it on the cx. The interrupt handler is invoked at points
890     // that are not expected to throw catchable exceptions, like at
891     // JSOp::RetRval.
892     //
893     // If the interrupted JS code was already throwing, any exceptions
894     // thrown by the interrupt handler are silently swallowed.
895     {
896       Maybe<AutoReportException> are;
897       if (!wasAlreadyThrowing) {
898         are.emplace(cx);
899       }
900       result = JS_CallFunctionValue(cx, nullptr, sc->interruptFunc,
901                                     JS::HandleValueArray::empty(), &rval);
902     }
903     savedExc.restore();
904 
905     if (rval.isBoolean()) {
906       result = rval.toBoolean();
907     } else {
908       result = false;
909     }
910   } else {
911     result = false;
912   }
913 
914   if (!result && sc->exitCode == 0) {
915     static const char msg[] = "Script terminated by interrupt handler.\n";
916     fputs(msg, stderr);
917 
918     sc->exitCode = EXITCODE_TIMEOUT;
919   }
920 
921   return result;
922 }
923 
924 /*
925  * Some UTF-8 files, notably those written using Notepad, have a Unicode
926  * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order
927  * is meaningless for UTF-8) but causes a syntax error unless we skip it.
928  */
SkipUTF8BOM(FILE * file)929 static void SkipUTF8BOM(FILE* file) {
930   int ch1 = fgetc(file);
931   int ch2 = fgetc(file);
932   int ch3 = fgetc(file);
933 
934   // Skip the BOM
935   if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF) {
936     return;
937   }
938 
939   // No BOM - revert
940   if (ch3 != EOF) {
941     ungetc(ch3, file);
942   }
943   if (ch2 != EOF) {
944     ungetc(ch2, file);
945   }
946   if (ch1 != EOF) {
947     ungetc(ch1, file);
948   }
949 }
950 
invoke(HandleObject global,Closure & closure)951 void EnvironmentPreparer::invoke(HandleObject global, Closure& closure) {
952   MOZ_ASSERT(JS_IsGlobalObject(global));
953 
954   JSContext* cx = TlsContext.get();
955   MOZ_ASSERT(!JS_IsExceptionPending(cx));
956 
957   AutoRealm ar(cx, global);
958   AutoReportException are(cx);
959   if (!closure(cx)) {
960     return;
961   }
962 }
963 
CreateScriptPrivate(JSContext * cx,HandleString path)964 JSObject* js::shell::CreateScriptPrivate(JSContext* cx, HandleString path) {
965   RootedObject info(cx, JS_NewPlainObject(cx));
966   if (!info) {
967     return nullptr;
968   }
969 
970   if (path) {
971     RootedValue pathValue(cx, StringValue(path));
972     if (!JS_DefineProperty(cx, info, "path", pathValue, JSPROP_ENUMERATE)) {
973       return nullptr;
974     }
975   }
976 
977   return info;
978 }
979 
RegisterScriptPathWithModuleLoader(JSContext * cx,HandleScript script,const char * filename)980 static bool RegisterScriptPathWithModuleLoader(JSContext* cx,
981                                                HandleScript script,
982                                                const char* filename) {
983   // Set the private value associated with a script to a object containing the
984   // script's filename so that the module loader can use it to resolve
985   // relative imports.
986 
987   RootedString path(cx, JS_NewStringCopyZ(cx, filename));
988   if (!path) {
989     return false;
990   }
991 
992   MOZ_ASSERT(JS::GetScriptPrivate(script).isUndefined());
993   RootedObject infoObject(cx, CreateScriptPrivate(cx, path));
994   if (!infoObject) {
995     return false;
996   }
997 
998   JS::SetScriptPrivate(script, ObjectValue(*infoObject));
999   return true;
1000 }
1001 
1002 enum class CompileUtf8 {
1003   InflateToUtf16,
1004   DontInflate,
1005 };
1006 
RunFile(JSContext * cx,const char * filename,FILE * file,CompileUtf8 compileMethod,bool compileOnly)1007 [[nodiscard]] static bool RunFile(JSContext* cx, const char* filename,
1008                                   FILE* file, CompileUtf8 compileMethod,
1009                                   bool compileOnly) {
1010   SkipUTF8BOM(file);
1011 
1012   int64_t t1 = PRMJ_Now();
1013   RootedScript script(cx);
1014 
1015   {
1016     CompileOptions options(cx);
1017     options.setIntroductionType("js shell file")
1018         .setFileAndLine(filename, 1)
1019         .setIsRunOnce(true)
1020         .setNoScriptRval(true);
1021 
1022     if (compileMethod == CompileUtf8::DontInflate) {
1023       script = JS::CompileUtf8File(cx, options, file);
1024     } else {
1025       fprintf(stderr, "(compiling '%s' after inflating to UTF-16)\n", filename);
1026 
1027       FileContents buffer(cx);
1028       if (!ReadCompleteFile(cx, file, buffer)) {
1029         return false;
1030       }
1031 
1032       size_t length = buffer.length();
1033       auto chars = UniqueTwoByteChars(
1034           UTF8CharsToNewTwoByteCharsZ(
1035               cx,
1036               JS::UTF8Chars(reinterpret_cast<const char*>(buffer.begin()),
1037                             buffer.length()),
1038               &length, js::MallocArena)
1039               .get());
1040       if (!chars) {
1041         return false;
1042       }
1043 
1044       JS::SourceText<char16_t> source;
1045       if (!source.init(cx, std::move(chars), length)) {
1046         return false;
1047       }
1048 
1049       script = JS::Compile(cx, options, source);
1050     }
1051 
1052     if (!script) {
1053       return false;
1054     }
1055   }
1056 
1057   if (!RegisterScriptPathWithModuleLoader(cx, script, filename)) {
1058     return false;
1059   }
1060 
1061 #ifdef DEBUG
1062   if (dumpEntrainedVariables) {
1063     AnalyzeEntrainedVariables(cx, script);
1064   }
1065 #endif
1066   if (!compileOnly) {
1067     if (!JS_ExecuteScript(cx, script)) {
1068       return false;
1069     }
1070     int64_t t2 = PRMJ_Now() - t1;
1071     if (printTiming) {
1072       printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC);
1073     }
1074   }
1075   return true;
1076 }
1077 
RunModule(JSContext * cx,const char * filename,bool compileOnly)1078 [[nodiscard]] static bool RunModule(JSContext* cx, const char* filename,
1079                                     bool compileOnly) {
1080   ShellContext* sc = GetShellContext(cx);
1081 
1082   RootedString path(cx, JS_NewStringCopyZ(cx, filename));
1083   if (!path) {
1084     return false;
1085   }
1086 
1087   path = ResolvePath(cx, path, RootRelative);
1088   if (!path) {
1089     return false;
1090   }
1091 
1092   return sc->moduleLoader->loadRootModule(cx, path);
1093 }
1094 
ShellCleanupFinalizationRegistryCallback(JSFunction * doCleanup,JSObject * incumbentGlobal,void * data)1095 static void ShellCleanupFinalizationRegistryCallback(JSFunction* doCleanup,
1096                                                      JSObject* incumbentGlobal,
1097                                                      void* data) {
1098   // In the browser this queues a task. Shell jobs correspond to microtasks so
1099   // we arrange for cleanup to happen after all jobs/microtasks have run. The
1100   // incumbent global is ignored in the shell.
1101 
1102   auto sc = static_cast<ShellContext*>(data);
1103   AutoEnterOOMUnsafeRegion oomUnsafe;
1104   if (!sc->finalizationRegistryCleanupCallbacks.append(doCleanup)) {
1105     oomUnsafe.crash("ShellCleanupFinalizationRegistryCallback");
1106   }
1107 }
1108 
1109 // Run any FinalizationRegistry cleanup tasks and return whether any ran.
MaybeRunFinalizationRegistryCleanupTasks(JSContext * cx)1110 static bool MaybeRunFinalizationRegistryCleanupTasks(JSContext* cx) {
1111   ShellContext* sc = GetShellContext(cx);
1112   MOZ_ASSERT(!sc->quitting);
1113 
1114   Rooted<ShellContext::FunctionVector> callbacks(cx);
1115   std::swap(callbacks.get(), sc->finalizationRegistryCleanupCallbacks.get());
1116 
1117   bool ranTasks = false;
1118 
1119   RootedFunction callback(cx);
1120   for (JSFunction* f : callbacks) {
1121     callback = f;
1122 
1123     AutoRealm ar(cx, f);
1124 
1125     {
1126       AutoReportException are(cx);
1127       RootedValue unused(cx);
1128       (void)JS_CallFunction(cx, nullptr, callback, HandleValueArray::empty(),
1129                             &unused);
1130     }
1131 
1132     ranTasks = true;
1133 
1134     if (sc->quitting) {
1135       break;
1136     }
1137   }
1138 
1139   return ranTasks;
1140 }
1141 
EnqueueJob(JSContext * cx,unsigned argc,Value * vp)1142 static bool EnqueueJob(JSContext* cx, unsigned argc, Value* vp) {
1143   CallArgs args = CallArgsFromVp(argc, vp);
1144 
1145   if (!IsFunctionObject(args.get(0))) {
1146     JS_ReportErrorASCII(cx, "EnqueueJob's first argument must be a function");
1147     return false;
1148   }
1149 
1150   args.rval().setUndefined();
1151 
1152   RootedObject job(cx, &args[0].toObject());
1153   return js::EnqueueJob(cx, job);
1154 }
1155 
RunShellJobs(JSContext * cx)1156 static void RunShellJobs(JSContext* cx) {
1157   ShellContext* sc = GetShellContext(cx);
1158   if (sc->quitting) {
1159     return;
1160   }
1161 
1162   while (true) {
1163     // Run microtasks.
1164     js::RunJobs(cx);
1165     if (sc->quitting) {
1166       return;
1167     }
1168 
1169     // Run tasks (only finalization registry clean tasks are possible).
1170     bool ranTasks = MaybeRunFinalizationRegistryCleanupTasks(cx);
1171     if (!ranTasks) {
1172       break;
1173     }
1174   }
1175 }
1176 
DrainJobQueue(JSContext * cx,unsigned argc,Value * vp)1177 static bool DrainJobQueue(JSContext* cx, unsigned argc, Value* vp) {
1178   CallArgs args = CallArgsFromVp(argc, vp);
1179 
1180   if (GetShellContext(cx)->quitting) {
1181     JS_ReportErrorASCII(
1182         cx, "Mustn't drain the job queue when the shell is quitting");
1183     return false;
1184   }
1185 
1186   RunShellJobs(cx);
1187 
1188   if (GetShellContext(cx)->quitting) {
1189     return false;
1190   }
1191 
1192   args.rval().setUndefined();
1193   return true;
1194 }
1195 
GlobalOfFirstJobInQueue(JSContext * cx,unsigned argc,Value * vp)1196 static bool GlobalOfFirstJobInQueue(JSContext* cx, unsigned argc, Value* vp) {
1197   CallArgs args = CallArgsFromVp(argc, vp);
1198 
1199   RootedObject job(cx, cx->internalJobQueue->maybeFront());
1200   if (!job) {
1201     JS_ReportErrorASCII(cx, "Job queue is empty");
1202     return false;
1203   }
1204 
1205   RootedObject global(cx, &job->nonCCWGlobal());
1206   if (!cx->compartment()->wrap(cx, &global)) {
1207     return false;
1208   }
1209 
1210   args.rval().setObject(*global);
1211   return true;
1212 }
1213 
TrackUnhandledRejections(JSContext * cx,JS::HandleObject promise,JS::PromiseRejectionHandlingState state)1214 static bool TrackUnhandledRejections(JSContext* cx, JS::HandleObject promise,
1215                                      JS::PromiseRejectionHandlingState state) {
1216   ShellContext* sc = GetShellContext(cx);
1217   if (!sc->trackUnhandledRejections) {
1218     return true;
1219   }
1220 
1221 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
1222   if (cx->runningOOMTest) {
1223     // When OOM happens, we cannot reliably track the set of unhandled
1224     // promise rejections. Throw error only when simulated OOM is used
1225     // *and* promises are used in the test.
1226     JS_ReportErrorASCII(
1227         cx,
1228         "Can't track unhandled rejections while running simulated OOM "
1229         "test. Call ignoreUnhandledRejections before using oomTest etc.");
1230     return false;
1231   }
1232 #endif
1233 
1234   if (!sc->unhandledRejectedPromises) {
1235     sc->unhandledRejectedPromises = SetObject::create(cx);
1236     if (!sc->unhandledRejectedPromises) {
1237       return false;
1238     }
1239   }
1240 
1241   RootedValue promiseVal(cx, ObjectValue(*promise));
1242 
1243   AutoRealm ar(cx, sc->unhandledRejectedPromises);
1244   if (!cx->compartment()->wrap(cx, &promiseVal)) {
1245     return false;
1246   }
1247 
1248   switch (state) {
1249     case JS::PromiseRejectionHandlingState::Unhandled:
1250       if (!SetObject::add(cx, sc->unhandledRejectedPromises, promiseVal)) {
1251         return false;
1252       }
1253       break;
1254     case JS::PromiseRejectionHandlingState::Handled:
1255       bool deleted = false;
1256       if (!SetObject::delete_(cx, sc->unhandledRejectedPromises, promiseVal,
1257                               &deleted)) {
1258         return false;
1259       }
1260       // We can't MOZ_ASSERT(deleted) here, because it's possible we failed to
1261       // add the promise in the first place, due to OOM.
1262       break;
1263   }
1264 
1265   return true;
1266 }
1267 
ForwardingPromiseRejectionTrackerCallback(JSContext * cx,bool mutedErrors,JS::HandleObject promise,JS::PromiseRejectionHandlingState state,void * data)1268 static void ForwardingPromiseRejectionTrackerCallback(
1269     JSContext* cx, bool mutedErrors, JS::HandleObject promise,
1270     JS::PromiseRejectionHandlingState state, void* data) {
1271   AutoReportException are(cx);
1272 
1273   if (!TrackUnhandledRejections(cx, promise, state)) {
1274     return;
1275   }
1276 
1277   RootedValue callback(cx,
1278                        GetShellContext(cx)->promiseRejectionTrackerCallback);
1279   if (callback.isNull()) {
1280     return;
1281   }
1282 
1283   AutoRealm ar(cx, &callback.toObject());
1284 
1285   FixedInvokeArgs<2> args(cx);
1286   args[0].setObject(*promise);
1287   args[1].setInt32(static_cast<int32_t>(state));
1288 
1289   if (!JS_WrapValue(cx, args[0])) {
1290     return;
1291   }
1292 
1293   RootedValue rval(cx);
1294   (void)Call(cx, callback, UndefinedHandleValue, args, &rval);
1295 }
1296 
SetPromiseRejectionTrackerCallback(JSContext * cx,unsigned argc,Value * vp)1297 static bool SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc,
1298                                                Value* vp) {
1299   CallArgs args = CallArgsFromVp(argc, vp);
1300 
1301   if (!IsFunctionObject(args.get(0))) {
1302     JS_ReportErrorASCII(
1303         cx,
1304         "setPromiseRejectionTrackerCallback expects a function as its sole "
1305         "argument");
1306     return false;
1307   }
1308 
1309   GetShellContext(cx)->promiseRejectionTrackerCallback = args[0];
1310 
1311   args.rval().setUndefined();
1312   return true;
1313 }
1314 
1315 // clang-format off
1316 #define LIT(NAME) #NAME,
1317 static const char* telemetryNames[JS_TELEMETRY_END + 1] = {
1318   MAP_JS_TELEMETRY(LIT)
1319   "JS_TELEMETRY_END"
1320 };
1321 #undef LIT
1322 // clang-format on
1323 
1324 // Telemetry can be executed from multiple threads, and the callback is
1325 // responsible to avoid contention on the recorded telemetry data.
1326 static Mutex* telemetryLock = nullptr;
1327 class MOZ_RAII AutoLockTelemetry : public LockGuard<Mutex> {
1328   using Base = LockGuard<Mutex>;
1329 
1330  public:
AutoLockTelemetry()1331   AutoLockTelemetry() : Base(*telemetryLock) { MOZ_ASSERT(telemetryLock); }
1332 };
1333 
1334 using TelemetryData = uint32_t;
1335 using TelemetryVec = Vector<TelemetryData, 0, SystemAllocPolicy>;
1336 static mozilla::Array<TelemetryVec, JS_TELEMETRY_END> telemetryResults;
AccumulateTelemetryDataCallback(int id,uint32_t sample,const char * key)1337 static void AccumulateTelemetryDataCallback(int id, uint32_t sample,
1338                                             const char* key) {
1339   AutoLockTelemetry alt;
1340   // We ignore OOMs while writting teleemtry data.
1341   if (telemetryResults[id].append(sample)) {
1342     return;
1343   }
1344 }
1345 
WriteTelemetryDataToDisk(const char * dir)1346 static void WriteTelemetryDataToDisk(const char* dir) {
1347   const int pathLen = 260;
1348   char fileName[pathLen];
1349   Fprinter output;
1350   auto initOutput = [&](const char* name) -> bool {
1351     if (SprintfLiteral(fileName, "%s%s.csv", dir, name) >= pathLen) {
1352       return false;
1353     }
1354     FILE* file = fopen(fileName, "a");
1355     if (!file) {
1356       return false;
1357     }
1358     output.init(file);
1359     return true;
1360   };
1361 
1362   for (size_t id = 0; id < JS_TELEMETRY_END; id++) {
1363     auto clear = MakeScopeExit([&] { telemetryResults[id].clearAndFree(); });
1364     if (!initOutput(telemetryNames[id])) {
1365       continue;
1366     }
1367     for (uint32_t data : telemetryResults[id]) {
1368       output.printf("%u\n", data);
1369     }
1370     output.finish();
1371   }
1372 }
1373 
1374 #undef MAP_TELEMETRY
1375 
BoundToAsyncStack(JSContext * cx,unsigned argc,Value * vp)1376 static bool BoundToAsyncStack(JSContext* cx, unsigned argc, Value* vp) {
1377   CallArgs args = CallArgsFromVp(argc, vp);
1378 
1379   RootedValue function(cx, GetFunctionNativeReserved(&args.callee(), 0));
1380   RootedObject options(
1381       cx, &GetFunctionNativeReserved(&args.callee(), 1).toObject());
1382 
1383   RootedSavedFrame stack(cx, nullptr);
1384   bool isExplicit;
1385 
1386   RootedValue v(cx);
1387 
1388   if (!JS_GetProperty(cx, options, "stack", &v)) {
1389     return false;
1390   }
1391   if (!v.isObject() || !v.toObject().is<SavedFrame>()) {
1392     JS_ReportErrorASCII(cx,
1393                         "The 'stack' property must be a SavedFrame object.");
1394     return false;
1395   }
1396   stack = &v.toObject().as<SavedFrame>();
1397 
1398   if (!JS_GetProperty(cx, options, "cause", &v)) {
1399     return false;
1400   }
1401   RootedString causeString(cx, ToString(cx, v));
1402   if (!causeString) {
1403     MOZ_ASSERT(cx->isExceptionPending());
1404     return false;
1405   }
1406 
1407   UniqueChars cause = JS_EncodeStringToUTF8(cx, causeString);
1408   if (!cause) {
1409     MOZ_ASSERT(cx->isExceptionPending());
1410     return false;
1411   }
1412 
1413   if (!JS_GetProperty(cx, options, "explicit", &v)) {
1414     return false;
1415   }
1416   isExplicit = v.isUndefined() ? true : ToBoolean(v);
1417 
1418   auto kind =
1419       (isExplicit ? JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT
1420                   : JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::IMPLICIT);
1421 
1422   JS::AutoSetAsyncStackForNewCalls asasfnckthxbye(cx, stack, cause.get(), kind);
1423   return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
1424               args.rval());
1425 }
1426 
BindToAsyncStack(JSContext * cx,unsigned argc,Value * vp)1427 static bool BindToAsyncStack(JSContext* cx, unsigned argc, Value* vp) {
1428   CallArgs args = CallArgsFromVp(argc, vp);
1429 
1430   if (args.length() != 2) {
1431     JS_ReportErrorASCII(cx, "bindToAsyncStack takes exactly two arguments.");
1432     return false;
1433   }
1434 
1435   if (!args[0].isObject() || !IsCallable(args[0])) {
1436     JS_ReportErrorASCII(
1437         cx, "bindToAsyncStack's first argument should be a function.");
1438     return false;
1439   }
1440 
1441   if (!args[1].isObject()) {
1442     JS_ReportErrorASCII(
1443         cx, "bindToAsyncStack's second argument should be an object.");
1444     return false;
1445   }
1446 
1447   RootedFunction bound(cx, NewFunctionWithReserved(cx, BoundToAsyncStack, 0, 0,
1448                                                    "bindToAsyncStack thunk"));
1449   if (!bound) {
1450     return false;
1451   }
1452   SetFunctionNativeReserved(bound, 0, args[0]);
1453   SetFunctionNativeReserved(bound, 1, args[1]);
1454 
1455   args.rval().setObject(*bound);
1456   return true;
1457 }
1458 
1459 #ifdef JS_HAS_INTL_API
AddIntlExtras(JSContext * cx,unsigned argc,Value * vp)1460 static bool AddIntlExtras(JSContext* cx, unsigned argc, Value* vp) {
1461   CallArgs args = CallArgsFromVp(argc, vp);
1462   if (!args.get(0).isObject()) {
1463     JS_ReportErrorASCII(cx, "addIntlExtras must be passed an object");
1464     return false;
1465   }
1466   JS::RootedObject intl(cx, &args[0].toObject());
1467 
1468   static const JSFunctionSpec funcs[] = {
1469       JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0),
1470       JS_SELF_HOSTED_FN("getLocaleInfo", "Intl_getLocaleInfo", 1, 0),
1471       JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0),
1472       JS_FS_END};
1473 
1474   if (!JS_DefineFunctions(cx, intl, funcs)) {
1475     return false;
1476   }
1477 
1478   if (!JS::AddMozDateTimeFormatConstructor(cx, intl)) {
1479     return false;
1480   }
1481 
1482   if (!JS::AddMozDisplayNamesConstructor(cx, intl)) {
1483     return false;
1484   }
1485 
1486   args.rval().setUndefined();
1487   return true;
1488 }
1489 #endif  // JS_HAS_INTL_API
1490 
EvalUtf8AndPrint(JSContext * cx,const char * bytes,size_t length,int lineno,bool compileOnly)1491 [[nodiscard]] static bool EvalUtf8AndPrint(JSContext* cx, const char* bytes,
1492                                            size_t length, int lineno,
1493                                            bool compileOnly) {
1494   // Eval.
1495   JS::CompileOptions options(cx);
1496   options.setIntroductionType("js shell interactive")
1497       .setIsRunOnce(true)
1498       .setFileAndLine("typein", lineno);
1499 
1500   JS::SourceText<Utf8Unit> srcBuf;
1501   if (!srcBuf.init(cx, bytes, length, JS::SourceOwnership::Borrowed)) {
1502     return false;
1503   }
1504 
1505   RootedScript script(cx, JS::Compile(cx, options, srcBuf));
1506   if (!script) {
1507     return false;
1508   }
1509   if (compileOnly) {
1510     return true;
1511   }
1512   RootedValue result(cx);
1513   if (!JS_ExecuteScript(cx, script, &result)) {
1514     return false;
1515   }
1516 
1517   if (!result.isUndefined() && gOutFile->isOpen()) {
1518     // Print.
1519     RootedString str(cx, JS_ValueToSource(cx, result));
1520     if (!str) {
1521       return false;
1522     }
1523 
1524     UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
1525     if (!utf8chars) {
1526       return false;
1527     }
1528     fprintf(gOutFile->fp, "%s\n", utf8chars.get());
1529   }
1530   return true;
1531 }
1532 
ReadEvalPrintLoop(JSContext * cx,FILE * in,bool compileOnly)1533 [[nodiscard]] static bool ReadEvalPrintLoop(JSContext* cx, FILE* in,
1534                                             bool compileOnly) {
1535   ShellContext* sc = GetShellContext(cx);
1536   int lineno = 1;
1537   bool hitEOF = false;
1538 
1539   do {
1540     /*
1541      * Accumulate lines until we get a 'compilable unit' - one that either
1542      * generates an error (before running out of source) or that compiles
1543      * cleanly.  This should be whenever we get a complete statement that
1544      * coincides with the end of a line.
1545      */
1546     int startline = lineno;
1547     typedef Vector<char, 32> CharBuffer;
1548     RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
1549     CharBuffer buffer(cx);
1550     do {
1551       ScheduleWatchdog(cx, -1);
1552       sc->serviceInterrupt = false;
1553       errno = 0;
1554 
1555       mozilla::UniqueFreePtr<char[]> line =
1556           GetLine(in, startline == lineno ? "js> " : "");
1557       if (!line) {
1558         if (errno) {
1559           /*
1560            * Use Latin1 variant here because strerror(errno)'s
1561            * encoding depends on the user's C locale.
1562            */
1563           JS_ReportErrorLatin1(cx, "%s", strerror(errno));
1564           return false;
1565         }
1566         hitEOF = true;
1567         break;
1568       }
1569 
1570       if (!buffer.append(line.get(), strlen(line.get())) ||
1571           !buffer.append('\n')) {
1572         return false;
1573       }
1574 
1575       lineno++;
1576       if (!ScheduleWatchdog(cx, sc->timeoutInterval)) {
1577         hitEOF = true;
1578         break;
1579       }
1580     } while (!JS_Utf8BufferIsCompilableUnit(cx, cx->global(), buffer.begin(),
1581                                             buffer.length()));
1582 
1583     if (hitEOF && buffer.empty()) {
1584       break;
1585     }
1586 
1587     {
1588       // Report exceptions but keep going.
1589       AutoReportException are(cx);
1590       (void)EvalUtf8AndPrint(cx, buffer.begin(), buffer.length(), startline,
1591                              compileOnly);
1592     }
1593 
1594     // If a let or const fail to initialize they will remain in an unusable
1595     // without further intervention. This call cleans up the global scope,
1596     // setting uninitialized lexicals to undefined so that they may still
1597     // be used. This behavior is _only_ acceptable in the context of the repl.
1598     if (JS::ForceLexicalInitialization(cx, globalLexical) &&
1599         gErrFile->isOpen()) {
1600       fputs(
1601           "Warning: According to the standard, after the above exception,\n"
1602           "Warning: the global bindings should be permanently uninitialized.\n"
1603           "Warning: We have non-standard-ly initialized them to `undefined`"
1604           "for you.\nWarning: This nicety only happens in the JS shell.\n",
1605           stderr);
1606     }
1607 
1608     RunShellJobs(cx);
1609   } while (!hitEOF && !sc->quitting);
1610 
1611   if (gOutFile->isOpen()) {
1612     fprintf(gOutFile->fp, "\n");
1613   }
1614 
1615   return true;
1616 }
1617 
1618 enum FileKind {
1619   FileScript,       // UTF-8, directly parsed as such
1620   FileScriptUtf16,  // FileScript, but inflate to UTF-16 before parsing
1621   FileModule,
1622 };
1623 
ReportCantOpenErrorUnknownEncoding(JSContext * cx,const char * filename)1624 static void ReportCantOpenErrorUnknownEncoding(JSContext* cx,
1625                                                const char* filename) {
1626   /*
1627    * Filenames are in some random system encoding.  *Probably* it's UTF-8,
1628    * but no guarantees.
1629    *
1630    * strerror(errno)'s encoding, in contrast, depends on the user's C locale.
1631    *
1632    * Latin-1 is possibly wrong for both of these -- but at least if it's
1633    * wrong it'll produce mojibake *safely*.  Run with Latin-1 til someone
1634    * complains.
1635    */
1636   JS_ReportErrorNumberLatin1(cx, my_GetErrorMessage, nullptr, JSSMSG_CANT_OPEN,
1637                              filename, strerror(errno));
1638 }
1639 
Process(JSContext * cx,const char * filename,bool forceTTY,FileKind kind)1640 [[nodiscard]] static bool Process(JSContext* cx, const char* filename,
1641                                   bool forceTTY, FileKind kind) {
1642   FILE* file;
1643   if (forceTTY || !filename || strcmp(filename, "-") == 0) {
1644     file = stdin;
1645   } else {
1646     file = fopen(filename, "rb");
1647     if (!file) {
1648       ReportCantOpenErrorUnknownEncoding(cx, filename);
1649       return false;
1650     }
1651   }
1652   AutoCloseFile autoClose(file);
1653 
1654   if (!forceTTY && !isatty(fileno(file))) {
1655     // It's not interactive - just execute it.
1656     switch (kind) {
1657       case FileScript:
1658         if (!RunFile(cx, filename, file, CompileUtf8::DontInflate,
1659                      compileOnly)) {
1660           return false;
1661         }
1662         break;
1663       case FileScriptUtf16:
1664         if (!RunFile(cx, filename, file, CompileUtf8::InflateToUtf16,
1665                      compileOnly)) {
1666           return false;
1667         }
1668         break;
1669       case FileModule:
1670         if (!RunModule(cx, filename, compileOnly)) {
1671           return false;
1672         }
1673         break;
1674       default:
1675         MOZ_CRASH("Impossible FileKind!");
1676     }
1677   } else {
1678     // It's an interactive filehandle; drop into read-eval-print loop.
1679     MOZ_ASSERT(kind == FileScript);
1680     if (!ReadEvalPrintLoop(cx, file, compileOnly)) {
1681       return false;
1682     }
1683   }
1684   return true;
1685 }
1686 
1687 #ifdef XP_WIN
1688 #  define GET_FD_FROM_FILE(a) int(_get_osfhandle(fileno(a)))
1689 #else
1690 #  define GET_FD_FROM_FILE(a) fileno(a)
1691 #endif
1692 
freeExternalCallback(void * contents,void * userData)1693 static void freeExternalCallback(void* contents, void* userData) {
1694   MOZ_ASSERT(!userData);
1695   js_free(contents);
1696 }
1697 
CreateExternalArrayBuffer(JSContext * cx,unsigned argc,Value * vp)1698 static bool CreateExternalArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
1699   CallArgs args = CallArgsFromVp(argc, vp);
1700   if (args.length() != 1) {
1701     JS_ReportErrorNumberASCII(
1702         cx, my_GetErrorMessage, nullptr,
1703         args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
1704         "createExternalArrayBuffer");
1705     return false;
1706   }
1707 
1708   int32_t bytes = 0;
1709   if (!ToInt32(cx, args[0], &bytes)) {
1710     return false;
1711   }
1712 
1713   if (bytes <= 0) {
1714     JS_ReportErrorASCII(cx, "Size must be positive");
1715     return false;
1716   }
1717 
1718   void* buffer = js_malloc(bytes);
1719   if (!buffer) {
1720     JS_ReportOutOfMemory(cx);
1721     return false;
1722   }
1723 
1724   RootedObject arrayBuffer(
1725       cx, JS::NewExternalArrayBuffer(cx, bytes, buffer, &freeExternalCallback));
1726   if (!arrayBuffer) {
1727     return false;
1728   }
1729 
1730   args.rval().setObject(*arrayBuffer);
1731   return true;
1732 }
1733 
CreateMappedArrayBuffer(JSContext * cx,unsigned argc,Value * vp)1734 static bool CreateMappedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
1735   CallArgs args = CallArgsFromVp(argc, vp);
1736 
1737   if (args.length() < 1 || args.length() > 3) {
1738     JS_ReportErrorNumberASCII(
1739         cx, my_GetErrorMessage, nullptr,
1740         args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
1741         "createMappedArrayBuffer");
1742     return false;
1743   }
1744 
1745   RootedString rawFilenameStr(cx, JS::ToString(cx, args[0]));
1746   if (!rawFilenameStr) {
1747     return false;
1748   }
1749   // It's a little bizarre to resolve relative to the script, but for testing
1750   // I need a file at a known location, and the only good way I know of to do
1751   // that right now is to include it in the repo alongside the test script.
1752   // Bug 944164 would introduce an alternative.
1753   JSString* filenameStr = ResolvePath(cx, rawFilenameStr, ScriptRelative);
1754   if (!filenameStr) {
1755     return false;
1756   }
1757   UniqueChars filename = JS_EncodeStringToLatin1(cx, filenameStr);
1758   if (!filename) {
1759     return false;
1760   }
1761 
1762   uint32_t offset = 0;
1763   if (args.length() >= 2) {
1764     if (!JS::ToUint32(cx, args[1], &offset)) {
1765       return false;
1766     }
1767   }
1768 
1769   bool sizeGiven = false;
1770   uint32_t size;
1771   if (args.length() >= 3) {
1772     if (!JS::ToUint32(cx, args[2], &size)) {
1773       return false;
1774     }
1775     sizeGiven = true;
1776     if (size == 0) {
1777       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1778                                 JSMSG_BAD_ARRAY_LENGTH);
1779       return false;
1780     }
1781   }
1782 
1783   FILE* file = fopen(filename.get(), "rb");
1784   if (!file) {
1785     ReportCantOpenErrorUnknownEncoding(cx, filename.get());
1786     return false;
1787   }
1788   AutoCloseFile autoClose(file);
1789 
1790   struct stat st;
1791   if (fstat(fileno(file), &st) < 0) {
1792     JS_ReportErrorASCII(cx, "Unable to stat file");
1793     return false;
1794   }
1795 
1796   if ((st.st_mode & S_IFMT) != S_IFREG) {
1797     JS_ReportErrorASCII(cx, "Path is not a regular file");
1798     return false;
1799   }
1800 
1801   if (!sizeGiven) {
1802     if (off_t(offset) >= st.st_size) {
1803       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1804                                 JSMSG_OFFSET_LARGER_THAN_FILESIZE);
1805       return false;
1806     }
1807     size = st.st_size - offset;
1808   }
1809 
1810   void* contents =
1811       JS::CreateMappedArrayBufferContents(GET_FD_FROM_FILE(file), offset, size);
1812   if (!contents) {
1813     JS_ReportErrorASCII(cx,
1814                         "failed to allocate mapped array buffer contents "
1815                         "(possibly due to bad alignment)");
1816     return false;
1817   }
1818 
1819   RootedObject obj(cx,
1820                    JS::NewMappedArrayBufferWithContents(cx, size, contents));
1821   if (!obj) {
1822     return false;
1823   }
1824 
1825   args.rval().setObject(*obj);
1826   return true;
1827 }
1828 
1829 #undef GET_FD_FROM_FILE
1830 
AddPromiseReactions(JSContext * cx,unsigned argc,Value * vp)1831 static bool AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp) {
1832   CallArgs args = CallArgsFromVp(argc, vp);
1833 
1834   if (args.length() != 3) {
1835     JS_ReportErrorNumberASCII(
1836         cx, my_GetErrorMessage, nullptr,
1837         args.length() < 3 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
1838         "addPromiseReactions");
1839     return false;
1840   }
1841 
1842   RootedObject promise(cx);
1843   if (args[0].isObject()) {
1844     promise = &args[0].toObject();
1845   }
1846 
1847   if (!promise || !JS::IsPromiseObject(promise)) {
1848     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
1849                               JSSMSG_INVALID_ARGS, "addPromiseReactions");
1850     return false;
1851   }
1852 
1853   RootedObject onResolve(cx);
1854   if (args[1].isObject()) {
1855     onResolve = &args[1].toObject();
1856   }
1857 
1858   RootedObject onReject(cx);
1859   if (args[2].isObject()) {
1860     onReject = &args[2].toObject();
1861   }
1862 
1863   if (!onResolve || !onResolve->is<JSFunction>() || !onReject ||
1864       !onReject->is<JSFunction>()) {
1865     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
1866                               JSSMSG_INVALID_ARGS, "addPromiseReactions");
1867     return false;
1868   }
1869 
1870   return JS::AddPromiseReactions(cx, promise, onResolve, onReject);
1871 }
1872 
IgnoreUnhandledRejections(JSContext * cx,unsigned argc,Value * vp)1873 static bool IgnoreUnhandledRejections(JSContext* cx, unsigned argc, Value* vp) {
1874   CallArgs args = CallArgsFromVp(argc, vp);
1875 
1876   ShellContext* sc = GetShellContext(cx);
1877   sc->trackUnhandledRejections = false;
1878 
1879   args.rval().setUndefined();
1880   return true;
1881 }
1882 
Options(JSContext * cx,unsigned argc,Value * vp)1883 static bool Options(JSContext* cx, unsigned argc, Value* vp) {
1884   CallArgs args = CallArgsFromVp(argc, vp);
1885 
1886   JS::ContextOptions oldContextOptions = JS::ContextOptionsRef(cx);
1887   for (unsigned i = 0; i < args.length(); i++) {
1888     RootedString str(cx, JS::ToString(cx, args[i]));
1889     if (!str) {
1890       return false;
1891     }
1892 
1893     RootedLinearString opt(cx, str->ensureLinear(cx));
1894     if (!opt) {
1895       return false;
1896     }
1897 
1898     if (StringEqualsLiteral(opt, "throw_on_asmjs_validation_failure")) {
1899       JS::ContextOptionsRef(cx).toggleThrowOnAsmJSValidationFailure();
1900     } else if (StringEqualsLiteral(opt, "strict_mode")) {
1901       JS::ContextOptionsRef(cx).toggleStrictMode();
1902     } else {
1903       UniqueChars optChars = QuoteString(cx, opt, '"');
1904       if (!optChars) {
1905         return false;
1906       }
1907 
1908       JS_ReportErrorASCII(cx,
1909                           "unknown option name %s."
1910                           " The valid names are "
1911                           "throw_on_asmjs_validation_failure and strict_mode.",
1912                           optChars.get());
1913       return false;
1914     }
1915   }
1916 
1917   UniqueChars names = DuplicateString("");
1918   bool found = false;
1919   if (names && oldContextOptions.throwOnAsmJSValidationFailure()) {
1920     names = JS_sprintf_append(std::move(names), "%s%s", found ? "," : "",
1921                               "throw_on_asmjs_validation_failure");
1922     found = true;
1923   }
1924   if (names && oldContextOptions.strictMode()) {
1925     names = JS_sprintf_append(std::move(names), "%s%s", found ? "," : "",
1926                               "strict_mode");
1927     found = true;
1928   }
1929   if (!names) {
1930     JS_ReportOutOfMemory(cx);
1931     return false;
1932   }
1933 
1934   JSString* str = JS_NewStringCopyZ(cx, names.get());
1935   if (!str) {
1936     return false;
1937   }
1938   args.rval().setString(str);
1939   return true;
1940 }
1941 
LoadScript(JSContext * cx,unsigned argc,Value * vp,bool scriptRelative)1942 static bool LoadScript(JSContext* cx, unsigned argc, Value* vp,
1943                        bool scriptRelative) {
1944   CallArgs args = CallArgsFromVp(argc, vp);
1945 
1946   RootedString str(cx);
1947   for (unsigned i = 0; i < args.length(); i++) {
1948     str = JS::ToString(cx, args[i]);
1949     if (!str) {
1950       JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
1951                                 JSSMSG_INVALID_ARGS, "load");
1952       return false;
1953     }
1954 
1955     str = ResolvePath(cx, str, scriptRelative ? ScriptRelative : RootRelative);
1956     if (!str) {
1957       JS_ReportErrorASCII(cx, "unable to resolve path");
1958       return false;
1959     }
1960 
1961     UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
1962     if (!filename) {
1963       return false;
1964     }
1965 
1966     errno = 0;
1967 
1968     CompileOptions opts(cx);
1969     opts.setIntroductionType("js shell load")
1970         .setIsRunOnce(true)
1971         .setNoScriptRval(true);
1972 
1973     RootedValue unused(cx);
1974     if (!(compileOnly
1975               ? JS::CompileUtf8Path(cx, opts, filename.get()) != nullptr
1976               : JS::EvaluateUtf8Path(cx, opts, filename.get(), &unused))) {
1977       return false;
1978     }
1979   }
1980 
1981   args.rval().setUndefined();
1982   return true;
1983 }
1984 
Load(JSContext * cx,unsigned argc,Value * vp)1985 static bool Load(JSContext* cx, unsigned argc, Value* vp) {
1986   return LoadScript(cx, argc, vp, false);
1987 }
1988 
LoadScriptRelativeToScript(JSContext * cx,unsigned argc,Value * vp)1989 static bool LoadScriptRelativeToScript(JSContext* cx, unsigned argc,
1990                                        Value* vp) {
1991   return LoadScript(cx, argc, vp, true);
1992 }
1993 
ParseDebugMetadata(JSContext * cx,HandleObject opts,MutableHandleValue privateValue,MutableHandleString elementAttributeName)1994 static bool ParseDebugMetadata(JSContext* cx, HandleObject opts,
1995                                MutableHandleValue privateValue,
1996                                MutableHandleString elementAttributeName) {
1997   RootedValue v(cx);
1998   RootedString s(cx);
1999 
2000   if (!JS_GetProperty(cx, opts, "element", &v)) {
2001     return false;
2002   }
2003   if (v.isObject()) {
2004     RootedObject infoObject(cx, CreateScriptPrivate(cx));
2005     if (!infoObject) {
2006       return false;
2007     }
2008     RootedValue elementValue(cx, v);
2009     if (!JS_WrapValue(cx, &elementValue)) {
2010       return false;
2011     }
2012     if (!JS_DefineProperty(cx, infoObject, "element", elementValue, 0)) {
2013       return false;
2014     }
2015     privateValue.set(ObjectValue(*infoObject));
2016   }
2017 
2018   if (!JS_GetProperty(cx, opts, "elementAttributeName", &v)) {
2019     return false;
2020   }
2021   if (!v.isUndefined()) {
2022     s = ToString(cx, v);
2023     if (!s) {
2024       return false;
2025     }
2026     elementAttributeName.set(s);
2027   }
2028 
2029   return true;
2030 }
2031 
my_LargeAllocFailCallback()2032 static void my_LargeAllocFailCallback() {
2033   JSContext* cx = TlsContext.get();
2034   if (!cx || cx->isHelperThreadContext()) {
2035     return;
2036   }
2037 
2038   MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
2039 
2040   JS::PrepareForFullGC(cx);
2041   cx->runtime()->gc.gc(JS::GCOptions::Shrink,
2042                        JS::GCReason::SHARED_MEMORY_LIMIT);
2043 }
2044 
2045 static const uint32_t CacheEntry_SOURCE = 0;
2046 static const uint32_t CacheEntry_BYTECODE = 1;
2047 static const uint32_t CacheEntry_KIND = 2;
2048 static const uint32_t CacheEntry_OPTIONS = 3;
2049 
2050 enum class BytecodeCacheKind : uint32_t {
2051   Undefined = 0,
2052   Script,
2053   Stencil,
2054 };
2055 
2056 // Some compile options can't be combined differently between save and load.
2057 //
2058 // CacheEntries store a CacheOption set, and on load an exception is thrown
2059 // if the entries are incompatible.
2060 
2061 enum CacheOptions : uint32_t {
2062   IsRunOnce,
2063   NoScriptRval,
2064   Global,
2065   NonSyntactic,
2066   SourceIsLazy,
2067   ForceFullParse,
2068 };
2069 
2070 struct CacheOptionSet : public mozilla::EnumSet<CacheOptions> {
2071   using mozilla::EnumSet<CacheOptions>::EnumSet;
2072 
CacheOptionSetCacheOptionSet2073   explicit CacheOptionSet(const CompileOptions& options) : EnumSet() {
2074     initFromOptions(options);
2075   }
2076 
initFromOptionsCacheOptionSet2077   void initFromOptions(const CompileOptions& options) {
2078     if (options.noScriptRval) {
2079       *this += CacheOptions::NoScriptRval;
2080     }
2081     if (options.isRunOnce) {
2082       *this += CacheOptions::IsRunOnce;
2083     }
2084     if (options.sourceIsLazy) {
2085       *this += CacheOptions::SourceIsLazy;
2086     }
2087     if (options.forceFullParse()) {
2088       *this += CacheOptions::ForceFullParse;
2089     }
2090     if (options.nonSyntacticScope) {
2091       *this += CacheOptions::NonSyntactic;
2092     }
2093   }
2094 };
2095 
CacheOptionsCompatible(const CacheOptionSet & a,const CacheOptionSet & b)2096 static bool CacheOptionsCompatible(const CacheOptionSet& a,
2097                                    const CacheOptionSet& b) {
2098   // If the options are identical, they are trivially compatible.
2099   return a == b;
2100 }
2101 
2102 static const JSClass CacheEntry_class = {"CacheEntryObject",
2103                                          JSCLASS_HAS_RESERVED_SLOTS(4)};
2104 
CacheEntry(JSContext * cx,unsigned argc,JS::Value * vp)2105 static bool CacheEntry(JSContext* cx, unsigned argc, JS::Value* vp) {
2106   CallArgs args = CallArgsFromVp(argc, vp);
2107 
2108   if (args.length() != 1 || !args[0].isString()) {
2109     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
2110                               JSSMSG_INVALID_ARGS, "CacheEntry");
2111     return false;
2112   }
2113 
2114   RootedObject obj(cx, JS_NewObject(cx, &CacheEntry_class));
2115   if (!obj) {
2116     return false;
2117   }
2118 
2119   JS::SetReservedSlot(obj, CacheEntry_SOURCE, args[0]);
2120   JS::SetReservedSlot(obj, CacheEntry_BYTECODE, UndefinedValue());
2121   JS::SetReservedSlot(obj, CacheEntry_KIND,
2122                       Int32Value(int32_t(BytecodeCacheKind::Undefined)));
2123 
2124   // Fill in empty option set.
2125   CacheOptionSet defaultOptions;
2126   JS::SetReservedSlot(obj, CacheEntry_OPTIONS,
2127                       Int32Value(defaultOptions.serialize()));
2128 
2129   args.rval().setObject(*obj);
2130   return true;
2131 }
2132 
CacheEntry_isCacheEntry(JSObject * cache)2133 static bool CacheEntry_isCacheEntry(JSObject* cache) {
2134   return cache->hasClass(&CacheEntry_class);
2135 }
2136 
CacheEntry_getSource(JSContext * cx,HandleObject cache)2137 static JSString* CacheEntry_getSource(JSContext* cx, HandleObject cache) {
2138   MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
2139   Value v = JS::GetReservedSlot(cache, CacheEntry_SOURCE);
2140   if (!v.isString()) {
2141     JS_ReportErrorASCII(
2142         cx, "CacheEntry_getSource: Unexpected type of source reserved slot.");
2143     return nullptr;
2144   }
2145 
2146   return v.toString();
2147 }
2148 
CacheEntry_getKind(JSContext * cx,HandleObject cache)2149 static BytecodeCacheKind CacheEntry_getKind(JSContext* cx, HandleObject cache) {
2150   MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
2151   Value v = JS::GetReservedSlot(cache, CacheEntry_KIND);
2152   return BytecodeCacheKind(v.toInt32());
2153 }
2154 
CacheEntry_setKind(JSContext * cx,HandleObject cache,BytecodeCacheKind kind)2155 static void CacheEntry_setKind(JSContext* cx, HandleObject cache,
2156                                BytecodeCacheKind kind) {
2157   MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
2158   JS::SetReservedSlot(cache, CacheEntry_KIND, Int32Value(int32_t(kind)));
2159 }
2160 
CacheEntry_compatible(JSContext * cx,HandleObject cache,const CacheOptionSet & currentOptionSet)2161 static bool CacheEntry_compatible(JSContext* cx, HandleObject cache,
2162                                   const CacheOptionSet& currentOptionSet) {
2163   CacheOptionSet cacheEntryOptions;
2164   MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
2165   Value v = JS::GetReservedSlot(cache, CacheEntry_OPTIONS);
2166   cacheEntryOptions.deserialize(v.toInt32());
2167   if (!CacheOptionsCompatible(cacheEntryOptions, currentOptionSet)) {
2168     JS_ReportErrorASCII(cx,
2169                         "CacheEntry_compatible: Incompatible cache contents");
2170     return false;
2171   }
2172   return true;
2173 }
2174 
CacheEntry_getBytecode(JSContext * cx,HandleObject cache,size_t * length)2175 static uint8_t* CacheEntry_getBytecode(JSContext* cx, HandleObject cache,
2176                                        size_t* length) {
2177   MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
2178   Value v = JS::GetReservedSlot(cache, CacheEntry_BYTECODE);
2179   if (!v.isObject() || !v.toObject().is<ArrayBufferObject>()) {
2180     JS_ReportErrorASCII(
2181         cx,
2182         "CacheEntry_getBytecode: Unexpected type of bytecode reserved slot.");
2183     return nullptr;
2184   }
2185 
2186   ArrayBufferObject* arrayBuffer = &v.toObject().as<ArrayBufferObject>();
2187   *length = arrayBuffer->byteLength();
2188   return arrayBuffer->dataPointer();
2189 }
2190 
CacheEntry_setBytecode(JSContext * cx,HandleObject cache,const CacheOptionSet & cacheOptions,uint8_t * buffer,uint32_t length)2191 static bool CacheEntry_setBytecode(JSContext* cx, HandleObject cache,
2192                                    const CacheOptionSet& cacheOptions,
2193                                    uint8_t* buffer, uint32_t length) {
2194   MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
2195 
2196   using BufferContents = ArrayBufferObject::BufferContents;
2197 
2198   BufferContents contents = BufferContents::createMalloced(buffer);
2199   Rooted<ArrayBufferObject*> arrayBuffer(
2200       cx, ArrayBufferObject::createForContents(cx, length, contents));
2201   if (!arrayBuffer) {
2202     return false;
2203   }
2204 
2205   JS::SetReservedSlot(cache, CacheEntry_BYTECODE, ObjectValue(*arrayBuffer));
2206   JS::SetReservedSlot(cache, CacheEntry_OPTIONS,
2207                       Int32Value(cacheOptions.serialize()));
2208   return true;
2209 }
2210 
ConvertTranscodeResultToJSException(JSContext * cx,JS::TranscodeResult rv)2211 static bool ConvertTranscodeResultToJSException(JSContext* cx,
2212                                                 JS::TranscodeResult rv) {
2213   switch (rv) {
2214     case JS::TranscodeResult::Ok:
2215       return true;
2216 
2217     default:
2218       [[fallthrough]];
2219     case JS::TranscodeResult::Failure:
2220       MOZ_ASSERT(!cx->isExceptionPending());
2221       JS_ReportErrorASCII(cx, "generic warning");
2222       return false;
2223     case JS::TranscodeResult::Failure_BadBuildId:
2224       MOZ_ASSERT(!cx->isExceptionPending());
2225       JS_ReportErrorASCII(cx, "the build-id does not match");
2226       return false;
2227     case JS::TranscodeResult::Failure_RunOnceNotSupported:
2228       MOZ_ASSERT(!cx->isExceptionPending());
2229       JS_ReportErrorASCII(cx, "run-once script are not supported by XDR");
2230       return false;
2231     case JS::TranscodeResult::Failure_AsmJSNotSupported:
2232       MOZ_ASSERT(!cx->isExceptionPending());
2233       JS_ReportErrorASCII(cx, "Asm.js is not supported by XDR");
2234       return false;
2235     case JS::TranscodeResult::Failure_BadDecode:
2236       MOZ_ASSERT(!cx->isExceptionPending());
2237       JS_ReportErrorASCII(cx, "XDR data corruption");
2238       return false;
2239     case JS::TranscodeResult::Failure_WrongCompileOption:
2240       MOZ_ASSERT(!cx->isExceptionPending());
2241       JS_ReportErrorASCII(
2242           cx, "Compile options differs from Compile options of the encoding");
2243       return false;
2244     case JS::TranscodeResult::Failure_NotInterpretedFun:
2245       MOZ_ASSERT(!cx->isExceptionPending());
2246       JS_ReportErrorASCII(cx,
2247                           "Only interepreted functions are supported by XDR");
2248       return false;
2249 
2250     case JS::TranscodeResult::Throw:
2251       MOZ_ASSERT(cx->isExceptionPending());
2252       return false;
2253   }
2254 }
2255 
Evaluate(JSContext * cx,unsigned argc,Value * vp)2256 static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
2257   CallArgs args = CallArgsFromVp(argc, vp);
2258 
2259   if (args.length() < 1 || args.length() > 2) {
2260     JS_ReportErrorNumberASCII(
2261         cx, my_GetErrorMessage, nullptr,
2262         args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
2263         "evaluate");
2264     return false;
2265   }
2266 
2267   RootedString code(cx, nullptr);
2268   RootedObject cacheEntry(cx, nullptr);
2269   if (args[0].isString()) {
2270     code = args[0].toString();
2271   } else if (args[0].isObject() &&
2272              CacheEntry_isCacheEntry(&args[0].toObject())) {
2273     cacheEntry = &args[0].toObject();
2274     code = CacheEntry_getSource(cx, cacheEntry);
2275     if (!code) {
2276       return false;
2277     }
2278   }
2279 
2280   if (!code || (args.length() == 2 && args[1].isPrimitive())) {
2281     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
2282                               JSSMSG_INVALID_ARGS, "evaluate");
2283     return false;
2284   }
2285 
2286   CompileOptions options(cx);
2287   UniqueChars fileNameBytes;
2288   RootedString displayURL(cx);
2289   RootedString sourceMapURL(cx);
2290   RootedObject global(cx, nullptr);
2291   bool catchTermination = false;
2292   bool loadBytecode = false;
2293   bool saveBytecode = false;
2294   bool saveIncrementalBytecode = false;
2295   bool transcodeOnly = false;
2296   bool assertEqBytecode = false;
2297   JS::RootedObjectVector envChain(cx);
2298   RootedObject callerGlobal(cx, cx->global());
2299   CacheOptionSet optionSet;
2300 
2301   options.setIntroductionType("js shell evaluate")
2302       .setFileAndLine("@evaluate", 1)
2303       .setdeferDebugMetadata();
2304 
2305   global = JS::CurrentGlobalOrNull(cx);
2306   MOZ_ASSERT(global);
2307 
2308   RootedValue privateValue(cx);
2309   RootedString elementAttributeName(cx);
2310 
2311   if (args.length() == 2) {
2312     RootedObject opts(cx, &args[1].toObject());
2313     if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
2314       return false;
2315     }
2316     if (!ParseDebugMetadata(cx, opts, &privateValue, &elementAttributeName)) {
2317       return false;
2318     }
2319 
2320     RootedValue v(cx);
2321     if (!JS_GetProperty(cx, opts, "displayURL", &v)) {
2322       return false;
2323     }
2324     if (!v.isUndefined()) {
2325       displayURL = ToString(cx, v);
2326       if (!displayURL) {
2327         return false;
2328       }
2329     }
2330 
2331     if (!JS_GetProperty(cx, opts, "sourceMapURL", &v)) {
2332       return false;
2333     }
2334     if (!v.isUndefined()) {
2335       sourceMapURL = ToString(cx, v);
2336       if (!sourceMapURL) {
2337         return false;
2338       }
2339     }
2340 
2341     if (!JS_GetProperty(cx, opts, "catchTermination", &v)) {
2342       return false;
2343     }
2344     if (!v.isUndefined()) {
2345       catchTermination = ToBoolean(v);
2346     }
2347 
2348     if (!JS_GetProperty(cx, opts, "loadBytecode", &v)) {
2349       return false;
2350     }
2351     if (!v.isUndefined()) {
2352       loadBytecode = ToBoolean(v);
2353     }
2354 
2355     if (!JS_GetProperty(cx, opts, "saveBytecode", &v)) {
2356       return false;
2357     }
2358     if (!v.isUndefined()) {
2359       saveBytecode = ToBoolean(v);
2360     }
2361 
2362     if (!JS_GetProperty(cx, opts, "saveIncrementalBytecode", &v)) {
2363       return false;
2364     }
2365     if (!v.isUndefined()) {
2366       saveIncrementalBytecode = ToBoolean(v);
2367     }
2368 
2369     if (!JS_GetProperty(cx, opts, "transcodeOnly", &v)) {
2370       return false;
2371     }
2372     if (!v.isUndefined()) {
2373       transcodeOnly = ToBoolean(v);
2374     }
2375 
2376     if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v)) {
2377       return false;
2378     }
2379     if (!v.isUndefined()) {
2380       assertEqBytecode = ToBoolean(v);
2381     }
2382 
2383     if (!JS_GetProperty(cx, opts, "envChainObject", &v)) {
2384       return false;
2385     }
2386     if (!v.isUndefined()) {
2387       if (!v.isObject()) {
2388         JS_ReportErrorNumberASCII(
2389             cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
2390             "\"envChainObject\" passed to evaluate()", "not an object");
2391         return false;
2392       } else if (v.toObject().is<GlobalObject>()) {
2393         JS_ReportErrorASCII(
2394             cx,
2395             "\"envChainObject\" passed to evaluate() should not be a global");
2396         return false;
2397       } else if (!envChain.append(&v.toObject())) {
2398         JS_ReportOutOfMemory(cx);
2399         return false;
2400       }
2401     }
2402 
2403     // We cannot load or save the bytecode if we have no object where the
2404     // bytecode cache is stored.
2405     if (loadBytecode || saveBytecode || saveIncrementalBytecode) {
2406       if (!cacheEntry) {
2407         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
2408                                   JSSMSG_INVALID_ARGS, "evaluate");
2409         return false;
2410       }
2411       if (saveIncrementalBytecode && saveBytecode) {
2412         JS_ReportErrorASCII(
2413             cx,
2414             "saveIncrementalBytecode and saveBytecode cannot be used"
2415             " at the same time.");
2416         return false;
2417       }
2418       if (saveIncrementalBytecode && js::UseOffThreadParseGlobal()) {
2419         JS_ReportErrorASCII(
2420             cx, "saveIncrementalBytecode cannot be used with legacy XDR.");
2421         return false;
2422       }
2423     }
2424 
2425     if (saveIncrementalBytecode && assertEqBytecode) {
2426       JS_ReportErrorASCII(
2427           cx, "saveIncrementalBytecode cannot be used with assertEqBytecode.");
2428       return false;
2429     }
2430 
2431     // NOTE: Check custom "global" after handling all CompileOption-related
2432     //       properties.
2433     if (!JS_GetProperty(cx, opts, "global", &v)) {
2434       return false;
2435     }
2436     if (!v.isUndefined()) {
2437       if (v.isObject()) {
2438         global = js::CheckedUnwrapDynamic(&v.toObject(), cx,
2439                                           /* stopAtWindowProxy = */ false);
2440         if (!global) {
2441           return false;
2442         }
2443       }
2444       if (!global || !(JS::GetClass(global)->flags & JSCLASS_IS_GLOBAL)) {
2445         JS_ReportErrorNumberASCII(
2446             cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
2447             "\"global\" passed to evaluate()", "not a global object");
2448         return false;
2449       }
2450 
2451       // Validate the consistency between the passed CompileOptions and
2452       // global's option.
2453       {
2454         JSAutoRealm ar(cx, global);
2455         JS::CompileOptions globalOptions(cx);
2456 
2457         // If the passed global discards source, delazification isn't allowed.
2458         // The CompileOption should discard source as well.
2459         if (globalOptions.discardSource && !options.discardSource) {
2460           JS_ReportErrorASCII(cx, "discardSource option mismatch");
2461           return false;
2462         }
2463       }
2464     }
2465   }
2466 
2467   if (envChain.length() != 0) {
2468     {
2469       // Wrap the envChainObject list into target realm.
2470       JSAutoRealm ar(cx, global);
2471       for (size_t i = 0; i < envChain.length(); ++i) {
2472         if (!JS_WrapObject(cx, envChain[i])) {
2473           return false;
2474         }
2475       }
2476     }
2477 
2478     options.setNonSyntacticScope(true);
2479   }
2480 
2481   optionSet.initFromOptions(options);
2482 
2483   AutoStableStringChars codeChars(cx);
2484   if (!codeChars.initTwoByte(cx, code)) {
2485     return false;
2486   }
2487 
2488   JS::TranscodeBuffer loadBuffer;
2489   JS::TranscodeBuffer saveBuffer;
2490   BytecodeCacheKind loadCacheKind = BytecodeCacheKind::Undefined;
2491   BytecodeCacheKind saveCacheKind = BytecodeCacheKind::Undefined;
2492 
2493   if (loadBytecode) {
2494     size_t loadLength = 0;
2495     uint8_t* loadData = nullptr;
2496 
2497     if (!CacheEntry_compatible(cx, cacheEntry, optionSet)) {
2498       return false;
2499     }
2500 
2501     loadData = CacheEntry_getBytecode(cx, cacheEntry, &loadLength);
2502     if (!loadData) {
2503       return false;
2504     }
2505     if (!loadBuffer.append(loadData, loadLength)) {
2506       JS_ReportOutOfMemory(cx);
2507       return false;
2508     }
2509     loadCacheKind = CacheEntry_getKind(cx, cacheEntry);
2510   }
2511 
2512   {
2513     JSAutoRealm ar(cx, global);
2514     RootedScript script(cx);
2515 
2516     {
2517       if (loadBytecode) {
2518         JS::TranscodeResult rv;
2519         if (saveIncrementalBytecode) {
2520           bool useStencilXDR = !options.useOffThreadParseGlobal;
2521           if (useStencilXDR) {
2522             if (CacheEntry_getKind(cx, cacheEntry) !=
2523                 BytecodeCacheKind::Stencil) {
2524               // This can happen.
2525               JS_ReportErrorASCII(
2526                   cx,
2527                   "if both loadBytecode and saveIncrementalBytecode are set "
2528                   "and --off-thread-parse-global isn't used, bytecode should "
2529                   "have been saved with saveIncrementalBytecode");
2530               return false;
2531             }
2532           } else {
2533             if (CacheEntry_getKind(cx, cacheEntry) !=
2534                 BytecodeCacheKind::Script) {
2535               // NOTE: This shouldn't happen unless the cache is used across
2536               // processes with different --no-off-thread-parse-global option.
2537               JS_ReportErrorASCII(
2538                   cx,
2539                   "if both loadBytecode and saveIncrementalBytecode are set "
2540                   "and --off-thread-parse-global is used, bytecode "
2541                   "should have been saved with saveBytecode");
2542               return false;
2543             }
2544           }
2545 
2546           rv = JS::DecodeScriptAndStartIncrementalEncoding(cx, options,
2547                                                            loadBuffer, &script);
2548           if (!ConvertTranscodeResultToJSException(cx, rv)) {
2549             return false;
2550           }
2551         } else if (loadCacheKind == BytecodeCacheKind::Script) {
2552           rv = JS::DecodeScript(cx, options, loadBuffer, &script);
2553           if (!ConvertTranscodeResultToJSException(cx, rv)) {
2554             return false;
2555           }
2556         } else {
2557           MOZ_ASSERT(loadCacheKind == BytecodeCacheKind::Stencil);
2558           rv = JS::DecodeScriptMaybeStencil(cx, options, loadBuffer, &script);
2559           if (!ConvertTranscodeResultToJSException(cx, rv)) {
2560             return false;
2561           }
2562         }
2563       } else {
2564         mozilla::Range<const char16_t> chars = codeChars.twoByteRange();
2565         JS::SourceText<char16_t> srcBuf;
2566         if (!srcBuf.init(cx, chars.begin().get(), chars.length(),
2567                          JS::SourceOwnership::Borrowed)) {
2568           return false;
2569         }
2570 
2571         if (saveIncrementalBytecode) {
2572           script = JS::CompileAndStartIncrementalEncoding(cx, options, srcBuf);
2573           if (!script) {
2574             return false;
2575           }
2576         } else {
2577           script = JS::Compile(cx, options, srcBuf);
2578           if (!script) {
2579             return false;
2580           }
2581         }
2582       }
2583     }
2584 
2585     if (displayURL && !script->scriptSource()->hasDisplayURL()) {
2586       UniqueTwoByteChars chars = JS_CopyStringCharsZ(cx, displayURL);
2587       if (!chars) {
2588         return false;
2589       }
2590       if (!script->scriptSource()->setDisplayURL(cx, std::move(chars))) {
2591         return false;
2592       }
2593     }
2594     if (sourceMapURL && !script->scriptSource()->hasSourceMapURL()) {
2595       UniqueTwoByteChars chars = JS_CopyStringCharsZ(cx, sourceMapURL);
2596       if (!chars) {
2597         return false;
2598       }
2599       if (!script->scriptSource()->setSourceMapURL(cx, std::move(chars))) {
2600         return false;
2601       }
2602     }
2603 
2604     if (!JS::UpdateDebugMetadata(cx, script, options, privateValue,
2605                                  elementAttributeName, nullptr, nullptr)) {
2606       return false;
2607     }
2608 
2609     if (!transcodeOnly) {
2610       if (!(envChain.empty()
2611                 ? JS_ExecuteScript(cx, script, args.rval())
2612                 : JS_ExecuteScript(cx, envChain, script, args.rval()))) {
2613         if (catchTermination && !JS_IsExceptionPending(cx)) {
2614           JSAutoRealm ar1(cx, callerGlobal);
2615           JSString* str = JS_NewStringCopyZ(cx, "terminated");
2616           if (!str) {
2617             return false;
2618           }
2619           args.rval().setString(str);
2620           return true;
2621         }
2622         return false;
2623       }
2624     }
2625 
2626     // Encode the bytecode after the execution of the script.
2627     if (saveBytecode) {
2628       JS::TranscodeResult rv = JS::EncodeScript(cx, saveBuffer, script);
2629       if (!ConvertTranscodeResultToJSException(cx, rv)) {
2630         return false;
2631       }
2632       saveCacheKind = BytecodeCacheKind::Script;
2633     }
2634 
2635     // Serialize the encoded bytecode, recorded before the execution, into a
2636     // buffer which can be deserialized linearly.
2637     if (saveIncrementalBytecode) {
2638       if (!FinishIncrementalEncoding(cx, script, saveBuffer)) {
2639         return false;
2640       }
2641       if (options.useStencilXDR) {
2642         saveCacheKind = BytecodeCacheKind::Stencil;
2643       } else {
2644         saveCacheKind = BytecodeCacheKind::Script;
2645       }
2646     }
2647   }
2648 
2649   if (saveBytecode || saveIncrementalBytecode) {
2650     // If we are both loading and saving, we assert that we are going to
2651     // replace the current bytecode by the same stream of bytes.
2652     if (loadBytecode && assertEqBytecode) {
2653       if (saveBuffer.length() != loadBuffer.length()) {
2654         char loadLengthStr[16];
2655         SprintfLiteral(loadLengthStr, "%zu", loadBuffer.length());
2656         char saveLengthStr[16];
2657         SprintfLiteral(saveLengthStr, "%zu", saveBuffer.length());
2658 
2659         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
2660                                   JSSMSG_CACHE_EQ_SIZE_FAILED, loadLengthStr,
2661                                   saveLengthStr);
2662         return false;
2663       }
2664 
2665       if (!ArrayEqual(loadBuffer.begin(), saveBuffer.begin(),
2666                       loadBuffer.length())) {
2667         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
2668                                   JSSMSG_CACHE_EQ_CONTENT_FAILED);
2669         return false;
2670       }
2671     }
2672 
2673     size_t saveLength = saveBuffer.length();
2674     if (saveLength >= INT32_MAX) {
2675       JS_ReportErrorASCII(cx, "Cannot save large cache entry content");
2676       return false;
2677     }
2678     uint8_t* saveData = saveBuffer.extractOrCopyRawBuffer();
2679     if (!CacheEntry_setBytecode(cx, cacheEntry, optionSet, saveData,
2680                                 saveLength)) {
2681       js_free(saveData);
2682       return false;
2683     }
2684     MOZ_ASSERT(saveCacheKind != BytecodeCacheKind::Undefined);
2685     CacheEntry_setKind(cx, cacheEntry, saveCacheKind);
2686   }
2687 
2688   return JS_WrapValue(cx, args.rval());
2689 }
2690 
FileAsString(JSContext * cx,JS::HandleString pathnameStr)2691 JSString* js::shell::FileAsString(JSContext* cx, JS::HandleString pathnameStr) {
2692   UniqueChars pathname = JS_EncodeStringToLatin1(cx, pathnameStr);
2693   if (!pathname) {
2694     return nullptr;
2695   }
2696 
2697   FILE* file;
2698 
2699   file = fopen(pathname.get(), "rb");
2700   if (!file) {
2701     ReportCantOpenErrorUnknownEncoding(cx, pathname.get());
2702     return nullptr;
2703   }
2704 
2705   AutoCloseFile autoClose(file);
2706 
2707   struct stat st;
2708   if (fstat(fileno(file), &st) != 0) {
2709     JS_ReportErrorUTF8(cx, "can't stat %s", pathname.get());
2710     return nullptr;
2711   }
2712 
2713   if ((st.st_mode & S_IFMT) != S_IFREG) {
2714     JS_ReportErrorUTF8(cx, "can't read non-regular file %s", pathname.get());
2715     return nullptr;
2716   }
2717 
2718   if (fseek(file, 0, SEEK_END) != 0) {
2719     pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
2720     if (!pathname) {
2721       return nullptr;
2722     }
2723     JS_ReportErrorUTF8(cx, "can't seek end of %s", pathname.get());
2724     return nullptr;
2725   }
2726 
2727   long endPos = ftell(file);
2728   if (endPos < 0) {
2729     JS_ReportErrorUTF8(cx, "can't read length of %s", pathname.get());
2730     return nullptr;
2731   }
2732 
2733   size_t len = endPos;
2734   if (fseek(file, 0, SEEK_SET) != 0) {
2735     pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
2736     if (!pathname) {
2737       return nullptr;
2738     }
2739     JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.get());
2740     return nullptr;
2741   }
2742 
2743   UniqueChars buf(js_pod_malloc<char>(len + 1));
2744   if (!buf) {
2745     JS_ReportErrorUTF8(cx, "out of memory reading %s", pathname.get());
2746     return nullptr;
2747   }
2748 
2749   size_t cc = fread(buf.get(), 1, len, file);
2750   if (cc != len) {
2751     if (ptrdiff_t(cc) < 0) {
2752       ReportCantOpenErrorUnknownEncoding(cx, pathname.get());
2753     } else {
2754       pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
2755       if (!pathname) {
2756         return nullptr;
2757       }
2758       JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.get());
2759     }
2760     return nullptr;
2761   }
2762 
2763   UniqueTwoByteChars ucbuf(
2764       JS::LossyUTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf.get(), len),
2765                                            &len, js::MallocArena)
2766           .get());
2767   if (!ucbuf) {
2768     pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
2769     if (!pathname) {
2770       return nullptr;
2771     }
2772     JS_ReportErrorUTF8(cx, "Invalid UTF-8 in file '%s'", pathname.get());
2773     return nullptr;
2774   }
2775 
2776   return JS_NewUCStringCopyN(cx, ucbuf.get(), len);
2777 }
2778 
2779 /*
2780  * Function to run scripts and return compilation + execution time. Semantics
2781  * are closely modelled after the equivalent function in WebKit, as this is used
2782  * to produce benchmark timings by SunSpider.
2783  */
Run(JSContext * cx,unsigned argc,Value * vp)2784 static bool Run(JSContext* cx, unsigned argc, Value* vp) {
2785   CallArgs args = CallArgsFromVp(argc, vp);
2786   if (args.length() != 1) {
2787     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
2788                               JSSMSG_INVALID_ARGS, "run");
2789     return false;
2790   }
2791 
2792   RootedString str(cx, JS::ToString(cx, args[0]));
2793   if (!str) {
2794     return false;
2795   }
2796   args[0].setString(str);
2797 
2798   str = FileAsString(cx, str);
2799   if (!str) {
2800     return false;
2801   }
2802 
2803   AutoStableStringChars chars(cx);
2804   if (!chars.initTwoByte(cx, str)) {
2805     return false;
2806   }
2807 
2808   JS::SourceText<char16_t> srcBuf;
2809   if (!srcBuf.init(cx, chars.twoByteRange().begin().get(), str->length(),
2810                    JS::SourceOwnership::Borrowed)) {
2811     return false;
2812   }
2813 
2814   RootedScript script(cx);
2815   int64_t startClock = PRMJ_Now();
2816   {
2817     /* FIXME: This should use UTF-8 (bug 987069). */
2818     UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
2819     if (!filename) {
2820       return false;
2821     }
2822 
2823     JS::CompileOptions options(cx);
2824     options.setIntroductionType("js shell run")
2825         .setFileAndLine(filename.get(), 1)
2826         .setIsRunOnce(true)
2827         .setNoScriptRval(true);
2828 
2829     script = JS::Compile(cx, options, srcBuf);
2830     if (!script) {
2831       return false;
2832     }
2833   }
2834 
2835   if (!JS_ExecuteScript(cx, script)) {
2836     return false;
2837   }
2838 
2839   int64_t endClock = PRMJ_Now();
2840 
2841   args.rval().setDouble((endClock - startClock) / double(PRMJ_USEC_PER_MSEC));
2842   return true;
2843 }
2844 
js_fgets(char * buf,int size,FILE * file)2845 static int js_fgets(char* buf, int size, FILE* file) {
2846   int n, i, c;
2847   bool crflag;
2848 
2849   n = size - 1;
2850   if (n < 0) {
2851     return -1;
2852   }
2853 
2854   // Use the fastest available getc.
2855   auto fast_getc =
2856 #if defined(HAVE_GETC_UNLOCKED)
2857       getc_unlocked
2858 #elif defined(HAVE__GETC_NOLOCK)
2859       _getc_nolock
2860 #else
2861       getc
2862 #endif
2863       ;
2864 
2865   crflag = false;
2866   for (i = 0; i < n && (c = fast_getc(file)) != EOF; i++) {
2867     buf[i] = c;
2868     if (c == '\n') {  // any \n ends a line
2869       i++;            // keep the \n; we know there is room for \0
2870       break;
2871     }
2872     if (crflag) {  // \r not followed by \n ends line at the \r
2873       ungetc(c, file);
2874       break;  // and overwrite c in buf with \0
2875     }
2876     crflag = (c == '\r');
2877   }
2878 
2879   buf[i] = '\0';
2880   return i;
2881 }
2882 
2883 /*
2884  * function readline()
2885  * Provides a hook for scripts to read a line from stdin.
2886  */
ReadLine(JSContext * cx,unsigned argc,Value * vp)2887 static bool ReadLine(JSContext* cx, unsigned argc, Value* vp) {
2888   CallArgs args = CallArgsFromVp(argc, vp);
2889 
2890   static constexpr size_t BUFSIZE = 256;
2891   FILE* from = stdin;
2892   size_t buflength = 0;
2893   size_t bufsize = BUFSIZE;
2894   char* buf = (char*)JS_malloc(cx, bufsize);
2895   if (!buf) {
2896     return false;
2897   }
2898 
2899   bool sawNewline = false;
2900   size_t gotlength;
2901   while ((gotlength = js_fgets(buf + buflength, bufsize - buflength, from)) >
2902          0) {
2903     buflength += gotlength;
2904 
2905     /* Are we done? */
2906     if (buf[buflength - 1] == '\n') {
2907       buf[buflength - 1] = '\0';
2908       sawNewline = true;
2909       break;
2910     } else if (buflength < bufsize - 1) {
2911       break;
2912     }
2913 
2914     /* Else, grow our buffer for another pass. */
2915     char* tmp;
2916     bufsize *= 2;
2917     if (bufsize > buflength) {
2918       tmp = static_cast<char*>(JS_realloc(cx, buf, bufsize / 2, bufsize));
2919     } else {
2920       JS_ReportOutOfMemory(cx);
2921       tmp = nullptr;
2922     }
2923 
2924     if (!tmp) {
2925       JS_free(cx, buf);
2926       return false;
2927     }
2928 
2929     buf = tmp;
2930   }
2931 
2932   /* Treat the empty string specially. */
2933   if (buflength == 0) {
2934     args.rval().set(feof(from) ? NullValue() : JS_GetEmptyStringValue(cx));
2935     JS_free(cx, buf);
2936     return true;
2937   }
2938 
2939   /* Shrink the buffer to the real size. */
2940   char* tmp = static_cast<char*>(JS_realloc(cx, buf, bufsize, buflength));
2941   if (!tmp) {
2942     JS_free(cx, buf);
2943     return false;
2944   }
2945 
2946   buf = tmp;
2947 
2948   /*
2949    * Turn buf into a JSString. Note that buflength includes the trailing null
2950    * character.
2951    */
2952   JSString* str =
2953       JS_NewStringCopyN(cx, buf, sawNewline ? buflength - 1 : buflength);
2954   JS_free(cx, buf);
2955   if (!str) {
2956     return false;
2957   }
2958 
2959   args.rval().setString(str);
2960   return true;
2961 }
2962 
2963 /*
2964  * function readlineBuf()
2965  * Provides a hook for scripts to emulate readline() using a string object.
2966  */
ReadLineBuf(JSContext * cx,unsigned argc,Value * vp)2967 static bool ReadLineBuf(JSContext* cx, unsigned argc, Value* vp) {
2968   CallArgs args = CallArgsFromVp(argc, vp);
2969   ShellContext* sc = GetShellContext(cx);
2970 
2971   if (!args.length()) {
2972     if (!sc->readLineBuf) {
2973       JS_ReportErrorASCII(cx,
2974                           "No source buffer set. You must initially "
2975                           "call readlineBuf with an argument.");
2976       return false;
2977     }
2978 
2979     char* currentBuf = sc->readLineBuf.get() + sc->readLineBufPos;
2980     size_t buflen = strlen(currentBuf);
2981 
2982     if (!buflen) {
2983       args.rval().setNull();
2984       return true;
2985     }
2986 
2987     size_t len = 0;
2988     while (len < buflen) {
2989       if (currentBuf[len] == '\n') {
2990         break;
2991       }
2992       len++;
2993     }
2994 
2995     JSString* str = JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(currentBuf, len));
2996     if (!str) {
2997       return false;
2998     }
2999 
3000     if (currentBuf[len] == '\0') {
3001       sc->readLineBufPos += len;
3002     } else {
3003       sc->readLineBufPos += len + 1;
3004     }
3005 
3006     args.rval().setString(str);
3007     return true;
3008   }
3009 
3010   if (args.length() == 1) {
3011     sc->readLineBuf = nullptr;
3012     sc->readLineBufPos = 0;
3013 
3014     RootedString str(cx, JS::ToString(cx, args[0]));
3015     if (!str) {
3016       return false;
3017     }
3018     sc->readLineBuf = JS_EncodeStringToUTF8(cx, str);
3019     if (!sc->readLineBuf) {
3020       return false;
3021     }
3022 
3023     args.rval().setUndefined();
3024     return true;
3025   }
3026 
3027   JS_ReportErrorASCII(cx, "Must specify at most one argument");
3028   return false;
3029 }
3030 
PutStr(JSContext * cx,unsigned argc,Value * vp)3031 static bool PutStr(JSContext* cx, unsigned argc, Value* vp) {
3032   CallArgs args = CallArgsFromVp(argc, vp);
3033 
3034   if (args.length() != 0) {
3035     if (!gOutFile->isOpen()) {
3036       JS_ReportErrorASCII(cx, "output file is closed");
3037       return false;
3038     }
3039 
3040     RootedString str(cx, JS::ToString(cx, args[0]));
3041     if (!str) {
3042       return false;
3043     }
3044     UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
3045     if (!bytes) {
3046       return false;
3047     }
3048     fputs(bytes.get(), gOutFile->fp);
3049     fflush(gOutFile->fp);
3050   }
3051 
3052   args.rval().setUndefined();
3053   return true;
3054 }
3055 
Now(JSContext * cx,unsigned argc,Value * vp)3056 static bool Now(JSContext* cx, unsigned argc, Value* vp) {
3057   CallArgs args = CallArgsFromVp(argc, vp);
3058   double now = PRMJ_Now() / double(PRMJ_USEC_PER_MSEC);
3059   args.rval().setDouble(now);
3060   return true;
3061 }
3062 
CpuNow(JSContext * cx,unsigned argc,Value * vp)3063 static bool CpuNow(JSContext* cx, unsigned argc, Value* vp) {
3064   CallArgs args = CallArgsFromVp(argc, vp);
3065   double now = double(std::clock()) / double(CLOCKS_PER_SEC);
3066   args.rval().setDouble(now);
3067   return true;
3068 }
3069 
PrintInternal(JSContext * cx,const CallArgs & args,RCFile * file)3070 static bool PrintInternal(JSContext* cx, const CallArgs& args, RCFile* file) {
3071   if (!file->isOpen()) {
3072     JS_ReportErrorASCII(cx, "output file is closed");
3073     return false;
3074   }
3075 
3076   for (unsigned i = 0; i < args.length(); i++) {
3077     RootedString str(cx, JS::ToString(cx, args[i]));
3078     if (!str) {
3079       return false;
3080     }
3081     UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
3082     if (!bytes) {
3083       return false;
3084     }
3085     fprintf(file->fp, "%s%s", i ? " " : "", bytes.get());
3086   }
3087 
3088   fputc('\n', file->fp);
3089   fflush(file->fp);
3090 
3091   args.rval().setUndefined();
3092   return true;
3093 }
3094 
Print(JSContext * cx,unsigned argc,Value * vp)3095 static bool Print(JSContext* cx, unsigned argc, Value* vp) {
3096   CallArgs args = CallArgsFromVp(argc, vp);
3097 #ifdef FUZZING_INTERFACES
3098   if (fuzzHaveModule && !fuzzDoDebug) {
3099     // When fuzzing and not debugging, suppress any print() output,
3100     // as it slows down fuzzing and makes libFuzzer's output hard
3101     // to read.
3102     args.rval().setUndefined();
3103     return true;
3104   }
3105 #endif  // FUZZING_INTERFACES
3106   return PrintInternal(cx, args, gOutFile);
3107 }
3108 
PrintErr(JSContext * cx,unsigned argc,Value * vp)3109 static bool PrintErr(JSContext* cx, unsigned argc, Value* vp) {
3110   CallArgs args = CallArgsFromVp(argc, vp);
3111   return PrintInternal(cx, args, gErrFile);
3112 }
3113 
3114 static bool Help(JSContext* cx, unsigned argc, Value* vp);
3115 
Quit(JSContext * cx,unsigned argc,Value * vp)3116 static bool Quit(JSContext* cx, unsigned argc, Value* vp) {
3117   ShellContext* sc = GetShellContext(cx);
3118 
3119   // Print a message to stderr in differential testing to help jsfunfuzz
3120   // find uncatchable-exception bugs.
3121   if (js::SupportDifferentialTesting()) {
3122     fprintf(stderr, "quit called\n");
3123   }
3124 
3125   CallArgs args = CallArgsFromVp(argc, vp);
3126   int32_t code;
3127   if (!ToInt32(cx, args.get(0), &code)) {
3128     return false;
3129   }
3130 
3131   // The fuzzers check the shell's exit code and assume a value >= 128 means
3132   // the process crashed (for instance, SIGSEGV will result in code 139). On
3133   // POSIX platforms, the exit code is 8-bit and negative values can also
3134   // result in an exit code >= 128. We restrict the value to range [0, 127] to
3135   // avoid false positives.
3136   if (code < 0 || code >= 128) {
3137     JS_ReportErrorASCII(cx, "quit exit code should be in range 0-127");
3138     return false;
3139   }
3140 
3141   js::StopDrainingJobQueue(cx);
3142   sc->exitCode = code;
3143   sc->quitting = true;
3144   return false;
3145 }
3146 
StartTimingMutator(JSContext * cx,unsigned argc,Value * vp)3147 static bool StartTimingMutator(JSContext* cx, unsigned argc, Value* vp) {
3148   CallArgs args = CallArgsFromVp(argc, vp);
3149   if (args.length() > 0) {
3150     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
3151                               JSSMSG_TOO_MANY_ARGS, "startTimingMutator");
3152     return false;
3153   }
3154 
3155   if (!cx->runtime()->gc.stats().startTimingMutator()) {
3156     JS_ReportErrorASCII(
3157         cx, "StartTimingMutator should only be called from outside of GC");
3158     return false;
3159   }
3160 
3161   args.rval().setUndefined();
3162   return true;
3163 }
3164 
StopTimingMutator(JSContext * cx,unsigned argc,Value * vp)3165 static bool StopTimingMutator(JSContext* cx, unsigned argc, Value* vp) {
3166   CallArgs args = CallArgsFromVp(argc, vp);
3167   if (args.length() > 0) {
3168     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
3169                               JSSMSG_TOO_MANY_ARGS, "stopTimingMutator");
3170     return false;
3171   }
3172 
3173   double mutator_ms, gc_ms;
3174   if (!cx->runtime()->gc.stats().stopTimingMutator(mutator_ms, gc_ms)) {
3175     JS_ReportErrorASCII(cx,
3176                         "stopTimingMutator called when not timing the mutator");
3177     return false;
3178   }
3179   double total_ms = mutator_ms + gc_ms;
3180   if (total_ms > 0 && gOutFile->isOpen()) {
3181     fprintf(gOutFile->fp, "Mutator: %.3fms (%.1f%%), GC: %.3fms (%.1f%%)\n",
3182             mutator_ms, mutator_ms / total_ms * 100.0, gc_ms,
3183             gc_ms / total_ms * 100.0);
3184   }
3185 
3186   args.rval().setUndefined();
3187   return true;
3188 }
3189 
ToSource(JSContext * cx,HandleValue vp,UniqueChars * bytes)3190 static const char* ToSource(JSContext* cx, HandleValue vp, UniqueChars* bytes) {
3191   RootedString str(cx, JS_ValueToSource(cx, vp));
3192   if (str) {
3193     *bytes = JS_EncodeStringToUTF8(cx, str);
3194     if (*bytes) {
3195       return bytes->get();
3196     }
3197   }
3198   JS_ClearPendingException(cx);
3199   return "<<error converting value to string>>";
3200 }
3201 
AssertEq(JSContext * cx,unsigned argc,Value * vp)3202 static bool AssertEq(JSContext* cx, unsigned argc, Value* vp) {
3203   CallArgs args = CallArgsFromVp(argc, vp);
3204   if (!(args.length() == 2 || (args.length() == 3 && args[2].isString()))) {
3205     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
3206                               (args.length() < 2)    ? JSSMSG_NOT_ENOUGH_ARGS
3207                               : (args.length() == 3) ? JSSMSG_INVALID_ARGS
3208                                                      : JSSMSG_TOO_MANY_ARGS,
3209                               "assertEq");
3210     return false;
3211   }
3212 
3213   bool same;
3214   if (!JS::SameValue(cx, args[0], args[1], &same)) {
3215     return false;
3216   }
3217   if (!same) {
3218     UniqueChars bytes0, bytes1;
3219     const char* actual = ToSource(cx, args[0], &bytes0);
3220     const char* expected = ToSource(cx, args[1], &bytes1);
3221     if (args.length() == 2) {
3222       JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr,
3223                                JSSMSG_ASSERT_EQ_FAILED, actual, expected);
3224     } else {
3225       RootedString message(cx, args[2].toString());
3226       UniqueChars bytes2 = QuoteString(cx, message);
3227       if (!bytes2) {
3228         return false;
3229       }
3230       JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr,
3231                                JSSMSG_ASSERT_EQ_FAILED_MSG, actual, expected,
3232                                bytes2.get());
3233     }
3234     return false;
3235   }
3236   args.rval().setUndefined();
3237   return true;
3238 }
3239 
GetTopScript(JSContext * cx)3240 static JSScript* GetTopScript(JSContext* cx) {
3241   NonBuiltinScriptFrameIter iter(cx);
3242   return iter.done() ? nullptr : iter.script();
3243 }
3244 
GetScriptAndPCArgs(JSContext * cx,CallArgs & args,MutableHandleScript scriptp,int32_t * ip)3245 static bool GetScriptAndPCArgs(JSContext* cx, CallArgs& args,
3246                                MutableHandleScript scriptp, int32_t* ip) {
3247   RootedScript script(cx, GetTopScript(cx));
3248   *ip = 0;
3249   if (!args.get(0).isUndefined()) {
3250     HandleValue v = args[0];
3251     unsigned intarg = 0;
3252     if (v.isObject() && JS::GetClass(&v.toObject()) == &JSFunction::class_) {
3253       script = TestingFunctionArgumentToScript(cx, v);
3254       if (!script) {
3255         return false;
3256       }
3257       intarg++;
3258     }
3259     if (!args.get(intarg).isUndefined()) {
3260       if (!JS::ToInt32(cx, args[intarg], ip)) {
3261         return false;
3262       }
3263       if ((uint32_t)*ip >= script->length()) {
3264         JS_ReportErrorASCII(cx, "Invalid PC");
3265         return false;
3266       }
3267     }
3268   }
3269 
3270   scriptp.set(script);
3271 
3272   return true;
3273 }
3274 
LineToPC(JSContext * cx,unsigned argc,Value * vp)3275 static bool LineToPC(JSContext* cx, unsigned argc, Value* vp) {
3276   CallArgs args = CallArgsFromVp(argc, vp);
3277 
3278   if (args.length() == 0) {
3279     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
3280                               JSSMSG_LINE2PC_USAGE);
3281     return false;
3282   }
3283 
3284   RootedScript script(cx, GetTopScript(cx));
3285   int32_t lineArg = 0;
3286   if (args[0].isObject() && args[0].toObject().is<JSFunction>()) {
3287     script = TestingFunctionArgumentToScript(cx, args[0]);
3288     if (!script) {
3289       return false;
3290     }
3291     lineArg++;
3292   }
3293 
3294   uint32_t lineno;
3295   if (!ToUint32(cx, args.get(lineArg), &lineno)) {
3296     return false;
3297   }
3298 
3299   jsbytecode* pc = LineNumberToPC(script, lineno);
3300   if (!pc) {
3301     return false;
3302   }
3303   args.rval().setInt32(script->pcToOffset(pc));
3304   return true;
3305 }
3306 
PCToLine(JSContext * cx,unsigned argc,Value * vp)3307 static bool PCToLine(JSContext* cx, unsigned argc, Value* vp) {
3308   CallArgs args = CallArgsFromVp(argc, vp);
3309   RootedScript script(cx);
3310   int32_t i;
3311   unsigned lineno;
3312 
3313   if (!GetScriptAndPCArgs(cx, args, &script, &i)) {
3314     return false;
3315   }
3316   lineno = PCToLineNumber(script, script->offsetToPC(i));
3317   if (!lineno) {
3318     return false;
3319   }
3320   args.rval().setInt32(lineno);
3321   return true;
3322 }
3323 
3324 #if defined(DEBUG) || defined(JS_JITSPEW)
3325 
SrcNotes(JSContext * cx,HandleScript script,Sprinter * sp)3326 [[nodiscard]] static bool SrcNotes(JSContext* cx, HandleScript script,
3327                                    Sprinter* sp) {
3328   if (!sp->put("\nSource notes:\n") ||
3329       !sp->jsprintf("%4s %4s %6s %5s %6s %-10s %s\n", "ofs", "line", "column",
3330                     "pc", "delta", "desc", "args") ||
3331       !sp->put("---- ---- ------ ----- ------ ---------- ------\n")) {
3332     return false;
3333   }
3334 
3335   unsigned offset = 0;
3336   unsigned lineno = script->lineno();
3337   unsigned column = script->column();
3338   SrcNote* notes = script->notes();
3339   for (SrcNoteIterator iter(notes); !iter.atEnd(); ++iter) {
3340     auto sn = *iter;
3341 
3342     unsigned delta = sn->delta();
3343     offset += delta;
3344     SrcNoteType type = sn->type();
3345     const char* name = sn->name();
3346     if (!sp->jsprintf("%3u: %4u %6u %5u [%4u] %-10s", unsigned(sn - notes),
3347                       lineno, column, offset, delta, name)) {
3348       return false;
3349     }
3350 
3351     switch (type) {
3352       case SrcNoteType::Null:
3353       case SrcNoteType::AssignOp:
3354       case SrcNoteType::Breakpoint:
3355       case SrcNoteType::StepSep:
3356       case SrcNoteType::XDelta:
3357         break;
3358 
3359       case SrcNoteType::ColSpan: {
3360         uint32_t colspan = SrcNote::ColSpan::getSpan(sn);
3361         if (!sp->jsprintf(" colspan %u", colspan)) {
3362           return false;
3363         }
3364         column += colspan;
3365         break;
3366       }
3367 
3368       case SrcNoteType::SetLine:
3369         lineno = SrcNote::SetLine::getLine(sn, script->lineno());
3370         if (!sp->jsprintf(" lineno %u", lineno)) {
3371           return false;
3372         }
3373         column = 0;
3374         break;
3375 
3376       case SrcNoteType::NewLine:
3377         ++lineno;
3378         column = 0;
3379         break;
3380 
3381       default:
3382         MOZ_ASSERT_UNREACHABLE("unrecognized srcnote");
3383     }
3384     if (!sp->put("\n")) {
3385       return false;
3386     }
3387   }
3388 
3389   return true;
3390 }
3391 
Notes(JSContext * cx,unsigned argc,Value * vp)3392 static bool Notes(JSContext* cx, unsigned argc, Value* vp) {
3393   CallArgs args = CallArgsFromVp(argc, vp);
3394   Sprinter sprinter(cx);
3395   if (!sprinter.init()) {
3396     return false;
3397   }
3398 
3399   for (unsigned i = 0; i < args.length(); i++) {
3400     RootedScript script(cx, TestingFunctionArgumentToScript(cx, args[i]));
3401     if (!script) {
3402       return false;
3403     }
3404 
3405     if (!SrcNotes(cx, script, &sprinter)) {
3406       return false;
3407     }
3408   }
3409 
3410   JSString* str = JS_NewStringCopyZ(cx, sprinter.string());
3411   if (!str) {
3412     return false;
3413   }
3414   args.rval().setString(str);
3415   return true;
3416 }
3417 
TryNoteName(TryNoteKind kind)3418 static const char* TryNoteName(TryNoteKind kind) {
3419   switch (kind) {
3420     case TryNoteKind::Catch:
3421       return "catch";
3422     case TryNoteKind::Finally:
3423       return "finally";
3424     case TryNoteKind::ForIn:
3425       return "for-in";
3426     case TryNoteKind::ForOf:
3427       return "for-of";
3428     case TryNoteKind::Loop:
3429       return "loop";
3430     case TryNoteKind::ForOfIterClose:
3431       return "for-of-iterclose";
3432     case TryNoteKind::Destructuring:
3433       return "destructuring";
3434   }
3435 
3436   MOZ_CRASH("Bad TryNoteKind");
3437 }
3438 
TryNotes(JSContext * cx,HandleScript script,Sprinter * sp)3439 [[nodiscard]] static bool TryNotes(JSContext* cx, HandleScript script,
3440                                    Sprinter* sp) {
3441   if (!sp->put(
3442           "\nException table:\nkind               stack    start      end\n")) {
3443     return false;
3444   }
3445 
3446   for (const TryNote& tn : script->trynotes()) {
3447     if (!sp->jsprintf(" %-16s %6u %8u %8u\n", TryNoteName(tn.kind()),
3448                       tn.stackDepth, tn.start, tn.start + tn.length)) {
3449       return false;
3450     }
3451   }
3452   return true;
3453 }
3454 
ScopeNotes(JSContext * cx,HandleScript script,Sprinter * sp)3455 [[nodiscard]] static bool ScopeNotes(JSContext* cx, HandleScript script,
3456                                      Sprinter* sp) {
3457   if (!sp->put("\nScope notes:\n   index   parent    start      end\n")) {
3458     return false;
3459   }
3460 
3461   for (const ScopeNote& note : script->scopeNotes()) {
3462     if (note.index == ScopeNote::NoScopeIndex) {
3463       if (!sp->jsprintf("%8s ", "(none)")) {
3464         return false;
3465       }
3466     } else {
3467       if (!sp->jsprintf("%8u ", note.index.index)) {
3468         return false;
3469       }
3470     }
3471     if (note.parent == ScopeNote::NoScopeIndex) {
3472       if (!sp->jsprintf("%8s ", "(none)")) {
3473         return false;
3474       }
3475     } else {
3476       if (!sp->jsprintf("%8u ", note.parent)) {
3477         return false;
3478       }
3479     }
3480     if (!sp->jsprintf("%8u %8u\n", note.start, note.start + note.length)) {
3481       return false;
3482     }
3483   }
3484   return true;
3485 }
3486 
GCThings(JSContext * cx,HandleScript script,Sprinter * sp)3487 [[nodiscard]] static bool GCThings(JSContext* cx, HandleScript script,
3488                                    Sprinter* sp) {
3489   if (!sp->put("\nGC things:\n   index   type       value\n")) {
3490     return false;
3491   }
3492 
3493   size_t i = 0;
3494   for (JS::GCCellPtr gcThing : script->gcthings()) {
3495     if (!sp->jsprintf("%8zu   ", i)) {
3496       return false;
3497     }
3498     if (gcThing.is<BigInt>()) {
3499       if (!sp->put("BigInt     ")) {
3500         return false;
3501       }
3502       gcThing.as<BigInt>().dump(*sp);
3503       if (!sp->put("\n")) {
3504         return false;
3505       }
3506     } else if (gcThing.is<Scope>()) {
3507       if (!sp->put("Scope      ")) {
3508         return false;
3509       }
3510       Rooted<Scope*> scope(cx, &gcThing.as<Scope>());
3511       if (!Scope::dumpForDisassemble(cx, scope, *sp,
3512                                      "                      ")) {
3513         return false;
3514       }
3515       if (!sp->put("\n")) {
3516         return false;
3517       }
3518     } else if (gcThing.is<JSObject>()) {
3519       JSObject* obj = &gcThing.as<JSObject>();
3520       if (obj->is<JSFunction>()) {
3521         if (!sp->put("Function   ")) {
3522           return false;
3523         }
3524         RootedFunction fun(cx, &obj->as<JSFunction>());
3525         if (fun->displayAtom()) {
3526           Rooted<JSAtom*> name(cx, fun->displayAtom());
3527           UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, name);
3528           if (!utf8chars) {
3529             return false;
3530           }
3531           if (!sp->put(utf8chars.get())) {
3532             return false;
3533           }
3534         } else {
3535           if (!sp->put("(anonymous)")) {
3536             return false;
3537           }
3538         }
3539 
3540         if (fun->hasBaseScript()) {
3541           BaseScript* script = fun->baseScript();
3542           if (!sp->jsprintf(" @ %u:%u\n", script->lineno(), script->column())) {
3543             return false;
3544           }
3545         } else {
3546           if (!sp->put(" (no script)\n")) {
3547             return false;
3548           }
3549         }
3550       } else {
3551         if (obj->is<RegExpObject>()) {
3552           if (!sp->put("RegExp     ")) {
3553             return false;
3554           }
3555         } else {
3556           if (!sp->put("Object     ")) {
3557             return false;
3558           }
3559         }
3560 
3561         RootedValue objValue(cx, ObjectValue(*obj));
3562         RootedString str(cx, ValueToSource(cx, objValue));
3563         if (!str) {
3564           return false;
3565         }
3566         UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
3567         if (!utf8chars) {
3568           return false;
3569         }
3570         if (!sp->put(utf8chars.get())) {
3571           return false;
3572         }
3573 
3574         if (!sp->put("\n")) {
3575           return false;
3576         }
3577       }
3578     } else if (gcThing.is<JSString>()) {
3579       if (!sp->put("Atom       ")) {
3580         return false;
3581       }
3582       RootedAtom atom(cx, &gcThing.as<JSString>().asAtom());
3583       UniqueChars chars = QuoteString(cx, atom, '"');
3584       if (!chars) {
3585         return false;
3586       }
3587       if (!sp->put(chars.get())) {
3588         return false;
3589       }
3590       if (!sp->put("\n")) {
3591         return false;
3592       }
3593     } else {
3594       if (!sp->put("Unknown\n")) {
3595         return false;
3596       }
3597     }
3598     i++;
3599   }
3600 
3601   return true;
3602 }
3603 
DisassembleScript(JSContext * cx,HandleScript script,HandleFunction fun,bool lines,bool recursive,bool sourceNotes,bool gcThings,Sprinter * sp)3604 [[nodiscard]] static bool DisassembleScript(JSContext* cx, HandleScript script,
3605                                             HandleFunction fun, bool lines,
3606                                             bool recursive, bool sourceNotes,
3607                                             bool gcThings, Sprinter* sp) {
3608   if (fun) {
3609     if (!sp->put("flags:")) {
3610       return false;
3611     }
3612     if (fun->isLambda()) {
3613       if (!sp->put(" LAMBDA")) {
3614         return false;
3615       }
3616     }
3617     if (fun->needsCallObject()) {
3618       if (!sp->put(" NEEDS_CALLOBJECT")) {
3619         return false;
3620       }
3621     }
3622     if (fun->needsExtraBodyVarEnvironment()) {
3623       if (!sp->put(" NEEDS_EXTRABODYVARENV")) {
3624         return false;
3625       }
3626     }
3627     if (fun->needsNamedLambdaEnvironment()) {
3628       if (!sp->put(" NEEDS_NAMEDLAMBDAENV")) {
3629         return false;
3630       }
3631     }
3632     if (fun->isConstructor()) {
3633       if (!sp->put(" CONSTRUCTOR")) {
3634         return false;
3635       }
3636     }
3637     if (fun->isSelfHostedBuiltin()) {
3638       if (!sp->put(" SELF_HOSTED")) {
3639         return false;
3640       }
3641     }
3642     if (fun->isArrow()) {
3643       if (!sp->put(" ARROW")) {
3644         return false;
3645       }
3646     }
3647     if (!sp->put("\n")) {
3648       return false;
3649     }
3650   }
3651 
3652   if (!Disassemble(cx, script, lines, sp)) {
3653     return false;
3654   }
3655   if (sourceNotes) {
3656     if (!SrcNotes(cx, script, sp)) {
3657       return false;
3658     }
3659   }
3660   if (!TryNotes(cx, script, sp)) {
3661     return false;
3662   }
3663   if (!ScopeNotes(cx, script, sp)) {
3664     return false;
3665   }
3666   if (gcThings) {
3667     if (!GCThings(cx, script, sp)) {
3668       return false;
3669     }
3670   }
3671 
3672   if (recursive) {
3673     for (JS::GCCellPtr gcThing : script->gcthings()) {
3674       if (!gcThing.is<JSObject>()) {
3675         continue;
3676       }
3677 
3678       JSObject* obj = &gcThing.as<JSObject>();
3679       if (obj->is<JSFunction>()) {
3680         if (!sp->put("\n")) {
3681           return false;
3682         }
3683 
3684         RootedFunction fun(cx, &obj->as<JSFunction>());
3685         if (fun->isInterpreted()) {
3686           RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
3687           if (!script || !DisassembleScript(cx, script, fun, lines, recursive,
3688                                             sourceNotes, gcThings, sp)) {
3689             return false;
3690           }
3691         } else {
3692           if (!sp->put("[native code]\n")) {
3693             return false;
3694           }
3695         }
3696       }
3697     }
3698   }
3699 
3700   return true;
3701 }
3702 
3703 namespace {
3704 
3705 struct DisassembleOptionParser {
3706   unsigned argc;
3707   Value* argv;
3708   bool lines;
3709   bool recursive;
3710   bool sourceNotes;
3711   bool gcThings;
3712 
DisassembleOptionParser__anon14ecce4c0311::DisassembleOptionParser3713   DisassembleOptionParser(unsigned argc, Value* argv)
3714       : argc(argc),
3715         argv(argv),
3716         lines(false),
3717         recursive(false),
3718         sourceNotes(true),
3719         gcThings(false) {}
3720 
parse__anon14ecce4c0311::DisassembleOptionParser3721   bool parse(JSContext* cx) {
3722     /* Read options off early arguments */
3723     while (argc > 0 && argv[0].isString()) {
3724       JSString* str = argv[0].toString();
3725       JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
3726       if (!linearStr) {
3727         return false;
3728       }
3729       if (JS_LinearStringEqualsLiteral(linearStr, "-l")) {
3730         lines = true;
3731       } else if (JS_LinearStringEqualsLiteral(linearStr, "-r")) {
3732         recursive = true;
3733       } else if (JS_LinearStringEqualsLiteral(linearStr, "-S")) {
3734         sourceNotes = false;
3735       } else if (JS_LinearStringEqualsLiteral(linearStr, "-g")) {
3736         gcThings = true;
3737       } else {
3738         break;
3739       }
3740       argv++;
3741       argc--;
3742     }
3743     return true;
3744   }
3745 };
3746 
3747 } /* anonymous namespace */
3748 
DisassembleToSprinter(JSContext * cx,unsigned argc,Value * vp,Sprinter * sprinter)3749 static bool DisassembleToSprinter(JSContext* cx, unsigned argc, Value* vp,
3750                                   Sprinter* sprinter) {
3751   CallArgs args = CallArgsFromVp(argc, vp);
3752   DisassembleOptionParser p(args.length(), args.array());
3753   if (!p.parse(cx)) {
3754     return false;
3755   }
3756 
3757   if (p.argc == 0) {
3758     /* Without arguments, disassemble the current script. */
3759     RootedScript script(cx, GetTopScript(cx));
3760     if (script) {
3761       JSAutoRealm ar(cx, script);
3762       if (!Disassemble(cx, script, p.lines, sprinter)) {
3763         return false;
3764       }
3765       if (!SrcNotes(cx, script, sprinter)) {
3766         return false;
3767       }
3768       if (!TryNotes(cx, script, sprinter)) {
3769         return false;
3770       }
3771       if (!ScopeNotes(cx, script, sprinter)) {
3772         return false;
3773       }
3774       if (p.gcThings) {
3775         if (!GCThings(cx, script, sprinter)) {
3776           return false;
3777         }
3778       }
3779     }
3780   } else {
3781     for (unsigned i = 0; i < p.argc; i++) {
3782       RootedFunction fun(cx);
3783       RootedScript script(cx);
3784       RootedValue value(cx, p.argv[i]);
3785       if (value.isObject() && value.toObject().is<ShellModuleObjectWrapper>()) {
3786         script = value.toObject()
3787                      .as<ShellModuleObjectWrapper>()
3788                      .get()
3789                      ->maybeScript();
3790       } else {
3791         script = TestingFunctionArgumentToScript(cx, value, fun.address());
3792       }
3793       if (!script) {
3794         return false;
3795       }
3796       if (!DisassembleScript(cx, script, fun, p.lines, p.recursive,
3797                              p.sourceNotes, p.gcThings, sprinter)) {
3798         return false;
3799       }
3800     }
3801   }
3802 
3803   return !sprinter->hadOutOfMemory();
3804 }
3805 
DisassembleToString(JSContext * cx,unsigned argc,Value * vp)3806 static bool DisassembleToString(JSContext* cx, unsigned argc, Value* vp) {
3807   CallArgs args = CallArgsFromVp(argc, vp);
3808   Sprinter sprinter(cx);
3809   if (!sprinter.init()) {
3810     return false;
3811   }
3812   if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) {
3813     return false;
3814   }
3815 
3816   JS::ConstUTF8CharsZ utf8chars(sprinter.string(), strlen(sprinter.string()));
3817   JSString* str = JS_NewStringCopyUTF8Z(cx, utf8chars);
3818   if (!str) {
3819     return false;
3820   }
3821   args.rval().setString(str);
3822   return true;
3823 }
3824 
Disassemble(JSContext * cx,unsigned argc,Value * vp)3825 static bool Disassemble(JSContext* cx, unsigned argc, Value* vp) {
3826   CallArgs args = CallArgsFromVp(argc, vp);
3827 
3828   if (!gOutFile->isOpen()) {
3829     JS_ReportErrorASCII(cx, "output file is closed");
3830     return false;
3831   }
3832 
3833   Sprinter sprinter(cx);
3834   if (!sprinter.init()) {
3835     return false;
3836   }
3837   if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) {
3838     return false;
3839   }
3840 
3841   fprintf(gOutFile->fp, "%s\n", sprinter.string());
3842   args.rval().setUndefined();
3843   return true;
3844 }
3845 
DisassFile(JSContext * cx,unsigned argc,Value * vp)3846 static bool DisassFile(JSContext* cx, unsigned argc, Value* vp) {
3847   CallArgs args = CallArgsFromVp(argc, vp);
3848 
3849   if (!gOutFile->isOpen()) {
3850     JS_ReportErrorASCII(cx, "output file is closed");
3851     return false;
3852   }
3853 
3854   /* Support extra options at the start, just like Disassemble. */
3855   DisassembleOptionParser p(args.length(), args.array());
3856   if (!p.parse(cx)) {
3857     return false;
3858   }
3859 
3860   if (!p.argc) {
3861     args.rval().setUndefined();
3862     return true;
3863   }
3864 
3865   // We should change DisassembleOptionParser to store CallArgs.
3866   JSString* str = JS::ToString(cx, HandleValue::fromMarkedLocation(&p.argv[0]));
3867   if (!str) {
3868     return false;
3869   }
3870   UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
3871   if (!filename) {
3872     return false;
3873   }
3874   RootedScript script(cx);
3875 
3876   {
3877     CompileOptions options(cx);
3878     options.setIntroductionType("js shell disFile")
3879         .setFileAndLine(filename.get(), 1)
3880         .setIsRunOnce(true)
3881         .setNoScriptRval(true);
3882 
3883     script = JS::CompileUtf8Path(cx, options, filename.get());
3884     if (!script) {
3885       return false;
3886     }
3887   }
3888 
3889   Sprinter sprinter(cx);
3890   if (!sprinter.init()) {
3891     return false;
3892   }
3893   if (!DisassembleScript(cx, script, nullptr, p.lines, p.recursive,
3894                          p.sourceNotes, p.gcThings, &sprinter)) {
3895     return false;
3896   }
3897 
3898   fprintf(gOutFile->fp, "%s\n", sprinter.string());
3899 
3900   args.rval().setUndefined();
3901   return true;
3902 }
3903 
DisassWithSrc(JSContext * cx,unsigned argc,Value * vp)3904 static bool DisassWithSrc(JSContext* cx, unsigned argc, Value* vp) {
3905   CallArgs args = CallArgsFromVp(argc, vp);
3906 
3907   if (!gOutFile->isOpen()) {
3908     JS_ReportErrorASCII(cx, "output file is closed");
3909     return false;
3910   }
3911 
3912   const size_t lineBufLen = 512;
3913   unsigned len, line1, line2, bupline;
3914   char linebuf[lineBufLen];
3915   static const char sep[] = ";-------------------------";
3916 
3917   RootedScript script(cx);
3918   for (unsigned i = 0; i < args.length(); i++) {
3919     script = TestingFunctionArgumentToScript(cx, args[i]);
3920     if (!script) {
3921       return false;
3922     }
3923 
3924     if (!script->filename()) {
3925       JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
3926                                 JSSMSG_FILE_SCRIPTS_ONLY);
3927       return false;
3928     }
3929 
3930     FILE* file = fopen(script->filename(), "rb");
3931     if (!file) {
3932       /* FIXME: script->filename() should become UTF-8 (bug 987069). */
3933       ReportCantOpenErrorUnknownEncoding(cx, script->filename());
3934       return false;
3935     }
3936     auto closeFile = MakeScopeExit([file] { fclose(file); });
3937 
3938     jsbytecode* pc = script->code();
3939     jsbytecode* end = script->codeEnd();
3940 
3941     Sprinter sprinter(cx);
3942     if (!sprinter.init()) {
3943       return false;
3944     }
3945 
3946     /* burn the leading lines */
3947     line2 = PCToLineNumber(script, pc);
3948     for (line1 = 0; line1 < line2 - 1; line1++) {
3949       char* tmp = fgets(linebuf, lineBufLen, file);
3950       if (!tmp) {
3951         /* FIXME: This should use UTF-8 (bug 987069). */
3952         JS_ReportErrorLatin1(cx, "failed to read %s fully", script->filename());
3953         return false;
3954       }
3955     }
3956 
3957     bupline = 0;
3958     while (pc < end) {
3959       line2 = PCToLineNumber(script, pc);
3960 
3961       if (line2 < line1) {
3962         if (bupline != line2) {
3963           bupline = line2;
3964           if (!sprinter.jsprintf("%s %3u: BACKUP\n", sep, line2)) {
3965             return false;
3966           }
3967         }
3968       } else {
3969         if (bupline && line1 == line2) {
3970           if (!sprinter.jsprintf("%s %3u: RESTORE\n", sep, line2)) {
3971             return false;
3972           }
3973         }
3974         bupline = 0;
3975         while (line1 < line2) {
3976           if (!fgets(linebuf, lineBufLen, file)) {
3977             /*
3978              * FIXME: script->filename() should become UTF-8
3979              *        (bug 987069).
3980              */
3981             JS_ReportErrorNumberLatin1(cx, my_GetErrorMessage, nullptr,
3982                                        JSSMSG_UNEXPECTED_EOF,
3983                                        script->filename());
3984             return false;
3985           }
3986           line1++;
3987           if (!sprinter.jsprintf("%s %3u: %s", sep, line1, linebuf)) {
3988             return false;
3989           }
3990         }
3991       }
3992 
3993       len =
3994           Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter);
3995       if (!len) {
3996         return false;
3997       }
3998 
3999       pc += len;
4000     }
4001 
4002     fprintf(gOutFile->fp, "%s\n", sprinter.string());
4003   }
4004 
4005   args.rval().setUndefined();
4006   return true;
4007 }
4008 
4009 #endif /* defined(DEBUG) || defined(JS_JITSPEW) */
4010 
4011 #ifdef JS_CACHEIR_SPEW
CacheIRHealthReport(JSContext * cx,unsigned argc,Value * vp)4012 static bool CacheIRHealthReport(JSContext* cx, unsigned argc, Value* vp) {
4013   CallArgs args = CallArgsFromVp(argc, vp);
4014 
4015   js::jit::CacheIRHealth cih;
4016   RootedScript script(cx);
4017 
4018   // In the case that we are calling this function from the shell and
4019   // the environment variable is not set, AutoSpewChannel automatically
4020   // sets and unsets the proper channel for the duration of spewing
4021   // a health report.
4022   AutoSpewChannel channel(cx, SpewChannel::CacheIRHealthReport, script);
4023   if (!argc) {
4024     // Calling CacheIRHealthReport without any arguments will create health
4025     // reports for all scripts in the zone.
4026     for (auto base = cx->zone()->cellIter<BaseScript>(); !base.done();
4027          base.next()) {
4028       if (!base->hasJitScript() || base->selfHosted()) {
4029         continue;
4030       }
4031 
4032       script = base->asJSScript();
4033       cih.healthReportForScript(cx, script, js::jit::SpewContext::Shell);
4034     }
4035   } else {
4036     RootedValue value(cx, args.get(0));
4037 
4038     if (value.isObject() && value.toObject().is<ShellModuleObjectWrapper>()) {
4039       script =
4040           value.toObject().as<ShellModuleObjectWrapper>().get()->maybeScript();
4041     } else {
4042       script = TestingFunctionArgumentToScript(cx, args.get(0));
4043     }
4044 
4045     if (!script) {
4046       return false;
4047     }
4048 
4049     cih.healthReportForScript(cx, script, js::jit::SpewContext::Shell);
4050   }
4051 
4052   args.rval().setUndefined();
4053   return true;
4054 }
4055 #endif /* JS_CACHEIR_SPEW */
4056 
4057 /* Pretend we can always preserve wrappers for dummy DOM objects. */
DummyPreserveWrapperCallback(JSContext * cx,HandleObject obj)4058 static bool DummyPreserveWrapperCallback(JSContext* cx, HandleObject obj) {
4059   return true;
4060 }
4061 
DummyHasReleasedWrapperCallback(HandleObject obj)4062 static bool DummyHasReleasedWrapperCallback(HandleObject obj) { return true; }
4063 
Intern(JSContext * cx,unsigned argc,Value * vp)4064 static bool Intern(JSContext* cx, unsigned argc, Value* vp) {
4065   CallArgs args = CallArgsFromVp(argc, vp);
4066 
4067   JSString* str = JS::ToString(cx, args.get(0));
4068   if (!str) {
4069     return false;
4070   }
4071 
4072   AutoStableStringChars strChars(cx);
4073   if (!strChars.initTwoByte(cx, str)) {
4074     return false;
4075   }
4076 
4077   mozilla::Range<const char16_t> chars = strChars.twoByteRange();
4078 
4079   if (!JS_AtomizeAndPinUCStringN(cx, chars.begin().get(), chars.length())) {
4080     return false;
4081   }
4082 
4083   args.rval().setUndefined();
4084   return true;
4085 }
4086 
4087 #ifdef FUZZING_JS_FUZZILLI
4088 // We have to assume that the fuzzer will be able to call this function e.g. by
4089 // enumerating the properties of the global object and eval'ing them. As such
4090 // this function is implemented in a way that requires passing some magic value
4091 // as first argument (with the idea being that the fuzzer won't be able to
4092 // generate this value) which then also acts as a selector for the operation
4093 // to perform.
Fuzzilli(JSContext * cx,unsigned argc,Value * vp)4094 static bool Fuzzilli(JSContext* cx, unsigned argc, Value* vp) {
4095   CallArgs args = CallArgsFromVp(argc, vp);
4096 
4097   RootedString arg(cx, JS::ToString(cx, args.get(0)));
4098   if (!arg) {
4099     return false;
4100   }
4101   RootedLinearString operation(cx, StringToLinearString(cx, arg));
4102   if (!operation) {
4103     return false;
4104   }
4105 
4106   if (StringEqualsAscii(operation, "FUZZILLI_CRASH")) {
4107     int type;
4108     if (!ToInt32(cx, args.get(1), &type)) {
4109       return false;
4110     }
4111 
4112     // With this, we can test the various ways the JS shell can crash and make
4113     // sure that Fuzzilli is able to detect all of these failures properly.
4114     switch (type) {
4115       case 0:
4116         *((int*)0x41414141) = 0x1337;
4117         break;
4118       case 1:
4119         MOZ_RELEASE_ASSERT(false);
4120         break;
4121       case 2:
4122         MOZ_ASSERT(false);
4123         break;
4124       case 3:
4125         __asm__("int3");
4126         break;
4127       default:
4128         exit(1);
4129     }
4130   } else if (StringEqualsAscii(operation, "FUZZILLI_PRINT")) {
4131     static FILE* fzliout = fdopen(REPRL_DWFD, "w");
4132     if (!fzliout) {
4133       fprintf(
4134           stderr,
4135           "Fuzzer output channel not available, printing to stdout instead\n");
4136       fzliout = stdout;
4137     }
4138 
4139     RootedString str(cx, JS::ToString(cx, args.get(1)));
4140     if (!str) {
4141       return false;
4142     }
4143     UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
4144     if (!bytes) {
4145       return false;
4146     }
4147     fprintf(fzliout, "%s\n", bytes.get());
4148     fflush(fzliout);
4149   }
4150 
4151   args.rval().setUndefined();
4152   return true;
4153 }
4154 
FuzzilliReprlGetAndRun(JSContext * cx)4155 static bool FuzzilliReprlGetAndRun(JSContext* cx) {
4156   size_t scriptSize = 0;
4157 
4158   unsigned action;
4159   MOZ_RELEASE_ASSERT(read(REPRL_CRFD, &action, 4) == 4);
4160   if (action == 'cexe') {
4161     MOZ_RELEASE_ASSERT(read(REPRL_CRFD, &scriptSize, 8) == 8);
4162   } else {
4163     fprintf(stderr, "Unknown action: %u\n", action);
4164     _exit(-1);
4165   }
4166 
4167   CompileOptions options(cx);
4168   options.setIntroductionType("reprl")
4169       .setFileAndLine("reprl", 1)
4170       .setIsRunOnce(true)
4171       .setNoScriptRval(true);
4172 
4173   char* scriptSrc = static_cast<char*>(js_malloc(scriptSize));
4174 
4175   char* ptr = scriptSrc;
4176   size_t remaining = scriptSize;
4177   while (remaining > 0) {
4178     ssize_t rv = read(REPRL_DRFD, ptr, remaining);
4179     if (rv <= 0) {
4180       fprintf(stderr, "Failed to load script\n");
4181       _exit(-1);
4182     }
4183     remaining -= rv;
4184     ptr += rv;
4185   }
4186 
4187   JS::SourceText<Utf8Unit> srcBuf;
4188   if (!srcBuf.init(cx, scriptSrc, scriptSize,
4189                    JS::SourceOwnership::TakeOwnership)) {
4190     return false;
4191   }
4192 
4193   RootedScript script(cx, JS::Compile(cx, options, srcBuf));
4194   if (!script) {
4195     return false;
4196   }
4197 
4198   if (!JS_ExecuteScript(cx, script)) {
4199     return false;
4200   }
4201 
4202   return true;
4203 }
4204 
4205 #endif /* FUZZING_JS_FUZZILLI */
4206 
FuzzilliUseReprlMode(OptionParser * op)4207 static bool FuzzilliUseReprlMode(OptionParser* op) {
4208 #ifdef FUZZING_JS_FUZZILLI
4209   // Check if we should use REPRL mode
4210   bool reprl_mode = op->getBoolOption("reprl");
4211   if (reprl_mode) {
4212     // Check in with parent
4213     char helo[] = "HELO";
4214     if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) {
4215       reprl_mode = false;
4216     }
4217 
4218     if (memcmp(helo, "HELO", 4) != 0) {
4219       fprintf(stderr, "Invalid response from parent\n");
4220       _exit(-1);
4221     }
4222   }
4223   return reprl_mode;
4224 #else
4225   return false;
4226 #endif /* FUZZING_JS_FUZZILLI */
4227 }
4228 
Crash(JSContext * cx,unsigned argc,Value * vp)4229 static bool Crash(JSContext* cx, unsigned argc, Value* vp) {
4230   CallArgs args = CallArgsFromVp(argc, vp);
4231   if (args.length() == 0) {
4232     MOZ_CRASH("forced crash");
4233   }
4234   RootedString message(cx, JS::ToString(cx, args[0]));
4235   if (!message) {
4236     return false;
4237   }
4238   UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, message);
4239   if (!utf8chars) {
4240     return false;
4241   }
4242   if (args.get(1).isObject()) {
4243     RootedValue v(cx);
4244     RootedObject opts(cx, &args[1].toObject());
4245     if (!JS_GetProperty(cx, opts, "suppress_minidump", &v)) {
4246       return false;
4247     }
4248     if (v.isBoolean() && v.toBoolean()) {
4249       js::NoteIntentionalCrash();
4250     }
4251   }
4252 #ifndef DEBUG
4253   MOZ_ReportCrash(utf8chars.get(), __FILE__, __LINE__);
4254 #endif
4255   MOZ_CRASH_UNSAFE(utf8chars.get());
4256 }
4257 
GetSLX(JSContext * cx,unsigned argc,Value * vp)4258 static bool GetSLX(JSContext* cx, unsigned argc, Value* vp) {
4259   CallArgs args = CallArgsFromVp(argc, vp);
4260   RootedScript script(cx);
4261 
4262   script = TestingFunctionArgumentToScript(cx, args.get(0));
4263   if (!script) {
4264     return false;
4265   }
4266   args.rval().setInt32(GetScriptLineExtent(script));
4267   return true;
4268 }
4269 
ThrowError(JSContext * cx,unsigned argc,Value * vp)4270 static bool ThrowError(JSContext* cx, unsigned argc, Value* vp) {
4271   JS_ReportErrorASCII(cx, "This is an error");
4272   return false;
4273 }
4274 
CopyErrorReportToObject(JSContext * cx,JSErrorReport * report,HandleObject obj)4275 static bool CopyErrorReportToObject(JSContext* cx, JSErrorReport* report,
4276                                     HandleObject obj) {
4277   RootedString nameStr(cx);
4278   if (report->exnType == JSEXN_WARN) {
4279     nameStr = JS_NewStringCopyZ(cx, "Warning");
4280     if (!nameStr) {
4281       return false;
4282     }
4283   } else {
4284     nameStr = GetErrorTypeName(cx, report->exnType);
4285     // GetErrorTypeName doesn't set an exception, but
4286     // can fail for InternalError or non-error objects.
4287     if (!nameStr) {
4288       nameStr = cx->runtime()->emptyString;
4289     }
4290   }
4291   RootedValue nameVal(cx, StringValue(nameStr));
4292   if (!DefineDataProperty(cx, obj, cx->names().name, nameVal)) {
4293     return false;
4294   }
4295 
4296   RootedString messageStr(cx, report->newMessageString(cx));
4297   if (!messageStr) {
4298     return false;
4299   }
4300   RootedValue messageVal(cx, StringValue(messageStr));
4301   if (!DefineDataProperty(cx, obj, cx->names().message, messageVal)) {
4302     return false;
4303   }
4304 
4305   RootedValue linenoVal(cx, Int32Value(report->lineno));
4306   if (!DefineDataProperty(cx, obj, cx->names().lineNumber, linenoVal)) {
4307     return false;
4308   }
4309 
4310   RootedValue columnVal(cx, Int32Value(report->column));
4311   if (!DefineDataProperty(cx, obj, cx->names().columnNumber, columnVal)) {
4312     return false;
4313   }
4314 
4315   RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
4316   if (!notesArray) {
4317     return false;
4318   }
4319 
4320   RootedValue notesArrayVal(cx, ObjectValue(*notesArray));
4321   return DefineDataProperty(cx, obj, cx->names().notes, notesArrayVal);
4322 }
4323 
CreateErrorReport(JSContext * cx,unsigned argc,Value * vp)4324 static bool CreateErrorReport(JSContext* cx, unsigned argc, Value* vp) {
4325   CallArgs args = CallArgsFromVp(argc, vp);
4326 
4327   // We don't have a stack here, so just initialize with null.
4328   JS::ExceptionStack exnStack(cx, args.get(0), nullptr);
4329   JS::ErrorReportBuilder report(cx);
4330   if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
4331     return false;
4332   }
4333 
4334   MOZ_ASSERT(!report.report()->isWarning());
4335 
4336   RootedObject obj(cx, JS_NewPlainObject(cx));
4337   if (!obj) {
4338     return false;
4339   }
4340 
4341   RootedString toString(cx,
4342                         NewStringCopyUTF8Z<CanGC>(cx, report.toStringResult()));
4343   if (!toString) {
4344     return false;
4345   }
4346 
4347   if (!JS_DefineProperty(cx, obj, "toStringResult", toString,
4348                          JSPROP_ENUMERATE)) {
4349     return false;
4350   }
4351 
4352   if (!CopyErrorReportToObject(cx, report.report(), obj)) {
4353     return false;
4354   }
4355 
4356   args.rval().setObject(*obj);
4357   return true;
4358 }
4359 
4360 #define LAZY_STANDARD_CLASSES
4361 
4362 /* A class for easily testing the inner/outer object callbacks. */
4363 typedef struct ComplexObject {
4364   bool isInner;
4365   bool frozen;
4366   JSObject* inner;
4367   JSObject* outer;
4368 } ComplexObject;
4369 
sandbox_enumerate(JSContext * cx,JS::HandleObject obj,JS::MutableHandleIdVector properties,bool enumerableOnly)4370 static bool sandbox_enumerate(JSContext* cx, JS::HandleObject obj,
4371                               JS::MutableHandleIdVector properties,
4372                               bool enumerableOnly) {
4373   RootedValue v(cx);
4374 
4375   if (!JS_GetProperty(cx, obj, "lazy", &v)) {
4376     return false;
4377   }
4378 
4379   if (!ToBoolean(v)) {
4380     return true;
4381   }
4382 
4383   return JS_NewEnumerateStandardClasses(cx, obj, properties, enumerableOnly);
4384 }
4385 
sandbox_resolve(JSContext * cx,HandleObject obj,HandleId id,bool * resolvedp)4386 static bool sandbox_resolve(JSContext* cx, HandleObject obj, HandleId id,
4387                             bool* resolvedp) {
4388   RootedValue v(cx);
4389   if (!JS_GetProperty(cx, obj, "lazy", &v)) {
4390     return false;
4391   }
4392 
4393   if (ToBoolean(v)) {
4394     return JS_ResolveStandardClass(cx, obj, id, resolvedp);
4395   }
4396   return true;
4397 }
4398 
4399 static const JSClassOps sandbox_classOps = {
4400     nullptr,                   // addProperty
4401     nullptr,                   // delProperty
4402     nullptr,                   // enumerate
4403     sandbox_enumerate,         // newEnumerate
4404     sandbox_resolve,           // resolve
4405     nullptr,                   // mayResolve
4406     nullptr,                   // finalize
4407     nullptr,                   // call
4408     nullptr,                   // hasInstance
4409     nullptr,                   // construct
4410     JS_GlobalObjectTraceHook,  // trace
4411 };
4412 
4413 static const JSClass sandbox_class = {"sandbox", JSCLASS_GLOBAL_FLAGS,
4414                                       &sandbox_classOps};
4415 
SetStandardRealmOptions(JS::RealmOptions & options)4416 static void SetStandardRealmOptions(JS::RealmOptions& options) {
4417   options.creationOptions()
4418       .setSharedMemoryAndAtomicsEnabled(enableSharedMemory)
4419       .setCoopAndCoepEnabled(false)
4420       .setStreamsEnabled(enableStreams)
4421       .setReadableByteStreamsEnabled(enableReadableByteStreams)
4422       .setBYOBStreamReadersEnabled(enableBYOBStreamReaders)
4423       .setWritableStreamsEnabled(enableWritableStreams)
4424       .setReadableStreamPipeToEnabled(enableReadableStreamPipeTo)
4425       .setWeakRefsEnabled(enableWeakRefs
4426                               ? JS::WeakRefSpecifier::EnabledWithCleanupSome
4427                               : JS::WeakRefSpecifier::Disabled)
4428       .setToSourceEnabled(enableToSource)
4429       .setPropertyErrorMessageFixEnabled(enablePropertyErrorMessageFix)
4430       .setIteratorHelpersEnabled(enableIteratorHelpers);
4431 }
4432 
CheckRealmOptions(JSContext * cx,JS::RealmOptions & options,JSPrincipals * principals)4433 [[nodiscard]] static bool CheckRealmOptions(JSContext* cx,
4434                                             JS::RealmOptions& options,
4435                                             JSPrincipals* principals) {
4436   JS::RealmCreationOptions& creationOptions = options.creationOptions();
4437   if (creationOptions.compartmentSpecifier() !=
4438       JS::CompartmentSpecifier::ExistingCompartment) {
4439     return true;
4440   }
4441 
4442   JS::Compartment* comp = creationOptions.compartment();
4443 
4444   // All realms in a compartment must be either system or non-system.
4445   bool isSystem =
4446       principals && principals == cx->runtime()->trustedPrincipals();
4447   if (isSystem != IsSystemCompartment(comp)) {
4448     JS_ReportErrorASCII(cx,
4449                         "Cannot create system and non-system realms in the "
4450                         "same compartment");
4451     return false;
4452   }
4453 
4454   // Debugger visibility is per-compartment, not per-realm, so make sure the
4455   // requested visibility matches the existing compartment's.
4456   if (creationOptions.invisibleToDebugger() != comp->invisibleToDebugger()) {
4457     JS_ReportErrorASCII(cx,
4458                         "All the realms in a compartment must have "
4459                         "the same debugger visibility");
4460     return false;
4461   }
4462 
4463   return true;
4464 }
4465 
NewSandbox(JSContext * cx,bool lazy)4466 static JSObject* NewSandbox(JSContext* cx, bool lazy) {
4467   JS::RealmOptions options;
4468   SetStandardRealmOptions(options);
4469 
4470   if (defaultToSameCompartment) {
4471     options.creationOptions().setExistingCompartment(cx->global());
4472   } else {
4473     options.creationOptions().setNewCompartmentAndZone();
4474   }
4475 
4476   JSPrincipals* principals = nullptr;
4477   if (!CheckRealmOptions(cx, options, principals)) {
4478     return nullptr;
4479   }
4480 
4481   RootedObject obj(cx,
4482                    JS_NewGlobalObject(cx, &sandbox_class, principals,
4483                                       JS::DontFireOnNewGlobalHook, options));
4484   if (!obj) {
4485     return nullptr;
4486   }
4487 
4488   {
4489     JSAutoRealm ar(cx, obj);
4490     if (!lazy && !JS::InitRealmStandardClasses(cx)) {
4491       return nullptr;
4492     }
4493 
4494     RootedValue value(cx, BooleanValue(lazy));
4495     if (!JS_DefineProperty(cx, obj, "lazy", value,
4496                            JSPROP_PERMANENT | JSPROP_READONLY)) {
4497       return nullptr;
4498     }
4499 
4500     JS_FireOnNewGlobalObject(cx, obj);
4501   }
4502 
4503   if (!cx->compartment()->wrap(cx, &obj)) {
4504     return nullptr;
4505   }
4506   return obj;
4507 }
4508 
EvalInContext(JSContext * cx,unsigned argc,Value * vp)4509 static bool EvalInContext(JSContext* cx, unsigned argc, Value* vp) {
4510   CallArgs args = CallArgsFromVp(argc, vp);
4511   if (!args.requireAtLeast(cx, "evalcx", 1)) {
4512     return false;
4513   }
4514 
4515   RootedString str(cx, ToString(cx, args[0]));
4516   if (!str) {
4517     return false;
4518   }
4519 
4520   RootedObject sobj(cx);
4521   if (args.hasDefined(1)) {
4522     sobj = ToObject(cx, args[1]);
4523     if (!sobj) {
4524       return false;
4525     }
4526   }
4527 
4528   AutoStableStringChars strChars(cx);
4529   if (!strChars.initTwoByte(cx, str)) {
4530     return false;
4531   }
4532 
4533   mozilla::Range<const char16_t> chars = strChars.twoByteRange();
4534   size_t srclen = chars.length();
4535   const char16_t* src = chars.begin().get();
4536 
4537   bool lazy = false;
4538   if (srclen == 4) {
4539     if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
4540       lazy = true;
4541       srclen = 0;
4542     }
4543   }
4544 
4545   if (!sobj) {
4546     sobj = NewSandbox(cx, lazy);
4547     if (!sobj) {
4548       return false;
4549     }
4550   }
4551 
4552   if (srclen == 0) {
4553     args.rval().setObject(*sobj);
4554     return true;
4555   }
4556 
4557   JS::AutoFilename filename;
4558   unsigned lineno;
4559 
4560   DescribeScriptedCaller(cx, &filename, &lineno);
4561   {
4562     sobj = UncheckedUnwrap(sobj, true);
4563 
4564     JSAutoRealm ar(cx, sobj);
4565 
4566     sobj = ToWindowIfWindowProxy(sobj);
4567 
4568     if (!JS_IsGlobalObject(sobj)) {
4569       JS_ReportErrorASCII(cx, "Invalid scope argument to evalcx");
4570       return false;
4571     }
4572 
4573     JS::CompileOptions opts(cx);
4574     opts.setFileAndLine(filename.get(), lineno);
4575 
4576     JS::SourceText<char16_t> srcBuf;
4577     if (!srcBuf.init(cx, src, srclen, JS::SourceOwnership::Borrowed) ||
4578         !JS::Evaluate(cx, opts, srcBuf, args.rval())) {
4579       return false;
4580     }
4581   }
4582 
4583   if (!cx->compartment()->wrap(cx, args.rval())) {
4584     return false;
4585   }
4586 
4587   return true;
4588 }
4589 
EnsureGeckoProfilingStackInstalled(JSContext * cx,ShellContext * sc)4590 static bool EnsureGeckoProfilingStackInstalled(JSContext* cx,
4591                                                ShellContext* sc) {
4592   if (cx->geckoProfiler().infraInstalled()) {
4593     MOZ_ASSERT(sc->geckoProfilingStack);
4594     return true;
4595   }
4596 
4597   MOZ_ASSERT(!sc->geckoProfilingStack);
4598   sc->geckoProfilingStack = MakeUnique<ProfilingStack>();
4599   if (!sc->geckoProfilingStack) {
4600     JS_ReportOutOfMemory(cx);
4601     return false;
4602   }
4603 
4604   SetContextProfilingStack(cx, sc->geckoProfilingStack.get());
4605   return true;
4606 }
4607 
4608 struct WorkerInput {
4609   JSRuntime* parentRuntime;
4610   UniqueTwoByteChars chars;
4611   size_t length;
4612 
WorkerInputWorkerInput4613   WorkerInput(JSRuntime* parentRuntime, UniqueTwoByteChars chars, size_t length)
4614       : parentRuntime(parentRuntime), chars(std::move(chars)), length(length) {}
4615 
4616   ~WorkerInput() = default;
4617 };
4618 
DestroyShellCompartmentPrivate(JSFreeOp * fop,JS::Compartment * compartment)4619 static void DestroyShellCompartmentPrivate(JSFreeOp* fop,
4620                                            JS::Compartment* compartment) {
4621   auto priv = static_cast<ShellCompartmentPrivate*>(
4622       JS_GetCompartmentPrivate(compartment));
4623   js_delete(priv);
4624 }
4625 
4626 static void SetWorkerContextOptions(JSContext* cx);
4627 static bool ShellBuildId(JS::BuildIdCharVector* buildId);
4628 
ShellSourceElementCallback(JSContext * cx,JS::HandleValue privateValue)4629 static JSObject* ShellSourceElementCallback(JSContext* cx,
4630                                             JS::HandleValue privateValue) {
4631   if (!privateValue.isObject()) {
4632     return nullptr;
4633   }
4634 
4635   // Due to nukeCCW shenanigans in the shell, we need to check for dead-proxy
4636   // objects that may have replaced an CCW.  Otherwise the GetProperty below
4637   // would throw an exception which we do not want to support in this callback.
4638   if (js::IsDeadProxyObject(&privateValue.toObject())) {
4639     return nullptr;
4640   }
4641 
4642   RootedObject infoObject(cx,
4643                           CheckedUnwrapStatic(privateValue.toObjectOrNull()));
4644   AutoRealm ar(cx, infoObject);
4645 
4646   RootedValue elementValue(cx);
4647   if (!JS_GetProperty(cx, infoObject, "element", &elementValue)) {
4648     // This shouldn't happen in the shell, as ParseDebugMetadata always
4649     // creates the infoObject with this property. In any case, this callback
4650     // must not leave an exception pending, so:
4651     MOZ_CRASH("error getting source element");
4652   }
4653 
4654   if (elementValue.isObject()) {
4655     return &elementValue.toObject();
4656   }
4657 
4658   return nullptr;
4659 }
4660 
4661 static constexpr size_t gWorkerStackSize = 2 * 128 * sizeof(size_t) * 1024;
4662 
WorkerMain(WorkerInput * input)4663 static void WorkerMain(WorkerInput* input) {
4664   MOZ_ASSERT(input->parentRuntime);
4665 
4666   JSContext* cx = JS_NewContext(8L * 1024L * 1024L, input->parentRuntime);
4667   if (!cx) {
4668     return;
4669   }
4670 
4671   ShellContext* sc = js_new<ShellContext>(cx);
4672   if (!sc) {
4673     return;
4674   }
4675 
4676   auto guard = mozilla::MakeScopeExit([&] {
4677     CancelOffThreadJobsForContext(cx);
4678     sc->markObservers.reset();
4679     JS_SetContextPrivate(cx, nullptr);
4680     js_delete(sc);
4681     JS_DestroyContext(cx);
4682     js_delete(input);
4683   });
4684 
4685   sc->isWorker = true;
4686 
4687   JS_SetContextPrivate(cx, sc);
4688   JS_SetGrayGCRootsTracer(cx, TraceGrayRoots, nullptr);
4689   SetWorkerContextOptions(cx);
4690 
4691   JS_SetFutexCanWait(cx);
4692   JS::SetWarningReporter(cx, WarningReporter);
4693   js::SetPreserveWrapperCallbacks(cx, DummyPreserveWrapperCallback,
4694                                   DummyHasReleasedWrapperCallback);
4695   JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy);
4696   JS_SetDestroyCompartmentCallback(cx, DestroyShellCompartmentPrivate);
4697   JS::SetSourceElementCallback(cx, ShellSourceElementCallback);
4698 
4699   js::SetWindowProxyClass(cx, &ShellWindowProxyClass);
4700 
4701   js::UseInternalJobQueues(cx);
4702 
4703   JS::SetHostCleanupFinalizationRegistryCallback(
4704       cx, ShellCleanupFinalizationRegistryCallback, sc);
4705 
4706   if (!JS::InitSelfHostedCode(cx)) {
4707     return;
4708   }
4709 
4710   EnvironmentPreparer environmentPreparer(cx);
4711 
4712   do {
4713     JS::RealmOptions realmOptions;
4714     SetStandardRealmOptions(realmOptions);
4715 
4716     RootedObject global(cx, NewGlobalObject(cx, realmOptions, nullptr,
4717                                             ShellGlobalKind::WindowProxy,
4718                                             /* immutablePrototype = */ true));
4719     if (!global) {
4720       break;
4721     }
4722 
4723     JSAutoRealm ar(cx, global);
4724 
4725     JS::ConstUTF8CharsZ path(processWideModuleLoadPath.get(),
4726                              strlen(processWideModuleLoadPath.get()));
4727     RootedString moduleLoadPath(cx, JS_NewStringCopyUTF8Z(cx, path));
4728     if (!moduleLoadPath) {
4729       return;
4730     }
4731     sc->moduleLoader = js::MakeUnique<ModuleLoader>();
4732     if (!sc->moduleLoader || !sc->moduleLoader->init(cx, moduleLoadPath)) {
4733       return;
4734     }
4735 
4736     JS::CompileOptions options(cx);
4737     options.setFileAndLine("<string>", 1).setIsRunOnce(true);
4738 
4739     AutoReportException are(cx);
4740     JS::SourceText<char16_t> srcBuf;
4741     if (!srcBuf.init(cx, input->chars.get(), input->length,
4742                      JS::SourceOwnership::Borrowed)) {
4743       break;
4744     }
4745 
4746     RootedScript script(cx, JS::Compile(cx, options, srcBuf));
4747     if (!script) {
4748       break;
4749     }
4750     RootedValue result(cx);
4751     JS_ExecuteScript(cx, script, &result);
4752   } while (0);
4753 
4754   KillWatchdog(cx);
4755   JS_SetGrayGCRootsTracer(cx, nullptr, nullptr);
4756 }
4757 
4758 // Workers can spawn other workers, so we need a lock to access workerThreads.
4759 static Mutex* workerThreadsLock = nullptr;
4760 static Vector<js::Thread*, 0, SystemAllocPolicy> workerThreads;
4761 
4762 class MOZ_RAII AutoLockWorkerThreads : public LockGuard<Mutex> {
4763   using Base = LockGuard<Mutex>;
4764 
4765  public:
AutoLockWorkerThreads()4766   AutoLockWorkerThreads() : Base(*workerThreadsLock) {
4767     MOZ_ASSERT(workerThreadsLock);
4768   }
4769 };
4770 
EvalInWorker(JSContext * cx,unsigned argc,Value * vp)4771 static bool EvalInWorker(JSContext* cx, unsigned argc, Value* vp) {
4772   if (!CanUseExtraThreads()) {
4773     JS_ReportErrorASCII(cx, "Can't create threads with --no-threads");
4774     return false;
4775   }
4776 
4777   CallArgs args = CallArgsFromVp(argc, vp);
4778   if (!args.get(0).isString()) {
4779     JS_ReportErrorASCII(cx, "Invalid arguments");
4780     return false;
4781   }
4782 
4783 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
4784   if (cx->runningOOMTest) {
4785     JS_ReportErrorASCII(
4786         cx, "Can't create threads while running simulated OOM test");
4787     return false;
4788   }
4789 #endif
4790 
4791   if (!args[0].toString()->ensureLinear(cx)) {
4792     return false;
4793   }
4794 
4795   if (!workerThreadsLock) {
4796     workerThreadsLock = js_new<Mutex>(mutexid::ShellWorkerThreads);
4797     if (!workerThreadsLock) {
4798       ReportOutOfMemory(cx);
4799       return false;
4800     }
4801   }
4802 
4803   JSLinearString* str = &args[0].toString()->asLinear();
4804 
4805   UniqueTwoByteChars chars(js_pod_malloc<char16_t>(str->length()));
4806   if (!chars) {
4807     ReportOutOfMemory(cx);
4808     return false;
4809   }
4810 
4811   CopyChars(chars.get(), *str);
4812 
4813   WorkerInput* input = js_new<WorkerInput>(JS_GetParentRuntime(cx),
4814                                            std::move(chars), str->length());
4815   if (!input) {
4816     ReportOutOfMemory(cx);
4817     return false;
4818   }
4819 
4820   Thread* thread;
4821   {
4822     AutoEnterOOMUnsafeRegion oomUnsafe;
4823     thread = js_new<Thread>(
4824         Thread::Options().setStackSize(gWorkerStackSize + 512 * 1024));
4825     if (!thread || !thread->init(WorkerMain, input)) {
4826       oomUnsafe.crash("EvalInWorker");
4827     }
4828   }
4829 
4830   AutoLockWorkerThreads alwt;
4831   if (!workerThreads.append(thread)) {
4832     ReportOutOfMemory(cx);
4833     thread->join();
4834     js_delete(thread);
4835     return false;
4836   }
4837 
4838   args.rval().setUndefined();
4839   return true;
4840 }
4841 
ShapeOf(JSContext * cx,unsigned argc,JS::Value * vp)4842 static bool ShapeOf(JSContext* cx, unsigned argc, JS::Value* vp) {
4843   CallArgs args = CallArgsFromVp(argc, vp);
4844   if (!args.get(0).isObject()) {
4845     JS_ReportErrorASCII(cx, "shapeOf: object expected");
4846     return false;
4847   }
4848   JSObject* obj = &args[0].toObject();
4849   args.rval().set(JS_NumberValue(double(uintptr_t(obj->shape()) >> 3)));
4850   return true;
4851 }
4852 
Sleep_fn(JSContext * cx,unsigned argc,Value * vp)4853 static bool Sleep_fn(JSContext* cx, unsigned argc, Value* vp) {
4854   ShellContext* sc = GetShellContext(cx);
4855   CallArgs args = CallArgsFromVp(argc, vp);
4856 
4857   TimeDuration duration = TimeDuration::FromSeconds(0.0);
4858   if (args.length() > 0) {
4859     double t_secs;
4860     if (!ToNumber(cx, args[0], &t_secs)) {
4861       return false;
4862     }
4863     if (mozilla::IsNaN(t_secs)) {
4864       JS_ReportErrorASCII(cx, "sleep interval is not a number");
4865       return false;
4866     }
4867 
4868     duration = TimeDuration::FromSeconds(std::max(0.0, t_secs));
4869     const TimeDuration MAX_TIMEOUT_INTERVAL =
4870         TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS);
4871     if (duration > MAX_TIMEOUT_INTERVAL) {
4872       JS_ReportErrorASCII(cx, "Excessive sleep interval");
4873       return false;
4874     }
4875   }
4876   {
4877     LockGuard<Mutex> guard(sc->watchdogLock);
4878     TimeStamp toWakeup = TimeStamp::Now() + duration;
4879     for (;;) {
4880       sc->sleepWakeup.wait_for(guard, duration);
4881       if (sc->serviceInterrupt) {
4882         break;
4883       }
4884       auto now = TimeStamp::Now();
4885       if (now >= toWakeup) {
4886         break;
4887       }
4888       duration = toWakeup - now;
4889     }
4890   }
4891   args.rval().setUndefined();
4892   return !sc->serviceInterrupt;
4893 }
4894 
KillWatchdog(JSContext * cx)4895 static void KillWatchdog(JSContext* cx) {
4896   ShellContext* sc = GetShellContext(cx);
4897   Maybe<Thread> thread;
4898 
4899   {
4900     LockGuard<Mutex> guard(sc->watchdogLock);
4901     std::swap(sc->watchdogThread, thread);
4902     if (thread) {
4903       // The watchdog thread becoming Nothing is its signal to exit.
4904       sc->watchdogWakeup.notify_one();
4905     }
4906   }
4907   if (thread) {
4908     thread->join();
4909   }
4910 
4911   MOZ_ASSERT(!sc->watchdogThread);
4912 }
4913 
WatchdogMain(JSContext * cx)4914 static void WatchdogMain(JSContext* cx) {
4915   ThisThread::SetName("JS Watchdog");
4916 
4917   ShellContext* sc = GetShellContext(cx);
4918 
4919   {
4920     LockGuard<Mutex> guard(sc->watchdogLock);
4921     while (sc->watchdogThread) {
4922       auto now = TimeStamp::Now();
4923       if (sc->watchdogTimeout && now >= sc->watchdogTimeout.value()) {
4924         /*
4925          * The timeout has just expired. Request an interrupt callback
4926          * outside the lock.
4927          */
4928         sc->watchdogTimeout = Nothing();
4929         {
4930           UnlockGuard<Mutex> unlock(guard);
4931           CancelExecution(cx);
4932         }
4933 
4934         /* Wake up any threads doing sleep. */
4935         sc->sleepWakeup.notify_all();
4936       } else {
4937         if (sc->watchdogTimeout) {
4938           /*
4939            * Time hasn't expired yet. Simulate an interrupt callback
4940            * which doesn't abort execution.
4941            */
4942           JS_RequestInterruptCallback(cx);
4943         }
4944 
4945         TimeDuration sleepDuration = sc->watchdogTimeout
4946                                          ? TimeDuration::FromSeconds(0.1)
4947                                          : TimeDuration::Forever();
4948         sc->watchdogWakeup.wait_for(guard, sleepDuration);
4949       }
4950     }
4951   }
4952 }
4953 
ScheduleWatchdog(JSContext * cx,double t)4954 static bool ScheduleWatchdog(JSContext* cx, double t) {
4955   ShellContext* sc = GetShellContext(cx);
4956 
4957   if (t <= 0) {
4958     LockGuard<Mutex> guard(sc->watchdogLock);
4959     sc->watchdogTimeout = Nothing();
4960     return true;
4961   }
4962 
4963 #ifdef __wasi__
4964   return false;
4965 #endif
4966 
4967   auto interval = TimeDuration::FromSeconds(t);
4968   auto timeout = TimeStamp::Now() + interval;
4969   LockGuard<Mutex> guard(sc->watchdogLock);
4970   if (!sc->watchdogThread) {
4971     MOZ_ASSERT(!sc->watchdogTimeout);
4972     sc->watchdogThread.emplace();
4973     AutoEnterOOMUnsafeRegion oomUnsafe;
4974     if (!sc->watchdogThread->init(WatchdogMain, cx)) {
4975       oomUnsafe.crash("watchdogThread.init");
4976     }
4977   } else if (!sc->watchdogTimeout || timeout < sc->watchdogTimeout.value()) {
4978     sc->watchdogWakeup.notify_one();
4979   }
4980   sc->watchdogTimeout = Some(timeout);
4981   return true;
4982 }
4983 
KillWorkerThreads(JSContext * cx)4984 static void KillWorkerThreads(JSContext* cx) {
4985   MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty());
4986 
4987   if (!workerThreadsLock) {
4988     MOZ_ASSERT(workerThreads.empty());
4989     return;
4990   }
4991 
4992   while (true) {
4993     // We need to leave the AutoLockWorkerThreads scope before we call
4994     // js::Thread::join, to avoid deadlocks when AutoLockWorkerThreads is
4995     // used by the worker thread.
4996     Thread* thread;
4997     {
4998       AutoLockWorkerThreads alwt;
4999       if (workerThreads.empty()) {
5000         break;
5001       }
5002       thread = workerThreads.popCopy();
5003     }
5004     thread->join();
5005     js_delete(thread);
5006   }
5007 
5008   workerThreads.clearAndFree();
5009 
5010   js_delete(workerThreadsLock);
5011   workerThreadsLock = nullptr;
5012 }
5013 
CancelExecution(JSContext * cx)5014 static void CancelExecution(JSContext* cx) {
5015   ShellContext* sc = GetShellContext(cx);
5016   sc->serviceInterrupt = true;
5017   JS_RequestInterruptCallback(cx);
5018 }
5019 
SetTimeoutValue(JSContext * cx,double t)5020 static bool SetTimeoutValue(JSContext* cx, double t) {
5021   if (mozilla::IsNaN(t)) {
5022     JS_ReportErrorASCII(cx, "timeout is not a number");
5023     return false;
5024   }
5025   const TimeDuration MAX_TIMEOUT_INTERVAL =
5026       TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS);
5027   if (TimeDuration::FromSeconds(t) > MAX_TIMEOUT_INTERVAL) {
5028     JS_ReportErrorASCII(cx, "Excessive timeout value");
5029     return false;
5030   }
5031   GetShellContext(cx)->timeoutInterval = t;
5032   if (!ScheduleWatchdog(cx, t)) {
5033     JS_ReportErrorASCII(cx, "Failed to create the watchdog");
5034     return false;
5035   }
5036   return true;
5037 }
5038 
Timeout(JSContext * cx,unsigned argc,Value * vp)5039 static bool Timeout(JSContext* cx, unsigned argc, Value* vp) {
5040   ShellContext* sc = GetShellContext(cx);
5041   CallArgs args = CallArgsFromVp(argc, vp);
5042 
5043   if (args.length() == 0) {
5044     args.rval().setNumber(sc->timeoutInterval);
5045     return true;
5046   }
5047 
5048   if (args.length() > 2) {
5049     JS_ReportErrorASCII(cx, "Wrong number of arguments");
5050     return false;
5051   }
5052 
5053   double t;
5054   if (!ToNumber(cx, args[0], &t)) {
5055     return false;
5056   }
5057 
5058   if (args.length() > 1) {
5059     RootedValue value(cx, args[1]);
5060     if (!value.isObject() || !value.toObject().is<JSFunction>()) {
5061       JS_ReportErrorASCII(cx, "Second argument must be a timeout function");
5062       return false;
5063     }
5064     sc->interruptFunc = value;
5065     sc->haveInterruptFunc = true;
5066   }
5067 
5068   args.rval().setUndefined();
5069   return SetTimeoutValue(cx, t);
5070 }
5071 
InterruptIf(JSContext * cx,unsigned argc,Value * vp)5072 static bool InterruptIf(JSContext* cx, unsigned argc, Value* vp) {
5073   CallArgs args = CallArgsFromVp(argc, vp);
5074 
5075   if (args.length() != 1) {
5076     JS_ReportErrorASCII(cx, "Wrong number of arguments");
5077     return false;
5078   }
5079 
5080   if (ToBoolean(args[0])) {
5081     GetShellContext(cx)->serviceInterrupt = true;
5082     JS_RequestInterruptCallback(cx);
5083   }
5084 
5085   args.rval().setUndefined();
5086   return true;
5087 }
5088 
InvokeInterruptCallbackWrapper(JSContext * cx,unsigned argc,Value * vp)5089 static bool InvokeInterruptCallbackWrapper(JSContext* cx, unsigned argc,
5090                                            Value* vp) {
5091   CallArgs args = CallArgsFromVp(argc, vp);
5092   if (args.length() != 1) {
5093     JS_ReportErrorASCII(cx, "Wrong number of arguments");
5094     return false;
5095   }
5096 
5097   GetShellContext(cx)->serviceInterrupt = true;
5098   JS_RequestInterruptCallback(cx);
5099   bool interruptRv = CheckForInterrupt(cx);
5100 
5101   // The interrupt handler could have set a pending exception. Since we call
5102   // back into JS, don't have it see the pending exception. If we have an
5103   // uncatchable exception that's not propagating a debug mode forced
5104   // return, return.
5105   if (!interruptRv && !cx->isExceptionPending() &&
5106       !cx->isPropagatingForcedReturn()) {
5107     return false;
5108   }
5109 
5110   JS::AutoSaveExceptionState savedExc(cx);
5111 
5112   FixedInvokeArgs<1> iargs(cx);
5113 
5114   iargs[0].setBoolean(interruptRv);
5115 
5116   RootedValue rv(cx);
5117   if (!js::Call(cx, args[0], UndefinedHandleValue, iargs, &rv)) {
5118     return false;
5119   }
5120 
5121   args.rval().setUndefined();
5122   return interruptRv;
5123 }
5124 
SetInterruptCallback(JSContext * cx,unsigned argc,Value * vp)5125 static bool SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp) {
5126   CallArgs args = CallArgsFromVp(argc, vp);
5127 
5128   if (args.length() != 1) {
5129     JS_ReportErrorASCII(cx, "Wrong number of arguments");
5130     return false;
5131   }
5132 
5133   RootedValue value(cx, args[0]);
5134   if (!value.isObject() || !value.toObject().is<JSFunction>()) {
5135     JS_ReportErrorASCII(cx, "Argument must be a function");
5136     return false;
5137   }
5138   GetShellContext(cx)->interruptFunc = value;
5139   GetShellContext(cx)->haveInterruptFunc = true;
5140 
5141   args.rval().setUndefined();
5142   return true;
5143 }
5144 
5145 #ifdef DEBUG
5146 // var s0 = "A".repeat(10*1024);
5147 // interruptRegexp(/a(bc|bd)/, s0);
5148 // first arg is regexp
5149 // second arg is string
InterruptRegexp(JSContext * cx,unsigned argc,Value * vp)5150 static bool InterruptRegexp(JSContext* cx, unsigned argc, Value* vp) {
5151   CallArgs args = CallArgsFromVp(argc, vp);
5152   ShellContext* sc = GetShellContext(cx);
5153   RootedObject callee(cx, &args.callee());
5154 
5155   if (args.length() != 2) {
5156     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments.");
5157     return false;
5158   }
5159   if (!(args[0].isObject() && args[0].toObject().is<RegExpObject>())) {
5160     ReportUsageErrorASCII(cx, callee,
5161                           "First argument must be a regular expression.");
5162     return false;
5163   }
5164   if (!args[1].isString()) {
5165     ReportUsageErrorASCII(cx, callee, "Second argument must be a String.");
5166     return false;
5167   }
5168   // Set interrupt flags
5169   sc->serviceInterrupt = true;
5170   js::irregexp::IsolateSetShouldSimulateInterrupt(cx->isolate);
5171 
5172   RootedObject regexp(cx, &args[0].toObject());
5173   RootedString string(cx, args[1].toString());
5174   int32_t lastIndex = 0;
5175 
5176   return js::RegExpMatcherRaw(cx, regexp, string, lastIndex, nullptr,
5177                               args.rval());
5178 }
5179 #endif
5180 
SetJitCompilerOption(JSContext * cx,unsigned argc,Value * vp)5181 static bool SetJitCompilerOption(JSContext* cx, unsigned argc, Value* vp) {
5182   CallArgs args = CallArgsFromVp(argc, vp);
5183   RootedObject callee(cx, &args.callee());
5184 
5185   if (args.length() != 2) {
5186     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments.");
5187     return false;
5188   }
5189 
5190   if (!args[0].isString()) {
5191     ReportUsageErrorASCII(cx, callee, "First argument must be a String.");
5192     return false;
5193   }
5194 
5195   if (!args[1].isInt32()) {
5196     ReportUsageErrorASCII(cx, callee, "Second argument must be an Int32.");
5197     return false;
5198   }
5199 
5200   // Disallow setting JIT options when there are worker threads, to avoid
5201   // races.
5202   if (workerThreadsLock) {
5203     ReportUsageErrorASCII(
5204         cx, callee, "Can't set JIT options when there are worker threads.");
5205     return false;
5206   }
5207 
5208   JSLinearString* strArg = JS_EnsureLinearString(cx, args[0].toString());
5209   if (!strArg) {
5210     return false;
5211   }
5212 
5213 #define JIT_COMPILER_MATCH(key, string)                        \
5214   else if (JS_LinearStringEqualsLiteral(strArg, string)) opt = \
5215       JSJITCOMPILER_##key;
5216 
5217   JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
5218   if (false) {
5219   }
5220   JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
5221 #undef JIT_COMPILER_MATCH
5222 
5223   if (opt == JSJITCOMPILER_NOT_AN_OPTION) {
5224     ReportUsageErrorASCII(
5225         cx, callee,
5226         "First argument does not name a valid option (see jsapi.h).");
5227     return false;
5228   }
5229 
5230   int32_t number = args[1].toInt32();
5231   if (number < 0) {
5232     number = -1;
5233   }
5234 
5235   // Disallow enabling or disabling the Baseline Interpreter at runtime.
5236   // Enabling is a problem because the Baseline Interpreter code is only
5237   // present if the interpreter was enabled when the JitRuntime was created.
5238   // To support disabling we would have to discard all JitScripts. Furthermore,
5239   // we really want JitOptions to be immutable after startup so it's better to
5240   // use shell flags.
5241   if (opt == JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE &&
5242       bool(number) != jit::IsBaselineInterpreterEnabled()) {
5243     JS_ReportErrorASCII(cx,
5244                         "Enabling or disabling the Baseline Interpreter at "
5245                         "runtime is not supported.");
5246     return false;
5247   }
5248 
5249   // Throw if disabling the JITs and there's JIT code on the stack, to avoid
5250   // assertion failures.
5251   if ((opt == JSJITCOMPILER_BASELINE_ENABLE ||
5252        opt == JSJITCOMPILER_ION_ENABLE) &&
5253       number == 0) {
5254     js::jit::JitActivationIterator iter(cx);
5255     if (!iter.done()) {
5256       JS_ReportErrorASCII(cx,
5257                           "Can't turn off JITs with JIT code on the stack.");
5258       return false;
5259     }
5260   }
5261 
5262   // Throw if trying to disable all the Wasm compilers.  The logic here is that
5263   // if we're trying to disable a compiler that is currently enabled and that is
5264   // the last compiler enabled then we must throw.
5265   //
5266   // Note that this check does not prevent an error from being thrown later.
5267   // Actual compiler availability is dynamic and depends on other conditions,
5268   // such as other options set and whether a debugger is present.
5269   if ((opt == JSJITCOMPILER_WASM_JIT_BASELINE ||
5270        opt == JSJITCOMPILER_WASM_JIT_OPTIMIZING) &&
5271       number == 0) {
5272     uint32_t baseline, optimizing;
5273     MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
5274         cx, JSJITCOMPILER_WASM_JIT_BASELINE, &baseline));
5275     MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
5276         cx, JSJITCOMPILER_WASM_JIT_OPTIMIZING, &optimizing));
5277     if (baseline + optimizing == 1) {
5278       if ((opt == JSJITCOMPILER_WASM_JIT_BASELINE && baseline) ||
5279           (opt == JSJITCOMPILER_WASM_JIT_OPTIMIZING && optimizing)) {
5280         JS_ReportErrorASCII(
5281             cx,
5282             "Disabling all the Wasm compilers at runtime is not supported.");
5283         return false;
5284       }
5285     }
5286   }
5287 
5288   // JIT compiler options are process-wide, so we have to stop off-thread
5289   // compilations for all runtimes to avoid races.
5290   WaitForAllHelperThreads();
5291 
5292   // Only release JIT code for the current runtime because there's no good
5293   // way to discard code for other runtimes.
5294   ReleaseAllJITCode(cx->runtime()->defaultFreeOp());
5295 
5296   JS_SetGlobalJitCompilerOption(cx, opt, uint32_t(number));
5297 
5298   args.rval().setUndefined();
5299   return true;
5300 }
5301 
EnableLastWarning(JSContext * cx,unsigned argc,Value * vp)5302 static bool EnableLastWarning(JSContext* cx, unsigned argc, Value* vp) {
5303   ShellContext* sc = GetShellContext(cx);
5304   CallArgs args = CallArgsFromVp(argc, vp);
5305 
5306   sc->lastWarningEnabled = true;
5307   sc->lastWarning.setNull();
5308 
5309   args.rval().setUndefined();
5310   return true;
5311 }
5312 
DisableLastWarning(JSContext * cx,unsigned argc,Value * vp)5313 static bool DisableLastWarning(JSContext* cx, unsigned argc, Value* vp) {
5314   ShellContext* sc = GetShellContext(cx);
5315   CallArgs args = CallArgsFromVp(argc, vp);
5316 
5317   sc->lastWarningEnabled = false;
5318   sc->lastWarning.setNull();
5319 
5320   args.rval().setUndefined();
5321   return true;
5322 }
5323 
GetLastWarning(JSContext * cx,unsigned argc,Value * vp)5324 static bool GetLastWarning(JSContext* cx, unsigned argc, Value* vp) {
5325   ShellContext* sc = GetShellContext(cx);
5326   CallArgs args = CallArgsFromVp(argc, vp);
5327 
5328   if (!sc->lastWarningEnabled) {
5329     JS_ReportErrorASCII(cx, "Call enableLastWarning first.");
5330     return false;
5331   }
5332 
5333   if (!JS_WrapValue(cx, &sc->lastWarning)) {
5334     return false;
5335   }
5336 
5337   args.rval().set(sc->lastWarning);
5338   return true;
5339 }
5340 
ClearLastWarning(JSContext * cx,unsigned argc,Value * vp)5341 static bool ClearLastWarning(JSContext* cx, unsigned argc, Value* vp) {
5342   ShellContext* sc = GetShellContext(cx);
5343   CallArgs args = CallArgsFromVp(argc, vp);
5344 
5345   if (!sc->lastWarningEnabled) {
5346     JS_ReportErrorASCII(cx, "Call enableLastWarning first.");
5347     return false;
5348   }
5349 
5350   sc->lastWarning.setNull();
5351 
5352   args.rval().setUndefined();
5353   return true;
5354 }
5355 
5356 #if defined(DEBUG) || defined(JS_JITSPEW)
StackDump(JSContext * cx,unsigned argc,Value * vp)5357 static bool StackDump(JSContext* cx, unsigned argc, Value* vp) {
5358   CallArgs args = CallArgsFromVp(argc, vp);
5359 
5360   if (!gOutFile->isOpen()) {
5361     JS_ReportErrorASCII(cx, "output file is closed");
5362     return false;
5363   }
5364 
5365   bool showArgs = ToBoolean(args.get(0));
5366   bool showLocals = ToBoolean(args.get(1));
5367   bool showThisProps = ToBoolean(args.get(2));
5368 
5369   JS::UniqueChars buf =
5370       JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
5371   if (!buf) {
5372     fputs("Failed to format JavaScript stack for dump\n", gOutFile->fp);
5373     JS_ClearPendingException(cx);
5374   } else {
5375     fputs(buf.get(), gOutFile->fp);
5376   }
5377 
5378   args.rval().setUndefined();
5379   return true;
5380 }
5381 #endif
5382 
StackPointerInfo(JSContext * cx,unsigned argc,Value * vp)5383 static bool StackPointerInfo(JSContext* cx, unsigned argc, Value* vp) {
5384   CallArgs args = CallArgsFromVp(argc, vp);
5385 
5386   // Copy the truncated stack pointer to the result.  This value is not used
5387   // as a pointer but as a way to measure frame-size from JS.
5388   args.rval().setInt32(int32_t(reinterpret_cast<size_t>(&args) & 0xfffffff));
5389   return true;
5390 }
5391 
Elapsed(JSContext * cx,unsigned argc,Value * vp)5392 static bool Elapsed(JSContext* cx, unsigned argc, Value* vp) {
5393   CallArgs args = CallArgsFromVp(argc, vp);
5394   if (args.length() == 0) {
5395     double d = PRMJ_Now() - GetShellContext(cx)->startTime;
5396     args.rval().setDouble(d);
5397     return true;
5398   }
5399   JS_ReportErrorASCII(cx, "Wrong number of arguments");
5400   return false;
5401 }
5402 
Compile(JSContext * cx,unsigned argc,Value * vp)5403 static bool Compile(JSContext* cx, unsigned argc, Value* vp) {
5404   CallArgs args = CallArgsFromVp(argc, vp);
5405   if (!args.requireAtLeast(cx, "compile", 1)) {
5406     return false;
5407   }
5408   if (!args[0].isString()) {
5409     const char* typeName = InformalValueTypeName(args[0]);
5410     JS_ReportErrorASCII(cx, "expected string to compile, got %s", typeName);
5411     return false;
5412   }
5413 
5414   RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
5415   RootedString scriptContents(cx, args[0].toString());
5416 
5417   AutoStableStringChars stableChars(cx);
5418   if (!stableChars.initTwoByte(cx, scriptContents)) {
5419     return false;
5420   }
5421 
5422   JS::CompileOptions options(cx);
5423   options.setIntroductionType("js shell compile")
5424       .setFileAndLine("<string>", 1)
5425       .setIsRunOnce(true)
5426       .setNoScriptRval(true)
5427       .setdeferDebugMetadata();
5428   RootedValue privateValue(cx);
5429   RootedString elementAttributeName(cx);
5430 
5431   if (args.length() >= 2) {
5432     if (args[1].isPrimitive()) {
5433       JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
5434                                 JSSMSG_INVALID_ARGS, "compile");
5435       return false;
5436     }
5437 
5438     RootedObject opts(cx, &args[1].toObject());
5439     if (!js::ParseCompileOptions(cx, options, opts, nullptr)) {
5440       return false;
5441     }
5442     if (!ParseDebugMetadata(cx, opts, &privateValue, &elementAttributeName)) {
5443       return false;
5444     }
5445   }
5446 
5447   JS::SourceText<char16_t> srcBuf;
5448   if (!srcBuf.init(cx, stableChars.twoByteRange().begin().get(),
5449                    scriptContents->length(), JS::SourceOwnership::Borrowed)) {
5450     return false;
5451   }
5452 
5453   RootedScript script(cx, JS::Compile(cx, options, srcBuf));
5454   if (!script) {
5455     return false;
5456   }
5457 
5458   if (!JS::UpdateDebugMetadata(cx, script, options, privateValue,
5459                                elementAttributeName, nullptr, nullptr)) {
5460     return false;
5461   }
5462 
5463   args.rval().setUndefined();
5464   return true;
5465 }
5466 
EnsureShellCompartmentPrivate(JSContext * cx)5467 static ShellCompartmentPrivate* EnsureShellCompartmentPrivate(JSContext* cx) {
5468   Compartment* comp = cx->compartment();
5469   auto priv =
5470       static_cast<ShellCompartmentPrivate*>(JS_GetCompartmentPrivate(comp));
5471   if (!priv) {
5472     priv = cx->new_<ShellCompartmentPrivate>();
5473     JS_SetCompartmentPrivate(cx->compartment(), priv);
5474   }
5475   return priv;
5476 }
5477 
ParseModule(JSContext * cx,unsigned argc,Value * vp)5478 static bool ParseModule(JSContext* cx, unsigned argc, Value* vp) {
5479   CallArgs args = CallArgsFromVp(argc, vp);
5480   if (!args.requireAtLeast(cx, "parseModule", 1)) {
5481     return false;
5482   }
5483 
5484   if (!args[0].isString()) {
5485     const char* typeName = InformalValueTypeName(args[0]);
5486     JS_ReportErrorASCII(cx, "expected string to compile, got %s", typeName);
5487     return false;
5488   }
5489 
5490   JSString* scriptContents = args[0].toString();
5491 
5492   UniqueChars filename;
5493   CompileOptions options(cx);
5494   if (args.length() > 1) {
5495     if (!args[1].isString()) {
5496       const char* typeName = InformalValueTypeName(args[1]);
5497       JS_ReportErrorASCII(cx, "expected filename string, got %s", typeName);
5498       return false;
5499     }
5500 
5501     RootedString str(cx, args[1].toString());
5502     filename = JS_EncodeStringToLatin1(cx, str);
5503     if (!filename) {
5504       return false;
5505     }
5506 
5507     options.setFileAndLine(filename.get(), 1);
5508   } else {
5509     options.setFileAndLine("<string>", 1);
5510   }
5511   options.setModule();
5512 
5513   AutoStableStringChars stableChars(cx);
5514   if (!stableChars.initTwoByte(cx, scriptContents)) {
5515     return false;
5516   }
5517 
5518   const char16_t* chars = stableChars.twoByteRange().begin().get();
5519   JS::SourceText<char16_t> srcBuf;
5520   if (!srcBuf.init(cx, chars, scriptContents->length(),
5521                    JS::SourceOwnership::Borrowed)) {
5522     return false;
5523   }
5524 
5525   RootedObject module(cx, frontend::CompileModule(cx, options, srcBuf));
5526   if (!module) {
5527     return false;
5528   }
5529 
5530   Rooted<ShellModuleObjectWrapper*> wrapper(
5531       cx, ShellModuleObjectWrapper::create(cx, module.as<ModuleObject>()));
5532   if (!wrapper) {
5533     return false;
5534   }
5535   args.rval().setObject(*wrapper);
5536   return true;
5537 }
5538 
5539 // A JSObject that holds XDRBuffer.
5540 class XDRBufferObject : public NativeObject {
5541   static const size_t VECTOR_SLOT = 0;
5542   static const unsigned RESERVED_SLOTS = 1;
5543 
5544  public:
5545   static const JSClassOps classOps_;
5546   static const JSClass class_;
5547 
5548   [[nodiscard]] inline static XDRBufferObject* create(
5549       JSContext* cx, JS::TranscodeBuffer&& buf);
5550 
data() const5551   JS::TranscodeBuffer* data() const {
5552     Value value = getReservedSlot(VECTOR_SLOT);
5553     auto buf = static_cast<JS::TranscodeBuffer*>(value.toPrivate());
5554     MOZ_ASSERT(buf);
5555     return buf;
5556   }
5557 
hasData() const5558   bool hasData() const {
5559     // Data may not be present if we hit OOM in initialization.
5560     return !getReservedSlot(VECTOR_SLOT).isUndefined();
5561   }
5562 
5563   static void finalize(JSFreeOp* fop, JSObject* obj);
5564 };
5565 
5566 /*static */ const JSClassOps XDRBufferObject::classOps_ = {
5567     nullptr,                    // addProperty
5568     nullptr,                    // delProperty
5569     nullptr,                    // enumerate
5570     nullptr,                    // newEnumerate
5571     nullptr,                    // resolve
5572     nullptr,                    // mayResolve
5573     XDRBufferObject::finalize,  // finalize
5574     nullptr,                    // call
5575     nullptr,                    // hasInstance
5576     nullptr,                    // construct
5577     nullptr,                    // trace
5578 };
5579 
5580 /*static */ const JSClass XDRBufferObject::class_ = {
5581     "XDRBufferObject",
5582     JSCLASS_HAS_RESERVED_SLOTS(XDRBufferObject::RESERVED_SLOTS) |
5583         JSCLASS_BACKGROUND_FINALIZE,
5584     &XDRBufferObject::classOps_};
5585 
create(JSContext * cx,JS::TranscodeBuffer && buf)5586 XDRBufferObject* XDRBufferObject::create(JSContext* cx,
5587                                          JS::TranscodeBuffer&& buf) {
5588   XDRBufferObject* bufObj =
5589       NewObjectWithGivenProto<XDRBufferObject>(cx, nullptr);
5590   if (!bufObj) {
5591     return nullptr;
5592   }
5593 
5594   auto heapBuf = cx->make_unique<JS::TranscodeBuffer>(std::move(buf));
5595   if (!heapBuf) {
5596     return nullptr;
5597   }
5598 
5599   size_t len = heapBuf->length();
5600   InitReservedSlot(bufObj, VECTOR_SLOT, heapBuf.release(), len,
5601                    MemoryUse::XDRBufferElements);
5602 
5603   return bufObj;
5604 }
5605 
finalize(JSFreeOp * fop,JSObject * obj)5606 void XDRBufferObject::finalize(JSFreeOp* fop, JSObject* obj) {
5607   XDRBufferObject* buf = &obj->as<XDRBufferObject>();
5608   if (buf->hasData()) {
5609     fop->delete_(buf, buf->data(), buf->data()->length(),
5610                  MemoryUse::XDRBufferElements);
5611   }
5612 }
5613 
CodeModule(JSContext * cx,unsigned argc,Value * vp)5614 static bool CodeModule(JSContext* cx, unsigned argc, Value* vp) {
5615   CallArgs args = CallArgsFromVp(argc, vp);
5616   if (!args.requireAtLeast(cx, "codeModule", 1)) {
5617     return false;
5618   }
5619 
5620   if (!args[0].isObject() ||
5621       !args[0].toObject().is<ShellModuleObjectWrapper>()) {
5622     const char* typeName = InformalValueTypeName(args[0]);
5623     JS_ReportErrorASCII(cx, "expected module object, got %s", typeName);
5624     return false;
5625   }
5626 
5627   RootedModuleObject modObject(
5628       cx, args[0].toObject().as<ShellModuleObjectWrapper>().get());
5629   if (modObject->status() >= MODULE_STATUS_LINKING) {
5630     JS_ReportErrorASCII(cx, "cannot encode module after instantiation.");
5631     return false;
5632   }
5633 
5634   JS::TranscodeBuffer buf;
5635   XDREncoder xdrEncoder_(cx, buf);
5636   XDRResult res = xdrEncoder_.codeModuleObject(&modObject);
5637   if (res.isErr()) {
5638     return false;
5639   }
5640 
5641   XDRBufferObject* xdrBuf = XDRBufferObject::create(cx, std::move(buf));
5642   if (!xdrBuf) {
5643     return false;
5644   }
5645   args.rval().setObject(*xdrBuf);
5646   return true;
5647 }
5648 
DecodeModule(JSContext * cx,unsigned argc,Value * vp)5649 static bool DecodeModule(JSContext* cx, unsigned argc, Value* vp) {
5650   CallArgs args = CallArgsFromVp(argc, vp);
5651   if (!args.requireAtLeast(cx, "decodeModule", 1)) {
5652     return false;
5653   }
5654 
5655   if (!args[0].isObject() || !args[0].toObject().is<XDRBufferObject>()) {
5656     const char* typeName = InformalValueTypeName(args[0]);
5657     JS_ReportErrorASCII(cx, "expected XDRBufferObject to compile, got %s",
5658                         typeName);
5659     return false;
5660   }
5661 
5662   JS::CompileOptions options(cx);
5663   options.setModule();
5664 
5665   XDRDecoder xdrDecoder_(cx, &options,
5666                          *args[0].toObject().as<XDRBufferObject>().data());
5667   RootedModuleObject modObject(cx, nullptr);
5668   XDRResult res = xdrDecoder_.codeModuleObject(&modObject);
5669   if (res.isErr()) {
5670     return false;
5671   }
5672 
5673   if (!ModuleObject::Freeze(cx, modObject)) {
5674     return false;
5675   }
5676 
5677   Rooted<ShellModuleObjectWrapper*> wrapper(
5678       cx, ShellModuleObjectWrapper::create(cx, modObject));
5679   if (!wrapper) {
5680     return false;
5681   }
5682   args.rval().setObject(*wrapper);
5683   return true;
5684 }
5685 
RegisterModule(JSContext * cx,unsigned argc,Value * vp)5686 static bool RegisterModule(JSContext* cx, unsigned argc, Value* vp) {
5687   CallArgs args = CallArgsFromVp(argc, vp);
5688   if (!args.requireAtLeast(cx, "registerModule", 2)) {
5689     return false;
5690   }
5691 
5692   if (!args[0].isString()) {
5693     const char* typeName = InformalValueTypeName(args[0]);
5694     JS_ReportErrorASCII(cx, "expected string, got %s", typeName);
5695     return false;
5696   }
5697 
5698   if (!args[1].isObject() ||
5699       !args[1].toObject().is<ShellModuleObjectWrapper>()) {
5700     const char* typeName = InformalValueTypeName(args[1]);
5701     JS_ReportErrorASCII(cx, "expected module, got %s", typeName);
5702     return false;
5703   }
5704 
5705   ShellContext* sc = GetShellContext(cx);
5706   RootedModuleObject module(
5707       cx, args[1].toObject().as<ShellModuleObjectWrapper>().get());
5708 
5709   RootedAtom specifier(cx, AtomizeString(cx, args[0].toString()));
5710   if (!specifier) {
5711     return false;
5712   }
5713 
5714   RootedObject moduleRequest(cx, ModuleRequestObject::create(cx, specifier));
5715   if (!moduleRequest) {
5716     return false;
5717   }
5718 
5719   if (!sc->moduleLoader->registerTestModule(cx, moduleRequest, module)) {
5720     return false;
5721   }
5722 
5723   Rooted<ShellModuleObjectWrapper*> wrapper(
5724       cx, ShellModuleObjectWrapper::create(cx, module));
5725   if (!wrapper) {
5726     return false;
5727   }
5728   args.rval().setObject(*wrapper);
5729   return true;
5730 }
5731 
GetModuleEnvironment(JSContext * cx,HandleModuleObject module)5732 static ModuleEnvironmentObject* GetModuleEnvironment(
5733     JSContext* cx, HandleModuleObject module) {
5734   // Use the initial environment so that tests can check bindings exists
5735   // before they have been instantiated.
5736   RootedModuleEnvironmentObject env(cx, &module->initialEnvironment());
5737   MOZ_ASSERT(env);
5738   return env;
5739 }
5740 
GetModuleEnvironmentNames(JSContext * cx,unsigned argc,Value * vp)5741 static bool GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp) {
5742   CallArgs args = CallArgsFromVp(argc, vp);
5743   if (args.length() != 1) {
5744     JS_ReportErrorASCII(cx, "Wrong number of arguments");
5745     return false;
5746   }
5747 
5748   if (!args[0].isObject() ||
5749       !args[0].toObject().is<ShellModuleObjectWrapper>()) {
5750     JS_ReportErrorASCII(cx,
5751                         "First argument should be a ShellModuleObjectWrapper");
5752     return false;
5753   }
5754 
5755   RootedModuleObject module(
5756       cx, args[0].toObject().as<ShellModuleObjectWrapper>().get());
5757   if (module->hadEvaluationError()) {
5758     JS_ReportErrorASCII(cx, "Module environment unavailable");
5759     return false;
5760   }
5761 
5762   RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, module));
5763   Rooted<IdVector> ids(cx, IdVector(cx));
5764   if (!JS_Enumerate(cx, env, &ids)) {
5765     return false;
5766   }
5767 
5768   // The "*namespace*" binding is a detail of current implementation so hide
5769   // it to give stable results in tests.
5770   ids.eraseIfEqual(NameToId(cx->names().starNamespaceStar));
5771 
5772   uint32_t length = ids.length();
5773   RootedArrayObject array(cx, NewDenseFullyAllocatedArray(cx, length));
5774   if (!array) {
5775     return false;
5776   }
5777 
5778   array->setDenseInitializedLength(length);
5779   for (uint32_t i = 0; i < length; i++) {
5780     array->initDenseElement(i, StringValue(JSID_TO_STRING(ids[i])));
5781   }
5782 
5783   args.rval().setObject(*array);
5784   return true;
5785 }
5786 
GetModuleEnvironmentValue(JSContext * cx,unsigned argc,Value * vp)5787 static bool GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp) {
5788   CallArgs args = CallArgsFromVp(argc, vp);
5789   if (args.length() != 2) {
5790     JS_ReportErrorASCII(cx, "Wrong number of arguments");
5791     return false;
5792   }
5793 
5794   if (!args[0].isObject() ||
5795       !args[0].toObject().is<ShellModuleObjectWrapper>()) {
5796     JS_ReportErrorASCII(cx,
5797                         "First argument should be a ShellModuleObjectWrapper");
5798     return false;
5799   }
5800 
5801   if (!args[1].isString()) {
5802     JS_ReportErrorASCII(cx, "Second argument should be a string");
5803     return false;
5804   }
5805 
5806   RootedModuleObject module(
5807       cx, args[0].toObject().as<ShellModuleObjectWrapper>().get());
5808   if (module->hadEvaluationError()) {
5809     JS_ReportErrorASCII(cx, "Module environment unavailable");
5810     return false;
5811   }
5812 
5813   RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, module));
5814   RootedString name(cx, args[1].toString());
5815   RootedId id(cx);
5816   if (!JS_StringToId(cx, name, &id)) {
5817     return false;
5818   }
5819 
5820   if (!GetProperty(cx, env, env, id, args.rval())) {
5821     return false;
5822   }
5823 
5824   if (args.rval().isMagic(JS_UNINITIALIZED_LEXICAL)) {
5825     ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
5826     return false;
5827   }
5828 
5829   return true;
5830 }
5831 
5832 enum class DumpType {
5833   ParseNode,
5834   Stencil,
5835 };
5836 
5837 template <typename Unit>
DumpAST(JSContext * cx,const JS::ReadOnlyCompileOptions & options,const Unit * units,size_t length,js::frontend::CompilationState & compilationState,js::frontend::ParseGoal goal)5838 static bool DumpAST(JSContext* cx, const JS::ReadOnlyCompileOptions& options,
5839                     const Unit* units, size_t length,
5840                     js::frontend::CompilationState& compilationState,
5841                     js::frontend::ParseGoal goal) {
5842   using namespace js::frontend;
5843 
5844   Parser<FullParseHandler, Unit> parser(cx, options, units, length,
5845                                         /* foldConstants = */ false,
5846                                         compilationState,
5847                                         /* syntaxParser = */ nullptr);
5848   if (!parser.checkOptions()) {
5849     return false;
5850   }
5851 
5852   // Emplace the top-level stencil.
5853   MOZ_ASSERT(compilationState.scriptData.length() ==
5854              CompilationStencil::TopLevelIndex);
5855   if (!compilationState.appendScriptStencilAndData(cx)) {
5856     return false;
5857   }
5858 
5859   js::frontend::ParseNode* pn;
5860   if (goal == frontend::ParseGoal::Script) {
5861     pn = parser.parse();
5862   } else {
5863     if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global())) {
5864       return false;
5865     }
5866 
5867     ModuleBuilder builder(cx, &parser);
5868 
5869     SourceExtent extent = SourceExtent::makeGlobalExtent(length);
5870     ModuleSharedContext modulesc(cx, options, builder, extent);
5871     pn = parser.moduleBody(&modulesc);
5872   }
5873 
5874   if (!pn) {
5875     return false;
5876   }
5877 
5878 #if defined(DEBUG)
5879   js::Fprinter out(stderr);
5880   DumpParseTree(&parser, pn, out);
5881 #endif
5882 
5883   return true;
5884 }
5885 
5886 template <typename Unit>
DumpStencil(JSContext * cx,const JS::ReadOnlyCompileOptions & options,const Unit * units,size_t length,js::frontend::ParseGoal goal)5887 [[nodiscard]] static bool DumpStencil(JSContext* cx,
5888                                       const JS::ReadOnlyCompileOptions& options,
5889                                       const Unit* units, size_t length,
5890                                       js::frontend::ParseGoal goal) {
5891   Rooted<frontend::CompilationInput> input(cx,
5892                                            frontend::CompilationInput(options));
5893 
5894   JS::SourceText<Unit> srcBuf;
5895   if (!srcBuf.init(cx, units, length, JS::SourceOwnership::Borrowed)) {
5896     return false;
5897   }
5898 
5899   UniquePtr<frontend::ExtensibleCompilationStencil> stencil;
5900   if (goal == frontend::ParseGoal::Script) {
5901     stencil = frontend::CompileGlobalScriptToExtensibleStencil(
5902         cx, input.get(), srcBuf, ScopeKind::Global);
5903   } else {
5904     stencil = frontend::ParseModuleToExtensibleStencil(cx, input.get(), srcBuf);
5905   }
5906 
5907   if (!stencil) {
5908     return false;
5909   }
5910 
5911 #if defined(DEBUG) || defined(JS_JITSPEW)
5912   {
5913     frontend::BorrowingCompilationStencil borrowingStencil(*stencil);
5914     borrowingStencil.dump();
5915   }
5916 #endif
5917 
5918   return true;
5919 }
5920 
FrontendTest(JSContext * cx,unsigned argc,Value * vp,const char * funcName,DumpType dumpType)5921 static bool FrontendTest(JSContext* cx, unsigned argc, Value* vp,
5922                          const char* funcName, DumpType dumpType) {
5923   using namespace js::frontend;
5924 
5925   CallArgs args = CallArgsFromVp(argc, vp);
5926 
5927   if (!args.requireAtLeast(cx, funcName, 1)) {
5928     return false;
5929   }
5930   if (!args[0].isString()) {
5931     const char* typeName = InformalValueTypeName(args[0]);
5932     JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
5933     return false;
5934   }
5935 
5936   frontend::ParseGoal goal = frontend::ParseGoal::Script;
5937 #ifdef JS_ENABLE_SMOOSH
5938   bool smoosh = false;
5939 #endif
5940 
5941   CompileOptions options(cx);
5942   options.setIntroductionType("js shell parse")
5943       .setFileAndLine("<string>", 1)
5944       .setIsRunOnce(true)
5945       .setNoScriptRval(true);
5946 
5947   if (args.length() >= 2) {
5948     if (!args[1].isObject()) {
5949       const char* typeName = InformalValueTypeName(args[1]);
5950       JS_ReportErrorASCII(cx, "expected object (options) to parse, got %s",
5951                           typeName);
5952       return false;
5953     }
5954     RootedObject objOptions(cx, &args[1].toObject());
5955 
5956     RootedValue optionModule(cx);
5957     if (!JS_GetProperty(cx, objOptions, "module", &optionModule)) {
5958       return false;
5959     }
5960 
5961     if (optionModule.isBoolean()) {
5962       if (optionModule.toBoolean()) {
5963         goal = frontend::ParseGoal::Module;
5964       }
5965     } else if (!optionModule.isUndefined()) {
5966       const char* typeName = InformalValueTypeName(optionModule);
5967       JS_ReportErrorASCII(cx, "option `module` should be a boolean, got %s",
5968                           typeName);
5969       return false;
5970     }
5971     if (!js::ParseCompileOptions(cx, options, objOptions, nullptr)) {
5972       return false;
5973     }
5974 
5975 #ifdef JS_ENABLE_SMOOSH
5976     bool found = false;
5977     if (!JS_HasProperty(cx, objOptions, "rustFrontend", &found)) {
5978       return false;
5979     }
5980     if (found) {
5981       JS_ReportErrorASCII(cx, "'rustFrontend' option is renamed to 'smoosh'");
5982       return false;
5983     }
5984 
5985     RootedValue optionSmoosh(cx);
5986     if (!JS_GetProperty(cx, objOptions, "smoosh", &optionSmoosh)) {
5987       return false;
5988     }
5989 
5990     if (optionSmoosh.isBoolean()) {
5991       smoosh = optionSmoosh.toBoolean();
5992     } else if (!optionSmoosh.isUndefined()) {
5993       const char* typeName = InformalValueTypeName(optionSmoosh);
5994       JS_ReportErrorASCII(cx, "option `smoosh` should be a boolean, got %s",
5995                           typeName);
5996       return false;
5997     }
5998 #endif  // JS_ENABLE_SMOOSH
5999   }
6000 
6001   JSString* scriptContents = args[0].toString();
6002   RootedLinearString linearString(cx, scriptContents->ensureLinear(cx));
6003   if (!linearString) {
6004     return false;
6005   }
6006 
6007   bool isAscii = false;
6008   if (linearString->hasLatin1Chars()) {
6009     JS::AutoCheckCannotGC nogc;
6010     isAscii = JS::StringIsASCII(mozilla::Span(
6011         reinterpret_cast<const char*>(linearString->latin1Chars(nogc)),
6012         linearString->length()));
6013   }
6014 
6015   AutoStableStringChars stableChars(cx);
6016   if (isAscii) {
6017     if (!stableChars.init(cx, scriptContents)) {
6018       return false;
6019     }
6020     MOZ_ASSERT(stableChars.isLatin1());
6021   } else {
6022     if (!stableChars.initTwoByte(cx, scriptContents)) {
6023       return false;
6024     }
6025   }
6026 
6027   size_t length = scriptContents->length();
6028 #ifdef JS_ENABLE_SMOOSH
6029   if (dumpType == DumpType::ParseNode) {
6030     if (smoosh) {
6031       if (isAscii) {
6032         const Latin1Char* chars = stableChars.latin1Range().begin().get();
6033 
6034         if (goal == frontend::ParseGoal::Script) {
6035           if (!SmooshParseScript(cx, chars, length)) {
6036             return false;
6037           }
6038         } else {
6039           if (!SmooshParseModule(cx, chars, length)) {
6040             return false;
6041           }
6042         }
6043         args.rval().setUndefined();
6044         return true;
6045       }
6046       JS_ReportErrorASCII(cx,
6047                           "SmooshMonkey does not support non-ASCII chars yet");
6048       return false;
6049     }
6050   }
6051 #endif  // JS_ENABLE_SMOOSH
6052 
6053   if (goal == frontend::ParseGoal::Module) {
6054     // See frontend::CompileModule.
6055     options.setForceStrictMode();
6056     options.allowHTMLComments = false;
6057   }
6058 
6059   if (dumpType == DumpType::Stencil) {
6060 #ifdef JS_ENABLE_SMOOSH
6061     if (smoosh) {
6062       if (isAscii) {
6063         if (goal == frontend::ParseGoal::Script) {
6064           const Latin1Char* latin1 = stableChars.latin1Range().begin().get();
6065           auto utf8 = reinterpret_cast<const mozilla::Utf8Unit*>(latin1);
6066           JS::SourceText<Utf8Unit> srcBuf;
6067           if (!srcBuf.init(cx, utf8, length, JS::SourceOwnership::Borrowed)) {
6068             return false;
6069           }
6070 
6071           Rooted<frontend::CompilationInput> input(
6072               cx, frontend::CompilationInput(options));
6073           UniquePtr<frontend::ExtensibleCompilationStencil> stencil;
6074           if (!Smoosh::tryCompileGlobalScriptToExtensibleStencil(
6075                   cx, input.get(), srcBuf, stencil)) {
6076             return false;
6077           }
6078           if (!stencil) {
6079             JS_ReportErrorASCII(cx, "SmooshMonkey failed to parse");
6080             return false;
6081           }
6082 
6083 #  ifdef DEBUG
6084           {
6085             frontend::BorrowingCompilationStencil borrowingStencil(*stencil);
6086             borrowingStencil.dump();
6087           }
6088 #  endif
6089         } else {
6090           JS_ReportErrorASCII(cx,
6091                               "SmooshMonkey does not support module stencil");
6092           return false;
6093         }
6094         args.rval().setUndefined();
6095         return true;
6096       }
6097       JS_ReportErrorASCII(cx,
6098                           "SmooshMonkey does not support non-ASCII chars yet");
6099       return false;
6100     }
6101 #endif  // JS_ENABLE_SMOOSH
6102 
6103     if (isAscii) {
6104       const Latin1Char* latin1 = stableChars.latin1Range().begin().get();
6105       auto utf8 = reinterpret_cast<const mozilla::Utf8Unit*>(latin1);
6106       if (!DumpStencil<mozilla::Utf8Unit>(cx, options, utf8, length, goal)) {
6107         return false;
6108       }
6109     } else {
6110       MOZ_ASSERT(stableChars.isTwoByte());
6111       const char16_t* chars = stableChars.twoByteRange().begin().get();
6112       if (!DumpStencil<char16_t>(cx, options, chars, length, goal)) {
6113         return false;
6114       }
6115     }
6116 
6117     args.rval().setUndefined();
6118     return true;
6119   }
6120 
6121   Rooted<frontend::CompilationInput> input(cx,
6122                                            frontend::CompilationInput(options));
6123   if (goal == frontend::ParseGoal::Script) {
6124     if (!input.get().initForGlobal(cx)) {
6125       return false;
6126     }
6127   } else {
6128     if (!input.get().initForModule(cx)) {
6129       return false;
6130     }
6131   }
6132 
6133   LifoAllocScope allocScope(&cx->tempLifoAlloc());
6134   frontend::CompilationState compilationState(cx, allocScope, input.get());
6135   if (!compilationState.init(cx)) {
6136     return false;
6137   }
6138 
6139   if (isAscii) {
6140     const Latin1Char* latin1 = stableChars.latin1Range().begin().get();
6141     auto utf8 = reinterpret_cast<const mozilla::Utf8Unit*>(latin1);
6142     if (!DumpAST<mozilla::Utf8Unit>(cx, options, utf8, length, compilationState,
6143                                     goal)) {
6144       return false;
6145     }
6146   } else {
6147     MOZ_ASSERT(stableChars.isTwoByte());
6148     const char16_t* chars = stableChars.twoByteRange().begin().get();
6149     if (!DumpAST<char16_t>(cx, options, chars, length, compilationState,
6150                            goal)) {
6151       return false;
6152     }
6153   }
6154   args.rval().setUndefined();
6155   return true;
6156 }
6157 
DumpStencil(JSContext * cx,unsigned argc,Value * vp)6158 static bool DumpStencil(JSContext* cx, unsigned argc, Value* vp) {
6159   return FrontendTest(cx, argc, vp, "dumpStencil", DumpType::Stencil);
6160 }
6161 
Parse(JSContext * cx,unsigned argc,Value * vp)6162 static bool Parse(JSContext* cx, unsigned argc, Value* vp) {
6163   // Parse returns local scope information with variables ordered
6164   // differently, depending on the underlying JIT implementation.
6165   if (js::SupportDifferentialTesting()) {
6166     JS_ReportErrorASCII(cx,
6167                         "Function not available in differential testing mode.");
6168     return false;
6169   }
6170 
6171   return FrontendTest(cx, argc, vp, "parse", DumpType::ParseNode);
6172 }
6173 
SyntaxParse(JSContext * cx,unsigned argc,Value * vp)6174 static bool SyntaxParse(JSContext* cx, unsigned argc, Value* vp) {
6175   using namespace js::frontend;
6176 
6177   CallArgs args = CallArgsFromVp(argc, vp);
6178 
6179   if (!args.requireAtLeast(cx, "syntaxParse", 1)) {
6180     return false;
6181   }
6182   if (!args[0].isString()) {
6183     const char* typeName = InformalValueTypeName(args[0]);
6184     JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
6185     return false;
6186   }
6187 
6188   JSString* scriptContents = args[0].toString();
6189 
6190   CompileOptions options(cx);
6191   options.setIntroductionType("js shell syntaxParse")
6192       .setFileAndLine("<string>", 1);
6193 
6194   AutoStableStringChars stableChars(cx);
6195   if (!stableChars.initTwoByte(cx, scriptContents)) {
6196     return false;
6197   }
6198 
6199   const char16_t* chars = stableChars.twoByteRange().begin().get();
6200   size_t length = scriptContents->length();
6201 
6202   Rooted<frontend::CompilationInput> input(cx,
6203                                            frontend::CompilationInput(options));
6204   if (!input.get().initForGlobal(cx)) {
6205     return false;
6206   }
6207 
6208   LifoAllocScope allocScope(&cx->tempLifoAlloc());
6209   frontend::CompilationState compilationState(cx, allocScope, input.get());
6210   if (!compilationState.init(cx)) {
6211     return false;
6212   }
6213 
6214   Parser<frontend::SyntaxParseHandler, char16_t> parser(
6215       cx, options, chars, length,
6216       /* foldConstants = */ false, compilationState,
6217       /* syntaxParser = */ nullptr);
6218   if (!parser.checkOptions()) {
6219     return false;
6220   }
6221 
6222   bool succeeded = parser.parse();
6223   if (cx->isExceptionPending()) {
6224     return false;
6225   }
6226 
6227   if (!succeeded && !parser.hadAbortedSyntaxParse()) {
6228     // If no exception is posted, either there was an OOM or a language
6229     // feature unhandled by the syntax parser was encountered.
6230     MOZ_ASSERT(cx->runtime()->hadOutOfMemory);
6231     return false;
6232   }
6233 
6234   args.rval().setBoolean(succeeded);
6235   return true;
6236 }
6237 
OffThreadCompileScriptCallback(JS::OffThreadToken * token,void * callbackData)6238 static void OffThreadCompileScriptCallback(JS::OffThreadToken* token,
6239                                            void* callbackData) {
6240   auto job = static_cast<OffThreadJob*>(callbackData);
6241   job->markDone(token);
6242 }
6243 
OffThreadCompileScript(JSContext * cx,unsigned argc,Value * vp)6244 static bool OffThreadCompileScript(JSContext* cx, unsigned argc, Value* vp) {
6245   if (!CanUseExtraThreads()) {
6246     JS_ReportErrorASCII(cx,
6247                         "Can't use offThreadCompileScript with --no-threads");
6248     return false;
6249   }
6250 
6251   CallArgs args = CallArgsFromVp(argc, vp);
6252 
6253   if (!args.requireAtLeast(cx, "offThreadCompileScript", 1)) {
6254     return false;
6255   }
6256   if (!args[0].isString()) {
6257     const char* typeName = InformalValueTypeName(args[0]);
6258     JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
6259     return false;
6260   }
6261 
6262   UniqueChars fileNameBytes;
6263   CompileOptions options(cx);
6264   options.setIntroductionType("js shell offThreadCompileScript")
6265       .setFileAndLine("<string>", 1)
6266       .setdeferDebugMetadata();
6267 
6268   if (args.length() >= 2) {
6269     if (args[1].isPrimitive()) {
6270       JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
6271                                 JSSMSG_INVALID_ARGS, "evaluate");
6272       return false;
6273     }
6274 
6275     // Offthread compilation requires that the debug metadata be set when the
6276     // script is collected from offthread, rather than when compiled.
6277     RootedObject opts(cx, &args[1].toObject());
6278     if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
6279       return false;
6280     }
6281   }
6282 
6283   // These option settings must override whatever the caller requested.
6284   options.setIsRunOnce(true).setSourceIsLazy(false);
6285 
6286   // We assume the caller wants caching if at all possible, ignoring
6287   // heuristics that make sense for a real browser.
6288   options.forceAsync = true;
6289 
6290   JSString* scriptContents = args[0].toString();
6291   AutoStableStringChars stableChars(cx);
6292   if (!stableChars.initTwoByte(cx, scriptContents)) {
6293     return false;
6294   }
6295 
6296   size_t length = scriptContents->length();
6297   const char16_t* chars = stableChars.twoByteChars();
6298 
6299   // Make sure we own the string's chars, so that they are not freed before
6300   // the compilation is finished.
6301   UniqueTwoByteChars ownedChars;
6302   if (stableChars.maybeGiveOwnershipToCaller()) {
6303     ownedChars.reset(const_cast<char16_t*>(chars));
6304   } else {
6305     ownedChars.reset(cx->pod_malloc<char16_t>(length));
6306     if (!ownedChars) {
6307       return false;
6308     }
6309 
6310     mozilla::PodCopy(ownedChars.get(), chars, length);
6311   }
6312 
6313   if (!JS::CanCompileOffThread(cx, options, length)) {
6314     JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
6315     return false;
6316   }
6317 
6318   OffThreadJob* job =
6319       NewOffThreadJob(cx, ScriptKind::Script, options,
6320                       OffThreadJob::Source(std::move(ownedChars)));
6321   if (!job) {
6322     return false;
6323   }
6324 
6325   JS::SourceText<char16_t> srcBuf;
6326   if (!srcBuf.init(cx, job->sourceChars(), length,
6327                    JS::SourceOwnership::Borrowed) ||
6328       !JS::CompileOffThread(cx, options, srcBuf, OffThreadCompileScriptCallback,
6329                             job)) {
6330     job->cancel();
6331     DeleteOffThreadJob(cx, job);
6332     return false;
6333   }
6334 
6335   args.rval().setInt32(job->id);
6336   return true;
6337 }
6338 
runOffThreadScript(JSContext * cx,unsigned argc,Value * vp)6339 static bool runOffThreadScript(JSContext* cx, unsigned argc, Value* vp) {
6340   CallArgs args = CallArgsFromVp(argc, vp);
6341 
6342   OffThreadJob* job =
6343       LookupOffThreadJobForArgs(cx, ScriptKind::Script, args, 0);
6344   if (!job) {
6345     return false;
6346   }
6347 
6348   if (job->useOffThreadParseGlobal &&
6349       OffThreadParsingMustWaitForGC(cx->runtime())) {
6350     gc::FinishGC(cx);
6351   }
6352 
6353   JS::OffThreadToken* token = job->waitUntilDone(cx);
6354   MOZ_ASSERT(token);
6355 
6356   RootedScript script(cx, JS::FinishOffThreadScript(cx, token));
6357   DeleteOffThreadJob(cx, job);
6358   if (!script) {
6359     return false;
6360   }
6361 
6362   RootedValue privateValue(cx);
6363   RootedString elementAttributeName(cx);
6364 
6365   if (args.length() >= 2) {
6366     if (args[1].isPrimitive()) {
6367       JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
6368                                 JSSMSG_INVALID_ARGS, "compile");
6369       return false;
6370     }
6371 
6372     RootedObject opts(cx, &args[1].toObject());
6373     if (!ParseDebugMetadata(cx, opts, &privateValue, &elementAttributeName)) {
6374       return false;
6375     }
6376   }
6377 
6378   CompileOptions dummyOptions(cx);
6379   if (!JS::UpdateDebugMetadata(cx, script, dummyOptions, privateValue,
6380                                elementAttributeName, nullptr, nullptr)) {
6381     return false;
6382   }
6383 
6384   return JS_ExecuteScript(cx, script, args.rval());
6385 }
6386 
OffThreadCompileToStencil(JSContext * cx,unsigned argc,Value * vp)6387 static bool OffThreadCompileToStencil(JSContext* cx, unsigned argc, Value* vp) {
6388   if (!CanUseExtraThreads()) {
6389     JS_ReportErrorASCII(
6390         cx, "Can't use offThreadCompileToStencil with --no-threads");
6391     return false;
6392   }
6393 
6394   CallArgs args = CallArgsFromVp(argc, vp);
6395 
6396   if (!args.requireAtLeast(cx, "offThreadCompileToStencil", 1)) {
6397     return false;
6398   }
6399   if (!args[0].isString()) {
6400     const char* typeName = InformalValueTypeName(args[0]);
6401     JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
6402     return false;
6403   }
6404 
6405   UniqueChars fileNameBytes;
6406   CompileOptions options(cx);
6407   options.setIntroductionType("js shell offThreadCompileToStencil")
6408       .setFileAndLine("<string>", 1)
6409       .setdeferDebugMetadata();
6410 
6411   if (args.length() >= 2) {
6412     if (args[1].isPrimitive()) {
6413       JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
6414                                 JSSMSG_INVALID_ARGS, "evaluate");
6415       return false;
6416     }
6417 
6418     // Offthread compilation requires that the debug metadata be set when the
6419     // script is collected from offthread, rather than when compiled.
6420     RootedObject opts(cx, &args[1].toObject());
6421     if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
6422       return false;
6423     }
6424   }
6425 
6426   // This option setting must override whatever the caller requested.
6427   options.setIsRunOnce(true);
6428 
6429   // We assume the caller wants caching if at all possible, ignoring
6430   // heuristics that make sense for a real browser.
6431   options.forceAsync = true;
6432 
6433   JSString* scriptContents = args[0].toString();
6434   AutoStableStringChars stableChars(cx);
6435   if (!stableChars.initTwoByte(cx, scriptContents)) {
6436     return false;
6437   }
6438 
6439   size_t length = scriptContents->length();
6440   const char16_t* chars = stableChars.twoByteChars();
6441 
6442   // Make sure we own the string's chars, so that they are not freed before
6443   // the compilation is finished.
6444   UniqueTwoByteChars ownedChars;
6445   if (stableChars.maybeGiveOwnershipToCaller()) {
6446     ownedChars.reset(const_cast<char16_t*>(chars));
6447   } else {
6448     ownedChars.reset(cx->pod_malloc<char16_t>(length));
6449     if (!ownedChars) {
6450       return false;
6451     }
6452 
6453     mozilla::PodCopy(ownedChars.get(), chars, length);
6454   }
6455 
6456   if (!JS::CanCompileOffThread(cx, options, length)) {
6457     JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
6458     return false;
6459   }
6460 
6461   OffThreadJob* job =
6462       NewOffThreadJob(cx, ScriptKind::ScriptStencil, options,
6463                       OffThreadJob::Source(std::move(ownedChars)));
6464   if (!job) {
6465     return false;
6466   }
6467 
6468   JS::SourceText<char16_t> srcBuf;
6469   if (!srcBuf.init(cx, job->sourceChars(), length,
6470                    JS::SourceOwnership::Borrowed) ||
6471       !JS::CompileToStencilOffThread(cx, options, srcBuf,
6472                                      OffThreadCompileScriptCallback, job)) {
6473     job->cancel();
6474     DeleteOffThreadJob(cx, job);
6475     return false;
6476   }
6477 
6478   args.rval().setInt32(job->id);
6479   return true;
6480 }
6481 
FinishOffThreadCompileToStencil(JSContext * cx,unsigned argc,Value * vp)6482 static bool FinishOffThreadCompileToStencil(JSContext* cx, unsigned argc,
6483                                             Value* vp) {
6484   CallArgs args = CallArgsFromVp(argc, vp);
6485 
6486   OffThreadJob* job =
6487       LookupOffThreadJobForArgs(cx, ScriptKind::ScriptStencil, args, 0);
6488   if (!job) {
6489     return false;
6490   }
6491 
6492   MOZ_ASSERT(!job->useOffThreadParseGlobal);
6493 
6494   JS::OffThreadToken* token = job->waitUntilDone(cx);
6495   MOZ_ASSERT(token);
6496 
6497   RefPtr<JS::Stencil> stencil = JS::FinishOffThreadCompileToStencil(cx, token);
6498   DeleteOffThreadJob(cx, job);
6499   if (!stencil) {
6500     return false;
6501   }
6502   RootedObject stencilObj(cx,
6503                           js::StencilObject::create(cx, std::move(stencil)));
6504   if (!stencilObj) {
6505     return false;
6506   }
6507 
6508   args.rval().setObject(*stencilObj);
6509   return true;
6510 }
6511 
OffThreadCompileModule(JSContext * cx,unsigned argc,Value * vp)6512 static bool OffThreadCompileModule(JSContext* cx, unsigned argc, Value* vp) {
6513   CallArgs args = CallArgsFromVp(argc, vp);
6514 
6515   if (args.length() != 1 || !args[0].isString()) {
6516     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
6517                               JSSMSG_INVALID_ARGS, "offThreadCompileModule");
6518     return false;
6519   }
6520 
6521   UniqueChars fileNameBytes;
6522   CompileOptions options(cx);
6523   options.setIntroductionType("js shell offThreadCompileModule")
6524       .setFileAndLine("<string>", 1);
6525   options.setIsRunOnce(true).setSourceIsLazy(false);
6526   options.forceAsync = true;
6527 
6528   JSString* scriptContents = args[0].toString();
6529   AutoStableStringChars stableChars(cx);
6530   if (!stableChars.initTwoByte(cx, scriptContents)) {
6531     return false;
6532   }
6533 
6534   size_t length = scriptContents->length();
6535   const char16_t* chars = stableChars.twoByteChars();
6536 
6537   // Make sure we own the string's chars, so that they are not freed before
6538   // the compilation is finished.
6539   UniqueTwoByteChars ownedChars;
6540   if (stableChars.maybeGiveOwnershipToCaller()) {
6541     ownedChars.reset(const_cast<char16_t*>(chars));
6542   } else {
6543     ownedChars.reset(cx->pod_malloc<char16_t>(length));
6544     if (!ownedChars) {
6545       return false;
6546     }
6547 
6548     mozilla::PodCopy(ownedChars.get(), chars, length);
6549   }
6550 
6551   if (!JS::CanCompileOffThread(cx, options, length)) {
6552     JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
6553     return false;
6554   }
6555 
6556   OffThreadJob* job =
6557       NewOffThreadJob(cx, ScriptKind::Module, options,
6558                       OffThreadJob::Source(std::move(ownedChars)));
6559   if (!job) {
6560     return false;
6561   }
6562 
6563   JS::SourceText<char16_t> srcBuf;
6564   if (!srcBuf.init(cx, job->sourceChars(), length,
6565                    JS::SourceOwnership::Borrowed) ||
6566       !JS::CompileOffThreadModule(cx, options, srcBuf,
6567                                   OffThreadCompileScriptCallback, job)) {
6568     job->cancel();
6569     DeleteOffThreadJob(cx, job);
6570     return false;
6571   }
6572 
6573   args.rval().setInt32(job->id);
6574   return true;
6575 }
6576 
FinishOffThreadModule(JSContext * cx,unsigned argc,Value * vp)6577 static bool FinishOffThreadModule(JSContext* cx, unsigned argc, Value* vp) {
6578   CallArgs args = CallArgsFromVp(argc, vp);
6579 
6580   OffThreadJob* job =
6581       LookupOffThreadJobForArgs(cx, ScriptKind::Module, args, 0);
6582   if (!job) {
6583     return false;
6584   }
6585 
6586   if (job->useOffThreadParseGlobal &&
6587       OffThreadParsingMustWaitForGC(cx->runtime())) {
6588     gc::FinishGC(cx);
6589   }
6590 
6591   JS::OffThreadToken* token = job->waitUntilDone(cx);
6592   MOZ_ASSERT(token);
6593 
6594   RootedObject module(cx, JS::FinishOffThreadModule(cx, token));
6595   DeleteOffThreadJob(cx, job);
6596   if (!module) {
6597     return false;
6598   }
6599 
6600   Rooted<ShellModuleObjectWrapper*> wrapper(
6601       cx, ShellModuleObjectWrapper::create(cx, module.as<ModuleObject>()));
6602   if (!wrapper) {
6603     return false;
6604   }
6605   args.rval().setObject(*wrapper);
6606   return true;
6607 }
6608 
OffThreadDecodeScript(JSContext * cx,unsigned argc,Value * vp)6609 static bool OffThreadDecodeScript(JSContext* cx, unsigned argc, Value* vp) {
6610   if (!CanUseExtraThreads()) {
6611     JS_ReportErrorASCII(cx,
6612                         "Can't use offThreadDecodeScript with --no-threads");
6613     return false;
6614   }
6615 
6616   CallArgs args = CallArgsFromVp(argc, vp);
6617 
6618   if (!args.requireAtLeast(cx, "offThreadDecodeScript", 1)) {
6619     return false;
6620   }
6621   if (!args[0].isObject() || !CacheEntry_isCacheEntry(&args[0].toObject())) {
6622     const char* typeName = InformalValueTypeName(args[0]);
6623     JS_ReportErrorASCII(cx, "expected cache entry, got %s", typeName);
6624     return false;
6625   }
6626   RootedObject cacheEntry(cx, &args[0].toObject());
6627 
6628   UniqueChars fileNameBytes;
6629   CompileOptions options(cx);
6630   options.setIntroductionType("js shell offThreadDecodeScript")
6631       .setFileAndLine("<string>", 1);
6632   // NOTE: If --off-thread-parse-global is not used, input can be either script
6633   // for saveBytecode, or stencil for saveIncrementalBytecode.
6634   options.useOffThreadParseGlobal =
6635       CacheEntry_getKind(cx, cacheEntry) == BytecodeCacheKind::Script;
6636   options.useStencilXDR =
6637       CacheEntry_getKind(cx, cacheEntry) == BytecodeCacheKind::Stencil;
6638 
6639   // In browser, we always use off-thread parse global for decode task, for
6640   // performance reason.
6641   // (see ScriptLoader::AttemptAsyncScriptCompile)
6642   //
6643   // This code should be removed, and the above code should be used, once
6644   // bug 1687973 gets fixed.
6645   options.useOffThreadParseGlobal = true;
6646 
6647   if (args.length() >= 2) {
6648     if (args[1].isPrimitive()) {
6649       JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
6650                                 JSSMSG_INVALID_ARGS, "evaluate");
6651       return false;
6652     }
6653 
6654     RootedObject opts(cx, &args[1].toObject());
6655     if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
6656       return false;
6657     }
6658   }
6659 
6660   // This option setting must override whatever the caller requested, and
6661   // this should match `Evaluate` that encodes the script.
6662   options.setIsRunOnce(false);
6663 
6664   // We assume the caller wants caching if at all possible, ignoring
6665   // heuristics that make sense for a real browser.
6666   options.forceAsync = true;
6667 
6668   JS::TranscodeBuffer loadBuffer;
6669   size_t loadLength = 0;
6670   uint8_t* loadData = nullptr;
6671   loadData = CacheEntry_getBytecode(cx, cacheEntry, &loadLength);
6672   if (!loadData) {
6673     return false;
6674   }
6675   if (!loadBuffer.append(loadData, loadLength)) {
6676     JS_ReportOutOfMemory(cx);
6677     return false;
6678   }
6679 
6680   if (!JS::CanDecodeOffThread(cx, options, loadLength)) {
6681     JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
6682     return false;
6683   }
6684 
6685   OffThreadJob* job =
6686       NewOffThreadJob(cx, ScriptKind::DecodeScript, options,
6687                       OffThreadJob::Source(std::move(loadBuffer)));
6688   if (!job) {
6689     return false;
6690   }
6691 
6692   if (!JS::DecodeOffThreadScript(cx, options, job->xdrBuffer(), 0,
6693                                  OffThreadCompileScriptCallback, job)) {
6694     job->cancel();
6695     DeleteOffThreadJob(cx, job);
6696     return false;
6697   }
6698 
6699   args.rval().setInt32(job->id);
6700   return true;
6701 }
6702 
runOffThreadDecodedScript(JSContext * cx,unsigned argc,Value * vp)6703 static bool runOffThreadDecodedScript(JSContext* cx, unsigned argc, Value* vp) {
6704   CallArgs args = CallArgsFromVp(argc, vp);
6705 
6706   OffThreadJob* job =
6707       LookupOffThreadJobForArgs(cx, ScriptKind::DecodeScript, args, 0);
6708   if (!job) {
6709     return false;
6710   }
6711 
6712   if (job->useOffThreadParseGlobal &&
6713       OffThreadParsingMustWaitForGC(cx->runtime())) {
6714     gc::FinishGC(cx);
6715   }
6716 
6717   JS::OffThreadToken* token = job->waitUntilDone(cx);
6718   MOZ_ASSERT(token);
6719 
6720   RootedScript script(cx, JS::FinishOffThreadScriptDecoder(cx, token));
6721   DeleteOffThreadJob(cx, job);
6722   if (!script) {
6723     return false;
6724   }
6725 
6726   return JS_ExecuteScript(cx, script, args.rval());
6727 }
6728 
6729 class AutoCStringVector {
6730   Vector<char*> argv_;
6731 
6732  public:
AutoCStringVector(JSContext * cx)6733   explicit AutoCStringVector(JSContext* cx) : argv_(cx) {}
~AutoCStringVector()6734   ~AutoCStringVector() {
6735     for (size_t i = 0; i < argv_.length(); i++) {
6736       js_free(argv_[i]);
6737     }
6738   }
append(UniqueChars && arg)6739   bool append(UniqueChars&& arg) {
6740     if (!argv_.append(arg.get())) {
6741       return false;
6742     }
6743 
6744     // Now owned by this vector.
6745     (void)arg.release();
6746     return true;
6747   }
get() const6748   char* const* get() const { return argv_.begin(); }
length() const6749   size_t length() const { return argv_.length(); }
operator [](size_t i) const6750   char* operator[](size_t i) const { return argv_[i]; }
replace(size_t i,UniqueChars arg)6751   void replace(size_t i, UniqueChars arg) {
6752     js_free(argv_[i]);
6753     argv_[i] = arg.release();
6754   }
6755 };
6756 
6757 #if defined(XP_WIN)
EscapeForShell(JSContext * cx,AutoCStringVector & argv)6758 static bool EscapeForShell(JSContext* cx, AutoCStringVector& argv) {
6759   // Windows will break arguments in argv by various spaces, so we wrap each
6760   // argument in quotes and escape quotes within. Even with quotes, \ will be
6761   // treated like an escape character, so inflate each \ to \\.
6762 
6763   for (size_t i = 0; i < argv.length(); i++) {
6764     if (!argv[i]) {
6765       continue;
6766     }
6767 
6768     size_t newLen = 3;  // quotes before and after and null-terminator
6769     for (char* p = argv[i]; *p; p++) {
6770       newLen++;
6771       if (*p == '\"' || *p == '\\') {
6772         newLen++;
6773       }
6774     }
6775 
6776     auto escaped = cx->make_pod_array<char>(newLen);
6777     if (!escaped) {
6778       return false;
6779     }
6780 
6781     char* src = argv[i];
6782     char* dst = escaped.get();
6783     *dst++ = '\"';
6784     while (*src) {
6785       if (*src == '\"' || *src == '\\') {
6786         *dst++ = '\\';
6787       }
6788       *dst++ = *src++;
6789     }
6790     *dst++ = '\"';
6791     *dst++ = '\0';
6792     MOZ_ASSERT(escaped.get() + newLen == dst);
6793 
6794     argv.replace(i, std::move(escaped));
6795   }
6796   return true;
6797 }
6798 #endif
6799 
6800 #ifndef __wasi__
ReadAll(int fd,wasm::Bytes * bytes)6801 static bool ReadAll(int fd, wasm::Bytes* bytes) {
6802   size_t lastLength = bytes->length();
6803   while (true) {
6804     static const int ChunkSize = 64 * 1024;
6805     if (!bytes->growBy(ChunkSize)) {
6806       return false;
6807     }
6808 
6809     intptr_t readCount;
6810     while (true) {
6811       readCount = read(fd, bytes->begin() + lastLength, ChunkSize);
6812       if (readCount >= 0) {
6813         break;
6814       }
6815       if (errno != EINTR) {
6816         return false;
6817       }
6818     }
6819 
6820     if (readCount < ChunkSize) {
6821       bytes->shrinkTo(lastLength + readCount);
6822       if (readCount == 0) {
6823         return true;
6824       }
6825     }
6826 
6827     lastLength = bytes->length();
6828   }
6829 }
6830 
WriteAll(int fd,const uint8_t * bytes,size_t length)6831 static bool WriteAll(int fd, const uint8_t* bytes, size_t length) {
6832   while (length > 0) {
6833     int written = write(fd, bytes, length);
6834     if (written < 0) {
6835       if (errno == EINTR) {
6836         continue;
6837       }
6838       return false;
6839     }
6840     MOZ_ASSERT(unsigned(written) <= length);
6841     length -= written;
6842     bytes += written;
6843   }
6844 
6845   return true;
6846 }
6847 
6848 class AutoPipe {
6849   int fds_[2];
6850 
6851  public:
AutoPipe()6852   AutoPipe() {
6853     fds_[0] = -1;
6854     fds_[1] = -1;
6855   }
6856 
~AutoPipe()6857   ~AutoPipe() {
6858     if (fds_[0] != -1) {
6859       close(fds_[0]);
6860     }
6861     if (fds_[1] != -1) {
6862       close(fds_[1]);
6863     }
6864   }
6865 
init()6866   bool init() {
6867 #  ifdef XP_WIN
6868     return !_pipe(fds_, 4096, O_BINARY);
6869 #  else
6870     return !pipe(fds_);
6871 #  endif
6872   }
6873 
reader() const6874   int reader() const {
6875     MOZ_ASSERT(fds_[0] != -1);
6876     return fds_[0];
6877   }
6878 
writer() const6879   int writer() const {
6880     MOZ_ASSERT(fds_[1] != -1);
6881     return fds_[1];
6882   }
6883 
closeReader()6884   void closeReader() {
6885     MOZ_ASSERT(fds_[0] != -1);
6886     close(fds_[0]);
6887     fds_[0] = -1;
6888   }
6889 
closeWriter()6890   void closeWriter() {
6891     MOZ_ASSERT(fds_[1] != -1);
6892     close(fds_[1]);
6893     fds_[1] = -1;
6894   }
6895 };
6896 #endif  // __wasi__
6897 
6898 int shell::sArgc;
6899 char** shell::sArgv;
6900 
6901 #ifndef __wasi__
6902 static const char sWasmCompileAndSerializeFlag[] =
6903     "--wasm-compile-and-serialize";
6904 static Vector<const char*, 5, js::SystemAllocPolicy> sCompilerProcessFlags;
6905 
CompileAndSerializeInSeparateProcess(JSContext * cx,const uint8_t * bytecode,size_t bytecodeLength,wasm::Bytes * serialized)6906 static bool CompileAndSerializeInSeparateProcess(JSContext* cx,
6907                                                  const uint8_t* bytecode,
6908                                                  size_t bytecodeLength,
6909                                                  wasm::Bytes* serialized) {
6910   AutoPipe stdIn, stdOut;
6911   if (!stdIn.init() || !stdOut.init()) {
6912     return false;
6913   }
6914 
6915   AutoCStringVector argv(cx);
6916 
6917   UniqueChars argv0 = DuplicateString(cx, sArgv[0]);
6918   if (!argv0 || !argv.append(std::move(argv0))) {
6919     return false;
6920   }
6921 
6922   // Put compiler flags first since they must precede the non-option
6923   // file-descriptor args (passed on Windows, below).
6924   for (unsigned i = 0; i < sCompilerProcessFlags.length(); i++) {
6925     UniqueChars flags = DuplicateString(cx, sCompilerProcessFlags[i]);
6926     if (!flags || !argv.append(std::move(flags))) {
6927       return false;
6928     }
6929   }
6930 
6931   UniqueChars arg;
6932 
6933   arg = DuplicateString(sWasmCompileAndSerializeFlag);
6934   if (!arg || !argv.append(std::move(arg))) {
6935     return false;
6936   }
6937 
6938 #  ifdef XP_WIN
6939   // The spawned process will have all the stdIn/stdOut file handles open, but
6940   // without the power of fork, we need some other way to communicate the
6941   // integer fd values so we encode them in argv and WasmCompileAndSerialize()
6942   // has a matching #ifdef XP_WIN to parse them out. Communicate both ends of
6943   // both pipes so the child process can closed the unused ends.
6944 
6945   arg = JS_smprintf("%d", stdIn.reader());
6946   if (!arg || !argv.append(std::move(arg))) {
6947     return false;
6948   }
6949 
6950   arg = JS_smprintf("%d", stdIn.writer());
6951   if (!arg || !argv.append(std::move(arg))) {
6952     return false;
6953   }
6954 
6955   arg = JS_smprintf("%d", stdOut.reader());
6956   if (!arg || !argv.append(std::move(arg))) {
6957     return false;
6958   }
6959 
6960   arg = JS_smprintf("%d", stdOut.writer());
6961   if (!arg || !argv.append(std::move(arg))) {
6962     return false;
6963   }
6964 #  endif
6965 
6966   // Required by both _spawnv and exec.
6967   if (!argv.append(nullptr)) {
6968     return false;
6969   }
6970 
6971 #  ifdef XP_WIN
6972   if (!EscapeForShell(cx, argv)) {
6973     return false;
6974   }
6975 
6976   int childPid = _spawnv(P_NOWAIT, sArgv[0], argv.get());
6977   if (childPid == -1) {
6978     return false;
6979   }
6980 #  else
6981   pid_t childPid = fork();
6982   switch (childPid) {
6983     case -1:
6984       return false;
6985     case 0:
6986       // In the child process. Redirect stdin/stdout to the respective ends of
6987       // the pipes. Closing stdIn.writer() is necessary for stdin to hit EOF.
6988       // This case statement must not return before exec() takes over. Rather,
6989       // exit(-1) is used to return failure to the parent process.
6990       if (dup2(stdIn.reader(), STDIN_FILENO) == -1) {
6991         exit(-1);
6992       }
6993       if (dup2(stdOut.writer(), STDOUT_FILENO) == -1) {
6994         exit(-1);
6995       }
6996       close(stdIn.reader());
6997       close(stdIn.writer());
6998       close(stdOut.reader());
6999       close(stdOut.writer());
7000       execv(sArgv[0], argv.get());
7001       exit(-1);
7002   }
7003 #  endif
7004 
7005   // In the parent process. Closing stdOut.writer() is necessary for
7006   // stdOut.reader() below to hit EOF.
7007   stdIn.closeReader();
7008   stdOut.closeWriter();
7009 
7010   if (!WriteAll(stdIn.writer(), bytecode, bytecodeLength)) {
7011     return false;
7012   }
7013 
7014   stdIn.closeWriter();
7015 
7016   if (!ReadAll(stdOut.reader(), serialized)) {
7017     return false;
7018   }
7019 
7020   stdOut.closeReader();
7021 
7022   int status;
7023 #  ifdef XP_WIN
7024   if (_cwait(&status, childPid, WAIT_CHILD) == -1) {
7025     return false;
7026   }
7027 #  else
7028   while (true) {
7029     if (waitpid(childPid, &status, 0) >= 0) {
7030       break;
7031     }
7032     if (errno != EINTR) {
7033       return false;
7034     }
7035   }
7036 #  endif
7037 
7038   return status == 0;
7039 }
7040 
WasmCompileAndSerialize(JSContext * cx)7041 static bool WasmCompileAndSerialize(JSContext* cx) {
7042   MOZ_ASSERT(wasm::CodeCachingAvailable(cx));
7043 
7044 #  ifdef XP_WIN
7045   // See CompileAndSerializeInSeparateProcess for why we've had to smuggle
7046   // these fd values through argv. Closing the writing ends is necessary for
7047   // the reading ends to hit EOF.
7048   int flagIndex = 0;
7049   for (; flagIndex < sArgc; flagIndex++) {
7050     if (!strcmp(sArgv[flagIndex], sWasmCompileAndSerializeFlag)) {
7051       break;
7052     }
7053   }
7054   MOZ_RELEASE_ASSERT(flagIndex < sArgc);
7055 
7056   int fdsIndex = flagIndex + 1;
7057   MOZ_RELEASE_ASSERT(fdsIndex + 4 == sArgc);
7058 
7059   int stdInReader = atoi(sArgv[fdsIndex + 0]);
7060   int stdInWriter = atoi(sArgv[fdsIndex + 1]);
7061   int stdOutReader = atoi(sArgv[fdsIndex + 2]);
7062   int stdOutWriter = atoi(sArgv[fdsIndex + 3]);
7063 
7064   int stdIn = stdInReader;
7065   close(stdInWriter);
7066   close(stdOutReader);
7067   int stdOut = stdOutWriter;
7068 #  else
7069   int stdIn = STDIN_FILENO;
7070   int stdOut = STDOUT_FILENO;
7071 #  endif
7072 
7073   wasm::MutableBytes bytecode = js_new<wasm::ShareableBytes>();
7074   if (!ReadAll(stdIn, &bytecode->bytes)) {
7075     return false;
7076   }
7077 
7078   wasm::Bytes serialized;
7079   if (!wasm::CompileAndSerialize(*bytecode, &serialized)) {
7080     return false;
7081   }
7082 
7083   if (!WriteAll(stdOut, serialized.begin(), serialized.length())) {
7084     return false;
7085   }
7086 
7087   return true;
7088 }
7089 
WasmCompileInSeparateProcess(JSContext * cx,unsigned argc,Value * vp)7090 static bool WasmCompileInSeparateProcess(JSContext* cx, unsigned argc,
7091                                          Value* vp) {
7092   if (!wasm::CodeCachingAvailable(cx)) {
7093     JS_ReportErrorASCII(cx, "WebAssembly caching not supported");
7094     return false;
7095   }
7096 
7097   CallArgs args = CallArgsFromVp(argc, vp);
7098   if (!args.requireAtLeast(cx, "wasmCompileInSeparateProcess", 1)) {
7099     return false;
7100   }
7101 
7102   SharedMem<uint8_t*> bytecode;
7103   size_t numBytes;
7104   if (!args[0].isObject() ||
7105       !IsBufferSource(&args[0].toObject(), &bytecode, &numBytes)) {
7106     RootedObject callee(cx, &args.callee());
7107     ReportUsageErrorASCII(cx, callee, "Argument must be a buffer source");
7108     return false;
7109   }
7110 
7111   wasm::Bytes serialized;
7112   if (!CompileAndSerializeInSeparateProcess(cx, bytecode.unwrap(), numBytes,
7113                                             &serialized)) {
7114     if (!cx->isExceptionPending()) {
7115       JS_ReportErrorASCII(cx, "creating and executing child process");
7116     }
7117     return false;
7118   }
7119 
7120   RootedObject module(cx);
7121   if (!wasm::DeserializeModule(cx, serialized, &module)) {
7122     return false;
7123   }
7124 
7125   args.rval().setObject(*module);
7126   return true;
7127 }
7128 #endif  // __wasi__
7129 
DecompileFunction(JSContext * cx,unsigned argc,Value * vp)7130 static bool DecompileFunction(JSContext* cx, unsigned argc, Value* vp) {
7131   CallArgs args = CallArgsFromVp(argc, vp);
7132   if (args.length() < 1 || !args[0].isObject() ||
7133       !args[0].toObject().is<JSFunction>()) {
7134     args.rval().setUndefined();
7135     return true;
7136   }
7137   RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
7138   JSString* result = JS_DecompileFunction(cx, fun);
7139   if (!result) {
7140     return false;
7141   }
7142   args.rval().setString(result);
7143   return true;
7144 }
7145 
DecompileThisScript(JSContext * cx,unsigned argc,Value * vp)7146 static bool DecompileThisScript(JSContext* cx, unsigned argc, Value* vp) {
7147   CallArgs args = CallArgsFromVp(argc, vp);
7148 
7149   NonBuiltinScriptFrameIter iter(cx);
7150   if (iter.done()) {
7151     args.rval().setString(cx->runtime()->emptyString);
7152     return true;
7153   }
7154 
7155   {
7156     JSAutoRealm ar(cx, iter.script());
7157 
7158     RootedScript script(cx, iter.script());
7159     JSString* result = JS_DecompileScript(cx, script);
7160     if (!result) {
7161       return false;
7162     }
7163 
7164     args.rval().setString(result);
7165   }
7166 
7167   return JS_WrapValue(cx, args.rval());
7168 }
7169 
ValueToSource(JSContext * cx,unsigned argc,Value * vp)7170 static bool ValueToSource(JSContext* cx, unsigned argc, Value* vp) {
7171   CallArgs args = CallArgsFromVp(argc, vp);
7172 
7173   JSString* str = ValueToSource(cx, args.get(0));
7174   if (!str) {
7175     return false;
7176   }
7177 
7178   args.rval().setString(str);
7179   return true;
7180 }
7181 
ThisFilename(JSContext * cx,unsigned argc,Value * vp)7182 static bool ThisFilename(JSContext* cx, unsigned argc, Value* vp) {
7183   CallArgs args = CallArgsFromVp(argc, vp);
7184 
7185   JS::AutoFilename filename;
7186   if (!DescribeScriptedCaller(cx, &filename) || !filename.get()) {
7187     args.rval().setString(cx->runtime()->emptyString);
7188     return true;
7189   }
7190 
7191   JSString* str = JS_NewStringCopyZ(cx, filename.get());
7192   if (!str) {
7193     return false;
7194   }
7195 
7196   args.rval().setString(str);
7197   return true;
7198 }
7199 
WrapWithProto(JSContext * cx,unsigned argc,Value * vp)7200 static bool WrapWithProto(JSContext* cx, unsigned argc, Value* vp) {
7201   CallArgs args = CallArgsFromVp(argc, vp);
7202   Value obj = args.get(0);
7203   Value proto = args.get(1);
7204   if (!obj.isObject() || !proto.isObjectOrNull()) {
7205     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
7206                               JSSMSG_INVALID_ARGS, "wrapWithProto");
7207     return false;
7208   }
7209 
7210   // Disallow constructing (deeply) nested wrapper chains, to avoid running
7211   // out of stack space in isCallable/isConstructor. See bug 1126105.
7212   if (IsWrapper(&obj.toObject())) {
7213     JS_ReportErrorASCII(cx, "wrapWithProto cannot wrap a wrapper");
7214     return false;
7215   }
7216 
7217   WrapperOptions options(cx);
7218   options.setProto(proto.toObjectOrNull());
7219   JSObject* wrapped = Wrapper::New(cx, &obj.toObject(),
7220                                    &Wrapper::singletonWithPrototype, options);
7221   if (!wrapped) {
7222     return false;
7223   }
7224 
7225   args.rval().setObject(*wrapped);
7226   return true;
7227 }
7228 
NewGlobal(JSContext * cx,unsigned argc,Value * vp)7229 static bool NewGlobal(JSContext* cx, unsigned argc, Value* vp) {
7230   JS::RealmOptions options;
7231   JS::RealmCreationOptions& creationOptions = options.creationOptions();
7232   JS::RealmBehaviors& behaviors = options.behaviors();
7233   ShellGlobalKind kind = ShellGlobalKind::WindowProxy;
7234   bool immutablePrototype = true;
7235 
7236   SetStandardRealmOptions(options);
7237 
7238   // Default to creating the global in the current compartment unless
7239   // --more-compartments is used.
7240   if (defaultToSameCompartment) {
7241     creationOptions.setExistingCompartment(cx->global());
7242   } else {
7243     creationOptions.setNewCompartmentAndZone();
7244   }
7245 
7246   JS::AutoHoldPrincipals principals(cx);
7247 
7248   CallArgs args = CallArgsFromVp(argc, vp);
7249   if (args.length() == 1 && args[0].isObject()) {
7250     RootedObject opts(cx, &args[0].toObject());
7251     RootedValue v(cx);
7252 
7253     if (!JS_GetProperty(cx, opts, "invisibleToDebugger", &v)) {
7254       return false;
7255     }
7256     if (v.isBoolean()) {
7257       creationOptions.setInvisibleToDebugger(v.toBoolean());
7258     }
7259 
7260     if (!JS_GetProperty(cx, opts, "sameZoneAs", &v)) {
7261       return false;
7262     }
7263     if (v.isObject()) {
7264       creationOptions.setNewCompartmentInExistingZone(
7265           UncheckedUnwrap(&v.toObject()));
7266     }
7267 
7268     if (!JS_GetProperty(cx, opts, "sameCompartmentAs", &v)) {
7269       return false;
7270     }
7271     if (v.isObject()) {
7272       creationOptions.setExistingCompartment(UncheckedUnwrap(&v.toObject()));
7273     }
7274 
7275     if (!JS_GetProperty(cx, opts, "newCompartment", &v)) {
7276       return false;
7277     }
7278     if (v.isBoolean() && v.toBoolean()) {
7279       creationOptions.setNewCompartmentAndZone();
7280     }
7281 
7282     if (!JS_GetProperty(cx, opts, "discardSource", &v)) {
7283       return false;
7284     }
7285     if (v.isBoolean()) {
7286       behaviors.setDiscardSource(v.toBoolean());
7287     }
7288 
7289     if (!JS_GetProperty(cx, opts, "useWindowProxy", &v)) {
7290       return false;
7291     }
7292     if (v.isBoolean()) {
7293       kind = v.toBoolean() ? ShellGlobalKind::WindowProxy
7294                            : ShellGlobalKind::GlobalObject;
7295     }
7296 
7297     if (!JS_GetProperty(cx, opts, "immutablePrototype", &v)) {
7298       return false;
7299     }
7300     if (v.isBoolean()) {
7301       immutablePrototype = v.toBoolean();
7302     }
7303 
7304     if (!JS_GetProperty(cx, opts, "enableWritableStreams", &v)) {
7305       return false;
7306     }
7307     if (v.isBoolean()) {
7308       creationOptions.setWritableStreamsEnabled(v.toBoolean());
7309     }
7310 
7311     if (!JS_GetProperty(cx, opts, "enableReadableStreamPipeTo", &v)) {
7312       return false;
7313     }
7314     if (v.isBoolean()) {
7315       creationOptions.setReadableStreamPipeToEnabled(v.toBoolean());
7316     }
7317 
7318     if (!JS_GetProperty(cx, opts, "systemPrincipal", &v)) {
7319       return false;
7320     }
7321     if (v.isBoolean()) {
7322       principals.reset(&ShellPrincipals::fullyTrusted);
7323     }
7324 
7325     if (!JS_GetProperty(cx, opts, "principal", &v)) {
7326       return false;
7327     }
7328     if (!v.isUndefined()) {
7329       uint32_t bits;
7330       if (!ToUint32(cx, v, &bits)) {
7331         return false;
7332       }
7333       JSPrincipals* newPrincipals = cx->new_<ShellPrincipals>(bits);
7334       if (!newPrincipals) {
7335         return false;
7336       }
7337       principals.reset(newPrincipals);
7338     }
7339 
7340     if (!JS_GetProperty(cx, opts, "enableCoopAndCoep", &v)) {
7341       return false;
7342     }
7343     if (v.isBoolean()) {
7344       creationOptions.setCoopAndCoepEnabled(v.toBoolean());
7345     }
7346 
7347     // On the web, the SharedArrayBuffer constructor is not installed as a
7348     // global property in pages that aren't isolated in a separate process (and
7349     // thus can't allow the structured cloning of shared memory).  Specify false
7350     // for this option to reproduce this behavior.
7351     if (!JS_GetProperty(cx, opts, "defineSharedArrayBufferConstructor", &v)) {
7352       return false;
7353     }
7354     if (v.isBoolean()) {
7355       creationOptions.setDefineSharedArrayBufferConstructor(v.toBoolean());
7356     }
7357   }
7358 
7359   if (!CheckRealmOptions(cx, options, principals.get())) {
7360     return false;
7361   }
7362 
7363   RootedObject global(cx, NewGlobalObject(cx, options, principals.get(), kind,
7364                                           immutablePrototype));
7365   if (!global) {
7366     return false;
7367   }
7368 
7369   RootedObject wrapped(cx, ToWindowProxyIfWindow(global));
7370   if (!JS_WrapObject(cx, &wrapped)) {
7371     return false;
7372   }
7373 
7374   args.rval().setObject(*wrapped);
7375   return true;
7376 }
7377 
NukeCCW(JSContext * cx,unsigned argc,Value * vp)7378 static bool NukeCCW(JSContext* cx, unsigned argc, Value* vp) {
7379   CallArgs args = CallArgsFromVp(argc, vp);
7380 
7381   if (args.length() != 1 || !args[0].isObject() ||
7382       !IsCrossCompartmentWrapper(&args[0].toObject())) {
7383     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
7384                               JSSMSG_INVALID_ARGS, "nukeCCW");
7385     return false;
7386   }
7387 
7388   NukeCrossCompartmentWrapper(cx, &args[0].toObject());
7389   args.rval().setUndefined();
7390   return true;
7391 }
7392 
NukeAllCCWs(JSContext * cx,unsigned argc,Value * vp)7393 static bool NukeAllCCWs(JSContext* cx, unsigned argc, Value* vp) {
7394   CallArgs args = CallArgsFromVp(argc, vp);
7395 
7396   if (args.length() != 0) {
7397     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
7398                               JSSMSG_INVALID_ARGS, "nukeAllCCWs");
7399     return false;
7400   }
7401 
7402   NukeCrossCompartmentWrappers(cx, AllCompartments(), cx->realm(),
7403                                NukeWindowReferences, NukeAllReferences);
7404   args.rval().setUndefined();
7405   return true;
7406 }
7407 
RecomputeWrappers(JSContext * cx,unsigned argc,Value * vp)7408 static bool RecomputeWrappers(JSContext* cx, unsigned argc, Value* vp) {
7409   CallArgs args = CallArgsFromVp(argc, vp);
7410 
7411   if (args.length() > 2) {
7412     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
7413                               JSSMSG_INVALID_ARGS, "recomputeWrappers");
7414     return false;
7415   }
7416 
7417   JS::Compartment* sourceComp = nullptr;
7418   if (args.get(0).isObject()) {
7419     sourceComp = JS::GetCompartment(UncheckedUnwrap(&args[0].toObject()));
7420   }
7421 
7422   JS::Compartment* targetComp = nullptr;
7423   if (args.get(1).isObject()) {
7424     targetComp = JS::GetCompartment(UncheckedUnwrap(&args[1].toObject()));
7425   }
7426 
7427   struct SingleOrAllCompartments final : public CompartmentFilter {
7428     JS::Compartment* comp;
7429     explicit SingleOrAllCompartments(JS::Compartment* c) : comp(c) {}
7430     virtual bool match(JS::Compartment* c) const override {
7431       return !comp || comp == c;
7432     }
7433   };
7434 
7435   if (!js::RecomputeWrappers(cx, SingleOrAllCompartments(sourceComp),
7436                              SingleOrAllCompartments(targetComp))) {
7437     return false;
7438   }
7439 
7440   args.rval().setUndefined();
7441   return true;
7442 }
7443 
DumpObjectWrappers(JSContext * cx,unsigned argc,Value * vp)7444 static bool DumpObjectWrappers(JSContext* cx, unsigned argc, Value* vp) {
7445   CallArgs args = CallArgsFromVp(argc, vp);
7446 
7447   bool printedHeader = false;
7448   for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
7449     bool printedZoneInfo = false;
7450     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
7451       bool printedCompartmentInfo = false;
7452       for (Compartment::ObjectWrapperEnum e(comp); !e.empty(); e.popFront()) {
7453         JSObject* wrapper = e.front().value().unbarrieredGet();
7454         JSObject* wrapped = e.front().key();
7455         if (!printedHeader) {
7456           fprintf(stderr, "Cross-compartment object wrappers:\n");
7457           printedHeader = true;
7458         }
7459         if (!printedZoneInfo) {
7460           fprintf(stderr, "  Zone %p:\n", zone.get());
7461           printedZoneInfo = true;
7462         }
7463         if (!printedCompartmentInfo) {
7464           fprintf(stderr, "    Compartment %p:\n", comp.get());
7465           printedCompartmentInfo = true;
7466         }
7467         fprintf(stderr,
7468                 "      Object wrapper %p -> %p in zone %p compartment %p\n",
7469                 wrapper, wrapped, wrapped->zone(), wrapped->compartment());
7470       }
7471     }
7472   }
7473 
7474   if (!printedHeader) {
7475     fprintf(stderr, "No cross-compartment object wrappers.\n");
7476   }
7477 
7478   args.rval().setUndefined();
7479   return true;
7480 }
7481 
GetMaxArgs(JSContext * cx,unsigned argc,Value * vp)7482 static bool GetMaxArgs(JSContext* cx, unsigned argc, Value* vp) {
7483   CallArgs args = CallArgsFromVp(argc, vp);
7484   args.rval().setInt32(ARGS_LENGTH_MAX);
7485   return true;
7486 }
7487 
IsHTMLDDA_Call(JSContext * cx,unsigned argc,Value * vp)7488 static bool IsHTMLDDA_Call(JSContext* cx, unsigned argc, Value* vp) {
7489   CallArgs args = CallArgsFromVp(argc, vp);
7490 
7491   // These are the required conditions under which this object may be called
7492   // by test262 tests, and the required behavior under those conditions.
7493   if (args.length() == 0 ||
7494       (args[0].isString() && args[0].toString()->length() == 0)) {
7495     args.rval().setNull();
7496     return true;
7497   }
7498 
7499   JS_ReportErrorASCII(
7500       cx, "IsHTMLDDA object is being called in an impermissible manner");
7501   return false;
7502 }
7503 
CreateIsHTMLDDA(JSContext * cx,unsigned argc,Value * vp)7504 static bool CreateIsHTMLDDA(JSContext* cx, unsigned argc, Value* vp) {
7505   CallArgs args = CallArgsFromVp(argc, vp);
7506 
7507   static const JSClassOps classOps = {
7508       nullptr,         // addProperty
7509       nullptr,         // delProperty
7510       nullptr,         // enumerate
7511       nullptr,         // newEnumerate
7512       nullptr,         // resolve
7513       nullptr,         // mayResolve
7514       nullptr,         // finalize
7515       IsHTMLDDA_Call,  // call
7516       nullptr,         // hasInstance
7517       nullptr,         // construct
7518       nullptr,         // trace
7519   };
7520 
7521   static const JSClass cls = {
7522       "IsHTMLDDA",
7523       JSCLASS_EMULATES_UNDEFINED,
7524       &classOps,
7525   };
7526 
7527   JSObject* obj = JS_NewObject(cx, &cls);
7528   if (!obj) {
7529     return false;
7530   }
7531   args.rval().setObject(*obj);
7532   return true;
7533 }
7534 
GetSelfHostedValue(JSContext * cx,unsigned argc,Value * vp)7535 static bool GetSelfHostedValue(JSContext* cx, unsigned argc, Value* vp) {
7536   CallArgs args = CallArgsFromVp(argc, vp);
7537 
7538   if (args.length() != 1 || !args[0].isString()) {
7539     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
7540                               JSSMSG_INVALID_ARGS, "getSelfHostedValue");
7541     return false;
7542   }
7543   RootedAtom srcAtom(cx, ToAtom<CanGC>(cx, args[0]));
7544   if (!srcAtom) {
7545     return false;
7546   }
7547   RootedPropertyName srcName(cx, srcAtom->asPropertyName());
7548   return GlobalObject::getIntrinsicValue(cx, cx->global(), srcName,
7549                                          args.rval());
7550 }
7551 
7552 class ShellSourceHook : public SourceHook {
7553   // The function we should call to lazily retrieve source code.
7554   PersistentRootedFunction fun;
7555 
7556  public:
ShellSourceHook(JSContext * cx,JSFunction & fun)7557   ShellSourceHook(JSContext* cx, JSFunction& fun) : fun(cx, &fun) {}
7558 
load(JSContext * cx,const char * filename,char16_t ** twoByteSource,char ** utf8Source,size_t * length)7559   bool load(JSContext* cx, const char* filename, char16_t** twoByteSource,
7560             char** utf8Source, size_t* length) override {
7561     MOZ_ASSERT((twoByteSource != nullptr) != (utf8Source != nullptr),
7562                "must be called requesting only one of UTF-8 or UTF-16 source");
7563 
7564     RootedString str(cx, JS_NewStringCopyZ(cx, filename));
7565     if (!str) {
7566       return false;
7567     }
7568     RootedValue filenameValue(cx, StringValue(str));
7569 
7570     RootedValue result(cx);
7571     if (!Call(cx, UndefinedHandleValue, fun, HandleValueArray(filenameValue),
7572               &result)) {
7573       return false;
7574     }
7575 
7576     str = JS::ToString(cx, result);
7577     if (!str) {
7578       return false;
7579     }
7580 
7581     Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
7582     if (!linear) {
7583       return false;
7584     }
7585 
7586     if (twoByteSource) {
7587       *length = JS_GetStringLength(linear);
7588 
7589       *twoByteSource = cx->pod_malloc<char16_t>(*length);
7590       if (!*twoByteSource) {
7591         return false;
7592       }
7593 
7594       CopyChars(*twoByteSource, *linear);
7595     } else {
7596       MOZ_ASSERT(utf8Source != nullptr);
7597 
7598       *length = JS::GetDeflatedUTF8StringLength(linear);
7599 
7600       *utf8Source = cx->pod_malloc<char>(*length);
7601       if (!*utf8Source) {
7602         return false;
7603       }
7604 
7605       mozilla::DebugOnly<size_t> dstLen = JS::DeflateStringToUTF8Buffer(
7606           linear, mozilla::Span(*utf8Source, *length));
7607       MOZ_ASSERT(dstLen == *length);
7608     }
7609 
7610     return true;
7611   }
7612 };
7613 
WithSourceHook(JSContext * cx,unsigned argc,Value * vp)7614 static bool WithSourceHook(JSContext* cx, unsigned argc, Value* vp) {
7615   CallArgs args = CallArgsFromVp(argc, vp);
7616   RootedObject callee(cx, &args.callee());
7617 
7618   if (args.length() != 2) {
7619     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments.");
7620     return false;
7621   }
7622 
7623   if (!args[0].isObject() || !args[0].toObject().is<JSFunction>() ||
7624       !args[1].isObject() || !args[1].toObject().is<JSFunction>()) {
7625     ReportUsageErrorASCII(cx, callee,
7626                           "First and second arguments must be functions.");
7627     return false;
7628   }
7629 
7630   mozilla::UniquePtr<ShellSourceHook> hook =
7631       mozilla::MakeUnique<ShellSourceHook>(cx,
7632                                            args[0].toObject().as<JSFunction>());
7633   if (!hook) {
7634     return false;
7635   }
7636 
7637   mozilla::UniquePtr<SourceHook> savedHook = js::ForgetSourceHook(cx);
7638   js::SetSourceHook(cx, std::move(hook));
7639 
7640   RootedObject fun(cx, &args[1].toObject());
7641   bool result = Call(cx, UndefinedHandleValue, fun,
7642                      JS::HandleValueArray::empty(), args.rval());
7643   js::SetSourceHook(cx, std::move(savedHook));
7644   return result;
7645 }
7646 
PrintProfilerEvents_Callback(const char * msg,const char * details)7647 static void PrintProfilerEvents_Callback(const char* msg, const char* details) {
7648   fprintf(stderr, "PROFILER EVENT: %s %s\n", msg, details);
7649 }
7650 
PrintProfilerEvents(JSContext * cx,unsigned argc,Value * vp)7651 static bool PrintProfilerEvents(JSContext* cx, unsigned argc, Value* vp) {
7652   CallArgs args = CallArgsFromVp(argc, vp);
7653   if (cx->runtime()->geckoProfiler().enabled()) {
7654     js::RegisterContextProfilingEventMarker(cx, &PrintProfilerEvents_Callback);
7655   }
7656   args.rval().setUndefined();
7657   return true;
7658 }
7659 
7660 #ifdef SINGLESTEP_PROFILING
SingleStepCallback(void * arg,jit::Simulator * sim,void * pc)7661 static void SingleStepCallback(void* arg, jit::Simulator* sim, void* pc) {
7662   JSContext* cx = reinterpret_cast<JSContext*>(arg);
7663 
7664   // If profiling is not enabled, don't do anything.
7665   if (!cx->runtime()->geckoProfiler().enabled()) {
7666     return;
7667   }
7668 
7669   JS::ProfilingFrameIterator::RegisterState state;
7670   state.pc = pc;
7671 #  if defined(JS_SIMULATOR_ARM)
7672   state.sp = (void*)sim->get_register(jit::Simulator::sp);
7673   state.lr = (void*)sim->get_register(jit::Simulator::lr);
7674   state.fp = (void*)sim->get_register(jit::Simulator::fp);
7675 #  elif defined(JS_SIMULATOR_MIPS64) || defined(JS_SIMULATOR_MIPS32)
7676   state.sp = (void*)sim->getRegister(jit::Simulator::sp);
7677   state.lr = (void*)sim->getRegister(jit::Simulator::ra);
7678   state.fp = (void*)sim->getRegister(jit::Simulator::fp);
7679 #  else
7680 #    error "NYI: Single-step profiling support"
7681 #  endif
7682 
7683   mozilla::DebugOnly<void*> lastStackAddress = nullptr;
7684   StackChars stack;
7685   uint32_t frameNo = 0;
7686   AutoEnterOOMUnsafeRegion oomUnsafe;
7687   for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) {
7688     MOZ_ASSERT(i.stackAddress() != nullptr);
7689     MOZ_ASSERT(lastStackAddress <= i.stackAddress());
7690     lastStackAddress = i.stackAddress();
7691     JS::ProfilingFrameIterator::Frame frames[16];
7692     uint32_t nframes = i.extractStack(frames, 0, 16);
7693     for (uint32_t i = 0; i < nframes; i++) {
7694       if (frameNo > 0) {
7695         if (!stack.append(",", 1)) {
7696           oomUnsafe.crash("stack.append");
7697         }
7698       }
7699       if (!stack.append(frames[i].label, strlen(frames[i].label))) {
7700         oomUnsafe.crash("stack.append");
7701       }
7702       frameNo++;
7703     }
7704   }
7705 
7706   ShellContext* sc = GetShellContext(cx);
7707 
7708   // Only append the stack if it differs from the last stack.
7709   if (sc->stacks.empty() || sc->stacks.back().length() != stack.length() ||
7710       !ArrayEqual(sc->stacks.back().begin(), stack.begin(), stack.length())) {
7711     if (!sc->stacks.append(std::move(stack))) {
7712       oomUnsafe.crash("stacks.append");
7713     }
7714   }
7715 }
7716 #endif
7717 
EnableSingleStepProfiling(JSContext * cx,unsigned argc,Value * vp)7718 static bool EnableSingleStepProfiling(JSContext* cx, unsigned argc, Value* vp) {
7719 #ifdef SINGLESTEP_PROFILING
7720   CallArgs args = CallArgsFromVp(argc, vp);
7721 
7722   jit::Simulator* sim = cx->simulator();
7723   sim->enable_single_stepping(SingleStepCallback, cx);
7724 
7725   args.rval().setUndefined();
7726   return true;
7727 #else
7728   JS_ReportErrorASCII(cx, "single-step profiling not enabled on this platform");
7729   return false;
7730 #endif
7731 }
7732 
DisableSingleStepProfiling(JSContext * cx,unsigned argc,Value * vp)7733 static bool DisableSingleStepProfiling(JSContext* cx, unsigned argc,
7734                                        Value* vp) {
7735 #ifdef SINGLESTEP_PROFILING
7736   CallArgs args = CallArgsFromVp(argc, vp);
7737 
7738   jit::Simulator* sim = cx->simulator();
7739   sim->disable_single_stepping();
7740 
7741   ShellContext* sc = GetShellContext(cx);
7742 
7743   RootedValueVector elems(cx);
7744   for (size_t i = 0; i < sc->stacks.length(); i++) {
7745     JSString* stack =
7746         JS_NewUCStringCopyN(cx, sc->stacks[i].begin(), sc->stacks[i].length());
7747     if (!stack) {
7748       return false;
7749     }
7750     if (!elems.append(StringValue(stack))) {
7751       return false;
7752     }
7753   }
7754 
7755   JSObject* array = JS::NewArrayObject(cx, elems);
7756   if (!array) {
7757     return false;
7758   }
7759 
7760   sc->stacks.clear();
7761   args.rval().setObject(*array);
7762   return true;
7763 #else
7764   JS_ReportErrorASCII(cx, "single-step profiling not enabled on this platform");
7765   return false;
7766 #endif
7767 }
7768 
IsLatin1(JSContext * cx,unsigned argc,Value * vp)7769 static bool IsLatin1(JSContext* cx, unsigned argc, Value* vp) {
7770   CallArgs args = CallArgsFromVp(argc, vp);
7771   bool isLatin1 =
7772       args.get(0).isString() && args[0].toString()->hasLatin1Chars();
7773   args.rval().setBoolean(isLatin1);
7774   return true;
7775 }
7776 
EnableGeckoProfiling(JSContext * cx,unsigned argc,Value * vp)7777 static bool EnableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp) {
7778   CallArgs args = CallArgsFromVp(argc, vp);
7779 
7780   if (!EnsureGeckoProfilingStackInstalled(cx, GetShellContext(cx))) {
7781     return false;
7782   }
7783 
7784   cx->runtime()->geckoProfiler().enableSlowAssertions(false);
7785   cx->runtime()->geckoProfiler().enable(true);
7786 
7787   args.rval().setUndefined();
7788   return true;
7789 }
7790 
EnableGeckoProfilingWithSlowAssertions(JSContext * cx,unsigned argc,Value * vp)7791 static bool EnableGeckoProfilingWithSlowAssertions(JSContext* cx, unsigned argc,
7792                                                    Value* vp) {
7793   CallArgs args = CallArgsFromVp(argc, vp);
7794   args.rval().setUndefined();
7795 
7796   if (cx->runtime()->geckoProfiler().enabled()) {
7797     // If profiling already enabled with slow assertions disabled,
7798     // this is a no-op.
7799     if (cx->runtime()->geckoProfiler().slowAssertionsEnabled()) {
7800       return true;
7801     }
7802 
7803     // Slow assertions are off.  Disable profiling before re-enabling
7804     // with slow assertions on.
7805     cx->runtime()->geckoProfiler().enable(false);
7806   }
7807 
7808   if (!EnsureGeckoProfilingStackInstalled(cx, GetShellContext(cx))) {
7809     return false;
7810   }
7811 
7812   cx->runtime()->geckoProfiler().enableSlowAssertions(true);
7813   cx->runtime()->geckoProfiler().enable(true);
7814 
7815   return true;
7816 }
7817 
DisableGeckoProfiling(JSContext * cx,unsigned argc,Value * vp)7818 static bool DisableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp) {
7819   CallArgs args = CallArgsFromVp(argc, vp);
7820   args.rval().setUndefined();
7821 
7822   if (!cx->runtime()->geckoProfiler().enabled()) {
7823     return true;
7824   }
7825 
7826   cx->runtime()->geckoProfiler().enable(false);
7827   return true;
7828 }
7829 
7830 // Global mailbox that is used to communicate a shareable object value from one
7831 // worker to another.
7832 //
7833 // These object types are shareable:
7834 //
7835 //   - SharedArrayBuffer
7836 //   - WasmMemoryObject (when constructed with shared:true)
7837 //   - WasmModuleObject
7838 //
7839 // For the SharedArrayBuffer and WasmMemoryObject we transmit the underlying
7840 // SharedArrayRawBuffer ("SARB"). For the WasmModuleObject we transmit the
7841 // underlying JS::WasmModule.  The transmitted types are refcounted.  When they
7842 // are in the mailbox their reference counts are at least 1, accounting for the
7843 // reference from the mailbox.
7844 //
7845 // The lock guards the mailbox variable and prevents a race where two workers
7846 // try to set the mailbox at the same time to replace an object that is only
7847 // referenced from the mailbox: the workers will both decrement the reference
7848 // count on the old object, and one of those decrements will be on a garbage
7849 // object.  We could implement this with atomics and a CAS loop but it's not
7850 // worth the bother.
7851 //
7852 // Note that if a thread reads the mailbox repeatedly it will get distinct
7853 // objects on each read.  The alternatives are to cache created objects locally,
7854 // but this retains storage we don't need to retain, or to somehow clear the
7855 // mailbox locally, but this creates a coordination headache.  Buyer beware.
7856 
7857 enum class MailboxTag {
7858   Empty,
7859   SharedArrayBuffer,
7860   WasmMemory,
7861   WasmModule,
7862   Number,
7863 };
7864 
7865 struct SharedObjectMailbox {
7866   union Value {
7867     struct {
7868       SharedArrayRawBuffer* buffer;
7869       size_t length;
7870     } sarb;
7871     JS::WasmModule* module;
7872     double number;
7873 
Value()7874     Value() : number(0.0) {}
7875   };
7876 
7877   MailboxTag tag = MailboxTag::Empty;
7878   Value val;
7879 };
7880 
7881 typedef ExclusiveData<SharedObjectMailbox> SOMailbox;
7882 
7883 // Never null after successful initialization.
7884 static SOMailbox* sharedObjectMailbox;
7885 
InitSharedObjectMailbox()7886 static bool InitSharedObjectMailbox() {
7887   sharedObjectMailbox = js_new<SOMailbox>(mutexid::ShellObjectMailbox);
7888   return sharedObjectMailbox != nullptr;
7889 }
7890 
DestructSharedObjectMailbox()7891 static void DestructSharedObjectMailbox() {
7892   // All workers need to have terminated at this point.
7893 
7894   {
7895     auto mbx = sharedObjectMailbox->lock();
7896     switch (mbx->tag) {
7897       case MailboxTag::Empty:
7898       case MailboxTag::Number:
7899         break;
7900       case MailboxTag::SharedArrayBuffer:
7901       case MailboxTag::WasmMemory:
7902         mbx->val.sarb.buffer->dropReference();
7903         break;
7904       case MailboxTag::WasmModule:
7905         mbx->val.module->Release();
7906         break;
7907       default:
7908         MOZ_CRASH();
7909     }
7910   }
7911 
7912   js_delete(sharedObjectMailbox);
7913   sharedObjectMailbox = nullptr;
7914 }
7915 
GetSharedObject(JSContext * cx,unsigned argc,Value * vp)7916 static bool GetSharedObject(JSContext* cx, unsigned argc, Value* vp) {
7917   CallArgs args = CallArgsFromVp(argc, vp);
7918   RootedObject newObj(cx);
7919 
7920   {
7921     auto mbx = sharedObjectMailbox->lock();
7922     switch (mbx->tag) {
7923       case MailboxTag::Empty: {
7924         break;
7925       }
7926       case MailboxTag::Number: {
7927         args.rval().setNumber(mbx->val.number);
7928         return true;
7929       }
7930       case MailboxTag::SharedArrayBuffer:
7931       case MailboxTag::WasmMemory: {
7932         // Flag was set in the sender; ensure it is set in the receiver.
7933         MOZ_ASSERT(
7934             cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
7935 
7936         // The protocol for creating a SAB requires the refcount to be
7937         // incremented prior to the SAB creation.
7938 
7939         SharedArrayRawBuffer* buf = mbx->val.sarb.buffer;
7940         size_t length = mbx->val.sarb.length;
7941         if (!buf->addReference()) {
7942           JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
7943                                     JSMSG_SC_SAB_REFCNT_OFLO);
7944           return false;
7945         }
7946 
7947         // If the allocation fails we must decrement the refcount before
7948         // returning.
7949 
7950         Rooted<ArrayBufferObjectMaybeShared*> maybesab(
7951             cx, SharedArrayBufferObject::New(cx, buf, length));
7952         if (!maybesab) {
7953           buf->dropReference();
7954           return false;
7955         }
7956 
7957         // At this point the SAB was created successfully and it owns the
7958         // refcount-increase on the buffer that we performed above.  So even
7959         // if we fail to allocate along any path below we must not decrement
7960         // the refcount; the garbage collector must be allowed to handle
7961         // that via finalization of the orphaned SAB object.
7962 
7963         if (mbx->tag == MailboxTag::SharedArrayBuffer) {
7964           newObj = maybesab;
7965         } else {
7966           if (!GlobalObject::ensureConstructor(cx, cx->global(),
7967                                                JSProto_WebAssembly)) {
7968             return false;
7969           }
7970           RootedObject proto(
7971               cx, &cx->global()->getPrototype(JSProto_WasmMemory).toObject());
7972           newObj = WasmMemoryObject::create(cx, maybesab, proto);
7973           MOZ_ASSERT_IF(newObj, newObj->as<WasmMemoryObject>().isShared());
7974           if (!newObj) {
7975             return false;
7976           }
7977         }
7978 
7979         break;
7980       }
7981       case MailboxTag::WasmModule: {
7982         // Flag was set in the sender; ensure it is set in the receiver.
7983         MOZ_ASSERT(
7984             cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
7985 
7986         if (!GlobalObject::ensureConstructor(cx, cx->global(),
7987                                              JSProto_WebAssembly)) {
7988           return false;
7989         }
7990 
7991         // WasmModuleObject::create() increments the refcount on the module
7992         // and signals an error and returns null if that fails.
7993         newObj = mbx->val.module->createObject(cx);
7994         if (!newObj) {
7995           return false;
7996         }
7997         break;
7998       }
7999       default: {
8000         MOZ_CRASH();
8001       }
8002     }
8003   }
8004 
8005   args.rval().setObjectOrNull(newObj);
8006   return true;
8007 }
8008 
SetSharedObject(JSContext * cx,unsigned argc,Value * vp)8009 static bool SetSharedObject(JSContext* cx, unsigned argc, Value* vp) {
8010   CallArgs args = CallArgsFromVp(argc, vp);
8011 
8012   MailboxTag tag = MailboxTag::Empty;
8013   SharedObjectMailbox::Value value;
8014 
8015   // Increase refcounts when we obtain the value to avoid operating on dead
8016   // storage during self-assignment.
8017 
8018   if (args.get(0).isObject()) {
8019     RootedObject obj(cx, &args[0].toObject());
8020     if (obj->is<SharedArrayBufferObject>()) {
8021       Rooted<SharedArrayBufferObject*> sab(cx,
8022                                            &obj->as<SharedArrayBufferObject>());
8023       tag = MailboxTag::SharedArrayBuffer;
8024       value.sarb.buffer = sab->rawBufferObject();
8025       value.sarb.length = sab->byteLength();
8026       if (!value.sarb.buffer->addReference()) {
8027         JS_ReportErrorASCII(cx,
8028                             "Reference count overflow on SharedArrayBuffer");
8029         return false;
8030       }
8031     } else if (obj->is<WasmMemoryObject>()) {
8032       // Here we must transmit sab.byteLength() as the length; the SARB has its
8033       // own notion of the length which may be greater, and that's fine.
8034       if (obj->as<WasmMemoryObject>().isShared()) {
8035         Rooted<SharedArrayBufferObject*> sab(
8036             cx, &obj->as<WasmMemoryObject>()
8037                      .buffer()
8038                      .as<SharedArrayBufferObject>());
8039         tag = MailboxTag::WasmMemory;
8040         value.sarb.buffer = sab->rawBufferObject();
8041         value.sarb.length = sab->byteLength();
8042         if (!value.sarb.buffer->addReference()) {
8043           JS_ReportErrorASCII(cx,
8044                               "Reference count overflow on SharedArrayBuffer");
8045           return false;
8046         }
8047       } else {
8048         JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject");
8049         return false;
8050       }
8051     } else if (JS::IsWasmModuleObject(obj)) {
8052       tag = MailboxTag::WasmModule;
8053       value.module = JS::GetWasmModule(obj).forget().take();
8054     } else {
8055       JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject");
8056       return false;
8057     }
8058   } else if (args.get(0).isNumber()) {
8059     tag = MailboxTag::Number;
8060     value.number = args.get(0).toNumber();
8061     // Nothing
8062   } else if (args.get(0).isNullOrUndefined()) {
8063     // Nothing
8064   } else {
8065     JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject");
8066     return false;
8067   }
8068 
8069   {
8070     auto mbx = sharedObjectMailbox->lock();
8071 
8072     switch (mbx->tag) {
8073       case MailboxTag::Empty:
8074       case MailboxTag::Number:
8075         break;
8076       case MailboxTag::SharedArrayBuffer:
8077       case MailboxTag::WasmMemory:
8078         mbx->val.sarb.buffer->dropReference();
8079         break;
8080       case MailboxTag::WasmModule:
8081         mbx->val.module->Release();
8082         break;
8083       default:
8084         MOZ_CRASH();
8085     }
8086 
8087     mbx->tag = tag;
8088     mbx->val = value;
8089   }
8090 
8091   args.rval().setUndefined();
8092   return true;
8093 }
8094 
8095 typedef Vector<uint8_t, 0, SystemAllocPolicy> Uint8Vector;
8096 
8097 class StreamCacheEntry : public AtomicRefCounted<StreamCacheEntry>,
8098                          public JS::OptimizedEncodingListener {
8099   typedef AtomicRefCounted<StreamCacheEntry> AtomicBase;
8100 
8101   Uint8Vector bytes_;
8102   ExclusiveData<Uint8Vector> optimized_;
8103 
8104  public:
StreamCacheEntry(Uint8Vector && original)8105   explicit StreamCacheEntry(Uint8Vector&& original)
8106       : bytes_(std::move(original)),
8107         optimized_(mutexid::ShellStreamCacheEntryState) {}
8108 
8109   // Implement JS::OptimizedEncodingListener:
8110 
AddRef()8111   MozExternalRefCountType MOZ_XPCOM_ABI AddRef() override {
8112     AtomicBase::AddRef();
8113     return 1;  // unused
8114   }
Release()8115   MozExternalRefCountType MOZ_XPCOM_ABI Release() override {
8116     AtomicBase::Release();
8117     return 0;  // unused
8118   }
8119 
bytes() const8120   const Uint8Vector& bytes() const { return bytes_; }
8121 
storeOptimizedEncoding(JS::UniqueOptimizedEncodingBytes src)8122   void storeOptimizedEncoding(JS::UniqueOptimizedEncodingBytes src) override {
8123     MOZ_ASSERT(src->length() > 0);
8124 
8125     // Tolerate races since a single StreamCacheEntry object can be used as
8126     // the source of multiple streaming compilations.
8127     auto dstBytes = optimized_.lock();
8128     if (dstBytes->length() > 0) {
8129       return;
8130     }
8131 
8132     if (!dstBytes->resize(src->length())) {
8133       return;
8134     }
8135     memcpy(dstBytes->begin(), src->begin(), src->length());
8136   }
8137 
hasOptimizedEncoding() const8138   bool hasOptimizedEncoding() const { return !optimized_.lock()->empty(); }
optimizedEncoding() const8139   const Uint8Vector& optimizedEncoding() const {
8140     return optimized_.lock().get();
8141   }
8142 };
8143 
8144 typedef RefPtr<StreamCacheEntry> StreamCacheEntryPtr;
8145 
8146 class StreamCacheEntryObject : public NativeObject {
8147   static const unsigned CACHE_ENTRY_SLOT = 0;
8148   static const JSClassOps classOps_;
8149   static const JSPropertySpec properties_;
8150 
finalize(JSFreeOp *,JSObject * obj)8151   static void finalize(JSFreeOp*, JSObject* obj) {
8152     obj->as<StreamCacheEntryObject>().cache().Release();
8153   }
8154 
cachedGetter(JSContext * cx,unsigned argc,Value * vp)8155   static bool cachedGetter(JSContext* cx, unsigned argc, Value* vp) {
8156     CallArgs args = CallArgsFromVp(argc, vp);
8157     if (!args.thisv().isObject() ||
8158         !args.thisv().toObject().is<StreamCacheEntryObject>()) {
8159       return false;
8160     }
8161 
8162     StreamCacheEntryObject& obj =
8163         args.thisv().toObject().as<StreamCacheEntryObject>();
8164     args.rval().setBoolean(obj.cache().hasOptimizedEncoding());
8165     return true;
8166   }
getBuffer(JSContext * cx,unsigned argc,Value * vp)8167   static bool getBuffer(JSContext* cx, unsigned argc, Value* vp) {
8168     CallArgs args = CallArgsFromVp(argc, vp);
8169     if (!args.thisv().isObject() ||
8170         !args.thisv().toObject().is<StreamCacheEntryObject>()) {
8171       return false;
8172     }
8173 
8174     auto& bytes =
8175         args.thisv().toObject().as<StreamCacheEntryObject>().cache().bytes();
8176     RootedArrayBufferObject buffer(
8177         cx, ArrayBufferObject::createZeroed(cx, bytes.length()));
8178     if (!buffer) {
8179       return false;
8180     }
8181 
8182     memcpy(buffer->dataPointer(), bytes.begin(), bytes.length());
8183 
8184     args.rval().setObject(*buffer);
8185     return true;
8186   }
8187 
8188  public:
8189   static const unsigned RESERVED_SLOTS = 1;
8190   static const JSClass class_;
8191   static const JSPropertySpec properties[];
8192 
construct(JSContext * cx,unsigned argc,Value * vp)8193   static bool construct(JSContext* cx, unsigned argc, Value* vp) {
8194     CallArgs args = CallArgsFromVp(argc, vp);
8195     if (!args.requireAtLeast(cx, "streamCacheEntry", 1)) {
8196       return false;
8197     }
8198 
8199     SharedMem<uint8_t*> ptr;
8200     size_t numBytes;
8201     if (!args[0].isObject() ||
8202         !IsBufferSource(&args[0].toObject(), &ptr, &numBytes)) {
8203       RootedObject callee(cx, &args.callee());
8204       ReportUsageErrorASCII(cx, callee, "Argument must be an ArrayBuffer");
8205       return false;
8206     }
8207 
8208     Uint8Vector bytes;
8209     if (!bytes.resize(numBytes)) {
8210       return false;
8211     }
8212 
8213     memcpy(bytes.begin(), ptr.unwrap(), numBytes);
8214 
8215     RefPtr<StreamCacheEntry> cache =
8216         cx->new_<StreamCacheEntry>(std::move(bytes));
8217     if (!cache) {
8218       return false;
8219     }
8220 
8221     RootedNativeObject obj(
8222         cx, NewObjectWithGivenProto<StreamCacheEntryObject>(cx, nullptr));
8223     if (!obj) {
8224       return false;
8225     }
8226     obj->initReservedSlot(CACHE_ENTRY_SLOT,
8227                           PrivateValue(cache.forget().take()));
8228 
8229     if (!JS_DefineProperty(cx, obj, "cached", cachedGetter, nullptr, 0)) {
8230       return false;
8231     }
8232     if (!JS_DefineFunction(cx, obj, "getBuffer", getBuffer, 0, 0)) {
8233       return false;
8234     }
8235 
8236     args.rval().setObject(*obj);
8237     return true;
8238   }
8239 
cache() const8240   StreamCacheEntry& cache() const {
8241     return *(StreamCacheEntry*)getReservedSlot(CACHE_ENTRY_SLOT).toPrivate();
8242   }
8243 };
8244 
8245 const JSClassOps StreamCacheEntryObject::classOps_ = {
8246     nullptr,                           // addProperty
8247     nullptr,                           // delProperty
8248     nullptr,                           // enumerate
8249     nullptr,                           // newEnumerate
8250     nullptr,                           // resolve
8251     nullptr,                           // mayResolve
8252     StreamCacheEntryObject::finalize,  // finalize
8253     nullptr,                           // call
8254     nullptr,                           // hasInstance
8255     nullptr,                           // construct
8256     nullptr,                           // trace
8257 };
8258 
8259 const JSClass StreamCacheEntryObject::class_ = {
8260     "StreamCacheEntryObject",
8261     JSCLASS_HAS_RESERVED_SLOTS(StreamCacheEntryObject::RESERVED_SLOTS) |
8262         JSCLASS_BACKGROUND_FINALIZE,
8263     &StreamCacheEntryObject::classOps_};
8264 
8265 struct BufferStreamJob {
8266   Variant<Uint8Vector, StreamCacheEntryPtr> source;
8267   Thread thread;
8268   JS::StreamConsumer* consumer;
8269 
BufferStreamJobBufferStreamJob8270   BufferStreamJob(Uint8Vector&& source, JS::StreamConsumer* consumer)
8271       : source(AsVariant<Uint8Vector>(std::move(source))), consumer(consumer) {}
BufferStreamJobBufferStreamJob8272   BufferStreamJob(StreamCacheEntry& source, JS::StreamConsumer* consumer)
8273       : source(AsVariant<StreamCacheEntryPtr>(&source)), consumer(consumer) {}
8274 };
8275 
8276 struct BufferStreamState {
8277   Vector<UniquePtr<BufferStreamJob>, 0, SystemAllocPolicy> jobs;
8278   size_t delayMillis;
8279   size_t chunkSize;
8280   bool shutdown;
8281 
BufferStreamStateBufferStreamState8282   BufferStreamState() : delayMillis(1), chunkSize(10), shutdown(false) {}
8283 
~BufferStreamStateBufferStreamState8284   ~BufferStreamState() { MOZ_ASSERT(jobs.empty()); }
8285 };
8286 
8287 static ExclusiveWaitableData<BufferStreamState>* bufferStreamState;
8288 
BufferStreamMain(BufferStreamJob * job)8289 static void BufferStreamMain(BufferStreamJob* job) {
8290   const uint8_t* bytes;
8291   size_t byteLength;
8292   JS::OptimizedEncodingListener* listener;
8293   if (job->source.is<StreamCacheEntryPtr>()) {
8294     StreamCacheEntry& cache = *job->source.as<StreamCacheEntryPtr>();
8295     if (cache.hasOptimizedEncoding()) {
8296       const Uint8Vector& optimized = cache.optimizedEncoding();
8297       job->consumer->consumeOptimizedEncoding(optimized.begin(),
8298                                               optimized.length());
8299       goto done;
8300     }
8301 
8302     bytes = cache.bytes().begin();
8303     byteLength = cache.bytes().length();
8304     listener = &cache;
8305   } else {
8306     bytes = job->source.as<Uint8Vector>().begin();
8307     byteLength = job->source.as<Uint8Vector>().length();
8308     listener = nullptr;
8309   }
8310 
8311   size_t byteOffset;
8312   byteOffset = 0;
8313   while (true) {
8314     if (byteOffset == byteLength) {
8315       job->consumer->streamEnd(listener);
8316       break;
8317     }
8318 
8319     bool shutdown;
8320     size_t delayMillis;
8321     size_t chunkSize;
8322     {
8323       auto state = bufferStreamState->lock();
8324       shutdown = state->shutdown;
8325       delayMillis = state->delayMillis;
8326       chunkSize = state->chunkSize;
8327     }
8328 
8329     if (shutdown) {
8330       job->consumer->streamError(JSMSG_STREAM_CONSUME_ERROR);
8331       break;
8332     }
8333 
8334     ThisThread::SleepMilliseconds(delayMillis);
8335 
8336     chunkSize = std::min(chunkSize, byteLength - byteOffset);
8337 
8338     if (!job->consumer->consumeChunk(bytes + byteOffset, chunkSize)) {
8339       break;
8340     }
8341 
8342     byteOffset += chunkSize;
8343   }
8344 
8345 done:
8346   auto state = bufferStreamState->lock();
8347   size_t jobIndex = 0;
8348   while (state->jobs[jobIndex].get() != job) {
8349     jobIndex++;
8350   }
8351   job->thread.detach();  // quiet assert in ~Thread() called by erase().
8352   state->jobs.erase(state->jobs.begin() + jobIndex);
8353   if (state->jobs.empty()) {
8354     state.notify_all(/* jobs empty */);
8355   }
8356 }
8357 
EnsureLatin1CharsLinearString(JSContext * cx,HandleValue value,UniqueChars * result)8358 static bool EnsureLatin1CharsLinearString(JSContext* cx, HandleValue value,
8359                                           UniqueChars* result) {
8360   if (!value.isString()) {
8361     result->reset(nullptr);
8362     return true;
8363   }
8364   RootedString str(cx, value.toString());
8365   if (!str->isLinear() || !str->hasLatin1Chars()) {
8366     JS_ReportErrorASCII(cx,
8367                         "only latin1 chars and linear strings are expected");
8368     return false;
8369   }
8370 
8371   // Use JS_EncodeStringToLatin1 to null-terminate.
8372   *result = JS_EncodeStringToLatin1(cx, str);
8373   return !!*result;
8374 }
8375 
ConsumeBufferSource(JSContext * cx,JS::HandleObject obj,JS::MimeType,JS::StreamConsumer * consumer)8376 static bool ConsumeBufferSource(JSContext* cx, JS::HandleObject obj,
8377                                 JS::MimeType, JS::StreamConsumer* consumer) {
8378   {
8379     RootedValue url(cx);
8380     if (!JS_GetProperty(cx, obj, "url", &url)) {
8381       return false;
8382     }
8383     UniqueChars urlChars;
8384     if (!EnsureLatin1CharsLinearString(cx, url, &urlChars)) {
8385       return false;
8386     }
8387 
8388     RootedValue mapUrl(cx);
8389     if (!JS_GetProperty(cx, obj, "sourceMappingURL", &mapUrl)) {
8390       return false;
8391     }
8392     UniqueChars mapUrlChars;
8393     if (!EnsureLatin1CharsLinearString(cx, mapUrl, &mapUrlChars)) {
8394       return false;
8395     }
8396 
8397     consumer->noteResponseURLs(urlChars.get(), mapUrlChars.get());
8398   }
8399 
8400   UniquePtr<BufferStreamJob> job;
8401 
8402   SharedMem<uint8_t*> dataPointer;
8403   size_t byteLength;
8404   if (IsBufferSource(obj, &dataPointer, &byteLength)) {
8405     Uint8Vector bytes;
8406     if (!bytes.resize(byteLength)) {
8407       JS_ReportOutOfMemory(cx);
8408       return false;
8409     }
8410 
8411     memcpy(bytes.begin(), dataPointer.unwrap(), byteLength);
8412     job = cx->make_unique<BufferStreamJob>(std::move(bytes), consumer);
8413   } else if (obj->is<StreamCacheEntryObject>()) {
8414     job = cx->make_unique<BufferStreamJob>(
8415         obj->as<StreamCacheEntryObject>().cache(), consumer);
8416   } else {
8417     JS_ReportErrorASCII(
8418         cx,
8419         "shell streaming consumes a buffer source (buffer or view) "
8420         "or StreamCacheEntryObject");
8421     return false;
8422   }
8423   if (!job) {
8424     return false;
8425   }
8426 
8427   BufferStreamJob* jobPtr = job.get();
8428 
8429   {
8430     auto state = bufferStreamState->lock();
8431     MOZ_ASSERT(!state->shutdown);
8432     if (!state->jobs.append(std::move(job))) {
8433       JS_ReportOutOfMemory(cx);
8434       return false;
8435     }
8436   }
8437 
8438   {
8439     AutoEnterOOMUnsafeRegion oomUnsafe;
8440     if (!jobPtr->thread.init(BufferStreamMain, jobPtr)) {
8441       oomUnsafe.crash("ConsumeBufferSource");
8442     }
8443   }
8444 
8445   return true;
8446 }
8447 
ReportStreamError(JSContext * cx,size_t errorNumber)8448 static void ReportStreamError(JSContext* cx, size_t errorNumber) {
8449   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
8450 }
8451 
SetBufferStreamParams(JSContext * cx,unsigned argc,Value * vp)8452 static bool SetBufferStreamParams(JSContext* cx, unsigned argc, Value* vp) {
8453   CallArgs args = CallArgsFromVp(argc, vp);
8454   if (!args.requireAtLeast(cx, "setBufferStreamParams", 2)) {
8455     return false;
8456   }
8457 
8458   double delayMillis;
8459   if (!ToNumber(cx, args[0], &delayMillis)) {
8460     return false;
8461   }
8462 
8463   double chunkSize;
8464   if (!ToNumber(cx, args[1], &chunkSize)) {
8465     return false;
8466   }
8467 
8468   {
8469     auto state = bufferStreamState->lock();
8470     state->delayMillis = delayMillis;
8471     state->chunkSize = chunkSize;
8472   }
8473 
8474   args.rval().setUndefined();
8475   return true;
8476 }
8477 
ShutdownBufferStreams()8478 static void ShutdownBufferStreams() {
8479   auto state = bufferStreamState->lock();
8480   state->shutdown = true;
8481   while (!state->jobs.empty()) {
8482     state.wait(/* jobs empty */);
8483   }
8484   state->jobs.clearAndFree();
8485 }
8486 
DumpScopeChain(JSContext * cx,unsigned argc,Value * vp)8487 static bool DumpScopeChain(JSContext* cx, unsigned argc, Value* vp) {
8488   CallArgs args = CallArgsFromVp(argc, vp);
8489   RootedObject callee(cx, &args.callee());
8490 
8491   if (js::SupportDifferentialTesting()) {
8492     ReportUsageErrorASCII(
8493         cx, callee, "Function not available in differential testing mode.");
8494     return false;
8495   }
8496 
8497   if (args.length() != 1) {
8498     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8499     return false;
8500   }
8501 
8502   if (!args[0].isObject() ||
8503       !(args[0].toObject().is<JSFunction>() ||
8504         args[0].toObject().is<ShellModuleObjectWrapper>())) {
8505     ReportUsageErrorASCII(
8506         cx, callee, "Argument must be an interpreted function or a module");
8507     return false;
8508   }
8509 
8510   RootedObject obj(cx, &args[0].toObject());
8511   RootedScript script(cx);
8512 
8513   if (obj->is<JSFunction>()) {
8514     RootedFunction fun(cx, &obj->as<JSFunction>());
8515     if (!fun->isInterpreted()) {
8516       ReportUsageErrorASCII(cx, callee,
8517                             "Argument must be an interpreted function");
8518       return false;
8519     }
8520     script = JSFunction::getOrCreateScript(cx, fun);
8521     if (!script) {
8522       return false;
8523     }
8524   } else {
8525     script = obj->as<ShellModuleObjectWrapper>().get()->maybeScript();
8526     if (!script) {
8527       JS_ReportErrorASCII(cx, "module does not have an associated script");
8528       return false;
8529     }
8530   }
8531 
8532   script->bodyScope()->dump();
8533 
8534   args.rval().setUndefined();
8535   return true;
8536 }
8537 
8538 // For testing gray marking, grayRoot() will heap-allocate an address
8539 // where we can store a JSObject*, and create a new object if one doesn't
8540 // already exist.
8541 //
8542 // Note that EnsureGrayRoot() will blacken the returned object, so it will not
8543 // actually end up marked gray until the following GC clears the black bit
8544 // (assuming nothing is holding onto it.)
8545 //
8546 // The idea is that you can set up a whole graph of objects to be marked gray,
8547 // hanging off of the object returned from grayRoot(). Then you GC to clear the
8548 // black bits and set the gray bits.
8549 //
8550 // To test grayness, register the objects of interest with addMarkObservers(),
8551 // which takes an Array of objects (which will be marked black at the time
8552 // they're passed in). Their mark bits may be retrieved at any time with
8553 // getMarks(), in the form of an array of strings with each index corresponding
8554 // to the original objects passed to addMarkObservers().
8555 
EnsureGrayRoot(JSContext * cx,unsigned argc,Value * vp)8556 static bool EnsureGrayRoot(JSContext* cx, unsigned argc, Value* vp) {
8557   CallArgs args = CallArgsFromVp(argc, vp);
8558 
8559   auto priv = EnsureShellCompartmentPrivate(cx);
8560   if (!priv) {
8561     return false;
8562   }
8563 
8564   if (!priv->grayRoot) {
8565     if (!(priv->grayRoot = NewTenuredDenseEmptyArray(cx, nullptr))) {
8566       return false;
8567     }
8568   }
8569 
8570   // Barrier to enforce the invariant that JS does not touch gray objects.
8571   JSObject* obj = priv->grayRoot;
8572   JS::ExposeObjectToActiveJS(obj);
8573 
8574   args.rval().setObject(*obj);
8575   return true;
8576 }
8577 
EnsureMarkBitObservers(JSContext * cx)8578 static MarkBitObservers* EnsureMarkBitObservers(JSContext* cx) {
8579   ShellContext* sc = GetShellContext(cx);
8580   if (!sc->markObservers) {
8581     auto* observers =
8582         cx->new_<MarkBitObservers>(cx->runtime(), NonshrinkingGCObjectVector());
8583     if (!observers) {
8584       return nullptr;
8585     }
8586     sc->markObservers.reset(observers);
8587   }
8588   return sc->markObservers.get();
8589 }
8590 
ClearMarkObservers(JSContext * cx,unsigned argc,Value * vp)8591 static bool ClearMarkObservers(JSContext* cx, unsigned argc, Value* vp) {
8592   CallArgs args = CallArgsFromVp(argc, vp);
8593 
8594   auto markObservers = EnsureMarkBitObservers(cx);
8595   if (!markObservers) {
8596     return false;
8597   }
8598 
8599   markObservers->get().clear();
8600 
8601   args.rval().setUndefined();
8602   return true;
8603 }
8604 
AddMarkObservers(JSContext * cx,unsigned argc,Value * vp)8605 static bool AddMarkObservers(JSContext* cx, unsigned argc, Value* vp) {
8606   CallArgs args = CallArgsFromVp(argc, vp);
8607 
8608   auto markObservers = EnsureMarkBitObservers(cx);
8609   if (!markObservers) {
8610     return false;
8611   }
8612 
8613   if (!args.get(0).isObject()) {
8614     JS_ReportErrorASCII(cx, "argument must be an Array of objects");
8615     return false;
8616   }
8617 
8618   RootedObject observersArg(cx, &args[0].toObject());
8619   uint64_t length;
8620   if (!GetLengthProperty(cx, observersArg, &length)) {
8621     return false;
8622   }
8623 
8624   if (length > UINT32_MAX) {
8625     JS_ReportErrorASCII(cx, "Invalid length for observers array");
8626     return false;
8627   }
8628 
8629   RootedValue value(cx);
8630   RootedObject object(cx);
8631   for (uint32_t i = 0; i < length; i++) {
8632     if (!JS_GetElement(cx, observersArg, i, &value)) {
8633       return false;
8634     }
8635 
8636     if (!value.isObject()) {
8637       JS_ReportErrorASCII(cx, "argument must be an Array of objects");
8638       return false;
8639     }
8640 
8641     object = &value.toObject();
8642     if (gc::IsInsideNursery(object)) {
8643       // WeakCaches are not swept during a minor GC. To prevent
8644       // nursery-allocated contents from having the mark bits be deceptively
8645       // black until the second GC, they would need to be marked weakly (cf
8646       // NurseryAwareHashMap). It is simpler to evict the nursery to prevent
8647       // nursery objects from being observed.
8648       cx->runtime()->gc.evictNursery();
8649     }
8650 
8651     if (!markObservers->get().append(object)) {
8652       return false;
8653     }
8654   }
8655 
8656   args.rval().setInt32(length);
8657   return true;
8658 }
8659 
GetMarks(JSContext * cx,unsigned argc,Value * vp)8660 static bool GetMarks(JSContext* cx, unsigned argc, Value* vp) {
8661   CallArgs args = CallArgsFromVp(argc, vp);
8662 
8663   auto& observers = GetShellContext(cx)->markObservers;
8664   if (!observers) {
8665     args.rval().setUndefined();
8666     return true;
8667   }
8668 
8669   size_t length = observers->get().length();
8670   Rooted<ArrayObject*> ret(cx, js::NewDenseEmptyArray(cx));
8671   if (!ret) {
8672     return false;
8673   }
8674 
8675   for (uint32_t i = 0; i < length; i++) {
8676     const char* color;
8677     JSObject* obj = observers->get()[i];
8678     if (!obj) {
8679       color = "dead";
8680     } else {
8681       gc::TenuredCell* cell = &obj->asTenured();
8682       if (cell->isMarkedGray()) {
8683         color = "gray";
8684       } else if (cell->isMarkedBlack()) {
8685         color = "black";
8686       } else {
8687         color = "unmarked";
8688       }
8689     }
8690     JSString* s = JS_NewStringCopyZ(cx, color);
8691     if (!s) {
8692       return false;
8693     }
8694     if (!NewbornArrayPush(cx, ret, StringValue(s))) {
8695       return false;
8696     }
8697   }
8698 
8699   args.rval().setObject(*ret);
8700   return true;
8701 }
8702 
8703 namespace js {
8704 namespace shell {
8705 
8706 class ShellAutoEntryMonitor : JS::dbg::AutoEntryMonitor {
8707   Vector<UniqueChars, 1, js::SystemAllocPolicy> log;
8708   bool oom;
8709   bool enteredWithoutExit;
8710 
8711  public:
ShellAutoEntryMonitor(JSContext * cx)8712   explicit ShellAutoEntryMonitor(JSContext* cx)
8713       : AutoEntryMonitor(cx), oom(false), enteredWithoutExit(false) {}
8714 
~ShellAutoEntryMonitor()8715   ~ShellAutoEntryMonitor() { MOZ_ASSERT(!enteredWithoutExit); }
8716 
Entry(JSContext * cx,JSFunction * function,JS::HandleValue asyncStack,const char * asyncCause)8717   void Entry(JSContext* cx, JSFunction* function, JS::HandleValue asyncStack,
8718              const char* asyncCause) override {
8719     MOZ_ASSERT(!enteredWithoutExit);
8720     enteredWithoutExit = true;
8721 
8722     RootedString displayId(cx, JS_GetFunctionDisplayId(function));
8723     if (displayId) {
8724       UniqueChars displayIdStr = JS_EncodeStringToUTF8(cx, displayId);
8725       if (!displayIdStr) {
8726         // We report OOM in buildResult.
8727         cx->recoverFromOutOfMemory();
8728         oom = true;
8729         return;
8730       }
8731       oom = !log.append(std::move(displayIdStr));
8732       return;
8733     }
8734 
8735     oom = !log.append(DuplicateString("anonymous"));
8736   }
8737 
Entry(JSContext * cx,JSScript * script,JS::HandleValue asyncStack,const char * asyncCause)8738   void Entry(JSContext* cx, JSScript* script, JS::HandleValue asyncStack,
8739              const char* asyncCause) override {
8740     MOZ_ASSERT(!enteredWithoutExit);
8741     enteredWithoutExit = true;
8742 
8743     UniqueChars label(JS_smprintf("eval:%s", JS_GetScriptFilename(script)));
8744     oom = !label || !log.append(std::move(label));
8745   }
8746 
Exit(JSContext * cx)8747   void Exit(JSContext* cx) override {
8748     MOZ_ASSERT(enteredWithoutExit);
8749     enteredWithoutExit = false;
8750   }
8751 
buildResult(JSContext * cx,MutableHandleValue resultValue)8752   bool buildResult(JSContext* cx, MutableHandleValue resultValue) {
8753     if (oom) {
8754       JS_ReportOutOfMemory(cx);
8755       return false;
8756     }
8757 
8758     RootedObject result(cx, JS::NewArrayObject(cx, log.length()));
8759     if (!result) {
8760       return false;
8761     }
8762 
8763     for (size_t i = 0; i < log.length(); i++) {
8764       char* name = log[i].get();
8765       RootedString string(cx, Atomize(cx, name, strlen(name)));
8766       if (!string) {
8767         return false;
8768       }
8769       RootedValue value(cx, StringValue(string));
8770       if (!JS_SetElement(cx, result, i, value)) {
8771         return false;
8772       }
8773     }
8774 
8775     resultValue.setObject(*result.get());
8776     return true;
8777   }
8778 };
8779 
8780 }  // namespace shell
8781 }  // namespace js
8782 
EntryPoints(JSContext * cx,unsigned argc,Value * vp)8783 static bool EntryPoints(JSContext* cx, unsigned argc, Value* vp) {
8784   CallArgs args = CallArgsFromVp(argc, vp);
8785 
8786   if (args.length() != 1) {
8787     JS_ReportErrorASCII(cx, "Wrong number of arguments");
8788     return false;
8789   }
8790 
8791   RootedObject opts(cx, ToObject(cx, args[0]));
8792   if (!opts) {
8793     return false;
8794   }
8795 
8796   // { function: f } --- Call f.
8797   {
8798     RootedValue fun(cx), dummy(cx);
8799 
8800     if (!JS_GetProperty(cx, opts, "function", &fun)) {
8801       return false;
8802     }
8803     if (!fun.isUndefined()) {
8804       js::shell::ShellAutoEntryMonitor sarep(cx);
8805       if (!Call(cx, UndefinedHandleValue, fun, JS::HandleValueArray::empty(),
8806                 &dummy)) {
8807         return false;
8808       }
8809       return sarep.buildResult(cx, args.rval());
8810     }
8811   }
8812 
8813   // { object: o, property: p, value: v } --- Fetch o[p], or if
8814   // v is present, assign o[p] = v.
8815   {
8816     RootedValue objectv(cx), propv(cx), valuev(cx);
8817 
8818     if (!JS_GetProperty(cx, opts, "object", &objectv) ||
8819         !JS_GetProperty(cx, opts, "property", &propv))
8820       return false;
8821     if (!objectv.isUndefined() && !propv.isUndefined()) {
8822       RootedObject object(cx, ToObject(cx, objectv));
8823       if (!object) {
8824         return false;
8825       }
8826 
8827       RootedString string(cx, ToString(cx, propv));
8828       if (!string) {
8829         return false;
8830       }
8831       RootedId id(cx);
8832       if (!JS_StringToId(cx, string, &id)) {
8833         return false;
8834       }
8835 
8836       if (!JS_GetProperty(cx, opts, "value", &valuev)) {
8837         return false;
8838       }
8839 
8840       js::shell::ShellAutoEntryMonitor sarep(cx);
8841 
8842       if (!valuev.isUndefined()) {
8843         if (!JS_SetPropertyById(cx, object, id, valuev)) {
8844           return false;
8845         }
8846       } else {
8847         if (!JS_GetPropertyById(cx, object, id, &valuev)) {
8848           return false;
8849         }
8850       }
8851 
8852       return sarep.buildResult(cx, args.rval());
8853     }
8854   }
8855 
8856   // { ToString: v } --- Apply JS::ToString to v.
8857   {
8858     RootedValue v(cx);
8859 
8860     if (!JS_GetProperty(cx, opts, "ToString", &v)) {
8861       return false;
8862     }
8863     if (!v.isUndefined()) {
8864       js::shell::ShellAutoEntryMonitor sarep(cx);
8865       if (!JS::ToString(cx, v)) {
8866         return false;
8867       }
8868       return sarep.buildResult(cx, args.rval());
8869     }
8870   }
8871 
8872   // { ToNumber: v } --- Apply JS::ToNumber to v.
8873   {
8874     RootedValue v(cx);
8875     double dummy;
8876 
8877     if (!JS_GetProperty(cx, opts, "ToNumber", &v)) {
8878       return false;
8879     }
8880     if (!v.isUndefined()) {
8881       js::shell::ShellAutoEntryMonitor sarep(cx);
8882       if (!JS::ToNumber(cx, v, &dummy)) {
8883         return false;
8884       }
8885       return sarep.buildResult(cx, args.rval());
8886     }
8887   }
8888 
8889   // { eval: code } --- Apply ToString and then Evaluate to code.
8890   {
8891     RootedValue code(cx), dummy(cx);
8892 
8893     if (!JS_GetProperty(cx, opts, "eval", &code)) {
8894       return false;
8895     }
8896     if (!code.isUndefined()) {
8897       RootedString codeString(cx, ToString(cx, code));
8898       if (!codeString) {
8899         return false;
8900       }
8901 
8902       AutoStableStringChars stableChars(cx);
8903       if (!stableChars.initTwoByte(cx, codeString)) {
8904         return false;
8905       }
8906       JS::SourceText<char16_t> srcBuf;
8907       if (!srcBuf.init(cx, stableChars.twoByteRange().begin().get(),
8908                        codeString->length(), JS::SourceOwnership::Borrowed)) {
8909         return false;
8910       }
8911 
8912       CompileOptions options(cx);
8913       options.setIntroductionType("entryPoint eval")
8914           .setFileAndLine("entryPoint eval", 1);
8915 
8916       js::shell::ShellAutoEntryMonitor sarep(cx);
8917       if (!JS::Evaluate(cx, options, srcBuf, &dummy)) {
8918         return false;
8919       }
8920       return sarep.buildResult(cx, args.rval());
8921     }
8922   }
8923 
8924   JS_ReportErrorASCII(cx, "bad 'params' object");
8925   return false;
8926 }
8927 
8928 #ifndef __wasi__
WasmTextToBinary(JSContext * cx,unsigned argc,Value * vp)8929 static bool WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) {
8930   CallArgs args = CallArgsFromVp(argc, vp);
8931   RootedObject callee(cx, &args.callee());
8932 
8933   if (!args.requireAtLeast(cx, "wasmTextToBinary", 1)) {
8934     return false;
8935   }
8936 
8937   if (!args[0].isString()) {
8938     ReportUsageErrorASCII(cx, callee, "First argument must be a String");
8939     return false;
8940   }
8941 
8942   size_t textLen = args[0].toString()->length();
8943 
8944   AutoStableStringChars twoByteChars(cx);
8945   if (!twoByteChars.initTwoByte(cx, args[0].toString())) {
8946     return false;
8947   }
8948 
8949   wasm::Bytes bytes;
8950   UniqueChars error;
8951   if (!wasm::TextToBinary(twoByteChars.twoByteChars(), textLen, &bytes,
8952                           &error)) {
8953     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
8954                              error.get() ? error.get() : "out of memory");
8955     return false;
8956   }
8957 
8958   RootedObject binary(cx, JS_NewUint8Array(cx, bytes.length()));
8959   if (!binary) {
8960     return false;
8961   }
8962 
8963   memcpy(binary->as<TypedArrayObject>().dataPointerUnshared(), bytes.begin(),
8964          bytes.length());
8965 
8966   args.rval().setObject(*binary);
8967   return true;
8968 }
8969 
WasmCodeOffsets(JSContext * cx,unsigned argc,Value * vp)8970 static bool WasmCodeOffsets(JSContext* cx, unsigned argc, Value* vp) {
8971   CallArgs args = CallArgsFromVp(argc, vp);
8972   RootedObject callee(cx, &args.callee());
8973 
8974   if (!args.requireAtLeast(cx, "wasmCodeOffsets", 1)) {
8975     return false;
8976   }
8977 
8978   if (!args.get(0).isObject()) {
8979     JS_ReportErrorASCII(cx, "argument is not an object");
8980     return false;
8981   }
8982 
8983   SharedMem<uint8_t*> bytes;
8984   size_t byteLength;
8985 
8986   JSObject* bufferObject = &args[0].toObject();
8987   JSObject* unwrappedBufferObject = CheckedUnwrapStatic(bufferObject);
8988   if (!unwrappedBufferObject ||
8989       !IsBufferSource(unwrappedBufferObject, &bytes, &byteLength)) {
8990     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
8991                              JSMSG_WASM_BAD_BUF_ARG);
8992     return false;
8993   }
8994 
8995   wasm::Uint32Vector offsets;
8996   wasm::CodeOffsets(bytes.unwrap(), byteLength, &offsets);
8997 
8998   RootedObject jsOffsets(cx, JS::NewArrayObject(cx, offsets.length()));
8999   if (!jsOffsets) {
9000     return false;
9001   }
9002   for (size_t i = 0; i < offsets.length(); i++) {
9003     uint32_t offset = offsets[i];
9004     RootedValue offsetVal(cx, NumberValue(offset));
9005     if (!JS_SetElement(cx, jsOffsets, i, offsetVal)) {
9006       return false;
9007     }
9008   }
9009   args.rval().setObject(*jsOffsets);
9010   return true;
9011 }
9012 
9013 #  ifndef __AFL_HAVE_MANUAL_CONTROL
9014 #    define __AFL_LOOP(x) true
9015 #  endif
9016 
WasmLoop(JSContext * cx,unsigned argc,Value * vp)9017 static bool WasmLoop(JSContext* cx, unsigned argc, Value* vp) {
9018   CallArgs args = CallArgsFromVp(argc, vp);
9019   RootedObject callee(cx, &args.callee());
9020 
9021   if (args.length() < 1 || args.length() > 2) {
9022     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
9023     return false;
9024   }
9025 
9026   if (!args[0].isString()) {
9027     ReportUsageErrorASCII(cx, callee, "First argument must be a String");
9028     return false;
9029   }
9030 
9031   RootedObject importObj(cx);
9032   if (!args.get(1).isUndefined()) {
9033     if (!args.get(1).isObject()) {
9034       ReportUsageErrorASCII(cx, callee,
9035                             "Second argument, if present, must be an Object");
9036       return false;
9037     }
9038     importObj = &args[1].toObject();
9039   }
9040 
9041   RootedString givenPath(cx, args[0].toString());
9042   RootedString filename(cx, ResolvePath(cx, givenPath, RootRelative));
9043   if (!filename) {
9044     return false;
9045   }
9046 
9047   while (__AFL_LOOP(1000)) {
9048     Rooted<JSObject*> ret(cx, FileAsTypedArray(cx, filename));
9049     if (!ret) {
9050       return false;
9051     }
9052 
9053     Rooted<TypedArrayObject*> typedArray(cx, &ret->as<TypedArrayObject>());
9054     RootedWasmInstanceObject instanceObj(cx);
9055     // No additional compile options here, we don't need them for this use case.
9056     RootedValue maybeOptions(cx);
9057     if (!wasm::Eval(cx, typedArray, importObj, maybeOptions, &instanceObj)) {
9058       // Clear any pending exceptions, we don't care about them
9059       cx->clearPendingException();
9060     }
9061   }
9062 
9063 #  ifdef __AFL_HAVE_MANUAL_CONTROL  // to silence unreachable code warning
9064   return true;
9065 #  endif
9066 }
9067 #endif  // __wasi__
9068 
9069 static constexpr uint32_t DOM_OBJECT_SLOT = 0;
9070 static constexpr uint32_t DOM_OBJECT_SLOT2 = 1;
9071 
9072 static const JSClass* GetDomClass();
9073 
9074 static JSObject* GetDOMPrototype(JSContext* cx, JSObject* global);
9075 
9076 static const JSClass TransplantableDOMObjectClass = {
9077     "TransplantableDOMObject",
9078     JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1)};
9079 
9080 static const JSClass TransplantableDOMProxyObjectClass =
9081     PROXY_CLASS_DEF("TransplantableDOMProxyObject",
9082                     JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1));
9083 
9084 class TransplantableDOMProxyHandler final : public ForwardingProxyHandler {
9085  public:
9086   static const TransplantableDOMProxyHandler singleton;
9087   static const char family;
9088 
TransplantableDOMProxyHandler()9089   constexpr TransplantableDOMProxyHandler() : ForwardingProxyHandler(&family) {}
9090 
9091   // These two proxy traps are called in |js::DeadProxyTargetValue|, which in
9092   // turn is called when nuking proxies. Because this proxy can temporarily be
9093   // without an object in its private slot, see |EnsureExpandoObject|, the
9094   // default implementation inherited from ForwardingProxyHandler can't be used,
9095   // since it tries to derive the callable/constructible value from the target.
isCallable(JSObject * obj) const9096   bool isCallable(JSObject* obj) const override { return false; }
isConstructor(JSObject * obj) const9097   bool isConstructor(JSObject* obj) const override { return false; }
9098 
9099   // Simplified implementation of |DOMProxyHandler::GetAndClearExpandoObject|.
GetAndClearExpandoObject(JSObject * obj)9100   static JSObject* GetAndClearExpandoObject(JSObject* obj) {
9101     const Value& v = GetProxyPrivate(obj);
9102     if (v.isUndefined()) {
9103       return nullptr;
9104     }
9105 
9106     JSObject* expandoObject = &v.toObject();
9107     SetProxyPrivate(obj, UndefinedValue());
9108     return expandoObject;
9109   }
9110 
9111   // Simplified implementation of |DOMProxyHandler::EnsureExpandoObject|.
EnsureExpandoObject(JSContext * cx,JS::HandleObject obj)9112   static JSObject* EnsureExpandoObject(JSContext* cx, JS::HandleObject obj) {
9113     const Value& v = GetProxyPrivate(obj);
9114     if (v.isObject()) {
9115       return &v.toObject();
9116     }
9117     MOZ_ASSERT(v.isUndefined());
9118 
9119     JSObject* expando = JS_NewObjectWithGivenProto(cx, nullptr, nullptr);
9120     if (!expando) {
9121       return nullptr;
9122     }
9123     SetProxyPrivate(obj, ObjectValue(*expando));
9124     return expando;
9125   }
9126 };
9127 
9128 const TransplantableDOMProxyHandler TransplantableDOMProxyHandler::singleton;
9129 const char TransplantableDOMProxyHandler::family = 0;
9130 
9131 enum TransplantObjectSlots {
9132   TransplantSourceObject = 0,
9133 };
9134 
TransplantObject(JSContext * cx,unsigned argc,Value * vp)9135 static bool TransplantObject(JSContext* cx, unsigned argc, Value* vp) {
9136   CallArgs args = CallArgsFromVp(argc, vp);
9137   RootedFunction callee(cx, &args.callee().as<JSFunction>());
9138 
9139   if (args.length() != 1 || !args[0].isObject()) {
9140     JS_ReportErrorASCII(cx, "transplant() must be called with an object");
9141     return false;
9142   }
9143 
9144   // |newGlobal| needs to be a GlobalObject.
9145   RootedObject newGlobal(
9146       cx, js::CheckedUnwrapDynamic(&args[0].toObject(), cx,
9147                                    /* stopAtWindowProxy = */ false));
9148   if (!newGlobal) {
9149     ReportAccessDenied(cx);
9150     return false;
9151   }
9152   if (!JS_IsGlobalObject(newGlobal)) {
9153     JS_ReportErrorNumberASCII(
9154         cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
9155         "\"global\" passed to transplant()", "not a global object");
9156     return false;
9157   }
9158 
9159   const Value& reserved =
9160       GetFunctionNativeReserved(callee, TransplantSourceObject);
9161   RootedObject source(cx, CheckedUnwrapStatic(&reserved.toObject()));
9162   if (!source) {
9163     ReportAccessDenied(cx);
9164     return false;
9165   }
9166   MOZ_ASSERT(source->getClass()->isDOMClass());
9167 
9168   // The following steps aim to replicate the behavior of UpdateReflectorGlobal
9169   // in dom/bindings/BindingUtils.cpp. In detail:
9170   // 1. Check the recursion depth using checkConservative.
9171   // 2. Enter the target compartment.
9172   // 3. Clone the source object using JS_CloneObject.
9173   // 4. Check if new wrappers can be created if source and target are in
9174   //    different compartments.
9175   // 5. Copy all properties from source to a temporary holder object.
9176   // 6. Actually transplant the object.
9177   // 7. And finally copy the properties back to the source object.
9178   //
9179   // As an extension to the algorithm in UpdateReflectorGlobal, we also allow
9180   // to transplant an object into the same compartment as the source object to
9181   // cover all operations supported by JS_TransplantObject.
9182 
9183   AutoCheckRecursionLimit recursion(cx);
9184   if (!recursion.checkConservative(cx)) {
9185     return false;
9186   }
9187 
9188   bool isProxy = IsProxy(source);
9189   RootedObject expandoObject(cx);
9190   if (isProxy) {
9191     expandoObject =
9192         TransplantableDOMProxyHandler::GetAndClearExpandoObject(source);
9193   }
9194 
9195   JSAutoRealm ar(cx, newGlobal);
9196 
9197   RootedObject proto(cx);
9198   if (JS::GetClass(source) == GetDomClass()) {
9199     proto = GetDOMPrototype(cx, newGlobal);
9200   } else {
9201     proto = JS::GetRealmObjectPrototype(cx);
9202   }
9203   if (!proto) {
9204     return false;
9205   }
9206 
9207   RootedObject target(cx, JS_CloneObject(cx, source, proto));
9208   if (!target) {
9209     return false;
9210   }
9211 
9212   if (JS::GetCompartment(source) != JS::GetCompartment(target) &&
9213       !AllowNewWrapper(JS::GetCompartment(source), target)) {
9214     JS_ReportErrorASCII(cx, "Cannot transplant into nuked compartment");
9215     return false;
9216   }
9217 
9218   RootedObject copyFrom(cx, isProxy ? expandoObject : source);
9219   RootedObject propertyHolder(cx,
9220                               JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
9221   if (!propertyHolder) {
9222     return false;
9223   }
9224 
9225   if (!JS_CopyOwnPropertiesAndPrivateFields(cx, propertyHolder, copyFrom)) {
9226     return false;
9227   }
9228 
9229   JS::SetReservedSlot(target, DOM_OBJECT_SLOT,
9230                       JS::GetReservedSlot(source, DOM_OBJECT_SLOT));
9231   JS::SetReservedSlot(source, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
9232   if (JS::GetClass(source) == GetDomClass()) {
9233     JS::SetReservedSlot(target, DOM_OBJECT_SLOT2,
9234                         JS::GetReservedSlot(source, DOM_OBJECT_SLOT2));
9235     JS::SetReservedSlot(source, DOM_OBJECT_SLOT2, UndefinedValue());
9236   }
9237 
9238   source = JS_TransplantObject(cx, source, target);
9239   if (!source) {
9240     return false;
9241   }
9242 
9243   RootedObject copyTo(cx);
9244   if (isProxy) {
9245     copyTo = TransplantableDOMProxyHandler::EnsureExpandoObject(cx, source);
9246     if (!copyTo) {
9247       return false;
9248     }
9249   } else {
9250     copyTo = source;
9251   }
9252   if (!JS_CopyOwnPropertiesAndPrivateFields(cx, copyTo, propertyHolder)) {
9253     return false;
9254   }
9255 
9256   args.rval().setUndefined();
9257   return true;
9258 }
9259 
TransplantableObject(JSContext * cx,unsigned argc,Value * vp)9260 static bool TransplantableObject(JSContext* cx, unsigned argc, Value* vp) {
9261   CallArgs args = CallArgsFromVp(argc, vp);
9262   RootedObject callee(cx, &args.callee());
9263 
9264   if (args.length() > 1) {
9265     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
9266     return false;
9267   }
9268 
9269   bool createProxy = false;
9270   RootedObject source(cx);
9271   if (args.length() == 1 && !args[0].isUndefined()) {
9272     if (!args[0].isObject()) {
9273       ReportUsageErrorASCII(cx, callee, "Argument must be an object");
9274       return false;
9275     }
9276 
9277     RootedObject options(cx, &args[0].toObject());
9278     RootedValue value(cx);
9279 
9280     if (!JS_GetProperty(cx, options, "proxy", &value)) {
9281       return false;
9282     }
9283     createProxy = JS::ToBoolean(value);
9284 
9285     if (!JS_GetProperty(cx, options, "object", &value)) {
9286       return false;
9287     }
9288     if (!value.isUndefined()) {
9289       if (!value.isObject()) {
9290         ReportUsageErrorASCII(cx, callee, "'object' option must be an object");
9291         return false;
9292       }
9293 
9294       source = &value.toObject();
9295       if (JS::GetClass(source) != GetDomClass()) {
9296         ReportUsageErrorASCII(cx, callee, "Object not a FakeDOMObject");
9297         return false;
9298       }
9299 
9300       // |source| must be a tenured object to be transplantable.
9301       if (gc::IsInsideNursery(source)) {
9302         JS_GC(cx);
9303 
9304         MOZ_ASSERT(!gc::IsInsideNursery(source),
9305                    "Live objects should be tenured after one GC, because "
9306                    "the nursery has only a single generation");
9307       }
9308     }
9309   }
9310 
9311   if (!source) {
9312     if (!createProxy) {
9313       source = NewBuiltinClassInstance(cx, &TransplantableDOMObjectClass,
9314                                        TenuredObject);
9315       if (!source) {
9316         return false;
9317       }
9318 
9319       JS::SetReservedSlot(source, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
9320     } else {
9321       JSObject* expando = JS_NewPlainObject(cx);
9322       if (!expando) {
9323         return false;
9324       }
9325       RootedValue expandoVal(cx, ObjectValue(*expando));
9326 
9327       ProxyOptions options;
9328       options.setClass(&TransplantableDOMProxyObjectClass);
9329       options.setLazyProto(true);
9330 
9331       source = NewProxyObject(cx, &TransplantableDOMProxyHandler::singleton,
9332                               expandoVal, nullptr, options);
9333       if (!source) {
9334         return false;
9335       }
9336 
9337       SetProxyReservedSlot(source, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
9338     }
9339   }
9340 
9341   jsid emptyId = NameToId(cx->names().empty);
9342   RootedObject transplant(
9343       cx, NewFunctionByIdWithReserved(cx, TransplantObject, 0, 0, emptyId));
9344   if (!transplant) {
9345     return false;
9346   }
9347 
9348   SetFunctionNativeReserved(transplant, TransplantSourceObject,
9349                             ObjectValue(*source));
9350 
9351   RootedObject result(cx, JS_NewPlainObject(cx));
9352   if (!result) {
9353     return false;
9354   }
9355 
9356   RootedValue sourceVal(cx, ObjectValue(*source));
9357   RootedValue transplantVal(cx, ObjectValue(*transplant));
9358   if (!JS_DefineProperty(cx, result, "object", sourceVal, 0) ||
9359       !JS_DefineProperty(cx, result, "transplant", transplantVal, 0)) {
9360     return false;
9361   }
9362 
9363   args.rval().setObject(*result);
9364   return true;
9365 }
9366 
9367 // clang-format off
9368 static const JSFunctionSpecWithHelp shell_functions[] = {
9369     JS_FN_HELP("options", Options, 0, 0,
9370 "options([option ...])",
9371 "  Get or toggle JavaScript options."),
9372 
9373     JS_FN_HELP("load", Load, 1, 0,
9374 "load(['foo.js' ...])",
9375 "  Load files named by string arguments. Filename is relative to the\n"
9376 "      current working directory."),
9377 
9378     JS_FN_HELP("loadRelativeToScript", LoadScriptRelativeToScript, 1, 0,
9379 "loadRelativeToScript(['foo.js' ...])",
9380 "  Load files named by string arguments. Filename is relative to the\n"
9381 "      calling script."),
9382 
9383     JS_FN_HELP("evaluate", Evaluate, 2, 0,
9384 "evaluate(code[, options])",
9385 "  Evaluate code as though it were the contents of a file.\n"
9386 "  options is an optional object that may have these properties:\n"
9387 "      isRunOnce: use the isRunOnce compiler option (default: false)\n"
9388 "      noScriptRval: use the no-script-rval compiler option (default: false)\n"
9389 "      fileName: filename for error messages and debug info\n"
9390 "      skipFileNameValidation: skip the filename-validation callback\n"
9391 "      lineNumber: starting line number for error messages and debug info\n"
9392 "      columnNumber: starting column number for error messages and debug info\n"
9393 "      global: global in which to execute the code\n"
9394 "      newContext: if true, create and use a new cx (default: false)\n"
9395 "      catchTermination: if true, catch termination (failure without\n"
9396 "         an exception value, as for slow scripts or out-of-memory)\n"
9397 "         and return 'terminated'\n"
9398 "      element: if present with value |v|, convert |v| to an object |o| and\n"
9399 "         mark the source as being attached to the DOM element |o|. If the\n"
9400 "         property is omitted or |v| is null, don't attribute the source to\n"
9401 "         any DOM element.\n"
9402 "      elementAttributeName: if present and not undefined, the name of\n"
9403 "         property of 'element' that holds this code. This is what\n"
9404 "         Debugger.Source.prototype.elementAttributeName returns.\n"
9405 "      sourceMapURL: if present with value |v|, convert |v| to a string, and\n"
9406 "         provide that as the code's source map URL. If omitted, attach no\n"
9407 "         source map URL to the code (although the code may provide one itself,\n"
9408 "         via a //#sourceMappingURL comment).\n"
9409 "      sourceIsLazy: if present and true, indicates that, after compilation, \n"
9410 "          script source should not be cached by the JS engine and should be \n"
9411 "          lazily loaded from the embedding as-needed.\n"
9412 "      forceFullParse: if present and true, disable syntax-parse.\n"
9413 "      loadBytecode: if true, and if the source is a CacheEntryObject,\n"
9414 "         the bytecode would be loaded and decoded from the cache entry instead\n"
9415 "         of being parsed, then it would be executed as usual.\n"
9416 "      saveBytecode: if true, and if the source is a CacheEntryObject,\n"
9417 "         the bytecode would be encoded and saved into the cache entry after\n"
9418 "         the script execution.\n"
9419 "         The encoded bytecode's kind is 'script'\n"
9420 "      saveIncrementalBytecode: if true, and if the source is a\n"
9421 "         CacheEntryObject, the bytecode would be incrementally encoded and\n"
9422 "         saved into the cache entry.\n"
9423 "         If --off-thread-parse-global is not used, the encoded bytecode's\n"
9424 "         kind is 'stencil'. If not, the encoded bytecode's kind is 'script'\n"
9425 "         If both loadBytecode and saveIncrementalBytecode are set,\n"
9426 "         and --off-thread-parse-global is not used, the input bytecode's\n"
9427 "         kind should be 'stencil'."
9428 "      transcodeOnly: if true, do not execute the script.\n"
9429 "      assertEqBytecode: if true, and if both loadBytecode and either\n"
9430 "         saveBytecode or saveIncrementalBytecode is true, then the loaded\n"
9431 "         bytecode and the encoded bytecode are compared.\n"
9432 "         and an assertion is raised if they differ.\n"
9433 "      envChainObject: object to put on the scope chain, with its fields added\n"
9434 "         as var bindings, akin to how elements are added to the environment in\n"
9435 "         event handlers in Gecko.\n"
9436 ),
9437 
9438     JS_FN_HELP("run", Run, 1, 0,
9439 "run('foo.js')",
9440 "  Run the file named by the first argument, returning the number of\n"
9441 "  of milliseconds spent compiling and executing it."),
9442 
9443     JS_FN_HELP("readline", ReadLine, 0, 0,
9444 "readline()",
9445 "  Read a single line from stdin."),
9446 
9447     JS_FN_HELP("readlineBuf", ReadLineBuf, 1, 0,
9448 "readlineBuf([ buf ])",
9449 "  Emulate readline() on the specified string. The first call with a string\n"
9450 "  argument sets the source buffer. Subsequent calls without an argument\n"
9451 "  then read from this buffer line by line.\n"),
9452 
9453     JS_FN_HELP("print", Print, 0, 0,
9454 "print([exp ...])",
9455 "  Evaluate and print expressions to stdout."),
9456 
9457     JS_FN_HELP("printErr", PrintErr, 0, 0,
9458 "printErr([exp ...])",
9459 "  Evaluate and print expressions to stderr."),
9460 
9461     JS_FN_HELP("putstr", PutStr, 0, 0,
9462 "putstr([exp])",
9463 "  Evaluate and print expression without newline."),
9464 
9465     JS_FN_HELP("dateNow", Now, 0, 0,
9466 "dateNow()",
9467 "  Return the current time with sub-ms precision."),
9468 
9469     JS_FN_HELP("help", Help, 0, 0,
9470 "help([function or interface object or /pattern/])",
9471 "  Display usage and help messages."),
9472 
9473     JS_FN_HELP("quit", Quit, 0, 0,
9474 "quit()",
9475 "  Quit the shell."),
9476 
9477     JS_FN_HELP("assertEq", AssertEq, 2, 0,
9478 "assertEq(actual, expected[, msg])",
9479 "  Throw if the first two arguments are not the same (both +0 or both -0,\n"
9480 "  both NaN, or non-zero and ===)."),
9481 
9482     JS_FN_HELP("startTimingMutator", StartTimingMutator, 0, 0,
9483 "startTimingMutator()",
9484 "  Start accounting time to mutator vs GC."),
9485 
9486     JS_FN_HELP("stopTimingMutator", StopTimingMutator, 0, 0,
9487 "stopTimingMutator()",
9488 "  Stop accounting time to mutator vs GC and dump the results."),
9489 
9490     JS_FN_HELP("throwError", ThrowError, 0, 0,
9491 "throwError()",
9492 "  Throw an error from JS_ReportError."),
9493 
9494     JS_FN_HELP("createErrorReport", CreateErrorReport, 1, 0,
9495 "createErrorReport(value)",
9496 "  Create an JS::ErrorReportBuilder object from the given value and serialize\n"
9497 "  to an object."),
9498 
9499 #if defined(DEBUG) || defined(JS_JITSPEW)
9500     JS_FN_HELP("disassemble", DisassembleToString, 1, 0,
9501 "disassemble([fun/code])",
9502 "  Return the disassembly for the given function or code.\n"
9503 "  All disassembly functions take these options as leading string arguments:\n"
9504 "    \"-r\" (disassemble recursively)\n"
9505 "    \"-l\" (show line numbers)\n"
9506 "    \"-S\" (omit source notes)"),
9507 
9508     JS_FN_HELP("dis", Disassemble, 1, 0,
9509 "dis([fun/code])",
9510 "  Disassemble functions into bytecodes."),
9511 
9512     JS_FN_HELP("disfile", DisassFile, 1, 0,
9513 "disfile('foo.js')",
9514 "  Disassemble script file into bytecodes.\n"),
9515 
9516     JS_FN_HELP("dissrc", DisassWithSrc, 1, 0,
9517 "dissrc([fun/code])",
9518 "  Disassemble functions with source lines."),
9519 
9520     JS_FN_HELP("notes", Notes, 1, 0,
9521 "notes([fun])",
9522 "  Show source notes for functions."),
9523 
9524     JS_FN_HELP("stackDump", StackDump, 3, 0,
9525 "stackDump(showArgs, showLocals, showThisProps)",
9526 "  Tries to print a lot of information about the current stack. \n"
9527 "  Similar to the DumpJSStack() function in the browser."),
9528 
9529 #endif
9530 
9531     JS_FN_HELP("intern", Intern, 1, 0,
9532 "intern(str)",
9533 "  Internalize str in the atom table."),
9534 
9535     JS_FN_HELP("getslx", GetSLX, 1, 0,
9536 "getslx(obj)",
9537 "  Get script line extent."),
9538 
9539     JS_FN_HELP("evalcx", EvalInContext, 1, 0,
9540 "evalcx(s[, o])",
9541 "  Evaluate s in optional sandbox object o.\n"
9542 "  if (s == '' && !o) return new o with eager standard classes\n"
9543 "  if (s == 'lazy' && !o) return new o with lazy standard classes"),
9544 
9545     JS_FN_HELP("evalInWorker", EvalInWorker, 1, 0,
9546 "evalInWorker(str)",
9547 "  Evaluate 'str' in a separate thread with its own runtime.\n"),
9548 
9549     JS_FN_HELP("getSharedObject", GetSharedObject, 0, 0,
9550 "getSharedObject()",
9551 "  Retrieve the shared object from the cross-worker mailbox.\n"
9552 "  The object retrieved may not be identical to the object that was\n"
9553 "  installed, but it references the same shared memory.\n"
9554 "  getSharedObject performs an ordering memory barrier.\n"),
9555 
9556     JS_FN_HELP("setSharedObject", SetSharedObject, 0, 0,
9557 "setSharedObject(obj)",
9558 "  Install the shared object in the cross-worker mailbox.  The object\n"
9559 "  may be null.  setSharedObject performs an ordering memory barrier.\n"),
9560 
9561     JS_FN_HELP("getSharedArrayBuffer", GetSharedObject, 0, 0,
9562 "getSharedArrayBuffer()",
9563 "  Obsolete alias for getSharedObject().\n"),
9564 
9565     JS_FN_HELP("setSharedArrayBuffer", SetSharedObject, 0, 0,
9566 "setSharedArrayBuffer(obj)",
9567 "  Obsolete alias for setSharedObject(obj).\n"),
9568 
9569     JS_FN_HELP("shapeOf", ShapeOf, 1, 0,
9570 "shapeOf(obj)",
9571 "  Get the shape of obj (an implementation detail)."),
9572 
9573 #ifdef DEBUG
9574     JS_FN_HELP("arrayInfo", ArrayInfo, 1, 0,
9575 "arrayInfo(a1, a2, ...)",
9576 "  Report statistics about arrays."),
9577 #endif
9578 
9579     JS_FN_HELP("sleep", Sleep_fn, 1, 0,
9580 "sleep(dt)",
9581 "  Sleep for dt seconds."),
9582 
9583     JS_FN_HELP("compile", Compile, 1, 0,
9584 "compile(code, [options])",
9585 "  Compiles a string to bytecode, potentially throwing.\n"
9586 "  If present, |options| may have CompileOptions-related properties of\n"
9587 "  evaluate function"),
9588 
9589     JS_FN_HELP("parseModule", ParseModule, 1, 0,
9590 "parseModule(code)",
9591 "  Parses source text as a module and returns a ModuleObject wrapper object."),
9592 
9593     JS_FN_HELP("codeModule", CodeModule, 1, 0,
9594 "codeModule(module)",
9595 "   Takes an uninstantiated ModuleObject wrapper and returns a XDR bytecode\n"
9596 "   representation of that ModuleObject."),
9597 
9598     JS_FN_HELP("decodeModule", DecodeModule, 1, 0,
9599 "decodeModule(code)",
9600 "   Takes a XDR bytecode representation of an uninstantiated\n"
9601 "   ModuleObject and returns a ModuleObject wrapper."),
9602 
9603     JS_FN_HELP("registerModule", RegisterModule, 2, 0,
9604 "registerModule(specifier, module)",
9605 "  Register a module with the module loader, so that subsequent import from\n"
9606 "  |specifier| will resolve to |module|.  Returns |module|."),
9607 
9608     JS_FN_HELP("getModuleEnvironmentNames", GetModuleEnvironmentNames, 1, 0,
9609 "getModuleEnvironmentNames(module)",
9610 "  Get the list of a module environment's bound names for a specified module.\n"),
9611 
9612     JS_FN_HELP("getModuleEnvironmentValue", GetModuleEnvironmentValue, 2, 0,
9613 "getModuleEnvironmentValue(module, name)",
9614 "  Get the value of a bound name in a module environment.\n"),
9615 
9616     JS_FN_HELP("dumpStencil", DumpStencil, 1, 0,
9617 "dumpStencil(code, [options])",
9618 "  Parses a string and returns string that represents stencil.\n"
9619 "  If present, |options| may have properties saying how the code should be\n"
9620 "  compiled:\n"
9621 "      module: if present and true, compile the source as module.\n"
9622 "      smoosh: if present and true, use SmooshMonkey.\n"
9623 "  CompileOptions-related properties of evaluate function's option can also\n"
9624 "  be used."),
9625 
9626     JS_FN_HELP("parse", Parse, 1, 0,
9627 "parse(code, [options])",
9628 "  Parses a string, potentially throwing. If present, |options| may\n"
9629 "  have properties saying how the code should be compiled:\n"
9630 "      module: if present and true, compile the source as module.\n"
9631 "      smoosh: if present and true, use SmooshMonkey.\n"
9632 "  CompileOptions-related properties of evaluate function's option can also\n"
9633 "  be used. except forceFullParse. This function always use full parse."),
9634 
9635     JS_FN_HELP("syntaxParse", SyntaxParse, 1, 0,
9636 "syntaxParse(code)",
9637 "  Check the syntax of a string, returning success value"),
9638 
9639     JS_FN_HELP("offThreadCompileScript", OffThreadCompileScript, 1, 0,
9640 "offThreadCompileScript(code[, options])",
9641 "  Compile |code| on a helper thread, returning a job ID.\n"
9642 "  To wait for the compilation to finish and run the code, call\n"
9643 "  |runOffThreadScript| passing the job ID. If present, |options| may\n"
9644 "  have properties saying how the code should be compiled:\n"
9645 "      noScriptRval: use the no-script-rval compiler option (default: false)\n"
9646 "      fileName: filename for error messages and debug info\n"
9647 "      lineNumber: starting line number for error messages and debug info\n"
9648 "      columnNumber: starting column number for error messages and debug info\n"
9649 "      element: if present with value |v|, convert |v| to an object |o| and\n"
9650 "         mark the source as being attached to the DOM element |o|. If the\n"
9651 "         property is omitted or |v| is null, don't attribute the source to\n"
9652 "         any DOM element.\n"
9653 "      elementAttributeName: if present and not undefined, the name of\n"
9654 "         property of 'element' that holds this code. This is what\n"
9655 "         Debugger.Source.prototype.elementAttributeName returns."),
9656 
9657     JS_FN_HELP("runOffThreadScript", runOffThreadScript, 0, 0,
9658 "runOffThreadScript([jobID])",
9659 "  Wait for an off-thread compilation job to complete. The job ID can be\n"
9660 "  ommitted if there is only one job pending. If an error occurred,\n"
9661 "  throw the appropriate exception; otherwise, run the script and return\n"
9662 "  its value."),
9663 
9664     JS_FN_HELP("offThreadCompileModule", OffThreadCompileModule, 1, 0,
9665 "offThreadCompileModule(code)",
9666 "  Compile |code| on a helper thread, returning a job ID. To wait for the\n"
9667 "  compilation to finish and and get the module record object call\n"
9668 "  |finishOffThreadModule| passing the job ID."),
9669 
9670     JS_FN_HELP("finishOffThreadModule", FinishOffThreadModule, 0, 0,
9671 "finishOffThreadModule([jobID])",
9672 "  Wait for an off-thread compilation job to complete. The job ID can be\n"
9673 "  ommitted if there is only one job pending. If an error occurred,\n"
9674 "  throw the appropriate exception; otherwise, return the module record object."),
9675 
9676     JS_FN_HELP("offThreadDecodeScript", OffThreadDecodeScript, 1, 0,
9677 "offThreadDecodeScript(cacheEntry[, options])",
9678 "  Decode |code| on a helper thread, returning a job ID. To wait for the\n"
9679 "  decoding to finish and run the code, call |runOffThreadDecodeScript| passing\n"
9680 "  the job ID. If present, |options| may have properties saying how the code\n"
9681 "  should be compiled (see also offThreadCompileScript)."),
9682 
9683     JS_FN_HELP("runOffThreadDecodedScript", runOffThreadDecodedScript, 0, 0,
9684 "runOffThreadDecodedScript([jobID])",
9685 "  Wait for off-thread decoding to complete. The job ID can be ommitted if there\n"
9686 "  is only one job pending. If an error occurred, throw the appropriate\n"
9687 "  exception; otherwise, run the script and return its value."),
9688 
9689     JS_FN_HELP("offThreadCompileToStencil", OffThreadCompileToStencil, 1, 0,
9690 "offThreadCompileToStencil(code[, options])",
9691 "  Compile |code| on a helper thread, returning a job ID. To wait for the\n"
9692 "  compilation to finish and get the stencil object, call\n"
9693 "  |finishOffThreadCompileToStencil| passing the job ID."),
9694 
9695     JS_FN_HELP("finishOffThreadCompileToStencil", FinishOffThreadCompileToStencil, 0, 0,
9696 "finishOffThreadCompileToStencil([jobID])",
9697 "  Wait for an off-thread compilation job to complete. The job ID can be\n"
9698 "  ommitted if there is only one job pending. If an error occurred,\n"
9699 "  throw the appropriate exception; otherwise, return the stencil object,"
9700 "  that can be passed to |evalStencil|."),
9701 
9702     JS_FN_HELP("timeout", Timeout, 1, 0,
9703 "timeout([seconds], [func])",
9704 "  Get/Set the limit in seconds for the execution time for the current context.\n"
9705 "  When the timeout expires the current interrupt callback is invoked.\n"
9706 "  The timeout is used just once.  If the callback returns a falsy value, the\n"
9707 "  script is aborted.  A negative value for seconds (this is the default) cancels\n"
9708 "  any pending timeout.\n"
9709 "  If a second argument is provided, it is installed as the interrupt handler,\n"
9710 "  exactly as if by |setInterruptCallback|.\n"),
9711 
9712     JS_FN_HELP("interruptIf", InterruptIf, 1, 0,
9713 "interruptIf(cond)",
9714 "  Requests interrupt callback if cond is true. If a callback function is set via\n"
9715 "  |timeout| or |setInterruptCallback|, it will be called. No-op otherwise."),
9716 
9717     JS_FN_HELP("invokeInterruptCallback", InvokeInterruptCallbackWrapper, 0, 0,
9718 "invokeInterruptCallback(fun)",
9719 "  Forcefully set the interrupt flag and invoke the interrupt handler. If a\n"
9720 "  callback function is set via |timeout| or |setInterruptCallback|, it will\n"
9721 "  be called. Before returning, fun is called with the return value of the\n"
9722 "  interrupt handler."),
9723 
9724     JS_FN_HELP("setInterruptCallback", SetInterruptCallback, 1, 0,
9725 "setInterruptCallback(func)",
9726 "  Sets func as the interrupt callback function.\n"
9727 "  Calling this function will replace any callback set by |timeout|.\n"
9728 "  If the callback returns a falsy value, the script is aborted.\n"),
9729 
9730     JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption, 2, 0,
9731 "setJitCompilerOption(<option>, <number>)",
9732 "  Set a compiler option indexed in JSCompileOption enum to a number.\n"),
9733 #ifdef DEBUG
9734     JS_FN_HELP("interruptRegexp", InterruptRegexp, 2, 0,
9735 "interruptRegexp(<regexp>, <string>)",
9736 "  Interrrupt the execution of regular expression.\n"),
9737 #endif
9738     JS_FN_HELP("enableLastWarning", EnableLastWarning, 0, 0,
9739 "enableLastWarning()",
9740 "  Enable storing the last warning."),
9741 
9742     JS_FN_HELP("disableLastWarning", DisableLastWarning, 0, 0,
9743 "disableLastWarning()",
9744 "  Disable storing the last warning."),
9745 
9746     JS_FN_HELP("getLastWarning", GetLastWarning, 0, 0,
9747 "getLastWarning()",
9748 "  Returns an object that represents the last warning."),
9749 
9750     JS_FN_HELP("clearLastWarning", ClearLastWarning, 0, 0,
9751 "clearLastWarning()",
9752 "  Clear the last warning."),
9753 
9754     JS_FN_HELP("elapsed", Elapsed, 0, 0,
9755 "elapsed()",
9756 "  Execution time elapsed for the current thread."),
9757 
9758     JS_FN_HELP("decompileFunction", DecompileFunction, 1, 0,
9759 "decompileFunction(func)",
9760 "  Decompile a function."),
9761 
9762     JS_FN_HELP("decompileThis", DecompileThisScript, 0, 0,
9763 "decompileThis()",
9764 "  Decompile the currently executing script."),
9765 
9766     JS_FN_HELP("valueToSource", ValueToSource, 1, 0,
9767 "valueToSource(value)",
9768 "  Format a value for inspection."),
9769 
9770     JS_FN_HELP("thisFilename", ThisFilename, 0, 0,
9771 "thisFilename()",
9772 "  Return the filename of the current script"),
9773 
9774     JS_FN_HELP("newGlobal", NewGlobal, 1, 0,
9775 "newGlobal([options])",
9776 "  Return a new global object/realm. The new global is created in the\n"
9777 "  'newGlobal' function object's compartment and zone, unless the\n"
9778 "  '--more-compartments' command-line flag was given, in which case new\n"
9779 "  globals get a fresh compartment and zone. If options is given, it may\n"
9780 "  have any of the following properties:\n"
9781 "      sameCompartmentAs: If an object, the global will be in the same\n"
9782 "         compartment and zone as the given object.\n"
9783 "      sameZoneAs: The global will be in a new compartment in the same zone\n"
9784 "         as the given object.\n"
9785 "      newCompartment: If true, the global will always be created in a new\n"
9786 "         compartment and zone.\n"
9787 "      invisibleToDebugger: If true, the global will be invisible to the\n"
9788 "         debugger (default false)\n"
9789 "      discardSource: If true, discard source after compiling a script\n"
9790 "         (default false).\n"
9791 "      useWindowProxy: the global will be created with a WindowProxy attached. In this\n"
9792 "          case, the WindowProxy will be returned.\n"
9793 "      immutablePrototype: whether the global's prototype is immutable.\n"
9794 "      principal: if present, its value converted to a number must be an\n"
9795 "         integer that fits in 32 bits; use that as the new realm's\n"
9796 "         principal. Shell principals are toys, meant only for testing; one\n"
9797 "         shell principal subsumes another if its set bits are a superset of\n"
9798 "         the other's. Thus, a principal of 0 subsumes nothing, while a\n"
9799 "         principals of ~0 subsumes all other principals. The absence of a\n"
9800 "         principal is treated as if its bits were 0xffff, for subsumption\n"
9801 "         purposes. If this property is omitted, supply no principal.\n"
9802 "      systemPrincipal: If true, use the shell's trusted principals for the\n"
9803 "         new realm. This creates a realm that's marked as a 'system' realm."),
9804 
9805     JS_FN_HELP("nukeCCW", NukeCCW, 1, 0,
9806 "nukeCCW(wrapper)",
9807 "  Nuke a CrossCompartmentWrapper, which turns it into a DeadProxyObject."),
9808 
9809     JS_FN_HELP("nukeAllCCWs", NukeAllCCWs, 0, 0,
9810 "nukeAllCCWs()",
9811 "  Like nukeCCW, but for all CrossCompartmentWrappers targeting the current realm."),
9812 
9813     JS_FN_HELP("recomputeWrappers", RecomputeWrappers, 2, 0,
9814 "recomputeWrappers([src, [target]])",
9815 "  Recompute all cross-compartment wrappers. src and target are both optional\n"
9816 "  and can be used to filter source or target compartments: the unwrapped\n"
9817 "  object's compartment is used as CompartmentFilter.\n"),
9818 
9819     JS_FN_HELP("dumpObjectWrappers", DumpObjectWrappers, 2, 0,
9820 "dumpObjectWrappers()",
9821 "  Print information about cross-compartment object wrappers.\n"),
9822 
9823     JS_FN_HELP("wrapWithProto", WrapWithProto, 2, 0,
9824 "wrapWithProto(obj)",
9825 "  Wrap an object into a noop wrapper with prototype semantics."),
9826 
9827     JS_FN_HELP("createExternalArrayBuffer", CreateExternalArrayBuffer, 1, 0,
9828 "createExternalArrayBuffer(size)",
9829 "  Create an array buffer that has external data of size."),
9830 
9831     JS_FN_HELP("createMappedArrayBuffer", CreateMappedArrayBuffer, 1, 0,
9832 "createMappedArrayBuffer(filename, [offset, [size]])",
9833 "  Create an array buffer that mmaps the given file."),
9834 
9835     JS_FN_HELP("addPromiseReactions", AddPromiseReactions, 3, 0,
9836 "addPromiseReactions(promise, onResolve, onReject)",
9837 "  Calls the JS::AddPromiseReactions JSAPI function with the given arguments."),
9838 
9839     JS_FN_HELP("ignoreUnhandledRejections", IgnoreUnhandledRejections, 0, 0,
9840 "ignoreUnhandledRejections()",
9841 "  By default, js shell tracks unhandled promise rejections and reports\n"
9842 "  them at the end of the exectuion.  If a testcase isn't interested\n"
9843 "  in those rejections, call this to stop tracking and reporting."),
9844 
9845     JS_FN_HELP("getMaxArgs", GetMaxArgs, 0, 0,
9846 "getMaxArgs()",
9847 "  Return the maximum number of supported args for a call."),
9848 
9849     JS_FN_HELP("createIsHTMLDDA", CreateIsHTMLDDA, 0, 0,
9850 "createIsHTMLDDA()",
9851 "  Return an object |obj| that \"looks like\" the |document.all| object in\n"
9852 "  browsers in certain ways: |typeof obj === \"undefined\"|, |obj == null|\n"
9853 "  and |obj == undefined| (vice versa for !=), |ToBoolean(obj) === false|,\n"
9854 "  and when called with no arguments or the single argument \"\" returns\n"
9855 "  null.  (Calling |obj| any other way crashes or throws an exception.)\n"
9856 "  This function implements the exact requirements of the $262.IsHTMLDDA\n"
9857 "  property in test262."),
9858 
9859     JS_FN_HELP("cacheEntry", CacheEntry, 1, 0,
9860 "cacheEntry(code)",
9861 "  Return a new opaque object which emulates a cache entry of a script.  This\n"
9862 "  object encapsulates the code and its cached content. The cache entry is filled\n"
9863 "  and read by the \"evaluate\" function by using it in place of the source, and\n"
9864 "  by setting \"saveBytecode\" and \"loadBytecode\" options."),
9865 
9866     JS_FN_HELP("streamCacheEntry", StreamCacheEntryObject::construct, 1, 0,
9867 "streamCacheEntry(buffer)",
9868 "  Create a shell-only object that holds wasm bytecode and can be streaming-\n"
9869 "  compiled and cached by WebAssembly.{compile,instantiate}Streaming(). On a\n"
9870 "  second compilation of the same cache entry, the cached code will be used."),
9871 
9872     JS_FN_HELP("printProfilerEvents", PrintProfilerEvents, 0, 0,
9873 "printProfilerEvents()",
9874 "  Register a callback with the profiler that prints javascript profiler events\n"
9875 "  to stderr.  Callback is only registered if profiling is enabled."),
9876 
9877     JS_FN_HELP("enableSingleStepProfiling", EnableSingleStepProfiling, 0, 0,
9878 "enableSingleStepProfiling()",
9879 "  This function will fail on platforms that don't support single-step profiling\n"
9880 "  (currently ARM and MIPS64 support it). When enabled, at every instruction a\n"
9881 "  backtrace will be recorded and stored in an array. Adjacent duplicate backtraces\n"
9882 "  are discarded."),
9883 
9884     JS_FN_HELP("disableSingleStepProfiling", DisableSingleStepProfiling, 0, 0,
9885 "disableSingleStepProfiling()",
9886 "  Return the array of backtraces recorded by enableSingleStepProfiling."),
9887 
9888     JS_FN_HELP("enableGeckoProfiling", EnableGeckoProfiling, 0, 0,
9889 "enableGeckoProfiling()",
9890 "  Enables Gecko Profiler instrumentation and corresponding assertions, with slow\n"
9891 "  assertions disabled.\n"),
9892 
9893     JS_FN_HELP("enableGeckoProfilingWithSlowAssertions", EnableGeckoProfilingWithSlowAssertions, 0, 0,
9894 "enableGeckoProfilingWithSlowAssertions()",
9895 "  Enables Gecko Profiler instrumentation and corresponding assertions, with slow\n"
9896 "  assertions enabled.\n"),
9897 
9898     JS_FN_HELP("disableGeckoProfiling", DisableGeckoProfiling, 0, 0,
9899 "disableGeckoProfiling()",
9900 "  Disables Gecko Profiler instrumentation"),
9901 
9902     JS_FN_HELP("isLatin1", IsLatin1, 1, 0,
9903 "isLatin1(s)",
9904 "  Return true iff the string's characters are stored as Latin1."),
9905 
9906     JS_FN_HELP("stackPointerInfo", StackPointerInfo, 0, 0,
9907 "stackPointerInfo()",
9908 "  Return an int32 value which corresponds to the offset of the latest stack\n"
9909 "  pointer, such that one can take the differences of 2 to estimate a frame-size."),
9910 
9911     JS_FN_HELP("entryPoints", EntryPoints, 1, 0,
9912 "entryPoints(params)",
9913 "Carry out some JSAPI operation as directed by |params|, and return an array of\n"
9914 "objects describing which JavaScript entry points were invoked as a result.\n"
9915 "|params| is an object whose properties indicate what operation to perform. Here\n"
9916 "are the recognized groups of properties:\n"
9917 "\n"
9918 "{ function }: Call the object |params.function| with no arguments.\n"
9919 "\n"
9920 "{ object, property }: Fetch the property named |params.property| of\n"
9921 "|params.object|.\n"
9922 "\n"
9923 "{ ToString }: Apply JS::ToString to |params.toString|.\n"
9924 "\n"
9925 "{ ToNumber }: Apply JS::ToNumber to |params.toNumber|.\n"
9926 "\n"
9927 "{ eval }: Apply JS::Evaluate to |params.eval|.\n"
9928 "\n"
9929 "The return value is an array of strings, with one element for each\n"
9930 "JavaScript invocation that occurred as a result of the given\n"
9931 "operation. Each element is the name of the function invoked, or the\n"
9932 "string 'eval:FILENAME' if the code was invoked by 'eval' or something\n"
9933 "similar.\n"),
9934 
9935     JS_FN_HELP("enqueueJob", EnqueueJob, 1, 0,
9936 "enqueueJob(fn)",
9937 "  Enqueue 'fn' on the shell's job queue."),
9938 
9939     JS_FN_HELP("globalOfFirstJobInQueue", GlobalOfFirstJobInQueue, 0, 0,
9940 "globalOfFirstJobInQueue()",
9941 "  Returns the global of the first item in the job queue. Throws an exception\n"
9942 "  if the queue is empty.\n"),
9943 
9944     JS_FN_HELP("drainJobQueue", DrainJobQueue, 0, 0,
9945 "drainJobQueue()",
9946 "Take jobs from the shell's job queue in FIFO order and run them until the\n"
9947 "queue is empty.\n"),
9948 
9949     JS_FN_HELP("setPromiseRejectionTrackerCallback", SetPromiseRejectionTrackerCallback, 1, 0,
9950 "setPromiseRejectionTrackerCallback()",
9951 "Sets the callback to be invoked whenever a Promise rejection is unhandled\n"
9952 "or a previously-unhandled rejection becomes handled."),
9953 
9954     JS_FN_HELP("dumpScopeChain", DumpScopeChain, 1, 0,
9955 "dumpScopeChain(obj)",
9956 "  Prints the scope chain of an interpreted function or a module."),
9957 
9958     JS_FN_HELP("grayRoot", EnsureGrayRoot, 0, 0,
9959 "grayRoot()",
9960 "  Create a gray root Array, if needed, for the current compartment, and\n"
9961 "  return it."),
9962 
9963     JS_FN_HELP("addMarkObservers", AddMarkObservers, 1, 0,
9964 "addMarkObservers(array_of_objects)",
9965 "  Register an array of objects whose mark bits will be tested by calls to\n"
9966 "  getMarks. The objects will be in calling compartment. Objects from\n"
9967 "  multiple compartments may be monitored by calling this function in\n"
9968 "  different compartments."),
9969 
9970     JS_FN_HELP("clearMarkObservers", ClearMarkObservers, 1, 0,
9971 "clearMarkObservers()",
9972 "  Clear out the list of objects whose mark bits will be tested.\n"),
9973 
9974     JS_FN_HELP("getMarks", GetMarks, 0, 0,
9975 "getMarks()",
9976 "  Return an array of strings representing the current state of the mark\n"
9977 "  bits ('gray' or 'black', or 'dead' if the object has been collected)\n"
9978 "  for the objects registered via addMarkObservers. Note that some of the\n"
9979 "  objects tested may be from different compartments than the one in which\n"
9980 "  this function runs."),
9981 
9982     JS_FN_HELP("bindToAsyncStack", BindToAsyncStack, 2, 0,
9983 "bindToAsyncStack(fn, { stack, cause, explicit })",
9984 "  Returns a new function that calls 'fn' with no arguments, passing\n"
9985 "  'undefined' as the 'this' value, and supplies an async stack for the\n"
9986 "  call as described by the second argument, an object with the following\n"
9987 "  properties (which are not optional, unless specified otherwise):\n"
9988 "\n"
9989 "  stack:    A SavedFrame object, like that returned by 'saveStack'. Stacks\n"
9990 "            captured during calls to the returned function capture this as\n"
9991 "            their async stack parent, accessible via a SavedFrame's\n"
9992 "            'asyncParent' property.\n"
9993 "\n"
9994 "  cause:    A string, supplied as the async cause on the top frame of\n"
9995 "            captured async stacks.\n"
9996 "\n"
9997 "  explicit: A boolean value, indicating whether the given 'stack' should\n"
9998 "            always supplant the returned function's true callers (true),\n"
9999 "            or only when there are no other JavaScript frames on the stack\n"
10000 "            below it (false). If omitted, this is treated as 'true'."),
10001 
10002 #ifdef JS_HAS_INTL_API
10003     JS_FN_HELP("addIntlExtras", AddIntlExtras, 1, 0,
10004 "addIntlExtras(obj)",
10005 "Adds various not-yet-standardized Intl functions as properties on the\n"
10006 "provided object (this should generally be Intl itself).  The added\n"
10007 "functions and their behavior are experimental: don't depend upon them\n"
10008 "unless you're willing to update your code if these experimental APIs change\n"
10009 "underneath you."),
10010 #endif // JS_HAS_INTL_API
10011 
10012 #ifndef __wasi__
10013     JS_FN_HELP("wasmCompileInSeparateProcess", WasmCompileInSeparateProcess, 1, 0,
10014 "wasmCompileInSeparateProcess(buffer)",
10015 "  Compile the given buffer in a separate process, serialize the resulting\n"
10016 "  wasm::Module into bytes, and deserialize those bytes in the current\n"
10017 "  process, returning the resulting WebAssembly.Module."),
10018 
10019     JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0,
10020 "wasmTextToBinary(str)",
10021 "  Translates the given text wasm module into its binary encoding."),
10022 
10023     JS_FN_HELP("wasmCodeOffsets", WasmCodeOffsets, 1, 0,
10024 "wasmCodeOffsets(binary)",
10025 "  Decodes the given wasm binary to find the offsets of every instruction in the"
10026 "  code section."),
10027 #endif // __wasi__
10028 
10029     JS_FN_HELP("transplantableObject", TransplantableObject, 0, 0,
10030 "transplantableObject([options])",
10031 "  Returns the pair {object, transplant}. |object| is an object which can be\n"
10032 "  transplanted into a new object when the |transplant| function, which must\n"
10033 "  be invoked with a global object, is called.\n"
10034 "  |object| is swapped with a cross-compartment wrapper if the global object\n"
10035 "  is in a different compartment.\n"
10036 "\n"
10037 "  If options is given, it may have any of the following properties:\n"
10038 "    proxy: Create a DOM Proxy object instead of a plain DOM object.\n"
10039 "    object: Don't create a new DOM object, but instead use the supplied\n"
10040 "            FakeDOMObject."),
10041 
10042     JS_FN_HELP("cpuNow", CpuNow, /* nargs= */ 0, /* flags = */ 0,
10043 "cpuNow()",
10044 " Returns the approximate processor time used by the process since an arbitrary epoch, in seconds.\n"
10045 " Only the difference between two calls to `cpuNow()` is meaningful."),
10046 
10047 #ifdef FUZZING_JS_FUZZILLI
10048     JS_FN_HELP("fuzzilli", Fuzzilli, 0, 0,
10049 "fuzzilli(operation, arg)",
10050 "  Exposes functionality used by the Fuzzilli JavaScript fuzzer."),
10051 #endif
10052 
10053     JS_FS_HELP_END
10054 };
10055 // clang-format on
10056 
10057 // clang-format off
10058 static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
10059     JS_FN_HELP("getSelfHostedValue", GetSelfHostedValue, 1, 0,
10060 "getSelfHostedValue()",
10061 "  Get a self-hosted value by its name. Note that these values don't get \n"
10062 "  cached, so repeatedly getting the same value creates multiple distinct clones."),
10063 
10064     JS_FN_HELP("line2pc", LineToPC, 0, 0,
10065 "line2pc([fun,] line)",
10066 "  Map line number to PC."),
10067 
10068     JS_FN_HELP("pc2line", PCToLine, 0, 0,
10069 "pc2line(fun[, pc])",
10070 "  Map PC to line number."),
10071 
10072     JS_INLINABLE_FN_HELP("assertFloat32", testingFunc_assertFloat32, 2, 0, TestAssertFloat32,
10073 "assertFloat32(value, isFloat32)",
10074 "  In IonMonkey only, asserts that value has (resp. hasn't) the MIRType::Float32 if isFloat32 is true (resp. false)."),
10075 
10076     JS_INLINABLE_FN_HELP("assertRecoveredOnBailout", testingFunc_assertRecoveredOnBailout, 2, 0,
10077 TestAssertRecoveredOnBailout,
10078 "assertRecoveredOnBailout(var)",
10079 "  In IonMonkey only, asserts that variable has RecoveredOnBailout flag."),
10080 
10081     JS_FN_HELP("withSourceHook", WithSourceHook, 1, 0,
10082 "withSourceHook(hook, fun)",
10083 "  Set this JS runtime's lazy source retrieval hook (that is, the hook\n"
10084 "  used to find sources compiled with |CompileOptions::LAZY_SOURCE|) to\n"
10085 "  |hook|; call |fun| with no arguments; and then restore the runtime's\n"
10086 "  original hook. Return or throw whatever |fun| did. |hook| gets\n"
10087 "  passed the requested code's URL, and should return a string.\n"
10088 "\n"
10089 "  Notes:\n"
10090 "\n"
10091 "  1) SpiderMonkey may assert if the returned code isn't close enough\n"
10092 "  to the script's real code, so this function is not fuzzer-safe.\n"
10093 "\n"
10094 "  2) The runtime can have only one source retrieval hook active at a\n"
10095 "  time. If |fun| is not careful, |hook| could be asked to retrieve the\n"
10096 "  source code for compilations that occurred long before it was set,\n"
10097 "  and that it knows nothing about. The reverse applies as well: the\n"
10098 "  original hook, that we reinstate after the call to |fun| completes,\n"
10099 "  might be asked for the source code of compilations that |fun|\n"
10100 "  performed, and which, presumably, only |hook| knows how to find.\n"),
10101 
10102     JS_FN_HELP("crash", Crash, 0, 0,
10103 "crash([message, [{disable_minidump:true}]])",
10104 "  Crashes the process with a MOZ_CRASH, optionally providing a message.\n"
10105 "  An options object may be passed as the second argument. If the key\n"
10106 "  'suppress_minidump' is set to true, then a minidump will not be\n"
10107 "  generated by the crash (which only has an effect if the breakpad\n"
10108 "  dumping library is loaded.)"),
10109 
10110 #ifndef __wasi__
10111     JS_FN_HELP("wasmLoop", WasmLoop, 2, 0,
10112 "wasmLoop(filename, imports)",
10113 "  Performs an AFL-style persistent loop reading data from the given file and passing it\n"
10114 "  to the 'wasmEval' function together with the specified imports object."),
10115 #endif // __wasi__
10116 
10117     JS_FN_HELP("setBufferStreamParams", SetBufferStreamParams, 2, 0,
10118 "setBufferStreamParams(delayMillis, chunkByteSize)",
10119 "  Set the delay time (between calls to StreamConsumer::consumeChunk) and chunk\n"
10120 "  size (in bytes)."),
10121 
10122 #ifdef JS_CACHEIR_SPEW
10123   JS_FN_HELP("cacheIRHealthReport", CacheIRHealthReport, 0, 0,
10124 "cacheIRHealthReport()",
10125 "  Show health rating of CacheIR stubs."),
10126 #endif
10127 
10128     JS_FS_HELP_END
10129 };
10130 // clang-format on
10131 
10132 // clang-format off
10133 static const JSFunctionSpecWithHelp performance_functions[] = {
10134     JS_FN_HELP("now", Now, 0, 0,
10135 "now()",
10136 "  Return the current time with sub-ms precision.\n"
10137 "  This function is an alias of the dateNow() function."),
10138     JS_FS_HELP_END
10139 };
10140 // clang-format on
10141 
10142 // clang-format off
10143 static const JSFunctionSpecWithHelp console_functions[] = {
10144     JS_FN_HELP("log", Print, 0, 0,
10145 "log([exp ...])",
10146 "  Evaluate and print expressions to stdout.\n"
10147 "  This function is an alias of the print() function."),
10148     JS_FS_HELP_END
10149 };
10150 // clang-format on
10151 
DefineConsole(JSContext * cx,HandleObject global)10152 bool DefineConsole(JSContext* cx, HandleObject global) {
10153   RootedObject obj(cx, JS_NewPlainObject(cx));
10154   return obj && JS_DefineFunctionsWithHelp(cx, obj, console_functions) &&
10155          JS_DefineProperty(cx, global, "console", obj, 0);
10156 }
10157 
10158 #ifdef MOZ_PROFILING
10159 #  define PROFILING_FUNCTION_COUNT 5
10160 #  ifdef MOZ_CALLGRIND
10161 #    define CALLGRIND_FUNCTION_COUNT 3
10162 #  else
10163 #    define CALLGRIND_FUNCTION_COUNT 0
10164 #  endif
10165 #  ifdef MOZ_VTUNE
10166 #    define VTUNE_FUNCTION_COUNT 4
10167 #  else
10168 #    define VTUNE_FUNCTION_COUNT 0
10169 #  endif
10170 #  define EXTERNAL_FUNCTION_COUNT \
10171     (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT)
10172 #else
10173 #  define EXTERNAL_FUNCTION_COUNT 0
10174 #endif
10175 
10176 #undef PROFILING_FUNCTION_COUNT
10177 #undef CALLGRIND_FUNCTION_COUNT
10178 #undef VTUNE_FUNCTION_COUNT
10179 #undef EXTERNAL_FUNCTION_COUNT
10180 
PrintHelpString(JSContext * cx,HandleValue v)10181 static bool PrintHelpString(JSContext* cx, HandleValue v) {
10182   RootedString str(cx, v.toString());
10183   MOZ_ASSERT(gOutFile->isOpen());
10184 
10185   UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
10186   if (!bytes) {
10187     return false;
10188   }
10189 
10190   fprintf(gOutFile->fp, "%s\n", bytes.get());
10191   return true;
10192 }
10193 
PrintHelp(JSContext * cx,HandleObject obj)10194 static bool PrintHelp(JSContext* cx, HandleObject obj) {
10195   RootedValue usage(cx);
10196   if (!JS_GetProperty(cx, obj, "usage", &usage)) {
10197     return false;
10198   }
10199   RootedValue help(cx);
10200   if (!JS_GetProperty(cx, obj, "help", &help)) {
10201     return false;
10202   }
10203 
10204   if (!usage.isString() || !help.isString()) {
10205     return true;
10206   }
10207 
10208   return PrintHelpString(cx, usage) && PrintHelpString(cx, help);
10209 }
10210 
PrintEnumeratedHelp(JSContext * cx,HandleObject obj,HandleObject pattern,bool brief)10211 static bool PrintEnumeratedHelp(JSContext* cx, HandleObject obj,
10212                                 HandleObject pattern, bool brief) {
10213   RootedIdVector idv(cx);
10214   if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &idv)) {
10215     return false;
10216   }
10217 
10218   Rooted<RegExpObject*> regex(cx);
10219   if (pattern) {
10220     regex = &UncheckedUnwrap(pattern)->as<RegExpObject>();
10221   }
10222 
10223   for (size_t i = 0; i < idv.length(); i++) {
10224     RootedValue v(cx);
10225     RootedId id(cx, idv[i]);
10226     if (!JS_GetPropertyById(cx, obj, id, &v)) {
10227       return false;
10228     }
10229     if (!v.isObject()) {
10230       continue;
10231     }
10232 
10233     RootedObject funcObj(cx, &v.toObject());
10234     if (regex) {
10235       // Only pay attention to objects with a 'help' property, which will
10236       // either be documented functions or interface objects.
10237       if (!JS_GetProperty(cx, funcObj, "help", &v)) {
10238         return false;
10239       }
10240       if (!v.isString()) {
10241         continue;
10242       }
10243 
10244       // For functions, match against the name. For interface objects,
10245       // match against the usage string.
10246       if (!JS_GetProperty(cx, funcObj, "name", &v)) {
10247         return false;
10248       }
10249       if (!v.isString()) {
10250         if (!JS_GetProperty(cx, funcObj, "usage", &v)) {
10251           return false;
10252         }
10253         if (!v.isString()) {
10254           continue;
10255         }
10256       }
10257 
10258       size_t ignored = 0;
10259       if (!JSString::ensureLinear(cx, v.toString())) {
10260         return false;
10261       }
10262       RootedLinearString input(cx, &v.toString()->asLinear());
10263       if (!ExecuteRegExpLegacy(cx, nullptr, regex, input, &ignored, true, &v)) {
10264         return false;
10265       }
10266       if (v.isNull()) {
10267         continue;
10268       }
10269     }
10270 
10271     if (!PrintHelp(cx, funcObj)) {
10272       return false;
10273     }
10274   }
10275 
10276   return true;
10277 }
10278 
Help(JSContext * cx,unsigned argc,Value * vp)10279 static bool Help(JSContext* cx, unsigned argc, Value* vp) {
10280   if (!gOutFile->isOpen()) {
10281     JS_ReportErrorASCII(cx, "output file is closed");
10282     return false;
10283   }
10284 
10285   CallArgs args = CallArgsFromVp(argc, vp);
10286   args.rval().setUndefined();
10287   RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
10288 
10289   // help() - display the version and dump out help for all functions on the
10290   // global.
10291   if (args.length() == 0) {
10292     fprintf(gOutFile->fp, "%s\n", JS_GetImplementationVersion());
10293 
10294     if (!PrintEnumeratedHelp(cx, global, nullptr, false)) {
10295       return false;
10296     }
10297     return true;
10298   }
10299 
10300   RootedValue v(cx);
10301 
10302   if (args[0].isPrimitive()) {
10303     // help("foo")
10304     JS_ReportErrorASCII(cx, "primitive arg");
10305     return false;
10306   }
10307 
10308   RootedObject obj(cx, &args[0].toObject());
10309   if (!obj) {
10310     return true;
10311   }
10312   bool isRegexp;
10313   if (!JS::ObjectIsRegExp(cx, obj, &isRegexp)) {
10314     return false;
10315   }
10316 
10317   if (isRegexp) {
10318     // help(/pattern/)
10319     return PrintEnumeratedHelp(cx, global, obj, false);
10320   }
10321 
10322   // help(function)
10323   // help(namespace_obj)
10324   return PrintHelp(cx, obj);
10325 }
10326 
10327 static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = {
10328 #define MSG_DEF(name, count, exception, format) \
10329   {#name, format, count, JSEXN_ERR},
10330 #include "jsshell.msg"
10331 #undef MSG_DEF
10332 };
10333 
my_GetErrorMessage(void * userRef,const unsigned errorNumber)10334 const JSErrorFormatString* js::shell::my_GetErrorMessage(
10335     void* userRef, const unsigned errorNumber) {
10336   if (errorNumber == 0 || errorNumber >= JSShellErr_Limit) {
10337     return nullptr;
10338   }
10339 
10340   return &jsShell_ErrorFormatString[errorNumber];
10341 }
10342 
CreateLastWarningObject(JSContext * cx,JSErrorReport * report)10343 static bool CreateLastWarningObject(JSContext* cx, JSErrorReport* report) {
10344   RootedObject warningObj(cx, JS_NewObject(cx, nullptr));
10345   if (!warningObj) {
10346     return false;
10347   }
10348 
10349   if (!CopyErrorReportToObject(cx, report, warningObj)) {
10350     return false;
10351   }
10352 
10353   GetShellContext(cx)->lastWarning.setObject(*warningObj);
10354   return true;
10355 }
10356 
ErrorFilePointer()10357 static FILE* ErrorFilePointer() {
10358   if (gErrFile->isOpen()) {
10359     return gErrFile->fp;
10360   }
10361 
10362   fprintf(stderr, "error file is closed; falling back to stderr\n");
10363   return stderr;
10364 }
10365 
PrintStackTrace(JSContext * cx,HandleObject stackObj)10366 bool shell::PrintStackTrace(JSContext* cx, HandleObject stackObj) {
10367   if (!stackObj || !stackObj->is<SavedFrame>()) {
10368     return true;
10369   }
10370 
10371   JSPrincipals* principals = stackObj->nonCCWRealm()->principals();
10372   RootedString stackStr(cx);
10373   if (!BuildStackString(cx, principals, stackObj, &stackStr, 2)) {
10374     return false;
10375   }
10376 
10377   UniqueChars stack = JS_EncodeStringToUTF8(cx, stackStr);
10378   if (!stack) {
10379     return false;
10380   }
10381 
10382   FILE* fp = ErrorFilePointer();
10383   fputs("Stack:\n", fp);
10384   fputs(stack.get(), fp);
10385 
10386   return true;
10387 }
10388 
~AutoReportException()10389 js::shell::AutoReportException::~AutoReportException() {
10390   if (!JS_IsExceptionPending(cx)) {
10391     return;
10392   }
10393 
10394   auto printError = [](JSContext* cx, auto& report, const auto& exnStack,
10395                        const char* prefix = nullptr) {
10396     if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
10397       fprintf(stderr, "out of memory initializing JS::ErrorReportBuilder\n");
10398       fflush(stderr);
10399       JS_ClearPendingException(cx);
10400       return false;
10401     }
10402 
10403     MOZ_ASSERT(!report.report()->isWarning());
10404 
10405     FILE* fp = ErrorFilePointer();
10406     if (prefix) {
10407       fputs(prefix, fp);
10408     }
10409     JS::PrintError(fp, report, reportWarnings);
10410     JS_ClearPendingException(cx);
10411 
10412     if (!PrintStackTrace(cx, exnStack.stack())) {
10413       fputs("(Unable to print stack trace)\n", fp);
10414       JS_ClearPendingException(cx);
10415     }
10416 
10417     return true;
10418   };
10419 
10420   // Get exception object and stack before printing and clearing exception.
10421   JS::ExceptionStack exnStack(cx);
10422   if (!JS::StealPendingExceptionStack(cx, &exnStack)) {
10423     fprintf(stderr, "out of memory while stealing exception\n");
10424     fflush(stderr);
10425     JS_ClearPendingException(cx);
10426     return;
10427   }
10428 
10429   ShellContext* sc = GetShellContext(cx);
10430   JS::ErrorReportBuilder report(cx);
10431   if (!printError(cx, report, exnStack)) {
10432     // Return if we couldn't initialize the error report.
10433     return;
10434   }
10435 
10436   // Print the error's cause, if available.
10437   if (exnStack.exception().isObject()) {
10438     JSObject* exception = &exnStack.exception().toObject();
10439     if (exception->is<ErrorObject>()) {
10440       auto* error = &exception->as<ErrorObject>();
10441       if (auto maybeCause = error->getCause()) {
10442         RootedValue cause(cx, maybeCause.value());
10443 
10444         RootedObject causeStack(cx);
10445         if (cause.isObject()) {
10446           RootedObject causeObj(cx, &cause.toObject());
10447           causeStack = JS::ExceptionStackOrNull(causeObj);
10448         }
10449 
10450         JS::ExceptionStack exnStack(cx, cause, causeStack);
10451         JS::ErrorReportBuilder report(cx);
10452         printError(cx, report, exnStack, "Caused by: ");
10453       }
10454     }
10455   }
10456 
10457 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
10458   // Don't quit the shell if an unhandled exception is reported during OOM
10459   // testing.
10460   if (cx->runningOOMTest) {
10461     return;
10462   }
10463 #endif
10464 
10465   if (report.report()->errorNumber == JSMSG_OUT_OF_MEMORY) {
10466     sc->exitCode = EXITCODE_OUT_OF_MEMORY;
10467   } else {
10468     sc->exitCode = EXITCODE_RUNTIME_ERROR;
10469   }
10470 }
10471 
WarningReporter(JSContext * cx,JSErrorReport * report)10472 void js::shell::WarningReporter(JSContext* cx, JSErrorReport* report) {
10473   ShellContext* sc = GetShellContext(cx);
10474   FILE* fp = ErrorFilePointer();
10475 
10476   MOZ_ASSERT(report->isWarning());
10477 
10478   if (sc->lastWarningEnabled) {
10479     JS::AutoSaveExceptionState savedExc(cx);
10480     if (!CreateLastWarningObject(cx, report)) {
10481       fputs("Unhandled error happened while creating last warning object.\n",
10482             fp);
10483       fflush(fp);
10484     }
10485     savedExc.restore();
10486   }
10487 
10488   // Print the warning.
10489   JS::PrintError(fp, report, reportWarnings);
10490 }
10491 
global_enumerate(JSContext * cx,JS::HandleObject obj,JS::MutableHandleIdVector properties,bool enumerableOnly)10492 static bool global_enumerate(JSContext* cx, JS::HandleObject obj,
10493                              JS::MutableHandleIdVector properties,
10494                              bool enumerableOnly) {
10495 #ifdef LAZY_STANDARD_CLASSES
10496   return JS_NewEnumerateStandardClasses(cx, obj, properties, enumerableOnly);
10497 #else
10498   return true;
10499 #endif
10500 }
10501 
global_resolve(JSContext * cx,HandleObject obj,HandleId id,bool * resolvedp)10502 static bool global_resolve(JSContext* cx, HandleObject obj, HandleId id,
10503                            bool* resolvedp) {
10504 #ifdef LAZY_STANDARD_CLASSES
10505   if (!JS_ResolveStandardClass(cx, obj, id, resolvedp)) {
10506     return false;
10507   }
10508 #endif
10509   return true;
10510 }
10511 
global_mayResolve(const JSAtomState & names,jsid id,JSObject * maybeObj)10512 static bool global_mayResolve(const JSAtomState& names, jsid id,
10513                               JSObject* maybeObj) {
10514   return JS_MayResolveStandardClass(names, id, maybeObj);
10515 }
10516 
10517 static const JSClassOps global_classOps = {
10518     nullptr,                   // addProperty
10519     nullptr,                   // delProperty
10520     nullptr,                   // enumerate
10521     global_enumerate,          // newEnumerate
10522     global_resolve,            // resolve
10523     global_mayResolve,         // mayResolve
10524     nullptr,                   // finalize
10525     nullptr,                   // call
10526     nullptr,                   // hasInstance
10527     nullptr,                   // construct
10528     JS_GlobalObjectTraceHook,  // trace
10529 };
10530 
10531 static constexpr uint32_t DOM_PROTOTYPE_SLOT = JSCLASS_GLOBAL_SLOT_COUNT;
10532 static constexpr uint32_t DOM_GLOBAL_SLOTS = 1;
10533 
10534 static const JSClass global_class = {
10535     "global",
10536     JSCLASS_GLOBAL_FLAGS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS),
10537     &global_classOps};
10538 
10539 /*
10540  * Define a FakeDOMObject constructor. It returns an object with a getter,
10541  * setter and method with attached JitInfo. This object can be used to test
10542  * IonMonkey DOM optimizations in the shell.
10543  */
10544 
10545 /* Fow now just use to a constant we can check. */
10546 static const void* DOM_PRIVATE_VALUE = (void*)0x1234;
10547 
10548 static bool dom_genericGetter(JSContext* cx, unsigned argc, JS::Value* vp);
10549 
10550 static bool dom_genericSetter(JSContext* cx, unsigned argc, JS::Value* vp);
10551 
10552 static bool dom_genericMethod(JSContext* cx, unsigned argc, JS::Value* vp);
10553 
dom_get_x(JSContext * cx,HandleObject obj,void * self,JSJitGetterCallArgs args)10554 static bool dom_get_x(JSContext* cx, HandleObject obj, void* self,
10555                       JSJitGetterCallArgs args) {
10556   MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
10557   MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
10558   args.rval().set(JS_NumberValue(double(3.14)));
10559   return true;
10560 }
10561 
dom_set_x(JSContext * cx,HandleObject obj,void * self,JSJitSetterCallArgs args)10562 static bool dom_set_x(JSContext* cx, HandleObject obj, void* self,
10563                       JSJitSetterCallArgs args) {
10564   MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
10565   MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
10566   return true;
10567 }
10568 
dom_get_slot(JSContext * cx,HandleObject obj,void * self,JSJitGetterCallArgs args)10569 static bool dom_get_slot(JSContext* cx, HandleObject obj, void* self,
10570                          JSJitGetterCallArgs args) {
10571   MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
10572   MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
10573 
10574   Value v = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT2);
10575   MOZ_ASSERT(v.toInt32() == 42);
10576   args.rval().set(v);
10577   return true;
10578 }
10579 
dom_get_global(JSContext * cx,HandleObject obj,void * self,JSJitGetterCallArgs args)10580 static bool dom_get_global(JSContext* cx, HandleObject obj, void* self,
10581                            JSJitGetterCallArgs args) {
10582   MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
10583   MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
10584 
10585   // Return the current global (instead of obj->global()) to test cx->realm
10586   // switching in the JIT.
10587   args.rval().setObject(*ToWindowProxyIfWindow(cx->global()));
10588 
10589   return true;
10590 }
10591 
dom_set_global(JSContext * cx,HandleObject obj,void * self,JSJitSetterCallArgs args)10592 static bool dom_set_global(JSContext* cx, HandleObject obj, void* self,
10593                            JSJitSetterCallArgs args) {
10594   MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
10595   MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
10596 
10597   // Throw an exception if our argument is not the current global. This lets
10598   // us test cx->realm switching.
10599   if (!args[0].isObject() ||
10600       ToWindowIfWindowProxy(&args[0].toObject()) != cx->global()) {
10601     JS_ReportErrorASCII(cx, "Setter not called with matching global argument");
10602     return false;
10603   }
10604 
10605   return true;
10606 }
10607 
dom_doFoo(JSContext * cx,HandleObject obj,void * self,const JSJitMethodCallArgs & args)10608 static bool dom_doFoo(JSContext* cx, HandleObject obj, void* self,
10609                       const JSJitMethodCallArgs& args) {
10610   MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
10611   MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
10612   MOZ_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm());
10613 
10614   /* Just return args.length(). */
10615   args.rval().setInt32(args.length());
10616   return true;
10617 }
10618 
10619 static const JSJitInfo dom_x_getterinfo = {
10620     {(JSJitGetterOp)dom_get_x},
10621     {0}, /* protoID */
10622     {0}, /* depth */
10623     JSJitInfo::Getter,
10624     JSJitInfo::AliasNone, /* aliasSet */
10625     JSVAL_TYPE_UNKNOWN,   /* returnType */
10626     true,                 /* isInfallible. False in setters. */
10627     true,                 /* isMovable */
10628     true,                 /* isEliminatable */
10629     false,                /* isAlwaysInSlot */
10630     false,                /* isLazilyCachedInSlot */
10631     false,                /* isTypedMethod */
10632     0                     /* slotIndex */
10633 };
10634 
10635 static const JSJitInfo dom_x_setterinfo = {
10636     {(JSJitGetterOp)dom_set_x},
10637     {0}, /* protoID */
10638     {0}, /* depth */
10639     JSJitInfo::Setter,
10640     JSJitInfo::AliasEverything, /* aliasSet */
10641     JSVAL_TYPE_UNKNOWN,         /* returnType */
10642     false,                      /* isInfallible. False in setters. */
10643     false,                      /* isMovable. */
10644     false,                      /* isEliminatable. */
10645     false,                      /* isAlwaysInSlot */
10646     false,                      /* isLazilyCachedInSlot */
10647     false,                      /* isTypedMethod */
10648     0                           /* slotIndex */
10649 };
10650 
10651 static const JSJitInfo dom_slot_getterinfo = {
10652     {(JSJitGetterOp)dom_get_slot},
10653     {0}, /* protoID */
10654     {0}, /* depth */
10655     JSJitInfo::Getter,
10656     JSJitInfo::AliasNone, /* aliasSet */
10657     JSVAL_TYPE_INT32,     /* returnType */
10658     false,                /* isInfallible. False in setters. */
10659     true,                 /* isMovable */
10660     true,                 /* isEliminatable */
10661     true,                 /* isAlwaysInSlot */
10662     false,                /* isLazilyCachedInSlot */
10663     false,                /* isTypedMethod */
10664     DOM_OBJECT_SLOT2      /* slotIndex */
10665 };
10666 
10667 // Note: this getter uses AliasEverything and is marked as fallible and
10668 // non-movable (1) to prevent Ion from getting too clever optimizing it and
10669 // (2) it's nice to have a few different kinds of getters in the shell.
10670 static const JSJitInfo dom_global_getterinfo = {
10671     {(JSJitGetterOp)dom_get_global},
10672     {0}, /* protoID */
10673     {0}, /* depth */
10674     JSJitInfo::Getter,
10675     JSJitInfo::AliasEverything, /* aliasSet */
10676     JSVAL_TYPE_OBJECT,          /* returnType */
10677     false,                      /* isInfallible. False in setters. */
10678     false,                      /* isMovable */
10679     false,                      /* isEliminatable */
10680     false,                      /* isAlwaysInSlot */
10681     false,                      /* isLazilyCachedInSlot */
10682     false,                      /* isTypedMethod */
10683     0                           /* slotIndex */
10684 };
10685 
10686 static const JSJitInfo dom_global_setterinfo = {
10687     {(JSJitGetterOp)dom_set_global},
10688     {0}, /* protoID */
10689     {0}, /* depth */
10690     JSJitInfo::Setter,
10691     JSJitInfo::AliasEverything, /* aliasSet */
10692     JSVAL_TYPE_UNKNOWN,         /* returnType */
10693     false,                      /* isInfallible. False in setters. */
10694     false,                      /* isMovable. */
10695     false,                      /* isEliminatable. */
10696     false,                      /* isAlwaysInSlot */
10697     false,                      /* isLazilyCachedInSlot */
10698     false,                      /* isTypedMethod */
10699     0                           /* slotIndex */
10700 };
10701 
10702 static const JSJitInfo doFoo_methodinfo = {
10703     {(JSJitGetterOp)dom_doFoo},
10704     {0}, /* protoID */
10705     {0}, /* depth */
10706     JSJitInfo::Method,
10707     JSJitInfo::AliasEverything, /* aliasSet */
10708     JSVAL_TYPE_UNKNOWN,         /* returnType */
10709     false,                      /* isInfallible. False in setters. */
10710     false,                      /* isMovable */
10711     false,                      /* isEliminatable */
10712     false,                      /* isAlwaysInSlot */
10713     false,                      /* isLazilyCachedInSlot */
10714     false,                      /* isTypedMethod */
10715     0                           /* slotIndex */
10716 };
10717 
10718 static const JSPropertySpec dom_props[] = {
10719     JSPropertySpec::nativeAccessors("x", JSPROP_ENUMERATE, dom_genericGetter,
10720                                     &dom_x_getterinfo, dom_genericSetter,
10721                                     &dom_x_setterinfo),
10722     JSPropertySpec::nativeAccessors("slot", JSPROP_ENUMERATE, dom_genericGetter,
10723                                     &dom_slot_getterinfo),
10724     JSPropertySpec::nativeAccessors("global", JSPROP_ENUMERATE,
10725                                     dom_genericGetter, &dom_global_getterinfo,
10726                                     dom_genericSetter, &dom_global_setterinfo),
10727     JS_PS_END};
10728 
10729 static const JSFunctionSpec dom_methods[] = {
10730     JS_FNINFO("doFoo", dom_genericMethod, &doFoo_methodinfo, 3,
10731               JSPROP_ENUMERATE),
10732     JS_FS_END};
10733 
10734 static const JSClass dom_class = {
10735     "FakeDOMObject", JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2)};
10736 
GetDomClass()10737 static const JSClass* GetDomClass() { return &dom_class; }
10738 
dom_genericGetter(JSContext * cx,unsigned argc,JS::Value * vp)10739 static bool dom_genericGetter(JSContext* cx, unsigned argc, JS::Value* vp) {
10740   CallArgs args = CallArgsFromVp(argc, vp);
10741 
10742   if (!args.thisv().isObject()) {
10743     args.rval().setUndefined();
10744     return true;
10745   }
10746 
10747   RootedObject obj(cx, &args.thisv().toObject());
10748   if (JS::GetClass(obj) != &dom_class) {
10749     args.rval().set(UndefinedValue());
10750     return true;
10751   }
10752 
10753   JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
10754 
10755   const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
10756   MOZ_ASSERT(info->type() == JSJitInfo::Getter);
10757   JSJitGetterOp getter = info->getter;
10758   return getter(cx, obj, val.toPrivate(), JSJitGetterCallArgs(args));
10759 }
10760 
dom_genericSetter(JSContext * cx,unsigned argc,JS::Value * vp)10761 static bool dom_genericSetter(JSContext* cx, unsigned argc, JS::Value* vp) {
10762   CallArgs args = CallArgsFromVp(argc, vp);
10763 
10764   if (args.length() < 1 || !args.thisv().isObject()) {
10765     args.rval().setUndefined();
10766     return true;
10767   }
10768 
10769   RootedObject obj(cx, &args.thisv().toObject());
10770   if (JS::GetClass(obj) != &dom_class) {
10771     args.rval().set(UndefinedValue());
10772     return true;
10773   }
10774 
10775   JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
10776 
10777   const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
10778   MOZ_ASSERT(info->type() == JSJitInfo::Setter);
10779   JSJitSetterOp setter = info->setter;
10780   if (!setter(cx, obj, val.toPrivate(), JSJitSetterCallArgs(args))) {
10781     return false;
10782   }
10783   args.rval().set(UndefinedValue());
10784   return true;
10785 }
10786 
dom_genericMethod(JSContext * cx,unsigned argc,JS::Value * vp)10787 static bool dom_genericMethod(JSContext* cx, unsigned argc, JS::Value* vp) {
10788   CallArgs args = CallArgsFromVp(argc, vp);
10789 
10790   if (!args.thisv().isObject()) {
10791     args.rval().setUndefined();
10792     return true;
10793   }
10794 
10795   RootedObject obj(cx, &args.thisv().toObject());
10796   if (JS::GetClass(obj) != &dom_class) {
10797     args.rval().set(UndefinedValue());
10798     return true;
10799   }
10800 
10801   JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
10802 
10803   const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
10804   MOZ_ASSERT(info->type() == JSJitInfo::Method);
10805   JSJitMethodOp method = info->method;
10806   return method(cx, obj, val.toPrivate(), JSJitMethodCallArgs(args));
10807 }
10808 
InitDOMObject(HandleObject obj)10809 static void InitDOMObject(HandleObject obj) {
10810   JS::SetReservedSlot(obj, DOM_OBJECT_SLOT,
10811                       PrivateValue(const_cast<void*>(DOM_PRIVATE_VALUE)));
10812   JS::SetReservedSlot(obj, DOM_OBJECT_SLOT2, Int32Value(42));
10813 }
10814 
GetDOMPrototype(JSContext * cx,JSObject * global)10815 static JSObject* GetDOMPrototype(JSContext* cx, JSObject* global) {
10816   MOZ_ASSERT(JS_IsGlobalObject(global));
10817   if (JS::GetClass(global) != &global_class) {
10818     JS_ReportErrorASCII(cx, "Can't get FakeDOMObject prototype in sandbox");
10819     return nullptr;
10820   }
10821 
10822   const JS::Value& slot = JS::GetReservedSlot(global, DOM_PROTOTYPE_SLOT);
10823   MOZ_ASSERT(slot.isObject());
10824   return &slot.toObject();
10825 }
10826 
dom_constructor(JSContext * cx,unsigned argc,JS::Value * vp)10827 static bool dom_constructor(JSContext* cx, unsigned argc, JS::Value* vp) {
10828   CallArgs args = CallArgsFromVp(argc, vp);
10829 
10830   RootedObject callee(cx, &args.callee());
10831   RootedValue protov(cx);
10832   if (!GetProperty(cx, callee, callee, cx->names().prototype, &protov)) {
10833     return false;
10834   }
10835 
10836   if (!protov.isObject()) {
10837     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_PROTOTYPE,
10838                               "FakeDOMObject");
10839     return false;
10840   }
10841 
10842   RootedObject proto(cx, &protov.toObject());
10843   RootedObject domObj(cx, JS_NewObjectWithGivenProto(cx, &dom_class, proto));
10844   if (!domObj) {
10845     return false;
10846   }
10847 
10848   InitDOMObject(domObj);
10849 
10850   args.rval().setObject(*domObj);
10851   return true;
10852 }
10853 
InstanceClassHasProtoAtDepth(const JSClass * clasp,uint32_t protoID,uint32_t depth)10854 static bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID,
10855                                          uint32_t depth) {
10856   // Only the (fake) DOM object supports any JIT optimizations.
10857   return clasp == GetDomClass();
10858 }
10859 
ShellBuildId(JS::BuildIdCharVector * buildId)10860 static bool ShellBuildId(JS::BuildIdCharVector* buildId) {
10861   // The browser embeds the date into the buildid and the buildid is embedded
10862   // in the binary, so every 'make' necessarily builds a new firefox binary.
10863   // Fortunately, the actual firefox executable is tiny -- all the code is in
10864   // libxul.so and other shared modules -- so this isn't a big deal. Not so
10865   // for the statically-linked JS shell. To avoid recompiling js.cpp and
10866   // re-linking 'js' on every 'make', we use a constant buildid and rely on
10867   // the shell user to manually clear any caches between cache-breaking updates.
10868   const char buildid[] = "JS-shell";
10869   return buildId->append(buildid, sizeof(buildid));
10870 }
10871 
TimesAccessed(JSContext * cx,unsigned argc,Value * vp)10872 static bool TimesAccessed(JSContext* cx, unsigned argc, Value* vp) {
10873   static int32_t accessed = 0;
10874   CallArgs args = CallArgsFromVp(argc, vp);
10875   args.rval().setInt32(++accessed);
10876   return true;
10877 }
10878 
10879 static const JSPropertySpec TestingProperties[] = {
10880     JS_PSG("timesAccessed", TimesAccessed, 0), JS_PS_END};
10881 
NewGlobalObject(JSContext * cx,JS::RealmOptions & options,JSPrincipals * principals,ShellGlobalKind kind,bool immutablePrototype)10882 static JSObject* NewGlobalObject(JSContext* cx, JS::RealmOptions& options,
10883                                  JSPrincipals* principals, ShellGlobalKind kind,
10884                                  bool immutablePrototype) {
10885   RootedObject glob(cx,
10886                     JS_NewGlobalObject(cx, &global_class, principals,
10887                                        JS::DontFireOnNewGlobalHook, options));
10888   if (!glob) {
10889     return nullptr;
10890   }
10891 
10892   {
10893     JSAutoRealm ar(cx, glob);
10894 
10895     if (kind == ShellGlobalKind::WindowProxy) {
10896       RootedObject proxy(cx, NewShellWindowProxy(cx, glob));
10897       if (!proxy) {
10898         return nullptr;
10899       }
10900       js::SetWindowProxy(cx, glob, proxy);
10901     }
10902 
10903 #ifndef LAZY_STANDARD_CLASSES
10904     if (!JS::InitRealmStandardClasses(cx)) {
10905       return nullptr;
10906     }
10907 #endif
10908 
10909     if (immutablePrototype) {
10910       bool succeeded;
10911       if (!JS_SetImmutablePrototype(cx, glob, &succeeded)) {
10912         return nullptr;
10913       }
10914       MOZ_ASSERT(succeeded,
10915                  "a fresh, unexposed global object is always capable of "
10916                  "having its [[Prototype]] be immutable");
10917     }
10918 
10919 #ifdef JS_HAS_CTYPES
10920     if (!fuzzingSafe && !JS::InitCTypesClass(cx, glob)) {
10921       return nullptr;
10922     }
10923 #endif
10924     if (!JS_InitReflectParse(cx, glob)) {
10925       return nullptr;
10926     }
10927     if (!JS_DefineDebuggerObject(cx, glob)) {
10928       return nullptr;
10929     }
10930     if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) ||
10931         !JS_DefineProfilingFunctions(cx, glob)) {
10932       return nullptr;
10933     }
10934     if (!js::DefineTestingFunctions(cx, glob, fuzzingSafe,
10935                                     disableOOMFunctions)) {
10936       return nullptr;
10937     }
10938     if (!JS_DefineProperties(cx, glob, TestingProperties)) {
10939       return nullptr;
10940     }
10941 
10942     if (!fuzzingSafe) {
10943       if (!JS_DefineFunctionsWithHelp(cx, glob, fuzzing_unsafe_functions)) {
10944         return nullptr;
10945       }
10946       if (!DefineConsole(cx, glob)) {
10947         return nullptr;
10948       }
10949     }
10950 
10951     if (!DefineOS(cx, glob, fuzzingSafe, &gOutFile, &gErrFile)) {
10952       return nullptr;
10953     }
10954 
10955     RootedObject performanceObj(cx, JS_NewObject(cx, nullptr));
10956     if (!performanceObj) {
10957       return nullptr;
10958     }
10959     if (!JS_DefineFunctionsWithHelp(cx, performanceObj,
10960                                     performance_functions)) {
10961       return nullptr;
10962     }
10963     RootedObject mozMemoryObj(cx, JS_NewObject(cx, nullptr));
10964     if (!mozMemoryObj) {
10965       return nullptr;
10966     }
10967     RootedObject gcObj(cx, gc::NewMemoryInfoObject(cx));
10968     if (!gcObj) {
10969       return nullptr;
10970     }
10971     if (!JS_DefineProperty(cx, glob, "performance", performanceObj,
10972                            JSPROP_ENUMERATE)) {
10973       return nullptr;
10974     }
10975     if (!JS_DefineProperty(cx, performanceObj, "mozMemory", mozMemoryObj,
10976                            JSPROP_ENUMERATE)) {
10977       return nullptr;
10978     }
10979     if (!JS_DefineProperty(cx, mozMemoryObj, "gc", gcObj, JSPROP_ENUMERATE)) {
10980       return nullptr;
10981     }
10982 
10983     /* Initialize FakeDOMObject. */
10984     static const js::DOMCallbacks DOMcallbacks = {InstanceClassHasProtoAtDepth};
10985     SetDOMCallbacks(cx, &DOMcallbacks);
10986 
10987     RootedObject domProto(
10988         cx, JS_InitClass(cx, glob, nullptr, &dom_class, dom_constructor, 0,
10989                          dom_props, dom_methods, nullptr, nullptr));
10990     if (!domProto) {
10991       return nullptr;
10992     }
10993 
10994     // FakeDOMObject.prototype is the only DOM object which needs to retrieved
10995     // in the shell; store it directly instead of creating a separate layer
10996     // (ProtoAndIfaceCache) as done in the browser.
10997     JS::SetReservedSlot(glob, DOM_PROTOTYPE_SLOT, ObjectValue(*domProto));
10998 
10999     /* Initialize FakeDOMObject.prototype */
11000     InitDOMObject(domProto);
11001 
11002     if (!DefineToStringTag(cx, glob, cx->names().global)) {
11003       return nullptr;
11004     }
11005 
11006     JS_FireOnNewGlobalObject(cx, glob);
11007   }
11008 
11009   return glob;
11010 }
11011 
BindScriptArgs(JSContext * cx,OptionParser * op)11012 static bool BindScriptArgs(JSContext* cx, OptionParser* op) {
11013   AutoReportException are(cx);
11014 
11015   MultiStringRange msr = op->getMultiStringArg("scriptArgs");
11016   RootedObject scriptArgs(cx);
11017   scriptArgs = JS::NewArrayObject(cx, 0);
11018   if (!scriptArgs) {
11019     return false;
11020   }
11021 
11022   if (!JS_DefineProperty(cx, cx->global(), "scriptArgs", scriptArgs, 0)) {
11023     return false;
11024   }
11025 
11026   for (size_t i = 0; !msr.empty(); msr.popFront(), ++i) {
11027     const char* scriptArg = msr.front();
11028     JS::RootedString str(cx, JS_NewStringCopyZ(cx, scriptArg));
11029     if (!str || !JS_DefineElement(cx, scriptArgs, i, str, JSPROP_ENUMERATE)) {
11030       return false;
11031     }
11032   }
11033 
11034   const char* scriptPath = op->getStringArg("script");
11035   RootedValue scriptPathValue(cx);
11036   if (scriptPath) {
11037     RootedString scriptPathString(cx, JS_NewStringCopyZ(cx, scriptPath));
11038     if (!scriptPathString) {
11039       return false;
11040     }
11041     scriptPathValue = StringValue(scriptPathString);
11042   } else {
11043     scriptPathValue = UndefinedValue();
11044   }
11045 
11046   if (!JS_DefineProperty(cx, cx->global(), "scriptPath", scriptPathValue, 0)) {
11047     return false;
11048   }
11049 
11050   return true;
11051 }
11052 
OptionFailure(const char * option,const char * str)11053 static bool OptionFailure(const char* option, const char* str) {
11054   fprintf(stderr, "Unrecognized option for %s: %s\n", option, str);
11055   return false;
11056 }
11057 
ProcessArgs(JSContext * cx,OptionParser * op)11058 [[nodiscard]] static bool ProcessArgs(JSContext* cx, OptionParser* op) {
11059   ShellContext* sc = GetShellContext(cx);
11060 
11061 #ifdef JS_ENABLE_SMOOSH
11062   if (op->getBoolOption("smoosh")) {
11063     JS::ContextOptionsRef(cx).setTrySmoosh(true);
11064     js::frontend::InitSmoosh();
11065   }
11066 
11067   if (const char* filename = op->getStringOption("not-implemented-watchfile")) {
11068     FILE* out = fopen(filename, "a");
11069     MOZ_RELEASE_ASSERT(out);
11070     setbuf(out, nullptr);  // Make unbuffered
11071     cx->runtime()->parserWatcherFile.init(out);
11072     JS::ContextOptionsRef(cx).setTrackNotImplemented(true);
11073   }
11074 #endif
11075 
11076   /* |scriptArgs| gets bound on the global before any code is run. */
11077   if (!BindScriptArgs(cx, op)) {
11078     return false;
11079   }
11080 
11081   RootedString moduleLoadPath(cx);
11082   if (const char* option = op->getStringOption("module-load-path")) {
11083     RootedString jspath(cx, JS_NewStringCopyZ(cx, option));
11084     if (!jspath) {
11085       return false;
11086     }
11087 
11088     moduleLoadPath = js::shell::ResolvePath(cx, jspath, RootRelative);
11089   } else {
11090     UniqueChars cwd = js::shell::GetCWD();
11091     moduleLoadPath = JS_NewStringCopyZ(cx, cwd.get());
11092   }
11093 
11094   if (!moduleLoadPath) {
11095     return false;
11096   }
11097 
11098   processWideModuleLoadPath = JS_EncodeStringToUTF8(cx, moduleLoadPath);
11099   if (!processWideModuleLoadPath) {
11100     MOZ_ASSERT(cx->isExceptionPending());
11101     return false;
11102   }
11103 
11104   sc->moduleLoader = js::MakeUnique<ModuleLoader>();
11105   if (!sc->moduleLoader || !sc->moduleLoader->init(cx, moduleLoadPath)) {
11106     return false;
11107   }
11108 
11109   MultiStringRange filePaths = op->getMultiStringOption('f');
11110   MultiStringRange utf16FilePaths = op->getMultiStringOption('u');
11111   MultiStringRange codeChunks = op->getMultiStringOption('e');
11112   MultiStringRange modulePaths = op->getMultiStringOption('m');
11113 
11114 #ifdef FUZZING_JS_FUZZILLI
11115   // Check for REPRL file source
11116   if (op->getBoolOption("reprl")) {
11117     return FuzzilliReprlGetAndRun(cx);
11118   }
11119 #endif /* FUZZING_JS_FUZZILLI */
11120 
11121   if (filePaths.empty() && utf16FilePaths.empty() && codeChunks.empty() &&
11122       modulePaths.empty() && !op->getStringArg("script")) {
11123     // Always use the interactive shell when -i is used. Without -i we let
11124     // Process figure it out based on isatty.
11125     bool forceTTY = op->getBoolOption('i');
11126     return Process(cx, nullptr, forceTTY, FileScript);
11127   }
11128 
11129   while (!filePaths.empty() || !utf16FilePaths.empty() || !codeChunks.empty() ||
11130          !modulePaths.empty()) {
11131     size_t fpArgno = filePaths.empty() ? SIZE_MAX : filePaths.argno();
11132     size_t ufpArgno =
11133         utf16FilePaths.empty() ? SIZE_MAX : utf16FilePaths.argno();
11134     size_t ccArgno = codeChunks.empty() ? SIZE_MAX : codeChunks.argno();
11135     size_t mpArgno = modulePaths.empty() ? SIZE_MAX : modulePaths.argno();
11136 
11137     if (fpArgno < ufpArgno && fpArgno < ccArgno && fpArgno < mpArgno) {
11138       char* path = filePaths.front();
11139       if (!Process(cx, path, false, FileScript)) {
11140         return false;
11141       }
11142 
11143       filePaths.popFront();
11144       continue;
11145     }
11146 
11147     if (ufpArgno < fpArgno && ufpArgno < ccArgno && ufpArgno < mpArgno) {
11148       char* path = utf16FilePaths.front();
11149       if (!Process(cx, path, false, FileScriptUtf16)) {
11150         return false;
11151       }
11152 
11153       utf16FilePaths.popFront();
11154       continue;
11155     }
11156 
11157     if (ccArgno < fpArgno && ccArgno < ufpArgno && ccArgno < mpArgno) {
11158       const char* code = codeChunks.front();
11159 
11160       JS::CompileOptions opts(cx);
11161       opts.setFileAndLine("-e", 1);
11162 
11163       JS::SourceText<Utf8Unit> srcBuf;
11164       if (!srcBuf.init(cx, code, strlen(code), JS::SourceOwnership::Borrowed)) {
11165         return false;
11166       }
11167 
11168       RootedValue rval(cx);
11169       if (!JS::Evaluate(cx, opts, srcBuf, &rval)) {
11170         return false;
11171       }
11172 
11173       codeChunks.popFront();
11174       if (sc->quitting) {
11175         break;
11176       }
11177 
11178       continue;
11179     }
11180 
11181     MOZ_ASSERT(mpArgno < fpArgno && mpArgno < ufpArgno && mpArgno < ccArgno);
11182 
11183     char* path = modulePaths.front();
11184     if (!Process(cx, path, false, FileModule)) {
11185       return false;
11186     }
11187 
11188     modulePaths.popFront();
11189   }
11190 
11191   if (sc->quitting) {
11192     return false;
11193   }
11194 
11195   /* The |script| argument is processed after all options. */
11196   if (const char* path = op->getStringArg("script")) {
11197     if (!Process(cx, path, false, FileScript)) {
11198       return false;
11199     }
11200   }
11201 
11202   if (op->getBoolOption('i')) {
11203     if (!Process(cx, nullptr, true, FileScript)) {
11204       return false;
11205     }
11206   }
11207 
11208   return true;
11209 }
11210 
SetContextOptions(JSContext * cx,const OptionParser & op)11211 static bool SetContextOptions(JSContext* cx, const OptionParser& op) {
11212   enableAsmJS = !op.getBoolOption("no-asmjs");
11213 
11214   enableWasm = true;
11215   enableWasmBaseline = true;
11216   enableWasmOptimizing = true;
11217 
11218   if (const char* str = op.getStringOption("wasm-compiler")) {
11219     if (strcmp(str, "none") == 0) {
11220       enableWasm = false;
11221     } else if (strcmp(str, "baseline") == 0) {
11222       MOZ_ASSERT(enableWasmBaseline);
11223       enableWasmOptimizing = false;
11224     } else if (strcmp(str, "optimizing") == 0 ||
11225                strcmp(str, "optimized") == 0) {
11226       enableWasmBaseline = false;
11227       MOZ_ASSERT(enableWasmOptimizing);
11228     } else if (strcmp(str, "baseline+optimizing") == 0 ||
11229                strcmp(str, "baseline+optimized") == 0) {
11230       MOZ_ASSERT(enableWasmBaseline);
11231       MOZ_ASSERT(enableWasmOptimizing);
11232 #ifdef ENABLE_WASM_CRANELIFT
11233     } else if (strcmp(str, "cranelift") == 0) {
11234       enableWasmBaseline = false;
11235       enableWasmOptimizing = true;
11236     } else if (strcmp(str, "baseline+cranelift") == 0) {
11237       MOZ_ASSERT(enableWasmBaseline);
11238       enableWasmOptimizing = true;
11239 #else
11240     } else if (strcmp(str, "ion") == 0) {
11241       enableWasmBaseline = false;
11242       enableWasmOptimizing = true;
11243     } else if (strcmp(str, "baseline+ion") == 0) {
11244       MOZ_ASSERT(enableWasmBaseline);
11245       enableWasmOptimizing = true;
11246 #endif
11247     } else {
11248       return OptionFailure("wasm-compiler", str);
11249     }
11250   }
11251 
11252 #define WASM_DEFAULT_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, \
11253                              FLAG_PRED, SHELL, ...)                         \
11254   enableWasm##NAME = !op.getBoolOption("no-wasm-" SHELL);
11255 #define WASM_EXPERIMENTAL_FEATURE(NAME, LOWER_NAME, COMPILE_PRED,       \
11256                                   COMPILER_PRED, FLAG_PRED, SHELL, ...) \
11257   enableWasm##NAME = op.getBoolOption("wasm-" SHELL);
11258   JS_FOR_WASM_FEATURES(WASM_DEFAULT_FEATURE, WASM_EXPERIMENTAL_FEATURE);
11259 #undef WASM_DEFAULT_FEATURE
11260 #undef WASM_EXPERIMENTAL_FEATURE
11261 
11262 #ifdef ENABLE_WASM_SIMD_WORMHOLE
11263   enableWasmSimdWormhole = op.getBoolOption("wasm-simd-wormhole");
11264 #endif
11265   enableWasmVerbose = op.getBoolOption("wasm-verbose");
11266   enableTestWasmAwaitTier2 = op.getBoolOption("test-wasm-await-tier2");
11267   enableSourcePragmas = !op.getBoolOption("no-source-pragmas");
11268   enableAsyncStacks = !op.getBoolOption("no-async-stacks");
11269   enableAsyncStackCaptureDebuggeeOnly =
11270       op.getBoolOption("async-stacks-capture-debuggee-only");
11271   enableStreams = !op.getBoolOption("no-streams");
11272   enableReadableByteStreams = op.getBoolOption("enable-readable-byte-streams");
11273   enableBYOBStreamReaders = op.getBoolOption("enable-byob-stream-readers");
11274   enableWritableStreams = op.getBoolOption("enable-writable-streams");
11275   enableReadableStreamPipeTo = op.getBoolOption("enable-readablestream-pipeto");
11276   enableWeakRefs = !op.getBoolOption("disable-weak-refs");
11277   enableToSource = !op.getBoolOption("disable-tosource");
11278   enablePropertyErrorMessageFix =
11279       !op.getBoolOption("disable-property-error-message-fix");
11280   enableIteratorHelpers = op.getBoolOption("enable-iterator-helpers");
11281   enablePrivateClassFields = !op.getBoolOption("disable-private-fields");
11282   enablePrivateClassMethods = !op.getBoolOption("disable-private-methods");
11283   enableErgonomicBrandChecks =
11284       !op.getBoolOption("disable-ergonomic-brand-checks");
11285   enableTopLevelAwait = op.getBoolOption("enable-top-level-await");
11286   enableClassStaticBlocks = op.getBoolOption("enable-class-static-blocks");
11287   useOffThreadParseGlobal = op.getBoolOption("off-thread-parse-global");
11288   useFdlibmForSinCosTan = op.getBoolOption("use-fdlibm-for-sin-cos-tan");
11289 
11290   JS::ContextOptionsRef(cx)
11291       .setAsmJS(enableAsmJS)
11292       .setWasm(enableWasm)
11293       .setWasmForTrustedPrinciples(enableWasm)
11294       .setWasmBaseline(enableWasmBaseline)
11295 #if defined(ENABLE_WASM_CRANELIFT)
11296       .setWasmCranelift(enableWasmOptimizing)
11297       .setWasmIon(false)
11298 #else
11299       .setWasmCranelift(false)
11300       .setWasmIon(enableWasmOptimizing)
11301 #endif
11302 
11303 #define WASM_FEATURE(NAME, ...) .setWasm##NAME(enableWasm##NAME)
11304           JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE)
11305 #undef WASM_FEATURE
11306 
11307 #ifdef ENABLE_WASM_SIMD_WORMHOLE
11308       .setWasmSimdWormhole(enableWasmSimdWormhole)
11309 #endif
11310       .setWasmVerbose(enableWasmVerbose)
11311       .setTestWasmAwaitTier2(enableTestWasmAwaitTier2)
11312       .setSourcePragmas(enableSourcePragmas)
11313       .setAsyncStack(enableAsyncStacks)
11314       .setAsyncStackCaptureDebuggeeOnly(enableAsyncStackCaptureDebuggeeOnly)
11315       .setPrivateClassFields(enablePrivateClassFields)
11316       .setPrivateClassMethods(enablePrivateClassMethods)
11317       .setErgnomicBrandChecks(enableErgonomicBrandChecks)
11318       .setTopLevelAwait(enableTopLevelAwait)
11319       .setClassStaticBlocks(enableClassStaticBlocks);
11320 
11321   JS::SetUseOffThreadParseGlobal(useOffThreadParseGlobal);
11322   JS::SetUseFdlibmForSinCosTan(useFdlibmForSinCosTan);
11323 
11324   // Check --fast-warmup first because it sets default warm-up thresholds. These
11325   // thresholds can then be overridden below by --ion-eager and other flags.
11326   if (op.getBoolOption("fast-warmup")) {
11327     jit::JitOptions.setFastWarmUp();
11328   }
11329 
11330   if (op.getBoolOption("no-ion-for-main-context")) {
11331     JS::ContextOptionsRef(cx).setDisableIon();
11332   }
11333 
11334   if (const char* str = op.getStringOption("cache-ir-stubs")) {
11335     if (strcmp(str, "on") == 0) {
11336       jit::JitOptions.disableCacheIR = false;
11337     } else if (strcmp(str, "off") == 0) {
11338       jit::JitOptions.disableCacheIR = true;
11339     } else {
11340       return OptionFailure("cache-ir-stubs", str);
11341     }
11342   }
11343 
11344   if (const char* str = op.getStringOption("spectre-mitigations")) {
11345     if (strcmp(str, "on") == 0) {
11346       jit::JitOptions.spectreIndexMasking = true;
11347       jit::JitOptions.spectreObjectMitigations = true;
11348       jit::JitOptions.spectreStringMitigations = true;
11349       jit::JitOptions.spectreValueMasking = true;
11350       jit::JitOptions.spectreJitToCxxCalls = true;
11351     } else if (strcmp(str, "off") == 0) {
11352       jit::JitOptions.spectreIndexMasking = false;
11353       jit::JitOptions.spectreObjectMitigations = false;
11354       jit::JitOptions.spectreStringMitigations = false;
11355       jit::JitOptions.spectreValueMasking = false;
11356       jit::JitOptions.spectreJitToCxxCalls = false;
11357     } else {
11358       return OptionFailure("spectre-mitigations", str);
11359     }
11360   }
11361 
11362   if (const char* str = op.getStringOption("ion-scalar-replacement")) {
11363     if (strcmp(str, "on") == 0) {
11364       jit::JitOptions.disableScalarReplacement = false;
11365     } else if (strcmp(str, "off") == 0) {
11366       jit::JitOptions.disableScalarReplacement = true;
11367     } else {
11368       return OptionFailure("ion-scalar-replacement", str);
11369     }
11370   }
11371 
11372   if (op.getStringOption("ion-shared-stubs")) {
11373     // Dead option, preserved for now for potential fuzzer interaction.
11374   }
11375 
11376   if (const char* str = op.getStringOption("ion-gvn")) {
11377     if (strcmp(str, "off") == 0) {
11378       jit::JitOptions.disableGvn = true;
11379     } else if (strcmp(str, "on") != 0 && strcmp(str, "optimistic") != 0 &&
11380                strcmp(str, "pessimistic") != 0) {
11381       // We accept "pessimistic" and "optimistic" as synonyms for "on"
11382       // for backwards compatibility.
11383       return OptionFailure("ion-gvn", str);
11384     }
11385   }
11386 
11387   if (const char* str = op.getStringOption("ion-licm")) {
11388     if (strcmp(str, "on") == 0) {
11389       jit::JitOptions.disableLicm = false;
11390     } else if (strcmp(str, "off") == 0) {
11391       jit::JitOptions.disableLicm = true;
11392     } else {
11393       return OptionFailure("ion-licm", str);
11394     }
11395   }
11396 
11397   if (const char* str = op.getStringOption("ion-edgecase-analysis")) {
11398     if (strcmp(str, "on") == 0) {
11399       jit::JitOptions.disableEdgeCaseAnalysis = false;
11400     } else if (strcmp(str, "off") == 0) {
11401       jit::JitOptions.disableEdgeCaseAnalysis = true;
11402     } else {
11403       return OptionFailure("ion-edgecase-analysis", str);
11404     }
11405   }
11406 
11407   if (const char* str = op.getStringOption("ion-pruning")) {
11408     if (strcmp(str, "on") == 0) {
11409       jit::JitOptions.disablePruning = false;
11410     } else if (strcmp(str, "off") == 0) {
11411       jit::JitOptions.disablePruning = true;
11412     } else {
11413       return OptionFailure("ion-pruning", str);
11414     }
11415   }
11416 
11417   if (const char* str = op.getStringOption("ion-range-analysis")) {
11418     if (strcmp(str, "on") == 0) {
11419       jit::JitOptions.disableRangeAnalysis = false;
11420     } else if (strcmp(str, "off") == 0) {
11421       jit::JitOptions.disableRangeAnalysis = true;
11422     } else {
11423       return OptionFailure("ion-range-analysis", str);
11424     }
11425   }
11426 
11427   if (const char* str = op.getStringOption("ion-sink")) {
11428     if (strcmp(str, "on") == 0) {
11429       jit::JitOptions.disableSink = false;
11430     } else if (strcmp(str, "off") == 0) {
11431       jit::JitOptions.disableSink = true;
11432     } else {
11433       return OptionFailure("ion-sink", str);
11434     }
11435   }
11436 
11437   if (const char* str = op.getStringOption("ion-instruction-reordering")) {
11438     if (strcmp(str, "on") == 0) {
11439       jit::JitOptions.disableInstructionReordering = false;
11440     } else if (strcmp(str, "off") == 0) {
11441       jit::JitOptions.disableInstructionReordering = true;
11442     } else {
11443       return OptionFailure("ion-instruction-reordering", str);
11444     }
11445   }
11446 
11447   if (op.getBoolOption("ion-check-range-analysis")) {
11448     jit::JitOptions.checkRangeAnalysis = true;
11449   }
11450 
11451   if (op.getBoolOption("ion-extra-checks")) {
11452     jit::JitOptions.runExtraChecks = true;
11453   }
11454 
11455   if (const char* str = op.getStringOption("ion-inlining")) {
11456     if (strcmp(str, "on") == 0) {
11457       jit::JitOptions.disableInlining = false;
11458     } else if (strcmp(str, "off") == 0) {
11459       jit::JitOptions.disableInlining = true;
11460     } else {
11461       return OptionFailure("ion-inlining", str);
11462     }
11463   }
11464 
11465   if (const char* str = op.getStringOption("ion-osr")) {
11466     if (strcmp(str, "on") == 0) {
11467       jit::JitOptions.osr = true;
11468     } else if (strcmp(str, "off") == 0) {
11469       jit::JitOptions.osr = false;
11470     } else {
11471       return OptionFailure("ion-osr", str);
11472     }
11473   }
11474 
11475   if (const char* str = op.getStringOption("ion-limit-script-size")) {
11476     if (strcmp(str, "on") == 0) {
11477       jit::JitOptions.limitScriptSize = true;
11478     } else if (strcmp(str, "off") == 0) {
11479       jit::JitOptions.limitScriptSize = false;
11480     } else {
11481       return OptionFailure("ion-limit-script-size", str);
11482     }
11483   }
11484 
11485   int32_t warmUpThreshold = op.getIntOption("ion-warmup-threshold");
11486   if (warmUpThreshold >= 0) {
11487     jit::JitOptions.setNormalIonWarmUpThreshold(warmUpThreshold);
11488   }
11489 
11490   warmUpThreshold = op.getIntOption("baseline-warmup-threshold");
11491   if (warmUpThreshold >= 0) {
11492     jit::JitOptions.baselineJitWarmUpThreshold = warmUpThreshold;
11493   }
11494 
11495   warmUpThreshold = op.getIntOption("trial-inlining-warmup-threshold");
11496   if (warmUpThreshold >= 0) {
11497     jit::JitOptions.trialInliningWarmUpThreshold = warmUpThreshold;
11498   }
11499 
11500   warmUpThreshold = op.getIntOption("regexp-warmup-threshold");
11501   if (warmUpThreshold >= 0) {
11502     jit::JitOptions.regexpWarmUpThreshold = warmUpThreshold;
11503   }
11504 
11505   if (op.getBoolOption("baseline-eager")) {
11506     jit::JitOptions.setEagerBaselineCompilation();
11507   }
11508 
11509   if (op.getBoolOption("blinterp")) {
11510     jit::JitOptions.baselineInterpreter = true;
11511   }
11512 
11513   if (op.getBoolOption("no-blinterp")) {
11514     jit::JitOptions.baselineInterpreter = false;
11515   }
11516 
11517   warmUpThreshold = op.getIntOption("blinterp-warmup-threshold");
11518   if (warmUpThreshold >= 0) {
11519     jit::JitOptions.baselineInterpreterWarmUpThreshold = warmUpThreshold;
11520   }
11521 
11522   if (op.getBoolOption("blinterp-eager")) {
11523     jit::JitOptions.baselineInterpreterWarmUpThreshold = 0;
11524   }
11525 
11526   if (op.getBoolOption("no-baseline")) {
11527     jit::JitOptions.baselineJit = false;
11528   }
11529 
11530   if (op.getBoolOption("no-warp-async")) {
11531     jit::JitOptions.warpAsync = false;
11532   }
11533 
11534   if (op.getBoolOption("no-warp-generator")) {
11535     jit::JitOptions.warpGenerator = false;
11536   }
11537 
11538   if (op.getBoolOption("warp-async")) {
11539     jit::JitOptions.warpAsync = true;
11540   }
11541 
11542   if (op.getBoolOption("warp-generator")) {
11543     jit::JitOptions.warpGenerator = true;
11544   }
11545 
11546   if (op.getBoolOption("no-ion")) {
11547     jit::JitOptions.ion = false;
11548   }
11549 
11550   if (op.getBoolOption("no-native-regexp")) {
11551     jit::JitOptions.nativeRegExp = false;
11552   }
11553 
11554   if (op.getBoolOption("trace-regexp-parser")) {
11555     jit::JitOptions.traceRegExpParser = true;
11556   }
11557   if (op.getBoolOption("trace-regexp-assembler")) {
11558     jit::JitOptions.traceRegExpAssembler = true;
11559   }
11560   if (op.getBoolOption("trace-regexp-interpreter")) {
11561     jit::JitOptions.traceRegExpInterpreter = true;
11562   }
11563   if (op.getBoolOption("trace-regexp-peephole")) {
11564     jit::JitOptions.traceRegExpPeephole = true;
11565   }
11566 
11567   int32_t inliningEntryThreshold = op.getIntOption("inlining-entry-threshold");
11568   if (inliningEntryThreshold > 0) {
11569     jit::JitOptions.inliningEntryThreshold = inliningEntryThreshold;
11570   }
11571 
11572   int32_t smallFunctionLength = op.getIntOption("small-function-length");
11573   if (smallFunctionLength > 0) {
11574     jit::JitOptions.smallFunctionMaxBytecodeLength = smallFunctionLength;
11575   }
11576 
11577   if (const char* str = op.getStringOption("ion-regalloc")) {
11578     jit::JitOptions.forcedRegisterAllocator = jit::LookupRegisterAllocator(str);
11579     if (!jit::JitOptions.forcedRegisterAllocator.isSome()) {
11580       return OptionFailure("ion-regalloc", str);
11581     }
11582   }
11583 
11584   if (op.getBoolOption("ion-eager")) {
11585     jit::JitOptions.setEagerIonCompilation();
11586   }
11587 
11588   offthreadCompilation = true;
11589   if (const char* str = op.getStringOption("ion-offthread-compile")) {
11590     if (strcmp(str, "off") == 0) {
11591       offthreadCompilation = false;
11592     } else if (strcmp(str, "on") != 0) {
11593       return OptionFailure("ion-offthread-compile", str);
11594     }
11595   }
11596   cx->runtime()->setOffthreadIonCompilationEnabled(offthreadCompilation);
11597 
11598   if (op.getStringOption("ion-parallel-compile")) {
11599     fprintf(stderr,
11600             "--ion-parallel-compile is deprecated. Please use "
11601             "--ion-offthread-compile instead.\n");
11602     return false;
11603   }
11604 
11605   if (const char* str = op.getStringOption("shared-memory")) {
11606     if (strcmp(str, "off") == 0) {
11607       enableSharedMemory = false;
11608     } else if (strcmp(str, "on") == 0) {
11609       enableSharedMemory = true;
11610     } else {
11611       return OptionFailure("shared-memory", str);
11612     }
11613   }
11614 
11615   if (op.getBoolOption("no-large-arraybuffers")) {
11616     JS::SetLargeArrayBuffersEnabled(false);
11617   }
11618 
11619   if (op.getBoolOption("disable-bailout-loop-check")) {
11620     jit::JitOptions.disableBailoutLoopCheck = true;
11621   }
11622 
11623 #if defined(JS_CODEGEN_ARM)
11624   if (const char* str = op.getStringOption("arm-hwcap")) {
11625     jit::ParseARMHwCapFlags(str);
11626     jit::ComputeJitSupportFlags();
11627   }
11628 
11629   int32_t fill = op.getIntOption("arm-asm-nop-fill");
11630   if (fill >= 0) {
11631     jit::Assembler::NopFill = fill;
11632   }
11633 
11634   int32_t poolMaxOffset = op.getIntOption("asm-pool-max-offset");
11635   if (poolMaxOffset >= 5 && poolMaxOffset <= 1024) {
11636     jit::Assembler::AsmPoolMaxOffset = poolMaxOffset;
11637   }
11638 #endif
11639 
11640 #if defined(JS_SIMULATOR_ARM)
11641   if (op.getBoolOption("arm-sim-icache-checks")) {
11642     jit::SimulatorProcess::ICacheCheckingDisableCount = 0;
11643   }
11644 
11645   int32_t stopAt = op.getIntOption("arm-sim-stop-at");
11646   if (stopAt >= 0) {
11647     jit::Simulator::StopSimAt = stopAt;
11648   }
11649 #elif defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
11650   if (op.getBoolOption("mips-sim-icache-checks")) {
11651     jit::SimulatorProcess::ICacheCheckingDisableCount = 0;
11652   }
11653 
11654   int32_t stopAt = op.getIntOption("mips-sim-stop-at");
11655   if (stopAt >= 0) {
11656     jit::Simulator::StopSimAt = stopAt;
11657   }
11658 #endif
11659 
11660   reportWarnings = op.getBoolOption('w');
11661   compileOnly = op.getBoolOption('c');
11662   printTiming = op.getBoolOption('b');
11663   enableDisassemblyDumps = op.getBoolOption('D');
11664   cx->runtime()->profilingScripts =
11665       enableCodeCoverage || enableDisassemblyDumps;
11666 
11667 #ifdef DEBUG
11668   dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
11669 #endif
11670 
11671 #ifdef JS_GC_ZEAL
11672   const char* zealStr = op.getStringOption("gc-zeal");
11673   if (zealStr) {
11674     if (!cx->runtime()->gc.parseAndSetZeal(zealStr)) {
11675       return false;
11676     }
11677     uint32_t nextScheduled;
11678     cx->runtime()->gc.getZealBits(&gZealBits, &gZealFrequency, &nextScheduled);
11679   }
11680 #endif
11681 
11682   return true;
11683 }
11684 
SetWorkerContextOptions(JSContext * cx)11685 static void SetWorkerContextOptions(JSContext* cx) {
11686   // Copy option values from the main thread.
11687   JS::ContextOptionsRef(cx)
11688       .setAsmJS(enableAsmJS)
11689       .setWasm(enableWasm)
11690       .setWasmBaseline(enableWasmBaseline)
11691 #if defined(ENABLE_WASM_CRANELIFT)
11692       .setWasmCranelift(enableWasmOptimizing)
11693       .setWasmIon(false)
11694 #else
11695       .setWasmCranelift(false)
11696       .setWasmIon(enableWasmOptimizing)
11697 #endif
11698 #define WASM_FEATURE(NAME, ...) .setWasm##NAME(enableWasm##NAME)
11699           JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE)
11700 #undef WASM_FEATURE
11701 
11702 #ifdef ENABLE_WASM_SIMD_WORMHOLE
11703       .setWasmSimdWormhole(enableWasmSimdWormhole)
11704 #endif
11705       .setWasmVerbose(enableWasmVerbose)
11706       .setTestWasmAwaitTier2(enableTestWasmAwaitTier2)
11707       .setSourcePragmas(enableSourcePragmas);
11708 
11709   cx->runtime()->setOffthreadIonCompilationEnabled(offthreadCompilation);
11710   cx->runtime()->profilingScripts =
11711       enableCodeCoverage || enableDisassemblyDumps;
11712 
11713 #ifdef JS_GC_ZEAL
11714   if (gZealBits && gZealFrequency) {
11715     for (size_t i = 0; i < size_t(gc::ZealMode::Count); i++) {
11716       if (gZealBits & (1 << i)) {
11717         cx->runtime()->gc.setZeal(i, gZealFrequency);
11718       }
11719     }
11720   }
11721 #endif
11722 
11723   JS_SetNativeStackQuota(cx, gWorkerStackSize);
11724 }
11725 
PrintUnhandledRejection(JSContext * cx,Handle<PromiseObject * > promise)11726 [[nodiscard]] static bool PrintUnhandledRejection(
11727     JSContext* cx, Handle<PromiseObject*> promise) {
11728   RootedValue reason(cx, promise->reason());
11729   RootedObject site(cx, promise->resolutionSite());
11730 
11731   RootedString str(cx, JS_ValueToSource(cx, reason));
11732   if (!str) {
11733     return false;
11734   }
11735 
11736   UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
11737   if (!utf8chars) {
11738     return false;
11739   }
11740 
11741   FILE* fp = ErrorFilePointer();
11742   fprintf(fp, "Unhandled rejection: %s\n", utf8chars.get());
11743 
11744   if (!site) {
11745     fputs("(no stack trace available)\n", stderr);
11746     return true;
11747   }
11748 
11749   JSPrincipals* principals = cx->realm()->principals();
11750   RootedString stackStr(cx);
11751   if (!BuildStackString(cx, principals, site, &stackStr, 2)) {
11752     return false;
11753   }
11754 
11755   UniqueChars stack = JS_EncodeStringToUTF8(cx, stackStr);
11756   if (!stack) {
11757     return false;
11758   }
11759 
11760   fputs("Stack:\n", fp);
11761   fputs(stack.get(), fp);
11762 
11763   return true;
11764 }
11765 
ReportUnhandledRejections(JSContext * cx)11766 [[nodiscard]] static bool ReportUnhandledRejections(JSContext* cx) {
11767   ShellContext* sc = GetShellContext(cx);
11768   if (!sc->trackUnhandledRejections) {
11769     return true;
11770   }
11771 
11772   if (!sc->unhandledRejectedPromises) {
11773     return true;
11774   }
11775 
11776   AutoRealm ar(cx, sc->unhandledRejectedPromises);
11777 
11778   if (!SetObject::size(cx, sc->unhandledRejectedPromises)) {
11779     return true;
11780   }
11781 
11782   sc->exitCode = EXITCODE_RUNTIME_ERROR;
11783 
11784   RootedValue iter(cx);
11785   if (!SetObject::iterator(cx, SetObject::IteratorKind::Values,
11786                            sc->unhandledRejectedPromises, &iter)) {
11787     return false;
11788   }
11789 
11790   Rooted<SetIteratorObject*> iterObj(cx,
11791                                      &iter.toObject().as<SetIteratorObject>());
11792   JSObject* obj = SetIteratorObject::createResult(cx);
11793   if (!obj) {
11794     return false;
11795   }
11796 
11797   RootedArrayObject resultObj(cx, &obj->as<ArrayObject>());
11798   while (true) {
11799     bool done = SetIteratorObject::next(iterObj, resultObj);
11800     if (done) {
11801       break;
11802     }
11803 
11804     RootedObject obj(cx, &resultObj->getDenseElement(0).toObject());
11805     Rooted<PromiseObject*> promise(cx, obj->maybeUnwrapIf<PromiseObject>());
11806     if (!promise) {
11807       FILE* fp = ErrorFilePointer();
11808       fputs(
11809           "Unhandled rejection: dead proxy found in unhandled "
11810           "rejections set\n",
11811           fp);
11812       continue;
11813     }
11814 
11815     AutoRealm ar2(cx, promise);
11816 
11817     if (!PrintUnhandledRejection(cx, promise)) {
11818       return false;
11819     }
11820   }
11821 
11822   sc->unhandledRejectedPromises = nullptr;
11823 
11824   return true;
11825 }
11826 
Shell(JSContext * cx,OptionParser * op)11827 static int Shell(JSContext* cx, OptionParser* op) {
11828   if (JS::TraceLoggerSupported()) {
11829     JS::StartTraceLogger(cx);
11830   }
11831 #ifdef JS_STRUCTURED_SPEW
11832   cx->spewer().enableSpewing();
11833 #endif
11834 
11835   auto exitShell = MakeScopeExit([&] {
11836     if (JS::TraceLoggerSupported()) {
11837       JS::SpewTraceLoggerForCurrentProcess();
11838       JS::StopTraceLogger(cx);
11839     }
11840 #ifdef JS_STRUCTURED_SPEW
11841     cx->spewer().disableSpewing();
11842 #endif
11843   });
11844 
11845   if (op->getBoolOption("wasm-compile-and-serialize")) {
11846 #ifdef __wasi__
11847     MOZ_CRASH("WASI doesn't support wasm");
11848 #else
11849     if (!WasmCompileAndSerialize(cx)) {
11850       // Errors have been printed directly to stderr.
11851       MOZ_ASSERT(!cx->isExceptionPending());
11852       return -1;
11853     }
11854 #endif
11855     return EXIT_SUCCESS;
11856   }
11857 
11858 #ifdef MOZ_CODE_COVERAGE
11859   InstallCoverageSignalHandlers();
11860 #endif
11861 
11862   Maybe<JS::AutoDisableGenerationalGC> noggc;
11863   if (op->getBoolOption("no-ggc")) {
11864     noggc.emplace(cx);
11865   }
11866 
11867   Maybe<AutoDisableCompactingGC> nocgc;
11868   if (op->getBoolOption("no-cgc")) {
11869     nocgc.emplace(cx);
11870   }
11871 
11872   if (op->getBoolOption("fuzzing-safe")) {
11873     fuzzingSafe = true;
11874   } else {
11875     fuzzingSafe =
11876         (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0');
11877   }
11878 
11879 #ifdef DEBUG
11880   if (op->getBoolOption("differential-testing")) {
11881     JS::SetSupportDifferentialTesting(true);
11882   }
11883 #endif
11884 
11885   if (op->getBoolOption("disable-oom-functions")) {
11886     disableOOMFunctions = true;
11887   }
11888 
11889   if (op->getBoolOption("more-compartments")) {
11890     defaultToSameCompartment = false;
11891   }
11892 
11893   bool reprl_mode = FuzzilliUseReprlMode(op);
11894 
11895   // Begin REPRL Loop
11896   int result = EXIT_SUCCESS;
11897   do {
11898     JS::RealmOptions options;
11899     SetStandardRealmOptions(options);
11900     RootedObject glob(
11901         cx, NewGlobalObject(cx, options, nullptr, ShellGlobalKind::WindowProxy,
11902                             /* immutablePrototype = */ true));
11903     if (!glob) {
11904       return 1;
11905     }
11906 
11907     JSAutoRealm ar(cx, glob);
11908 
11909 #ifdef FUZZING_INTERFACES
11910     if (fuzzHaveModule) {
11911       return FuzzJSRuntimeStart(cx, &sArgc, &sArgv);
11912     }
11913 #endif
11914 
11915     ShellContext* sc = GetShellContext(cx);
11916     sc->exitCode = 0;
11917     result = EXIT_SUCCESS;
11918     {
11919       AutoReportException are(cx);
11920       if (!ProcessArgs(cx, op) && !sc->quitting) {
11921         result = EXITCODE_RUNTIME_ERROR;
11922       }
11923     }
11924 
11925     /*
11926      * The job queue must be drained even on error to finish outstanding async
11927      * tasks before the main thread JSRuntime is torn down. Drain after
11928      * uncaught exceptions have been reported since draining runs callbacks.
11929      */
11930     RunShellJobs(cx);
11931 
11932     // Only if there's no other error, report unhandled rejections.
11933     if (!result && !sc->exitCode) {
11934       AutoReportException are(cx);
11935       if (!ReportUnhandledRejections(cx)) {
11936         FILE* fp = ErrorFilePointer();
11937         fputs("Error while printing unhandled rejection\n", fp);
11938       }
11939     }
11940 
11941     if (sc->exitCode) {
11942       result = sc->exitCode;
11943     }
11944 
11945 #ifdef FUZZING_JS_FUZZILLI
11946     if (reprl_mode) {
11947       fflush(stdout);
11948       fflush(stderr);
11949       // Send return code to parent and reset edge counters.
11950       int status = (result & 0xff) << 8;
11951       MOZ_RELEASE_ASSERT(write(REPRL_CWFD, &status, 4) == 4);
11952       __sanitizer_cov_reset_edgeguards();
11953     }
11954 #endif
11955 
11956     if (enableDisassemblyDumps) {
11957       AutoReportException are(cx);
11958       if (!js::DumpRealmPCCounts(cx)) {
11959         result = EXITCODE_OUT_OF_MEMORY;
11960       }
11961     }
11962 
11963     // End REPRL loop
11964   } while (reprl_mode);
11965 
11966   return result;
11967 }
11968 
11969 // Used to allocate memory when jemalloc isn't yet initialized.
JS_DECLARE_NEW_METHODS(SystemAlloc_New,malloc,static)11970 JS_DECLARE_NEW_METHODS(SystemAlloc_New, malloc, static)
11971 
11972 static void SetOutputFile(const char* const envVar, RCFile* defaultOut,
11973                           RCFile** outFileP) {
11974   RCFile* outFile;
11975 
11976   const char* outPath = getenv(envVar);
11977   FILE* newfp;
11978   if (outPath && *outPath && (newfp = fopen(outPath, "w"))) {
11979     outFile = SystemAlloc_New<RCFile>(newfp);
11980   } else {
11981     outFile = defaultOut;
11982   }
11983 
11984   if (!outFile) {
11985     MOZ_CRASH("Failed to allocate output file");
11986   }
11987 
11988   outFile->acquire();
11989   *outFileP = outFile;
11990 }
11991 
PreInit()11992 static void PreInit() {
11993 #ifdef XP_WIN
11994   const char* crash_option = getenv("XRE_NO_WINDOWS_CRASH_DIALOG");
11995   if (crash_option && crash_option[0] == '1') {
11996     // Disable the segfault dialog. We want to fail the tests immediately
11997     // instead of hanging automation.
11998     UINT newMode = SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX;
11999     UINT prevMode = SetErrorMode(newMode);
12000     SetErrorMode(prevMode | newMode);
12001   }
12002 #endif
12003 }
12004 
12005 #ifndef JS_WITHOUT_NSPR
12006 class AutoLibraryLoader {
12007   Vector<PRLibrary*, 4, SystemAllocPolicy> libraries;
12008 
12009  public:
~AutoLibraryLoader()12010   ~AutoLibraryLoader() {
12011     for (auto dll : libraries) {
12012       PR_UnloadLibrary(dll);
12013     }
12014   }
12015 
load(const char * path)12016   PRLibrary* load(const char* path) {
12017     PRLibSpec libSpec;
12018     libSpec.type = PR_LibSpec_Pathname;
12019     libSpec.value.pathname = path;
12020     PRLibrary* dll = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_GLOBAL);
12021     if (!dll) {
12022       fprintf(stderr, "LoadLibrary '%s' failed with code %d\n", path,
12023               PR_GetError());
12024       MOZ_CRASH("Failed to load library");
12025     }
12026 
12027     MOZ_ALWAYS_TRUE(libraries.append(dll));
12028     return dll;
12029   }
12030 };
12031 #endif
12032 
ReadSelfHostedXDRFile(JSContext * cx,FileContents & buf)12033 static bool ReadSelfHostedXDRFile(JSContext* cx, FileContents& buf) {
12034   FILE* file = fopen(selfHostedXDRPath, "rb");
12035   if (!file) {
12036     fprintf(stderr, "Can't open self-hosted stencil XDR file.\n");
12037     return false;
12038   }
12039   AutoCloseFile autoClose(file);
12040 
12041   struct stat st;
12042   if (fstat(fileno(file), &st) < 0) {
12043     fprintf(stderr, "Unable to stat self-hosted stencil XDR file.\n");
12044     return false;
12045   }
12046 
12047   if (st.st_size >= INT32_MAX) {
12048     fprintf(stderr, "self-hosted stencil XDR file too large.\n");
12049     return false;
12050   }
12051   uint32_t filesize = uint32_t(st.st_size);
12052 
12053   if (!buf.growBy(filesize)) {
12054     return false;
12055   }
12056   size_t cc = fread(buf.begin(), 1, filesize, file);
12057   if (cc != filesize) {
12058     fprintf(stderr, "Short read on self-hosted stencil XDR file.\n");
12059     return false;
12060   }
12061 
12062   return true;
12063 }
12064 
WriteSelfHostedXDRFile(JSContext * cx,JS::SelfHostedCache buffer)12065 static bool WriteSelfHostedXDRFile(JSContext* cx, JS::SelfHostedCache buffer) {
12066   FILE* file = fopen(selfHostedXDRPath, "wb");
12067   if (!file) {
12068     JS_ReportErrorUTF8(cx, "Can't open self-hosted stencil XDR file.");
12069     return false;
12070   }
12071   AutoCloseFile autoClose(file);
12072 
12073   size_t cc = fwrite(buffer.Elements(), 1, buffer.LengthBytes(), file);
12074   if (cc != buffer.LengthBytes()) {
12075     JS_ReportErrorUTF8(cx, "Short write on self-hosted stencil XDR file.");
12076     return false;
12077   }
12078 
12079   return true;
12080 }
12081 
main(int argc,char ** argv)12082 int main(int argc, char** argv) {
12083   PreInit();
12084 
12085   sArgc = argc;
12086   sArgv = argv;
12087 
12088   int result;
12089 
12090   setlocale(LC_ALL, "");
12091 
12092   // Special-case stdout and stderr. We bump their refcounts to prevent them
12093   // from getting closed and then having some printf fail somewhere.
12094   RCFile rcStdout(stdout);
12095   rcStdout.acquire();
12096   RCFile rcStderr(stderr);
12097   rcStderr.acquire();
12098 
12099   SetOutputFile("JS_STDOUT", &rcStdout, &gOutFile);
12100   SetOutputFile("JS_STDERR", &rcStderr, &gErrFile);
12101 
12102   // Start the engine.
12103   if (const char* message = JS_InitWithFailureDiagnostic()) {
12104     fprintf(gErrFile->fp, "JS_Init failed: %s\n", message);
12105     return 1;
12106   }
12107 
12108   // `selfHostedXDRBuffer` contains XDR buffer of the self-hosted JS.
12109   // A part of it is borrowed by ImmutableScriptData of the self-hosted scripts.
12110   //
12111   // This buffer's should outlive JS_Shutdown.
12112   Maybe<FileContents> selfHostedXDRBuffer;
12113 
12114   auto shutdownEngine = MakeScopeExit([] { JS_ShutDown(); });
12115 
12116   OptionParser op("Usage: {progname} [options] [[script] scriptArgs*]");
12117 
12118   op.setDescription(
12119       "The SpiderMonkey shell provides a command line interface to the "
12120       "JavaScript engine. Code and file options provided via the command line "
12121       "are "
12122       "run left to right. If provided, the optional script argument is run "
12123       "after "
12124       "all options have been processed. Just-In-Time compilation modes may be "
12125       "enabled via "
12126       "command line options.");
12127   op.setDescriptionWidth(72);
12128   op.setHelpWidth(80);
12129   op.setVersion(JS_GetImplementationVersion());
12130 
12131   if (!op.addMultiStringOption(
12132           'f', "file", "PATH",
12133           "File path to run, parsing file contents as UTF-8") ||
12134       !op.addMultiStringOption(
12135           'u', "utf16-file", "PATH",
12136           "File path to run, inflating the file's UTF-8 contents to UTF-16 and "
12137           "then parsing that") ||
12138       !op.addMultiStringOption('m', "module", "PATH", "Module path to run") ||
12139       !op.addMultiStringOption('e', "execute", "CODE", "Inline code to run") ||
12140       !op.addStringOption('\0', "selfhosted-xdr-path", "[filename]",
12141                           "Read/Write selfhosted script data from/to the given "
12142                           "XDR file") ||
12143       !op.addStringOption('\0', "selfhosted-xdr-mode", "(encode,decode,off)",
12144                           "Whether to encode/decode data of the file provided"
12145                           "with --selfhosted-xdr-path.") ||
12146       !op.addBoolOption('i', "shell", "Enter prompt after running code") ||
12147       !op.addBoolOption('c', "compileonly",
12148                         "Only compile, don't run (syntax checking mode)") ||
12149       !op.addBoolOption('w', "warnings", "Emit warnings") ||
12150       !op.addBoolOption('W', "nowarnings", "Don't emit warnings") ||
12151       !op.addBoolOption('D', "dump-bytecode",
12152                         "Dump bytecode with exec count for all scripts") ||
12153       !op.addBoolOption('b', "print-timing",
12154                         "Print sub-ms runtime for each file that's run") ||
12155       !op.addBoolOption('\0', "code-coverage",
12156                         "Enable code coverage instrumentation.") ||
12157       !op.addBoolOption(
12158           '\0', "disable-parser-deferred-alloc",
12159           "Disable deferred allocation of GC objects until after parser") ||
12160 #ifdef DEBUG
12161       !op.addBoolOption('O', "print-alloc",
12162                         "Print the number of allocations at exit") ||
12163 #endif
12164       !op.addOptionalStringArg("script",
12165                                "A script to execute (after all options)") ||
12166       !op.addOptionalMultiStringArg(
12167           "scriptArgs",
12168           "String arguments to bind as |scriptArgs| in the "
12169           "shell's global") ||
12170       !op.addIntOption(
12171           '\0', "cpu-count", "COUNT",
12172           "Set the number of CPUs (hardware threads) to COUNT, the "
12173           "default is the actual number of CPUs. The total number of "
12174           "background helper threads is the CPU count plus some constant.",
12175           -1) ||
12176       !op.addIntOption('\0', "thread-count", "COUNT", "Alias for --cpu-count.",
12177                        -1) ||
12178       !op.addBoolOption('\0', "ion", "Enable IonMonkey (default)") ||
12179       !op.addBoolOption('\0', "no-ion", "Disable IonMonkey") ||
12180       !op.addBoolOption('\0', "no-ion-for-main-context",
12181                         "Disable IonMonkey for the main context only") ||
12182       !op.addIntOption('\0', "inlining-entry-threshold", "COUNT",
12183                        "The minimum stub entry count before trial-inlining a"
12184                        " call",
12185                        -1) ||
12186       !op.addIntOption('\0', "small-function-length", "COUNT",
12187                        "The maximum bytecode length of a 'small function' for "
12188                        "the purpose of inlining.",
12189                        -1) ||
12190       !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") ||
12191       !op.addStringOption(
12192           '\0', "wasm-compiler", "[option]",
12193           "Choose to enable a subset of the wasm compilers, valid options are "
12194           "'none', 'baseline', 'ion', 'cranelift', 'optimizing', "
12195           "'baseline+ion', "
12196           "'baseline+cranelift', 'baseline+optimizing'. Choosing 'ion' when "
12197           "Ion is "
12198           "not available or 'cranelift' when Cranelift is not available will "
12199           "fail; "
12200           "use 'optimizing' for cross-compiler compatibility.") ||
12201       !op.addBoolOption('\0', "wasm-verbose",
12202                         "Enable WebAssembly verbose logging") ||
12203       !op.addBoolOption('\0', "disable-wasm-huge-memory",
12204                         "Disable WebAssembly huge memory") ||
12205       !op.addBoolOption('\0', "test-wasm-await-tier2",
12206                         "Forcibly activate tiering and block "
12207                         "instantiation on completion of tier2") ||
12208 #define WASM_DEFAULT_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, \
12209                              FLAG_PRED, SHELL, ...)                         \
12210   !op.addBoolOption('\0', "no-wasm-" SHELL, "Disable wasm " SHELL "feature.") ||
12211 #define WASM_EXPERIMENTAL_FEATURE(NAME, LOWER_NAME, COMPILE_PRED,       \
12212                                   COMPILER_PRED, FLAG_PRED, SHELL, ...) \
12213   !op.addBoolOption('\0', "wasm-" SHELL,                                \
12214                     "Enable experimental wasm " SHELL "feature.") ||
12215       JS_FOR_WASM_FEATURES(WASM_DEFAULT_FEATURE, WASM_EXPERIMENTAL_FEATURE)
12216 #undef WASM_DEFAULT_FEATURE
12217 #undef WASM_EXPERIMENTAL_FEATURE
12218 #ifdef ENABLE_WASM_SIMD_WORMHOLE
12219           !op.addBoolOption('\0', "wasm-simd-wormhole",
12220                             "Enable wasm SIMD wormhole (UTSL)") ||
12221 #else
12222           !op.addBoolOption('\0', "wasm-simd-wormhole", "No-op") ||
12223 #endif
12224       !op.addBoolOption('\0', "no-native-regexp",
12225                         "Disable native regexp compilation") ||
12226       !op.addIntOption(
12227           '\0', "regexp-warmup-threshold", "COUNT",
12228           "Wait for COUNT invocations before compiling regexps to native code "
12229           "(default 10)",
12230           -1) ||
12231       !op.addBoolOption('\0', "trace-regexp-parser", "Trace regexp parsing") ||
12232       !op.addBoolOption('\0', "trace-regexp-assembler",
12233                         "Trace regexp assembler") ||
12234       !op.addBoolOption('\0', "trace-regexp-interpreter",
12235                         "Trace regexp interpreter") ||
12236       !op.addBoolOption('\0', "trace-regexp-peephole",
12237                         "Trace regexp peephole optimization") ||
12238       !op.addBoolOption('\0', "enable-streams",
12239                         "Enable WHATWG Streams (default)") ||
12240       !op.addBoolOption('\0', "no-streams", "Disable WHATWG Streams") ||
12241       !op.addBoolOption('\0', "enable-readable-byte-streams",
12242                         "Enable support for WHATWG ReadableStreams of type "
12243                         "'bytes'") ||
12244       !op.addBoolOption('\0', "enable-byob-stream-readers",
12245                         "Enable support for getting BYOB readers for WHATWG "
12246                         "ReadableStreams of type \"bytes\"") ||
12247       !op.addBoolOption('\0', "enable-writable-streams",
12248                         "Enable support for WHATWG WritableStreams") ||
12249       !op.addBoolOption('\0', "enable-readablestream-pipeto",
12250                         "Enable support for "
12251                         "WHATWG ReadableStream.prototype.pipeTo") ||
12252       !op.addBoolOption('\0', "disable-weak-refs", "Disable weak references") ||
12253       !op.addBoolOption('\0', "disable-tosource", "Disable toSource/uneval") ||
12254       !op.addBoolOption('\0', "disable-property-error-message-fix",
12255                         "Disable fix for the error message when accessing "
12256                         "property of null or undefined") ||
12257       !op.addBoolOption('\0', "enable-iterator-helpers",
12258                         "Enable iterator helpers") ||
12259       !op.addBoolOption('\0', "disable-private-fields",
12260                         "Disable private class fields") ||
12261       !op.addBoolOption('\0', "disable-private-methods",
12262                         "Disable private class methods") ||
12263       !op.addBoolOption(
12264           '\0', "enable-ergonomic-brand-checks",
12265           "Enable ergonomic brand checks for private class fields (no-op)") ||
12266       !op.addBoolOption(
12267           '\0', "disable-ergonomic-brand-checks",
12268           "Disable ergonomic brand checks for private class fields") ||
12269       !op.addBoolOption('\0', "enable-top-level-await",
12270                         "Enable top-level await") ||
12271       !op.addBoolOption('\0', "enable-class-static-blocks",
12272                         "Enable class static blocks") ||
12273       !op.addBoolOption('\0', "off-thread-parse-global",
12274                         "Use parseGlobal in all off-thread compilation") ||
12275       !op.addBoolOption('\0', "no-large-arraybuffers",
12276                         "Disallow creating ArrayBuffers larger than 2 GB on "
12277                         "64-bit platforms") ||
12278       !op.addStringOption('\0', "shared-memory", "on/off",
12279                           "SharedArrayBuffer and Atomics "
12280 #if SHARED_MEMORY_DEFAULT
12281                           "(default: on, off to disable)"
12282 #else
12283                           "(default: off, on to enable)"
12284 #endif
12285                           ) ||
12286       !op.addStringOption('\0', "spectre-mitigations", "on/off",
12287                           "Whether Spectre mitigations are enabled (default: "
12288                           "off, on to enable)") ||
12289       !op.addStringOption('\0', "cache-ir-stubs", "on/off/call",
12290                           "Use CacheIR stubs (default: on, off to disable, "
12291                           "call to enable work-in-progress call ICs)") ||
12292       !op.addStringOption('\0', "ion-shared-stubs", "on/off",
12293                           "Use shared stubs (default: on, off to disable)") ||
12294       !op.addStringOption('\0', "ion-scalar-replacement", "on/off",
12295                           "Scalar Replacement (default: on, off to disable)") ||
12296       !op.addStringOption('\0', "ion-gvn", "[mode]",
12297                           "Specify Ion global value numbering:\n"
12298                           "  off: disable GVN\n"
12299                           "  on:  enable GVN (default)\n") ||
12300       !op.addStringOption(
12301           '\0', "ion-licm", "on/off",
12302           "Loop invariant code motion (default: on, off to disable)") ||
12303       !op.addStringOption('\0', "ion-edgecase-analysis", "on/off",
12304                           "Find edge cases where Ion can avoid bailouts "
12305                           "(default: on, off to disable)") ||
12306       !op.addStringOption('\0', "ion-pruning", "on/off",
12307                           "Branch pruning (default: on, off to disable)") ||
12308       !op.addStringOption('\0', "ion-range-analysis", "on/off",
12309                           "Range analysis (default: on, off to disable)") ||
12310       !op.addStringOption('\0', "ion-sink", "on/off",
12311                           "Sink code motion (default: off, on to enable)") ||
12312       !op.addStringOption('\0', "ion-optimization-levels", "on/off",
12313                           "No-op for fuzzing") ||
12314       !op.addStringOption('\0', "ion-loop-unrolling", "on/off",
12315                           "(NOP for fuzzers)") ||
12316       !op.addStringOption(
12317           '\0', "ion-instruction-reordering", "on/off",
12318           "Instruction reordering (default: off, on to enable)") ||
12319       !op.addBoolOption('\0', "ion-check-range-analysis",
12320                         "Range analysis checking") ||
12321       !op.addBoolOption('\0', "ion-extra-checks",
12322                         "Perform extra dynamic validation checks") ||
12323       !op.addStringOption(
12324           '\0', "ion-inlining", "on/off",
12325           "Inline methods where possible (default: on, off to disable)") ||
12326       !op.addStringOption(
12327           '\0', "ion-osr", "on/off",
12328           "On-Stack Replacement (default: on, off to disable)") ||
12329       !op.addBoolOption('\0', "disable-bailout-loop-check",
12330                         "Turn off bailout loop check") ||
12331       !op.addBoolOption('\0', "scalar-replace-arguments",
12332                         "Use scalar replacement to optimize ArgumentsObject") ||
12333       !op.addStringOption(
12334           '\0', "ion-limit-script-size", "on/off",
12335           "Don't compile very large scripts (default: on, off to disable)") ||
12336       !op.addIntOption('\0', "ion-warmup-threshold", "COUNT",
12337                        "Wait for COUNT calls or iterations before compiling "
12338                        "at the normal optimization level (default: 1000)",
12339                        -1) ||
12340       !op.addIntOption('\0', "ion-full-warmup-threshold", "COUNT",
12341                        "No-op for fuzzing", -1) ||
12342       !op.addStringOption(
12343           '\0', "ion-regalloc", "[mode]",
12344           "Specify Ion register allocation:\n"
12345           "  backtracking: Priority based backtracking register allocation "
12346           "(default)\n"
12347           "  testbed: Backtracking allocator with experimental features\n"
12348           "  stupid: Simple block local register allocation") ||
12349       !op.addBoolOption(
12350           '\0', "ion-eager",
12351           "Always ion-compile methods (implies --baseline-eager)") ||
12352       !op.addBoolOption('\0', "fast-warmup",
12353                         "Reduce warmup thresholds for each tier.") ||
12354       !op.addStringOption('\0', "ion-offthread-compile", "on/off",
12355                           "Compile scripts off thread (default: on)") ||
12356       !op.addStringOption('\0', "ion-parallel-compile", "on/off",
12357                           "--ion-parallel compile is deprecated. Use "
12358                           "--ion-offthread-compile.") ||
12359       !op.addBoolOption('\0', "baseline",
12360                         "Enable baseline compiler (default)") ||
12361       !op.addBoolOption('\0', "no-baseline", "Disable baseline compiler") ||
12362       !op.addBoolOption('\0', "no-warp-async",
12363                         "Don't Warp compile Async Functions") ||
12364       !op.addBoolOption('\0', "no-warp-generator",
12365                         "Warp compile Generator Functions") ||
12366       !op.addBoolOption('\0', "warp-async", "Warp compile Async Functions") ||
12367       !op.addBoolOption('\0', "warp-generator",
12368                         "Don't Warp compile Generator Functions") ||
12369       !op.addBoolOption('\0', "baseline-eager",
12370                         "Always baseline-compile methods") ||
12371       !op.addIntOption(
12372           '\0', "baseline-warmup-threshold", "COUNT",
12373           "Wait for COUNT calls or iterations before baseline-compiling "
12374           "(default: 10)",
12375           -1) ||
12376       !op.addBoolOption('\0', "blinterp",
12377                         "Enable Baseline Interpreter (default)") ||
12378       !op.addBoolOption('\0', "no-blinterp", "Disable Baseline Interpreter") ||
12379       !op.addBoolOption('\0', "blinterp-eager",
12380                         "Always Baseline-interpret scripts") ||
12381       !op.addIntOption(
12382           '\0', "blinterp-warmup-threshold", "COUNT",
12383           "Wait for COUNT calls or iterations before Baseline-interpreting "
12384           "(default: 10)",
12385           -1) ||
12386       !op.addIntOption(
12387           '\0', "trial-inlining-warmup-threshold", "COUNT",
12388           "Wait for COUNT calls or iterations before trial-inlining "
12389           "(default: 500)",
12390           -1) ||
12391       !op.addBoolOption(
12392           '\0', "non-writable-jitcode",
12393           "(NOP for fuzzers) Allocate JIT code as non-writable memory.") ||
12394       !op.addBoolOption(
12395           '\0', "no-sse3",
12396           "Pretend CPU does not support SSE3 instructions and above "
12397           "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12398       !op.addBoolOption(
12399           '\0', "no-ssse3",
12400           "Pretend CPU does not support SSSE3 [sic] instructions and above "
12401           "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12402       !op.addBoolOption(
12403           '\0', "no-sse41",
12404           "Pretend CPU does not support SSE4.1 instructions "
12405           "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12406       !op.addBoolOption('\0', "no-sse4", "Alias for --no-sse41") ||
12407       !op.addBoolOption(
12408           '\0', "no-sse42",
12409           "Pretend CPU does not support SSE4.2 instructions "
12410           "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
12411       !op.addBoolOption('\0', "enable-avx",
12412                         "AVX is disabled by default. Enable AVX. "
12413                         "(no-op on platforms other than x86 and x64).") ||
12414       !op.addBoolOption('\0', "no-avx",
12415                         "No-op. AVX is currently disabled by default.") ||
12416       !op.addBoolOption('\0', "more-compartments",
12417                         "Make newGlobal default to creating a new "
12418                         "compartment.") ||
12419       !op.addBoolOption('\0', "fuzzing-safe",
12420                         "Don't expose functions that aren't safe for "
12421                         "fuzzers to call") ||
12422 #ifdef DEBUG
12423       !op.addBoolOption('\0', "differential-testing",
12424                         "Avoid random/undefined behavior that disturbs "
12425                         "differential testing (correctness fuzzing)") ||
12426 #endif
12427       !op.addBoolOption('\0', "disable-oom-functions",
12428                         "Disable functions that cause "
12429                         "artificial OOMs") ||
12430       !op.addBoolOption('\0', "no-threads", "Disable helper threads") ||
12431 #ifdef DEBUG
12432       !op.addBoolOption('\0', "dump-entrained-variables",
12433                         "Print variables which are "
12434                         "unnecessarily entrained by inner functions") ||
12435 #endif
12436       !op.addBoolOption('\0', "no-ggc", "Disable Generational GC") ||
12437       !op.addBoolOption('\0', "no-cgc", "Disable Compacting GC") ||
12438       !op.addBoolOption('\0', "no-incremental-gc", "Disable Incremental GC") ||
12439       !op.addStringOption('\0', "nursery-strings", "on/off",
12440                           "Allocate strings in the nursery") ||
12441       !op.addStringOption('\0', "nursery-bigints", "on/off",
12442                           "Allocate BigInts in the nursery") ||
12443       !op.addIntOption('\0', "available-memory", "SIZE",
12444                        "Select GC settings based on available memory (MB)",
12445                        0) ||
12446       !op.addStringOption('\0', "arm-hwcap", "[features]",
12447                           "Specify ARM code generation features, or 'help' to "
12448                           "list all features.") ||
12449       !op.addIntOption('\0', "arm-asm-nop-fill", "SIZE",
12450                        "Insert the given number of NOP instructions at all "
12451                        "possible pool locations.",
12452                        0) ||
12453       !op.addIntOption('\0', "asm-pool-max-offset", "OFFSET",
12454                        "The maximum pc relative OFFSET permitted in pool "
12455                        "reference instructions.",
12456                        1024) ||
12457       !op.addBoolOption('\0', "arm-sim-icache-checks",
12458                         "Enable icache flush checks in the ARM "
12459                         "simulator.") ||
12460       !op.addIntOption('\0', "arm-sim-stop-at", "NUMBER",
12461                        "Stop the ARM simulator after the given "
12462                        "NUMBER of instructions.",
12463                        -1) ||
12464       !op.addBoolOption('\0', "mips-sim-icache-checks",
12465                         "Enable icache flush checks in the MIPS "
12466                         "simulator.") ||
12467       !op.addIntOption('\0', "mips-sim-stop-at", "NUMBER",
12468                        "Stop the MIPS simulator after the given "
12469                        "NUMBER of instructions.",
12470                        -1) ||
12471       !op.addIntOption('\0', "nursery-size", "SIZE-MB",
12472                        "Set the maximum nursery size in MB",
12473                        JS::DefaultNurseryMaxBytes / 1024 / 1024) ||
12474 #ifdef JS_GC_ZEAL
12475       !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
12476                           gc::ZealModeHelpText) ||
12477 #else
12478       !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
12479                           "option ignored in non-gc-zeal builds") ||
12480 #endif
12481       !op.addStringOption('\0', "module-load-path", "DIR",
12482                           "Set directory to load modules from") ||
12483       !op.addBoolOption('\0', "no-source-pragmas",
12484                         "Disable source(Mapping)URL pragma parsing") ||
12485       !op.addBoolOption('\0', "no-async-stacks", "Disable async stacks") ||
12486       !op.addBoolOption('\0', "async-stacks-capture-debuggee-only",
12487                         "Limit async stack capture to only debuggees") ||
12488       !op.addMultiStringOption('\0', "dll", "LIBRARY",
12489                                "Dynamically load LIBRARY") ||
12490       !op.addBoolOption('\0', "suppress-minidump",
12491                         "Suppress crash minidumps") ||
12492 #ifdef JS_ENABLE_SMOOSH
12493       !op.addBoolOption('\0', "smoosh", "Use SmooshMonkey") ||
12494       !op.addStringOption('\0', "not-implemented-watchfile", "[filename]",
12495                           "Track NotImplemented errors in the new frontend") ||
12496 #else
12497       !op.addBoolOption('\0', "smoosh", "No-op") ||
12498 #endif
12499       !op.addBoolOption('\0', "wasm-compile-and-serialize",
12500                         "Compile the wasm bytecode from stdin and serialize "
12501                         "the results to stdout") ||
12502 #ifdef FUZZING_JS_FUZZILLI
12503       !op.addBoolOption('\0', "reprl", "Enable REPRL mode for fuzzing") ||
12504 #endif
12505       !op.addStringOption('\0', "telemetry-dir", "[directory]",
12506                           "Output telemetry results in a directory") ||
12507       !op.addBoolOption('\0', "use-fdlibm-for-sin-cos-tan",
12508                         "Use fdlibm for Math.sin, Math.cos, and Math.tan")) {
12509     return EXIT_FAILURE;
12510   }
12511 
12512   op.setArgTerminatesOptions("script", true);
12513   op.setArgCapturesRest("scriptArgs");
12514 
12515   switch (op.parseArgs(argc, argv)) {
12516     case OptionParser::EarlyExit:
12517       return EXIT_SUCCESS;
12518     case OptionParser::ParseError:
12519       op.printHelp(argv[0]);
12520       return EXIT_FAILURE;
12521     case OptionParser::Fail:
12522       return EXIT_FAILURE;
12523     case OptionParser::Okay:
12524       break;
12525   }
12526 
12527   if (op.getHelpOption()) {
12528     return EXIT_SUCCESS;
12529   }
12530 
12531   // Record aggregated telemetry data on disk. Do this as early as possible such
12532   // that the telemetry is recording both before starting the context and after
12533   // closing it.
12534   if (op.getStringOption("telemetry-dir")) {
12535     if (!telemetryLock) {
12536       telemetryLock = js_new<Mutex>(mutexid::ShellTelemetry);
12537       if (!telemetryLock) {
12538         return EXIT_FAILURE;
12539       }
12540     }
12541   }
12542   auto writeTelemetryResults = MakeScopeExit([&op] {
12543     if (const char* dir = op.getStringOption("telemetry-dir")) {
12544       WriteTelemetryDataToDisk(dir);
12545       js_free(telemetryLock);
12546       telemetryLock = nullptr;
12547     }
12548   });
12549 
12550   /*
12551    * Allow dumping on Linux with the fuzzing flag set, even when running with
12552    * the suid/sgid flag set on the shell.
12553    */
12554 #ifdef XP_LINUX
12555   if (op.getBoolOption("fuzzing-safe")) {
12556     prctl(PR_SET_DUMPABLE, 1);
12557   }
12558 #endif
12559 
12560 #ifdef DEBUG
12561   /*
12562    * Process OOM options as early as possible so that we can observe as many
12563    * allocations as possible.
12564    */
12565   OOM_printAllocationCount = op.getBoolOption('O');
12566 #endif
12567 
12568   if (op.getBoolOption("no-threads")) {
12569     js::DisableExtraThreads();
12570   }
12571 
12572   enableCodeCoverage = op.getBoolOption("code-coverage");
12573   if (enableCodeCoverage) {
12574     js::EnableCodeCoverage();
12575   }
12576 
12577   if (const char* xdr = op.getStringOption("selfhosted-xdr-path")) {
12578     shell::selfHostedXDRPath = xdr;
12579   }
12580   if (const char* opt = op.getStringOption("selfhosted-xdr-mode")) {
12581     if (strcmp(opt, "encode") == 0) {
12582       shell::encodeSelfHostedCode = true;
12583     } else if (strcmp(opt, "decode") == 0) {
12584       shell::encodeSelfHostedCode = false;
12585     } else if (strcmp(opt, "off") == 0) {
12586       shell::selfHostedXDRPath = nullptr;
12587     } else {
12588       MOZ_CRASH(
12589           "invalid option value for --selfhosted-xdr-mode, must be "
12590           "encode/decode");
12591     }
12592   }
12593 
12594 #ifdef JS_WITHOUT_NSPR
12595   if (!op.getMultiStringOption("dll").empty()) {
12596     fprintf(stderr, "Error: --dll requires NSPR support!\n");
12597     return EXIT_FAILURE;
12598   }
12599 #else
12600   AutoLibraryLoader loader;
12601   MultiStringRange dllPaths = op.getMultiStringOption("dll");
12602   while (!dllPaths.empty()) {
12603     char* path = dllPaths.front();
12604     loader.load(path);
12605     dllPaths.popFront();
12606   }
12607 #endif
12608 
12609   if (op.getBoolOption("suppress-minidump")) {
12610     js::NoteIntentionalCrash();
12611   }
12612 
12613   if (!InitSharedObjectMailbox()) {
12614     return 1;
12615   }
12616 
12617   JS::SetProcessBuildIdOp(ShellBuildId);
12618 
12619   // The fake CPU count must be set before initializing the Runtime,
12620   // which spins up the thread pool.
12621   int32_t cpuCount = op.getIntOption("cpu-count");  // What we're really setting
12622   if (cpuCount < 0) {
12623     cpuCount = op.getIntOption("thread-count");  // Legacy name
12624   }
12625   if (cpuCount >= 0 && !SetFakeCPUCount(cpuCount)) {
12626     return 1;
12627   }
12628 
12629   /* Use the same parameters as the browser in xpcjsruntime.cpp. */
12630   JSContext* const cx = JS_NewContext(JS::DefaultHeapMaxBytes);
12631   if (!cx) {
12632     return 1;
12633   }
12634 
12635   // Register telemetry callbacks, if needed.
12636   if (telemetryLock) {
12637     JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryDataCallback);
12638   }
12639 
12640   size_t nurseryBytes = op.getIntOption("nursery-size") * 1024L * 1024L;
12641   if (nurseryBytes == 0) {
12642     fprintf(stderr, "Error: --nursery-size parameter must be non-zero.\n");
12643     fprintf(stderr,
12644             "The nursery can be disabled by passing the --no-ggc option.\n");
12645     return EXIT_FAILURE;
12646   }
12647   JS_SetGCParameter(cx, JSGC_MAX_NURSERY_BYTES, nurseryBytes);
12648 
12649   auto destroyCx = MakeScopeExit([cx] { JS_DestroyContext(cx); });
12650 
12651   UniquePtr<ShellContext> sc = MakeUnique<ShellContext>(cx);
12652   if (!sc) {
12653     return 1;
12654   }
12655   auto destroyShellContext = MakeScopeExit([cx, &sc] {
12656     // Must clear out some of sc's pointer containers before JS_DestroyContext.
12657     sc->markObservers.reset();
12658 
12659     JS_SetContextPrivate(cx, nullptr);
12660     sc.reset();
12661   });
12662 
12663   JS_SetContextPrivate(cx, sc.get());
12664   JS_SetGrayGCRootsTracer(cx, TraceGrayRoots, nullptr);
12665   auto resetGrayGCRootsTracer =
12666       MakeScopeExit([cx] { JS_SetGrayGCRootsTracer(cx, nullptr, nullptr); });
12667 
12668   // Waiting is allowed on the shell's main thread, for now.
12669   JS_SetFutexCanWait(cx);
12670   JS::SetWarningReporter(cx, WarningReporter);
12671   if (!SetContextOptions(cx, op)) {
12672     return 1;
12673   }
12674 
12675   JS_SetGCParameter(cx, JSGC_MAX_BYTES, 0xffffffff);
12676 
12677   size_t availMem = op.getIntOption("available-memory");
12678   if (availMem > 0) {
12679     JS_SetGCParametersBasedOnAvailableMemory(cx, availMem);
12680   }
12681 
12682   JS_SetTrustedPrincipals(cx, &ShellPrincipals::fullyTrusted);
12683   JS_SetSecurityCallbacks(cx, &ShellPrincipals::securityCallbacks);
12684   JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy);
12685   JS_SetDestroyCompartmentCallback(cx, DestroyShellCompartmentPrivate);
12686   JS::SetSourceElementCallback(cx, ShellSourceElementCallback);
12687 
12688   js::SetWindowProxyClass(cx, &ShellWindowProxyClass);
12689 
12690   JS_AddInterruptCallback(cx, ShellInterruptCallback);
12691 
12692   bufferStreamState = js_new<ExclusiveWaitableData<BufferStreamState>>(
12693       mutexid::BufferStreamState);
12694   if (!bufferStreamState) {
12695     return 1;
12696   }
12697   auto shutdownBufferStreams = MakeScopeExit([] {
12698     ShutdownBufferStreams();
12699     js_delete(bufferStreamState);
12700   });
12701   JS::InitConsumeStreamCallback(cx, ConsumeBufferSource, ReportStreamError);
12702 
12703   JS::SetPromiseRejectionTrackerCallback(
12704       cx, ForwardingPromiseRejectionTrackerCallback);
12705 
12706   JS::dbg::SetDebuggerMallocSizeOf(cx, moz_malloc_size_of);
12707 
12708   js::UseInternalJobQueues(cx);
12709 
12710   JS::SetHostCleanupFinalizationRegistryCallback(
12711       cx, ShellCleanupFinalizationRegistryCallback, sc.get());
12712 
12713   auto shutdownShellThreads = MakeScopeExit([cx] {
12714     KillWatchdog(cx);
12715     KillWorkerThreads(cx);
12716     DestructSharedObjectMailbox();
12717     CancelOffThreadJobsForRuntime(cx);
12718   });
12719 
12720   if (const char* opt = op.getStringOption("nursery-strings")) {
12721     if (strcmp(opt, "on") == 0) {
12722       cx->runtime()->gc.nursery().enableStrings();
12723     } else if (strcmp(opt, "off") == 0) {
12724       cx->runtime()->gc.nursery().disableStrings();
12725     } else {
12726       MOZ_CRASH("invalid option value for --nursery-strings, must be on/off");
12727     }
12728   }
12729 
12730   if (const char* opt = op.getStringOption("nursery-bigints")) {
12731     if (strcmp(opt, "on") == 0) {
12732       cx->runtime()->gc.nursery().enableBigInts();
12733     } else if (strcmp(opt, "off") == 0) {
12734       cx->runtime()->gc.nursery().disableBigInts();
12735     } else {
12736       MOZ_CRASH("invalid option value for --nursery-bigints, must be on/off");
12737     }
12738   }
12739 
12740   // The file content should stay alive as long as Worker thread can be
12741   // initialized.
12742   JS::SelfHostedCache xdrSpan = nullptr;
12743   JS::SelfHostedWriter xdrWriter = nullptr;
12744   if (selfHostedXDRPath) {
12745     if (encodeSelfHostedCode) {
12746       xdrWriter = WriteSelfHostedXDRFile;
12747     } else {
12748       selfHostedXDRBuffer.emplace(cx);
12749       if (ReadSelfHostedXDRFile(cx, *selfHostedXDRBuffer)) {
12750         MOZ_ASSERT(selfHostedXDRBuffer->length() > 0);
12751         JS::SelfHostedCache span(selfHostedXDRBuffer->begin(),
12752                                  selfHostedXDRBuffer->end());
12753         xdrSpan = span;
12754       } else {
12755         fprintf(stderr, "Falling back on parsing source.\n");
12756         selfHostedXDRPath = nullptr;
12757       }
12758     }
12759   }
12760 
12761 #ifndef __wasi__
12762   // This must be set before self-hosted code is initialized, as self-hosted
12763   // code reads the property and the property may not be changed later.
12764   bool disabledHugeMemory = false;
12765   if (op.getBoolOption("disable-wasm-huge-memory")) {
12766     disabledHugeMemory = JS::DisableWasmHugeMemory();
12767     MOZ_RELEASE_ASSERT(disabledHugeMemory);
12768   }
12769 #endif
12770 
12771   if (!JS::InitSelfHostedCode(cx, xdrSpan, xdrWriter)) {
12772     return 1;
12773   }
12774 
12775   EnvironmentPreparer environmentPreparer(cx);
12776 
12777   bool incrementalGC = !op.getBoolOption("no-incremental-gc");
12778   JS_SetGCParameter(cx, JSGC_INCREMENTAL_GC_ENABLED, incrementalGC);
12779   JS_SetGCParameter(cx, JSGC_SLICE_TIME_BUDGET_MS, 10);
12780 
12781   JS_SetGCParameter(cx, JSGC_PER_ZONE_GC_ENABLED, true);
12782 
12783   JS::SetProcessLargeAllocationFailureCallback(my_LargeAllocFailCallback);
12784 
12785   js::SetPreserveWrapperCallbacks(cx, DummyPreserveWrapperCallback,
12786                                   DummyHasReleasedWrapperCallback);
12787 
12788   // Fish around in `op` for various important compiler-configuration flags
12789   // and make sure they get handed on to any child processes we might create.
12790   // See bug 1700900.  Semantically speaking, this is all rather dubious:
12791   //
12792   // * What set of flags need to be propagated in order to guarantee that the
12793   //   child produces code that is "compatible" (in whatever sense) with that
12794   //   produced by the parent?  This isn't always easy to determine.
12795   //
12796   // * There's nothing that ensures that flags given to the child are
12797   //   presented in the same order that they exist in the parent's `argv[]`.
12798   //   That could be a problem in the case where two flags with contradictory
12799   //   meanings are given, and they are presented to the child in the opposite
12800   //   order.  For example: --wasm-compiler=optimizing --wasm-compiler=baseline.
12801 
12802 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
12803   if (op.getBoolOption("no-sse3")) {
12804     js::jit::CPUInfo::SetSSE3Disabled();
12805     if (!sCompilerProcessFlags.append("--no-sse3")) {
12806       return EXIT_FAILURE;
12807     }
12808   }
12809   if (op.getBoolOption("no-ssse3")) {
12810     js::jit::CPUInfo::SetSSSE3Disabled();
12811     if (!sCompilerProcessFlags.append("--no-ssse3")) {
12812       return EXIT_FAILURE;
12813     }
12814   }
12815   if (op.getBoolOption("no-sse4") || op.getBoolOption("no-sse41")) {
12816     js::jit::CPUInfo::SetSSE41Disabled();
12817     if (!sCompilerProcessFlags.append("--no-sse41")) {
12818       return EXIT_FAILURE;
12819     }
12820   }
12821   if (op.getBoolOption("no-sse42")) {
12822     js::jit::CPUInfo::SetSSE42Disabled();
12823     if (!sCompilerProcessFlags.append("--no-sse42")) {
12824       return EXIT_FAILURE;
12825     }
12826   }
12827   if (op.getBoolOption("enable-avx")) {
12828     js::jit::CPUInfo::SetAVXEnabled();
12829     if (!sCompilerProcessFlags.append("--enable-avx")) {
12830       return EXIT_FAILURE;
12831     }
12832     // Disable AVX completely for now.  We're not supporting AVX and things
12833     // break easily if asking for AVX.
12834     fprintf(stderr, "Error: AVX encodings are currently disabled\n");
12835     return EXIT_FAILURE;
12836   }
12837 #endif
12838 
12839 #ifndef __wasi__
12840   // --disable-wasm-huge-memory needs to be propagated.  See bug 1518210.
12841   if (disabledHugeMemory &&
12842       !sCompilerProcessFlags.append("--disable-wasm-huge-memory")) {
12843     return EXIT_FAILURE;
12844   }
12845 
12846   // Also the following are to be propagated.
12847   const char* to_propagate[] = {
12848 #  define WASM_DEFAULT_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, \
12849                                FLAG_PRED, SHELL, ...)                         \
12850     "--no-wasm-" SHELL,
12851 #  define WASM_EXPERIMENTAL_FEATURE(NAME, LOWER_NAME, COMPILE_PRED,       \
12852                                     COMPILER_PRED, FLAG_PRED, SHELL, ...) \
12853     "--wasm-" SHELL,
12854       JS_FOR_WASM_FEATURES(WASM_DEFAULT_FEATURE, WASM_EXPERIMENTAL_FEATURE)
12855 #  undef WASM_DEFAULT_FEATURE
12856 #  undef WASM_EXPERIMENTAL_FEATURE
12857       // Feature selection options
12858       "--wasm-simd-wormhole",
12859       // Compiler selection options
12860       "--test-wasm-await-tier2", NULL};
12861   for (const char** p = &to_propagate[0]; *p; p++) {
12862     if (op.getBoolOption(&(*p)[2] /* 2 => skip the leading '--' */)) {
12863       if (!sCompilerProcessFlags.append(*p)) {
12864         return EXIT_FAILURE;
12865       }
12866     }
12867   }
12868 
12869   // Also --wasm-compiler= is to be propagated.  This is tricky because it is
12870   // necessary to reconstitute the --wasm-compiler=<whatever> string from its
12871   // pieces, without causing a leak.  Hence it is copied into a static buffer.
12872   // This is thread-unsafe, but we're in `main()` and on the process' root
12873   // thread.  Also, we do this only once -- it wouldn't work properly if we
12874   // handled multiple --wasm-compiler= flags in a loop.
12875   const char* wasm_compiler = op.getStringOption("wasm-compiler");
12876   if (wasm_compiler) {
12877     size_t n_needed =
12878         2 + strlen("wasm-compiler") + 1 + strlen(wasm_compiler) + 1;
12879     const size_t n_avail = 128;
12880     static char buf[n_avail];
12881     // `n_needed` depends on the compiler name specified.  However, it can't
12882     // be arbitrarily long, since previous flag-checking should have limited
12883     // it to a set of known possibilities: "baseline", "ion", "cranelift",
12884     // "baseline+ion", "baseline+cranelift", etc.  Still, assert this for
12885     // safety.
12886     MOZ_RELEASE_ASSERT(n_needed < n_avail);
12887     memset(buf, 0, sizeof(buf));
12888     SprintfBuf(buf, n_avail, "--%s=%s", "wasm-compiler", wasm_compiler);
12889     if (!sCompilerProcessFlags.append(buf)) {
12890       return EXIT_FAILURE;
12891     }
12892   }
12893 #endif  // __wasi__
12894 
12895   result = Shell(cx, &op);
12896 
12897 #ifdef DEBUG
12898   if (OOM_printAllocationCount) {
12899     printf("OOM max count: %" PRIu64 "\n", js::oom::simulator.counter());
12900   }
12901 #endif
12902 
12903   return result;
12904 }
12905