Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


SSL js.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/* JS shell. */

#include "mozilla/AlreadyAddRefed.h"  // mozilla::already_AddRefed
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_ASSERT_IF, MOZ_RELEASE_ASSERT, MOZ_CRASH
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "mozilla/Compression.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EnumSet.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/mozalloc.h"
#include "mozilla/PodOperations.h"
#include "mozilla/RandomNum.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Sprintf.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtrExtensions.h"  // UniqueFreePtr
#include "mozilla/Utf8.h"
#include "mozilla/Variant.h"

#include <algorithm>
#include <cctype>
#include <chrono>
#ifdef XP_WIN
#  include <direct.h>
#  include <process.h>
#endif
#include <errno.h>
#include <fcntl.h>
#if defined(XP_WIN)
#  include <io.h> /* for isatty() */
#endif
#include <locale.h>
#if defined(MALLOC_H)
#  include MALLOC_H /* for malloc_usable_size, malloc_size, _msize */
#endif
#include <ctime>
#include <math.h>
#ifndef __wasi__
#  include <signal.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <utility>
#ifdef XP_UNIX
#  ifndef __wasi__
#    include <sys/mman.h>
#    include <sys/wait.h>
#  endif
#  include <sys/stat.h>
#  include <unistd.h>
#endif
#ifdef XP_LINUX
#  include <sys/prctl.h>
#endif

#include "jsapi.h"
#include "jsfriendapi.h"
#include "jstypes.h"
#ifndef JS_WITHOUT_NSPR
#  include "prerror.h"
#  include "prlink.h"
#endif

#include "builtin/Array.h"
#include "builtin/MapObject.h"
#include "builtin/ModuleObject.h"
#include "builtin/RegExp.h"
#include "builtin/TestingFunctions.h"
#include "builtin/TestingUtility.h"  // js::ParseCompileOptions, js::ParseDebugMetadata, js::CreateScriptPrivate
#include "debugger/DebugAPI.h"
#include "frontend/CompilationStencil.h"
#ifdef JS_ENABLE_SMOOSH
#  include "frontend/Frontend2.h"
#endif
#include "frontend/FrontendContext.h"  // AutoReportFrontendContext
#include "frontend/ModuleSharedContext.h"
#include "frontend/Parser.h"
#include "frontend/ScopeBindingCache.h"  // js::frontend::ScopeBindingCache
#include "gc/GC.h"
#include "gc/PublicIterators.h"
#ifdef DEBUG
#  include "irregexp/RegExpAPI.h"
#endif

#ifdef JS_SIMULATOR_ARM
#  include "jit/arm/Simulator-arm.h"
#endif
#ifdef JS_SIMULATOR_MIPS32
#  include "jit/mips32/Simulator-mips32.h"
#endif
#ifdef JS_SIMULATOR_MIPS64
#  include "jit/mips64/Simulator-mips64.h"
#endif
#ifdef JS_SIMULATOR_LOONG64
#  include "jit/loong64/Simulator-loong64.h"
#endif
#ifdef JS_SIMULATOR_RISCV64
#  include "jit/riscv64/Simulator-riscv64.h"
#endif
#include "jit/CacheIRHealth.h"
#include "jit/InlinableNatives.h"
#include "jit/Ion.h"
#include "jit/JitcodeMap.h"
#include "jit/JitZone.h"
#include "jit/shared/CodeGenerator-shared.h"
#include "js/Array.h"        // JS::NewArrayObject
#include "js/ArrayBuffer.h"  // JS::{CreateMappedArrayBufferContents,NewMappedArrayBufferWithContents,IsArrayBufferObject,GetArrayBufferLengthAndData}
#include "js/BuildId.h"      // JS::BuildIdCharVector, JS::SetProcessBuildIdOp
#include "js/CallAndConstruct.h"  // JS::Call, JS::IsCallable, JS_CallFunction, JS_CallFunctionValue
#include "js/CharacterEncoding.h"  // JS::StringIsASCII
#include "js/CompilationAndEvaluation.h"
#include "js/CompileOptions.h"  // JS::ReadOnlyCompileOptions, JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::InstantiateOptions
#include "js/ContextOptions.h"  // JS::ContextOptions{,Ref}
#include "js/Debug.h"  // JS::dbg::ShouldAvoidSideEffects, JS::ExecutionTrace
#include "js/EnvironmentChain.h"            // JS::EnvironmentChain
#include "js/Equality.h"                    // JS::SameValue
#include "js/ErrorReport.h"                 // JS::PrintError
#include "js/Exception.h"                   // JS::StealPendingExceptionStack
#include "js/experimental/BindingAllocs.h"  // JS_NewObjectWithGivenProtoAndUseAllocSite
#include "js/experimental/CodeCoverage.h"   // js::EnableCodeCoverage
#include "js/experimental/CompileScript.h"  // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::HadFrontendErrors, JS::ConvertFrontendErrorsToRuntimeErrors, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil
#include "js/experimental/CTypes.h"         // JS::InitCTypesClass
#include "js/experimental/Intl.h"  // JS::AddMoz{DateTimeFormat,DisplayNames}Constructor
#include "js/experimental/JitInfo.h"  // JSJit{Getter,Setter,Method}CallArgs, JSJitGetterInfo, JSJit{Getter,Setter}Op, JSJitInfo
#include "js/experimental/JSStencil.h"  // JS::Stencil, JS::DecodeStencil, JS::InstantiateModuleStencil
#include "js/experimental/SourceHook.h"  // js::{Set,Forget,}SourceHook
#include "js/experimental/TypedData.h"   // JS_NewUint8Array
#include "js/friend/DumpFunctions.h"     // JS::FormatStackDump
#include "js/friend/ErrorMessages.h"     // js::GetErrorMessage, JSMSG_*
#include "js/friend/StackLimits.h"       // js::AutoCheckRecursionLimit
#include "js/friend/WindowProxy.h"  // js::IsWindowProxy, js::SetWindowProxyClass, js::ToWindowProxyIfWindow, js::ToWindowIfWindowProxy
#include "js/GCAPI.h"               // JS::AutoCheckCannotGC
#include "js/GCVector.h"
#include "js/GlobalObject.h"
#include "js/Initialization.h"
#include "js/Interrupt.h"
#include "js/JSON.h"
#include "js/MemoryCallbacks.h"
#include "js/MemoryFunctions.h"
#include "js/Modules.h"  // JS::GetModulePrivate, JS::SetModule{DynamicImport,Metadata,Resolve}Hook, JS::SetModulePrivate, JS::CompileModule
#include "js/Object.h"  // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
#include "js/Prefs.h"
#include "js/Principals.h"
#include "js/Printer.h"  // QuoteString
#include "js/Printf.h"
#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
#include "js/PropertySpec.h"
#include "js/Realm.h"
#include "js/RegExp.h"  // JS::ObjectIsRegExp
#include "js/ScriptPrivate.h"
#include "js/SourceText.h"  // JS::SourceText
#include "js/StableStringChars.h"
#include "js/Stack.h"
#include "js/StreamConsumer.h"
#include "js/StructuredClone.h"
#include "js/SweepingAPI.h"
#include "js/Transcoding.h"  // JS::TranscodeBuffer, JS::TranscodeRange, JS::IsTranscodeFailureResult
#include "js/Warnings.h"      // JS::SetWarningReporter
#include "js/WasmFeatures.h"  // JS_FOR_WASM_FEATURES
#include "js/WasmModule.h"    // JS::WasmModule
#include "js/Wrapper.h"
#include "proxy/DeadObjectProxy.h"  // js::IsDeadProxyObject
#include "shell/jsoptparse.h"
#include "shell/jsshell.h"
#include "shell/OSObject.h"
#include "shell/ShellModuleObjectWrapper.h"
#include "shell/WasmTesting.h"
#include "threading/ConditionVariable.h"
#include "threading/ExclusiveData.h"
#include "threading/LockGuard.h"
#include "threading/Thread.h"
#include "util/CompleteFile.h"  // js::FileContents, js::ReadCompleteFile
#include "util/DifferentialTesting.h"
#include "util/StringBuilder.h"
#include "util/Text.h"
#include "util/WindowsWrapper.h"
#include "vm/ArgumentsObject.h"
#include "vm/Compression.h"
#include "vm/ErrorObject.h"
#include "vm/ErrorReporting.h"
#include "vm/HelperThreads.h"
#include "vm/JSAtomUtils.h"  // AtomizeUTF8Chars, AtomizeString, ToAtom
#include "vm/JSContext.h"
#include "vm/JSFunction.h"
#include "vm/JSObject.h"
#include "vm/JSScript.h"
#include "vm/Logging.h"
#include "vm/ModuleBuilder.h"  // js::ModuleBuilder
#include "vm/Modules.h"
#include "vm/Monitor.h"
#include "vm/MutexIDs.h"
#include "vm/PromiseObject.h"  // js::PromiseObject
#include "vm/Shape.h"
#include "vm/SharedArrayObject.h"
#include "vm/StencilObject.h"  // js::StencilObject
#include "vm/Time.h"
#include "vm/ToSource.h"  // js::ValueToSource
#include "vm/TypedArrayObject.h"
#include "vm/WrapperObject.h"
#include "wasm/WasmFeatures.h"
#include "wasm/WasmJS.h"

