tor-browser

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

JSContext.cpp (53300B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * JS execution context.
      9 */
     10 
     11 #include "vm/JSContext-inl.h"
     12 
     13 #include "mozilla/CheckedInt.h"
     14 #include "mozilla/DebugOnly.h"
     15 #include "mozilla/MemoryReporting.h"
     16 #include "mozilla/Sprintf.h"
     17 #include "mozilla/Utf8.h"  // mozilla::ConvertUtf16ToUtf8
     18 
     19 #include <string.h>
     20 #ifdef ANDROID
     21 #  include <android/log.h>
     22 #  include <fstream>
     23 #endif  // ANDROID
     24 #ifdef XP_WIN
     25 #  include <processthreadsapi.h>
     26 #endif  // XP_WIN
     27 
     28 #include "jsapi.h"  // JS_SetNativeStackQuota
     29 #include "jsexn.h"
     30 #include "jstypes.h"
     31 
     32 #include "builtin/RegExp.h"  // js::RegExpSearcherLastLimitSentinel
     33 #ifdef MOZ_EXECUTION_TRACING
     34 #  include "debugger/ExecutionTracer.h"
     35 #endif
     36 #include "frontend/FrontendContext.h"
     37 #include "gc/GC.h"
     38 #include "gc/PublicIterators.h"  // js::RealmsIter
     39 #include "irregexp/RegExpAPI.h"
     40 #include "jit/Simulator.h"
     41 #include "js/CallAndConstruct.h"  // JS::Call
     42 #include "js/CharacterEncoding.h"
     43 #include "js/ContextOptions.h"        // JS::ContextOptions
     44 #include "js/ErrorInterceptor.h"      // JSErrorInterceptor
     45 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     46 #include "js/friend/MicroTask.h"
     47 #include "js/friend/StackLimits.h"  // js::ReportOverRecursed
     48 #include "js/MemoryCallbacks.h"
     49 #include "js/Prefs.h"
     50 #include "js/Printf.h"
     51 #include "js/PropertyAndElement.h"  // JS_GetProperty
     52 #include "js/Stack.h"  // JS::NativeStackSize, JS::NativeStackLimit, JS::NativeStackLimitMin
     53 #include "util/DiagnosticAssertions.h"
     54 #include "util/DifferentialTesting.h"
     55 #include "util/DoubleToString.h"
     56 #include "util/NativeStack.h"
     57 #include "util/Text.h"
     58 #include "util/WindowsWrapper.h"
     59 #include "js/friend/DumpFunctions.h"  // for stack trace utilities
     60 #include "js/Printer.h"               // for FixedBufferPrinter
     61 #include "vm/BytecodeUtil.h"          // JSDVG_IGNORE_STACK
     62 #include "vm/ErrorObject.h"
     63 #include "vm/ErrorReporting.h"
     64 #include "vm/FrameIter.h"
     65 #include "vm/JSFunction.h"
     66 #include "vm/JSObject.h"
     67 #include "vm/PlainObject.h"  // js::PlainObject
     68 #include "vm/Realm.h"
     69 #include "vm/StringType.h"  // StringToNewUTF8CharsZ
     70 #include "vm/ToSource.h"    // js::ValueToSource
     71 
     72 #include "vm/Compartment-inl.h"
     73 #include "vm/Stack-inl.h"
     74 
     75 using namespace js;
     76 
     77 #ifdef DEBUG
     78 JSContext* js::MaybeGetJSContext() {
     79  if (!TlsContext.init()) {
     80    return nullptr;
     81  }
     82  return TlsContext.get();
     83 }
     84 #endif
     85 
     86 bool js::AutoCycleDetector::init() {
     87  MOZ_ASSERT(cyclic);
     88 
     89  AutoCycleDetector::Vector& vector = cx->cycleDetectorVector();
     90 
     91  for (JSObject* obj2 : vector) {
     92    if (MOZ_UNLIKELY(obj == obj2)) {
     93      return true;
     94    }
     95  }
     96 
     97  if (!vector.append(obj)) {
     98    return false;
     99  }
    100 
    101  cyclic = false;
    102  return true;
    103 }
    104 
    105 js::AutoCycleDetector::~AutoCycleDetector() {
    106  if (MOZ_LIKELY(!cyclic)) {
    107    AutoCycleDetector::Vector& vec = cx->cycleDetectorVector();
    108    MOZ_ASSERT(vec.back() == obj);
    109    if (vec.length() > 1) {
    110      vec.popBack();
    111    } else {
    112      // Avoid holding on to unused heap allocations.
    113      vec.clearAndFree();
    114    }
    115  }
    116 }
    117 
    118 bool JSContext::init() {
    119  TlsContext.set(this);
    120  nativeStackBase_.emplace(GetNativeStackBase());
    121 
    122  if (!fx.initInstance()) {
    123    return false;
    124  }
    125 
    126 #ifdef JS_SIMULATOR
    127  simulator_ = jit::Simulator::Create();
    128  if (!simulator_) {
    129    return false;
    130  }
    131 #endif
    132 
    133  isolate = irregexp::CreateIsolate(this);
    134  if (!isolate) {
    135    return false;
    136  }
    137 
    138  this->microTaskQueues = js::MakeUnique<js::MicroTaskQueueSet>(this);
    139  if (!this->microTaskQueues) {
    140    return false;
    141  }
    142 
    143 #ifdef DEBUG
    144  // Set the initialized_ last, so that ProtectedData checks will allow us to
    145  // initialize this context before it becomes the runtime's active context.
    146  initialized_ = true;
    147 #endif
    148 
    149  return true;
    150 }
    151 
    152 static void InitDefaultStackQuota(JSContext* cx) {
    153  // Initialize stack quota to a reasonable default. Embedders can override this
    154  // by calling JS_SetNativeStackQuota.
    155  //
    156  // NOTE: Firefox overrides these values. For the main thread this happens in
    157  // XPCJSContext::Initialize.
    158 
    159 #if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN))
    160  static constexpr JS::NativeStackSize MaxStackSize =
    161      2 * 128 * sizeof(size_t) * 1024;
    162 #else
    163  static constexpr JS::NativeStackSize MaxStackSize =
    164      128 * sizeof(size_t) * 1024;
    165 #endif
    166  JS_SetNativeStackQuota(cx, MaxStackSize);
    167 }
    168 
    169 JSContext* js::NewContext(uint32_t maxBytes, JSRuntime* parentRuntime) {
    170  AutoNoteSingleThreadedRegion anstr;
    171 
    172  MOZ_RELEASE_ASSERT(!TlsContext.get());
    173 
    174 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
    175  js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_MAIN
    176                                        : js::THREAD_TYPE_WORKER);
    177 #endif
    178 
    179  JSRuntime* runtime = js_new<JSRuntime>(parentRuntime);
    180  if (!runtime) {
    181    return nullptr;
    182  }
    183 
    184  JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
    185  if (!cx) {
    186    js_delete(runtime);
    187    return nullptr;
    188  }
    189 
    190  if (!cx->init()) {
    191    js_delete(cx);
    192    js_delete(runtime);
    193    return nullptr;
    194  }
    195 
    196  if (!runtime->init(cx, maxBytes)) {
    197    runtime->destroyRuntime();
    198    js_delete(cx);
    199    js_delete(runtime);
    200    return nullptr;
    201  }
    202 
    203  // Initialize stack quota last because simulators rely on the JSRuntime having
    204  // been initialized.
    205  InitDefaultStackQuota(cx);
    206 
    207  return cx;
    208 }
    209 
    210 void js::DestroyContext(JSContext* cx) {
    211  JS_AbortIfWrongThread(cx);
    212 
    213  MOZ_ASSERT(!cx->realm(), "Shouldn't destroy context with active realm");
    214  MOZ_ASSERT(!cx->activation(), "Shouldn't destroy context with activations");
    215 
    216  cx->checkNoGCRooters();
    217 
    218  // Cancel all off thread compiles. Completed compiles may try to
    219  // interrupt this context. See HelperThread::handleIonWorkload.
    220  CancelOffThreadCompile(cx->runtime());
    221 
    222  cx->jobQueue = nullptr;
    223  cx->internalJobQueue = nullptr;
    224  cx->microTaskQueues = nullptr;
    225  SetContextProfilingStack(cx, nullptr);
    226 
    227  JSRuntime* rt = cx->runtime();
    228 
    229  // Flush promise tasks executing in helper threads early, before any parts
    230  // of the JSRuntime that might be visible to helper threads are torn down.
    231  rt->offThreadPromiseState.ref().shutdown(cx);
    232 
    233  // Destroy the runtime along with its last context.
    234  js::AutoNoteSingleThreadedRegion nochecks;
    235  rt->destroyRuntime();
    236  js_delete_poison(cx);
    237  js_delete_poison(rt);
    238 }
    239 
    240 void JS::RootingContext::checkNoGCRooters() {
    241 #ifdef DEBUG
    242  for (auto const& stackRootPtr : stackRoots_) {
    243    MOZ_ASSERT(stackRootPtr == nullptr);
    244  }
    245 #endif
    246 }
    247 
    248 bool AutoResolving::alreadyStartedSlow() const {
    249  MOZ_ASSERT(link);
    250  AutoResolving* cursor = link;
    251  do {
    252    MOZ_ASSERT(this != cursor);
    253    if (object.get() == cursor->object && id.get() == cursor->id) {
    254      return true;
    255    }
    256  } while (!!(cursor = cursor->link));
    257  return false;
    258 }
    259 
    260 static void MaybeReportOutOfMemoryForDifferentialTesting() {
    261  /*
    262   * OOMs are non-deterministic, especially across different execution modes
    263   * (e.g. interpreter vs JIT). When doing differential testing, print to stderr
    264   * so that the fuzzers can detect this.
    265   */
    266  if (js::SupportDifferentialTesting()) {
    267    fprintf(stderr, "ReportOutOfMemory called\n");
    268  }
    269 }
    270 
    271 /*
    272 * Since memory has been exhausted, avoid the normal error-handling path which
    273 * allocates an error object, report and callstack. Instead simply throw the
    274 * static atom "out of memory".
    275 *
    276 * Furthermore, callers of ReportOutOfMemory (viz., malloc) assume a GC does
    277 * not occur, so GC must be avoided or suppressed.
    278 */
    279 void JSContext::onOutOfMemory() {
    280  runtime()->hadOutOfMemory = true;
    281  gc::AutoSuppressGC suppressGC(this);
    282 
    283  requestInterrupt(js::InterruptReason::OOMStackTrace);
    284 
    285  /* Report the oom. */
    286  if (JS::OutOfMemoryCallback oomCallback = runtime()->oomCallback) {
    287    oomCallback(this, runtime()->oomCallbackData);
    288  }
    289 
    290  // If we OOM early in process startup, this may be unavailable so just return
    291  // instead of crashing unexpectedly.
    292  if (MOZ_UNLIKELY(!runtime()->hasInitializedSelfHosting())) {
    293    return;
    294  }
    295 
    296  RootedValue oomMessage(this, StringValue(names().out_of_memory_));
    297  setPendingException(oomMessage, nullptr);
    298  MOZ_ASSERT(status == JS::ExceptionStatus::Throwing);
    299  status = JS::ExceptionStatus::OutOfMemory;
    300 
    301  reportResourceExhaustion();
    302 }
    303 
    304 JS_PUBLIC_API void js::ReportOutOfMemory(JSContext* cx) {
    305  MaybeReportOutOfMemoryForDifferentialTesting();
    306 
    307  cx->onOutOfMemory();
    308 }
    309 
    310 JS_PUBLIC_API void js::ReportLargeOutOfMemory(JSContext* cx) {
    311  js::ReportOutOfMemory(cx);
    312 }
    313 
    314 JS_PUBLIC_API void js::ReportOutOfMemory(FrontendContext* fc) {
    315  MaybeReportOutOfMemoryForDifferentialTesting();
    316 
    317  fc->onOutOfMemory();
    318 }
    319 
    320 static void MaybeReportOverRecursedForDifferentialTesting() {
    321  /*
    322   * We cannot make stack depth deterministic across different
    323   * implementations (e.g. JIT vs. interpreter will differ in
    324   * their maximum stack depth).
    325   * However, we can detect externally when we hit the maximum
    326   * stack depth which is useful for external testing programs
    327   * like fuzzers.
    328   */
    329  if (js::SupportDifferentialTesting()) {
    330    fprintf(stderr, "ReportOverRecursed called\n");
    331  }
    332 }
    333 
    334 void JSContext::onOverRecursed() {
    335  // Try to construct an over-recursed error and then update the exception
    336  // status to `OverRecursed`. Creating the error can fail, so check there
    337  // is a reasonable looking exception pending before updating status.
    338  JS_ReportErrorNumberASCII(this, GetErrorMessage, nullptr,
    339                            JSMSG_OVER_RECURSED);
    340  if (isExceptionPending() && !isThrowingOutOfMemory()) {
    341    MOZ_ASSERT(unwrappedException().isObject());
    342    MOZ_ASSERT(status == JS::ExceptionStatus::Throwing);
    343    status = JS::ExceptionStatus::OverRecursed;
    344  }
    345 
    346  reportResourceExhaustion();
    347 }
    348 
    349 JS_PUBLIC_API void js::ReportOverRecursed(JSContext* maybecx) {
    350  MaybeReportOverRecursedForDifferentialTesting();
    351 
    352  if (!maybecx) {
    353    return;
    354  }
    355 
    356  maybecx->onOverRecursed();
    357 }
    358 
    359 JS_PUBLIC_API void js::ReportOverRecursed(FrontendContext* fc) {
    360  MaybeReportOverRecursedForDifferentialTesting();
    361 
    362  fc->onOverRecursed();
    363 }
    364 
    365 void js::ReportOversizedAllocation(JSContext* cx, const unsigned errorNumber) {
    366  // The JIT may optimize away allocations if it determines that they aren't
    367  // used. This can affect whether we throw an exception when the size of an
    368  // allocation exceeds implementation-defined limits (eg JSString::MAX_LENGTH).
    369  // These errors aren't interesting for the purposes of differential fuzzing.
    370  // We print a message so that fuzzers can detect this case. To simplify
    371  // tooling updates, we use the same message as ReportOutOfMemory.
    372  if (js::SupportDifferentialTesting()) {
    373    fprintf(stderr, "ReportOutOfMemory called\n");
    374  }
    375 
    376  gc::AutoSuppressGC suppressGC(cx);
    377  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
    378 
    379  cx->reportResourceExhaustion();
    380 }
    381 
    382 void js::ReportAllocationOverflow(JSContext* cx) {
    383  if (js::SupportDifferentialTesting()) {
    384    fprintf(stderr, "ReportAllocationOverflow called\n");
    385  }
    386 
    387  if (!cx) {
    388    return;
    389  }
    390 
    391  cx->reportAllocationOverflow();
    392 }
    393 
    394 void js::ReportAllocationOverflow(FrontendContext* fc) {
    395  fc->onAllocationOverflow();
    396 }
    397 
    398 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
    399 void js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee,
    400                               const char* msg) {
    401  RootedValue usage(cx);
    402  if (!JS_GetProperty(cx, callee, "usage", &usage)) {
    403    return;
    404  }
    405 
    406  if (!usage.isString()) {
    407    JS_ReportErrorASCII(cx, "%s", msg);
    408  } else {
    409    RootedString usageStr(cx, usage.toString());
    410    UniqueChars str = JS_EncodeStringToUTF8(cx, usageStr);
    411    if (!str) {
    412      return;
    413    }
    414    JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.get());
    415  }
    416 }
    417 
    418 enum class PrintErrorKind { Error, Warning, Note };
    419 
    420 static void PrintErrorLine(FILE* file, const char* prefix,
    421                           JSErrorReport* report) {
    422  if (const char16_t* linebuf = report->linebuf()) {
    423    UniqueChars line;
    424    size_t n;
    425    {
    426      size_t linebufLen = report->linebufLength();
    427 
    428      // This function is only used for shell command-line sorts of stuff where
    429      // performance doesn't really matter, so just encode into max-sized
    430      // memory.
    431      mozilla::CheckedInt<size_t> utf8Len(linebufLen);
    432      utf8Len *= 3;
    433      if (utf8Len.isValid()) {
    434        line = UniqueChars(js_pod_malloc<char>(utf8Len.value()));
    435        if (line) {
    436          n = mozilla::ConvertUtf16toUtf8({linebuf, linebufLen},
    437                                          {line.get(), utf8Len.value()});
    438        }
    439      }
    440    }
    441 
    442    const char* utf8buf;
    443    if (line) {
    444      utf8buf = line.get();
    445    } else {
    446      static const char unavailableStr[] = "<context unavailable>";
    447      utf8buf = unavailableStr;
    448      n = js_strlen(unavailableStr);
    449    }
    450 
    451    fputs(":\n", file);
    452    if (prefix) {
    453      fputs(prefix, file);
    454    }
    455 
    456    for (size_t i = 0; i < n; i++) {
    457      fputc(utf8buf[i], file);
    458    }
    459 
    460    // linebuf/utf8buf usually ends with a newline. If not, add one here.
    461    if (n == 0 || utf8buf[n - 1] != '\n') {
    462      fputc('\n', file);
    463    }
    464 
    465    if (prefix) {
    466      fputs(prefix, file);
    467    }
    468 
    469    n = report->tokenOffset();
    470    for (size_t i = 0, j = 0; i < n; i++) {
    471      if (utf8buf[i] == '\t') {
    472        for (size_t k = (j + 8) & ~7; j < k; j++) {
    473          fputc('.', file);
    474        }
    475        continue;
    476      }
    477      fputc('.', file);
    478      j++;
    479    }
    480    fputc('^', file);
    481  }
    482 }
    483 
    484 static void PrintErrorLine(FILE* file, const char* prefix,
    485                           JSErrorNotes::Note* note) {}
    486 
    487 template <typename T>
    488 static void PrintSingleError(FILE* file, JS::ConstUTF8CharsZ toStringResult,
    489                             T* report, PrintErrorKind kind) {
    490  UniqueChars prefix;
    491  if (report->filename) {
    492    prefix = JS_smprintf("%s:", report->filename.c_str());
    493  }
    494 
    495  if (report->lineno) {
    496    prefix = JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno,
    497                         report->column.oneOriginValue());
    498  }
    499 
    500  if (kind != PrintErrorKind::Error) {
    501    const char* kindPrefix = nullptr;
    502    switch (kind) {
    503      case PrintErrorKind::Error:
    504        MOZ_CRASH("unreachable");
    505      case PrintErrorKind::Warning:
    506        kindPrefix = "warning";
    507        break;
    508      case PrintErrorKind::Note:
    509        kindPrefix = "note";
    510        break;
    511    }
    512 
    513    prefix = JS_smprintf("%s%s: ", prefix ? prefix.get() : "", kindPrefix);
    514  }
    515 
    516  const char* message =
    517      toStringResult ? toStringResult.c_str() : report->message().c_str();
    518 
    519  /* embedded newlines -- argh! */
    520  const char* ctmp;
    521  while ((ctmp = strchr(message, '\n')) != 0) {
    522    ctmp++;
    523    if (prefix) {
    524      fputs(prefix.get(), file);
    525    }
    526    (void)fwrite(message, 1, ctmp - message, file);
    527    message = ctmp;
    528  }
    529 
    530  /* If there were no filename or lineno, the prefix might be empty */
    531  if (prefix) {
    532    fputs(prefix.get(), file);
    533  }
    534  fputs(message, file);
    535 
    536  PrintErrorLine(file, prefix.get(), report);
    537  fputc('\n', file);
    538 
    539  fflush(file);
    540 }
    541 
    542 static void PrintErrorImpl(FILE* file, JS::ConstUTF8CharsZ toStringResult,
    543                           JSErrorReport* report, bool reportWarnings) {
    544  MOZ_ASSERT(report);
    545 
    546  /* Conditionally ignore reported warnings. */
    547  if (report->isWarning() && !reportWarnings) {
    548    return;
    549  }
    550 
    551  PrintErrorKind kind = PrintErrorKind::Error;
    552  if (report->isWarning()) {
    553    kind = PrintErrorKind::Warning;
    554  }
    555  PrintSingleError(file, toStringResult, report, kind);
    556 
    557  if (report->notes) {
    558    for (auto&& note : *report->notes) {
    559      PrintSingleError(file, JS::ConstUTF8CharsZ(), note.get(),
    560                       PrintErrorKind::Note);
    561    }
    562  }
    563 }
    564 
    565 JS_PUBLIC_API void JS::PrintError(FILE* file, JSErrorReport* report,
    566                                  bool reportWarnings) {
    567  PrintErrorImpl(file, JS::ConstUTF8CharsZ(), report, reportWarnings);
    568 }
    569 
    570 JS_PUBLIC_API void JS::PrintError(FILE* file,
    571                                  const JS::ErrorReportBuilder& builder,
    572                                  bool reportWarnings) {
    573  PrintErrorImpl(file, builder.toStringResult(), builder.report(),
    574                 reportWarnings);
    575 }
    576 
    577 void js::ReportIsNotDefined(JSContext* cx, HandleId id) {
    578  if (UniqueChars printable =
    579          IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) {
    580    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED,
    581                             printable.get());
    582  }
    583 }
    584 
    585 void js::ReportIsNotDefined(JSContext* cx, Handle<PropertyName*> name) {
    586  RootedId id(cx, NameToId(name));
    587  ReportIsNotDefined(cx, id);
    588 }
    589 
    590 const char* NullOrUndefinedToCharZ(HandleValue v) {
    591  MOZ_ASSERT(v.isNullOrUndefined());
    592  return v.isNull() ? "null" : "undefined";
    593 }
    594 
    595 void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v,
    596                                                  int vIndex) {
    597  MOZ_ASSERT(v.isNullOrUndefined());
    598 
    599  if (vIndex == JSDVG_IGNORE_STACK) {
    600    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    601                              JSMSG_CANT_CONVERT_TO, NullOrUndefinedToCharZ(v),
    602                              "object");
    603    return;
    604  }
    605 
    606  UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr);
    607  if (!bytes) {
    608    return;
    609  }
    610 
    611  if (strcmp(bytes.get(), "undefined") == 0 ||
    612      strcmp(bytes.get(), "null") == 0) {
    613    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
    614                             bytes.get());
    615  } else {
    616    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    617                             JSMSG_UNEXPECTED_TYPE, bytes.get(),
    618                             NullOrUndefinedToCharZ(v));
    619  }
    620 }
    621 
    622 void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v,
    623                                                  int vIndex, HandleId key) {
    624  MOZ_ASSERT(v.isNullOrUndefined());
    625 
    626  if (!JS::Prefs::property_error_message_fix()) {
    627    ReportIsNullOrUndefinedForPropertyAccess(cx, v, vIndex);
    628    return;
    629  }
    630 
    631  RootedValue idVal(cx, IdToValue(key));
    632  RootedString idStr(cx, ValueToSource(cx, idVal));
    633  if (!idStr) {
    634    return;
    635  }
    636 
    637  UniqueChars keyStr = StringToNewUTF8CharsZ(cx, *idStr);
    638  if (!keyStr) {
    639    return;
    640  }
    641 
    642  if (vIndex == JSDVG_IGNORE_STACK) {
    643    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
    644                             keyStr.get(), NullOrUndefinedToCharZ(v));
    645    return;
    646  }
    647 
    648  UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr);
    649  if (!bytes) {
    650    return;
    651  }
    652 
    653  if (strcmp(bytes.get(), "undefined") == 0 ||
    654      strcmp(bytes.get(), "null") == 0) {
    655    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
    656                             keyStr.get(), bytes.get());
    657    return;
    658  }
    659 
    660  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    661                           JSMSG_PROPERTY_FAIL_EXPR, keyStr.get(), bytes.get(),
    662                           NullOrUndefinedToCharZ(v));
    663 }
    664 
    665 bool js::ReportValueError(JSContext* cx, const unsigned errorNumber,
    666                          int spindex, HandleValue v, HandleString fallback,
    667                          const char* arg1, const char* arg2) {
    668  MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
    669  MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
    670  UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback);
    671  if (!bytes) {
    672    return false;
    673  }
    674 
    675  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
    676                           bytes.get(), arg1, arg2);
    677  return false;
    678 }
    679 
    680 JSObject* js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report) {
    681  Rooted<ArrayObject*> notesArray(cx, NewDenseEmptyArray(cx));
    682  if (!notesArray) {
    683    return nullptr;
    684  }
    685 
    686  if (!report->notes) {
    687    return notesArray;
    688  }
    689 
    690  for (auto&& note : *report->notes) {
    691    Rooted<PlainObject*> noteObj(cx, NewPlainObject(cx));
    692    if (!noteObj) {
    693      return nullptr;
    694    }
    695 
    696    RootedString messageStr(cx, note->newMessageString(cx));
    697    if (!messageStr) {
    698      return nullptr;
    699    }
    700    RootedValue messageVal(cx, StringValue(messageStr));
    701    if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal)) {
    702      return nullptr;
    703    }
    704 
    705    RootedValue filenameVal(cx);
    706    if (const char* filename = note->filename.c_str()) {
    707      JS::UTF8Chars utf8chars(filename, strlen(filename));
    708      Rooted<JSString*> filenameStr(cx, NewStringCopyUTF8N(cx, utf8chars));
    709      if (!filenameStr) {
    710        return nullptr;
    711      }
    712      filenameVal = StringValue(filenameStr);
    713    }
    714    if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal)) {
    715      return nullptr;
    716    }
    717 
    718    RootedValue linenoVal(cx, Int32Value(note->lineno));
    719    if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal)) {
    720      return nullptr;
    721    }
    722    RootedValue columnVal(cx, Int32Value(note->column.oneOriginValue()));
    723    if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal)) {
    724      return nullptr;
    725    }
    726 
    727    if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj))) {
    728      return nullptr;
    729    }
    730  }
    731 
    732  return notesArray;
    733 }
    734 
    735 void JSContext::recoverFromOutOfMemory() {
    736  if (isExceptionPending()) {
    737    MOZ_ASSERT(isThrowingOutOfMemory());
    738    clearPendingException();
    739  }
    740 }
    741 
    742 void JSContext::reportAllocationOverflow() {
    743  gc::AutoSuppressGC suppressGC(this);
    744  JS_ReportErrorNumberASCII(this, GetErrorMessage, nullptr,
    745                            JSMSG_ALLOC_OVERFLOW);
    746 }
    747 
    748 JS::StackKind JSContext::stackKindForCurrentPrincipal() {
    749  return runningWithTrustedPrincipals() ? JS::StackForTrustedScript
    750                                        : JS::StackForUntrustedScript;
    751 }
    752 
    753 JS::NativeStackLimit JSContext::stackLimitForCurrentPrincipal() {
    754  return stackLimit(stackKindForCurrentPrincipal());
    755 }
    756 
    757 JS_PUBLIC_API bool js::UseInternalJobQueues(JSContext* cx) {
    758  // Internal job queue handling must be set up very early. Self-hosting
    759  // initialization is as good a marker for that as any.
    760  MOZ_RELEASE_ASSERT(
    761      !cx->runtime()->hasInitializedSelfHosting(),
    762      "js::UseInternalJobQueues must be called early during runtime startup.");
    763  MOZ_ASSERT(!cx->jobQueue);
    764  auto queue = MakeUnique<InternalJobQueue>(cx);
    765  if (!queue) {
    766    return false;
    767  }
    768 
    769  cx->internalJobQueue = std::move(queue);
    770  cx->jobQueue = cx->internalJobQueue.ref().get();
    771 
    772  cx->runtime()->offThreadPromiseState.ref().initInternalDispatchQueue();
    773  MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized());
    774 
    775  return true;
    776 }
    777 
    778 #ifdef DEBUG
    779 JSObject* InternalJobQueue::copyJobs(JSContext* cx) {
    780  Rooted<ArrayObject*> jobs(cx, NewDenseEmptyArray(cx));
    781  if (!jobs) {
    782    return nullptr;
    783  }
    784 
    785  if (JS::Prefs::use_js_microtask_queue()) {
    786    auto& queues = cx->microTaskQueues;
    787    auto addToArray = [&](auto& queue) -> bool {
    788      for (const auto& e : queue) {
    789        JS::JSMicroTask* task = JS::ToUnwrappedJSMicroTask(e);
    790        if (task) {
    791          // All any test cares about is the global of the job so let's do it.
    792          RootedObject global(cx, JS::GetExecutionGlobalFromJSMicroTask(task));
    793          if (!global) {
    794            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    795                                      JSMSG_DEAD_OBJECT);
    796            return false;
    797          }
    798          if (!cx->compartment()->wrap(cx, &global)) {
    799            return false;
    800          }
    801          if (!NewbornArrayPush(cx, jobs, ObjectValue(*global))) {
    802            return false;
    803          }
    804        }
    805      }
    806 
    807      return true;
    808    };
    809 
    810    if (!addToArray(queues->debugMicroTaskQueue)) {
    811      return nullptr;
    812    }
    813    if (!addToArray(queues->microTaskQueue)) {
    814      return nullptr;
    815    }
    816  } else {
    817    for (const JSObject* unwrappedJob : queue.get()) {
    818      RootedObject job(cx, const_cast<JSObject*>(unwrappedJob));
    819      if (!cx->compartment()->wrap(cx, &job)) {
    820        return nullptr;
    821      }
    822 
    823      if (!NewbornArrayPush(cx, jobs, ObjectValue(*job))) {
    824        return nullptr;
    825      }
    826    }
    827  }
    828 
    829  return jobs;
    830 }
    831 
    832 JS_PUBLIC_API JSObject* js::GetJobsInInternalJobQueue(JSContext* cx) {
    833  MOZ_ASSERT(cx->internalJobQueue.ref());
    834  return cx->internalJobQueue->copyJobs(cx);
    835 }
    836 #endif
    837 
    838 JS_PUBLIC_API void js::StopDrainingJobQueue(JSContext* cx) {
    839  MOZ_ASSERT(cx->internalJobQueue.ref());
    840  cx->internalJobQueue->interrupt();
    841 }
    842 
    843 JS_PUBLIC_API void js::RestartDrainingJobQueue(JSContext* cx) {
    844  MOZ_ASSERT(cx->internalJobQueue.ref());
    845  cx->internalJobQueue->uninterrupt();
    846 }
    847 
    848 JS_PUBLIC_API void js::RunJobs(JSContext* cx) {
    849  MOZ_ASSERT(cx->jobQueue);
    850  MOZ_ASSERT(cx->isEvaluatingModule == 0);
    851  cx->jobQueue->runJobs(cx);
    852  JS::ClearKeptObjects(cx);
    853 }
    854 
    855 bool InternalJobQueue::getHostDefinedGlobal(
    856    JSContext* cx, MutableHandle<JSObject*> out) const {
    857  return true;
    858 }
    859 
    860 bool InternalJobQueue::getHostDefinedData(
    861    JSContext* cx, JS::MutableHandle<JSObject*> data) const {
    862  data.set(nullptr);
    863  return true;
    864 }
    865 
    866 bool InternalJobQueue::enqueuePromiseJob(JSContext* cx,
    867                                         JS::HandleObject promise,
    868                                         JS::HandleObject job,
    869                                         JS::HandleObject allocationSite,
    870                                         JS::HandleObject hostDefinedData) {
    871  MOZ_ASSERT(job);
    872  if (!queue.pushBack(job)) {
    873    ReportOutOfMemory(cx);
    874    return false;
    875  }
    876 
    877  JS::JobQueueMayNotBeEmpty(cx);
    878  return true;
    879 }
    880 
    881 void InternalJobQueue::runJobs(JSContext* cx) {
    882  if (draining_ || interrupted_) {
    883    return;
    884  }
    885 
    886  while (true) {
    887    cx->runtime()->offThreadPromiseState.ref().internalDrain(cx);
    888 
    889    // It doesn't make sense for job queue draining to be reentrant. At the
    890    // same time we don't want to assert against it, because that'd make
    891    // drainJobQueue unsafe for fuzzers. We do want fuzzers to test this,
    892    // so we simply ignore nested calls of drainJobQueue.
    893    draining_ = true;
    894 
    895    if (JS::Prefs::use_js_microtask_queue()) {
    896      // Execute jobs in a loop until we've reached the end of the queue.
    897      JS::Rooted<JS::JSMicroTask*> job(cx);
    898      JS::Rooted<JS::GenericMicroTask> dequeueJob(cx);
    899      while (JS::HasAnyMicroTasks(cx)) {
    900        MOZ_ASSERT(queue.empty());
    901        // A previous job might have set this flag. E.g., the js shell
    902        // sets it if the `quit` builtin function is called.
    903        if (interrupted_) {
    904          break;
    905        }
    906 
    907        cx->runtime()->offThreadPromiseState.ref().internalDrain(cx);
    908 
    909        dequeueJob = JS::DequeueNextMicroTask(cx);
    910        MOZ_ASSERT(!dequeueJob.isNull());
    911        job = JS::ToMaybeWrappedJSMicroTask(dequeueJob);
    912        MOZ_ASSERT(job);
    913 
    914        // If the next job is the last job in the job queue, allow
    915        // skipping the standard job queuing behavior.
    916        if (!JS::HasAnyMicroTasks(cx)) {
    917          JS::JobQueueIsEmpty(cx);
    918        }
    919 
    920        if (!JS::GetExecutionGlobalFromJSMicroTask(job)) {
    921          continue;
    922        }
    923        AutoRealm ar(cx, JS::GetExecutionGlobalFromJSMicroTask(job));
    924        {
    925          if (!JS::RunJSMicroTask(cx, job)) {
    926            // Nothing we can do about uncatchable exceptions.
    927            if (!cx->isExceptionPending()) {
    928              continue;
    929            }
    930 
    931            // Always clear the exception, because
    932            // PrepareScriptEnvironmentAndInvoke will assert that we don't have
    933            // one.
    934            RootedValue exn(cx);
    935            bool success = cx->getPendingException(&exn);
    936            cx->clearPendingException();
    937            if (success) {
    938              js::ReportExceptionClosure reportExn(exn);
    939              PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn);
    940            }
    941          }
    942        }
    943      }
    944    } else {
    945      RootedObject job(cx);
    946      JS::HandleValueArray args(JS::HandleValueArray::empty());
    947      RootedValue rval(cx);
    948      // Execute jobs in a loop until we've reached the end of the queue.
    949      while (!queue.empty()) {
    950        // A previous job might have set this flag. E.g., the js shell
    951        // sets it if the `quit` builtin function is called.
    952        if (interrupted_) {
    953          break;
    954        }
    955 
    956        cx->runtime()->offThreadPromiseState.ref().internalDrain(cx);
    957 
    958        job = queue.front();
    959        queue.popFront();
    960 
    961        // If the next job is the last job in the job queue, allow
    962        // skipping the standard job queuing behavior.
    963        if (queue.empty()) {
    964          JS::JobQueueIsEmpty(cx);
    965        }
    966 
    967        AutoRealm ar(cx, &job->as<JSFunction>());
    968        {
    969          if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) {
    970            // Nothing we can do about uncatchable exceptions.
    971            if (!cx->isExceptionPending()) {
    972              continue;
    973            }
    974 
    975            // Always clear the exception, because
    976            // PrepareScriptEnvironmentAndInvoke will assert that we don't have
    977            // one.
    978            RootedValue exn(cx);
    979            bool success = cx->getPendingException(&exn);
    980            cx->clearPendingException();
    981            if (success) {
    982              js::ReportExceptionClosure reportExn(exn);
    983              PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn);
    984            }
    985          }
    986        }
    987      }
    988    }
    989 
    990    draining_ = false;
    991 
    992    if (interrupted_) {
    993      break;
    994    }
    995 
    996    if (JS::Prefs::use_js_microtask_queue()) {
    997      // MG:XXX: Should use public API here.
    998      cx->microTaskQueues->clear();
    999    } else {
   1000      queue.clear();
   1001    }
   1002 
   1003    // It's possible a job added a new off-thread promise task.
   1004    if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending()) {
   1005      break;
   1006    }
   1007  }
   1008 }
   1009 
   1010 bool InternalJobQueue::empty() const { return queue.empty(); }
   1011 
   1012 JSObject* InternalJobQueue::maybeFront() const {
   1013  if (queue.empty()) {
   1014    return nullptr;
   1015  }
   1016 
   1017  return queue.get().front();
   1018 }
   1019 
   1020 class js::InternalJobQueue::SavedQueue : public JobQueue::SavedJobQueue {
   1021 public:
   1022  SavedQueue(JSContext* cx, Queue&& saved, MicroTaskQueueSet&& queueSet,
   1023             bool draining)
   1024      : cx(cx),
   1025        saved(cx, std::move(saved)),
   1026        savedQueues(cx, std::move(queueSet)),
   1027        draining_(draining) {
   1028    MOZ_ASSERT(cx->internalJobQueue.ref());
   1029    if (JS::Prefs::use_js_microtask_queue()) {
   1030      MOZ_ASSERT(saved.empty());
   1031    } else {
   1032      MOZ_ASSERT(queueSet.empty());
   1033    }
   1034  }
   1035 
   1036  ~SavedQueue() {
   1037    MOZ_ASSERT(cx->internalJobQueue.ref());
   1038    cx->internalJobQueue->queue = std::move(saved.get());
   1039    cx->internalJobQueue->draining_ = draining_;
   1040    *cx->microTaskQueues.get() = std::move(savedQueues.get());
   1041  }
   1042 
   1043 private:
   1044  JSContext* cx;
   1045  PersistentRooted<Queue> saved;
   1046  PersistentRooted<MicroTaskQueueSet> savedQueues;
   1047  bool draining_;
   1048 };
   1049 
   1050 js::UniquePtr<JS::JobQueue::SavedJobQueue> InternalJobQueue::saveJobQueue(
   1051    JSContext* cx) {
   1052  auto saved = js::MakeUnique<SavedQueue>(
   1053      cx, std::move(queue.get()), std::move(*cx->microTaskQueues), draining_);
   1054  if (!saved) {
   1055    // When MakeUnique's allocation fails, the SavedQueue constructor is never
   1056    // called, so this->queue is still initialized. (The move doesn't occur
   1057    // until the constructor gets called.)
   1058    ReportOutOfMemory(cx);
   1059    return nullptr;
   1060  }
   1061 
   1062  queue = Queue(SystemAllocPolicy());
   1063  draining_ = false;
   1064  return saved;
   1065 }
   1066 
   1067 void js::MicroTaskQueueElement::trace(JSTracer* trc) {
   1068  // For non-objects (like Private values), call the JobQueue hook
   1069  JSContext* cx = trc->runtime()->mainContextFromOwnThread();
   1070  MOZ_ASSERT(cx);
   1071  auto* queue = cx->jobQueue.ref();
   1072 
   1073  if (!queue || value.isGCThing()) {
   1074    TraceRoot(trc, &value, "microtask-queue-entry");
   1075  } else {
   1076    queue->traceNonGCThingMicroTask(trc, &value);
   1077  }
   1078 }
   1079 
   1080 JS::GenericMicroTask js::MicroTaskQueueSet::popDebugFront() {
   1081  JS_LOG(mtq, Info, "JS Drain Queue: popDebugFront");
   1082  if (!debugMicroTaskQueue.empty()) {
   1083    JS::Value p = debugMicroTaskQueue.front();
   1084    debugMicroTaskQueue.popFront();
   1085    return p;
   1086  }
   1087  return JS::NullValue();
   1088 }
   1089 
   1090 JS::GenericMicroTask js::MicroTaskQueueSet::popFront() {
   1091  JS_LOG(mtq, Info, "JS Drain Queue");
   1092  if (!debugMicroTaskQueue.empty()) {
   1093    JS::Value p = debugMicroTaskQueue.front();
   1094    debugMicroTaskQueue.popFront();
   1095    return p;
   1096  }
   1097  if (!microTaskQueue.empty()) {
   1098    JS::Value p = microTaskQueue.front();
   1099    microTaskQueue.popFront();
   1100    return p;
   1101  }
   1102 
   1103  return JS::NullValue();
   1104 }
   1105 
   1106 bool js::MicroTaskQueueSet::enqueueRegularMicroTask(
   1107    JSContext* cx, const JS::GenericMicroTask& entry) {
   1108  JS_LOG(mtq, Verbose, "JS: Enqueue Regular MT");
   1109  JS::JobQueueMayNotBeEmpty(cx);
   1110  return microTaskQueue.pushBack(entry);
   1111 }
   1112 
   1113 bool js::MicroTaskQueueSet::prependRegularMicroTask(
   1114    JSContext* cx, const JS::GenericMicroTask& entry) {
   1115  JS_LOG(mtq, Verbose, "JS: Prepend Regular MT");
   1116  JS::JobQueueMayNotBeEmpty(cx);
   1117  return microTaskQueue.emplaceFront(entry);
   1118 }
   1119 
   1120 bool js::MicroTaskQueueSet::enqueueDebugMicroTask(
   1121    JSContext* cx, const JS::GenericMicroTask& entry) {
   1122  JS_LOG(mtq, Verbose, "JS: Enqueue Debug MT");
   1123  return debugMicroTaskQueue.pushBack(entry);
   1124 }
   1125 
   1126 JS_PUBLIC_API bool JS::EnqueueMicroTask(JSContext* cx,
   1127                                        const JS::GenericMicroTask& entry) {
   1128  JS_LOG(mtq, Info, "Enqueue of non JS MT");
   1129 
   1130  return cx->microTaskQueues->enqueueRegularMicroTask(cx, entry);
   1131 }
   1132 
   1133 JS_PUBLIC_API bool JS::EnqueueDebugMicroTask(
   1134    JSContext* cx, const JS::GenericMicroTask& entry) {
   1135  JS_LOG(mtq, Info, "Enqueue of non JS MT");
   1136 
   1137  return cx->microTaskQueues->enqueueDebugMicroTask(cx, entry);
   1138 }
   1139 
   1140 JS_PUBLIC_API bool JS::PrependMicroTask(JSContext* cx,
   1141                                        const JS::GenericMicroTask& entry) {
   1142  JS_LOG(mtq, Info, "Prepend job to MTQ");
   1143 
   1144  return cx->microTaskQueues->prependRegularMicroTask(cx, entry);
   1145 }
   1146 
   1147 JS_PUBLIC_API JS::GenericMicroTask JS::DequeueNextMicroTask(JSContext* cx) {
   1148  return cx->microTaskQueues->popFront();
   1149 }
   1150 
   1151 JS_PUBLIC_API JS::GenericMicroTask JS::DequeueNextDebuggerMicroTask(
   1152    JSContext* cx) {
   1153  return cx->microTaskQueues->popDebugFront();
   1154 }
   1155 
   1156 JS_PUBLIC_API bool JS::HasAnyMicroTasks(JSContext* cx) {
   1157  return !cx->microTaskQueues->empty();
   1158 }
   1159 
   1160 JS_PUBLIC_API bool JS::HasDebuggerMicroTasks(JSContext* cx) {
   1161  return !cx->microTaskQueues->debugMicroTaskQueue.empty();
   1162 }
   1163 
   1164 // Concrete implementation of the saved queue.
   1165 struct SavedMicroTaskQueueImpl : public JS::SavedMicroTaskQueue {
   1166  explicit SavedMicroTaskQueueImpl(JSContext* cx) : savedQueues(cx) {
   1167    savedQueues = js::MakeUnique<js::MicroTaskQueueSet>(cx);
   1168    std::swap(cx->microTaskQueues.get(), savedQueues.get());
   1169  }
   1170  ~SavedMicroTaskQueueImpl() override = default;
   1171  JS::PersistentRooted<js::UniquePtr<js::MicroTaskQueueSet>> savedQueues;
   1172 };
   1173 
   1174 JS_PUBLIC_API js::UniquePtr<JS::SavedMicroTaskQueue> JS::SaveMicroTaskQueue(
   1175    JSContext* cx) {
   1176  auto saved = js::MakeUnique<SavedMicroTaskQueueImpl>(cx);
   1177  if (!saved) {
   1178    ReportOutOfMemory(cx);
   1179    return nullptr;
   1180  }
   1181  return saved;
   1182 }
   1183 
   1184 JS_PUBLIC_API void JS::RestoreMicroTaskQueue(
   1185    JSContext* cx, js::UniquePtr<JS::SavedMicroTaskQueue> savedQueue) {
   1186  MOZ_ASSERT(cx->microTaskQueues->empty(), "Don't drop jobs on the floor");
   1187 
   1188  // There's only one impl, so we know this is safe.
   1189  SavedMicroTaskQueueImpl* savedQueueImpl =
   1190      static_cast<SavedMicroTaskQueueImpl*>(savedQueue.get());
   1191  std::swap(savedQueueImpl->savedQueues.get(), cx->microTaskQueues.get());
   1192 }
   1193 
   1194 JS_PUBLIC_API size_t JS::GetRegularMicroTaskCount(JSContext* cx) {
   1195  return cx->microTaskQueues->microTaskQueue.length();
   1196 }
   1197 
   1198 JS_PUBLIC_API bool JS::HasRegularMicroTasks(JSContext* cx) {
   1199  return !cx->microTaskQueues->microTaskQueue.empty();
   1200 }
   1201 
   1202 JS_PUBLIC_API JS::GenericMicroTask JS::DequeueNextRegularMicroTask(
   1203    JSContext* cx) {
   1204  auto& queue = cx->microTaskQueues->microTaskQueue;
   1205  if (!queue.empty()) {
   1206    JS::GenericMicroTask p = queue.front();
   1207    queue.popFront();
   1208    return p;
   1209  }
   1210  return JS::NullValue();
   1211 }
   1212 
   1213 mozilla::GenericErrorResult<OOM> JSContext::alreadyReportedOOM() {
   1214  MOZ_ASSERT(isThrowingOutOfMemory());
   1215  return mozilla::Err(JS::OOM());
   1216 }
   1217 
   1218 mozilla::GenericErrorResult<JS::Error> JSContext::alreadyReportedError() {
   1219  return mozilla::Err(JS::Error());
   1220 }
   1221 
   1222 JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
   1223    : RootingContext(runtime ? &runtime->gc.nursery() : nullptr),
   1224      runtime_(runtime),
   1225      options_(this, options),
   1226      measuringExecutionTimeEnabled_(this, false),
   1227      jitActivation(this, nullptr),
   1228      isolate(this, nullptr),
   1229      activation_(this, nullptr),
   1230      profilingActivation_(nullptr),
   1231      noExecuteDebuggerTop(this, nullptr),
   1232 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
   1233      inUnsafeCallWithABI(this, false),
   1234      hasAutoUnsafeCallWithABI(this, false),
   1235 #endif
   1236 #ifdef DEBUG
   1237      liveArraySortDataInstances(this, 0),
   1238 #endif
   1239 #ifdef JS_SIMULATOR
   1240      simulator_(this, nullptr),
   1241 #endif
   1242      dtoaState(this, nullptr),
   1243      suppressGC(this, 0),
   1244 #ifdef FUZZING_JS_FUZZILLI
   1245      executionHash(1),
   1246      executionHashInputs(0),
   1247 #endif
   1248 #ifdef DEBUG
   1249      noNurseryAllocationCheck(this, 0),
   1250      disableStrictProxyCheckingCount(this, 0),
   1251 #endif
   1252 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
   1253      runningOOMTest(this, false),
   1254 #endif
   1255      inUnsafeRegion(this, 0),
   1256      generationalDisabled(this, 0),
   1257      compactingDisabledCount(this, 0),
   1258 #ifdef DEBUG
   1259      regExpSearcherLastLimit(this, RegExpSearcherLastLimitSentinel),
   1260 #else
   1261      regExpSearcherLastLimit(this, 0),
   1262 #endif
   1263      isEvaluatingModule(this, 0),
   1264      frontendCollectionPool_(this),
   1265      suppressProfilerSampling(false),
   1266      tempLifoAlloc_(this, (size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE,
   1267                     js::MallocArena),
   1268      debuggerMutations(this, 0),
   1269      status(this, JS::ExceptionStatus::None),
   1270      unwrappedException_(this),
   1271      unwrappedExceptionStack_(this),
   1272 #ifdef DEBUG
   1273      hadResourceExhaustion_(this, false),
   1274      hadUncatchableException_(this, false),
   1275 #endif
   1276      reportGranularity(this, JS_DEFAULT_JITREPORT_GRANULARITY),
   1277      resolvingList(this, nullptr),
   1278 #ifdef DEBUG
   1279      enteredPolicy(this, nullptr),
   1280 #endif
   1281      generatingError(this, false),
   1282      cycleDetectorVector_(this, this),
   1283      data(nullptr),
   1284      asyncStackForNewActivations_(this),
   1285      asyncCauseForNewActivations(this, nullptr),
   1286      asyncCallIsExplicit(this, false),
   1287      interruptCallbacks_(this),
   1288      interruptCallbackDisabled(this, false),
   1289      interruptBits_(0),
   1290      jitStackLimit(JS::NativeStackLimitMin),
   1291      jitStackLimitNoInterrupt(this, JS::NativeStackLimitMin),
   1292      jobQueue(this, nullptr),
   1293      internalJobQueue(this),
   1294      canSkipEnqueuingJobs(this, false),
   1295      promiseRejectionTrackerCallback(this, nullptr),
   1296      promiseRejectionTrackerCallbackData(this, nullptr),
   1297      oomStackTraceBuffer_(this, nullptr),
   1298      oomStackTraceBufferValid_(this, false),
   1299      bypassCSPForDebugger(this, false),
   1300      insideExclusiveDebuggerOnEval(this, nullptr),
   1301      microTaskQueues(this) {
   1302  MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
   1303             JS::RootingContext::get(this));
   1304 
   1305  if (JS::Prefs::experimental_capture_oom_stack_trace()) {
   1306    // Allocate pre-allocated buffer for OOM stack traces
   1307    oomStackTraceBuffer_ =
   1308        static_cast<char*>(js_calloc(OOMStackTraceBufferSize));
   1309  }
   1310 }
   1311 
   1312 JSContext::~JSContext() {
   1313 #ifdef DEBUG
   1314  // Clear the initialized_ first, so that ProtectedData checks will allow us to
   1315  // destroy this context even if the runtime is already gone.
   1316  initialized_ = false;
   1317 #endif
   1318 
   1319  /* Free the stuff hanging off of cx. */
   1320  MOZ_ASSERT(!resolvingList);
   1321 
   1322  // Ensure we didn't leak memory for the ArraySortData vector.
   1323  MOZ_ASSERT(liveArraySortDataInstances == 0);
   1324 
   1325  if (dtoaState) {
   1326    DestroyDtoaState(dtoaState);
   1327  }
   1328 
   1329  fx.destroyInstance();
   1330 
   1331 #ifdef JS_SIMULATOR
   1332  js::jit::Simulator::Destroy(simulator_);
   1333 #endif
   1334 
   1335  if (isolate) {
   1336    irregexp::DestroyIsolate(isolate.ref());
   1337  }
   1338 
   1339  // Free the pre-allocated OOM stack trace buffer
   1340  if (oomStackTraceBuffer_) {
   1341    js_free(oomStackTraceBuffer_);
   1342  }
   1343 
   1344  TlsContext.set(nullptr);
   1345 }
   1346 
   1347 void JSContext::unsetOOMStackTrace() { oomStackTraceBufferValid_ = false; }
   1348 
   1349 const char* JSContext::getOOMStackTrace() const {
   1350  if (!oomStackTraceBufferValid_ || !oomStackTraceBuffer_) {
   1351    return nullptr;
   1352  }
   1353  return oomStackTraceBuffer_;
   1354 }
   1355 
   1356 bool JSContext::hasOOMStackTrace() const { return oomStackTraceBufferValid_; }
   1357 
   1358 void JSContext::captureOOMStackTrace() {
   1359  // Clear any existing stack trace
   1360  oomStackTraceBufferValid_ = false;
   1361 
   1362  if (!oomStackTraceBuffer_) {
   1363    return;  // Buffer not available
   1364  }
   1365 
   1366  // Write directly to pre-allocated buffer to avoid any memory allocation
   1367  FixedBufferPrinter fbp(oomStackTraceBuffer_, OOMStackTraceBufferSize);
   1368  js::DumpBacktrace(this, fbp);
   1369  MOZ_ASSERT(strlen(oomStackTraceBuffer_) < OOMStackTraceBufferSize);
   1370 
   1371  oomStackTraceBufferValid_ = true;
   1372 }
   1373 
   1374 void JSContext::setRuntime(JSRuntime* rt) {
   1375  MOZ_ASSERT(!resolvingList);
   1376  MOZ_ASSERT(!compartment());
   1377  MOZ_ASSERT(!activation());
   1378  MOZ_ASSERT(!unwrappedException_.ref().initialized());
   1379  MOZ_ASSERT(!unwrappedExceptionStack_.ref().initialized());
   1380  MOZ_ASSERT(!asyncStackForNewActivations_.ref().initialized());
   1381 
   1382  runtime_ = rt;
   1383 }
   1384 
   1385 #if defined(NIGHTLY_BUILD)
   1386 static bool IsOutOfMemoryException(JSContext* cx, const Value& v) {
   1387  return v == StringValue(cx->names().out_of_memory_);
   1388 }
   1389 #endif
   1390 
   1391 void JSContext::setPendingException(HandleValue v, Handle<SavedFrame*> stack) {
   1392 #if defined(NIGHTLY_BUILD)
   1393  do {
   1394    // Do not intercept exceptions if we are already
   1395    // in the exception interceptor. That would lead
   1396    // to infinite recursion.
   1397    if (this->runtime()->errorInterception.isExecuting) {
   1398      break;
   1399    }
   1400 
   1401    // Check whether we have an interceptor at all.
   1402    if (!this->runtime()->errorInterception.interceptor) {
   1403      break;
   1404    }
   1405 
   1406    // Don't report OOM exceptions. The interceptor isn't interested in those
   1407    // and they can confuse the interceptor because OOM can be thrown when we
   1408    // are not in a realm (atom allocation, for example).
   1409    if (IsOutOfMemoryException(this, v)) {
   1410      break;
   1411    }
   1412 
   1413    // Make sure that we do not call the interceptor from within
   1414    // the interceptor.
   1415    this->runtime()->errorInterception.isExecuting = true;
   1416 
   1417    // The interceptor must be infallible.
   1418    const mozilla::DebugOnly<bool> wasExceptionPending =
   1419        this->isExceptionPending();
   1420    this->runtime()->errorInterception.interceptor->interceptError(this, v);
   1421    MOZ_ASSERT(wasExceptionPending == this->isExceptionPending());
   1422 
   1423    this->runtime()->errorInterception.isExecuting = false;
   1424  } while (false);
   1425 #endif  // defined(NIGHTLY_BUILD)
   1426 
   1427  // overRecursed_ is set after the fact by ReportOverRecursed.
   1428  this->status = JS::ExceptionStatus::Throwing;
   1429  this->unwrappedException() = v;
   1430  this->unwrappedExceptionStack() = stack;
   1431 }
   1432 
   1433 void JSContext::setPendingException(HandleValue value,
   1434                                    ShouldCaptureStack captureStack) {
   1435  Rooted<SavedFrame*> nstack(this);
   1436  if (captureStack == ShouldCaptureStack::Always ||
   1437      realm()->shouldCaptureStackForThrow()) {
   1438    RootedObject stack(this);
   1439    if (!CaptureStack(this, &stack)) {
   1440      clearPendingException();
   1441    }
   1442    if (stack) {
   1443      nstack = &stack->as<SavedFrame>();
   1444    }
   1445  }
   1446  setPendingException(value, nstack);
   1447 }
   1448 
   1449 bool JSContext::getPendingException(MutableHandleValue rval) {
   1450  MOZ_ASSERT(isExceptionPending());
   1451 
   1452  RootedValue exception(this, unwrappedException());
   1453  if (zone()->isAtomsZone()) {
   1454    rval.set(exception);
   1455    return true;
   1456  }
   1457 
   1458  Rooted<SavedFrame*> stack(this, unwrappedExceptionStack());
   1459  JS::ExceptionStatus prevStatus = status;
   1460  clearPendingException();
   1461  if (!compartment()->wrap(this, &exception)) {
   1462    return false;
   1463  }
   1464  this->check(exception);
   1465  setPendingException(exception, stack);
   1466  status = prevStatus;
   1467 
   1468  rval.set(exception);
   1469  return true;
   1470 }
   1471 
   1472 bool JSContext::getPendingExceptionStack(MutableHandleValue rval) {
   1473  MOZ_ASSERT(isExceptionPending());
   1474 
   1475  Rooted<SavedFrame*> exceptionStack(this, unwrappedExceptionStack());
   1476  if (!exceptionStack) {
   1477    rval.setNull();
   1478    return true;
   1479  }
   1480  if (zone()->isAtomsZone()) {
   1481    rval.setObject(*exceptionStack);
   1482    return true;
   1483  }
   1484 
   1485  RootedValue stack(this, ObjectValue(*exceptionStack));
   1486  RootedValue exception(this, unwrappedException());
   1487  JS::ExceptionStatus prevStatus = status;
   1488  clearPendingException();
   1489  if (!compartment()->wrap(this, &exception) ||
   1490      !compartment()->wrap(this, &stack)) {
   1491    return false;
   1492  }
   1493  this->check(stack);
   1494  setPendingException(exception, exceptionStack);
   1495  status = prevStatus;
   1496 
   1497  rval.set(stack);
   1498  return true;
   1499 }
   1500 
   1501 SavedFrame* JSContext::getPendingExceptionStack() {
   1502  return unwrappedExceptionStack();
   1503 }
   1504 
   1505 #ifdef DEBUG
   1506 const JS::Value& JSContext::getPendingExceptionUnwrapped() {
   1507  MOZ_ASSERT(isExceptionPending());
   1508  return unwrappedException();
   1509 }
   1510 #endif
   1511 
   1512 bool JSContext::isClosingGenerator() {
   1513  return isExceptionPending() &&
   1514         unwrappedException().isMagic(JS_GENERATOR_CLOSING);
   1515 }
   1516 
   1517 bool JSContext::isThrowingDebuggeeWouldRun() {
   1518  return isExceptionPending() && unwrappedException().isObject() &&
   1519         unwrappedException().toObject().is<ErrorObject>() &&
   1520         unwrappedException().toObject().as<ErrorObject>().type() ==
   1521             JSEXN_DEBUGGEEWOULDRUN;
   1522 }
   1523 
   1524 bool JSContext::isRuntimeCodeGenEnabled(
   1525    JS::RuntimeCode kind, JS::Handle<JSString*> codeString,
   1526    JS::CompilationType compilationType,
   1527    JS::Handle<JS::StackGCVector<JSString*>> parameterStrings,
   1528    JS::Handle<JSString*> bodyString,
   1529    JS::Handle<JS::StackGCVector<JS::Value>> parameterArgs,
   1530    JS::Handle<JS::Value> bodyArg, bool* outCanCompileStrings) {
   1531  // Make sure that the CSP callback is installed and that it permits runtime
   1532  // code generation.
   1533  if (JSCSPEvalChecker allows =
   1534          runtime()->securityCallbacks->contentSecurityPolicyAllows) {
   1535    return allows(this, kind, codeString, compilationType, parameterStrings,
   1536                  bodyString, parameterArgs, bodyArg, outCanCompileStrings);
   1537  }
   1538 
   1539  // Default implementation from the "Dynamic Code Brand Checks" spec.
   1540  // https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostensurecancompilestrings
   1541  *outCanCompileStrings = true;
   1542  return true;
   1543 }
   1544 
   1545 bool JSContext::getCodeForEval(HandleObject code,
   1546                               JS::MutableHandle<JSString*> outCode) {
   1547  if (JSCodeForEvalOp gets = runtime()->securityCallbacks->codeForEvalGets) {
   1548    return gets(this, code, outCode);
   1549  }
   1550  // Default implementation from the "Dynamic Code Brand Checks" spec.
   1551  // https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostgetcodeforeval
   1552  outCode.set(nullptr);
   1553  return true;
   1554 }
   1555 
   1556 size_t JSContext::sizeOfExcludingThis(
   1557    mozilla::MallocSizeOf mallocSizeOf) const {
   1558  /*
   1559   * There are other JSContext members that could be measured; the following
   1560   * ones have been found by DMD to be worth measuring.  More stuff may be
   1561   * added later.
   1562   */
   1563  return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf) +
   1564         irregexp::IsolateSizeOfIncludingThis(isolate, mallocSizeOf);
   1565 }
   1566 
   1567 size_t JSContext::sizeOfIncludingThis(
   1568    mozilla::MallocSizeOf mallocSizeOf) const {
   1569  return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
   1570 }
   1571 
   1572 #ifdef DEBUG
   1573 bool JSContext::inAtomsZone() const { return zone_->isAtomsZone(); }
   1574 #endif
   1575 
   1576 void JSContext::trace(JSTracer* trc) {
   1577  cycleDetectorVector().trace(trc);
   1578  geckoProfiler().trace(trc);
   1579  if (isolate) {
   1580    irregexp::TraceIsolate(trc, isolate.ref());
   1581  }
   1582 #ifdef ENABLE_WASM_JSPI
   1583  wasm().trace(trc);
   1584 #endif
   1585 }
   1586 
   1587 JS::NativeStackLimit JSContext::stackLimitForJitCode(JS::StackKind kind) {
   1588 #ifdef JS_SIMULATOR
   1589  return simulator()->stackLimit();
   1590 #else
   1591  return stackLimit(kind);
   1592 #endif
   1593 }
   1594 
   1595 void JSContext::resetJitStackLimit() {
   1596  // Note that, for now, we use the untrusted limit for ion. This is fine,
   1597  // because it's the most conservative limit, and if we hit it, we'll bail
   1598  // out of ion into the interpreter, which will do a proper recursion check.
   1599 #ifdef JS_SIMULATOR
   1600  jitStackLimit = jit::Simulator::StackLimit();
   1601 #else
   1602  jitStackLimit = nativeStackLimit[JS::StackForUntrustedScript];
   1603 #endif
   1604  jitStackLimitNoInterrupt = jitStackLimit;
   1605 }
   1606 
   1607 void JSContext::initJitStackLimit() {
   1608  resetJitStackLimit();
   1609  wasm_.initStackLimit(this);
   1610 }
   1611 
   1612 JSScript* JSContext::currentScript(jsbytecode** ppc,
   1613                                   AllowCrossRealm allowCrossRealm) {
   1614  if (ppc) {
   1615    *ppc = nullptr;
   1616  }
   1617 
   1618  // Fast path: there are no JS frames on the stack if there's no activation.
   1619  if (!activation()) {
   1620    return nullptr;
   1621  }
   1622 
   1623  FrameIter iter(this);
   1624  if (iter.done() || !iter.hasScript()) {
   1625    return nullptr;
   1626  }
   1627 
   1628  JSScript* script = iter.script();
   1629  if (allowCrossRealm == AllowCrossRealm::DontAllow &&
   1630      script->realm() != realm()) {
   1631    return nullptr;
   1632  }
   1633 
   1634  if (ppc) {
   1635    *ppc = iter.pc();
   1636  }
   1637  return script;
   1638 }
   1639 
   1640 #ifdef JS_CRASH_DIAGNOSTICS
   1641 void ContextChecks::check(AbstractFramePtr frame, int argIndex) {
   1642  if (frame) {
   1643    check(frame.realm(), argIndex);
   1644  }
   1645 }
   1646 #endif
   1647 
   1648 void AutoEnterOOMUnsafeRegion::crash_impl(const char* reason) {
   1649  char msgbuf[1024];
   1650  js::NoteIntentionalCrash();
   1651  SprintfLiteral(msgbuf, "[unhandlable oom] %s", reason);
   1652 #ifndef DEBUG
   1653  // In non-DEBUG builds MOZ_CRASH normally doesn't print to stderr so we have
   1654  // to do this explicitly (the jit-test allow-unhandlable-oom annotation and
   1655  // fuzzers depend on it).
   1656  fprintf(stderr, "Hit MOZ_CRASH(%s) at %s:%d\n", msgbuf, __FILE__, __LINE__);
   1657 #endif
   1658  MOZ_CRASH_UNSAFE(msgbuf);
   1659 }
   1660 
   1661 mozilla::Atomic<AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback,
   1662                mozilla::Relaxed>
   1663    AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback(nullptr);
   1664 
   1665 void AutoEnterOOMUnsafeRegion::crash_impl(size_t size, const char* reason) {
   1666  {
   1667    JS::AutoSuppressGCAnalysis suppress;
   1668    if (annotateOOMSizeCallback) {
   1669      annotateOOMSizeCallback(size);
   1670    }
   1671  }
   1672  crash_impl(reason);
   1673 }
   1674 
   1675 void ExternalValueArray::trace(JSTracer* trc) {
   1676  if (Value* vp = begin()) {
   1677    TraceRootRange(trc, length(), vp, "js::ExternalValueArray");
   1678  }
   1679 }
   1680 
   1681 #ifdef MOZ_EXECUTION_TRACING
   1682 
   1683 bool JSContext::enableExecutionTracing() {
   1684  if (!executionTracer_) {
   1685    for (RealmsIter realm(runtime()); !realm.done(); realm.next()) {
   1686      if (realm->debuggerObservesCoverage()) {
   1687        JS_ReportErrorNumberASCII(
   1688            this, GetErrorMessage, nullptr,
   1689            JSMSG_DEBUG_EXCLUSIVE_EXECUTION_TRACE_COVERAGE);
   1690        return false;
   1691      }
   1692    }
   1693 
   1694    executionTracer_ = js::MakeUnique<ExecutionTracer>();
   1695 
   1696    if (!executionTracer_) {
   1697      return false;
   1698    }
   1699 
   1700    if (!executionTracer_->init()) {
   1701      executionTracer_ = nullptr;
   1702      return false;
   1703    }
   1704 
   1705    for (RealmsIter realm(runtime()); !realm.done(); realm.next()) {
   1706      if (realm->isSystem()) {
   1707        continue;
   1708      }
   1709      realm->enableExecutionTracing();
   1710    }
   1711  }
   1712 
   1713  executionTracerSuspended_ = false;
   1714  return true;
   1715 }
   1716 
   1717 void JSContext::cleanUpExecutionTracingState() {
   1718  MOZ_ASSERT(executionTracer_);
   1719 
   1720  for (RealmsIter realm(runtime()); !realm.done(); realm.next()) {
   1721    if (realm->isSystem()) {
   1722      continue;
   1723    }
   1724    realm->disableExecutionTracing();
   1725  }
   1726 
   1727  caches().tracingCaches.clearAll();
   1728 }
   1729 
   1730 void JSContext::disableExecutionTracing() {
   1731  if (executionTracer_) {
   1732    cleanUpExecutionTracingState();
   1733    executionTracer_ = nullptr;
   1734  }
   1735 }
   1736 
   1737 void JSContext::suspendExecutionTracing() {
   1738  if (executionTracer_) {
   1739    cleanUpExecutionTracingState();
   1740    executionTracerSuspended_ = true;
   1741  }
   1742 }
   1743 
   1744 #endif
   1745 
   1746 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI
   1747 
   1748 AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness)
   1749    : cx_(TlsContext.get()),
   1750      nested_(cx_ ? cx_->hasAutoUnsafeCallWithABI : false),
   1751      nogc(cx_) {
   1752  if (!cx_) {
   1753    // This is a helper thread doing Ion or Wasm compilation - nothing to do.
   1754    return;
   1755  }
   1756  switch (strictness) {
   1757    case UnsafeABIStrictness::NoExceptions:
   1758      MOZ_ASSERT(!JS_IsExceptionPending(cx_));
   1759      checkForPendingException_ = true;
   1760      break;
   1761    case UnsafeABIStrictness::AllowPendingExceptions:
   1762      checkForPendingException_ = !JS_IsExceptionPending(cx_);
   1763      break;
   1764  }
   1765 
   1766  cx_->hasAutoUnsafeCallWithABI = true;
   1767 }
   1768 
   1769 AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI() {
   1770  if (!cx_) {
   1771    return;
   1772  }
   1773  MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI);
   1774  if (!nested_) {
   1775    cx_->hasAutoUnsafeCallWithABI = false;
   1776    cx_->inUnsafeCallWithABI = false;
   1777  }
   1778  MOZ_ASSERT_IF(checkForPendingException_, !JS_IsExceptionPending(cx_));
   1779 }
   1780 
   1781 #endif  // JS_CHECK_UNSAFE_CALL_WITH_ABI
   1782 
   1783 #ifdef __wasi__
   1784 JS_PUBLIC_API void js::IncWasiRecursionDepth(JSContext* cx) {
   1785  ++JS::RootingContext::get(cx)->wasiRecursionDepth;
   1786 }
   1787 
   1788 JS_PUBLIC_API void js::DecWasiRecursionDepth(JSContext* cx) {
   1789  MOZ_ASSERT(JS::RootingContext::get(cx)->wasiRecursionDepth > 0);
   1790  --JS::RootingContext::get(cx)->wasiRecursionDepth;
   1791 }
   1792 
   1793 JS_PUBLIC_API bool js::CheckWasiRecursionLimit(JSContext* cx) {
   1794  // WASI has two limits:
   1795  // 1) The stack pointer in linear memory that grows to zero. See
   1796  //    --stack-first in js/src/shell/moz.build.
   1797  // 2) The JS::RootingContext::wasiRecursionDepth that counts recursion depth.
   1798  //    Here we should check both.
   1799  if (JS::RootingContext::get(cx)->wasiRecursionDepth >=
   1800      JS::RootingContext::wasiRecursionDepthLimit) {
   1801    return false;
   1802  }
   1803  return true;
   1804 }
   1805 #endif  // __wasi__