Bug 1885337 - Part 1: Implement to/from hex methods. r=dminor
[gecko.git] / js / src / vm / JSContext.cpp
blob8715a40989abc51362bae4d3b1f434753e2c49d1
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/Prefs.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
62 #include "vm/Realm.h"
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"
69 using namespace js;
71 #ifdef DEBUG
72 JSContext* js::MaybeGetJSContext() {
73 if (!TlsContext.init()) {
74 return nullptr;
76 return TlsContext.get();
78 #endif
80 bool js::AutoCycleDetector::init() {
81 MOZ_ASSERT(cyclic);
83 AutoCycleDetector::Vector& vector = cx->cycleDetectorVector();
85 for (JSObject* obj2 : vector) {
86 if (MOZ_UNLIKELY(obj == obj2)) {
87 return true;
91 if (!vector.append(obj)) {
92 return false;
95 cyclic = false;
96 return true;
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) {
104 vec.popBack();
105 } else {
106 // Avoid holding on to unused heap allocations.
107 vec.clearAndFree();
112 bool JSContext::init() {
113 TlsContext.set(this);
114 nativeStackBase_.emplace(GetNativeStackBase());
116 if (!fx.initInstance()) {
117 return false;
120 #ifdef JS_SIMULATOR
121 simulator_ = jit::Simulator::Create();
122 if (!simulator_) {
123 return false;
125 #endif
127 isolate = irregexp::CreateIsolate(this);
128 if (!isolate) {
129 return false;
132 #ifdef DEBUG
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.
135 initialized_ = true;
136 #endif
138 return true;
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;
151 #else
152 static constexpr JS::NativeStackSize MaxStackSize =
153 128 * sizeof(size_t) * 1024;
154 #endif
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);
166 #endif
168 JSRuntime* runtime = js_new<JSRuntime>(parentRuntime);
169 if (!runtime) {
170 return nullptr;
173 JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
174 if (!cx) {
175 js_delete(runtime);
176 return nullptr;
179 if (!cx->init()) {
180 js_delete(cx);
181 js_delete(runtime);
182 return nullptr;
185 if (!runtime->init(cx, maxBytes)) {
186 runtime->destroyRuntime();
187 js_delete(cx);
188 js_delete(runtime);
189 return nullptr;
192 // Initialize stack quota last because simulators rely on the JSRuntime having
193 // been initialized.
194 InitDefaultStackQuota(cx);
196 return 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() {
229 #ifdef DEBUG
230 for (auto const& stackRootPtr : stackRoots_) {
231 MOZ_ASSERT(stackRootPtr == nullptr);
233 #endif
236 bool AutoResolving::alreadyStartedSlow() const {
237 MOZ_ASSERT(link);
238 AutoResolving* cursor = link;
239 do {
240 MOZ_ASSERT(this != cursor);
241 if (object.get() == cursor->object && id.get() == cursor->id &&
242 kind == cursor->kind) {
243 return true;
245 } while (!!(cursor = cursor->link));
246 return false;
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())) {
280 return;
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();
294 cx->onOutOfMemory();
297 JS_PUBLIC_API void js::ReportLargeOutOfMemory(JSContext* cx) {
298 js::ReportOutOfMemory(cx);
301 JS_PUBLIC_API void js::ReportOutOfMemory(FrontendContext* fc) {
302 MaybeReportOutOfMemoryForDifferentialTesting();
304 fc->onOutOfMemory();
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
314 * like fuzzers.
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();
339 if (!maybecx) {
340 return;
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");
374 if (!cx) {
375 return;
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,
387 const char* msg) {
388 RootedValue usage(cx);
389 if (!JS_GetProperty(cx, callee, "usage", &usage)) {
390 return;
393 if (!usage.isString()) {
394 JS_ReportErrorASCII(cx, "%s", msg);
395 } else {
396 RootedString usageStr(cx, usage.toString());
397 UniqueChars str = JS_EncodeStringToUTF8(cx, usageStr);
398 if (!str) {
399 return;
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()) {
410 UniqueChars line;
411 size_t n;
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
417 // memory.
418 mozilla::CheckedInt<size_t> utf8Len(linebufLen);
419 utf8Len *= 3;
420 if (utf8Len.isValid()) {
421 line = UniqueChars(js_pod_malloc<char>(utf8Len.value()));
422 if (line) {
423 n = mozilla::ConvertUtf16toUtf8({linebuf, linebufLen},
424 {line.get(), utf8Len.value()});
429 const char* utf8buf;
430 if (line) {
431 utf8buf = line.get();
432 } else {
433 static const char unavailableStr[] = "<context unavailable>";
434 utf8buf = unavailableStr;
435 n = js_strlen(unavailableStr);
438 fputs(":\n", file);
439 if (prefix) {
440 fputs(prefix, file);
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') {
449 fputc('\n', file);
452 if (prefix) {
453 fputs(prefix, file);
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++) {
460 fputc('.', file);
462 continue;
464 fputc('.', file);
465 j++;
467 fputc('^', file);
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) {
477 UniqueChars prefix;
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;
489 switch (kind) {
490 case PrintErrorKind::Error:
491 MOZ_CRASH("unreachable");
492 case PrintErrorKind::Warning:
493 kindPrefix = "warning";
494 break;
495 case PrintErrorKind::Note:
496 kindPrefix = "note";
497 break;
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! */
507 const char* ctmp;
508 while ((ctmp = strchr(message, '\n')) != 0) {
509 ctmp++;
510 if (prefix) {
511 fputs(prefix.get(), file);
513 (void)fwrite(message, 1, ctmp - message, file);
514 message = ctmp;
517 /* If there were no filename or lineno, the prefix might be empty */
518 if (prefix) {
519 fputs(prefix.get(), file);
521 fputs(message, file);
523 PrintErrorLine(file, prefix.get(), report);
524 fputc('\n', file);
526 fflush(file);
529 static void PrintErrorImpl(FILE* file, JS::ConstUTF8CharsZ toStringResult,
530 JSErrorReport* report, bool reportWarnings) {
531 MOZ_ASSERT(report);
533 /* Conditionally ignore reported warnings. */
534 if (report->isWarning() && !reportWarnings) {
535 return;
538 PrintErrorKind kind = PrintErrorKind::Error;
539 if (report->isWarning()) {
540 kind = PrintErrorKind::Warning;
542 PrintSingleError(file, toStringResult, report, kind);
544 if (report->notes) {
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(),
561 reportWarnings);
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,
568 printable.get());
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,
583 int vIndex) {
584 MOZ_ASSERT(v.isNullOrUndefined());
586 if (vIndex == JSDVG_IGNORE_STACK) {
587 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
588 JSMSG_CANT_CONVERT_TO, NullOrUndefinedToCharZ(v),
589 "object");
590 return;
593 UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr);
594 if (!bytes) {
595 return;
598 if (strcmp(bytes.get(), "undefined") == 0 ||
599 strcmp(bytes.get(), "null") == 0) {
600 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
601 bytes.get());
602 } else {
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);
615 return;
618 RootedValue idVal(cx, IdToValue(key));
619 RootedString idStr(cx, ValueToSource(cx, idVal));
620 if (!idStr) {
621 return;
624 UniqueChars keyStr = StringToNewUTF8CharsZ(cx, *idStr);
625 if (!keyStr) {
626 return;
629 if (vIndex == JSDVG_IGNORE_STACK) {
630 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
631 keyStr.get(), NullOrUndefinedToCharZ(v));
632 return;
635 UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr);
636 if (!bytes) {
637 return;
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());
644 return;
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);
658 if (!bytes) {
659 return false;
662 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
663 bytes.get(), arg1, arg2);
664 return false;
667 JSObject* js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report) {
668 Rooted<ArrayObject*> notesArray(cx, NewDenseEmptyArray(cx));
669 if (!notesArray) {
670 return nullptr;
673 if (!report->notes) {
674 return notesArray;
677 for (auto&& note : *report->notes) {
678 Rooted<PlainObject*> noteObj(cx, NewPlainObject(cx));
679 if (!noteObj) {
680 return nullptr;
683 RootedString messageStr(cx, note->newMessageString(cx));
684 if (!messageStr) {
685 return nullptr;
687 RootedValue messageVal(cx, StringValue(messageStr));
688 if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal)) {
689 return nullptr;
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));
696 if (!filenameStr) {
697 return nullptr;
699 filenameVal = StringValue(filenameStr);
701 if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal)) {
702 return nullptr;
705 RootedValue linenoVal(cx, Int32Value(note->lineno));
706 if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal)) {
707 return nullptr;
709 RootedValue columnVal(cx, Int32Value(note->column.oneOriginValue()));
710 if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal)) {
711 return nullptr;
714 if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj))) {
715 return nullptr;
719 return notesArray;
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.
747 MOZ_RELEASE_ASSERT(
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);
752 if (!queue) {
753 return false;
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());
762 return true;
765 #ifdef DEBUG
766 JSObject* InternalJobQueue::copyJobs(JSContext* cx) {
767 Rooted<ArrayObject*> jobs(cx, NewDenseEmptyArray(cx));
768 if (!jobs) {
769 return nullptr;
772 for (const JSObject* unwrappedJob : queue.get()) {
773 RootedObject job(cx, const_cast<JSObject*>(unwrappedJob));
774 if (!cx->compartment()->wrap(cx, &job)) {
775 return nullptr;
778 if (!NewbornArrayPush(cx, jobs, ObjectValue(*job))) {
779 return nullptr;
783 return jobs;
786 JS_PUBLIC_API JSObject* js::GetJobsInInternalJobQueue(JSContext* cx) {
787 MOZ_ASSERT(cx->internalJobQueue.ref());
788 return cx->internalJobQueue->copyJobs(cx);
790 #endif
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()) {
815 return nullptr;
817 return cx->global();
820 bool InternalJobQueue::enqueuePromiseJob(JSContext* cx,
821 JS::HandleObject promise,
822 JS::HandleObject job,
823 JS::HandleObject allocationSite,
824 JS::HandleObject incumbentGlobal) {
825 MOZ_ASSERT(job);
826 if (!queue.pushBack(job)) {
827 ReportOutOfMemory(cx);
828 return false;
831 JS::JobQueueMayNotBeEmpty(cx);
832 return true;
835 void InternalJobQueue::runJobs(JSContext* cx) {
836 if (draining_ || interrupted_) {
837 return;
840 while (true) {
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.
847 draining_ = true;
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.
857 if (interrupted_) {
858 break;
861 job = queue.front();
862 queue.popFront();
864 // If the next job is the last job in the job queue, allow
865 // skipping the standard job queuing behavior.
866 if (queue.empty()) {
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()) {
875 continue;
877 RootedValue exn(cx);
878 if (cx->getPendingException(&exn)) {
880 * Clear the exception, because
881 * PrepareScriptEnvironmentAndInvoke will assert that we don't
882 * have one.
884 cx->clearPendingException();
885 js::ReportExceptionClosure reportExn(exn);
886 PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn);
892 draining_ = false;
894 if (interrupted_) {
895 break;
898 queue.clear();
900 // It's possible a job added a new off-thread promise task.
901 if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending()) {
902 break;
907 bool InternalJobQueue::empty() const { return queue.empty(); }
909 JSObject* InternalJobQueue::maybeFront() const {
910 if (queue.empty()) {
911 return nullptr;
914 return queue.get().front();
917 class js::InternalJobQueue::SavedQueue : public JobQueue::SavedJobQueue {
918 public:
919 SavedQueue(JSContext* cx, Queue&& saved, bool draining)
920 : cx(cx), saved(cx, std::move(saved)), draining_(draining) {
921 MOZ_ASSERT(cx->internalJobQueue.ref());
924 ~SavedQueue() {
925 MOZ_ASSERT(cx->internalJobQueue.ref());
926 cx->internalJobQueue->queue = std::move(saved.get());
927 cx->internalJobQueue->draining_ = draining_;
930 private:
931 JSContext* cx;
932 PersistentRooted<Queue> saved;
933 bool draining_;
936 js::UniquePtr<JS::JobQueue::SavedJobQueue> InternalJobQueue::saveJobQueue(
937 JSContext* cx) {
938 auto saved =
939 js::MakeUnique<SavedQueue>(cx, std::move(queue.get()), draining_);
940 if (!saved) {
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);
945 return nullptr;
948 queue = Queue(SystemAllocPolicy());
949 draining_ = false;
950 return saved;
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),
964 runtime_(runtime),
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),
973 #ifdef DEBUG
974 inUnsafeCallWithABI(this, false),
975 hasAutoUnsafeCallWithABI(this, false),
976 #endif
977 #ifdef JS_SIMULATOR
978 simulator_(this, nullptr),
979 #endif
980 dtoaState(this, nullptr),
981 suppressGC(this, 0),
982 #ifdef FUZZING_JS_FUZZILLI
983 executionHash(1),
984 executionHashInputs(0),
985 #endif
986 #ifdef DEBUG
987 noNurseryAllocationCheck(this, 0),
988 disableStrictProxyCheckingCount(this, 0),
989 #endif
990 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
991 runningOOMTest(this, false),
992 #endif
993 inUnsafeRegion(this, 0),
994 generationalDisabled(this, 0),
995 compactingDisabledCount(this, 0),
996 #ifdef DEBUG
997 regExpSearcherLastLimit(this, RegExpSearcherLastLimitSentinel),
998 #else
999 regExpSearcherLastLimit(this, 0),
1000 #endif
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),
1008 #ifdef DEBUG
1009 hadResourceExhaustion_(this, false),
1010 #endif
1011 reportGranularity(this, JS_DEFAULT_JITREPORT_GRANULARITY),
1012 resolvingList(this, nullptr),
1013 #ifdef DEBUG
1014 enteredPolicy(this, nullptr),
1015 #endif
1016 generatingError(this, false),
1017 cycleDetectorVector_(this, this),
1018 data(nullptr),
1019 asyncStackForNewActivations_(this),
1020 asyncCauseForNewActivations(this, nullptr),
1021 asyncCallIsExplicit(this, false),
1022 interruptCallbacks_(this),
1023 interruptCallbackDisabled(this, false),
1024 interruptBits_(0),
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() {
1039 #ifdef DEBUG
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;
1043 #endif
1045 /* Free the stuff hanging off of cx. */
1046 MOZ_ASSERT(!resolvingList);
1048 if (dtoaState) {
1049 DestroyDtoaState(dtoaState);
1052 fx.destroyInstance();
1054 #ifdef JS_SIMULATOR
1055 js::jit::Simulator::Destroy(simulator_);
1056 #endif
1058 if (isolate) {
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());
1073 runtime_ = rt;
1076 #if defined(NIGHTLY_BUILD)
1077 static bool IsOutOfMemoryException(JSContext* cx, const Value& v) {
1078 return v == StringValue(cx->names().out_of_memory_);
1080 #endif
1082 void JSContext::setPendingException(HandleValue v, Handle<SavedFrame*> stack) {
1083 #if defined(NIGHTLY_BUILD)
1084 do {
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) {
1089 break;
1092 // Check whether we have an interceptor at all.
1093 if (!this->runtime()->errorInterception.interceptor) {
1094 break;
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)) {
1101 break;
1104 // Make sure that we do not call the interceptor from within
1105 // the interceptor.
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;
1115 } while (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();
1133 if (stack) {
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);
1146 return true;
1149 Rooted<SavedFrame*> stack(this, unwrappedExceptionStack());
1150 JS::ExceptionStatus prevStatus = status;
1151 clearPendingException();
1152 if (!compartment()->wrap(this, &exception)) {
1153 return false;
1155 this->check(exception);
1156 setPendingException(exception, stack);
1157 status = prevStatus;
1159 rval.set(exception);
1160 return true;
1163 bool JSContext::getPendingExceptionStack(MutableHandleValue rval) {
1164 MOZ_ASSERT(isExceptionPending());
1166 Rooted<SavedFrame*> exceptionStack(this, unwrappedExceptionStack());
1167 if (!exceptionStack) {
1168 rval.setNull();
1169 return true;
1171 if (zone()->isAtomsZone()) {
1172 rval.setObject(*exceptionStack);
1173 return true;
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)) {
1182 return false;
1184 this->check(stack);
1185 setPendingException(exception, exceptionStack);
1186 status = prevStatus;
1188 rval.set(stack);
1189 return true;
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
1211 // code generation.
1212 if (JSCSPEvalChecker allows =
1213 runtime()->securityCallbacks->contentSecurityPolicyAllows) {
1214 return allows(this, kind, code);
1217 return true;
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
1225 * added later.
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);
1236 #ifdef DEBUG
1237 bool JSContext::inAtomsZone() const { return zone_->isAtomsZone(); }
1238 #endif
1240 void JSContext::trace(JSTracer* trc) {
1241 cycleDetectorVector().trace(trc);
1242 geckoProfiler().trace(trc);
1243 if (isolate) {
1244 irregexp::TraceIsolate(trc, isolate.ref());
1248 JS::NativeStackLimit JSContext::stackLimitForJitCode(JS::StackKind kind) {
1249 #ifdef JS_SIMULATOR
1250 return simulator()->stackLimit();
1251 #else
1252 return stackLimit(kind);
1253 #endif
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.
1260 #ifdef JS_SIMULATOR
1261 jitStackLimit = jit::Simulator::StackLimit();
1262 #else
1263 jitStackLimit = nativeStackLimit[JS::StackForUntrustedScript];
1264 #endif
1265 jitStackLimitNoInterrupt = jitStackLimit;
1268 void JSContext::initJitStackLimit() { resetJitStackLimit(); }
1270 JSScript* JSContext::currentScript(jsbytecode** ppc,
1271 AllowCrossRealm allowCrossRealm) {
1272 if (ppc) {
1273 *ppc = nullptr;
1276 // Fast path: there are no JS frames on the stack if there's no activation.
1277 if (!activation()) {
1278 return nullptr;
1281 FrameIter iter(this);
1282 if (iter.done() || !iter.hasScript()) {
1283 return nullptr;
1286 JSScript* script = iter.script();
1287 if (allowCrossRealm == AllowCrossRealm::DontAllow &&
1288 script->realm() != realm()) {
1289 return nullptr;
1292 if (ppc) {
1293 *ppc = iter.pc();
1295 return script;
1298 #ifdef JS_CRASH_DIAGNOSTICS
1299 void ContextChecks::check(AbstractFramePtr frame, int argIndex) {
1300 if (frame) {
1301 check(frame.realm(), argIndex);
1304 #endif
1306 void AutoEnterOOMUnsafeRegion::crash_impl(const char* reason) {
1307 char msgbuf[1024];
1308 js::NoteIntentionalCrash();
1309 SprintfLiteral(msgbuf, "[unhandlable oom] %s", reason);
1310 #ifndef DEBUG
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__);
1315 #endif
1316 MOZ_CRASH_UNSAFE(msgbuf);
1319 mozilla::Atomic<AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback,
1320 mozilla::Relaxed>
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);
1330 crash_impl(reason);
1333 void ExternalValueArray::trace(JSTracer* trc) {
1334 if (Value* vp = begin()) {
1335 TraceRootRange(trc, length(), vp, "js::ExternalValueArray");
1339 #ifdef DEBUG
1340 AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness)
1341 : cx_(TlsContext.get()),
1342 nested_(cx_ ? cx_->hasAutoUnsafeCallWithABI : false),
1343 nogc(cx_) {
1344 if (!cx_) {
1345 // This is a helper thread doing Ion or Wasm compilation - nothing to do.
1346 return;
1348 switch (strictness) {
1349 case UnsafeABIStrictness::NoExceptions:
1350 MOZ_ASSERT(!JS_IsExceptionPending(cx_));
1351 checkForPendingException_ = true;
1352 break;
1353 case UnsafeABIStrictness::AllowPendingExceptions:
1354 checkForPendingException_ = !JS_IsExceptionPending(cx_);
1355 break;
1356 case UnsafeABIStrictness::AllowThrownExceptions:
1357 checkForPendingException_ = false;
1358 break;
1361 cx_->hasAutoUnsafeCallWithABI = true;
1364 AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI() {
1365 if (!cx_) {
1366 return;
1368 MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI);
1369 if (!nested_) {
1370 cx_->hasAutoUnsafeCallWithABI = false;
1371 cx_->inUnsafeCallWithABI = false;
1373 MOZ_ASSERT_IF(checkForPendingException_, !JS_IsExceptionPending(cx_));
1375 #endif
1377 #ifdef __wasi__
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) {
1395 return false;
1397 return true;
1399 #endif // __wasi__