#include "vm/Compartment-inl.h"
#include "vm/ErrorObject-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/Realm-inl.h"
#include "vm/Stack-inl.h"

#undef compress

using namespace js;
using namespace js::cli;
using namespace js::shell;

using JS::AutoStableStringChars;
using JS::CompileOptions;

using js::shell::RCFile;

using mozilla::ArrayEqual;
using mozilla::AsVariant;
using mozilla::Atomic;
using mozilla::MakeScopeExit;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::NumberEqualsInt32;
using mozilla::TimeDuration;
using mozilla::TimeStamp;
using mozilla::Utf8Unit;
using mozilla::Variant;

bool InitOptionParser(OptionParser& op);
bool SetGlobalOptionsPreJSInit(const OptionParser& op);
bool SetGlobalOptionsPostJSInit(const OptionParser& op);
bool SetContextOptions(JSContext* cx, const OptionParser& op);
bool SetContextWasmOptions(JSContext* cx, const OptionParser& op);
bool SetContextJITOptions(JSContext* cx, const OptionParser& op);
bool SetContextGCOptions(JSContext* cx, const OptionParser& op);
bool InitModuleLoader(JSContext* cx, const OptionParser& op);

#ifdef FUZZING_JS_FUZZILLI
#  define REPRL_CRFD 100
#  define REPRL_CWFD 101
#  define REPRL_DRFD 102
#  define REPRL_DWFD 103

#  define SHM_SIZE 0x100000
#  define MAX_EDGES ((SHM_SIZE - 4) * 8)

struct shmem_data {
  uint32_t num_edges;
  unsigned char edges[];
};

struct shmem_data* __shmem;

uint32_t *__edges_start, *__edges_stop;
void __sanitizer_cov_reset_edgeguards() {
  uint64_t N = 0;
  for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++)
    *x = ++N;
}

extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start,
                                                    uint32_t* stop) {
  // Avoid duplicate initialization
  if (start == stop || *start) return;

  if (__edges_start != NULL || __edges_stop != NULL) {
    fprintf(stderr,
            "Coverage instrumentation is only supported for a single module\n");
    _exit(-1);
  }

  __edges_start = start;
  __edges_stop = stop;

  // Map the shared memory region
  const char* shm_key = getenv("SHM_ID");
  if (!shm_key) {
    puts("[COV] no shared memory bitmap available, skipping");
    __shmem = (struct shmem_data*)malloc(SHM_SIZE);
  } else {
    int fd = shm_open(shm_key, O_RDWR, S_IREAD | S_IWRITE);
    if (fd <= -1) {
      fprintf(stderr, "Failed to open shared memory region: %s\n",
              strerror(errno));
      _exit(-1);
    }

    __shmem = (struct shmem_data*)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE,
                                       MAP_SHARED, fd, 0);
    if (__shmem == MAP_FAILED) {
      fprintf(stderr, "Failed to mmap shared memory region\n");
      _exit(-1);
    }
  }

  __sanitizer_cov_reset_edgeguards();

  __shmem->num_edges = stop - start;
  printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n",
         shm_key, __shmem->num_edges);
}

extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
  // There's a small race condition here: if this function executes in two
  // threads for the same edge at the same time, the first thread might disable
  // the edge (by setting the guard to zero) before the second thread fetches
  // the guard value (and thus the index). However, our instrumentation ignores
  // the first edge (see libcoverage.c) and so the race is unproblematic.
  uint32_t index = *guard;
  // If this function is called before coverage instrumentation is properly
  // initialized we want to return early.
  if (!index) return;
  __shmem->edges[index / 8] |= 1 << (index % 8);
  *guard = 0;
}
#endif /* FUZZING_JS_FUZZILLI */

enum JSShellExitCode {
  EXITCODE_RUNTIME_ERROR = 3,
  EXITCODE_FILE_NOT_FOUND = 4,
  EXITCODE_OUT_OF_MEMORY = 5,
  EXITCODE_TIMEOUT = 6
};

struct ShellLogModule {
  // Since ShellLogModules have references to their levels created
  // we can't move them.
  ShellLogModule(ShellLogModule&&) = delete;

  const char* name;
  explicit ShellLogModule(const char* name) : name(name) {}
  mozilla::AtomicLogLevel level;
};

// If asserts related to this ever fail, simply bump this number.
//
// This is used to construct a mozilla::Array, which is used because a
// ShellLogModule cannot move once constructed to avoid invalidating
// a levelRef.
static const int MAX_LOG_MODULES = 64;
static int initialized_modules = 0;
mozilla::Array<mozilla::Maybe<ShellLogModule>, MAX_LOG_MODULES> logModules;

JS::OpaqueLogger GetLoggerByName(const char* name) {
  // Check for pre-existing module
  for (auto& logger : logModules) {
    if (logger) {
      if (logger->name == name) {
        return logger.ptr();
      }
    }
    // We've seen all initialized, not there, break out.
    if (!logger) break;
  }

  // Not found, allocate a new module.
  MOZ_RELEASE_ASSERT(initialized_modules < MAX_LOG_MODULES - 1);
  auto index = initialized_modules++;
  logModules[index].emplace(name);
  return logModules[index].ptr();
}

mozilla::AtomicLogLevel& GetLevelRef(JS::OpaqueLogger logger) {
  ShellLogModule* slm = static_cast<ShellLogModule*>(logger);
  return slm->level;
}

void LogPrintVA(const JS::OpaqueLogger logger, mozilla::LogLevel level,
                const char* fmt, va_list ap) {
  ShellLogModule* mod = static_cast<ShellLogModule*>(logger);
  fprintf(stderr, "[%s] ", mod->name);
  vfprintf(stderr, fmt, ap);
  fprintf(stderr, "\n");
}

JS::LoggingInterface shellLoggingInterface = {GetLoggerByName, LogPrintVA,
                                              GetLevelRef};

static void ToLower(const char* src, char* dest, size_t len) {
  for (size_t c = 0; c < len; c++) {
    dest[c] = (char)(tolower(src[c]));
  }
}

// Run this after initialiation!
void ParseLoggerOptions() {
  char* mixedCaseOpts = getenv("MOZ_LOG");
  if (!mixedCaseOpts) {
    return;
  }

  // Copy into a new buffer and lower case to do case insensitive matching.
  //
  // Done this way rather than just using strcasestr because Windows doesn't
  // have strcasestr as part of its base C library.
  size_t len = strlen(mixedCaseOpts);
  mozilla::UniqueFreePtr<char[]> logOpts(
      static_cast<char*>(calloc(len + 1, 1)));
  if (!logOpts) {
    return;
  }

  ToLower(mixedCaseOpts, logOpts.get(), len);

  // This is a really permissive parser, but will suffice!
  for (auto& logger : logModules) {
    if (logger) {
      // Lowercase the logger name for strstr
      size_t len = strlen(logger->name);
      mozilla::UniqueFreePtr<char[]> lowerName(
          static_cast<char*>(calloc(len + 1, 1)));
      ToLower(logger->name, lowerName.get(), len);

      if (char* needle = strstr(logOpts.get(), lowerName.get())) {
        // If the string to enable a logger is present, but no level is provided
        // then default to Debug level.
        int logLevel = static_cast<int>(mozilla::LogLevel::Debug);

        if (char* colon = strchr(needle, ':')) {
          // Parse character after colon as log level.
          if (*(colon + 1)) {
            logLevel = atoi(colon + 1);
          }
        }

        fprintf(stderr, "[JS_LOG] Enabling Logger %s at level %d\n",
                logger->name, logLevel);
        logger->level = mozilla::ToLogLevel(logLevel);
      }
    }
  }
}

/*
 * Limit the timeout to 30 minutes to prevent an overflow on platfoms
 * that represent the time internally in microseconds using 32-bit int.
 */

static const double MAX_TIMEOUT_SECONDS = 1800.0;

// Not necessarily in sync with the browser
#ifdef ENABLE_SHARED_MEMORY
#  define SHARED_MEMORY_DEFAULT 1
#else
#  define SHARED_MEMORY_DEFAULT 0
#endif

// Fuzzing support for JS runtime fuzzing
#ifdef FUZZING_INTERFACES
#  include "shell/jsrtfuzzing/jsrtfuzzing.h"
MOZ_RUNINIT static bool fuzzDoDebug = !!getenv("MOZ_FUZZ_DEBUG");
MOZ_RUNINIT static bool fuzzHaveModule = !!getenv("FUZZER");
#endif  // FUZZING_INTERFACES

// Code to support GCOV code coverage measurements on standalone shell
#ifdef MOZ_CODE_COVERAGE
#  if defined(__GNUC__) && !defined(__clang__)
extern "C" void __gcov_dump();
extern "C" void __gcov_reset();

