LCOV - code coverage report
Current view: top level - js/src/vm - JSContext.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 143 607 23.6 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       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/ArrayUtils.h"
      14             : #include "mozilla/DebugOnly.h"
      15             : #include "mozilla/MemoryReporting.h"
      16             : #include "mozilla/Sprintf.h"
      17             : #include "mozilla/Unused.h"
      18             : 
      19             : #include <ctype.h>
      20             : #include <stdarg.h>
      21             : #include <string.h>
      22             : #ifdef ANDROID
      23             : # include <android/log.h>
      24             : # include <fstream>
      25             : # include <string>
      26             : #endif // ANDROID
      27             : #ifdef XP_WIN
      28             : # include <processthreadsapi.h>
      29             : #endif // XP_WIN
      30             : 
      31             : #include "jsexn.h"
      32             : #include "jspubtd.h"
      33             : #include "jstypes.h"
      34             : 
      35             : #include "builtin/String.h"
      36             : #include "gc/FreeOp.h"
      37             : #include "gc/Marking.h"
      38             : #include "jit/Ion.h"
      39             : #include "jit/PcScriptCache.h"
      40             : #include "js/CharacterEncoding.h"
      41             : #include "js/Printf.h"
      42             : #include "util/DoubleToString.h"
      43             : #include "util/NativeStack.h"
      44             : #include "util/Windows.h"
      45             : #include "vm/BytecodeUtil.h"
      46             : #include "vm/ErrorReporting.h"
      47             : #include "vm/HelperThreads.h"
      48             : #include "vm/Iteration.h"
      49             : #include "vm/JSAtom.h"
      50             : #include "vm/JSFunction.h"
      51             : #include "vm/JSObject.h"
      52             : #include "vm/JSScript.h"
      53             : #include "vm/Realm.h"
      54             : #include "vm/Shape.h"
      55             : 
      56             : #include "vm/Compartment-inl.h"
      57             : #include "vm/JSObject-inl.h"
      58             : #include "vm/JSScript-inl.h"
      59             : #include "vm/Stack-inl.h"
      60             : 
      61             : using namespace js;
      62             : using namespace js::gc;
      63             : 
      64             : using mozilla::DebugOnly;
      65             : using mozilla::PodArrayZero;
      66             : 
      67             : bool
      68        1283 : js::AutoCycleDetector::init()
      69             : {
      70        1283 :     MOZ_ASSERT(cyclic);
      71             : 
      72        2566 :     AutoCycleDetector::Vector& vector = cx->cycleDetectorVector();
      73             : 
      74           0 :     for (JSObject* obj2 : vector) {
      75           0 :         if (MOZ_UNLIKELY(obj == obj2))
      76             :             return true;
      77             :     }
      78             : 
      79        2566 :     if (!vector.append(obj))
      80             :         return false;
      81             : 
      82           0 :     cyclic = false;
      83        1283 :     return true;
      84             : }
      85             : 
      86        2566 : js::AutoCycleDetector::~AutoCycleDetector()
      87             : {
      88           0 :     if (MOZ_LIKELY(!cyclic)) {
      89           0 :         AutoCycleDetector::Vector& vec = cx->cycleDetectorVector();
      90           0 :         MOZ_ASSERT(vec.back() == obj);
      91        1283 :         if (vec.length() > 1) {
      92             :             vec.popBack();
      93             :         } else {
      94             :             // Avoid holding on to unused heap allocations.
      95             :             vec.clearAndFree();
      96             :         }
      97             :     }
      98        1283 : }
      99             : 
     100             : bool
     101           8 : JSContext::init(ContextKind kind)
     102             : {
     103             :     // Skip most of the initialization if this thread will not be running JS.
     104           0 :     if (kind == ContextKind::MainThread) {
     105           4 :         if (!regexpStack.ref().init())
     106             :             return false;
     107             : 
     108           4 :         if (!fx.initInstance())
     109             :             return false;
     110             : 
     111             : #ifdef JS_SIMULATOR
     112             :         simulator_ = jit::Simulator::Create(this);
     113             :         if (!simulator_)
     114             :             return false;
     115             : #endif
     116             : 
     117           4 :         if (!wasm::EnsureSignalHandlers(this))
     118             :             return false;
     119             :     }
     120             : 
     121             :     // Set the ContextKind last, so that ProtectedData checks will allow us to
     122             :     // initialize this context before it becomes the runtime's active context.
     123           8 :     kind_ = kind;
     124             : 
     125             :     return true;
     126             : }
     127             : 
     128             : JSContext*
     129           4 : js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
     130             : {
     131           4 :     AutoNoteSingleThreadedRegion anstr;
     132             : 
     133           4 :     MOZ_RELEASE_ASSERT(!TlsContext.get());
     134             : 
     135             : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     136           0 :     js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_MAIN
     137           4 :                                           : js::THREAD_TYPE_WORKER);
     138             : #endif
     139             : 
     140           0 :     JSRuntime* runtime = js_new<JSRuntime>(parentRuntime);
     141           4 :     if (!runtime)
     142             :         return nullptr;
     143             : 
     144           0 :     JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
     145           0 :     if (!cx) {
     146           0 :         js_delete(runtime);
     147             :         return nullptr;
     148             :     }
     149             : 
     150           0 :     if (!runtime->init(cx, maxBytes, maxNurseryBytes)) {
     151           0 :         runtime->destroyRuntime();
     152           0 :         js_delete(cx);
     153           0 :         js_delete(runtime);
     154             :         return nullptr;
     155             :     }
     156             : 
     157           0 :     if (!cx->init(ContextKind::MainThread)) {
     158           0 :         runtime->destroyRuntime();
     159           0 :         js_delete(cx);
     160           0 :         js_delete(runtime);
     161             :         return nullptr;
     162             :     }
     163             : 
     164             :     return cx;
     165             : }
     166             : 
     167             : static void
     168           0 : FreeJobQueueHandling(JSContext* cx)
     169             : {
     170           0 :     if (!cx->jobQueue)
     171             :         return;
     172             : 
     173           0 :     cx->jobQueue->reset();
     174           0 :     FreeOp* fop = cx->defaultFreeOp();
     175           0 :     fop->delete_(cx->jobQueue.ref());
     176           0 :     cx->getIncumbentGlobalCallback = nullptr;
     177           0 :     cx->enqueuePromiseJobCallback = nullptr;
     178           0 :     cx->enqueuePromiseJobCallbackData = nullptr;
     179             : }
     180             : 
     181             : void
     182           0 : js::DestroyContext(JSContext* cx)
     183             : {
     184           0 :     JS_AbortIfWrongThread(cx);
     185             : 
     186           0 :     if (cx->outstandingRequests != 0)
     187           0 :         MOZ_CRASH("Attempted to destroy a context while it is in a request.");
     188             : 
     189           0 :     cx->checkNoGCRooters();
     190             : 
     191             :     // Cancel all off thread Ion compiles. Completed Ion compiles may try to
     192             :     // interrupt this context. See HelperThread::handleIonWorkload.
     193           0 :     CancelOffThreadIonCompile(cx->runtime());
     194             : 
     195           0 :     FreeJobQueueHandling(cx);
     196             : 
     197             :     // Flush promise tasks executing in helper threads early, before any parts
     198             :     // of the JSRuntime that might be visible to helper threads are torn down.
     199           0 :     cx->runtime()->offThreadPromiseState.ref().shutdown(cx);
     200             : 
     201             :     // Destroy the runtime along with its last context.
     202           0 :     cx->runtime()->destroyRuntime();
     203           0 :     js_delete(cx->runtime());
     204           0 :     js_delete_poison(cx);
     205           0 : }
     206             : 
     207             : void
     208           0 : JS::RootingContext::checkNoGCRooters() {
     209             : #ifdef DEBUG
     210           0 :     for (auto const& stackRootPtr : stackRoots_)
     211           0 :         MOZ_ASSERT(stackRootPtr == nullptr);
     212             : #endif
     213           0 : }
     214             : 
     215             : bool
     216         152 : AutoResolving::alreadyStartedSlow() const
     217             : {
     218         152 :     MOZ_ASSERT(link);
     219             :     AutoResolving* cursor = link;
     220           0 :     do {
     221           0 :         MOZ_ASSERT(this != cursor);
     222         456 :         if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind)
     223             :             return true;
     224         152 :     } while (!!(cursor = cursor->link));
     225             :     return false;
     226             : }
     227             : 
     228             : static void
     229          22 : ReportError(JSContext* cx, JSErrorReport* reportp, JSErrorCallback callback,
     230             :             void* userRef)
     231             : {
     232             :     /*
     233             :      * Check the error report, and set a JavaScript-catchable exception
     234             :      * if the error is defined to have an associated exception.  If an
     235             :      * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
     236             :      * on the error report, and exception-aware hosts should ignore it.
     237             :      */
     238           0 :     MOZ_ASSERT(reportp);
     239           0 :     if ((!callback || callback == GetErrorMessage) &&
     240          10 :         reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
     241             :     {
     242           0 :         reportp->flags |= JSREPORT_EXCEPTION;
     243             :     }
     244             : 
     245           0 :     if (JSREPORT_IS_WARNING(reportp->flags)) {
     246           0 :         CallWarningReporter(cx, reportp);
     247           0 :         return;
     248             :     }
     249             : 
     250          22 :     ErrorToException(cx, reportp, callback, userRef);
     251             : }
     252             : 
     253             : /*
     254             :  * The given JSErrorReport object have been zeroed and must not outlive
     255             :  * cx->fp() (otherwise owned fields may become invalid).
     256             :  */
     257             : static void
     258          22 : PopulateReportBlame(JSContext* cx, JSErrorReport* report)
     259             : {
     260           0 :     JS::Realm* realm = cx->realm();
     261           0 :     if (!realm)
     262           0 :         return;
     263             : 
     264             :     /*
     265             :      * Walk stack until we find a frame that is associated with a non-builtin
     266             :      * rather than a builtin frame and which we're allowed to know about.
     267             :      */
     268           0 :     NonBuiltinFrameIter iter(cx, realm->principals());
     269          22 :     if (iter.done())
     270             :         return;
     271             : 
     272          22 :     report->filename = iter.filename();
     273             :     uint32_t column;
     274           0 :     report->lineno = iter.computeLine(&column);
     275           0 :     report->column = FixupColumnForDisplay(column);
     276          22 :     report->isMuted = iter.mutedErrors();
     277             : }
     278             : 
     279             : /*
     280             :  * Since memory has been exhausted, avoid the normal error-handling path which
     281             :  * allocates an error object, report and callstack. If code is running, simply
     282             :  * throw the static atom "out of memory". If code is not running, call the
     283             :  * error reporter directly.
     284             :  *
     285             :  * Furthermore, callers of ReportOutOfMemory (viz., malloc) assume a GC does
     286             :  * not occur, so GC must be avoided or suppressed.
     287             :  */
     288             : JS_FRIEND_API(void)
     289           0 : js::ReportOutOfMemory(JSContext* cx)
     290             : {
     291             : #ifdef JS_MORE_DETERMINISTIC
     292             :     /*
     293             :      * OOMs are non-deterministic, especially across different execution modes
     294             :      * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr
     295             :      * so that the fuzzers can detect this.
     296             :      */
     297             :     fprintf(stderr, "ReportOutOfMemory called\n");
     298             : #endif
     299             : 
     300           0 :     if (cx->helperThread())
     301           0 :         return cx->addPendingOutOfMemory();
     302             : 
     303           0 :     cx->runtime()->hadOutOfMemory = true;
     304           0 :     AutoSuppressGC suppressGC(cx);
     305             : 
     306             :     /* Report the oom. */
     307           0 :     if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback)
     308           0 :         oomCallback(cx, cx->runtime()->oomCallbackData);
     309             : 
     310           0 :     cx->setPendingException(StringValue(cx->names().outOfMemory));
     311             : }
     312             : 
     313             : mozilla::GenericErrorResult<OOM&>
     314           0 : js::ReportOutOfMemoryResult(JSContext* cx)
     315             : {
     316           0 :     ReportOutOfMemory(cx);
     317           0 :     return cx->alreadyReportedOOM();
     318             : }
     319             : 
     320             : void
     321           0 : js::ReportOverRecursed(JSContext* maybecx, unsigned errorNumber)
     322             : {
     323             : #ifdef JS_MORE_DETERMINISTIC
     324             :     /*
     325             :      * We cannot make stack depth deterministic across different
     326             :      * implementations (e.g. JIT vs. interpreter will differ in
     327             :      * their maximum stack depth).
     328             :      * However, we can detect externally when we hit the maximum
     329             :      * stack depth which is useful for external testing programs
     330             :      * like fuzzers.
     331             :      */
     332             :     fprintf(stderr, "ReportOverRecursed called\n");
     333             : #endif
     334           0 :     if (maybecx) {
     335           0 :         if (!maybecx->helperThread()) {
     336           0 :             JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr, errorNumber);
     337           0 :             maybecx->overRecursed_ = true;
     338             :         } else {
     339           0 :             maybecx->addPendingOverRecursed();
     340             :         }
     341             :     }
     342           0 : }
     343             : 
     344             : JS_FRIEND_API(void)
     345           0 : js::ReportOverRecursed(JSContext* maybecx)
     346             : {
     347           0 :     ReportOverRecursed(maybecx, JSMSG_OVER_RECURSED);
     348           0 : }
     349             : 
     350             : void
     351           0 : js::ReportAllocationOverflow(JSContext* cx)
     352             : {
     353           0 :     if (!cx)
     354           0 :         return;
     355             : 
     356           0 :     if (cx->helperThread())
     357             :         return;
     358             : 
     359           0 :     AutoSuppressGC suppressGC(cx);
     360           0 :     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW);
     361             : }
     362             : 
     363             : /*
     364             :  * Given flags and the state of cx, decide whether we should report an
     365             :  * error, a warning, or just continue execution normally.  Return
     366             :  * true if we should continue normally, without reporting anything;
     367             :  * otherwise, adjust *flags as appropriate and return false.
     368             :  */
     369             : static bool
     370          22 : checkReportFlags(JSContext* cx, unsigned* flags)
     371             : {
     372          22 :     if (JSREPORT_IS_STRICT(*flags)) {
     373             :         /* Warning/error only when JSOPTION_STRICT is set. */
     374           0 :         if (!cx->realm()->behaviors().extraWarnings(cx))
     375             :             return true;
     376             :     }
     377             : 
     378             :     /* Warnings become errors when JSOPTION_WERROR is set. */
     379           0 :     if (JSREPORT_IS_WARNING(*flags) && cx->options().werror())
     380           0 :         *flags &= ~JSREPORT_WARNING;
     381             : 
     382             :     return false;
     383             : }
     384             : 
     385             : bool
     386           8 : js::ReportErrorVA(JSContext* cx, unsigned flags, const char* format,
     387             :                   ErrorArgumentsType argumentsType, va_list ap)
     388             : {
     389          16 :     JSErrorReport report;
     390             : 
     391           8 :     if (checkReportFlags(cx, &flags))
     392             :         return true;
     393             : 
     394           0 :     UniqueChars message(JS_vsmprintf(format, ap));
     395           0 :     if (!message) {
     396           0 :         ReportOutOfMemory(cx);
     397           0 :         return false;
     398             :     }
     399             : 
     400          10 :     MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(message.get()));
     401             : 
     402           0 :     report.flags = flags;
     403           0 :     report.errorNumber = JSMSG_USER_DEFINED_ERROR;
     404           0 :     if (argumentsType == ArgumentsAreASCII || argumentsType == ArgumentsAreUTF8) {
     405           8 :         report.initOwnedMessage(message.release());
     406             :     } else {
     407           0 :         MOZ_ASSERT(argumentsType == ArgumentsAreLatin1);
     408           0 :         Latin1Chars latin1(message.get(), strlen(message.get()));
     409           0 :         UTF8CharsZ utf8(JS::CharsToNewUTF8CharsZ(cx, latin1));
     410           0 :         if (!utf8)
     411           0 :             return false;
     412           0 :         report.initOwnedMessage(reinterpret_cast<const char*>(utf8.get()));
     413             :     }
     414           8 :     PopulateReportBlame(cx, &report);
     415             : 
     416           8 :     bool warning = JSREPORT_IS_WARNING(report.flags);
     417             : 
     418           0 :     ReportError(cx, &report, nullptr, nullptr);
     419           8 :     return warning;
     420             : }
     421             : 
     422             : /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
     423             : void
     424           0 : js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg)
     425             : {
     426           0 :     RootedValue usage(cx);
     427           0 :     if (!JS_GetProperty(cx, callee, "usage", &usage))
     428           0 :         return;
     429             : 
     430           0 :     if (!usage.isString()) {
     431           0 :         JS_ReportErrorASCII(cx, "%s", msg);
     432             :     } else {
     433           0 :         RootedString usageStr(cx, usage.toString());
     434           0 :         JSAutoByteString str;
     435           0 :         if (!str.encodeUtf8(cx, usageStr))
     436           0 :             return;
     437           0 :         JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.ptr());
     438             :     }
     439             : }
     440             : 
     441             : enum class PrintErrorKind {
     442             :     Error,
     443             :     Warning,
     444             :     StrictWarning,
     445             :     Note
     446             : };
     447             : 
     448             : static void
     449           0 : PrintErrorLine(FILE* file, const char* prefix, JSErrorReport* report)
     450             : {
     451           0 :     if (const char16_t* linebuf = report->linebuf()) {
     452           0 :         size_t n = report->linebufLength();
     453             : 
     454           0 :         fputs(":\n", file);
     455           0 :         if (prefix)
     456           0 :             fputs(prefix, file);
     457             : 
     458           0 :         for (size_t i = 0; i < n; i++)
     459           0 :             fputc(static_cast<char>(linebuf[i]), file);
     460             : 
     461             :         // linebuf usually ends with a newline. If not, add one here.
     462           0 :         if (n == 0 || linebuf[n-1] != '\n')
     463           0 :             fputc('\n', file);
     464             : 
     465           0 :         if (prefix)
     466           0 :             fputs(prefix, file);
     467             : 
     468           0 :         n = report->tokenOffset();
     469           0 :         for (size_t i = 0, j = 0; i < n; i++) {
     470           0 :             if (linebuf[i] == '\t') {
     471           0 :                 for (size_t k = (j + 8) & ~7; j < k; j++)
     472           0 :                     fputc('.', file);
     473             :                 continue;
     474             :             }
     475           0 :             fputc('.', file);
     476           0 :             j++;
     477             :         }
     478           0 :         fputc('^', file);
     479             :     }
     480           0 : }
     481             : 
     482             : static void
     483             : PrintErrorLine(FILE* file, const char* prefix, JSErrorNotes::Note* note)
     484             : {
     485             : }
     486             : 
     487             : template <typename T>
     488             : static bool
     489           0 : PrintSingleError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
     490             :                  T* report, PrintErrorKind kind)
     491             : {
     492           0 :     UniqueChars prefix;
     493           0 :     if (report->filename)
     494           0 :         prefix = JS_smprintf("%s:", report->filename);
     495             : 
     496           0 :     if (report->lineno) {
     497           0 :         prefix = JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno,
     498             :                                         report->column);
     499             :     }
     500             : 
     501           0 :     if (kind != PrintErrorKind::Error) {
     502           0 :         const char* kindPrefix = nullptr;
     503           0 :         switch (kind) {
     504             :           case PrintErrorKind::Error:
     505           0 :             MOZ_CRASH("unreachable");
     506             :           case PrintErrorKind::Warning:
     507           0 :             kindPrefix = "warning";
     508             :             break;
     509             :           case PrintErrorKind::StrictWarning:
     510           0 :             kindPrefix = "strict warning";
     511             :             break;
     512             :           case PrintErrorKind::Note:
     513           0 :             kindPrefix = "note";
     514             :             break;
     515             :         }
     516             : 
     517           0 :         prefix = JS_smprintf("%s%s: ", prefix ? prefix.get() : "", kindPrefix);
     518             :     }
     519             : 
     520           0 :     const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str();
     521             : 
     522             :     /* embedded newlines -- argh! */
     523             :     const char* ctmp;
     524           0 :     while ((ctmp = strchr(message, '\n')) != 0) {
     525           0 :         ctmp++;
     526           0 :         if (prefix)
     527           0 :             fputs(prefix.get(), file);
     528           0 :         mozilla::Unused << fwrite(message, 1, ctmp - message, file);
     529           0 :         message = ctmp;
     530             :     }
     531             : 
     532             :     /* If there were no filename or lineno, the prefix might be empty */
     533           0 :     if (prefix)
     534           0 :         fputs(prefix.get(), file);
     535           0 :     fputs(message, file);
     536             : 
     537           0 :     PrintErrorLine(file, prefix.get(), report);
     538           0 :     fputc('\n', file);
     539             : 
     540           0 :     fflush(file);
     541           0 :     return true;
     542             : }
     543             : 
     544             : bool
     545           0 : js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
     546             :                JSErrorReport* report, bool reportWarnings)
     547             : {
     548           0 :     MOZ_ASSERT(report);
     549             : 
     550             :     /* Conditionally ignore reported warnings. */
     551           0 :     if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
     552             :         return false;
     553             : 
     554           0 :     PrintErrorKind kind = PrintErrorKind::Error;
     555           0 :     if (JSREPORT_IS_WARNING(report->flags)) {
     556           0 :         if (JSREPORT_IS_STRICT(report->flags))
     557             :             kind = PrintErrorKind::StrictWarning;
     558             :         else
     559           0 :             kind = PrintErrorKind::Warning;
     560             :     }
     561           0 :     PrintSingleError(cx, file, toStringResult, report, kind);
     562             : 
     563           0 :     if (report->notes) {
     564           0 :         for (auto&& note : *report->notes)
     565           0 :             PrintSingleError(cx, file, JS::ConstUTF8CharsZ(), note.get(), PrintErrorKind::Note);
     566             :     }
     567             : 
     568             :     return true;
     569             : }
     570             : 
     571             : class MOZ_RAII AutoMessageArgs
     572             : {
     573             :     size_t totalLength_;
     574             :     /* only {0} thru {9} supported */
     575             :     mozilla::Array<const char*, JS::MaxNumErrorArguments> args_;
     576             :     mozilla::Array<size_t, JS::MaxNumErrorArguments> lengths_;
     577             :     uint16_t count_;
     578             :     bool allocatedElements_ : 1;
     579             : 
     580             :   public:
     581             :     AutoMessageArgs()
     582          42 :       : totalLength_(0), count_(0), allocatedElements_(false)
     583             :     {
     584          28 :         PodArrayZero(args_);
     585             :     }
     586             : 
     587           0 :     ~AutoMessageArgs()
     588          14 :     {
     589             :         /* free the arguments only if we allocated them */
     590          14 :         if (allocatedElements_) {
     591             :             uint16_t i = 0;
     592           0 :             while (i < count_) {
     593           0 :                 if (args_[i])
     594           0 :                     js_free((void*)args_[i]);
     595          16 :                 i++;
     596             :             }
     597             :         }
     598          14 :     }
     599             : 
     600           0 :     const char* args(size_t i) const {
     601           0 :         MOZ_ASSERT(i < count_);
     602          32 :         return args_[i];
     603             :     }
     604             : 
     605             :     size_t totalLength() const {
     606             :         return totalLength_;
     607             :     }
     608             : 
     609           0 :     size_t lengths(size_t i) const {
     610           0 :         MOZ_ASSERT(i < count_);
     611          64 :         return lengths_[i];
     612             :     }
     613             : 
     614             :     uint16_t count() const {
     615             :         return count_;
     616             :     }
     617             : 
     618             :     /* Gather the arguments into an array, and accumulate their sizes. */
     619          14 :     bool init(JSContext* cx, const char16_t** argsArg, uint16_t countArg,
     620             :               ErrorArgumentsType typeArg, va_list ap) {
     621          14 :         MOZ_ASSERT(countArg > 0);
     622             : 
     623          14 :         count_ = countArg;
     624             : 
     625           0 :         for (uint16_t i = 0; i < count_; i++) {
     626          16 :             switch (typeArg) {
     627             :               case ArgumentsAreASCII:
     628             :               case ArgumentsAreUTF8: {
     629           0 :                 MOZ_ASSERT(!argsArg);
     630           0 :                 args_[i] = va_arg(ap, char*);
     631           0 :                 MOZ_ASSERT_IF(typeArg == ArgumentsAreASCII, JS::StringIsASCII(args_[i]));
     632           0 :                 lengths_[i] = strlen(args_[i]);
     633           0 :                 break;
     634             :               }
     635             :               case ArgumentsAreLatin1: {
     636           0 :                 MOZ_ASSERT(!argsArg);
     637           0 :                 const Latin1Char* latin1 = va_arg(ap, Latin1Char*);
     638           0 :                 size_t len = strlen(reinterpret_cast<const char*>(latin1));
     639           0 :                 mozilla::Range<const Latin1Char> range(latin1, len);
     640           0 :                 char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str();
     641           0 :                 if (!utf8)
     642           0 :                     return false;
     643             : 
     644           0 :                 args_[i] = utf8;
     645           0 :                 lengths_[i] = strlen(utf8);
     646           0 :                 allocatedElements_ = true;
     647           4 :                 break;
     648             :               }
     649             :               case ArgumentsAreUnicode: {
     650           0 :                 const char16_t* uc = argsArg ? argsArg[i] : va_arg(ap, char16_t*);
     651           0 :                 size_t len = js_strlen(uc);
     652           0 :                 mozilla::Range<const char16_t> range(uc, len);
     653           0 :                 char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str();
     654           1 :                 if (!utf8)
     655           0 :                     return false;
     656             : 
     657           0 :                 args_[i] = utf8;
     658           0 :                 lengths_[i] = strlen(utf8);
     659           0 :                 allocatedElements_ = true;
     660          12 :                 break;
     661             :               }
     662             :             }
     663          32 :             totalLength_ += lengths_[i];
     664             :         }
     665             :         return true;
     666             :     }
     667             : };
     668             : 
     669             : static void
     670             : SetExnType(JSErrorReport* reportp, int16_t exnType)
     671             : {
     672          14 :     reportp->exnType = exnType;
     673             : }
     674             : 
     675             : static void
     676             : SetExnType(JSErrorNotes::Note* notep, int16_t exnType)
     677             : {
     678             :     // Do nothing for JSErrorNotes::Note.
     679             : }
     680             : 
     681             : /*
     682             :  * The arguments from ap need to be packaged up into an array and stored
     683             :  * into the report struct.
     684             :  *
     685             :  * The format string addressed by the error number may contain operands
     686             :  * identified by the format {N}, where N is a decimal digit. Each of these
     687             :  * is to be replaced by the Nth argument from the va_list. The complete
     688             :  * message is placed into reportp->message_.
     689             :  *
     690             :  * Returns true if the expansion succeeds (can fail if out of memory).
     691             :  */
     692             : template <typename T>
     693             : bool
     694          14 : ExpandErrorArgumentsHelper(JSContext* cx, JSErrorCallback callback,
     695             :                            void* userRef, const unsigned errorNumber,
     696             :                            const char16_t** messageArgs,
     697             :                            ErrorArgumentsType argumentsType,
     698             :                            T* reportp, va_list ap)
     699             : {
     700             :     const JSErrorFormatString* efs;
     701             : 
     702           1 :     if (!callback)
     703           0 :         callback = GetErrorMessage;
     704             : 
     705             :     {
     706           0 :         AutoSuppressGC suppressGC(cx);
     707          14 :         efs = callback(userRef, errorNumber);
     708             :     }
     709             : 
     710           0 :     if (efs) {
     711          28 :         SetExnType(reportp, efs->exnType);
     712             : 
     713          14 :         MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII, JS::StringIsASCII(efs->format));
     714             : 
     715           0 :         uint16_t argCount = efs->argCount;
     716           0 :         MOZ_RELEASE_ASSERT(argCount <= JS::MaxNumErrorArguments);
     717          14 :         if (argCount > 0) {
     718             :             /*
     719             :              * Parse the error format, substituting the argument X
     720             :              * for {X} in the format.
     721             :              */
     722          14 :             if (efs->format) {
     723             :                 const char* fmt;
     724             :                 char* out;
     725             : #ifdef DEBUG
     726          14 :                 int expandedArgs = 0;
     727             : #endif
     728             :                 size_t expandedLength;
     729          14 :                 size_t len = strlen(efs->format);
     730             : 
     731           0 :                 AutoMessageArgs args;
     732           0 :                 if (!args.init(cx, messageArgs, argCount, argumentsType, ap))
     733           0 :                     return false;
     734             : 
     735           0 :                 expandedLength = len
     736           0 :                                  - (3 * args.count()) /* exclude the {n} */
     737          14 :                                  + args.totalLength();
     738             : 
     739             :                 /*
     740             :                 * Note - the above calculation assumes that each argument
     741             :                 * is used once and only once in the expansion !!!
     742             :                 */
     743           0 :                 char* utf8 = out = cx->pod_malloc<char>(expandedLength + 1);
     744          14 :                 if (!out)
     745             :                     return false;
     746             : 
     747           0 :                 fmt = efs->format;
     748           0 :                 while (*fmt) {
     749           0 :                     if (*fmt == '{') {
     750           0 :                         if (isdigit(fmt[1])) {
     751           0 :                             int d = JS7_UNDEC(fmt[1]);
     752           0 :                             MOZ_RELEASE_ASSERT(d < args.count());
     753           0 :                             strncpy(out, args.args(d), args.lengths(d));
     754           0 :                             out += args.lengths(d);
     755          16 :                             fmt += 3;
     756             : #ifdef DEBUG
     757          16 :                             expandedArgs++;
     758             : #endif
     759          16 :                             continue;
     760             :                         }
     761             :                     }
     762         248 :                     *out++ = *fmt++;
     763             :                 }
     764           0 :                 MOZ_ASSERT(expandedArgs == args.count());
     765          14 :                 *out = 0;
     766             : 
     767          28 :                 reportp->initOwnedMessage(utf8);
     768             :             }
     769             :         } else {
     770             :             /* Non-null messageArgs should have at least one non-null arg. */
     771           0 :             MOZ_ASSERT(!messageArgs);
     772             :             /*
     773             :              * Zero arguments: the format string (if it exists) is the
     774             :              * entire message.
     775             :              */
     776           0 :             if (efs->format)
     777           0 :                 reportp->initBorrowedMessage(efs->format);
     778             :         }
     779             :     }
     780          14 :     if (!reportp->message()) {
     781             :         /* where's the right place for this ??? */
     782             :         const char* defaultErrorMessage
     783           0 :             = "No error message available for error number %d";
     784           0 :         size_t nbytes = strlen(defaultErrorMessage) + 16;
     785           0 :         char* message = cx->pod_malloc<char>(nbytes);
     786           0 :         if (!message)
     787             :             return false;
     788           0 :         snprintf(message, nbytes, defaultErrorMessage, errorNumber);
     789           0 :         reportp->initOwnedMessage(message);
     790             :     }
     791             :     return true;
     792             : }
     793             : 
     794             : bool
     795           0 : js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
     796             :                            void* userRef, const unsigned errorNumber,
     797             :                            const char16_t** messageArgs,
     798             :                            ErrorArgumentsType argumentsType,
     799             :                            JSErrorReport* reportp, va_list ap)
     800             : {
     801             :     return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
     802          14 :                                       messageArgs, argumentsType, reportp, ap);
     803             : }
     804             : 
     805             : bool
     806           0 : js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
     807             :                            void* userRef, const unsigned errorNumber,
     808             :                            const char16_t** messageArgs,
     809             :                            ErrorArgumentsType argumentsType,
     810             :                            JSErrorNotes::Note* notep, va_list ap)
     811             : {
     812             :     return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
     813           0 :                                       messageArgs, argumentsType, notep, ap);
     814             : }
     815             : 
     816             : bool
     817           2 : js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback,
     818             :                         void* userRef, const unsigned errorNumber,
     819             :                         ErrorArgumentsType argumentsType, va_list ap)
     820             : {
     821           4 :     JSErrorReport report;
     822             :     bool warning;
     823             : 
     824           2 :     if (checkReportFlags(cx, &flags))
     825             :         return true;
     826           2 :     warning = JSREPORT_IS_WARNING(flags);
     827             : 
     828           0 :     report.flags = flags;
     829           0 :     report.errorNumber = errorNumber;
     830           2 :     PopulateReportBlame(cx, &report);
     831             : 
     832           2 :     if (!ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
     833             :                                 nullptr, argumentsType, &report, ap)) {
     834             :         return false;
     835             :     }
     836             : 
     837           2 :     ReportError(cx, &report, callback, userRef);
     838             : 
     839           2 :     return warning;
     840             : }
     841             : 
     842             : static bool
     843          12 : ExpandErrorArguments(JSContext* cx, JSErrorCallback callback,
     844             :                      void* userRef, const unsigned errorNumber,
     845             :                      const char16_t** messageArgs,
     846             :                      ErrorArgumentsType argumentsType,
     847             :                      JSErrorReport* reportp, ...)
     848             : {
     849             :     va_list ap;
     850          12 :     va_start(ap, reportp);
     851             :     bool expanded = js::ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
     852           0 :                                                messageArgs, argumentsType, reportp, ap);
     853           0 :     va_end(ap);
     854          12 :     return expanded;
     855             : }
     856             : 
     857             : bool
     858          12 : js::ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback callback,
     859             :                              void* userRef, const unsigned errorNumber,
     860             :                              const char16_t** args)
     861             : {
     862          12 :     if (checkReportFlags(cx, &flags))
     863             :         return true;
     864          12 :     bool warning = JSREPORT_IS_WARNING(flags);
     865             : 
     866           0 :     JSErrorReport report;
     867           0 :     report.flags = flags;
     868           0 :     report.errorNumber = errorNumber;
     869          12 :     PopulateReportBlame(cx, &report);
     870             : 
     871          12 :     if (!ExpandErrorArguments(cx, callback, userRef, errorNumber,
     872             :                               args, ArgumentsAreUnicode, &report))
     873             :     {
     874             :         return false;
     875             :     }
     876             : 
     877          12 :     ReportError(cx, &report, callback, userRef);
     878             : 
     879          12 :     return warning;
     880             : }
     881             : 
     882             : void
     883           0 : js::ReportIsNotDefined(JSContext* cx, HandleId id)
     884             : {
     885           0 :     JSAutoByteString printable;
     886           0 :     if (!ValueToPrintableUTF8(cx, IdToValue(id), &printable))
     887           0 :         return;
     888           0 :     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, printable.ptr());
     889             : }
     890             : 
     891             : void
     892           0 : js::ReportIsNotDefined(JSContext* cx, HandlePropertyName name)
     893             : {
     894           0 :     RootedId id(cx, NameToId(name));
     895           0 :     ReportIsNotDefined(cx, id);
     896           0 : }
     897             : 
     898             : void
     899           2 : js::ReportIsNullOrUndefined(JSContext* cx, int spindex, HandleValue v)
     900             : {
     901             :     MOZ_ASSERT(v.isNullOrUndefined());
     902             : 
     903             :     UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, nullptr);
     904           0 :     if (!bytes)
     905           2 :         return;
     906             : 
     907             :     if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
     908           0 :         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
     909           0 :                                    bytes.get());
     910           0 :     } else if (v.isUndefined()) {
     911             :         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
     912             :                                    bytes.get(), js_undefined_str);
     913           0 :     } else {
     914           0 :         MOZ_ASSERT(v.isNull());
     915           1 :         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
     916             :                                    bytes.get(), js_null_str);
     917             :     }
     918           1 : }
     919             : 
     920           0 : void
     921           1 : js::ReportMissingArg(JSContext* cx, HandleValue v, unsigned arg)
     922             : {
     923             :     char argbuf[11];
     924           1 :     UniqueChars bytes;
     925             : 
     926             :     SprintfLiteral(argbuf, "%u", arg);
     927             :     if (IsFunctionObject(v)) {
     928             :         RootedAtom name(cx, v.toObject().as<JSFunction>().explicitName());
     929             :         bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, name);
     930             :         if (!bytes)
     931           0 :             return;
     932             :     }
     933             :     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
     934           0 :                                JSMSG_MISSING_FUN_ARG,
     935             :                                argbuf, bytes ? bytes.get() : "");
     936           0 : }
     937           0 : 
     938           0 : bool
     939           0 : js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber,
     940           0 :                           int spindex, HandleValue v, HandleString fallback,
     941           0 :                           const char* arg1, const char* arg2)
     942             : {
     943           0 :     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
     944             :     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
     945           0 :     UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback);
     946             :     if (!bytes)
     947             :         return false;
     948             : 
     949           0 :     return JS_ReportErrorFlagsAndNumberLatin1(cx, flags, GetErrorMessage, nullptr, errorNumber,
     950             :                                               bytes.get(), arg1, arg2);
     951             : }
     952             : 
     953           0 : JSObject*
     954             : js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report)
     955             : {
     956           0 :     RootedArrayObject notesArray(cx, NewDenseEmptyArray(cx));
     957           0 :     if (!notesArray)
     958           0 :         return nullptr;
     959           0 : 
     960             :     if (!report->notes)
     961             :         return notesArray;
     962           0 : 
     963           0 :     for (auto&& note : *report->notes) {
     964           0 :         RootedPlainObject noteObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     965             :         if (!noteObj)
     966             :             return nullptr;
     967             : 
     968           0 :         RootedString messageStr(cx, note->newMessageString(cx));
     969             :         if (!messageStr)
     970           0 :             return nullptr;
     971           0 :         RootedValue messageVal(cx, StringValue(messageStr));
     972             :         if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal))
     973             :             return nullptr;
     974           0 : 
     975             :         RootedValue filenameVal(cx);
     976             :         if (note->filename) {
     977           0 :             RootedString filenameStr(cx, NewStringCopyZ<CanGC>(cx, note->filename));
     978           0 :             if (!filenameStr)
     979           0 :                 return nullptr;
     980           0 :             filenameVal = StringValue(filenameStr);
     981             :         }
     982           0 :         if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal))
     983           0 :             return nullptr;
     984           0 : 
     985           0 :         RootedValue linenoVal(cx, Int32Value(note->lineno));
     986           0 :         if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal))
     987           0 :             return nullptr;
     988             :         RootedValue columnVal(cx, Int32Value(note->column));
     989           0 :         if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal))
     990           0 :             return nullptr;
     991           0 : 
     992           0 :         if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj)))
     993           0 :             return nullptr;
     994           0 :     }
     995             : 
     996           0 :     return notesArray;
     997             : }
     998             : 
     999           0 : const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
    1000           0 : #define MSG_DEF(name, count, exception, format) \
    1001           0 :     { #name, format, count, exception } ,
    1002           0 : #include "js.msg"
    1003           0 : #undef MSG_DEF
    1004           0 : };
    1005             : 
    1006           0 : JS_FRIEND_API(const JSErrorFormatString*)
    1007             : js::GetErrorMessage(void* userRef, const unsigned errorNumber)
    1008             : {
    1009             :     if (errorNumber > 0 && errorNumber < JSErr_Limit)
    1010           0 :         return &js_ErrorFormatString[errorNumber];
    1011             :     return nullptr;
    1012             : }
    1013             : 
    1014             : void
    1015             : JSContext::recoverFromOutOfMemory()
    1016             : {
    1017             :     if (helperThread()) {
    1018             :         // Keep in sync with addPendingOutOfMemory.
    1019             :         if (ParseTask* task = helperThread()->parseTask())
    1020             :             task->outOfMemory = false;
    1021          14 :     } else {
    1022             :         if (isExceptionPending()) {
    1023           0 :             MOZ_ASSERT(isThrowingOutOfMemory());
    1024          14 :             clearPendingException();
    1025             :         }
    1026             :     }
    1027             : }
    1028             : 
    1029           0 : static bool
    1030             : InternalEnqueuePromiseJobCallback(JSContext* cx, JS::HandleObject job,
    1031           0 :                                   JS::HandleObject allocationSite,
    1032             :                                   JS::HandleObject incumbentGlobal, void* data)
    1033           0 : {
    1034           0 :     MOZ_ASSERT(job);
    1035             :     return cx->jobQueue->append(job);
    1036           0 : }
    1037           0 : 
    1038           0 : namespace {
    1039             : class MOZ_STACK_CLASS ReportExceptionClosure : public ScriptEnvironmentPreparer::Closure
    1040             : {
    1041           0 :   public:
    1042             :     explicit ReportExceptionClosure(HandleValue exn)
    1043             :         : exn_(exn)
    1044           0 :     {
    1045             :     }
    1046             : 
    1047             :     bool operator()(JSContext* cx) override
    1048           0 :     {
    1049           0 :         cx->setPendingException(exn_);
    1050             :         return false;
    1051             :     }
    1052             : 
    1053             :   private:
    1054             :     HandleValue exn_;
    1055             : };
    1056             : } // anonymous namespace
    1057           0 : 
    1058             : JS_FRIEND_API(bool)
    1059             : js::UseInternalJobQueues(JSContext* cx)
    1060             : {
    1061           0 :     // Internal job queue handling must be set up very early. Self-hosting
    1062             :     // initialization is as good a marker for that as any.
    1063           0 :     MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(),
    1064           0 :                        "js::UseInternalJobQueues must be called early during runtime startup.");
    1065             :     MOZ_ASSERT(!cx->jobQueue);
    1066             :     auto* queue = js_new<PersistentRooted<JobQueue>>(cx, JobQueue(SystemAllocPolicy()));
    1067             :     if (!queue)
    1068             :         return false;
    1069             : 
    1070             :     cx->jobQueue = queue;
    1071             : 
    1072             :     cx->runtime()->offThreadPromiseState.ref().initInternalDispatchQueue();
    1073           0 :     MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized());
    1074             : 
    1075             :     JS::SetEnqueuePromiseJobCallback(cx, InternalEnqueuePromiseJobCallback);
    1076             : 
    1077           0 :     return true;
    1078             : }
    1079           0 : 
    1080           0 : JS_FRIEND_API(bool)
    1081           0 : js::EnqueueJob(JSContext* cx, JS::HandleObject job)
    1082             : {
    1083             :     MOZ_ASSERT(cx->jobQueue);
    1084           0 :     if (!cx->jobQueue->append(job)) {
    1085             :         ReportOutOfMemory(cx);
    1086           0 :         return false;
    1087           0 :     }
    1088             : 
    1089           0 :     return true;
    1090             : }
    1091           0 : 
    1092             : JS_FRIEND_API(void)
    1093             : js::StopDrainingJobQueue(JSContext* cx)
    1094             : {
    1095           0 :     MOZ_ASSERT(cx->jobQueue);
    1096             :     cx->stopDrainingJobQueue = true;
    1097           0 : }
    1098           0 : 
    1099           0 : JS_FRIEND_API(void)
    1100           0 : js::RunJobs(JSContext* cx)
    1101             : {
    1102             :     MOZ_ASSERT(cx->jobQueue);
    1103             : 
    1104             :     if (cx->drainingJobQueue || cx->stopDrainingJobQueue)
    1105             :         return;
    1106             : 
    1107           0 :     while (true) {
    1108             :         cx->runtime()->offThreadPromiseState.ref().internalDrain(cx);
    1109           0 : 
    1110           0 :         // It doesn't make sense for job queue draining to be reentrant. At the
    1111           0 :         // same time we don't want to assert against it, because that'd make
    1112             :         // drainJobQueue unsafe for fuzzers. We do want fuzzers to test this,
    1113             :         // so we simply ignore nested calls of drainJobQueue.
    1114           0 :         cx->drainingJobQueue = true;
    1115             : 
    1116           0 :         RootedObject job(cx);
    1117             :         JS::HandleValueArray args(JS::HandleValueArray::empty());
    1118           0 :         RootedValue rval(cx);
    1119             : 
    1120             :         // Execute jobs in a loop until we've reached the end of the queue.
    1121             :         // Since executing a job can trigger enqueuing of additional jobs,
    1122           0 :         // it's crucial to re-check the queue length during each iteration.
    1123             :         for (size_t i = 0; i < cx->jobQueue->length(); i++) {
    1124             :             // A previous job might have set this flag. E.g., the js shell
    1125             :             // sets it if the `quit` builtin function is called.
    1126             :             if (cx->stopDrainingJobQueue)
    1127             :                 break;
    1128           0 : 
    1129             :             job = cx->jobQueue->get()[i];
    1130           0 : 
    1131           0 :             // It's possible that queue draining was interrupted prematurely,
    1132           0 :             // leaving the queue partly processed. In that case, slots for
    1133             :             // already-executed entries will contain nullptrs, which we should
    1134             :             // just skip.
    1135             :             if (!job)
    1136             :                 continue;
    1137           0 : 
    1138             :             cx->jobQueue->get()[i] = nullptr;
    1139             :             AutoRealm ar(cx, job);
    1140           0 :             {
    1141             :                 if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) {
    1142             :                     // Nothing we can do about uncatchable exceptions.
    1143           0 :                     if (!cx->isExceptionPending())
    1144             :                         continue;
    1145             :                     RootedValue exn(cx);
    1146             :                     if (cx->getPendingException(&exn)) {
    1147             :                         /*
    1148             :                          * Clear the exception, because
    1149           0 :                          * PrepareScriptEnvironmentAndInvoke will assert that we don't
    1150             :                          * have one.
    1151             :                          */
    1152           0 :                         cx->clearPendingException();
    1153           0 :                         ReportExceptionClosure reportExn(exn);
    1154             :                         PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn);
    1155           0 :                     }
    1156             :                 }
    1157           0 :             }
    1158           0 :         }
    1159           0 : 
    1160           0 :         cx->drainingJobQueue = false;
    1161             : 
    1162             :         if (cx->stopDrainingJobQueue) {
    1163             :             cx->stopDrainingJobQueue = false;
    1164             :             break;
    1165             :         }
    1166           0 : 
    1167           0 :         cx->jobQueue->clear();
    1168           0 : 
    1169             :         // It's possible a job added a new off-thread promise task.
    1170             :         if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending())
    1171             :             break;
    1172             :     }
    1173             : }
    1174           0 : 
    1175             : JS::Error JSContext::reportedError;
    1176           0 : JS::OOM JSContext::reportedOOM;
    1177           0 : 
    1178           0 : mozilla::GenericErrorResult<OOM&>
    1179             : JSContext::alreadyReportedOOM()
    1180             : {
    1181           0 : #ifdef DEBUG
    1182             :     if (helperThread()) {
    1183             :         // Keep in sync with addPendingOutOfMemory.
    1184           0 :         if (ParseTask* task = helperThread()->parseTask())
    1185             :             MOZ_ASSERT(task->outOfMemory);
    1186             :     } else {
    1187             :         MOZ_ASSERT(isThrowingOutOfMemory());
    1188             :     }
    1189             : #endif
    1190             :     return mozilla::Err(reportedOOM);
    1191             : }
    1192             : 
    1193           0 : mozilla::GenericErrorResult<JS::Error&>
    1194             : JSContext::alreadyReportedError()
    1195             : {
    1196           0 : #ifdef DEBUG
    1197             :     if (!helperThread())
    1198           0 :         MOZ_ASSERT(isExceptionPending());
    1199           0 : #endif
    1200             :     return mozilla::Err(reportedError);
    1201           0 : }
    1202             : 
    1203             : JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
    1204           0 :   : runtime_(runtime),
    1205             :     kind_(ContextKind::HelperThread),
    1206             :     helperThread_(nullptr),
    1207             :     options_(options),
    1208           0 :     arenas_(nullptr),
    1209             :     jitActivation(nullptr),
    1210             :     activation_(nullptr),
    1211           0 :     profilingActivation_(nullptr),
    1212           0 :     nativeStackBase(GetNativeStackBase()),
    1213             :     entryMonitor(nullptr),
    1214           0 :     noExecuteDebuggerTop(nullptr),
    1215             :     activityCallback(nullptr),
    1216             :     activityCallbackArg(nullptr),
    1217           8 :     requestDepth(0),
    1218             : #ifdef DEBUG
    1219             :     checkRequestDepth(0),
    1220             :     inUnsafeCallWithABI(false),
    1221             :     hasAutoUnsafeCallWithABI(false),
    1222             : #endif
    1223             : #ifdef JS_SIMULATOR
    1224             :     simulator_(nullptr),
    1225             : #endif
    1226           8 : #ifdef JS_TRACE_LOGGING
    1227             :     traceLogger(nullptr),
    1228             : #endif
    1229             :     autoFlushICache_(nullptr),
    1230             :     dtoaState(nullptr),
    1231             :     suppressGC(0),
    1232             : #ifdef DEBUG
    1233             :     ionCompiling(false),
    1234             :     ionCompilingSafeForMinorGC(false),
    1235             :     performingGC(false),
    1236             :     gcSweeping(false),
    1237             :     gcHelperStateThread(false),
    1238             :     isTouchingGrayThings(false),
    1239             :     noGCOrAllocationCheck(0),
    1240             :     noNurseryAllocationCheck(0),
    1241             :     disableStrictProxyCheckingCount(0),
    1242             : #endif
    1243             : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
    1244             :     runningOOMTest(false),
    1245             : #endif
    1246             :     enableAccessValidation(false),
    1247             :     inUnsafeRegion(0),
    1248             :     generationalDisabled(0),
    1249             :     compactingDisabledCount(0),
    1250             :     suppressProfilerSampling(false),
    1251             :     tempLifoAlloc_((size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
    1252             :     debuggerMutations(0),
    1253             :     ionPcScriptCache(nullptr),
    1254             :     throwing(false),
    1255             :     overRecursed_(false),
    1256             :     propagatingForcedReturn_(false),
    1257             :     liveVolatileJitFrameIter_(nullptr),
    1258             :     reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
    1259             :     resolvingList(nullptr),
    1260             : #ifdef DEBUG
    1261             :     enteredPolicy(nullptr),
    1262             : #endif
    1263             :     generatingError(false),
    1264             :     cycleDetectorVector_(this),
    1265             :     data(nullptr),
    1266             :     outstandingRequests(0),
    1267             :     jitIsBroken(false),
    1268             :     asyncCauseForNewActivations(nullptr),
    1269             :     asyncCallIsExplicit(false),
    1270             :     interruptCallbackDisabled(false),
    1271             :     interruptBits_(0),
    1272             :     osrTempData_(nullptr),
    1273             :     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
    1274             :     jitStackLimit(UINTPTR_MAX),
    1275             :     jitStackLimitNoInterrupt(UINTPTR_MAX),
    1276             :     getIncumbentGlobalCallback(nullptr),
    1277             :     enqueuePromiseJobCallback(nullptr),
    1278             :     enqueuePromiseJobCallbackData(nullptr),
    1279             :     jobQueue(nullptr),
    1280             :     drainingJobQueue(false),
    1281             :     stopDrainingJobQueue(false),
    1282             :     promiseRejectionTrackerCallback(nullptr),
    1283             :     promiseRejectionTrackerCallbackData(nullptr)
    1284             : {
    1285             :     MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
    1286             :                JS::RootingContext::get(this));
    1287             : 
    1288           8 :     MOZ_ASSERT(!TlsContext.get());
    1289             :     TlsContext.set(this);
    1290             : 
    1291             :     for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++)
    1292             :         nativeStackQuota[i] = 0;
    1293             : }
    1294             : 
    1295             : JSContext::~JSContext()
    1296             : {
    1297             :     // Clear the ContextKind first, so that ProtectedData checks will allow us to
    1298         584 :     // destroy this context even if the runtime is already gone.
    1299             :     kind_ = ContextKind::HelperThread;
    1300           8 : 
    1301             :     /* Free the stuff hanging off of cx. */
    1302             :     MOZ_ASSERT(!resolvingList);
    1303           0 : 
    1304           8 :     js_delete(ionPcScriptCache.ref());
    1305             : 
    1306           0 :     if (dtoaState)
    1307           0 :         DestroyDtoaState(dtoaState);
    1308           8 : 
    1309             :     fx.destroyInstance();
    1310           0 :     freeOsrTempData();
    1311             : 
    1312             : #ifdef JS_SIMULATOR
    1313             :     js::jit::Simulator::Destroy(simulator_);
    1314           0 : #endif
    1315             : 
    1316             : #ifdef JS_TRACE_LOGGING
    1317           0 :     if (traceLogger)
    1318             :         DestroyTraceLogger(traceLogger);
    1319           0 : #endif
    1320             : 
    1321           0 :     MOZ_ASSERT(TlsContext.get() == this);
    1322           0 :     TlsContext.set(nullptr);
    1323             : }
    1324           0 : 
    1325           0 : void
    1326             : JSContext::setRuntime(JSRuntime* rt)
    1327             : {
    1328             :     MOZ_ASSERT(!resolvingList);
    1329             :     MOZ_ASSERT(!compartment());
    1330             :     MOZ_ASSERT(!activation());
    1331             :     MOZ_ASSERT(!unwrappedException_.ref().initialized());
    1332           0 :     MOZ_ASSERT(!asyncStackForNewActivations_.ref().initialized());
    1333           0 : 
    1334             :     runtime_ = rt;
    1335             : }
    1336           0 : 
    1337           0 : bool
    1338           0 : JSContext::getPendingException(MutableHandleValue rval)
    1339             : {
    1340             :     MOZ_ASSERT(throwing);
    1341           0 :     rval.set(unwrappedException());
    1342             :     if (zone()->isAtomsZone())
    1343         304 :         return true;
    1344           0 :     bool wasOverRecursed = overRecursed_;
    1345         152 :     clearPendingException();
    1346           0 :     if (!compartment()->wrap(this, rval))
    1347           0 :         return false;
    1348             :     assertSameCompartment(this, rval);
    1349           0 :     setPendingException(rval);
    1350           0 :     overRecursed_ = wasOverRecursed;
    1351             :     return true;
    1352             : }
    1353           0 : 
    1354             : bool
    1355         302 : JSContext::isThrowingOutOfMemory()
    1356           0 : {
    1357         302 :     return throwing && unwrappedException() == StringValue(names().outOfMemory);
    1358             : }
    1359           0 : 
    1360           0 : bool
    1361         151 : JSContext::isClosingGenerator()
    1362             : {
    1363           0 :     return throwing && unwrappedException().isMagic(JS_GENERATOR_CLOSING);
    1364           0 : }
    1365         302 : 
    1366           0 : bool
    1367             : JSContext::isThrowingDebuggeeWouldRun()
    1368             : {
    1369             :     return throwing &&
    1370           9 :            unwrappedException().isObject() &&
    1371             :            unwrappedException().toObject().is<ErrorObject>() &&
    1372          18 :            unwrappedException().toObject().as<ErrorObject>().type() == JSEXN_DEBUGGEEWOULDRUN;
    1373             : }
    1374             : 
    1375             : static bool
    1376         423 : ComputeIsJITBroken()
    1377             : {
    1378         846 : #if !defined(ANDROID)
    1379             :     return false;
    1380             : #else  // ANDROID
    1381             :     if (getenv("JS_IGNORE_JIT_BROKENNESS")) {
    1382           0 :         return false;
    1383             :     }
    1384           0 : 
    1385           0 :     std::string line;
    1386           0 : 
    1387           0 :     // Check for the known-bad kernel version (2.6.29).
    1388             :     std::ifstream osrelease("/proc/sys/kernel/osrelease");
    1389             :     std::getline(osrelease, line);
    1390             :     __android_log_print(ANDROID_LOG_INFO, "Gecko", "Detected osrelease `%s'",
    1391             :                         line.c_str());
    1392             : 
    1393             :     if (line.npos == line.find("2.6.29")) {
    1394             :         // We're using something other than 2.6.29, so the JITs should work.
    1395             :         __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are not broken");
    1396             :         return false;
    1397             :     }
    1398             : 
    1399             :     // We're using 2.6.29, and this causes trouble with the JITs on i9000.
    1400             :     line = "";
    1401             :     bool broken = false;
    1402             :     std::ifstream cpuinfo("/proc/cpuinfo");
    1403             :     do {
    1404             :         if (0 == line.find("Hardware")) {
    1405             :             static const char* const blacklist[] = {
    1406             :                 "SCH-I400",     // Samsung Continuum
    1407             :                 "SGH-T959",     // Samsung i9000, Vibrant device
    1408             :                 "SGH-I897",     // Samsung i9000, Captivate device
    1409             :                 "SCH-I500",     // Samsung i9000, Fascinate device
    1410             :                 "SPH-D700",     // Samsung i9000, Epic device
    1411             :                 "GT-I9000",     // Samsung i9000, UK/Europe device
    1412             :                 nullptr
    1413             :             };
    1414             :             for (const char* const* hw = &blacklist[0]; *hw; ++hw) {
    1415             :                 if (line.npos != line.find(*hw)) {
    1416             :                     __android_log_print(ANDROID_LOG_INFO, "Gecko",
    1417             :                                         "Blacklisted device `%s'", *hw);
    1418             :                     broken = true;
    1419             :                     break;
    1420             :                 }
    1421             :             }
    1422             :             break;
    1423             :         }
    1424             :         std::getline(cpuinfo, line);
    1425             :     } while(!cpuinfo.fail() && !cpuinfo.eof());
    1426             : 
    1427             :     __android_log_print(ANDROID_LOG_INFO, "Gecko", "JITs are %sbroken",
    1428             :                         broken ? "" : "not ");
    1429             : 
    1430             :     return broken;
    1431             : #endif  // ifndef ANDROID
    1432             : }
    1433             : 
    1434             : static bool
    1435             : IsJITBrokenHere()
    1436             : {
    1437             :     static bool computedIsBroken = false;
    1438             :     static bool isBroken = false;
    1439             :     if (!computedIsBroken) {
    1440             :         isBroken = ComputeIsJITBroken();
    1441             :         computedIsBroken = true;
    1442             :     }
    1443             :     return isBroken;
    1444             : }
    1445             : 
    1446             : void
    1447             : JSContext::updateJITEnabled()
    1448             : {
    1449             :     jitIsBroken = IsJITBrokenHere();
    1450             : }
    1451             : 
    1452             : size_t
    1453             : JSContext::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
    1454           0 : {
    1455           0 :     /*
    1456           0 :      * There are other JSContext members that could be measured; the following
    1457             :      * ones have been found by DMD to be worth measuring.  More stuff may be
    1458           0 :      * added later.
    1459             :      */
    1460             :     return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf);
    1461             : }
    1462           0 : 
    1463             : #ifdef DEBUG
    1464           0 : bool
    1465           0 : JSContext::inAtomsZone() const
    1466             : {
    1467             :     return zone_->isAtomsZone();
    1468           0 : }
    1469             : #endif
    1470             : 
    1471             : void
    1472             : JSContext::trace(JSTracer* trc)
    1473             : {
    1474             :     cycleDetectorVector().trace(trc);
    1475           0 :     geckoProfiler().trace(trc);
    1476             : 
    1477             :     if (trc->isMarkingTracer() && realm_)
    1478             :         realm_->mark();
    1479             : }
    1480      180133 : 
    1481             : void*
    1482      360266 : JSContext::stackLimitAddressForJitCode(JS::StackKind kind)
    1483             : {
    1484             : #ifdef JS_SIMULATOR
    1485             :     return addressOfSimulatorStackLimit();
    1486             : #else
    1487           4 :     return stackLimitAddress(kind);
    1488             : #endif
    1489           4 : }
    1490           0 : 
    1491             : uintptr_t
    1492           0 : JSContext::stackLimitForJitCode(JS::StackKind kind)
    1493           0 : {
    1494           4 : #ifdef JS_SIMULATOR
    1495             :     return simulator()->stackLimit();
    1496             : #else
    1497           0 :     return stackLimit(kind);
    1498             : #endif
    1499             : }
    1500             : 
    1501             : void
    1502           0 : JSContext::resetJitStackLimit()
    1503             : {
    1504             :     // Note that, for now, we use the untrusted limit for ion. This is fine,
    1505             :     // because it's the most conservative limit, and if we hit it, we'll bail
    1506             :     // out of ion into the interpreter, which will do a proper recursion check.
    1507           0 : #ifdef JS_SIMULATOR
    1508             :     jitStackLimit = jit::Simulator::StackLimit();
    1509             : #else
    1510             :     jitStackLimit = nativeStackLimit[JS::StackForUntrustedScript];
    1511             : #endif
    1512           0 :     jitStackLimitNoInterrupt = jitStackLimit;
    1513             : }
    1514             : 
    1515             : void
    1516             : JSContext::initJitStackLimit()
    1517          55 : {
    1518             :     resetJitStackLimit();
    1519             : }
    1520             : 
    1521             : void
    1522             : JSContext::updateMallocCounter(size_t nbytes)
    1523             : {
    1524             :     if (!zone()) {
    1525         110 :         runtime()->updateMallocCounter(nbytes);
    1526             :         return;
    1527          55 :     }
    1528           0 : 
    1529             :     zone()->updateMallocCounter(nbytes);
    1530             : }
    1531           0 : 
    1532             : #ifdef DEBUG
    1533           4 : 
    1534           0 : JS::AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext* cxArg)
    1535             :   : cx(cxArg->helperThread() ? nullptr : cxArg)
    1536             : {
    1537           0 :     if (cx) {
    1538             :         MOZ_ASSERT(cx->requestDepth || JS::RuntimeHeapIsBusy());
    1539       54861 :         MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
    1540           0 :         cx->checkRequestDepth++;
    1541          66 :     }
    1542             : }
    1543             : 
    1544           0 : JS::AutoCheckRequestDepth::~AutoCheckRequestDepth()
    1545             : {
    1546             :     if (cx) {
    1547             :         MOZ_ASSERT(cx->checkRequestDepth != 0);
    1548             :         cx->checkRequestDepth--;
    1549      600653 :     }
    1550      600661 : }
    1551             : 
    1552           0 : #endif
    1553           0 : 
    1554     1192362 : #ifdef JS_CRASH_DIAGNOSTICS
    1555           0 : void
    1556             : CompartmentChecker::check(InterpreterFrame* fp)
    1557           0 : {
    1558             :     if (fp)
    1559     1201280 :         check(fp->environmentChain());
    1560             : }
    1561      600647 : 
    1562           0 : void
    1563      596195 : CompartmentChecker::check(AbstractFramePtr frame)
    1564             : {
    1565           0 :     if (frame)
    1566             :         check(frame.environmentChain());
    1567             : }
    1568             : #endif
    1569             : 
    1570             : void
    1571           0 : AutoEnterOOMUnsafeRegion::crash(const char* reason)
    1572             : {
    1573           0 :     char msgbuf[1024];
    1574           0 :     js::NoteIntentionalCrash();
    1575           0 :     SprintfLiteral(msgbuf, "[unhandlable oom] %s", reason);
    1576             :     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
    1577             :     MOZ_CRASH();
    1578           1 : }
    1579             : 
    1580       25185 : AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback
    1581           0 : AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback = nullptr;
    1582       25185 : 
    1583             : void
    1584             : AutoEnterOOMUnsafeRegion::crash(size_t size, const char* reason)
    1585             : {
    1586           0 :     {
    1587             :         JS::AutoSuppressGCAnalysis suppress;
    1588             :         if (annotateOOMSizeCallback)
    1589           0 :             annotateOOMSizeCallback(size);
    1590           0 :     }
    1591           0 :     crash(reason);
    1592           0 : }
    1593             : 
    1594             : #ifdef DEBUG
    1595             : AutoUnsafeCallWithABI::AutoUnsafeCallWithABI()
    1596             :   : cx_(TlsContext.get()),
    1597             :     nested_(cx_->hasAutoUnsafeCallWithABI),
    1598             :     nogc(cx_)
    1599           0 : {
    1600             :     cx_->hasAutoUnsafeCallWithABI = true;
    1601             : }
    1602           0 : 
    1603           0 : AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI()
    1604           0 : {
    1605             :     MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI);
    1606           0 :     if (!nested_) {
    1607             :         cx_->hasAutoUnsafeCallWithABI = false;
    1608             :         cx_->inUnsafeCallWithABI = false;
    1609             :     }
    1610      109494 : }
    1611      109494 : #endif

Generated by: LCOV version 1.13-14-ga5dd952