Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/vm/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 42 kB image not shown  

Quelle  JSContext.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 execution context.
 */


#include "vm/JSContext-inl.h"

#include "mozilla/CheckedInt.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Sprintf.h"
#include "mozilla/Utf8.h"  // mozilla::ConvertUtf16ToUtf8

#include <string.h>
#ifdef ANDROID
#  include <android/log.h>
#  include <fstream>
#  include <string>
#endif  // ANDROID
#ifdef XP_WIN
#  include <processthreadsapi.h>
#endif  // XP_WIN

#include "jsapi.h"  // JS_SetNativeStackQuota
#include "jsexn.h"
#include "jstypes.h"

#include "builtin/RegExp.h"  // js::RegExpSearcherLastLimitSentinel
#ifdef MOZ_EXECUTION_TRACING
#  include "debugger/ExecutionTracer.h"
#endif
#include "frontend/FrontendContext.h"
#include "gc/GC.h"
#include "gc/PublicIterators.h"  // js::RealmsIter
#include "irregexp/RegExpAPI.h"
#include "jit/Simulator.h"
#include "js/CallAndConstruct.h"  // JS::Call
#include "js/CharacterEncoding.h"
#include "js/ContextOptions.h"        // JS::ContextOptions
#include "js/ErrorInterceptor.h"      // JSErrorInterceptor
#include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
#include "js/friend/StackLimits.h"    // js::ReportOverRecursed
#include "js/MemoryCallbacks.h"
#include "js/Prefs.h"
#include "js/Printf.h"
#include "js/PropertyAndElement.h"  // JS_GetProperty
#include "js/Stack.h"  // JS::NativeStackSize, JS::NativeStackLimit, JS::NativeStackLimitMin
#include "util/DiagnosticAssertions.h"
#include "util/DifferentialTesting.h"
#include "util/DoubleToString.h"
#include "util/NativeStack.h"
#include "util/Text.h"
#include "util/WindowsWrapper.h"
#include "vm/BytecodeUtil.h"  // JSDVG_IGNORE_STACK
#include "vm/ErrorObject.h"
#include "vm/ErrorReporting.h"
#include "vm/FrameIter.h"
#include "vm/JSFunction.h"
#include "vm/JSObject.h"
#include "vm/PlainObject.h"  // js::PlainObject
#include "vm/Realm.h"
#include "vm/StringType.h"  // StringToNewUTF8CharsZ
#include "vm/ToSource.h"    // js::ValueToSource

#include "vm/Compartment-inl.h"
#include "vm/Stack-inl.h"

using namespace js;

#ifdef DEBUG
JSContext* js::MaybeGetJSContext() {
  if (!TlsContext.init()) {
    return nullptr;
  }
  return TlsContext.get();
}
#endif

bool js::AutoCycleDetector::init() {
  MOZ_ASSERT(cyclic);

  AutoCycleDetector::Vector& vector = cx->cycleDetectorVector();

  for (JSObject* obj2 : vector) {
    if (MOZ_UNLIKELY(obj == obj2)) {
      return true;
    }
  }

  if (!vector.append(obj)) {
    return false;
  }

  cyclic = false;
  return true;
}

js::AutoCycleDetector::~AutoCycleDetector() {
  if (MOZ_LIKELY(!cyclic)) {
    AutoCycleDetector::Vector& vec = cx->cycleDetectorVector();
    MOZ_ASSERT(vec.back() == obj);
    if (vec.length() > 1) {
      vec.popBack();
    } else {
      // Avoid holding on to unused heap allocations.
      vec.clearAndFree();
    }
  }
}

bool JSContext::init() {
  TlsContext.set(this);
  nativeStackBase_.emplace(GetNativeStackBase());

  if (!fx.initInstance()) {
    return false;
  }

#ifdef JS_SIMULATOR
  simulator_ = jit::Simulator::Create();
  if (!simulator_) {
    return false;
  }
#endif

  isolate = irregexp::CreateIsolate(this);
  if (!isolate) {
    return false;
  }

#ifdef DEBUG
  // Set the initialized_ last, so that ProtectedData checks will allow us to
  // initialize this context before it becomes the runtime's active context.
  initialized_ = true;
#endif

  return true;
}

static void InitDefaultStackQuota(JSContext* cx) {
  // Initialize stack quota to a reasonable default. Embedders can override this
  // by calling JS_SetNativeStackQuota.
  //
  // NOTE: Firefox overrides these values. For the main thread this happens in
  // XPCJSContext::Initialize.

#if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN))
  static constexpr JS::NativeStackSize MaxStackSize =
      2 * 128 * sizeof(size_t) * 1024;
#else
  static constexpr JS::NativeStackSize MaxStackSize =
      128 * sizeof(size_t) * 1024;
#endif
  JS_SetNativeStackQuota(cx, MaxStackSize);
}

JSContext* js::NewContext(uint32_t maxBytes, JSRuntime* parentRuntime) {
  AutoNoteSingleThreadedRegion anstr;

  MOZ_RELEASE_ASSERT(!TlsContext.get());

#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
  js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_MAIN
                                        : js::THREAD_TYPE_WORKER);
#endif

  JSRuntime* runtime = js_new<JSRuntime>(parentRuntime);
  if (!runtime) {
    return nullptr;
  }

  JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
  if (!cx) {
    js_delete(runtime);
    return nullptr;
  }

  if (!cx->init()) {
    js_delete(cx);
    js_delete(runtime);
    return nullptr;
  }

  if (!runtime->init(cx, maxBytes)) {
    runtime->destroyRuntime();
    js_delete(cx);
    js_delete(runtime);
    return nullptr;
  }

  // Initialize stack quota last because simulators rely on the JSRuntime having
  // been initialized.
  InitDefaultStackQuota(cx);

  return cx;
}