void counters_dump(int) { __gcov_dump(); }

void counters_reset(int) { __gcov_reset(); }
#  else
void counters_dump(int) { /* Do nothing */ }

void counters_reset(int) { /* Do nothing */ }
#  endif

static void InstallCoverageSignalHandlers() {
#  ifndef XP_WIN
  fprintf(stderr, "[CodeCoverage] Setting handlers for process %d.\n",
          getpid());

  struct sigaction dump_sa;
  dump_sa.sa_handler = counters_dump;
  dump_sa.sa_flags = SA_RESTART;
  sigemptyset(&dump_sa.sa_mask);
  mozilla::DebugOnly<int> r1 = sigaction(SIGUSR1, &dump_sa, nullptr);
  MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler");

  struct sigaction reset_sa;
  reset_sa.sa_handler = counters_reset;
  reset_sa.sa_flags = SA_RESTART;
  sigemptyset(&reset_sa.sa_mask);
  mozilla::DebugOnly<int> r2 = sigaction(SIGUSR2, &reset_sa, nullptr);
  MOZ_ASSERT(r2 == 0, "Failed to install GCOV SIGUSR2 handler");
#  endif
}
#endif

// An off-thread parse or decode job.
class js::shell::OffThreadJob {
  static constexpr size_t kCompileStackQuota = 128 * sizeof(size_t) * 1024;
  static constexpr size_t kThreadStackQuota =
      kCompileStackQuota + 128 * sizeof(size_t) * 1024;

  enum State {
    RUNNING,   // Working; no stencil.
    DONE,      // Finished; have stencil.
    CANCELLED  // Cancelled due to error.
  };

 public:
  enum class Kind {
    CompileScript,
    CompileModule,
    Decode,
  };

  OffThreadJob(ShellContext* sc, Kind kind, JS::SourceText<char16_t>&& srcBuf);
  OffThreadJob(ShellContext* sc, Kind kind, JS::TranscodeBuffer&& xdrBuf);

  ~OffThreadJob();

  bool init(JSContext* cx, const JS::ReadOnlyCompileOptions& options);
  bool dispatch();

  static void OffThreadMain(OffThreadJob* self);
  void run();

  void cancel();
  void waitUntilDone();

  already_AddRefed<JS::Stencil> stealStencil(JSContext* cx);

 public:
  const int32_t id;

 private:
  Kind kind_;
  State state_;

  JS::FrontendContext* fc_ = nullptr;
  JS::OwningCompileOptions options_;

  UniquePtr<Thread> thread_;

  JS::SourceText<char16_t> srcBuf_;
  JS::TranscodeBuffer xdrBuf_;

  RefPtr<JS::Stencil> stencil_;

  JS::TranscodeResult transcodeResult_ = JS::TranscodeResult::Ok;
};

template <typename T>
static OffThreadJob* NewOffThreadJob(JSContext* cx, OffThreadJob::Kind kind,
                                     JS::ReadOnlyCompileOptions& options,
                                     T&& source) {
  ShellContext* sc = GetShellContext(cx);
  if (sc->isWorker) {
    // Off-thread compilation/decode is used by main-thread, in order to improve
    // the responsiveness.  It's not used by worker in browser, and there's not
    // much reason to support worker here.
    JS_ReportErrorASCII(cx, "Off-thread job is not supported in worker");
    return nullptr;
  }

  UniquePtr<OffThreadJob> job(
      cx->new_<OffThreadJob>(sc, kind, std::move(source)));
  if (!job) {
    return nullptr;
  }

  if (!job->init(cx, options)) {
    return nullptr;
  }

  if (!sc->offThreadJobs.append(job.get())) {
    job->cancel();
    JS_ReportErrorASCII(cx, "OOM adding off-thread job");
    return nullptr;
  }

  return job.release();
}

static OffThreadJob* GetSingleOffThreadJob(JSContext* cx) {
  ShellContext* sc = GetShellContext(cx);
  const auto& jobs = sc->offThreadJobs;
  if (jobs.empty()) {
    JS_ReportErrorASCII(cx, "No off-thread jobs are pending");
    return nullptr;
  }

  if (jobs.length() > 1) {
    JS_ReportErrorASCII(
        cx, "Multiple off-thread jobs are pending: must specify job ID");
    return nullptr;
  }

  return jobs[0];
}

static OffThreadJob* LookupOffThreadJobByID(JSContext* cx, int32_t id) {
  if (id <= 0) {
    JS_ReportErrorASCII(cx, "Bad off-thread job ID");
    return nullptr;
  }

  ShellContext* sc = GetShellContext(cx);
  const auto& jobs = sc->offThreadJobs;
  if (jobs.empty()) {
    JS_ReportErrorASCII(cx, "No off-thread jobs are pending");
    return nullptr;
  }

  OffThreadJob* job = nullptr;
  for (auto someJob : jobs) {
    if (someJob->id == id) {
      job = someJob;
      break;
    }
  }

  if (!job) {
    JS_ReportErrorASCII(cx, "Off-thread job not found");
    return nullptr;
  }

  return job;
}

static OffThreadJob* LookupOffThreadJobForArgs(JSContext* cx,
                                               const CallArgs& args,
                                               size_t arg) {
  // If the optional ID argument isn't present, get the single pending job.
  if (args.length() <= arg) {
    return GetSingleOffThreadJob(cx);
  }

  // Lookup the job using the specified ID.
  int32_t id = 0;
  RootedValue value(cx, args[arg]);
  if (!ToInt32(cx, value, &id)) {
    return nullptr;
  }

  return LookupOffThreadJobByID(cx, id);
}

static void DeleteOffThreadJob(JSContext* cx, OffThreadJob* job) {
  ShellContext* sc = GetShellContext(cx);
  for (size_t i = 0; i < sc->offThreadJobs.length(); i++) {
    if (sc->offThreadJobs[i] == job) {
      sc->offThreadJobs.erase(&sc->offThreadJobs[i]);
      js_delete(job);
      return;
    }
  }

  MOZ_CRASH("Off-thread job not found");
}

static void CancelOffThreadJobsForRuntime(JSContext* cx) {
  ShellContext* sc = GetShellContext(cx);
  while (!sc->offThreadJobs.empty()) {
    OffThreadJob* job = sc->offThreadJobs.popCopy();
    job->waitUntilDone();
    js_delete(job);
  }
}

mozilla::Atomic<int32_t> gOffThreadJobSerial(1);

OffThreadJob::OffThreadJob(ShellContext* sc, Kind kind,
                           JS::SourceText<char16_t>&& srcBuf)
    : id(gOffThreadJobSerial++),
      kind_(kind),
      state_(RUNNING),
      options_(JS::OwningCompileOptions::ForFrontendContext()),
      srcBuf_(std::move(srcBuf)) {
  MOZ_RELEASE_ASSERT(id > 0, "Off-thread job IDs exhausted");
}

OffThreadJob::OffThreadJob(ShellContext* sc, Kind kind,
                           JS::TranscodeBuffer&& xdrBuf)
    : id(gOffThreadJobSerial++),
      kind_(kind),
      state_(RUNNING),
      options_(JS::OwningCompileOptions::ForFrontendContext()),
      xdrBuf_(std::move(xdrBuf)) {
  MOZ_RELEASE_ASSERT(id > 0, "Off-thread job IDs exhausted");
}

OffThreadJob::~OffThreadJob() {
  if (fc_) {
    JS::DestroyFrontendContext(fc_);
  }
  MOZ_ASSERT(state_ != RUNNING);
}

bool OffThreadJob::init(JSContext* cx,
                        const JS::ReadOnlyCompileOptions& options) {
  fc_ = JS::NewFrontendContext();
  if (!fc_) {
    ReportOutOfMemory(cx);
    state_ = CANCELLED;
    return false;
  }

  if (!options_.copy(cx, options)) {
    state_ = CANCELLED;
    return false;
  }

  return true;
}

bool OffThreadJob::dispatch() {
  thread_ =
      js::MakeUnique<Thread>(Thread::Options().setStackSize(kThreadStackQuota));
  if (!thread_) {
    state_ = CANCELLED;
    return false;
  }

  if (!thread_->init(OffThreadJob::OffThreadMain, this)) {
    state_ = CANCELLED;
    thread_ = nullptr;
    return false;
  }

  return true;
}

/* static */ void OffThreadJob::OffThreadMain(OffThreadJob* self) {
  self->run();
}

void OffThreadJob::run() {
  MOZ_ASSERT(state_ == RUNNING);
  MOZ_ASSERT(!stencil_);

  JS::SetNativeStackQuota(fc_, kCompileStackQuota);

  switch (kind_) {
    case Kind::CompileScript: {
      stencil_ = JS::CompileGlobalScriptToStencil(fc_, options_, srcBuf_);
      break;
    }
    case Kind::CompileModule: {
      stencil_ = JS::CompileModuleScriptToStencil(fc_, options_, srcBuf_);
      break;
    }
    case Kind::Decode: {
      JS::DecodeOptions decodeOptions(options_);
      JS::TranscodeRange range(xdrBuf_.begin(), xdrBuf_.length());
      transcodeResult_ = JS::DecodeStencil(fc_, decodeOptions, range,
                                           getter_AddRefs(stencil_));
      break;
    }
  }

  state_ = DONE;
}

