Bug 1843499 - Part 1: Add ThrowWithStack and ExceptionAndStack opcodes. r=iain
[gecko.git] / js / src / vm / JSContext.cpp
bloba9a5f4ee1e9d0ede0f42d6f37e33c95f85e32032
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/. */
7 /*
8 * JS execution context.
9 */
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
19 #include <string.h>
20 #ifdef ANDROID
21 # include <android/log.h>
22 # include <fstream>
23 # include <string>
24 #endif // ANDROID
25 #ifdef XP_WIN
26 # include <processthreadsapi.h>
27 #endif // XP_WIN
29 #include "jsapi.h" // JS_SetNativeStackQuota
30 #include "jsexn.h"
31 #include "jstypes.h"
33 #include "builtin/RegExp.h" // js::RegExpSearcherLastLimitSentinel
34 #include "frontend/FrontendContext.h"
35 #include "gc/GC.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
60 #include "vm/Realm.h"
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"
67 using namespace js;
69 #ifdef DEBUG
70 JSContext* js::MaybeGetJSContext() {
71 if (!TlsContext.init()) {
72 return nullptr;
74 return TlsContext.get();
76 #endif
78 bool js::AutoCycleDetector::init() {
79 MOZ_ASSERT(cyclic);
81 AutoCycleDetector::Vector& vector = cx->cycleDetectorVector();
83 for (JSObject* obj2 : vector) {
84 if (MOZ_UNLIKELY(obj == obj2)) {
85 return true;
89 if (!vector.append(obj)) {
90 return false;
93 cyclic = false;
94 return true;
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) {
102 vec.popBack();
103 } else {
104 // Avoid holding on to unused heap allocations.
105 vec.clearAndFree();
110 bool JSContext::init() {
111 TlsContext.set(this);
112 nativeStackBase_.emplace(GetNativeStackBase());
114 if (!fx.initInstance()) {
115 return false;
118 #ifdef JS_SIMULATOR
119 simulator_ = jit::Simulator::Create();
120 if (!simulator_) {
121 return false;
123 #endif
125 isolate = irregexp::CreateIsolate(this);
126 if (!isolate) {
127 return false;
130 #ifdef DEBUG
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.
133 initialized_ = true;
134 #endif
136 return true;
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;
149 #else
150 static constexpr JS::NativeStackSize MaxStackSize =
151 128 * sizeof(size_t) * 1024;
152 #endif
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);
164 #endif
166 JSRuntime* runtime = js_new<JSRuntime>(parentRuntime);
167 if (!runtime) {
168 return nullptr;
171 JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
172 if (!cx) {
173 js_delete(runtime);
174 return nullptr;
177 if (!cx->init()) {
178 js_delete(cx);
179 js_delete(runtime);
180 return nullptr;
183 if (!runtime->init(cx, maxBytes)) {
184 runtime->destroyRuntime();
185 js_delete(cx);
186 js_delete(runtime);
187 return nullptr;
190 // Initialize stack quota last because simulators rely on the JSRuntime having
191 // been initialized.
192 InitDefaultStackQuota(cx);
194 return 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() {
227 #ifdef DEBUG
228 for (auto const& stackRootPtr : stackRoots_) {
229 MOZ_ASSERT(stackRootPtr == nullptr);
231 #endif
234 bool AutoResolving::alreadyStartedSlow() const {
235 MOZ_ASSERT(link);
236 AutoResolving* cursor = link;
237 do {
238 MOZ_ASSERT(this != cursor);
239 if (object.get() == cursor->object && id.get() == cursor->id &&
240 kind == cursor->kind) {
241 return true;
243 } while (!!(cursor = cursor->link));
244 return false;
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())) {
278 return;
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();
292 cx->onOutOfMemory();
295 JS_PUBLIC_API void js::ReportLargeOutOfMemory(JSContext* cx) {
296 js::ReportOutOfMemory(cx);
299 JS_PUBLIC_API void js::ReportOutOfMemory(FrontendContext* fc) {
300 MaybeReportOutOfMemoryForDifferentialTesting();
302 fc->onOutOfMemory();
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
312 * like fuzzers.
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();
337 if (!maybecx) {
338 return;
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");
372 if (!cx) {
373 return;
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,
385 const char* msg) {
386 RootedValue usage(cx);
387 if (!JS_GetProperty(cx, callee, "usage", &usage)) {
388 return;
391 if (!usage.isString()) {
392 JS_ReportErrorASCII(cx, "%s", msg);
393 } else {
394 RootedString usageStr(cx, usage.toString());
395 UniqueChars str = JS_EncodeStringToUTF8(cx, usageStr);
396 if (!str) {
397 return;
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()) {
408 UniqueChars line;
409 size_t n;
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
415 // memory.
416 mozilla::CheckedInt<size_t> utf8Len(linebufLen);
417 utf8Len *= 3;
418 if (utf8Len.isValid()) {
419 line = UniqueChars(js_pod_malloc<char>(utf8Len.value()));
420 if (line) {
421 n = mozilla::ConvertUtf16toUtf8({linebuf, linebufLen},
422 {line.get(), utf8Len.value()});
427 const char* utf8buf;
428 if (line) {
429 utf8buf = line.get();
430 } else {
431 static const char unavailableStr[] = "<context unavailable>";
432 utf8buf = unavailableStr;
433 n = js_strlen(unavailableStr);
436 fputs(":\n", file);
437 if (prefix) {
438 fputs(prefix, file);
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') {
447 fputc('\n', file);
450 if (prefix) {
451 fputs(prefix, file);
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++) {
458 fputc('.', file);
460 continue;
462 fputc('.', file);
463 j++;
465 fputc('^', file);
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) {
475 UniqueChars prefix;
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;
487 switch (kind) {
488 case PrintErrorKind::Error:
489 MOZ_CRASH("unreachable");
490 case PrintErrorKind::Warning:
491 kindPrefix = "warning";
492 break;
493 case PrintErrorKind::Note:
494 kindPrefix = "note";
495 break;
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! */
505 const char* ctmp;
506 while ((ctmp = strchr(message, '\n')) != 0) {
507 ctmp++;
508 if (prefix) {
509 fputs(prefix.get(), file);
511 (void)fwrite(message, 1, ctmp - message, file);
512 message = ctmp;
515 /* If there were no filename or lineno, the prefix might be empty */
516 if (prefix) {
517 fputs(prefix.get(), file);
519 fputs(message, file);
521 PrintErrorLine(file, prefix.get(), report);
522 fputc('\n', file);
524 fflush(file);
527 static void PrintErrorImpl(FILE* file, JS::ConstUTF8CharsZ toStringResult,
528 JSErrorReport* report, bool reportWarnings) {
529 MOZ_ASSERT(report);
531 /* Conditionally ignore reported warnings. */
532 if (report->isWarning() && !reportWarnings) {
533 return;
536 PrintErrorKind kind = PrintErrorKind::Error;
537 if (report->isWarning()) {
538 kind = PrintErrorKind::Warning;
540 PrintSingleError(file, toStringResult, report, kind);
542 if (report->notes) {
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(),
559 reportWarnings);
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,
566 printable.get());
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,
581 int vIndex) {
582 MOZ_ASSERT(v.isNullOrUndefined());
584 if (vIndex == JSDVG_IGNORE_STACK) {
585 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
586 JSMSG_CANT_CONVERT_TO, NullOrUndefinedToCharZ(v),
587 "object");
588 return;
591 UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr);
592 if (!bytes) {
593 return;
596 if (strcmp(bytes.get(), "undefined") == 0 ||
597 strcmp(bytes.get(), "null") == 0) {
598 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
599 bytes.get());
600 } else {
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);
613 return;
616 RootedValue idVal(cx, IdToValue(key));
617 RootedString idStr(cx, ValueToSource(cx, idVal));
618 if (!idStr) {
619 return;
622 UniqueChars keyStr = StringToNewUTF8CharsZ(cx, *idStr);
623 if (!keyStr) {
624 return;
627 if (vIndex == JSDVG_IGNORE_STACK) {
628 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
629 keyStr.get(), NullOrUndefinedToCharZ(v));
630 return;
633 UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr);
634 if (!bytes) {
635 return;
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());
642 return;
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);
656 if (!bytes) {
657 return false;
660 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
661 bytes.get(), arg1, arg2);
662 return false;
665 JSObject* js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report) {
666 Rooted<ArrayObject*> notesArray(cx, NewDenseEmptyArray(cx));
667 if (!notesArray) {
668 return nullptr;
671 if (!report->notes) {
672 return notesArray;
675 for (auto&& note : *report->notes) {
676 Rooted<PlainObject*> noteObj(cx, NewPlainObject(cx));
677 if (!noteObj) {
678 return nullptr;
681 RootedString messageStr(cx, note->newMessageString(cx));
682 if (!messageStr) {
683 return nullptr;
685 RootedValue messageVal(cx, StringValue(messageStr));
686 if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal)) {
687 return nullptr;
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));
694 if (!filenameStr) {
695 return nullptr;
697 filenameVal = StringValue(filenameStr);
699 if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal)) {
700 return nullptr;
703 RootedValue linenoVal(cx, Int32Value(note->lineno));
704 if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal)) {
705 return nullptr;
707 RootedValue columnVal(cx, Int32Value(note->column.oneOriginValue()));
708 if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal)) {
709 return nullptr;
712 if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj))) {
713 return nullptr;
717 return notesArray;
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.
745 MOZ_RELEASE_ASSERT(
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);
750 if (!queue) {
751 return false;
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());
760 return true;
763 #ifdef DEBUG
764 JSObject* InternalJobQueue::copyJobs(JSContext* cx) {
765 Rooted<ArrayObject*> jobs(cx, NewDenseEmptyArray(cx));
766 if (!jobs) {
767 return nullptr;
770 for (const JSObject* unwrappedJob : queue.get()) {
771 RootedObject job(cx, const_cast<JSObject*>(unwrappedJob));
772 if (!cx->compartment()->wrap(cx, &job)) {
773 return nullptr;
776 if (!NewbornArrayPush(cx, jobs, ObjectValue(*job))) {
777 return nullptr;
781 return jobs;
784 JS_PUBLIC_API JSObject* js::GetJobsInInternalJobQueue(JSContext* cx) {
785 MOZ_ASSERT(cx->internalJobQueue.ref());
786 return cx->internalJobQueue->copyJobs(cx);
788 #endif
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()) {
808 return nullptr;
810 return cx->global();
813 bool InternalJobQueue::enqueuePromiseJob(JSContext* cx,
814 JS::HandleObject promise,
815 JS::HandleObject job,
816 JS::HandleObject allocationSite,
817 JS::HandleObject incumbentGlobal) {
818 MOZ_ASSERT(job);
819 if (!queue.pushBack(job)) {
820 ReportOutOfMemory(cx);
821 return false;
824 JS::JobQueueMayNotBeEmpty(cx);
825 return true;
828 void InternalJobQueue::runJobs(JSContext* cx) {
829 if (draining_ || interrupted_) {
830 return;
833 while (true) {
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.
840 draining_ = true;
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.
850 if (interrupted_) {
851 break;
854 job = queue.front();
855 queue.popFront();
857 // If the next job is the last job in the job queue, allow
858 // skipping the standard job queuing behavior.
859 if (queue.empty()) {
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()) {
868 continue;
870 RootedValue exn(cx);
871 if (cx->getPendingException(&exn)) {
873 * Clear the exception, because
874 * PrepareScriptEnvironmentAndInvoke will assert that we don't
875 * have one.
877 cx->clearPendingException();
878 js::ReportExceptionClosure reportExn(exn);
879 PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn);
885 draining_ = false;
887 if (interrupted_) {
888 interrupted_ = false;
889 break;
892 queue.clear();
894 // It's possible a job added a new off-thread promise task.
895 if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending()) {
896 break;
901 bool InternalJobQueue::empty() const { return queue.empty(); }
903 JSObject* InternalJobQueue::maybeFront() const {
904 if (queue.empty()) {
905 return nullptr;
908 return queue.get().front();
911 class js::InternalJobQueue::SavedQueue : public JobQueue::SavedJobQueue {
912 public:
913 SavedQueue(JSContext* cx, Queue&& saved, bool draining)
914 : cx(cx), saved(cx, std::move(saved)), draining_(draining) {
915 MOZ_ASSERT(cx->internalJobQueue.ref());
918 ~SavedQueue() {
919 MOZ_ASSERT(cx->internalJobQueue.ref());
920 cx->internalJobQueue->queue = std::move(saved.get());
921 cx->internalJobQueue->draining_ = draining_;
924 private:
925 JSContext* cx;
926 PersistentRooted<Queue> saved;
927 bool draining_;
930 js::UniquePtr<JS::JobQueue::SavedJobQueue> InternalJobQueue::saveJobQueue(
931 JSContext* cx) {
932 auto saved =
933 js::MakeUnique<SavedQueue>(cx, std::move(queue.get()), draining_);
934 if (!saved) {
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);
939 return nullptr;
942 queue = Queue(SystemAllocPolicy());
943 draining_ = false;
944 return saved;
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),
958 runtime_(runtime),
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),
967 #ifdef DEBUG
968 inUnsafeCallWithABI(this, false),
969 hasAutoUnsafeCallWithABI(this, false),
970 #endif
971 #ifdef JS_SIMULATOR
972 simulator_(this, nullptr),
973 #endif
974 dtoaState(this, nullptr),
975 suppressGC(this, 0),
976 #ifdef FUZZING_JS_FUZZILLI
977 executionHash(1),
978 executionHashInputs(0),
979 #endif
980 #ifdef DEBUG
981 noNurseryAllocationCheck(this, 0),
982 disableStrictProxyCheckingCount(this, 0),
983 #endif
984 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
985 runningOOMTest(this, false),
986 #endif
987 inUnsafeRegion(this, 0),
988 generationalDisabled(this, 0),
989 compactingDisabledCount(this, 0),
990 #ifdef DEBUG
991 regExpSearcherLastLimit(this, RegExpSearcherLastLimitSentinel),
992 #else
993 regExpSearcherLastLimit(this, 0),
994 #endif
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),
1003 #ifdef DEBUG
1004 hadResourceExhaustion_(this, false),
1005 #endif
1006 reportGranularity(this, JS_DEFAULT_JITREPORT_GRANULARITY),
1007 resolvingList(this, nullptr),
1008 #ifdef DEBUG
1009 enteredPolicy(this, nullptr),
1010 #endif
1011 generatingError(this, false),
1012 cycleDetectorVector_(this, this),
1013 data(nullptr),
1014 asyncStackForNewActivations_(this),
1015 asyncCauseForNewActivations(this, nullptr),
1016 asyncCallIsExplicit(this, false),
1017 interruptCallbacks_(this),
1018 interruptCallbackDisabled(this, false),
1019 interruptBits_(0),
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() {
1034 #ifdef DEBUG
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;
1038 #endif
1040 /* Free the stuff hanging off of cx. */
1041 MOZ_ASSERT(!resolvingList);
1043 if (dtoaState) {
1044 DestroyDtoaState(dtoaState);
1047 fx.destroyInstance();
1049 #ifdef JS_SIMULATOR
1050 js::jit::Simulator::Destroy(simulator_);
1051 #endif
1053 if (isolate) {
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());
1068 runtime_ = rt;
1071 #if defined(NIGHTLY_BUILD)
1072 static bool IsOutOfMemoryException(JSContext* cx, const Value& v) {
1073 return v == StringValue(cx->names().out_of_memory_);
1075 #endif
1077 void JSContext::setPendingException(HandleValue v, Handle<SavedFrame*> stack) {
1078 #if defined(NIGHTLY_BUILD)
1079 do {
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) {
1084 break;
1087 // Check whether we have an interceptor at all.
1088 if (!this->runtime()->errorInterception.interceptor) {
1089 break;
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)) {
1096 break;
1099 // Make sure that we do not call the interceptor from within
1100 // the interceptor.
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;
1110 } while (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();
1128 if (stack) {
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);
1141 return true;
1144 Rooted<SavedFrame*> stack(this, unwrappedExceptionStack());
1145 JS::ExceptionStatus prevStatus = status;
1146 clearPendingException();
1147 if (!compartment()->wrap(this, &exception)) {
1148 return false;
1150 this->check(exception);
1151 setPendingException(exception, stack);
1152 status = prevStatus;
1154 rval.set(exception);
1155 return true;
1158 bool JSContext::getPendingExceptionStack(MutableHandleValue rval) {
1159 MOZ_ASSERT(isExceptionPending());
1161 Rooted<SavedFrame*> exceptionStack(this, unwrappedExceptionStack());
1162 if (!exceptionStack) {
1163 rval.setNull();
1164 return true;
1166 if (zone()->isAtomsZone()) {
1167 rval.setObject(*exceptionStack);
1168 return true;
1171 RootedValue stack(this, ObjectValue(*exceptionStack));
1172 RootedValue exception(this, unwrappedException());
1173 JS::ExceptionStatus prevStatus = status;
1174 clearPendingException();
1175 if (!compartment()->wrap(this, &stack)) {
1176 return false;
1178 this->check(stack);
1179 setPendingException(exception, exceptionStack);
1180 status = prevStatus;
1182 rval.set(stack);
1183 return true;
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
1205 // code generation.
1206 if (JSCSPEvalChecker allows =
1207 runtime()->securityCallbacks->contentSecurityPolicyAllows) {
1208 return allows(this, kind, code);
1211 return true;
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
1219 * added later.
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);
1230 #ifdef DEBUG
1231 bool JSContext::inAtomsZone() const { return zone_->isAtomsZone(); }
1232 #endif
1234 void JSContext::trace(JSTracer* trc) {
1235 cycleDetectorVector().trace(trc);
1236 geckoProfiler().trace(trc);
1237 if (isolate) {
1238 irregexp::TraceIsolate(trc, isolate.ref());
1242 JS::NativeStackLimit JSContext::stackLimitForJitCode(JS::StackKind kind) {
1243 #ifdef JS_SIMULATOR
1244 return simulator()->stackLimit();
1245 #else
1246 return stackLimit(kind);
1247 #endif
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.
1254 #ifdef JS_SIMULATOR
1255 jitStackLimit = jit::Simulator::StackLimit();
1256 #else
1257 jitStackLimit = nativeStackLimit[JS::StackForUntrustedScript];
1258 #endif
1259 jitStackLimitNoInterrupt = jitStackLimit;
1262 void JSContext::initJitStackLimit() { resetJitStackLimit(); }
1264 #ifdef JS_CRASH_DIAGNOSTICS
1265 void ContextChecks::check(AbstractFramePtr frame, int argIndex) {
1266 if (frame) {
1267 check(frame.realm(), argIndex);
1270 #endif
1272 void AutoEnterOOMUnsafeRegion::crash_impl(const char* reason) {
1273 char msgbuf[1024];
1274 js::NoteIntentionalCrash();
1275 SprintfLiteral(msgbuf, "[unhandlable oom] %s", reason);
1276 #ifndef DEBUG
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__);
1281 #endif
1282 MOZ_CRASH_UNSAFE(msgbuf);
1285 mozilla::Atomic<AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback,
1286 mozilla::Relaxed>
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);
1296 crash_impl(reason);
1299 void ExternalValueArray::trace(JSTracer* trc) {
1300 if (Value* vp = begin()) {
1301 TraceRootRange(trc, length(), vp, "js::ExternalValueArray");
1305 #ifdef DEBUG
1306 AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness)
1307 : cx_(TlsContext.get()),
1308 nested_(cx_ ? cx_->hasAutoUnsafeCallWithABI : false),
1309 nogc(cx_) {
1310 if (!cx_) {
1311 // This is a helper thread doing Ion or Wasm compilation - nothing to do.
1312 return;
1314 switch (strictness) {
1315 case UnsafeABIStrictness::NoExceptions:
1316 MOZ_ASSERT(!JS_IsExceptionPending(cx_));
1317 checkForPendingException_ = true;
1318 break;
1319 case UnsafeABIStrictness::AllowPendingExceptions:
1320 checkForPendingException_ = !JS_IsExceptionPending(cx_);
1321 break;
1322 case UnsafeABIStrictness::AllowThrownExceptions:
1323 checkForPendingException_ = false;
1324 break;
1327 cx_->hasAutoUnsafeCallWithABI = true;
1330 AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI() {
1331 if (!cx_) {
1332 return;
1334 MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI);
1335 if (!nested_) {
1336 cx_->hasAutoUnsafeCallWithABI = false;
1337 cx_->inUnsafeCallWithABI = false;
1339 MOZ_ASSERT_IF(checkForPendingException_, !JS_IsExceptionPending(cx_));
1341 #endif
1343 #ifdef __wasi__
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) {
1361 return false;
1363 return true;
1365 #endif // __wasi__