void js::DestroyContext(JSContext* cx) {
  JS_AbortIfWrongThread(cx);

  MOZ_ASSERT(!cx->realm(), "Shouldn't destroy context with active realm");
  MOZ_ASSERT(!cx->activation(), "Shouldn't destroy context with activations");

  cx->checkNoGCRooters();

  // Cancel all off thread compiles. Completed compiles may try to
  // interrupt this context. See HelperThread::handleIonWorkload.
  CancelOffThreadCompile(cx->runtime());

  cx->jobQueue = nullptr;
  cx->internalJobQueue = nullptr;
  SetContextProfilingStack(cx, nullptr);

  JSRuntime* rt = cx->runtime();

  // Flush promise tasks executing in helper threads early, before any parts
  // of the JSRuntime that might be visible to helper threads are torn down.
  rt->offThreadPromiseState.ref().shutdown(cx);

  // Destroy the runtime along with its last context.
  js::AutoNoteSingleThreadedRegion nochecks;
  rt->destroyRuntime();
  js_delete_poison(cx);
  js_delete_poison(rt);
}

void JS::RootingContext::checkNoGCRooters() {
#ifdef DEBUG
  for (auto const& stackRootPtr : stackRoots_) {
    MOZ_ASSERT(stackRootPtr == nullptr);
  }
#endif
}

bool AutoResolving::alreadyStartedSlow() const {
  MOZ_ASSERT(link);
  AutoResolving* cursor = link;
  do {
    MOZ_ASSERT(this != cursor);
    if (object.get() == cursor->object && id.get() == cursor->id) {
      return true;
    }
  } while (!!(cursor = cursor->link));
  return false;
}

static void MaybeReportOutOfMemoryForDifferentialTesting() {
  /*
   * OOMs are non-deterministic, especially across different execution modes
   * (e.g. interpreter vs JIT). When doing differential testing, print to stderr
   * so that the fuzzers can detect this.
   */

  if (js::SupportDifferentialTesting()) {
    fprintf(stderr, "ReportOutOfMemory called\n");
  }
}

/*
 * Since memory has been exhausted, avoid the normal error-handling path which
 * allocates an error object, report and callstack. Instead simply throw the
 * static atom "out of memory".
 *
 * Furthermore, callers of ReportOutOfMemory (viz., malloc) assume a GC does
 * not occur, so GC must be avoided or suppressed.
 */

void JSContext::onOutOfMemory() {
  runtime()->hadOutOfMemory = true;
  gc::AutoSuppressGC suppressGC(this);

  /* Report the oom. */
  if (JS::OutOfMemoryCallback oomCallback = runtime()->oomCallback) {
    oomCallback(this, runtime()->oomCallbackData);
  }

  // If we OOM early in process startup, this may be unavailable so just return
  // instead of crashing unexpectedly.
  if (MOZ_UNLIKELY(!runtime()->hasInitializedSelfHosting())) {
    return;
  }

  RootedValue oomMessage(this, StringValue(names().out_of_memory_));
  setPendingException(oomMessage, nullptr);
  MOZ_ASSERT(status == JS::ExceptionStatus::Throwing);
  status = JS::ExceptionStatus::OutOfMemory;

  reportResourceExhaustion();
}

JS_PUBLIC_API void js::ReportOutOfMemory(JSContext* cx) {
  MaybeReportOutOfMemoryForDifferentialTesting();

  cx->onOutOfMemory();
}

JS_PUBLIC_API void js::ReportLargeOutOfMemory(JSContext* cx) {
  js::ReportOutOfMemory(cx);
}

JS_PUBLIC_API void js::ReportOutOfMemory(FrontendContext* fc) {
  MaybeReportOutOfMemoryForDifferentialTesting();

  fc->onOutOfMemory();
}

static void MaybeReportOverRecursedForDifferentialTesting() {
  /*
   * We cannot make stack depth deterministic across different
   * implementations (e.g. JIT vs. interpreter will differ in
   * their maximum stack depth).
   * However, we can detect externally when we hit the maximum
   * stack depth which is useful for external testing programs
   * like fuzzers.
   */

  if (js::SupportDifferentialTesting()) {
    fprintf(stderr, "ReportOverRecursed called\n");
  }
}

void JSContext::onOverRecursed() {
  // Try to construct an over-recursed error and then update the exception
  // status to `OverRecursed`. Creating the error can fail, so check there
  // is a reasonable looking exception pending before updating status.
  JS_ReportErrorNumberASCII(this, GetErrorMessage, nullptr,
                            JSMSG_OVER_RECURSED);
  if (isExceptionPending() && !isThrowingOutOfMemory()) {
    MOZ_ASSERT(unwrappedException().isObject());
    MOZ_ASSERT(status == JS::ExceptionStatus::Throwing);
    status = JS::ExceptionStatus::OverRecursed;
  }

  reportResourceExhaustion();
}

JS_PUBLIC_API void js::ReportOverRecursed(JSContext* maybecx) {
  MaybeReportOverRecursedForDifferentialTesting();

  if (!maybecx) {
    return;
  }

  maybecx->onOverRecursed();
}

JS_PUBLIC_API void js::ReportOverRecursed(FrontendContext* fc) {
  MaybeReportOverRecursedForDifferentialTesting();

  fc->onOverRecursed();
}

void js::ReportOversizedAllocation(JSContext* cx, const unsigned errorNumber) {
  // The JIT may optimize away allocations if it determines that they aren't
  // used. This can affect whether we throw an exception when the size of an
  // allocation exceeds implementation-defined limits (eg JSString::MAX_LENGTH).
  // These errors aren't interesting for the purposes of differential fuzzing.
  // We print a message so that fuzzers can detect this case. To simplify
  // tooling updates, we use the same message as ReportOutOfMemory.
  if (js::SupportDifferentialTesting()) {
    fprintf(stderr, "ReportOutOfMemory called\n");
  }

  gc::AutoSuppressGC suppressGC(cx);
  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);

  cx->reportResourceExhaustion();
}