void OffThreadJob::cancel() {
  MOZ_ASSERT(state_ == RUNNING);
  MOZ_ASSERT(!stencil_);
  MOZ_ASSERT(!thread_, "cannot cancel after starting a thread");

  state_ = CANCELLED;
}

void OffThreadJob::waitUntilDone() {
  MOZ_ASSERT(state_ != CANCELLED);
  thread_->join();
}

already_AddRefed<JS::Stencil> OffThreadJob::stealStencil(JSContext* cx) {
  JS::FrontendContext* fc = fc_;
  fc_ = nullptr;
  auto destroyFrontendContext =
      mozilla::MakeScopeExit([&]() { JS::DestroyFrontendContext(fc); });

  MOZ_ASSERT(fc);

  if (JS::HadFrontendErrors(fc)) {
    (void)JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options_);
    return nullptr;
  }

  if (!stencil_ && JS::IsTranscodeFailureResult(transcodeResult_)) {
    JS_ReportErrorASCII(cx, "failed to decode cache");
    return nullptr;
  }

  // Report warnings.
  if (!JS::ConvertFrontendErrorsToRuntimeErrors(cx, fc, options_)) {
    return nullptr;
  }

  return stencil_.forget();
}

struct ShellCompartmentPrivate {
  GCPtr<ArrayObject*> blackRoot;
  GCPtr<ArrayObject*> grayRoot;
};

struct MOZ_STACK_CLASS EnvironmentPreparer
    : public js::ScriptEnvironmentPreparer {
  explicit EnvironmentPreparer(JSContext* cx) {
    js::SetScriptEnvironmentPreparer(cx, this);
  }
  void invoke(JS::HandleObject global, Closure& closure) override;
};

const char* shell::selfHostedXDRPath = nullptr;
bool shell::encodeSelfHostedCode = false;
bool shell::enableCodeCoverage = false;
bool shell::enableDisassemblyDumps = false;
bool shell::offthreadBaselineCompilation = false;
bool shell::offthreadIonCompilation = false;
JS::DelazificationOption shell::defaultDelazificationMode =
    JS::DelazificationOption::OnDemandOnly;
bool shell::enableAsmJS = false;
bool shell::enableWasm = false;
bool shell::enableSharedMemory = SHARED_MEMORY_DEFAULT;
bool shell::enableWasmBaseline = false;
bool shell::enableWasmOptimizing = false;
bool shell::enableWasmVerbose = false;
bool shell::enableTestWasmAwaitTier2 = false;
bool shell::enableSourcePragmas = true;
bool shell::enableAsyncStacks = false;
bool shell::enableAsyncStackCaptureDebuggeeOnly = false;
bool shell::enableToSource = false;
bool shell::enableImportAttributes = false;
#ifdef JS_GC_ZEAL
uint32_t shell::gZealBits = 0;
uint32_t shell::gZealFrequency = 0;
#endif
bool shell::printTiming = false;
RCFile* shell::gErrFile = nullptr;
RCFile* shell::gOutFile = nullptr;
bool shell::reportWarnings = true;
bool shell::compileOnly = false;
bool shell::disableOOMFunctions = false;
bool shell::defaultToSameCompartment = true;

#ifdef DEBUG
bool shell::dumpEntrainedVariables = false;
bool shell::OOM_printAllocationCount = false;
#endif

MOZ_RUNINIT UniqueChars shell::processWideModuleLoadPath;

static bool SetTimeoutValue(JSContext* cx, double t);

static void KillWatchdog(JSContext* cx);

static bool ScheduleWatchdog(JSContext* cx, double t);

static void CancelExecution(JSContext* cx);

enum class ShellGlobalKind {
  GlobalObject,
  WindowProxy,
};

static JSObject* NewGlobalObject(JSContext* cx, JS::RealmOptions& options,
                                 JSPrincipals* principals, ShellGlobalKind kind,
                                 bool immutablePrototype);

/*
 * A toy WindowProxy class for the shell. This is intended for testing code
 * where global |this| is a WindowProxy. All requests are forwarded to the
 * underlying global and no navigation is supported.
 */

const JSClass ShellWindowProxyClass =
    PROXY_CLASS_DEF("ShellWindowProxy", JSCLASS_HAS_RESERVED_SLOTS(1));

JSObject* NewShellWindowProxy(JSContext* cx, JS::HandleObject global) {
  MOZ_ASSERT(global->is<GlobalObject>());

  js::WrapperOptions options;
  options.setClass(&ShellWindowProxyClass);

  JSAutoRealm ar(cx, global);
  JSObject* obj =
      js::Wrapper::New(cx, global, &js::Wrapper::singleton, options);
  MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
  return obj;
}

/*
 * A toy principals type for the shell.
 *
 * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the
 * set bits in P are a superset of those in Q. Thus, the principal 0 is
 * subsumed by everything, and the principal ~0 subsumes everything.
 *
 * As a special case, a null pointer as a principal is treated like 0xffff.
 *
 * The 'newGlobal' function takes an option indicating which principal the
 * new global should have; 'evaluate' does for the new code.
 */

class ShellPrincipals final : public JSPrincipals {
  uint32_t bits;

  static uint32_t getBits(JSPrincipals* p) {
    if (!p) {
      return 0xffff;
    }
    return static_cast<ShellPrincipals*>(p)->bits;
  }

 public:
  explicit ShellPrincipals(uint32_t bits, int32_t refcount = 0) : bits(bits) {
    this->refcount = refcount;
  }

  bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
    // The shell doesn't have a read principals hook, so it doesn't really
    // matter what we write here, but we have to write something so the
    // fuzzer is happy.
    return JS_WriteUint32Pair(writer, bits, 0);
  }

  bool isSystemOrAddonPrincipal() override { return true; }

  static void destroy(JSPrincipals* principals) {
    MOZ_ASSERT(principals != &fullyTrusted);
    MOZ_ASSERT(principals->refcount == 0);
    js_delete(static_cast<const ShellPrincipals*>(principals));
  }

  static bool subsumes(JSPrincipals* first, JSPrincipals* second) {
    uint32_t firstBits = getBits(first);
    uint32_t secondBits = getBits(second);
    return (firstBits | secondBits) == firstBits;
  }

  static JSSecurityCallbacks securityCallbacks;

  // Fully-trusted principals singleton.
  static ShellPrincipals fullyTrusted;
};

JSSecurityCallbacks ShellPrincipals::securityCallbacks = {
    nullptr,  // contentSecurityPolicyAllows
    nullptr,  // codeForEvalGets
    subsumes};

// The fully-trusted principal subsumes all other principals.
MOZ_RUNINIT ShellPrincipals ShellPrincipals::fullyTrusted(-1, 1);

#ifdef EDITLINE
extern "C" {
extern MOZ_EXPORT char* readline(const char* prompt);
extern MOZ_EXPORT void add_history(char* line);
}  // extern "C"
#endif

ShellContext::ShellContext(JSContext* cx, IsWorkerEnum isWorker_)
    : cx_(nullptr),
      isWorker(isWorker_),
      lastWarningEnabled(false),
      trackUnhandledRejections(true),
      timeoutInterval(-1.0),
      startTime(PRMJ_Now()),
      serviceInterrupt(false),
      haveInterruptFunc(false),
      interruptFunc(cx, NullValue()),
      lastWarning(cx, NullValue()),
      promiseRejectionTrackerCallback(cx, NullValue()),
      unhandledRejectedPromises(cx),
      watchdogLock(mutexid::ShellContextWatchdog),
      exitCode(0),
      quitting(false),
      readLineBufPos(0),
      errFilePtr(nullptr),
      outFilePtr(nullptr),
      offThreadMonitor(mutexid::ShellOffThreadState),
      finalizationRegistryCleanupCallbacks(cx) {}

ShellContext* js::shell::GetShellContext(JSContext* cx) {
  ShellContext* sc = static_cast<ShellContext*>(JS_GetContextPrivate(cx));
  MOZ_ASSERT(sc);
  return sc;
}

static void TraceRootArrays(JSTracer* trc, gc::MarkColor color) {
  JSRuntime* rt = trc->runtime();
  for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
    for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
      auto priv = static_cast<ShellCompartmentPrivate*>(
          JS_GetCompartmentPrivate(comp.get()));
      if (!priv) {
        continue;
      }

      GCPtr<ArrayObject*>& array =
          (color == gc::MarkColor::Black) ? priv->blackRoot : priv->grayRoot;
      TraceNullableEdge(trc, &array, "shell root array");

      if (array) {
        // Trace the array elements as part of root marking.
        for (uint32_t i = 0; i < array->getDenseInitializedLength(); i++) {
          Value& value = const_cast<Value&>(array->getDenseElement(i));
          TraceManuallyBarrieredEdge(trc, &value, "shell root array element");
        }
      }
    }
  }
}

