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"
46 #include "js/Printf.h"
47 #include "js/PropertyAndElement.h" // JS_GetProperty
48 #include "js/Stack.h" // JS::NativeStackSize, JS::NativeStackLimit, JS::NativeStackLimitMin
49 #include "util/DiagnosticAssertions.h"
50 #include "util/DifferentialTesting.h"
51 #include "util/DoubleToString.h"
52 #include "util/NativeStack.h"
53 #include "util/Text.h"
54 #include "util/WindowsWrapper.h"
55 #include "vm/BytecodeUtil.h" // JSDVG_IGNORE_STACK
56 #include "vm/ErrorObject.h"
57 #include "vm/ErrorReporting.h"
58 #include "vm/FrameIter.h"
59 #include "vm/JSFunction.h"
60 #include "vm/JSObject.h"
61 #include "vm/PlainObject.h" // js::PlainObject
63 #include "vm/StringType.h" // StringToNewUTF8CharsZ
64 #include "vm/ToSource.h" // js::ValueToSource
66 #include "vm/Compartment-inl.h"
67 #include "vm/Stack-inl.h"
72 JSContext
* js::MaybeGetJSContext() {
73 if (!TlsContext
.init()) {
76 return TlsContext
.get();
80 bool js::AutoCycleDetector::init() {
83 AutoCycleDetector::Vector
& vector
= cx
->cycleDetectorVector();
85 for (JSObject
* obj2
: vector
) {
86 if (MOZ_UNLIKELY(obj
== obj2
)) {
91 if (!vector
.append(obj
)) {
99 js::AutoCycleDetector::~AutoCycleDetector() {
100 if (MOZ_LIKELY(!cyclic
)) {
101 AutoCycleDetector::Vector
& vec
= cx
->cycleDetectorVector();
102 MOZ_ASSERT(vec
.back() == obj
);
103 if (vec
.length() > 1) {
106 // Avoid holding on to unused heap allocations.
112 bool JSContext::init() {
113 TlsContext
.set(this);
114 nativeStackBase_
.emplace(GetNativeStackBase());
116 if (!fx
.initInstance()) {
121 simulator_
= jit::Simulator::Create();
127 isolate
= irregexp::CreateIsolate(this);
133 // Set the initialized_ last, so that ProtectedData checks will allow us to
134 // initialize this context before it becomes the runtime's active context.
141 static void InitDefaultStackQuota(JSContext
* cx
) {
142 // Initialize stack quota to a reasonable default. Embedders can override this
143 // by calling JS_SetNativeStackQuota.
145 // NOTE: Firefox overrides these values. For the main thread this happens in
146 // XPCJSContext::Initialize.
148 #if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN))
149 static constexpr JS::NativeStackSize MaxStackSize
=
150 2 * 128 * sizeof(size_t) * 1024;
152 static constexpr JS::NativeStackSize MaxStackSize
=
153 128 * sizeof(size_t) * 1024;
155 JS_SetNativeStackQuota(cx
, MaxStackSize
);
158 JSContext
* js::NewContext(uint32_t maxBytes
, JSRuntime
* parentRuntime
) {
159 AutoNoteSingleThreadedRegion anstr
;
161 MOZ_RELEASE_ASSERT(!TlsContext
.get());
163 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
164 js::oom::SetThreadType(!parentRuntime
? js::THREAD_TYPE_MAIN
165 : js::THREAD_TYPE_WORKER
);
168 JSRuntime
* runtime
= js_new
<JSRuntime
>(parentRuntime
);
173 JSContext
* cx
= js_new
<JSContext
>(runtime
, JS::ContextOptions());
185 if (!runtime
->init(cx
, maxBytes
)) {
186 runtime
->destroyRuntime();
192 // Initialize stack quota last because simulators rely on the JSRuntime having
194 InitDefaultStackQuota(cx
);
199 void js::DestroyContext(JSContext
* cx
) {
200 JS_AbortIfWrongThread(cx
);
202 MOZ_ASSERT(!cx
->realm(), "Shouldn't destroy context with active realm");
203 MOZ_ASSERT(!cx
->activation(), "Shouldn't destroy context with activations");
205 cx
->checkNoGCRooters();
207 // Cancel all off thread Ion compiles. Completed Ion compiles may try to
208 // interrupt this context. See HelperThread::handleIonWorkload.
209 CancelOffThreadIonCompile(cx
->runtime());
211 cx
->jobQueue
= nullptr;
212 cx
->internalJobQueue
= nullptr;
213 SetContextProfilingStack(cx
, nullptr);
215 JSRuntime
* rt
= cx
->runtime();
217 // Flush promise tasks executing in helper threads early, before any parts
218 // of the JSRuntime that might be visible to helper threads are torn down.
219 rt
->offThreadPromiseState
.ref().shutdown(cx
);
221 // Destroy the runtime along with its last context.
222 js::AutoNoteSingleThreadedRegion nochecks
;
223 rt
->destroyRuntime();
224 js_delete_poison(cx
);
225 js_delete_poison(rt
);
228 void JS::RootingContext::checkNoGCRooters() {
230 for (auto const& stackRootPtr
: stackRoots_
) {
231 MOZ_ASSERT(stackRootPtr
== nullptr);
236 bool AutoResolving::alreadyStartedSlow() const {
238 AutoResolving
* cursor
= link
;
240 MOZ_ASSERT(this != cursor
);
241 if (object
.get() == cursor
->object
&& id
.get() == cursor
->id
&&
242 kind
== cursor
->kind
) {
245 } while (!!(cursor
= cursor
->link
));
249 static void MaybeReportOutOfMemoryForDifferentialTesting() {
251 * OOMs are non-deterministic, especially across different execution modes
252 * (e.g. interpreter vs JIT). When doing differential testing, print to stderr
253 * so that the fuzzers can detect this.
255 if (js::SupportDifferentialTesting()) {
256 fprintf(stderr
, "ReportOutOfMemory called\n");
261 * Since memory has been exhausted, avoid the normal error-handling path which
262 * allocates an error object, report and callstack. Instead simply throw the
263 * static atom "out of memory".
265 * Furthermore, callers of ReportOutOfMemory (viz., malloc) assume a GC does
266 * not occur, so GC must be avoided or suppressed.
268 void JSContext::onOutOfMemory() {
269 runtime()->hadOutOfMemory
= true;
270 gc::AutoSuppressGC
suppressGC(this);
272 /* Report the oom. */
273 if (JS::OutOfMemoryCallback oomCallback
= runtime()->oomCallback
) {
274 oomCallback(this, runtime()->oomCallbackData
);
277 // If we OOM early in process startup, this may be unavailable so just return
278 // instead of crashing unexpectedly.
279 if (MOZ_UNLIKELY(!runtime()->hasInitializedSelfHosting())) {
283 RootedValue
oomMessage(this, StringValue(names().out_of_memory_
));
284 setPendingException(oomMessage
, nullptr);
285 MOZ_ASSERT(status
== JS::ExceptionStatus::Throwing
);
286 status
= JS::ExceptionStatus::OutOfMemory
;
288 reportResourceExhaustion();
291 JS_PUBLIC_API
void js::ReportOutOfMemory(JSContext
* cx
) {
292 MaybeReportOutOfMemoryForDifferentialTesting();
297 JS_PUBLIC_API
void js::ReportLargeOutOfMemory(JSContext
* cx
) {
298 js::ReportOutOfMemory(cx
);
301 JS_PUBLIC_API
void js::ReportOutOfMemory(FrontendContext
* fc
) {
302 MaybeReportOutOfMemoryForDifferentialTesting();
307 static void MaybeReportOverRecursedForDifferentialTesting() {
309 * We cannot make stack depth deterministic across different
310 * implementations (e.g. JIT vs. interpreter will differ in
311 * their maximum stack depth).
312 * However, we can detect externally when we hit the maximum
313 * stack depth which is useful for external testing programs
316 if (js::SupportDifferentialTesting()) {
317 fprintf(stderr
, "ReportOverRecursed called\n");
321 void JSContext::onOverRecursed() {
322 // Try to construct an over-recursed error and then update the exception
323 // status to `OverRecursed`. Creating the error can fail, so check there
324 // is a reasonable looking exception pending before updating status.
325 JS_ReportErrorNumberASCII(this, GetErrorMessage
, nullptr,
326 JSMSG_OVER_RECURSED
);
327 if (isExceptionPending() && !isThrowingOutOfMemory()) {
328 MOZ_ASSERT(unwrappedException().isObject());
329 MOZ_ASSERT(status
== JS::ExceptionStatus::Throwing
);
330 status
= JS::ExceptionStatus::OverRecursed
;
333 reportResourceExhaustion();
336 JS_PUBLIC_API
void js::ReportOverRecursed(JSContext
* maybecx
) {
337 MaybeReportOverRecursedForDifferentialTesting();
343 maybecx
->onOverRecursed();
346 JS_PUBLIC_API
void js::ReportOverRecursed(FrontendContext
* fc
) {
347 MaybeReportOverRecursedForDifferentialTesting();
349 fc
->onOverRecursed();
352 void js::ReportOversizedAllocation(JSContext
* cx
, const unsigned errorNumber
) {
353 // The JIT may optimize away allocations if it determines that they aren't
354 // used. This can affect whether we throw an exception when the size of an
355 // allocation exceeds implementation-defined limits (eg JSString::MAX_LENGTH).
356 // These errors aren't interesting for the purposes of differential fuzzing.
357 // We print a message so that fuzzers can detect this case. To simplify
358 // tooling updates, we use the same message as ReportOutOfMemory.
359 if (js::SupportDifferentialTesting()) {
360 fprintf(stderr
, "ReportOutOfMemory called\n");
363 gc::AutoSuppressGC
suppressGC(cx
);
364 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, errorNumber
);
366 cx
->reportResourceExhaustion();
369 void js::ReportAllocationOverflow(JSContext
* cx
) {
370 if (js::SupportDifferentialTesting()) {
371 fprintf(stderr
, "ReportAllocationOverflow called\n");
378 cx
->reportAllocationOverflow();
381 void js::ReportAllocationOverflow(FrontendContext
* fc
) {
382 fc
->onAllocationOverflow();
385 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
386 void js::ReportUsageErrorASCII(JSContext
* cx
, HandleObject callee
,
388 RootedValue
usage(cx
);
389 if (!JS_GetProperty(cx
, callee
, "usage", &usage
)) {
393 if (!usage
.isString()) {
394 JS_ReportErrorASCII(cx
, "%s", msg
);
396 RootedString
usageStr(cx
, usage
.toString());
397 UniqueChars str
= JS_EncodeStringToUTF8(cx
, usageStr
);
401 JS_ReportErrorUTF8(cx
, "%s. Usage: %s", msg
, str
.get());
405 enum class PrintErrorKind
{ Error
, Warning
, Note
};
407 static void PrintErrorLine(FILE* file
, const char* prefix
,
408 JSErrorReport
* report
) {
409 if (const char16_t
* linebuf
= report
->linebuf()) {
413 size_t linebufLen
= report
->linebufLength();
415 // This function is only used for shell command-line sorts of stuff where
416 // performance doesn't really matter, so just encode into max-sized
418 mozilla::CheckedInt
<size_t> utf8Len(linebufLen
);
420 if (utf8Len
.isValid()) {
421 line
= UniqueChars(js_pod_malloc
<char>(utf8Len
.value()));
423 n
= mozilla::ConvertUtf16toUtf8({linebuf
, linebufLen
},
424 {line
.get(), utf8Len
.value()});
431 utf8buf
= line
.get();
433 static const char unavailableStr
[] = "<context unavailable>";
434 utf8buf
= unavailableStr
;
435 n
= js_strlen(unavailableStr
);
443 for (size_t i
= 0; i
< n
; i
++) {
444 fputc(utf8buf
[i
], file
);
447 // linebuf/utf8buf usually ends with a newline. If not, add one here.
448 if (n
== 0 || utf8buf
[n
- 1] != '\n') {
456 n
= report
->tokenOffset();
457 for (size_t i
= 0, j
= 0; i
< n
; i
++) {
458 if (utf8buf
[i
] == '\t') {
459 for (size_t k
= (j
+ 8) & ~7; j
< k
; j
++) {
471 static void PrintErrorLine(FILE* file
, const char* prefix
,
472 JSErrorNotes::Note
* note
) {}
474 template <typename T
>
475 static void PrintSingleError(FILE* file
, JS::ConstUTF8CharsZ toStringResult
,
476 T
* report
, PrintErrorKind kind
) {
478 if (report
->filename
) {
479 prefix
= JS_smprintf("%s:", report
->filename
.c_str());
482 if (report
->lineno
) {
483 prefix
= JS_smprintf("%s%u:%u ", prefix
? prefix
.get() : "", report
->lineno
,
484 report
->column
.oneOriginValue());
487 if (kind
!= PrintErrorKind::Error
) {
488 const char* kindPrefix
= nullptr;
490 case PrintErrorKind::Error
:
491 MOZ_CRASH("unreachable");
492 case PrintErrorKind::Warning
:
493 kindPrefix
= "warning";
495 case PrintErrorKind::Note
:
500 prefix
= JS_smprintf("%s%s: ", prefix
? prefix
.get() : "", kindPrefix
);
503 const char* message
=
504 toStringResult
? toStringResult
.c_str() : report
->message().c_str();
506 /* embedded newlines -- argh! */
508 while ((ctmp
= strchr(message
, '\n')) != 0) {
511 fputs(prefix
.get(), file
);
513 (void)fwrite(message
, 1, ctmp
- message
, file
);
517 /* If there were no filename or lineno, the prefix might be empty */
519 fputs(prefix
.get(), file
);
521 fputs(message
, file
);
523 PrintErrorLine(file
, prefix
.get(), report
);
529 static void PrintErrorImpl(FILE* file
, JS::ConstUTF8CharsZ toStringResult
,
530 JSErrorReport
* report
, bool reportWarnings
) {
533 /* Conditionally ignore reported warnings. */
534 if (report
->isWarning() && !reportWarnings
) {
538 PrintErrorKind kind
= PrintErrorKind::Error
;
539 if (report
->isWarning()) {
540 kind
= PrintErrorKind::Warning
;
542 PrintSingleError(file
, toStringResult
, report
, kind
);
545 for (auto&& note
: *report
->notes
) {
546 PrintSingleError(file
, JS::ConstUTF8CharsZ(), note
.get(),
547 PrintErrorKind::Note
);
552 JS_PUBLIC_API
void JS::PrintError(FILE* file
, JSErrorReport
* report
,
553 bool reportWarnings
) {
554 PrintErrorImpl(file
, JS::ConstUTF8CharsZ(), report
, reportWarnings
);
557 JS_PUBLIC_API
void JS::PrintError(FILE* file
,
558 const JS::ErrorReportBuilder
& builder
,
559 bool reportWarnings
) {
560 PrintErrorImpl(file
, builder
.toStringResult(), builder
.report(),
564 void js::ReportIsNotDefined(JSContext
* cx
, HandleId id
) {
565 if (UniqueChars printable
=
566 IdToPrintableUTF8(cx
, id
, IdToPrintableBehavior::IdIsIdentifier
)) {
567 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_NOT_DEFINED
,
572 void js::ReportIsNotDefined(JSContext
* cx
, Handle
<PropertyName
*> name
) {
573 RootedId
id(cx
, NameToId(name
));
574 ReportIsNotDefined(cx
, id
);
577 const char* NullOrUndefinedToCharZ(HandleValue v
) {
578 MOZ_ASSERT(v
.isNullOrUndefined());
579 return v
.isNull() ? "null" : "undefined";
582 void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext
* cx
, HandleValue v
,
584 MOZ_ASSERT(v
.isNullOrUndefined());
586 if (vIndex
== JSDVG_IGNORE_STACK
) {
587 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
588 JSMSG_CANT_CONVERT_TO
, NullOrUndefinedToCharZ(v
),
593 UniqueChars bytes
= DecompileValueGenerator(cx
, vIndex
, v
, nullptr);
598 if (strcmp(bytes
.get(), "undefined") == 0 ||
599 strcmp(bytes
.get(), "null") == 0) {
600 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_NO_PROPERTIES
,
603 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
604 JSMSG_UNEXPECTED_TYPE
, bytes
.get(),
605 NullOrUndefinedToCharZ(v
));
609 void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext
* cx
, HandleValue v
,
610 int vIndex
, HandleId key
) {
611 MOZ_ASSERT(v
.isNullOrUndefined());
613 if (!JS::Prefs::property_error_message_fix()) {
614 ReportIsNullOrUndefinedForPropertyAccess(cx
, v
, vIndex
);
618 RootedValue
idVal(cx
, IdToValue(key
));
619 RootedString
idStr(cx
, ValueToSource(cx
, idVal
));
624 UniqueChars keyStr
= StringToNewUTF8CharsZ(cx
, *idStr
);
629 if (vIndex
== JSDVG_IGNORE_STACK
) {
630 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_PROPERTY_FAIL
,
631 keyStr
.get(), NullOrUndefinedToCharZ(v
));
635 UniqueChars bytes
= DecompileValueGenerator(cx
, vIndex
, v
, nullptr);
640 if (strcmp(bytes
.get(), "undefined") == 0 ||
641 strcmp(bytes
.get(), "null") == 0) {
642 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_PROPERTY_FAIL
,
643 keyStr
.get(), bytes
.get());
647 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
648 JSMSG_PROPERTY_FAIL_EXPR
, keyStr
.get(), bytes
.get(),
649 NullOrUndefinedToCharZ(v
));
652 bool js::ReportValueError(JSContext
* cx
, const unsigned errorNumber
,
653 int spindex
, HandleValue v
, HandleString fallback
,
654 const char* arg1
, const char* arg2
) {
655 MOZ_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
>= 1);
656 MOZ_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
<= 3);
657 UniqueChars bytes
= DecompileValueGenerator(cx
, spindex
, v
, fallback
);
662 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, errorNumber
,
663 bytes
.get(), arg1
, arg2
);
667 JSObject
* js::CreateErrorNotesArray(JSContext
* cx
, JSErrorReport
* report
) {
668 Rooted
<ArrayObject
*> notesArray(cx
, NewDenseEmptyArray(cx
));
673 if (!report
->notes
) {
677 for (auto&& note
: *report
->notes
) {
678 Rooted
<PlainObject
*> noteObj(cx
, NewPlainObject(cx
));
683 RootedString
messageStr(cx
, note
->newMessageString(cx
));
687 RootedValue
messageVal(cx
, StringValue(messageStr
));
688 if (!DefineDataProperty(cx
, noteObj
, cx
->names().message
, messageVal
)) {
692 RootedValue
filenameVal(cx
);
693 if (const char* filename
= note
->filename
.c_str()) {
694 JS::UTF8Chars
utf8chars(filename
, strlen(filename
));
695 Rooted
<JSString
*> filenameStr(cx
, NewStringCopyUTF8N(cx
, utf8chars
));
699 filenameVal
= StringValue(filenameStr
);
701 if (!DefineDataProperty(cx
, noteObj
, cx
->names().fileName
, filenameVal
)) {
705 RootedValue
linenoVal(cx
, Int32Value(note
->lineno
));
706 if (!DefineDataProperty(cx
, noteObj
, cx
->names().lineNumber
, linenoVal
)) {
709 RootedValue
columnVal(cx
, Int32Value(note
->column
.oneOriginValue()));
710 if (!DefineDataProperty(cx
, noteObj
, cx
->names().columnNumber
, columnVal
)) {
714 if (!NewbornArrayPush(cx
, notesArray
, ObjectValue(*noteObj
))) {
722 void JSContext::recoverFromOutOfMemory() {
723 if (isExceptionPending()) {
724 MOZ_ASSERT(isThrowingOutOfMemory());
725 clearPendingException();
729 void JSContext::reportAllocationOverflow() {
730 gc::AutoSuppressGC
suppressGC(this);
731 JS_ReportErrorNumberASCII(this, GetErrorMessage
, nullptr,
732 JSMSG_ALLOC_OVERFLOW
);
735 JS::StackKind
JSContext::stackKindForCurrentPrincipal() {
736 return runningWithTrustedPrincipals() ? JS::StackForTrustedScript
737 : JS::StackForUntrustedScript
;
740 JS::NativeStackLimit
JSContext::stackLimitForCurrentPrincipal() {
741 return stackLimit(stackKindForCurrentPrincipal());
744 JS_PUBLIC_API
bool js::UseInternalJobQueues(JSContext
* cx
) {
745 // Internal job queue handling must be set up very early. Self-hosting
746 // initialization is as good a marker for that as any.
748 !cx
->runtime()->hasInitializedSelfHosting(),
749 "js::UseInternalJobQueues must be called early during runtime startup.");
750 MOZ_ASSERT(!cx
->jobQueue
);
751 auto queue
= MakeUnique
<InternalJobQueue
>(cx
);
756 cx
->internalJobQueue
= std::move(queue
);
757 cx
->jobQueue
= cx
->internalJobQueue
.ref().get();
759 cx
->runtime()->offThreadPromiseState
.ref().initInternalDispatchQueue();
760 MOZ_ASSERT(cx
->runtime()->offThreadPromiseState
.ref().initialized());
766 JSObject
* InternalJobQueue::copyJobs(JSContext
* cx
) {
767 Rooted
<ArrayObject
*> jobs(cx
, NewDenseEmptyArray(cx
));
772 for (const JSObject
* unwrappedJob
: queue
.get()) {
773 RootedObject
job(cx
, const_cast<JSObject
*>(unwrappedJob
));
774 if (!cx
->compartment()->wrap(cx
, &job
)) {
778 if (!NewbornArrayPush(cx
, jobs
, ObjectValue(*job
))) {
786 JS_PUBLIC_API JSObject
* js::GetJobsInInternalJobQueue(JSContext
* cx
) {
787 MOZ_ASSERT(cx
->internalJobQueue
.ref());
788 return cx
->internalJobQueue
->copyJobs(cx
);
792 JS_PUBLIC_API
bool js::EnqueueJob(JSContext
* cx
, JS::HandleObject job
) {
793 MOZ_ASSERT(cx
->jobQueue
);
794 return cx
->jobQueue
->enqueuePromiseJob(cx
, nullptr, job
, nullptr, nullptr);
797 JS_PUBLIC_API
void js::StopDrainingJobQueue(JSContext
* cx
) {
798 MOZ_ASSERT(cx
->internalJobQueue
.ref());
799 cx
->internalJobQueue
->interrupt();
802 JS_PUBLIC_API
void js::RestartDrainingJobQueue(JSContext
* cx
) {
803 MOZ_ASSERT(cx
->internalJobQueue
.ref());
804 cx
->internalJobQueue
->uninterrupt();
807 JS_PUBLIC_API
void js::RunJobs(JSContext
* cx
) {
808 MOZ_ASSERT(cx
->jobQueue
);
809 cx
->jobQueue
->runJobs(cx
);
810 JS::ClearKeptObjects(cx
);
813 JSObject
* InternalJobQueue::getIncumbentGlobal(JSContext
* cx
) {
814 if (!cx
->compartment()) {
820 bool InternalJobQueue::enqueuePromiseJob(JSContext
* cx
,
821 JS::HandleObject promise
,
822 JS::HandleObject job
,
823 JS::HandleObject allocationSite
,
824 JS::HandleObject incumbentGlobal
) {
826 if (!queue
.pushBack(job
)) {
827 ReportOutOfMemory(cx
);
831 JS::JobQueueMayNotBeEmpty(cx
);
835 void InternalJobQueue::runJobs(JSContext
* cx
) {
836 if (draining_
|| interrupted_
) {
841 cx
->runtime()->offThreadPromiseState
.ref().internalDrain(cx
);
843 // It doesn't make sense for job queue draining to be reentrant. At the
844 // same time we don't want to assert against it, because that'd make
845 // drainJobQueue unsafe for fuzzers. We do want fuzzers to test this,
846 // so we simply ignore nested calls of drainJobQueue.
849 RootedObject
job(cx
);
850 JS::HandleValueArray
args(JS::HandleValueArray::empty());
851 RootedValue
rval(cx
);
853 // Execute jobs in a loop until we've reached the end of the queue.
854 while (!queue
.empty()) {
855 // A previous job might have set this flag. E.g., the js shell
856 // sets it if the `quit` builtin function is called.
864 // If the next job is the last job in the job queue, allow
865 // skipping the standard job queuing behavior.
867 JS::JobQueueIsEmpty(cx
);
870 AutoRealm
ar(cx
, &job
->as
<JSFunction
>());
872 if (!JS::Call(cx
, UndefinedHandleValue
, job
, args
, &rval
)) {
873 // Nothing we can do about uncatchable exceptions.
874 if (!cx
->isExceptionPending()) {
878 if (cx
->getPendingException(&exn
)) {
880 * Clear the exception, because
881 * PrepareScriptEnvironmentAndInvoke will assert that we don't
884 cx
->clearPendingException();
885 js::ReportExceptionClosure
reportExn(exn
);
886 PrepareScriptEnvironmentAndInvoke(cx
, cx
->global(), reportExn
);
900 // It's possible a job added a new off-thread promise task.
901 if (!cx
->runtime()->offThreadPromiseState
.ref().internalHasPending()) {
907 bool InternalJobQueue::empty() const { return queue
.empty(); }
909 JSObject
* InternalJobQueue::maybeFront() const {
914 return queue
.get().front();
917 class js::InternalJobQueue::SavedQueue
: public JobQueue::SavedJobQueue
{
919 SavedQueue(JSContext
* cx
, Queue
&& saved
, bool draining
)
920 : cx(cx
), saved(cx
, std::move(saved
)), draining_(draining
) {
921 MOZ_ASSERT(cx
->internalJobQueue
.ref());
925 MOZ_ASSERT(cx
->internalJobQueue
.ref());
926 cx
->internalJobQueue
->queue
= std::move(saved
.get());
927 cx
->internalJobQueue
->draining_
= draining_
;
932 PersistentRooted
<Queue
> saved
;
936 js::UniquePtr
<JS::JobQueue::SavedJobQueue
> InternalJobQueue::saveJobQueue(
939 js::MakeUnique
<SavedQueue
>(cx
, std::move(queue
.get()), draining_
);
941 // When MakeUnique's allocation fails, the SavedQueue constructor is never
942 // called, so this->queue is still initialized. (The move doesn't occur
943 // until the constructor gets called.)
944 ReportOutOfMemory(cx
);
948 queue
= Queue(SystemAllocPolicy());
953 mozilla::GenericErrorResult
<OOM
> JSContext::alreadyReportedOOM() {
954 MOZ_ASSERT(isThrowingOutOfMemory());
955 return mozilla::Err(JS::OOM());
958 mozilla::GenericErrorResult
<JS::Error
> JSContext::alreadyReportedError() {
959 return mozilla::Err(JS::Error());
962 JSContext::JSContext(JSRuntime
* runtime
, const JS::ContextOptions
& options
)
963 : RootingContext(runtime
? &runtime
->gc
.nursery() : nullptr),
965 options_(this, options
),
966 measuringExecutionTime_(this, false),
967 jitActivation(this, nullptr),
968 isolate(this, nullptr),
969 activation_(this, nullptr),
970 profilingActivation_(nullptr),
971 entryMonitor(this, nullptr),
972 noExecuteDebuggerTop(this, nullptr),
974 inUnsafeCallWithABI(this, false),
975 hasAutoUnsafeCallWithABI(this, false),
978 simulator_(this, nullptr),
980 dtoaState(this, nullptr),
982 #ifdef FUZZING_JS_FUZZILLI
984 executionHashInputs(0),
987 noNurseryAllocationCheck(this, 0),
988 disableStrictProxyCheckingCount(this, 0),
990 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
991 runningOOMTest(this, false),
993 inUnsafeRegion(this, 0),
994 generationalDisabled(this, 0),
995 compactingDisabledCount(this, 0),
997 regExpSearcherLastLimit(this, RegExpSearcherLastLimitSentinel
),
999 regExpSearcherLastLimit(this, 0),
1001 frontendCollectionPool_(this),
1002 suppressProfilerSampling(false),
1003 tempLifoAlloc_(this, (size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE
),
1004 debuggerMutations(this, 0),
1005 status(this, JS::ExceptionStatus::None
),
1006 unwrappedException_(this),
1007 unwrappedExceptionStack_(this),
1009 hadResourceExhaustion_(this, false),
1011 reportGranularity(this, JS_DEFAULT_JITREPORT_GRANULARITY
),
1012 resolvingList(this, nullptr),
1014 enteredPolicy(this, nullptr),
1016 generatingError(this, false),
1017 cycleDetectorVector_(this, this),
1019 asyncStackForNewActivations_(this),
1020 asyncCauseForNewActivations(this, nullptr),
1021 asyncCallIsExplicit(this, false),
1022 interruptCallbacks_(this),
1023 interruptCallbackDisabled(this, false),
1025 inlinedICScript_(this, nullptr),
1026 jitStackLimit(JS::NativeStackLimitMin
),
1027 jitStackLimitNoInterrupt(this, JS::NativeStackLimitMin
),
1028 jobQueue(this, nullptr),
1029 internalJobQueue(this),
1030 canSkipEnqueuingJobs(this, false),
1031 promiseRejectionTrackerCallback(this, nullptr),
1032 promiseRejectionTrackerCallbackData(this, nullptr),
1033 insideExclusiveDebuggerOnEval(this, nullptr) {
1034 MOZ_ASSERT(static_cast<JS::RootingContext
*>(this) ==
1035 JS::RootingContext::get(this));
1038 JSContext::~JSContext() {
1040 // Clear the initialized_ first, so that ProtectedData checks will allow us to
1041 // destroy this context even if the runtime is already gone.
1042 initialized_
= false;
1045 /* Free the stuff hanging off of cx. */
1046 MOZ_ASSERT(!resolvingList
);
1049 DestroyDtoaState(dtoaState
);
1052 fx
.destroyInstance();
1055 js::jit::Simulator::Destroy(simulator_
);
1059 irregexp::DestroyIsolate(isolate
.ref());
1062 TlsContext
.set(nullptr);
1065 void JSContext::setRuntime(JSRuntime
* rt
) {
1066 MOZ_ASSERT(!resolvingList
);
1067 MOZ_ASSERT(!compartment());
1068 MOZ_ASSERT(!activation());
1069 MOZ_ASSERT(!unwrappedException_
.ref().initialized());
1070 MOZ_ASSERT(!unwrappedExceptionStack_
.ref().initialized());
1071 MOZ_ASSERT(!asyncStackForNewActivations_
.ref().initialized());
1076 #if defined(NIGHTLY_BUILD)
1077 static bool IsOutOfMemoryException(JSContext
* cx
, const Value
& v
) {
1078 return v
== StringValue(cx
->names().out_of_memory_
);
1082 void JSContext::setPendingException(HandleValue v
, Handle
<SavedFrame
*> stack
) {
1083 #if defined(NIGHTLY_BUILD)
1085 // Do not intercept exceptions if we are already
1086 // in the exception interceptor. That would lead
1087 // to infinite recursion.
1088 if (this->runtime()->errorInterception
.isExecuting
) {
1092 // Check whether we have an interceptor at all.
1093 if (!this->runtime()->errorInterception
.interceptor
) {
1097 // Don't report OOM exceptions. The interceptor isn't interested in those
1098 // and they can confuse the interceptor because OOM can be thrown when we
1099 // are not in a realm (atom allocation, for example).
1100 if (IsOutOfMemoryException(this, v
)) {
1104 // Make sure that we do not call the interceptor from within
1106 this->runtime()->errorInterception
.isExecuting
= true;
1108 // The interceptor must be infallible.
1109 const mozilla::DebugOnly
<bool> wasExceptionPending
=
1110 this->isExceptionPending();
1111 this->runtime()->errorInterception
.interceptor
->interceptError(this, v
);
1112 MOZ_ASSERT(wasExceptionPending
== this->isExceptionPending());
1114 this->runtime()->errorInterception
.isExecuting
= false;
1116 #endif // defined(NIGHTLY_BUILD)
1118 // overRecursed_ is set after the fact by ReportOverRecursed.
1119 this->status
= JS::ExceptionStatus::Throwing
;
1120 this->unwrappedException() = v
;
1121 this->unwrappedExceptionStack() = stack
;
1124 void JSContext::setPendingException(HandleValue value
,
1125 ShouldCaptureStack captureStack
) {
1126 Rooted
<SavedFrame
*> nstack(this);
1127 if (captureStack
== ShouldCaptureStack::Always
||
1128 realm()->shouldCaptureStackForThrow()) {
1129 RootedObject
stack(this);
1130 if (!CaptureStack(this, &stack
)) {
1131 clearPendingException();
1134 nstack
= &stack
->as
<SavedFrame
>();
1137 setPendingException(value
, nstack
);
1140 bool JSContext::getPendingException(MutableHandleValue rval
) {
1141 MOZ_ASSERT(isExceptionPending());
1143 RootedValue
exception(this, unwrappedException());
1144 if (zone()->isAtomsZone()) {
1145 rval
.set(exception
);
1149 Rooted
<SavedFrame
*> stack(this, unwrappedExceptionStack());
1150 JS::ExceptionStatus prevStatus
= status
;
1151 clearPendingException();
1152 if (!compartment()->wrap(this, &exception
)) {
1155 this->check(exception
);
1156 setPendingException(exception
, stack
);
1157 status
= prevStatus
;
1159 rval
.set(exception
);
1163 bool JSContext::getPendingExceptionStack(MutableHandleValue rval
) {
1164 MOZ_ASSERT(isExceptionPending());
1166 Rooted
<SavedFrame
*> exceptionStack(this, unwrappedExceptionStack());
1167 if (!exceptionStack
) {
1171 if (zone()->isAtomsZone()) {
1172 rval
.setObject(*exceptionStack
);
1176 RootedValue
stack(this, ObjectValue(*exceptionStack
));
1177 RootedValue
exception(this, unwrappedException());
1178 JS::ExceptionStatus prevStatus
= status
;
1179 clearPendingException();
1180 if (!compartment()->wrap(this, &exception
) ||
1181 !compartment()->wrap(this, &stack
)) {
1185 setPendingException(exception
, exceptionStack
);
1186 status
= prevStatus
;
1192 SavedFrame
* JSContext::getPendingExceptionStack() {
1193 return unwrappedExceptionStack();
1196 bool JSContext::isClosingGenerator() {
1197 return isExceptionPending() &&
1198 unwrappedException().isMagic(JS_GENERATOR_CLOSING
);
1201 bool JSContext::isThrowingDebuggeeWouldRun() {
1202 return isExceptionPending() && unwrappedException().isObject() &&
1203 unwrappedException().toObject().is
<ErrorObject
>() &&
1204 unwrappedException().toObject().as
<ErrorObject
>().type() ==
1205 JSEXN_DEBUGGEEWOULDRUN
;
1208 bool JSContext::isRuntimeCodeGenEnabled(JS::RuntimeCode kind
,
1209 HandleString code
) {
1210 // Make sure that the CSP callback is installed and that it permits runtime
1212 if (JSCSPEvalChecker allows
=
1213 runtime()->securityCallbacks
->contentSecurityPolicyAllows
) {
1214 return allows(this, kind
, code
);
1220 size_t JSContext::sizeOfExcludingThis(
1221 mozilla::MallocSizeOf mallocSizeOf
) const {
1223 * There are other JSContext members that could be measured; the following
1224 * ones have been found by DMD to be worth measuring. More stuff may be
1227 return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf
) +
1228 irregexp::IsolateSizeOfIncludingThis(isolate
, mallocSizeOf
);
1231 size_t JSContext::sizeOfIncludingThis(
1232 mozilla::MallocSizeOf mallocSizeOf
) const {
1233 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf
);
1237 bool JSContext::inAtomsZone() const { return zone_
->isAtomsZone(); }
1240 void JSContext::trace(JSTracer
* trc
) {
1241 cycleDetectorVector().trace(trc
);
1242 geckoProfiler().trace(trc
);
1244 irregexp::TraceIsolate(trc
, isolate
.ref());
1248 JS::NativeStackLimit
JSContext::stackLimitForJitCode(JS::StackKind kind
) {
1250 return simulator()->stackLimit();
1252 return stackLimit(kind
);
1256 void JSContext::resetJitStackLimit() {
1257 // Note that, for now, we use the untrusted limit for ion. This is fine,
1258 // because it's the most conservative limit, and if we hit it, we'll bail
1259 // out of ion into the interpreter, which will do a proper recursion check.
1261 jitStackLimit
= jit::Simulator::StackLimit();
1263 jitStackLimit
= nativeStackLimit
[JS::StackForUntrustedScript
];
1265 jitStackLimitNoInterrupt
= jitStackLimit
;
1268 void JSContext::initJitStackLimit() { resetJitStackLimit(); }
1270 JSScript
* JSContext::currentScript(jsbytecode
** ppc
,
1271 AllowCrossRealm allowCrossRealm
) {
1276 // Fast path: there are no JS frames on the stack if there's no activation.
1277 if (!activation()) {
1281 FrameIter
iter(this);
1282 if (iter
.done() || !iter
.hasScript()) {
1286 JSScript
* script
= iter
.script();
1287 if (allowCrossRealm
== AllowCrossRealm::DontAllow
&&
1288 script
->realm() != realm()) {
1298 #ifdef JS_CRASH_DIAGNOSTICS
1299 void ContextChecks::check(AbstractFramePtr frame
, int argIndex
) {
1301 check(frame
.realm(), argIndex
);
1306 void AutoEnterOOMUnsafeRegion::crash_impl(const char* reason
) {
1308 js::NoteIntentionalCrash();
1309 SprintfLiteral(msgbuf
, "[unhandlable oom] %s", reason
);
1311 // In non-DEBUG builds MOZ_CRASH normally doesn't print to stderr so we have
1312 // to do this explicitly (the jit-test allow-unhandlable-oom annotation and
1313 // fuzzers depend on it).
1314 fprintf(stderr
, "Hit MOZ_CRASH(%s) at %s:%d\n", msgbuf
, __FILE__
, __LINE__
);
1316 MOZ_CRASH_UNSAFE(msgbuf
);
1319 mozilla::Atomic
<AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback
,
1321 AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback(nullptr);
1323 void AutoEnterOOMUnsafeRegion::crash_impl(size_t size
, const char* reason
) {
1325 JS::AutoSuppressGCAnalysis suppress
;
1326 if (annotateOOMSizeCallback
) {
1327 annotateOOMSizeCallback(size
);
1333 void ExternalValueArray::trace(JSTracer
* trc
) {
1334 if (Value
* vp
= begin()) {
1335 TraceRootRange(trc
, length(), vp
, "js::ExternalValueArray");
1340 AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness
)
1341 : cx_(TlsContext
.get()),
1342 nested_(cx_
? cx_
->hasAutoUnsafeCallWithABI
: false),
1345 // This is a helper thread doing Ion or Wasm compilation - nothing to do.
1348 switch (strictness
) {
1349 case UnsafeABIStrictness::NoExceptions
:
1350 MOZ_ASSERT(!JS_IsExceptionPending(cx_
));
1351 checkForPendingException_
= true;
1353 case UnsafeABIStrictness::AllowPendingExceptions
:
1354 checkForPendingException_
= !JS_IsExceptionPending(cx_
);
1356 case UnsafeABIStrictness::AllowThrownExceptions
:
1357 checkForPendingException_
= false;
1361 cx_
->hasAutoUnsafeCallWithABI
= true;
1364 AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI() {
1368 MOZ_ASSERT(cx_
->hasAutoUnsafeCallWithABI
);
1370 cx_
->hasAutoUnsafeCallWithABI
= false;
1371 cx_
->inUnsafeCallWithABI
= false;
1373 MOZ_ASSERT_IF(checkForPendingException_
, !JS_IsExceptionPending(cx_
));
1378 JS_PUBLIC_API
void js::IncWasiRecursionDepth(JSContext
* cx
) {
1379 ++JS::RootingContext::get(cx
)->wasiRecursionDepth
;
1382 JS_PUBLIC_API
void js::DecWasiRecursionDepth(JSContext
* cx
) {
1383 MOZ_ASSERT(JS::RootingContext::get(cx
)->wasiRecursionDepth
> 0);
1384 --JS::RootingContext::get(cx
)->wasiRecursionDepth
;
1387 JS_PUBLIC_API
bool js::CheckWasiRecursionLimit(JSContext
* cx
) {
1388 // WASI has two limits:
1389 // 1) The stack pointer in linear memory that grows to zero. See
1390 // --stack-first in js/src/shell/moz.build.
1391 // 2) The JS::RootingContext::wasiRecursionDepth that counts recursion depth.
1392 // Here we should check both.
1393 if (JS::RootingContext::get(cx
)->wasiRecursionDepth
>=
1394 JS::RootingContext::wasiRecursionDepthLimit
) {