void js::ReportAllocationOverflow(JSContext* cx) {
  if (js::SupportDifferentialTesting()) {
    fprintf(stderr, "ReportAllocationOverflow called\n");
  }

  if (!cx) {
    return;
  }

  cx->reportAllocationOverflow();
}

void js::ReportAllocationOverflow(FrontendContext* fc) {
  fc->onAllocationOverflow();
}

/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
void js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee,
                               const char* msg) {
  RootedValue usage(cx);
  if (!JS_GetProperty(cx, callee, "usage", &usage)) {
    return;
  }

  if (!usage.isString()) {
    JS_ReportErrorASCII(cx, "%s", msg);
  } else {
    RootedString usageStr(cx, usage.toString());
    UniqueChars str = JS_EncodeStringToUTF8(cx, usageStr);
    if (!str) {
      return;
    }
    JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.get());
  }
}

enum class PrintErrorKind { Error, Warning, Note };

static void PrintErrorLine(FILE* file, const char* prefix,
                           JSErrorReport* report) {
  if (const char16_t* linebuf = report->linebuf()) {
    UniqueChars line;
    size_t n;
    {
      size_t linebufLen = report->linebufLength();

      // This function is only used for shell command-line sorts of stuff where
      // performance doesn't really matter, so just encode into max-sized
      // memory.
      mozilla::CheckedInt<size_t> utf8Len(linebufLen);
      utf8Len *= 3;
      if (utf8Len.isValid()) {
        line = UniqueChars(js_pod_malloc<char>(utf8Len.value()));
        if (line) {
          n = mozilla::ConvertUtf16toUtf8({linebuf, linebufLen},
                                          {line.get(), utf8Len.value()});
        }
      }
    }

    const char* utf8buf;
    if (line) {
      utf8buf = line.get();
    } else {
      static const char unavailableStr[] = "";
      utf8buf = unavailableStr;
      n = js_strlen(unavailableStr);
    }

    fputs(":\n", file);
    if (prefix) {
      fputs(prefix, file);
    }

    for (size_t i = 0; i < n; i++) {
      fputc(utf8buf[i], file);
    }

    // linebuf/utf8buf usually ends with a newline. If not, add one here.
    if (n == 0 || utf8buf[n - 1] != '\n') {
      fputc('\n', file);
    }

    if (prefix) {
      fputs(prefix, file);
    }

    n = report->tokenOffset();
    for (size_t i = 0, j = 0; i < n; i++) {
      if (utf8buf[i] == '\t') {
        for (size_t k = (j + 8) & ~7; j < k; j++) {
          fputc('.', file);
        }
        continue;
      }
      fputc('.', file);
      j++;
    }
    fputc('^', file);
  }
}

static void PrintErrorLine(FILE* file, const char* prefix,
                           JSErrorNotes::Note* note) {}

template <typename T>
static void PrintSingleError(FILE* file, JS::ConstUTF8CharsZ toStringResult,
                             T* report, PrintErrorKind kind) {
  UniqueChars prefix;
  if (report->filename) {
    prefix = JS_smprintf("%s:", report->filename.c_str());
  }

  if (report->lineno) {
    prefix = JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno,
                         report->column.oneOriginValue());
  }

  if (kind != PrintErrorKind::Error) {
    const char* kindPrefix = nullptr;
    switch (kind) {
      case PrintErrorKind::Error:
        MOZ_CRASH("unreachable");
      case PrintErrorKind::Warning:
        kindPrefix = "warning";
        break;
      case PrintErrorKind::Note:
        kindPrefix = "note";
        break;
    }

    prefix = JS_smprintf("%s%s: ", prefix ? prefix.get() : "", kindPrefix);
  }

  const char* message =
      toStringResult ? toStringResult.c_str() : report->message().c_str();

  /* embedded newlines -- argh! */
  const char* ctmp;
  while ((ctmp = strchr(message, '\n')) != 0) {
    ctmp++;
    if (prefix) {
      fputs(prefix.get(), file);
    }
    (void)fwrite(message, 1, ctmp - message, file);
    message = ctmp;
  }

  /* If there were no filename or lineno, the prefix might be empty */
  if (prefix) {
    fputs(prefix.get(), file);
  }
  fputs(message, file);

  PrintErrorLine(file, prefix.get(), report);
  fputc('\n', file);

  fflush(file);
}

static void PrintErrorImpl(FILE* file, JS::ConstUTF8CharsZ toStringResult,
                           JSErrorReport* report, bool reportWarnings) {
  MOZ_ASSERT(report);

  /* Conditionally ignore reported warnings. */
  if (report->isWarning() && !reportWarnings) {
    return;
  }

  PrintErrorKind kind = PrintErrorKind::Error;
  if (report->isWarning()) {
    kind = PrintErrorKind::Warning;
  }
  PrintSingleError(file, toStringResult, report, kind);

  if (report->notes) {
    for (auto&& note : *report->notes) {
      PrintSingleError(file, JS::ConstUTF8CharsZ(), note.get(),
                       PrintErrorKind::Note);
    }
  }
}

JS_PUBLIC_API void JS::PrintError(FILE* file, JSErrorReport* report,
                                  bool reportWarnings) {
  PrintErrorImpl(file, JS::ConstUTF8CharsZ(), report, reportWarnings);
}

JS_PUBLIC_API void JS::PrintError(FILE* file,
                                  const JS::ErrorReportBuilder& builder,
                                  bool reportWarnings) {
  PrintErrorImpl(file, builder.toStringResult(), builder.report(),
                 reportWarnings);
}

void js::ReportIsNotDefined(JSContext* cx, HandleId id) {
  if (UniqueChars printable =
          IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) {
    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED,
                             printable.get());
  }
}

void js::ReportIsNotDefined(JSContext* cx, Handle<PropertyName*> name) {
  RootedId id(cx, NameToId(name));
  ReportIsNotDefined(cx, id);
}

const char* NullOrUndefinedToCharZ(HandleValue v) {
  MOZ_ASSERT(v.isNullOrUndefined());
  return v.isNull() ? "null" : "undefined";
}