static void TraceBlackRoots(JSTracer* trc, void* data) {
  TraceRootArrays(trc, gc::MarkColor::Black);
}

static bool TraceGrayRoots(JSTracer* trc, JS::SliceBudget& budget, void* data) {
  TraceRootArrays(trc, gc::MarkColor::Gray);
  return true;
}

static inline JSString* NewStringCopyUTF8(JSContext* cx, const char* chars) {
  return JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(chars, strlen(chars)));
}

static mozilla::UniqueFreePtr<char[]> GetLine(FILE* file, const char* prompt) {
#ifdef EDITLINE
  /*
   * Use readline only if file is stdin, because there's no way to specify
   * another handle.  Are other filehandles interactive?
   */

  if (file == stdin) {
    mozilla::UniqueFreePtr<char[]> linep(readline(prompt));
    /*
     * We set it to zero to avoid complaining about inappropriate ioctl
     * for device in the case of EOF. Looks like errno == 251 if line is
     * finished with EOF and errno == 25 (EINVAL on Mac) if there is
     * nothing left to read.
     */

    if (errno == 251 || errno == 25 || errno == EINVAL) {
      errno = 0;
    }
    if (!linep) {
      return nullptr;
    }
    if (linep[0] != '\0') {
      add_history(linep.get());
    }
    return linep;
  }
#endif

  size_t len = 0;
  if (*prompt != '\0' && gOutFile->isOpen()) {
    fprintf(gOutFile->fp, "%s", prompt);
    fflush(gOutFile->fp);
  }

  size_t size = 80;
  mozilla::UniqueFreePtr<char[]> buffer(static_cast<char*>(malloc(size)));
  if (!buffer) {
    return nullptr;
  }

  char* current = buffer.get();
  do {
    while (true) {
      if (fgets(current, size - len, file)) {
        break;
      }
      if (errno != EINTR) {
        return nullptr;
      }
    }

    len += strlen(current);
    char* t = buffer.get() + len - 1;
    if (*t == '\n') {
      /* Line was read. We remove '\n' and exit. */
      *t = '\0';
      break;
    }

    if (len + 1 == size) {
      size = size * 2;
      char* raw = buffer.release();
      char* tmp = static_cast<char*>(realloc(raw, size));
      if (!tmp) {
        free(raw);
        return nullptr;
      }
      buffer.reset(tmp);
    }
    current = buffer.get() + len;
  } while (true);
  return buffer;
}

static bool ShellInterruptCallback(JSContext* cx) {
  ShellContext* sc = GetShellContext(cx);
  if (!sc->serviceInterrupt) {
    return true;
  }

  // Reset serviceInterrupt. CancelExecution or InterruptIf will set it to
  // true to distinguish watchdog or user triggered interrupts.
  // Do this first to prevent other interrupts that may occur while the
  // user-supplied callback is executing from re-entering the handler.
  sc->serviceInterrupt = false;

  bool result;
  if (sc->haveInterruptFunc) {
    bool wasAlreadyThrowing = cx->isExceptionPending();
    JS::AutoSaveExceptionState savedExc(cx);
    JSAutoRealm ar(cx, &sc->interruptFunc.toObject());
    RootedValue rval(cx);

    // Report any exceptions thrown by the JS interrupt callback, but do
    // *not* keep it on the cx. The interrupt handler is invoked at points
    // that are not expected to throw catchable exceptions, like at
    // JSOp::RetRval.
    //
    // If the interrupted JS code was already throwing, any exceptions
    // thrown by the interrupt handler are silently swallowed.
    {
      Maybe<AutoReportException> are;
      if (!wasAlreadyThrowing) {
        are.emplace(cx);
      }
      result = JS_CallFunctionValue(cx, nullptr, sc->interruptFunc,
                                    JS::HandleValueArray::empty(), &rval);
    }
    savedExc.restore();

    if (rval.isBoolean()) {
      result = rval.toBoolean();
    } else {
      result = false;
    }
  } else {
    result = false;
  }

  if (!result && sc->exitCode == 0) {
    static const char msg[] = "Script terminated by interrupt handler.\n";
    fputs(msg, stderr);

    sc->exitCode = EXITCODE_TIMEOUT;
  }

  return result;
}

static void GCSliceCallback(JSContext* cx, JS::GCProgress progress,
                            const JS::GCDescription& desc) {
  if (progress == JS::GC_CYCLE_END) {
#if defined(MOZ_MEMORY)
    // We call this here to match the browser's DOMGCSliceCallback.
    jemalloc_free_dirty_pages();
#endif
  }
}

/*
 * Some UTF-8 files, notably those written using Notepad, have a Unicode
 * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order
 * is meaningless for UTF-8) but causes a syntax error unless we skip it.
 */

static void SkipUTF8BOM(FILE* file) {
  int ch1 = fgetc(file);
  int ch2 = fgetc(file);
  int ch3 = fgetc(file);

  // Skip the BOM
  if (ch1 == 0xEF && ch2 == 0xBB && ch3 == 0xBF) {
    return;
  }

  // No BOM - revert
  if (ch3 != EOF) {
    ungetc(ch3, file);
  }
  if (ch2 != EOF) {
    ungetc(ch2, file);
  }
  if (ch1 != EOF) {
    ungetc(ch1, file);
  }
}

void EnvironmentPreparer::invoke(HandleObject global, Closure& closure) {
  MOZ_ASSERT(JS_IsGlobalObject(global));

  JSContext* cx = TlsContext.get();
  MOZ_ASSERT(!JS_IsExceptionPending(cx));

  AutoRealm ar(cx, global);
  AutoReportException are(cx);
  if (!closure(cx)) {
    return;
  }
}

static bool RegisterScriptPathWithModuleLoader(JSContext* cx,
                                               HandleScript script,
                                               const char* filename) {
  // Set the private value associated with a script to a object containing the
  // script's filename so that the module loader can use it to resolve
  // relative imports.

  RootedString path(cx, NewStringCopyUTF8(cx, filename));
  if (!path) {
    return false;
  }

  MOZ_ASSERT(JS::GetScriptPrivate(script).isUndefined());
  RootedObject infoObject(cx, js::CreateScriptPrivate(cx, path));
  if (!infoObject) {
    return false;
  }

  JS::SetScriptPrivate(script, ObjectValue(*infoObject));
  return true;
}

enum class CompileUtf8 {
  InflateToUtf16,
  DontInflate,
};

[[nodiscard]] static bool RunFile(JSContext* cx, const char* filename,
                                  FILE* file, CompileUtf8 compileMethod,
                                  bool compileOnly, bool fullParse) {
  SkipUTF8BOM(file);

  int64_t t1 = PRMJ_Now();
  RootedScript script(cx);

  if (!filename) filename = "-";

  {
    CompileOptions options(cx);
    options.setIntroductionType("js shell file")
        .setFileAndLine(filename, 1)
        .setIsRunOnce(true)
        .setNoScriptRval(true);

    if (fullParse) {
      options.setForceFullParse();
    } else {
      options.setEagerDelazificationStrategy(defaultDelazificationMode);
    }

    if (compileMethod == CompileUtf8::DontInflate) {
      script = JS::CompileUtf8File(cx, options, file);
    } else {
      fprintf(stderr, "(compiling '%s' after inflating to UTF-16)\n", filename);

      FileContents buffer(cx);
      if (!ReadCompleteFile(cx, file, buffer)) {
        return false;
      }

      size_t length = buffer.length();
      auto chars = UniqueTwoByteChars(
          UTF8CharsToNewTwoByteCharsZ(
              cx,
              JS::UTF8Chars(reinterpret_cast<const char*>(buffer.begin()),
                            buffer.length()),
              &length, js::MallocArena)
              .get());
      if (!chars) {
        return false;
      }

      JS::SourceText<char16_t> source;
      if (!source.init(cx, std::move(chars), length)) {
        return false;
      }

      script = JS::Compile(cx, options, source);
    }

    if (!script) {
      return false;
    }
  }

  if (!RegisterScriptPathWithModuleLoader(cx, script, filename)) {
    return false;
  }

#ifdef DEBUG
  if (dumpEntrainedVariables) {
    AnalyzeEntrainedVariables(cx, script);
  }
#endif
  if (!compileOnly) {
    if (!JS_ExecuteScript(cx, script)) {
      return false;
    }
    int64_t t2 = PRMJ_Now() - t1;
    if (printTiming) {
      printf("runtime = %.3f ms\n"double(t2) / PRMJ_USEC_PER_MSEC);
    }
  }
  return true;
}

[[nodiscard]] static bool RunModule(JSContext* cx, const char* filename,
                                    bool compileOnly) {
  ShellContext* sc = GetShellContext(cx);

  RootedString path(cx, NewStringCopyUTF8(cx, filename));
  if (!path) {
    return false;
  }

  path = ResolvePath(cx, path, RootRelative);
  if (!path) {
    return false;
  }

  return sc->moduleLoader->loadRootModule(cx, path);
}

