tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

js.cpp (451622B)


      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/AlreadyAddRefed.h"  // mozilla::already_AddRefed
     10 #include "mozilla/ArrayUtils.h"
     11 #include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_ASSERT_IF, MOZ_RELEASE_ASSERT, MOZ_CRASH
     12 #include "mozilla/Atomics.h"
     13 #include "mozilla/Attributes.h"
     14 #include "mozilla/Compression.h"
     15 #include "mozilla/DebugOnly.h"
     16 #include "mozilla/EnumSet.h"
     17 #include "mozilla/IntegerPrintfMacros.h"
     18 #include "mozilla/mozalloc.h"
     19 #include "mozilla/PodOperations.h"
     20 #include "mozilla/RandomNum.h"
     21 #include "mozilla/RefPtr.h"
     22 #include "mozilla/ScopeExit.h"
     23 #include "mozilla/Sprintf.h"
     24 #include "mozilla/TimeStamp.h"
     25 #include "mozilla/UniquePtrExtensions.h"  // UniqueFreePtr
     26 #include "mozilla/Utf8.h"
     27 #include "mozilla/Variant.h"
     28 
     29 #include <algorithm>
     30 #include <cctype>
     31 #include <chrono>
     32 #ifdef XP_WIN
     33 #  include <direct.h>
     34 #  include <process.h>
     35 #endif
     36 #include <errno.h>
     37 #include <fcntl.h>
     38 #if defined(XP_WIN)
     39 #  include <io.h> /* for isatty() */
     40 #endif
     41 #include <locale.h>
     42 #if defined(MALLOC_H)
     43 #  include MALLOC_H /* for malloc_usable_size, malloc_size, _msize */
     44 #endif
     45 #include <ctime>
     46 #include <math.h>
     47 #ifndef __wasi__
     48 #  include <signal.h>
     49 #endif
     50 #include <stdio.h>
     51 #include <stdlib.h>
     52 #include <string.h>
     53 #include <sys/stat.h>
     54 #include <sys/types.h>
     55 #include <utility>
     56 #ifdef XP_UNIX
     57 #  ifndef __wasi__
     58 #    include <sys/mman.h>
     59 #    include <sys/wait.h>
     60 #  endif
     61 #  include <sys/stat.h>
     62 #  include <unistd.h>
     63 #endif
     64 #ifdef XP_LINUX
     65 #  include <sys/prctl.h>
     66 #endif
     67 
     68 #include "jsapi.h"
     69 #include "jsfriendapi.h"
     70 #include "jstypes.h"
     71 #include "fmt/format.h"
     72 #ifndef JS_WITHOUT_NSPR
     73 #  include "prerror.h"
     74 #  include "prlink.h"
     75 #endif
     76 
     77 #include "builtin/Array.h"
     78 #include "builtin/MapObject.h"
     79 #include "builtin/ModuleObject.h"
     80 #include "builtin/RegExp.h"
     81 #include "builtin/TestingFunctions.h"
     82 #include "builtin/TestingUtility.h"  // js::ParseCompileOptions, js::ParseDebugMetadata, js::CreateScriptPrivate
     83 #include "debugger/DebugAPI.h"
     84 #include "frontend/CompilationStencil.h"
     85 #include "frontend/FrontendContext.h"  // AutoReportFrontendContext
     86 #include "frontend/ModuleSharedContext.h"
     87 #include "frontend/Parser.h"
     88 #include "frontend/ScopeBindingCache.h"  // js::frontend::ScopeBindingCache
     89 #include "gc/GC.h"
     90 #include "gc/PublicIterators.h"
     91 #ifdef DEBUG
     92 #  include "irregexp/RegExpAPI.h"
     93 #endif
     94 
     95 #ifdef JS_SIMULATOR_ARM
     96 #  include "jit/arm/Simulator-arm.h"
     97 #endif
     98 #ifdef JS_SIMULATOR_MIPS64
     99 #  include "jit/mips64/Simulator-mips64.h"
    100 #endif
    101 #ifdef JS_SIMULATOR_LOONG64
    102 #  include "jit/loong64/Simulator-loong64.h"
    103 #endif
    104 #ifdef JS_SIMULATOR_RISCV64
    105 #  include "jit/riscv64/Simulator-riscv64.h"
    106 #endif
    107 #include "jit/BaselineCompileQueue.h"
    108 #include "jit/CacheIRHealth.h"
    109 #include "jit/InlinableNatives.h"
    110 #include "jit/Ion.h"
    111 #include "jit/JitcodeMap.h"
    112 #include "jit/JitZone.h"
    113 #include "jit/shared/CodeGenerator-shared.h"
    114 #ifdef JS_CODEGEN_ARM64
    115 #  include "jit/arm64/vixl/Cpu-Features-vixl.h"
    116 #endif
    117 #include "js/Array.h"        // JS::NewArrayObject
    118 #include "js/ArrayBuffer.h"  // JS::{CreateMappedArrayBufferContents,NewMappedArrayBufferWithContents,IsArrayBufferObject,GetArrayBufferLengthAndData}
    119 #include "js/BuildId.h"      // JS::BuildIdCharVector, JS::SetProcessBuildIdOp
    120 #include "js/CallAndConstruct.h"  // JS::Call, JS::IsCallable, JS_CallFunction, JS_CallFunctionValue
    121 #include "js/CharacterEncoding.h"  // JS::StringIsASCII
    122 #include "js/CompilationAndEvaluation.h"
    123 #include "js/CompileOptions.h"  // JS::ReadOnlyCompileOptions, JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::InstantiateOptions
    124 #include "js/ContextOptions.h"  // JS::ContextOptions{,Ref}
    125 #include "js/Debug.h"  // JS::dbg::ShouldAvoidSideEffects, JS::ExecutionTrace
    126 #include "js/EnvironmentChain.h"            // JS::EnvironmentChain
    127 #include "js/Equality.h"                    // JS::SameValue
    128 #include "js/ErrorReport.h"                 // JS::PrintError
    129 #include "js/Exception.h"                   // JS::StealPendingExceptionStack
    130 #include "js/experimental/BindingAllocs.h"  // JS_NewObjectWithGivenProtoAndUseAllocSite
    131 #include "js/experimental/CodeCoverage.h"   // js::EnableCodeCoverage
    132 #include "js/experimental/CompileScript.h"  // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::HadFrontendErrors, JS::ConvertFrontendErrorsToRuntimeErrors, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil
    133 #include "js/experimental/CTypes.h"         // JS::InitCTypesClass
    134 #include "js/experimental/Intl.h"  // JS::AddMoz{DateTimeFormat,DisplayNames}Constructor
    135 #include "js/experimental/JitInfo.h"  // JSJit{Getter,Setter,Method}CallArgs, JSJitGetterInfo, JSJit{Getter,Setter}Op, JSJitInfo
    136 #include "js/experimental/JSStencil.h"  // JS::Stencil, JS::DecodeStencil, JS::InstantiateModuleStencil
    137 #include "js/experimental/SourceHook.h"  // js::{Set,Forget,}SourceHook
    138 #include "js/experimental/TypedData.h"   // JS_NewUint8Array
    139 #include "js/friend/DumpFunctions.h"     // JS::FormatStackDump
    140 #include "js/friend/ErrorMessages.h"     // js::GetErrorMessage, JSMSG_*
    141 #include "js/friend/StackLimits.h"       // js::AutoCheckRecursionLimit
    142 #include "js/friend/WindowProxy.h"  // js::IsWindowProxy, js::SetWindowProxyClass, js::ToWindowProxyIfWindow, js::ToWindowIfWindowProxy
    143 #include "js/GCAPI.h"               // JS::AutoCheckCannotGC
    144 #include "js/GCVector.h"
    145 #include "js/GlobalObject.h"
    146 #include "js/Initialization.h"
    147 #include "js/Interrupt.h"
    148 #include "js/JSON.h"
    149 #include "js/MemoryCallbacks.h"
    150 #include "js/MemoryFunctions.h"
    151 #include "js/Modules.h"  // JS::GetModulePrivate, JS::SetModule{DynamicImport,Metadata,Resolve}Hook, JS::SetModulePrivate, JS::CompileModule
    152 #include "js/Object.h"  // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
    153 #include "js/Prefs.h"
    154 #include "js/Principals.h"
    155 #include "js/Printer.h"  // QuoteString
    156 #include "js/Printf.h"
    157 #include "js/PropertyAndElement.h"  // JS_DefineElement, JS_DefineFunction, JS_DefineFunctions, JS_DefineProperties, JS_DefineProperty, JS_GetElement, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty, JS_SetPropertyById
    158 #include "js/PropertySpec.h"
    159 #include "js/Realm.h"
    160 #include "js/RegExp.h"  // JS::ObjectIsRegExp
    161 #include "js/ScriptPrivate.h"
    162 #include "js/SourceText.h"  // JS::SourceText
    163 #include "js/StableStringChars.h"
    164 #include "js/Stack.h"
    165 #include "js/StreamConsumer.h"
    166 #include "js/StructuredClone.h"
    167 #include "js/SweepingAPI.h"
    168 #include "js/Transcoding.h"  // JS::TranscodeBuffer, JS::TranscodeRange, JS::IsTranscodeFailureResult
    169 #include "js/Warnings.h"      // JS::SetWarningReporter
    170 #include "js/WasmFeatures.h"  // JS_FOR_WASM_FEATURES
    171 #include "js/WasmModule.h"    // JS::WasmModule
    172 #include "js/Wrapper.h"
    173 #include "proxy/DeadObjectProxy.h"  // js::IsDeadProxyObject
    174 #include "shell/jsoptparse.h"
    175 #include "shell/jsshell.h"
    176 #include "shell/OSObject.h"
    177 #include "shell/ShellModuleObjectWrapper.h"
    178 #include "shell/WasmTesting.h"
    179 #include "threading/ConditionVariable.h"
    180 #include "threading/ExclusiveData.h"
    181 #include "threading/LockGuard.h"
    182 #include "threading/Thread.h"
    183 #include "util/CompleteFile.h"  // js::FileContents, js::ReadCompleteFile
    184 #include "util/DifferentialTesting.h"
    185 #include "util/StringBuilder.h"
    186 #include "util/Text.h"
    187 #include "util/WindowsWrapper.h"
    188 #include "vm/ArgumentsObject.h"
    189 #include "vm/Compression.h"
    190 #include "vm/ErrorObject.h"
    191 #include "vm/ErrorReporting.h"
    192 #include "vm/HelperThreads.h"
    193 #include "vm/JSAtomUtils.h"  // AtomizeUTF8Chars, AtomizeString, ToAtom
    194 #include "vm/JSContext.h"
    195 #include "vm/JSFunction.h"
    196 #include "vm/JSObject.h"
    197 #include "vm/JSScript.h"
    198 #include "vm/Logging.h"
    199 #include "vm/ModuleBuilder.h"  // js::ModuleBuilder
    200 #include "vm/Modules.h"
    201 #include "vm/Monitor.h"
    202 #include "vm/MutexIDs.h"
    203 #include "vm/PromiseObject.h"  // js::PromiseObject
    204 #include "vm/Shape.h"
    205 #include "vm/SharedArrayObject.h"
    206 #include "vm/StencilObject.h"  // js::StencilObject
    207 #include "vm/Time.h"
    208 #include "vm/ToSource.h"  // js::ValueToSource
    209 #include "vm/TypedArrayObject.h"
    210 #include "vm/WrapperObject.h"
    211 #include "wasm/WasmFeatures.h"
    212 #include "wasm/WasmJS.h"
    213 
    214 #include "gc/WeakMap-inl.h"
    215 #include "vm/Compartment-inl.h"
    216 #include "vm/ErrorObject-inl.h"
    217 #include "vm/Interpreter-inl.h"
    218 #include "vm/JSObject-inl.h"
    219 #include "vm/Realm-inl.h"
    220 #include "vm/Stack-inl.h"
    221 
    222 #undef compress
    223 
    224 using namespace js;
    225 using namespace js::cli;
    226 using namespace js::shell;
    227 
    228 using JS::AutoStableStringChars;
    229 using JS::CompileOptions;
    230 
    231 using js::shell::RCFile;
    232 
    233 using mozilla::ArrayEqual;
    234 using mozilla::AsVariant;
    235 using mozilla::Atomic;
    236 using mozilla::MakeScopeExit;
    237 using mozilla::Maybe;
    238 using mozilla::Nothing;
    239 using mozilla::NumberEqualsInt32;
    240 using mozilla::TimeDuration;
    241 using mozilla::TimeStamp;
    242 using mozilla::Utf8Unit;
    243 using mozilla::Variant;
    244 
    245 bool InitOptionParser(OptionParser& op);
    246 bool SetGlobalOptionsPreJSInit(const OptionParser& op);
    247 bool SetGlobalOptionsPostJSInit(const OptionParser& op);
    248 bool SetContextOptions(JSContext* cx, const OptionParser& op);
    249 bool SetContextWasmOptions(JSContext* cx, const OptionParser& op);
    250 bool SetContextJITOptions(JSContext* cx, const OptionParser& op);
    251 bool SetContextGCOptions(JSContext* cx, const OptionParser& op);
    252 bool InitModuleLoader(JSContext* cx, const OptionParser& op);
    253 
    254 #ifdef FUZZING_JS_FUZZILLI
    255 #  define REPRL_CRFD 100
    256 #  define REPRL_CWFD 101
    257 #  define REPRL_DRFD 102
    258 #  define REPRL_DWFD 103
    259 
    260 #  define SHM_SIZE 0x100000
    261 #  define MAX_EDGES ((SHM_SIZE - 4) * 8)
    262 
    263 struct shmem_data {
    264  uint32_t num_edges;
    265  unsigned char edges[];
    266 };
    267 
    268 struct shmem_data* __shmem;
    269 
    270 uint32_t *__edges_start, *__edges_stop;
    271 void __sanitizer_cov_reset_edgeguards() {
    272  uint64_t N = 0;
    273  for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++)
    274    *x = ++N;
    275 }
    276 
    277 extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start,
    278                                                    uint32_t* stop) {
    279  // Avoid duplicate initialization
    280  if (start == stop || *start) return;
    281 
    282  if (__edges_start != NULL || __edges_stop != NULL) {
    283    fprintf(stderr,
    284            "Coverage instrumentation is only supported for a single module\n");
    285    _exit(-1);
    286  }
    287 
    288  __edges_start = start;
    289  __edges_stop = stop;
    290 
    291  // Map the shared memory region
    292  const char* shm_key = getenv("SHM_ID");
    293  if (!shm_key) {
    294    puts("[COV] no shared memory bitmap available, skipping");
    295    __shmem = (struct shmem_data*)malloc(SHM_SIZE);
    296  } else {
    297    int fd = shm_open(shm_key, O_RDWR, S_IREAD | S_IWRITE);
    298    if (fd <= -1) {
    299      fprintf(stderr, "Failed to open shared memory region: %s\n",
    300              strerror(errno));
    301      _exit(-1);
    302    }
    303 
    304    __shmem = (struct shmem_data*)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE,
    305                                       MAP_SHARED, fd, 0);
    306    if (__shmem == MAP_FAILED) {
    307      fprintf(stderr, "Failed to mmap shared memory region\n");
    308      _exit(-1);
    309    }
    310  }
    311 
    312  __sanitizer_cov_reset_edgeguards();
    313 
    314  __shmem->num_edges = stop - start;
    315  printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n",
    316         shm_key, __shmem->num_edges);
    317 }
    318 
    319 extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
    320  // There's a small race condition here: if this function executes in two
    321  // threads for the same edge at the same time, the first thread might disable
    322  // the edge (by setting the guard to zero) before the second thread fetches
    323  // the guard value (and thus the index). However, our instrumentation ignores
    324  // the first edge (see libcoverage.c) and so the race is unproblematic.
    325  uint32_t index = *guard;
    326  // If this function is called before coverage instrumentation is properly
    327  // initialized we want to return early.
    328  if (!index) return;
    329  __shmem->edges[index / 8] |= 1 << (index % 8);
    330  *guard = 0;
    331 }
    332 #endif /* FUZZING_JS_FUZZILLI */
    333 
    334 enum JSShellExitCode {
    335  EXITCODE_RUNTIME_ERROR = 3,
    336  EXITCODE_FILE_NOT_FOUND = 4,
    337  EXITCODE_OUT_OF_MEMORY = 5,
    338  EXITCODE_TIMEOUT = 6
    339 };
    340 
    341 struct ShellLogModule {
    342  // Since ShellLogModules have references to their levels created
    343  // we can't move them.
    344  ShellLogModule(ShellLogModule&&) = delete;
    345 
    346  const char* name;
    347  explicit ShellLogModule(const char* name) : name(name) {}
    348  mozilla::AtomicLogLevel level;
    349 };
    350 
    351 // If asserts related to this ever fail, simply bump this number.
    352 //
    353 // This is used to construct a mozilla::Array, which is used because a
    354 // ShellLogModule cannot move once constructed to avoid invalidating
    355 // a levelRef.
    356 static const int MAX_LOG_MODULES = 64;
    357 static int initialized_modules = 0;
    358 mozilla::Array<mozilla::Maybe<ShellLogModule>, MAX_LOG_MODULES> logModules;
    359 
    360 JS::OpaqueLogger GetLoggerByName(const char* name) {
    361  // Check for pre-existing module
    362  for (auto& logger : logModules) {
    363    if (logger) {
    364      if (logger->name == name) {
    365        return logger.ptr();
    366      }
    367    }
    368    // We've seen all initialized, not there, break out.
    369    if (!logger) break;
    370  }
    371 
    372  // Not found, allocate a new module.
    373  MOZ_RELEASE_ASSERT(initialized_modules < MAX_LOG_MODULES - 1);
    374  auto index = initialized_modules++;
    375  logModules[index].emplace(name);
    376  return logModules[index].ptr();
    377 }
    378 
    379 mozilla::AtomicLogLevel& GetLevelRef(JS::OpaqueLogger logger) {
    380  ShellLogModule* slm = static_cast<ShellLogModule*>(logger);
    381  return slm->level;
    382 }
    383 
    384 void LogPrintVA(const JS::OpaqueLogger logger, mozilla::LogLevel level,
    385                const char* fmt, va_list ap) {
    386  ShellLogModule* mod = static_cast<ShellLogModule*>(logger);
    387  fprintf(stderr, "[%s] ", mod->name);
    388  vfprintf(stderr, fmt, ap);
    389  fprintf(stderr, "\n");
    390 }
    391 
    392 void LogPrintFmt(const JS::OpaqueLogger logger, mozilla::LogLevel level,
    393                 fmt::string_view fmt, fmt::format_args args) {
    394  ShellLogModule* mod = static_cast<ShellLogModule*>(logger);
    395  fmt::print(stderr, FMT_STRING("[{}] {}\n"), mod->name,
    396             fmt::vformat(fmt, args));
    397 }
    398 
    399 JS::LoggingInterface shellLoggingInterface = {GetLoggerByName, LogPrintVA,
    400                                              LogPrintFmt, GetLevelRef};
    401 
    402 static void ToLower(const char* src, char* dest, size_t len) {
    403  for (size_t c = 0; c < len; c++) {
    404    dest[c] = (char)(tolower(src[c]));
    405  }
    406 }
    407 
    408 // This should be run once after initialization, and may be rerun (via the
    409 // mozLog() command) again later.
    410 static void ParseLoggerOptions(mozilla::Range<const char> mixedCaseOpts) {
    411  // Copy into a new buffer and lower case to do case insensitive matching.
    412  //
    413  // Done this way rather than just using strcasestr because Windows doesn't
    414  // have strcasestr as part of its base C library.
    415  size_t len = mixedCaseOpts.length();
    416  mozilla::UniqueFreePtr<char[]> logOpts(
    417      static_cast<char*>(calloc(len + 1, 1)));
    418  if (!logOpts) {
    419    return;
    420  }
    421 
    422  ToLower(mixedCaseOpts.begin().get(), logOpts.get(), len);
    423  logOpts.get()[len] = '\0';
    424 
    425  // This is a really permissive parser, but will suffice!
    426  for (auto& logger : logModules) {
    427    if (logger) {
    428      // Lowercase the logger name for strstr
    429      size_t len = strlen(logger->name);
    430      mozilla::UniqueFreePtr<char[]> lowerName(
    431          static_cast<char*>(calloc(len + 1, 1)));
    432      ToLower(logger->name, lowerName.get(), len);
    433      lowerName.get()[len] = '\0';
    434 
    435      int logLevel = 0;
    436      if (char* needle = strstr(logOpts.get(), lowerName.get())) {
    437        // If the string to enable a logger is present, but no level is provided
    438        // then default to Debug level.
    439        logLevel = static_cast<int>(mozilla::LogLevel::Debug);
    440 
    441        if (char* colon = strchr(needle, ':')) {
    442          // Parse character after colon as log level.
    443          if (*(colon + 1)) {
    444            logLevel = atoi(colon + 1);
    445          }
    446        }
    447 
    448        fprintf(stderr, "[JS_LOG] Enabling Logger %s at level %d\n",
    449                logger->name, logLevel);
    450      } else {
    451        if (logger->level != mozilla::ToLogLevel(logLevel)) {
    452          fprintf(stderr, "[JS_LOG] Resetting Logger %s to level %d\n",
    453                  logger->name, logLevel);
    454        }
    455      }
    456 
    457      logger->level = mozilla::ToLogLevel(logLevel);
    458    }
    459  }
    460 }
    461 
    462 static bool SetMozLog(JSContext* cx, unsigned argc, Value* vp) {
    463  CallArgs args = CallArgsFromVp(argc, vp);
    464 
    465  Rooted<JSString*> spec(cx, ToString(cx, args.get(0)));
    466  if (!spec) {
    467    return false;
    468  }
    469 
    470  if (!spec->hasLatin1Chars()) {
    471    JS_ReportErrorASCII(cx, "invalid MOZ_LOG setting");
    472    return false;
    473  }
    474 
    475  AutoStableStringChars stable(cx);
    476  if (!stable.init(cx, spec)) {
    477    return false;
    478  }
    479 
    480  mozilla::Range<const Latin1Char> r = stable.latin1Range();
    481  ParseLoggerOptions({(const char*)r.begin().get(), r.length()});
    482  return true;
    483 }
    484 
    485 /*
    486 * Limit the timeout to 30 minutes to prevent an overflow on platfoms
    487 * that represent the time internally in microseconds using 32-bit int.
    488 */
    489 static const double MAX_TIMEOUT_SECONDS = 1800.0;
    490 
    491 // Not necessarily in sync with the browser
    492 #ifdef ENABLE_SHARED_MEMORY
    493 #  define SHARED_MEMORY_DEFAULT 1
    494 #else
    495 #  define SHARED_MEMORY_DEFAULT 0
    496 #endif
    497 
    498 // Fuzzing support for JS runtime fuzzing
    499 #ifdef FUZZING_INTERFACES
    500 #  include "shell/jsrtfuzzing/jsrtfuzzing.h"
    501 MOZ_RUNINIT static bool fuzzDoDebug = !!getenv("MOZ_FUZZ_DEBUG");
    502 MOZ_RUNINIT static bool fuzzHaveModule = !!getenv("FUZZER");
    503 #endif  // FUZZING_INTERFACES
    504 
    505 // Code to support GCOV code coverage measurements on standalone shell
    506 #ifdef MOZ_CODE_COVERAGE
    507 #  if defined(__GNUC__) && !defined(__clang__)
    508 extern "C" void __gcov_dump();
    509 extern "C" void __gcov_reset();
    510 
    511 void counters_dump(int) { __gcov_dump(); }
    512 
    513 void counters_reset(int) { __gcov_reset(); }
    514 #  else
    515 void counters_dump(int) { /* Do nothing */ }
    516 
    517 void counters_reset(int) { /* Do nothing */ }
    518 #  endif
    519 
    520 static void InstallCoverageSignalHandlers() {
    521 #  ifndef XP_WIN
    522  fprintf(stderr, "[CodeCoverage] Setting handlers for process %d.\n",
    523          getpid());
    524 
    525  struct sigaction dump_sa;
    526  dump_sa.sa_handler = counters_dump;
    527  dump_sa.sa_flags = SA_RESTART;
    528  sigemptyset(&dump_sa.sa_mask);
    529  mozilla::DebugOnly<int> r1 = sigaction(SIGUSR1, &dump_sa, nullptr);
    530  MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler");
    531 
    532  struct sigaction reset_sa;
    533  reset_sa.sa_handler = counters_reset;
    534  reset_sa.sa_flags = SA_RESTART;
    535  sigemptyset(&reset_sa.sa_mask);
    536  mozilla::DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr);
    537  MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler");
    538 #  endif
    539 }
    540 #endif
    541 
    542 // An off-thread parse or decode job.
    543 class js::shell::OffThreadJob {
    544  static constexpr size_t kCompileStackQuota = 128 * sizeof(size_t) * 1024;
    545  static constexpr size_t kThreadStackQuota =
    546      kCompileStackQuota + 128 * sizeof(size_t) * 1024;
    547 
    548  enum State {
    549    RUNNING,   // Working; no stencil.
    550    DONE,      // Finished; have stencil.
    551    CANCELLED  // Cancelled due to error.
    552  };
    553 
    554 public:
    555  enum class Kind {
    556    CompileScript,
    557    CompileModule,
    558    Decode,
    559  };
    560 
    561  OffThreadJob(ShellContext* sc, Kind kind, JS::SourceText<char16_t>&& srcBuf);
    562  OffThreadJob(ShellContext* sc, Kind kind, JS::TranscodeBuffer&& xdrBuf);
    563 
    564  ~OffThreadJob();
    565 
    566  bool init(JSContext* cx, const JS::ReadOnlyCompileOptions& options);
    567  bool dispatch();
    568 
    569  static void OffThreadMain(OffThreadJob* self);
    570  void run();
    571 
    572  void cancel();
    573  void waitUntilDone();
    574 
    575  already_AddRefed<JS::Stencil> stealStencil(JSContext* cx);
    576 
    577 public:
    578  const int32_t id;
    579 
    580 private:
    581  Kind kind_;
    582  State state_;
    583 
    584  JS::FrontendContext* fc_ = nullptr;
    585  JS::OwningCompileOptions options_;
    586 
    587  UniquePtr<Thread> thread_;
    588 
    589  JS::SourceText<char16_t> srcBuf_;
    590  JS::TranscodeBuffer xdrBuf_;
    591 
    592  RefPtr<JS::Stencil> stencil_;
    593 
    594  JS::TranscodeResult transcodeResult_ = JS::TranscodeResult::Ok;
    595 };
    596 
    597 template <typename T>
    598 static OffThreadJob* NewOffThreadJob(JSContext* cx, OffThreadJob::Kind kind,
    599                                     JS::ReadOnlyCompileOptions& options,
    600                                     T&& source) {
    601  ShellContext* sc = GetShellContext(cx);
    602  if (sc->isWorker) {
    603    // Off-thread compilation/decode is used by main-thread, in order to improve
    604    // the responsiveness.  It's not used by worker in browser, and there's not
    605    // much reason to support worker here.
    606    JS_ReportErrorASCII(cx, "Off-thread job is not supported in worker");
    607    return nullptr;
    608  }
    609 
    610  UniquePtr<OffThreadJob> job(
    611      cx->new_<OffThreadJob>(sc, kind, std::move(source)));
    612  if (!job) {
    613    return nullptr;
    614  }
    615 
    616  if (!job->init(cx, options)) {
    617    return nullptr;
    618  }
    619 
    620  if (!sc->offThreadJobs.append(job.get())) {
    621    job->cancel();
    622    JS_ReportErrorASCII(cx, "OOM adding off-thread job");
    623    return nullptr;
    624  }
    625 
    626  return job.release();
    627 }
    628 
    629 static OffThreadJob* GetSingleOffThreadJob(JSContext* cx) {
    630  ShellContext* sc = GetShellContext(cx);
    631  const auto& jobs = sc->offThreadJobs;
    632  if (jobs.empty()) {
    633    JS_ReportErrorASCII(cx, "No off-thread jobs are pending");
    634    return nullptr;
    635  }
    636 
    637  if (jobs.length() > 1) {
    638    JS_ReportErrorASCII(
    639        cx, "Multiple off-thread jobs are pending: must specify job ID");
    640    return nullptr;
    641  }
    642 
    643  return jobs[0];
    644 }
    645 
    646 static OffThreadJob* LookupOffThreadJobByID(JSContext* cx, int32_t id) {
    647  if (id <= 0) {
    648    JS_ReportErrorASCII(cx, "Bad off-thread job ID");
    649    return nullptr;
    650  }
    651 
    652  ShellContext* sc = GetShellContext(cx);
    653  const auto& jobs = sc->offThreadJobs;
    654  if (jobs.empty()) {
    655    JS_ReportErrorASCII(cx, "No off-thread jobs are pending");
    656    return nullptr;
    657  }
    658 
    659  OffThreadJob* job = nullptr;
    660  for (auto someJob : jobs) {
    661    if (someJob->id == id) {
    662      job = someJob;
    663      break;
    664    }
    665  }
    666 
    667  if (!job) {
    668    JS_ReportErrorASCII(cx, "Off-thread job not found");
    669    return nullptr;
    670  }
    671 
    672  return job;
    673 }
    674 
    675 static OffThreadJob* LookupOffThreadJobForArgs(JSContext* cx,
    676                                               const CallArgs& args,
    677                                               size_t arg) {
    678  // If the optional ID argument isn't present, get the single pending job.
    679  if (args.length() <= arg) {
    680    return GetSingleOffThreadJob(cx);
    681  }
    682 
    683  // Lookup the job using the specified ID.
    684  int32_t id = 0;
    685  RootedValue value(cx, args[arg]);
    686  if (!ToInt32(cx, value, &id)) {
    687    return nullptr;
    688  }
    689 
    690  return LookupOffThreadJobByID(cx, id);
    691 }
    692 
    693 static void DeleteOffThreadJob(JSContext* cx, OffThreadJob* job) {
    694  ShellContext* sc = GetShellContext(cx);
    695  for (size_t i = 0; i < sc->offThreadJobs.length(); i++) {
    696    if (sc->offThreadJobs[i] == job) {
    697      sc->offThreadJobs.erase(&sc->offThreadJobs[i]);
    698      js_delete(job);
    699      return;
    700    }
    701  }
    702 
    703  MOZ_CRASH("Off-thread job not found");
    704 }
    705 
    706 static void CancelOffThreadJobsForRuntime(JSContext* cx) {
    707  ShellContext* sc = GetShellContext(cx);
    708  while (!sc->offThreadJobs.empty()) {
    709    OffThreadJob* job = sc->offThreadJobs.popCopy();
    710    job->waitUntilDone();
    711    js_delete(job);
    712  }
    713 }
    714 
    715 mozilla::Atomic<int32_t> gOffThreadJobSerial(1);
    716 
    717 OffThreadJob::OffThreadJob(ShellContext* sc, Kind kind,
    718                           JS::SourceText<char16_t>&& srcBuf)
    719    : id(gOffThreadJobSerial++),
    720      kind_(kind),
    721      state_(RUNNING),
    722      options_(JS::OwningCompileOptions::ForFrontendContext()),
    723      srcBuf_(std::move(srcBuf)) {
    724  MOZ_RELEASE_ASSERT(id > 0, "Off-thread job IDs exhausted");
    725 }
    726 
    727 OffThreadJob::OffThreadJob(ShellContext* sc, Kind kind,
    728                           JS::TranscodeBuffer&& xdrBuf)
    729    : id(gOffThreadJobSerial++),
    730      kind_(kind),
    731      state_(RUNNING),
    732      options_(JS::OwningCompileOptions::ForFrontendContext()),
    733      xdrBuf_(std::move(xdrBuf)) {
    734  MOZ_RELEASE_ASSERT(id > 0, "Off-thread job IDs exhausted");
    735 }
    736 
    737 OffThreadJob::~OffThreadJob() {
    738  if (fc_) {
    739    JS::DestroyFrontendContext(fc_);
    740  }
    741  MOZ_ASSERT(state_ != RUNNING);
    742 }
    743 
    744 bool OffThreadJob::init(JSContext* cx,
    745                        const JS::ReadOnlyCompileOptions& options) {
    746  fc_ = JS::NewFrontendContext();
    747  if (!fc_) {
    748    ReportOutOfMemory(cx);
    749    state_ = CANCELLED;
    750    return false;
    751  }
    752 
    753  if (!options_.copy(cx, options)) {
    754    state_ = CANCELLED;
    755    return false;
    756  }
    757 
    758  return true;
    759 }
    760 
    761 bool OffThreadJob::dispatch() {
    762  thread_ =
    763      js::MakeUnique<Thread>(Thread::Options().setStackSize(kThreadStackQuota));
    764  if (!thread_) {
    765    state_ = CANCELLED;
    766    return false;
    767  }
    768 
    769  if (!thread_->init(OffThreadJob::OffThreadMain, this)) {
    770    state_ = CANCELLED;
    771    thread_ = nullptr;
    772    return false;
    773  }
    774 
    775  return true;
    776 }
    777 
    778 /* static */ void OffThreadJob::OffThreadMain(OffThreadJob* self) {
    779  self->run();
    780 }
    781 
    782 void OffThreadJob::run() {
    783  MOZ_ASSERT(state_ == RUNNING);
    784  MOZ_ASSERT(!stencil_);
    785 
    786  JS::SetNativeStackQuota(fc_, kCompileStackQuota);
    787 
    788  switch (kind_) {
    789    case Kind::CompileScript: {
    790      stencil_ = JS::CompileGlobalScriptToStencil(fc_, options_, srcBuf_);
    791      break;
    792    }
    793    case Kind::CompileModule: {
    794      stencil_ = JS::CompileModuleScriptToStencil(fc_, options_, srcBuf_);
    795      break;
    796    }
    797    case Kind::Decode: {
    798      JS::DecodeOptions decodeOptions(options_);
    799      JS::TranscodeRange range(xdrBuf_.begin(), xdrBuf_.length());
    800      transcodeResult_ = JS::DecodeStencil(fc_, decodeOptions, range,
    801                                           getter_AddRefs(stencil_));
    802      break;
    803    }
    804  }
    805 
    806  state_ = DONE;
    807 }
    808 
    809 void OffThreadJob::cancel() {
    810  MOZ_ASSERT(state_ == RUNNING);
    811  MOZ_ASSERT(!stencil_);
    812  MOZ_ASSERT(!thread_, "cannot cancel after starting a thread");
    813 
    814  state_ = CANCELLED;
    815 }
    816 
    817 void OffThreadJob::waitUntilDone() {
    818  MOZ_ASSERT(state_ != CANCELLED);
    819  thread_->join();
    820 }
    821 
    822 already_AddRefed<JS::Stencil> OffThreadJob::stealStencil(JSContext* cx) {
    823  JS::FrontendContext* fc = fc_;
    824  fc_ = nullptr;
    825  auto destroyFrontendContext =
    826      mozilla::MakeScopeExit([&]() { JS::DestroyFrontendContext(fc); });
    827 
    828  MOZ_ASSERT(fc);
    829 
    830  if (JS::HadFrontendErrors(fc)) {
    831    (void)JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options_);
    832    return nullptr;
    833  }
    834 
    835  if (!stencil_ && JS::IsTranscodeFailureResult(transcodeResult_)) {
    836    JS_ReportErrorASCII(cx, "failed to decode cache");
    837    return nullptr;
    838  }
    839 
    840  // Report warnings.
    841  if (!JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options_)) {
    842    return nullptr;
    843  }
    844 
    845  return stencil_.forget();
    846 }
    847 
    848 struct ShellCompartmentPrivate {
    849  GCPtr<ArrayObject*> blackRoot;
    850  GCPtr<ArrayObject*> grayRoot;
    851 };
    852 
    853 struct MOZ_STACK_CLASS EnvironmentPreparer
    854    : public js::ScriptEnvironmentPreparer {
    855  explicit EnvironmentPreparer(JSContext* cx) {
    856    js::SetScriptEnvironmentPreparer(cx, this);
    857  }
    858  void invoke(JS::HandleObject global, Closure& closure) override;
    859 };
    860 
    861 const char* shell::selfHostedXDRPath = nullptr;
    862 bool shell::encodeSelfHostedCode = false;
    863 bool shell::enableCodeCoverage = false;
    864 bool shell::enableDisassemblyDumps = false;
    865 bool shell::offthreadBaselineCompilation = false;
    866 bool shell::offthreadIonCompilation = false;
    867 JS::DelazificationOption shell::defaultDelazificationMode =
    868    JS::DelazificationOption::OnDemandOnly;
    869 bool shell::enableAsmJS = false;
    870 bool shell::enableWasm = false;
    871 bool shell::enableSharedMemory = SHARED_MEMORY_DEFAULT;
    872 bool shell::enableWasmBaseline = false;
    873 bool shell::enableWasmOptimizing = false;
    874 bool shell::enableTestWasmAwaitTier2 = false;
    875 bool shell::enableSourcePragmas = true;
    876 bool shell::enableAsyncStacks = false;
    877 bool shell::enableAsyncStackCaptureDebuggeeOnly = false;
    878 bool shell::enableToSource = false;
    879 #ifdef JS_GC_ZEAL
    880 uint32_t shell::gZealBits = 0;
    881 uint32_t shell::gZealFrequency = 0;
    882 #endif
    883 bool shell::printTiming = false;
    884 RCFile* shell::gErrFile = nullptr;
    885 RCFile* shell::gOutFile = nullptr;
    886 bool shell::reportWarnings = true;
    887 bool shell::compileOnly = false;
    888 bool shell::disableOOMFunctions = false;
    889 bool shell::defaultToSameCompartment = true;
    890 
    891 #ifdef DEBUG
    892 bool shell::dumpEntrainedVariables = false;
    893 bool shell::OOM_printAllocationCount = false;
    894 #endif
    895 
    896 MOZ_RUNINIT UniqueChars shell::processWideModuleLoadPath;
    897 
    898 static bool SetTimeoutValue(JSContext* cx, double t);
    899 
    900 static void KillWatchdog(JSContext* cx);
    901 
    902 static bool ScheduleWatchdog(JSContext* cx, double t);
    903 
    904 static void CancelExecution(JSContext* cx);
    905 
    906 enum class ShellGlobalKind {
    907  GlobalObject,
    908  WindowProxy,
    909 };
    910 
    911 static void SetStandardRealmOptions(JS::RealmOptions& options);
    912 static JSObject* NewGlobalObject(JSContext* cx, JS::RealmOptions& options,
    913                                 JSPrincipals* principals, ShellGlobalKind kind,
    914                                 bool immutablePrototype);
    915 
    916 /*
    917 * A toy WindowProxy class for the shell. This is intended for testing code
    918 * where global |this| is a WindowProxy. All requests are forwarded to the
    919 * underlying global and no navigation is supported.
    920 */
    921 const JSClass ShellWindowProxyClass =
    922    PROXY_CLASS_DEF("ShellWindowProxy", JSCLASS_HAS_RESERVED_SLOTS(1));
    923 
    924 JSObject* NewShellWindowProxy(JSContext* cx, JS::HandleObject global) {
    925  MOZ_ASSERT(global->is<GlobalObject>());
    926 
    927  js::WrapperOptions options;
    928  options.setClass(&ShellWindowProxyClass);
    929 
    930  JSAutoRealm ar(cx, global);
    931  JSObject* obj =
    932      js::Wrapper::New(cx, global, &js::Wrapper::singleton, options);
    933  MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
    934  return obj;
    935 }
    936 
    937 /*
    938 * A toy principals type for the shell.
    939 *
    940 * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the
    941 * set bits in P are a superset of those in Q. Thus, the principal 0 is
    942 * subsumed by everything, and the principal ~0 subsumes everything.
    943 *
    944 * As a special case, a null pointer as a principal is treated like 0xffff.
    945 *
    946 * The 'newGlobal' function takes an option indicating which principal the
    947 * new global should have; 'evaluate' does for the new code.
    948 */
    949 class ShellPrincipals final : public JSPrincipals {
    950  uint32_t bits;
    951 
    952  static uint32_t getBits(JSPrincipals* p) {
    953    if (!p) {
    954      return 0xffff;
    955    }
    956    return static_cast<ShellPrincipals*>(p)->bits;
    957  }
    958 
    959 public:
    960  explicit ShellPrincipals(uint32_t bits, int32_t refcount = 0) : bits(bits) {
    961    this->refcount = refcount;
    962  }
    963 
    964  bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
    965    // The shell doesn't have a read principals hook, so it doesn't really
    966    // matter what we write here, but we have to write something so the
    967    // fuzzer is happy.
    968    return JS_WriteUint32Pair(writer, bits, 0);
    969  }
    970 
    971  bool isSystemOrAddonPrincipal() override { return true; }
    972 
    973  static void destroy(JSPrincipals* principals) {
    974    MOZ_ASSERT(principals != &fullyTrusted);
    975    MOZ_ASSERT(principals->refcount == 0);
    976    js_delete(static_cast<const ShellPrincipals*>(principals));
    977  }
    978 
    979  static bool subsumes(JSPrincipals* first, JSPrincipals* second) {
    980    uint32_t firstBits = getBits(first);
    981    uint32_t secondBits = getBits(second);
    982    return (firstBits | secondBits) == firstBits;
    983  }
    984 
    985  static JSSecurityCallbacks securityCallbacks;
    986 
    987  // Fully-trusted principals singleton.
    988  static ShellPrincipals fullyTrusted;
    989 };
    990 
    991 // Global CSP state for the shell. When true, CSP restrictions are enforced.
    992 static bool gCSPEnabled = false;
    993 
    994 static bool ContentSecurityPolicyAllows(
    995    JSContext* cx, JS::RuntimeCode kind, JS::Handle<JSString*> codeString,
    996    JS::CompilationType compilationType,
    997    JS::Handle<JS::StackGCVector<JSString*>> parameterStrings,
    998    JS::Handle<JSString*> bodyString,
    999    JS::Handle<JS::StackGCVector<JS::Value>> parameterArgs,
   1000    JS::Handle<JS::Value> bodyArg, bool* outCanCompileStrings) {
   1001  // If CSP is enabled, block string compilation.
   1002  *outCanCompileStrings = !gCSPEnabled;
   1003  return true;
   1004 }
   1005 
   1006 JSSecurityCallbacks ShellPrincipals::securityCallbacks = {
   1007    ContentSecurityPolicyAllows,
   1008    nullptr,  // codeForEvalGets
   1009    subsumes};
   1010 
   1011 // The fully-trusted principal subsumes all other principals.
   1012 MOZ_RUNINIT ShellPrincipals ShellPrincipals::fullyTrusted(-1, 1);
   1013 
   1014 #ifdef EDITLINE
   1015 extern "C" {
   1016 extern MOZ_EXPORT char* readline(const char* prompt);
   1017 extern MOZ_EXPORT void add_history(char* line);
   1018 }  // extern "C"
   1019 #endif
   1020 
   1021 ShellContext::ShellContext(JSContext* cx, IsWorkerEnum isWorker_)
   1022    : cx_(nullptr),
   1023      isWorker(isWorker_),
   1024      lastWarningEnabled(false),
   1025      trackUnhandledRejections(true),
   1026      timeoutInterval(-1.0),
   1027      startTime(PRMJ_Now()),
   1028      serviceInterrupt(false),
   1029      haveInterruptFunc(false),
   1030      interruptFunc(cx, NullValue()),
   1031      lastWarning(cx, NullValue()),
   1032      promiseRejectionTrackerCallback(cx, NullValue()),
   1033      unhandledRejectedPromises(cx),
   1034      watchdogLock(mutexid::ShellContextWatchdog),
   1035      exitCode(0),
   1036      quitting(false),
   1037      readLineBufPos(0),
   1038      errFilePtr(nullptr),
   1039      outFilePtr(nullptr),
   1040      offThreadMonitor(mutexid::ShellOffThreadState),
   1041      taskCallbacks(cx) {}
   1042 
   1043 ShellContext* js::shell::GetShellContext(JSContext* cx) {
   1044  ShellContext* sc = static_cast<ShellContext*>(JS_GetContextPrivate(cx));
   1045  MOZ_ASSERT(sc);
   1046  return sc;
   1047 }
   1048 
   1049 static void TraceRootArrays(JSTracer* trc, gc::MarkColor color) {
   1050  JSRuntime* rt = trc->runtime();
   1051  for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
   1052    for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
   1053      auto priv = static_cast<ShellCompartmentPrivate*>(
   1054          JS_GetCompartmentPrivate(comp.get()));
   1055      if (!priv) {
   1056        continue;
   1057      }
   1058 
   1059      GCPtr<ArrayObject*>& array =
   1060          (color == gc::MarkColor::Black) ? priv->blackRoot : priv->grayRoot;
   1061      TraceNullableEdge(trc, &array, "shell root array");
   1062 
   1063      if (array) {
   1064        // Trace the array elements as part of root marking.
   1065        for (uint32_t i = 0; i < array->getDenseInitializedLength(); i++) {
   1066          Value& value = const_cast<Value&>(array->getDenseElement(i));
   1067          TraceManuallyBarrieredEdge(trc, &value, "shell root array element");
   1068        }
   1069      }
   1070    }
   1071  }
   1072 }
   1073 
   1074 static void TraceBlackRoots(JSTracer* trc, void* data) {
   1075  TraceRootArrays(trc, gc::MarkColor::Black);
   1076 }
   1077 
   1078 static bool TraceGrayRoots(JSTracer* trc, JS::SliceBudget& budget, void* data) {
   1079  TraceRootArrays(trc, gc::MarkColor::Gray);
   1080  return true;
   1081 }
   1082 
   1083 static inline JSString* NewStringCopyUTF8(JSContext* cx, const char* chars) {
   1084  return JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(chars, strlen(chars)));
   1085 }
   1086 
   1087 static mozilla::UniqueFreePtr<char[]> GetLine(FILE* file, const char* prompt) {
   1088 #ifdef EDITLINE
   1089  /*
   1090   * Use readline only if file is stdin, because there's no way to specify
   1091   * another handle.  Are other filehandles interactive?
   1092   */
   1093  if (file == stdin) {
   1094    mozilla::UniqueFreePtr<char[]> linep(readline(prompt));
   1095    /*
   1096     * We set it to zero to avoid complaining about inappropriate ioctl
   1097     * for device in the case of EOF. Looks like errno == 251 if line is
   1098     * finished with EOF and errno == 25 (EINVAL on Mac) if there is
   1099     * nothing left to read.
   1100     */
   1101    if (errno == 251 || errno == 25 || errno == EINVAL) {
   1102      errno = 0;
   1103    }
   1104    if (!linep) {
   1105      return nullptr;
   1106    }
   1107    if (linep[0] != '\0') {
   1108      add_history(linep.get());
   1109    }
   1110    return linep;
   1111  }
   1112 #endif
   1113 
   1114  size_t len = 0;
   1115  if (*prompt != '\0' && gOutFile->isOpen()) {
   1116    fprintf(gOutFile->fp, "%s", prompt);
   1117    fflush(gOutFile->fp);
   1118  }
   1119 
   1120  size_t size = 80;
   1121  mozilla::UniqueFreePtr<char[]> buffer(static_cast<char*>(malloc(size)));
   1122  if (!buffer) {
   1123    return nullptr;
   1124  }
   1125 
   1126  char* current = buffer.get();
   1127  do {
   1128    while (true) {
   1129      if (fgets(current, size - len, file)) {
   1130        break;
   1131      }
   1132      if (errno != EINTR) {
   1133        return nullptr;
   1134      }
   1135    }
   1136 
   1137    len += strlen(current);
   1138    char* t = buffer.get() + len - 1;
   1139    if (*t == '\n') {
   1140      /* Line was read. We remove '\n' and exit. */
   1141      *t = '\0';
   1142      break;
   1143    }
   1144 
   1145    if (len + 1 == size) {
   1146      size = size * 2;
   1147      char* raw = buffer.release();
   1148      char* tmp = static_cast<char*>(realloc(raw, size));
   1149      if (!tmp) {
   1150        free(raw);
   1151        return nullptr;
   1152      }
   1153      buffer.reset(tmp);
   1154    }
   1155    current = buffer.get() + len;
   1156  } while (true);
   1157  return buffer;
   1158 }
   1159 
   1160 static bool EvaluateInner(JSContext* cx, HandleString code,
   1161                          MutableHandleObject global, HandleObject opts,
   1162                          HandleObject cacheEntry, MutableHandleValue rval);
   1163 
   1164 static bool ShellInterruptCallback(JSContext* cx) {
   1165  ShellContext* sc = GetShellContext(cx);
   1166  if (!sc->serviceInterrupt) {
   1167    return true;
   1168  }
   1169 
   1170  // Reset serviceInterrupt. CancelExecution or InterruptIf will set it to
   1171  // true to distinguish watchdog or user triggered interrupts.
   1172  // Do this first to prevent other interrupts that may occur while the
   1173  // user-supplied callback is executing from re-entering the handler.
   1174  sc->serviceInterrupt = false;
   1175 
   1176  bool result;
   1177  if (sc->haveInterruptFunc) {
   1178    RootedValue rval(cx);
   1179    bool wasAlreadyThrowing = cx->isExceptionPending();
   1180    JS::AutoSaveExceptionState savedExc(cx);
   1181 
   1182    if (sc->interruptFunc.isObject()) {
   1183      JSAutoRealm ar(cx, &sc->interruptFunc.toObject());
   1184 
   1185      // Report any exceptions thrown by the JS interrupt callback, but do
   1186      // *not* keep it on the cx. The interrupt handler is invoked at points
   1187      // that are not expected to throw catchable exceptions, like at
   1188      // JSOp::RetRval.
   1189      //
   1190      // If the interrupted JS code was already throwing, any exceptions
   1191      // thrown by the interrupt handler are silently swallowed.
   1192      Maybe<AutoReportException> are;
   1193      if (!wasAlreadyThrowing) {
   1194        are.emplace(cx);
   1195      }
   1196      result = JS_CallFunctionValue(cx, nullptr, sc->interruptFunc,
   1197                                    JS::HandleValueArray::empty(), &rval);
   1198    } else {
   1199      RootedString str(cx, sc->interruptFunc.toString());
   1200 
   1201      Maybe<AutoReportException> are;
   1202      if (!wasAlreadyThrowing) {
   1203        are.emplace(cx);
   1204      }
   1205 
   1206      JS::RealmOptions options;
   1207      SetStandardRealmOptions(options);
   1208      RootedObject glob(cx, NewGlobalObject(cx, options, nullptr,
   1209                                            ShellGlobalKind::WindowProxy,
   1210                                            /* immutablePrototype = */ true));
   1211      if (!glob) {
   1212        return false;
   1213      }
   1214 
   1215      RootedObject opts(cx, nullptr);
   1216      RootedObject cacheEntry(cx, nullptr);
   1217      JSAutoRealm ar(cx, glob);
   1218      if (!EvaluateInner(cx, str, &glob, opts, cacheEntry, &rval)) {
   1219        return false;
   1220      }
   1221    }
   1222    savedExc.restore();
   1223 
   1224    if (rval.isBoolean()) {
   1225      result = rval.toBoolean();
   1226    } else {
   1227      result = false;
   1228    }
   1229  } else {
   1230    result = false;
   1231  }
   1232 
   1233  if (!result && sc->exitCode == 0) {
   1234    static const char msg[] = "Script terminated by interrupt handler.\n";
   1235    fputs(msg, stderr);
   1236 
   1237    sc->exitCode = EXITCODE_TIMEOUT;
   1238  }
   1239 
   1240  return result;
   1241 }
   1242 
   1243 static void GCSliceCallback(JSContext* cx, JS::GCProgress progress,
   1244                            const JS::GCDescription& desc) {
   1245  if (progress == JS::GC_CYCLE_END) {
   1246 #if defined(MOZ_MEMORY)
   1247    // We call this here to match the browser's DOMGCSliceCallback.
   1248    jemalloc_free_dirty_pages();
   1249 #endif
   1250  }
   1251 }
   1252 
   1253 /*
   1254 * Some UTF-8 files, notably those written using Notepad, have a Unicode
   1255 * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order
   1256 * is meaningless for UTF-8) but causes a syntax error unless we skip it.
   1257 */
   1258 static void SkipUTF8BOM(FILE* file) {
   1259  int ch1 = fgetc(file);
   1260  int ch2 = fgetc(file);
   1261  int ch3 = fgetc(file);
   1262 
   1263  // Skip the BOM
   1264  if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF) {
   1265    return;
   1266  }
   1267 
   1268  // No BOM - revert
   1269  if (ch3 != EOF) {
   1270    ungetc(ch3, file);
   1271  }
   1272  if (ch2 != EOF) {
   1273    ungetc(ch2, file);
   1274  }
   1275  if (ch1 != EOF) {
   1276    ungetc(ch1, file);
   1277  }
   1278 }
   1279 
   1280 void EnvironmentPreparer::invoke(HandleObject global, Closure& closure) {
   1281  MOZ_ASSERT(JS_IsGlobalObject(global));
   1282 
   1283  JSContext* cx = TlsContext.get();
   1284  MOZ_ASSERT(!JS_IsExceptionPending(cx));
   1285 
   1286  AutoRealm ar(cx, global);
   1287  AutoReportException are(cx);
   1288  if (!closure(cx)) {
   1289    return;
   1290  }
   1291 }
   1292 
   1293 static bool RegisterScriptPathWithModuleLoader(JSContext* cx,
   1294                                               HandleScript script,
   1295                                               const char* filename) {
   1296  // Set the private value associated with a script to a object containing the
   1297  // script's filename so that the module loader can use it to resolve
   1298  // relative imports.
   1299 
   1300  RootedString path(cx, NewStringCopyUTF8(cx, filename));
   1301  if (!path) {
   1302    return false;
   1303  }
   1304 
   1305  MOZ_ASSERT(JS::GetScriptPrivate(script).isUndefined());
   1306  RootedObject infoObject(cx, js::CreateScriptPrivate(cx, path));
   1307  if (!infoObject) {
   1308    return false;
   1309  }
   1310 
   1311  JS::SetScriptPrivate(script, ObjectValue(*infoObject));
   1312  return true;
   1313 }
   1314 
   1315 enum class CompileUtf8 {
   1316  InflateToUtf16,
   1317  DontInflate,
   1318 };
   1319 
   1320 [[nodiscard]] static bool RunFile(JSContext* cx, const char* filename,
   1321                                  FILE* file, CompileUtf8 compileMethod,
   1322                                  bool compileOnly, bool fullParse) {
   1323  SkipUTF8BOM(file);
   1324 
   1325  int64_t t1 = PRMJ_Now();
   1326  RootedScript script(cx);
   1327 
   1328  if (!filename) filename = "-";
   1329 
   1330  {
   1331    CompileOptions options(cx);
   1332    options.setIntroductionType("js shell file")
   1333        .setFileAndLine(filename, 1)
   1334        .setIsRunOnce(true)
   1335        .setNoScriptRval(true);
   1336 
   1337    if (fullParse) {
   1338      options.setForceFullParse();
   1339    } else {
   1340      options.setEagerDelazificationStrategy(defaultDelazificationMode);
   1341    }
   1342 
   1343    if (compileMethod == CompileUtf8::DontInflate) {
   1344      script = JS::CompileUtf8File(cx, options, file);
   1345    } else {
   1346      fprintf(stderr, "(compiling '%s' after inflating to UTF-16)\n", filename);
   1347 
   1348      FileContents buffer(cx);
   1349      if (!ReadCompleteFile(cx, file, buffer)) {
   1350        return false;
   1351      }
   1352 
   1353      size_t length = buffer.length();
   1354      auto chars = UniqueTwoByteChars(
   1355          UTF8CharsToNewTwoByteCharsZ(
   1356              cx,
   1357              JS::UTF8Chars(reinterpret_cast<const char*>(buffer.begin()),
   1358                            buffer.length()),
   1359              &length, js::MallocArena)
   1360              .get());
   1361      if (!chars) {
   1362        return false;
   1363      }
   1364 
   1365      JS::SourceText<char16_t> source;
   1366      if (!source.init(cx, std::move(chars), length)) {
   1367        return false;
   1368      }
   1369 
   1370      script = JS::Compile(cx, options, source);
   1371    }
   1372 
   1373    if (!script) {
   1374      return false;
   1375    }
   1376  }
   1377 
   1378  if (!RegisterScriptPathWithModuleLoader(cx, script, filename)) {
   1379    return false;
   1380  }
   1381 
   1382 #ifdef DEBUG
   1383  if (dumpEntrainedVariables) {
   1384    AnalyzeEntrainedVariables(cx, script);
   1385  }
   1386 #endif
   1387  if (!compileOnly) {
   1388    if (!JS_ExecuteScript(cx, script)) {
   1389      return false;
   1390    }
   1391    int64_t t2 = PRMJ_Now() - t1;
   1392    if (printTiming) {
   1393      printf("runtime = %.3f ms\n", double(t2) / PRMJ_USEC_PER_MSEC);
   1394    }
   1395  }
   1396  return true;
   1397 }
   1398 
   1399 [[nodiscard]] static bool RunModule(JSContext* cx, const char* filename,
   1400                                    bool compileOnly) {
   1401  ShellContext* sc = GetShellContext(cx);
   1402 
   1403  RootedString path(cx, NewStringCopyUTF8(cx, filename));
   1404  if (!path) {
   1405    return false;
   1406  }
   1407 
   1408  path = ResolvePath(cx, path, RootRelative);
   1409  if (!path) {
   1410    return false;
   1411  }
   1412 
   1413  return sc->moduleLoader->loadRootModule(cx, path);
   1414 }
   1415 
   1416 static void ShellCleanupFinalizationRegistryCallback(JSFunction* doCleanup,
   1417                                                     JSObject* incumbentGlobal,
   1418                                                     void* data) {
   1419  // In the browser this queues a task. Shell jobs correspond to microtasks so
   1420  // we arrange for cleanup to happen after all jobs/microtasks have run. The
   1421  // incumbent global is ignored in the shell.
   1422 
   1423  auto sc = static_cast<ShellContext*>(data);
   1424  AutoEnterOOMUnsafeRegion oomUnsafe;
   1425  if (!sc->taskCallbacks.append(doCleanup)) {
   1426    oomUnsafe.crash("ShellCleanupFinalizationRegistryCallback");
   1427  }
   1428 }
   1429 
   1430 // Run any tasks queued on the ShellContext and return whether any ran.
   1431 static bool MaybeRunShellTasks(JSContext* cx) {
   1432  ShellContext* sc = GetShellContext(cx);
   1433  MOZ_ASSERT(!sc->quitting);
   1434 
   1435  Rooted<ShellContext::ObjectVector> callbacks(cx);
   1436  std::swap(callbacks.get(), sc->taskCallbacks.get());
   1437 
   1438  bool ranTasks = false;
   1439 
   1440  RootedValue callback(cx);
   1441  for (JSObject* o : callbacks) {
   1442    callback = ObjectValue(*o);
   1443 
   1444    JS::ExposeValueToActiveJS(callback);
   1445    AutoRealm ar(cx, o);
   1446 
   1447    {
   1448      AutoReportException are(cx);
   1449      RootedValue unused(cx);
   1450      (void)JS_CallFunctionValue(cx, nullptr, callback,
   1451                                 HandleValueArray::empty(), &unused);
   1452    }
   1453 
   1454    ranTasks = true;
   1455 
   1456    if (sc->quitting) {
   1457      break;
   1458    }
   1459  }
   1460 
   1461  return ranTasks;
   1462 }
   1463 
   1464 static void RunShellJobs(JSContext* cx) {
   1465  ShellContext* sc = GetShellContext(cx);
   1466  if (sc->quitting) {
   1467    return;
   1468  }
   1469 
   1470  while (true) {
   1471    // Run microtasks.
   1472    js::RunJobs(cx);
   1473    if (sc->quitting) {
   1474      return;
   1475    }
   1476 
   1477    // Run tasks.
   1478    bool ranTasks = MaybeRunShellTasks(cx);
   1479    if (!ranTasks) {
   1480      break;
   1481    }
   1482  }
   1483 }
   1484 
   1485 static bool DrainJobQueue(JSContext* cx, unsigned argc, Value* vp) {
   1486  CallArgs args = CallArgsFromVp(argc, vp);
   1487 
   1488  if (GetShellContext(cx)->quitting) {
   1489    JS_ReportErrorASCII(
   1490        cx, "Mustn't drain the job queue when the shell is quitting");
   1491    return false;
   1492  }
   1493 
   1494  if (cx->isEvaluatingModule != 0) {
   1495    JS_ReportErrorASCII(
   1496        cx,
   1497        "Can't drain the job queue when executing the top level of a module");
   1498    return false;
   1499  }
   1500 
   1501  RunShellJobs(cx);
   1502 
   1503  if (GetShellContext(cx)->quitting) {
   1504    return false;
   1505  }
   1506 
   1507  args.rval().setUndefined();
   1508  return true;
   1509 }
   1510 
   1511 static bool GlobalOfFirstJobInQueue(JSContext* cx, unsigned argc, Value* vp) {
   1512  CallArgs args = CallArgsFromVp(argc, vp);
   1513 
   1514  if (JS::Prefs::use_js_microtask_queue()) {
   1515    if (cx->microTaskQueues->microTaskQueue.empty()) {
   1516      JS_ReportErrorASCII(cx, "Job queue is empty");
   1517      return false;
   1518    }
   1519 
   1520    auto& genericJob = cx->microTaskQueues->microTaskQueue.front();
   1521    JS::JSMicroTask* job = JS::ToUnwrappedJSMicroTask(genericJob);
   1522    if (!job) {
   1523      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1524                                JSMSG_DEAD_OBJECT);
   1525 
   1526      return false;
   1527    }
   1528 
   1529    RootedObject global(cx, JS::GetExecutionGlobalFromJSMicroTask(job));
   1530    if (!global) {
   1531      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1532                                JSMSG_DEAD_OBJECT);
   1533      return false;
   1534    }
   1535    MOZ_ASSERT(global);
   1536    if (!cx->compartment()->wrap(cx, &global)) {
   1537      return false;
   1538    }
   1539 
   1540    args.rval().setObject(*global);
   1541  } else {
   1542    RootedObject job(cx, cx->internalJobQueue->maybeFront());
   1543    if (!job) {
   1544      JS_ReportErrorASCII(cx, "Job queue is empty");
   1545      return false;
   1546    }
   1547 
   1548    RootedObject global(cx, &job->nonCCWGlobal());
   1549    if (!cx->compartment()->wrap(cx, &global)) {
   1550      return false;
   1551    }
   1552 
   1553    args.rval().setObject(*global);
   1554  }
   1555 
   1556  return true;
   1557 }
   1558 
   1559 static bool TrackUnhandledRejections(JSContext* cx, JS::HandleObject promise,
   1560                                     JS::PromiseRejectionHandlingState state) {
   1561  ShellContext* sc = GetShellContext(cx);
   1562  if (!sc->trackUnhandledRejections) {
   1563    return true;
   1564  }
   1565 
   1566 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
   1567  if (cx->runningOOMTest) {
   1568    // When OOM happens, we cannot reliably track the set of unhandled
   1569    // promise rejections. Throw error only when simulated OOM is used
   1570    // *and* promises are used in the test.
   1571    JS_ReportErrorASCII(
   1572        cx,
   1573        "Can't track unhandled rejections while running simulated OOM "
   1574        "test. Call ignoreUnhandledRejections before using oomTest etc.");
   1575    return false;
   1576  }
   1577 #endif
   1578 
   1579  if (!sc->unhandledRejectedPromises) {
   1580    sc->unhandledRejectedPromises = SetObject::create(cx);
   1581    if (!sc->unhandledRejectedPromises) {
   1582      return false;
   1583    }
   1584  }
   1585 
   1586  RootedValue promiseVal(cx, ObjectValue(*promise));
   1587 
   1588  AutoRealm ar(cx, sc->unhandledRejectedPromises);
   1589  if (!cx->compartment()->wrap(cx, &promiseVal)) {
   1590    return false;
   1591  }
   1592 
   1593  switch (state) {
   1594    case JS::PromiseRejectionHandlingState::Unhandled:
   1595      if (!sc->unhandledRejectedPromises->add(cx, promiseVal)) {
   1596        return false;
   1597      }
   1598      break;
   1599    case JS::PromiseRejectionHandlingState::Handled:
   1600      bool deleted = false;
   1601      if (!sc->unhandledRejectedPromises->delete_(cx, promiseVal, &deleted)) {
   1602        return false;
   1603      }
   1604      // We can't MOZ_ASSERT(deleted) here, because it's possible we failed to
   1605      // add the promise in the first place, due to OOM.
   1606      break;
   1607  }
   1608 
   1609  return true;
   1610 }
   1611 
   1612 static void ForwardingPromiseRejectionTrackerCallback(
   1613    JSContext* cx, bool mutedErrors, JS::HandleObject promise,
   1614    JS::PromiseRejectionHandlingState state, void* data) {
   1615  AutoReportException are(cx);
   1616 
   1617  if (!TrackUnhandledRejections(cx, promise, state)) {
   1618    return;
   1619  }
   1620 
   1621  RootedValue callback(cx,
   1622                       GetShellContext(cx)->promiseRejectionTrackerCallback);
   1623  if (callback.isNull()) {
   1624    return;
   1625  }
   1626 
   1627  AutoRealm ar(cx, &callback.toObject());
   1628 
   1629  FixedInvokeArgs<2> args(cx);
   1630  args[0].setObject(*promise);
   1631  args[1].setInt32(static_cast<int32_t>(state));
   1632 
   1633  if (!JS_WrapValue(cx, args[0])) {
   1634    return;
   1635  }
   1636 
   1637  RootedValue rval(cx);
   1638  (void)Call(cx, callback, UndefinedHandleValue, args, &rval);
   1639 }
   1640 
   1641 static bool SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc,
   1642                                               Value* vp) {
   1643  CallArgs args = CallArgsFromVp(argc, vp);
   1644 
   1645  if (!IsFunctionObject(args.get(0))) {
   1646    JS_ReportErrorASCII(
   1647        cx,
   1648        "setPromiseRejectionTrackerCallback expects a function as its sole "
   1649        "argument");
   1650    return false;
   1651  }
   1652 
   1653  GetShellContext(cx)->promiseRejectionTrackerCallback = args[0];
   1654 
   1655  args.rval().setUndefined();
   1656  return true;
   1657 }
   1658 
   1659 static bool SetTimeout(JSContext* cx, unsigned argc, Value* vp) {
   1660  CallArgs args = CallArgsFromVp(argc, vp);
   1661  if (args.length() != 1 && args.length() != 2) {
   1662    JS_ReportErrorASCII(cx, "expected one or two arguments");
   1663    return false;
   1664  }
   1665 
   1666  JS::RootedValue functionRefValue(cx, args.get(0));
   1667  JS::RootedValue delayValue(cx, args.get(1));
   1668 
   1669  if (!functionRefValue.isObject() ||
   1670      !JS::IsCallable(&functionRefValue.toObject())) {
   1671    JS_ReportErrorASCII(cx, "functionRef must be callable");
   1672    return false;
   1673  }
   1674 
   1675  if (IsCrossCompartmentWrapper(functionRefValue.toObjectOrNull())) {
   1676    JS_ReportErrorASCII(cx, "functionRef cannot be a CCW");
   1677    return false;
   1678  }
   1679 
   1680  int32_t delay;
   1681  if (!JS::ToInt32(cx, delayValue, &delay)) {
   1682    return false;
   1683  }
   1684  if (delay != 0) {
   1685    JS::WarnASCII(cx, "Treating non-zero delay as zero in setTimeout");
   1686    if (cx->isThrowingOutOfMemory()) {
   1687      return false;
   1688    }
   1689  }
   1690 
   1691  ShellContext* sc = GetShellContext(cx);
   1692  if (sc->quitting) {
   1693    JS_ReportErrorASCII(cx, "Cannot setTimeout while quitting");
   1694    return false;
   1695  }
   1696 
   1697  if (!sc->taskCallbacks.append(functionRefValue.toObjectOrNull())) {
   1698    ReportOutOfMemory(cx);
   1699    return false;
   1700  }
   1701 
   1702  args.rval().setUndefined();
   1703  return true;
   1704 }
   1705 
   1706 static bool SetCSPEnabled(JSContext* cx, unsigned argc, Value* vp) {
   1707  CallArgs args = CallArgsFromVp(argc, vp);
   1708 
   1709  if (args.length() != 1) {
   1710    JS_ReportErrorASCII(cx, "setCSPEnabled() requires one boolean argument");
   1711    return false;
   1712  }
   1713 
   1714  gCSPEnabled = JS::ToBoolean(args[0]);
   1715 
   1716  args.rval().setUndefined();
   1717  return true;
   1718 }
   1719 
   1720 static const char* telemetryNames[static_cast<int>(JSMetric::Count)] = {
   1721 #define LIT(NAME, _) #NAME,
   1722    FOR_EACH_JS_METRIC(LIT)
   1723 #undef LIT
   1724 };
   1725 
   1726 // Telemetry can be executed from multiple threads, and the callback is
   1727 // responsible to avoid contention on the recorded telemetry data.
   1728 class MOZ_RAII AutoLockTelemetry : public LockGuard<Mutex> {
   1729  using Base = LockGuard<Mutex>;
   1730 
   1731  Mutex& getMutex() {
   1732    static Mutex mutex(mutexid::ShellTelemetry);
   1733    return mutex;
   1734  }
   1735 
   1736 public:
   1737  AutoLockTelemetry() : Base(getMutex()) {}
   1738 };
   1739 
   1740 using TelemetrySamples = mozilla::Vector<uint32_t, 0, js::SystemAllocPolicy>;
   1741 constinit static mozilla::Array<UniquePtr<TelemetrySamples>,
   1742                                size_t(JSMetric::Count)>
   1743    recordedTelemetrySamples;
   1744 
   1745 static void AccumulateTelemetryDataCallback(JSMetric id, uint32_t sample) {
   1746  AutoLockTelemetry alt;
   1747  TelemetrySamples* samples = recordedTelemetrySamples[size_t(id)].get();
   1748  if (!samples) {
   1749    return;
   1750  }
   1751 
   1752  AutoEnterOOMUnsafeRegion oomUnsafe;
   1753  if (!samples->append(sample)) {
   1754    oomUnsafe.crash("AccumulateTelemetrySamplesCallback");
   1755  }
   1756 }
   1757 
   1758 static bool LookupTelemetryName(JSContext* cx, Handle<Value> nameValue,
   1759                                JSMetric* resultOut) {
   1760  static_assert(size_t(JSMetric::Count) == std::size(telemetryNames));
   1761  MOZ_ASSERT(resultOut);
   1762 
   1763  JSString* str = ToString(cx, nameValue);
   1764  if (!str) {
   1765    return false;
   1766  }
   1767 
   1768  UniqueChars name = EncodeLatin1(cx, str);
   1769  if (!name) {
   1770    return false;
   1771  }
   1772 
   1773  for (size_t i = 0; i < std::size(telemetryNames); i++) {
   1774    if (strcmp(telemetryNames[i], name.get()) == 0) {
   1775      *resultOut = JSMetric(i);
   1776      return true;
   1777    }
   1778  }
   1779 
   1780  return false;
   1781 }
   1782 
   1783 static bool StartRecordingTelemetry(JSContext* cx, unsigned argc, Value* vp) {
   1784  CallArgs args = CallArgsFromVp(argc, vp);
   1785  if (args.length() != 1) {
   1786    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   1787                              JSSMSG_INVALID_ARGS, "startRecordingTelemetry");
   1788    return false;
   1789  }
   1790 
   1791  JSMetric id;
   1792  if (!LookupTelemetryName(cx, args[0], &id)) {
   1793    JS_ReportErrorASCII(cx, "Telemetry probe name not found");
   1794    return false;
   1795  }
   1796 
   1797  if (recordedTelemetrySamples[size_t(id)]) {
   1798    JS_ReportErrorASCII(cx, "Already recording telemetry for this probe");
   1799    return false;
   1800  }
   1801 
   1802  auto samples = MakeUnique<TelemetrySamples>();
   1803  if (!samples) {
   1804    ReportOutOfMemory(cx);
   1805    return false;
   1806  }
   1807 
   1808  recordedTelemetrySamples[size_t(id)] = std::move(samples);
   1809  args.rval().setUndefined();
   1810  return true;
   1811 }
   1812 
   1813 static bool StopRecordingTelemetry(JSContext* cx, unsigned argc, Value* vp) {
   1814  CallArgs args = CallArgsFromVp(argc, vp);
   1815  if (args.length() != 1) {
   1816    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   1817                              JSSMSG_INVALID_ARGS, "stopRecordingTelemetry");
   1818    return false;
   1819  }
   1820 
   1821  JSMetric id;
   1822  if (!LookupTelemetryName(cx, args[0], &id)) {
   1823    JS_ReportErrorASCII(cx, "Telemetry probe name not found");
   1824    return false;
   1825  }
   1826 
   1827  if (!recordedTelemetrySamples[size_t(id)]) {
   1828    JS_ReportErrorASCII(cx, "Not recording telemetry for this probe");
   1829    return false;
   1830  }
   1831 
   1832  recordedTelemetrySamples[size_t(id)].reset();
   1833  args.rval().setUndefined();
   1834  return true;
   1835 }
   1836 
   1837 static bool GetTelemetrySamples(JSContext* cx, unsigned argc, Value* vp) {
   1838  CallArgs args = CallArgsFromVp(argc, vp);
   1839  if (args.length() != 1) {
   1840    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   1841                              JSSMSG_INVALID_ARGS, "getTelemetrySamples");
   1842    return false;
   1843  }
   1844 
   1845  JSMetric id;
   1846  if (!LookupTelemetryName(cx, args[0], &id)) {
   1847    JS_ReportErrorASCII(cx, "Telemetry probe name not found");
   1848    return false;
   1849  }
   1850 
   1851  TelemetrySamples* samples = recordedTelemetrySamples[size_t(id)].get();
   1852  if (!samples) {
   1853    args.rval().setUndefined();
   1854    return true;
   1855  }
   1856 
   1857  Rooted<ArrayObject*> array(cx, NewDenseEmptyArray(cx));
   1858  if (!array) {
   1859    return false;
   1860  }
   1861 
   1862  for (uint32_t sample : *samples) {
   1863    if (!NewbornArrayPush(cx, array, NumberValue(sample))) {
   1864      return false;
   1865    }
   1866  }
   1867 
   1868  args.rval().setObject(*array);
   1869  return true;
   1870 }
   1871 
   1872 // Use Counter introspection
   1873 MOZ_RUNINIT static Mutex useCounterLock(mutexid::ShellUseCounters);
   1874 class MOZ_RAII AutoLockUseCounters : public LockGuard<Mutex> {
   1875  using Base = LockGuard<Mutex>;
   1876 
   1877 public:
   1878  AutoLockUseCounters() : Base(useCounterLock) {}
   1879 };
   1880 
   1881 using UseCounterArray =
   1882    mozilla::Array<uint32_t, static_cast<size_t>(JSUseCounter::COUNT)>;
   1883 static UseCounterArray useCounterResults;
   1884 static void SetUseCounterCallback(JSObject* obj, JSUseCounter counter) {
   1885  MOZ_RELEASE_ASSERT(obj);
   1886  AutoLockUseCounters aluc;
   1887  // Maybe should ensure obj is a global object?
   1888  useCounterResults[static_cast<size_t>(counter)]++;
   1889 }
   1890 
   1891 static bool GetUseCounterResults(JSContext* cx, unsigned argc, Value* vp) {
   1892  CallArgs args = CallArgsFromVp(argc, vp);
   1893  Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
   1894  if (!obj) {
   1895    return false;
   1896  }
   1897 
   1898  // Make a private copy holding the lock then release, because we can't
   1899  // hold this mutex while doing JS_DefineProperty, which holds MemoryTracker
   1900  // mutex.
   1901  UseCounterArray local;
   1902  {
   1903    AutoLockUseCounters aluc;
   1904    local = useCounterResults;
   1905  }
   1906 
   1907  RootedValue val(cx);
   1908 #define ADD_VALUE(ENUM, NAME)                                      \
   1909  val.setInt32(local[static_cast<size_t>(JSUseCounter::ENUM)]);    \
   1910  if (!JS_DefineProperty(cx, obj, #NAME, val, JSPROP_ENUMERATE)) { \
   1911    return false;                                                  \
   1912  }
   1913 
   1914  FOR_EACH_JS_USE_COUNTER(ADD_VALUE);
   1915 
   1916  args.rval().setObject(*obj);
   1917  return true;
   1918 }
   1919 
   1920 static bool BoundToAsyncStack(JSContext* cx, unsigned argc, Value* vp) {
   1921  CallArgs args = CallArgsFromVp(argc, vp);
   1922 
   1923  RootedValue function(cx, GetFunctionNativeReserved(&args.callee(), 0));
   1924  RootedObject options(
   1925      cx, &GetFunctionNativeReserved(&args.callee(), 1).toObject());
   1926 
   1927  Rooted<SavedFrame*> stack(cx, nullptr);
   1928  bool isExplicit;
   1929 
   1930  RootedValue v(cx);
   1931 
   1932  if (!JS_GetProperty(cx, options, "stack", &v)) {
   1933    return false;
   1934  }
   1935  if (!v.isObject() || !v.toObject().is<SavedFrame>()) {
   1936    JS_ReportErrorASCII(cx,
   1937                        "The 'stack' property must be a SavedFrame object.");
   1938    return false;
   1939  }
   1940  stack = &v.toObject().as<SavedFrame>();
   1941 
   1942  if (!JS_GetProperty(cx, options, "cause", &v)) {
   1943    return false;
   1944  }
   1945  RootedString causeString(cx, ToString(cx, v));
   1946  if (!causeString) {
   1947    return false;
   1948  }
   1949 
   1950  UniqueChars cause = JS_EncodeStringToUTF8(cx, causeString);
   1951  if (!cause) {
   1952    MOZ_ASSERT(cx->isExceptionPending());
   1953    return false;
   1954  }
   1955 
   1956  if (!JS_GetProperty(cx, options, "explicit", &v)) {
   1957    return false;
   1958  }
   1959  isExplicit = v.isUndefined() ? true : ToBoolean(v);
   1960 
   1961  auto kind =
   1962      (isExplicit ? JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT
   1963                  : JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::IMPLICIT);
   1964 
   1965  JS::AutoSetAsyncStackForNewCalls asasfnckthxbye(cx, stack, cause.get(), kind);
   1966  return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
   1967              args.rval());
   1968 }
   1969 
   1970 static bool BindToAsyncStack(JSContext* cx, unsigned argc, Value* vp) {
   1971  CallArgs args = CallArgsFromVp(argc, vp);
   1972 
   1973  if (args.length() != 2) {
   1974    JS_ReportErrorASCII(cx, "bindToAsyncStack takes exactly two arguments.");
   1975    return false;
   1976  }
   1977 
   1978  if (!args[0].isObject() || !IsCallable(args[0])) {
   1979    JS_ReportErrorASCII(
   1980        cx, "bindToAsyncStack's first argument should be a function.");
   1981    return false;
   1982  }
   1983 
   1984  if (!args[1].isObject()) {
   1985    JS_ReportErrorASCII(
   1986        cx, "bindToAsyncStack's second argument should be an object.");
   1987    return false;
   1988  }
   1989 
   1990  RootedFunction bound(cx, NewFunctionWithReserved(cx, BoundToAsyncStack, 0, 0,
   1991                                                   "bindToAsyncStack thunk"));
   1992  if (!bound) {
   1993    return false;
   1994  }
   1995  SetFunctionNativeReserved(bound, 0, args[0]);
   1996  SetFunctionNativeReserved(bound, 1, args[1]);
   1997 
   1998  args.rval().setObject(*bound);
   1999  return true;
   2000 }
   2001 
   2002 #ifdef JS_HAS_INTL_API
   2003 static bool AddIntlExtras(JSContext* cx, unsigned argc, Value* vp) {
   2004  CallArgs args = CallArgsFromVp(argc, vp);
   2005  if (!args.get(0).isObject()) {
   2006    JS_ReportErrorASCII(cx, "addIntlExtras must be passed an object");
   2007    return false;
   2008  }
   2009  JS::RootedObject intl(cx, &args[0].toObject());
   2010 
   2011  static const JSFunctionSpec funcs[] = {
   2012      JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0),
   2013      JS_FS_END,
   2014  };
   2015 
   2016  if (!JS_DefineFunctions(cx, intl, funcs)) {
   2017    return false;
   2018  }
   2019 
   2020  if (!JS::AddMozDateTimeFormatConstructor(cx, intl)) {
   2021    return false;
   2022  }
   2023 
   2024  if (!JS::AddMozDisplayNamesConstructor(cx, intl)) {
   2025    return false;
   2026  }
   2027 
   2028  args.rval().setUndefined();
   2029  return true;
   2030 }
   2031 #endif  // JS_HAS_INTL_API
   2032 
   2033 [[nodiscard]] static bool EvalUtf8AndPrint(JSContext* cx, const char* bytes,
   2034                                           size_t length, int lineno,
   2035                                           bool compileOnly) {
   2036  // Eval.
   2037  JS::CompileOptions options(cx);
   2038  options.setIntroductionType("js shell interactive")
   2039      .setIsRunOnce(true)
   2040      .setFileAndLine("typein", lineno)
   2041      .setEagerDelazificationStrategy(defaultDelazificationMode);
   2042 
   2043  JS::SourceText<Utf8Unit> srcBuf;
   2044  if (!srcBuf.init(cx, bytes, length, JS::SourceOwnership::Borrowed)) {
   2045    return false;
   2046  }
   2047 
   2048  RootedScript script(cx, JS::Compile(cx, options, srcBuf));
   2049  if (!script) {
   2050    return false;
   2051  }
   2052  if (compileOnly) {
   2053    return true;
   2054  }
   2055  RootedValue result(cx);
   2056  if (!JS_ExecuteScript(cx, script, &result)) {
   2057    return false;
   2058  }
   2059 
   2060  if (!result.isUndefined() && gOutFile->isOpen()) {
   2061    // Print.
   2062    RootedString str(cx, JS_ValueToSource(cx, result));
   2063    if (!str) {
   2064      return false;
   2065    }
   2066 
   2067    UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
   2068    if (!utf8chars) {
   2069      return false;
   2070    }
   2071    fprintf(gOutFile->fp, "%s\n", utf8chars.get());
   2072  }
   2073  return true;
   2074 }
   2075 
   2076 [[nodiscard]] static bool ReadEvalPrintLoop(JSContext* cx, FILE* in,
   2077                                            bool compileOnly) {
   2078  ShellContext* sc = GetShellContext(cx);
   2079  int lineno = 1;
   2080  bool hitEOF = false;
   2081 
   2082  do {
   2083    /*
   2084     * Accumulate lines until we get a 'compilable unit' - one that either
   2085     * generates an error (before running out of source) or that compiles
   2086     * cleanly.  This should be whenever we get a complete statement that
   2087     * coincides with the end of a line.
   2088     */
   2089    int startline = lineno;
   2090    using CharBuffer = Vector<char, 32>;
   2091    RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
   2092    CharBuffer buffer(cx);
   2093    do {
   2094      ScheduleWatchdog(cx, -1);
   2095      sc->serviceInterrupt = false;
   2096      errno = 0;
   2097 
   2098      mozilla::UniqueFreePtr<char[]> line =
   2099          GetLine(in, startline == lineno ? "js> " : "");
   2100      if (!line) {
   2101        if (errno) {
   2102          if (UniqueChars error = SystemErrorMessage(cx, errno)) {
   2103            JS_ReportErrorUTF8(cx, "%s", error.get());
   2104          }
   2105          return false;
   2106        }
   2107        hitEOF = true;
   2108        break;
   2109      }
   2110 
   2111      if (!buffer.append(line.get(), strlen(line.get())) ||
   2112          !buffer.append('\n')) {
   2113        return false;
   2114      }
   2115 
   2116      lineno++;
   2117      if (!ScheduleWatchdog(cx, sc->timeoutInterval)) {
   2118        hitEOF = true;
   2119        break;
   2120      }
   2121    } while (!JS_Utf8BufferIsCompilableUnit(cx, cx->global(), buffer.begin(),
   2122                                            buffer.length()));
   2123 
   2124    if (hitEOF && buffer.empty()) {
   2125      break;
   2126    }
   2127 
   2128    {
   2129      // Report exceptions but keep going.
   2130      AutoReportException are(cx);
   2131      (void)EvalUtf8AndPrint(cx, buffer.begin(), buffer.length(), startline,
   2132                             compileOnly);
   2133    }
   2134 
   2135    // If a let or const fail to initialize they will remain in an unusable
   2136    // without further intervention. This call cleans up the global scope,
   2137    // setting uninitialized lexicals to undefined so that they may still
   2138    // be used. This behavior is _only_ acceptable in the context of the repl.
   2139    if (JS::ForceLexicalInitialization(cx, globalLexical) &&
   2140        gErrFile->isOpen()) {
   2141      fputs(
   2142          "Warning: According to the standard, after the above exception,\n"
   2143          "Warning: the global bindings should be permanently uninitialized.\n"
   2144          "Warning: We have non-standard-ly initialized them to `undefined`"
   2145          "for you.\nWarning: This nicety only happens in the JS shell.\n",
   2146          stderr);
   2147    }
   2148 
   2149    RunShellJobs(cx);
   2150  } while (!hitEOF && !sc->quitting);
   2151 
   2152  if (gOutFile->isOpen()) {
   2153    fprintf(gOutFile->fp, "\n");
   2154  }
   2155 
   2156  return true;
   2157 }
   2158 
   2159 enum FileKind {
   2160  PreludeScript,    // UTF-8 script, fully-parsed, to avoid conflicting
   2161                    // configurations.
   2162  FileScript,       // UTF-8, directly parsed as such
   2163  FileScriptUtf16,  // FileScript, but inflate to UTF-16 before parsing
   2164  FileModule,
   2165 };
   2166 
   2167 [[nodiscard]] static bool Process(JSContext* cx, const char* filename,
   2168                                  bool forceTTY, FileKind kind) {
   2169  FILE* file;
   2170  if (forceTTY || !filename || strcmp(filename, "-") == 0) {
   2171    file = stdin;
   2172  } else {
   2173    file = OpenFile(cx, filename, "rb");
   2174    if (!file) {
   2175      return false;
   2176    }
   2177  }
   2178  AutoCloseFile autoClose(file);
   2179 
   2180  bool fullParse = false;
   2181  if (!forceTTY && !isatty(fileno(file))) {
   2182    // It's not interactive - just execute it.
   2183    switch (kind) {
   2184      case PreludeScript:
   2185        fullParse = true;
   2186        if (!RunFile(cx, filename, file, CompileUtf8::DontInflate, compileOnly,
   2187                     fullParse)) {
   2188          return false;
   2189        }
   2190        break;
   2191      case FileScript:
   2192        if (!RunFile(cx, filename, file, CompileUtf8::DontInflate, compileOnly,
   2193                     fullParse)) {
   2194          return false;
   2195        }
   2196        break;
   2197      case FileScriptUtf16:
   2198        if (!RunFile(cx, filename, file, CompileUtf8::InflateToUtf16,
   2199                     compileOnly, fullParse)) {
   2200          return false;
   2201        }
   2202        break;
   2203      case FileModule:
   2204        if (!RunModule(cx, filename, compileOnly)) {
   2205          return false;
   2206        }
   2207        break;
   2208      default:
   2209        MOZ_CRASH("Impossible FileKind!");
   2210    }
   2211  } else {
   2212    // It's an interactive filehandle; drop into read-eval-print loop.
   2213    MOZ_ASSERT(kind == FileScript);
   2214    if (!ReadEvalPrintLoop(cx, file, compileOnly)) {
   2215      return false;
   2216    }
   2217  }
   2218 #ifdef FUZZING_JS_FUZZILLI
   2219  fprintf(stderr, "executionHash is 0x%x with %d inputs\n", cx->executionHash,
   2220          cx->executionHashInputs);
   2221 #endif
   2222  return true;
   2223 }
   2224 
   2225 #ifdef XP_WIN
   2226 #  define GET_FD_FROM_FILE(a) int(_get_osfhandle(fileno(a)))
   2227 #else
   2228 #  define GET_FD_FROM_FILE(a) fileno(a)
   2229 #endif
   2230 
   2231 static void freeExternalCallback(void* contents, void* userData) {
   2232  MOZ_ASSERT(!userData);
   2233  js_free(contents);
   2234 }
   2235 
   2236 static bool CreateExternalArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
   2237  CallArgs args = CallArgsFromVp(argc, vp);
   2238  if (args.length() != 1) {
   2239    JS_ReportErrorNumberASCII(
   2240        cx, my_GetErrorMessage, nullptr,
   2241        args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
   2242        "createExternalArrayBuffer");
   2243    return false;
   2244  }
   2245 
   2246  int32_t bytes = 0;
   2247  if (!ToInt32(cx, args[0], &bytes)) {
   2248    return false;
   2249  }
   2250 
   2251  if (bytes < 0) {
   2252    JS_ReportErrorASCII(cx, "Size must be non-negative");
   2253    return false;
   2254  }
   2255 
   2256  void* buffer = js_calloc(bytes);
   2257  if (!buffer) {
   2258    JS_ReportOutOfMemory(cx);
   2259    return false;
   2260  }
   2261 
   2262  UniquePtr<void, JS::BufferContentsDeleter> ptr{buffer,
   2263                                                 {&freeExternalCallback}};
   2264  RootedObject arrayBuffer(
   2265      cx, JS::NewExternalArrayBuffer(cx, bytes, std::move(ptr)));
   2266  if (!arrayBuffer) {
   2267    return false;
   2268  }
   2269 
   2270  args.rval().setObject(*arrayBuffer);
   2271  return true;
   2272 }
   2273 
   2274 static bool CreateMappedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
   2275  CallArgs args = CallArgsFromVp(argc, vp);
   2276 
   2277  if (args.length() < 1 || args.length() > 3) {
   2278    JS_ReportErrorNumberASCII(
   2279        cx, my_GetErrorMessage, nullptr,
   2280        args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
   2281        "createMappedArrayBuffer");
   2282    return false;
   2283  }
   2284 
   2285  RootedString rawFilenameStr(cx, JS::ToString(cx, args[0]));
   2286  if (!rawFilenameStr) {
   2287    return false;
   2288  }
   2289  // It's a little bizarre to resolve relative to the script, but for testing
   2290  // I need a file at a known location, and the only good way I know of to do
   2291  // that right now is to include it in the repo alongside the test script.
   2292  // Bug 944164 would introduce an alternative.
   2293  Rooted<JSString*> filenameStr(
   2294      cx, ResolvePath(cx, rawFilenameStr, ScriptRelative));
   2295  if (!filenameStr) {
   2296    return false;
   2297  }
   2298  UniqueChars filename = JS_EncodeStringToUTF8(cx, filenameStr);
   2299  if (!filename) {
   2300    return false;
   2301  }
   2302 
   2303  uint32_t offset = 0;
   2304  if (args.length() >= 2) {
   2305    if (!JS::ToUint32(cx, args[1], &offset)) {
   2306      return false;
   2307    }
   2308  }
   2309 
   2310  bool sizeGiven = false;
   2311  uint32_t size;
   2312  if (args.length() >= 3) {
   2313    if (!JS::ToUint32(cx, args[2], &size)) {
   2314      return false;
   2315    }
   2316    sizeGiven = true;
   2317    if (size == 0) {
   2318      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2319                                JSMSG_BAD_ARRAY_LENGTH);
   2320      return false;
   2321    }
   2322  }
   2323 
   2324  FILE* file = OpenFile(cx, filename.get(), "rb");
   2325  if (!file) {
   2326    return false;
   2327  }
   2328  AutoCloseFile autoClose(file);
   2329 
   2330  struct stat st;
   2331  if (fstat(fileno(file), &st) < 0) {
   2332    JS_ReportErrorASCII(cx, "Unable to stat file");
   2333    return false;
   2334  }
   2335 
   2336  if ((st.st_mode & S_IFMT) != S_IFREG) {
   2337    JS_ReportErrorASCII(cx, "Path is not a regular file");
   2338    return false;
   2339  }
   2340 
   2341  if (!sizeGiven) {
   2342    if (off_t(offset) >= st.st_size) {
   2343      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2344                                JSMSG_OFFSET_LARGER_THAN_FILESIZE);
   2345      return false;
   2346    }
   2347    size = st.st_size - offset;
   2348  }
   2349 
   2350  void* contents =
   2351      JS::CreateMappedArrayBufferContents(GET_FD_FROM_FILE(file), offset, size);
   2352  if (!contents) {
   2353    JS_ReportErrorASCII(cx,
   2354                        "failed to allocate mapped array buffer contents "
   2355                        "(possibly due to bad alignment)");
   2356    return false;
   2357  }
   2358 
   2359  RootedObject obj(cx,
   2360                   JS::NewMappedArrayBufferWithContents(cx, size, contents));
   2361  if (!obj) {
   2362    return false;
   2363  }
   2364 
   2365  args.rval().setObject(*obj);
   2366  return true;
   2367 }
   2368 
   2369 #undef GET_FD_FROM_FILE
   2370 
   2371 class UserBufferObject : public NativeObject {
   2372  static const uint32_t BUFFER_SLOT = 0;
   2373  static const uint32_t BYTE_LENGTH_SLOT = 1;
   2374  static const uint32_t RESERVED_SLOTS = 2;
   2375 
   2376  static constexpr auto BufferMemoryUse = MemoryUse::Embedding1;
   2377 
   2378  static void finalize(JS::GCContext* gcx, JSObject* obj);
   2379 
   2380 public:
   2381  static const JSClassOps classOps_;
   2382  static const JSClass class_;
   2383 
   2384  [[nodiscard]] static UserBufferObject* create(JSContext* cx,
   2385                                                size_t byteLength);
   2386 
   2387  void* buffer() const {
   2388    auto& buffer = getReservedSlot(BUFFER_SLOT);
   2389    if (buffer.isUndefined()) {
   2390      return nullptr;
   2391    }
   2392    return buffer.toPrivate();
   2393  }
   2394 
   2395  size_t byteLength() const {
   2396    return size_t(getReservedSlot(BYTE_LENGTH_SLOT).toPrivate());
   2397  }
   2398 };
   2399 
   2400 const JSClassOps UserBufferObject::classOps_ = {
   2401    nullptr,                     // addProperty
   2402    nullptr,                     // delProperty
   2403    nullptr,                     // enumerate
   2404    nullptr,                     // newEnumerate
   2405    nullptr,                     // resolve
   2406    nullptr,                     // mayResolve
   2407    UserBufferObject::finalize,  // finalize
   2408    nullptr,                     // call
   2409    nullptr,                     // construct
   2410    nullptr,                     // trace
   2411 };
   2412 
   2413 const JSClass UserBufferObject::class_ = {
   2414    "UserBufferObject",
   2415    JSCLASS_HAS_RESERVED_SLOTS(UserBufferObject::RESERVED_SLOTS) |
   2416        JSCLASS_BACKGROUND_FINALIZE,
   2417    &UserBufferObject::classOps_,
   2418 };
   2419 
   2420 UserBufferObject* UserBufferObject::create(JSContext* cx, size_t byteLength) {
   2421  void* buffer = js_calloc(byteLength);
   2422  if (!buffer) {
   2423    JS_ReportOutOfMemory(cx);
   2424    return nullptr;
   2425  }
   2426  UniquePtr<void, JS::FreePolicy> ptr(buffer);
   2427 
   2428  auto* userBuffer = NewObjectWithGivenProto<UserBufferObject>(cx, nullptr);
   2429  if (!userBuffer) {
   2430    return nullptr;
   2431  }
   2432 
   2433  InitReservedSlot(userBuffer, BUFFER_SLOT, ptr.release(), byteLength,
   2434                   BufferMemoryUse);
   2435  userBuffer->initReservedSlot(BYTE_LENGTH_SLOT, PrivateValue(byteLength));
   2436 
   2437  return userBuffer;
   2438 }
   2439 
   2440 void UserBufferObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   2441  auto* userBuffer = &obj->as<UserBufferObject>();
   2442  if (auto* buffer = userBuffer->buffer()) {
   2443    gcx->free_(userBuffer, buffer, userBuffer->byteLength(), BufferMemoryUse);
   2444  }
   2445 }
   2446 
   2447 static bool CreateUserArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
   2448  CallArgs args = CallArgsFromVp(argc, vp);
   2449  if (args.length() != 1) {
   2450    JS_ReportErrorNumberASCII(
   2451        cx, my_GetErrorMessage, nullptr,
   2452        args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
   2453        "createUserArrayBuffer");
   2454    return false;
   2455  }
   2456 
   2457  int32_t bytes = 0;
   2458  if (!ToInt32(cx, args[0], &bytes)) {
   2459    return false;
   2460  }
   2461  if (bytes < 0) {
   2462    JS_ReportErrorASCII(cx, "Size must be non-negative");
   2463    return false;
   2464  }
   2465 
   2466  Rooted<UserBufferObject*> userBuffer(cx, UserBufferObject::create(cx, bytes));
   2467  if (!userBuffer) {
   2468    return false;
   2469  }
   2470 
   2471  Rooted<JSObject*> arrayBuffer(
   2472      cx, JS::NewArrayBufferWithUserOwnedContents(cx, userBuffer->byteLength(),
   2473                                                  userBuffer->buffer()));
   2474  if (!arrayBuffer) {
   2475    return false;
   2476  }
   2477 
   2478  // Create a strong reference from |arrayBuffer| to |userBuffer|. This ensures
   2479  // |userBuffer| can't outlive |arrayBuffer|. That way we don't have to worry
   2480  // about detaching the ArrayBuffer object when |userBuffer| gets finalized.
   2481  // The reference is made through a private name, because we don't want to
   2482  // expose |userBuffer| to user-code.
   2483 
   2484  auto* privateName = NewPrivateName(cx, cx->names().empty_.toHandle());
   2485  if (!privateName) {
   2486    return false;
   2487  }
   2488 
   2489  Rooted<PropertyKey> id(cx, PropertyKey::Symbol(privateName));
   2490  Rooted<JS::Value> userBufferVal(cx, ObjectValue(*userBuffer));
   2491  if (!js::DefineDataProperty(cx, arrayBuffer, id, userBufferVal, 0)) {
   2492    return false;
   2493  }
   2494 
   2495  args.rval().setObject(*arrayBuffer);
   2496  return true;
   2497 }
   2498 
   2499 static bool AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp) {
   2500  CallArgs args = CallArgsFromVp(argc, vp);
   2501 
   2502  if (args.length() != 3) {
   2503    JS_ReportErrorNumberASCII(
   2504        cx, my_GetErrorMessage, nullptr,
   2505        args.length() < 3 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
   2506        "addPromiseReactions");
   2507    return false;
   2508  }
   2509 
   2510  RootedObject promise(cx);
   2511  if (args[0].isObject()) {
   2512    promise = &args[0].toObject();
   2513  }
   2514 
   2515  if (!promise || !JS::IsPromiseObject(promise)) {
   2516    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   2517                              JSSMSG_INVALID_ARGS, "addPromiseReactions");
   2518    return false;
   2519  }
   2520 
   2521  RootedObject onResolve(cx);
   2522  if (args[1].isObject()) {
   2523    onResolve = &args[1].toObject();
   2524  }
   2525 
   2526  RootedObject onReject(cx);
   2527  if (args[2].isObject()) {
   2528    onReject = &args[2].toObject();
   2529  }
   2530 
   2531  if (!onResolve || !onResolve->is<JSFunction>() || !onReject ||
   2532      !onReject->is<JSFunction>()) {
   2533    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   2534                              JSSMSG_INVALID_ARGS, "addPromiseReactions");
   2535    return false;
   2536  }
   2537 
   2538  return JS::AddPromiseReactions(cx, promise, onResolve, onReject);
   2539 }
   2540 
   2541 static bool IgnoreUnhandledRejections(JSContext* cx, unsigned argc, Value* vp) {
   2542  CallArgs args = CallArgsFromVp(argc, vp);
   2543 
   2544  ShellContext* sc = GetShellContext(cx);
   2545  sc->trackUnhandledRejections = false;
   2546 
   2547  args.rval().setUndefined();
   2548  return true;
   2549 }
   2550 
   2551 static bool Options(JSContext* cx, unsigned argc, Value* vp) {
   2552  CallArgs args = CallArgsFromVp(argc, vp);
   2553 
   2554  JS::ContextOptions oldContextOptions = JS::ContextOptionsRef(cx);
   2555  for (unsigned i = 0; i < args.length(); i++) {
   2556    RootedString str(cx, JS::ToString(cx, args[i]));
   2557    if (!str) {
   2558      return false;
   2559    }
   2560 
   2561    Rooted<JSLinearString*> opt(cx, str->ensureLinear(cx));
   2562    if (!opt) {
   2563      return false;
   2564    }
   2565 
   2566    if (StringEqualsLiteral(opt, "throw_on_asmjs_validation_failure")) {
   2567      JS::ContextOptionsRef(cx).toggleThrowOnAsmJSValidationFailure();
   2568    } else {
   2569      UniqueChars optChars = QuoteString(cx, opt, '"');
   2570      if (!optChars) {
   2571        return false;
   2572      }
   2573 
   2574      JS_ReportErrorASCII(cx,
   2575                          "unknown option name %s."
   2576                          " The valid name is "
   2577                          "throw_on_asmjs_validation_failure.",
   2578                          optChars.get());
   2579      return false;
   2580    }
   2581  }
   2582 
   2583  UniqueChars names = DuplicateString("");
   2584  bool found = false;
   2585  if (names && oldContextOptions.throwOnAsmJSValidationFailure()) {
   2586    names = JS_sprintf_append(std::move(names), "%s%s", found ? "," : "",
   2587                              "throw_on_asmjs_validation_failure");
   2588    found = true;
   2589  }
   2590  if (!names) {
   2591    JS_ReportOutOfMemory(cx);
   2592    return false;
   2593  }
   2594 
   2595  JSString* str = JS_NewStringCopyZ(cx, names.get());
   2596  if (!str) {
   2597    return false;
   2598  }
   2599  args.rval().setString(str);
   2600  return true;
   2601 }
   2602 
   2603 static bool LoadScript(JSContext* cx, unsigned argc, Value* vp,
   2604                       bool scriptRelative) {
   2605  CallArgs args = CallArgsFromVp(argc, vp);
   2606 
   2607  RootedString str(cx);
   2608  for (unsigned i = 0; i < args.length(); i++) {
   2609    str = JS::ToString(cx, args[i]);
   2610    if (!str) {
   2611      JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   2612                                JSSMSG_INVALID_ARGS, "load");
   2613      return false;
   2614    }
   2615 
   2616    str = ResolvePath(cx, str, scriptRelative ? ScriptRelative : RootRelative);
   2617    if (!str) {
   2618      JS_ReportErrorASCII(cx, "unable to resolve path");
   2619      return false;
   2620    }
   2621 
   2622    UniqueChars filename = JS_EncodeStringToUTF8(cx, str);
   2623    if (!filename) {
   2624      return false;
   2625    }
   2626 
   2627    errno = 0;
   2628 
   2629    CompileOptions opts(cx);
   2630    opts.setIntroductionType("js shell load")
   2631        .setIsRunOnce(true)
   2632        .setNoScriptRval(true)
   2633        .setEagerDelazificationStrategy(defaultDelazificationMode);
   2634 
   2635    RootedValue unused(cx);
   2636    if (!(compileOnly
   2637              ? JS::CompileUtf8Path(cx, opts, filename.get()) != nullptr
   2638              : JS::EvaluateUtf8Path(cx, opts, filename.get(), &unused))) {
   2639      return false;
   2640    }
   2641  }
   2642 
   2643  args.rval().setUndefined();
   2644  return true;
   2645 }
   2646 
   2647 static bool Load(JSContext* cx, unsigned argc, Value* vp) {
   2648  return LoadScript(cx, argc, vp, false);
   2649 }
   2650 
   2651 static bool LoadScriptRelativeToScript(JSContext* cx, unsigned argc,
   2652                                       Value* vp) {
   2653  return LoadScript(cx, argc, vp, true);
   2654 }
   2655 
   2656 static void my_LargeAllocFailCallback() {
   2657  JSContext* cx = TlsContext.get();
   2658  if (!cx) {
   2659    return;
   2660  }
   2661 
   2662  MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
   2663 
   2664  JS::PrepareForFullGC(cx);
   2665  cx->runtime()->gc.gc(JS::GCOptions::Shrink,
   2666                       JS::GCReason::SHARED_MEMORY_LIMIT);
   2667 }
   2668 
   2669 static const uint32_t CacheEntry_SOURCE = 0;
   2670 static const uint32_t CacheEntry_BYTECODE = 1;
   2671 static const uint32_t CacheEntry_OPTIONS = 2;
   2672 
   2673 // Some compile options can't be combined differently between save and load.
   2674 //
   2675 // CacheEntries store a CacheOption set, and on load an exception is thrown
   2676 // if the entries are incompatible.
   2677 
   2678 enum CacheOptions : uint32_t {
   2679  IsRunOnce,
   2680  NoScriptRval,
   2681  Global,
   2682  NonSyntactic,
   2683  SourceIsLazy,
   2684  ForceFullParse,
   2685 };
   2686 
   2687 struct CacheOptionSet : public mozilla::EnumSet<CacheOptions> {
   2688  using mozilla::EnumSet<CacheOptions>::EnumSet;
   2689 
   2690  explicit CacheOptionSet(const CompileOptions& options) : EnumSet() {
   2691    initFromOptions(options);
   2692  }
   2693 
   2694  void initFromOptions(const CompileOptions& options) {
   2695    if (options.noScriptRval) {
   2696      *this += CacheOptions::NoScriptRval;
   2697    }
   2698    if (options.isRunOnce) {
   2699      *this += CacheOptions::IsRunOnce;
   2700    }
   2701    if (options.sourceIsLazy) {
   2702      *this += CacheOptions::SourceIsLazy;
   2703    }
   2704    if (options.forceFullParse()) {
   2705      *this += CacheOptions::ForceFullParse;
   2706    }
   2707    if (options.nonSyntacticScope) {
   2708      *this += CacheOptions::NonSyntactic;
   2709    }
   2710  }
   2711 };
   2712 
   2713 static bool CacheOptionsCompatible(const CacheOptionSet& a,
   2714                                   const CacheOptionSet& b) {
   2715  // If the options are identical, they are trivially compatible.
   2716  return a == b;
   2717 }
   2718 
   2719 static const JSClass CacheEntry_class = {
   2720    "CacheEntryObject",
   2721    JSCLASS_HAS_RESERVED_SLOTS(3),
   2722 };
   2723 
   2724 static bool CacheEntry(JSContext* cx, unsigned argc, JS::Value* vp) {
   2725  CallArgs args = CallArgsFromVp(argc, vp);
   2726 
   2727  if (args.length() != 1 || !args[0].isString()) {
   2728    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   2729                              JSSMSG_INVALID_ARGS, "CacheEntry");
   2730    return false;
   2731  }
   2732 
   2733  RootedObject obj(cx, JS_NewObject(cx, &CacheEntry_class));
   2734  if (!obj) {
   2735    return false;
   2736  }
   2737 
   2738  JS::SetReservedSlot(obj, CacheEntry_SOURCE, args[0]);
   2739  JS::SetReservedSlot(obj, CacheEntry_BYTECODE, UndefinedValue());
   2740 
   2741  // Fill in empty option set.
   2742  CacheOptionSet defaultOptions;
   2743  JS::SetReservedSlot(obj, CacheEntry_OPTIONS,
   2744                      Int32Value(defaultOptions.serialize()));
   2745 
   2746  args.rval().setObject(*obj);
   2747  return true;
   2748 }
   2749 
   2750 static bool CacheEntry_isCacheEntry(JSObject* cache) {
   2751  return cache->hasClass(&CacheEntry_class);
   2752 }
   2753 
   2754 static JSString* CacheEntry_getSource(JSContext* cx, HandleObject cache) {
   2755  MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
   2756  Value v = JS::GetReservedSlot(cache, CacheEntry_SOURCE);
   2757  if (!v.isString()) {
   2758    JS_ReportErrorASCII(
   2759        cx, "CacheEntry_getSource: Unexpected type of source reserved slot.");
   2760    return nullptr;
   2761  }
   2762 
   2763  return v.toString();
   2764 }
   2765 
   2766 static bool CacheEntry_compatible(JSContext* cx, HandleObject cache,
   2767                                  const CacheOptionSet& currentOptionSet) {
   2768  CacheOptionSet cacheEntryOptions;
   2769  MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
   2770  Value v = JS::GetReservedSlot(cache, CacheEntry_OPTIONS);
   2771  cacheEntryOptions.deserialize(v.toInt32());
   2772  if (!CacheOptionsCompatible(cacheEntryOptions, currentOptionSet)) {
   2773    JS_ReportErrorASCII(cx,
   2774                        "CacheEntry_compatible: Incompatible cache contents");
   2775    return false;
   2776  }
   2777  return true;
   2778 }
   2779 
   2780 static uint8_t* CacheEntry_getBytecode(JSContext* cx, HandleObject cache,
   2781                                       size_t* length) {
   2782  MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
   2783  Value v = JS::GetReservedSlot(cache, CacheEntry_BYTECODE);
   2784  if (!v.isObject() || !v.toObject().is<ArrayBufferObject>()) {
   2785    JS_ReportErrorASCII(
   2786        cx,
   2787        "CacheEntry_getBytecode: Unexpected type of bytecode reserved slot.");
   2788    return nullptr;
   2789  }
   2790 
   2791  ArrayBufferObject* arrayBuffer = &v.toObject().as<ArrayBufferObject>();
   2792  *length = arrayBuffer->byteLength();
   2793  return arrayBuffer->dataPointer();
   2794 }
   2795 
   2796 static bool CacheEntry_setBytecode(JSContext* cx, HandleObject cache,
   2797                                   const CacheOptionSet& cacheOptions,
   2798                                   uint8_t* buffer, uint32_t length) {
   2799  MOZ_ASSERT(CacheEntry_isCacheEntry(cache));
   2800 
   2801  using BufferContents = ArrayBufferObject::BufferContents;
   2802 
   2803  BufferContents contents = BufferContents::createMallocedUnknownArena(buffer);
   2804  Rooted<ArrayBufferObject*> arrayBuffer(
   2805      cx, ArrayBufferObject::createForContents(cx, length, contents));
   2806  if (!arrayBuffer) {
   2807    return false;
   2808  }
   2809 
   2810  JS::SetReservedSlot(cache, CacheEntry_BYTECODE, ObjectValue(*arrayBuffer));
   2811  JS::SetReservedSlot(cache, CacheEntry_OPTIONS,
   2812                      Int32Value(cacheOptions.serialize()));
   2813  return true;
   2814 }
   2815 
   2816 static bool ConvertTranscodeResultToJSException(JSContext* cx,
   2817                                                JS::TranscodeResult rv) {
   2818  switch (rv) {
   2819    case JS::TranscodeResult::Ok:
   2820      return true;
   2821 
   2822    default:
   2823      [[fallthrough]];
   2824    case JS::TranscodeResult::Failure:
   2825      MOZ_ASSERT(!cx->isExceptionPending());
   2826      JS_ReportErrorASCII(cx, "generic warning");
   2827      return false;
   2828    case JS::TranscodeResult::Failure_BadBuildId:
   2829      MOZ_ASSERT(!cx->isExceptionPending());
   2830      JS_ReportErrorASCII(cx, "the build-id does not match");
   2831      return false;
   2832    case JS::TranscodeResult::Failure_AsmJSNotSupported:
   2833      MOZ_ASSERT(!cx->isExceptionPending());
   2834      JS_ReportErrorASCII(cx, "Asm.js is not supported by XDR");
   2835      return false;
   2836    case JS::TranscodeResult::Failure_BadDecode:
   2837      MOZ_ASSERT(!cx->isExceptionPending());
   2838      JS_ReportErrorASCII(cx, "XDR data corruption");
   2839      return false;
   2840 
   2841    case JS::TranscodeResult::Throw:
   2842      MOZ_ASSERT(cx->isExceptionPending());
   2843      return false;
   2844  }
   2845 }
   2846 
   2847 static void SetQuitting(JSContext* cx, int32_t code) {
   2848  ShellContext* sc = GetShellContext(cx);
   2849  js::StopDrainingJobQueue(cx);
   2850 #ifdef DEBUG
   2851  {
   2852    AutoLockHelperThreadState helperLock;
   2853 
   2854    OffThreadPromiseRuntimeState& state =
   2855        cx->runtime()->offThreadPromiseState.ref();
   2856    state.setForceQuitting();
   2857  }
   2858 #endif
   2859  sc->exitCode = code;
   2860  sc->quitting = true;
   2861 }
   2862 
   2863 static void UnsetQuitting(JSContext* cx) {
   2864  ShellContext* sc = GetShellContext(cx);
   2865  js::RestartDrainingJobQueue(cx);
   2866  sc->exitCode = 0;
   2867  sc->quitting = false;
   2868 }
   2869 
   2870 static bool Evaluate(JSContext* cx, unsigned argc, Value* vp) {
   2871  CallArgs args = CallArgsFromVp(argc, vp);
   2872 
   2873  if (args.length() < 1 || args.length() > 2) {
   2874    JS_ReportErrorNumberASCII(
   2875        cx, my_GetErrorMessage, nullptr,
   2876        args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
   2877        "evaluate");
   2878    return false;
   2879  }
   2880 
   2881  RootedString code(cx, nullptr);
   2882  RootedObject cacheEntry(cx, nullptr);
   2883  if (args[0].isString()) {
   2884    code = args[0].toString();
   2885  } else if (args[0].isObject() &&
   2886             CacheEntry_isCacheEntry(&args[0].toObject())) {
   2887    cacheEntry = &args[0].toObject();
   2888    code = CacheEntry_getSource(cx, cacheEntry);
   2889    if (!code) {
   2890      return false;
   2891    }
   2892  }
   2893 
   2894  if (!code || (args.length() == 2 && args[1].isPrimitive())) {
   2895    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   2896                              JSSMSG_INVALID_ARGS, "evaluate");
   2897    return false;
   2898  }
   2899 
   2900  RootedObject opts(cx);
   2901  if (args.length() == 2) {
   2902    if (!args[1].isObject()) {
   2903      JS_ReportErrorASCII(cx, "evaluate: The 2nd argument must be an object");
   2904      return false;
   2905    }
   2906 
   2907    opts = &args[1].toObject();
   2908  }
   2909 
   2910  RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
   2911  MOZ_ASSERT(global);
   2912 
   2913  return EvaluateInner(cx, code, &global, opts, cacheEntry, args.rval());
   2914 }
   2915 
   2916 static bool EvaluateInner(JSContext* cx, HandleString code,
   2917                          MutableHandleObject global, HandleObject opts,
   2918                          HandleObject cacheEntry, MutableHandleValue rval) {
   2919  // Check "global" property before everything to use the given global's
   2920  // option as the default value.
   2921  Maybe<CompileOptions> maybeOptions;
   2922  if (opts) {
   2923    RootedValue v(cx);
   2924    if (!JS_GetProperty(cx, opts, "global", &v)) {
   2925      return false;
   2926    }
   2927    if (!v.isUndefined()) {
   2928      if (v.isObject()) {
   2929        global.set(js::CheckedUnwrapDynamic(&v.toObject(), cx,
   2930                                            /* stopAtWindowProxy = */ false));
   2931        if (!global) {
   2932          return false;
   2933        }
   2934      }
   2935      if (!global || !(JS::GetClass(global)->flags & JSCLASS_IS_GLOBAL)) {
   2936        JS_ReportErrorNumberASCII(
   2937            cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   2938            "\"global\" passed to evaluate()", "not a global object");
   2939        return false;
   2940      }
   2941 
   2942      JSAutoRealm ar(cx, global);
   2943      maybeOptions.emplace(cx);
   2944    }
   2945  }
   2946  if (!maybeOptions) {
   2947    // If "global" property is not given, use the current global's option as
   2948    // the default value.
   2949    maybeOptions.emplace(cx);
   2950  }
   2951 
   2952  CompileOptions& options = maybeOptions.ref();
   2953  UniqueChars fileNameBytes;
   2954  RootedString displayURL(cx);
   2955  RootedString sourceMapURL(cx);
   2956  bool catchTermination = false;
   2957  bool loadBytecode = false;
   2958  bool saveBytecodeWithDelazifications = false;
   2959  bool execute = true;
   2960  bool assertEqBytecode = false;
   2961  JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No);
   2962  RootedObject callerGlobal(cx, cx->global());
   2963 
   2964  options.setIntroductionType("js shell evaluate")
   2965      .setFileAndLine("@evaluate", 1)
   2966      .setDeferDebugMetadata();
   2967 
   2968  RootedValue privateValue(cx);
   2969  RootedString elementAttributeName(cx);
   2970 
   2971  if (opts) {
   2972    if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
   2973      return false;
   2974    }
   2975    if (!ParseDebugMetadata(cx, opts, &privateValue, &elementAttributeName)) {
   2976      return false;
   2977    }
   2978    if (!ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) {
   2979      return false;
   2980    }
   2981 
   2982    RootedValue v(cx);
   2983    if (!JS_GetProperty(cx, opts, "catchTermination", &v)) {
   2984      return false;
   2985    }
   2986    if (!v.isUndefined()) {
   2987      catchTermination = ToBoolean(v);
   2988    }
   2989 
   2990    if (!JS_GetProperty(cx, opts, "loadBytecode", &v)) {
   2991      return false;
   2992    }
   2993    if (!v.isUndefined()) {
   2994      loadBytecode = ToBoolean(v);
   2995    }
   2996 
   2997    if (!JS_GetProperty(cx, opts, "saveBytecodeWithDelazifications", &v)) {
   2998      return false;
   2999    }
   3000    if (!v.isUndefined()) {
   3001      saveBytecodeWithDelazifications = ToBoolean(v);
   3002    }
   3003 
   3004    if (!JS_GetProperty(cx, opts, "execute", &v)) {
   3005      return false;
   3006    }
   3007    if (!v.isUndefined()) {
   3008      execute = ToBoolean(v);
   3009    }
   3010 
   3011    if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v)) {
   3012      return false;
   3013    }
   3014    if (!v.isUndefined()) {
   3015      assertEqBytecode = ToBoolean(v);
   3016    }
   3017 
   3018    if (!JS_GetProperty(cx, opts, "envChainObject", &v)) {
   3019      return false;
   3020    }
   3021    if (!v.isUndefined()) {
   3022      if (!v.isObject()) {
   3023        JS_ReportErrorNumberASCII(
   3024            cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   3025            "\"envChainObject\" passed to evaluate()", "not an object");
   3026        return false;
   3027      }
   3028 
   3029      RootedObject obj(cx, &v.toObject());
   3030      {
   3031        // This may be a CCW, so try to unwrap before checking
   3032        // if it is an unqualified variables object. We still append
   3033        // the original object to the environment chain however.
   3034        JSObject* unwrappedObj = js::UncheckedUnwrap(obj, cx);
   3035        if (unwrappedObj->isUnqualifiedVarObj()) {
   3036          JS_ReportErrorASCII(
   3037              cx,
   3038              "\"envChainObject\" passed to evaluate() should not be an "
   3039              "unqualified variables object");
   3040          return false;
   3041        }
   3042      }
   3043 
   3044      if (!envChain.append(obj)) {
   3045        return false;
   3046      }
   3047    }
   3048 
   3049    if (!JS_GetProperty(cx, opts, "supportUnscopables", &v)) {
   3050      return false;
   3051    }
   3052    if (!v.isUndefined()) {
   3053      envChain.setSupportUnscopables(JS::SupportUnscopables(ToBoolean(v)));
   3054    }
   3055 
   3056    // We cannot load or save the bytecode if we have no object where the
   3057    // bytecode cache is stored.
   3058    if (loadBytecode || saveBytecodeWithDelazifications) {
   3059      if (!cacheEntry) {
   3060        JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   3061                                  JSSMSG_INVALID_ARGS, "evaluate");
   3062        return false;
   3063      }
   3064    }
   3065  }
   3066 
   3067  if (envChain.length() != 0) {
   3068    // Wrap the envChainObject list into target realm.
   3069    JSAutoRealm ar(cx, global);
   3070    for (size_t i = 0; i < envChain.length(); ++i) {
   3071      if (!JS_WrapObject(cx, envChain.chain()[i])) {
   3072        return false;
   3073      }
   3074    }
   3075 
   3076    options.setNonSyntacticScope(true);
   3077  }
   3078 
   3079  // The `loadBuffer` we use below outlives the Stencil we generate so we can
   3080  // use its contents directly in the Stencil.
   3081  options.borrowBuffer = true;
   3082 
   3083  // We need to track the options used to generate bytecode for a CacheEntry to
   3084  // avoid mismatches. This is primarily a concern when fuzzing the jsshell.
   3085  CacheOptionSet cacheOptions;
   3086  cacheOptions.initFromOptions(options);
   3087 
   3088  JS::TranscodeBuffer loadBuffer;
   3089  JS::TranscodeBuffer saveBuffer;
   3090 
   3091  if (loadBytecode) {
   3092    size_t loadLength = 0;
   3093    uint8_t* loadData = nullptr;
   3094 
   3095    if (!CacheEntry_compatible(cx, cacheEntry, cacheOptions)) {
   3096      return false;
   3097    }
   3098 
   3099    loadData = CacheEntry_getBytecode(cx, cacheEntry, &loadLength);
   3100    if (!loadData) {
   3101      return false;
   3102    }
   3103    if (!loadBuffer.append(loadData, loadLength)) {
   3104      JS_ReportOutOfMemory(cx);
   3105      return false;
   3106    }
   3107  }
   3108 
   3109  {
   3110    JSAutoRealm ar(cx, global);
   3111    RefPtr<JS::Stencil> stencil;
   3112 
   3113    if (loadBytecode) {
   3114      JS::TranscodeRange range(loadBuffer.begin(), loadBuffer.length());
   3115      JS::DecodeOptions decodeOptions(options);
   3116 
   3117      JS::TranscodeResult rv =
   3118          JS::DecodeStencil(cx, decodeOptions, range, getter_AddRefs(stencil));
   3119      if (JS::IsTranscodeFailureResult(rv)) {
   3120        JS_ReportErrorASCII(cx, "failed to decode cache");
   3121        return false;
   3122      }
   3123 
   3124      if (!ConvertTranscodeResultToJSException(cx, rv)) {
   3125        return false;
   3126      }
   3127    } else {
   3128      AutoStableStringChars linearChars(cx);
   3129      if (!linearChars.initTwoByte(cx, code)) {
   3130        return false;
   3131      }
   3132 
   3133      JS::SourceText<char16_t> srcBuf;
   3134      if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
   3135        return false;
   3136      }
   3137 
   3138      stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
   3139      if (!stencil) {
   3140        return false;
   3141      }
   3142    }
   3143 
   3144    if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencil.get())) {
   3145      return false;
   3146    }
   3147 
   3148    JS::InstantiateOptions instantiateOptions(options);
   3149    RootedScript script(
   3150        cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil));
   3151    if (!script) {
   3152      return false;
   3153    }
   3154 
   3155    AutoReportFrontendContext fc(cx);
   3156    if (!SetSourceOptions(cx, &fc, script->scriptSource(), displayURL,
   3157                          sourceMapURL)) {
   3158      return false;
   3159    }
   3160 
   3161    if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue,
   3162                                 elementAttributeName, nullptr, nullptr)) {
   3163      return false;
   3164    }
   3165 
   3166    if (saveBytecodeWithDelazifications) {
   3167      bool alreadyStarted;
   3168      if (!JS::StartCollectingDelazifications(cx, script, stencil,
   3169                                              alreadyStarted)) {
   3170        return false;
   3171      }
   3172      MOZ_ASSERT(!alreadyStarted);
   3173    }
   3174 
   3175    if (execute) {
   3176      if (!(envChain.empty() ? JS_ExecuteScript(cx, script, rval)
   3177                             : JS_ExecuteScript(cx, envChain, script, rval))) {
   3178        if (catchTermination && !JS_IsExceptionPending(cx)) {
   3179          ShellContext* sc = GetShellContext(cx);
   3180          if (sc->quitting) {
   3181            UnsetQuitting(cx);
   3182          }
   3183 
   3184          JSAutoRealm ar1(cx, callerGlobal);
   3185          JSString* str = JS_NewStringCopyZ(cx, "terminated");
   3186          if (!str) {
   3187            return false;
   3188          }
   3189          rval.setString(str);
   3190          return true;
   3191        }
   3192        return false;
   3193      }
   3194    }
   3195 
   3196    // Serialize the encoded bytecode, recorded before the execution, into a
   3197    // buffer which can be deserialized linearly.
   3198    if (saveBytecodeWithDelazifications) {
   3199      RefPtr<JS::Stencil> stencil;
   3200      if (!FinishCollectingDelazifications(cx, script,
   3201                                           getter_AddRefs(stencil))) {
   3202        return false;
   3203      }
   3204      JS::TranscodeResult result = JS::EncodeStencil(cx, stencil, saveBuffer);
   3205      if (result != JS::TranscodeResult::Ok) {
   3206        return false;
   3207      }
   3208    }
   3209  }
   3210 
   3211  if (saveBytecodeWithDelazifications) {
   3212    // If we are both loading and saving, we assert that we are going to
   3213    // replace the current bytecode by the same stream of bytes.
   3214    if (loadBytecode && assertEqBytecode) {
   3215      if (saveBuffer.length() != loadBuffer.length()) {
   3216        char loadLengthStr[16];
   3217        SprintfLiteral(loadLengthStr, "%zu", loadBuffer.length());
   3218        char saveLengthStr[16];
   3219        SprintfLiteral(saveLengthStr, "%zu", saveBuffer.length());
   3220 
   3221        JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   3222                                  JSSMSG_CACHE_EQ_SIZE_FAILED, loadLengthStr,
   3223                                  saveLengthStr);
   3224        return false;
   3225      }
   3226 
   3227      if (!ArrayEqual(loadBuffer.begin(), saveBuffer.begin(),
   3228                      loadBuffer.length())) {
   3229        JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   3230                                  JSSMSG_CACHE_EQ_CONTENT_FAILED);
   3231        return false;
   3232      }
   3233    }
   3234 
   3235    size_t saveLength = saveBuffer.length();
   3236    if (saveLength >= INT32_MAX) {
   3237      JS_ReportErrorASCII(cx, "Cannot save large cache entry content");
   3238      return false;
   3239    }
   3240    uint8_t* saveData = saveBuffer.extractOrCopyRawBuffer();
   3241    if (!CacheEntry_setBytecode(cx, cacheEntry, cacheOptions, saveData,
   3242                                saveLength)) {
   3243      js_free(saveData);
   3244      return false;
   3245    }
   3246  }
   3247 
   3248  return JS_WrapValue(cx, rval);
   3249 }
   3250 
   3251 JSString* js::shell::FileAsString(JSContext* cx, JS::HandleString pathnameStr) {
   3252  UniqueChars pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
   3253  if (!pathname) {
   3254    return nullptr;
   3255  }
   3256 
   3257  FILE* file = OpenFile(cx, pathname.get(), "rb");
   3258  if (!file) {
   3259    return nullptr;
   3260  }
   3261 
   3262  AutoCloseFile autoClose(file);
   3263 
   3264  struct stat st;
   3265  if (fstat(fileno(file), &st) != 0) {
   3266    JS_ReportErrorUTF8(cx, "can't stat %s", pathname.get());
   3267    return nullptr;
   3268  }
   3269 
   3270  if ((st.st_mode & S_IFMT) != S_IFREG) {
   3271    JS_ReportErrorUTF8(cx, "can't read non-regular file %s", pathname.get());
   3272    return nullptr;
   3273  }
   3274 
   3275  size_t len;
   3276  if (!FileSize(cx, pathname.get(), file, &len)) {
   3277    return nullptr;
   3278  }
   3279 
   3280  UniqueChars buf(js_pod_malloc<char>(len + 1));
   3281  if (!buf) {
   3282    JS_ReportErrorUTF8(cx, "out of memory reading %s", pathname.get());
   3283    return nullptr;
   3284  }
   3285 
   3286  if (!ReadFile(cx, pathname.get(), file, buf.get(), len)) {
   3287    return nullptr;
   3288  }
   3289 
   3290  UniqueTwoByteChars ucbuf(
   3291      JS::LossyUTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf.get(), len),
   3292                                           &len, js::MallocArena)
   3293          .get());
   3294  if (!ucbuf) {
   3295    JS_ReportErrorUTF8(cx, "Invalid UTF-8 in file '%s'", pathname.get());
   3296    return nullptr;
   3297  }
   3298 
   3299  return JS_NewUCStringCopyN(cx, ucbuf.get(), len);
   3300 }
   3301 
   3302 /*
   3303 * Function to run scripts and return compilation + execution time. Semantics
   3304 * are closely modelled after the equivalent function in WebKit, as this is used
   3305 * to produce benchmark timings by SunSpider.
   3306 */
   3307 static bool Run(JSContext* cx, unsigned argc, Value* vp) {
   3308  CallArgs args = CallArgsFromVp(argc, vp);
   3309  if (args.length() != 1) {
   3310    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   3311                              JSSMSG_INVALID_ARGS, "run");
   3312    return false;
   3313  }
   3314 
   3315  RootedString str(cx, JS::ToString(cx, args[0]));
   3316  if (!str) {
   3317    return false;
   3318  }
   3319  args[0].setString(str);
   3320 
   3321  str = FileAsString(cx, str);
   3322  if (!str) {
   3323    return false;
   3324  }
   3325 
   3326  AutoStableStringChars linearChars(cx);
   3327  if (!linearChars.initTwoByte(cx, str)) {
   3328    return false;
   3329  }
   3330 
   3331  JS::SourceText<char16_t> srcBuf;
   3332  if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
   3333    return false;
   3334  }
   3335 
   3336  RootedScript script(cx);
   3337  int64_t startClock = PRMJ_Now();
   3338  {
   3339    UniqueChars filename = JS_EncodeStringToUTF8(cx, str);
   3340    if (!filename) {
   3341      return false;
   3342    }
   3343 
   3344    JS::CompileOptions options(cx);
   3345    options.setIntroductionType("js shell run")
   3346        .setFileAndLine(filename.get(), 1)
   3347        .setIsRunOnce(true)
   3348        .setNoScriptRval(true)
   3349        .setEagerDelazificationStrategy(defaultDelazificationMode);
   3350 
   3351    script = JS::Compile(cx, options, srcBuf);
   3352    if (!script) {
   3353      return false;
   3354    }
   3355  }
   3356 
   3357  if (!JS_ExecuteScript(cx, script)) {
   3358    return false;
   3359  }
   3360 
   3361  int64_t endClock = PRMJ_Now();
   3362 
   3363  args.rval().setDouble((endClock - startClock) / double(PRMJ_USEC_PER_MSEC));
   3364  return true;
   3365 }
   3366 
   3367 static int js_fgets(char* buf, int size, FILE* file) {
   3368  int n, i, c;
   3369  bool crflag;
   3370 
   3371  n = size - 1;
   3372  if (n < 0) {
   3373    return -1;
   3374  }
   3375 
   3376  // Use the fastest available getc.
   3377  auto fast_getc =
   3378 #if defined(HAVE_GETC_UNLOCKED)
   3379      getc_unlocked
   3380 #elif defined(HAVE__GETC_NOLOCK)
   3381      _getc_nolock
   3382 #else
   3383      getc
   3384 #endif
   3385      ;
   3386 
   3387  crflag = false;
   3388  for (i = 0; i < n && (c = fast_getc(file)) != EOF; i++) {
   3389    buf[i] = c;
   3390    if (c == '\n') {  // any \n ends a line
   3391      i++;            // keep the \n; we know there is room for \0
   3392      break;
   3393    }
   3394    if (crflag) {  // \r not followed by \n ends line at the \r
   3395      ungetc(c, file);
   3396      break;  // and overwrite c in buf with \0
   3397    }
   3398    crflag = (c == '\r');
   3399  }
   3400 
   3401  buf[i] = '\0';
   3402  return i;
   3403 }
   3404 
   3405 /*
   3406 * function readline()
   3407 * Provides a hook for scripts to read a line from stdin.
   3408 */
   3409 static bool ReadLine(JSContext* cx, unsigned argc, Value* vp) {
   3410  CallArgs args = CallArgsFromVp(argc, vp);
   3411 
   3412  // Don't support readline() on worker threads because js_fgets is not
   3413  // thread-safe. This also avoids non-deterministic behavior for fuzzers that
   3414  // use readline() for communication.
   3415  if (GetShellContext(cx)->isWorker) {
   3416    JS_ReportErrorASCII(cx, "readline() is not supported on worker threads");
   3417    return false;
   3418  }
   3419 
   3420  static constexpr size_t BUFSIZE = 256;
   3421  FILE* from = stdin;
   3422  size_t buflength = 0;
   3423  size_t bufsize = BUFSIZE;
   3424  char* buf = (char*)JS_malloc(cx, bufsize);
   3425  if (!buf) {
   3426    JS_ReportOutOfMemory(cx);
   3427    return false;
   3428  }
   3429  auto freeBuf = mozilla::MakeScopeExit([&]() {
   3430    JS_free(cx, buf);
   3431    buf = nullptr;
   3432  });
   3433 
   3434  bool sawNewline = false;
   3435  size_t gotlength;
   3436  while ((gotlength = js_fgets(buf + buflength, bufsize - buflength, from)) >
   3437         0) {
   3438    buflength += gotlength;
   3439 
   3440    /* Are we done? */
   3441    if (buf[buflength - 1] == '\n') {
   3442      buf[buflength - 1] = '\0';
   3443      sawNewline = true;
   3444      break;
   3445    } else if (buflength < bufsize - 1) {
   3446      break;
   3447    }
   3448 
   3449    /* Else, grow our buffer for another pass. */
   3450    bufsize *= 2;
   3451    if (bufsize <= buflength) {
   3452      JS_ReportAllocationOverflow(cx);
   3453      return false;
   3454    }
   3455    char* tmp = static_cast<char*>(JS_realloc(cx, buf, bufsize / 2, bufsize));
   3456    if (!tmp) {
   3457      JS_ReportOutOfMemory(cx);
   3458      return false;
   3459    }
   3460    buf = tmp;
   3461  }
   3462 
   3463  /* Treat the empty string specially. */
   3464  if (buflength == 0) {
   3465    args.rval().set(feof(from) ? NullValue() : JS_GetEmptyStringValue(cx));
   3466    return true;
   3467  }
   3468 
   3469  /*
   3470   * Turn buf into a JSString. Note that buflength includes the trailing null
   3471   * character.
   3472   */
   3473  JSString* str =
   3474      JS_NewStringCopyN(cx, buf, sawNewline ? buflength - 1 : buflength);
   3475  if (!str) {
   3476    return false;
   3477  }
   3478 
   3479  args.rval().setString(str);
   3480  return true;
   3481 }
   3482 
   3483 /*
   3484 * function readlineBuf()
   3485 * Provides a hook for scripts to emulate readline() using a string object.
   3486 */
   3487 static bool ReadLineBuf(JSContext* cx, unsigned argc, Value* vp) {
   3488  CallArgs args = CallArgsFromVp(argc, vp);
   3489  ShellContext* sc = GetShellContext(cx);
   3490 
   3491  if (!args.length()) {
   3492    if (!sc->readLineBuf) {
   3493      JS_ReportErrorASCII(cx,
   3494                          "No source buffer set. You must initially "
   3495                          "call readlineBuf with an argument.");
   3496      return false;
   3497    }
   3498 
   3499    char* currentBuf = sc->readLineBuf.get() + sc->readLineBufPos;
   3500    size_t buflen = strlen(currentBuf);
   3501 
   3502    if (!buflen) {
   3503      args.rval().setNull();
   3504      return true;
   3505    }
   3506 
   3507    size_t len = 0;
   3508    while (len < buflen) {
   3509      if (currentBuf[len] == '\n') {
   3510        break;
   3511      }
   3512      len++;
   3513    }
   3514 
   3515    JSString* str = JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(currentBuf, len));
   3516    if (!str) {
   3517      return false;
   3518    }
   3519 
   3520    if (currentBuf[len] == '\0') {
   3521      sc->readLineBufPos += len;
   3522    } else {
   3523      sc->readLineBufPos += len + 1;
   3524    }
   3525 
   3526    args.rval().setString(str);
   3527    return true;
   3528  }
   3529 
   3530  if (args.length() == 1) {
   3531    sc->readLineBuf = nullptr;
   3532    sc->readLineBufPos = 0;
   3533 
   3534    RootedString str(cx, JS::ToString(cx, args[0]));
   3535    if (!str) {
   3536      return false;
   3537    }
   3538    sc->readLineBuf = JS_EncodeStringToUTF8(cx, str);
   3539    if (!sc->readLineBuf) {
   3540      return false;
   3541    }
   3542 
   3543    args.rval().setUndefined();
   3544    return true;
   3545  }
   3546 
   3547  JS_ReportErrorASCII(cx, "Must specify at most one argument");
   3548  return false;
   3549 }
   3550 
   3551 static bool PutStr(JSContext* cx, unsigned argc, Value* vp) {
   3552  CallArgs args = CallArgsFromVp(argc, vp);
   3553 
   3554  if (args.length() != 0) {
   3555    if (!gOutFile->isOpen()) {
   3556      JS_ReportErrorASCII(cx, "output file is closed");
   3557      return false;
   3558    }
   3559 
   3560    RootedString str(cx, JS::ToString(cx, args[0]));
   3561    if (!str) {
   3562      return false;
   3563    }
   3564    UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
   3565    if (!bytes) {
   3566      return false;
   3567    }
   3568    fputs(bytes.get(), gOutFile->fp);
   3569    fflush(gOutFile->fp);
   3570  }
   3571 
   3572  args.rval().setUndefined();
   3573  return true;
   3574 }
   3575 
   3576 static bool Now(JSContext* cx, unsigned argc, Value* vp) {
   3577  CallArgs args = CallArgsFromVp(argc, vp);
   3578  double now = PRMJ_Now() / double(PRMJ_USEC_PER_MSEC);
   3579  args.rval().setDouble(now);
   3580  return true;
   3581 }
   3582 
   3583 static bool CpuNow(JSContext* cx, unsigned argc, Value* vp) {
   3584  CallArgs args = CallArgsFromVp(argc, vp);
   3585  double now = double(std::clock()) / double(CLOCKS_PER_SEC);
   3586  args.rval().setDouble(now);
   3587  return true;
   3588 }
   3589 
   3590 static bool PrintInternal(JSContext* cx, const CallArgs& args, RCFile* file) {
   3591  if (!file->isOpen()) {
   3592    JS_ReportErrorASCII(cx, "output file is closed");
   3593    return false;
   3594  }
   3595 
   3596  for (unsigned i = 0; i < args.length(); i++) {
   3597    RootedString str(cx, JS::ToString(cx, args[i]));
   3598    if (!str) {
   3599      return false;
   3600    }
   3601    UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
   3602    if (!bytes) {
   3603      return false;
   3604    }
   3605    fprintf(file->fp, "%s%s", i ? " " : "", bytes.get());
   3606  }
   3607 
   3608  fputc('\n', file->fp);
   3609  fflush(file->fp);
   3610 
   3611  args.rval().setUndefined();
   3612  return true;
   3613 }
   3614 
   3615 static bool Print(JSContext* cx, unsigned argc, Value* vp) {
   3616  CallArgs args = CallArgsFromVp(argc, vp);
   3617 #ifdef FUZZING_INTERFACES
   3618  if (fuzzHaveModule && !fuzzDoDebug) {
   3619    // When fuzzing and not debugging, suppress any print() output,
   3620    // as it slows down fuzzing and makes libFuzzer's output hard
   3621    // to read.
   3622    args.rval().setUndefined();
   3623    return true;
   3624  }
   3625 #endif  // FUZZING_INTERFACES
   3626  return PrintInternal(cx, args, gOutFile);
   3627 }
   3628 
   3629 static bool PrintErr(JSContext* cx, unsigned argc, Value* vp) {
   3630  CallArgs args = CallArgsFromVp(argc, vp);
   3631  return PrintInternal(cx, args, gErrFile);
   3632 }
   3633 
   3634 static bool Help(JSContext* cx, unsigned argc, Value* vp);
   3635 
   3636 static bool Quit(JSContext* cx, unsigned argc, Value* vp) {
   3637  // Print a message to stderr in differential testing to help jsfunfuzz
   3638  // find uncatchable-exception bugs.
   3639  if (js::SupportDifferentialTesting()) {
   3640    fprintf(stderr, "quit called\n");
   3641  }
   3642 
   3643  CallArgs args = CallArgsFromVp(argc, vp);
   3644  int32_t code;
   3645  if (!ToInt32(cx, args.get(0), &code)) {
   3646    return false;
   3647  }
   3648 
   3649  // The fuzzers check the shell's exit code and assume a value >= 128 means
   3650  // the process crashed (for instance, SIGSEGV will result in code 139). On
   3651  // POSIX platforms, the exit code is 8-bit and negative values can also
   3652  // result in an exit code >= 128. We restrict the value to range [0, 127] to
   3653  // avoid false positives.
   3654  if (code < 0 || code >= 128) {
   3655    JS_ReportErrorASCII(cx, "quit exit code should be in range 0-127");
   3656    return false;
   3657  }
   3658 
   3659  SetQuitting(cx, code);
   3660  JS::ReportUncatchableException(cx);
   3661  return false;
   3662 }
   3663 
   3664 static bool StartTimingMutator(JSContext* cx, unsigned argc, Value* vp) {
   3665  CallArgs args = CallArgsFromVp(argc, vp);
   3666  if (args.length() > 0) {
   3667    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   3668                              JSSMSG_TOO_MANY_ARGS, "startTimingMutator");
   3669    return false;
   3670  }
   3671 
   3672  if (!cx->runtime()->gc.stats().startTimingMutator()) {
   3673    JS_ReportErrorASCII(
   3674        cx, "StartTimingMutator should only be called from outside of GC");
   3675    return false;
   3676  }
   3677 
   3678  args.rval().setUndefined();
   3679  return true;
   3680 }
   3681 
   3682 static bool StopTimingMutator(JSContext* cx, unsigned argc, Value* vp) {
   3683  CallArgs args = CallArgsFromVp(argc, vp);
   3684  if (args.length() > 0) {
   3685    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   3686                              JSSMSG_TOO_MANY_ARGS, "stopTimingMutator");
   3687    return false;
   3688  }
   3689 
   3690  double mutator_ms, gc_ms;
   3691  if (!cx->runtime()->gc.stats().stopTimingMutator(mutator_ms, gc_ms)) {
   3692    JS_ReportErrorASCII(cx,
   3693                        "stopTimingMutator called when not timing the mutator");
   3694    return false;
   3695  }
   3696  double total_ms = mutator_ms + gc_ms;
   3697  if (total_ms > 0 && gOutFile->isOpen()) {
   3698    fprintf(gOutFile->fp, "Mutator: %.3fms (%.1f%%), GC: %.3fms (%.1f%%)\n",
   3699            mutator_ms, mutator_ms / total_ms * 100.0, gc_ms,
   3700            gc_ms / total_ms * 100.0);
   3701  }
   3702 
   3703  args.rval().setUndefined();
   3704  return true;
   3705 }
   3706 
   3707 static const char* ToSource(JSContext* cx, HandleValue vp, UniqueChars* bytes) {
   3708  RootedString str(cx, JS_ValueToSource(cx, vp));
   3709  if (str) {
   3710    *bytes = JS_EncodeStringToUTF8(cx, str);
   3711    if (*bytes) {
   3712      return bytes->get();
   3713    }
   3714  }
   3715  JS_ClearPendingException(cx);
   3716  return "<<error converting value to string>>";
   3717 }
   3718 
   3719 static bool AssertEq(JSContext* cx, unsigned argc, Value* vp) {
   3720  CallArgs args = CallArgsFromVp(argc, vp);
   3721  if (!(args.length() == 2 || (args.length() == 3 && args[2].isString()))) {
   3722    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   3723                              (args.length() < 2)    ? JSSMSG_NOT_ENOUGH_ARGS
   3724                              : (args.length() == 3) ? JSSMSG_INVALID_ARGS
   3725                                                     : JSSMSG_TOO_MANY_ARGS,
   3726                              "assertEq");
   3727    return false;
   3728  }
   3729 
   3730  bool same;
   3731  if (!JS::SameValue(cx, args[0], args[1], &same)) {
   3732    return false;
   3733  }
   3734  if (!same) {
   3735    UniqueChars bytes0, bytes1;
   3736    const char* actual = ToSource(cx, args[0], &bytes0);
   3737    const char* expected = ToSource(cx, args[1], &bytes1);
   3738    if (args.length() == 2) {
   3739      JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr,
   3740                               JSSMSG_ASSERT_EQ_FAILED, actual, expected);
   3741    } else {
   3742      RootedString message(cx, args[2].toString());
   3743      UniqueChars bytes2 = QuoteString(cx, message);
   3744      if (!bytes2) {
   3745        return false;
   3746      }
   3747      JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr,
   3748                               JSSMSG_ASSERT_EQ_FAILED_MSG, actual, expected,
   3749                               bytes2.get());
   3750    }
   3751    return false;
   3752  }
   3753  args.rval().setUndefined();
   3754  return true;
   3755 }
   3756 
   3757 static JSScript* GetTopScript(JSContext* cx) {
   3758  NonBuiltinScriptFrameIter iter(cx);
   3759  return iter.done() ? nullptr : iter.script();
   3760 }
   3761 
   3762 static bool GetScriptAndPCArgs(JSContext* cx, CallArgs& args,
   3763                               MutableHandleScript scriptp, int32_t* ip) {
   3764  RootedScript script(cx, GetTopScript(cx));
   3765  *ip = 0;
   3766  if (!args.get(0).isUndefined()) {
   3767    HandleValue v = args[0];
   3768    unsigned intarg = 0;
   3769    if (v.isObject() && JS::GetClass(&v.toObject())->isJSFunction()) {
   3770      script = TestingFunctionArgumentToScript(cx, v);
   3771      if (!script) {
   3772        return false;
   3773      }
   3774      intarg++;
   3775    }
   3776    if (!args.get(intarg).isUndefined()) {
   3777      if (!JS::ToInt32(cx, args[intarg], ip)) {
   3778        return false;
   3779      }
   3780      if ((uint32_t)*ip >= script->length()) {
   3781        JS_ReportErrorASCII(cx, "Invalid PC");
   3782        return false;
   3783      }
   3784    }
   3785  }
   3786 
   3787  scriptp.set(script);
   3788 
   3789  return true;
   3790 }
   3791 
   3792 static bool LineToPC(JSContext* cx, unsigned argc, Value* vp) {
   3793  CallArgs args = CallArgsFromVp(argc, vp);
   3794 
   3795  if (args.length() == 0) {
   3796    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   3797                              JSSMSG_LINE2PC_USAGE);
   3798    return false;
   3799  }
   3800 
   3801  RootedScript script(cx, GetTopScript(cx));
   3802  int32_t lineArg = 0;
   3803  if (args[0].isObject() && args[0].toObject().is<JSFunction>()) {
   3804    script = TestingFunctionArgumentToScript(cx, args[0]);
   3805    if (!script) {
   3806      return false;
   3807    }
   3808    lineArg++;
   3809  }
   3810 
   3811  uint32_t lineno;
   3812  if (!ToUint32(cx, args.get(lineArg), &lineno)) {
   3813    return false;
   3814  }
   3815 
   3816  jsbytecode* pc = LineNumberToPC(script, lineno);
   3817  if (!pc) {
   3818    return false;
   3819  }
   3820  args.rval().setInt32(script->pcToOffset(pc));
   3821  return true;
   3822 }
   3823 
   3824 static bool PCToLine(JSContext* cx, unsigned argc, Value* vp) {
   3825  CallArgs args = CallArgsFromVp(argc, vp);
   3826  RootedScript script(cx);
   3827  int32_t i;
   3828  unsigned lineno;
   3829 
   3830  if (!GetScriptAndPCArgs(cx, args, &script, &i)) {
   3831    return false;
   3832  }
   3833  lineno = PCToLineNumber(script, script->offsetToPC(i));
   3834  if (!lineno) {
   3835    return false;
   3836  }
   3837  args.rval().setInt32(lineno);
   3838  return true;
   3839 }
   3840 
   3841 #if defined(DEBUG) || defined(JS_JITSPEW)
   3842 
   3843 static bool Notes(JSContext* cx, unsigned argc, Value* vp) {
   3844  CallArgs args = CallArgsFromVp(argc, vp);
   3845  JSSprinter sprinter(cx);
   3846  if (!sprinter.init()) {
   3847    return false;
   3848  }
   3849 
   3850  for (unsigned i = 0; i < args.length(); i++) {
   3851    RootedScript script(cx, TestingFunctionArgumentToScript(cx, args[i]));
   3852    if (!script) {
   3853      return false;
   3854    }
   3855 
   3856    if (!JSScript::dumpSrcNotes(cx, script, &sprinter)) {
   3857      return false;
   3858    }
   3859  }
   3860 
   3861  JSString* str = sprinter.release(cx);
   3862  if (!str) {
   3863    return false;
   3864  }
   3865  args.rval().setString(str);
   3866  return true;
   3867 }
   3868 
   3869 namespace {
   3870 
   3871 struct DisassembleOptionParser {
   3872  unsigned argc;
   3873  Value* argv;
   3874  JSScript::DumpOptions options;
   3875 
   3876  DisassembleOptionParser(unsigned argc, Value* argv)
   3877      : argc(argc), argv(argv) {}
   3878 
   3879  bool parse(JSContext* cx) {
   3880    options.recursive = false;
   3881 
   3882    /* Read options off early arguments */
   3883    while (argc > 0 && argv[0].isString()) {
   3884      JSString* str = argv[0].toString();
   3885      JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
   3886      if (!linearStr) {
   3887        return false;
   3888      }
   3889      if (JS_LinearStringEqualsLiteral(linearStr, "-r")) {
   3890        options.recursive = true;
   3891      } else {
   3892        break;
   3893      }
   3894      argv++;
   3895      argc--;
   3896    }
   3897    return true;
   3898  }
   3899 };
   3900 
   3901 } /* anonymous namespace */
   3902 
   3903 static bool DisassembleToSprinter(JSContext* cx, unsigned argc, Value* vp,
   3904                                  StringPrinter* sp) {
   3905  CallArgs args = CallArgsFromVp(argc, vp);
   3906  DisassembleOptionParser p(args.length(), args.array());
   3907  if (!p.parse(cx)) {
   3908    return false;
   3909  }
   3910 
   3911  if (p.argc == 0) {
   3912    /* Without arguments, disassemble the current script. */
   3913    RootedScript script(cx, GetTopScript(cx));
   3914    if (script) {
   3915      JSAutoRealm ar(cx, script);
   3916      if (!JSScript::dump(cx, script, p.options, sp)) {
   3917        return false;
   3918      }
   3919    }
   3920  } else {
   3921    for (unsigned i = 0; i < p.argc; i++) {
   3922      RootedFunction fun(cx);
   3923      RootedScript script(cx);
   3924      RootedValue value(cx, p.argv[i]);
   3925      if (value.isObject() && value.toObject().is<ShellModuleObjectWrapper>()) {
   3926        auto* module = value.toObject().as<ShellModuleObjectWrapper>().get();
   3927        script = module->maybeScript();
   3928        if (!script) {
   3929          JS_ReportErrorASCII(cx, "module does not have an associated script");
   3930          return false;
   3931        }
   3932      } else {
   3933        script = TestingFunctionArgumentToScript(cx, value, fun.address());
   3934        if (!script) {
   3935          return false;
   3936        }
   3937      }
   3938 
   3939      if (!JSScript::dump(cx, script, p.options, sp)) {
   3940        return false;
   3941      }
   3942    }
   3943  }
   3944 
   3945  return true;
   3946 }
   3947 
   3948 static bool DisassembleToString(JSContext* cx, unsigned argc, Value* vp) {
   3949  CallArgs args = CallArgsFromVp(argc, vp);
   3950  JSSprinter sprinter(cx);
   3951  if (!sprinter.init()) {
   3952    return false;
   3953  }
   3954  if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) {
   3955    return false;
   3956  }
   3957 
   3958  JSString* str = sprinter.release(cx);
   3959  if (!str) {
   3960    return false;
   3961  }
   3962  args.rval().setString(str);
   3963  return true;
   3964 }
   3965 
   3966 static bool Disassemble(JSContext* cx, unsigned argc, Value* vp) {
   3967  CallArgs args = CallArgsFromVp(argc, vp);
   3968 
   3969  if (!gOutFile->isOpen()) {
   3970    JS_ReportErrorASCII(cx, "output file is closed");
   3971    return false;
   3972  }
   3973 
   3974  Sprinter sprinter(cx);
   3975  if (!sprinter.init()) {
   3976    return false;
   3977  }
   3978  if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter)) {
   3979    return false;
   3980  }
   3981 
   3982  JS::UniqueChars str = sprinter.release();
   3983  if (!str) {
   3984    return false;
   3985  }
   3986  fprintf(gOutFile->fp, "%s\n", str.get());
   3987  args.rval().setUndefined();
   3988  return true;
   3989 }
   3990 
   3991 static bool DisassFile(JSContext* cx, unsigned argc, Value* vp) {
   3992  CallArgs args = CallArgsFromVp(argc, vp);
   3993 
   3994  if (!gOutFile->isOpen()) {
   3995    JS_ReportErrorASCII(cx, "output file is closed");
   3996    return false;
   3997  }
   3998 
   3999  /* Support extra options at the start, just like Disassemble. */
   4000  DisassembleOptionParser p(args.length(), args.array());
   4001  if (!p.parse(cx)) {
   4002    return false;
   4003  }
   4004 
   4005  if (!p.argc) {
   4006    args.rval().setUndefined();
   4007    return true;
   4008  }
   4009 
   4010  // We should change DisassembleOptionParser to store CallArgs.
   4011  Rooted<JSString*> str(
   4012      cx, JS::ToString(cx, HandleValue::fromMarkedLocation(&p.argv[0])));
   4013  if (!str) {
   4014    return false;
   4015  }
   4016  UniqueChars filename = JS_EncodeStringToUTF8(cx, str);
   4017  if (!filename) {
   4018    return false;
   4019  }
   4020  RootedScript script(cx);
   4021 
   4022  {
   4023    CompileOptions options(cx);
   4024    options.setIntroductionType("js shell disFile")
   4025        .setFileAndLine(filename.get(), 1)
   4026        .setIsRunOnce(true)
   4027        .setNoScriptRval(true)
   4028        .setEagerDelazificationStrategy(defaultDelazificationMode);
   4029 
   4030    script = JS::CompileUtf8Path(cx, options, filename.get());
   4031    if (!script) {
   4032      return false;
   4033    }
   4034  }
   4035 
   4036  Sprinter sprinter(cx);
   4037  if (!sprinter.init()) {
   4038    return false;
   4039  }
   4040  if (!JSScript::dump(cx, script, p.options, &sprinter)) {
   4041    return false;
   4042  }
   4043 
   4044  JS::UniqueChars chars = sprinter.release();
   4045  if (!chars) {
   4046    return false;
   4047  }
   4048  fprintf(gOutFile->fp, "%s\n", chars.get());
   4049 
   4050  args.rval().setUndefined();
   4051  return true;
   4052 }
   4053 
   4054 static bool DisassWithSrc(JSContext* cx, unsigned argc, Value* vp) {
   4055  CallArgs args = CallArgsFromVp(argc, vp);
   4056 
   4057  if (!gOutFile->isOpen()) {
   4058    JS_ReportErrorASCII(cx, "output file is closed");
   4059    return false;
   4060  }
   4061 
   4062  const size_t lineBufLen = 512;
   4063  unsigned len, line1, line2, bupline;
   4064  char linebuf[lineBufLen];
   4065  static const char sep[] = ";-------------------------";
   4066 
   4067  RootedScript script(cx);
   4068  for (unsigned i = 0; i < args.length(); i++) {
   4069    script = TestingFunctionArgumentToScript(cx, args[i]);
   4070    if (!script) {
   4071      return false;
   4072    }
   4073 
   4074    if (!script->filename()) {
   4075      JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   4076                                JSSMSG_FILE_SCRIPTS_ONLY);
   4077      return false;
   4078    }
   4079 
   4080    FILE* file = OpenFile(cx, script->filename(), "rb");
   4081    if (!file) {
   4082      return false;
   4083    }
   4084    auto closeFile = MakeScopeExit([file] { fclose(file); });
   4085 
   4086    jsbytecode* pc = script->code();
   4087    jsbytecode* end = script->codeEnd();
   4088 
   4089    Sprinter sprinter(cx);
   4090    if (!sprinter.init()) {
   4091      return false;
   4092    }
   4093 
   4094    /* burn the leading lines */
   4095    line2 = PCToLineNumber(script, pc);
   4096    for (line1 = 0; line1 < line2 - 1; line1++) {
   4097      char* tmp = fgets(linebuf, lineBufLen, file);
   4098      if (!tmp) {
   4099        JS_ReportErrorUTF8(cx, "failed to read %s fully", script->filename());
   4100        return false;
   4101      }
   4102    }
   4103 
   4104    bupline = 0;
   4105    while (pc < end) {
   4106      line2 = PCToLineNumber(script, pc);
   4107 
   4108      if (line2 < line1) {
   4109        if (bupline != line2) {
   4110          bupline = line2;
   4111          sprinter.printf("%s %3u: BACKUP\n", sep, line2);
   4112        }
   4113      } else {
   4114        if (bupline && line1 == line2) {
   4115          sprinter.printf("%s %3u: RESTORE\n", sep, line2);
   4116        }
   4117        bupline = 0;
   4118        while (line1 < line2) {
   4119          if (!fgets(linebuf, lineBufLen, file)) {
   4120            JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr,
   4121                                     JSSMSG_UNEXPECTED_EOF, script->filename());
   4122            return false;
   4123          }
   4124          line1++;
   4125          sprinter.printf("%s %3u: %s", sep, line1, linebuf);
   4126        }
   4127      }
   4128 
   4129      len =
   4130          Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter);
   4131      if (!len) {
   4132        return false;
   4133      }
   4134 
   4135      pc += len;
   4136    }
   4137 
   4138    JS::UniqueChars str = sprinter.release();
   4139    if (!str) {
   4140      return false;
   4141    }
   4142    fprintf(gOutFile->fp, "%s\n", str.get());
   4143  }
   4144 
   4145  args.rval().setUndefined();
   4146  return true;
   4147 }
   4148 
   4149 #endif /* defined(DEBUG) || defined(JS_JITSPEW) */
   4150 
   4151 #ifdef JS_CACHEIR_SPEW
   4152 static bool CacheIRHealthReport(JSContext* cx, unsigned argc, Value* vp) {
   4153  CallArgs args = CallArgsFromVp(argc, vp);
   4154 
   4155  js::jit::CacheIRHealth cih;
   4156  RootedScript script(cx);
   4157 
   4158  // In the case that we are calling this function from the shell and
   4159  // the environment variable is not set, AutoSpewChannel automatically
   4160  // sets and unsets the proper channel for the duration of spewing
   4161  // a health report.
   4162  AutoSpewChannel channel(cx, SpewChannel::CacheIRHealthReport, script);
   4163  if (!argc) {
   4164    // Calling CacheIRHealthReport without any arguments will create health
   4165    // reports for all scripts in the zone.
   4166    if (jit::JitZone* jitZone = cx->zone()->jitZone()) {
   4167      jitZone->forEachJitScript([&](jit::JitScript* jitScript) {
   4168        script = jitScript->owningScript();
   4169        if (!script->selfHosted()) {
   4170          cih.healthReportForScript(cx, script, js::jit::SpewContext::Shell);
   4171        }
   4172      });
   4173    }
   4174  } else {
   4175    RootedValue value(cx, args.get(0));
   4176 
   4177    if (value.isObject() && value.toObject().is<ShellModuleObjectWrapper>()) {
   4178      script =
   4179          value.toObject().as<ShellModuleObjectWrapper>().get()->maybeScript();
   4180      if (!script) {
   4181        JS_ReportErrorASCII(cx, "module does not have an associated script");
   4182        return false;
   4183      }
   4184    } else {
   4185      script = TestingFunctionArgumentToScript(cx, args.get(0));
   4186      if (!script) {
   4187        return false;
   4188      }
   4189    }
   4190 
   4191    cih.healthReportForScript(cx, script, js::jit::SpewContext::Shell);
   4192  }
   4193 
   4194  args.rval().setUndefined();
   4195  return true;
   4196 }
   4197 #endif /* JS_CACHEIR_SPEW */
   4198 
   4199 /* Pretend we can always preserve wrappers for dummy DOM objects. */
   4200 static bool DummyPreserveWrapperCallback(JSContext* cx, HandleObject obj) {
   4201  return true;
   4202 }
   4203 
   4204 static bool DummyHasReleasedWrapperCallback(HandleObject obj) { return true; }
   4205 
   4206 #ifdef FUZZING_JS_FUZZILLI
   4207 static bool fuzzilli_hash(JSContext* cx, unsigned argc, Value* vp) {
   4208  CallArgs args = CallArgsFromVp(argc, vp);
   4209  args.rval().setUndefined();
   4210 
   4211  if (argc != 1) {
   4212    return true;
   4213  }
   4214  uint32_t hash;
   4215  JS::Handle<JS::Value> v = args.get(0);
   4216  if (v.isInt32()) {
   4217    int32_t i = v.toInt32();
   4218    hash = FuzzilliHashDouble((double)i);
   4219  } else if (v.isDouble()) {
   4220    double d = v.toDouble();
   4221    d = JS::CanonicalizeNaN(d);
   4222    hash = FuzzilliHashDouble(d);
   4223  } else if (v.isNull()) {
   4224    hash = FuzzilliHashDouble(1.0);
   4225  } else if (v.isUndefined()) {
   4226    hash = FuzzilliHashDouble(2.0);
   4227  } else if (v.isBoolean()) {
   4228    hash = FuzzilliHashDouble(3.0 + v.toBoolean());
   4229  } else if (v.isBigInt()) {
   4230    JS::BigInt* bigInt = v.toBigInt();
   4231    hash = FuzzilliHashBigInt(bigInt);
   4232  } else if (v.isObject()) {
   4233    JSObject& obj = v.toObject();
   4234    FuzzilliHashObject(cx, &obj);
   4235    return true;
   4236  } else {
   4237    hash = 0;
   4238  }
   4239 
   4240  cx->executionHashInputs += 1;
   4241  cx->executionHash = mozilla::RotateLeft(cx->executionHash + hash, 1);
   4242  return true;
   4243 }
   4244 
   4245 // We have to assume that the fuzzer will be able to call this function e.g. by
   4246 // enumerating the properties of the global object and eval'ing them. As such
   4247 // this function is implemented in a way that requires passing some magic value
   4248 // as first argument (with the idea being that the fuzzer won't be able to
   4249 // generate this value) which then also acts as a selector for the operation
   4250 // to perform.
   4251 static bool Fuzzilli(JSContext* cx, unsigned argc, Value* vp) {
   4252  CallArgs args = CallArgsFromVp(argc, vp);
   4253 
   4254  RootedString arg(cx, JS::ToString(cx, args.get(0)));
   4255  if (!arg) {
   4256    return false;
   4257  }
   4258  Rooted<JSLinearString*> operation(cx, StringToLinearString(cx, arg));
   4259  if (!operation) {
   4260    return false;
   4261  }
   4262 
   4263  if (StringEqualsLiteral(operation, "FUZZILLI_CRASH")) {
   4264    int type;
   4265    if (!ToInt32(cx, args.get(1), &type)) {
   4266      return false;
   4267    }
   4268 
   4269    // With this, we can test the various ways the JS shell can crash and make
   4270    // sure that Fuzzilli is able to detect all of these failures properly.
   4271    switch (type) {
   4272      case 0:
   4273        *((int*)0x41414141) = 0x1337;
   4274        break;
   4275      case 1:
   4276        MOZ_RELEASE_ASSERT(false);
   4277        break;
   4278      case 2:
   4279        MOZ_ASSERT(false);
   4280        break;
   4281      case 3:
   4282 #  if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
   4283        __asm__("int3");
   4284 #  elif defined(JS_CODEGEN_ARM64)
   4285        __asm__("brk #0");
   4286 #  endif
   4287        break;
   4288      default:
   4289        exit(1);
   4290    }
   4291  } else if (StringEqualsLiteral(operation, "FUZZILLI_PRINT")) {
   4292    static FILE* fzliout = fdopen(REPRL_DWFD, "w");
   4293    if (!fzliout) {
   4294      fprintf(
   4295          stderr,
   4296          "Fuzzer output channel not available, printing to stdout instead\n");
   4297      fzliout = stdout;
   4298    }
   4299 
   4300    RootedString str(cx, JS::ToString(cx, args.get(1)));
   4301    if (!str) {
   4302      return false;
   4303    }
   4304    UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
   4305    if (!bytes) {
   4306      return false;
   4307    }
   4308    fprintf(fzliout, "%s\n", bytes.get());
   4309    fflush(fzliout);
   4310  } else if (StringEqualsLiteral(operation, "FUZZILLI_RANDOM")) {
   4311    // This is an entropy source which can be called during fuzzing.
   4312    // Its currently used to tests whether Fuzzilli detects non-deterministic
   4313    // behavior.
   4314    args.rval().setInt32(static_cast<uint32_t>(mozilla::RandomUint64OrDie()));
   4315    return true;
   4316  }
   4317 
   4318  args.rval().setUndefined();
   4319  return true;
   4320 }
   4321 
   4322 static bool FuzzilliReprlGetAndRun(JSContext* cx) {
   4323  size_t scriptSize = 0;
   4324 
   4325  unsigned action;
   4326  MOZ_RELEASE_ASSERT(read(REPRL_CRFD, &action, 4) == 4);
   4327  if (action == 'cexe') {
   4328    MOZ_RELEASE_ASSERT(read(REPRL_CRFD, &scriptSize, 8) == 8);
   4329  } else {
   4330    fprintf(stderr, "Unknown action: %u\n", action);
   4331    _exit(-1);
   4332  }
   4333 
   4334  CompileOptions options(cx);
   4335  options.setIntroductionType("reprl")
   4336      .setFileAndLine("reprl", 1)
   4337      .setIsRunOnce(true)
   4338      .setNoScriptRval(true)
   4339      .setEagerDelazificationStrategy(defaultDelazificationMode);
   4340 
   4341  char* scriptSrc = static_cast<char*>(js_malloc(scriptSize));
   4342 
   4343  char* ptr = scriptSrc;
   4344  size_t remaining = scriptSize;
   4345  while (remaining > 0) {
   4346    ssize_t rv = read(REPRL_DRFD, ptr, remaining);
   4347    if (rv <= 0) {
   4348      fprintf(stderr, "Failed to load script\n");
   4349      _exit(-1);
   4350    }
   4351    remaining -= rv;
   4352    ptr += rv;
   4353  }
   4354 
   4355  JS::SourceText<Utf8Unit> srcBuf;
   4356  if (!srcBuf.init(cx, scriptSrc, scriptSize,
   4357                   JS::SourceOwnership::TakeOwnership)) {
   4358    return false;
   4359  }
   4360 
   4361  RootedScript script(cx, JS::Compile(cx, options, srcBuf));
   4362  if (!script) {
   4363    return false;
   4364  }
   4365 
   4366  if (!JS_ExecuteScript(cx, script)) {
   4367    return false;
   4368  }
   4369 
   4370  return true;
   4371 }
   4372 
   4373 #endif /* FUZZING_JS_FUZZILLI */
   4374 
   4375 static bool FuzzilliUseReprlMode(OptionParser* op) {
   4376 #ifdef FUZZING_JS_FUZZILLI
   4377  // Check if we should use REPRL mode
   4378  bool reprl_mode = op->getBoolOption("reprl");
   4379  if (reprl_mode) {
   4380    // Check in with parent
   4381    char helo[] = "HELO";
   4382    if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) {
   4383      reprl_mode = false;
   4384    }
   4385 
   4386    if (memcmp(helo, "HELO", 4) != 0) {
   4387      fprintf(stderr, "Invalid response from parent\n");
   4388      _exit(-1);
   4389    }
   4390  }
   4391  return reprl_mode;
   4392 #else
   4393  return false;
   4394 #endif /* FUZZING_JS_FUZZILLI */
   4395 }
   4396 
   4397 static bool Crash(JSContext* cx, unsigned argc, Value* vp) {
   4398  CallArgs args = CallArgsFromVp(argc, vp);
   4399  if (args.length() == 0) {
   4400    MOZ_CRASH("forced crash");
   4401  }
   4402  RootedString message(cx, JS::ToString(cx, args[0]));
   4403  if (!message) {
   4404    return false;
   4405  }
   4406  UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, message);
   4407  if (!utf8chars) {
   4408    return false;
   4409  }
   4410  if (args.get(1).isObject()) {
   4411    RootedValue v(cx);
   4412    RootedObject opts(cx, &args[1].toObject());
   4413    if (!JS_GetProperty(cx, opts, "suppress_minidump", &v)) {
   4414      return false;
   4415    }
   4416    if (v.isBoolean() && v.toBoolean()) {
   4417      js::NoteIntentionalCrash();
   4418    }
   4419  }
   4420 #ifndef DEBUG
   4421  MOZ_ReportCrash(utf8chars.get(), __FILE__, __LINE__);
   4422 #endif
   4423  MOZ_CRASH_UNSAFE(utf8chars.get());
   4424 }
   4425 
   4426 static bool GetSLX(JSContext* cx, unsigned argc, Value* vp) {
   4427  CallArgs args = CallArgsFromVp(argc, vp);
   4428  RootedScript script(cx);
   4429 
   4430  script = TestingFunctionArgumentToScript(cx, args.get(0));
   4431  if (!script) {
   4432    return false;
   4433  }
   4434  args.rval().setInt32(GetScriptLineExtent(script));
   4435  return true;
   4436 }
   4437 
   4438 static bool ThrowError(JSContext* cx, unsigned argc, Value* vp) {
   4439  JS_ReportErrorASCII(cx, "This is an error");
   4440  return false;
   4441 }
   4442 
   4443 static bool CopyErrorReportToObject(JSContext* cx, JSErrorReport* report,
   4444                                    HandleObject obj) {
   4445  RootedString nameStr(cx);
   4446  if (report->exnType == JSEXN_WARN) {
   4447    nameStr = JS_NewStringCopyZ(cx, "Warning");
   4448    if (!nameStr) {
   4449      return false;
   4450    }
   4451  } else {
   4452    nameStr = GetErrorTypeName(cx, report->exnType);
   4453    // GetErrorTypeName doesn't set an exception, but
   4454    // can fail for InternalError or non-error objects.
   4455    if (!nameStr) {
   4456      nameStr = cx->runtime()->emptyString;
   4457    }
   4458  }
   4459  RootedValue nameVal(cx, StringValue(nameStr));
   4460  if (!DefineDataProperty(cx, obj, cx->names().name, nameVal)) {
   4461    return false;
   4462  }
   4463 
   4464  RootedString messageStr(cx, report->newMessageString(cx));
   4465  if (!messageStr) {
   4466    return false;
   4467  }
   4468  RootedValue messageVal(cx, StringValue(messageStr));
   4469  if (!DefineDataProperty(cx, obj, cx->names().message, messageVal)) {
   4470    return false;
   4471  }
   4472 
   4473  RootedValue linenoVal(cx, Int32Value(report->lineno));
   4474  if (!DefineDataProperty(cx, obj, cx->names().lineNumber, linenoVal)) {
   4475    return false;
   4476  }
   4477 
   4478  RootedValue columnVal(cx, Int32Value(report->column.oneOriginValue()));
   4479  if (!DefineDataProperty(cx, obj, cx->names().columnNumber, columnVal)) {
   4480    return false;
   4481  }
   4482 
   4483  RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
   4484  if (!notesArray) {
   4485    return false;
   4486  }
   4487 
   4488  RootedValue notesArrayVal(cx, ObjectValue(*notesArray));
   4489  return DefineDataProperty(cx, obj, cx->names().notes, notesArrayVal);
   4490 }
   4491 
   4492 static bool CreateErrorReport(JSContext* cx, unsigned argc, Value* vp) {
   4493  CallArgs args = CallArgsFromVp(argc, vp);
   4494 
   4495  // We don't have a stack here, so just initialize with null.
   4496  JS::ExceptionStack exnStack(cx, args.get(0), nullptr);
   4497  JS::ErrorReportBuilder report(cx);
   4498  if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
   4499    return false;
   4500  }
   4501 
   4502  MOZ_ASSERT(!report.report()->isWarning());
   4503 
   4504  RootedObject obj(cx, JS_NewPlainObject(cx));
   4505  if (!obj) {
   4506    return false;
   4507  }
   4508 
   4509  RootedString toString(cx, NewStringCopyUTF8Z(cx, report.toStringResult()));
   4510  if (!toString) {
   4511    return false;
   4512  }
   4513 
   4514  if (!JS_DefineProperty(cx, obj, "toStringResult", toString,
   4515                         JSPROP_ENUMERATE)) {
   4516    return false;
   4517  }
   4518 
   4519  if (!CopyErrorReportToObject(cx, report.report(), obj)) {
   4520    return false;
   4521  }
   4522 
   4523  args.rval().setObject(*obj);
   4524  return true;
   4525 }
   4526 
   4527 #define LAZY_STANDARD_CLASSES
   4528 
   4529 /* A class for easily testing the inner/outer object callbacks. */
   4530 struct ComplexObject {
   4531  bool isInner;
   4532  bool frozen;
   4533  JSObject* inner;
   4534  JSObject* outer;
   4535 };
   4536 
   4537 static bool sandbox_enumerate(JSContext* cx, JS::HandleObject obj,
   4538                              JS::MutableHandleIdVector properties,
   4539                              bool enumerableOnly) {
   4540  RootedValue v(cx);
   4541 
   4542  if (!JS_GetProperty(cx, obj, "lazy", &v)) {
   4543    return false;
   4544  }
   4545 
   4546  if (!ToBoolean(v)) {
   4547    return true;
   4548  }
   4549 
   4550  return JS_NewEnumerateStandardClasses(cx, obj, properties, enumerableOnly);
   4551 }
   4552 
   4553 static bool sandbox_resolve(JSContext* cx, HandleObject obj, HandleId id,
   4554                            bool* resolvedp) {
   4555  RootedValue v(cx);
   4556  if (!JS_GetProperty(cx, obj, "lazy", &v)) {
   4557    return false;
   4558  }
   4559 
   4560  if (ToBoolean(v)) {
   4561    return JS_ResolveStandardClass(cx, obj, id, resolvedp);
   4562  }
   4563  return true;
   4564 }
   4565 
   4566 static const JSClassOps sandbox_classOps = {
   4567    nullptr,                   // addProperty
   4568    nullptr,                   // delProperty
   4569    nullptr,                   // enumerate
   4570    sandbox_enumerate,         // newEnumerate
   4571    sandbox_resolve,           // resolve
   4572    nullptr,                   // mayResolve
   4573    nullptr,                   // finalize
   4574    nullptr,                   // call
   4575    nullptr,                   // construct
   4576    JS_GlobalObjectTraceHook,  // trace
   4577 };
   4578 
   4579 static const JSClass sandbox_class = {
   4580    "sandbox",
   4581    JSCLASS_GLOBAL_FLAGS,
   4582    &sandbox_classOps,
   4583 };
   4584 
   4585 static void SetStandardRealmOptions(JS::RealmOptions& options) {
   4586  options.creationOptions()
   4587      .setSharedMemoryAndAtomicsEnabled(enableSharedMemory)
   4588      .setCoopAndCoepEnabled(false)
   4589      .setToSourceEnabled(enableToSource);
   4590 }
   4591 
   4592 [[nodiscard]] static bool CheckRealmOptions(JSContext* cx,
   4593                                            JS::RealmOptions& options,
   4594                                            JSPrincipals* principals) {
   4595  JS::RealmCreationOptions& creationOptions = options.creationOptions();
   4596  if (creationOptions.compartmentSpecifier() !=
   4597      JS::CompartmentSpecifier::ExistingCompartment) {
   4598    return true;
   4599  }
   4600 
   4601  JS::Compartment* comp = creationOptions.compartment();
   4602 
   4603  // All realms in a compartment must be either system or non-system.
   4604  bool isSystem =
   4605      principals && principals == cx->runtime()->trustedPrincipals();
   4606  if (isSystem != IsSystemCompartment(comp)) {
   4607    JS_ReportErrorASCII(cx,
   4608                        "Cannot create system and non-system realms in the "
   4609                        "same compartment");
   4610    return false;
   4611  }
   4612 
   4613  // Debugger visibility is per-compartment, not per-realm, so make sure the
   4614  // requested visibility matches the existing compartment's.
   4615  if (creationOptions.invisibleToDebugger() != comp->invisibleToDebugger()) {
   4616    JS_ReportErrorASCII(cx,
   4617                        "All the realms in a compartment must have "
   4618                        "the same debugger visibility");
   4619    return false;
   4620  }
   4621 
   4622  return true;
   4623 }
   4624 
   4625 static JSObject* NewSandbox(JSContext* cx, bool lazy) {
   4626  JS::RealmOptions options;
   4627  SetStandardRealmOptions(options);
   4628 
   4629  if (defaultToSameCompartment) {
   4630    options.creationOptions().setExistingCompartment(cx->global());
   4631  } else {
   4632    options.creationOptions().setNewCompartmentAndZone();
   4633  }
   4634 
   4635  JSPrincipals* principals = nullptr;
   4636  if (!CheckRealmOptions(cx, options, principals)) {
   4637    return nullptr;
   4638  }
   4639 
   4640  RootedObject obj(cx,
   4641                   JS_NewGlobalObject(cx, &sandbox_class, principals,
   4642                                      JS::DontFireOnNewGlobalHook, options));
   4643  if (!obj) {
   4644    return nullptr;
   4645  }
   4646 
   4647  {
   4648    JSAutoRealm ar(cx, obj);
   4649    if (!lazy && !JS::InitRealmStandardClasses(cx)) {
   4650      return nullptr;
   4651    }
   4652 
   4653    RootedValue value(cx, BooleanValue(lazy));
   4654    if (!JS_DefineProperty(cx, obj, "lazy", value,
   4655                           JSPROP_PERMANENT | JSPROP_READONLY)) {
   4656      return nullptr;
   4657    }
   4658 
   4659    JS_FireOnNewGlobalObject(cx, obj);
   4660  }
   4661 
   4662  if (!cx->compartment()->wrap(cx, &obj)) {
   4663    return nullptr;
   4664  }
   4665  return obj;
   4666 }
   4667 
   4668 static bool EvalInContext(JSContext* cx, unsigned argc, Value* vp) {
   4669  CallArgs args = CallArgsFromVp(argc, vp);
   4670  if (!args.requireAtLeast(cx, "evalcx", 1)) {
   4671    return false;
   4672  }
   4673 
   4674  RootedString str(cx, ToString(cx, args[0]));
   4675  if (!str) {
   4676    return false;
   4677  }
   4678 
   4679  RootedObject sobj(cx);
   4680  if (args.hasDefined(1)) {
   4681    sobj = ToObject(cx, args[1]);
   4682    if (!sobj) {
   4683      return false;
   4684    }
   4685  }
   4686 
   4687  AutoStableStringChars strChars(cx);
   4688  if (!strChars.initTwoByte(cx, str)) {
   4689    return false;
   4690  }
   4691 
   4692  mozilla::Range<const char16_t> chars = strChars.twoByteRange();
   4693  size_t srclen = chars.length();
   4694  const char16_t* src = chars.begin().get();
   4695 
   4696  bool lazy = false;
   4697  if (srclen == 4) {
   4698    if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
   4699      lazy = true;
   4700      srclen = 0;
   4701    }
   4702  }
   4703 
   4704  if (!sobj) {
   4705    sobj = NewSandbox(cx, lazy);
   4706    if (!sobj) {
   4707      return false;
   4708    }
   4709  }
   4710 
   4711  if (srclen == 0) {
   4712    args.rval().setObject(*sobj);
   4713    return true;
   4714  }
   4715 
   4716  JS::AutoFilename filename;
   4717  uint32_t lineno;
   4718 
   4719  DescribeScriptedCaller(&filename, cx, &lineno);
   4720  {
   4721    sobj = UncheckedUnwrap(sobj, true);
   4722 
   4723    JSAutoRealm ar(cx, sobj);
   4724 
   4725    sobj = ToWindowIfWindowProxy(sobj);
   4726 
   4727    if (!JS_IsGlobalObject(sobj)) {
   4728      JS_ReportErrorASCII(cx, "Invalid scope argument to evalcx");
   4729      return false;
   4730    }
   4731 
   4732    JS::CompileOptions opts(cx);
   4733    opts.setFileAndLine(filename.get(), lineno)
   4734        .setEagerDelazificationStrategy(defaultDelazificationMode);
   4735 
   4736    JS::SourceText<char16_t> srcBuf;
   4737    if (!srcBuf.init(cx, src, srclen, JS::SourceOwnership::Borrowed) ||
   4738        !JS::Evaluate(cx, opts, srcBuf, args.rval())) {
   4739      return false;
   4740    }
   4741  }
   4742 
   4743  if (!cx->compartment()->wrap(cx, args.rval())) {
   4744    return false;
   4745  }
   4746 
   4747  return true;
   4748 }
   4749 
   4750 static bool EnsureGeckoProfilingStackInstalled(JSContext* cx,
   4751                                               ShellContext* sc) {
   4752  if (cx->geckoProfiler().infraInstalled()) {
   4753    MOZ_ASSERT(sc->geckoProfilingStack);
   4754    return true;
   4755  }
   4756 
   4757  MOZ_ASSERT(!sc->geckoProfilingStack);
   4758  sc->geckoProfilingStack = MakeUnique<ProfilingStack>();
   4759  if (!sc->geckoProfilingStack) {
   4760    JS_ReportOutOfMemory(cx);
   4761    return false;
   4762  }
   4763 
   4764  SetContextProfilingStack(cx, sc->geckoProfilingStack.get());
   4765  return true;
   4766 }
   4767 
   4768 struct WorkerInput {
   4769  JSRuntime* parentRuntime;
   4770  UniqueTwoByteChars chars;
   4771  size_t length;
   4772 
   4773  WorkerInput(JSRuntime* parentRuntime, UniqueTwoByteChars chars, size_t length)
   4774      : parentRuntime(parentRuntime), chars(std::move(chars)), length(length) {}
   4775 };
   4776 
   4777 static void DestroyShellCompartmentPrivate(JS::GCContext* gcx,
   4778                                           JS::Compartment* compartment) {
   4779  auto priv = static_cast<ShellCompartmentPrivate*>(
   4780      JS_GetCompartmentPrivate(compartment));
   4781  js_delete(priv);
   4782 }
   4783 
   4784 static void SetWorkerContextOptions(JSContext* cx);
   4785 static bool ShellBuildId(JS::BuildIdCharVector* buildId);
   4786 
   4787 static constexpr size_t gWorkerStackSize = 2 * 128 * sizeof(size_t) * 1024;
   4788 
   4789 static void WorkerMain(UniquePtr<WorkerInput> input) {
   4790  MOZ_ASSERT(input->parentRuntime);
   4791 
   4792  JSContext* cx = JS_NewContext(8L * 1024L * 1024L, input->parentRuntime);
   4793  if (!cx) {
   4794    return;
   4795  }
   4796  auto destroyContext = MakeScopeExit([cx] { JS_DestroyContext(cx); });
   4797 
   4798  UniquePtr<ShellContext> sc =
   4799      MakeUnique<ShellContext>(cx, ShellContext::Worker);
   4800  if (!sc || !sc->registerWithCx(cx)) {
   4801    return;
   4802  }
   4803 
   4804  if (!JS::InitSelfHostedCode(cx)) {
   4805    return;
   4806  }
   4807 
   4808  EnvironmentPreparer environmentPreparer(cx);
   4809 
   4810  do {
   4811    JS::RealmOptions realmOptions;
   4812    SetStandardRealmOptions(realmOptions);
   4813 
   4814    RootedObject global(cx, NewGlobalObject(cx, realmOptions, nullptr,
   4815                                            ShellGlobalKind::WindowProxy,
   4816                                            /* immutablePrototype = */ true));
   4817    if (!global) {
   4818      break;
   4819    }
   4820 
   4821    JSAutoRealm ar(cx, global);
   4822 
   4823    JS::ConstUTF8CharsZ path(processWideModuleLoadPath.get(),
   4824                             strlen(processWideModuleLoadPath.get()));
   4825    RootedString moduleLoadPath(cx, JS_NewStringCopyUTF8Z(cx, path));
   4826    if (!moduleLoadPath) {
   4827      return;
   4828    }
   4829    sc->moduleLoader = js::MakeUnique<ModuleLoader>();
   4830    if (!sc->moduleLoader || !sc->moduleLoader->init(cx, moduleLoadPath)) {
   4831      return;
   4832    }
   4833 
   4834    JS::CompileOptions options(cx);
   4835    options.setFileAndLine("<string>", 1)
   4836        .setIsRunOnce(true)
   4837        .setEagerDelazificationStrategy(defaultDelazificationMode);
   4838 
   4839    {
   4840      AutoReportException are(cx);
   4841      JS::SourceText<char16_t> srcBuf;
   4842      if (!srcBuf.init(cx, input->chars.get(), input->length,
   4843                       JS::SourceOwnership::Borrowed)) {
   4844        break;
   4845      }
   4846 
   4847      RootedScript script(cx, JS::Compile(cx, options, srcBuf));
   4848      if (!script) {
   4849        break;
   4850      }
   4851      RootedValue result(cx);
   4852      JS_ExecuteScript(cx, script, &result);
   4853    }
   4854 
   4855    RunShellJobs(cx);
   4856  } while (0);
   4857 
   4858  KillWatchdog(cx);
   4859 }
   4860 
   4861 // Workers can spawn other workers, so we need a lock to access workerThreads.
   4862 static Mutex* workerThreadsLock = nullptr;
   4863 MOZ_RUNINIT static Vector<UniquePtr<js::Thread>, 0, SystemAllocPolicy>
   4864    workerThreads;
   4865 
   4866 class MOZ_RAII AutoLockWorkerThreads : public LockGuard<Mutex> {
   4867  using Base = LockGuard<Mutex>;
   4868 
   4869 public:
   4870  AutoLockWorkerThreads() : Base(*workerThreadsLock) {
   4871    MOZ_ASSERT(workerThreadsLock);
   4872  }
   4873 };
   4874 
   4875 static bool EvalInWorker(JSContext* cx, unsigned argc, Value* vp) {
   4876  if (!CanUseExtraThreads()) {
   4877    JS_ReportErrorASCII(cx, "Can't create threads with --no-threads");
   4878    return false;
   4879  }
   4880 
   4881  CallArgs args = CallArgsFromVp(argc, vp);
   4882  if (!args.get(0).isString()) {
   4883    JS_ReportErrorASCII(cx, "Invalid arguments");
   4884    return false;
   4885  }
   4886 
   4887 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
   4888  if (cx->runningOOMTest) {
   4889    JS_ReportErrorASCII(
   4890        cx, "Can't create threads while running simulated OOM test");
   4891    return false;
   4892  }
   4893 #endif
   4894 
   4895  if (!args[0].toString()->ensureLinear(cx)) {
   4896    return false;
   4897  }
   4898 
   4899  if (!workerThreadsLock) {
   4900    workerThreadsLock = js_new<Mutex>(mutexid::ShellWorkerThreads);
   4901    if (!workerThreadsLock) {
   4902      ReportOutOfMemory(cx);
   4903      return false;
   4904    }
   4905  }
   4906 
   4907  JSLinearString* str = &args[0].toString()->asLinear();
   4908 
   4909  UniqueTwoByteChars chars(js_pod_malloc<char16_t>(str->length()));
   4910  if (!chars) {
   4911    ReportOutOfMemory(cx);
   4912    return false;
   4913  }
   4914 
   4915  CopyChars(chars.get(), *str);
   4916 
   4917  auto input = js::MakeUnique<WorkerInput>(JS_GetParentRuntime(cx),
   4918                                           std::move(chars), str->length());
   4919  if (!input) {
   4920    ReportOutOfMemory(cx);
   4921    return false;
   4922  }
   4923 
   4924  UniquePtr<Thread> thread;
   4925  {
   4926    AutoEnterOOMUnsafeRegion oomUnsafe;
   4927    thread = js::MakeUnique<Thread>(
   4928        Thread::Options().setStackSize(gWorkerStackSize + 512 * 1024));
   4929    if (!thread || !thread->init(WorkerMain, std::move(input))) {
   4930      oomUnsafe.crash("EvalInWorker");
   4931    }
   4932  }
   4933 
   4934  AutoLockWorkerThreads alwt;
   4935  if (!workerThreads.append(std::move(thread))) {
   4936    ReportOutOfMemory(cx);
   4937    thread->join();
   4938    return false;
   4939  }
   4940 
   4941  args.rval().setUndefined();
   4942  return true;
   4943 }
   4944 
   4945 static bool ShapeOf(JSContext* cx, unsigned argc, JS::Value* vp) {
   4946  CallArgs args = CallArgsFromVp(argc, vp);
   4947  if (!args.get(0).isObject()) {
   4948    JS_ReportErrorASCII(cx, "shapeOf: object expected");
   4949    return false;
   4950  }
   4951  JSObject* obj = &args[0].toObject();
   4952  args.rval().set(JS_NumberValue(double(uintptr_t(obj->shape()) >> 3)));
   4953  return true;
   4954 }
   4955 
   4956 static bool Sleep_fn(JSContext* cx, unsigned argc, Value* vp) {
   4957  ShellContext* sc = GetShellContext(cx);
   4958  CallArgs args = CallArgsFromVp(argc, vp);
   4959 
   4960  TimeDuration duration = TimeDuration::FromSeconds(0.0);
   4961  if (args.length() > 0) {
   4962    double t_secs;
   4963    if (!ToNumber(cx, args[0], &t_secs)) {
   4964      return false;
   4965    }
   4966    if (std::isnan(t_secs)) {
   4967      JS_ReportErrorASCII(cx, "sleep interval is not a number");
   4968      return false;
   4969    }
   4970 
   4971    duration = TimeDuration::FromSeconds(std::max(0.0, t_secs));
   4972    const TimeDuration MAX_TIMEOUT_INTERVAL =
   4973        TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS);
   4974    if (duration > MAX_TIMEOUT_INTERVAL) {
   4975      JS_ReportErrorASCII(cx, "Excessive sleep interval");
   4976      return false;
   4977    }
   4978  }
   4979  {
   4980    LockGuard<Mutex> guard(sc->watchdogLock);
   4981    TimeStamp toWakeup = TimeStamp::Now() + duration;
   4982    for (;;) {
   4983      sc->sleepWakeup.wait_for(guard, duration);
   4984      if (sc->serviceInterrupt) {
   4985        break;
   4986      }
   4987      auto now = TimeStamp::Now();
   4988      if (now >= toWakeup) {
   4989        break;
   4990      }
   4991      duration = toWakeup - now;
   4992    }
   4993  }
   4994 
   4995  if (sc->serviceInterrupt) {
   4996    JS::ReportUncatchableException(cx);
   4997    return false;
   4998  }
   4999 
   5000  args.rval().setUndefined();
   5001  return true;
   5002 }
   5003 
   5004 static void KillWatchdog(JSContext* cx) {
   5005  ShellContext* sc = GetShellContext(cx);
   5006  Maybe<Thread> thread;
   5007 
   5008  {
   5009    LockGuard<Mutex> guard(sc->watchdogLock);
   5010    std::swap(sc->watchdogThread, thread);
   5011    if (thread) {
   5012      // The watchdog thread becoming Nothing is its signal to exit.
   5013      sc->watchdogWakeup.notify_one();
   5014    }
   5015  }
   5016  if (thread) {
   5017    thread->join();
   5018  }
   5019 
   5020  MOZ_ASSERT(!sc->watchdogThread);
   5021 }
   5022 
   5023 static void WatchdogMain(JSContext* cx) {
   5024  ThisThread::SetName("JS Watchdog");
   5025 
   5026  ShellContext* sc = GetShellContext(cx);
   5027 
   5028  {
   5029    LockGuard<Mutex> guard(sc->watchdogLock);
   5030    while (sc->watchdogThread) {
   5031      auto now = TimeStamp::Now();
   5032      if (sc->watchdogTimeout && now >= sc->watchdogTimeout.value()) {
   5033        /*
   5034         * The timeout has just expired. Request an interrupt callback
   5035         * outside the lock.
   5036         */
   5037        sc->watchdogTimeout = Nothing();
   5038        {
   5039          UnlockGuard unlock(guard);
   5040          CancelExecution(cx);
   5041        }
   5042 
   5043        /* Wake up any threads doing sleep. */
   5044        sc->sleepWakeup.notify_all();
   5045      } else {
   5046        if (sc->watchdogTimeout) {
   5047          /*
   5048           * Time hasn't expired yet. Simulate an interrupt callback
   5049           * which doesn't abort execution.
   5050           */
   5051          JS_RequestInterruptCallback(cx);
   5052        }
   5053 
   5054        TimeDuration sleepDuration = sc->watchdogTimeout
   5055                                         ? TimeDuration::FromSeconds(0.1)
   5056                                         : TimeDuration::Forever();
   5057        sc->watchdogWakeup.wait_for(guard, sleepDuration);
   5058      }
   5059    }
   5060  }
   5061 }
   5062 
   5063 static bool ScheduleWatchdog(JSContext* cx, double t) {
   5064  ShellContext* sc = GetShellContext(cx);
   5065 
   5066  if (t <= 0) {
   5067    LockGuard<Mutex> guard(sc->watchdogLock);
   5068    sc->watchdogTimeout = Nothing();
   5069    return true;
   5070  }
   5071 
   5072 #ifdef __wasi__
   5073  return false;
   5074 #endif
   5075 
   5076  auto interval = TimeDuration::FromSeconds(t);
   5077  auto timeout = TimeStamp::Now() + interval;
   5078  LockGuard<Mutex> guard(sc->watchdogLock);
   5079  if (!sc->watchdogThread) {
   5080    MOZ_ASSERT(!sc->watchdogTimeout);
   5081    sc->watchdogThread.emplace();
   5082    AutoEnterOOMUnsafeRegion oomUnsafe;
   5083    if (!sc->watchdogThread->init(WatchdogMain, cx)) {
   5084      oomUnsafe.crash("watchdogThread.init");
   5085    }
   5086  } else if (!sc->watchdogTimeout || timeout < sc->watchdogTimeout.value()) {
   5087    sc->watchdogWakeup.notify_one();
   5088  }
   5089  sc->watchdogTimeout = Some(timeout);
   5090  return true;
   5091 }
   5092 
   5093 static void KillWorkerThreads(JSContext* cx) {
   5094  MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty());
   5095 
   5096  if (!workerThreadsLock) {
   5097    MOZ_ASSERT(workerThreads.empty());
   5098    return;
   5099  }
   5100 
   5101  while (true) {
   5102    // We need to leave the AutoLockWorkerThreads scope before we call
   5103    // js::Thread::join, to avoid deadlocks when AutoLockWorkerThreads is
   5104    // used by the worker thread.
   5105    UniquePtr<Thread> thread;
   5106    {
   5107      AutoLockWorkerThreads alwt;
   5108      if (workerThreads.empty()) {
   5109        break;
   5110      }
   5111      thread = std::move(workerThreads.back());
   5112      workerThreads.popBack();
   5113    }
   5114    thread->join();
   5115  }
   5116 
   5117  workerThreads.clearAndFree();
   5118 
   5119  js_delete(workerThreadsLock);
   5120  workerThreadsLock = nullptr;
   5121 }
   5122 
   5123 static void CancelExecution(JSContext* cx) {
   5124  ShellContext* sc = GetShellContext(cx);
   5125  sc->serviceInterrupt = true;
   5126  JS_RequestInterruptCallback(cx);
   5127 }
   5128 
   5129 static bool SetTimeoutValue(JSContext* cx, double t) {
   5130  if (std::isnan(t)) {
   5131    JS_ReportErrorASCII(cx, "timeout is not a number");
   5132    return false;
   5133  }
   5134  const TimeDuration MAX_TIMEOUT_INTERVAL =
   5135      TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS);
   5136  if (TimeDuration::FromSeconds(t) > MAX_TIMEOUT_INTERVAL) {
   5137    JS_ReportErrorASCII(cx, "Excessive timeout value");
   5138    return false;
   5139  }
   5140  GetShellContext(cx)->timeoutInterval = t;
   5141  if (!ScheduleWatchdog(cx, t)) {
   5142    JS_ReportErrorASCII(cx, "Failed to create the watchdog");
   5143    return false;
   5144  }
   5145  return true;
   5146 }
   5147 
   5148 static bool MaybeSetInterruptFunc(JSContext* cx, HandleValue func) {
   5149  ShellContext* sc = GetShellContext(cx);
   5150 
   5151  bool isSupportedFunction =
   5152      func.isObject() && func.toObject().is<JSFunction>() && !fuzzingSafe;
   5153  if (!func.isString() && !isSupportedFunction) {
   5154    JS_ReportErrorASCII(cx, "Argument must be a function or string");
   5155    return false;
   5156  }
   5157  sc->interruptFunc = func;
   5158  sc->haveInterruptFunc = true;
   5159 
   5160  return true;
   5161 }
   5162 
   5163 static bool Timeout(JSContext* cx, unsigned argc, Value* vp) {
   5164  ShellContext* sc = GetShellContext(cx);
   5165  CallArgs args = CallArgsFromVp(argc, vp);
   5166 
   5167  if (args.length() == 0) {
   5168    args.rval().setNumber(sc->timeoutInterval);
   5169    return true;
   5170  }
   5171 
   5172  if (args.length() > 2) {
   5173    JS_ReportErrorASCII(cx, "Wrong number of arguments");
   5174    return false;
   5175  }
   5176 
   5177  double t;
   5178  if (!ToNumber(cx, args[0], &t)) {
   5179    return false;
   5180  }
   5181 
   5182  if (args.length() > 1) {
   5183    RootedValue value(cx, args[1]);
   5184    if (!MaybeSetInterruptFunc(cx, value)) {
   5185      return false;
   5186    }
   5187  }
   5188 
   5189  args.rval().setUndefined();
   5190  return SetTimeoutValue(cx, t);
   5191 }
   5192 
   5193 static bool InterruptIf(JSContext* cx, unsigned argc, Value* vp) {
   5194  CallArgs args = CallArgsFromVp(argc, vp);
   5195 
   5196  if (args.length() != 1) {
   5197    JS_ReportErrorASCII(cx, "Wrong number of arguments");
   5198    return false;
   5199  }
   5200 
   5201  if (ToBoolean(args[0])) {
   5202    GetShellContext(cx)->serviceInterrupt = true;
   5203    JS_RequestInterruptCallback(cx);
   5204  }
   5205 
   5206  args.rval().setUndefined();
   5207  return true;
   5208 }
   5209 
   5210 static bool InvokeInterruptCallbackWrapper(JSContext* cx, unsigned argc,
   5211                                           Value* vp) {
   5212  CallArgs args = CallArgsFromVp(argc, vp);
   5213  if (args.length() != 1) {
   5214    JS_ReportErrorASCII(cx, "Wrong number of arguments");
   5215    return false;
   5216  }
   5217 
   5218  GetShellContext(cx)->serviceInterrupt = true;
   5219  JS_RequestInterruptCallback(cx);
   5220  bool interruptRv = CheckForInterrupt(cx);
   5221 
   5222  // The interrupt handler could have set a pending exception. Since we call
   5223  // back into JS, don't have it see the pending exception. If we have an
   5224  // uncatchable exception that's not propagating a debug mode forced
   5225  // return, return.
   5226  if (!interruptRv && !cx->isExceptionPending() &&
   5227      !cx->isPropagatingForcedReturn()) {
   5228    return false;
   5229  }
   5230 
   5231  JS::AutoSaveExceptionState savedExc(cx);
   5232 
   5233  FixedInvokeArgs<1> iargs(cx);
   5234 
   5235  iargs[0].setBoolean(interruptRv);
   5236 
   5237  RootedValue rv(cx);
   5238  if (!js::Call(cx, args[0], UndefinedHandleValue, iargs, &rv)) {
   5239    return false;
   5240  }
   5241 
   5242  args.rval().setUndefined();
   5243  return interruptRv;
   5244 }
   5245 
   5246 static bool SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp) {
   5247  CallArgs args = CallArgsFromVp(argc, vp);
   5248 
   5249  if (args.length() != 1) {
   5250    JS_ReportErrorASCII(cx, "Wrong number of arguments");
   5251    return false;
   5252  }
   5253 
   5254  RootedValue value(cx, args[0]);
   5255  if (!MaybeSetInterruptFunc(cx, value)) {
   5256    return false;
   5257  }
   5258 
   5259  args.rval().setUndefined();
   5260  return true;
   5261 }
   5262 
   5263 #ifdef DEBUG
   5264 // var s0 = "A".repeat(10*1024);
   5265 // interruptRegexp(/a(bc|bd)/, s0);
   5266 // first arg is regexp
   5267 // second arg is string
   5268 static bool InterruptRegexp(JSContext* cx, unsigned argc, Value* vp) {
   5269  CallArgs args = CallArgsFromVp(argc, vp);
   5270  ShellContext* sc = GetShellContext(cx);
   5271  RootedObject callee(cx, &args.callee());
   5272 
   5273  if (args.length() != 2) {
   5274    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments.");
   5275    return false;
   5276  }
   5277  if (!(args[0].isObject() && args[0].toObject().is<RegExpObject>())) {
   5278    ReportUsageErrorASCII(cx, callee,
   5279                          "First argument must be a regular expression.");
   5280    return false;
   5281  }
   5282  if (!args[1].isString()) {
   5283    ReportUsageErrorASCII(cx, callee, "Second argument must be a String.");
   5284    return false;
   5285  }
   5286  // Set interrupt flags
   5287  sc->serviceInterrupt = true;
   5288  js::irregexp::IsolateSetShouldSimulateInterrupt(cx->isolate);
   5289 
   5290  RootedObject regexp(cx, &args[0].toObject());
   5291  RootedString string(cx, args[1].toString());
   5292  int32_t lastIndex = 0;
   5293 
   5294  return js::RegExpMatcherRaw(cx, regexp, string, lastIndex, nullptr,
   5295                              args.rval());
   5296 }
   5297 #endif
   5298 
   5299 static bool CheckRegExpSyntax(JSContext* cx, unsigned argc, Value* vp) {
   5300  CallArgs args = CallArgsFromVp(argc, vp);
   5301  RootedObject callee(cx, &args.callee());
   5302 
   5303  if (args.length() != 1) {
   5304    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments.");
   5305    return false;
   5306  }
   5307  if (!args[0].isString()) {
   5308    ReportUsageErrorASCII(cx, callee, "First argument must be a string.");
   5309    return false;
   5310  }
   5311 
   5312  RootedString string(cx, args[0].toString());
   5313  AutoStableStringChars stableChars(cx);
   5314  if (!stableChars.initTwoByte(cx, string)) {
   5315    return false;
   5316  }
   5317 
   5318  const char16_t* chars = stableChars.twoByteRange().begin().get();
   5319  size_t length = string->length();
   5320 
   5321  Rooted<JS::Value> error(cx);
   5322  if (!JS::CheckRegExpSyntax(cx, chars, length, JS::RegExpFlag::NoFlags,
   5323                             &error)) {
   5324    return false;
   5325  }
   5326 
   5327  args.rval().set(error);
   5328  return true;
   5329 }
   5330 
   5331 static bool IsPrefAvailable(const char* pref) {
   5332  if (!fuzzingSafe) {
   5333    // All prefs in fuzzing unsafe mode are enabled.
   5334    return true;
   5335  }
   5336 #define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \
   5337                     FLAG_FORCE_ON, FLAG_FUZZ_ON, PREF)                        \
   5338  if constexpr (!FLAG_FUZZ_ON) {                                               \
   5339    if (strcmp("wasm_" #PREF, pref) == 0) {                                    \
   5340      return false;                                                            \
   5341    }                                                                          \
   5342  }
   5343  JS_FOR_WASM_FEATURES(WASM_FEATURE)
   5344 #undef WASM_FEATURE
   5345  return true;
   5346 }
   5347 
   5348 template <typename T>
   5349 static bool ParsePrefValue(const char* name, const char* val, T* result) {
   5350  if constexpr (std::is_same_v<T, bool>) {
   5351    if (strcmp(val, "true") == 0) {
   5352      *result = true;
   5353      return true;
   5354    }
   5355    if (strcmp(val, "false") == 0) {
   5356      *result = false;
   5357      return true;
   5358    }
   5359    fprintf(stderr, "Invalid value for boolean pref %s: %s\n", name, val);
   5360    return false;
   5361  } else {
   5362    static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>);
   5363    char* end;
   5364    long v = strtol(val, &end, 10);
   5365    if (end != val + strlen(val) || static_cast<long>(static_cast<T>(v)) != v) {
   5366      fprintf(stderr, "Invalid value for integer pref %s: %s\n", name, val);
   5367      return false;
   5368    }
   5369    *result = static_cast<T>(v);
   5370    return true;
   5371  }
   5372 }
   5373 
   5374 static bool SetPrefToTrueForBool(const char* name) {
   5375  // Search for a matching pref and try to set it to a default value for the
   5376  // type.
   5377 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF)      \
   5378  if (strcmp(name, NAME) == 0) {                                       \
   5379    if constexpr (std::is_same_v<TYPE, bool>) {                        \
   5380      JS::Prefs::SETTER(true);                                         \
   5381      return true;                                                     \
   5382    } else {                                                           \
   5383      fprintf(stderr, "Pref %s must have a value specified.\n", name); \
   5384      return false;                                                    \
   5385    }                                                                  \
   5386  }
   5387  FOR_EACH_JS_PREF(CHECK_PREF)
   5388 #undef CHECK_PREF
   5389 
   5390  // Nothing matched. If --fuzzing-safe is used, return true after printing a
   5391  // message, to continue execution without breaking fuzzing when a pref is
   5392  // removed.
   5393  if (fuzzingSafe) {
   5394    fprintf(stderr, "Warning: Ignoring unknown pref name: %s\n", name);
   5395    return true;
   5396  }
   5397  fprintf(stderr, "Invalid pref name: %s\n", name);
   5398  return false;
   5399 }
   5400 
   5401 template <typename T>
   5402 static bool ConvertPrefValue(JSContext* cx, HandleValue val, T* result) {
   5403  if constexpr (std::is_same_v<T, bool>) {
   5404    *result = ToBoolean(val);
   5405    return true;
   5406  } else if constexpr (std::is_same_v<T, int32_t>) {
   5407    return ToInt32(cx, val, result);
   5408  } else {
   5409    static_assert(std::is_same_v<T, uint32_t>);
   5410    return ToUint32(cx, val, result);
   5411  }
   5412 }
   5413 
   5414 static bool SetPrefValue(JSContext* cx, unsigned argc, Value* vp) {
   5415  CallArgs args = CallArgsFromVp(argc, vp);
   5416  if (!args.requireAtLeast(cx, "setPrefValue", 2)) {
   5417    return false;
   5418  }
   5419 
   5420  if (!args[0].isString()) {
   5421    JS_ReportErrorASCII(cx, "expected string argument");
   5422    return false;
   5423  }
   5424 
   5425  Rooted<JSLinearString*> name(cx, args[0].toString()->ensureLinear(cx));
   5426  if (!name) {
   5427    return false;
   5428  }
   5429 
   5430  // Search for a matching pref and try to set it to the provided value.
   5431 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF)             \
   5432  if (IsPrefAvailable(NAME) && StringEqualsLiteral(name, NAME)) {             \
   5433    if (IS_STARTUP_PREF) {                                                    \
   5434      JS_ReportErrorASCII(cx, "%s is a startup pref and can't be set", NAME); \
   5435      return false;                                                           \
   5436    }                                                                         \
   5437    TYPE v;                                                                   \
   5438    if (!ConvertPrefValue<TYPE>(cx, args[1], &v)) {                           \
   5439      return false;                                                           \
   5440    }                                                                         \
   5441    JS::Prefs::SETTER(v);                                                     \
   5442    args.rval().setUndefined();                                               \
   5443    return true;                                                              \
   5444  }
   5445  FOR_EACH_JS_PREF(CHECK_PREF)
   5446 #undef CHECK_PREF
   5447 
   5448  // Fuzzing ignores missing prefs so it doesn't break if we remove a pref
   5449  if (fuzzingSafe) {
   5450    args.rval().setUndefined();
   5451    return true;
   5452  }
   5453  JS_ReportErrorASCII(cx, "invalid pref name");
   5454  return false;
   5455 }
   5456 
   5457 static bool SetPrefToValue(const char* name, size_t nameLen,
   5458                           const char* value) {
   5459  // Search for a matching pref and try to set it to the provided value.
   5460 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF)         \
   5461  if (nameLen == strlen(NAME) && memcmp(name, NAME, strlen(NAME)) == 0) { \
   5462    TYPE v;                                                               \
   5463    if (!ParsePrefValue<TYPE>(NAME, value, &v)) {                         \
   5464      return false;                                                       \
   5465    }                                                                     \
   5466    JS::Prefs::SETTER(v);                                                 \
   5467    return true;                                                          \
   5468  }
   5469  FOR_EACH_JS_PREF(CHECK_PREF)
   5470 #undef CHECK_PREF
   5471 
   5472  // Nothing matched. If --fuzzing-safe is used, return true after printing a
   5473  // message, to continue execution without breaking fuzzing when a pref is
   5474  // removed.
   5475  if (fuzzingSafe) {
   5476    fprintf(stderr, "Warning: Ignoring unknown pref name: %s\n", name);
   5477    return true;
   5478  }
   5479  fprintf(stderr, "Invalid pref name: %s\n", name);
   5480  return false;
   5481 }
   5482 
   5483 static bool SetPref(const char* pref) {
   5484  const char* assign = strchr(pref, '=');
   5485  if (!assign) {
   5486    if (IsPrefAvailable(pref) && !SetPrefToTrueForBool(pref)) {
   5487      return false;
   5488    }
   5489    return true;
   5490  }
   5491 
   5492  size_t nameLen = assign - pref;
   5493  const char* valStart = assign + 1;  // Skip '='.
   5494 
   5495  if (IsPrefAvailable(pref) && !SetPrefToValue(pref, nameLen, valStart)) {
   5496    return false;
   5497  }
   5498  return true;
   5499 }
   5500 
   5501 static void ListPrefs() {
   5502  auto printPref = [](const char* name, auto defaultVal) {
   5503    if (!IsPrefAvailable(name)) {
   5504      return;
   5505    }
   5506    using T = decltype(defaultVal);
   5507    if constexpr (std::is_same_v<T, bool>) {
   5508      fprintf(stderr, "%s=%s\n", name, defaultVal ? "true" : "false");
   5509    } else if constexpr (std::is_same_v<T, int32_t>) {
   5510      fprintf(stderr, "%s=%d\n", name, defaultVal);
   5511    } else {
   5512      static_assert(std::is_same_v<T, uint32_t>);
   5513      fprintf(stderr, "%s=%u\n", name, defaultVal);
   5514    }
   5515  };
   5516 
   5517 #define PRINT_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
   5518  printPref(NAME, JS::Prefs::CPP_NAME());
   5519  FOR_EACH_JS_PREF(PRINT_PREF)
   5520 #undef PRINT_PREF
   5521 }
   5522 
   5523 static bool SetJitCompilerOption(JSContext* cx, unsigned argc, Value* vp) {
   5524  CallArgs args = CallArgsFromVp(argc, vp);
   5525  RootedObject callee(cx, &args.callee());
   5526 
   5527  if (args.length() != 2) {
   5528    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments.");
   5529    return false;
   5530  }
   5531 
   5532  if (!args[0].isString()) {
   5533    ReportUsageErrorASCII(cx, callee, "First argument must be a String.");
   5534    return false;
   5535  }
   5536 
   5537  if (!args[1].isInt32()) {
   5538    ReportUsageErrorASCII(cx, callee, "Second argument must be an Int32.");
   5539    return false;
   5540  }
   5541 
   5542  // Disallow setting JIT options when there are worker threads, to avoid
   5543  // races.
   5544  if (workerThreadsLock) {
   5545    ReportUsageErrorASCII(
   5546        cx, callee, "Can't set JIT options when there are worker threads.");
   5547    return false;
   5548  }
   5549 
   5550  JSLinearString* strArg = JS_EnsureLinearString(cx, args[0].toString());
   5551  if (!strArg) {
   5552    return false;
   5553  }
   5554 
   5555 #define JIT_COMPILER_MATCH(key, string)                        \
   5556  else if (JS_LinearStringEqualsLiteral(strArg, string)) opt = \
   5557      JSJITCOMPILER_##key;
   5558 
   5559  JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
   5560  if (false) {
   5561  }
   5562  JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
   5563 #undef JIT_COMPILER_MATCH
   5564 
   5565  if (opt == JSJITCOMPILER_NOT_AN_OPTION) {
   5566    ReportUsageErrorASCII(
   5567        cx, callee,
   5568        "First argument does not name a valid option (see jsapi.h).");
   5569    return false;
   5570  }
   5571 
   5572  int32_t number = args[1].toInt32();
   5573  if (number < 0) {
   5574    number = -1;
   5575  }
   5576 
   5577  // Disallow enabling or disabling the Baseline Interpreter at runtime.
   5578  // Enabling is a problem because the Baseline Interpreter code is only
   5579  // present if the interpreter was enabled when the JitRuntime was created.
   5580  // To support disabling we would have to discard all JitScripts. Furthermore,
   5581  // we really want JitOptions to be immutable after startup so it's better to
   5582  // use shell flags.
   5583  if (opt == JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE &&
   5584      bool(number) != jit::IsBaselineInterpreterEnabled()) {
   5585    JS_ReportErrorASCII(cx,
   5586                        "Enabling or disabling the Baseline Interpreter at "
   5587                        "runtime is not supported.");
   5588    return false;
   5589  }
   5590 
   5591  // Throw if disabling the JITs and there's JIT code on the stack, to avoid
   5592  // assertion failures.
   5593  if ((opt == JSJITCOMPILER_BASELINE_ENABLE ||
   5594       opt == JSJITCOMPILER_ION_ENABLE) &&
   5595      number == 0) {
   5596    js::jit::JitActivationIterator iter(cx);
   5597    if (!iter.done()) {
   5598      JS_ReportErrorASCII(cx,
   5599                          "Can't turn off JITs with JIT code on the stack.");
   5600      return false;
   5601    }
   5602  }
   5603 
   5604  // Changing code memory protection settings at runtime is not supported. Don't
   5605  // throw if not changing the setting because some jit-tests depend on that.
   5606  if (opt == JSJITCOMPILER_WRITE_PROTECT_CODE) {
   5607    uint32_t writeProtect;
   5608    MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
   5609        cx, JSJITCOMPILER_WRITE_PROTECT_CODE, &writeProtect));
   5610    if (bool(number) != writeProtect) {
   5611      JS_ReportErrorASCII(cx, "Can't change code write protection at runtime");
   5612      return false;
   5613    }
   5614    return true;
   5615  }
   5616 
   5617  // Throw if trying to disable all the Wasm compilers.  The logic here is that
   5618  // if we're trying to disable a compiler that is currently enabled and that is
   5619  // the last compiler enabled then we must throw.
   5620  //
   5621  // Note that this check does not prevent an error from being thrown later.
   5622  // Actual compiler availability is dynamic and depends on other conditions,
   5623  // such as other options set and whether a debugger is present.
   5624  if ((opt == JSJITCOMPILER_WASM_JIT_BASELINE ||
   5625       opt == JSJITCOMPILER_WASM_JIT_OPTIMIZING) &&
   5626      number == 0) {
   5627    uint32_t baseline, optimizing;
   5628    MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
   5629        cx, JSJITCOMPILER_WASM_JIT_BASELINE, &baseline));
   5630    MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
   5631        cx, JSJITCOMPILER_WASM_JIT_OPTIMIZING, &optimizing));
   5632    if (baseline + optimizing == 1) {
   5633      if ((opt == JSJITCOMPILER_WASM_JIT_BASELINE && baseline) ||
   5634          (opt == JSJITCOMPILER_WASM_JIT_OPTIMIZING && optimizing)) {
   5635        JS_ReportErrorASCII(
   5636            cx,
   5637            "Disabling all the Wasm compilers at runtime is not supported.");
   5638        return false;
   5639      }
   5640    }
   5641  }
   5642 
   5643  // JIT compiler options are process-wide, so we have to stop off-thread
   5644  // compilations for all runtimes to avoid races.
   5645  WaitForAllHelperThreads();
   5646 
   5647  // Only release JIT code for the current runtime because there's no good
   5648  // way to discard code for other runtimes.
   5649  ReleaseAllJITCode(cx->gcContext());
   5650 
   5651  JS_SetGlobalJitCompilerOption(cx, opt, uint32_t(number));
   5652 
   5653  args.rval().setUndefined();
   5654  return true;
   5655 }
   5656 
   5657 static bool EnableLastWarning(JSContext* cx, unsigned argc, Value* vp) {
   5658  ShellContext* sc = GetShellContext(cx);
   5659  CallArgs args = CallArgsFromVp(argc, vp);
   5660 
   5661  sc->lastWarningEnabled = true;
   5662  sc->lastWarning.setNull();
   5663 
   5664  args.rval().setUndefined();
   5665  return true;
   5666 }
   5667 
   5668 static bool DisableLastWarning(JSContext* cx, unsigned argc, Value* vp) {
   5669  ShellContext* sc = GetShellContext(cx);
   5670  CallArgs args = CallArgsFromVp(argc, vp);
   5671 
   5672  sc->lastWarningEnabled = false;
   5673  sc->lastWarning.setNull();
   5674 
   5675  args.rval().setUndefined();
   5676  return true;
   5677 }
   5678 
   5679 static bool GetLastWarning(JSContext* cx, unsigned argc, Value* vp) {
   5680  ShellContext* sc = GetShellContext(cx);
   5681  CallArgs args = CallArgsFromVp(argc, vp);
   5682 
   5683  if (!sc->lastWarningEnabled) {
   5684    JS_ReportErrorASCII(cx, "Call enableLastWarning first.");
   5685    return false;
   5686  }
   5687 
   5688  if (!JS_WrapValue(cx, &sc->lastWarning)) {
   5689    return false;
   5690  }
   5691 
   5692  args.rval().set(sc->lastWarning);
   5693  return true;
   5694 }
   5695 
   5696 static bool ClearLastWarning(JSContext* cx, unsigned argc, Value* vp) {
   5697  ShellContext* sc = GetShellContext(cx);
   5698  CallArgs args = CallArgsFromVp(argc, vp);
   5699 
   5700  if (!sc->lastWarningEnabled) {
   5701    JS_ReportErrorASCII(cx, "Call enableLastWarning first.");
   5702    return false;
   5703  }
   5704 
   5705  sc->lastWarning.setNull();
   5706 
   5707  args.rval().setUndefined();
   5708  return true;
   5709 }
   5710 
   5711 #if defined(DEBUG) || defined(JS_JITSPEW)
   5712 static bool StackDump(JSContext* cx, unsigned argc, Value* vp) {
   5713  CallArgs args = CallArgsFromVp(argc, vp);
   5714 
   5715  if (!gOutFile->isOpen()) {
   5716    JS_ReportErrorASCII(cx, "output file is closed");
   5717    return false;
   5718  }
   5719 
   5720  bool showArgs = ToBoolean(args.get(0));
   5721  bool showLocals = ToBoolean(args.get(1));
   5722  bool showThisProps = ToBoolean(args.get(2));
   5723 
   5724  JS::UniqueChars buf =
   5725      JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
   5726  if (!buf) {
   5727    fputs("Failed to format JavaScript stack for dump\n", gOutFile->fp);
   5728    JS_ClearPendingException(cx);
   5729  } else {
   5730    fputs(buf.get(), gOutFile->fp);
   5731  }
   5732 
   5733  args.rval().setUndefined();
   5734  return true;
   5735 }
   5736 #endif
   5737 
   5738 MOZ_ASAN_IGNORE static bool StackPointerInfo(JSContext* cx, unsigned argc,
   5739                                             Value* vp) {
   5740  CallArgs args = CallArgsFromVp(argc, vp);
   5741 
   5742  // Copy the truncated stack pointer to the result.  This value is not used
   5743  // as a pointer but as a way to measure frame-size from JS.
   5744  // The ASAN must be disabled for this function -- it may allocate `args`
   5745  // not on the stack.
   5746  args.rval().setInt32(int32_t(reinterpret_cast<size_t>(&args) & 0xfffffff));
   5747  return true;
   5748 }
   5749 
   5750 static bool Elapsed(JSContext* cx, unsigned argc, Value* vp) {
   5751  CallArgs args = CallArgsFromVp(argc, vp);
   5752  if (args.length() == 0) {
   5753    double d = PRMJ_Now() - GetShellContext(cx)->startTime;
   5754    args.rval().setDouble(d);
   5755    return true;
   5756  }
   5757  JS_ReportErrorASCII(cx, "Wrong number of arguments");
   5758  return false;
   5759 }
   5760 
   5761 static ShellCompartmentPrivate* EnsureShellCompartmentPrivate(JSContext* cx) {
   5762  Compartment* comp = cx->compartment();
   5763  auto priv =
   5764      static_cast<ShellCompartmentPrivate*>(JS_GetCompartmentPrivate(comp));
   5765  if (!priv) {
   5766    priv = cx->new_<ShellCompartmentPrivate>();
   5767    JS_SetCompartmentPrivate(cx->compartment(), priv);
   5768  }
   5769  return priv;
   5770 }
   5771 
   5772 static bool ParseModule(JSContext* cx, unsigned argc, Value* vp) {
   5773  CallArgs args = CallArgsFromVp(argc, vp);
   5774  if (!args.requireAtLeast(cx, "parseModule", 1)) {
   5775    return false;
   5776  }
   5777 
   5778  UniqueChars filename;
   5779  CompileOptions options(cx);
   5780  JS::ModuleType moduleType = JS::ModuleType::JavaScript;
   5781  if (args.length() > 1) {
   5782    if (!args[1].isString()) {
   5783      const char* typeName = InformalValueTypeName(args[1]);
   5784      JS_ReportErrorASCII(cx, "expected filename string, got %s", typeName);
   5785      return false;
   5786    }
   5787 
   5788    RootedString str(cx, args[1].toString());
   5789    filename = JS_EncodeStringToUTF8(cx, str);
   5790    if (!filename) {
   5791      return false;
   5792    }
   5793 
   5794    options.setFileAndLine(filename.get(), 1);
   5795 
   5796    // The 2nd argument is the module type string. "js", "json" or "bytes" is
   5797    // expected.
   5798    if (args.length() == 3) {
   5799      if (!args[2].isString()) {
   5800        const char* typeName = InformalValueTypeName(args[2]);
   5801        JS_ReportErrorASCII(cx, "expected moduleType string, got %s", typeName);
   5802        return false;
   5803      }
   5804 
   5805      RootedString str(cx, args[2].toString());
   5806      JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
   5807      if (!linearStr) {
   5808        return false;
   5809      }
   5810      if (JS_LinearStringEqualsLiteral(linearStr, "json")) {
   5811        moduleType = JS::ModuleType::JSON;
   5812      } else if (JS_LinearStringEqualsLiteral(linearStr, "bytes")) {
   5813        moduleType = JS::ModuleType::Bytes;
   5814      } else if (!JS_LinearStringEqualsLiteral(linearStr, "js")) {
   5815        JS_ReportErrorASCII(
   5816            cx, "moduleType string ('js' or 'json' or 'bytes') expected");
   5817        return false;
   5818      }
   5819    }
   5820  } else {
   5821    options.setFileAndLine("<string>", 1);
   5822  }
   5823 
   5824  RootedObject module(cx);
   5825  switch (moduleType) {
   5826    case JS::ModuleType::JavaScript:
   5827    case JS::ModuleType::JSON: {
   5828      if (!args[0].isString()) {
   5829        const char* typeName = InformalValueTypeName(args[0]);
   5830        JS_ReportErrorASCII(cx, "expected string to compile, got %s", typeName);
   5831        return false;
   5832      }
   5833 
   5834      JSString* scriptContents = args[0].toString();
   5835 
   5836      AutoStableStringChars linearChars(cx);
   5837      if (!linearChars.initTwoByte(cx, scriptContents)) {
   5838        return false;
   5839      }
   5840 
   5841      JS::SourceText<char16_t> srcBuf;
   5842      if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
   5843        return false;
   5844      }
   5845 
   5846      if (moduleType == JS::ModuleType::JSON) {
   5847        module = JS::CompileJsonModule(cx, options, srcBuf);
   5848      } else {
   5849        options.setModule();
   5850        module = JS::CompileModule(cx, options, srcBuf);
   5851      }
   5852 
   5853      break;
   5854    }
   5855 
   5856    case JS::ModuleType::Bytes: {
   5857      if (!args[0].isObject() ||
   5858          !JS::IsArrayBufferObject(&args[0].toObject())) {
   5859        const char* typeName = InformalValueTypeName(args[0]);
   5860        JS_ReportErrorASCII(cx, "expected ArrayBuffer for bytes module, got %s",
   5861                            typeName);
   5862        return false;
   5863      }
   5864 
   5865      /*
   5866       * NOTE: The spec requires checking that the ArrayBuffer is immutable.
   5867       * Immutable ArrayBuffers (see bug 1952253) are still only a Stage 2.7
   5868       * proposal. This check will be added in a future update.
   5869       */
   5870      module = JS::CreateDefaultExportSyntheticModule(cx, args[0]);
   5871      break;
   5872    }
   5873 
   5874    case JS::ModuleType::CSS:
   5875    case JS::ModuleType::Unknown:
   5876      JS_ReportErrorASCII(cx, "Unsupported module type in parseModule");
   5877      return false;
   5878  }
   5879 
   5880  if (!module) {
   5881    return false;
   5882  }
   5883 
   5884  Rooted<ShellModuleObjectWrapper*> wrapper(
   5885      cx, ShellModuleObjectWrapper::create(cx, module.as<ModuleObject>(),
   5886                                           moduleType));
   5887  if (!wrapper) {
   5888    return false;
   5889  }
   5890  args.rval().setObject(*wrapper);
   5891  return true;
   5892 }
   5893 
   5894 // A JSObject that holds XDRBuffer.
   5895 class XDRBufferObject : public NativeObject {
   5896  static const size_t VECTOR_SLOT = 0;
   5897  static const unsigned RESERVED_SLOTS = 1;
   5898 
   5899 public:
   5900  static const JSClassOps classOps_;
   5901  static const JSClass class_;
   5902 
   5903  [[nodiscard]] inline static XDRBufferObject* create(
   5904      JSContext* cx, JS::TranscodeBuffer&& buf);
   5905 
   5906  JS::TranscodeBuffer* data() const {
   5907    Value value = getReservedSlot(VECTOR_SLOT);
   5908    auto buf = static_cast<JS::TranscodeBuffer*>(value.toPrivate());
   5909    MOZ_ASSERT(buf);
   5910    return buf;
   5911  }
   5912 
   5913  bool hasData() const {
   5914    // Data may not be present if we hit OOM in initialization.
   5915    return !getReservedSlot(VECTOR_SLOT).isUndefined();
   5916  }
   5917 
   5918  static void finalize(JS::GCContext* gcx, JSObject* obj);
   5919 };
   5920 
   5921 /*static */ const JSClassOps XDRBufferObject::classOps_ = {
   5922    nullptr,                    // addProperty
   5923    nullptr,                    // delProperty
   5924    nullptr,                    // enumerate
   5925    nullptr,                    // newEnumerate
   5926    nullptr,                    // resolve
   5927    nullptr,                    // mayResolve
   5928    XDRBufferObject::finalize,  // finalize
   5929    nullptr,                    // call
   5930    nullptr,                    // construct
   5931    nullptr,                    // trace
   5932 };
   5933 
   5934 /*static */ const JSClass XDRBufferObject::class_ = {
   5935    "XDRBufferObject",
   5936    JSCLASS_HAS_RESERVED_SLOTS(XDRBufferObject::RESERVED_SLOTS) |
   5937        JSCLASS_BACKGROUND_FINALIZE,
   5938    &XDRBufferObject::classOps_,
   5939 };
   5940 
   5941 XDRBufferObject* XDRBufferObject::create(JSContext* cx,
   5942                                         JS::TranscodeBuffer&& buf) {
   5943  XDRBufferObject* bufObj =
   5944      NewObjectWithGivenProto<XDRBufferObject>(cx, nullptr);
   5945  if (!bufObj) {
   5946    return nullptr;
   5947  }
   5948 
   5949  auto heapBuf = cx->make_unique<JS::TranscodeBuffer>(std::move(buf));
   5950  if (!heapBuf) {
   5951    return nullptr;
   5952  }
   5953 
   5954  size_t len = heapBuf->length();
   5955  InitReservedSlot(bufObj, VECTOR_SLOT, heapBuf.release(), len,
   5956                   MemoryUse::XDRBufferElements);
   5957 
   5958  return bufObj;
   5959 }
   5960 
   5961 void XDRBufferObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   5962  XDRBufferObject* buf = &obj->as<XDRBufferObject>();
   5963  if (buf->hasData()) {
   5964    gcx->delete_(buf, buf->data(), buf->data()->length(),
   5965                 MemoryUse::XDRBufferElements);
   5966  }
   5967 }
   5968 
   5969 static bool InstantiateModuleStencil(JSContext* cx, uint32_t argc, Value* vp) {
   5970  CallArgs args = CallArgsFromVp(argc, vp);
   5971 
   5972  if (!args.requireAtLeast(cx, "instantiateModuleStencil", 1)) {
   5973    return false;
   5974  }
   5975 
   5976  /* Prepare the input byte array. */
   5977  if (!args[0].isObject()) {
   5978    JS_ReportErrorASCII(cx,
   5979                        "instantiateModuleStencil: Stencil object expected");
   5980    return false;
   5981  }
   5982  Rooted<js::StencilObject*> stencilObj(
   5983      cx, args[0].toObject().maybeUnwrapIf<js::StencilObject>());
   5984  if (!stencilObj) {
   5985    JS_ReportErrorASCII(cx,
   5986                        "instantiateModuleStencil: Stencil object expected");
   5987    return false;
   5988  }
   5989 
   5990  if (!stencilObj->stencil()->getInitial()->isModule()) {
   5991    JS_ReportErrorASCII(cx,
   5992                        "instantiateModuleStencil: Module stencil expected");
   5993    return false;
   5994  }
   5995 
   5996  CompileOptions options(cx);
   5997  UniqueChars fileNameBytes;
   5998  if (args.length() == 2) {
   5999    if (!args[1].isObject()) {
   6000      JS_ReportErrorASCII(
   6001          cx, "instantiateModuleStencil: The 2nd argument must be an object");
   6002      return false;
   6003    }
   6004 
   6005    RootedObject opts(cx, &args[1].toObject());
   6006    if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
   6007      return false;
   6008    }
   6009  }
   6010 
   6011  if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencilObj->stencil())) {
   6012    return false;
   6013  }
   6014 
   6015  JS::InstantiateOptions instantiateOptions(options);
   6016  Rooted<JSObject*> modObject(
   6017      cx, JS::InstantiateModuleStencil(cx, instantiateOptions,
   6018                                       stencilObj->stencil(), nullptr));
   6019  if (!modObject) {
   6020    return false;
   6021  }
   6022 
   6023  Rooted<ModuleObject*> module(cx, &modObject->as<ModuleObject>());
   6024  Rooted<ShellModuleObjectWrapper*> wrapper(
   6025      cx, ShellModuleObjectWrapper::create(cx, module));
   6026  if (!wrapper) {
   6027    return false;
   6028  }
   6029  args.rval().setObject(*wrapper);
   6030  return true;
   6031 }
   6032 
   6033 static bool InstantiateModuleStencilXDR(JSContext* cx, uint32_t argc,
   6034                                        Value* vp) {
   6035  CallArgs args = CallArgsFromVp(argc, vp);
   6036 
   6037  if (!args.requireAtLeast(cx, "instantiateModuleStencilXDR", 1)) {
   6038    return false;
   6039  }
   6040 
   6041  /* Prepare the input byte array. */
   6042  if (!args[0].isObject()) {
   6043    JS_ReportErrorASCII(
   6044        cx, "instantiateModuleStencilXDR: Stencil XDR object expected");
   6045    return false;
   6046  }
   6047  Rooted<StencilXDRBufferObject*> xdrObj(
   6048      cx, args[0].toObject().maybeUnwrapIf<StencilXDRBufferObject>());
   6049  if (!xdrObj) {
   6050    JS_ReportErrorASCII(
   6051        cx, "instantiateModuleStencilXDR: Stencil XDR object expected");
   6052    return false;
   6053  }
   6054  MOZ_ASSERT(xdrObj->hasBuffer());
   6055 
   6056  CompileOptions options(cx);
   6057  UniqueChars fileNameBytes;
   6058  if (args.length() == 2) {
   6059    if (!args[1].isObject()) {
   6060      JS_ReportErrorASCII(
   6061          cx,
   6062          "instantiateModuleStencilXDR: The 2nd argument must be an object");
   6063      return false;
   6064    }
   6065 
   6066    RootedObject opts(cx, &args[1].toObject());
   6067    if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
   6068      return false;
   6069    }
   6070  }
   6071 
   6072  JS::TranscodeRange xdrRange(xdrObj->buffer(), xdrObj->bufferLength());
   6073  JS::DecodeOptions decodeOptions(options);
   6074  RefPtr<JS::Stencil> stencil;
   6075  auto result =
   6076      JS::DecodeStencil(cx, decodeOptions, xdrRange, getter_AddRefs(stencil));
   6077  if (result == JS::TranscodeResult::Throw) {
   6078    return false;
   6079  }
   6080  if (JS::IsTranscodeFailureResult(result)) {
   6081    JS_ReportErrorASCII(cx, "Decoding failure");
   6082    return false;
   6083  }
   6084 
   6085  if (!stencil->getInitial()->isModule()) {
   6086    JS_ReportErrorASCII(cx,
   6087                        "instantiateModuleStencilXDR: Module stencil expected");
   6088    return false;
   6089  }
   6090 
   6091  if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencil.get())) {
   6092    return false;
   6093  }
   6094 
   6095  JS::InstantiateOptions instantiateOptions(options);
   6096  Rooted<JSObject*> modObject(
   6097      cx,
   6098      JS::InstantiateModuleStencil(cx, instantiateOptions, stencil, nullptr));
   6099  if (!modObject) {
   6100    return false;
   6101  }
   6102 
   6103  Rooted<ModuleObject*> module(cx, &modObject->as<ModuleObject>());
   6104  Rooted<ShellModuleObjectWrapper*> wrapper(
   6105      cx, ShellModuleObjectWrapper::create(cx, module));
   6106  if (!wrapper) {
   6107    return false;
   6108  }
   6109  args.rval().setObject(*wrapper);
   6110  return true;
   6111 }
   6112 
   6113 static bool RegisterModule(JSContext* cx, unsigned argc, Value* vp) {
   6114  CallArgs args = CallArgsFromVp(argc, vp);
   6115  if (!args.requireAtLeast(cx, "registerModule", 2)) {
   6116    return false;
   6117  }
   6118 
   6119  if (!args[0].isString()) {
   6120    const char* typeName = InformalValueTypeName(args[0]);
   6121    JS_ReportErrorASCII(cx, "Expected string, got %s", typeName);
   6122    return false;
   6123  }
   6124 
   6125  if (!args[1].isObject() ||
   6126      !args[1].toObject().is<ShellModuleObjectWrapper>()) {
   6127    const char* typeName = InformalValueTypeName(args[1]);
   6128    JS_ReportErrorASCII(cx, "Expected module, got %s", typeName);
   6129    return false;
   6130  }
   6131 
   6132  ShellContext* sc = GetShellContext(cx);
   6133  Rooted<ModuleObject*> module(
   6134      cx, args[1].toObject().as<ShellModuleObjectWrapper>().get());
   6135  if (module->realm() != cx->realm()) {
   6136    JS_ReportErrorASCII(cx, "Module is in a different realm");
   6137    return false;
   6138  }
   6139 
   6140  Rooted<JSAtom*> specifier(cx, AtomizeString(cx, args[0].toString()));
   6141  if (!specifier) {
   6142    return false;
   6143  }
   6144 
   6145  JS::ModuleType moduleType =
   6146      args[1].toObject().as<ShellModuleObjectWrapper>().getModuleType();
   6147 
   6148  RootedObject moduleRequest(
   6149      cx, ModuleRequestObject::create(cx, specifier, moduleType));
   6150  if (!moduleRequest) {
   6151    return false;
   6152  }
   6153 
   6154  if (!sc->moduleLoader->registerTestModule(cx, moduleRequest, module)) {
   6155    return false;
   6156  }
   6157 
   6158  Rooted<ShellModuleObjectWrapper*> wrapper(
   6159      cx, ShellModuleObjectWrapper::create(cx, module, moduleType));
   6160  if (!wrapper) {
   6161    return false;
   6162  }
   6163  args.rval().setObject(*wrapper);
   6164  return true;
   6165 }
   6166 
   6167 static bool ClearModules(JSContext* cx, unsigned argc, Value* vp) {
   6168  CallArgs args = CallArgsFromVp(argc, vp);
   6169  ShellContext* sc = GetShellContext(cx);
   6170  sc->moduleLoader->clearModules(cx);
   6171  args.rval().setUndefined();
   6172  return true;
   6173 }
   6174 
   6175 static bool ModuleLoadResolved(JSContext* cx, HandleValue hostDefined) {
   6176  RootedObject module(cx, &hostDefined.toObject());
   6177  return JS::ModuleLink(cx, module);
   6178 }
   6179 
   6180 static bool ModuleLoadRejected(JSContext* cx, HandleValue hostDefined,
   6181                               HandleValue error) {
   6182  JS_SetPendingException(cx, error);
   6183  return false;
   6184 }
   6185 
   6186 static bool ModuleLink(JSContext* cx, unsigned argc, Value* vp) {
   6187  CallArgs args = CallArgsFromVp(argc, vp);
   6188 
   6189  if (args.length() != 1 || !args[0].isObject()) {
   6190    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
   6191                              "moduleLink");
   6192    return false;
   6193  }
   6194 
   6195  RootedObject object(cx, UncheckedUnwrap(&args[0].toObject()));
   6196  if (!object->is<ShellModuleObjectWrapper>()) {
   6197    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
   6198                              "moduleLink");
   6199    return false;
   6200  }
   6201 
   6202  AutoRealm ar(cx, object);
   6203 
   6204  Rooted<ModuleObject*> module(cx,
   6205                               object->as<ShellModuleObjectWrapper>().get());
   6206 
   6207  // TODO: Bug 1968904: Update ModuleLink
   6208  RootedValue hostDefined(cx, ObjectValue(*module));
   6209  if (!JS::LoadRequestedModules(cx, module, hostDefined, ModuleLoadResolved,
   6210                                ModuleLoadRejected)) {
   6211    return false;
   6212  }
   6213 
   6214  args.rval().setUndefined();
   6215  return true;
   6216 }
   6217 
   6218 static bool ModuleEvaluate(JSContext* cx, unsigned argc, Value* vp) {
   6219  CallArgs args = CallArgsFromVp(argc, vp);
   6220 
   6221  if (args.length() != 1 || !args[0].isObject()) {
   6222    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
   6223                              "moduleEvaluate");
   6224    return false;
   6225  }
   6226 
   6227  RootedObject object(cx, UncheckedUnwrap(&args[0].toObject()));
   6228  if (!object->is<ShellModuleObjectWrapper>()) {
   6229    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
   6230                              "moduleEvaluate");
   6231    return false;
   6232  }
   6233 
   6234  {
   6235    AutoRealm ar(cx, object);
   6236 
   6237    Rooted<ModuleObject*> module(cx,
   6238                                 object->as<ShellModuleObjectWrapper>().get());
   6239    if (!JS::ModuleEvaluate(cx, module, args.rval())) {
   6240      return false;
   6241    }
   6242  }
   6243 
   6244  return JS_WrapValue(cx, args.rval());
   6245 }
   6246 
   6247 static ModuleEnvironmentObject* GetModuleInitialEnvironment(
   6248    JSContext* cx, Handle<ModuleObject*> module) {
   6249  // Use the initial environment so that tests can check bindings exists
   6250  // before they have been instantiated.
   6251  Rooted<ModuleEnvironmentObject*> env(cx, &module->initialEnvironment());
   6252  MOZ_ASSERT(env);
   6253  return env;
   6254 }
   6255 
   6256 static bool GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp) {
   6257  CallArgs args = CallArgsFromVp(argc, vp);
   6258  if (args.length() != 1) {
   6259    JS_ReportErrorASCII(cx, "Wrong number of arguments");
   6260    return false;
   6261  }
   6262 
   6263  if (!args[0].isObject() ||
   6264      !args[0].toObject().is<ShellModuleObjectWrapper>()) {
   6265    JS_ReportErrorASCII(cx,
   6266                        "First argument should be a ShellModuleObjectWrapper");
   6267    return false;
   6268  }
   6269 
   6270  Rooted<ModuleObject*> module(
   6271      cx, args[0].toObject().as<ShellModuleObjectWrapper>().get());
   6272  if (module->hasSyntheticModuleFields()) {
   6273    JS_ReportErrorASCII(cx,
   6274                        "Operation is not supported on JSON module objects.");
   6275    return false;
   6276  }
   6277 
   6278  if (module->hadEvaluationError()) {
   6279    JS_ReportErrorASCII(cx, "Module environment unavailable");
   6280    return false;
   6281  }
   6282 
   6283  Rooted<ModuleEnvironmentObject*> env(cx,
   6284                                       GetModuleInitialEnvironment(cx, module));
   6285  Rooted<IdVector> ids(cx, IdVector(cx));
   6286  if (!JS_Enumerate(cx, env, &ids)) {
   6287    return false;
   6288  }
   6289 
   6290  // The "*namespace*" binding is a detail of current implementation so hide
   6291  // it to give stable results in tests.
   6292  ids.eraseIfEqual(NameToId(cx->names().star_namespace_star_));
   6293 
   6294  uint32_t length = ids.length();
   6295  Rooted<ArrayObject*> array(cx, NewDenseFullyAllocatedArray(cx, length));
   6296  if (!array) {
   6297    return false;
   6298  }
   6299 
   6300  array->setDenseInitializedLength(length);
   6301  for (uint32_t i = 0; i < length; i++) {
   6302    array->initDenseElement(i, StringValue(ids[i].toString()));
   6303  }
   6304 
   6305  args.rval().setObject(*array);
   6306  return true;
   6307 }
   6308 
   6309 static bool GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp) {
   6310  CallArgs args = CallArgsFromVp(argc, vp);
   6311  if (args.length() != 2) {
   6312    JS_ReportErrorASCII(cx, "Wrong number of arguments");
   6313    return false;
   6314  }
   6315 
   6316  if (!args[0].isObject() ||
   6317      !args[0].toObject().is<ShellModuleObjectWrapper>()) {
   6318    JS_ReportErrorASCII(cx,
   6319                        "First argument should be a ShellModuleObjectWrapper");
   6320    return false;
   6321  }
   6322 
   6323  if (!args[1].isString()) {
   6324    JS_ReportErrorASCII(cx, "Second argument should be a string");
   6325    return false;
   6326  }
   6327 
   6328  Rooted<ModuleObject*> module(
   6329      cx, args[0].toObject().as<ShellModuleObjectWrapper>().get());
   6330  if (module->hasSyntheticModuleFields()) {
   6331    JS_ReportErrorASCII(cx,
   6332                        "Operation is not supported on JSON module objects.");
   6333    return false;
   6334  }
   6335 
   6336  if (module->hadEvaluationError()) {
   6337    JS_ReportErrorASCII(cx, "Module environment unavailable");
   6338    return false;
   6339  }
   6340 
   6341  Rooted<ModuleEnvironmentObject*> env(cx,
   6342                                       GetModuleInitialEnvironment(cx, module));
   6343  RootedString name(cx, args[1].toString());
   6344  RootedId id(cx);
   6345  if (!JS_StringToId(cx, name, &id)) {
   6346    return false;
   6347  }
   6348 
   6349  if (!GetProperty(cx, env, env, id, args.rval())) {
   6350    return false;
   6351  }
   6352 
   6353  if (args.rval().isMagic(JS_UNINITIALIZED_LEXICAL)) {
   6354    ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
   6355    return false;
   6356  }
   6357 
   6358  return true;
   6359 }
   6360 
   6361 enum class DumpType {
   6362  ParseNode,
   6363  Stencil,
   6364 };
   6365 
   6366 template <typename Unit>
   6367 static bool DumpAST(JSContext* cx, const JS::ReadOnlyCompileOptions& options,
   6368                    const Unit* units, size_t length,
   6369                    js::frontend::CompilationState& compilationState,
   6370                    js::frontend::ParseGoal goal) {
   6371  using namespace js::frontend;
   6372 
   6373  AutoReportFrontendContext fc(cx);
   6374  Parser<FullParseHandler, Unit> parser(&fc, options, units, length,
   6375                                        compilationState,
   6376                                        /* syntaxParser = */ nullptr);
   6377  if (!parser.checkOptions()) {
   6378    return false;
   6379  }
   6380 
   6381  // Emplace the top-level stencil.
   6382  MOZ_ASSERT(compilationState.scriptData.length() ==
   6383             CompilationStencil::TopLevelIndex);
   6384  if (!compilationState.appendScriptStencilAndData(&fc)) {
   6385    return false;
   6386  }
   6387 
   6388  js::frontend::ParseNode* pn;
   6389  if (goal == frontend::ParseGoal::Script) {
   6390    pn = parser.parse().unwrapOr(nullptr);
   6391  } else {
   6392    ModuleBuilder builder(&fc, &parser);
   6393 
   6394    SourceExtent extent = SourceExtent::makeGlobalExtent(length);
   6395    ModuleSharedContext modulesc(&fc, options, builder, extent);
   6396    pn = parser.moduleBody(&modulesc).unwrapOr(nullptr);
   6397  }
   6398 
   6399  if (!pn) {
   6400    return false;
   6401  }
   6402 
   6403 #if defined(DEBUG)
   6404  js::Fprinter out(stderr);
   6405  DumpParseTree(&parser, pn, out);
   6406 #endif
   6407 
   6408  return true;
   6409 }
   6410 
   6411 template <typename Unit>
   6412 [[nodiscard]] static bool DumpStencil(JSContext* cx,
   6413                                      const JS::ReadOnlyCompileOptions& options,
   6414                                      const Unit* units, size_t length,
   6415                                      js::frontend::ParseGoal goal) {
   6416  JS::SourceText<Unit> srcBuf;
   6417  if (!srcBuf.init(cx, units, length, JS::SourceOwnership::Borrowed)) {
   6418    return false;
   6419  }
   6420 
   6421  RefPtr<JS::Stencil> stencil;
   6422  if (goal == frontend::ParseGoal::Script) {
   6423    stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
   6424  } else {
   6425    stencil = JS::CompileModuleScriptToStencil(cx, options, srcBuf);
   6426  }
   6427 
   6428  if (!stencil) {
   6429    return false;
   6430  }
   6431 
   6432 #if defined(DEBUG) || defined(JS_JITSPEW)
   6433  stencil->dump();
   6434 #endif
   6435 
   6436  return true;
   6437 }
   6438 
   6439 static bool FrontendTest(JSContext* cx, unsigned argc, Value* vp,
   6440                         const char* funcName, DumpType dumpType) {
   6441  using namespace js::frontend;
   6442 
   6443  CallArgs args = CallArgsFromVp(argc, vp);
   6444 
   6445  if (!args.requireAtLeast(cx, funcName, 1)) {
   6446    return false;
   6447  }
   6448  if (!args[0].isString()) {
   6449    const char* typeName = InformalValueTypeName(args[0]);
   6450    JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
   6451    return false;
   6452  }
   6453 
   6454  frontend::ParseGoal goal = frontend::ParseGoal::Script;
   6455 
   6456  CompileOptions options(cx);
   6457  options.setIntroductionType("js shell parse")
   6458      .setFileAndLine("<string>", 1)
   6459      .setIsRunOnce(true)
   6460      .setNoScriptRval(true);
   6461 
   6462  if (args.length() >= 2) {
   6463    if (!args[1].isObject()) {
   6464      JS_ReportErrorASCII(cx, "The 2nd argument must be an object");
   6465      return false;
   6466    }
   6467 
   6468    RootedObject objOptions(cx, &args[1].toObject());
   6469 
   6470    RootedValue optionModule(cx);
   6471    if (!JS_GetProperty(cx, objOptions, "module", &optionModule)) {
   6472      return false;
   6473    }
   6474 
   6475    if (optionModule.isBoolean()) {
   6476      if (optionModule.toBoolean()) {
   6477        goal = frontend::ParseGoal::Module;
   6478      }
   6479    } else if (!optionModule.isUndefined()) {
   6480      const char* typeName = InformalValueTypeName(optionModule);
   6481      JS_ReportErrorASCII(cx, "option `module` should be a boolean, got %s",
   6482                          typeName);
   6483      return false;
   6484    }
   6485    if (!js::ParseCompileOptions(cx, options, objOptions, nullptr)) {
   6486      return false;
   6487    }
   6488 
   6489    if (goal == frontend::ParseGoal::Module && options.lineno == 0) {
   6490      JS_ReportErrorASCII(cx, "Module cannot be compiled with lineNumber == 0");
   6491      return false;
   6492    }
   6493  }
   6494 
   6495  JSString* scriptContents = args[0].toString();
   6496  Rooted<JSLinearString*> linearString(cx, scriptContents->ensureLinear(cx));
   6497  if (!linearString) {
   6498    return false;
   6499  }
   6500 
   6501  bool isAscii = false;
   6502  if (linearString->hasLatin1Chars()) {
   6503    JS::AutoCheckCannotGC nogc;
   6504    isAscii = JS::StringIsASCII(mozilla::Span(
   6505        reinterpret_cast<const char*>(linearString->latin1Chars(nogc)),
   6506        linearString->length()));
   6507  }
   6508 
   6509  AutoStableStringChars stableChars(cx);
   6510  if (isAscii) {
   6511    if (!stableChars.init(cx, scriptContents)) {
   6512      return false;
   6513    }
   6514    MOZ_ASSERT(stableChars.isLatin1());
   6515  } else {
   6516    if (!stableChars.initTwoByte(cx, scriptContents)) {
   6517      return false;
   6518    }
   6519  }
   6520 
   6521  size_t length = scriptContents->length();
   6522 
   6523  if (goal == frontend::ParseGoal::Module) {
   6524    // See frontend::CompileModule.
   6525    options.setForceStrictMode();
   6526    options.allowHTMLComments = false;
   6527  }
   6528 
   6529  if (dumpType == DumpType::Stencil) {
   6530    if (isAscii) {
   6531      const Latin1Char* latin1 = stableChars.latin1Range().begin().get();
   6532      auto utf8 = reinterpret_cast<const mozilla::Utf8Unit*>(latin1);
   6533      if (!DumpStencil<mozilla::Utf8Unit>(cx, options, utf8, length, goal)) {
   6534        return false;
   6535      }
   6536    } else {
   6537      MOZ_ASSERT(stableChars.isTwoByte());
   6538      const char16_t* chars = stableChars.twoByteRange().begin().get();
   6539      if (!DumpStencil<char16_t>(cx, options, chars, length, goal)) {
   6540        return false;
   6541      }
   6542    }
   6543 
   6544    args.rval().setUndefined();
   6545    return true;
   6546  }
   6547 
   6548  AutoReportFrontendContext fc(cx);
   6549  Rooted<frontend::CompilationInput> input(cx,
   6550                                           frontend::CompilationInput(options));
   6551  if (goal == frontend::ParseGoal::Script) {
   6552    if (!input.get().initForGlobal(&fc)) {
   6553      return false;
   6554    }
   6555  } else {
   6556    if (!input.get().initForModule(&fc)) {
   6557      return false;
   6558    }
   6559  }
   6560 
   6561  LifoAllocScope allocScope(&cx->tempLifoAlloc());
   6562  frontend::NoScopeBindingCache scopeCache;
   6563  frontend::CompilationState compilationState(&fc, allocScope, input.get());
   6564  if (!compilationState.init(&fc, &scopeCache)) {
   6565    return false;
   6566  }
   6567 
   6568  if (isAscii) {
   6569    const Latin1Char* latin1 = stableChars.latin1Range().begin().get();
   6570    auto utf8 = reinterpret_cast<const mozilla::Utf8Unit*>(latin1);
   6571    if (!DumpAST<mozilla::Utf8Unit>(cx, options, utf8, length, compilationState,
   6572                                    goal)) {
   6573      return false;
   6574    }
   6575  } else {
   6576    MOZ_ASSERT(stableChars.isTwoByte());
   6577    const char16_t* chars = stableChars.twoByteRange().begin().get();
   6578    if (!DumpAST<char16_t>(cx, options, chars, length, compilationState,
   6579                           goal)) {
   6580      return false;
   6581    }
   6582  }
   6583  args.rval().setUndefined();
   6584  return true;
   6585 }
   6586 
   6587 static bool DumpStencil(JSContext* cx, unsigned argc, Value* vp) {
   6588  return FrontendTest(cx, argc, vp, "dumpStencil", DumpType::Stencil);
   6589 }
   6590 
   6591 static bool Parse(JSContext* cx, unsigned argc, Value* vp) {
   6592  // Parse returns local scope information with variables ordered
   6593  // differently, depending on the underlying JIT implementation.
   6594  if (js::SupportDifferentialTesting()) {
   6595    JS_ReportErrorASCII(cx,
   6596                        "Function not available in differential testing mode.");
   6597    return false;
   6598  }
   6599 
   6600  return FrontendTest(cx, argc, vp, "parse", DumpType::ParseNode);
   6601 }
   6602 
   6603 static bool SyntaxParse(JSContext* cx, unsigned argc, Value* vp) {
   6604  using namespace js::frontend;
   6605 
   6606  CallArgs args = CallArgsFromVp(argc, vp);
   6607 
   6608  if (!args.requireAtLeast(cx, "syntaxParse", 1)) {
   6609    return false;
   6610  }
   6611  if (!args[0].isString()) {
   6612    const char* typeName = InformalValueTypeName(args[0]);
   6613    JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
   6614    return false;
   6615  }
   6616 
   6617  JSString* scriptContents = args[0].toString();
   6618 
   6619  CompileOptions options(cx);
   6620  options.setIntroductionType("js shell syntaxParse")
   6621      .setFileAndLine("<string>", 1);
   6622 
   6623  AutoStableStringChars stableChars(cx);
   6624  if (!stableChars.initTwoByte(cx, scriptContents)) {
   6625    return false;
   6626  }
   6627 
   6628  const char16_t* chars = stableChars.twoByteRange().begin().get();
   6629  size_t length = scriptContents->length();
   6630 
   6631  AutoReportFrontendContext fc(cx);
   6632  Rooted<frontend::CompilationInput> input(cx,
   6633                                           frontend::CompilationInput(options));
   6634  if (!input.get().initForGlobal(&fc)) {
   6635    return false;
   6636  }
   6637 
   6638  LifoAllocScope allocScope(&cx->tempLifoAlloc());
   6639  frontend::NoScopeBindingCache scopeCache;
   6640  frontend::CompilationState compilationState(&fc, allocScope, input.get());
   6641  if (!compilationState.init(&fc, &scopeCache)) {
   6642    return false;
   6643  }
   6644 
   6645  Parser<frontend::SyntaxParseHandler, char16_t> parser(
   6646      &fc, options, chars, length, compilationState,
   6647      /* syntaxParser = */ nullptr);
   6648  if (!parser.checkOptions()) {
   6649    return false;
   6650  }
   6651 
   6652  bool succeeded = parser.parse().isOk();
   6653  if (fc.hadErrors()) {
   6654    return false;
   6655  }
   6656 
   6657  if (!succeeded && !parser.hadAbortedSyntaxParse()) {
   6658    // If no exception is posted, either there was an OOM or a language
   6659    // feature unhandled by the syntax parser was encountered.
   6660    MOZ_ASSERT(fc.hadOutOfMemory());
   6661    return false;
   6662  }
   6663 
   6664  args.rval().setBoolean(succeeded);
   6665  return true;
   6666 }
   6667 
   6668 static bool OffThreadCompileToStencil(JSContext* cx, unsigned argc, Value* vp) {
   6669  if (!CanUseExtraThreads()) {
   6670    JS_ReportErrorASCII(
   6671        cx, "Can't use offThreadCompileToStencil with --no-threads");
   6672    return false;
   6673  }
   6674 
   6675  CallArgs args = CallArgsFromVp(argc, vp);
   6676 
   6677  if (!args.requireAtLeast(cx, "offThreadCompileToStencil", 1)) {
   6678    return false;
   6679  }
   6680  if (!args[0].isString()) {
   6681    const char* typeName = InformalValueTypeName(args[0]);
   6682    JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
   6683    return false;
   6684  }
   6685 
   6686  UniqueChars fileNameBytes;
   6687  CompileOptions options(cx);
   6688  options.setIntroductionType("js shell offThreadCompileToStencil")
   6689      .setFileAndLine("<string>", 1);
   6690 
   6691  if (args.length() >= 2) {
   6692    if (!args[1].isObject()) {
   6693      JS_ReportErrorASCII(
   6694          cx, "offThreadCompileToStencil: The 2nd argument must be an object");
   6695      return false;
   6696    }
   6697 
   6698    // Offthread compilation requires that the debug metadata be set when the
   6699    // script is collected from offthread, rather than when compiled.
   6700    RootedObject opts(cx, &args[1].toObject());
   6701    if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
   6702      return false;
   6703    }
   6704  }
   6705 
   6706  // This option setting must override whatever the caller requested.
   6707  options.setIsRunOnce(true);
   6708 
   6709  JSString* scriptContents = args[0].toString();
   6710  AutoStableStringChars stableChars(cx);
   6711  if (!stableChars.initTwoByte(cx, scriptContents)) {
   6712    return false;
   6713  }
   6714 
   6715  size_t length = scriptContents->length();
   6716  const char16_t* chars = stableChars.twoByteChars();
   6717 
   6718  // Make sure we own the string's chars, so that they are not freed before
   6719  // the compilation is finished.
   6720  UniqueTwoByteChars ownedChars;
   6721  if (stableChars.maybeGiveOwnershipToCaller()) {
   6722    ownedChars.reset(const_cast<char16_t*>(chars));
   6723  } else {
   6724    ownedChars.reset(cx->pod_malloc<char16_t>(length));
   6725    if (!ownedChars) {
   6726      return false;
   6727    }
   6728 
   6729    mozilla::PodCopy(ownedChars.get(), chars, length);
   6730  }
   6731 
   6732  if (!js::CanUseExtraThreads()) {
   6733    JS_ReportErrorASCII(cx, "cannot compile code on helper thread");
   6734    return false;
   6735  }
   6736 
   6737  JS::SourceText<char16_t> srcBuf;
   6738  if (!srcBuf.init(cx, std::move(ownedChars), length)) {
   6739    return false;
   6740  }
   6741 
   6742  OffThreadJob* job = NewOffThreadJob(cx, OffThreadJob::Kind::CompileScript,
   6743                                      options, std::move(srcBuf));
   6744  if (!job) {
   6745    return false;
   6746  }
   6747 
   6748  if (!job->dispatch()) {
   6749    ReportOutOfMemory(cx);
   6750    DeleteOffThreadJob(cx, job);
   6751    return false;
   6752  }
   6753 
   6754  args.rval().setInt32(job->id);
   6755  return true;
   6756 }
   6757 
   6758 static bool FinishOffThreadStencil(JSContext* cx, unsigned argc, Value* vp) {
   6759  CallArgs args = CallArgsFromVp(argc, vp);
   6760 
   6761  OffThreadJob* job = LookupOffThreadJobForArgs(cx, args, 0);
   6762  if (!job) {
   6763    return false;
   6764  }
   6765 
   6766  job->waitUntilDone();
   6767 
   6768  RefPtr<JS::Stencil> stencil = job->stealStencil(cx);
   6769  DeleteOffThreadJob(cx, job);
   6770  if (!stencil) {
   6771    return false;
   6772  }
   6773  RootedObject stencilObj(cx,
   6774                          js::StencilObject::create(cx, std::move(stencil)));
   6775  if (!stencilObj) {
   6776    return false;
   6777  }
   6778 
   6779  args.rval().setObject(*stencilObj);
   6780  return true;
   6781 }
   6782 
   6783 static bool OffThreadCompileModuleToStencil(JSContext* cx, unsigned argc,
   6784                                            Value* vp) {
   6785  CallArgs args = CallArgsFromVp(argc, vp);
   6786 
   6787  if (!args.requireAtLeast(cx, "offThreadCompileModuleToStencil", 1)) {
   6788    return false;
   6789  }
   6790  if (!args[0].isString()) {
   6791    const char* typeName = InformalValueTypeName(args[0]);
   6792    JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
   6793    return false;
   6794  }
   6795 
   6796  UniqueChars fileNameBytes;
   6797  CompileOptions options(cx);
   6798  options.setIntroductionType("js shell offThreadCompileModuleToStencil")
   6799      .setFileAndLine("<string>", 1);
   6800 
   6801  if (args.length() >= 2) {
   6802    if (!args[1].isObject()) {
   6803      JS_ReportErrorASCII(cx,
   6804                          "offThreadCompileModuleToStencil: The 2nd argument "
   6805                          "must be an object");
   6806      return false;
   6807    }
   6808 
   6809    // Offthread compilation requires that the debug metadata be set when the
   6810    // script is collected from offthread, rather than when compiled.
   6811    RootedObject opts(cx, &args[1].toObject());
   6812    if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
   6813      return false;
   6814    }
   6815 
   6816    if (!ValidateModuleCompileOptions(cx, options)) {
   6817      return false;
   6818    }
   6819  }
   6820 
   6821  options.setIsRunOnce(true).setSourceIsLazy(false);
   6822 
   6823  JSString* scriptContents = args[0].toString();
   6824  AutoStableStringChars stableChars(cx);
   6825  if (!stableChars.initTwoByte(cx, scriptContents)) {
   6826    return false;
   6827  }
   6828 
   6829  size_t length = scriptContents->length();
   6830  const char16_t* chars = stableChars.twoByteChars();
   6831 
   6832  // Make sure we own the string's chars, so that they are not freed before
   6833  // the compilation is finished.
   6834  UniqueTwoByteChars ownedChars;
   6835  if (stableChars.maybeGiveOwnershipToCaller()) {
   6836    ownedChars.reset(const_cast<char16_t*>(chars));
   6837  } else {
   6838    ownedChars.reset(cx->pod_malloc<char16_t>(length));
   6839    if (!ownedChars) {
   6840      return false;
   6841    }
   6842 
   6843    mozilla::PodCopy(ownedChars.get(), chars, length);
   6844  }
   6845 
   6846  if (!js::CanUseExtraThreads()) {
   6847    JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
   6848    return false;
   6849  }
   6850 
   6851  JS::SourceText<char16_t> srcBuf;
   6852  if (!srcBuf.init(cx, std::move(ownedChars), length)) {
   6853    return false;
   6854  }
   6855 
   6856  OffThreadJob* job = NewOffThreadJob(cx, OffThreadJob::Kind::CompileModule,
   6857                                      options, std::move(srcBuf));
   6858  if (!job) {
   6859    return false;
   6860  }
   6861 
   6862  if (!job->dispatch()) {
   6863    ReportOutOfMemory(cx);
   6864    DeleteOffThreadJob(cx, job);
   6865    return false;
   6866  }
   6867 
   6868  args.rval().setInt32(job->id);
   6869  return true;
   6870 }
   6871 
   6872 static bool OffThreadDecodeStencil(JSContext* cx, unsigned argc, Value* vp) {
   6873  if (!CanUseExtraThreads()) {
   6874    JS_ReportErrorASCII(cx,
   6875                        "Can't use offThreadDecodeStencil with --no-threads");
   6876    return false;
   6877  }
   6878 
   6879  CallArgs args = CallArgsFromVp(argc, vp);
   6880 
   6881  if (!args.requireAtLeast(cx, "offThreadDecodeStencil", 1)) {
   6882    return false;
   6883  }
   6884  if (!args[0].isObject() || !CacheEntry_isCacheEntry(&args[0].toObject())) {
   6885    const char* typeName = InformalValueTypeName(args[0]);
   6886    JS_ReportErrorASCII(cx, "expected cache entry, got %s", typeName);
   6887    return false;
   6888  }
   6889  RootedObject cacheEntry(cx, &args[0].toObject());
   6890 
   6891  UniqueChars fileNameBytes;
   6892  CompileOptions options(cx);
   6893  options.setIntroductionType("js shell offThreadDecodeStencil")
   6894      .setFileAndLine("<string>", 1);
   6895 
   6896  if (args.length() >= 2) {
   6897    if (!args[1].isObject()) {
   6898      JS_ReportErrorASCII(
   6899          cx, "offThreadDecodeStencil: The 2nd argument must be an object");
   6900      return false;
   6901    }
   6902 
   6903    RootedObject opts(cx, &args[1].toObject());
   6904    if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
   6905      return false;
   6906    }
   6907  }
   6908 
   6909  // This option setting must override whatever the caller requested, and
   6910  // this should match `Evaluate` that encodes the script.
   6911  options.setIsRunOnce(false);
   6912 
   6913  JS::TranscodeBuffer loadBuffer;
   6914  size_t loadLength = 0;
   6915  uint8_t* loadData = nullptr;
   6916  loadData = CacheEntry_getBytecode(cx, cacheEntry, &loadLength);
   6917  if (!loadData) {
   6918    return false;
   6919  }
   6920  if (!loadBuffer.append(loadData, loadLength)) {
   6921    JS_ReportOutOfMemory(cx);
   6922    return false;
   6923  }
   6924 
   6925  if (!js::CanUseExtraThreads()) {
   6926    JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
   6927    return false;
   6928  }
   6929 
   6930  OffThreadJob* job = NewOffThreadJob(cx, OffThreadJob::Kind::Decode, options,
   6931                                      std::move(loadBuffer));
   6932  if (!job) {
   6933    return false;
   6934  }
   6935 
   6936  if (!job->dispatch()) {
   6937    ReportOutOfMemory(cx);
   6938    DeleteOffThreadJob(cx, job);
   6939    return false;
   6940  }
   6941 
   6942  args.rval().setInt32(job->id);
   6943  return true;
   6944 }
   6945 
   6946 class AutoCStringVector {
   6947  Vector<char*> argv_;
   6948 
   6949 public:
   6950  explicit AutoCStringVector(JSContext* cx) : argv_(cx) {}
   6951  ~AutoCStringVector() {
   6952    for (size_t i = 0; i < argv_.length(); i++) {
   6953      js_free(argv_[i]);
   6954    }
   6955  }
   6956  bool append(UniqueChars&& arg) {
   6957    if (!argv_.append(arg.get())) {
   6958      return false;
   6959    }
   6960 
   6961    // Now owned by this vector.
   6962    (void)arg.release();
   6963    return true;
   6964  }
   6965  char* const* get() const { return argv_.begin(); }
   6966  size_t length() const { return argv_.length(); }
   6967  char* operator[](size_t i) const { return argv_[i]; }
   6968  void replace(size_t i, UniqueChars arg) {
   6969    js_free(argv_[i]);
   6970    argv_[i] = arg.release();
   6971  }
   6972 };
   6973 
   6974 #if defined(XP_WIN)
   6975 static bool EscapeForShell(JSContext* cx, AutoCStringVector& argv) {
   6976  // Windows will break arguments in argv by various spaces, so we wrap each
   6977  // argument in quotes and escape quotes within. Even with quotes, \ will be
   6978  // treated like an escape character, so inflate each \ to \\.
   6979 
   6980  for (size_t i = 0; i < argv.length(); i++) {
   6981    if (!argv[i]) {
   6982      continue;
   6983    }
   6984 
   6985    size_t newLen = 3;  // quotes before and after and null-terminator
   6986    for (char* p = argv[i]; *p; p++) {
   6987      newLen++;
   6988      if (*p == '\"' || *p == '\\') {
   6989        newLen++;
   6990      }
   6991    }
   6992 
   6993    auto escaped = cx->make_pod_array<char>(newLen);
   6994    if (!escaped) {
   6995      return false;
   6996    }
   6997 
   6998    char* src = argv[i];
   6999    char* dst = escaped.get();
   7000    *dst++ = '\"';
   7001    while (*src) {
   7002      if (*src == '\"' || *src == '\\') {
   7003        *dst++ = '\\';
   7004      }
   7005      *dst++ = *src++;
   7006    }
   7007    *dst++ = '\"';
   7008    *dst++ = '\0';
   7009    MOZ_ASSERT(escaped.get() + newLen == dst);
   7010 
   7011    argv.replace(i, std::move(escaped));
   7012  }
   7013  return true;
   7014 }
   7015 #endif
   7016 
   7017 #ifndef __wasi__
   7018 static bool ReadAll(int fd, wasm::Bytes* bytes) {
   7019  size_t lastLength = bytes->length();
   7020  while (true) {
   7021    static const int ChunkSize = 64 * 1024;
   7022    if (!bytes->growBy(ChunkSize)) {
   7023      return false;
   7024    }
   7025 
   7026    intptr_t readCount;
   7027    while (true) {
   7028      readCount = read(fd, bytes->begin() + lastLength, ChunkSize);
   7029      if (readCount >= 0) {
   7030        break;
   7031      }
   7032      if (errno != EINTR) {
   7033        return false;
   7034      }
   7035    }
   7036 
   7037    if (readCount < ChunkSize) {
   7038      bytes->shrinkTo(lastLength + readCount);
   7039      if (readCount == 0) {
   7040        return true;
   7041      }
   7042    }
   7043 
   7044    lastLength = bytes->length();
   7045  }
   7046 }
   7047 
   7048 static bool WriteAll(int fd, const uint8_t* bytes, size_t length) {
   7049  while (length > 0) {
   7050    int written = write(fd, bytes, length);
   7051    if (written < 0) {
   7052      if (errno == EINTR) {
   7053        continue;
   7054      }
   7055      return false;
   7056    }
   7057    MOZ_ASSERT(unsigned(written) <= length);
   7058    length -= written;
   7059    bytes += written;
   7060  }
   7061 
   7062  return true;
   7063 }
   7064 
   7065 class AutoPipe {
   7066  int fds_[2];
   7067 
   7068 public:
   7069  AutoPipe() {
   7070    fds_[0] = -1;
   7071    fds_[1] = -1;
   7072  }
   7073 
   7074  ~AutoPipe() {
   7075    if (fds_[0] != -1) {
   7076      close(fds_[0]);
   7077    }
   7078    if (fds_[1] != -1) {
   7079      close(fds_[1]);
   7080    }
   7081  }
   7082 
   7083  bool init() {
   7084 #  ifdef XP_WIN
   7085    return !_pipe(fds_, 4096, O_BINARY);
   7086 #  else
   7087    return !pipe(fds_);
   7088 #  endif
   7089  }
   7090 
   7091  int reader() const {
   7092    MOZ_ASSERT(fds_[0] != -1);
   7093    return fds_[0];
   7094  }
   7095 
   7096  int writer() const {
   7097    MOZ_ASSERT(fds_[1] != -1);
   7098    return fds_[1];
   7099  }
   7100 
   7101  void closeReader() {
   7102    MOZ_ASSERT(fds_[0] != -1);
   7103    close(fds_[0]);
   7104    fds_[0] = -1;
   7105  }
   7106 
   7107  void closeWriter() {
   7108    MOZ_ASSERT(fds_[1] != -1);
   7109    close(fds_[1]);
   7110    fds_[1] = -1;
   7111  }
   7112 };
   7113 #endif  // __wasi__
   7114 
   7115 int shell::sArgc;
   7116 char** shell::sArgv;
   7117 
   7118 #ifndef __wasi__
   7119 static const char sWasmCompileAndSerializeFlag[] =
   7120    "--wasm-compile-and-serialize";
   7121 MOZ_RUNINIT static Vector<const char*, 5, js::SystemAllocPolicy>
   7122    sCompilerProcessFlags;
   7123 
   7124 static bool CompileAndSerializeInSeparateProcess(JSContext* cx,
   7125                                                 const uint8_t* bytecode,
   7126                                                 size_t bytecodeLength,
   7127                                                 wasm::Bytes* serialized) {
   7128  AutoPipe stdIn, stdOut;
   7129  if (!stdIn.init() || !stdOut.init()) {
   7130    return false;
   7131  }
   7132 
   7133  AutoCStringVector argv(cx);
   7134 
   7135  UniqueChars argv0 = DuplicateString(cx, sArgv[0]);
   7136  if (!argv0 || !argv.append(std::move(argv0))) {
   7137    return false;
   7138  }
   7139 
   7140  // Put compiler flags first since they must precede the non-option
   7141  // file-descriptor args (passed on Windows, below).
   7142  for (unsigned i = 0; i < sCompilerProcessFlags.length(); i++) {
   7143    UniqueChars flags = DuplicateString(cx, sCompilerProcessFlags[i]);
   7144    if (!flags || !argv.append(std::move(flags))) {
   7145      return false;
   7146    }
   7147  }
   7148 
   7149  UniqueChars arg;
   7150 
   7151  arg = DuplicateString(sWasmCompileAndSerializeFlag);
   7152  if (!arg || !argv.append(std::move(arg))) {
   7153    return false;
   7154  }
   7155 
   7156 #  ifdef XP_WIN
   7157  // The spawned process will have all the stdIn/stdOut file handles open, but
   7158  // without the power of fork, we need some other way to communicate the
   7159  // integer fd values so we encode them in argv and WasmCompileAndSerialize()
   7160  // has a matching #ifdef XP_WIN to parse them out. Communicate both ends of
   7161  // both pipes so the child process can closed the unused ends.
   7162 
   7163  arg = JS_smprintf("%d", stdIn.reader());
   7164  if (!arg || !argv.append(std::move(arg))) {
   7165    return false;
   7166  }
   7167 
   7168  arg = JS_smprintf("%d", stdIn.writer());
   7169  if (!arg || !argv.append(std::move(arg))) {
   7170    return false;
   7171  }
   7172 
   7173  arg = JS_smprintf("%d", stdOut.reader());
   7174  if (!arg || !argv.append(std::move(arg))) {
   7175    return false;
   7176  }
   7177 
   7178  arg = JS_smprintf("%d", stdOut.writer());
   7179  if (!arg || !argv.append(std::move(arg))) {
   7180    return false;
   7181  }
   7182 #  endif
   7183 
   7184  // Required by both _spawnv and exec.
   7185  if (!argv.append(nullptr)) {
   7186    return false;
   7187  }
   7188 
   7189 #  ifdef XP_WIN
   7190  if (!EscapeForShell(cx, argv)) {
   7191    return false;
   7192  }
   7193 
   7194  int childPid = _spawnv(P_NOWAIT, sArgv[0], argv.get());
   7195  if (childPid == -1) {
   7196    return false;
   7197  }
   7198 #  else
   7199  pid_t childPid = fork();
   7200  switch (childPid) {
   7201    case -1:
   7202      return false;
   7203    case 0:
   7204      // In the child process. Redirect stdin/stdout to the respective ends of
   7205      // the pipes. Closing stdIn.writer() is necessary for stdin to hit EOF.
   7206      // This case statement must not return before exec() takes over. Rather,
   7207      // exit(-1) is used to return failure to the parent process.
   7208      if (dup2(stdIn.reader(), STDIN_FILENO) == -1) {
   7209        exit(-1);
   7210      }
   7211      if (dup2(stdOut.writer(), STDOUT_FILENO) == -1) {
   7212        exit(-1);
   7213      }
   7214      close(stdIn.reader());
   7215      close(stdIn.writer());
   7216      close(stdOut.reader());
   7217      close(stdOut.writer());
   7218      execv(sArgv[0], argv.get());
   7219      exit(-1);
   7220  }
   7221 #  endif
   7222 
   7223  // In the parent process. Closing stdOut.writer() is necessary for
   7224  // stdOut.reader() below to hit EOF.
   7225  stdIn.closeReader();
   7226  stdOut.closeWriter();
   7227 
   7228  if (!WriteAll(stdIn.writer(), bytecode, bytecodeLength)) {
   7229    return false;
   7230  }
   7231 
   7232  stdIn.closeWriter();
   7233 
   7234  if (!ReadAll(stdOut.reader(), serialized)) {
   7235    return false;
   7236  }
   7237 
   7238  stdOut.closeReader();
   7239 
   7240  int status;
   7241 #  ifdef XP_WIN
   7242  if (_cwait(&status, childPid, WAIT_CHILD) == -1) {
   7243    return false;
   7244  }
   7245 #  else
   7246  while (true) {
   7247    if (waitpid(childPid, &status, 0) >= 0) {
   7248      break;
   7249    }
   7250    if (errno != EINTR) {
   7251      return false;
   7252    }
   7253  }
   7254 #  endif
   7255 
   7256  return status == 0;
   7257 }
   7258 
   7259 static bool WasmCompileAndSerialize(JSContext* cx) {
   7260  MOZ_ASSERT(wasm::CodeCachingAvailable(cx));
   7261 
   7262 #  ifdef XP_WIN
   7263  // See CompileAndSerializeInSeparateProcess for why we've had to smuggle
   7264  // these fd values through argv. Closing the writing ends is necessary for
   7265  // the reading ends to hit EOF.
   7266  int flagIndex = 0;
   7267  for (; flagIndex < sArgc; flagIndex++) {
   7268    if (!strcmp(sArgv[flagIndex], sWasmCompileAndSerializeFlag)) {
   7269      break;
   7270    }
   7271  }
   7272  MOZ_RELEASE_ASSERT(flagIndex < sArgc);
   7273 
   7274  int fdsIndex = flagIndex + 1;
   7275  MOZ_RELEASE_ASSERT(fdsIndex + 4 == sArgc);
   7276 
   7277  int stdInReader = atoi(sArgv[fdsIndex + 0]);
   7278  int stdInWriter = atoi(sArgv[fdsIndex + 1]);
   7279  int stdOutReader = atoi(sArgv[fdsIndex + 2]);
   7280  int stdOutWriter = atoi(sArgv[fdsIndex + 3]);
   7281 
   7282  int stdIn = stdInReader;
   7283  close(stdInWriter);
   7284  close(stdOutReader);
   7285  int stdOut = stdOutWriter;
   7286 #  else
   7287  int stdIn = STDIN_FILENO;
   7288  int stdOut = STDOUT_FILENO;
   7289 #  endif
   7290 
   7291  wasm::MutableBytes bytecode = js_new<wasm::ShareableBytes>();
   7292  if (!ReadAll(stdIn, &bytecode->vector)) {
   7293    return false;
   7294  }
   7295 
   7296  wasm::BytecodeSource bytecodeSource(bytecode->begin(), bytecode->length());
   7297  wasm::Bytes serialized;
   7298  if (!wasm::CompileAndSerialize(cx, bytecodeSource, &serialized)) {
   7299    return false;
   7300  }
   7301 
   7302  if (!WriteAll(stdOut, serialized.begin(), serialized.length())) {
   7303    return false;
   7304  }
   7305 
   7306  return true;
   7307 }
   7308 
   7309 static bool WasmCompileInSeparateProcess(JSContext* cx, unsigned argc,
   7310                                         Value* vp) {
   7311  if (!wasm::CodeCachingAvailable(cx)) {
   7312    JS_ReportErrorASCII(cx, "WebAssembly caching not supported");
   7313    return false;
   7314  }
   7315 
   7316  CallArgs args = CallArgsFromVp(argc, vp);
   7317  if (!args.requireAtLeast(cx, "wasmCompileInSeparateProcess", 1)) {
   7318    return false;
   7319  }
   7320 
   7321  SharedMem<uint8_t*> bytecode;
   7322  size_t numBytes;
   7323  if (!args[0].isObject() ||
   7324      !IsBufferSource(cx, &args[0].toObject(), /*allowShared*/ false,
   7325                      /*allowResizable*/ false, &bytecode, &numBytes)) {
   7326    RootedObject callee(cx, &args.callee());
   7327    ReportUsageErrorASCII(cx, callee, "Argument must be a buffer source");
   7328    return false;
   7329  }
   7330 
   7331  wasm::Bytes serialized;
   7332  if (!CompileAndSerializeInSeparateProcess(cx, bytecode.unwrap(), numBytes,
   7333                                            &serialized)) {
   7334    if (!cx->isExceptionPending()) {
   7335      JS_ReportErrorASCII(cx, "creating and executing child process");
   7336    }
   7337    return false;
   7338  }
   7339 
   7340  RootedObject module(cx);
   7341  if (!wasm::DeserializeModule(cx, serialized, &module)) {
   7342    return false;
   7343  }
   7344 
   7345  args.rval().setObject(*module);
   7346  return true;
   7347 }
   7348 #endif  // __wasi__
   7349 
   7350 static bool DecompileFunction(JSContext* cx, unsigned argc, Value* vp) {
   7351  CallArgs args = CallArgsFromVp(argc, vp);
   7352  if (args.length() < 1 || !args[0].isObject() ||
   7353      !args[0].toObject().is<JSFunction>()) {
   7354    args.rval().setUndefined();
   7355    return true;
   7356  }
   7357  RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
   7358  JSString* result = JS_DecompileFunction(cx, fun);
   7359  if (!result) {
   7360    return false;
   7361  }
   7362  args.rval().setString(result);
   7363  return true;
   7364 }
   7365 
   7366 static bool DecompileThisScript(JSContext* cx, unsigned argc, Value* vp) {
   7367  CallArgs args = CallArgsFromVp(argc, vp);
   7368 
   7369  NonBuiltinScriptFrameIter iter(cx);
   7370  if (iter.done()) {
   7371    args.rval().setString(cx->runtime()->emptyString);
   7372    return true;
   7373  }
   7374 
   7375  {
   7376    JSAutoRealm ar(cx, iter.script());
   7377 
   7378    RootedScript script(cx, iter.script());
   7379    JSString* result = JS_DecompileScript(cx, script);
   7380    if (!result) {
   7381      return false;
   7382    }
   7383 
   7384    args.rval().setString(result);
   7385  }
   7386 
   7387  return JS_WrapValue(cx, args.rval());
   7388 }
   7389 
   7390 static bool ValueToSource(JSContext* cx, unsigned argc, Value* vp) {
   7391  CallArgs args = CallArgsFromVp(argc, vp);
   7392 
   7393  JSString* str = ValueToSource(cx, args.get(0));
   7394  if (!str) {
   7395    return false;
   7396  }
   7397 
   7398  args.rval().setString(str);
   7399  return true;
   7400 }
   7401 
   7402 static bool ThisFilename(JSContext* cx, unsigned argc, Value* vp) {
   7403  CallArgs args = CallArgsFromVp(argc, vp);
   7404 
   7405  JS::AutoFilename filename;
   7406  if (!DescribeScriptedCaller(&filename, cx) || !filename.get()) {
   7407    args.rval().setString(cx->runtime()->emptyString);
   7408    return true;
   7409  }
   7410 
   7411  JSString* str = NewStringCopyUTF8(cx, filename.get());
   7412  if (!str) {
   7413    return false;
   7414  }
   7415 
   7416  args.rval().setString(str);
   7417  return true;
   7418 }
   7419 
   7420 static bool WrapWithProto(JSContext* cx, unsigned argc, Value* vp) {
   7421  CallArgs args = CallArgsFromVp(argc, vp);
   7422  Value obj = args.get(0);
   7423  Value proto = args.get(1);
   7424  if (!obj.isObject() || !proto.isObjectOrNull()) {
   7425    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   7426                              JSSMSG_INVALID_ARGS, "wrapWithProto");
   7427    return false;
   7428  }
   7429 
   7430  // Disallow constructing (deeply) nested wrapper chains, to avoid running
   7431  // out of stack space in isCallable/isConstructor. See bug 1126105.
   7432  if (IsWrapper(&obj.toObject())) {
   7433    JS_ReportErrorASCII(cx, "wrapWithProto cannot wrap a wrapper");
   7434    return false;
   7435  }
   7436 
   7437  WrapperOptions options(cx);
   7438  options.setProto(proto.toObjectOrNull());
   7439  JSObject* wrapped = Wrapper::New(cx, &obj.toObject(),
   7440                                   &Wrapper::singletonWithPrototype, options);
   7441  if (!wrapped) {
   7442    return false;
   7443  }
   7444 
   7445  args.rval().setObject(*wrapped);
   7446  return true;
   7447 }
   7448 
   7449 static bool NewGlobal(JSContext* cx, unsigned argc, Value* vp) {
   7450  CallArgs args = CallArgsFromVp(argc, vp);
   7451  RootedObject callee(cx, &args.callee());
   7452 
   7453  JS::RealmOptions options;
   7454  JS::RealmCreationOptions& creationOptions = options.creationOptions();
   7455  JS::RealmBehaviors& behaviors = options.behaviors();
   7456  ShellGlobalKind kind = ShellGlobalKind::WindowProxy;
   7457  bool immutablePrototype = true;
   7458 
   7459  SetStandardRealmOptions(options);
   7460 
   7461  // Default to creating the global in the current compartment unless
   7462  // --more-compartments is used.
   7463  if (defaultToSameCompartment) {
   7464    creationOptions.setExistingCompartment(cx->global());
   7465  } else {
   7466    creationOptions.setNewCompartmentAndZone();
   7467  }
   7468 
   7469  // Ensure the target compartment/zone is kept alive when sameCompartmentAs or
   7470  // sameZoneAs is used.
   7471  Rooted<JSObject*> compartmentRoot(cx);
   7472 
   7473  JS::AutoHoldPrincipals principals(cx);
   7474 
   7475  if (args.length() == 1 && args[0].isObject()) {
   7476    RootedObject opts(cx, &args[0].toObject());
   7477    RootedValue v(cx);
   7478 
   7479    if (!JS_GetProperty(cx, opts, "invisibleToDebugger", &v)) {
   7480      return false;
   7481    }
   7482    if (v.isBoolean()) {
   7483      creationOptions.setInvisibleToDebugger(v.toBoolean());
   7484    }
   7485 
   7486    if (!JS_GetProperty(cx, opts, "sameZoneAs", &v)) {
   7487      return false;
   7488    }
   7489    if (v.isObject()) {
   7490      compartmentRoot = UncheckedUnwrap(&v.toObject());
   7491      creationOptions.setNewCompartmentInExistingZone(compartmentRoot);
   7492    }
   7493 
   7494    if (!JS_GetProperty(cx, opts, "sameCompartmentAs", &v)) {
   7495      return false;
   7496    }
   7497    if (v.isObject()) {
   7498      compartmentRoot = UncheckedUnwrap(&v.toObject());
   7499      creationOptions.setExistingCompartment(compartmentRoot);
   7500    }
   7501 
   7502    if (!JS_GetProperty(cx, opts, "newCompartment", &v)) {
   7503      return false;
   7504    }
   7505    if (v.isBoolean()) {
   7506      if (v.toBoolean()) {
   7507        creationOptions.setNewCompartmentAndZone();
   7508      } else {
   7509        creationOptions.setExistingCompartment(cx->global());
   7510      }
   7511    }
   7512 
   7513    if (!JS_GetProperty(cx, opts, "discardSource", &v)) {
   7514      return false;
   7515    }
   7516    if (v.isBoolean()) {
   7517      behaviors.setDiscardSource(v.toBoolean());
   7518    }
   7519 
   7520    if (!JS_GetProperty(cx, opts, "useWindowProxy", &v)) {
   7521      return false;
   7522    }
   7523    if (v.isBoolean()) {
   7524      kind = v.toBoolean() ? ShellGlobalKind::WindowProxy
   7525                           : ShellGlobalKind::GlobalObject;
   7526    }
   7527 
   7528    if (!JS_GetProperty(cx, opts, "immutablePrototype", &v)) {
   7529      return false;
   7530    }
   7531    if (v.isBoolean()) {
   7532      immutablePrototype = v.toBoolean();
   7533    }
   7534 
   7535    if (!JS_GetProperty(cx, opts, "systemPrincipal", &v)) {
   7536      return false;
   7537    }
   7538    if (v.isBoolean()) {
   7539      principals.reset(&ShellPrincipals::fullyTrusted);
   7540    }
   7541 
   7542    if (!JS_GetProperty(cx, opts, "principal", &v)) {
   7543      return false;
   7544    }
   7545    if (!v.isUndefined()) {
   7546      uint32_t bits;
   7547      if (!ToUint32(cx, v, &bits)) {
   7548        return false;
   7549      }
   7550      JSPrincipals* newPrincipals = cx->new_<ShellPrincipals>(bits);
   7551      if (!newPrincipals) {
   7552        return false;
   7553      }
   7554      principals.reset(newPrincipals);
   7555    }
   7556 
   7557    if (!JS_GetProperty(cx, opts, "enableCoopAndCoep", &v)) {
   7558      return false;
   7559    }
   7560    if (v.isBoolean()) {
   7561      creationOptions.setCoopAndCoepEnabled(v.toBoolean());
   7562    }
   7563 
   7564    if (!JS_GetProperty(cx, opts, "freezeBuiltins", &v)) {
   7565      return false;
   7566    }
   7567    if (v.isBoolean()) {
   7568      creationOptions.setFreezeBuiltins(v.toBoolean());
   7569    }
   7570 
   7571    // On the web, the SharedArrayBuffer constructor is not installed as a
   7572    // global property in pages that aren't isolated in a separate process (and
   7573    // thus can't allow the structured cloning of shared memory).  Specify false
   7574    // for this option to reproduce this behavior.
   7575    if (!JS_GetProperty(cx, opts, "defineSharedArrayBufferConstructor", &v)) {
   7576      return false;
   7577    }
   7578    if (v.isBoolean()) {
   7579      creationOptions.setDefineSharedArrayBufferConstructor(v.toBoolean());
   7580    }
   7581 
   7582    if (!JS_GetProperty(cx, opts, "timeZone", &v)) {
   7583      return false;
   7584    }
   7585    if (v.isString()) {
   7586      RootedString str(cx, v.toString());
   7587      UniqueChars timeZone =
   7588          StringToTimeZone(cx, callee, str, AllowTimeZoneLink::No);
   7589      if (!timeZone) {
   7590        return false;
   7591      }
   7592      behaviors.setTimeZoneOverride(timeZone.get());
   7593    }
   7594 
   7595    if (!JS_GetProperty(cx, opts, "alwaysUseFdlibm", &v)) {
   7596      return false;
   7597    }
   7598    if (v.isBoolean()) {
   7599      creationOptions.setAlwaysUseFdlibm(v.toBoolean());
   7600    }
   7601 
   7602    if (!JS_GetProperty(cx, opts, "locale", &v)) {
   7603      return false;
   7604    }
   7605    if (v.isString()) {
   7606      RootedString str(cx, v.toString());
   7607      UniqueChars locale = StringToLocale(cx, callee, str);
   7608      if (!locale) {
   7609        return false;
   7610      }
   7611      behaviors.setLocaleOverride(locale.get());
   7612    }
   7613  }
   7614 
   7615  if (!CheckRealmOptions(cx, options, principals.get())) {
   7616    return false;
   7617  }
   7618 
   7619  RootedObject global(cx, NewGlobalObject(cx, options, principals.get(), kind,
   7620                                          immutablePrototype));
   7621  if (!global) {
   7622    return false;
   7623  }
   7624 
   7625  RootedObject wrapped(cx, ToWindowProxyIfWindow(global));
   7626  if (!JS_WrapObject(cx, &wrapped)) {
   7627    return false;
   7628  }
   7629 
   7630  args.rval().setObject(*wrapped);
   7631  return true;
   7632 }
   7633 
   7634 static bool NukeAllCCWs(JSContext* cx, unsigned argc, Value* vp) {
   7635  CallArgs args = CallArgsFromVp(argc, vp);
   7636 
   7637  if (args.length() != 0) {
   7638    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   7639                              JSSMSG_INVALID_ARGS, "nukeAllCCWs");
   7640    return false;
   7641  }
   7642 
   7643  NukeCrossCompartmentWrappers(cx, AllCompartments(), cx->realm(),
   7644                               NukeWindowReferences, NukeAllReferences);
   7645  args.rval().setUndefined();
   7646  return true;
   7647 }
   7648 
   7649 static bool RecomputeWrappers(JSContext* cx, unsigned argc, Value* vp) {
   7650  CallArgs args = CallArgsFromVp(argc, vp);
   7651 
   7652  if (args.length() > 2) {
   7653    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   7654                              JSSMSG_INVALID_ARGS, "recomputeWrappers");
   7655    return false;
   7656  }
   7657 
   7658  JS::Compartment* sourceComp = nullptr;
   7659  if (args.get(0).isObject()) {
   7660    sourceComp = JS::GetCompartment(UncheckedUnwrap(&args[0].toObject()));
   7661  }
   7662 
   7663  JS::Compartment* targetComp = nullptr;
   7664  if (args.get(1).isObject()) {
   7665    targetComp = JS::GetCompartment(UncheckedUnwrap(&args[1].toObject()));
   7666  }
   7667 
   7668  struct SingleOrAllCompartments final : public CompartmentFilter {
   7669    JS::Compartment* comp;
   7670    explicit SingleOrAllCompartments(JS::Compartment* c) : comp(c) {}
   7671    virtual bool match(JS::Compartment* c) const override {
   7672      return !comp || comp == c;
   7673    }
   7674  };
   7675 
   7676  if (!js::RecomputeWrappers(cx, SingleOrAllCompartments(sourceComp),
   7677                             SingleOrAllCompartments(targetComp))) {
   7678    return false;
   7679  }
   7680 
   7681  args.rval().setUndefined();
   7682  return true;
   7683 }
   7684 
   7685 static bool DumpObjectWrappers(JSContext* cx, unsigned argc, Value* vp) {
   7686  CallArgs args = CallArgsFromVp(argc, vp);
   7687 
   7688  bool printedHeader = false;
   7689  for (ZonesIter zone(cx->runtime(), WithAtoms); !zone.done(); zone.next()) {
   7690    bool printedZoneInfo = false;
   7691    for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
   7692      bool printedCompartmentInfo = false;
   7693      for (Compartment::ObjectWrapperEnum e(comp); !e.empty(); e.popFront()) {
   7694        JSObject* wrapper = e.front().value().unbarrieredGet();
   7695        JSObject* wrapped = e.front().key();
   7696        if (!printedHeader) {
   7697          fprintf(stderr, "Cross-compartment object wrappers:\n");
   7698          printedHeader = true;
   7699        }
   7700        if (!printedZoneInfo) {
   7701          fprintf(stderr, "  Zone %p:\n", zone.get());
   7702          printedZoneInfo = true;
   7703        }
   7704        if (!printedCompartmentInfo) {
   7705          fprintf(stderr, "    Compartment %p:\n", comp.get());
   7706          printedCompartmentInfo = true;
   7707        }
   7708        fprintf(stderr,
   7709                "      Object wrapper %p -> %p in zone %p compartment %p\n",
   7710                wrapper, wrapped, wrapped->zone(), wrapped->compartment());
   7711      }
   7712    }
   7713  }
   7714 
   7715  if (!printedHeader) {
   7716    fprintf(stderr, "No cross-compartment object wrappers.\n");
   7717  }
   7718 
   7719  args.rval().setUndefined();
   7720  return true;
   7721 }
   7722 
   7723 static bool GetMaxArgs(JSContext* cx, unsigned argc, Value* vp) {
   7724  CallArgs args = CallArgsFromVp(argc, vp);
   7725  args.rval().setInt32(ARGS_LENGTH_MAX);
   7726  return true;
   7727 }
   7728 
   7729 static bool IsHTMLDDA_Call(JSContext* cx, unsigned argc, Value* vp) {
   7730  CallArgs args = CallArgsFromVp(argc, vp);
   7731 
   7732  // These are the required conditions under which this object may be called
   7733  // by test262 tests, and the required behavior under those conditions.
   7734  if (args.length() == 0 ||
   7735      (args[0].isString() && args[0].toString()->length() == 0)) {
   7736    args.rval().setNull();
   7737    return true;
   7738  }
   7739 
   7740  JS_ReportErrorASCII(
   7741      cx, "IsHTMLDDA object is being called in an impermissible manner");
   7742  return false;
   7743 }
   7744 
   7745 static bool CreateIsHTMLDDA(JSContext* cx, unsigned argc, Value* vp) {
   7746  CallArgs args = CallArgsFromVp(argc, vp);
   7747 
   7748  static const JSClassOps classOps = {
   7749      nullptr,         // addProperty
   7750      nullptr,         // delProperty
   7751      nullptr,         // enumerate
   7752      nullptr,         // newEnumerate
   7753      nullptr,         // resolve
   7754      nullptr,         // mayResolve
   7755      nullptr,         // finalize
   7756      IsHTMLDDA_Call,  // call
   7757      nullptr,         // construct
   7758      nullptr,         // trace
   7759  };
   7760 
   7761  static const JSClass cls = {
   7762      "IsHTMLDDA",
   7763      JSCLASS_EMULATES_UNDEFINED,
   7764      &classOps,
   7765  };
   7766 
   7767  JSObject* obj = JS_NewObject(cx, &cls);
   7768  if (!obj) {
   7769    return false;
   7770  }
   7771  args.rval().setObject(*obj);
   7772  return true;
   7773 }
   7774 
   7775 static bool GetSelfHostedValue(JSContext* cx, unsigned argc, Value* vp) {
   7776  CallArgs args = CallArgsFromVp(argc, vp);
   7777 
   7778  if (args.length() != 1 || !args[0].isString()) {
   7779    JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
   7780                              JSSMSG_INVALID_ARGS, "getSelfHostedValue");
   7781    return false;
   7782  }
   7783  Rooted<JSAtom*> srcAtom(cx, ToAtom<CanGC>(cx, args[0]));
   7784  if (!srcAtom) {
   7785    return false;
   7786  }
   7787  Rooted<PropertyName*> srcName(cx, srcAtom->asPropertyName());
   7788  return GlobalObject::getIntrinsicValue(cx, cx->global(), srcName,
   7789                                         args.rval());
   7790 }
   7791 
   7792 class ShellSourceHook : public SourceHook {
   7793  // The function we should call to lazily retrieve source code.
   7794  PersistentRootedFunction fun;
   7795 
   7796 public:
   7797  ShellSourceHook(JSContext* cx, JSFunction& fun) : fun(cx, &fun) {}
   7798 
   7799  bool load(JSContext* cx, const char* filename, char16_t** twoByteSource,
   7800            char** utf8Source, size_t* length) override {
   7801    MOZ_ASSERT((twoByteSource != nullptr) != (utf8Source != nullptr),
   7802               "must be called requesting only one of UTF-8 or UTF-16 source");
   7803 
   7804    RootedString str(cx);
   7805    if (filename) {
   7806      str = NewStringCopyUTF8(cx, filename);
   7807      if (!str) {
   7808        return false;
   7809      }
   7810    } else {
   7811      str = JS_GetEmptyString(cx);
   7812    }
   7813    RootedValue filenameValue(cx, StringValue(str));
   7814 
   7815    RootedValue result(cx);
   7816    if (!Call(cx, UndefinedHandleValue, fun, HandleValueArray(filenameValue),
   7817              &result)) {
   7818      return false;
   7819    }
   7820 
   7821    str = JS::ToString(cx, result);
   7822    if (!str) {
   7823      return false;
   7824    }
   7825 
   7826    Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   7827    if (!linear) {
   7828      return false;
   7829    }
   7830 
   7831    if (twoByteSource) {
   7832      *length = JS_GetStringLength(linear);
   7833 
   7834      *twoByteSource = cx->pod_malloc<char16_t>(*length);
   7835      if (!*twoByteSource) {
   7836        return false;
   7837      }
   7838 
   7839      CopyChars(*twoByteSource, *linear);
   7840    } else {
   7841      MOZ_ASSERT(utf8Source != nullptr);
   7842 
   7843      *length = JS::GetDeflatedUTF8StringLength(linear);
   7844 
   7845      *utf8Source = cx->pod_malloc<char>(*length);
   7846      if (!*utf8Source) {
   7847        return false;
   7848      }
   7849 
   7850      mozilla::DebugOnly<size_t> dstLen = JS::DeflateStringToUTF8Buffer(
   7851          linear, mozilla::Span(*utf8Source, *length));
   7852      MOZ_ASSERT(dstLen == *length);
   7853    }
   7854 
   7855    return true;
   7856  }
   7857 };
   7858 
   7859 static bool WithSourceHook(JSContext* cx, unsigned argc, Value* vp) {
   7860  CallArgs args = CallArgsFromVp(argc, vp);
   7861  RootedObject callee(cx, &args.callee());
   7862 
   7863  if (args.length() != 2) {
   7864    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments.");
   7865    return false;
   7866  }
   7867 
   7868  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>() ||
   7869      !args[1].isObject() || !args[1].toObject().is<JSFunction>()) {
   7870    ReportUsageErrorASCII(cx, callee,
   7871                          "First and second arguments must be functions.");
   7872    return false;
   7873  }
   7874 
   7875  mozilla::UniquePtr<ShellSourceHook> hook =
   7876      mozilla::MakeUnique<ShellSourceHook>(cx,
   7877                                           args[0].toObject().as<JSFunction>());
   7878  if (!hook) {
   7879    return false;
   7880  }
   7881 
   7882  mozilla::UniquePtr<SourceHook> savedHook = js::ForgetSourceHook(cx);
   7883  js::SetSourceHook(cx, std::move(hook));
   7884 
   7885  RootedObject fun(cx, &args[1].toObject());
   7886  bool result = Call(cx, UndefinedHandleValue, fun,
   7887                     JS::HandleValueArray::empty(), args.rval());
   7888  js::SetSourceHook(cx, std::move(savedHook));
   7889  return result;
   7890 }
   7891 
   7892 static void PrintProfilerEvents_Callback(mozilla::MarkerCategory,
   7893                                         const char* msg, const char* details) {
   7894  fprintf(stderr, "PROFILER EVENT: %s %s\n", msg, details);
   7895 }
   7896 
   7897 static void PrintProfilerIntervals_Callback(mozilla::MarkerCategory,
   7898                                            const char* msg,
   7899                                            mozilla::TimeStamp start,
   7900                                            const char* details) {
   7901  fprintf(stderr, "PROFILER INTERVAL (%.2fms): %s %s\n",
   7902          (TimeStamp::Now() - start).ToMilliseconds(), msg, details);
   7903 }
   7904 
   7905 static void PrintProfilerFlow_Callback(mozilla::MarkerCategory,
   7906                                       const char* markerName,
   7907                                       uint64_t flowId) {
   7908  fprintf(stderr, "PROFILER FLOW: %s (flowId=%" PRIu64 ")\n", markerName,
   7909          flowId);
   7910 }
   7911 
   7912 static void PrintProfilerTerminatingFlow_Callback(mozilla::MarkerCategory,
   7913                                                  const char* markerName,
   7914                                                  uint64_t flowId) {
   7915  fprintf(stderr, "PROFILER TERMINATING FLOW: %s (flowId=%" PRIu64 ")\n",
   7916          markerName, flowId);
   7917 }
   7918 
   7919 static bool PrintProfilerEvents(JSContext* cx, unsigned argc, Value* vp) {
   7920  CallArgs args = CallArgsFromVp(argc, vp);
   7921  if (cx->runtime()->geckoProfiler().enabled()) {
   7922    js::RegisterContextProfilerMarkers(
   7923        cx, &PrintProfilerEvents_Callback, &PrintProfilerIntervals_Callback,
   7924        &PrintProfilerFlow_Callback, &PrintProfilerTerminatingFlow_Callback);
   7925  }
   7926  args.rval().setUndefined();
   7927  return true;
   7928 }
   7929 
   7930 #ifdef SINGLESTEP_PROFILING
   7931 static void SingleStepCallback(void* arg, jit::Simulator* sim, void* pc) {
   7932  JSContext* cx = reinterpret_cast<JSContext*>(arg);
   7933 
   7934  // If profiling is not enabled, don't do anything.
   7935  if (!cx->runtime()->geckoProfiler().enabled()) {
   7936    return;
   7937  }
   7938 
   7939  JS::ProfilingFrameIterator::RegisterState state;
   7940  state.pc = pc;
   7941 #  if defined(JS_SIMULATOR_ARM)
   7942  state.sp = (void*)sim->get_register(jit::Simulator::sp);
   7943  state.lr = (void*)sim->get_register(jit::Simulator::lr);
   7944  state.fp = (void*)sim->get_register(jit::Simulator::fp);
   7945  state.tempFP = (void*)sim->get_register(jit::Simulator::r7);
   7946 #  elif defined(JS_SIMULATOR_ARM64)
   7947  state.sp = (void*)sim->get_sp();
   7948  state.lr = (void*)sim->get_lr();
   7949  state.fp = (void*)sim->get_fp();
   7950  state.tempFP = (void*)sim->xreg(11);
   7951 #  elif defined(JS_SIMULATOR_MIPS64)
   7952  state.sp = (void*)sim->getRegister(jit::Simulator::sp);
   7953  state.lr = (void*)sim->getRegister(jit::Simulator::ra);
   7954  state.fp = (void*)sim->getRegister(jit::Simulator::fp);
   7955  // see WasmTailCallFPScratchReg and CollapseWasmFrameFast
   7956  state.tempFP = (void*)sim->getRegister(jit::Simulator::t7);
   7957 #  elif defined(JS_SIMULATOR_LOONG64)
   7958  state.sp = (void*)sim->getRegister(jit::Simulator::sp);
   7959  state.lr = (void*)sim->getRegister(jit::Simulator::ra);
   7960  state.fp = (void*)sim->getRegister(jit::Simulator::fp);
   7961  // see WasmTailCallFPScratchReg and CollapseWasmFrameFast
   7962  state.tempFP = (void*)sim->getRegister(jit::Simulator::t3);
   7963 #  else
   7964 #    error "NYI: Single-step profiling support"
   7965 #  endif
   7966 
   7967  mozilla::DebugOnly<void*> lastStackAddress = nullptr;
   7968  StackChars stack;
   7969  uint32_t frameNo = 0;
   7970  AutoEnterOOMUnsafeRegion oomUnsafe;
   7971  for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) {
   7972    MOZ_ASSERT(i.stackAddress() != nullptr);
   7973 #  ifndef ENABLE_WASM_JSPI
   7974    // The stack addresses are monotonically increasing, except when
   7975    // suspendable stacks are present (e.g. when JS PI is enabled).
   7976    MOZ_ASSERT(lastStackAddress <= i.stackAddress());
   7977 #  endif
   7978    lastStackAddress = i.stackAddress();
   7979    JS::ProfilingFrameIterator::Frame frames[16];
   7980    uint32_t nframes = i.extractStack(frames, 0, 16);
   7981    for (uint32_t i = 0; i < nframes; i++) {
   7982 #  ifndef ENABLE_WASM_JSPI
   7983      // Assert endStackAddress never exceeds sp (bug 1782188).
   7984      MOZ_ASSERT(frames[i].endStackAddress >= state.sp);
   7985 #  endif
   7986      if (frameNo > 0) {
   7987        if (!stack.append(",", 1)) {
   7988          oomUnsafe.crash("stack.append");
   7989        }
   7990      }
   7991      if (!stack.append(frames[i].label, strlen(frames[i].label))) {
   7992        oomUnsafe.crash("stack.append");
   7993      }
   7994      frameNo++;
   7995    }
   7996  }
   7997 
   7998  ShellContext* sc = GetShellContext(cx);
   7999 
   8000  // Only append the stack if it differs from the last stack.
   8001  if (sc->stacks.empty() || sc->stacks.back().length() != stack.length() ||
   8002      !ArrayEqual(sc->stacks.back().begin(), stack.begin(), stack.length())) {
   8003    if (!sc->stacks.append(std::move(stack))) {
   8004      oomUnsafe.crash("stacks.append");
   8005    }
   8006  }
   8007 }
   8008 #endif
   8009 
   8010 static bool EnableSingleStepProfiling(JSContext* cx, unsigned argc, Value* vp) {
   8011 #ifdef SINGLESTEP_PROFILING
   8012  CallArgs args = CallArgsFromVp(argc, vp);
   8013 
   8014  jit::Simulator* sim = cx->simulator();
   8015  sim->enable_single_stepping(SingleStepCallback, cx);
   8016 
   8017  args.rval().setUndefined();
   8018  return true;
   8019 #else
   8020  JS_ReportErrorASCII(cx, "single-step profiling not enabled on this platform");
   8021  return false;
   8022 #endif
   8023 }
   8024 
   8025 static bool DisableSingleStepProfiling(JSContext* cx, unsigned argc,
   8026                                       Value* vp) {
   8027 #ifdef SINGLESTEP_PROFILING
   8028  CallArgs args = CallArgsFromVp(argc, vp);
   8029 
   8030  jit::Simulator* sim = cx->simulator();
   8031  sim->disable_single_stepping();
   8032 
   8033  ShellContext* sc = GetShellContext(cx);
   8034 
   8035  RootedValueVector elems(cx);
   8036  for (size_t i = 0; i < sc->stacks.length(); i++) {
   8037    JSString* stack =
   8038        JS_NewUCStringCopyN(cx, sc->stacks[i].begin(), sc->stacks[i].length());
   8039    if (!stack) {
   8040      return false;
   8041    }
   8042    if (!elems.append(StringValue(stack))) {
   8043      return false;
   8044    }
   8045  }
   8046 
   8047  JSObject* array = JS::NewArrayObject(cx, elems);
   8048  if (!array) {
   8049    return false;
   8050  }
   8051 
   8052  sc->stacks.clear();
   8053  args.rval().setObject(*array);
   8054  return true;
   8055 #else
   8056  JS_ReportErrorASCII(cx, "single-step profiling not enabled on this platform");
   8057  return false;
   8058 #endif
   8059 }
   8060 
   8061 static bool IsLatin1(JSContext* cx, unsigned argc, Value* vp) {
   8062  CallArgs args = CallArgsFromVp(argc, vp);
   8063  bool isLatin1 =
   8064      args.get(0).isString() && args[0].toString()->hasLatin1Chars();
   8065  args.rval().setBoolean(isLatin1);
   8066  return true;
   8067 }
   8068 
   8069 static bool EnableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp) {
   8070  CallArgs args = CallArgsFromVp(argc, vp);
   8071 
   8072  if (!EnsureGeckoProfilingStackInstalled(cx, GetShellContext(cx))) {
   8073    return false;
   8074  }
   8075 
   8076  cx->runtime()->geckoProfiler().enableSlowAssertions(false);
   8077  cx->runtime()->geckoProfiler().enable(true);
   8078 
   8079  args.rval().setUndefined();
   8080  return true;
   8081 }
   8082 
   8083 static bool EnableGeckoProfilingWithSlowAssertions(JSContext* cx, unsigned argc,
   8084                                                   Value* vp) {
   8085  CallArgs args = CallArgsFromVp(argc, vp);
   8086  args.rval().setUndefined();
   8087 
   8088  if (cx->runtime()->geckoProfiler().enabled()) {
   8089    // If profiling already enabled with slow assertions disabled,
   8090    // this is a no-op.
   8091    if (cx->runtime()->geckoProfiler().slowAssertionsEnabled()) {
   8092      return true;
   8093    }
   8094 
   8095    // Slow assertions are off.  Disable profiling before re-enabling
   8096    // with slow assertions on.
   8097    cx->runtime()->geckoProfiler().enable(false);
   8098  }
   8099 
   8100  if (!EnsureGeckoProfilingStackInstalled(cx, GetShellContext(cx))) {
   8101    return false;
   8102  }
   8103 
   8104  cx->runtime()->geckoProfiler().enableSlowAssertions(true);
   8105  cx->runtime()->geckoProfiler().enable(true);
   8106 
   8107  return true;
   8108 }
   8109 
   8110 static bool DisableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp) {
   8111  CallArgs args = CallArgsFromVp(argc, vp);
   8112  args.rval().setUndefined();
   8113 
   8114  if (!cx->runtime()->geckoProfiler().enabled()) {
   8115    return true;
   8116  }
   8117 
   8118  cx->runtime()->geckoProfiler().enable(false);
   8119  return true;
   8120 }
   8121 
   8122 static bool GetGeckoProfilingScriptSourcesCount(JSContext* cx, unsigned argc,
   8123                                                Value* vp) {
   8124  CallArgs args = CallArgsFromVp(argc, vp);
   8125  size_t count = cx->runtime()->geckoProfiler().scriptSourcesCount();
   8126  args.rval().setNumber(static_cast<double>(count));
   8127  return true;
   8128 }
   8129 
   8130 // Global mailbox that is used to communicate a shareable object value from one
   8131 // worker to another.
   8132 //
   8133 // These object types are shareable:
   8134 //
   8135 //   - SharedArrayBuffer
   8136 //   - WasmMemoryObject (when constructed with shared:true)
   8137 //   - WasmModuleObject
   8138 //
   8139 // For the SharedArrayBuffer and WasmMemoryObject we transmit the underlying
   8140 // SharedArrayRawBuffer ("SARB"). For the WasmModuleObject we transmit the
   8141 // underlying JS::WasmModule.  The transmitted types are refcounted.  When they
   8142 // are in the mailbox their reference counts are at least 1, accounting for the
   8143 // reference from the mailbox.
   8144 //
   8145 // The lock guards the mailbox variable and prevents a race where two workers
   8146 // try to set the mailbox at the same time to replace an object that is only
   8147 // referenced from the mailbox: the workers will both decrement the reference
   8148 // count on the old object, and one of those decrements will be on a garbage
   8149 // object.  We could implement this with atomics and a CAS loop but it's not
   8150 // worth the bother.
   8151 //
   8152 // Note that if a thread reads the mailbox repeatedly it will get distinct
   8153 // objects on each read.  The alternatives are to cache created objects locally,
   8154 // but this retains storage we don't need to retain, or to somehow clear the
   8155 // mailbox locally, but this creates a coordination headache.  Buyer beware.
   8156 
   8157 enum class MailboxTag {
   8158  Empty,
   8159  SharedArrayBuffer,
   8160  WasmMemory,
   8161  WasmModule,
   8162  Number,
   8163 };
   8164 
   8165 struct SharedObjectMailbox {
   8166  union Value {
   8167    struct {
   8168      SharedArrayRawBuffer* buffer;
   8169      size_t length;
   8170      bool isHugeMemory;  // For a WasmMemory tag, otherwise false
   8171      bool isGrowable;    // For GrowableSharedArrayBuffer, otherwise false
   8172    } sarb;
   8173    JS::WasmModule* module;
   8174    double number;
   8175 
   8176    Value() : number(0.0) {}
   8177  };
   8178 
   8179  MailboxTag tag = MailboxTag::Empty;
   8180  Value val;
   8181 };
   8182 
   8183 using SOMailbox = ExclusiveData<SharedObjectMailbox>;
   8184 
   8185 // Never null after successful initialization.
   8186 static SOMailbox* sharedObjectMailbox;
   8187 
   8188 static bool InitSharedObjectMailbox() {
   8189  sharedObjectMailbox = js_new<SOMailbox>(mutexid::ShellObjectMailbox);
   8190  return sharedObjectMailbox != nullptr;
   8191 }
   8192 
   8193 static void DestructSharedObjectMailbox() {
   8194  // All workers need to have terminated at this point.
   8195 
   8196  {
   8197    auto mbx = sharedObjectMailbox->lock();
   8198    switch (mbx->tag) {
   8199      case MailboxTag::Empty:
   8200      case MailboxTag::Number:
   8201        break;
   8202      case MailboxTag::SharedArrayBuffer:
   8203      case MailboxTag::WasmMemory:
   8204        mbx->val.sarb.buffer->dropReference();
   8205        break;
   8206      case MailboxTag::WasmModule:
   8207        mbx->val.module->Release();
   8208        break;
   8209      default:
   8210        MOZ_CRASH();
   8211    }
   8212  }
   8213 
   8214  js_delete(sharedObjectMailbox);
   8215  sharedObjectMailbox = nullptr;
   8216 }
   8217 
   8218 static bool GetSharedObject(JSContext* cx, unsigned argc, Value* vp) {
   8219  CallArgs args = CallArgsFromVp(argc, vp);
   8220  RootedObject newObj(cx);
   8221 
   8222  {
   8223    auto mbx = sharedObjectMailbox->lock();
   8224    switch (mbx->tag) {
   8225      case MailboxTag::Empty: {
   8226        break;
   8227      }
   8228      case MailboxTag::Number: {
   8229        args.rval().setNumber(mbx->val.number);
   8230        return true;
   8231      }
   8232      case MailboxTag::SharedArrayBuffer:
   8233      case MailboxTag::WasmMemory: {
   8234        // Flag was set in the sender; ensure it is set in the receiver.
   8235        MOZ_ASSERT(
   8236            cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
   8237 
   8238        // The protocol for creating a SAB requires the refcount to be
   8239        // incremented prior to the SAB creation.
   8240 
   8241        SharedArrayRawBuffer* buf = mbx->val.sarb.buffer;
   8242        size_t length = mbx->val.sarb.length;
   8243        if (!buf->addReference()) {
   8244          JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   8245                                    JSMSG_SC_SAB_REFCNT_OFLO);
   8246          return false;
   8247        }
   8248 
   8249        // If the allocation fails we must decrement the refcount before
   8250        // returning.
   8251 
   8252        Rooted<ArrayBufferObjectMaybeShared*> maybesab(cx);
   8253        if (!mbx->val.sarb.isGrowable) {
   8254          maybesab = SharedArrayBufferObject::New(cx, buf, length);
   8255        } else {
   8256          maybesab = SharedArrayBufferObject::NewGrowable(cx, buf, length);
   8257        }
   8258        if (!maybesab) {
   8259          buf->dropReference();
   8260          return false;
   8261        }
   8262 
   8263        // At this point the SAB was created successfully and it owns the
   8264        // refcount-increase on the buffer that we performed above.  So even
   8265        // if we fail to allocate along any path below we must not decrement
   8266        // the refcount; the garbage collector must be allowed to handle
   8267        // that via finalization of the orphaned SAB object.
   8268 
   8269        if (mbx->tag == MailboxTag::SharedArrayBuffer) {
   8270          newObj = maybesab;
   8271        } else {
   8272          if (!GlobalObject::ensureConstructor(cx, cx->global(),
   8273                                               JSProto_WebAssembly)) {
   8274            return false;
   8275          }
   8276          RootedObject proto(cx,
   8277                             &cx->global()->getPrototype(JSProto_WasmMemory));
   8278          newObj = WasmMemoryObject::create(cx, maybesab,
   8279                                            mbx->val.sarb.isHugeMemory, proto);
   8280          MOZ_ASSERT_IF(newObj, newObj->as<WasmMemoryObject>().isShared());
   8281          if (!newObj) {
   8282            return false;
   8283          }
   8284        }
   8285 
   8286        break;
   8287      }
   8288      case MailboxTag::WasmModule: {
   8289        if (!GlobalObject::ensureConstructor(cx, cx->global(),
   8290                                             JSProto_WebAssembly)) {
   8291          return false;
   8292        }
   8293 
   8294        // WasmModuleObject::create() increments the refcount on the module
   8295        // and signals an error and returns null if that fails.
   8296        newObj = mbx->val.module->createObject(cx);
   8297        if (!newObj) {
   8298          return false;
   8299        }
   8300        break;
   8301      }
   8302      default: {
   8303        MOZ_CRASH();
   8304      }
   8305    }
   8306  }
   8307 
   8308  args.rval().setObjectOrNull(newObj);
   8309  return true;
   8310 }
   8311 
   8312 static bool SetSharedObject(JSContext* cx, unsigned argc, Value* vp) {
   8313  CallArgs args = CallArgsFromVp(argc, vp);
   8314 
   8315  MailboxTag tag = MailboxTag::Empty;
   8316  SharedObjectMailbox::Value value;
   8317 
   8318  // Increase refcounts when we obtain the value to avoid operating on dead
   8319  // storage during self-assignment.
   8320 
   8321  if (args.get(0).isObject()) {
   8322    RootedObject obj(cx, &args[0].toObject());
   8323    if (obj->is<SharedArrayBufferObject>()) {
   8324      Rooted<SharedArrayBufferObject*> sab(cx,
   8325                                           &obj->as<SharedArrayBufferObject>());
   8326      tag = MailboxTag::SharedArrayBuffer;
   8327      value.sarb.buffer = sab->rawBufferObject();
   8328      value.sarb.length = sab->byteLengthOrMaxByteLength();
   8329      value.sarb.isHugeMemory = false;
   8330      value.sarb.isGrowable = sab->isGrowable();
   8331      if (!value.sarb.buffer->addReference()) {
   8332        JS_ReportErrorASCII(cx,
   8333                            "Reference count overflow on SharedArrayBuffer");
   8334        return false;
   8335      }
   8336    } else if (obj->is<WasmMemoryObject>()) {
   8337      // Here we must transmit sab.byteLength() as the length; the SARB has its
   8338      // own notion of the length which may be greater, and that's fine.
   8339      if (obj->as<WasmMemoryObject>().isShared()) {
   8340        Rooted<SharedArrayBufferObject*> sab(
   8341            cx, &obj->as<WasmMemoryObject>()
   8342                     .buffer()
   8343                     .as<SharedArrayBufferObject>());
   8344        tag = MailboxTag::WasmMemory;
   8345        value.sarb.buffer = sab->rawBufferObject();
   8346        value.sarb.length = sab->byteLength();
   8347        value.sarb.isHugeMemory = obj->as<WasmMemoryObject>().isHuge();
   8348        value.sarb.isGrowable = sab->isGrowable();
   8349        if (!value.sarb.buffer->addReference()) {
   8350          JS_ReportErrorASCII(cx,
   8351                              "Reference count overflow on SharedArrayBuffer");
   8352          return false;
   8353        }
   8354      } else {
   8355        JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject");
   8356        return false;
   8357      }
   8358    } else if (JS::IsWasmModuleObject(obj)) {
   8359      tag = MailboxTag::WasmModule;
   8360      value.module = JS::GetWasmModule(obj).forget().take();
   8361    } else {
   8362      JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject");
   8363      return false;
   8364    }
   8365  } else if (args.get(0).isNumber()) {
   8366    tag = MailboxTag::Number;
   8367    value.number = args.get(0).toNumber();
   8368    // Nothing
   8369  } else if (args.get(0).isNullOrUndefined()) {
   8370    // Nothing
   8371  } else {
   8372    JS_ReportErrorASCII(cx, "Invalid argument to SetSharedObject");
   8373    return false;
   8374  }
   8375 
   8376  {
   8377    auto mbx = sharedObjectMailbox->lock();
   8378 
   8379    switch (mbx->tag) {
   8380      case MailboxTag::Empty:
   8381      case MailboxTag::Number:
   8382        break;
   8383      case MailboxTag::SharedArrayBuffer:
   8384      case MailboxTag::WasmMemory:
   8385        mbx->val.sarb.buffer->dropReference();
   8386        break;
   8387      case MailboxTag::WasmModule:
   8388        mbx->val.module->Release();
   8389        break;
   8390      default:
   8391        MOZ_CRASH();
   8392    }
   8393 
   8394    mbx->tag = tag;
   8395    mbx->val = value;
   8396  }
   8397 
   8398  args.rval().setUndefined();
   8399  return true;
   8400 }
   8401 
   8402 using Uint8Vector = Vector<uint8_t, 0, SystemAllocPolicy>;
   8403 
   8404 class StreamCacheEntry : public AtomicRefCounted<StreamCacheEntry>,
   8405                         public JS::OptimizedEncodingListener {
   8406  using AtomicBase = AtomicRefCounted<StreamCacheEntry>;
   8407 
   8408  Uint8Vector bytes_;
   8409  ExclusiveData<Uint8Vector> optimized_;
   8410 
   8411 public:
   8412  explicit StreamCacheEntry(Uint8Vector&& original)
   8413      : bytes_(std::move(original)),
   8414        optimized_(mutexid::ShellStreamCacheEntryState) {}
   8415 
   8416  // Implement JS::OptimizedEncodingListener:
   8417 
   8418  MozExternalRefCountType MOZ_XPCOM_ABI AddRef() override {
   8419    AtomicBase::AddRef();
   8420    return 1;  // unused
   8421  }
   8422  MozExternalRefCountType MOZ_XPCOM_ABI Release() override {
   8423    AtomicBase::Release();
   8424    return 0;  // unused
   8425  }
   8426 
   8427  const Uint8Vector& bytes() const { return bytes_; }
   8428 
   8429  void storeOptimizedEncoding(const uint8_t* srcBytes,
   8430                              size_t srcLength) override {
   8431    MOZ_ASSERT(srcLength > 0);
   8432 
   8433    // Tolerate races since a single StreamCacheEntry object can be used as
   8434    // the source of multiple streaming compilations.
   8435    auto dstBytes = optimized_.lock();
   8436    if (dstBytes->length() > 0) {
   8437      return;
   8438    }
   8439 
   8440    if (!dstBytes->resize(srcLength)) {
   8441      return;
   8442    }
   8443    memcpy(dstBytes->begin(), srcBytes, srcLength);
   8444  }
   8445 
   8446  bool hasOptimizedEncoding() const { return !optimized_.lock()->empty(); }
   8447  const Uint8Vector& optimizedEncoding() const {
   8448    return optimized_.lock().get();
   8449  }
   8450 };
   8451 
   8452 using StreamCacheEntryPtr = RefPtr<StreamCacheEntry>;
   8453 
   8454 class StreamCacheEntryObject : public NativeObject {
   8455  static const unsigned CACHE_ENTRY_SLOT = 0;
   8456  static const JSClassOps classOps_;
   8457  static const JSPropertySpec properties_;
   8458 
   8459  static void finalize(JS::GCContext* gcx, JSObject* obj) {
   8460    obj->as<StreamCacheEntryObject>().cache().Release();
   8461  }
   8462 
   8463  static bool cachedGetter(JSContext* cx, unsigned argc, Value* vp) {
   8464    CallArgs args = CallArgsFromVp(argc, vp);
   8465    if (!args.thisv().isObject() ||
   8466        !args.thisv().toObject().is<StreamCacheEntryObject>()) {
   8467      JS_ReportErrorASCII(cx, "Expected StreamCacheEntry object");
   8468      return false;
   8469    }
   8470 
   8471    StreamCacheEntryObject& obj =
   8472        args.thisv().toObject().as<StreamCacheEntryObject>();
   8473    args.rval().setBoolean(obj.cache().hasOptimizedEncoding());
   8474    return true;
   8475  }
   8476  static bool getBuffer(JSContext* cx, unsigned argc, Value* vp) {
   8477    CallArgs args = CallArgsFromVp(argc, vp);
   8478    if (!args.thisv().isObject() ||
   8479        !args.thisv().toObject().is<StreamCacheEntryObject>()) {
   8480      JS_ReportErrorASCII(cx, "Expected StreamCacheEntry object");
   8481      return false;
   8482    }
   8483 
   8484    auto& bytes =
   8485        args.thisv().toObject().as<StreamCacheEntryObject>().cache().bytes();
   8486    auto* buffer = ArrayBufferObject::createZeroed(cx, bytes.length());
   8487    if (!buffer) {
   8488      return false;
   8489    }
   8490 
   8491    memcpy(buffer->dataPointer(), bytes.begin(), bytes.length());
   8492 
   8493    args.rval().setObject(*buffer);
   8494    return true;
   8495  }
   8496 
   8497 public:
   8498  static const unsigned RESERVED_SLOTS = 1;
   8499  static const JSClass class_;
   8500  static const JSPropertySpec properties[];
   8501 
   8502  static bool construct(JSContext* cx, unsigned argc, Value* vp) {
   8503    CallArgs args = CallArgsFromVp(argc, vp);
   8504    if (!args.requireAtLeast(cx, "streamCacheEntry", 1)) {
   8505      return false;
   8506    }
   8507 
   8508    SharedMem<uint8_t*> ptr;
   8509    size_t numBytes;
   8510    if (!args[0].isObject() ||
   8511        !IsBufferSource(cx, &args[0].toObject(), /*allowShared*/ true,
   8512                        /*allowResizable*/ true, &ptr, &numBytes)) {
   8513      RootedObject callee(cx, &args.callee());
   8514      ReportUsageErrorASCII(cx, callee, "Argument must be an ArrayBuffer");
   8515      return false;
   8516    }
   8517 
   8518    Uint8Vector bytes;
   8519    if (!bytes.resize(numBytes)) {
   8520      ReportOutOfMemory(cx);
   8521      return false;
   8522    }
   8523 
   8524    memcpy(bytes.begin(), ptr.unwrap(), numBytes);
   8525 
   8526    RefPtr<StreamCacheEntry> cache =
   8527        cx->new_<StreamCacheEntry>(std::move(bytes));
   8528    if (!cache) {
   8529      return false;
   8530    }
   8531 
   8532    Rooted<NativeObject*> obj(
   8533        cx, NewObjectWithGivenProto<StreamCacheEntryObject>(cx, nullptr));
   8534    if (!obj) {
   8535      return false;
   8536    }
   8537    obj->initReservedSlot(CACHE_ENTRY_SLOT,
   8538                          PrivateValue(cache.forget().take()));
   8539 
   8540    if (!JS_DefineProperty(cx, obj, "cached", cachedGetter, nullptr, 0)) {
   8541      return false;
   8542    }
   8543    if (!JS_DefineFunction(cx, obj, "getBuffer", getBuffer, 0, 0)) {
   8544      return false;
   8545    }
   8546 
   8547    args.rval().setObject(*obj);
   8548    return true;
   8549  }
   8550 
   8551  StreamCacheEntry& cache() const {
   8552    return *(StreamCacheEntry*)getReservedSlot(CACHE_ENTRY_SLOT).toPrivate();
   8553  }
   8554 };
   8555 
   8556 const JSClassOps StreamCacheEntryObject::classOps_ = {
   8557    nullptr,                           // addProperty
   8558    nullptr,                           // delProperty
   8559    nullptr,                           // enumerate
   8560    nullptr,                           // newEnumerate
   8561    nullptr,                           // resolve
   8562    nullptr,                           // mayResolve
   8563    StreamCacheEntryObject::finalize,  // finalize
   8564    nullptr,                           // call
   8565    nullptr,                           // construct
   8566    nullptr,                           // trace
   8567 };
   8568 
   8569 const JSClass StreamCacheEntryObject::class_ = {
   8570    "StreamCacheEntryObject",
   8571    JSCLASS_HAS_RESERVED_SLOTS(StreamCacheEntryObject::RESERVED_SLOTS) |
   8572        JSCLASS_BACKGROUND_FINALIZE,
   8573    &StreamCacheEntryObject::classOps_,
   8574 };
   8575 
   8576 struct BufferStreamJob {
   8577  Variant<Uint8Vector, StreamCacheEntryPtr> source;
   8578  Thread thread;
   8579  JS::StreamConsumer* consumer;
   8580 
   8581  BufferStreamJob(Uint8Vector&& source, JS::StreamConsumer* consumer)
   8582      : source(AsVariant<Uint8Vector>(std::move(source))), consumer(consumer) {}
   8583  BufferStreamJob(StreamCacheEntry& source, JS::StreamConsumer* consumer)
   8584      : source(AsVariant<StreamCacheEntryPtr>(&source)), consumer(consumer) {}
   8585 };
   8586 
   8587 struct BufferStreamState {
   8588  Vector<UniquePtr<BufferStreamJob>, 0, SystemAllocPolicy> jobs;
   8589  size_t delayMillis;
   8590  size_t chunkSize;
   8591  bool shutdown;
   8592 
   8593  BufferStreamState() : delayMillis(1), chunkSize(10), shutdown(false) {}
   8594 
   8595  ~BufferStreamState() { MOZ_ASSERT(jobs.empty()); }
   8596 };
   8597 
   8598 static ExclusiveWaitableData<BufferStreamState>* bufferStreamState;
   8599 
   8600 static void BufferStreamMain(BufferStreamJob* job) {
   8601  const uint8_t* bytes;
   8602  size_t byteLength;
   8603  JS::OptimizedEncodingListener* listener;
   8604  if (job->source.is<StreamCacheEntryPtr>()) {
   8605    StreamCacheEntry& cache = *job->source.as<StreamCacheEntryPtr>();
   8606    if (cache.hasOptimizedEncoding()) {
   8607      const Uint8Vector& optimized = cache.optimizedEncoding();
   8608      job->consumer->consumeOptimizedEncoding(optimized.begin(),
   8609                                              optimized.length());
   8610      goto done;
   8611    }
   8612 
   8613    bytes = cache.bytes().begin();
   8614    byteLength = cache.bytes().length();
   8615    listener = &cache;
   8616  } else {
   8617    bytes = job->source.as<Uint8Vector>().begin();
   8618    byteLength = job->source.as<Uint8Vector>().length();
   8619    listener = nullptr;
   8620  }
   8621 
   8622  size_t byteOffset;
   8623  byteOffset = 0;
   8624  while (true) {
   8625    if (byteOffset == byteLength) {
   8626      job->consumer->streamEnd(listener);
   8627      break;
   8628    }
   8629 
   8630    bool shutdown;
   8631    size_t delayMillis;
   8632    size_t chunkSize;
   8633    {
   8634      auto state = bufferStreamState->lock();
   8635      shutdown = state->shutdown;
   8636      delayMillis = state->delayMillis;
   8637      chunkSize = state->chunkSize;
   8638    }
   8639 
   8640    if (shutdown) {
   8641      job->consumer->streamError(JSMSG_STREAM_CONSUME_ERROR);
   8642      break;
   8643    }
   8644 
   8645    ThisThread::SleepMilliseconds(delayMillis);
   8646 
   8647    chunkSize = std::min(chunkSize, byteLength - byteOffset);
   8648 
   8649    if (!job->consumer->consumeChunk(bytes + byteOffset, chunkSize)) {
   8650      break;
   8651    }
   8652 
   8653    byteOffset += chunkSize;
   8654  }
   8655 
   8656 done:
   8657  auto state = bufferStreamState->lock();
   8658  size_t jobIndex = 0;
   8659  while (state->jobs[jobIndex].get() != job) {
   8660    jobIndex++;
   8661  }
   8662  job->thread.detach();  // quiet assert in ~Thread() called by erase().
   8663  state->jobs.erase(state->jobs.begin() + jobIndex);
   8664  if (state->jobs.empty()) {
   8665    state.notify_all(/* jobs empty */);
   8666  }
   8667 }
   8668 
   8669 static bool ConsumeBufferSource(JSContext* cx, JS::HandleObject obj,
   8670                                JS::MimeType, JS::StreamConsumer* consumer) {
   8671  {
   8672    RootedValue url(cx);
   8673    if (!JS_GetProperty(cx, obj, "url", &url)) {
   8674      return false;
   8675    }
   8676    UniqueChars urlChars;
   8677    if (url.isString()) {
   8678      Rooted<JSString*> str(cx, url.toString());
   8679      urlChars = JS_EncodeStringToUTF8(cx, str);
   8680      if (!urlChars) {
   8681        return false;
   8682      }
   8683    }
   8684 
   8685    RootedValue mapUrl(cx);
   8686    if (!JS_GetProperty(cx, obj, "sourceMappingURL", &mapUrl)) {
   8687      return false;
   8688    }
   8689    UniqueChars mapUrlChars;
   8690    if (mapUrl.isString()) {
   8691      Rooted<JSString*> str(cx, mapUrl.toString());
   8692      mapUrlChars = JS_EncodeStringToUTF8(cx, str);
   8693      if (!mapUrlChars) {
   8694        return false;
   8695      }
   8696    }
   8697 
   8698    consumer->noteResponseURLs(urlChars.get(), mapUrlChars.get());
   8699  }
   8700 
   8701  UniquePtr<BufferStreamJob> job;
   8702 
   8703  SharedMem<uint8_t*> dataPointer;
   8704  size_t byteLength;
   8705  if (IsBufferSource(cx, obj, /*allowShared*/ true, /*allowResizable*/ true,
   8706                     &dataPointer, &byteLength)) {
   8707    Uint8Vector bytes;
   8708    if (!bytes.resize(byteLength)) {
   8709      JS_ReportOutOfMemory(cx);
   8710      return false;
   8711    }
   8712 
   8713    memcpy(bytes.begin(), dataPointer.unwrap(), byteLength);
   8714    job = cx->make_unique<BufferStreamJob>(std::move(bytes), consumer);
   8715  } else if (obj->is<StreamCacheEntryObject>()) {
   8716    job = cx->make_unique<BufferStreamJob>(
   8717        obj->as<StreamCacheEntryObject>().cache(), consumer);
   8718  } else {
   8719    JS_ReportErrorASCII(
   8720        cx,
   8721        "shell streaming consumes a buffer source (buffer or view) "
   8722        "or StreamCacheEntryObject");
   8723    return false;
   8724  }
   8725  if (!job) {
   8726    return false;
   8727  }
   8728 
   8729  BufferStreamJob* jobPtr = job.get();
   8730 
   8731  {
   8732    auto state = bufferStreamState->lock();
   8733    MOZ_ASSERT(!state->shutdown);
   8734    if (!state->jobs.append(std::move(job))) {
   8735      JS_ReportOutOfMemory(cx);
   8736      return false;
   8737    }
   8738  }
   8739 
   8740  {
   8741    AutoEnterOOMUnsafeRegion oomUnsafe;
   8742    if (!jobPtr->thread.init(BufferStreamMain, jobPtr)) {
   8743      oomUnsafe.crash("ConsumeBufferSource");
   8744    }
   8745  }
   8746 
   8747  return true;
   8748 }
   8749 
   8750 static void ReportStreamError(JSContext* cx, size_t errorNumber) {
   8751  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
   8752 }
   8753 
   8754 static bool SetBufferStreamParams(JSContext* cx, unsigned argc, Value* vp) {
   8755  CallArgs args = CallArgsFromVp(argc, vp);
   8756  if (!args.requireAtLeast(cx, "setBufferStreamParams", 2)) {
   8757    return false;
   8758  }
   8759 
   8760  double delayMillis;
   8761  if (!ToNumber(cx, args[0], &delayMillis)) {
   8762    return false;
   8763  }
   8764 
   8765  double chunkSize;
   8766  if (!ToNumber(cx, args[1], &chunkSize)) {
   8767    return false;
   8768  }
   8769 
   8770  {
   8771    auto state = bufferStreamState->lock();
   8772    state->delayMillis = delayMillis;
   8773    state->chunkSize = chunkSize;
   8774  }
   8775 
   8776  args.rval().setUndefined();
   8777  return true;
   8778 }
   8779 
   8780 static void ShutdownBufferStreams() {
   8781  auto state = bufferStreamState->lock();
   8782  state->shutdown = true;
   8783  while (!state->jobs.empty()) {
   8784    state.wait(/* jobs empty */);
   8785  }
   8786  state->jobs.clearAndFree();
   8787 }
   8788 
   8789 static bool DumpScopeChain(JSContext* cx, unsigned argc, Value* vp) {
   8790  CallArgs args = CallArgsFromVp(argc, vp);
   8791  RootedObject callee(cx, &args.callee());
   8792 
   8793  if (js::SupportDifferentialTesting()) {
   8794    ReportUsageErrorASCII(
   8795        cx, callee, "Function not available in differential testing mode.");
   8796    return false;
   8797  }
   8798 
   8799  if (args.length() != 1) {
   8800    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   8801    return false;
   8802  }
   8803 
   8804  if (!args[0].isObject() ||
   8805      !(args[0].toObject().is<JSFunction>() ||
   8806        args[0].toObject().is<ShellModuleObjectWrapper>())) {
   8807    ReportUsageErrorASCII(
   8808        cx, callee, "Argument must be an interpreted function or a module");
   8809    return false;
   8810  }
   8811 
   8812  RootedObject obj(cx, &args[0].toObject());
   8813  RootedScript script(cx);
   8814 
   8815  if (obj->is<JSFunction>()) {
   8816    RootedFunction fun(cx, &obj->as<JSFunction>());
   8817    if (!fun->isInterpreted()) {
   8818      ReportUsageErrorASCII(cx, callee,
   8819                            "Argument must be an interpreted function");
   8820      return false;
   8821    }
   8822    script = JSFunction::getOrCreateScript(cx, fun);
   8823    if (!script) {
   8824      return false;
   8825    }
   8826  } else {
   8827    script = obj->as<ShellModuleObjectWrapper>().get()->maybeScript();
   8828    if (!script) {
   8829      JS_ReportErrorASCII(cx, "module does not have an associated script");
   8830      return false;
   8831    }
   8832  }
   8833 
   8834  script->bodyScope()->dump();
   8835 
   8836  args.rval().setUndefined();
   8837  return true;
   8838 }
   8839 
   8840 // For testing GC marking, blackRoot() and grayRoot() will heap-allocate an
   8841 // array whose elements (as well as the array itself) will be marked as roots in
   8842 // subsequent GCs.
   8843 //
   8844 // Note that EnsureGrayRoot() will blacken the returned object, so it will not
   8845 // actually end up marked gray until the following GC clears the black bit
   8846 // (assuming nothing is holding onto it.)
   8847 //
   8848 // The idea is that you can set up a whole graph of objects to be marked gray,
   8849 // hanging off of the object returned from grayRoot(). Then you GC to clear the
   8850 // black bits and set the gray bits.
   8851 //
   8852 // To test grayness, register the objects of interest with addMarkObservers(),
   8853 // which takes an Array of objects (which will be marked black at the time
   8854 // they're passed in). Their mark bits may be retrieved at any time with
   8855 // getMarks(), in the form of an array of strings with each index corresponding
   8856 // to the original objects passed to addMarkObservers().
   8857 
   8858 static bool EnsureRootArray(JSContext* cx, gc::MarkColor color, unsigned argc,
   8859                            Value* vp) {
   8860  CallArgs args = CallArgsFromVp(argc, vp);
   8861 
   8862  auto priv = EnsureShellCompartmentPrivate(cx);
   8863  if (!priv) {
   8864    return false;
   8865  }
   8866 
   8867  GCPtr<ArrayObject*>& root =
   8868      (color == gc::MarkColor::Black) ? priv->blackRoot : priv->grayRoot;
   8869 
   8870  if (!root && !(root = NewTenuredDenseEmptyArray(cx))) {
   8871    return false;
   8872  }
   8873 
   8874  // Barrier to enforce the invariant that JS does not touch gray objects.
   8875  JSObject* obj = root;
   8876  JS::ExposeObjectToActiveJS(obj);
   8877 
   8878  args.rval().setObject(*obj);
   8879  return true;
   8880 }
   8881 
   8882 static bool EnsureBlackRoot(JSContext* cx, unsigned argc, Value* vp) {
   8883  return EnsureRootArray(cx, gc::MarkColor::Black, argc, vp);
   8884 }
   8885 
   8886 static bool EnsureGrayRoot(JSContext* cx, unsigned argc, Value* vp) {
   8887  return EnsureRootArray(cx, gc::MarkColor::Gray, argc, vp);
   8888 }
   8889 
   8890 static MarkBitObservers* EnsureMarkBitObservers(JSContext* cx) {
   8891  ShellContext* sc = GetShellContext(cx);
   8892  if (!sc->markObservers) {
   8893    auto* observers =
   8894        cx->new_<MarkBitObservers>(cx->runtime(), NonShrinkingValueVector());
   8895    if (!observers) {
   8896      return nullptr;
   8897    }
   8898    sc->markObservers.reset(observers);
   8899  }
   8900  return sc->markObservers.get();
   8901 }
   8902 
   8903 static bool ClearMarkObservers(JSContext* cx, unsigned argc, Value* vp) {
   8904  CallArgs args = CallArgsFromVp(argc, vp);
   8905 
   8906  auto markObservers = EnsureMarkBitObservers(cx);
   8907  if (!markObservers) {
   8908    return false;
   8909  }
   8910 
   8911  markObservers->get().clear();
   8912 
   8913  args.rval().setUndefined();
   8914  return true;
   8915 }
   8916 
   8917 static bool AddMarkObservers(JSContext* cx, unsigned argc, Value* vp) {
   8918  CallArgs args = CallArgsFromVp(argc, vp);
   8919 
   8920  auto markObservers = EnsureMarkBitObservers(cx);
   8921  if (!markObservers) {
   8922    return false;
   8923  }
   8924 
   8925  if (!args.get(0).isObject()) {
   8926    JS_ReportErrorASCII(cx, "argument must be an Array of objects");
   8927    return false;
   8928  }
   8929 
   8930  RootedObject observersArg(cx, &args[0].toObject());
   8931  uint64_t length;
   8932  if (!GetLengthProperty(cx, observersArg, &length)) {
   8933    return false;
   8934  }
   8935 
   8936  if (length > UINT32_MAX) {
   8937    JS_ReportErrorASCII(cx, "Invalid length for observers array");
   8938    return false;
   8939  }
   8940 
   8941  RootedValue value(cx);
   8942  for (uint32_t i = 0; i < length; i++) {
   8943    if (!JS_GetElement(cx, observersArg, i, &value)) {
   8944      return false;
   8945    }
   8946 
   8947    if (!value.isObject() && !value.isSymbol()) {
   8948      JS_ReportErrorASCII(cx, "Can only observe objects and symbols");
   8949      return false;
   8950    }
   8951 
   8952    if (gc::IsInsideNursery(value.toGCThing())) {
   8953      // WeakCaches are not swept during a minor GC. To prevent
   8954      // nursery-allocated contents from having the mark bits be deceptively
   8955      // black until the second GC, they would need to be marked weakly (cf
   8956      // NurseryAwareHashMap). It is simpler to evict the nursery to prevent
   8957      // nursery objects from being observed.
   8958      cx->runtime()->gc.evictNursery();
   8959    }
   8960 
   8961    if (!markObservers->get().append(value)) {
   8962      ReportOutOfMemory(cx);
   8963      return false;
   8964    }
   8965  }
   8966 
   8967  args.rval().setInt32(length);
   8968  return true;
   8969 }
   8970 
   8971 static const char* ObserveMarkColor(const Value& value) {
   8972  if (value.isUndefined()) {
   8973    return "dead";
   8974  }
   8975 
   8976  gc::Cell* cell = value.toGCThing();
   8977  Zone* zone = cell->zone();
   8978  if (zone->isGCPreparing()) {
   8979    // The mark bits are not valid during unmarking.
   8980    return "unmarked";
   8981  }
   8982 
   8983  gc::TenuredCell* tc = &cell->asTenured();
   8984  if (tc->isMarkedGray()) {
   8985    return "gray";
   8986  }
   8987 
   8988  if (tc->isMarkedBlack()) {
   8989    return "black";
   8990  }
   8991 
   8992  return "unmarked";
   8993 }
   8994 
   8995 static bool GetMarks(JSContext* cx, unsigned argc, Value* vp) {
   8996  CallArgs args = CallArgsFromVp(argc, vp);
   8997 
   8998  auto& observers = GetShellContext(cx)->markObservers;
   8999  if (!observers) {
   9000    args.rval().setUndefined();
   9001    return true;
   9002  }
   9003 
   9004  size_t length = observers->get().length();
   9005  Rooted<ArrayObject*> ret(cx, js::NewDenseEmptyArray(cx));
   9006  if (!ret) {
   9007    return false;
   9008  }
   9009 
   9010  for (uint32_t i = 0; i < length; i++) {
   9011    Value value = observers->get()[i];
   9012    const char* color = ObserveMarkColor(value);
   9013    JSString* s = JS_NewStringCopyZ(cx, color);
   9014    if (!s || !NewbornArrayPush(cx, ret, StringValue(s))) {
   9015      return false;
   9016    }
   9017  }
   9018 
   9019  args.rval().setObject(*ret);
   9020  return true;
   9021 }
   9022 
   9023 #ifndef __wasi__
   9024 static bool WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) {
   9025  CallArgs args = CallArgsFromVp(argc, vp);
   9026  RootedObject callee(cx, &args.callee());
   9027 
   9028  if (!args.requireAtLeast(cx, "wasmTextToBinary", 1)) {
   9029    return false;
   9030  }
   9031 
   9032  if (!args[0].isString()) {
   9033    ReportUsageErrorASCII(cx, callee, "First argument must be a String");
   9034    return false;
   9035  }
   9036 
   9037  size_t textLen = args[0].toString()->length();
   9038 
   9039  AutoStableStringChars twoByteChars(cx);
   9040  if (!twoByteChars.initTwoByte(cx, args[0].toString())) {
   9041    return false;
   9042  }
   9043 
   9044  wasm::Bytes bytes;
   9045  UniqueChars error;
   9046  if (!wasm::TextToBinary(twoByteChars.twoByteChars(), textLen, &bytes,
   9047                          &error)) {
   9048    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
   9049                             error.get() ? error.get() : "out of memory");
   9050    return false;
   9051  }
   9052 
   9053  RootedObject binary(cx, JS_NewUint8Array(cx, bytes.length()));
   9054  if (!binary) {
   9055    return false;
   9056  }
   9057 
   9058  memcpy(binary->as<TypedArrayObject>().dataPointerUnshared(), bytes.begin(),
   9059         bytes.length());
   9060 
   9061  args.rval().setObject(*binary);
   9062  return true;
   9063 }
   9064 
   9065 #  ifndef __AFL_HAVE_MANUAL_CONTROL
   9066 #    define __AFL_LOOP(x) true
   9067 #  endif
   9068 
   9069 static bool WasmLoop(JSContext* cx, unsigned argc, Value* vp) {
   9070  CallArgs args = CallArgsFromVp(argc, vp);
   9071  RootedObject callee(cx, &args.callee());
   9072 
   9073  if (args.length() < 1 || args.length() > 2) {
   9074    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   9075    return false;
   9076  }
   9077 
   9078  if (!args[0].isString()) {
   9079    ReportUsageErrorASCII(cx, callee, "First argument must be a String");
   9080    return false;
   9081  }
   9082 
   9083  RootedObject importObj(cx);
   9084  if (!args.get(1).isUndefined()) {
   9085    if (!args.get(1).isObject()) {
   9086      ReportUsageErrorASCII(cx, callee,
   9087                            "Second argument, if present, must be an Object");
   9088      return false;
   9089    }
   9090    importObj = &args[1].toObject();
   9091  }
   9092 
   9093  RootedString givenPath(cx, args[0].toString());
   9094  RootedString filename(cx, ResolvePath(cx, givenPath, RootRelative));
   9095  if (!filename) {
   9096    return false;
   9097  }
   9098 
   9099  while (__AFL_LOOP(1000)) {
   9100    Rooted<JSObject*> ret(cx, FileAsTypedArray(cx, filename));
   9101    if (!ret) {
   9102      return false;
   9103    }
   9104 
   9105    Rooted<TypedArrayObject*> typedArray(cx, &ret->as<TypedArrayObject>());
   9106    Rooted<WasmInstanceObject*> instanceObj(cx);
   9107    if (!wasm::Eval(cx, typedArray, importObj, &instanceObj)) {
   9108      // Clear any pending exceptions, we don't care about them
   9109      cx->clearPendingException();
   9110    }
   9111  }
   9112 
   9113 #  ifdef __AFL_HAVE_MANUAL_CONTROL  // to silence unreachable code warning
   9114  return true;
   9115 #  endif
   9116 }
   9117 #endif  // __wasi__
   9118 
   9119 static constexpr uint32_t DOM_OBJECT_SLOT = 0;
   9120 static constexpr uint32_t DOM_OBJECT_SLOT2 = 1;
   9121 
   9122 static const JSClass* GetDomClass();
   9123 
   9124 static JSObject* GetDOMPrototype(JSContext* cx, JSObject* global);
   9125 
   9126 static void TransplantableDOMObject_finalize(JS::GCContext* gcx,
   9127                                             JSObject* obj) {
   9128  // Dummy finalize method so we can swap with background finalized object.
   9129 }
   9130 
   9131 static const JSClassOps TransplantableDOMObjectClassOps = {
   9132    nullptr,  // addProperty
   9133    nullptr,  // delProperty
   9134    nullptr,  // enumerate
   9135    nullptr,  // newEnumerate
   9136    nullptr,  // resolve
   9137    nullptr,  // mayResolve
   9138    TransplantableDOMObject_finalize,
   9139    nullptr,  // call
   9140    nullptr,  // construct
   9141    nullptr,
   9142 };
   9143 
   9144 static const JSClass TransplantableDOMObjectClass = {
   9145    "TransplantableDOMObject",
   9146    JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1) |
   9147        JSCLASS_BACKGROUND_FINALIZE,
   9148    &TransplantableDOMObjectClassOps};
   9149 
   9150 static const JSClass TransplantableDOMProxyObjectClass =
   9151    PROXY_CLASS_DEF("TransplantableDOMProxyObject",
   9152                    JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1));
   9153 
   9154 class TransplantableDOMProxyHandler final : public ForwardingProxyHandler {
   9155 public:
   9156  static const TransplantableDOMProxyHandler singleton;
   9157  static const char family;
   9158 
   9159  constexpr TransplantableDOMProxyHandler() : ForwardingProxyHandler(&family) {}
   9160 
   9161  // These two proxy traps are called in |js::DeadProxyTargetValue|, which in
   9162  // turn is called when nuking proxies. Because this proxy can temporarily be
   9163  // without an object in its private slot, see |EnsureExpandoObject|, the
   9164  // default implementation inherited from ForwardingProxyHandler can't be used,
   9165  // since it tries to derive the callable/constructible value from the target.
   9166  bool isCallable(JSObject* obj) const override { return false; }
   9167  bool isConstructor(JSObject* obj) const override { return false; }
   9168 
   9169  // Simplified implementation of |DOMProxyHandler::GetAndClearExpandoObject|.
   9170  static JSObject* GetAndClearExpandoObject(JSObject* obj) {
   9171    const Value& v = GetProxyPrivate(obj);
   9172    if (v.isUndefined()) {
   9173      return nullptr;
   9174    }
   9175 
   9176    JSObject* expandoObject = &v.toObject();
   9177    SetProxyPrivate(obj, UndefinedValue());
   9178    return expandoObject;
   9179  }
   9180 
   9181  // Simplified implementation of |DOMProxyHandler::EnsureExpandoObject|.
   9182  static JSObject* EnsureExpandoObject(JSContext* cx, JS::HandleObject obj) {
   9183    const Value& v = GetProxyPrivate(obj);
   9184    if (v.isObject()) {
   9185      return &v.toObject();
   9186    }
   9187    MOZ_ASSERT(v.isUndefined());
   9188 
   9189    JSObject* expando = JS_NewObjectWithGivenProto(cx, nullptr, nullptr);
   9190    if (!expando) {
   9191      return nullptr;
   9192    }
   9193    SetProxyPrivate(obj, ObjectValue(*expando));
   9194    return expando;
   9195  }
   9196 };
   9197 
   9198 const TransplantableDOMProxyHandler TransplantableDOMProxyHandler::singleton;
   9199 const char TransplantableDOMProxyHandler::family = 0;
   9200 
   9201 enum TransplantObjectSlots {
   9202  TransplantSourceObject = 0,
   9203 };
   9204 
   9205 static bool TransplantObject(JSContext* cx, unsigned argc, Value* vp) {
   9206  CallArgs args = CallArgsFromVp(argc, vp);
   9207  RootedFunction callee(cx, &args.callee().as<JSFunction>());
   9208 
   9209  if (args.length() != 1 || !args[0].isObject()) {
   9210    JS_ReportErrorASCII(cx, "transplant() must be called with an object");
   9211    return false;
   9212  }
   9213 
   9214  // |newGlobal| needs to be a GlobalObject.
   9215  RootedObject newGlobal(
   9216      cx, js::CheckedUnwrapDynamic(&args[0].toObject(), cx,
   9217                                   /* stopAtWindowProxy = */ false));
   9218  if (!newGlobal) {
   9219    ReportAccessDenied(cx);
   9220    return false;
   9221  }
   9222  if (!JS_IsGlobalObject(newGlobal)) {
   9223    JS_ReportErrorNumberASCII(
   9224        cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
   9225        "\"global\" passed to transplant()", "not a global object");
   9226    return false;
   9227  }
   9228 
   9229  const Value& reserved =
   9230      GetFunctionNativeReserved(callee, TransplantSourceObject);
   9231  RootedObject source(cx, CheckedUnwrapStatic(&reserved.toObject()));
   9232  if (!source) {
   9233    ReportAccessDenied(cx);
   9234    return false;
   9235  }
   9236  if (JS_IsDeadWrapper(source)) {
   9237    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
   9238    return false;
   9239  }
   9240  MOZ_ASSERT(source->getClass()->isDOMClass());
   9241 
   9242  // The following steps aim to replicate the behavior of UpdateReflectorGlobal
   9243  // in dom/bindings/BindingUtils.cpp. In detail:
   9244  // 1. Check the recursion depth using checkConservative.
   9245  // 2. Enter the target compartment.
   9246  // 3. Clone the source object using JS_CloneObject.
   9247  // 4. Check if new wrappers can be created if source and target are in
   9248  //    different compartments.
   9249  // 5. Copy all properties from source to a temporary holder object.
   9250  // 6. Actually transplant the object.
   9251  // 7. And finally copy the properties back to the source object.
   9252  //
   9253  // As an extension to the algorithm in UpdateReflectorGlobal, we also allow
   9254  // to transplant an object into the same compartment as the source object to
   9255  // cover all operations supported by JS_TransplantObject.
   9256 
   9257  AutoCheckRecursionLimit recursion(cx);
   9258  if (!recursion.checkConservative(cx)) {
   9259    return false;
   9260  }
   9261 
   9262  bool isProxy = IsProxy(source);
   9263  RootedObject expandoObject(cx);
   9264  if (isProxy) {
   9265    expandoObject =
   9266        TransplantableDOMProxyHandler::GetAndClearExpandoObject(source);
   9267  }
   9268 
   9269  JSAutoRealm ar(cx, newGlobal);
   9270 
   9271  RootedObject proto(cx);
   9272  if (JS::GetClass(source) == GetDomClass()) {
   9273    proto = GetDOMPrototype(cx, newGlobal);
   9274  } else {
   9275    proto = JS::GetRealmObjectPrototype(cx);
   9276  }
   9277  if (!proto) {
   9278    return false;
   9279  }
   9280 
   9281  RootedObject target(cx, JS_CloneObject(cx, source, proto));
   9282  if (!target) {
   9283    return false;
   9284  }
   9285 
   9286  if (JS::GetCompartment(source) != JS::GetCompartment(target) &&
   9287      !AllowNewWrapper(JS::GetCompartment(source), target)) {
   9288    JS_ReportErrorASCII(cx, "Cannot transplant into nuked compartment");
   9289    return false;
   9290  }
   9291 
   9292  RootedObject copyFrom(cx, isProxy ? expandoObject : source);
   9293  RootedObject propertyHolder(cx,
   9294                              JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
   9295  if (!propertyHolder) {
   9296    return false;
   9297  }
   9298 
   9299  if (!JS_CopyOwnPropertiesAndPrivateFields(cx, propertyHolder, copyFrom)) {
   9300    return false;
   9301  }
   9302 
   9303  JS::SetReservedSlot(target, DOM_OBJECT_SLOT,
   9304                      JS::GetReservedSlot(source, DOM_OBJECT_SLOT));
   9305  JS::SetReservedSlot(source, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
   9306  if (JS::GetClass(source) == GetDomClass()) {
   9307    JS::SetReservedSlot(target, DOM_OBJECT_SLOT2,
   9308                        JS::GetReservedSlot(source, DOM_OBJECT_SLOT2));
   9309    JS::SetReservedSlot(source, DOM_OBJECT_SLOT2, UndefinedValue());
   9310  }
   9311 
   9312  source = JS_TransplantObject(cx, source, target);
   9313  if (!source) {
   9314    return false;
   9315  }
   9316 
   9317  RootedObject copyTo(cx);
   9318  if (isProxy) {
   9319    copyTo = TransplantableDOMProxyHandler::EnsureExpandoObject(cx, source);
   9320    if (!copyTo) {
   9321      return false;
   9322    }
   9323  } else {
   9324    copyTo = source;
   9325  }
   9326  if (!JS_CopyOwnPropertiesAndPrivateFields(cx, copyTo, propertyHolder)) {
   9327    return false;
   9328  }
   9329 
   9330  args.rval().setUndefined();
   9331  return true;
   9332 }
   9333 
   9334 static bool TransplantableObject(JSContext* cx, unsigned argc, Value* vp) {
   9335  CallArgs args = CallArgsFromVp(argc, vp);
   9336  RootedObject callee(cx, &args.callee());
   9337 
   9338  if (args.length() > 1) {
   9339    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   9340    return false;
   9341  }
   9342 
   9343  bool createProxy = false;
   9344  RootedObject source(cx);
   9345  if (args.length() == 1 && !args[0].isUndefined()) {
   9346    if (!args[0].isObject()) {
   9347      ReportUsageErrorASCII(cx, callee, "Argument must be an object");
   9348      return false;
   9349    }
   9350 
   9351    RootedObject options(cx, &args[0].toObject());
   9352    RootedValue value(cx);
   9353 
   9354    if (!JS_GetProperty(cx, options, "proxy", &value)) {
   9355      return false;
   9356    }
   9357    createProxy = JS::ToBoolean(value);
   9358 
   9359    if (!JS_GetProperty(cx, options, "object", &value)) {
   9360      return false;
   9361    }
   9362    if (!value.isUndefined()) {
   9363      if (!value.isObject()) {
   9364        ReportUsageErrorASCII(cx, callee, "'object' option must be an object");
   9365        return false;
   9366      }
   9367 
   9368      source = &value.toObject();
   9369      if (JS::GetClass(source) != GetDomClass()) {
   9370        ReportUsageErrorASCII(cx, callee, "Object not a FakeDOMObject");
   9371        return false;
   9372      }
   9373 
   9374      // |source| must be a tenured object to be transplantable.
   9375      if (gc::IsInsideNursery(source)) {
   9376        JS_GC(cx);
   9377 
   9378        MOZ_ASSERT(!gc::IsInsideNursery(source),
   9379                   "Live objects should be tenured after one GC, because "
   9380                   "the nursery has only a single generation");
   9381      }
   9382    }
   9383  }
   9384 
   9385  if (!source) {
   9386    if (!createProxy) {
   9387      source = NewBuiltinClassInstance(cx, &TransplantableDOMObjectClass,
   9388                                       TenuredObject);
   9389      if (!source) {
   9390        return false;
   9391      }
   9392 
   9393      JS::SetReservedSlot(source, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
   9394    } else {
   9395      JSObject* expando = JS_NewPlainObject(cx);
   9396      if (!expando) {
   9397        return false;
   9398      }
   9399      RootedValue expandoVal(cx, ObjectValue(*expando));
   9400 
   9401      ProxyOptions options;
   9402      options.setClass(&TransplantableDOMProxyObjectClass);
   9403      options.setLazyProto(true);
   9404 
   9405      source = NewProxyObject(cx, &TransplantableDOMProxyHandler::singleton,
   9406                              expandoVal, nullptr, options);
   9407      if (!source) {
   9408        return false;
   9409      }
   9410 
   9411      SetProxyReservedSlot(source, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
   9412    }
   9413  }
   9414 
   9415  jsid emptyId = NameToId(cx->names().empty_);
   9416  RootedObject transplant(
   9417      cx, NewFunctionByIdWithReserved(cx, TransplantObject, 0, 0, emptyId));
   9418  if (!transplant) {
   9419    return false;
   9420  }
   9421 
   9422  SetFunctionNativeReserved(transplant, TransplantSourceObject,
   9423                            ObjectValue(*source));
   9424 
   9425  RootedObject result(cx, JS_NewPlainObject(cx));
   9426  if (!result) {
   9427    return false;
   9428  }
   9429 
   9430  RootedValue sourceVal(cx, ObjectValue(*source));
   9431  RootedValue transplantVal(cx, ObjectValue(*transplant));
   9432  if (!JS_DefineProperty(cx, result, "object", sourceVal, 0) ||
   9433      !JS_DefineProperty(cx, result, "transplant", transplantVal, 0)) {
   9434    return false;
   9435  }
   9436 
   9437  args.rval().setObject(*result);
   9438  return true;
   9439 }
   9440 
   9441 #ifdef DEBUG
   9442 static bool DebugGetQueuedJobs(JSContext* cx, unsigned argc, Value* vp) {
   9443  CallArgs args = CallArgsFromVp(argc, vp);
   9444 
   9445  JSObject* jobs = js::GetJobsInInternalJobQueue(cx);
   9446  if (!jobs) {
   9447    return false;
   9448  }
   9449 
   9450  args.rval().setObject(*jobs);
   9451  return true;
   9452 }
   9453 #endif
   9454 
   9455 #ifdef FUZZING_INTERFACES
   9456 extern "C" {
   9457 size_t gluesmith(uint8_t* data, size_t size, uint8_t* out, size_t maxsize);
   9458 }
   9459 
   9460 static bool GetWasmSmithModule(JSContext* cx, unsigned argc, Value* vp) {
   9461  CallArgs args = CallArgsFromVp(argc, vp);
   9462  RootedObject callee(cx, &args.callee());
   9463 
   9464  if (args.length() != 1) {
   9465    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   9466    return false;
   9467  }
   9468 
   9469  if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) {
   9470    ReportUsageErrorASCII(cx, callee, "Argument must be ArrayBuffer.");
   9471    return false;
   9472  }
   9473 
   9474  ArrayBufferObject* arrayBuffer = &args[0].toObject().as<ArrayBufferObject>();
   9475  size_t length = arrayBuffer->byteLength();
   9476  uint8_t* data = arrayBuffer->dataPointer();
   9477 
   9478  const size_t maxModuleSize = 4096;
   9479  uint8_t tmp[maxModuleSize];
   9480 
   9481  size_t outSize = gluesmith(data, length, tmp, maxModuleSize);
   9482  if (!outSize) {
   9483    JS_ReportErrorASCII(cx, "Generated module is too large.");
   9484    return false;
   9485  }
   9486 
   9487  JS::Rooted<JSObject*> outArr(cx, JS_NewUint8ClampedArray(cx, outSize));
   9488  if (!outArr) {
   9489    return false;
   9490  }
   9491 
   9492  {
   9493    JS::AutoCheckCannotGC nogc;
   9494    bool isShared;
   9495    uint8_t* data = JS_GetUint8ClampedArrayData(outArr, &isShared, nogc);
   9496    MOZ_RELEASE_ASSERT(!isShared);
   9497    memcpy(data, tmp, outSize);
   9498  }
   9499 
   9500  args.rval().setObject(*outArr);
   9501  return true;
   9502 }
   9503 
   9504 #endif
   9505 
   9506 static bool IsValidJSON(JSContext* cx, unsigned argc, Value* vp) {
   9507  CallArgs args = CallArgsFromVp(argc, vp);
   9508  RootedObject callee(cx, &args.callee());
   9509 
   9510  if (!args.get(0).isString()) {
   9511    ReportUsageErrorASCII(cx, callee, "First argument must be a String");
   9512    return false;
   9513  }
   9514 
   9515  JS::Rooted<JSLinearString*> input(cx, args[0].toString()->ensureLinear(cx));
   9516  if (!input) {
   9517    return false;
   9518  }
   9519 
   9520  bool result;
   9521  if (input->hasLatin1Chars()) {
   9522    JS::AutoCheckCannotGC nogc;
   9523    result = JS::IsValidJSON(input->latin1Chars(nogc), input->length());
   9524  } else {
   9525    JS::AutoCheckCannotGC nogc;
   9526    result = JS::IsValidJSON(input->twoByteChars(nogc), input->length());
   9527  }
   9528 
   9529  args.rval().setBoolean(result);
   9530  return true;
   9531 }
   9532 
   9533 // Quick file format for a LZ4 compressed file
   9534 static constexpr uint32_t LZ4MagicHeader = -1;
   9535 // A magic word and a length field
   9536 static constexpr size_t LZ4HeaderSize = sizeof(uint32_t) * 2;
   9537 static constexpr size_t LZ4MaxSize = UINT32_MAX;
   9538 
   9539 static bool CompressLZ4(JSContext* cx, unsigned argc, Value* vp) {
   9540  CallArgs args = CallArgsFromVp(argc, vp);
   9541  RootedObject callee(cx, &args.callee());
   9542 
   9543  if (!args.get(0).isObject() ||
   9544      !args.get(0).toObject().is<ArrayBufferObject>()) {
   9545    ReportUsageErrorASCII(cx, callee, "First argument must be an ArrayBuffer");
   9546    return false;
   9547  }
   9548 
   9549  JS::Rooted<ArrayBufferObject*> bytes(
   9550      cx, &args.get(0).toObject().as<ArrayBufferObject>());
   9551  size_t byteLength = bytes->byteLength();
   9552 #ifdef JS_64BIT
   9553  if (byteLength > LZ4MaxSize) {
   9554    ReportOutOfMemory(cx);
   9555    return false;
   9556  }
   9557 #else
   9558  static_assert(LZ4MaxSize == UINT32_MAX, "don't need to check max on 32-bit");
   9559 #endif
   9560 
   9561  // Create a buffer big enough for the header and the max amount of compressed
   9562  // bytes.
   9563  size_t outputCapacity =
   9564      LZ4HeaderSize + mozilla::Compression::LZ4::maxCompressedSize(byteLength);
   9565 
   9566  mozilla::UniquePtr<void, JS::FreePolicy> output(js_malloc(outputCapacity));
   9567  if (!output) {
   9568    ReportOutOfMemory(cx);
   9569    return false;
   9570  }
   9571 
   9572  // Write the magic header word and decompressed size in bytes.
   9573  ((uint32_t*)(output.get()))[0] = LZ4MagicHeader;
   9574  ((uint32_t*)(output.get()))[1] = byteLength;
   9575 
   9576  // Compress the bytes into the output
   9577  char* compressedBytesStart = ((char*)output.get()) + LZ4HeaderSize;
   9578  size_t compressedBytesLength = mozilla::Compression::LZ4::compress(
   9579      (const char*)bytes->dataPointer(), byteLength, compressedBytesStart);
   9580  size_t outputLength = compressedBytesLength + LZ4HeaderSize;
   9581 
   9582  // Create an ArrayBuffer wrapping the compressed bytes
   9583  JSObject* outputArrayBuffer =
   9584      NewArrayBufferWithContents(cx, outputLength, std::move(output));
   9585  if (!outputArrayBuffer) {
   9586    return false;
   9587  }
   9588 
   9589  args.rval().setObject(*outputArrayBuffer);
   9590  return true;
   9591 }
   9592 
   9593 static bool DecompressLZ4(JSContext* cx, unsigned argc, Value* vp) {
   9594  CallArgs args = CallArgsFromVp(argc, vp);
   9595  RootedObject callee(cx, &args.callee());
   9596 
   9597  if (!args.get(0).isObject() ||
   9598      !args.get(0).toObject().is<ArrayBufferObject>()) {
   9599    ReportUsageErrorASCII(cx, callee, "First argument must be an ArrayBuffer");
   9600    return false;
   9601  }
   9602 
   9603  JS::Rooted<ArrayBufferObject*> bytes(
   9604      cx, &args.get(0).toObject().as<ArrayBufferObject>());
   9605  size_t byteLength = bytes->byteLength();
   9606  if (byteLength < LZ4HeaderSize) {
   9607    JS_ReportErrorASCII(cx, "Invalid LZ4 buffer");
   9608    return false;
   9609  }
   9610 
   9611  // Check the magic header and get the decompressed byte length.
   9612  uint32_t magicHeader = ((uint32_t*)(bytes->dataPointer()))[0];
   9613  uint32_t decompressedBytesLength = ((uint32_t*)(bytes->dataPointer()))[1];
   9614  if (magicHeader != LZ4MagicHeader) {
   9615    JS_ReportErrorASCII(cx, "Invalid magic header");
   9616    return false;
   9617  }
   9618 
   9619  // Allocate a buffer to store the decompressed bytes.
   9620  mozilla::UniquePtr<void, JS::FreePolicy> decompressedBytes(
   9621      js_malloc(decompressedBytesLength));
   9622  if (!decompressedBytes) {
   9623    ReportOutOfMemory(cx);
   9624    return false;
   9625  }
   9626 
   9627  // Decompress the bytes into the output
   9628  const char* compressedBytesStart =
   9629      ((const char*)bytes->dataPointer()) + LZ4HeaderSize;
   9630  size_t compressedBytesLength = byteLength - LZ4HeaderSize;
   9631  size_t actualDecompressedBytesLength = 0;
   9632  if (!mozilla::Compression::LZ4::decompress(
   9633          compressedBytesStart, compressedBytesLength,
   9634          (char*)decompressedBytes.get(), decompressedBytesLength,
   9635          &actualDecompressedBytesLength) ||
   9636      actualDecompressedBytesLength != decompressedBytesLength) {
   9637    JS_ReportErrorASCII(cx, "Invalid LZ4 buffer");
   9638    return false;
   9639  }
   9640 
   9641  // Create an ArrayBuffer wrapping the decompressed bytes
   9642  JSObject* outputArrayBuffer = NewArrayBufferWithContents(
   9643      cx, decompressedBytesLength, std::move(decompressedBytes));
   9644  if (!outputArrayBuffer) {
   9645    return false;
   9646  }
   9647 
   9648  args.rval().setObject(*outputArrayBuffer);
   9649  return true;
   9650 }
   9651 
   9652 static bool SideEffectfulResolveObject_enumerate(
   9653    JSContext* cx, JS::HandleObject obj, JS::MutableHandleIdVector properties,
   9654    bool enumerableOnly) {
   9655  return properties.append(NameToId(cx->names().test));
   9656 }
   9657 
   9658 static bool SideEffectfulResolveObject_resolve(JSContext* cx, HandleObject obj,
   9659                                               HandleId id, bool* resolvedp) {
   9660  *resolvedp = false;
   9661  if (JS::dbg::ShouldAvoidSideEffects(cx)) {
   9662    JS::ReportUncatchableException(cx);
   9663    return false;
   9664  }
   9665 
   9666  if (id == NameToId(cx->names().test)) {
   9667    RootedValue value(cx, JS::NumberValue(42));
   9668    if (!JS_DefinePropertyById(cx, obj, id, value, JSPROP_ENUMERATE)) {
   9669      return false;
   9670    }
   9671    *resolvedp = true;
   9672  }
   9673 
   9674  return true;
   9675 }
   9676 
   9677 static const JSClassOps SideEffectfulResolveObject_classOps = {
   9678    nullptr,                               // addProperty
   9679    nullptr,                               // delProperty
   9680    nullptr,                               // enumerate
   9681    SideEffectfulResolveObject_enumerate,  // newEnumerate
   9682    SideEffectfulResolveObject_resolve,    // resolve
   9683    nullptr,                               // mayResolve
   9684    nullptr,                               // finalize
   9685    nullptr,                               // call
   9686    nullptr,                               // construct
   9687    nullptr,
   9688 };
   9689 
   9690 static const JSClass SideEffectfulResolveObject_class = {
   9691    "SideEffectfulResolveObject",
   9692    0,
   9693    &SideEffectfulResolveObject_classOps,
   9694 };
   9695 
   9696 static bool CreateSideEffectfulResolveObject(JSContext* cx, unsigned argc,
   9697                                             JS::Value* vp) {
   9698  CallArgs args = CallArgsFromVp(argc, vp);
   9699 
   9700  RootedObject obj(cx, JS_NewObject(cx, &SideEffectfulResolveObject_class));
   9701  if (!obj) {
   9702    return false;
   9703  }
   9704 
   9705  args.rval().setObject(*obj);
   9706  return true;
   9707 }
   9708 
   9709 #ifdef MOZ_EXECUTION_TRACING
   9710 
   9711 static bool EnableExecutionTracing(JSContext* cx, unsigned argc,
   9712                                   JS::Value* vp) {
   9713  CallArgs args = CallArgsFromVp(argc, vp);
   9714 
   9715  if (!JS_TracerBeginTracing(cx)) {
   9716    return false;
   9717  }
   9718 
   9719  args.rval().setUndefined();
   9720  return true;
   9721 }
   9722 
   9723 static bool GetExecutionTrace(JSContext* cx, unsigned argc, JS::Value* vp) {
   9724  CallArgs args = CallArgsFromVp(argc, vp);
   9725 
   9726  JS::ExecutionTrace trace;
   9727  if (!JS_TracerSnapshotTrace(trace)) {
   9728    JS_ReportErrorASCII(cx, "Failed to get the execution trace");
   9729    return false;
   9730  }
   9731 
   9732  JS::Rooted<JSString*> functionEnterStr(cx,
   9733                                         JS_AtomizeString(cx, "FunctionEnter"));
   9734  if (!functionEnterStr) {
   9735    return false;
   9736  }
   9737  JS::Rooted<JSString*> functionLeaveStr(cx,
   9738                                         JS_AtomizeString(cx, "FunctionLeave"));
   9739  if (!functionLeaveStr) {
   9740    return false;
   9741  }
   9742  JS::Rooted<JSString*> labelEnterStr(cx, JS_AtomizeString(cx, "LabelEnter"));
   9743  if (!labelEnterStr) {
   9744    return false;
   9745  }
   9746  JS::Rooted<JSString*> labelLeaveStr(cx, JS_AtomizeString(cx, "LabelLeave"));
   9747  if (!labelLeaveStr) {
   9748    return false;
   9749  }
   9750  JS::Rooted<JSString*> errorStr(cx, JS_AtomizeString(cx, "Error"));
   9751  if (!errorStr) {
   9752    return false;
   9753  }
   9754 
   9755  JS::Rooted<JSString*> interpreterStr(cx, JS_AtomizeString(cx, "interpreter"));
   9756  if (!interpreterStr) {
   9757    return false;
   9758  }
   9759  JS::Rooted<JSString*> baselineStr(cx, JS_AtomizeString(cx, "baseline"));
   9760  if (!baselineStr) {
   9761    return false;
   9762  }
   9763  JS::Rooted<JSString*> ionStr(cx, JS_AtomizeString(cx, "ion"));
   9764  if (!ionStr) {
   9765    return false;
   9766  }
   9767  JS::Rooted<JSString*> wasmStr(cx, JS_AtomizeString(cx, "wasm"));
   9768  if (!wasmStr) {
   9769    return false;
   9770  }
   9771 
   9772  JS::Rooted<JSString*> kindStr(cx, JS_AtomizeString(cx, "kind"));
   9773  if (!kindStr) {
   9774    return false;
   9775  }
   9776  JS::Rooted<JS::PropertyKey> kindId(cx, JS::PropertyKey::NonIntAtom(kindStr));
   9777 
   9778  JS::Rooted<JSString*> implementationStr(
   9779      cx, JS_AtomizeString(cx, "implementation"));
   9780  if (!implementationStr) {
   9781    return false;
   9782  }
   9783  JS::Rooted<JS::PropertyKey> implementationId(
   9784      cx, JS::PropertyKey::NonIntAtom(implementationStr));
   9785 
   9786  JS::Rooted<JSString*> timeStr(cx, JS_AtomizeString(cx, "time"));
   9787  if (!timeStr) {
   9788    return false;
   9789  }
   9790  JS::Rooted<JS::PropertyKey> timeId(cx, JS::PropertyKey::NonIntAtom(timeStr));
   9791 
   9792  JS::Rooted<JSString*> eventsStr(cx, JS_AtomizeString(cx, "events"));
   9793  if (!eventsStr) {
   9794    return false;
   9795  }
   9796  JS::Rooted<JS::PropertyKey> eventsId(cx,
   9797                                       JS::PropertyKey::NonIntAtom(eventsStr));
   9798 
   9799  JS::Rooted<JSString*> valueBufferStr(cx, JS_AtomizeString(cx, "valueBuffer"));
   9800  if (!valueBufferStr) {
   9801    return false;
   9802  }
   9803  JS::Rooted<JS::PropertyKey> valueBufferId(
   9804      cx, JS::PropertyKey::NonIntAtom(valueBufferStr));
   9805 
   9806  JS::Rooted<JSString*> shapeSummariesStr(
   9807      cx, JS_AtomizeString(cx, "shapeSummaries"));
   9808  if (!shapeSummariesStr) {
   9809    return false;
   9810  }
   9811  JS::Rooted<JS::PropertyKey> shapeSummariesId(
   9812      cx, JS::PropertyKey::NonIntAtom(shapeSummariesStr));
   9813 
   9814  JS::Rooted<JSString*> numPropertiesStr(cx,
   9815                                         JS_AtomizeString(cx, "numProperties"));
   9816  if (!numPropertiesStr) {
   9817    return false;
   9818  }
   9819  JS::Rooted<JS::PropertyKey> numPropertiesId(
   9820      cx, JS::PropertyKey::NonIntAtom(numPropertiesStr));
   9821 
   9822  JS::Rooted<JS::PropertyKey> lineNumberId(cx,
   9823                                           NameToId(cx->names().lineNumber));
   9824  JS::Rooted<JS::PropertyKey> columnNumberId(
   9825      cx, NameToId(cx->names().columnNumber));
   9826  JS::Rooted<JS::PropertyKey> scriptId(cx, NameToId(cx->names().script));
   9827  JS::Rooted<JS::PropertyKey> nameId(cx, NameToId(cx->names().name));
   9828  JS::Rooted<JS::PropertyKey> labelId(cx, NameToId(cx->names().label));
   9829  JS::Rooted<JS::PropertyKey> valuesId(cx, NameToId(cx->names().values));
   9830  JS::Rooted<JSString*> realmIDStr(cx, JS_AtomizeString(cx, "realmID"));
   9831  if (!realmIDStr) {
   9832    return false;
   9833  }
   9834  JS::Rooted<JS::PropertyKey> realmIDId(
   9835      cx, JS::PropertyKey::NonIntAtom(realmIDStr));
   9836 
   9837  JS::Rooted<JSObject*> contextObj(cx);
   9838  JS::Rooted<ArrayObject*> eventsArray(cx);
   9839  JS::Rooted<JSObject*> shapeSummariesObj(cx);
   9840  JS::Rooted<JSObject*> eventObj(cx);
   9841  JS::Rooted<JSObject*> shapeSummaryObj(cx);
   9842  JS::Rooted<JSString*> str(cx);
   9843 
   9844  JS::Rooted<ArrayObject*> traceArray(cx, NewDenseEmptyArray(cx));
   9845  if (!traceArray) {
   9846    return false;
   9847  }
   9848 
   9849  for (const auto& context : trace.contexts) {
   9850    contextObj = JS_NewPlainObject(cx);
   9851    if (!contextObj) {
   9852      return false;
   9853    }
   9854 
   9855    shapeSummariesObj = JS_NewPlainObject(cx);
   9856    if (!shapeSummariesObj) {
   9857      return false;
   9858    }
   9859 
   9860    eventsArray = NewDenseEmptyArray(cx);
   9861    if (!eventsArray) {
   9862      return false;
   9863    }
   9864 
   9865    for (const auto& event : context.events) {
   9866      eventObj = JS_NewPlainObject(cx);
   9867      if (!eventObj) {
   9868        return false;
   9869      }
   9870 
   9871      switch (event.kind) {
   9872        case JS::ExecutionTrace::EventKind::FunctionEnter:
   9873          str = functionEnterStr;
   9874          break;
   9875        case JS::ExecutionTrace::EventKind::FunctionLeave:
   9876          str = functionLeaveStr;
   9877          break;
   9878        case JS::ExecutionTrace::EventKind::LabelEnter:
   9879          str = labelEnterStr;
   9880          break;
   9881        case JS::ExecutionTrace::EventKind::LabelLeave:
   9882          str = labelLeaveStr;
   9883          break;
   9884        case JS::ExecutionTrace::EventKind::Error:
   9885          str = errorStr;
   9886          break;
   9887        default:
   9888          MOZ_CRASH("Unexpected EventKind");
   9889          break;
   9890      }
   9891      if (!JS_DefinePropertyById(cx, eventObj, kindId, str, JSPROP_ENUMERATE)) {
   9892        return false;
   9893      }
   9894 
   9895      if (event.kind == JS::ExecutionTrace::EventKind::FunctionEnter ||
   9896          event.kind == JS::ExecutionTrace::EventKind::FunctionLeave) {
   9897        switch (event.functionEvent.implementation) {
   9898          case JS::ExecutionTrace::ImplementationType::Interpreter:
   9899            str = interpreterStr;
   9900            break;
   9901          case JS::ExecutionTrace::ImplementationType::Baseline:
   9902            str = baselineStr;
   9903            break;
   9904          case JS::ExecutionTrace::ImplementationType::Ion:
   9905            str = ionStr;
   9906            break;
   9907          case JS::ExecutionTrace::ImplementationType::Wasm:
   9908            str = wasmStr;
   9909            break;
   9910          default:
   9911            MOZ_CRASH("Unexpected ImplementationType");
   9912            break;
   9913        }
   9914        if (!JS_DefinePropertyById(cx, eventObj, implementationId, str,
   9915                                   JSPROP_ENUMERATE)) {
   9916          return false;
   9917        }
   9918 
   9919        if (!JS_DefinePropertyById(cx, eventObj, lineNumberId,
   9920                                   event.functionEvent.lineNumber,
   9921                                   JSPROP_ENUMERATE)) {
   9922          return false;
   9923        }
   9924 
   9925        if (!JS_DefinePropertyById(cx, eventObj, columnNumberId,
   9926                                   event.functionEvent.column,
   9927                                   JSPROP_ENUMERATE)) {
   9928          return false;
   9929        }
   9930 
   9931        if (auto p = context.scriptUrls.lookup(event.functionEvent.scriptId)) {
   9932          str = JS_NewStringCopyUTF8Z(
   9933              cx, JS::ConstUTF8CharsZ(trace.stringBuffer.begin() + p->value()));
   9934          if (!str) {
   9935            return false;
   9936          }
   9937 
   9938          if (!JS_DefinePropertyById(cx, eventObj, scriptId, str,
   9939                                     JSPROP_ENUMERATE)) {
   9940            return false;
   9941          }
   9942        } else {
   9943          if (!JS_DefinePropertyById(cx, eventObj, scriptId,
   9944                                     JS::UndefinedHandleValue,
   9945                                     JSPROP_ENUMERATE)) {
   9946            return false;
   9947          }
   9948        }
   9949 
   9950        if (!JS_DefinePropertyById(
   9951                cx, eventObj, realmIDId,
   9952                // We are converting a uint64_t into double which is lossy. But
   9953                // this is okay because Firefox makes sure to only use 53 bits
   9954                // so it can be converted to and from a JS value without loss of
   9955                // precision. Additionally we don't set the realmID in JS shell.
   9956                double(event.functionEvent.realmID), JSPROP_ENUMERATE)) {
   9957          return false;
   9958        }
   9959 
   9960        if (!JS_DefinePropertyById(cx, eventObj, valuesId,
   9961                                   event.functionEvent.values,
   9962                                   JSPROP_ENUMERATE)) {
   9963          return false;
   9964        }
   9965 
   9966        if (auto p = context.atoms.lookup(event.functionEvent.functionNameId)) {
   9967          str = JS_NewStringCopyUTF8Z(
   9968              cx, JS::ConstUTF8CharsZ(trace.stringBuffer.begin() + p->value()));
   9969          if (!str) {
   9970            return false;
   9971          }
   9972 
   9973          if (!JS_DefinePropertyById(cx, eventObj, nameId, str,
   9974                                     JSPROP_ENUMERATE)) {
   9975            return false;
   9976          }
   9977        } else {
   9978          if (!JS_DefinePropertyById(cx, eventObj, nameId,
   9979                                     JS::UndefinedHandleValue,
   9980                                     JSPROP_ENUMERATE)) {
   9981            return false;
   9982          }
   9983        }
   9984      } else if (event.kind == JS::ExecutionTrace::EventKind::LabelEnter ||
   9985                 event.kind == JS::ExecutionTrace::EventKind::LabelLeave) {
   9986        str = JS_NewStringCopyUTF8Z(
   9987            cx, JS::ConstUTF8CharsZ(trace.stringBuffer.begin() +
   9988                                    event.labelEvent.label));
   9989        if (!str) {
   9990          return false;
   9991        }
   9992 
   9993        if (!JS_DefinePropertyById(cx, eventObj, labelId, str,
   9994                                   JSPROP_ENUMERATE)) {
   9995          return false;
   9996        }
   9997      }
   9998 
   9999      if (event.kind != JS::ExecutionTrace::EventKind::Error) {
  10000        if (!JS_DefinePropertyById(cx, eventObj, timeId, event.time,
  10001                                   JSPROP_ENUMERATE)) {
  10002          return false;
  10003        }
  10004      }
  10005 
  10006      if (!NewbornArrayPush(cx, eventsArray, JS::ObjectValue(*eventObj))) {
  10007        return false;
  10008      }
  10009    }
  10010 
  10011    if (!JS_DefinePropertyById(cx, contextObj, eventsId, eventsArray,
  10012                               JSPROP_ENUMERATE)) {
  10013      return false;
  10014    }
  10015 
  10016    for (const auto& shapeSummary : context.shapeSummaries) {
  10017      shapeSummaryObj = NewDenseEmptyArray(cx);
  10018      if (!shapeSummaryObj) {
  10019        return false;
  10020      }
  10021 
  10022      // 1 for the class name + num listed properties
  10023      const uint32_t realCount =
  10024          1 + std::min(shapeSummary.numProperties,
  10025                       uint32_t(JS::ValueSummary::MAX_COLLECTION_VALUES));
  10026      size_t accumulatedOffset = 0;
  10027      for (uint32_t i = 0; i < realCount; i++) {
  10028        const char* entry = trace.stringBuffer.begin() +
  10029                            shapeSummary.stringBufferOffset + accumulatedOffset;
  10030        size_t length = strlen(entry);
  10031        str = JS_NewStringCopyUTF8Z(cx, JS::ConstUTF8CharsZ(entry, length));
  10032        if (!str) {
  10033          return false;
  10034        }
  10035        accumulatedOffset += length + 1;
  10036 
  10037        if (!NewbornArrayPush(cx, shapeSummaryObj, JS::StringValue(str))) {
  10038          return false;
  10039        }
  10040      }
  10041 
  10042      if (!JS_DefinePropertyById(cx, shapeSummaryObj, numPropertiesId,
  10043                                 shapeSummary.numProperties,
  10044                                 JSPROP_ENUMERATE)) {
  10045        return false;
  10046      }
  10047 
  10048      if (!JS_DefineElement(cx, shapeSummariesObj, shapeSummary.id,
  10049                            shapeSummaryObj, JSPROP_ENUMERATE)) {
  10050        return false;
  10051      }
  10052    }
  10053 
  10054    if (!JS_DefinePropertyById(cx, contextObj, shapeSummariesId,
  10055                               shapeSummariesObj, JSPROP_ENUMERATE)) {
  10056      return false;
  10057    }
  10058 
  10059    size_t valuesLength = context.valueBuffer.length();
  10060    mozilla::UniquePtr<uint8_t[], JS::FreePolicy> valueBuffer(
  10061        js_pod_malloc<uint8_t>(valuesLength));
  10062    if (!valueBuffer) {
  10063      return false;
  10064    }
  10065 
  10066    memcpy(valueBuffer.get(), context.valueBuffer.begin(), valuesLength);
  10067    JS::Rooted<JSObject*> valuesArrayBuffer(
  10068        cx, JS::NewArrayBufferWithContents(cx, valuesLength,
  10069                                           std::move(valueBuffer)));
  10070    if (!valuesArrayBuffer) {
  10071      return false;
  10072    }
  10073 
  10074    if (!JS_DefinePropertyById(cx, contextObj, valueBufferId, valuesArrayBuffer,
  10075                               JSPROP_ENUMERATE)) {
  10076      return false;
  10077    }
  10078 
  10079    if (!NewbornArrayPush(cx, traceArray, JS::ObjectValue(*contextObj))) {
  10080      return false;
  10081    }
  10082  }
  10083 
  10084  args.rval().setObject(*traceArray);
  10085  return true;
  10086 }
  10087 
  10088 static bool DisableExecutionTracing(JSContext* cx, unsigned argc,
  10089                                    JS::Value* vp) {
  10090  CallArgs args = CallArgsFromVp(argc, vp);
  10091 
  10092  if (!JS_TracerEndTracing(cx)) {
  10093    return false;
  10094  }
  10095 
  10096  args.rval().setUndefined();
  10097  return true;
  10098 }
  10099 
  10100 #endif  // MOZ_EXECUTION_TRACING
  10101 
  10102 // clang-format off
  10103 static const JSFunctionSpecWithHelp shell_functions[] = {
  10104    JS_FN_HELP("options", Options, 0, 0,
  10105 "options([option ...])",
  10106 "  Get or toggle JavaScript options."),
  10107 
  10108    JS_FN_HELP("load", Load, 1, 0,
  10109 "load(['foo.js' ...])",
  10110 "  Load files named by string arguments. Filename is relative to the\n"
  10111 "      current working directory."),
  10112 
  10113    JS_FN_HELP("loadRelativeToScript", LoadScriptRelativeToScript, 1, 0,
  10114 "loadRelativeToScript(['foo.js' ...])",
  10115 "  Load files named by string arguments. Filename is relative to the\n"
  10116 "      calling script."),
  10117 
  10118    JS_FN_HELP("evaluate", Evaluate, 2, 0,
  10119 "evaluate(code[, options])",
  10120 "  Evaluate code as though it were the contents of a file.\n"
  10121 "  options is an optional object that may have these properties:\n"
  10122 "      isRunOnce: use the isRunOnce compiler option (default: false)\n"
  10123 "      noScriptRval: use the no-script-rval compiler option (default: false)\n"
  10124 "      fileName: filename for error messages and debug info\n"
  10125 "      skipFileNameValidation: skip the filename-validation callback\n"
  10126 "      lineNumber: starting line number for error messages and debug info\n"
  10127 "      columnNumber: starting column number for error messages and debug info\n"
  10128 "      global: global in which to execute the code\n"
  10129 "      newContext: if true, create and use a new cx (default: false)\n"
  10130 "      catchTermination: if true, catch termination (failure without\n"
  10131 "         an exception value, as for slow scripts or out-of-memory)\n"
  10132 "         and return 'terminated'\n"
  10133 "      element: if present with value |v|, convert |v| to an object |o| and\n"
  10134 "         mark the source as being attached to the DOM element |o|. If the\n"
  10135 "         property is omitted or |v| is null, don't attribute the source to\n"
  10136 "         any DOM element.\n"
  10137 "      elementAttributeName: if present and not undefined, the name of\n"
  10138 "         property of 'element' that holds this code. This is what\n"
  10139 "         Debugger.Source.prototype.elementAttributeName returns.\n"
  10140 "      sourceMapURL: if present with value |v|, convert |v| to a string, and\n"
  10141 "         provide that as the code's source map URL. If omitted, attach no\n"
  10142 "         source map URL to the code (although the code may provide one itself,\n"
  10143 "         via a //#sourceMappingURL comment).\n"
  10144 "      sourceIsLazy: if present and true, indicates that, after compilation, \n"
  10145 "          script source should not be cached by the JS engine and should be \n"
  10146 "          lazily loaded from the embedding as-needed.\n"
  10147 "      forceFullParse: if present and true, disable syntax-parse.\n"
  10148 "      loadBytecode: if true, and if the source is a CacheEntryObject,\n"
  10149 "         the bytecode would be loaded and decoded from the cache entry instead\n"
  10150 "         of being parsed, then it would be executed as usual.\n"
  10151 "      saveBytecodeWithDelazifications: if true, and if the source is a\n"
  10152 "         CacheEntryObject, the delazifications are collected during the\n"
  10153 "         execution, and encoded after that, and saved into the cache entry.\n"
  10154 "      execute: if false, do not execute the script, but do parse and/or\n"
  10155 "               transcode.\n"
  10156 "      assertEqBytecode: if true, and if both loadBytecode and either\n"
  10157 "         saveBytecodeWithDelazifications is true, then the loaded\n"
  10158 "         bytecode and the encoded bytecode are compared.\n"
  10159 "         and an assertion is raised if they differ.\n"
  10160 "      envChainObject: object to put on the scope chain, with its fields added\n"
  10161 "         as var bindings, akin to how elements are added to the environment in\n"
  10162 "         event handlers in Gecko.\n"
  10163 "      supportUnscopables: if true, support Symbol.unscopables lookups for\n"
  10164 "         envChainObject, similar to (syntactic) with-statements.\n"
  10165 ),
  10166 
  10167    JS_FN_HELP("run", Run, 1, 0,
  10168 "run('foo.js')",
  10169 "  Run the file named by the first argument, returning the number of\n"
  10170 "  of milliseconds spent compiling and executing it."),
  10171 
  10172    JS_FN_HELP("readline", ReadLine, 0, 0,
  10173 "readline()",
  10174 "  Read a single line from stdin."),
  10175 
  10176    JS_FN_HELP("readlineBuf", ReadLineBuf, 1, 0,
  10177 "readlineBuf([ buf ])",
  10178 "  Emulate readline() on the specified string. The first call with a string\n"
  10179 "  argument sets the source buffer. Subsequent calls without an argument\n"
  10180 "  then read from this buffer line by line.\n"),
  10181 
  10182    JS_FN_HELP("print", Print, 0, 0,
  10183 "print([exp ...])",
  10184 "  Evaluate and print expressions to stdout."),
  10185 
  10186    JS_FN_HELP("printErr", PrintErr, 0, 0,
  10187 "printErr([exp ...])",
  10188 "  Evaluate and print expressions to stderr."),
  10189 
  10190    JS_FN_HELP("putstr", PutStr, 0, 0,
  10191 "putstr([exp])",
  10192 "  Evaluate and print expression without newline."),
  10193 
  10194    JS_FN_HELP("dateNow", Now, 0, 0,
  10195 "dateNow()",
  10196 "  Return the current time with sub-ms precision."),
  10197 
  10198    JS_FN_HELP("help", Help, 0, 0,
  10199 "help([function or interface object or /pattern/])",
  10200 "  Display usage and help messages."),
  10201 
  10202    JS_FN_HELP("quit", Quit, 0, 0,
  10203 "quit()",
  10204 "  Quit the shell."),
  10205 
  10206    JS_FN_HELP("assertEq", AssertEq, 2, 0,
  10207 "assertEq(actual, expected[, msg])",
  10208 "  Throw if the first two arguments are not the same (both +0 or both -0,\n"
  10209 "  both NaN, or non-zero and ===)."),
  10210 
  10211    JS_FN_HELP("startTimingMutator", StartTimingMutator, 0, 0,
  10212 "startTimingMutator()",
  10213 "  Start accounting time to mutator vs GC."),
  10214 
  10215    JS_FN_HELP("stopTimingMutator", StopTimingMutator, 0, 0,
  10216 "stopTimingMutator()",
  10217 "  Stop accounting time to mutator vs GC and dump the results."),
  10218 
  10219    JS_FN_HELP("throwError", ThrowError, 0, 0,
  10220 "throwError()",
  10221 "  Throw an error from JS_ReportError."),
  10222 
  10223    JS_FN_HELP("createErrorReport", CreateErrorReport, 1, 0,
  10224 "createErrorReport(value)",
  10225 "  Create an JS::ErrorReportBuilder object from the given value and serialize\n"
  10226 "  to an object."),
  10227 
  10228 #if defined(DEBUG) || defined(JS_JITSPEW)
  10229    JS_FN_HELP("disassemble", DisassembleToString, 1, 0,
  10230 "disassemble([fun/code])",
  10231 "  Return the disassembly for the given function or code.\n"
  10232 "  All disassembly functions take these options as leading string arguments:\n"
  10233 "    \"-r\" (disassemble recursively)\n"
  10234 "    \"-l\" (show line numbers)\n"
  10235 "    \"-S\" (omit source notes)"),
  10236 
  10237    JS_FN_HELP("dis", Disassemble, 1, 0,
  10238 "dis([fun/code])",
  10239 "  Disassemble functions into bytecodes."),
  10240 
  10241    JS_FN_HELP("disfile", DisassFile, 1, 0,
  10242 "disfile('foo.js')",
  10243 "  Disassemble script file into bytecodes.\n"),
  10244 
  10245    JS_FN_HELP("dissrc", DisassWithSrc, 1, 0,
  10246 "dissrc([fun/code])",
  10247 "  Disassemble functions with source lines."),
  10248 
  10249    JS_FN_HELP("notes", Notes, 1, 0,
  10250 "notes([fun])",
  10251 "  Show source notes for functions."),
  10252 
  10253    JS_FN_HELP("stackDump", StackDump, 3, 0,
  10254 "stackDump(showArgs, showLocals, showThisProps)",
  10255 "  Tries to print a lot of information about the current stack. \n"
  10256 "  Similar to the DumpJSStack() function in the browser."),
  10257 
  10258 #endif
  10259 
  10260    JS_FN_HELP("getslx", GetSLX, 1, 0,
  10261 "getslx(obj)",
  10262 "  Get script line extent."),
  10263 
  10264    JS_FN_HELP("evalcx", EvalInContext, 1, 0,
  10265 "evalcx(s[, o])",
  10266 "  Evaluate s in optional sandbox object o.\n"
  10267 "  if (s == '' && !o) return new o with eager standard classes\n"
  10268 "  if (s == 'lazy' && !o) return new o with lazy standard classes"),
  10269 
  10270    JS_FN_HELP("evalInWorker", EvalInWorker, 1, 0,
  10271 "evalInWorker(str)",
  10272 "  Evaluate 'str' in a separate thread with its own runtime.\n"),
  10273 
  10274    JS_FN_HELP("getSharedObject", GetSharedObject, 0, 0,
  10275 "getSharedObject()",
  10276 "  Retrieve the shared object from the cross-worker mailbox.\n"
  10277 "  The object retrieved may not be identical to the object that was\n"
  10278 "  installed, but it references the same shared memory.\n"
  10279 "  getSharedObject performs an ordering memory barrier.\n"),
  10280 
  10281    JS_FN_HELP("setSharedObject", SetSharedObject, 0, 0,
  10282 "setSharedObject(obj)",
  10283 "  Install the shared object in the cross-worker mailbox.  The object\n"
  10284 "  may be null.  setSharedObject performs an ordering memory barrier.\n"),
  10285 
  10286    JS_FN_HELP("getSharedArrayBuffer", GetSharedObject, 0, 0,
  10287 "getSharedArrayBuffer()",
  10288 "  Obsolete alias for getSharedObject().\n"),
  10289 
  10290    JS_FN_HELP("setSharedArrayBuffer", SetSharedObject, 0, 0,
  10291 "setSharedArrayBuffer(obj)",
  10292 "  Obsolete alias for setSharedObject(obj).\n"),
  10293 
  10294    JS_FN_HELP("shapeOf", ShapeOf, 1, 0,
  10295 "shapeOf(obj)",
  10296 "  Get the shape of obj (an implementation detail)."),
  10297 
  10298 #ifdef DEBUG
  10299    JS_FN_HELP("arrayInfo", ArrayInfo, 1, 0,
  10300 "arrayInfo(a1, a2, ...)",
  10301 "  Report statistics about arrays."),
  10302 #endif
  10303 
  10304    JS_FN_HELP("sleep", Sleep_fn, 1, 0,
  10305 "sleep(dt)",
  10306 "  Sleep for dt seconds."),
  10307 
  10308    JS_FN_HELP("parseModule", ParseModule, 3, 0,
  10309 "parseModule(code, 'filename', 'js' | 'json' | 'bytes')",
  10310 "  Parses source text as a JS module ('js', this is the default) or a JSON"
  10311 " module ('json') or bytes module ('bytes') and returns a ModuleObject wrapper object."),
  10312 
  10313    JS_FN_HELP("instantiateModuleStencil", InstantiateModuleStencil, 1, 0,
  10314 "instantiateModuleStencil(stencil, [options])",
  10315 "  Instantiates the given stencil as module, and return the module object."),
  10316 
  10317    JS_FN_HELP("instantiateModuleStencilXDR", InstantiateModuleStencilXDR, 1, 0,
  10318 "instantiateModuleStencilXDR(stencil, [options])",
  10319 "  Reads the given stencil XDR object, instantiates the stencil as module, and"
  10320 "  return the module object."),
  10321 
  10322    JS_FN_HELP("registerModule", RegisterModule, 2, 0,
  10323 "registerModule(specifier, module)",
  10324 "  Register a module with the module loader, so that subsequent import from\n"
  10325 "  |specifier| will resolve to |module|.  Returns |module|."),
  10326 
  10327    JS_FN_HELP("clearModules", ClearModules, 0, 0,
  10328 "clearModules()",
  10329 "  Clear knowledge of all loaded modules."),
  10330 
  10331    JS_FN_HELP("moduleLink", ModuleLink, 1, 0,
  10332 "moduleLink(moduleOjbect)",
  10333 "  Link a module graph, performing the spec's Link method."),
  10334 
  10335    JS_FN_HELP("moduleEvaluate", ModuleEvaluate, 1, 0,
  10336 "moduleEvaluate(moduleOjbect)",
  10337 "  Evaluate a module graph, performing the spec's Evaluate method."),
  10338 
  10339    JS_FN_HELP("getModuleEnvironmentNames", GetModuleEnvironmentNames, 1, 0,
  10340 "getModuleEnvironmentNames(module)",
  10341 "  Get the list of a module environment's bound names for a specified module.\n"),
  10342 
  10343    JS_FN_HELP("getModuleEnvironmentValue", GetModuleEnvironmentValue, 2, 0,
  10344 "getModuleEnvironmentValue(module, name)",
  10345 "  Get the value of a bound name in a module environment.\n"),
  10346 
  10347    JS_FN_HELP("dumpStencil", DumpStencil, 1, 0,
  10348 "dumpStencil(code, [options])",
  10349 "  Parses a string and returns string that represents stencil.\n"
  10350 "  If present, |options| may have properties saying how the code should be\n"
  10351 "  compiled:\n"
  10352 "      module: if present and true, compile the source as module.\n"
  10353 "  CompileOptions-related properties of evaluate function's option can also\n"
  10354 "  be used."),
  10355 
  10356    JS_FN_HELP("parse", Parse, 1, 0,
  10357 "parse(code, [options])",
  10358 "  Parses a string, potentially throwing. If present, |options| may\n"
  10359 "  have properties saying how the code should be compiled:\n"
  10360 "      module: if present and true, compile the source as module.\n"
  10361 "  CompileOptions-related properties of evaluate function's option can also\n"
  10362 "  be used. except forceFullParse. This function always use full parse."),
  10363 
  10364    JS_FN_HELP("syntaxParse", SyntaxParse, 1, 0,
  10365 "syntaxParse(code)",
  10366 "  Check the syntax of a string, returning success value"),
  10367 
  10368    JS_FN_HELP("offThreadCompileModuleToStencil", OffThreadCompileModuleToStencil, 1, 0,
  10369 "offThreadCompileModuleToStencil(code[, options])",
  10370 "  Compile |code| on a helper thread, returning a job ID. To wait for the\n"
  10371 "  compilation to finish and and get the module stencil object call\n"
  10372 "  |finishOffThreadStencil| passing the job ID."),
  10373 
  10374    JS_FN_HELP("offThreadDecodeStencil", OffThreadDecodeStencil, 1, 0,
  10375 "offThreadDecodeStencil(cacheEntry[, options])",
  10376 "  Decode |code| on a helper thread, returning a job ID. To wait for the\n"
  10377 "  decoding to finish and run the code, call |finishOffThreadStencil| passing\n"
  10378 "  the job ID. If present, |options| may have properties saying how the code\n"
  10379 "  should be compiled (see also offThreadCompileToStencil)."),
  10380 
  10381    JS_FN_HELP("offThreadCompileToStencil", OffThreadCompileToStencil, 1, 0,
  10382 "offThreadCompileToStencil(code[, options])",
  10383 "  Compile |code| on a helper thread, returning a job ID. To wait for the\n"
  10384 "  compilation to finish and get the stencil object, call\n"
  10385 "  |finishOffThreadStencil| passing the job ID.  If present, \n"
  10386 "  |options| may have properties saying how the code should be compiled:\n"
  10387 "      noScriptRval: use the no-script-rval compiler option (default: false)\n"
  10388 "      fileName: filename for error messages and debug info\n"
  10389 "      lineNumber: starting line number for error messages and debug info\n"
  10390 "      columnNumber: starting column number for error messages and debug info\n"
  10391 "      element: if present with value |v|, convert |v| to an object |o| and\n"
  10392 "         mark the source as being attached to the DOM element |o|. If the\n"
  10393 "         property is omitted or |v| is null, don't attribute the source to\n"
  10394 "         any DOM element.\n"
  10395 "      elementAttributeName: if present and not undefined, the name of\n"
  10396 "         property of 'element' that holds this code. This is what\n"
  10397 "         Debugger.Source.prototype.elementAttributeName returns."),
  10398 
  10399    JS_FN_HELP("finishOffThreadStencil", FinishOffThreadStencil, 0, 0,
  10400 "finishOffThreadStencil([jobID])",
  10401 "  Wait for an off-thread compilation or decode job to complete. The job ID\n"
  10402 "  can be ommitted if there is only one job pending. If an error occurred,\n"
  10403 "  throw the appropriate exception; otherwise, return the stencil object,"
  10404 "  that can be passed to |evalStencil|."),
  10405 
  10406    JS_FN_HELP("timeout", Timeout, 1, 0,
  10407 "timeout([seconds], [func])",
  10408 "  Get/Set the limit in seconds for the execution time for the current context.\n"
  10409 "  When the timeout expires the current interrupt callback is invoked.\n"
  10410 "  The timeout is used just once.  If the callback returns a falsy value, the\n"
  10411 "  script is aborted.  A negative value for seconds (this is the default) cancels\n"
  10412 "  any pending timeout.\n"
  10413 "  If a second argument is provided, it is installed as the interrupt handler,\n"
  10414 "  exactly as if by |setInterruptCallback|.\n"),
  10415 
  10416    JS_FN_HELP("interruptIf", InterruptIf, 1, 0,
  10417 "interruptIf(cond)",
  10418 "  Requests interrupt callback if cond is true. If a callback function is set via\n"
  10419 "  |timeout| or |setInterruptCallback|, it will be called. No-op otherwise."),
  10420 
  10421    JS_FN_HELP("invokeInterruptCallback", InvokeInterruptCallbackWrapper, 0, 0,
  10422 "invokeInterruptCallback(fun)",
  10423 "  Forcefully set the interrupt flag and invoke the interrupt handler. If a\n"
  10424 "  callback function is set via |timeout| or |setInterruptCallback|, it will\n"
  10425 "  be called. Before returning, fun is called with the return value of the\n"
  10426 "  interrupt handler."),
  10427 
  10428    JS_FN_HELP("setInterruptCallback", SetInterruptCallback, 1, 0,
  10429 "setInterruptCallback(func)",
  10430 "  Sets func as the interrupt callback function.\n"
  10431 "  func must be a function or a string (which will be evaluated in a new\n"
  10432 "  global). Only strings are supported in fuzzing builds.\n"
  10433 "  Calling this function will replace any callback set by |timeout|.\n"
  10434 "  If the callback returns a falsy value, the script is aborted.\n"),
  10435 
  10436    JS_FN_HELP("setPrefValue", SetPrefValue, 2, 0,
  10437 "setPrefValue(name, value)",
  10438 "  Set the value of the JS pref with the given name."),
  10439 
  10440    JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption, 2, 0,
  10441 "setJitCompilerOption(<option>, <number>)",
  10442 "  Set a compiler option indexed in JSCompileOption enum to a number.\n"),
  10443 
  10444 #ifdef DEBUG
  10445    JS_FN_HELP("interruptRegexp", InterruptRegexp, 2, 0,
  10446 "interruptRegexp(<regexp>, <string>)",
  10447 "  Interrrupt the execution of regular expression.\n"),
  10448 #endif
  10449    JS_FN_HELP("checkRegExpSyntax", CheckRegExpSyntax, 1, 0,
  10450 "checkRegExpSyntax(<string>)",
  10451 "  Return undefined if the string parses as a RegExp. If the string does not\n"
  10452 "  parse correctly, return the SyntaxError that occurred."),
  10453 
  10454    JS_FN_HELP("enableLastWarning", EnableLastWarning, 0, 0,
  10455 "enableLastWarning()",
  10456 "  Enable storing the last warning."),
  10457    JS_FN_HELP("disableLastWarning", DisableLastWarning, 0, 0,
  10458 "disableLastWarning()",
  10459 "  Disable storing the last warning."),
  10460 
  10461    JS_FN_HELP("getLastWarning", GetLastWarning, 0, 0,
  10462 "getLastWarning()",
  10463 "  Returns an object that represents the last warning."),
  10464 
  10465    JS_FN_HELP("clearLastWarning", ClearLastWarning, 0, 0,
  10466 "clearLastWarning()",
  10467 "  Clear the last warning."),
  10468 
  10469    JS_FN_HELP("elapsed", Elapsed, 0, 0,
  10470 "elapsed()",
  10471 "  Execution time elapsed for the current thread."),
  10472 
  10473    JS_FN_HELP("decompileFunction", DecompileFunction, 1, 0,
  10474 "decompileFunction(func)",
  10475 "  Decompile a function."),
  10476 
  10477    JS_FN_HELP("decompileThis", DecompileThisScript, 0, 0,
  10478 "decompileThis()",
  10479 "  Decompile the currently executing script."),
  10480 
  10481    JS_FN_HELP("valueToSource", ValueToSource, 1, 0,
  10482 "valueToSource(value)",
  10483 "  Format a value for inspection."),
  10484 
  10485    JS_FN_HELP("thisFilename", ThisFilename, 0, 0,
  10486 "thisFilename()",
  10487 "  Return the filename of the current script"),
  10488 
  10489    JS_FN_HELP("newGlobal", NewGlobal, 1, 0,
  10490 "newGlobal([options])",
  10491 "  Return a new global object/realm. The new global is created in the\n"
  10492 "  'newGlobal' function object's compartment and zone, unless the\n"
  10493 "  '--more-compartments' command-line flag was given, in which case new\n"
  10494 "  globals get a fresh compartment and zone. If options is given, it may\n"
  10495 "  have any of the following properties:\n"
  10496 "      sameCompartmentAs: If an object, the global will be in the same\n"
  10497 "         compartment and zone as the given object.\n"
  10498 "      sameZoneAs: The global will be in a new compartment in the same zone\n"
  10499 "         as the given object.\n"
  10500 "      newCompartment: If true, the global will always be created in a new\n"
  10501 "         compartment and zone.\n"
  10502 "      invisibleToDebugger: If true, the global will be invisible to the\n"
  10503 "         debugger (default false)\n"
  10504 "      discardSource: If true, discard source after compiling a script\n"
  10505 "         (default false).\n"
  10506 "      useWindowProxy: the global will be created with a WindowProxy attached. In this\n"
  10507 "          case, the WindowProxy will be returned.\n"
  10508 "      freezeBuiltins: certain builtin constructors will be frozen when created and\n"
  10509 "          their prototypes will be sealed. These constructors will be defined on the\n"
  10510 "          global as non-configurable and non-writable.\n"
  10511 "      immutablePrototype: whether the global's prototype is immutable.\n"
  10512 "      principal: if present, its value converted to a number must be an\n"
  10513 "         integer that fits in 32 bits; use that as the new realm's\n"
  10514 "         principal. Shell principals are toys, meant only for testing; one\n"
  10515 "         shell principal subsumes another if its set bits are a superset of\n"
  10516 "         the other's. Thus, a principal of 0 subsumes nothing, while a\n"
  10517 "         principals of ~0 subsumes all other principals. The absence of a\n"
  10518 "         principal is treated as if its bits were 0xffff, for subsumption\n"
  10519 "         purposes. If this property is omitted, supply no principal.\n"
  10520 "      systemPrincipal: If true, use the shell's trusted principals for the\n"
  10521 "         new realm. This creates a realm that's marked as a 'system' realm."),
  10522 
  10523    JS_FN_HELP("nukeAllCCWs", NukeAllCCWs, 0, 0,
  10524 "nukeAllCCWs()",
  10525 "  Like nukeCCW, but for all CrossCompartmentWrappers targeting the current realm."),
  10526 
  10527    JS_FN_HELP("recomputeWrappers", RecomputeWrappers, 2, 0,
  10528 "recomputeWrappers([src, [target]])",
  10529 "  Recompute all cross-compartment wrappers. src and target are both optional\n"
  10530 "  and can be used to filter source or target compartments: the unwrapped\n"
  10531 "  object's compartment is used as CompartmentFilter.\n"),
  10532 
  10533    JS_FN_HELP("dumpObjectWrappers", DumpObjectWrappers, 2, 0,
  10534 "dumpObjectWrappers()",
  10535 "  Print information about cross-compartment object wrappers.\n"),
  10536 
  10537    JS_FN_HELP("wrapWithProto", WrapWithProto, 2, 0,
  10538 "wrapWithProto(obj)",
  10539 "  Wrap an object into a noop wrapper with prototype semantics."),
  10540 
  10541    JS_FN_HELP("createExternalArrayBuffer", CreateExternalArrayBuffer, 1, 0,
  10542 "createExternalArrayBuffer(size)",
  10543 "  Create an array buffer that has external data of size."),
  10544 
  10545    JS_FN_HELP("createMappedArrayBuffer", CreateMappedArrayBuffer, 1, 0,
  10546 "createMappedArrayBuffer(filename, [offset, [size]])",
  10547 "  Create an array buffer that mmaps the given file."),
  10548 
  10549 JS_FN_HELP("createUserArrayBuffer", CreateUserArrayBuffer, 1, 0,
  10550 "createUserArrayBuffer(size)",
  10551 "  Create an array buffer that uses user-controlled memory."),
  10552 
  10553    JS_FN_HELP("addPromiseReactions", AddPromiseReactions, 3, 0,
  10554 "addPromiseReactions(promise, onResolve, onReject)",
  10555 "  Calls the JS::AddPromiseReactions JSAPI function with the given arguments."),
  10556 
  10557    JS_FN_HELP("ignoreUnhandledRejections", IgnoreUnhandledRejections, 0, 0,
  10558 "ignoreUnhandledRejections()",
  10559 "  By default, js shell tracks unhandled promise rejections and reports\n"
  10560 "  them at the end of the exectuion.  If a testcase isn't interested\n"
  10561 "  in those rejections, call this to stop tracking and reporting."),
  10562 
  10563    JS_FN_HELP("getMaxArgs", GetMaxArgs, 0, 0,
  10564 "getMaxArgs()",
  10565 "  Return the maximum number of supported args for a call."),
  10566 
  10567    JS_FN_HELP("createIsHTMLDDA", CreateIsHTMLDDA, 0, 0,
  10568 "createIsHTMLDDA()",
  10569 "  Return an object |obj| that \"looks like\" the |document.all| object in\n"
  10570 "  browsers in certain ways: |typeof obj === \"undefined\"|, |obj == null|\n"
  10571 "  and |obj == undefined| (vice versa for !=), |ToBoolean(obj) === false|,\n"
  10572 "  and when called with no arguments or the single argument \"\" returns\n"
  10573 "  null.  (Calling |obj| any other way crashes or throws an exception.)\n"
  10574 "  This function implements the exact requirements of the $262.IsHTMLDDA\n"
  10575 "  property in test262."),
  10576 
  10577    JS_FN_HELP("cacheEntry", CacheEntry, 1, 0,
  10578 "cacheEntry(code)",
  10579 "  Return a new opaque object which emulates a cache entry of a script.  This\n"
  10580 "  object encapsulates the code and its cached content. The cache entry is filled\n"
  10581 "  and read by the \"evaluate\" function by using it in place of the source, and\n"
  10582 "  by setting \"saveBytecodeWithDelazifications\" and \"loadBytecode\" options."),
  10583 
  10584    JS_FN_HELP("streamCacheEntry", StreamCacheEntryObject::construct, 1, 0,
  10585 "streamCacheEntry(buffer)",
  10586 "  Create a shell-only object that holds wasm bytecode and can be streaming-\n"
  10587 "  compiled and cached by WebAssembly.{compile,instantiate}Streaming(). On a\n"
  10588 "  second compilation of the same cache entry, the cached code will be used."),
  10589 
  10590    JS_FN_HELP("printProfilerEvents", PrintProfilerEvents, 0, 0,
  10591 "printProfilerEvents()",
  10592 "  Register a callback with the profiler that prints javascript profiler events\n"
  10593 "  to stderr.  Callback is only registered if profiling is enabled."),
  10594 
  10595    JS_FN_HELP("enableSingleStepProfiling", EnableSingleStepProfiling, 0, 0,
  10596 "enableSingleStepProfiling()",
  10597 "  This function will fail on platforms that don't support single-step profiling\n"
  10598 "  (currently ARM and MIPS64 support it). When enabled, at every instruction a\n"
  10599 "  backtrace will be recorded and stored in an array. Adjacent duplicate backtraces\n"
  10600 "  are discarded."),
  10601 
  10602    JS_FN_HELP("disableSingleStepProfiling", DisableSingleStepProfiling, 0, 0,
  10603 "disableSingleStepProfiling()",
  10604 "  Return the array of backtraces recorded by enableSingleStepProfiling."),
  10605 
  10606    JS_FN_HELP("enableGeckoProfiling", EnableGeckoProfiling, 0, 0,
  10607 "enableGeckoProfiling()",
  10608 "  Enables Gecko Profiler instrumentation and corresponding assertions, with slow\n"
  10609 "  assertions disabled.\n"),
  10610 
  10611    JS_FN_HELP("enableGeckoProfilingWithSlowAssertions", EnableGeckoProfilingWithSlowAssertions, 0, 0,
  10612 "enableGeckoProfilingWithSlowAssertions()",
  10613 "  Enables Gecko Profiler instrumentation and corresponding assertions, with slow\n"
  10614 "  assertions enabled.\n"),
  10615 
  10616    JS_FN_HELP("disableGeckoProfiling", DisableGeckoProfiling, 0, 0,
  10617 "disableGeckoProfiling()",
  10618 "  Disables Gecko Profiler instrumentation"),
  10619 
  10620    JS_FN_HELP("getGeckoProfilingScriptSourcesCount", GetGeckoProfilingScriptSourcesCount, 0, 0,
  10621 "getGeckoProfilingScriptSourcesCount()",
  10622 "  Returns the number of script sources registered with the Gecko Profiler"),
  10623 
  10624    JS_FN_HELP("isLatin1", IsLatin1, 1, 0,
  10625 "isLatin1(s)",
  10626 "  Return true iff the string's characters are stored as Latin1."),
  10627 
  10628    JS_FN_HELP("stackPointerInfo", StackPointerInfo, 0, 0,
  10629 "stackPointerInfo()",
  10630 "  Return an int32 value which corresponds to the offset of the latest stack\n"
  10631 "  pointer, such that one can take the differences of 2 to estimate a frame-size."),
  10632 
  10633    JS_FN_HELP("globalOfFirstJobInQueue", GlobalOfFirstJobInQueue, 0, 0,
  10634 "globalOfFirstJobInQueue()",
  10635 "  Returns the global of the first item in the job queue. Throws an exception\n"
  10636 "  if the queue is empty.\n"),
  10637 
  10638    JS_FN_HELP("drainJobQueue", DrainJobQueue, 0, 0,
  10639 "drainJobQueue()",
  10640 "Take jobs from the shell's job queue in FIFO order and run them until the\n"
  10641 "queue is empty.\n"),
  10642 
  10643      JS_FN_HELP("setTimeout", SetTimeout, 1, 0,
  10644 "setTimeout(functionRef, delay)",
  10645 "Executes functionRef after the specified delay, like the Web builtin."
  10646 "This is currently restricted to require a delay of 0 and will not accept"
  10647 "any extra arguments. No return value is given and there is no clearTimeout."),
  10648 
  10649    JS_FN_HELP("setCSPEnabled", SetCSPEnabled, 1, 0,
  10650 "setCSPEnabled(enabled)",
  10651 "Enable or disable Content Security Policy restrictions for eval() and Function().\n"
  10652 "When enabled (true), string compilation will be blocked. When disabled (false),\n"
  10653 "string compilation is allowed. Defaults to disabled."),
  10654 
  10655    JS_FN_HELP("setPromiseRejectionTrackerCallback", SetPromiseRejectionTrackerCallback, 1, 0,
  10656 "setPromiseRejectionTrackerCallback()",
  10657 "Sets the callback to be invoked whenever a Promise rejection is unhandled\n"
  10658 "or a previously-unhandled rejection becomes handled."),
  10659 
  10660    JS_FN_HELP("dumpScopeChain", DumpScopeChain, 1, 0,
  10661 "dumpScopeChain(obj)",
  10662 "  Prints the scope chain of an interpreted function or a module."),
  10663 
  10664    JS_FN_HELP("blackRoot", EnsureBlackRoot, 0, 0,
  10665 "blackRoot()",
  10666 "  Return an array in the current compartment whose elements will be marked\n"
  10667 "  as black roots by the GC."),
  10668 
  10669    JS_FN_HELP("grayRoot", EnsureGrayRoot, 0, 0,
  10670 "grayRoot()",
  10671 "  Return an array in the current compartment whose elements will be marked\n"
  10672 "  as gray roots by the GC."),
  10673 
  10674    JS_FN_HELP("addMarkObservers", AddMarkObservers, 1, 0,
  10675 "addMarkObservers(array_of_objects)",
  10676 "  Register an array of objects whose mark bits will be tested by calls to\n"
  10677 "  getMarks. The objects will be in calling compartment. Objects from\n"
  10678 "  multiple compartments may be monitored by calling this function in\n"
  10679 "  different compartments."),
  10680 
  10681    JS_FN_HELP("clearMarkObservers", ClearMarkObservers, 1, 0,
  10682 "clearMarkObservers()",
  10683 "  Clear out the list of objects whose mark bits will be tested.\n"),
  10684 
  10685    JS_FN_HELP("getMarks", GetMarks, 0, 0,
  10686 "getMarks()",
  10687 "  Return an array of strings representing the current state of the mark\n"
  10688 "  bits ('gray' or 'black', or 'dead' if the object has been collected)\n"
  10689 "  for the objects registered via addMarkObservers. Note that some of the\n"
  10690 "  objects tested may be from different compartments than the one in which\n"
  10691 "  this function runs."),
  10692 
  10693    JS_FN_HELP("bindToAsyncStack", BindToAsyncStack, 2, 0,
  10694 "bindToAsyncStack(fn, { stack, cause, explicit })",
  10695 "  Returns a new function that calls 'fn' with no arguments, passing\n"
  10696 "  'undefined' as the 'this' value, and supplies an async stack for the\n"
  10697 "  call as described by the second argument, an object with the following\n"
  10698 "  properties (which are not optional, unless specified otherwise):\n"
  10699 "\n"
  10700 "  stack:    A SavedFrame object, like that returned by 'saveStack'. Stacks\n"
  10701 "            captured during calls to the returned function capture this as\n"
  10702 "            their async stack parent, accessible via a SavedFrame's\n"
  10703 "            'asyncParent' property.\n"
  10704 "\n"
  10705 "  cause:    A string, supplied as the async cause on the top frame of\n"
  10706 "            captured async stacks.\n"
  10707 "\n"
  10708 "  explicit: A boolean value, indicating whether the given 'stack' should\n"
  10709 "            always supplant the returned function's true callers (true),\n"
  10710 "            or only when there are no other JavaScript frames on the stack\n"
  10711 "            below it (false). If omitted, this is treated as 'true'."),
  10712 
  10713 #ifndef __wasi__
  10714    JS_FN_HELP("wasmCompileInSeparateProcess", WasmCompileInSeparateProcess, 1, 0,
  10715 "wasmCompileInSeparateProcess(buffer)",
  10716 "  Compile the given buffer in a separate process, serialize the resulting\n"
  10717 "  wasm::Module into bytes, and deserialize those bytes in the current\n"
  10718 "  process, returning the resulting WebAssembly.Module."),
  10719 
  10720    JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0,
  10721 "wasmTextToBinary(str)",
  10722 "  Translates the given text wasm module into its binary encoding."),
  10723 #endif // __wasi__
  10724 
  10725    JS_FN_HELP("transplantableObject", TransplantableObject, 0, 0,
  10726 "transplantableObject([options])",
  10727 "  Returns the pair {object, transplant}. |object| is an object which can be\n"
  10728 "  transplanted into a new object when the |transplant| function, which must\n"
  10729 "  be invoked with a global object, is called.\n"
  10730 "  |object| is swapped with a cross-compartment wrapper if the global object\n"
  10731 "  is in a different compartment.\n"
  10732 "\n"
  10733 "  If options is given, it may have any of the following properties:\n"
  10734 "    proxy: Create a DOM Proxy object instead of a plain DOM object.\n"
  10735 "    object: Don't create a new DOM object, but instead use the supplied\n"
  10736 "            FakeDOMObject."),
  10737 
  10738    JS_FN_HELP("cpuNow", CpuNow, /* nargs= */ 0, /* flags = */ 0,
  10739 "cpuNow()",
  10740 " Returns the approximate processor time used by the process since an arbitrary epoch, in seconds.\n"
  10741 " Only the difference between two calls to `cpuNow()` is meaningful."),
  10742 
  10743 #ifdef FUZZING_JS_FUZZILLI
  10744    JS_FN_HELP("fuzzilli", Fuzzilli, 0, 0,
  10745 "fuzzilli(operation, arg)",
  10746 "  Exposes functionality used by the Fuzzilli JavaScript fuzzer."),
  10747 #endif
  10748 
  10749 #ifdef FUZZING_INTERFACES
  10750    JS_FN_HELP("getWasmSmithModule", GetWasmSmithModule, 1, 0,
  10751 "getWasmSmithModule(arrayBuffer)",
  10752 "  Call wasm-smith to generate a random wasm module from the provided data."),
  10753 #endif
  10754 
  10755    JS_FN_HELP("isValidJSON", IsValidJSON, 1, 0,
  10756 "isValidJSON(source)",
  10757 " Returns true if the given source is valid JSON."),
  10758 
  10759    JS_FN_HELP("createSideEffectfulResolveObject", CreateSideEffectfulResolveObject, 0, 0,
  10760 "createSideEffectfulResolveObject()",
  10761 " Return an object with a property 'obj.test == 42', backed by a resolve hook "
  10762 " with the Debugger shouldAvoidSideEffects flag integration."),
  10763 
  10764  JS_FN_HELP("getUseCounterResults", GetUseCounterResults, 0, 0,
  10765 "getUseCounterResults()",
  10766 " Return the values of the shell use counters."),
  10767 
  10768    JS_FS_HELP_END
  10769 };
  10770 // clang-format on
  10771 
  10772 // clang-format off
  10773 #ifdef FUZZING_JS_FUZZILLI
  10774 static const JSFunctionSpec shell_function_fuzzilli_hash[] = {
  10775    JS_INLINABLE_FN("fuzzilli_hash", fuzzilli_hash, 1, 0, FuzzilliHash),
  10776    JS_FS_END,
  10777 };
  10778 #endif
  10779 // clang-format on
  10780 
  10781 // clang-format off
  10782 static const JSFunctionSpecWithHelp diff_testing_unsafe_functions[] = {
  10783 
  10784    JS_FS_HELP_END
  10785 };
  10786 // clang-format on
  10787 
  10788 // clang-format off
  10789 static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
  10790    JS_FN_HELP("getSelfHostedValue", GetSelfHostedValue, 1, 0,
  10791 "getSelfHostedValue()",
  10792 "  Get a self-hosted value by its name. Note that these values don't get \n"
  10793 "  cached, so repeatedly getting the same value creates multiple distinct clones."),
  10794 
  10795    JS_FN_HELP("line2pc", LineToPC, 0, 0,
  10796 "line2pc([fun,] line)",
  10797 "  Map line number to PC."),
  10798 
  10799    JS_FN_HELP("pc2line", PCToLine, 0, 0,
  10800 "pc2line(fun[, pc])",
  10801 "  Map PC to line number."),
  10802 
  10803    JS_INLINABLE_FN_HELP("assertFloat32", testingFunc_assertFloat32, 2, 0, TestAssertFloat32,
  10804 "assertFloat32(value, isFloat32)",
  10805 "  In IonMonkey only, asserts that value has (resp. hasn't) the MIRType::Float32 if isFloat32 is true (resp. false)."),
  10806 
  10807    JS_INLINABLE_FN_HELP("assertRecoveredOnBailout", testingFunc_assertRecoveredOnBailout, 2, 0,
  10808 TestAssertRecoveredOnBailout,
  10809 "assertRecoveredOnBailout(var)",
  10810 "  In IonMonkey only, asserts that variable has RecoveredOnBailout flag."),
  10811 
  10812    JS_FN_HELP("withSourceHook", WithSourceHook, 1, 0,
  10813 "withSourceHook(hook, fun)",
  10814 "  Set this JS runtime's lazy source retrieval hook (that is, the hook\n"
  10815 "  used to find sources compiled with |CompileOptions::LAZY_SOURCE|) to\n"
  10816 "  |hook|; call |fun| with no arguments; and then restore the runtime's\n"
  10817 "  original hook. Return or throw whatever |fun| did. |hook| gets\n"
  10818 "  passed the requested code's URL, and should return a string.\n"
  10819 "\n"
  10820 "  Notes:\n"
  10821 "\n"
  10822 "  1) SpiderMonkey may assert if the returned code isn't close enough\n"
  10823 "  to the script's real code, so this function is not fuzzer-safe.\n"
  10824 "\n"
  10825 "  2) The runtime can have only one source retrieval hook active at a\n"
  10826 "  time. If |fun| is not careful, |hook| could be asked to retrieve the\n"
  10827 "  source code for compilations that occurred long before it was set,\n"
  10828 "  and that it knows nothing about. The reverse applies as well: the\n"
  10829 "  original hook, that we reinstate after the call to |fun| completes,\n"
  10830 "  might be asked for the source code of compilations that |fun|\n"
  10831 "  performed, and which, presumably, only |hook| knows how to find.\n"),
  10832 
  10833    JS_FN_HELP("crash", Crash, 0, 0,
  10834 "crash([message, [{disable_minidump:true}]])",
  10835 "  Crashes the process with a MOZ_CRASH, optionally providing a message.\n"
  10836 "  An options object may be passed as the second argument. If the key\n"
  10837 "  'suppress_minidump' is set to true, then a minidump will not be\n"
  10838 "  generated by the crash (which only has an effect if the breakpad\n"
  10839 "  dumping library is loaded.)"),
  10840 
  10841 #ifndef __wasi__
  10842    JS_FN_HELP("wasmLoop", WasmLoop, 2, 0,
  10843 "wasmLoop(filename, imports)",
  10844 "  Performs an AFL-style persistent loop reading data from the given file and passing it\n"
  10845 "  to the 'wasmEval' function together with the specified imports object."),
  10846 #endif // __wasi__
  10847 
  10848    JS_FN_HELP("setBufferStreamParams", SetBufferStreamParams, 2, 0,
  10849 "setBufferStreamParams(delayMillis, chunkByteSize)",
  10850 "  Set the delay time (between calls to StreamConsumer::consumeChunk) and chunk\n"
  10851 "  size (in bytes)."),
  10852 
  10853 #ifdef JS_CACHEIR_SPEW
  10854  JS_FN_HELP("cacheIRHealthReport", CacheIRHealthReport, 0, 0,
  10855 "cacheIRHealthReport()",
  10856 "  Show health rating of CacheIR stubs."),
  10857 #endif
  10858 
  10859 #ifdef DEBUG
  10860  JS_FN_HELP("debugGetQueuedJobs", DebugGetQueuedJobs, 0, 0,
  10861 "debugGetQueuedJobs()",
  10862 "  Returns an array of queued jobs."),
  10863 #endif
  10864 
  10865 #ifdef JS_HAS_INTL_API
  10866  // One of the extras is AddMozDateTimeFormatConstructor, which is not fuzzing
  10867  // safe, since it doesn't validate the custom format pattern.
  10868  //
  10869  // See https://bugzilla.mozilla.org/show_bug.cgi?id=1887585#c1
  10870    JS_FN_HELP("addIntlExtras", AddIntlExtras, 1, 0,
  10871 "addIntlExtras(obj)",
  10872 "Adds various not-yet-standardized Intl functions as properties on the\n"
  10873 "provided object (this should generally be Intl itself).  The added\n"
  10874 "functions and their behavior are experimental: don't depend upon them\n"
  10875 "unless you're willing to update your code if these experimental APIs change\n"
  10876 "underneath you."),
  10877 #endif // JS_HAS_INTL_API
  10878 
  10879 #ifdef MOZ_EXECUTION_TRACING
  10880    JS_FN_HELP("enableExecutionTracing", EnableExecutionTracing, 0, 0,
  10881 "enableExecutionTracing()",
  10882 "  Enable execution tracing for the current context."),
  10883 
  10884    JS_FN_HELP("getExecutionTrace", GetExecutionTrace, 0, 0,
  10885 "getExecutionTrace()",
  10886 "  Get the execution trace."),
  10887 
  10888    JS_FN_HELP("disableExecutionTracing", DisableExecutionTracing, 0, 0,
  10889 "disableExecutionTracing()",
  10890 "  Disable execution tracing for the current context."),
  10891 #endif   // MOZ_EXECUTION_TRACING
  10892 
  10893    JS_FN_HELP("startRecordingTelemetry", StartRecordingTelemetry, 1, 0,
  10894 "startRecordingTelemetry(probeName)",
  10895 "  Start recording telemetry samples for the specified probe."),
  10896 
  10897    JS_FN_HELP("stopRecordingTelemetry", StopRecordingTelemetry, 1, 0,
  10898 "stopRecordingTelemetry(probeName)",
  10899 "  Stop recording telemetry samples for the specified probe."),
  10900 
  10901    JS_FN_HELP("getTelemetrySamples", GetTelemetrySamples, 1, 0,
  10902 "getTelemetry(probeName)",
  10903 "  Return an array of recorded telemetry samples for the specified probe."),
  10904 
  10905    // compressLZ4 and decompressLZ4 are fuzzing unsafe because of unfixed
  10906    // integer overflow issues. See bug 1900525.
  10907    JS_FN_HELP("compressLZ4", CompressLZ4, 1, 0,
  10908 "compressLZ4(bytes)",
  10909 " Return a compressed copy of bytes using LZ4."),
  10910 
  10911    JS_FN_HELP("decompressLZ4", DecompressLZ4, 1, 0,
  10912 "decompressLZ4(bytes)",
  10913 " Return a decompressed copy of bytes using LZ4."),
  10914 
  10915    JS_FN_HELP("mozLog", SetMozLog, 0, 0,
  10916 "mozLog(\"logLevels\")",
  10917 "  Modify log levels as if MOZ_LOG had been set to this at startup.\n"
  10918 "      `logLevels` is a comma-separated list of log modules, each\n"
  10919 "      with an optional \":<level>\" (defaults to 5 aka Debug).\n"
  10920 "      example: mozLog('gc,wasm:4')"),
  10921 
  10922    JS_FS_HELP_END
  10923 };
  10924 // clang-format on
  10925 
  10926 // clang-format off
  10927 static const JSFunctionSpecWithHelp performance_functions[] = {
  10928    JS_FN_HELP("now", Now, 0, 0,
  10929 "now()",
  10930 "  Return the current time with sub-ms precision.\n"
  10931 "  This function is an alias of the dateNow() function."),
  10932    JS_FS_HELP_END
  10933 };
  10934 // clang-format on
  10935 
  10936 // clang-format off
  10937 static const JSFunctionSpecWithHelp console_functions[] = {
  10938    JS_FN_HELP("log", Print, 0, 0,
  10939 "log([exp ...])",
  10940 "  Evaluate and print expressions to stdout.\n"
  10941 "  This function is an alias of the print() function."),
  10942    JS_FS_HELP_END
  10943 };
  10944 // clang-format on
  10945 
  10946 bool DefineConsole(JSContext* cx, HandleObject global) {
  10947  RootedObject obj(cx, JS_NewPlainObject(cx));
  10948  return obj && JS_DefineFunctionsWithHelp(cx, obj, console_functions) &&
  10949         JS_DefineProperty(cx, global, "console", obj, 0);
  10950 }
  10951 
  10952 #ifdef MOZ_PROFILING
  10953 #  define PROFILING_FUNCTION_COUNT 5
  10954 #  ifdef MOZ_CALLGRIND
  10955 #    define CALLGRIND_FUNCTION_COUNT 3
  10956 #  else
  10957 #    define CALLGRIND_FUNCTION_COUNT 0
  10958 #  endif
  10959 #  ifdef MOZ_VTUNE
  10960 #    define VTUNE_FUNCTION_COUNT 4
  10961 #  else
  10962 #    define VTUNE_FUNCTION_COUNT 0
  10963 #  endif
  10964 #  define EXTERNAL_FUNCTION_COUNT \
  10965    (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT)
  10966 #else
  10967 #  define EXTERNAL_FUNCTION_COUNT 0
  10968 #endif
  10969 
  10970 #undef PROFILING_FUNCTION_COUNT
  10971 #undef CALLGRIND_FUNCTION_COUNT
  10972 #undef VTUNE_FUNCTION_COUNT
  10973 #undef EXTERNAL_FUNCTION_COUNT
  10974 
  10975 static bool PrintHelpString(JSContext* cx, HandleValue v) {
  10976  RootedString str(cx, v.toString());
  10977  MOZ_ASSERT(gOutFile->isOpen());
  10978 
  10979  UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
  10980  if (!bytes) {
  10981    return false;
  10982  }
  10983 
  10984  fprintf(gOutFile->fp, "%s\n", bytes.get());
  10985  return true;
  10986 }
  10987 
  10988 static bool PrintHelp(JSContext* cx, HandleObject obj) {
  10989  RootedValue usage(cx);
  10990  if (!JS_GetProperty(cx, obj, "usage", &usage)) {
  10991    return false;
  10992  }
  10993  RootedValue help(cx);
  10994  if (!JS_GetProperty(cx, obj, "help", &help)) {
  10995    return false;
  10996  }
  10997 
  10998  if (!usage.isString() || !help.isString()) {
  10999    return true;
  11000  }
  11001 
  11002  return PrintHelpString(cx, usage) && PrintHelpString(cx, help);
  11003 }
  11004 
  11005 struct ExtraGlobalBindingWithHelp {
  11006  const char* name;
  11007  const char* help;
  11008 };
  11009 
  11010 // clang-format off
  11011 static ExtraGlobalBindingWithHelp extraGlobalBindingsWithHelp[] = {
  11012 // Defined in BindScriptArgs.
  11013    {
  11014 "scriptArgs",
  11015 "  An array containing the command line arguments passed after the path\n"
  11016 "  to a JS script."},
  11017    {
  11018 "scriptPath",
  11019 "  The path to the JS script passed to JS shell.  This does not reflect\n"
  11020 "  modules evaluated via -m option."},
  11021 
  11022 // Defined in DefineConsole.
  11023    {
  11024 "console",
  11025 "  An object with console.log() which aliases print()."},
  11026 
  11027 // Defined in NewGlobalObject.
  11028    {
  11029 "performance",
  11030 "  An object with the following properties:\n"
  11031 "    performance.now()\n"
  11032 "      See help(performance.now)\n"
  11033 "    performance.mozMemory.gc\n"
  11034 "      An object that represents GC statistics with the following properties:\n"
  11035 "        gcBytes\n"
  11036 "        gcMaxBytes\n"
  11037 "        mallocBytes\n"
  11038 "        gcIsHighFrequencyMode\n"
  11039 "        gcNumber\n"
  11040 "        majorGCCount\n"
  11041 "        minorGCCount\n"
  11042 "        sliceCount\n"
  11043 "        compartmentCount\n"
  11044 "        lastStartReason\n"
  11045 "        zone.gcBytes\n"
  11046 "        zone.gcTriggerBytes\n"
  11047 "        zone.gcAllocTrigger\n"
  11048 "        zone.mallocBytes\n"
  11049 "        zone.mallocTriggerBytes\n"
  11050 "        zone.gcNumber"},
  11051    {
  11052 "new FakeDOMObject()",
  11053 "  A constructor to test IonMonkey DOM optimizations in JS shell.\n"
  11054 "  The prototype object has the following properties:\n"
  11055 "    FakeDOMObject.prototype.x\n"
  11056 "      Generic getter/setter with JSJitInfo\n"
  11057 "    FakeDOMObject.prototype.slot\n"
  11058 "      Getter with JSJitInfo.slotIndex\n"
  11059 "    FakeDOMObject.prototype.global\n"
  11060 "      Getter/setter with JSJitInfo::AliasEverything\n"
  11061 "    FakeDOMObject.prototype.doFoo()\n"
  11062 "      Method with JSJitInfo\n"
  11063 "    FakeDOMObject.prototype.getObject()\n"
  11064 "      Method with JSJitInfo that returns an object."},
  11065 };
  11066 // clang-format on
  11067 
  11068 static bool MatchPattern(JSContext* cx, JS::Handle<RegExpObject*> regex,
  11069                         JS::Handle<JSString*> inputStr, bool* result) {
  11070  JS::Rooted<JSString*> linearInputStr(cx, inputStr);
  11071  if (!linearInputStr->ensureLinear(cx)) {
  11072    return false;
  11073  }
  11074 
  11075  // Execute the regular expression in |regex|'s compartment.
  11076  JSAutoRealm ar(cx, regex);
  11077  if (!cx->compartment()->wrap(cx, &linearInputStr)) {
  11078    return false;
  11079  }
  11080  JS::Rooted<JSLinearString*> input(cx, &linearInputStr->asLinear());
  11081  size_t ignored = 0;
  11082  JS::Rooted<JS::Value> v(cx);
  11083  if (!ExecuteRegExpLegacy(cx, nullptr, regex, input, &ignored, true, &v)) {
  11084    return false;
  11085  }
  11086  *result = !v.isNull();
  11087  return true;
  11088 }
  11089 
  11090 static bool PrintEnumeratedHelp(JSContext* cx, HandleObject obj,
  11091                                Handle<RegExpObject*> pattern, bool brief) {
  11092  RootedIdVector idv(cx);
  11093  if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &idv)) {
  11094    return false;
  11095  }
  11096 
  11097  for (size_t i = 0; i < idv.length(); i++) {
  11098    RootedValue v(cx);
  11099    RootedId id(cx, idv[i]);
  11100    if (!JS_GetPropertyById(cx, obj, id, &v)) {
  11101      return false;
  11102    }
  11103    if (!v.isObject()) {
  11104      continue;
  11105    }
  11106 
  11107    RootedObject funcObj(cx, &v.toObject());
  11108    if (pattern) {
  11109      // Only pay attention to objects with a 'help' property, which will
  11110      // either be documented functions or interface objects.
  11111      if (!JS_GetProperty(cx, funcObj, "help", &v)) {
  11112        return false;
  11113      }
  11114      if (!v.isString()) {
  11115        continue;
  11116      }
  11117 
  11118      // For functions, match against the name. For interface objects,
  11119      // match against the usage string.
  11120      if (!JS_GetProperty(cx, funcObj, "name", &v)) {
  11121        return false;
  11122      }
  11123      if (!v.isString()) {
  11124        if (!JS_GetProperty(cx, funcObj, "usage", &v)) {
  11125          return false;
  11126        }
  11127        if (!v.isString()) {
  11128          continue;
  11129        }
  11130      }
  11131 
  11132      Rooted<JSString*> inputStr(cx, v.toString());
  11133      bool result = false;
  11134      if (!MatchPattern(cx, pattern, inputStr, &result)) {
  11135        return false;
  11136      }
  11137      if (!result) {
  11138        continue;
  11139      }
  11140    }
  11141 
  11142    if (!PrintHelp(cx, funcObj)) {
  11143      return false;
  11144    }
  11145  }
  11146 
  11147  return true;
  11148 }
  11149 
  11150 static bool PrintExtraGlobalEnumeratedHelp(JSContext* cx,
  11151                                           Handle<RegExpObject*> pattern,
  11152                                           bool brief) {
  11153  for (const auto& item : extraGlobalBindingsWithHelp) {
  11154    if (pattern) {
  11155      JS::Rooted<JSString*> name(cx, JS_NewStringCopyZ(cx, item.name));
  11156      if (!name) {
  11157        return false;
  11158      }
  11159 
  11160      bool result = false;
  11161      if (!MatchPattern(cx, pattern, name, &result)) {
  11162        return false;
  11163      }
  11164      if (!result) {
  11165        continue;
  11166      }
  11167    }
  11168    fprintf(gOutFile->fp, "%s\n", item.name);
  11169    fprintf(gOutFile->fp, "%s\n", item.help);
  11170  }
  11171 
  11172  return true;
  11173 }
  11174 
  11175 static bool Help(JSContext* cx, unsigned argc, Value* vp) {
  11176  if (!gOutFile->isOpen()) {
  11177    JS_ReportErrorASCII(cx, "output file is closed");
  11178    return false;
  11179  }
  11180 
  11181  CallArgs args = CallArgsFromVp(argc, vp);
  11182  args.rval().setUndefined();
  11183  RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
  11184 
  11185  // help() - display the version and dump out help for all functions on the
  11186  // global.
  11187  if (args.length() == 0) {
  11188    fprintf(gOutFile->fp, "%s\n", JS_GetImplementationVersion());
  11189 
  11190    if (!PrintEnumeratedHelp(cx, global, nullptr, false)) {
  11191      return false;
  11192    }
  11193    if (!PrintExtraGlobalEnumeratedHelp(cx, nullptr, false)) {
  11194      return false;
  11195    }
  11196    return true;
  11197  }
  11198 
  11199  RootedValue v(cx);
  11200 
  11201  if (args[0].isPrimitive()) {
  11202    // help("foo")
  11203    JS_ReportErrorASCII(cx, "primitive arg");
  11204    return false;
  11205  }
  11206 
  11207  RootedObject obj(cx, &args[0].toObject());
  11208  if (!obj) {
  11209    return true;
  11210  }
  11211  bool isRegexp;
  11212  if (!JS::ObjectIsRegExp(cx, obj, &isRegexp)) {
  11213    return false;
  11214  }
  11215 
  11216  if (isRegexp) {
  11217    // help(/pattern/)
  11218    Rooted<RegExpObject*> pattern(cx,
  11219                                  &UncheckedUnwrap(obj)->as<RegExpObject>());
  11220    if (!PrintEnumeratedHelp(cx, global, pattern, false)) {
  11221      return false;
  11222    }
  11223    if (!PrintExtraGlobalEnumeratedHelp(cx, pattern, false)) {
  11224      return false;
  11225    }
  11226    return true;
  11227  }
  11228 
  11229  // help(function)
  11230  // help(namespace_obj)
  11231  return PrintHelp(cx, obj);
  11232 }
  11233 
  11234 static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = {
  11235 #define MSG_DEF(name, count, exception, format) \
  11236  {#name, format, count, JSEXN_ERR},
  11237 #include "jsshell.msg"
  11238 #undef MSG_DEF
  11239 };
  11240 
  11241 const JSErrorFormatString* js::shell::my_GetErrorMessage(
  11242    void* userRef, const unsigned errorNumber) {
  11243  if (errorNumber == 0 || errorNumber >= JSShellErr_Limit) {
  11244    return nullptr;
  11245  }
  11246 
  11247  return &jsShell_ErrorFormatString[errorNumber];
  11248 }
  11249 
  11250 static bool CreateLastWarningObject(JSContext* cx, JSErrorReport* report) {
  11251  RootedObject warningObj(cx, JS_NewObject(cx, nullptr));
  11252  if (!warningObj) {
  11253    return false;
  11254  }
  11255 
  11256  if (!CopyErrorReportToObject(cx, report, warningObj)) {
  11257    return false;
  11258  }
  11259 
  11260  GetShellContext(cx)->lastWarning.setObject(*warningObj);
  11261  return true;
  11262 }
  11263 
  11264 static FILE* ErrorFilePointer() {
  11265  if (gErrFile->isOpen()) {
  11266    return gErrFile->fp;
  11267  }
  11268 
  11269  fprintf(stderr, "error file is closed; falling back to stderr\n");
  11270  return stderr;
  11271 }
  11272 
  11273 bool shell::PrintStackTrace(JSContext* cx, HandleObject stackObj) {
  11274  if (!stackObj || !stackObj->is<SavedFrame>()) {
  11275    return true;
  11276  }
  11277 
  11278  JSPrincipals* principals = stackObj->nonCCWRealm()->principals();
  11279  RootedString stackStr(cx);
  11280  if (!BuildStackString(cx, principals, stackObj, &stackStr, 2)) {
  11281    return false;
  11282  }
  11283 
  11284  UniqueChars stack = JS_EncodeStringToUTF8(cx, stackStr);
  11285  if (!stack) {
  11286    return false;
  11287  }
  11288 
  11289  FILE* fp = ErrorFilePointer();
  11290  fputs("Stack:\n", fp);
  11291  fputs(stack.get(), fp);
  11292 
  11293  return true;
  11294 }
  11295 
  11296 js::shell::AutoReportException::~AutoReportException() {
  11297  if (!JS_IsExceptionPending(cx)) {
  11298    return;
  11299  }
  11300 
  11301  auto printError = [](JSContext* cx, auto& report, const auto& exnStack,
  11302                       const char* prefix = nullptr) {
  11303    if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
  11304      fprintf(stderr, "out of memory initializing JS::ErrorReportBuilder\n");
  11305      fflush(stderr);
  11306      JS_ClearPendingException(cx);
  11307      return false;
  11308    }
  11309 
  11310    MOZ_ASSERT(!report.report()->isWarning());
  11311 
  11312    FILE* fp = ErrorFilePointer();
  11313    if (prefix) {
  11314      fputs(prefix, fp);
  11315    }
  11316    JS::PrintError(fp, report, reportWarnings);
  11317    JS_ClearPendingException(cx);
  11318 
  11319    // If possible, use the original error stack as the source of truth, because
  11320    // finally block handlers may have overwritten the exception stack.
  11321    RootedObject stack(cx, exnStack.stack());
  11322    if (exnStack.exception().isObject()) {
  11323      RootedObject exception(cx, &exnStack.exception().toObject());
  11324      if (JSObject* exceptionStack = JS::ExceptionStackOrNull(exception)) {
  11325        stack.set(exceptionStack);
  11326      }
  11327    }
  11328 
  11329    if (!PrintStackTrace(cx, stack)) {
  11330      fputs("(Unable to print stack trace)\n", fp);
  11331      JS_ClearPendingException(cx);
  11332    }
  11333 
  11334    return true;
  11335  };
  11336 
  11337  // Get exception object and stack before printing and clearing exception.
  11338  JS::ExceptionStack exnStack(cx);
  11339  if (!JS::StealPendingExceptionStack(cx, &exnStack)) {
  11340    fprintf(stderr, "out of memory while stealing exception\n");
  11341    fflush(stderr);
  11342    JS_ClearPendingException(cx);
  11343    return;
  11344  }
  11345 
  11346  ShellContext* sc = GetShellContext(cx);
  11347  JS::ErrorReportBuilder report(cx);
  11348  if (!printError(cx, report, exnStack)) {
  11349    // Return if we couldn't initialize the error report.
  11350    return;
  11351  }
  11352 
  11353  // Print the error's cause, if available.
  11354  if (exnStack.exception().isObject()) {
  11355    JSObject* exception = &exnStack.exception().toObject();
  11356    if (exception->is<ErrorObject>()) {
  11357      auto* error = &exception->as<ErrorObject>();
  11358      if (auto maybeCause = error->getCause()) {
  11359        RootedValue cause(cx, maybeCause.value());
  11360 
  11361        RootedObject causeStack(cx);
  11362        if (cause.isObject()) {
  11363          RootedObject causeObj(cx, &cause.toObject());
  11364          causeStack = JS::ExceptionStackOrNull(causeObj);
  11365        }
  11366 
  11367        JS::ExceptionStack exnStack(cx, cause, causeStack);
  11368        JS::ErrorReportBuilder report(cx);
  11369        printError(cx, report, exnStack, "Caused by: ");
  11370      }
  11371    }
  11372  }
  11373 
  11374 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
  11375  // Don't quit the shell if an unhandled exception is reported during OOM
  11376  // testing.
  11377  if (cx->runningOOMTest) {
  11378    return;
  11379  }
  11380 #endif
  11381 
  11382  if (report.report()->errorNumber == JSMSG_OUT_OF_MEMORY) {
  11383    sc->exitCode = EXITCODE_OUT_OF_MEMORY;
  11384  } else {
  11385    sc->exitCode = EXITCODE_RUNTIME_ERROR;
  11386  }
  11387 }
  11388 
  11389 void js::shell::WarningReporter(JSContext* cx, JSErrorReport* report) {
  11390  ShellContext* sc = GetShellContext(cx);
  11391  FILE* fp = ErrorFilePointer();
  11392 
  11393  MOZ_ASSERT(report->isWarning());
  11394 
  11395  if (sc->lastWarningEnabled) {
  11396    JS::AutoSaveExceptionState savedExc(cx);
  11397    if (!CreateLastWarningObject(cx, report)) {
  11398      fputs("Unhandled error happened while creating last warning object.\n",
  11399            fp);
  11400      fflush(fp);
  11401    }
  11402    savedExc.restore();
  11403  }
  11404 
  11405  // Print the warning.
  11406  JS::PrintError(fp, report, reportWarnings);
  11407 }
  11408 
  11409 static bool global_enumerate(JSContext* cx, JS::HandleObject obj,
  11410                             JS::MutableHandleIdVector properties,
  11411                             bool enumerableOnly) {
  11412 #ifdef LAZY_STANDARD_CLASSES
  11413  return JS_NewEnumerateStandardClasses(cx, obj, properties, enumerableOnly);
  11414 #else
  11415  return true;
  11416 #endif
  11417 }
  11418 
  11419 static bool global_resolve(JSContext* cx, HandleObject obj, HandleId id,
  11420                           bool* resolvedp) {
  11421 #ifdef LAZY_STANDARD_CLASSES
  11422  if (!JS_ResolveStandardClass(cx, obj, id, resolvedp)) {
  11423    return false;
  11424  }
  11425 #endif
  11426  return true;
  11427 }
  11428 
  11429 static bool global_mayResolve(const JSAtomState& names, jsid id,
  11430                              JSObject* maybeObj) {
  11431  return JS_MayResolveStandardClass(names, id, maybeObj);
  11432 }
  11433 
  11434 static const JSClassOps global_classOps = {
  11435    nullptr,                   // addProperty
  11436    nullptr,                   // delProperty
  11437    nullptr,                   // enumerate
  11438    global_enumerate,          // newEnumerate
  11439    global_resolve,            // resolve
  11440    global_mayResolve,         // mayResolve
  11441    nullptr,                   // finalize
  11442    nullptr,                   // call
  11443    nullptr,                   // construct
  11444    JS_GlobalObjectTraceHook,  // trace
  11445 };
  11446 
  11447 static constexpr uint32_t DOM_PROTOTYPE_SLOT = JSCLASS_GLOBAL_SLOT_COUNT;
  11448 static constexpr uint32_t DOM_GLOBAL_SLOTS = 1;
  11449 
  11450 static const JSClass global_class = {
  11451    "global",
  11452    JSCLASS_GLOBAL_FLAGS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS),
  11453    &global_classOps,
  11454 };
  11455 
  11456 /*
  11457 * Define a FakeDOMObject constructor. It returns an object with a getter,
  11458 * setter and method with attached JitInfo. This object can be used to test
  11459 * IonMonkey DOM optimizations in the shell.
  11460 */
  11461 
  11462 /* Fow now just use to a constant we can check. */
  11463 static const void* DOM_PRIVATE_VALUE = (void*)0x1234;
  11464 
  11465 static bool dom_genericGetter(JSContext* cx, unsigned argc, JS::Value* vp);
  11466 
  11467 static bool dom_genericSetter(JSContext* cx, unsigned argc, JS::Value* vp);
  11468 
  11469 static bool dom_genericMethod(JSContext* cx, unsigned argc, JS::Value* vp);
  11470 
  11471 static bool dom_get_x(JSContext* cx, HandleObject obj, void* self,
  11472                      JSJitGetterCallArgs args) {
  11473  MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
  11474  MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
  11475  args.rval().set(JS_NumberValue(double(3.14)));
  11476  return true;
  11477 }
  11478 
  11479 static bool dom_set_x(JSContext* cx, HandleObject obj, void* self,
  11480                      JSJitSetterCallArgs args) {
  11481  MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
  11482  MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
  11483  return true;
  11484 }
  11485 
  11486 static bool dom_get_slot(JSContext* cx, HandleObject obj, void* self,
  11487                         JSJitGetterCallArgs args) {
  11488  MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
  11489  MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
  11490 
  11491  Value v = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT2);
  11492  MOZ_ASSERT(v.toInt32() == 42);
  11493  args.rval().set(v);
  11494  return true;
  11495 }
  11496 
  11497 static bool dom_get_global(JSContext* cx, HandleObject obj, void* self,
  11498                           JSJitGetterCallArgs args) {
  11499  MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
  11500  MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
  11501 
  11502  // Return the current global (instead of obj->global()) to test cx->realm
  11503  // switching in the JIT.
  11504  args.rval().setObject(*ToWindowProxyIfWindow(cx->global()));
  11505 
  11506  return true;
  11507 }
  11508 
  11509 static bool dom_set_global(JSContext* cx, HandleObject obj, void* self,
  11510                           JSJitSetterCallArgs args) {
  11511  MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
  11512  MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
  11513 
  11514  // Throw an exception if our argument is not the current global. This lets
  11515  // us test cx->realm switching.
  11516  if (!args[0].isObject() ||
  11517      ToWindowIfWindowProxy(&args[0].toObject()) != cx->global()) {
  11518    JS_ReportErrorASCII(cx, "Setter not called with matching global argument");
  11519    return false;
  11520  }
  11521 
  11522  return true;
  11523 }
  11524 
  11525 static bool dom_doFoo(JSContext* cx, HandleObject obj, void* self,
  11526                      const JSJitMethodCallArgs& args) {
  11527  MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
  11528  MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
  11529  MOZ_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm());
  11530 
  11531  /* Just return args.length(). */
  11532  args.rval().setInt32(args.length());
  11533  return true;
  11534 }
  11535 
  11536 static bool dom_doBar(JSContext* cx, HandleObject obj, void* self,
  11537                      const JSJitMethodCallArgs& args) {
  11538  MOZ_ASSERT(JS::GetClass(obj) == GetDomClass());
  11539  MOZ_ASSERT(self == DOM_PRIVATE_VALUE);
  11540  MOZ_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm());
  11541 
  11542  static const JSClass barClass = {
  11543      "BarObj",
  11544  };
  11545 
  11546  JSObject* retObj =
  11547      JS_NewObjectWithGivenProtoAndUseAllocSite(cx, &barClass, nullptr);
  11548  if (!retObj) {
  11549    return false;
  11550  }
  11551 
  11552  args.rval().setObject(*retObj);
  11553  return true;
  11554 }
  11555 
  11556 static const JSJitInfo dom_x_getterinfo = {
  11557    {(JSJitGetterOp)dom_get_x},
  11558    {0}, /* protoID */
  11559    {0}, /* depth */
  11560    JSJitInfo::Getter,
  11561    JSJitInfo::AliasNone, /* aliasSet */
  11562    JSVAL_TYPE_UNKNOWN,   /* returnType */
  11563    true,                 /* isInfallible. False in setters. */
  11564    true,                 /* isMovable */
  11565    true,                 /* isEliminatable */
  11566    false,                /* isAlwaysInSlot */
  11567    false,                /* isLazilyCachedInSlot */
  11568    false,                /* isTypedMethod */
  11569    0                     /* slotIndex */
  11570 };
  11571 
  11572 static const JSJitInfo dom_x_setterinfo = {
  11573    {(JSJitGetterOp)dom_set_x},
  11574    {0}, /* protoID */
  11575    {0}, /* depth */
  11576    JSJitInfo::Setter,
  11577    JSJitInfo::AliasEverything, /* aliasSet */
  11578    JSVAL_TYPE_UNKNOWN,         /* returnType */
  11579    false,                      /* isInfallible. False in setters. */
  11580    false,                      /* isMovable. */
  11581    false,                      /* isEliminatable. */
  11582    false,                      /* isAlwaysInSlot */
  11583    false,                      /* isLazilyCachedInSlot */
  11584    false,                      /* isTypedMethod */
  11585    0                           /* slotIndex */
  11586 };
  11587 
  11588 static const JSJitInfo dom_slot_getterinfo = {
  11589    {(JSJitGetterOp)dom_get_slot},
  11590    {0}, /* protoID */
  11591    {0}, /* depth */
  11592    JSJitInfo::Getter,
  11593    JSJitInfo::AliasNone, /* aliasSet */
  11594    JSVAL_TYPE_INT32,     /* returnType */
  11595    false,                /* isInfallible. False in setters. */
  11596    true,                 /* isMovable */
  11597    true,                 /* isEliminatable */
  11598    true,                 /* isAlwaysInSlot */
  11599    false,                /* isLazilyCachedInSlot */
  11600    false,                /* isTypedMethod */
  11601    DOM_OBJECT_SLOT2      /* slotIndex */
  11602 };
  11603 
  11604 // Note: this getter uses AliasEverything and is marked as fallible and
  11605 // non-movable (1) to prevent Ion from getting too clever optimizing it and
  11606 // (2) it's nice to have a few different kinds of getters in the shell.
  11607 static const JSJitInfo dom_global_getterinfo = {
  11608    {(JSJitGetterOp)dom_get_global},
  11609    {0}, /* protoID */
  11610    {0}, /* depth */
  11611    JSJitInfo::Getter,
  11612    JSJitInfo::AliasEverything, /* aliasSet */
  11613    JSVAL_TYPE_OBJECT,          /* returnType */
  11614    false,                      /* isInfallible. False in setters. */
  11615    false,                      /* isMovable */
  11616    false,                      /* isEliminatable */
  11617    false,                      /* isAlwaysInSlot */
  11618    false,                      /* isLazilyCachedInSlot */
  11619    false,                      /* isTypedMethod */
  11620    0                           /* slotIndex */
  11621 };
  11622 
  11623 static const JSJitInfo dom_global_setterinfo = {
  11624    {(JSJitGetterOp)dom_set_global},
  11625    {0}, /* protoID */
  11626    {0}, /* depth */
  11627    JSJitInfo::Setter,
  11628    JSJitInfo::AliasEverything, /* aliasSet */
  11629    JSVAL_TYPE_UNKNOWN,         /* returnType */
  11630    false,                      /* isInfallible. False in setters. */
  11631    false,                      /* isMovable. */
  11632    false,                      /* isEliminatable. */
  11633    false,                      /* isAlwaysInSlot */
  11634    false,                      /* isLazilyCachedInSlot */
  11635    false,                      /* isTypedMethod */
  11636    0                           /* slotIndex */
  11637 };
  11638 
  11639 static const JSJitInfo doFoo_methodinfo = {
  11640    {(JSJitGetterOp)dom_doFoo},
  11641    {0}, /* protoID */
  11642    {0}, /* depth */
  11643    JSJitInfo::Method,
  11644    JSJitInfo::AliasEverything, /* aliasSet */
  11645    JSVAL_TYPE_UNKNOWN,         /* returnType */
  11646    false,                      /* isInfallible. False in setters. */
  11647    false,                      /* isMovable */
  11648    false,                      /* isEliminatable */
  11649    false,                      /* isAlwaysInSlot */
  11650    false,                      /* isLazilyCachedInSlot */
  11651    false,                      /* isTypedMethod */
  11652    0                           /* slotIndex */
  11653 };
  11654 
  11655 static const JSJitInfo doBar_methodinfo = {
  11656    {(JSJitGetterOp)dom_doBar},
  11657    {0}, /* protoID */
  11658    {0}, /* depth */
  11659    JSJitInfo::Method,
  11660    JSJitInfo::AliasEverything, /* aliasSet */
  11661    JSVAL_TYPE_OBJECT,          /* returnType */
  11662    false,                      /* isInfallible. False in setters. */
  11663    false,                      /* isMovable */
  11664    false,                      /* isEliminatable */
  11665    false,                      /* isAlwaysInSlot */
  11666    false,                      /* isLazilyCachedInSlot */
  11667    false,                      /* isTypedMethod */
  11668    0                           /* slotIndex */
  11669 };
  11670 
  11671 static const JSPropertySpec dom_props[] = {
  11672    JSPropertySpec::nativeAccessors("x", JSPROP_ENUMERATE, dom_genericGetter,
  11673                                    &dom_x_getterinfo, dom_genericSetter,
  11674                                    &dom_x_setterinfo),
  11675    JSPropertySpec::nativeAccessors("slot", JSPROP_ENUMERATE, dom_genericGetter,
  11676                                    &dom_slot_getterinfo),
  11677    JSPropertySpec::nativeAccessors("global", JSPROP_ENUMERATE,
  11678                                    dom_genericGetter, &dom_global_getterinfo,
  11679                                    dom_genericSetter, &dom_global_setterinfo),
  11680    JS_PS_END,
  11681 };
  11682 
  11683 static const JSFunctionSpec dom_methods[] = {
  11684    JS_FNINFO("doFoo", dom_genericMethod, &doFoo_methodinfo, 3,
  11685              JSPROP_ENUMERATE),
  11686    JS_FNINFO("doBar", dom_genericMethod, &doBar_methodinfo, 3,
  11687              JSPROP_ENUMERATE),
  11688    JS_FS_END,
  11689 };
  11690 
  11691 static void FakeDOMObject_finalize(JS::GCContext* gcx, JSObject* obj) {
  11692  // Dummy finalize method so we can swap with background finalized object.
  11693 }
  11694 
  11695 static const JSClassOps FakeDOMObjectClassOps = {
  11696    nullptr,  // addProperty
  11697    nullptr,  // delProperty
  11698    nullptr,  // enumerate
  11699    nullptr,  // newEnumerate
  11700    nullptr,  // resolve
  11701    nullptr,  // mayResolve
  11702    FakeDOMObject_finalize,
  11703    nullptr,  // call
  11704    nullptr,  // construct
  11705    nullptr,
  11706 };
  11707 
  11708 static const JSClass dom_class = {
  11709    "FakeDOMObject",
  11710    JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2) |
  11711        JSCLASS_BACKGROUND_FINALIZE | JSCLASS_PRESERVES_WRAPPER,
  11712    &FakeDOMObjectClassOps};
  11713 
  11714 static const JSClass* GetDomClass() { return &dom_class; }
  11715 
  11716 static bool dom_genericGetter(JSContext* cx, unsigned argc, JS::Value* vp) {
  11717  CallArgs args = CallArgsFromVp(argc, vp);
  11718 
  11719  if (!args.thisv().isObject()) {
  11720    args.rval().setUndefined();
  11721    return true;
  11722  }
  11723 
  11724  RootedObject obj(cx, &args.thisv().toObject());
  11725  if (JS::GetClass(obj) != &dom_class) {
  11726    args.rval().set(UndefinedValue());
  11727    return true;
  11728  }
  11729 
  11730  JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
  11731 
  11732  const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  11733  MOZ_ASSERT(info->type() == JSJitInfo::Getter);
  11734  JSJitGetterOp getter = info->getter;
  11735  return getter(cx, obj, val.toPrivate(), JSJitGetterCallArgs(args));
  11736 }
  11737 
  11738 static bool dom_genericSetter(JSContext* cx, unsigned argc, JS::Value* vp) {
  11739  CallArgs args = CallArgsFromVp(argc, vp);
  11740 
  11741  if (args.length() < 1 || !args.thisv().isObject()) {
  11742    args.rval().setUndefined();
  11743    return true;
  11744  }
  11745 
  11746  RootedObject obj(cx, &args.thisv().toObject());
  11747  if (JS::GetClass(obj) != &dom_class) {
  11748    args.rval().set(UndefinedValue());
  11749    return true;
  11750  }
  11751 
  11752  JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
  11753 
  11754  const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  11755  MOZ_ASSERT(info->type() == JSJitInfo::Setter);
  11756  JSJitSetterOp setter = info->setter;
  11757  if (!setter(cx, obj, val.toPrivate(), JSJitSetterCallArgs(args))) {
  11758    return false;
  11759  }
  11760  args.rval().set(UndefinedValue());
  11761  return true;
  11762 }
  11763 
  11764 static bool dom_genericMethod(JSContext* cx, unsigned argc, JS::Value* vp) {
  11765  CallArgs args = CallArgsFromVp(argc, vp);
  11766 
  11767  if (!args.thisv().isObject()) {
  11768    args.rval().setUndefined();
  11769    return true;
  11770  }
  11771 
  11772  RootedObject obj(cx, &args.thisv().toObject());
  11773  if (JS::GetClass(obj) != &dom_class) {
  11774    args.rval().set(UndefinedValue());
  11775    return true;
  11776  }
  11777 
  11778  JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
  11779 
  11780  const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  11781  MOZ_ASSERT(info->type() == JSJitInfo::Method);
  11782  JSJitMethodOp method = info->method;
  11783  return method(cx, obj, val.toPrivate(), JSJitMethodCallArgs(args));
  11784 }
  11785 
  11786 static void InitDOMObject(HandleObject obj) {
  11787  JS::SetReservedSlot(obj, DOM_OBJECT_SLOT,
  11788                      PrivateValue(const_cast<void*>(DOM_PRIVATE_VALUE)));
  11789  JS::SetReservedSlot(obj, DOM_OBJECT_SLOT2, Int32Value(42));
  11790 }
  11791 
  11792 static JSObject* GetDOMPrototype(JSContext* cx, JSObject* global) {
  11793  MOZ_ASSERT(JS_IsGlobalObject(global));
  11794  if (JS::GetClass(global) != &global_class) {
  11795    JS_ReportErrorASCII(cx, "Can't get FakeDOMObject prototype in sandbox");
  11796    return nullptr;
  11797  }
  11798 
  11799  const JS::Value& slot = JS::GetReservedSlot(global, DOM_PROTOTYPE_SLOT);
  11800  MOZ_ASSERT(slot.isObject());
  11801  return &slot.toObject();
  11802 }
  11803 
  11804 static bool dom_constructor(JSContext* cx, unsigned argc, JS::Value* vp) {
  11805  CallArgs args = CallArgsFromVp(argc, vp);
  11806 
  11807  RootedObject callee(cx, &args.callee());
  11808  RootedValue protov(cx);
  11809  if (!GetProperty(cx, callee, callee, cx->names().prototype, &protov)) {
  11810    return false;
  11811  }
  11812 
  11813  if (!protov.isObject()) {
  11814    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_PROTOTYPE,
  11815                              "FakeDOMObject");
  11816    return false;
  11817  }
  11818 
  11819  RootedObject proto(cx, &protov.toObject());
  11820  RootedObject domObj(cx, JS_NewObjectWithGivenProto(cx, &dom_class, proto));
  11821  if (!domObj) {
  11822    return false;
  11823  }
  11824 
  11825  InitDOMObject(domObj);
  11826 
  11827  args.rval().setObject(*domObj);
  11828  return true;
  11829 }
  11830 
  11831 static bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID,
  11832                                         uint32_t depth) {
  11833  // Only the (fake) DOM object supports any JIT optimizations.
  11834  return clasp == GetDomClass();
  11835 }
  11836 
  11837 static bool InstanceClassIsError(const JSClass* clasp) { return false; }
  11838 
  11839 static bool ExtractExceptionInfo(JSContext* cx, JS::HandleObject obj,
  11840                                 bool* isException,
  11841                                 JS::MutableHandle<JSString*> fileName,
  11842                                 uint32_t* line, uint32_t* column,
  11843                                 JS::MutableHandle<JSString*> message) {
  11844  *isException = false;
  11845  return true;
  11846 }
  11847 
  11848 static bool ShellBuildId(JS::BuildIdCharVector* buildId) {
  11849  // The browser embeds the date into the buildid and the buildid is embedded
  11850  // in the binary, so every 'make' necessarily builds a new firefox binary.
  11851  // Fortunately, the actual firefox executable is tiny -- all the code is in
  11852  // libxul.so and other shared modules -- so this isn't a big deal. Not so
  11853  // for the statically-linked JS shell. To avoid recompiling js.cpp and
  11854  // re-linking 'js' on every 'make', we use a constant buildid and rely on
  11855  // the shell user to manually clear any caches between cache-breaking updates.
  11856  const char buildid[] = "JS-shell";
  11857  return buildId->append(buildid, sizeof(buildid));
  11858 }
  11859 
  11860 static bool TimesAccessed(JSContext* cx, unsigned argc, Value* vp) {
  11861  static int32_t accessed = 0;
  11862  CallArgs args = CallArgsFromVp(argc, vp);
  11863  args.rval().setInt32(++accessed);
  11864  return true;
  11865 }
  11866 
  11867 static const JSPropertySpec TestingProperties[] = {
  11868    JS_PSG("timesAccessed", TimesAccessed, 0),
  11869    JS_PS_END,
  11870 };
  11871 
  11872 static JSObject* NewGlobalObject(JSContext* cx, JS::RealmOptions& options,
  11873                                 JSPrincipals* principals, ShellGlobalKind kind,
  11874                                 bool immutablePrototype) {
  11875  RootedObject glob(cx,
  11876                    JS_NewGlobalObject(cx, &global_class, principals,
  11877                                       JS::DontFireOnNewGlobalHook, options));
  11878  if (!glob) {
  11879    return nullptr;
  11880  }
  11881 
  11882  {
  11883    JSAutoRealm ar(cx, glob);
  11884 
  11885    if (kind == ShellGlobalKind::WindowProxy) {
  11886      RootedObject proxy(cx, NewShellWindowProxy(cx, glob));
  11887      if (!proxy) {
  11888        return nullptr;
  11889      }
  11890      js::SetWindowProxy(cx, glob, proxy);
  11891    }
  11892 
  11893 #ifndef LAZY_STANDARD_CLASSES
  11894    if (!JS::InitRealmStandardClasses(cx)) {
  11895      return nullptr;
  11896    }
  11897 #endif
  11898 
  11899    if (immutablePrototype) {
  11900      bool succeeded;
  11901      if (!JS_SetImmutablePrototype(cx, glob, &succeeded)) {
  11902        return nullptr;
  11903      }
  11904      MOZ_ASSERT(succeeded,
  11905                 "a fresh, unexposed global object is always capable of "
  11906                 "having its [[Prototype]] be immutable");
  11907    }
  11908 
  11909 #ifdef JS_HAS_CTYPES
  11910    if (!fuzzingSafe && !JS::InitCTypesClass(cx, glob)) {
  11911      return nullptr;
  11912    }
  11913 #endif
  11914    if (!JS_InitReflectParse(cx, glob)) {
  11915      return nullptr;
  11916    }
  11917    if (!JS_DefineDebuggerObject(cx, glob)) {
  11918      return nullptr;
  11919    }
  11920    if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) ||
  11921        !JS_DefineProfilingFunctions(cx, glob)) {
  11922      return nullptr;
  11923    }
  11924 #ifdef FUZZING_JS_FUZZILLI
  11925    if (!JS_DefineFunctions(cx, glob, shell_function_fuzzilli_hash)) {
  11926      return nullptr;
  11927    }
  11928 #endif
  11929    if (!js::DefineTestingFunctions(cx, glob, fuzzingSafe,
  11930                                    disableOOMFunctions)) {
  11931      return nullptr;
  11932    }
  11933    if (!JS_DefineProperties(cx, glob, TestingProperties)) {
  11934      return nullptr;
  11935    }
  11936 
  11937    if (!fuzzingSafe) {
  11938      if (!JS_DefineFunctionsWithHelp(cx, glob, fuzzing_unsafe_functions)) {
  11939        return nullptr;
  11940      }
  11941      if (!DefineConsole(cx, glob)) {
  11942        return nullptr;
  11943      }
  11944    }
  11945 
  11946    if (!DefineOS(cx, glob, fuzzingSafe, &gOutFile, &gErrFile)) {
  11947      return nullptr;
  11948    }
  11949 
  11950    if (!js::SupportDifferentialTesting()) {
  11951      if (!JS_DefineFunctionsWithHelp(cx, glob,
  11952                                      diff_testing_unsafe_functions)) {
  11953        return nullptr;
  11954      }
  11955 
  11956      RootedObject performanceObj(cx, JS_NewObject(cx, nullptr));
  11957      if (!performanceObj) {
  11958        return nullptr;
  11959      }
  11960      if (!JS_DefineFunctionsWithHelp(cx, performanceObj,
  11961                                      performance_functions)) {
  11962        return nullptr;
  11963      }
  11964      RootedObject mozMemoryObj(cx, JS_NewObject(cx, nullptr));
  11965      if (!mozMemoryObj) {
  11966        return nullptr;
  11967      }
  11968      RootedObject gcObj(cx, gc::NewMemoryInfoObject(cx));
  11969      if (!gcObj) {
  11970        return nullptr;
  11971      }
  11972      if (!JS_DefineProperty(cx, glob, "performance", performanceObj,
  11973                             JSPROP_ENUMERATE)) {
  11974        return nullptr;
  11975      }
  11976      if (!JS_DefineProperty(cx, performanceObj, "mozMemory", mozMemoryObj,
  11977                             JSPROP_ENUMERATE)) {
  11978        return nullptr;
  11979      }
  11980      if (!JS_DefineProperty(cx, mozMemoryObj, "gc", gcObj, JSPROP_ENUMERATE)) {
  11981        return nullptr;
  11982      }
  11983    }
  11984 
  11985    /* Initialize FakeDOMObject. */
  11986    static const js::DOMCallbacks DOMcallbacks = {InstanceClassHasProtoAtDepth,
  11987                                                  InstanceClassIsError,
  11988                                                  ExtractExceptionInfo};
  11989    SetDOMCallbacks(cx, &DOMcallbacks);
  11990 
  11991    RootedObject domProto(
  11992        cx, JS_InitClass(cx, glob, &dom_class, nullptr, "FakeDOMObject",
  11993                         dom_constructor, 0, dom_props, dom_methods, nullptr,
  11994                         nullptr));
  11995    if (!domProto) {
  11996      return nullptr;
  11997    }
  11998 
  11999    // FakeDOMObject.prototype is the only DOM object which needs to retrieved
  12000    // in the shell; store it directly instead of creating a separate layer
  12001    // (ProtoAndIfaceCache) as done in the browser.
  12002    JS::SetReservedSlot(glob, DOM_PROTOTYPE_SLOT, ObjectValue(*domProto));
  12003 
  12004    /* Initialize FakeDOMObject.prototype */
  12005    InitDOMObject(domProto);
  12006 
  12007    if (!DefineToStringTag(cx, glob, cx->names().global)) {
  12008      return nullptr;
  12009    }
  12010 
  12011    JS_FireOnNewGlobalObject(cx, glob);
  12012  }
  12013 
  12014  return glob;
  12015 }
  12016 
  12017 static bool BindScriptArgs(JSContext* cx, OptionParser* op) {
  12018  AutoReportException are(cx);
  12019 
  12020  MultiStringRange msr = op->getMultiStringArg("scriptArgs");
  12021  RootedObject scriptArgs(cx);
  12022  scriptArgs = JS::NewArrayObject(cx, 0);
  12023  if (!scriptArgs) {
  12024    return false;
  12025  }
  12026 
  12027  if (!JS_DefineProperty(cx, cx->global(), "scriptArgs", scriptArgs, 0)) {
  12028    return false;
  12029  }
  12030 
  12031  for (size_t i = 0; !msr.empty(); msr.popFront(), ++i) {
  12032    const char* scriptArg = msr.front();
  12033    UniqueChars scriptArgUtf8 = JS::EncodeNarrowToUtf8(cx, scriptArg);
  12034    if (!scriptArgUtf8) {
  12035      return false;
  12036    }
  12037    RootedString str(cx, NewStringCopyUTF8(cx, scriptArgUtf8.get()));
  12038    if (!str || !JS_DefineElement(cx, scriptArgs, i, str, JSPROP_ENUMERATE)) {
  12039      return false;
  12040    }
  12041  }
  12042 
  12043  RootedValue scriptPathValue(cx);
  12044  if (const char* scriptPath = op->getStringArg("script")) {
  12045    UniqueChars scriptPathUtf8 = JS::EncodeNarrowToUtf8(cx, scriptPath);
  12046    if (!scriptPathUtf8) {
  12047      return false;
  12048    }
  12049    RootedString scriptPathString(cx,
  12050                                  NewStringCopyUTF8(cx, scriptPathUtf8.get()));
  12051    if (!scriptPathString) {
  12052      return false;
  12053    }
  12054    scriptPathValue = StringValue(scriptPathString);
  12055  } else {
  12056    scriptPathValue = UndefinedValue();
  12057  }
  12058 
  12059  if (!JS_DefineProperty(cx, cx->global(), "scriptPath", scriptPathValue, 0)) {
  12060    return false;
  12061  }
  12062 
  12063  return true;
  12064 }
  12065 
  12066 static bool OptionFailure(const char* option, const char* str) {
  12067  fprintf(stderr, "Unrecognized option for %s: %s\n", option, str);
  12068  return false;
  12069 }
  12070 
  12071 template <typename... Ts>
  12072 auto minVal(Ts... args);
  12073 template <typename T>
  12074 auto minVal(T a) {
  12075  return a;
  12076 }
  12077 
  12078 template <typename T, typename... Ts>
  12079 auto minVal(T a, Ts... args) {
  12080  return std::min(a, minVal(args...));
  12081 }
  12082 
  12083 [[nodiscard]] static bool ProcessArgs(JSContext* cx, OptionParser* op) {
  12084  ShellContext* sc = GetShellContext(cx);
  12085 
  12086  /* |scriptArgs| gets bound on the global before any code is run. */
  12087  if (!BindScriptArgs(cx, op)) {
  12088    return false;
  12089  }
  12090 
  12091  MultiStringRange filePaths = op->getMultiStringOption('f');
  12092  MultiStringRange utf16FilePaths = op->getMultiStringOption('u');
  12093  MultiStringRange preludePaths = op->getMultiStringOption('p');
  12094  MultiStringRange codeChunks = op->getMultiStringOption('e');
  12095  MultiStringRange modulePaths = op->getMultiStringOption('m');
  12096 
  12097 #ifdef FUZZING_JS_FUZZILLI
  12098  // Check for REPRL file source
  12099  if (op->getBoolOption("reprl")) {
  12100    return FuzzilliReprlGetAndRun(cx);
  12101  }
  12102 #endif /* FUZZING_JS_FUZZILLI */
  12103 
  12104  if (filePaths.empty() && utf16FilePaths.empty() && codeChunks.empty() &&
  12105      modulePaths.empty() && !op->getStringArg("script")) {
  12106    // Always use the interactive shell when -i is used. Without -i we let
  12107    // Process figure it out based on isatty.
  12108    bool forceTTY = op->getBoolOption('i');
  12109    return Process(cx, nullptr, forceTTY, FileScript);
  12110  }
  12111 
  12112  while (!preludePaths.empty() || !filePaths.empty() ||
  12113         !utf16FilePaths.empty() || !codeChunks.empty() ||
  12114         !modulePaths.empty()) {
  12115    size_t ppArgno = preludePaths.empty() ? SIZE_MAX : preludePaths.argno();
  12116    size_t fpArgno = filePaths.empty() ? SIZE_MAX : filePaths.argno();
  12117    size_t ufpArgno =
  12118        utf16FilePaths.empty() ? SIZE_MAX : utf16FilePaths.argno();
  12119    size_t ccArgno = codeChunks.empty() ? SIZE_MAX : codeChunks.argno();
  12120    size_t mpArgno = modulePaths.empty() ? SIZE_MAX : modulePaths.argno();
  12121    size_t minArgno = minVal(ppArgno, fpArgno, ufpArgno, ccArgno, mpArgno);
  12122 
  12123    if (ppArgno == minArgno) {
  12124      UniqueChars path = JS::EncodeNarrowToUtf8(cx, preludePaths.front());
  12125      if (!path) {
  12126        return false;
  12127      }
  12128      if (!Process(cx, path.get(), false, PreludeScript)) {
  12129        return false;
  12130      }
  12131 
  12132      preludePaths.popFront();
  12133      continue;
  12134    }
  12135 
  12136    if (fpArgno == minArgno) {
  12137      UniqueChars path = JS::EncodeNarrowToUtf8(cx, filePaths.front());
  12138      if (!path) {
  12139        return false;
  12140      }
  12141      if (!Process(cx, path.get(), false, FileScript)) {
  12142        return false;
  12143      }
  12144 
  12145      filePaths.popFront();
  12146      continue;
  12147    }
  12148 
  12149    if (ufpArgno == minArgno) {
  12150      UniqueChars path = JS::EncodeNarrowToUtf8(cx, utf16FilePaths.front());
  12151      if (!path) {
  12152        return false;
  12153      }
  12154      if (!Process(cx, path.get(), false, FileScriptUtf16)) {
  12155        return false;
  12156      }
  12157 
  12158      utf16FilePaths.popFront();
  12159      continue;
  12160    }
  12161 
  12162    if (ccArgno == minArgno) {
  12163      UniqueChars code = JS::EncodeNarrowToUtf8(cx, codeChunks.front());
  12164      if (!code) {
  12165        return false;
  12166      }
  12167 
  12168      // Command line scripts are always parsed with full-parse to evaluate
  12169      // conditions which might filter code coverage conditions.
  12170      JS::CompileOptions opts(cx);
  12171      opts.setFileAndLine("-e", 1).setForceFullParse();
  12172 
  12173      JS::SourceText<Utf8Unit> srcBuf;
  12174      if (!srcBuf.init(cx, code.get(), strlen(code.get()),
  12175                       JS::SourceOwnership::Borrowed)) {
  12176        return false;
  12177      }
  12178 
  12179      RootedValue rval(cx);
  12180      if (!JS::Evaluate(cx, opts, srcBuf, &rval)) {
  12181        return false;
  12182      }
  12183 
  12184      codeChunks.popFront();
  12185      if (sc->quitting) {
  12186        break;
  12187      }
  12188 
  12189      continue;
  12190    }
  12191 
  12192    MOZ_ASSERT(mpArgno == minArgno);
  12193 
  12194    UniqueChars path = JS::EncodeNarrowToUtf8(cx, modulePaths.front());
  12195    if (!path) {
  12196      return false;
  12197    }
  12198    if (!Process(cx, path.get(), false, FileModule)) {
  12199      return false;
  12200    }
  12201 
  12202    modulePaths.popFront();
  12203  }
  12204 
  12205  if (sc->quitting) {
  12206    return false;
  12207  }
  12208 
  12209  /* The |script| argument is processed after all options. */
  12210  if (const char* path = op->getStringArg("script")) {
  12211    UniqueChars pathUtf8 = JS::EncodeNarrowToUtf8(cx, path);
  12212    if (!pathUtf8) {
  12213      return false;
  12214    }
  12215    if (!Process(cx, pathUtf8.get(), false, FileScript)) {
  12216      return false;
  12217    }
  12218  }
  12219 
  12220  if (op->getBoolOption('i')) {
  12221    if (!Process(cx, nullptr, true, FileScript)) {
  12222      return false;
  12223    }
  12224  }
  12225 
  12226  return true;
  12227 }
  12228 
  12229 static void SetWorkerContextOptions(JSContext* cx) {
  12230  // Copy option values from the main thread.
  12231  JS::ContextOptionsRef(cx)
  12232      .setAsmJS(enableAsmJS)
  12233      .setWasm(enableWasm)
  12234      .setWasmBaseline(enableWasmBaseline)
  12235      .setWasmIon(enableWasmOptimizing)
  12236 
  12237      .setTestWasmAwaitTier2(enableTestWasmAwaitTier2)
  12238      .setSourcePragmas(enableSourcePragmas);
  12239 
  12240  cx->runtime()->setOffthreadBaselineCompilationEnabled(
  12241      offthreadBaselineCompilation);
  12242  cx->runtime()->setOffthreadIonCompilationEnabled(offthreadIonCompilation);
  12243  cx->runtime()->profilingScripts =
  12244      enableCodeCoverage || enableDisassemblyDumps;
  12245 
  12246 #ifdef JS_GC_ZEAL
  12247  if (gZealBits && gZealFrequency) {
  12248    for (size_t i = 0; i < size_t(gc::ZealMode::Count); i++) {
  12249      if (gZealBits & (1 << i)) {
  12250        cx->runtime()->gc.setZeal(i, gZealFrequency);
  12251      }
  12252    }
  12253  }
  12254 #endif
  12255 
  12256  JS_SetNativeStackQuota(cx, gWorkerStackSize);
  12257 }
  12258 
  12259 [[nodiscard]] static bool PrintUnhandledRejection(
  12260    JSContext* cx, Handle<PromiseObject*> promise) {
  12261  RootedValue reason(cx, promise->reason());
  12262  RootedObject site(cx, promise->resolutionSite());
  12263 
  12264  RootedString str(cx, JS_ValueToSource(cx, reason));
  12265  if (!str) {
  12266    return false;
  12267  }
  12268 
  12269  UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
  12270  if (!utf8chars) {
  12271    return false;
  12272  }
  12273 
  12274  FILE* fp = ErrorFilePointer();
  12275  fprintf(fp, "Unhandled rejection: %s\n", utf8chars.get());
  12276 
  12277  if (!site) {
  12278    fputs("(no stack trace available)\n", stderr);
  12279    return true;
  12280  }
  12281 
  12282  JSPrincipals* principals = cx->realm()->principals();
  12283  RootedString stackStr(cx);
  12284  if (!BuildStackString(cx, principals, site, &stackStr, 2)) {
  12285    return false;
  12286  }
  12287 
  12288  UniqueChars stack = JS_EncodeStringToUTF8(cx, stackStr);
  12289  if (!stack) {
  12290    return false;
  12291  }
  12292 
  12293  fputs("Stack:\n", fp);
  12294  fputs(stack.get(), fp);
  12295 
  12296  return true;
  12297 }
  12298 
  12299 [[nodiscard]] static bool ReportUnhandledRejections(JSContext* cx) {
  12300  ShellContext* sc = GetShellContext(cx);
  12301  if (!sc->trackUnhandledRejections) {
  12302    return true;
  12303  }
  12304 
  12305  if (!sc->unhandledRejectedPromises) {
  12306    return true;
  12307  }
  12308 
  12309  AutoRealm ar(cx, sc->unhandledRejectedPromises);
  12310 
  12311  if (sc->unhandledRejectedPromises->size() == 0) {
  12312    return true;
  12313  }
  12314 
  12315  sc->exitCode = EXITCODE_RUNTIME_ERROR;
  12316 
  12317  RootedValue iter(cx);
  12318  if (!SetObject::iterator(cx, SetObject::IteratorKind::Values,
  12319                           sc->unhandledRejectedPromises, &iter)) {
  12320    return false;
  12321  }
  12322 
  12323  Rooted<SetIteratorObject*> iterObj(cx,
  12324                                     &iter.toObject().as<SetIteratorObject>());
  12325  JSObject* obj = SetIteratorObject::createResult(cx);
  12326  if (!obj) {
  12327    return false;
  12328  }
  12329 
  12330  Rooted<ArrayObject*> resultObj(cx, &obj->as<ArrayObject>());
  12331  while (true) {
  12332    bool done = SetIteratorObject::next(iterObj, resultObj);
  12333    if (done) {
  12334      break;
  12335    }
  12336 
  12337    RootedObject obj(cx, &resultObj->getDenseElement(0).toObject());
  12338    Rooted<PromiseObject*> promise(cx, obj->maybeUnwrapIf<PromiseObject>());
  12339    if (!promise) {
  12340      FILE* fp = ErrorFilePointer();
  12341      fputs(
  12342          "Unhandled rejection: dead proxy found in unhandled "
  12343          "rejections set\n",
  12344          fp);
  12345      continue;
  12346    }
  12347 
  12348    AutoRealm ar2(cx, promise);
  12349 
  12350    if (!PrintUnhandledRejection(cx, promise)) {
  12351      return false;
  12352    }
  12353  }
  12354 
  12355  sc->unhandledRejectedPromises = nullptr;
  12356 
  12357  return true;
  12358 }
  12359 
  12360 bool ShellContext::registerWithCx(JSContext* cx) {
  12361  cx_ = cx;
  12362  JS_SetContextPrivate(cx, this);
  12363 
  12364  if (isWorker) {
  12365    SetWorkerContextOptions(cx);
  12366  }
  12367 
  12368  JS::SetWarningReporter(cx, WarningReporter);
  12369  JS_SetFutexCanWait(cx);
  12370  JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy);
  12371  JS_SetDestroyCompartmentCallback(cx, DestroyShellCompartmentPrivate);
  12372  js::SetWindowProxyClass(cx, &ShellWindowProxyClass);
  12373 
  12374  js::UseInternalJobQueues(cx);
  12375 
  12376  js::SetPreserveWrapperCallbacks(cx, DummyPreserveWrapperCallback,
  12377                                  DummyHasReleasedWrapperCallback);
  12378 
  12379  JS::SetHostCleanupFinalizationRegistryCallback(
  12380      cx, ShellCleanupFinalizationRegistryCallback, this);
  12381  JS_AddExtraGCRootsTracer(cx, TraceBlackRoots, nullptr);
  12382  JS_SetGrayGCRootsTracer(cx, TraceGrayRoots, nullptr);
  12383 
  12384  return true;
  12385 }
  12386 
  12387 ShellContext::~ShellContext() {
  12388  markObservers.reset();
  12389  if (cx_) {
  12390    JS_SetContextPrivate(cx_, nullptr);
  12391    JS::SetHostCleanupFinalizationRegistryCallback(cx_, nullptr, nullptr);
  12392    JS_SetGrayGCRootsTracer(cx_, nullptr, nullptr);
  12393    JS_RemoveExtraGCRootsTracer(cx_, TraceBlackRoots, nullptr);
  12394  }
  12395  MOZ_ASSERT(offThreadJobs.empty());
  12396 }
  12397 
  12398 static int Shell(JSContext* cx, OptionParser* op) {
  12399 #ifdef JS_STRUCTURED_SPEW
  12400  cx->spewer().enableSpewing();
  12401 #endif
  12402 
  12403  auto exitShell = MakeScopeExit([&] {
  12404 #ifdef JS_STRUCTURED_SPEW
  12405    cx->spewer().disableSpewing();
  12406 #endif
  12407  });
  12408 
  12409 #ifdef MOZ_CODE_COVERAGE
  12410  InstallCoverageSignalHandlers();
  12411 #endif
  12412 
  12413  Maybe<JS::AutoDisableGenerationalGC> noggc;
  12414  if (op->getBoolOption("no-ggc")) {
  12415    noggc.emplace(cx);
  12416  }
  12417 
  12418  Maybe<AutoDisableCompactingGC> nocgc;
  12419  if (op->getBoolOption("no-cgc")) {
  12420    nocgc.emplace(cx);
  12421  }
  12422 
  12423 #ifdef DEBUG
  12424  if (op->getBoolOption("differential-testing")) {
  12425    JS::SetSupportDifferentialTesting(true);
  12426  }
  12427 #endif
  12428 
  12429  if (op->getBoolOption("disable-oom-functions")) {
  12430    disableOOMFunctions = true;
  12431  }
  12432 
  12433  if (op->getBoolOption("more-compartments")) {
  12434    defaultToSameCompartment = false;
  12435  }
  12436 
  12437  bool reprl_mode = FuzzilliUseReprlMode(op);
  12438 
  12439  // Begin REPRL Loop
  12440  int result = EXIT_SUCCESS;
  12441  do {
  12442    JS::RealmOptions options;
  12443    SetStandardRealmOptions(options);
  12444    RootedObject glob(
  12445        cx, NewGlobalObject(cx, options, nullptr, ShellGlobalKind::WindowProxy,
  12446                            /* immutablePrototype = */ true));
  12447    if (!glob) {
  12448      return 1;
  12449    }
  12450 
  12451    JSAutoRealm ar(cx, glob);
  12452 
  12453    ShellContext* sc = GetShellContext(cx);
  12454    if (!sc->moduleLoader && !InitModuleLoader(cx, *op)) {
  12455      return EXIT_FAILURE;
  12456    }
  12457 
  12458 #ifdef FUZZING_INTERFACES
  12459    if (fuzzHaveModule) {
  12460      return FuzzJSRuntimeStart(cx, &sArgc, &sArgv);
  12461    }
  12462 #endif
  12463 
  12464    sc->exitCode = 0;
  12465    result = EXIT_SUCCESS;
  12466    {
  12467      AutoReportException are(cx);
  12468      if (!ProcessArgs(cx, op) && !sc->quitting) {
  12469        result = EXITCODE_RUNTIME_ERROR;
  12470      }
  12471    }
  12472 
  12473    /*
  12474     * The job queue must be drained even on error to finish outstanding async
  12475     * tasks before the main thread JSRuntime is torn down. Drain after
  12476     * uncaught exceptions have been reported since draining runs callbacks.
  12477     */
  12478    RunShellJobs(cx);
  12479 
  12480    // Only if there's no other error, report unhandled rejections.
  12481    if (!result && !sc->exitCode) {
  12482      AutoReportException are(cx);
  12483      if (!ReportUnhandledRejections(cx)) {
  12484        FILE* fp = ErrorFilePointer();
  12485        fputs("Error while printing unhandled rejection\n", fp);
  12486      }
  12487    }
  12488 
  12489    if (sc->exitCode) {
  12490      result = sc->exitCode;
  12491    }
  12492 
  12493 #ifdef FUZZING_JS_FUZZILLI
  12494    if (reprl_mode) {
  12495      fflush(stdout);
  12496      fflush(stderr);
  12497      // Send return code to parent and reset edge counters.
  12498      int status = (result & 0xff) << 8;
  12499      if (js::SupportDifferentialTesting()) {
  12500        struct {
  12501          int status;
  12502          uint32_t execHash;
  12503          uint32_t execHashInputs;
  12504        } s;
  12505        s.status = status;
  12506        s.execHash = cx->executionHash;
  12507        s.execHashInputs = cx->executionHashInputs;
  12508        MOZ_RELEASE_ASSERT(write(REPRL_CWFD, &s, 12) == 12);
  12509      } else {
  12510        MOZ_RELEASE_ASSERT(write(REPRL_CWFD, &status, 4) == 4);
  12511      }
  12512      __sanitizer_cov_reset_edgeguards();
  12513      cx->executionHash = 1;
  12514      cx->executionHashInputs = 0;
  12515    }
  12516 #endif
  12517 
  12518    if (enableDisassemblyDumps) {
  12519      AutoReportException are(cx);
  12520      if (!js::DumpRealmPCCounts(cx)) {
  12521        result = EXITCODE_OUT_OF_MEMORY;
  12522      }
  12523    }
  12524 
  12525    // End REPRL loop
  12526  } while (reprl_mode);
  12527 
  12528  return result;
  12529 }
  12530 
  12531 // Used to allocate memory when jemalloc isn't yet initialized.
  12532 JS_DECLARE_NEW_METHODS(SystemAlloc_New, malloc, static)
  12533 
  12534 static void SetOutputFile(const char* const envVar, RCFile* defaultOut,
  12535                          RCFile** outFileP) {
  12536  RCFile* outFile;
  12537 
  12538  const char* outPath = getenv(envVar);
  12539  FILE* newfp;
  12540  if (outPath && *outPath && (newfp = fopen(outPath, "w"))) {
  12541    outFile = SystemAlloc_New<RCFile>(newfp);
  12542  } else {
  12543    outFile = defaultOut;
  12544  }
  12545 
  12546  if (!outFile) {
  12547    MOZ_CRASH("Failed to allocate output file");
  12548  }
  12549 
  12550  outFile->acquire();
  12551  *outFileP = outFile;
  12552 }
  12553 
  12554 static void PreInit() {
  12555 #ifdef XP_WIN
  12556  const char* crash_option = getenv("XRE_NO_WINDOWS_CRASH_DIALOG");
  12557  if (crash_option && crash_option[0] == '1') {
  12558    // Disable the segfault dialog. We want to fail the tests immediately
  12559    // instead of hanging automation.
  12560    UINT newMode = SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX;
  12561    UINT prevMode = SetErrorMode(newMode);
  12562    SetErrorMode(prevMode | newMode);
  12563  }
  12564 #endif
  12565 }
  12566 
  12567 #ifndef JS_WITHOUT_NSPR
  12568 class AutoLibraryLoader {
  12569  Vector<PRLibrary*, 4, SystemAllocPolicy> libraries;
  12570 
  12571 public:
  12572  ~AutoLibraryLoader() {
  12573    for (auto dll : libraries) {
  12574      PR_UnloadLibrary(dll);
  12575    }
  12576  }
  12577 
  12578  PRLibrary* load(const char* path) {
  12579    PRLibSpec libSpec;
  12580    libSpec.type = PR_LibSpec_Pathname;
  12581    libSpec.value.pathname = path;
  12582    PRLibrary* dll = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_GLOBAL);
  12583    if (!dll) {
  12584      fprintf(stderr, "LoadLibrary '%s' failed with code %d\n", path,
  12585              PR_GetError());
  12586      MOZ_CRASH("Failed to load library");
  12587    }
  12588 
  12589    MOZ_ALWAYS_TRUE(libraries.append(dll));
  12590    return dll;
  12591  }
  12592 };
  12593 #endif
  12594 
  12595 static bool ReadSelfHostedXDRFile(JSContext* cx, FileContents& buf) {
  12596  FILE* file = fopen(selfHostedXDRPath, "rb");
  12597  if (!file) {
  12598    fprintf(stderr, "Can't open self-hosted stencil XDR file.\n");
  12599    return false;
  12600  }
  12601  AutoCloseFile autoClose(file);
  12602 
  12603  struct stat st;
  12604  if (fstat(fileno(file), &st) < 0) {
  12605    fprintf(stderr, "Unable to stat self-hosted stencil XDR file.\n");
  12606    return false;
  12607  }
  12608 
  12609  if (st.st_size >= INT32_MAX) {
  12610    fprintf(stderr, "self-hosted stencil XDR file too large.\n");
  12611    return false;
  12612  }
  12613  uint32_t filesize = uint32_t(st.st_size);
  12614 
  12615  if (!buf.growBy(filesize)) {
  12616    return false;
  12617  }
  12618  size_t cc = fread(buf.begin(), 1, filesize, file);
  12619  if (cc != filesize) {
  12620    fprintf(stderr, "Short read on self-hosted stencil XDR file.\n");
  12621    return false;
  12622  }
  12623 
  12624  return true;
  12625 }
  12626 
  12627 static bool WriteSelfHostedXDRFile(JSContext* cx, JS::SelfHostedCache buffer) {
  12628  FILE* file = fopen(selfHostedXDRPath, "wb");
  12629  if (!file) {
  12630    JS_ReportErrorUTF8(cx, "Can't open self-hosted stencil XDR file.");
  12631    return false;
  12632  }
  12633  AutoCloseFile autoClose(file);
  12634 
  12635  size_t cc = fwrite(buffer.Elements(), 1, buffer.LengthBytes(), file);
  12636  if (cc != buffer.LengthBytes()) {
  12637    JS_ReportErrorUTF8(cx, "Short write on self-hosted stencil XDR file.");
  12638    return false;
  12639  }
  12640 
  12641  return true;
  12642 }
  12643 
  12644 static bool SetGCParameterFromArg(JSContext* cx, char* arg) {
  12645  char* c = strchr(arg, '=');
  12646  if (!c) {
  12647    fprintf(stderr,
  12648            "Error: --gc-param argument '%s' must be of the form "
  12649            "name=decimalValue\n",
  12650            arg);
  12651    return false;
  12652  }
  12653 
  12654  *c = '\0';
  12655  const char* name = arg;
  12656  const char* valueStr = c + 1;
  12657 
  12658  JSGCParamKey key;
  12659  bool writable;
  12660  if (!GetGCParameterInfo(name, &key, &writable)) {
  12661    fprintf(stderr, "Error: Unknown GC parameter name '%s'\n", name);
  12662    fprintf(stderr, "Writable GC parameter names are:\n");
  12663 #define PRINT_WRITABLE_PARAM_NAME(name, _, writable) \
  12664  if (writable) {                                    \
  12665    fprintf(stderr, "  %s\n", name);                 \
  12666  }
  12667    FOR_EACH_GC_PARAM(PRINT_WRITABLE_PARAM_NAME)
  12668 #undef PRINT_WRITABLE_PARAM_NAME
  12669    return false;
  12670  }
  12671 
  12672  if (!writable) {
  12673    fprintf(stderr, "Error: GC parameter '%s' is not writable\n", name);
  12674    return false;
  12675  }
  12676 
  12677  char* end = nullptr;
  12678  unsigned long int value = strtoul(valueStr, &end, 10);
  12679  if (end == valueStr || *end) {
  12680    fprintf(stderr,
  12681            "Error: Could not parse '%s' as decimal for GC parameter '%s'\n",
  12682            valueStr, name);
  12683    return false;
  12684  }
  12685 
  12686  uint32_t paramValue = uint32_t(value);
  12687  if (value == ULONG_MAX || value != paramValue ||
  12688      !cx->runtime()->gc.setParameter(cx, key, paramValue)) {
  12689    fprintf(stderr, "Error: Value %s is out of range for GC parameter '%s'\n",
  12690            valueStr, name);
  12691    return false;
  12692  }
  12693 
  12694  return true;
  12695 }
  12696 
  12697 int main(int argc, char** argv) {
  12698  PreInit();
  12699 
  12700  sArgc = argc;
  12701  sArgv = argv;
  12702 
  12703  int result;
  12704 
  12705  setlocale(LC_ALL, "");
  12706 
  12707  // Special-case stdout and stderr. We bump their refcounts to prevent them
  12708  // from getting closed and then having some printf fail somewhere.
  12709  RCFile rcStdout(stdout);
  12710  rcStdout.acquire();
  12711  RCFile rcStderr(stderr);
  12712  rcStderr.acquire();
  12713 
  12714  SetOutputFile("JS_STDOUT", &rcStdout, &gOutFile);
  12715  SetOutputFile("JS_STDERR", &rcStderr, &gErrFile);
  12716 
  12717 #ifdef MOZ_MEMORY
  12718  // Use a larger jemalloc page cache. This should match the value for browser
  12719  // foreground processes in ContentChild::RecvNotifyProcessPriorityChanged.
  12720  moz_set_max_dirty_page_modifier(4);
  12721 #endif
  12722 
  12723  OptionParser op("Usage: {progname} [options] [[script] scriptArgs*]");
  12724  if (!InitOptionParser(op)) {
  12725    return EXIT_FAILURE;
  12726  }
  12727 
  12728  switch (op.parseArgs(argc, argv)) {
  12729    case OptionParser::EarlyExit:
  12730      return EXIT_SUCCESS;
  12731    case OptionParser::ParseError:
  12732      op.printHelp(argv[0]);
  12733      return EXIT_FAILURE;
  12734    case OptionParser::Fail:
  12735      return EXIT_FAILURE;
  12736    case OptionParser::Okay:
  12737      break;
  12738  }
  12739 
  12740  if (op.getHelpOption()) {
  12741    return EXIT_SUCCESS;
  12742  }
  12743 
  12744  if (!SetGlobalOptionsPreJSInit(op)) {
  12745    return EXIT_FAILURE;
  12746  }
  12747 
  12748  if (!JS::SetLoggingInterface(shellLoggingInterface)) {
  12749    return 1;
  12750  }
  12751  char* logopts = getenv("MOZ_LOG");
  12752  if (logopts) {
  12753    ParseLoggerOptions(mozilla::Range(logopts, strlen(logopts)));
  12754  }
  12755 
  12756  // Start the engine.
  12757  if (const char* message = JS_InitWithFailureDiagnostic()) {
  12758    fprintf(gErrFile->fp, "JS_Init failed: %s\n", message);
  12759    return 1;
  12760  }
  12761 
  12762  // `selfHostedXDRBuffer` contains XDR buffer of the self-hosted JS.
  12763  // A part of it is borrowed by ImmutableScriptData of the self-hosted scripts.
  12764  //
  12765  // This buffer should outlive JS_Shutdown.
  12766  Maybe<FileContents> selfHostedXDRBuffer;
  12767 
  12768  auto shutdownEngine = MakeScopeExit([] { JS_ShutDown(); });
  12769 
  12770  if (!SetGlobalOptionsPostJSInit(op)) {
  12771    return EXIT_FAILURE;
  12772  }
  12773 
  12774  if (!InitSharedObjectMailbox()) {
  12775    return EXIT_FAILURE;
  12776  }
  12777 
  12778  JS::SetProcessBuildIdOp(ShellBuildId);
  12779 
  12780  /* Use the same parameters as the browser in xpcjsruntime.cpp. */
  12781  JSContext* const cx = JS_NewContext(JS::DefaultHeapMaxBytes);
  12782  if (!cx) {
  12783    return 1;
  12784  }
  12785 
  12786  // Register telemetry callbacks.
  12787  JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryDataCallback);
  12788  JS_SetSetUseCounterCallback(cx, SetUseCounterCallback);
  12789 
  12790  auto destroyCx = MakeScopeExit([cx] { JS_DestroyContext(cx); });
  12791 
  12792  UniquePtr<ShellContext> sc =
  12793      MakeUnique<ShellContext>(cx, ShellContext::MainThread);
  12794  if (!sc || !sc->registerWithCx(cx)) {
  12795    return 1;
  12796  }
  12797 
  12798  if (!SetContextOptions(cx, op)) {
  12799    return 1;
  12800  }
  12801 
  12802  JS_SetTrustedPrincipals(cx, &ShellPrincipals::fullyTrusted);
  12803  JS_SetSecurityCallbacks(cx, &ShellPrincipals::securityCallbacks);
  12804 
  12805  JS_AddInterruptCallback(cx, ShellInterruptCallback);
  12806 
  12807  JS::SetGCSliceCallback(cx, GCSliceCallback);
  12808 
  12809  bufferStreamState = js_new<ExclusiveWaitableData<BufferStreamState>>(
  12810      mutexid::BufferStreamState);
  12811  if (!bufferStreamState) {
  12812    return 1;
  12813  }
  12814  auto shutdownBufferStreams = MakeScopeExit([] {
  12815    ShutdownBufferStreams();
  12816    js_delete(bufferStreamState);
  12817  });
  12818  JS::InitConsumeStreamCallback(cx, ConsumeBufferSource, ReportStreamError);
  12819 
  12820  JS::SetPromiseRejectionTrackerCallback(
  12821      cx, ForwardingPromiseRejectionTrackerCallback);
  12822 
  12823  JS::dbg::SetDebuggerMallocSizeOf(cx, moz_malloc_size_of);
  12824 
  12825  auto shutdownShellThreads = MakeScopeExit([cx] {
  12826    KillWatchdog(cx);
  12827    KillWorkerThreads(cx);
  12828    DestructSharedObjectMailbox();
  12829    CancelOffThreadJobsForRuntime(cx);
  12830  });
  12831 
  12832  // The file content should stay alive as long as Worker thread can be
  12833  // initialized.
  12834  JS::SelfHostedCache xdrSpan = nullptr;
  12835  JS::SelfHostedWriter xdrWriter = nullptr;
  12836  if (selfHostedXDRPath) {
  12837    if (encodeSelfHostedCode) {
  12838      xdrWriter = WriteSelfHostedXDRFile;
  12839    } else {
  12840      selfHostedXDRBuffer.emplace(cx);
  12841      if (ReadSelfHostedXDRFile(cx, *selfHostedXDRBuffer)) {
  12842        MOZ_ASSERT(selfHostedXDRBuffer->length() > 0);
  12843        JS::SelfHostedCache span(selfHostedXDRBuffer->begin(),
  12844                                 selfHostedXDRBuffer->end());
  12845        xdrSpan = span;
  12846      } else {
  12847        fprintf(stderr, "Falling back on parsing source.\n");
  12848        selfHostedXDRPath = nullptr;
  12849      }
  12850    }
  12851  }
  12852 
  12853  if (!JS::InitSelfHostedCode(cx, xdrSpan, xdrWriter)) {
  12854    return 1;
  12855  }
  12856 
  12857  EnvironmentPreparer environmentPreparer(cx);
  12858 
  12859  JS::SetProcessLargeAllocationFailureCallback(my_LargeAllocFailCallback);
  12860 
  12861  if (op.getBoolOption("wasm-compile-and-serialize")) {
  12862 #ifdef __wasi__
  12863    MOZ_CRASH("WASI doesn't support wasm");
  12864 #else
  12865    if (!WasmCompileAndSerialize(cx)) {
  12866      // Errors have been printed directly to stderr.
  12867      MOZ_ASSERT(!cx->isExceptionPending());
  12868      return EXIT_FAILURE;
  12869    }
  12870 #endif
  12871    return EXIT_SUCCESS;
  12872  }
  12873 
  12874  result = Shell(cx, &op);
  12875 
  12876 #ifdef DEBUG
  12877  if (OOM_printAllocationCount) {
  12878    printf("OOM max count: %" PRIu64 "\n", js::oom::simulator.counter());
  12879  }
  12880 #endif
  12881 
  12882  return result;
  12883 }
  12884 
  12885 bool InitOptionParser(OptionParser& op) {
  12886  op.setDescription(
  12887      "The SpiderMonkey shell provides a command line interface to the "
  12888      "JavaScript engine. Code and file options provided via the command line "
  12889      "are "
  12890      "run left to right. If provided, the optional script argument is run "
  12891      "after "
  12892      "all options have been processed. Just-In-Time compilation modes may be "
  12893      "enabled via "
  12894      "command line options.");
  12895  op.setDescriptionWidth(72);
  12896  op.setHelpWidth(80);
  12897  op.setVersion(JS_GetImplementationVersion());
  12898 
  12899  if (!op.addMultiStringOption(
  12900          'f', "file", "PATH",
  12901          "File path to run, parsing file contents as UTF-8") ||
  12902      !op.addMultiStringOption(
  12903          'u', "utf16-file", "PATH",
  12904          "File path to run, inflating the file's UTF-8 contents to UTF-16 and "
  12905          "then parsing that") ||
  12906      !op.addMultiStringOption('m', "module", "PATH", "Module path to run") ||
  12907      !op.addMultiStringOption('p', "prelude", "PATH", "Prelude path to run") ||
  12908      !op.addMultiStringOption('e', "execute", "CODE", "Inline code to run") ||
  12909      !op.addStringOption('\0', "selfhosted-xdr-path", "[filename]",
  12910                          "Read/Write selfhosted script data from/to the given "
  12911                          "XDR file") ||
  12912      !op.addStringOption('\0', "selfhosted-xdr-mode", "(encode,decode,off)",
  12913                          "Whether to encode/decode data of the file provided"
  12914                          "with --selfhosted-xdr-path.") ||
  12915      !op.addBoolOption('i', "shell", "Enter prompt after running code") ||
  12916      !op.addBoolOption('c', "compileonly",
  12917                        "Only compile, don't run (syntax checking mode)") ||
  12918      !op.addBoolOption('w', "warnings", "Emit warnings") ||
  12919      !op.addBoolOption('W', "nowarnings", "Don't emit warnings") ||
  12920      !op.addBoolOption('D', "dump-bytecode",
  12921                        "Dump bytecode with exec count for all scripts") ||
  12922      !op.addBoolOption('b', "print-timing",
  12923                        "Print sub-ms runtime for each file that's run") ||
  12924      !op.addBoolOption('\0', "code-coverage",
  12925                        "Enable code coverage instrumentation.") ||
  12926      !op.addBoolOption(
  12927          '\0', "disable-parser-deferred-alloc",
  12928          "Disable deferred allocation of GC objects until after parser") ||
  12929 #ifdef DEBUG
  12930      !op.addBoolOption('O', "print-alloc",
  12931                        "Print the number of allocations at exit") ||
  12932 #endif
  12933      !op.addOptionalStringArg("script",
  12934                               "A script to execute (after all options)") ||
  12935      !op.addOptionalMultiStringArg(
  12936          "scriptArgs",
  12937          "String arguments to bind as |scriptArgs| in the "
  12938          "shell's global") ||
  12939      !op.addIntOption(
  12940          '\0', "cpu-count", "COUNT",
  12941          "Set the number of CPUs (hardware threads) to COUNT, the "
  12942          "default is the actual number of CPUs. The total number of "
  12943          "background helper threads is the CPU count plus some constant.",
  12944          -1) ||
  12945      !op.addIntOption('\0', "thread-count", "COUNT", "Alias for --cpu-count.",
  12946                       -1) ||
  12947      !op.addBoolOption('\0', "ion", "Enable IonMonkey (default)") ||
  12948      !op.addBoolOption('\0', "no-ion", "Disable IonMonkey") ||
  12949      !op.addBoolOption('\0', "no-ion-for-main-context",
  12950                        "Disable IonMonkey for the main context only") ||
  12951      !op.addIntOption('\0', "inlining-entry-threshold", "COUNT",
  12952                       "The minimum stub entry count before trial-inlining a"
  12953                       " call",
  12954                       -1) ||
  12955      !op.addIntOption('\0', "small-function-length", "COUNT",
  12956                       "The maximum bytecode length of a 'small function' for "
  12957                       "the purpose of inlining.",
  12958                       -1) ||
  12959      !op.addBoolOption('\0', "only-inline-selfhosted",
  12960                        "Only inline selfhosted functions") ||
  12961      !op.addBoolOption('\0', "asmjs", "Enable asm.js compilation") ||
  12962      !op.addStringOption(
  12963          '\0', "wasm-compiler", "[option]",
  12964          "Choose to enable a subset of the wasm compilers, valid options are "
  12965          "'none', 'baseline', 'ion', 'optimizing', "
  12966          "'baseline+ion', 'baseline+optimizing'.") ||
  12967      !op.addBoolOption('\0', "wasm-verbose",
  12968                        "Enable WebAssembly verbose logging") ||
  12969      !op.addBoolOption('\0', "disable-wasm-huge-memory",
  12970                        "Disable WebAssembly huge memory") ||
  12971      !op.addBoolOption('\0', "test-wasm-await-tier2",
  12972                        "Forcibly activate tiering and block "
  12973                        "instantiation on completion of tier2") ||
  12974      !op.addBoolOption('\0', "no-native-regexp",
  12975                        "Disable native regexp compilation") ||
  12976      !op.addIntOption(
  12977          '\0', "regexp-warmup-threshold", "COUNT",
  12978          "Wait for COUNT invocations before compiling regexps to native code "
  12979          "(default 10)",
  12980          -1) ||
  12981      !op.addBoolOption('\0', "trace-regexp-parser", "Trace regexp parsing") ||
  12982      !op.addBoolOption('\0', "trace-regexp-assembler",
  12983                        "Trace regexp assembler") ||
  12984      !op.addBoolOption('\0', "trace-regexp-interpreter",
  12985                        "Trace regexp interpreter") ||
  12986      !op.addBoolOption('\0', "trace-regexp-peephole",
  12987                        "Trace regexp peephole optimization") ||
  12988      !op.addBoolOption('\0', "less-debug-code",
  12989                        "Emit less machine code for "
  12990                        "checking assertions under DEBUG.") ||
  12991      !op.addBoolOption('\0', "disable-tosource", "Disable toSource/uneval") ||
  12992      !op.addBoolOption('\0', "disable-property-error-message-fix",
  12993                        "Disable fix for the error message when accessing "
  12994                        "property of null or undefined") ||
  12995      !op.addBoolOption('\0', "enable-iterator-helpers",
  12996                        "Enable iterator helpers") ||
  12997      !op.addBoolOption('\0', "enable-async-iterator-helpers",
  12998                        "Enable async iterator helpers") ||
  12999      !op.addBoolOption('\0', "enable-shadow-realms", "Enable ShadowRealms") ||
  13000      !op.addBoolOption('\0', "disable-array-grouping",
  13001                        "Disable Object.groupBy and Map.groupBy") ||
  13002      !op.addBoolOption('\0', "enable-symbols-as-weakmap-keys",
  13003                        "Enable Symbols As WeakMap keys") ||
  13004      !op.addBoolOption('\0', "no-symbols-as-weakmap-keys",
  13005                        "Disable Symbols As WeakMap keys") ||
  13006      !op.addBoolOption('\0', "enable-uint8array-base64",
  13007                        "Enable Uint8Array base64/hex methods") ||
  13008      !op.addBoolOption('\0', "enable-top-level-await",
  13009                        "Enable top-level await") ||
  13010      !op.addStringOption('\0', "shared-memory", "on/off",
  13011                          "SharedArrayBuffer and Atomics "
  13012 #if SHARED_MEMORY_DEFAULT
  13013                          "(default: on, off to disable)"
  13014 #else
  13015                          "(default: off, on to enable)"
  13016 #endif
  13017                          ) ||
  13018      !op.addStringOption('\0', "spectre-mitigations", "on/off",
  13019                          "Whether Spectre mitigations are enabled (default: "
  13020                          "off, on to enable)") ||
  13021      !op.addStringOption('\0', "write-protect-code", "on/off",
  13022                          "Whether the W^X policy is enforced to mark JIT code "
  13023                          "pages as either writable or executable but never "
  13024                          "both at the same time (default: on, off to "
  13025                          "disable)") ||
  13026      !op.addStringOption('\0', "cache-ir-stubs", "on/off/call",
  13027                          "Use CacheIR stubs (default: on, off to disable, "
  13028                          "call to enable work-in-progress call ICs)") ||
  13029      !op.addStringOption('\0', "ion-shared-stubs", "on/off",
  13030                          "Use shared stubs (default: on, off to disable)") ||
  13031      !op.addStringOption(
  13032          '\0', "stub-folding", "on/off",
  13033          "Enable stub folding (default: on, off to disable)") ||
  13034      !op.addStringOption('\0', "stub-folding-loads-and-stores", "on/off",
  13035                          "Enable stub folding for load and stores (default: "
  13036                          "on, off to disable)") ||
  13037      !op.addStringOption('\0', "ion-scalar-replacement", "on/off",
  13038                          "Scalar Replacement (default: on, off to disable)") ||
  13039      !op.addStringOption('\0', "ion-gvn", "[mode]",
  13040                          "Specify Ion global value numbering:\n"
  13041                          "  off: disable GVN\n"
  13042                          "  on:  enable GVN (default)\n") ||
  13043      !op.addStringOption(
  13044          '\0', "ion-licm", "on/off",
  13045          "Loop invariant code motion (default: on, off to disable)") ||
  13046      !op.addStringOption('\0', "ion-edgecase-analysis", "on/off",
  13047                          "Find edge cases where Ion can avoid bailouts "
  13048                          "(default: on, off to disable)") ||
  13049      !op.addStringOption('\0', "ion-pruning", "on/off",
  13050                          "Branch pruning (default: on, off to disable)") ||
  13051      !op.addStringOption('\0', "ion-range-analysis", "on/off",
  13052                          "Range analysis (default: on, off to disable)") ||
  13053      !op.addStringOption('\0', "ion-sink", "on/off",
  13054                          "Sink code motion (default: off, on to enable)") ||
  13055      !op.addStringOption(
  13056          '\0', "ion-instruction-reordering", "on/off",
  13057          "Instruction reordering (default: off, on to enable)") ||
  13058      !op.addStringOption(
  13059          '\0', "ion-optimize-shapeguards", "on/off",
  13060          "Eliminate redundant shape guards (default: on, off to disable)") ||
  13061      !op.addStringOption(
  13062          '\0', "ion-optimize-gcbarriers", "on/off",
  13063          "Eliminate redundant GC barriers (default: on, off to disable)") ||
  13064      !op.addStringOption('\0', "ion-iterator-indices", "on/off",
  13065                          "Optimize property access in for-in loops "
  13066                          "(default: on, off to disable)") ||
  13067      !op.addStringOption('\0', "ion-load-keys", "on/off",
  13068                          "Atomize property loads used as keys "
  13069                          "(default: on, off to disable)") ||
  13070      !op.addBoolOption('\0', "ion-check-range-analysis",
  13071                        "Range analysis checking") ||
  13072      !op.addBoolOption('\0', "ion-extra-checks",
  13073                        "Perform extra dynamic validation checks") ||
  13074      !op.addStringOption(
  13075          '\0', "ion-inlining", "on/off",
  13076          "Inline methods where possible (default: on, off to disable)") ||
  13077      !op.addStringOption(
  13078          '\0', "ion-osr", "on/off",
  13079          "On-Stack Replacement (default: on, off to disable)") ||
  13080      !op.addBoolOption('\0', "disable-bailout-loop-check",
  13081                        "Turn off bailout loop check") ||
  13082      !op.addBoolOption('\0', "enable-ic-frame-pointers",
  13083                        "Use frame pointers in all IC stubs") ||
  13084      !op.addBoolOption('\0', "scalar-replace-arguments",
  13085                        "Use scalar replacement to optimize ArgumentsObject") ||
  13086      !op.addStringOption(
  13087          '\0', "ion-limit-script-size", "on/off",
  13088          "Don't compile very large scripts (default: on, off to disable)") ||
  13089      !op.addIntOption('\0', "ion-warmup-threshold", "COUNT",
  13090                       "Wait for COUNT calls or iterations before compiling "
  13091                       "at the normal optimization level (default: 1500)",
  13092                       -1) ||
  13093      !op.addStringOption(
  13094          '\0', "ion-regalloc", "[mode]",
  13095          "Specify Ion register allocation:\n"
  13096          "  backtracking: Priority based backtracking register allocation "
  13097          "(default)\n"
  13098          "  simple: Simple register allocator optimized for compile time") ||
  13099      !op.addBoolOption(
  13100          '\0', "ion-eager",
  13101          "Always ion-compile methods (implies --baseline-eager)") ||
  13102      !op.addBoolOption('\0', "fast-warmup",
  13103                        "Reduce warmup thresholds for each tier.") ||
  13104      !op.addStringOption(
  13105          '\0', "baseline-offthread-compile", "on/off",
  13106          "Compile baseline scripts offthread (default: off)") ||
  13107      !op.addStringOption(
  13108          '\0', "baseline-batching", "on/off",
  13109          "Batch baseline scripts before dispatching (default: off)") ||
  13110      !op.addStringOption('\0', "ion-offthread-compile", "on/off",
  13111                          "Compile Ion scripts offthread (default: on)") ||
  13112      !op.addStringOption('\0', "ion-parallel-compile", "on/off",
  13113                          "--ion-parallel compile is deprecated. Use "
  13114                          "--ion-offthread-compile.") ||
  13115      !op.addBoolOption('\0', "disable-main-thread-denormals",
  13116                        "Disable Denormals on the main thread only, to "
  13117                        "emulate WebAudio worklets.") ||
  13118      !op.addStringOption('\0', "object-keys-scalar-replacement", "on/off",
  13119                          "Replace Object.keys with a NativeIterators "
  13120                          "(default: on)") ||
  13121      !op.addBoolOption('\0', "baseline",
  13122                        "Enable baseline compiler (default)") ||
  13123      !op.addBoolOption('\0', "no-baseline", "Disable baseline compiler") ||
  13124      !op.addBoolOption('\0', "baseline-eager",
  13125                        "Always baseline-compile methods") ||
  13126 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
  13127      !op.addBoolOption('\0', "portable-baseline-eager",
  13128                        "Always use the portable baseline interpreter") ||
  13129      !op.addBoolOption('\0', "portable-baseline",
  13130                        "Enable Portable Baseline Interpreter (default)") ||
  13131      !op.addBoolOption('\0', "no-portable-baseline",
  13132                        "Disable Portable Baseline Interpreter") ||
  13133 #endif
  13134 #ifdef ENABLE_JS_AOT_ICS
  13135      !op.addBoolOption('\0', "aot-ics", "Enable ahead-of-time-known ICs") ||
  13136      !op.addBoolOption(
  13137          '\0', "enforce-aot-ics",
  13138          "Enable enforcing only use of ahead-of-time-known ICs") ||
  13139 #endif
  13140      !op.addIntOption(
  13141          '\0', "baseline-warmup-threshold", "COUNT",
  13142          "Wait for COUNT calls or iterations before baseline-compiling "
  13143          "(default: 10)",
  13144          -1) ||
  13145      !op.addIntOption(
  13146          '\0', "baseline-queue-capacity", "CAPACITY",
  13147          "Wait for at most CAPACITY scripts to be added to baseline compile"
  13148          "queue before dispatching (default: 8)",
  13149          -1) ||
  13150      !op.addBoolOption('\0', "blinterp",
  13151                        "Enable Baseline Interpreter (default)") ||
  13152      !op.addBoolOption('\0', "no-blinterp", "Disable Baseline Interpreter") ||
  13153      !op.addBoolOption('\0', "disable-jithints",
  13154                        "Disable caching eager baseline compilation hints.") ||
  13155      !op.addBoolOption(
  13156          '\0', "emit-interpreter-entry",
  13157          "Emit Interpreter entry trampolines (default under --enable-perf)") ||
  13158      !op.addBoolOption(
  13159          '\0', "no-emit-interpreter-entry",
  13160          "Do not emit Interpreter entry trampolines (default).") ||
  13161      !op.addBoolOption('\0', "blinterp-eager",
  13162                        "Always Baseline-interpret scripts") ||
  13163      !op.addIntOption(
  13164          '\0', "blinterp-warmup-threshold", "COUNT",
  13165          "Wait for COUNT calls or iterations before Baseline-interpreting "
  13166          "(default: 10)",
  13167          -1) ||
  13168      !op.addIntOption(
  13169          '\0', "trial-inlining-warmup-threshold", "COUNT",
  13170          "Wait for COUNT calls or iterations before trial-inlining "
  13171          "(default: 500)",
  13172          -1) ||
  13173      !op.addStringOption(
  13174          '\0', "monomorphic-inlining", "default/always/never",
  13175          "Whether monomorphic inlining is used instead of trial inlining "
  13176          "always, never, or based on heuristics (default)") ||
  13177      !op.addBoolOption(
  13178          '\0', "no-sse3",
  13179          "Pretend CPU does not support SSE3 instructions and above "
  13180          "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
  13181      !op.addBoolOption(
  13182          '\0', "no-ssse3",
  13183          "Pretend CPU does not support SSSE3 [sic] instructions and above "
  13184          "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
  13185      !op.addBoolOption(
  13186          '\0', "no-sse41",
  13187          "Pretend CPU does not support SSE4.1 instructions "
  13188          "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
  13189      !op.addBoolOption('\0', "no-sse4", "Alias for --no-sse41") ||
  13190      !op.addBoolOption(
  13191          '\0', "no-sse42",
  13192          "Pretend CPU does not support SSE4.2 instructions "
  13193          "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
  13194 #ifdef ENABLE_WASM_AVX
  13195      !op.addBoolOption('\0', "enable-avx",
  13196                        "No-op. AVX is enabled by default, if available.") ||
  13197      !op.addBoolOption(
  13198          '\0', "no-avx",
  13199          "Pretend CPU does not support AVX or AVX2 instructions "
  13200          "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
  13201 #else
  13202      !op.addBoolOption('\0', "enable-avx",
  13203                        "AVX is disabled by default. Enable AVX. "
  13204                        "(no-op on platforms other than x86 and x64).") ||
  13205      !op.addBoolOption('\0', "no-avx",
  13206                        "No-op. AVX is currently disabled by default.") ||
  13207 #endif
  13208      !op.addBoolOption('\0', "no-fjcvtzs",
  13209                        "Pretend CPU does not support FJCVTZS instruction.") ||
  13210      !op.addBoolOption('\0', "no-cssc",
  13211                        "Pretend CPU does not support CSSC extension.") ||
  13212      !op.addBoolOption('\0', "more-compartments",
  13213                        "Make newGlobal default to creating a new "
  13214                        "compartment.") ||
  13215      !op.addBoolOption('\0', "fuzzing-safe",
  13216                        "Don't expose functions that aren't safe for "
  13217                        "fuzzers to call") ||
  13218 #ifdef DEBUG
  13219      !op.addBoolOption('\0', "differential-testing",
  13220                        "Avoid random/undefined behavior that disturbs "
  13221                        "differential testing (correctness fuzzing)") ||
  13222 #endif
  13223      !op.addBoolOption('\0', "disable-oom-functions",
  13224                        "Disable functions that cause "
  13225                        "artificial OOMs") ||
  13226      !op.addBoolOption('\0', "no-threads", "Disable helper threads") ||
  13227      !op.addBoolOption(
  13228          '\0', "no-jit-backend",
  13229          "Disable the JIT backend completely for this process") ||
  13230 #ifdef DEBUG
  13231      !op.addBoolOption('\0', "dump-entrained-variables",
  13232                        "Print variables which are "
  13233                        "unnecessarily entrained by inner functions") ||
  13234 #endif
  13235      !op.addBoolOption('\0', "no-ggc", "Disable Generational GC") ||
  13236      !op.addBoolOption('\0', "no-cgc", "Disable Compacting GC") ||
  13237      !op.addBoolOption('\0', "no-incremental-gc", "Disable Incremental GC") ||
  13238      !op.addBoolOption('\0', "no-parallel-marking",
  13239                        "Disable GC parallel marking") ||
  13240      !op.addBoolOption('\0', "enable-parallel-marking",
  13241                        "Enable GC parallel marking") ||
  13242      !op.addStringOption('\0', "nursery-strings", "on/off",
  13243                          "Allocate strings in the nursery") ||
  13244      !op.addStringOption('\0', "nursery-bigints", "on/off",
  13245                          "Allocate BigInts in the nursery") ||
  13246      !op.addIntOption('\0', "available-memory", "SIZE",
  13247                       "Select GC settings based on available memory (MB)",
  13248                       0) ||
  13249      !op.addBoolOption('\0', "disable-decommit",
  13250                        "Disable decommitting unsued GC memory") ||
  13251      !op.addStringOption('\0', "arm-hwcap", "[features]",
  13252                          "Specify ARM code generation features, or 'help' to "
  13253                          "list all features.") ||
  13254      !op.addIntOption('\0', "arm-asm-nop-fill", "SIZE",
  13255                       "Insert the given number of NOP instructions at all "
  13256                       "possible pool locations.",
  13257                       0) ||
  13258      !op.addIntOption('\0', "asm-pool-max-offset", "OFFSET",
  13259                       "The maximum pc relative OFFSET permitted in pool "
  13260                       "reference instructions.",
  13261                       1024) ||
  13262      !op.addBoolOption('\0', "arm-sim-icache-checks",
  13263                        "Enable icache flush checks in the ARM "
  13264                        "simulator.") ||
  13265      !op.addIntOption('\0', "arm-sim-stop-at", "NUMBER",
  13266                       "Stop the ARM simulator after the given "
  13267                       "NUMBER of instructions.",
  13268                       -1) ||
  13269      !op.addBoolOption('\0', "mips-sim-icache-checks",
  13270                        "Enable icache flush checks in the MIPS "
  13271                        "simulator.") ||
  13272      !op.addIntOption('\0', "mips-sim-stop-at", "NUMBER",
  13273                       "Stop the MIPS simulator after the given "
  13274                       "NUMBER of instructions.",
  13275                       -1) ||
  13276      !op.addBoolOption('\0', "loong64-sim-icache-checks",
  13277                        "Enable icache flush checks in the LoongArch64 "
  13278                        "simulator.") ||
  13279      !op.addIntOption('\0', "loong64-sim-stop-at", "NUMBER",
  13280                       "Stop the LoongArch64 simulator after the given "
  13281                       "NUMBER of instructions.",
  13282                       -1) ||
  13283 #ifdef JS_CODEGEN_RISCV64
  13284      !op.addBoolOption('\0', "riscv-debug", "debug print riscv info.") ||
  13285 #endif
  13286 #ifdef JS_SIMULATOR_RISCV64
  13287      !op.addBoolOption('\0', "trace-sim", "print simulator info.") ||
  13288      !op.addBoolOption('\0', "debug-sim", "debug simulator.") ||
  13289      !op.addBoolOption('\0', "riscv-trap-to-simulator-debugger",
  13290                        "trap into simulator debuggger.") ||
  13291      !op.addIntOption('\0', "riscv-sim-stop-at", "NUMBER",
  13292                       "Stop the riscv simulator after the given "
  13293                       "NUMBER of instructions.",
  13294                       -1) ||
  13295 #endif
  13296      !op.addIntOption('\0', "nursery-size", "SIZE-MB",
  13297                       "Set the maximum nursery size in MB",
  13298                       JS::DefaultNurseryMaxBytes / 1024 / 1024) ||
  13299 #ifdef JS_GC_ZEAL
  13300      !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
  13301                          gc::ZealModeHelpText) ||
  13302 #else
  13303      !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
  13304                          "option ignored in non-gc-zeal builds") ||
  13305 #endif
  13306      !op.addMultiStringOption('\0', "gc-param", "NAME=VALUE",
  13307                               "Set a named GC parameter") ||
  13308      !op.addStringOption('\0', "module-load-path", "DIR",
  13309                          "Set directory to load modules from") ||
  13310      !op.addBoolOption('\0', "no-source-pragmas",
  13311                        "Disable source(Mapping)URL pragma parsing") ||
  13312      !op.addBoolOption('\0', "no-async-stacks", "Disable async stacks") ||
  13313      !op.addBoolOption('\0', "async-stacks-capture-debuggee-only",
  13314                        "Limit async stack capture to only debuggees") ||
  13315      !op.addMultiStringOption('\0', "dll", "LIBRARY",
  13316                               "Dynamically load LIBRARY") ||
  13317      !op.addBoolOption('\0', "suppress-minidump",
  13318                        "Suppress crash minidumps") ||
  13319      !op.addStringOption(
  13320          '\0', "delazification-mode", "[option]",
  13321          "Select one of the delazification mode for scripts given on the "
  13322          "command line, valid options are: "
  13323          "'on-demand', 'concurrent-df', 'eager', 'concurrent-df+on-demand'. "
  13324          "Choosing 'concurrent-df+on-demand' will run both concurrent-df and "
  13325          "on-demand delazification mode, and compare compilation outcome. ") ||
  13326      !op.addBoolOption('\0', "wasm-compile-and-serialize",
  13327                        "Compile the wasm bytecode from stdin and serialize "
  13328                        "the results to stdout") ||
  13329 #ifdef FUZZING_JS_FUZZILLI
  13330      !op.addBoolOption('\0', "reprl", "Enable REPRL mode for fuzzing") ||
  13331 #endif
  13332      !op.addMultiStringOption('P', "setpref", "name[=val]",
  13333                               "Set the value of a JS pref. The value may "
  13334                               "be omitted for boolean prefs, in which case "
  13335                               "they default to true. Use --list-prefs "
  13336                               "to print all pref names.") ||
  13337      !op.addBoolOption(
  13338          '\0', "list-prefs",
  13339          "Print list of prefs that can be set with --setpref.") ||
  13340      !op.addBoolOption('\0', "use-fdlibm-for-sin-cos-tan",
  13341                        "Use fdlibm for Math.sin, Math.cos, and Math.tan") ||
  13342      !op.addBoolOption('\0', "wasm-gc", "Enable WebAssembly gc proposal.") ||
  13343      !op.addBoolOption('\0', "wasm-relaxed-simd",
  13344                        "Enable WebAssembly relaxed-simd proposal.") ||
  13345      !op.addBoolOption('\0', "wasm-multi-memory",
  13346                        "Enable WebAssembly multi-memory proposal.") ||
  13347      !op.addBoolOption('\0', "wasm-memory-control",
  13348                        "Enable WebAssembly memory-control proposal.") ||
  13349      !op.addBoolOption('\0', "wasm-memory64",
  13350                        "Enable WebAssembly memory64 proposal.") ||
  13351      !op.addBoolOption('\0', "wasm-tail-calls",
  13352                        "Enable WebAssembly tail-calls proposal.") ||
  13353      !op.addBoolOption('\0', "wasm-js-string-builtins",
  13354                        "Enable WebAssembly js-string-builtins proposal.") ||
  13355      !op.addBoolOption('\0', "enable-iterator-sequencing",
  13356                        "Enable Iterator Sequencing") ||
  13357      !op.addBoolOption('\0', "enable-math-sumprecise",
  13358                        "Enable Math.sumPrecise") ||
  13359      !op.addBoolOption('\0', "enable-error-iserror", "Enable Error.isError") ||
  13360      !op.addBoolOption('\0', "enable-iterator-range",
  13361                        "Enable Iterator.range") ||
  13362      !op.addBoolOption('\0', "enable-joint-iteration",
  13363                        "Enable Joint Iteration") ||
  13364      !op.addBoolOption('\0', "enable-atomics-pause", "Enable Atomics pause") ||
  13365      !op.addBoolOption('\0', "enable-explicit-resource-management",
  13366                        "Enable Explicit Resource Management") ||
  13367      !op.addBoolOption('\0', "disable-explicit-resource-management",
  13368                        "Disable Explicit Resource Management") ||
  13369      !op.addBoolOption('\0', "enable-temporal", "Enable Temporal") ||
  13370      !op.addBoolOption('\0', "enable-upsert", "Enable Upsert proposal") ||
  13371      !op.addBoolOption('\0', "enable-import-bytes", "Enable import bytes") ||
  13372      !op.addBoolOption('\0', "enable-promise-allkeyed",
  13373                        "Enable Promise.allKeyed") ||
  13374      !op.addBoolOption('\0', "enable-arraybuffer-immutable",
  13375                        "Enable immutable ArrayBuffers") ||
  13376      !op.addBoolOption('\0', "enable-iterator-chunking",
  13377                        "Enable Iterator Chunking") ||
  13378      !op.addBoolOption('\0', "enable-iterator-join", "Enable Iterator.join") ||
  13379      !op.addBoolOption('\0', "enable-legacy-regexp",
  13380                        "Enable Legacy RegExp features")) {
  13381    return false;
  13382  }
  13383 
  13384  op.setArgTerminatesOptions("script", true);
  13385  op.setArgCapturesRest("scriptArgs");
  13386 
  13387  // If --fuzzing-safe is used, print a warning for unknown shell flags instead
  13388  // of aborting execution.
  13389  op.setIgnoresUnknownOptions("fuzzing-safe", true);
  13390 
  13391  return true;
  13392 }
  13393 
  13394 bool SetGlobalOptionsPreJSInit(const OptionParser& op) {
  13395  if (op.getBoolOption("fuzzing-safe")) {
  13396    fuzzingSafe = true;
  13397  } else {
  13398    fuzzingSafe =
  13399        (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0');
  13400  }
  13401 
  13402  for (MultiStringRange args = op.getMultiStringOption("setpref");
  13403       !args.empty(); args.popFront()) {
  13404    if (!SetPref(args.front())) {
  13405      return false;
  13406    }
  13407  }
  13408 
  13409  // Override pref values for prefs that have a custom shell flag.
  13410  // If you're adding a new feature, consider using --setpref instead.
  13411  if (op.getBoolOption("enable-shadow-realms")) {
  13412    JS::Prefs::set_experimental_shadow_realms(true);
  13413  }
  13414  if (op.getBoolOption("enable-uint8array-base64")) {
  13415    JS::Prefs::setAtStartup_experimental_uint8array_base64(true);
  13416  }
  13417  if (op.getBoolOption("enable-math-sumprecise")) {
  13418    JS::Prefs::setAtStartup_experimental_math_sumprecise(true);
  13419  }
  13420  if (op.getBoolOption("enable-atomics-pause")) {
  13421    JS::Prefs::setAtStartup_experimental_atomics_pause(true);
  13422  }
  13423  if (op.getBoolOption("enable-error-iserror")) {
  13424    JS::Prefs::set_experimental_error_iserror(true);
  13425  }
  13426 
  13427  bool symbolsAsWeakMapKeys = true;
  13428  if (op.getBoolOption("enable-symbols-as-weakmap-keys")) {
  13429    symbolsAsWeakMapKeys = true;
  13430  }
  13431  if (op.getBoolOption("no-symbols-as-weakmap-keys")) {
  13432    symbolsAsWeakMapKeys = false;
  13433  }
  13434  JS::Prefs::setAtStartup_experimental_symbols_as_weakmap_keys(
  13435      symbolsAsWeakMapKeys);
  13436  if (op.getBoolOption("enable-joint-iteration")) {
  13437    JS::Prefs::setAtStartup_experimental_joint_iteration(true);
  13438  }
  13439 
  13440  if (op.getBoolOption("enable-legacy-regexp")) {
  13441    JS::Prefs::set_experimental_legacy_regexp(true);
  13442  }
  13443 #ifdef NIGHTLY_BUILD
  13444  if (op.getBoolOption("enable-async-iterator-helpers")) {
  13445    JS::Prefs::setAtStartup_experimental_async_iterator_helpers(true);
  13446  }
  13447  if (op.getBoolOption("enable-iterator-sequencing")) {
  13448    JS::Prefs::setAtStartup_experimental_iterator_sequencing(true);
  13449  }
  13450  if (op.getBoolOption("enable-iterator-range")) {
  13451    JS::Prefs::setAtStartup_experimental_iterator_range(true);
  13452  }
  13453  if (op.getBoolOption("enable-upsert")) {
  13454    JS::Prefs::setAtStartup_experimental_upsert(true);
  13455  }
  13456  if (op.getBoolOption("enable-arraybuffer-immutable")) {
  13457    JS::Prefs::setAtStartup_experimental_arraybuffer_immutable(true);
  13458  }
  13459  if (op.getBoolOption("enable-import-bytes")) {
  13460    JS::Prefs::setAtStartup_experimental_import_bytes(true);
  13461  }
  13462  if (op.getBoolOption("enable-promise-allkeyed")) {
  13463    JS::Prefs::setAtStartup_experimental_promise_allkeyed(true);
  13464  }
  13465  if (op.getBoolOption("enable-iterator-chunking")) {
  13466    JS::Prefs::setAtStartup_experimental_iterator_chunking(true);
  13467  }
  13468  if (op.getBoolOption("enable-iterator-join")) {
  13469    JS::Prefs::setAtStartup_experimental_iterator_join(true);
  13470  }
  13471 #endif
  13472 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
  13473  if (op.getBoolOption("enable-explicit-resource-management")) {
  13474    JS::Prefs::set_experimental_explicit_resource_management(true);
  13475  }
  13476  if (op.getBoolOption("disable-explicit-resource-management")) {
  13477    JS::Prefs::set_experimental_explicit_resource_management(false);
  13478  }
  13479 #endif
  13480 #ifdef JS_HAS_INTL_API
  13481  if (op.getBoolOption("enable-temporal")) {
  13482    JS::Prefs::setAtStartup_experimental_temporal(true);
  13483  }
  13484 #endif
  13485  JS::Prefs::setAtStartup_experimental_weakrefs_expose_cleanupSome(true);
  13486 
  13487  if (op.getBoolOption("disable-property-error-message-fix")) {
  13488    JS::Prefs::setAtStartup_property_error_message_fix(false);
  13489  }
  13490 
  13491  JS::Prefs::set_use_fdlibm_for_sin_cos_tan(
  13492      op.getBoolOption("use-fdlibm-for-sin-cos-tan"));
  13493 
  13494  if (const char* str = op.getStringOption("ion-regalloc")) {
  13495    auto allocator = jit::LookupRegisterAllocator(str);
  13496    if (allocator.isNothing()) {
  13497      return OptionFailure("ion-regalloc", str);
  13498    }
  13499    switch (*allocator) {
  13500      case jit::RegisterAllocator_Backtracking:
  13501        JS::Prefs::setAtStartup_ion_regalloc(1);
  13502        break;
  13503      case jit::RegisterAllocator_Simple:
  13504        JS::Prefs::setAtStartup_ion_regalloc(2);
  13505        break;
  13506    }
  13507  }
  13508 
  13509  if (op.getBoolOption("wasm-gc") || op.getBoolOption("wasm-relaxed-simd") ||
  13510      op.getBoolOption("wasm-multi-memory") ||
  13511      op.getBoolOption("wasm-memory-control") ||
  13512      op.getBoolOption("wasm-memory64") ||
  13513      op.getBoolOption("wasm-tail-calls") ||
  13514      op.getBoolOption("wasm-js-string-builtins")) {
  13515    fprintf(
  13516        stderr,
  13517        "Wasm shell flags are now using prefs, use -P wasm_feature instead.\n");
  13518    return false;
  13519  }
  13520 
  13521  if (op.getBoolOption("list-prefs")) {
  13522    ListPrefs();
  13523    return false;
  13524  }
  13525 
  13526  // Note: DisableJitBackend must be called before JS_InitWithFailureDiagnostic.
  13527  if (op.getBoolOption("no-jit-backend")) {
  13528    JS::DisableJitBackend();
  13529  }
  13530 
  13531 #if defined(JS_CODEGEN_ARM)
  13532  if (const char* str = op.getStringOption("arm-hwcap")) {
  13533    jit::SetARMHwCapFlagsString(str);
  13534  }
  13535 
  13536  int32_t fill = op.getIntOption("arm-asm-nop-fill");
  13537  if (fill >= 0) {
  13538    jit::Assembler::NopFill = fill;
  13539  }
  13540 
  13541  int32_t poolMaxOffset = op.getIntOption("asm-pool-max-offset");
  13542  if (poolMaxOffset >= 5 && poolMaxOffset <= 1024) {
  13543    jit::Assembler::AsmPoolMaxOffset = poolMaxOffset;
  13544  }
  13545 #endif
  13546 
  13547  // Fish around in `op` for various important compiler-configuration flags
  13548  // and make sure they get handed on to any child processes we might create.
  13549  // See bug 1700900.  Semantically speaking, this is all rather dubious:
  13550  //
  13551  // * What set of flags need to be propagated in order to guarantee that the
  13552  //   child produces code that is "compatible" (in whatever sense) with that
  13553  //   produced by the parent? This isn't always easy to determine.
  13554  //
  13555  // * There's nothing that ensures that flags given to the child are
  13556  //   presented in the same order that they exist in the parent's `argv[]`.
  13557  //   That could be a problem in the case where two flags with contradictory
  13558  //   meanings are given, and they are presented to the child in the opposite
  13559  //   order.  For example: --wasm-compiler=optimizing --wasm-compiler=baseline.
  13560 
  13561 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
  13562  MOZ_ASSERT(!js::jit::CPUFlagsHaveBeenComputed());
  13563 
  13564  if (op.getBoolOption("no-avx")) {
  13565    js::jit::CPUInfo::SetAVXDisabled();
  13566    if (!sCompilerProcessFlags.append("--no-avx")) {
  13567      return false;
  13568    }
  13569  }
  13570  if (op.getBoolOption("enable-avx")) {
  13571    js::jit::CPUInfo::SetAVXEnabled();
  13572    if (!sCompilerProcessFlags.append("--enable-avx")) {
  13573      return false;
  13574    }
  13575  }
  13576  if (op.getBoolOption("no-sse3")) {
  13577    js::jit::CPUInfo::SetSSE3Disabled();
  13578    if (!sCompilerProcessFlags.append("--no-sse3")) {
  13579      return false;
  13580    }
  13581  }
  13582  if (op.getBoolOption("no-ssse3")) {
  13583    js::jit::CPUInfo::SetSSSE3Disabled();
  13584    if (!sCompilerProcessFlags.append("--no-ssse3")) {
  13585      return false;
  13586    }
  13587  }
  13588  if (op.getBoolOption("no-sse4") || op.getBoolOption("no-sse41")) {
  13589    js::jit::CPUInfo::SetSSE41Disabled();
  13590    if (!sCompilerProcessFlags.append("--no-sse41")) {
  13591      return false;
  13592    }
  13593  }
  13594  if (op.getBoolOption("no-sse42")) {
  13595    js::jit::CPUInfo::SetSSE42Disabled();
  13596    if (!sCompilerProcessFlags.append("--no-sse42")) {
  13597      return false;
  13598    }
  13599  }
  13600 #endif
  13601 #if defined(JS_CODEGEN_ARM64)
  13602  if (op.getBoolOption("no-fjcvtzs")) {
  13603    vixl::CPUFeatures fjcvtzs(vixl::CPUFeatures::kJSCVT);
  13604    jit::ARM64Flags::DisableCPUFeatures(fjcvtzs);
  13605  }
  13606  if (op.getBoolOption("no-cssc")) {
  13607    vixl::CPUFeatures cssc(vixl::CPUFeatures::kCSSC);
  13608    jit::ARM64Flags::DisableCPUFeatures(cssc);
  13609  }
  13610 #endif
  13611 #ifndef __wasi__
  13612  if (op.getBoolOption("disable-wasm-huge-memory")) {
  13613    JS::Prefs::setAtStartup_wasm_disable_huge_memory(true);
  13614    if (!sCompilerProcessFlags.append("--disable-wasm-huge-memory")) {
  13615      return false;
  13616    }
  13617  }
  13618 #endif
  13619 
  13620  if (op.getBoolOption("disable-decommit")) {
  13621    gc::DisableDecommit();
  13622  }
  13623 
  13624  return true;
  13625 }
  13626 
  13627 bool SetGlobalOptionsPostJSInit(const OptionParser& op) {
  13628  // Allow dumping on Linux with the fuzzing flag set, even when running with
  13629  // the suid/sgid flag set on the shell.
  13630 #ifdef XP_LINUX
  13631  if (op.getBoolOption("fuzzing-safe")) {
  13632    prctl(PR_SET_DUMPABLE, 1);
  13633  }
  13634 #endif
  13635 
  13636 #ifdef DEBUG
  13637  /*
  13638   * Process OOM options as early as possible so that we can observe as many
  13639   * allocations as possible.
  13640   */
  13641  OOM_printAllocationCount = op.getBoolOption('O');
  13642 #endif
  13643 
  13644  if (op.getBoolOption("no-threads")) {
  13645    js::DisableExtraThreads();
  13646  }
  13647 
  13648  enableCodeCoverage = op.getBoolOption("code-coverage");
  13649  if (enableCodeCoverage) {
  13650    js::EnableCodeCoverage();
  13651  }
  13652 
  13653  // If LCov is enabled, then the default delazification mode should be changed
  13654  // to parse everything eagerly, such that we know the location of every
  13655  // instruction, to report them in the LCov summary, even if there is no uses
  13656  // of these instructions.
  13657  //
  13658  // Note: code coverage can be enabled either using the --code-coverage command
  13659  // line, or the JS_CODE_COVERAGE_OUTPUT_DIR environment variable, which is
  13660  // processed by JS_InitWithFailureDiagnostic.
  13661  if (coverage::IsLCovEnabled()) {
  13662    defaultDelazificationMode =
  13663        JS::DelazificationOption::ParseEverythingEagerly;
  13664  }
  13665 
  13666  if (const char* xdr = op.getStringOption("selfhosted-xdr-path")) {
  13667    shell::selfHostedXDRPath = xdr;
  13668  }
  13669  if (const char* opt = op.getStringOption("selfhosted-xdr-mode")) {
  13670    if (strcmp(opt, "encode") == 0) {
  13671      shell::encodeSelfHostedCode = true;
  13672    } else if (strcmp(opt, "decode") == 0) {
  13673      shell::encodeSelfHostedCode = false;
  13674    } else if (strcmp(opt, "off") == 0) {
  13675      shell::selfHostedXDRPath = nullptr;
  13676    } else {
  13677      MOZ_CRASH(
  13678          "invalid option value for --selfhosted-xdr-mode, must be "
  13679          "encode/decode");
  13680    }
  13681  }
  13682 
  13683 #ifdef JS_WITHOUT_NSPR
  13684  if (!op.getMultiStringOption("dll").empty()) {
  13685    fprintf(stderr, "Error: --dll requires NSPR support!\n");
  13686    return false;
  13687  }
  13688 #else
  13689  AutoLibraryLoader loader;
  13690  MultiStringRange dllPaths = op.getMultiStringOption("dll");
  13691  while (!dllPaths.empty()) {
  13692    char* path = dllPaths.front();
  13693    loader.load(path);
  13694    dllPaths.popFront();
  13695  }
  13696 #endif
  13697 
  13698  if (op.getBoolOption("suppress-minidump")) {
  13699    js::NoteIntentionalCrash();
  13700  }
  13701 
  13702  // The fake CPU count must be set before initializing the Runtime,
  13703  // which spins up the thread pool.
  13704  int32_t cpuCount = op.getIntOption("cpu-count");  // What we're really setting
  13705  if (cpuCount < 0) {
  13706    cpuCount = op.getIntOption("thread-count");  // Legacy name
  13707  }
  13708  if (cpuCount >= 0 && !SetFakeCPUCount(cpuCount)) {
  13709    return false;
  13710  }
  13711 
  13712  return true;
  13713 }
  13714 
  13715 bool SetContextOptions(JSContext* cx, const OptionParser& op) {
  13716  if (!SetContextWasmOptions(cx, op) || !SetContextJITOptions(cx, op) ||
  13717      !SetContextGCOptions(cx, op)) {
  13718    return false;
  13719  }
  13720 
  13721  enableSourcePragmas = !op.getBoolOption("no-source-pragmas");
  13722  enableAsyncStacks = !op.getBoolOption("no-async-stacks");
  13723  enableAsyncStackCaptureDebuggeeOnly =
  13724      op.getBoolOption("async-stacks-capture-debuggee-only");
  13725  enableToSource = !op.getBoolOption("disable-tosource");
  13726  JS::ContextOptionsRef(cx)
  13727      .setSourcePragmas(enableSourcePragmas)
  13728      .setAsyncStack(enableAsyncStacks)
  13729      .setAsyncStackCaptureDebuggeeOnly(enableAsyncStackCaptureDebuggeeOnly);
  13730 
  13731  if (const char* str = op.getStringOption("shared-memory")) {
  13732    if (strcmp(str, "off") == 0) {
  13733      enableSharedMemory = false;
  13734    } else if (strcmp(str, "on") == 0) {
  13735      enableSharedMemory = true;
  13736    } else {
  13737      return OptionFailure("shared-memory", str);
  13738    }
  13739  }
  13740 
  13741  reportWarnings = op.getBoolOption('w');
  13742  compileOnly = op.getBoolOption('c');
  13743  printTiming = op.getBoolOption('b');
  13744  enableDisassemblyDumps = op.getBoolOption('D');
  13745  cx->runtime()->profilingScripts =
  13746      enableCodeCoverage || enableDisassemblyDumps;
  13747 
  13748  if (const char* mode = op.getStringOption("delazification-mode")) {
  13749    if (strcmp(mode, "on-demand") == 0) {
  13750      defaultDelazificationMode = JS::DelazificationOption::OnDemandOnly;
  13751    } else if (strcmp(mode, "concurrent-df") == 0) {
  13752      defaultDelazificationMode =
  13753          JS::DelazificationOption::ConcurrentDepthFirst;
  13754    } else if (strcmp(mode, "eager") == 0) {
  13755      defaultDelazificationMode =
  13756          JS::DelazificationOption::ParseEverythingEagerly;
  13757    } else if (strcmp(mode, "concurrent-df+on-demand") == 0 ||
  13758               strcmp(mode, "on-demand+concurrent-df") == 0) {
  13759      defaultDelazificationMode =
  13760          JS::DelazificationOption::CheckConcurrentWithOnDemand;
  13761    } else {
  13762      return OptionFailure("delazification-mode", mode);
  13763    }
  13764  }
  13765 
  13766  return true;
  13767 }
  13768 
  13769 bool SetContextWasmOptions(JSContext* cx, const OptionParser& op) {
  13770  enableAsmJS = op.getBoolOption("asmjs");
  13771 
  13772  enableWasm = true;
  13773  enableWasmBaseline = true;
  13774  enableWasmOptimizing = true;
  13775 
  13776  if (const char* str = op.getStringOption("wasm-compiler")) {
  13777    if (strcmp(str, "none") == 0) {
  13778      enableWasm = false;
  13779      // Disable asm.js -- no wasm compilers available.
  13780      enableAsmJS = false;
  13781    } else if (strcmp(str, "baseline") == 0) {
  13782      MOZ_ASSERT(enableWasmBaseline);
  13783      enableWasmOptimizing = false;
  13784    } else if (strcmp(str, "optimizing") == 0 ||
  13785               strcmp(str, "optimized") == 0) {
  13786      enableWasmBaseline = false;
  13787      MOZ_ASSERT(enableWasmOptimizing);
  13788    } else if (strcmp(str, "baseline+optimizing") == 0 ||
  13789               strcmp(str, "baseline+optimized") == 0) {
  13790      MOZ_ASSERT(enableWasmBaseline);
  13791      MOZ_ASSERT(enableWasmOptimizing);
  13792    } else if (strcmp(str, "ion") == 0) {
  13793      enableWasmBaseline = false;
  13794      enableWasmOptimizing = true;
  13795    } else if (strcmp(str, "baseline+ion") == 0) {
  13796      MOZ_ASSERT(enableWasmBaseline);
  13797      enableWasmOptimizing = true;
  13798    } else {
  13799      return OptionFailure("wasm-compiler", str);
  13800    }
  13801  }
  13802 
  13803  enableTestWasmAwaitTier2 = op.getBoolOption("test-wasm-await-tier2");
  13804 
  13805  JS::ContextOptionsRef(cx)
  13806      .setAsmJS(enableAsmJS)
  13807      .setWasm(enableWasm)
  13808      .setWasmForTrustedPrinciples(enableWasm)
  13809      .setWasmBaseline(enableWasmBaseline)
  13810      .setWasmIon(enableWasmOptimizing)
  13811      .setTestWasmAwaitTier2(enableTestWasmAwaitTier2);
  13812 
  13813 #ifndef __wasi__
  13814  // Also the following are to be propagated.
  13815  const char* to_propagate[] = {
  13816      // Compiler selection options
  13817      "--test-wasm-await-tier2",
  13818  };
  13819  for (const char* p : to_propagate) {
  13820    if (op.getBoolOption(p + 2 /* 2 => skip the leading '--' */)) {
  13821      if (!sCompilerProcessFlags.append(p)) {
  13822        return false;
  13823      }
  13824    }
  13825  }
  13826 
  13827  // Also --wasm-compiler= is to be propagated.  This is tricky because it is
  13828  // necessary to reconstitute the --wasm-compiler=<whatever> string from its
  13829  // pieces, without causing a leak.  Hence it is copied into a static buffer.
  13830  // This is thread-unsafe, but we're in `main()` and on the process' root
  13831  // thread.  Also, we do this only once -- it wouldn't work properly if we
  13832  // handled multiple --wasm-compiler= flags in a loop.
  13833  const char* wasm_compiler = op.getStringOption("wasm-compiler");
  13834  if (wasm_compiler) {
  13835    size_t n_needed =
  13836        2 + strlen("wasm-compiler") + 1 + strlen(wasm_compiler) + 1;
  13837    const size_t n_avail = 128;
  13838    static char buf[n_avail];
  13839    // `n_needed` depends on the compiler name specified.  However, it can't
  13840    // be arbitrarily long, since previous flag-checking should have limited
  13841    // it to a set of known possibilities: "baseline", "ion",
  13842    // "baseline+ion",  Still, assert this for safety.
  13843    MOZ_RELEASE_ASSERT(n_needed < n_avail);
  13844    memset(buf, 0, sizeof(buf));
  13845    SprintfBuf(buf, n_avail, "--%s=%s", "wasm-compiler", wasm_compiler);
  13846    if (!sCompilerProcessFlags.append(buf)) {
  13847      return false;
  13848    }
  13849  }
  13850 #endif  // __wasi__
  13851 
  13852  return true;
  13853 }
  13854 
  13855 bool SetContextJITOptions(JSContext* cx, const OptionParser& op) {
  13856  // Check --fast-warmup first because it sets default warm-up thresholds. These
  13857  // thresholds can then be overridden below by --ion-eager and other flags.
  13858  if (op.getBoolOption("fast-warmup")) {
  13859    jit::JitOptions.setFastWarmUp();
  13860  }
  13861 
  13862  if (op.getBoolOption("no-ion-for-main-context")) {
  13863    JS::ContextOptionsRef(cx).setDisableIon();
  13864  }
  13865 
  13866  if (const char* str = op.getStringOption("cache-ir-stubs")) {
  13867    if (strcmp(str, "on") == 0) {
  13868      jit::JitOptions.disableCacheIR = false;
  13869    } else if (strcmp(str, "off") == 0) {
  13870      jit::JitOptions.disableCacheIR = true;
  13871    } else {
  13872      return OptionFailure("cache-ir-stubs", str);
  13873    }
  13874  }
  13875 
  13876  if (const char* str = op.getStringOption("stub-folding")) {
  13877    if (strcmp(str, "on") == 0) {
  13878      jit::JitOptions.disableStubFolding = false;
  13879    } else if (strcmp(str, "off") == 0) {
  13880      jit::JitOptions.disableStubFolding = true;
  13881    } else {
  13882      return OptionFailure("stub-folding", str);
  13883    }
  13884  }
  13885 
  13886  if (const char* str = op.getStringOption("stub-folding-loads-and-stores")) {
  13887    if (strcmp(str, "on") == 0) {
  13888      jit::JitOptions.disableStubFoldingLoadsAndStores = false;
  13889    } else if (strcmp(str, "off") == 0) {
  13890      jit::JitOptions.disableStubFoldingLoadsAndStores = true;
  13891    } else {
  13892      return OptionFailure("stub-folding-loads-and-stores", str);
  13893    }
  13894  }
  13895 
  13896  if (const char* str = op.getStringOption("spectre-mitigations")) {
  13897    if (strcmp(str, "on") == 0) {
  13898 #if defined(JS_CODEGEN_RISCV64) || defined(JS_CODEGEN_LOONG64) || \
  13899    defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_WASM32)
  13900      // MacroAssembler::spectreZeroRegister and MacroAssembler::spectreMovePtr
  13901      // are not implemented for these targets.
  13902      fprintf(
  13903          stderr,
  13904          "Warning: Spectre mitigations are not implemented for this target."
  13905          " --spectre-mitigations=on is ignored.\n");
  13906 #else
  13907      jit::JitOptions.spectreIndexMasking = true;
  13908      jit::JitOptions.spectreObjectMitigations = true;
  13909      jit::JitOptions.spectreStringMitigations = true;
  13910      jit::JitOptions.spectreValueMasking = true;
  13911      jit::JitOptions.spectreJitToCxxCalls = true;
  13912 #endif
  13913    } else if (strcmp(str, "off") == 0) {
  13914      jit::JitOptions.spectreIndexMasking = false;
  13915      jit::JitOptions.spectreObjectMitigations = false;
  13916      jit::JitOptions.spectreStringMitigations = false;
  13917      jit::JitOptions.spectreValueMasking = false;
  13918      jit::JitOptions.spectreJitToCxxCalls = false;
  13919    } else {
  13920      return OptionFailure("spectre-mitigations", str);
  13921    }
  13922  }
  13923 
  13924  if (const char* str = op.getStringOption("write-protect-code")) {
  13925    if (strcmp(str, "on") == 0) {
  13926      jit::JitOptions.maybeSetWriteProtectCode(true);
  13927    } else if (strcmp(str, "off") == 0) {
  13928      jit::JitOptions.maybeSetWriteProtectCode(false);
  13929    } else {
  13930      return OptionFailure("write-protect-code", str);
  13931    }
  13932  }
  13933 
  13934  if (const char* str = op.getStringOption("monomorphic-inlining")) {
  13935    if (strcmp(str, "default") == 0) {
  13936      jit::JitOptions.monomorphicInlining =
  13937          jit::UseMonomorphicInlining::Default;
  13938    } else if (strcmp(str, "always") == 0) {
  13939      jit::JitOptions.monomorphicInlining = jit::UseMonomorphicInlining::Always;
  13940    } else if (strcmp(str, "never") == 0) {
  13941      jit::JitOptions.monomorphicInlining = jit::UseMonomorphicInlining::Never;
  13942    } else {
  13943      return OptionFailure("monomorphic-inlining", str);
  13944    }
  13945  }
  13946 
  13947  if (const char* str = op.getStringOption("ion-scalar-replacement")) {
  13948    if (strcmp(str, "on") == 0) {
  13949      jit::JitOptions.disableScalarReplacement = false;
  13950    } else if (strcmp(str, "off") == 0) {
  13951      jit::JitOptions.disableScalarReplacement = true;
  13952    } else {
  13953      return OptionFailure("ion-scalar-replacement", str);
  13954    }
  13955  }
  13956 
  13957  if (op.getStringOption("ion-shared-stubs")) {
  13958    // Dead option, preserved for now for potential fuzzer interaction.
  13959  }
  13960 
  13961  if (const char* str = op.getStringOption("ion-gvn")) {
  13962    if (strcmp(str, "off") == 0) {
  13963      jit::JitOptions.disableGvn = true;
  13964    } else if (strcmp(str, "on") != 0 && strcmp(str, "optimistic") != 0 &&
  13965               strcmp(str, "pessimistic") != 0) {
  13966      // We accept "pessimistic" and "optimistic" as synonyms for "on"
  13967      // for backwards compatibility.
  13968      return OptionFailure("ion-gvn", str);
  13969    }
  13970  }
  13971 
  13972  if (const char* str = op.getStringOption("ion-licm")) {
  13973    if (strcmp(str, "on") == 0) {
  13974      jit::JitOptions.disableLicm = false;
  13975    } else if (strcmp(str, "off") == 0) {
  13976      jit::JitOptions.disableLicm = true;
  13977    } else {
  13978      return OptionFailure("ion-licm", str);
  13979    }
  13980  }
  13981 
  13982  if (const char* str = op.getStringOption("ion-edgecase-analysis")) {
  13983    if (strcmp(str, "on") == 0) {
  13984      jit::JitOptions.disableEdgeCaseAnalysis = false;
  13985    } else if (strcmp(str, "off") == 0) {
  13986      jit::JitOptions.disableEdgeCaseAnalysis = true;
  13987    } else {
  13988      return OptionFailure("ion-edgecase-analysis", str);
  13989    }
  13990  }
  13991 
  13992  if (const char* str = op.getStringOption("ion-pruning")) {
  13993    if (strcmp(str, "on") == 0) {
  13994      jit::JitOptions.disablePruning = false;
  13995    } else if (strcmp(str, "off") == 0) {
  13996      jit::JitOptions.disablePruning = true;
  13997    } else {
  13998      return OptionFailure("ion-pruning", str);
  13999    }
  14000  }
  14001 
  14002  if (const char* str = op.getStringOption("ion-range-analysis")) {
  14003    if (strcmp(str, "on") == 0) {
  14004      jit::JitOptions.disableRangeAnalysis = false;
  14005    } else if (strcmp(str, "off") == 0) {
  14006      jit::JitOptions.disableRangeAnalysis = true;
  14007    } else {
  14008      return OptionFailure("ion-range-analysis", str);
  14009    }
  14010  }
  14011 
  14012  if (const char* str = op.getStringOption("ion-sink")) {
  14013    if (strcmp(str, "on") == 0) {
  14014      jit::JitOptions.disableSink = false;
  14015    } else if (strcmp(str, "off") == 0) {
  14016      jit::JitOptions.disableSink = true;
  14017    } else {
  14018      return OptionFailure("ion-sink", str);
  14019    }
  14020  }
  14021 
  14022  if (const char* str = op.getStringOption("ion-optimize-shapeguards")) {
  14023    if (strcmp(str, "on") == 0) {
  14024      jit::JitOptions.disableRedundantShapeGuards = false;
  14025    } else if (strcmp(str, "off") == 0) {
  14026      jit::JitOptions.disableRedundantShapeGuards = true;
  14027    } else {
  14028      return OptionFailure("ion-optimize-shapeguards", str);
  14029    }
  14030  }
  14031 
  14032  if (const char* str = op.getStringOption("ion-optimize-gcbarriers")) {
  14033    if (strcmp(str, "on") == 0) {
  14034      jit::JitOptions.disableRedundantGCBarriers = false;
  14035    } else if (strcmp(str, "off") == 0) {
  14036      jit::JitOptions.disableRedundantGCBarriers = true;
  14037    } else {
  14038      return OptionFailure("ion-optimize-gcbarriers", str);
  14039    }
  14040  }
  14041 
  14042  if (const char* str = op.getStringOption("ion-instruction-reordering")) {
  14043    if (strcmp(str, "on") == 0) {
  14044      jit::JitOptions.disableInstructionReordering = false;
  14045    } else if (strcmp(str, "off") == 0) {
  14046      jit::JitOptions.disableInstructionReordering = true;
  14047    } else {
  14048      return OptionFailure("ion-instruction-reordering", str);
  14049    }
  14050  }
  14051 
  14052  if (op.getBoolOption("ion-check-range-analysis")) {
  14053    jit::JitOptions.checkRangeAnalysis = true;
  14054  }
  14055 
  14056  if (op.getBoolOption("ion-extra-checks")) {
  14057    jit::JitOptions.runExtraChecks = true;
  14058  }
  14059 
  14060  if (const char* str = op.getStringOption("ion-inlining")) {
  14061    if (strcmp(str, "on") == 0) {
  14062      jit::JitOptions.disableInlining = false;
  14063    } else if (strcmp(str, "off") == 0) {
  14064      jit::JitOptions.disableInlining = true;
  14065    } else {
  14066      return OptionFailure("ion-inlining", str);
  14067    }
  14068  }
  14069 
  14070  if (const char* str = op.getStringOption("ion-osr")) {
  14071    if (strcmp(str, "on") == 0) {
  14072      jit::JitOptions.osr = true;
  14073    } else if (strcmp(str, "off") == 0) {
  14074      jit::JitOptions.osr = false;
  14075    } else {
  14076      return OptionFailure("ion-osr", str);
  14077    }
  14078  }
  14079 
  14080  if (const char* str = op.getStringOption("ion-limit-script-size")) {
  14081    if (strcmp(str, "on") == 0) {
  14082      jit::JitOptions.limitScriptSize = true;
  14083    } else if (strcmp(str, "off") == 0) {
  14084      jit::JitOptions.limitScriptSize = false;
  14085    } else {
  14086      return OptionFailure("ion-limit-script-size", str);
  14087    }
  14088  }
  14089 
  14090  if (op.getBoolOption("disable-main-thread-denormals")) {
  14091    // This is a simplified version of WebAudio code, which is good enough for
  14092    // fuzzing purposes.
  14093    //
  14094    // See dom/media/webaudio/blink/DenormalDisabler.h#124
  14095 #if defined(__GNUC__) && defined(__SSE__) && defined(__x86_64__)
  14096    int savedCSR;
  14097    asm volatile("stmxcsr %0" : "=m"(savedCSR));
  14098    int newCSR = savedCSR | 0x8040;
  14099    asm volatile("ldmxcsr %0" : : "m"(newCSR));
  14100 #elif defined(__arm__)
  14101    int savedCSR;
  14102    asm volatile("vmrs %[result], FPSCR" : [result] "=r"(savedCSR));
  14103    // Bit 24 is the flush-to-zero mode control bit. Setting it to 1 flushes
  14104    // denormals to 0.
  14105    int newCSR = savedCSR | (1 << 24);
  14106    asm volatile("vmsr FPSCR, %[src]" : : [src] "r"(newCSR));
  14107 #elif defined(__aarch64__)
  14108    int savedCSR;
  14109    asm volatile("mrs %x[result], FPCR" : [result] "=r"(savedCSR));
  14110    // Bit 24 is the flush-to-zero mode control bit. Setting it to 1 flushes
  14111    // denormals to 0.
  14112    int newCSR = savedCSR | (1 << 24);
  14113    asm volatile("msr FPCR, %x[src]" : : [src] "r"(newCSR));
  14114 #else
  14115    // Do nothing on other architecture.
  14116 #endif
  14117  }
  14118 
  14119  if (const char* str = op.getStringOption("object-keys-scalar-replacement")) {
  14120    if (strcmp(str, "on") == 0) {
  14121      jit::JitOptions.disableObjectKeysScalarReplacement = false;
  14122    } else if (strcmp(str, "off") == 0) {
  14123      jit::JitOptions.disableObjectKeysScalarReplacement = true;
  14124    } else {
  14125      return OptionFailure("object-keys-scalar-replacement", str);
  14126    }
  14127  }
  14128 
  14129  int32_t warmUpThreshold = op.getIntOption("ion-warmup-threshold");
  14130  if (warmUpThreshold >= 0) {
  14131    jit::JitOptions.setNormalIonWarmUpThreshold(warmUpThreshold);
  14132  }
  14133 
  14134  warmUpThreshold = op.getIntOption("baseline-warmup-threshold");
  14135  if (warmUpThreshold >= 0) {
  14136    jit::JitOptions.baselineJitWarmUpThreshold = warmUpThreshold;
  14137  }
  14138 
  14139  warmUpThreshold = op.getIntOption("trial-inlining-warmup-threshold");
  14140  if (warmUpThreshold >= 0) {
  14141    jit::JitOptions.trialInliningWarmUpThreshold = warmUpThreshold;
  14142  }
  14143 
  14144  warmUpThreshold = op.getIntOption("regexp-warmup-threshold");
  14145  if (warmUpThreshold >= 0) {
  14146    jit::JitOptions.regexpWarmUpThreshold = warmUpThreshold;
  14147  }
  14148 
  14149  if (op.getBoolOption("baseline-eager")) {
  14150    jit::JitOptions.setEagerBaselineCompilation();
  14151  }
  14152 
  14153 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
  14154  if (op.getBoolOption("portable-baseline-eager")) {
  14155    jit::JitOptions.setEagerPortableBaselineInterpreter();
  14156  }
  14157  if (op.getBoolOption("portable-baseline")) {
  14158    jit::JitOptions.portableBaselineInterpreter = true;
  14159  }
  14160  if (op.getBoolOption("no-portable-baseline")) {
  14161    jit::JitOptions.portableBaselineInterpreter = false;
  14162  }
  14163 #endif
  14164 
  14165 #ifdef ENABLE_JS_AOT_ICS
  14166  if (op.getBoolOption("aot-ics")) {
  14167    jit::JitOptions.enableAOTICs = true;
  14168  }
  14169  if (op.getBoolOption("enforce-aot-ics")) {
  14170    jit::JitOptions.enableAOTICEnforce = true;
  14171  }
  14172 #endif
  14173 
  14174  if (op.getBoolOption("blinterp")) {
  14175    jit::JitOptions.baselineInterpreter = true;
  14176  }
  14177 
  14178  if (op.getBoolOption("no-blinterp")) {
  14179    jit::JitOptions.baselineInterpreter = false;
  14180  }
  14181 
  14182  if (op.getBoolOption("disable-jithints")) {
  14183    jit::JitOptions.disableJitHints = true;
  14184  }
  14185 
  14186  if (op.getBoolOption("emit-interpreter-entry")) {
  14187    jit::JitOptions.emitInterpreterEntryTrampoline = true;
  14188  }
  14189 
  14190  if (op.getBoolOption("no-emit-interpreter-entry")) {
  14191    jit::JitOptions.emitInterpreterEntryTrampoline = false;
  14192  }
  14193 
  14194  warmUpThreshold = op.getIntOption("blinterp-warmup-threshold");
  14195  if (warmUpThreshold >= 0) {
  14196    jit::JitOptions.baselineInterpreterWarmUpThreshold = warmUpThreshold;
  14197  }
  14198 
  14199  if (op.getBoolOption("blinterp-eager")) {
  14200    jit::JitOptions.baselineInterpreterWarmUpThreshold = 0;
  14201  }
  14202 
  14203  if (op.getBoolOption("no-baseline")) {
  14204    jit::JitOptions.baselineJit = false;
  14205  }
  14206 
  14207  if (op.getBoolOption("no-ion")) {
  14208    jit::JitOptions.ion = false;
  14209  }
  14210 
  14211  if (op.getBoolOption("no-native-regexp")) {
  14212    jit::JitOptions.nativeRegExp = false;
  14213  }
  14214 
  14215  if (op.getBoolOption("trace-regexp-parser")) {
  14216    jit::JitOptions.trace_regexp_parser = true;
  14217  }
  14218  if (op.getBoolOption("trace-regexp-assembler")) {
  14219    jit::JitOptions.trace_regexp_assembler = true;
  14220  }
  14221  if (op.getBoolOption("trace-regexp-interpreter")) {
  14222    jit::JitOptions.trace_regexp_bytecodes = true;
  14223  }
  14224  if (op.getBoolOption("trace-regexp-peephole")) {
  14225    jit::JitOptions.trace_regexp_peephole_optimization = true;
  14226  }
  14227 
  14228  if (op.getBoolOption("less-debug-code")) {
  14229    jit::JitOptions.lessDebugCode = true;
  14230  }
  14231 
  14232  int32_t inliningEntryThreshold = op.getIntOption("inlining-entry-threshold");
  14233  if (inliningEntryThreshold > 0) {
  14234    jit::JitOptions.inliningEntryThreshold = inliningEntryThreshold;
  14235  }
  14236 
  14237  int32_t smallFunctionLength = op.getIntOption("small-function-length");
  14238  if (smallFunctionLength > 0) {
  14239    jit::JitOptions.smallFunctionMaxBytecodeLength = smallFunctionLength;
  14240  }
  14241 
  14242  if (op.getBoolOption("ion-eager")) {
  14243    jit::JitOptions.setEagerIonCompilation();
  14244  }
  14245 
  14246  offthreadBaselineCompilation = false;
  14247  if (const char* str = op.getStringOption("baseline-offthread-compile")) {
  14248    if (strcmp(str, "on") == 0) {
  14249      offthreadBaselineCompilation = true;
  14250    } else if (strcmp(str, "off") != 0) {
  14251      return OptionFailure("baseline-offthread-compile", str);
  14252    }
  14253  }
  14254  cx->runtime()->setOffthreadBaselineCompilationEnabled(
  14255      offthreadBaselineCompilation);
  14256 
  14257  if (const char* str = op.getStringOption("baseline-batching")) {
  14258    if (strcmp(str, "on") == 0) {
  14259      jit::JitOptions.baselineBatching = true;
  14260    } else if (strcmp(str, "off") == 0) {
  14261      jit::JitOptions.baselineBatching = false;
  14262    } else {
  14263      return OptionFailure("baseline-batching", str);
  14264    }
  14265  }
  14266 
  14267  int32_t queueCapacity = op.getIntOption("baseline-queue-capacity");
  14268  if (queueCapacity == 0) {
  14269    fprintf(stderr, "baseline-queue-capacity must be positive\n");
  14270    return false;
  14271  }
  14272  if (queueCapacity > 0) {
  14273    jit::JitOptions.baselineQueueCapacity = std::min(
  14274        uint32_t(queueCapacity), js::jit::BaselineCompileQueue::MaxCapacity);
  14275  }
  14276 
  14277  offthreadIonCompilation = true;
  14278  if (const char* str = op.getStringOption("ion-offthread-compile")) {
  14279    if (strcmp(str, "off") == 0) {
  14280      offthreadIonCompilation = false;
  14281    } else if (strcmp(str, "on") != 0) {
  14282      return OptionFailure("ion-offthread-compile", str);
  14283    }
  14284  }
  14285  cx->runtime()->setOffthreadIonCompilationEnabled(offthreadIonCompilation);
  14286 
  14287  if (op.getStringOption("ion-parallel-compile")) {
  14288    fprintf(stderr,
  14289            "--ion-parallel-compile is deprecated. Please use "
  14290            "--ion-offthread-compile instead.\n");
  14291    return false;
  14292  }
  14293 
  14294  if (op.getBoolOption("disable-bailout-loop-check")) {
  14295    jit::JitOptions.disableBailoutLoopCheck = true;
  14296  }
  14297 
  14298  if (op.getBoolOption("only-inline-selfhosted")) {
  14299    jit::JitOptions.onlyInlineSelfHosted = true;
  14300  }
  14301 
  14302  if (op.getBoolOption("enable-ic-frame-pointers")) {
  14303    jit::JitOptions.enableICFramePointers = true;
  14304  }
  14305 
  14306  if (const char* str = op.getStringOption("ion-iterator-indices")) {
  14307    if (strcmp(str, "on") == 0) {
  14308      jit::JitOptions.disableIteratorIndices = false;
  14309    } else if (strcmp(str, "off") == 0) {
  14310      jit::JitOptions.disableIteratorIndices = true;
  14311    } else {
  14312      return OptionFailure("ion-iterator-indices", str);
  14313    }
  14314  }
  14315 
  14316  if (const char* str = op.getStringOption("ion-load-keys")) {
  14317    if (strcmp(str, "on") == 0) {
  14318      jit::JitOptions.disableMarkLoadsUsedAsPropertyKeys = false;
  14319    } else if (strcmp(str, "off") == 0) {
  14320      jit::JitOptions.disableMarkLoadsUsedAsPropertyKeys = true;
  14321    } else {
  14322      return OptionFailure("ion-load-keys", str);
  14323    }
  14324  }
  14325 
  14326 #if defined(JS_SIMULATOR_ARM)
  14327  if (op.getBoolOption("arm-sim-icache-checks")) {
  14328    jit::SimulatorProcess::ICacheCheckingDisableCount = 0;
  14329  }
  14330 
  14331  int32_t stopAt = op.getIntOption("arm-sim-stop-at");
  14332  if (stopAt >= 0) {
  14333    jit::Simulator::StopSimAt = stopAt;
  14334  }
  14335 #elif defined(JS_SIMULATOR_MIPS64)
  14336  if (op.getBoolOption("mips-sim-icache-checks")) {
  14337    jit::SimulatorProcess::ICacheCheckingDisableCount = 0;
  14338  }
  14339 
  14340  int32_t stopAt = op.getIntOption("mips-sim-stop-at");
  14341  if (stopAt >= 0) {
  14342    jit::Simulator::StopSimAt = stopAt;
  14343  }
  14344 #elif defined(JS_SIMULATOR_LOONG64)
  14345  if (op.getBoolOption("loong64-sim-icache-checks")) {
  14346    jit::SimulatorProcess::ICacheCheckingDisableCount = 0;
  14347  }
  14348 
  14349  int32_t stopAt = op.getIntOption("loong64-sim-stop-at");
  14350  if (stopAt >= 0) {
  14351    jit::Simulator::StopSimAt = stopAt;
  14352  }
  14353 #endif
  14354 
  14355 #ifdef DEBUG
  14356 #  ifdef JS_CODEGEN_RISCV64
  14357  if (op.getBoolOption("riscv-debug")) {
  14358    jit::Assembler::FLAG_riscv_debug = true;
  14359  }
  14360 #  endif
  14361 #  ifdef JS_SIMULATOR_RISCV64
  14362  if (op.getBoolOption("trace-sim")) {
  14363    jit::Simulator::FLAG_trace_sim = true;
  14364  }
  14365  if (op.getBoolOption("debug-sim")) {
  14366    jit::Simulator::FLAG_debug_sim = true;
  14367  }
  14368  if (op.getBoolOption("riscv-trap-to-simulator-debugger")) {
  14369    jit::Simulator::FLAG_riscv_trap_to_simulator_debugger = true;
  14370  }
  14371  int32_t stopAt = op.getIntOption("riscv-sim-stop-at");
  14372  if (stopAt >= 0) {
  14373    jit::Simulator::StopSimAt = stopAt;
  14374  }
  14375 #  endif
  14376 #endif
  14377 
  14378  return true;
  14379 }
  14380 
  14381 bool SetContextGCOptions(JSContext* cx, const OptionParser& op) {
  14382  JS_SetGCParameter(cx, JSGC_MAX_BYTES, 0xffffffff);
  14383 
  14384  size_t nurseryBytes = op.getIntOption("nursery-size") * 1024L * 1024L;
  14385  if (nurseryBytes == 0) {
  14386    fprintf(stderr, "Error: --nursery-size parameter must be non-zero.\n");
  14387    fprintf(stderr,
  14388            "The nursery can be disabled by passing the --no-ggc option.\n");
  14389    return false;
  14390  }
  14391  JS_SetGCParameter(cx, JSGC_MAX_NURSERY_BYTES, nurseryBytes);
  14392 
  14393  size_t availMemMB = op.getIntOption("available-memory");
  14394  if (availMemMB > 0) {
  14395    JS_SetGCParametersBasedOnAvailableMemory(cx, availMemMB);
  14396  }
  14397 
  14398  if (const char* opt = op.getStringOption("nursery-strings")) {
  14399    if (strcmp(opt, "on") == 0) {
  14400      cx->runtime()->gc.nursery().enableStrings();
  14401    } else if (strcmp(opt, "off") == 0) {
  14402      cx->runtime()->gc.nursery().disableStrings();
  14403    } else {
  14404      MOZ_CRASH("invalid option value for --nursery-strings, must be on/off");
  14405    }
  14406  }
  14407 
  14408  if (const char* opt = op.getStringOption("nursery-bigints")) {
  14409    if (strcmp(opt, "on") == 0) {
  14410      cx->runtime()->gc.nursery().enableBigInts();
  14411    } else if (strcmp(opt, "off") == 0) {
  14412      cx->runtime()->gc.nursery().disableBigInts();
  14413    } else {
  14414      MOZ_CRASH("invalid option value for --nursery-bigints, must be on/off");
  14415    }
  14416  }
  14417 
  14418  bool incrementalGC = !op.getBoolOption("no-incremental-gc");
  14419  JS_SetGCParameter(cx, JSGC_INCREMENTAL_GC_ENABLED, incrementalGC);
  14420 
  14421 #ifndef ANDROID
  14422  bool parallelMarking = true;
  14423 #else
  14424  bool parallelMarking = false;
  14425 #endif
  14426  if (op.getBoolOption("enable-parallel-marking")) {
  14427    parallelMarking = true;
  14428  }
  14429  if (op.getBoolOption("no-parallel-marking")) {
  14430    parallelMarking = false;
  14431  }
  14432  JS_SetGCParameter(cx, JSGC_PARALLEL_MARKING_ENABLED, parallelMarking);
  14433 
  14434  JS_SetGCParameter(cx, JSGC_SLICE_TIME_BUDGET_MS, 5);
  14435 
  14436  JS_SetGCParameter(cx, JSGC_PER_ZONE_GC_ENABLED, true);
  14437 
  14438  for (MultiStringRange args = op.getMultiStringOption("gc-param");
  14439       !args.empty(); args.popFront()) {
  14440    if (!SetGCParameterFromArg(cx, args.front())) {
  14441      return false;
  14442    }
  14443  }
  14444 
  14445 #ifdef DEBUG
  14446  dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
  14447 #endif
  14448 
  14449 #ifdef JS_GC_ZEAL
  14450  const char* zealStr = op.getStringOption("gc-zeal");
  14451  if (zealStr) {
  14452    if (!cx->runtime()->gc.parseAndSetZeal(zealStr)) {
  14453      return false;
  14454    }
  14455    uint32_t nextScheduled;
  14456    cx->runtime()->gc.getZealBits(&gZealBits, &gZealFrequency, &nextScheduled);
  14457  }
  14458 #endif
  14459 
  14460  return true;
  14461 }
  14462 
  14463 bool InitModuleLoader(JSContext* cx, const OptionParser& op) {
  14464  RootedString moduleLoadPath(cx);
  14465  if (const char* option = op.getStringOption("module-load-path")) {
  14466    UniqueChars pathUtf8 = JS::EncodeNarrowToUtf8(cx, option);
  14467    if (!pathUtf8) {
  14468      return false;
  14469    }
  14470 
  14471    Rooted<JSString*> jspath(cx, NewStringCopyUTF8(cx, pathUtf8.get()));
  14472    if (!jspath) {
  14473      return false;
  14474    }
  14475 
  14476    moduleLoadPath = js::shell::ResolvePath(cx, jspath, RootRelative);
  14477 
  14478    processWideModuleLoadPath = JS_EncodeStringToUTF8(cx, moduleLoadPath);
  14479    if (!processWideModuleLoadPath) {
  14480      return false;
  14481    }
  14482  } else {
  14483    processWideModuleLoadPath = js::shell::GetCWD(cx);
  14484    if (!processWideModuleLoadPath) {
  14485      return false;
  14486    }
  14487 
  14488    moduleLoadPath = NewStringCopyUTF8(cx, processWideModuleLoadPath.get());
  14489    if (!moduleLoadPath) {
  14490      return false;
  14491    }
  14492  }
  14493 
  14494  ShellContext* sc = GetShellContext(cx);
  14495  sc->moduleLoader = js::MakeUnique<ModuleLoader>();
  14496  if (!sc->moduleLoader || !sc->moduleLoader->init(cx, moduleLoadPath)) {
  14497    return false;
  14498  }
  14499 
  14500  return true;
  14501 }