void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v,
                                                  int vIndex) {
  MOZ_ASSERT(v.isNullOrUndefined());

  if (vIndex == JSDVG_IGNORE_STACK) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_CANT_CONVERT_TO, NullOrUndefinedToCharZ(v),
                              "object");
    return;
  }

  UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr);
  if (!bytes) {
    return;
  }

  if (strcmp(bytes.get(), "undefined") == 0 ||
      strcmp(bytes.get(), "null") == 0) {
    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
                             bytes.get());
  } else {
    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                             JSMSG_UNEXPECTED_TYPE, bytes.get(),
                             NullOrUndefinedToCharZ(v));
  }
}

void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v,
                                                  int vIndex, HandleId key) {
  MOZ_ASSERT(v.isNullOrUndefined());

  if (!JS::Prefs::property_error_message_fix()) {
    ReportIsNullOrUndefinedForPropertyAccess(cx, v, vIndex);
    return;
  }

  RootedValue idVal(cx, IdToValue(key));
  RootedString idStr(cx, ValueToSource(cx, idVal));
  if (!idStr) {
    return;
  }

  UniqueChars keyStr = StringToNewUTF8CharsZ(cx, *idStr);
  if (!keyStr) {
    return;
  }

  if (vIndex == JSDVG_IGNORE_STACK) {
    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
                             keyStr.get(), NullOrUndefinedToCharZ(v));
    return;
  }

  UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr);
  if (!bytes) {
    return;
  }

  if (strcmp(bytes.get(), "undefined") == 0 ||
      strcmp(bytes.get(), "null") == 0) {
    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
                             keyStr.get(), bytes.get());
    return;
  }

  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                           JSMSG_PROPERTY_FAIL_EXPR, keyStr.get(), bytes.get(),
                           NullOrUndefinedToCharZ(v));
}

bool js::ReportValueError(JSContext* cx, const unsigned errorNumber,
                          int spindex, HandleValue v, HandleString fallback,
                          const char* arg1, const char* arg2) {
  MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
  MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
  UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback);
  if (!bytes) {
    return false;
  }

  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
                           bytes.get(), arg1, arg2);
  return false;
}

JSObject* js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report) {
  Rooted<ArrayObject*> notesArray(cx, NewDenseEmptyArray(cx));
  if (!notesArray) {
    return nullptr;
  }

  if (!report->notes) {
    return notesArray;
  }

  for (auto&& note : *report->notes) {
    Rooted<PlainObject*> noteObj(cx, NewPlainObject(cx));
    if (!noteObj) {
      return nullptr;
    }

    RootedString messageStr(cx, note->newMessageString(cx));
    if (!messageStr) {
      return nullptr;
    }
    RootedValue messageVal(cx, StringValue(messageStr));
    if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal)) {
      return nullptr;
    }

    RootedValue filenameVal(cx);
    if (const char* filename = note->filename.c_str()) {
      JS::UTF8Chars utf8chars(filename, strlen(filename));
      Rooted<JSString*> filenameStr(cx, NewStringCopyUTF8N(cx, utf8chars));
      if (!filenameStr) {
        return nullptr;
      }
      filenameVal = StringValue(filenameStr);
    }
    if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal)) {
      return nullptr;
    }

    RootedValue linenoVal(cx, Int32Value(note->lineno));
    if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal)) {
      return nullptr;
    }
    RootedValue columnVal(cx, Int32Value(note->column.oneOriginValue()));
    if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal)) {
      return nullptr;
    }

    if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj))) {
      return nullptr;
    }
  }

  return notesArray;
}

void JSContext::recoverFromOutOfMemory() {
  if (isExceptionPending()) {
    MOZ_ASSERT(isThrowingOutOfMemory());
    clearPendingException();
  }
}

void JSContext::reportAllocationOverflow() {
  gc::AutoSuppressGC suppressGC(this);
  JS_ReportErrorNumberASCII(this, GetErrorMessage, nullptr,
                            JSMSG_ALLOC_OVERFLOW);
}

JS::StackKind JSContext::stackKindForCurrentPrincipal() {
  return runningWithTrustedPrincipals() ? JS::StackForTrustedScript
                                        : JS::StackForUntrustedScript;
}

JS::NativeStackLimit JSContext::stackLimitForCurrentPrincipal() {
  return stackLimit(stackKindForCurrentPrincipal());
}

JS_PUBLIC_API bool js::UseInternalJobQueues(JSContext* cx) {
  // Internal job queue handling must be set up very early. Self-hosting
  // initialization is as good a marker for that as any.
  MOZ_RELEASE_ASSERT(
      !cx->runtime()->hasInitializedSelfHosting(),
      "js::UseInternalJobQueues must be called early during runtime startup.");
  MOZ_ASSERT(!cx->jobQueue);
  auto queue = MakeUnique<InternalJobQueue>(cx);
  if (!queue) {
    return false;
  }

  cx->internalJobQueue = std::move(queue);
  cx->jobQueue = cx->internalJobQueue.ref().get();

  cx->runtime()->offThreadPromiseState.ref().initInternalDispatchQueue();
  MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized());

  return true;
}

#ifdef DEBUG
JSObject* InternalJobQueue::copyJobs(JSContext* cx) {
  Rooted<ArrayObject*> jobs(cx, NewDenseEmptyArray(cx));
  if (!jobs) {
    return nullptr;
  }

  for (const JSObject* unwrappedJob : queue.get()) {
    RootedObject job(cx, const_cast<JSObject*>(unwrappedJob));
    if (!cx->compartment()->wrap(cx, &job)) {
      return nullptr;
    }

    if (!NewbornArrayPush(cx, jobs, ObjectValue(*job))) {
      return nullptr;
    }
  }

  return jobs;
}

JS_PUBLIC_API JSObject* js::GetJobsInInternalJobQueue(JSContext* cx) {
  MOZ_ASSERT(cx->internalJobQueue.ref());
  return cx->internalJobQueue->copyJobs(cx);
}
#endif