static void ShellCleanupFinalizationRegistryCallback(JSFunction* doCleanup,
                                                     JSObject* incumbentGlobal,
                                                     void* data) {
  // In the browser this queues a task. Shell jobs correspond to microtasks so
  // we arrange for cleanup to happen after all jobs/microtasks have run. The
  // incumbent global is ignored in the shell.

  auto sc = static_cast<ShellContext*>(data);
  AutoEnterOOMUnsafeRegion oomUnsafe;
  if (!sc->finalizationRegistryCleanupCallbacks.append(doCleanup)) {
    oomUnsafe.crash("ShellCleanupFinalizationRegistryCallback");
  }
}

// Run any FinalizationRegistry cleanup tasks and return whether any ran.
static bool MaybeRunFinalizationRegistryCleanupTasks(JSContext* cx) {
  ShellContext* sc = GetShellContext(cx);
  MOZ_ASSERT(!sc->quitting);

  Rooted<ShellContext::FunctionVector> callbacks(cx);
  std::swap(callbacks.get(), sc->finalizationRegistryCleanupCallbacks.get());

  bool ranTasks = false;

  RootedFunction callback(cx);
  for (JSFunction* f : callbacks) {
    callback = f;

    JS::ExposeObjectToActiveJS(callback);
    AutoRealm ar(cx, callback);

    {
      AutoReportException are(cx);
      RootedValue unused(cx);
      (void)JS_CallFunction(cx, nullptr, callback, HandleValueArray::empty(),
                            &unused);
    }

    ranTasks = true;

    if (sc->quitting) {
      break;
    }
  }

  return ranTasks;
}

static bool EnqueueJob(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  if (!IsFunctionObject(args.get(0))) {
    JS_ReportErrorASCII(cx, "EnqueueJob's first argument must be a function");
    return false;
  }

  args.rval().setUndefined();

  RootedObject job(cx, &args[0].toObject());
  return js::EnqueueJob(cx, job);
}

static void RunShellJobs(JSContext* cx) {
  ShellContext* sc = GetShellContext(cx);
  if (sc->quitting) {
    return;
  }

  while (true) {
    // Run microtasks.
    js::RunJobs(cx);
    if (sc->quitting) {
      return;
    }

    // Run tasks (only finalization registry clean tasks are possible).
    bool ranTasks = MaybeRunFinalizationRegistryCleanupTasks(cx);
    if (!ranTasks) {
      break;
    }
  }
}

static bool DrainJobQueue(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  if (GetShellContext(cx)->quitting) {
    JS_ReportErrorASCII(
        cx, "Mustn't drain the job queue when the shell is quitting");
    return false;
  }

  if (cx->isEvaluatingModule != 0) {
    JS_ReportErrorASCII(
        cx,
        "Can't drain the job queue when executing the top level of a module");
    return false;
  }

  RunShellJobs(cx);

  if (GetShellContext(cx)->quitting) {
    return false;
  }

  args.rval().setUndefined();
  return true;
}

static bool GlobalOfFirstJobInQueue(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  RootedObject job(cx, cx->internalJobQueue->maybeFront());
  if (!job) {
    JS_ReportErrorASCII(cx, "Job queue is empty");
    return false;
  }

  RootedObject global(cx, &job->nonCCWGlobal());
  if (!cx->compartment()->wrap(cx, &global)) {
    return false;
  }

  args.rval().setObject(*global);
  return true;
}

static bool TrackUnhandledRejections(JSContext* cx, JS::HandleObject promise,
                                     JS::PromiseRejectionHandlingState state) {
  ShellContext* sc = GetShellContext(cx);
  if (!sc->trackUnhandledRejections) {
    return true;
  }

#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
  if (cx->runningOOMTest) {
    // When OOM happens, we cannot reliably track the set of unhandled
    // promise rejections. Throw error only when simulated OOM is used
    // *and* promises are used in the test.
    JS_ReportErrorASCII(
        cx,
        "Can't track unhandled rejections while running simulated OOM "
        "test. Call ignoreUnhandledRejections before using oomTest etc.");
    return false;
  }
#endif

  if (!sc->unhandledRejectedPromises) {
    sc->unhandledRejectedPromises = SetObject::create(cx);
    if (!sc->unhandledRejectedPromises) {
      return false;
    }
  }

  RootedValue promiseVal(cx, ObjectValue(*promise));

  AutoRealm ar(cx, sc->unhandledRejectedPromises);
  if (!cx->compartment()->wrap(cx, &promiseVal)) {
    return false;
  }

  switch (state) {
    case JS::PromiseRejectionHandlingState::Unhandled:
      if (!sc->unhandledRejectedPromises->add(cx, promiseVal)) {
        return false;
      }
      break;
    case JS::PromiseRejectionHandlingState::Handled:
      bool deleted = false;
      if (!sc->unhandledRejectedPromises->delete_(cx, promiseVal, &deleted)) {
        return false;
      }
      // We can't MOZ_ASSERT(deleted) here, because it's possible we failed to
      // add the promise in the first place, due to OOM.
      break;
  }

  return true;
}

static void ForwardingPromiseRejectionTrackerCallback(
    JSContext* cx, bool mutedErrors, JS::HandleObject promise,
    JS::PromiseRejectionHandlingState state, void* data) {
  AutoReportException are(cx);

  if (!TrackUnhandledRejections(cx, promise, state)) {
    return;
  }

  RootedValue callback(cx,
                       GetShellContext(cx)->promiseRejectionTrackerCallback);
  if (callback.isNull()) {
    return;
  }

  AutoRealm ar(cx, &callback.toObject());

  FixedInvokeArgs<2> args(cx);
  args[0].setObject(*promise);
  args[1].setInt32(static_cast<int32_t>(state));

  if (!JS_WrapValue(cx, args[0])) {
    return;
  }

  RootedValue rval(cx);
  (void)Call(cx, callback, UndefinedHandleValue, args, &rval);
}

static bool SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc,
                                               Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  if (!IsFunctionObject(args.get(0))) {
    JS_ReportErrorASCII(
        cx,
        "setPromiseRejectionTrackerCallback expects a function as its sole "
        "argument");
    return false;
  }

  GetShellContext(cx)->promiseRejectionTrackerCallback = args[0];

  args.rval().setUndefined();
  return true;
}

// clang-format off
static const char* telemetryNames[static_cast<int>(JSMetric::Count)] = {
#define LIT(NAME, _) #NAME,
  FOR_EACH_JS_METRIC(LIT)
#undef LIT
};
// clang-format on

// Telemetry can be executed from multiple threads, and the callback is
// responsible to avoid contention on the recorded telemetry data.
static Mutex* telemetryLock = nullptr;
class MOZ_RAII AutoLockTelemetry : public LockGuard<Mutex> {
  using Base = LockGuard<Mutex>;

 public:
  AutoLockTelemetry() : Base(*telemetryLock) { MOZ_ASSERT(telemetryLock); }
};

using TelemetryData = uint32_t;
using TelemetryVec = Vector<TelemetryData, 0, SystemAllocPolicy>;
MOZ_RUNINIT static mozilla::Array<TelemetryVec, size_t(JSMetric::Count)>
    telemetryResults;
static void AccumulateTelemetryDataCallback(JSMetric id, uint32_t sample) {
  AutoLockTelemetry alt;
  // We ignore OOMs while writting teleemtry data.
  if (telemetryResults[static_cast<int>(id)].append(sample)) {
    return;
  }
}

static void WriteTelemetryDataToDisk(const char* dir) {
  const int pathLen = 260;
  char fileName[pathLen];
  Fprinter output;
  auto initOutput = [&](const char* name) -> bool {
    if (SprintfLiteral(fileName, "%s%s.csv", dir, name) >= pathLen) {
      return false;
    }
    FILE* file = fopen(fileName, "a");
    if (!file) {
      return false;
    }
    output.init(file);
    return true;
  };

  for (size_t id = 0; id < size_t(JSMetric::Count); id++) {
    auto clear = MakeScopeExit([&] { telemetryResults[id].clearAndFree(); });
    if (!initOutput(telemetryNames[id])) {
      continue;
    }
    for (uint32_t data : telemetryResults[id]) {
      output.printf("%u\n", data);
    }
    output.finish();
  }
}

#undef MAP_TELEMETRY

// Use Counter introspection
MOZ_RUNINIT static Mutex useCounterLock(mutexid::ShellUseCounters);
class MOZ_RAII AutoLockUseCounters : public LockGuard<Mutex> {
  using Base = LockGuard<Mutex>;

 public:
  AutoLockUseCounters() : Base(useCounterLock) {}
};

using UseCounterArray =
    mozilla::Array<uint32_t, static_cast<size_t>(JSUseCounter::COUNT)>;
