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/. */
8 * JS execution context.
11 #include "vm/JSContext-inl.h"
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
21 # include <android/log.h>
26 # include <processthreadsapi.h>
29 #include "jsapi.h" // JS_SetNativeStackQuota
33 #include "builtin/RegExp.h" // js::RegExpSearcherLastLimitSentinel
34 #include "frontend/FrontendContext.h"
36 #include "irregexp/RegExpAPI.h"
37 #include "jit/Simulator.h"
38 #include "js/CallAndConstruct.h" // JS::Call
39 #include "js/CharacterEncoding.h"
40 #include "js/ContextOptions.h" // JS::ContextOptions
41 #include "js/ErrorInterceptor.h" // JSErrorInterceptor
42 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
43 #include "js/friend/StackLimits.h" // js::ReportOverRecursed
44 #include "js/MemoryCallbacks.h"
45 #include "js/Printf.h"
46 #include "js/PropertyAndElement.h" // JS_GetProperty
47 #include "js/Stack.h" // JS::NativeStackSize, JS::NativeStackLimit, JS::NativeStackLimitMin
48 #include "util/DiagnosticAssertions.h"
49 #include "util/DifferentialTesting.h"
50 #include "util/DoubleToString.h"
51 #include "util/NativeStack.h"
52 #include "util/Text.h"
53 #include "util/WindowsWrapper.h"
54 #include "vm/BytecodeUtil.h" // JSDVG_IGNORE_STACK
55 #include "vm/ErrorObject.h"
56 #include "vm/ErrorReporting.h"
57 #include "vm/JSFunction.h"
58 #include "vm/JSObject.h"
59 #include "vm/PlainObject.h" // js::PlainObject
61 #include "vm/StringType.h" // StringToNewUTF8CharsZ
62 #include "vm/ToSource.h" // js::ValueToSource
64 #include "vm/Compartment-inl.h"
65 #include "vm/Stack-inl.h"
70 JSContext
* js::MaybeGetJSContext() {
71 if (!TlsContext
.init()) {
74 return TlsContext
.get();
78 bool js::AutoCycleDetector::init() {
81 AutoCycleDetector::Vector
& vector
= cx
->cycleDetectorVector();
83 for (JSObject
* obj2
: vector
) {
84 if (MOZ_UNLIKELY(obj
== obj2
)) {
89 if (!vector
.append(obj
)) {
97 js::AutoCycleDetector::~AutoCycleDetector() {
98 if (MOZ_LIKELY(!cyclic
)) {
99 AutoCycleDetector::Vector
& vec
= cx
->cycleDetectorVector();
100 MOZ_ASSERT(vec
.back() == obj
);
101 if (vec
.length() > 1) {
104 // Avoid holding on to unused heap allocations.
110 bool JSContext::init() {
111 TlsContext
.set(this);
112 nativeStackBase_
.emplace(GetNativeStackBase());
114 if (!fx
.initInstance()) {
119 simulator_
= jit::Simulator::Create();
125 isolate
= irregexp::CreateIsolate(this);
131 // Set the initialized_ last, so that ProtectedData checks will allow us to
132 // initialize this context before it becomes the runtime's active context.
139 static void InitDefaultStackQuota(JSContext
* cx
) {
140 // Initialize stack quota to a reasonable default. Embedders can override this
141 // by calling JS_SetNativeStackQuota.
143 // NOTE: Firefox overrides these values. For the main thread this happens in
144 // XPCJSContext::Initialize.
146 #if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN))
147 static constexpr JS::NativeStackSize MaxStackSize
=
148 2 * 128 * sizeof(size_t) * 1024;
150 static constexpr JS::NativeStackSize MaxStackSize
=
151 128 * sizeof(size_t) * 1024;
153 JS_SetNativeStackQuota(cx
, MaxStackSize
);
156 JSContext
* js::NewContext(uint32_t maxBytes
, JSRuntime
* parentRuntime
) {
157 AutoNoteSingleThreadedRegion anstr
;
159 MOZ_RELEASE_ASSERT(!TlsContext
.get());
161 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
162 js::oom::SetThreadType(!parentRuntime
? js::THREAD_TYPE_MAIN
163 : js::THREAD_TYPE_WORKER
);
166 JSRuntime
* runtime
= js_new
<JSRuntime
>(parentRuntime
);
171 JSContext
* cx
= js_new
<JSContext
>(runtime
, JS::ContextOptions());
183 if (!runtime
->init(cx
, maxBytes
)) {
184 runtime
->destroyRuntime();
190 // Initialize stack quota last because simulators rely on the JSRuntime having
192 InitDefaultStackQuota(cx
);
197 void js::DestroyContext(JSContext
* cx
) {
198 JS_AbortIfWrongThread(cx
);
200 MOZ_ASSERT(!cx
->realm(), "Shouldn't destroy context with active realm");
201 MOZ_ASSERT(!cx
->activation(), "Shouldn't destroy context with activations");
203 cx
->checkNoGCRooters();
205 // Cancel all off thread Ion compiles. Completed Ion compiles may try to
206 // interrupt this context. See HelperThread::handleIonWorkload.
207 CancelOffThreadIonCompile(cx
->runtime());
209 cx
->jobQueue
= nullptr;
210 cx
->internalJobQueue
= nullptr;
211 SetContextProfilingStack(cx
, nullptr);
213 JSRuntime
* rt
= cx
->runtime();
215 // Flush promise tasks executing in helper threads early, before any parts
216 // of the JSRuntime that might be visible to helper threads are torn down.
217 rt
->offThreadPromiseState
.ref().shutdown(cx
);
219 // Destroy the runtime along with its last context.
220 js::AutoNoteSingleThreadedRegion nochecks
;
221 rt
->destroyRuntime();
222 js_delete_poison(cx
);
223 js_delete_poison(rt
);
226 void JS::RootingContext::checkNoGCRooters() {
228 for (auto const& stackRootPtr
: stackRoots_
) {
229 MOZ_ASSERT(stackRootPtr
== nullptr);
234 bool AutoResolving::alreadyStartedSlow() const {
236 AutoResolving
* cursor
= link
;
238 MOZ_ASSERT(this != cursor
);
239 if (object
.get() == cursor
->object
&& id
.get() == cursor
->id
&&
240 kind
== cursor
->kind
) {
243 } while (!!(cursor
= cursor
->link
));
247 static void MaybeReportOutOfMemoryForDifferentialTesting() {
249 * OOMs are non-deterministic, especially across different execution modes
250 * (e.g. interpreter vs JIT). When doing differential testing, print to stderr
251 * so that the fuzzers can detect this.
253 if (js::SupportDifferentialTesting()) {
254 fprintf(stderr
, "ReportOutOfMemory called\n");
259 * Since memory has been exhausted, avoid the normal error-handling path which
260 * allocates an error object, report and callstack. Instead simply throw the
261 * static atom "out of memory".
263 * Furthermore, callers of ReportOutOfMemory (viz., malloc) assume a GC does
264 * not occur, so GC must be avoided or suppressed.
266 void JSContext::onOutOfMemory() {
267 runtime()->hadOutOfMemory
= true;
268 gc::AutoSuppressGC
suppressGC(this);
270 /* Report the oom. */
271 if (JS::OutOfMemoryCallback oomCallback
= runtime()->oomCallback
) {
272 oomCallback(this, runtime()->oomCallbackData
);
275 // If we OOM early in process startup, this may be unavailable so just return
276 // instead of crashing unexpectedly.
277 if (MOZ_UNLIKELY(!runtime()->hasInitializedSelfHosting())) {
281 RootedValue
oomMessage(this, StringValue(names().out_of_memory_
));
282 setPendingException(oomMessage
, nullptr);
283 MOZ_ASSERT(status
== JS::ExceptionStatus::Throwing
);
284 status
= JS::ExceptionStatus::OutOfMemory
;
286 reportResourceExhaustion();
289 JS_PUBLIC_API
void js::ReportOutOfMemory(JSContext
* cx
) {
290 MaybeReportOutOfMemoryForDifferentialTesting();
295 JS_PUBLIC_API
void js::ReportLargeOutOfMemory(JSContext
* cx
) {
296 js::ReportOutOfMemory(cx
);
299 JS_PUBLIC_API
void js::ReportOutOfMemory(FrontendContext
* fc
) {
300 MaybeReportOutOfMemoryForDifferentialTesting();
305 static void MaybeReportOverRecursedForDifferentialTesting() {
307 * We cannot make stack depth deterministic across different
308 * implementations (e.g. JIT vs. interpreter will differ in
309 * their maximum stack depth).
310 * However, we can detect externally when we hit the maximum
311 * stack depth which is useful for external testing programs
314 if (js::SupportDifferentialTesting()) {
315 fprintf(stderr
, "ReportOverRecursed called\n");
319 void JSContext::onOverRecursed() {
320 // Try to construct an over-recursed error and then update the exception
321 // status to `OverRecursed`. Creating the error can fail, so check there
322 // is a reasonable looking exception pending before updating status.
323 JS_ReportErrorNumberASCII(this, GetErrorMessage
, nullptr,
324 JSMSG_OVER_RECURSED
);
325 if (isExceptionPending() && !isThrowingOutOfMemory()) {
326 MOZ_ASSERT(unwrappedException().isObject());
327 MOZ_ASSERT(status
== JS::ExceptionStatus::Throwing
);
328 status
= JS::ExceptionStatus::OverRecursed
;
331 reportResourceExhaustion();
334 JS_PUBLIC_API
void js::ReportOverRecursed(JSContext
* maybecx
) {
335 MaybeReportOverRecursedForDifferentialTesting();
341 maybecx
->onOverRecursed();
344 JS_PUBLIC_API
void js::ReportOverRecursed(FrontendContext
* fc
) {
345 MaybeReportOverRecursedForDifferentialTesting();
347 fc
->onOverRecursed();
350 void js::ReportOversizedAllocation(JSContext
* cx
, const unsigned errorNumber
) {
351 // The JIT may optimize away allocations if it determines that they aren't
352 // used. This can affect whether we throw an exception when the size of an
353 // allocation exceeds implementation-defined limits (eg JSString::MAX_LENGTH).
354 // These errors aren't interesting for the purposes of differential fuzzing.
355 // We print a message so that fuzzers can detect this case. To simplify
356 // tooling updates, we use the same message as ReportOutOfMemory.
357 if (js::SupportDifferentialTesting()) {
358 fprintf(stderr
, "ReportOutOfMemory called\n");
361 gc::AutoSuppressGC
suppressGC(cx
);
362 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, errorNumber
);
364 cx
->reportResourceExhaustion();
367 void js::ReportAllocationOverflow(JSContext
* cx
) {
368 if (js::SupportDifferentialTesting()) {
369 fprintf(stderr
, "ReportAllocationOverflow called\n");
376 cx
->reportAllocationOverflow();
379 void js::ReportAllocationOverflow(FrontendContext
* fc
) {
380 fc
->onAllocationOverflow();
383 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
384 void js::ReportUsageErrorASCII(JSContext
* cx
, HandleObject callee
,
386 RootedValue
usage(cx
);
387 if (!JS_GetProperty(cx
, callee
, "usage", &usage
)) {
391 if (!usage
.isString()) {
392 JS_ReportErrorASCII(cx
, "%s", msg
);
394 RootedString
usageStr(cx
, usage
.toString());
395 UniqueChars str
= JS_EncodeStringToUTF8(cx
, usageStr
);
399 JS_ReportErrorUTF8(cx
, "%s. Usage: %s", msg
, str
.get());
403 enum class PrintErrorKind
{ Error
, Warning
, Note
};
405 static void PrintErrorLine(FILE* file
, const char* prefix
,
406 JSErrorReport
* report
) {
407 if (const char16_t
* linebuf
= report
->linebuf()) {
411 size_t linebufLen
= report
->linebufLength();
413 // This function is only used for shell command-line sorts of stuff where
414 // performance doesn't really matter, so just encode into max-sized
416 mozilla::CheckedInt
<size_t> utf8Len(linebufLen
);
418 if (utf8Len
.isValid()) {
419 line
= UniqueChars(js_pod_malloc
<char>(utf8Len
.value()));
421 n
= mozilla::ConvertUtf16toUtf8({linebuf
, linebufLen
},
422 {line
.get(), utf8Len
.value()});
429 utf8buf
= line
.get();
431 static const char unavailableStr
[] = "<context unavailable>";
432 utf8buf
= unavailableStr
;
433 n
= js_strlen(unavailableStr
);
441 for (size_t i
= 0; i
< n
; i
++) {
442 fputc(utf8buf
[i
], file
);
445 // linebuf/utf8buf usually ends with a newline. If not, add one here.
446 if (n
== 0 || utf8buf
[n
- 1] != '\n') {
454 n
= report
->tokenOffset();
455 for (size_t i
= 0, j
= 0; i
< n
; i
++) {
456 if (utf8buf
[i
] == '\t') {
457 for (size_t k
= (j
+ 8) & ~7; j
< k
; j
++) {
469 static void PrintErrorLine(FILE* file
, const char* prefix
,
470 JSErrorNotes::Note
* note
) {}
472 template <typename T
>
473 static void PrintSingleError(FILE* file
, JS::ConstUTF8CharsZ toStringResult
,
474 T
* report
, PrintErrorKind kind
) {
476 if (report
->filename
) {
477 prefix
= JS_smprintf("%s:", report
->filename
.c_str());
480 if (report
->lineno
) {
481 prefix
= JS_smprintf("%s%u:%u ", prefix
? prefix
.get() : "", report
->lineno
,
482 report
->column
.oneOriginValue());
485 if (kind
!= PrintErrorKind::Error
) {
486 const char* kindPrefix
= nullptr;
488 case PrintErrorKind::Error
:
489 MOZ_CRASH("unreachable");
490 case PrintErrorKind::Warning
:
491 kindPrefix
= "warning";
493 case PrintErrorKind::Note
:
498 prefix
= JS_smprintf("%s%s: ", prefix
? prefix
.get() : "", kindPrefix
);
501 const char* message
=
502 toStringResult
? toStringResult
.c_str() : report
->message().c_str();
504 /* embedded newlines -- argh! */
506 while ((ctmp
= strchr(message
, '\n')) != 0) {
509 fputs(prefix
.get(), file
);
511 (void)fwrite(message
, 1, ctmp
- message
, file
);
515 /* If there were no filename or lineno, the prefix might be empty */
517 fputs(prefix
.get(), file
);
519 fputs(message
, file
);
521 PrintErrorLine(file
, prefix
.get(), report
);
527 static void PrintErrorImpl(FILE* file
, JS::ConstUTF8CharsZ toStringResult
,
528 JSErrorReport
* report
, bool reportWarnings
) {
531 /* Conditionally ignore reported warnings. */
532 if (report
->isWarning() && !reportWarnings
) {
536 PrintErrorKind kind
= PrintErrorKind::Error
;
537 if (report
->isWarning()) {
538 kind
= PrintErrorKind::Warning
;
540 PrintSingleError(file
, toStringResult
, report
, kind
);
543 for (auto&& note
: *report
->notes
) {
544 PrintSingleError(file
, JS::ConstUTF8CharsZ(), note
.get(),
545 PrintErrorKind::Note
);
550 JS_PUBLIC_API
void JS::PrintError(FILE* file
, JSErrorReport
* report
,
551 bool reportWarnings
) {
552 PrintErrorImpl(file
, JS::ConstUTF8CharsZ(), report
, reportWarnings
);
555 JS_PUBLIC_API
void JS::PrintError(FILE* file
,
556 const JS::ErrorReportBuilder
& builder
,
557 bool reportWarnings
) {
558 PrintErrorImpl(file
, builder
.toStringResult(), builder
.report(),
562 void js::ReportIsNotDefined(JSContext
* cx
, HandleId id
) {
563 if (UniqueChars printable
=
564 IdToPrintableUTF8(cx
, id
, IdToPrintableBehavior::IdIsIdentifier
)) {
565 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_NOT_DEFINED
,
570 void js::ReportIsNotDefined(JSContext
* cx
, Handle
<PropertyName
*> name
) {
571 RootedId
id(cx
, NameToId(name
));
572 ReportIsNotDefined(cx
, id
);
575 const char* NullOrUndefinedToCharZ(HandleValue v
) {
576 MOZ_ASSERT(v
.isNullOrUndefined());
577 return v
.isNull() ? "null" : "undefined";
580 void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext
* cx
, HandleValue v
,
582 MOZ_ASSERT(v
.isNullOrUndefined());
584 if (vIndex
== JSDVG_IGNORE_STACK
) {
585 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
586 JSMSG_CANT_CONVERT_TO
, NullOrUndefinedToCharZ(v
),
591 UniqueChars bytes
= DecompileValueGenerator(cx
, vIndex
, v
, nullptr);
596 if (strcmp(bytes
.get(), "undefined") == 0 ||
597 strcmp(bytes
.get(), "null") == 0) {
598 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_NO_PROPERTIES
,
601 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
602 JSMSG_UNEXPECTED_TYPE
, bytes
.get(),
603 NullOrUndefinedToCharZ(v
));
607 void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext
* cx
, HandleValue v
,
608 int vIndex
, HandleId key
) {
609 MOZ_ASSERT(v
.isNullOrUndefined());
611 if (!cx
->realm()->creationOptions().getPropertyErrorMessageFixEnabled()) {
612 ReportIsNullOrUndefinedForPropertyAccess(cx
, v
, vIndex
);
616 RootedValue
idVal(cx
, IdToValue(key
));
617 RootedString
idStr(cx
, ValueToSource(cx
, idVal
));
622 UniqueChars keyStr
= StringToNewUTF8CharsZ(cx
, *idStr
);
627 if (vIndex
== JSDVG_IGNORE_STACK
) {
628 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_PROPERTY_FAIL
,
629 keyStr
.get(), NullOrUndefinedToCharZ(v
));
633 UniqueChars bytes
= DecompileValueGenerator(cx
, vIndex
, v
, nullptr);
638 if (strcmp(bytes
.get(), "undefined") == 0 ||
639 strcmp(bytes
.get(), "null") == 0) {
640 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_PROPERTY_FAIL
,
641 keyStr
.get(), bytes
.get());
645 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
646 JSMSG_PROPERTY_FAIL_EXPR
, keyStr
.get(), bytes
.get(),
647 NullOrUndefinedToCharZ(v
));
650 bool js::ReportValueError(JSContext
* cx
, const unsigned errorNumber
,
651 int spindex
, HandleValue v
, HandleString fallback
,
652 const char* arg1
, const char* arg2
) {
653 MOZ_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
>= 1);
654 MOZ_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
<= 3);
655 UniqueChars bytes
= DecompileValueGenerator(cx
, spindex
, v
, fallback
);
660 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, errorNumber
,
661 bytes
.get(), arg1
, arg2
);
665 JSObject
* js::CreateErrorNotesArray(JSContext
* cx
, JSErrorReport
* report
) {
666 Rooted
<ArrayObject
*> notesArray(cx
, NewDenseEmptyArray(cx
));
671 if (!report
->notes
) {
675 for (auto&& note
: *report
->notes
) {
676 Rooted
<PlainObject
*> noteObj(cx
, NewPlainObject(cx
));
681 RootedString
messageStr(cx
, note
->newMessageString(cx
));
685 RootedValue
messageVal(cx
, StringValue(messageStr
));
686 if (!DefineDataProperty(cx
, noteObj
, cx
->names().message
, messageVal
)) {
690 RootedValue
filenameVal(cx
);
691 if (const char* filename
= note
->filename
.c_str()) {
692 JS::UTF8Chars
utf8chars(filename
, strlen(filename
));
693 Rooted
<JSString
*> filenameStr(cx
, NewStringCopyUTF8N(cx
, utf8chars
));
697 filenameVal
= StringValue(filenameStr
);
699 if (!DefineDataProperty(cx
, noteObj
, cx
->names().fileName
, filenameVal
)) {
703 RootedValue
linenoVal(cx
, Int32Value(note
->lineno
));
704 if (!DefineDataProperty(cx
, noteObj
, cx
->names().lineNumber
, linenoVal
)) {
707 RootedValue
columnVal(cx
, Int32Value(note
->column
.oneOriginValue()));
708 if (!DefineDataProperty(cx
, noteObj
, cx
->names().columnNumber
, columnVal
)) {
712 if (!NewbornArrayPush(cx
, notesArray
, ObjectValue(*noteObj
))) {
720 void JSContext::recoverFromOutOfMemory() {
721 if (isExceptionPending()) {
722 MOZ_ASSERT(isThrowingOutOfMemory());
723 clearPendingException();
727 void JSContext::reportAllocationOverflow() {
728 gc::AutoSuppressGC
suppressGC(this);
729 JS_ReportErrorNumberASCII(this, GetErrorMessage
, nullptr,
730 JSMSG_ALLOC_OVERFLOW
);
733 JS::StackKind
JSContext::stackKindForCurrentPrincipal() {
734 return runningWithTrustedPrincipals() ? JS::StackForTrustedScript
735 : JS::StackForUntrustedScript
;
738 JS::NativeStackLimit
JSContext::stackLimitForCurrentPrincipal() {
739 return stackLimit(stackKindForCurrentPrincipal());
742 JS_PUBLIC_API
bool js::UseInternalJobQueues(JSContext
* cx
) {
743 // Internal job queue handling must be set up very early. Self-hosting
744 // initialization is as good a marker for that as any.
746 !cx
->runtime()->hasInitializedSelfHosting(),
747 "js::UseInternalJobQueues must be called early during runtime startup.");
748 MOZ_ASSERT(!cx
->jobQueue
);
749 auto queue
= MakeUnique
<InternalJobQueue
>(cx
);
754 cx
->internalJobQueue
= std::move(queue
);
755 cx
->jobQueue
= cx
->internalJobQueue
.ref().get();
757 cx
->runtime()->offThreadPromiseState
.ref().initInternalDispatchQueue();
758 MOZ_ASSERT(cx
->runtime()->offThreadPromiseState
.ref().initialized());
764 JSObject
* InternalJobQueue::copyJobs(JSContext
* cx
) {
765 Rooted
<ArrayObject
*> jobs(cx
, NewDenseEmptyArray(cx
));
770 for (const JSObject
* unwrappedJob
: queue
.get()) {
771 RootedObject
job(cx
, const_cast<JSObject
*>(unwrappedJob
));
772 if (!cx
->compartment()->wrap(cx
, &job
)) {
776 if (!NewbornArrayPush(cx
, jobs
, ObjectValue(*job
))) {
784 JS_PUBLIC_API JSObject
* js::GetJobsInInternalJobQueue(JSContext
* cx
) {
785 MOZ_ASSERT(cx
->internalJobQueue
.ref());
786 return cx
->internalJobQueue
->copyJobs(cx
);
790 JS_PUBLIC_API
bool js::EnqueueJob(JSContext
* cx
, JS::HandleObject job
) {
791 MOZ_ASSERT(cx
->jobQueue
);
792 return cx
->jobQueue
->enqueuePromiseJob(cx
, nullptr, job
, nullptr, nullptr);
795 JS_PUBLIC_API
void js::StopDrainingJobQueue(JSContext
* cx
) {
796 MOZ_ASSERT(cx
->internalJobQueue
.ref());
797 cx
->internalJobQueue
->interrupt();
800 JS_PUBLIC_API
void js::RunJobs(JSContext
* cx
) {
801 MOZ_ASSERT(cx
->jobQueue
);
802 cx
->jobQueue
->runJobs(cx
);
803 JS::ClearKeptObjects(cx
);
806 JSObject
* InternalJobQueue::getIncumbentGlobal(JSContext
* cx
) {
807 if (!cx
->compartment()) {
813 bool InternalJobQueue::enqueuePromiseJob(JSContext
* cx
,
814 JS::HandleObject promise
,
815 JS::HandleObject job
,
816 JS::HandleObject allocationSite
,
817 JS::HandleObject incumbentGlobal
) {
819 if (!queue
.pushBack(job
)) {
820 ReportOutOfMemory(cx
);
824 JS::JobQueueMayNotBeEmpty(cx
);
828 void InternalJobQueue::runJobs(JSContext
* cx
) {
829 if (draining_
|| interrupted_
) {
834 cx
->runtime()->offThreadPromiseState
.ref().internalDrain(cx
);
836 // It doesn't make sense for job queue draining to be reentrant. At the
837 // same time we don't want to assert against it, because that'd make
838 // drainJobQueue unsafe for fuzzers. We do want fuzzers to test this,
839 // so we simply ignore nested calls of drainJobQueue.
842 RootedObject
job(cx
);
843 JS::HandleValueArray
args(JS::HandleValueArray::empty());
844 RootedValue
rval(cx
);
846 // Execute jobs in a loop until we've reached the end of the queue.
847 while (!queue
.empty()) {
848 // A previous job might have set this flag. E.g., the js shell
849 // sets it if the `quit` builtin function is called.
857 // If the next job is the last job in the job queue, allow
858 // skipping the standard job queuing behavior.
860 JS::JobQueueIsEmpty(cx
);
863 AutoRealm
ar(cx
, &job
->as
<JSFunction
>());
865 if (!JS::Call(cx
, UndefinedHandleValue
, job
, args
, &rval
)) {
866 // Nothing we can do about uncatchable exceptions.
867 if (!cx
->isExceptionPending()) {
871 if (cx
->getPendingException(&exn
)) {
873 * Clear the exception, because
874 * PrepareScriptEnvironmentAndInvoke will assert that we don't
877 cx
->clearPendingException();
878 js::ReportExceptionClosure
reportExn(exn
);
879 PrepareScriptEnvironmentAndInvoke(cx
, cx
->global(), reportExn
);
888 interrupted_
= false;
894 // It's possible a job added a new off-thread promise task.
895 if (!cx
->runtime()->offThreadPromiseState
.ref().internalHasPending()) {
901 bool InternalJobQueue::empty() const { return queue
.empty(); }
903 JSObject
* InternalJobQueue::maybeFront() const {
908 return queue
.get().front();
911 class js::InternalJobQueue::SavedQueue
: public JobQueue::SavedJobQueue
{
913 SavedQueue(JSContext
* cx
, Queue
&& saved
, bool draining
)
914 : cx(cx
), saved(cx
, std::move(saved
)), draining_(draining
) {
915 MOZ_ASSERT(cx
->internalJobQueue
.ref());
919 MOZ_ASSERT(cx
->internalJobQueue
.ref());
920 cx
->internalJobQueue
->queue
= std::move(saved
.get());
921 cx
->internalJobQueue
->draining_
= draining_
;
926 PersistentRooted
<Queue
> saved
;
930 js::UniquePtr
<JS::JobQueue::SavedJobQueue
> InternalJobQueue::saveJobQueue(
933 js::MakeUnique
<SavedQueue
>(cx
, std::move(queue
.get()), draining_
);
935 // When MakeUnique's allocation fails, the SavedQueue constructor is never
936 // called, so this->queue is still initialized. (The move doesn't occur
937 // until the constructor gets called.)
938 ReportOutOfMemory(cx
);
942 queue
= Queue(SystemAllocPolicy());
947 mozilla::GenericErrorResult
<OOM
> JSContext::alreadyReportedOOM() {
948 MOZ_ASSERT(isThrowingOutOfMemory());
949 return mozilla::Err(JS::OOM());
952 mozilla::GenericErrorResult
<JS::Error
> JSContext::alreadyReportedError() {
953 return mozilla::Err(JS::Error());
956 JSContext::JSContext(JSRuntime
* runtime
, const JS::ContextOptions
& options
)
957 : RootingContext(runtime
? &runtime
->gc
.nursery() : nullptr),
959 options_(this, options
),
960 measuringExecutionTime_(this, false),
961 jitActivation(this, nullptr),
962 isolate(this, nullptr),
963 activation_(this, nullptr),
964 profilingActivation_(nullptr),
965 entryMonitor(this, nullptr),
966 noExecuteDebuggerTop(this, nullptr),
968 inUnsafeCallWithABI(this, false),
969 hasAutoUnsafeCallWithABI(this, false),
972 simulator_(this, nullptr),
974 dtoaState(this, nullptr),
976 #ifdef FUZZING_JS_FUZZILLI
978 executionHashInputs(0),
981 noNurseryAllocationCheck(this, 0),
982 disableStrictProxyCheckingCount(this, 0),
984 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
985 runningOOMTest(this, false),
987 inUnsafeRegion(this, 0),
988 generationalDisabled(this, 0),
989 compactingDisabledCount(this, 0),
991 regExpSearcherLastLimit(this, RegExpSearcherLastLimitSentinel
),
993 regExpSearcherLastLimit(this, 0),
995 frontendCollectionPool_(this),
996 suppressProfilerSampling(false),
997 tempLifoAlloc_(this, (size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE
),
998 debuggerMutations(this, 0),
999 ionPcScriptCache(this, nullptr),
1000 status(this, JS::ExceptionStatus::None
),
1001 unwrappedException_(this),
1002 unwrappedExceptionStack_(this),
1004 hadResourceExhaustion_(this, false),
1006 reportGranularity(this, JS_DEFAULT_JITREPORT_GRANULARITY
),
1007 resolvingList(this, nullptr),
1009 enteredPolicy(this, nullptr),
1011 generatingError(this, false),
1012 cycleDetectorVector_(this, this),
1014 asyncStackForNewActivations_(this),
1015 asyncCauseForNewActivations(this, nullptr),
1016 asyncCallIsExplicit(this, false),
1017 interruptCallbacks_(this),
1018 interruptCallbackDisabled(this, false),
1020 inlinedICScript_(this, nullptr),
1021 jitStackLimit(JS::NativeStackLimitMin
),
1022 jitStackLimitNoInterrupt(this, JS::NativeStackLimitMin
),
1023 jobQueue(this, nullptr),
1024 internalJobQueue(this),
1025 canSkipEnqueuingJobs(this, false),
1026 promiseRejectionTrackerCallback(this, nullptr),
1027 promiseRejectionTrackerCallbackData(this, nullptr),
1028 insideExclusiveDebuggerOnEval(this, nullptr) {
1029 MOZ_ASSERT(static_cast<JS::RootingContext
*>(this) ==
1030 JS::RootingContext::get(this));
1033 JSContext::~JSContext() {
1035 // Clear the initialized_ first, so that ProtectedData checks will allow us to
1036 // destroy this context even if the runtime is already gone.
1037 initialized_
= false;
1040 /* Free the stuff hanging off of cx. */
1041 MOZ_ASSERT(!resolvingList
);
1044 DestroyDtoaState(dtoaState
);
1047 fx
.destroyInstance();
1050 js::jit::Simulator::Destroy(simulator_
);
1054 irregexp::DestroyIsolate(isolate
.ref());
1057 TlsContext
.set(nullptr);
1060 void JSContext::setRuntime(JSRuntime
* rt
) {
1061 MOZ_ASSERT(!resolvingList
);
1062 MOZ_ASSERT(!compartment());
1063 MOZ_ASSERT(!activation());
1064 MOZ_ASSERT(!unwrappedException_
.ref().initialized());
1065 MOZ_ASSERT(!unwrappedExceptionStack_
.ref().initialized());
1066 MOZ_ASSERT(!asyncStackForNewActivations_
.ref().initialized());
1071 #if defined(NIGHTLY_BUILD)
1072 static bool IsOutOfMemoryException(JSContext
* cx
, const Value
& v
) {
1073 return v
== StringValue(cx
->names().out_of_memory_
);
1077 void JSContext::setPendingException(HandleValue v
, Handle
<SavedFrame
*> stack
) {
1078 #if defined(NIGHTLY_BUILD)
1080 // Do not intercept exceptions if we are already
1081 // in the exception interceptor. That would lead
1082 // to infinite recursion.
1083 if (this->runtime()->errorInterception
.isExecuting
) {
1087 // Check whether we have an interceptor at all.
1088 if (!this->runtime()->errorInterception
.interceptor
) {
1092 // Don't report OOM exceptions. The interceptor isn't interested in those
1093 // and they can confuse the interceptor because OOM can be thrown when we
1094 // are not in a realm (atom allocation, for example).
1095 if (IsOutOfMemoryException(this, v
)) {
1099 // Make sure that we do not call the interceptor from within
1101 this->runtime()->errorInterception
.isExecuting
= true;
1103 // The interceptor must be infallible.
1104 const mozilla::DebugOnly
<bool> wasExceptionPending
=
1105 this->isExceptionPending();
1106 this->runtime()->errorInterception
.interceptor
->interceptError(this, v
);
1107 MOZ_ASSERT(wasExceptionPending
== this->isExceptionPending());
1109 this->runtime()->errorInterception
.isExecuting
= false;
1111 #endif // defined(NIGHTLY_BUILD)
1113 // overRecursed_ is set after the fact by ReportOverRecursed.
1114 this->status
= JS::ExceptionStatus::Throwing
;
1115 this->unwrappedException() = v
;
1116 this->unwrappedExceptionStack() = stack
;
1119 void JSContext::setPendingException(HandleValue value
,
1120 ShouldCaptureStack captureStack
) {
1121 Rooted
<SavedFrame
*> nstack(this);
1122 if (captureStack
== ShouldCaptureStack::Always
||
1123 realm()->shouldCaptureStackForThrow()) {
1124 RootedObject
stack(this);
1125 if (!CaptureStack(this, &stack
)) {
1126 clearPendingException();
1129 nstack
= &stack
->as
<SavedFrame
>();
1132 setPendingException(value
, nstack
);
1135 bool JSContext::getPendingException(MutableHandleValue rval
) {
1136 MOZ_ASSERT(isExceptionPending());
1138 RootedValue
exception(this, unwrappedException());
1139 if (zone()->isAtomsZone()) {
1140 rval
.set(exception
);
1144 Rooted
<SavedFrame
*> stack(this, unwrappedExceptionStack());
1145 JS::ExceptionStatus prevStatus
= status
;
1146 clearPendingException();
1147 if (!compartment()->wrap(this, &exception
)) {
1150 this->check(exception
);
1151 setPendingException(exception
, stack
);
1152 status
= prevStatus
;
1154 rval
.set(exception
);
1158 bool JSContext::getPendingExceptionStack(MutableHandleValue rval
) {
1159 MOZ_ASSERT(isExceptionPending());
1161 Rooted
<SavedFrame
*> exceptionStack(this, unwrappedExceptionStack());
1162 if (!exceptionStack
) {
1166 if (zone()->isAtomsZone()) {
1167 rval
.setObject(*exceptionStack
);
1171 RootedValue
stack(this, ObjectValue(*exceptionStack
));
1172 RootedValue
exception(this, unwrappedException());
1173 JS::ExceptionStatus prevStatus
= status
;
1174 clearPendingException();
1175 if (!compartment()->wrap(this, &stack
)) {
1179 setPendingException(exception
, exceptionStack
);
1180 status
= prevStatus
;
1186 SavedFrame
* JSContext::getPendingExceptionStack() {
1187 return unwrappedExceptionStack();
1190 bool JSContext::isClosingGenerator() {
1191 return isExceptionPending() &&
1192 unwrappedException().isMagic(JS_GENERATOR_CLOSING
);
1195 bool JSContext::isThrowingDebuggeeWouldRun() {
1196 return isExceptionPending() && unwrappedException().isObject() &&
1197 unwrappedException().toObject().is
<ErrorObject
>() &&
1198 unwrappedException().toObject().as
<ErrorObject
>().type() ==
1199 JSEXN_DEBUGGEEWOULDRUN
;
1202 bool JSContext::isRuntimeCodeGenEnabled(JS::RuntimeCode kind
,
1203 HandleString code
) {
1204 // Make sure that the CSP callback is installed and that it permits runtime
1206 if (JSCSPEvalChecker allows
=
1207 runtime()->securityCallbacks
->contentSecurityPolicyAllows
) {
1208 return allows(this, kind
, code
);
1214 size_t JSContext::sizeOfExcludingThis(
1215 mozilla::MallocSizeOf mallocSizeOf
) const {
1217 * There are other JSContext members that could be measured; the following
1218 * ones have been found by DMD to be worth measuring. More stuff may be
1221 return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf
) +
1222 irregexp::IsolateSizeOfIncludingThis(isolate
, mallocSizeOf
);
1225 size_t JSContext::sizeOfIncludingThis(
1226 mozilla::MallocSizeOf mallocSizeOf
) const {
1227 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf
);
1231 bool JSContext::inAtomsZone() const { return zone_
->isAtomsZone(); }
1234 void JSContext::trace(JSTracer
* trc
) {
1235 cycleDetectorVector().trace(trc
);
1236 geckoProfiler().trace(trc
);
1238 irregexp::TraceIsolate(trc
, isolate
.ref());
1242 JS::NativeStackLimit
JSContext::stackLimitForJitCode(JS::StackKind kind
) {
1244 return simulator()->stackLimit();
1246 return stackLimit(kind
);
1250 void JSContext::resetJitStackLimit() {
1251 // Note that, for now, we use the untrusted limit for ion. This is fine,
1252 // because it's the most conservative limit, and if we hit it, we'll bail
1253 // out of ion into the interpreter, which will do a proper recursion check.
1255 jitStackLimit
= jit::Simulator::StackLimit();
1257 jitStackLimit
= nativeStackLimit
[JS::StackForUntrustedScript
];
1259 jitStackLimitNoInterrupt
= jitStackLimit
;
1262 void JSContext::initJitStackLimit() { resetJitStackLimit(); }
1264 #ifdef JS_CRASH_DIAGNOSTICS
1265 void ContextChecks::check(AbstractFramePtr frame
, int argIndex
) {
1267 check(frame
.realm(), argIndex
);
1272 void AutoEnterOOMUnsafeRegion::crash_impl(const char* reason
) {
1274 js::NoteIntentionalCrash();
1275 SprintfLiteral(msgbuf
, "[unhandlable oom] %s", reason
);
1277 // In non-DEBUG builds MOZ_CRASH normally doesn't print to stderr so we have
1278 // to do this explicitly (the jit-test allow-unhandlable-oom annotation and
1279 // fuzzers depend on it).
1280 fprintf(stderr
, "Hit MOZ_CRASH(%s) at %s:%d\n", msgbuf
, __FILE__
, __LINE__
);
1282 MOZ_CRASH_UNSAFE(msgbuf
);
1285 mozilla::Atomic
<AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback
,
1287 AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback(nullptr);
1289 void AutoEnterOOMUnsafeRegion::crash_impl(size_t size
, const char* reason
) {
1291 JS::AutoSuppressGCAnalysis suppress
;
1292 if (annotateOOMSizeCallback
) {
1293 annotateOOMSizeCallback(size
);
1299 void ExternalValueArray::trace(JSTracer
* trc
) {
1300 if (Value
* vp
= begin()) {
1301 TraceRootRange(trc
, length(), vp
, "js::ExternalValueArray");
1306 AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness
)
1307 : cx_(TlsContext
.get()),
1308 nested_(cx_
? cx_
->hasAutoUnsafeCallWithABI
: false),
1311 // This is a helper thread doing Ion or Wasm compilation - nothing to do.
1314 switch (strictness
) {
1315 case UnsafeABIStrictness::NoExceptions
:
1316 MOZ_ASSERT(!JS_IsExceptionPending(cx_
));
1317 checkForPendingException_
= true;
1319 case UnsafeABIStrictness::AllowPendingExceptions
:
1320 checkForPendingException_
= !JS_IsExceptionPending(cx_
);
1322 case UnsafeABIStrictness::AllowThrownExceptions
:
1323 checkForPendingException_
= false;
1327 cx_
->hasAutoUnsafeCallWithABI
= true;
1330 AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI() {
1334 MOZ_ASSERT(cx_
->hasAutoUnsafeCallWithABI
);
1336 cx_
->hasAutoUnsafeCallWithABI
= false;
1337 cx_
->inUnsafeCallWithABI
= false;
1339 MOZ_ASSERT_IF(checkForPendingException_
, !JS_IsExceptionPending(cx_
));
1344 JS_PUBLIC_API
void js::IncWasiRecursionDepth(JSContext
* cx
) {
1345 ++JS::RootingContext::get(cx
)->wasiRecursionDepth
;
1348 JS_PUBLIC_API
void js::DecWasiRecursionDepth(JSContext
* cx
) {
1349 MOZ_ASSERT(JS::RootingContext::get(cx
)->wasiRecursionDepth
> 0);
1350 --JS::RootingContext::get(cx
)->wasiRecursionDepth
;
1353 JS_PUBLIC_API
bool js::CheckWasiRecursionLimit(JSContext
* cx
) {
1354 // WASI has two limits:
1355 // 1) The stack pointer in linear memory that grows to zero. See
1356 // --stack-first in js/src/shell/moz.build.
1357 // 2) The JS::RootingContext::wasiRecursionDepth that counts recursion depth.
1358 // Here we should check both.
1359 if (JS::RootingContext::get(cx
)->wasiRecursionDepth
>=
1360 JS::RootingContext::wasiRecursionDepthLimit
) {