JS_PUBLIC_API bool js::EnqueueJob(JSContext* cx, JS::HandleObject job) {
  MOZ_ASSERT(cx->jobQueue);
  return cx->jobQueue->enqueuePromiseJob(cx, nullptr, job, nullptr, nullptr);
}

JS_PUBLIC_API void js::StopDrainingJobQueue(JSContext* cx) {
  MOZ_ASSERT(cx->internalJobQueue.ref());
  cx->internalJobQueue->interrupt();
}

JS_PUBLIC_API void js::RestartDrainingJobQueue(JSContext* cx) {
  MOZ_ASSERT(cx->internalJobQueue.ref());
  cx->internalJobQueue->uninterrupt();
}

JS_PUBLIC_API void js::RunJobs(JSContext* cx) {
  MOZ_ASSERT(cx->jobQueue);
  MOZ_ASSERT(cx->isEvaluatingModule == 0);
  cx->jobQueue->runJobs(cx);
  JS::ClearKeptObjects(cx);
}

bool InternalJobQueue::getHostDefinedData(
    JSContext* cx, JS::MutableHandle<JSObject*> data) const {
  data.set(nullptr);
  return true;
}

bool InternalJobQueue::enqueuePromiseJob(JSContext* cx,
                                         JS::HandleObject promise,
                                         JS::HandleObject job,
                                         JS::HandleObject allocationSite,
                                         JS::HandleObject hostDefinedData) {
  MOZ_ASSERT(job);
  if (!queue.pushBack(job)) {
    ReportOutOfMemory(cx);
    return false;
  }

  JS::JobQueueMayNotBeEmpty(cx);
  return true;
}

void InternalJobQueue::runJobs(JSContext* cx) {
  if (draining_ || interrupted_) {
    return;
  }

  while (true) {
    cx->runtime()->offThreadPromiseState.ref().internalDrain(cx);

    // It doesn't make sense for job queue draining to be reentrant. At the
    // same time we don't want to assert against it, because that'd make
    // drainJobQueue unsafe for fuzzers. We do want fuzzers to test this,
    // so we simply ignore nested calls of drainJobQueue.
    draining_ = true;

    RootedObject job(cx);
    JS::HandleValueArray args(JS::HandleValueArray::empty());
    RootedValue rval(cx);

    // Execute jobs in a loop until we've reached the end of the queue.
    while (!queue.empty()) {
      // A previous job might have set this flag. E.g., the js shell
      // sets it if the `quit` builtin function is called.
      if (interrupted_) {
        break;
      }

      job = queue.front();
      queue.popFront();

      // If the next job is the last job in the job queue, allow
      // skipping the standard job queuing behavior.
      if (queue.empty()) {
        JS::JobQueueIsEmpty(cx);
      }

      AutoRealm ar(cx, &job->as<JSFunction>());
      {
        if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) {
          // Nothing we can do about uncatchable exceptions.
          if (!cx->isExceptionPending()) {
            continue;
          }

          // Always clear the exception, because
          // PrepareScriptEnvironmentAndInvoke will assert that we don't have
          // one.
          RootedValue exn(cx);
          bool success = cx->getPendingException(&exn);
          cx->clearPendingException();
          if (success) {
            js::ReportExceptionClosure reportExn(exn);
            PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn);
          }
        }
      }
    }

    draining_ = false;

    if (interrupted_) {
      break;
    }

    queue.clear();

    // It's possible a job added a new off-thread promise task.
    if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending()) {
      break;
    }
  }
}

bool InternalJobQueue::empty() const { return queue.empty(); }

JSObject* InternalJobQueue::maybeFront() const {
  if (queue.empty()) {
    return nullptr;
  }

  return queue.get().front();
}

class js::InternalJobQueue::SavedQueue : public JobQueue::SavedJobQueue {
 public:
  SavedQueue(JSContext* cx, Queue&& saved, bool draining)
      : cx(cx), saved(cx, std::move(saved)), draining_(draining) {
    MOZ_ASSERT(cx->internalJobQueue.ref());
  }

  ~SavedQueue() {
    MOZ_ASSERT(cx->internalJobQueue.ref());
    cx->internalJobQueue->queue = std::move(saved.get());
    cx->internalJobQueue->draining_ = draining_;
  }

 private:
  JSContext* cx;
  PersistentRooted<Queue> saved;
  bool draining_;
};

js::UniquePtr<JS::JobQueue::SavedJobQueue> InternalJobQueue::saveJobQueue(
    JSContext* cx) {
  auto saved =
      js::MakeUnique<SavedQueue>(cx, std::move(queue.get()), draining_);
  if (!saved) {
    // When MakeUnique's allocation fails, the SavedQueue constructor is never
    // called, so this->queue is still initialized. (The move doesn't occur
    // until the constructor gets called.)
    ReportOutOfMemory(cx);
    return nullptr;
  }

  queue = Queue(SystemAllocPolicy());
  draining_ = false;
  return saved;
}

mozilla::GenericErrorResult<OOM> JSContext::alreadyReportedOOM() {
  MOZ_ASSERT(isThrowingOutOfMemory());
  return mozilla::Err(JS::OOM());
}

mozilla::GenericErrorResult<JS::Error> JSContext::alreadyReportedError() {
  return mozilla::Err(JS::Error());
}

JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
    : RootingContext(runtime ? &runtime->gc.nursery() : nullptr),
      runtime_(runtime),
      options_(this, options),
      measuringExecutionTime_(thisfalse),
      jitActivation(this, nullptr),
      isolate(this, nullptr),
      activation_(this, nullptr),
      profilingActivation_(nullptr),
      noExecuteDebuggerTop(this, nullptr),
#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
      inUnsafeCallWithABI(thisfalse),
      hasAutoUnsafeCallWithABI(thisfalse),
#endif
#ifdef DEBUG
      liveArraySortDataInstances(this, 0),