static UseCounterArray useCounterResults;
static void SetUseCounterCallback(JSObject* obj, JSUseCounter counter) {
  MOZ_RELEASE_ASSERT(obj);
  AutoLockUseCounters aluc;
  // Maybe should ensure obj is a global object?
  useCounterResults[static_cast<size_t>(counter)]++;
}

static bool GetUseCounterResults(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
  if (!obj) {
    return false;
  }

  // Make a private copy holding the lock then release, because we can't
  // hold this mutex while doing JS_DefineProperty, which holds MemoryTracker
  // mutex.
  UseCounterArray local;
  {
    AutoLockUseCounters aluc;
    local = useCounterResults;
  }

  RootedValue val(cx);
#define ADD_VALUE(ENUM, NAME)                                      \
  val.setInt32(local[static_cast<size_t>(JSUseCounter::ENUM)]);    \
  if (!JS_DefineProperty(cx, obj, #NAME, val, JSPROP_ENUMERATE)) { \
    return false;                                                  \
  }

  FOR_EACH_JS_USE_COUNTER(ADD_VALUE);

  args.rval().setObject(*obj);
  return true;
}

static bool BoundToAsyncStack(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  RootedValue function(cx, GetFunctionNativeReserved(&args.callee(), 0));
  RootedObject options(
      cx, &GetFunctionNativeReserved(&args.callee(), 1).toObject());

  Rooted<SavedFrame*> stack(cx, nullptr);
  bool isExplicit;

  RootedValue v(cx);

  if (!JS_GetProperty(cx, options, "stack", &v)) {
    return false;
  }
  if (!v.isObject() || !v.toObject().is<SavedFrame>()) {
    JS_ReportErrorASCII(cx,
                        "The 'stack' property must be a SavedFrame object.");
    return false;
  }
  stack = &v.toObject().as<SavedFrame>();

  if (!JS_GetProperty(cx, options, "cause", &v)) {
    return false;
  }
  RootedString causeString(cx, ToString(cx, v));
  if (!causeString) {
    return false;
  }

  UniqueChars cause = JS_EncodeStringToUTF8(cx, causeString);
  if (!cause) {
    MOZ_ASSERT(cx->isExceptionPending());
    return false;
  }

  if (!JS_GetProperty(cx, options, "explicit", &v)) {
    return false;
  }
  isExplicit = v.isUndefined() ? true : ToBoolean(v);

  auto kind =
      (isExplicit ? JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT
                  : JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::IMPLICIT);

  JS::AutoSetAsyncStackForNewCalls asasfnckthxbye(cx, stack, cause.get(), kind);
  return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
              args.rval());
}

static bool BindToAsyncStack(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  if (args.length() != 2) {
    JS_ReportErrorASCII(cx, "bindToAsyncStack takes exactly two arguments.");
    return false;
  }

  if (!args[0].isObject() || !IsCallable(args[0])) {
    JS_ReportErrorASCII(
        cx, "bindToAsyncStack's first argument should be a function.");
    return false;
  }

  if (!args[1].isObject()) {
    JS_ReportErrorASCII(
        cx, "bindToAsyncStack's second argument should be an object.");
    return false;
  }

  RootedFunction bound(cx, NewFunctionWithReserved(cx, BoundToAsyncStack, 0, 0,
                                                   "bindToAsyncStack thunk"));
  if (!bound) {
    return false;
  }
  SetFunctionNativeReserved(bound, 0, args[0]);
  SetFunctionNativeReserved(bound, 1, args[1]);

  args.rval().setObject(*bound);
  return true;
}

#ifdef JS_HAS_INTL_API
static bool AddIntlExtras(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  if (!args.get(0).isObject()) {
    JS_ReportErrorASCII(cx, "addIntlExtras must be passed an object");
    return false;
  }
  JS::RootedObject intl(cx, &args[0].toObject());

  static const JSFunctionSpec funcs[] = {
      JS_SELF_HOSTED_FN("getCalendarInfo""Intl_getCalendarInfo", 1, 0),
      JS_FS_END,
  };

  if (!JS_DefineFunctions(cx, intl, funcs)) {
    return false;
  }

  if (!JS::AddMozDateTimeFormatConstructor(cx, intl)) {
    return false;
  }

  if (!JS::AddMozDisplayNamesConstructor(cx, intl)) {
    return false;
  }

  args.rval().setUndefined();
  return true;
}
#endif  // JS_HAS_INTL_API

[[nodiscard]] static bool EvalUtf8AndPrint(JSContext* cx, const char* bytes,
                                           size_t length, int lineno,
                                           bool compileOnly) {
  // Eval.
  JS::CompileOptions options(cx);
  options.setIntroductionType("js shell interactive")
      .setIsRunOnce(true)
      .setFileAndLine("typein", lineno)
      .setEagerDelazificationStrategy(defaultDelazificationMode);

  JS::SourceText<Utf8Unit> srcBuf;
  if (!srcBuf.init(cx, bytes, length, JS::SourceOwnership::Borrowed)) {
    return false;
  }

  RootedScript script(cx, JS::Compile(cx, options, srcBuf));
  if (!script) {
    return false;
  }
  if (compileOnly) {
    return true;
  }
  RootedValue result(cx);
  if (!JS_ExecuteScript(cx, script, &result)) {
    return false;
  }

  if (!result.isUndefined() && gOutFile->isOpen()) {
    // Print.
    RootedString str(cx, JS_ValueToSource(cx, result));
    if (!str) {
      return false;
    }

    UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
    if (!utf8chars) {
      return false;
    }
    fprintf(gOutFile->fp, "%s\n", utf8chars.get());
  }
  return true;
}

[[nodiscard]] static bool ReadEvalPrintLoop(JSContext* cx, FILE* in,
                                            bool compileOnly) {
  ShellContext* sc = GetShellContext(cx);
  int lineno = 1;
  bool hitEOF = false;

  do {
    /*
     * Accumulate lines until we get a 'compilable unit' - one that either
     * generates an error (before running out of source) or that compiles
     * cleanly.  This should be whenever we get a complete statement that
     * coincides with the end of a line.
     */

    int startline = lineno;
    using CharBuffer = Vector<char, 32>;
    RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
    CharBuffer buffer(cx);
    do {
      ScheduleWatchdog(cx, -1);
      sc->serviceInterrupt = false;
      errno = 0;

      mozilla::UniqueFreePtr<char[]> line =
          GetLine(in, startline == lineno ? "js> " : "");
      if (!line) {
        if (errno) {
          if (UniqueChars error = SystemErrorMessage(cx, errno)) {
            JS_ReportErrorUTF8(cx, "%s", error.get());
          }
          return false;
        }
        hitEOF = true;
        break;
      }

      if (!buffer.append(line.get(), strlen(line.get())) ||
          !buffer.append('\n')) {
        return false;
      }

      lineno++;
      if (!ScheduleWatchdog(cx, sc->timeoutInterval)) {
        hitEOF = true;
        break;
      }
    } while (!JS_Utf8BufferIsCompilableUnit(cx, cx->global(), buffer.begin(),
                                            buffer.length()));

    if (hitEOF && buffer.empty()) {
      break;
    }

    {
      // Report exceptions but keep going.
      AutoReportException are(cx);
      (void)EvalUtf8AndPrint(cx, buffer.begin(), buffer.length(), startline,
                             compileOnly);
    }

    // If a let or const fail to initialize they will remain in an unusable
    // without further intervention. This call cleans up the global scope,
    // setting uninitialized lexicals to undefined so that they may still
    // be used. This behavior is _only_ acceptable in the context of the repl.
    if (JS::ForceLexicalInitialization(cx, globalLexical) &&
        gErrFile->isOpen()) {
      fputs(
          "Warning: According to the standard, after the above exception,\n"
          "Warning: the global bindings should be permanently uninitialized.\n"
          "Warning: We have non-standard-ly initialized them to `undefined`"
          "for you.\nWarning: This nicety only happens in the JS shell.\n",
          stderr);
    }

    RunShellJobs(cx);
  } while (!hitEOF && !sc->quitting);

  if (gOutFile->isOpen()) {
    fprintf(gOutFile->fp, "\n");
  }

  return true;
}

enum FileKind {
  PreludeScript,    // UTF-8 script, fully-parsed, to avoid conflicting
                    // configurations.
  FileScript,       // UTF-8, directly parsed as such
  FileScriptUtf16,  // FileScript, but inflate to UTF-16 before parsing
  FileModule,
};