#endif
#ifdef JS_SIMULATOR
      simulator_(this, nullptr),
#endif
      dtoaState(this, nullptr),
      suppressGC(this, 0),
#ifdef FUZZING_JS_FUZZILLI
      executionHash(1),
      executionHashInputs(0),
#endif
#ifdef DEBUG
      noNurseryAllocationCheck(this, 0),
      disableStrictProxyCheckingCount(this, 0),
#endif
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
      runningOOMTest(thisfalse),
#endif
      inUnsafeRegion(this, 0),
      generationalDisabled(this, 0),
      compactingDisabledCount(this, 0),
#ifdef DEBUG
      regExpSearcherLastLimit(this, RegExpSearcherLastLimitSentinel),
#else
      regExpSearcherLastLimit(this, 0),
#endif
      isEvaluatingModule(this, 0),
      frontendCollectionPool_(this),
      suppressProfilerSampling(false),
      tempLifoAlloc_(this, (size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE,
                     js::MallocArena),
      debuggerMutations(this, 0),
      status(this, JS::ExceptionStatus::None),
      unwrappedException_(this),
      unwrappedExceptionStack_(this),
#ifdef DEBUG
      hadResourceExhaustion_(thisfalse),
      hadUncatchableException_(thisfalse),
#endif
      reportGranularity(this, JS_DEFAULT_JITREPORT_GRANULARITY),
      resolvingList(this, nullptr),
#ifdef DEBUG
      enteredPolicy(this, nullptr),
#endif
      generatingError(thisfalse),
      cycleDetectorVector_(thisthis),
      data(nullptr),
      asyncStackForNewActivations_(this),
      asyncCauseForNewActivations(this, nullptr),
      asyncCallIsExplicit(thisfalse),
      interruptCallbacks_(this),
      interruptCallbackDisabled(thisfalse),
      interruptBits_(0),
      inlinedICScript_(this, nullptr),
      jitStackLimit(JS::NativeStackLimitMin),
      jitStackLimitNoInterrupt(this, JS::NativeStackLimitMin),
      jobQueue(this, nullptr),
      internalJobQueue(this),
      canSkipEnqueuingJobs(thisfalse),
      promiseRejectionTrackerCallback(this, nullptr),
      promiseRejectionTrackerCallbackData(this, nullptr),
      insideExclusiveDebuggerOnEval(this, nullptr) {
  MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
             JS::RootingContext::get(this));
}

#ifdef ENABLE_WASM_JSPI
bool js::IsSuspendableStackActive(JSContext* cx) {
  return cx->wasm().suspendableStackLimit != JS::NativeStackLimitMin;
}

JS::NativeStackLimit js::GetSuspendableStackLimit(JSContext* cx) {
  return cx->wasm().suspendableStackLimit;
}
#endif

JSContext::~JSContext() {
#ifdef DEBUG
  // Clear the initialized_ first, so that ProtectedData checks will allow us to
  // destroy this context even if the runtime is already gone.
  initialized_ = false;
#endif

  /* Free the stuff hanging off of cx. */
  MOZ_ASSERT(!resolvingList);

  // Ensure we didn't leak memory for the ArraySortData vector.
  MOZ_ASSERT(liveArraySortDataInstances == 0);

  if (dtoaState) {
    DestroyDtoaState(dtoaState);
  }

  fx.destroyInstance();

#ifdef JS_SIMULATOR
  js::jit::Simulator::Destroy(simulator_);
#endif

  if (isolate) {
    irregexp::DestroyIsolate(isolate.ref());
  }

  TlsContext.set(nullptr);
}

void JSContext::setRuntime(JSRuntime* rt) {
  MOZ_ASSERT(!resolvingList);
  MOZ_ASSERT(!compartment());
  MOZ_ASSERT(!activation());
  MOZ_ASSERT(!unwrappedException_.ref().initialized());
  MOZ_ASSERT(!unwrappedExceptionStack_.ref().initialized());
  MOZ_ASSERT(!asyncStackForNewActivations_.ref().initialized());

  runtime_ = rt;
}

#if defined(NIGHTLY_BUILD)
static bool IsOutOfMemoryException(JSContext* cx, const Value& v) {
  return v == StringValue(cx->names().out_of_memory_);
}
#endif

void JSContext::setPendingException(HandleValue v, Handle<SavedFrame*> stack) {
#if defined(NIGHTLY_BUILD)
  do {
    // Do not intercept exceptions if we are already
    // in the exception interceptor. That would lead
    // to infinite recursion.
    if (this->runtime()->errorInterception.isExecuting) {
      break;
    }

    // Check whether we have an interceptor at all.
    if (!this->runtime()->errorInterception.interceptor) {
      break;
    }

    // Don't report OOM exceptions. The interceptor isn't interested in those
    // and they can confuse the interceptor because OOM can be thrown when we
    // are not in a realm (atom allocation, for example).
    if (IsOutOfMemoryException(this, v)) {
      break;
    }

    // Make sure that we do not call the interceptor from within
    // the interceptor.
    this->runtime()->errorInterception.isExecuting = true;

    // The interceptor must be infallible.
    const mozilla::DebugOnly<bool> wasExceptionPending =
        this->isExceptionPending();
    this->runtime()->errorInterception.interceptor->interceptError(this, v);
    MOZ_ASSERT(wasExceptionPending == this->isExceptionPending());

    this->runtime()->errorInterception.isExecuting = false;
  } while (false);
#endif  // defined(NIGHTLY_BUILD)

  // overRecursed_ is set after the fact by ReportOverRecursed.
  this->status = JS::ExceptionStatus::Throwing;
  this->unwrappedException() = v;
  this->unwrappedExceptionStack() = stack;
}

void JSContext::setPendingException(HandleValue value,
                                    ShouldCaptureStack captureStack) {
  Rooted<SavedFrame*> nstack(this);
  if (captureStack == ShouldCaptureStack::Always ||
      realm()->shouldCaptureStackForThrow()) {
    RootedObject stack(this);
    if (!CaptureStack(this, &stack)) {
      clearPendingException();
    }
    if (stack) {
      nstack = &stack->as<SavedFrame>();
    }
  }
  setPendingException(value, nstack);
}