[[nodiscard]] static bool Process(JSContext* cx, const char* filename,
                                  bool forceTTY, FileKind kind) {
  FILE* file;
  if (forceTTY || !filename || strcmp(filename, "-") == 0) {
    file = stdin;
  } else {
    file = OpenFile(cx, filename, "rb");
    if (!file) {
      return false;
    }
  }
  AutoCloseFile autoClose(file);

  bool fullParse = false;
  if (!forceTTY && !isatty(fileno(file))) {
    // It's not interactive - just execute it.
    switch (kind) {
      case PreludeScript:
        fullParse = true;
        if (!RunFile(cx, filename, file, CompileUtf8::DontInflate, compileOnly,
                     fullParse)) {
          return false;
        }
        break;
      case FileScript:
        if (!RunFile(cx, filename, file, CompileUtf8::DontInflate, compileOnly,
                     fullParse)) {
          return false;
        }
        break;
      case FileScriptUtf16:
        if (!RunFile(cx, filename, file, CompileUtf8::InflateToUtf16,
                     compileOnly, fullParse)) {
          return false;
        }
        break;
      case FileModule:
        if (!RunModule(cx, filename, compileOnly)) {
          return false;
        }
        break;
      default:
        MOZ_CRASH("Impossible FileKind!");
    }
  } else {
    // It's an interactive filehandle; drop into read-eval-print loop.
    MOZ_ASSERT(kind == FileScript);
    if (!ReadEvalPrintLoop(cx, file, compileOnly)) {
      return false;
    }
  }
#ifdef FUZZING_JS_FUZZILLI
  fprintf(stderr, "executionHash is 0x%x with %d inputs\n", cx->executionHash,
          cx->executionHashInputs);
#endif
  return true;
}

#ifdef XP_WIN
#  define GET_FD_FROM_FILE(a) int(_get_osfhandle(fileno(a)))
#else
#  define GET_FD_FROM_FILE(a) fileno(a)
#endif

static void freeExternalCallback(void* contents, void* userData) {
  MOZ_ASSERT(!userData);
  js_free(contents);
}

static bool CreateExternalArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  if (args.length() != 1) {
    JS_ReportErrorNumberASCII(
        cx, my_GetErrorMessage, nullptr,
        args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
        "createExternalArrayBuffer");
    return false;
  }

  int32_t bytes = 0;
  if (!ToInt32(cx, args[0], &bytes)) {
    return false;
  }

  if (bytes < 0) {
    JS_ReportErrorASCII(cx, "Size must be non-negative");
    return false;
  }

  void* buffer = js_calloc(bytes);
  if (!buffer) {
    JS_ReportOutOfMemory(cx);
    return false;
  }

  UniquePtr<void, JS::BufferContentsDeleter> ptr{buffer,
                                                 {&freeExternalCallback}};
  RootedObject arrayBuffer(
      cx, JS::NewExternalArrayBuffer(cx, bytes, std::move(ptr)));
  if (!arrayBuffer) {
    return false;
  }

  args.rval().setObject(*arrayBuffer);
  return true;
}

static bool CreateMappedArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  if (args.length() < 1 || args.length() > 3) {
    JS_ReportErrorNumberASCII(
        cx, my_GetErrorMessage, nullptr,
        args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
        "createMappedArrayBuffer");
    return false;
  }

  RootedString rawFilenameStr(cx, JS::ToString(cx, args[0]));
  if (!rawFilenameStr) {
    return false;
  }
  // It's a little bizarre to resolve relative to the script, but for testing
  // I need a file at a known location, and the only good way I know of to do
  // that right now is to include it in the repo alongside the test script.
  // Bug 944164 would introduce an alternative.
  Rooted<JSString*> filenameStr(
      cx, ResolvePath(cx, rawFilenameStr, ScriptRelative));
  if (!filenameStr) {
    return false;
  }
  UniqueChars filename = JS_EncodeStringToUTF8(cx, filenameStr);
  if (!filename) {
    return false;
  }

  uint32_t offset = 0;
  if (args.length() >= 2) {
    if (!JS::ToUint32(cx, args[1], &offset)) {
      return false;
    }
  }

  bool sizeGiven = false;
  uint32_t size;
  if (args.length() >= 3) {
    if (!JS::ToUint32(cx, args[2], &size)) {
      return false;
    }
    sizeGiven = true;
    if (size == 0) {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_BAD_ARRAY_LENGTH);
      return false;
    }
  }

  FILE* file = OpenFile(cx, filename.get(), "rb");
  if (!file) {
    return false;
  }
  AutoCloseFile autoClose(file);

  struct stat st;
  if (fstat(fileno(file), &st) < 0) {
    JS_ReportErrorASCII(cx, "Unable to stat file");
    return false;
  }

  if ((st.st_mode & S_IFMT) != S_IFREG) {
    JS_ReportErrorASCII(cx, "Path is not a regular file");
    return false;
  }

  if (!sizeGiven) {
    if (off_t(offset) >= st.st_size) {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_OFFSET_LARGER_THAN_FILESIZE);
      return false;
    }
    size = st.st_size - offset;
  }

  void* contents =
      JS::CreateMappedArrayBufferContents(GET_FD_FROM_FILE(file), offset, size);
  if (!contents) {
    JS_ReportErrorASCII(cx,
                        "failed to allocate mapped array buffer contents "
                        "(possibly due to bad alignment)");
    return false;
  }

  RootedObject obj(cx,
                   JS::NewMappedArrayBufferWithContents(cx, size, contents));
  if (!obj) {
    return false;
  }

  args.rval().setObject(*obj);
  return true;
}

#undef GET_FD_FROM_FILE

class UserBufferObject : public NativeObject {
  static const uint32_t BUFFER_SLOT = 0;
  static const uint32_t BYTE_LENGTH_SLOT = 1;
  static const uint32_t RESERVED_SLOTS = 2;

  static constexpr auto BufferMemoryUse = MemoryUse::Embedding1;

  static void finalize(JS::GCContext* gcx, JSObject* obj);

 public:
  static const JSClassOps classOps_;
  static const JSClass class_;

  [[nodiscard]] static UserBufferObject* create(JSContext* cx,
                                                size_t byteLength);

  void* buffer() const {
    auto& buffer = getReservedSlot(BUFFER_SLOT);
    if (buffer.isUndefined()) {
      return nullptr;
    }
    return buffer.toPrivate();
  }

  size_t byteLength() const {
    return size_t(getReservedSlot(BYTE_LENGTH_SLOT).toPrivate());
  }
};

const JSClassOps UserBufferObject::classOps_ = {
    nullptr,                     // addProperty
    nullptr,                     // delProperty
    nullptr,                     // enumerate
    nullptr,                     // newEnumerate
    nullptr,                     // resolve
    nullptr,                     // mayResolve
    UserBufferObject::finalize,  // finalize
    nullptr,                     // call
    nullptr,                     // construct
    nullptr,                     // trace
};

const JSClass UserBufferObject::class_ = {
    "UserBufferObject",
    JSCLASS_HAS_RESERVED_SLOTS(UserBufferObject::RESERVED_SLOTS) |
        JSCLASS_BACKGROUND_FINALIZE,
    &UserBufferObject::classOps_,
};

UserBufferObject* UserBufferObject::create(JSContext* cx, size_t byteLength) {
  void* buffer = js_calloc(byteLength);
  if (!buffer) {
    JS_ReportOutOfMemory(cx);
    return nullptr;
  }
  UniquePtr<void, JS::FreePolicy> ptr(buffer);

  auto* userBuffer = NewObjectWithGivenProto<UserBufferObject>(cx, nullptr);
  if (!userBuffer) {
    return nullptr;
  }

  InitReservedSlot(userBuffer, BUFFER_SLOT, ptr.release(), byteLength,
                   BufferMemoryUse);
  userBuffer->initReservedSlot(BYTE_LENGTH_SLOT, PrivateValue(byteLength));

  return userBuffer;
}

void UserBufferObject::finalize(JS::GCContext* gcx, JSObject* obj) {
  auto* userBuffer = &obj->as<UserBufferObject>();
  if (auto* buffer = userBuffer->buffer()) {
    gcx->free_(userBuffer, buffer, userBuffer->byteLength(), BufferMemoryUse);
  }
}

static bool CreateUserArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  if (args.length() != 1) {
    JS_ReportErrorNumberASCII(
        cx, my_GetErrorMessage, nullptr,
        args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
        "createUserArrayBuffer");
    return false;
  }

  int32_t bytes = 0;
  if (!ToInt32(cx, args[0], &bytes)) {
    return false;
  }
  if (bytes < 0) {
    JS_ReportErrorASCII(cx, "Size must be non-negative");
    return false;
  }

  Rooted<UserBufferObject*> userBuffer(cx, UserBufferObject::create(cx, bytes));
  if (!userBuffer) {
    return false;
  }

  Rooted<JSObject*> arrayBuffer(
      cx, JS::NewArrayBufferWithUserOwnedContents(cx, userBuffer->byteLength(),
                                                  userBuffer->buffer()));
  if (!arrayBuffer) {
    return false;
  }

--> --------------------

--> maximum size reached

--> --------------------

93%


¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.62Bemerkung:  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

letze Version des Elbe Quellennavigators

     letzte wissenschaftliche Artikel weltweit
     Neues von dieser Firma

letze Version des Agenda Kalenders

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

letze Version der Autor Authoringsoftware

     letze Version des Demonstrationsprogramms Goedel
     letze Version des Bille Abgleichprogramms
     Bilder

Jenseits des Üblichen ....

Besucher

Besucher

Monitoring

Montastic status badge