bool JSContext::getPendingException(MutableHandleValue rval) {
  MOZ_ASSERT(isExceptionPending());

  RootedValue exception(this, unwrappedException());
  if (zone()->isAtomsZone()) {
    rval.set(exception);
    return true;
  }

  Rooted<SavedFrame*> stack(this, unwrappedExceptionStack());
  JS::ExceptionStatus prevStatus = status;
  clearPendingException();
  if (!compartment()->wrap(this, &exception)) {
    return false;
  }
  this->check(exception);
  setPendingException(exception, stack);
  status = prevStatus;

  rval.set(exception);
  return true;
}

bool JSContext::getPendingExceptionStack(MutableHandleValue rval) {
  MOZ_ASSERT(isExceptionPending());

  Rooted<SavedFrame*> exceptionStack(this, unwrappedExceptionStack());
  if (!exceptionStack) {
    rval.setNull();
    return true;
  }
  if (zone()->isAtomsZone()) {
    rval.setObject(*exceptionStack);
    return true;
  }

  RootedValue stack(this, ObjectValue(*exceptionStack));
  RootedValue exception(this, unwrappedException());
  JS::ExceptionStatus prevStatus = status;
  clearPendingException();
  if (!compartment()->wrap(this, &exception) ||
      !compartment()->wrap(this, &stack)) {
    return false;
  }
  this->check(stack);
  setPendingException(exception, exceptionStack);
  status = prevStatus;

  rval.set(stack);
  return true;
}

SavedFrame* JSContext::getPendingExceptionStack() {
  return unwrappedExceptionStack();
}

#ifdef DEBUG
const JS::Value& JSContext::getPendingExceptionUnwrapped() {
  MOZ_ASSERT(isExceptionPending());
  return unwrappedException();
}
#endif

bool JSContext::isClosingGenerator() {
  return isExceptionPending() &&
         unwrappedException().isMagic(JS_GENERATOR_CLOSING);
}

bool JSContext::isThrowingDebuggeeWouldRun() {
  return isExceptionPending() && unwrappedException().isObject() &&
         unwrappedException().toObject().is<ErrorObject>() &&
         unwrappedException().toObject().as<ErrorObject>().type() ==
             JSEXN_DEBUGGEEWOULDRUN;
}

bool JSContext::isRuntimeCodeGenEnabled(
    JS::RuntimeCode kind, JS::Handle<JSString*> codeString,
    JS::CompilationType compilationType,
    JS::Handle<JS::StackGCVector<JSString*>> parameterStrings,
    JS::Handle<JSString*> bodyString,
    JS::Handle<JS::StackGCVector<JS::Value>> parameterArgs,
    JS::Handle<JS::Value> bodyArg, bool* outCanCompileStrings) {
  // Make sure that the CSP callback is installed and that it permits runtime
  // code generation.
  if (JSCSPEvalChecker allows =
          runtime()->securityCallbacks->contentSecurityPolicyAllows) {
    return allows(this, kind, codeString, compilationType, parameterStrings,
                  bodyString, parameterArgs, bodyArg, outCanCompileStrings);
  }

  // Default implementation from the "Dynamic Code Brand Checks" spec.
  // https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostensurecancompilestrings
  *outCanCompileStrings = true;
  return true;
}

bool JSContext::getCodeForEval(HandleObject code,
                               JS::MutableHandle<JSString*> outCode) {
  if (JSCodeForEvalOp gets = runtime()->securityCallbacks->codeForEvalGets) {
    return gets(this, code, outCode);
  }
  // Default implementation from the "Dynamic Code Brand Checks" spec.
  // https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostgetcodeforeval
  outCode.set(nullptr);
  return true;
}

size_t JSContext::sizeOfExcludingThis(
    mozilla::MallocSizeOf mallocSizeOf) const {
  /*
   * There are other JSContext members that could be measured; the following
   * ones have been found by DMD to be worth measuring.  More stuff may be
   * added later.
   */

  return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf) +
         irregexp::IsolateSizeOfIncludingThis(isolate, mallocSizeOf);
}

size_t JSContext::sizeOfIncludingThis(
    mozilla::MallocSizeOf mallocSizeOf) const {
  return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}

#ifdef DEBUG
bool JSContext::inAtomsZone() const { return zone_->isAtomsZone(); }
#endif

void JSContext::trace(JSTracer* trc) {
  cycleDetectorVector().trace(trc);
  geckoProfiler().trace(trc);
  if (isolate) {
    irregexp::TraceIsolate(trc, isolate.ref());
  }
#ifdef ENABLE_WASM_JSPI
  wasm().promiseIntegration.trace(trc);
#endif
}

JS::NativeStackLimit JSContext::stackLimitForJitCode(JS::StackKind kind) {
#ifdef JS_SIMULATOR
  return simulator()->stackLimit();
#else
  return stackLimit(kind);
#endif
}

void JSContext::resetJitStackLimit() {
  // Note that, for now, we use the untrusted limit for ion. This is fine,
  // because it's the most conservative limit, and if we hit it, we'll bail
  // out of ion into the interpreter, which will do a proper recursion check.
#ifdef JS_SIMULATOR
  jitStackLimit = jit::Simulator::StackLimit();
#else
  jitStackLimit = nativeStackLimit[JS::StackForUntrustedScript];
#endif
  jitStackLimitNoInterrupt = jitStackLimit;
}

void JSContext::initJitStackLimit() { resetJitStackLimit(); }

JSScript* JSContext::currentScript(jsbytecode** ppc,
                                   AllowCrossRealm allowCrossRealm) {
  if (ppc) {
    *ppc = nullptr;
  }

  // Fast path: there are no JS frames on the stack if there's no activation.
  if (!activation()) {
    return nullptr;
  }

  FrameIter iter(this);
  if (iter.done() || !iter.hasScript()) {
    return nullptr;
  }

  JSScript* script = iter.script();
  if (allowCrossRealm == AllowCrossRealm::DontAllow &&
      script->realm() != realm()) {
    return nullptr;
  }

  if (ppc) {
    *ppc = iter.pc();
  }
  return script;
}

#ifdef JS_CRASH_DIAGNOSTICS
void ContextChecks::check(AbstractFramePtr frame, int argIndex) {
  if (frame) {
    check(frame.realm(), argIndex);
  }
}
#endif

void AutoEnterOOMUnsafeRegion::crash_impl(const char* reason) {
  char msgbuf[1024];
  js::NoteIntentionalCrash();
  SprintfLiteral(msgbuf, "[unhandlable oom] %s", reason);
#ifndef DEBUG
  // In non-DEBUG builds MOZ_CRASH normally doesn't print to stderr so we have
  // to do this explicitly (the jit-test allow-unhandlable-oom annotation and
  // fuzzers depend on it).
  fprintf(stderr, "Hit MOZ_CRASH(%s) at %s:%d\n", msgbuf, __FILE__, __LINE__);
#endif
  MOZ_CRASH_UNSAFE(msgbuf);
}

mozilla::Atomic<AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback,
                mozilla::Relaxed>
    AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback(nullptr);

void AutoEnterOOMUnsafeRegion::crash_impl(size_t size, const char* reason) {
  {
    JS::AutoSuppressGCAnalysis suppress;
    if (annotateOOMSizeCallback) {
      annotateOOMSizeCallback(size);
    }
  }
  crash_impl(reason);
}

void ExternalValueArray::trace(JSTracer* trc) {
  if (Value* vp = begin()) {
    TraceRootRange(trc, length(), vp, "js::ExternalValueArray");
  }
}

#ifdef MOZ_EXECUTION_TRACING

bool JSContext::enableExecutionTracing() {
  if (!executionTracer_) {
    for (RealmsIter realm(runtime()); !realm.done(); realm.next()) {
      if (realm->debuggerObservesCoverage()) {
        JS_ReportErrorNumberASCII(
            this, GetErrorMessage, nullptr,
            JSMSG_DEBUG_EXCLUSIVE_EXECUTION_TRACE_COVERAGE);
        return false;
      }
    }

    executionTracer_ = js::MakeUnique<ExecutionTracer>();

    if (!executionTracer_) {
      return false;
    }

    if (!executionTracer_->init()) {
      executionTracer_ = nullptr;
      return false;
    }

    for (RealmsIter realm(runtime()); !realm.done(); realm.next()) {
      if (realm->isSystem()) {
        continue;
      }
      realm->enableExecutionTracing();
    }
  }

  executionTracerSuspended_ = false;
  return true;
}

void JSContext::cleanUpExecutionTracingState() {
  MOZ_ASSERT(executionTracer_);

  for (RealmsIter realm(runtime()); !realm.done(); realm.next()) {
    if (realm->isSystem()) {
      continue;
    }
    realm->disableExecutionTracing();
  }

  caches().tracingCaches.clearAll();
}

void JSContext::disableExecutionTracing() {
  if (executionTracer_) {
    cleanUpExecutionTracingState();
    executionTracer_ = nullptr;
  }
}

void JSContext::suspendExecutionTracing() {
  if (executionTracer_) {
    cleanUpExecutionTracingState();
    executionTracerSuspended_ = true;
  }
}

#endif

#ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI

AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness)
    : cx_(TlsContext.get()),
      nested_(cx_ ? cx_->hasAutoUnsafeCallWithABI : false),
      nogc(cx_) {
  if (!cx_) {
    // This is a helper thread doing Ion or Wasm compilation - nothing to do.
    return;
  }
  switch (strictness) {
    case UnsafeABIStrictness::NoExceptions:
      MOZ_ASSERT(!JS_IsExceptionPending(cx_));
      checkForPendingException_ = true;
      break;
    case UnsafeABIStrictness::AllowPendingExceptions:
      checkForPendingException_ = !JS_IsExceptionPending(cx_);
      break;
    case UnsafeABIStrictness::AllowThrownExceptions:
      checkForPendingException_ = false;
      break;
  }

  cx_->hasAutoUnsafeCallWithABI = true;
}

AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI() {
  if (!cx_) {
    return;
  }
  MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI);
  if (!nested_) {
    cx_->hasAutoUnsafeCallWithABI = false;
    cx_->inUnsafeCallWithABI = false;
  }
  MOZ_ASSERT_IF(checkForPendingException_, !JS_IsExceptionPending(cx_));
}

#endif  // JS_CHECK_UNSAFE_CALL_WITH_ABI

#ifdef __wasi__
JS_PUBLIC_API void js::IncWasiRecursionDepth(JSContext* cx) {
  ++JS::RootingContext::get(cx)->wasiRecursionDepth;
}

JS_PUBLIC_API void js::DecWasiRecursionDepth(JSContext* cx) {
  MOZ_ASSERT(JS::RootingContext::get(cx)->wasiRecursionDepth > 0);
  --JS::RootingContext::get(cx)->wasiRecursionDepth;
}

JS_PUBLIC_API bool js::CheckWasiRecursionLimit(JSContext* cx) {
  // WASI has two limits:
  // 1) The stack pointer in linear memory that grows to zero. See
  //    --stack-first in js/src/shell/moz.build.
  // 2) The JS::RootingContext::wasiRecursionDepth that counts recursion depth.
  //    Here we should check both.
  if (JS::RootingContext::get(cx)->wasiRecursionDepth >=
      JS::RootingContext::wasiRecursionDepthLimit) {
    return false;
  }
  return true;
}
#endif  // __wasi__

Messung V0.5
C=90 H=96 G=93

¤ Dauer der Verarbeitung: 0.39 Sekunden  (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 und die Messung sind noch experimentell.