JSContext.cpp (53300B)
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/. */ 6 7 /* 8 * JS execution context. 9 */ 10 11 #include "vm/JSContext-inl.h" 12 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 18 19 #include <string.h> 20 #ifdef ANDROID 21 # include <android/log.h> 22 # include <fstream> 23 #endif // ANDROID 24 #ifdef XP_WIN 25 # include <processthreadsapi.h> 26 #endif // XP_WIN 27 28 #include "jsapi.h" // JS_SetNativeStackQuota 29 #include "jsexn.h" 30 #include "jstypes.h" 31 32 #include "builtin/RegExp.h" // js::RegExpSearcherLastLimitSentinel 33 #ifdef MOZ_EXECUTION_TRACING 34 # include "debugger/ExecutionTracer.h" 35 #endif 36 #include "frontend/FrontendContext.h" 37 #include "gc/GC.h" 38 #include "gc/PublicIterators.h" // js::RealmsIter 39 #include "irregexp/RegExpAPI.h" 40 #include "jit/Simulator.h" 41 #include "js/CallAndConstruct.h" // JS::Call 42 #include "js/CharacterEncoding.h" 43 #include "js/ContextOptions.h" // JS::ContextOptions 44 #include "js/ErrorInterceptor.h" // JSErrorInterceptor 45 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 46 #include "js/friend/MicroTask.h" 47 #include "js/friend/StackLimits.h" // js::ReportOverRecursed 48 #include "js/MemoryCallbacks.h" 49 #include "js/Prefs.h" 50 #include "js/Printf.h" 51 #include "js/PropertyAndElement.h" // JS_GetProperty 52 #include "js/Stack.h" // JS::NativeStackSize, JS::NativeStackLimit, JS::NativeStackLimitMin 53 #include "util/DiagnosticAssertions.h" 54 #include "util/DifferentialTesting.h" 55 #include "util/DoubleToString.h" 56 #include "util/NativeStack.h" 57 #include "util/Text.h" 58 #include "util/WindowsWrapper.h" 59 #include "js/friend/DumpFunctions.h" // for stack trace utilities 60 #include "js/Printer.h" // for FixedBufferPrinter 61 #include "vm/BytecodeUtil.h" // JSDVG_IGNORE_STACK 62 #include "vm/ErrorObject.h" 63 #include "vm/ErrorReporting.h" 64 #include "vm/FrameIter.h" 65 #include "vm/JSFunction.h" 66 #include "vm/JSObject.h" 67 #include "vm/PlainObject.h" // js::PlainObject 68 #include "vm/Realm.h" 69 #include "vm/StringType.h" // StringToNewUTF8CharsZ 70 #include "vm/ToSource.h" // js::ValueToSource 71 72 #include "vm/Compartment-inl.h" 73 #include "vm/Stack-inl.h" 74 75 using namespace js; 76 77 #ifdef DEBUG 78 JSContext* js::MaybeGetJSContext() { 79 if (!TlsContext.init()) { 80 return nullptr; 81 } 82 return TlsContext.get(); 83 } 84 #endif 85 86 bool js::AutoCycleDetector::init() { 87 MOZ_ASSERT(cyclic); 88 89 AutoCycleDetector::Vector& vector = cx->cycleDetectorVector(); 90 91 for (JSObject* obj2 : vector) { 92 if (MOZ_UNLIKELY(obj == obj2)) { 93 return true; 94 } 95 } 96 97 if (!vector.append(obj)) { 98 return false; 99 } 100 101 cyclic = false; 102 return true; 103 } 104 105 js::AutoCycleDetector::~AutoCycleDetector() { 106 if (MOZ_LIKELY(!cyclic)) { 107 AutoCycleDetector::Vector& vec = cx->cycleDetectorVector(); 108 MOZ_ASSERT(vec.back() == obj); 109 if (vec.length() > 1) { 110 vec.popBack(); 111 } else { 112 // Avoid holding on to unused heap allocations. 113 vec.clearAndFree(); 114 } 115 } 116 } 117 118 bool JSContext::init() { 119 TlsContext.set(this); 120 nativeStackBase_.emplace(GetNativeStackBase()); 121 122 if (!fx.initInstance()) { 123 return false; 124 } 125 126 #ifdef JS_SIMULATOR 127 simulator_ = jit::Simulator::Create(); 128 if (!simulator_) { 129 return false; 130 } 131 #endif 132 133 isolate = irregexp::CreateIsolate(this); 134 if (!isolate) { 135 return false; 136 } 137 138 this->microTaskQueues = js::MakeUnique<js::MicroTaskQueueSet>(this); 139 if (!this->microTaskQueues) { 140 return false; 141 } 142 143 #ifdef DEBUG 144 // Set the initialized_ last, so that ProtectedData checks will allow us to 145 // initialize this context before it becomes the runtime's active context. 146 initialized_ = true; 147 #endif 148 149 return true; 150 } 151 152 static void InitDefaultStackQuota(JSContext* cx) { 153 // Initialize stack quota to a reasonable default. Embedders can override this 154 // by calling JS_SetNativeStackQuota. 155 // 156 // NOTE: Firefox overrides these values. For the main thread this happens in 157 // XPCJSContext::Initialize. 158 159 #if defined(MOZ_ASAN) || (defined(DEBUG) && !defined(XP_WIN)) 160 static constexpr JS::NativeStackSize MaxStackSize = 161 2 * 128 * sizeof(size_t) * 1024; 162 #else 163 static constexpr JS::NativeStackSize MaxStackSize = 164 128 * sizeof(size_t) * 1024; 165 #endif 166 JS_SetNativeStackQuota(cx, MaxStackSize); 167 } 168 169 JSContext* js::NewContext(uint32_t maxBytes, JSRuntime* parentRuntime) { 170 AutoNoteSingleThreadedRegion anstr; 171 172 MOZ_RELEASE_ASSERT(!TlsContext.get()); 173 174 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) 175 js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_MAIN 176 : js::THREAD_TYPE_WORKER); 177 #endif 178 179 JSRuntime* runtime = js_new<JSRuntime>(parentRuntime); 180 if (!runtime) { 181 return nullptr; 182 } 183 184 JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions()); 185 if (!cx) { 186 js_delete(runtime); 187 return nullptr; 188 } 189 190 if (!cx->init()) { 191 js_delete(cx); 192 js_delete(runtime); 193 return nullptr; 194 } 195 196 if (!runtime->init(cx, maxBytes)) { 197 runtime->destroyRuntime(); 198 js_delete(cx); 199 js_delete(runtime); 200 return nullptr; 201 } 202 203 // Initialize stack quota last because simulators rely on the JSRuntime having 204 // been initialized. 205 InitDefaultStackQuota(cx); 206 207 return cx; 208 } 209 210 void js::DestroyContext(JSContext* cx) { 211 JS_AbortIfWrongThread(cx); 212 213 MOZ_ASSERT(!cx->realm(), "Shouldn't destroy context with active realm"); 214 MOZ_ASSERT(!cx->activation(), "Shouldn't destroy context with activations"); 215 216 cx->checkNoGCRooters(); 217 218 // Cancel all off thread compiles. Completed compiles may try to 219 // interrupt this context. See HelperThread::handleIonWorkload. 220 CancelOffThreadCompile(cx->runtime()); 221 222 cx->jobQueue = nullptr; 223 cx->internalJobQueue = nullptr; 224 cx->microTaskQueues = nullptr; 225 SetContextProfilingStack(cx, nullptr); 226 227 JSRuntime* rt = cx->runtime(); 228 229 // Flush promise tasks executing in helper threads early, before any parts 230 // of the JSRuntime that might be visible to helper threads are torn down. 231 rt->offThreadPromiseState.ref().shutdown(cx); 232 233 // Destroy the runtime along with its last context. 234 js::AutoNoteSingleThreadedRegion nochecks; 235 rt->destroyRuntime(); 236 js_delete_poison(cx); 237 js_delete_poison(rt); 238 } 239 240 void JS::RootingContext::checkNoGCRooters() { 241 #ifdef DEBUG 242 for (auto const& stackRootPtr : stackRoots_) { 243 MOZ_ASSERT(stackRootPtr == nullptr); 244 } 245 #endif 246 } 247 248 bool AutoResolving::alreadyStartedSlow() const { 249 MOZ_ASSERT(link); 250 AutoResolving* cursor = link; 251 do { 252 MOZ_ASSERT(this != cursor); 253 if (object.get() == cursor->object && id.get() == cursor->id) { 254 return true; 255 } 256 } while (!!(cursor = cursor->link)); 257 return false; 258 } 259 260 static void MaybeReportOutOfMemoryForDifferentialTesting() { 261 /* 262 * OOMs are non-deterministic, especially across different execution modes 263 * (e.g. interpreter vs JIT). When doing differential testing, print to stderr 264 * so that the fuzzers can detect this. 265 */ 266 if (js::SupportDifferentialTesting()) { 267 fprintf(stderr, "ReportOutOfMemory called\n"); 268 } 269 } 270 271 /* 272 * Since memory has been exhausted, avoid the normal error-handling path which 273 * allocates an error object, report and callstack. Instead simply throw the 274 * static atom "out of memory". 275 * 276 * Furthermore, callers of ReportOutOfMemory (viz., malloc) assume a GC does 277 * not occur, so GC must be avoided or suppressed. 278 */ 279 void JSContext::onOutOfMemory() { 280 runtime()->hadOutOfMemory = true; 281 gc::AutoSuppressGC suppressGC(this); 282 283 requestInterrupt(js::InterruptReason::OOMStackTrace); 284 285 /* Report the oom. */ 286 if (JS::OutOfMemoryCallback oomCallback = runtime()->oomCallback) { 287 oomCallback(this, runtime()->oomCallbackData); 288 } 289 290 // If we OOM early in process startup, this may be unavailable so just return 291 // instead of crashing unexpectedly. 292 if (MOZ_UNLIKELY(!runtime()->hasInitializedSelfHosting())) { 293 return; 294 } 295 296 RootedValue oomMessage(this, StringValue(names().out_of_memory_)); 297 setPendingException(oomMessage, nullptr); 298 MOZ_ASSERT(status == JS::ExceptionStatus::Throwing); 299 status = JS::ExceptionStatus::OutOfMemory; 300 301 reportResourceExhaustion(); 302 } 303 304 JS_PUBLIC_API void js::ReportOutOfMemory(JSContext* cx) { 305 MaybeReportOutOfMemoryForDifferentialTesting(); 306 307 cx->onOutOfMemory(); 308 } 309 310 JS_PUBLIC_API void js::ReportLargeOutOfMemory(JSContext* cx) { 311 js::ReportOutOfMemory(cx); 312 } 313 314 JS_PUBLIC_API void js::ReportOutOfMemory(FrontendContext* fc) { 315 MaybeReportOutOfMemoryForDifferentialTesting(); 316 317 fc->onOutOfMemory(); 318 } 319 320 static void MaybeReportOverRecursedForDifferentialTesting() { 321 /* 322 * We cannot make stack depth deterministic across different 323 * implementations (e.g. JIT vs. interpreter will differ in 324 * their maximum stack depth). 325 * However, we can detect externally when we hit the maximum 326 * stack depth which is useful for external testing programs 327 * like fuzzers. 328 */ 329 if (js::SupportDifferentialTesting()) { 330 fprintf(stderr, "ReportOverRecursed called\n"); 331 } 332 } 333 334 void JSContext::onOverRecursed() { 335 // Try to construct an over-recursed error and then update the exception 336 // status to `OverRecursed`. Creating the error can fail, so check there 337 // is a reasonable looking exception pending before updating status. 338 JS_ReportErrorNumberASCII(this, GetErrorMessage, nullptr, 339 JSMSG_OVER_RECURSED); 340 if (isExceptionPending() && !isThrowingOutOfMemory()) { 341 MOZ_ASSERT(unwrappedException().isObject()); 342 MOZ_ASSERT(status == JS::ExceptionStatus::Throwing); 343 status = JS::ExceptionStatus::OverRecursed; 344 } 345 346 reportResourceExhaustion(); 347 } 348 349 JS_PUBLIC_API void js::ReportOverRecursed(JSContext* maybecx) { 350 MaybeReportOverRecursedForDifferentialTesting(); 351 352 if (!maybecx) { 353 return; 354 } 355 356 maybecx->onOverRecursed(); 357 } 358 359 JS_PUBLIC_API void js::ReportOverRecursed(FrontendContext* fc) { 360 MaybeReportOverRecursedForDifferentialTesting(); 361 362 fc->onOverRecursed(); 363 } 364 365 void js::ReportOversizedAllocation(JSContext* cx, const unsigned errorNumber) { 366 // The JIT may optimize away allocations if it determines that they aren't 367 // used. This can affect whether we throw an exception when the size of an 368 // allocation exceeds implementation-defined limits (eg JSString::MAX_LENGTH). 369 // These errors aren't interesting for the purposes of differential fuzzing. 370 // We print a message so that fuzzers can detect this case. To simplify 371 // tooling updates, we use the same message as ReportOutOfMemory. 372 if (js::SupportDifferentialTesting()) { 373 fprintf(stderr, "ReportOutOfMemory called\n"); 374 } 375 376 gc::AutoSuppressGC suppressGC(cx); 377 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber); 378 379 cx->reportResourceExhaustion(); 380 } 381 382 void js::ReportAllocationOverflow(JSContext* cx) { 383 if (js::SupportDifferentialTesting()) { 384 fprintf(stderr, "ReportAllocationOverflow called\n"); 385 } 386 387 if (!cx) { 388 return; 389 } 390 391 cx->reportAllocationOverflow(); 392 } 393 394 void js::ReportAllocationOverflow(FrontendContext* fc) { 395 fc->onAllocationOverflow(); 396 } 397 398 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */ 399 void js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, 400 const char* msg) { 401 RootedValue usage(cx); 402 if (!JS_GetProperty(cx, callee, "usage", &usage)) { 403 return; 404 } 405 406 if (!usage.isString()) { 407 JS_ReportErrorASCII(cx, "%s", msg); 408 } else { 409 RootedString usageStr(cx, usage.toString()); 410 UniqueChars str = JS_EncodeStringToUTF8(cx, usageStr); 411 if (!str) { 412 return; 413 } 414 JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.get()); 415 } 416 } 417 418 enum class PrintErrorKind { Error, Warning, Note }; 419 420 static void PrintErrorLine(FILE* file, const char* prefix, 421 JSErrorReport* report) { 422 if (const char16_t* linebuf = report->linebuf()) { 423 UniqueChars line; 424 size_t n; 425 { 426 size_t linebufLen = report->linebufLength(); 427 428 // This function is only used for shell command-line sorts of stuff where 429 // performance doesn't really matter, so just encode into max-sized 430 // memory. 431 mozilla::CheckedInt<size_t> utf8Len(linebufLen); 432 utf8Len *= 3; 433 if (utf8Len.isValid()) { 434 line = UniqueChars(js_pod_malloc<char>(utf8Len.value())); 435 if (line) { 436 n = mozilla::ConvertUtf16toUtf8({linebuf, linebufLen}, 437 {line.get(), utf8Len.value()}); 438 } 439 } 440 } 441 442 const char* utf8buf; 443 if (line) { 444 utf8buf = line.get(); 445 } else { 446 static const char unavailableStr[] = "<context unavailable>"; 447 utf8buf = unavailableStr; 448 n = js_strlen(unavailableStr); 449 } 450 451 fputs(":\n", file); 452 if (prefix) { 453 fputs(prefix, file); 454 } 455 456 for (size_t i = 0; i < n; i++) { 457 fputc(utf8buf[i], file); 458 } 459 460 // linebuf/utf8buf usually ends with a newline. If not, add one here. 461 if (n == 0 || utf8buf[n - 1] != '\n') { 462 fputc('\n', file); 463 } 464 465 if (prefix) { 466 fputs(prefix, file); 467 } 468 469 n = report->tokenOffset(); 470 for (size_t i = 0, j = 0; i < n; i++) { 471 if (utf8buf[i] == '\t') { 472 for (size_t k = (j + 8) & ~7; j < k; j++) { 473 fputc('.', file); 474 } 475 continue; 476 } 477 fputc('.', file); 478 j++; 479 } 480 fputc('^', file); 481 } 482 } 483 484 static void PrintErrorLine(FILE* file, const char* prefix, 485 JSErrorNotes::Note* note) {} 486 487 template <typename T> 488 static void PrintSingleError(FILE* file, JS::ConstUTF8CharsZ toStringResult, 489 T* report, PrintErrorKind kind) { 490 UniqueChars prefix; 491 if (report->filename) { 492 prefix = JS_smprintf("%s:", report->filename.c_str()); 493 } 494 495 if (report->lineno) { 496 prefix = JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno, 497 report->column.oneOriginValue()); 498 } 499 500 if (kind != PrintErrorKind::Error) { 501 const char* kindPrefix = nullptr; 502 switch (kind) { 503 case PrintErrorKind::Error: 504 MOZ_CRASH("unreachable"); 505 case PrintErrorKind::Warning: 506 kindPrefix = "warning"; 507 break; 508 case PrintErrorKind::Note: 509 kindPrefix = "note"; 510 break; 511 } 512 513 prefix = JS_smprintf("%s%s: ", prefix ? prefix.get() : "", kindPrefix); 514 } 515 516 const char* message = 517 toStringResult ? toStringResult.c_str() : report->message().c_str(); 518 519 /* embedded newlines -- argh! */ 520 const char* ctmp; 521 while ((ctmp = strchr(message, '\n')) != 0) { 522 ctmp++; 523 if (prefix) { 524 fputs(prefix.get(), file); 525 } 526 (void)fwrite(message, 1, ctmp - message, file); 527 message = ctmp; 528 } 529 530 /* If there were no filename or lineno, the prefix might be empty */ 531 if (prefix) { 532 fputs(prefix.get(), file); 533 } 534 fputs(message, file); 535 536 PrintErrorLine(file, prefix.get(), report); 537 fputc('\n', file); 538 539 fflush(file); 540 } 541 542 static void PrintErrorImpl(FILE* file, JS::ConstUTF8CharsZ toStringResult, 543 JSErrorReport* report, bool reportWarnings) { 544 MOZ_ASSERT(report); 545 546 /* Conditionally ignore reported warnings. */ 547 if (report->isWarning() && !reportWarnings) { 548 return; 549 } 550 551 PrintErrorKind kind = PrintErrorKind::Error; 552 if (report->isWarning()) { 553 kind = PrintErrorKind::Warning; 554 } 555 PrintSingleError(file, toStringResult, report, kind); 556 557 if (report->notes) { 558 for (auto&& note : *report->notes) { 559 PrintSingleError(file, JS::ConstUTF8CharsZ(), note.get(), 560 PrintErrorKind::Note); 561 } 562 } 563 } 564 565 JS_PUBLIC_API void JS::PrintError(FILE* file, JSErrorReport* report, 566 bool reportWarnings) { 567 PrintErrorImpl(file, JS::ConstUTF8CharsZ(), report, reportWarnings); 568 } 569 570 JS_PUBLIC_API void JS::PrintError(FILE* file, 571 const JS::ErrorReportBuilder& builder, 572 bool reportWarnings) { 573 PrintErrorImpl(file, builder.toStringResult(), builder.report(), 574 reportWarnings); 575 } 576 577 void js::ReportIsNotDefined(JSContext* cx, HandleId id) { 578 if (UniqueChars printable = 579 IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) { 580 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, 581 printable.get()); 582 } 583 } 584 585 void js::ReportIsNotDefined(JSContext* cx, Handle<PropertyName*> name) { 586 RootedId id(cx, NameToId(name)); 587 ReportIsNotDefined(cx, id); 588 } 589 590 const char* NullOrUndefinedToCharZ(HandleValue v) { 591 MOZ_ASSERT(v.isNullOrUndefined()); 592 return v.isNull() ? "null" : "undefined"; 593 } 594 595 void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, 596 int vIndex) { 597 MOZ_ASSERT(v.isNullOrUndefined()); 598 599 if (vIndex == JSDVG_IGNORE_STACK) { 600 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 601 JSMSG_CANT_CONVERT_TO, NullOrUndefinedToCharZ(v), 602 "object"); 603 return; 604 } 605 606 UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr); 607 if (!bytes) { 608 return; 609 } 610 611 if (strcmp(bytes.get(), "undefined") == 0 || 612 strcmp(bytes.get(), "null") == 0) { 613 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES, 614 bytes.get()); 615 } else { 616 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 617 JSMSG_UNEXPECTED_TYPE, bytes.get(), 618 NullOrUndefinedToCharZ(v)); 619 } 620 } 621 622 void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, 623 int vIndex, HandleId key) { 624 MOZ_ASSERT(v.isNullOrUndefined()); 625 626 if (!JS::Prefs::property_error_message_fix()) { 627 ReportIsNullOrUndefinedForPropertyAccess(cx, v, vIndex); 628 return; 629 } 630 631 RootedValue idVal(cx, IdToValue(key)); 632 RootedString idStr(cx, ValueToSource(cx, idVal)); 633 if (!idStr) { 634 return; 635 } 636 637 UniqueChars keyStr = StringToNewUTF8CharsZ(cx, *idStr); 638 if (!keyStr) { 639 return; 640 } 641 642 if (vIndex == JSDVG_IGNORE_STACK) { 643 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL, 644 keyStr.get(), NullOrUndefinedToCharZ(v)); 645 return; 646 } 647 648 UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr); 649 if (!bytes) { 650 return; 651 } 652 653 if (strcmp(bytes.get(), "undefined") == 0 || 654 strcmp(bytes.get(), "null") == 0) { 655 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL, 656 keyStr.get(), bytes.get()); 657 return; 658 } 659 660 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 661 JSMSG_PROPERTY_FAIL_EXPR, keyStr.get(), bytes.get(), 662 NullOrUndefinedToCharZ(v)); 663 } 664 665 bool js::ReportValueError(JSContext* cx, const unsigned errorNumber, 666 int spindex, HandleValue v, HandleString fallback, 667 const char* arg1, const char* arg2) { 668 MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1); 669 MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3); 670 UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback); 671 if (!bytes) { 672 return false; 673 } 674 675 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, 676 bytes.get(), arg1, arg2); 677 return false; 678 } 679 680 JSObject* js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report) { 681 Rooted<ArrayObject*> notesArray(cx, NewDenseEmptyArray(cx)); 682 if (!notesArray) { 683 return nullptr; 684 } 685 686 if (!report->notes) { 687 return notesArray; 688 } 689 690 for (auto&& note : *report->notes) { 691 Rooted<PlainObject*> noteObj(cx, NewPlainObject(cx)); 692 if (!noteObj) { 693 return nullptr; 694 } 695 696 RootedString messageStr(cx, note->newMessageString(cx)); 697 if (!messageStr) { 698 return nullptr; 699 } 700 RootedValue messageVal(cx, StringValue(messageStr)); 701 if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal)) { 702 return nullptr; 703 } 704 705 RootedValue filenameVal(cx); 706 if (const char* filename = note->filename.c_str()) { 707 JS::UTF8Chars utf8chars(filename, strlen(filename)); 708 Rooted<JSString*> filenameStr(cx, NewStringCopyUTF8N(cx, utf8chars)); 709 if (!filenameStr) { 710 return nullptr; 711 } 712 filenameVal = StringValue(filenameStr); 713 } 714 if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal)) { 715 return nullptr; 716 } 717 718 RootedValue linenoVal(cx, Int32Value(note->lineno)); 719 if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal)) { 720 return nullptr; 721 } 722 RootedValue columnVal(cx, Int32Value(note->column.oneOriginValue())); 723 if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal)) { 724 return nullptr; 725 } 726 727 if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj))) { 728 return nullptr; 729 } 730 } 731 732 return notesArray; 733 } 734 735 void JSContext::recoverFromOutOfMemory() { 736 if (isExceptionPending()) { 737 MOZ_ASSERT(isThrowingOutOfMemory()); 738 clearPendingException(); 739 } 740 } 741 742 void JSContext::reportAllocationOverflow() { 743 gc::AutoSuppressGC suppressGC(this); 744 JS_ReportErrorNumberASCII(this, GetErrorMessage, nullptr, 745 JSMSG_ALLOC_OVERFLOW); 746 } 747 748 JS::StackKind JSContext::stackKindForCurrentPrincipal() { 749 return runningWithTrustedPrincipals() ? JS::StackForTrustedScript 750 : JS::StackForUntrustedScript; 751 } 752 753 JS::NativeStackLimit JSContext::stackLimitForCurrentPrincipal() { 754 return stackLimit(stackKindForCurrentPrincipal()); 755 } 756 757 JS_PUBLIC_API bool js::UseInternalJobQueues(JSContext* cx) { 758 // Internal job queue handling must be set up very early. Self-hosting 759 // initialization is as good a marker for that as any. 760 MOZ_RELEASE_ASSERT( 761 !cx->runtime()->hasInitializedSelfHosting(), 762 "js::UseInternalJobQueues must be called early during runtime startup."); 763 MOZ_ASSERT(!cx->jobQueue); 764 auto queue = MakeUnique<InternalJobQueue>(cx); 765 if (!queue) { 766 return false; 767 } 768 769 cx->internalJobQueue = std::move(queue); 770 cx->jobQueue = cx->internalJobQueue.ref().get(); 771 772 cx->runtime()->offThreadPromiseState.ref().initInternalDispatchQueue(); 773 MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized()); 774 775 return true; 776 } 777 778 #ifdef DEBUG 779 JSObject* InternalJobQueue::copyJobs(JSContext* cx) { 780 Rooted<ArrayObject*> jobs(cx, NewDenseEmptyArray(cx)); 781 if (!jobs) { 782 return nullptr; 783 } 784 785 if (JS::Prefs::use_js_microtask_queue()) { 786 auto& queues = cx->microTaskQueues; 787 auto addToArray = [&](auto& queue) -> bool { 788 for (const auto& e : queue) { 789 JS::JSMicroTask* task = JS::ToUnwrappedJSMicroTask(e); 790 if (task) { 791 // All any test cares about is the global of the job so let's do it. 792 RootedObject global(cx, JS::GetExecutionGlobalFromJSMicroTask(task)); 793 if (!global) { 794 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 795 JSMSG_DEAD_OBJECT); 796 return false; 797 } 798 if (!cx->compartment()->wrap(cx, &global)) { 799 return false; 800 } 801 if (!NewbornArrayPush(cx, jobs, ObjectValue(*global))) { 802 return false; 803 } 804 } 805 } 806 807 return true; 808 }; 809 810 if (!addToArray(queues->debugMicroTaskQueue)) { 811 return nullptr; 812 } 813 if (!addToArray(queues->microTaskQueue)) { 814 return nullptr; 815 } 816 } else { 817 for (const JSObject* unwrappedJob : queue.get()) { 818 RootedObject job(cx, const_cast<JSObject*>(unwrappedJob)); 819 if (!cx->compartment()->wrap(cx, &job)) { 820 return nullptr; 821 } 822 823 if (!NewbornArrayPush(cx, jobs, ObjectValue(*job))) { 824 return nullptr; 825 } 826 } 827 } 828 829 return jobs; 830 } 831 832 JS_PUBLIC_API JSObject* js::GetJobsInInternalJobQueue(JSContext* cx) { 833 MOZ_ASSERT(cx->internalJobQueue.ref()); 834 return cx->internalJobQueue->copyJobs(cx); 835 } 836 #endif 837 838 JS_PUBLIC_API void js::StopDrainingJobQueue(JSContext* cx) { 839 MOZ_ASSERT(cx->internalJobQueue.ref()); 840 cx->internalJobQueue->interrupt(); 841 } 842 843 JS_PUBLIC_API void js::RestartDrainingJobQueue(JSContext* cx) { 844 MOZ_ASSERT(cx->internalJobQueue.ref()); 845 cx->internalJobQueue->uninterrupt(); 846 } 847 848 JS_PUBLIC_API void js::RunJobs(JSContext* cx) { 849 MOZ_ASSERT(cx->jobQueue); 850 MOZ_ASSERT(cx->isEvaluatingModule == 0); 851 cx->jobQueue->runJobs(cx); 852 JS::ClearKeptObjects(cx); 853 } 854 855 bool InternalJobQueue::getHostDefinedGlobal( 856 JSContext* cx, MutableHandle<JSObject*> out) const { 857 return true; 858 } 859 860 bool InternalJobQueue::getHostDefinedData( 861 JSContext* cx, JS::MutableHandle<JSObject*> data) const { 862 data.set(nullptr); 863 return true; 864 } 865 866 bool InternalJobQueue::enqueuePromiseJob(JSContext* cx, 867 JS::HandleObject promise, 868 JS::HandleObject job, 869 JS::HandleObject allocationSite, 870 JS::HandleObject hostDefinedData) { 871 MOZ_ASSERT(job); 872 if (!queue.pushBack(job)) { 873 ReportOutOfMemory(cx); 874 return false; 875 } 876 877 JS::JobQueueMayNotBeEmpty(cx); 878 return true; 879 } 880 881 void InternalJobQueue::runJobs(JSContext* cx) { 882 if (draining_ || interrupted_) { 883 return; 884 } 885 886 while (true) { 887 cx->runtime()->offThreadPromiseState.ref().internalDrain(cx); 888 889 // It doesn't make sense for job queue draining to be reentrant. At the 890 // same time we don't want to assert against it, because that'd make 891 // drainJobQueue unsafe for fuzzers. We do want fuzzers to test this, 892 // so we simply ignore nested calls of drainJobQueue. 893 draining_ = true; 894 895 if (JS::Prefs::use_js_microtask_queue()) { 896 // Execute jobs in a loop until we've reached the end of the queue. 897 JS::Rooted<JS::JSMicroTask*> job(cx); 898 JS::Rooted<JS::GenericMicroTask> dequeueJob(cx); 899 while (JS::HasAnyMicroTasks(cx)) { 900 MOZ_ASSERT(queue.empty()); 901 // A previous job might have set this flag. E.g., the js shell 902 // sets it if the `quit` builtin function is called. 903 if (interrupted_) { 904 break; 905 } 906 907 cx->runtime()->offThreadPromiseState.ref().internalDrain(cx); 908 909 dequeueJob = JS::DequeueNextMicroTask(cx); 910 MOZ_ASSERT(!dequeueJob.isNull()); 911 job = JS::ToMaybeWrappedJSMicroTask(dequeueJob); 912 MOZ_ASSERT(job); 913 914 // If the next job is the last job in the job queue, allow 915 // skipping the standard job queuing behavior. 916 if (!JS::HasAnyMicroTasks(cx)) { 917 JS::JobQueueIsEmpty(cx); 918 } 919 920 if (!JS::GetExecutionGlobalFromJSMicroTask(job)) { 921 continue; 922 } 923 AutoRealm ar(cx, JS::GetExecutionGlobalFromJSMicroTask(job)); 924 { 925 if (!JS::RunJSMicroTask(cx, job)) { 926 // Nothing we can do about uncatchable exceptions. 927 if (!cx->isExceptionPending()) { 928 continue; 929 } 930 931 // Always clear the exception, because 932 // PrepareScriptEnvironmentAndInvoke will assert that we don't have 933 // one. 934 RootedValue exn(cx); 935 bool success = cx->getPendingException(&exn); 936 cx->clearPendingException(); 937 if (success) { 938 js::ReportExceptionClosure reportExn(exn); 939 PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn); 940 } 941 } 942 } 943 } 944 } else { 945 RootedObject job(cx); 946 JS::HandleValueArray args(JS::HandleValueArray::empty()); 947 RootedValue rval(cx); 948 // Execute jobs in a loop until we've reached the end of the queue. 949 while (!queue.empty()) { 950 // A previous job might have set this flag. E.g., the js shell 951 // sets it if the `quit` builtin function is called. 952 if (interrupted_) { 953 break; 954 } 955 956 cx->runtime()->offThreadPromiseState.ref().internalDrain(cx); 957 958 job = queue.front(); 959 queue.popFront(); 960 961 // If the next job is the last job in the job queue, allow 962 // skipping the standard job queuing behavior. 963 if (queue.empty()) { 964 JS::JobQueueIsEmpty(cx); 965 } 966 967 AutoRealm ar(cx, &job->as<JSFunction>()); 968 { 969 if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) { 970 // Nothing we can do about uncatchable exceptions. 971 if (!cx->isExceptionPending()) { 972 continue; 973 } 974 975 // Always clear the exception, because 976 // PrepareScriptEnvironmentAndInvoke will assert that we don't have 977 // one. 978 RootedValue exn(cx); 979 bool success = cx->getPendingException(&exn); 980 cx->clearPendingException(); 981 if (success) { 982 js::ReportExceptionClosure reportExn(exn); 983 PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn); 984 } 985 } 986 } 987 } 988 } 989 990 draining_ = false; 991 992 if (interrupted_) { 993 break; 994 } 995 996 if (JS::Prefs::use_js_microtask_queue()) { 997 // MG:XXX: Should use public API here. 998 cx->microTaskQueues->clear(); 999 } else { 1000 queue.clear(); 1001 } 1002 1003 // It's possible a job added a new off-thread promise task. 1004 if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending()) { 1005 break; 1006 } 1007 } 1008 } 1009 1010 bool InternalJobQueue::empty() const { return queue.empty(); } 1011 1012 JSObject* InternalJobQueue::maybeFront() const { 1013 if (queue.empty()) { 1014 return nullptr; 1015 } 1016 1017 return queue.get().front(); 1018 } 1019 1020 class js::InternalJobQueue::SavedQueue : public JobQueue::SavedJobQueue { 1021 public: 1022 SavedQueue(JSContext* cx, Queue&& saved, MicroTaskQueueSet&& queueSet, 1023 bool draining) 1024 : cx(cx), 1025 saved(cx, std::move(saved)), 1026 savedQueues(cx, std::move(queueSet)), 1027 draining_(draining) { 1028 MOZ_ASSERT(cx->internalJobQueue.ref()); 1029 if (JS::Prefs::use_js_microtask_queue()) { 1030 MOZ_ASSERT(saved.empty()); 1031 } else { 1032 MOZ_ASSERT(queueSet.empty()); 1033 } 1034 } 1035 1036 ~SavedQueue() { 1037 MOZ_ASSERT(cx->internalJobQueue.ref()); 1038 cx->internalJobQueue->queue = std::move(saved.get()); 1039 cx->internalJobQueue->draining_ = draining_; 1040 *cx->microTaskQueues.get() = std::move(savedQueues.get()); 1041 } 1042 1043 private: 1044 JSContext* cx; 1045 PersistentRooted<Queue> saved; 1046 PersistentRooted<MicroTaskQueueSet> savedQueues; 1047 bool draining_; 1048 }; 1049 1050 js::UniquePtr<JS::JobQueue::SavedJobQueue> InternalJobQueue::saveJobQueue( 1051 JSContext* cx) { 1052 auto saved = js::MakeUnique<SavedQueue>( 1053 cx, std::move(queue.get()), std::move(*cx->microTaskQueues), draining_); 1054 if (!saved) { 1055 // When MakeUnique's allocation fails, the SavedQueue constructor is never 1056 // called, so this->queue is still initialized. (The move doesn't occur 1057 // until the constructor gets called.) 1058 ReportOutOfMemory(cx); 1059 return nullptr; 1060 } 1061 1062 queue = Queue(SystemAllocPolicy()); 1063 draining_ = false; 1064 return saved; 1065 } 1066 1067 void js::MicroTaskQueueElement::trace(JSTracer* trc) { 1068 // For non-objects (like Private values), call the JobQueue hook 1069 JSContext* cx = trc->runtime()->mainContextFromOwnThread(); 1070 MOZ_ASSERT(cx); 1071 auto* queue = cx->jobQueue.ref(); 1072 1073 if (!queue || value.isGCThing()) { 1074 TraceRoot(trc, &value, "microtask-queue-entry"); 1075 } else { 1076 queue->traceNonGCThingMicroTask(trc, &value); 1077 } 1078 } 1079 1080 JS::GenericMicroTask js::MicroTaskQueueSet::popDebugFront() { 1081 JS_LOG(mtq, Info, "JS Drain Queue: popDebugFront"); 1082 if (!debugMicroTaskQueue.empty()) { 1083 JS::Value p = debugMicroTaskQueue.front(); 1084 debugMicroTaskQueue.popFront(); 1085 return p; 1086 } 1087 return JS::NullValue(); 1088 } 1089 1090 JS::GenericMicroTask js::MicroTaskQueueSet::popFront() { 1091 JS_LOG(mtq, Info, "JS Drain Queue"); 1092 if (!debugMicroTaskQueue.empty()) { 1093 JS::Value p = debugMicroTaskQueue.front(); 1094 debugMicroTaskQueue.popFront(); 1095 return p; 1096 } 1097 if (!microTaskQueue.empty()) { 1098 JS::Value p = microTaskQueue.front(); 1099 microTaskQueue.popFront(); 1100 return p; 1101 } 1102 1103 return JS::NullValue(); 1104 } 1105 1106 bool js::MicroTaskQueueSet::enqueueRegularMicroTask( 1107 JSContext* cx, const JS::GenericMicroTask& entry) { 1108 JS_LOG(mtq, Verbose, "JS: Enqueue Regular MT"); 1109 JS::JobQueueMayNotBeEmpty(cx); 1110 return microTaskQueue.pushBack(entry); 1111 } 1112 1113 bool js::MicroTaskQueueSet::prependRegularMicroTask( 1114 JSContext* cx, const JS::GenericMicroTask& entry) { 1115 JS_LOG(mtq, Verbose, "JS: Prepend Regular MT"); 1116 JS::JobQueueMayNotBeEmpty(cx); 1117 return microTaskQueue.emplaceFront(entry); 1118 } 1119 1120 bool js::MicroTaskQueueSet::enqueueDebugMicroTask( 1121 JSContext* cx, const JS::GenericMicroTask& entry) { 1122 JS_LOG(mtq, Verbose, "JS: Enqueue Debug MT"); 1123 return debugMicroTaskQueue.pushBack(entry); 1124 } 1125 1126 JS_PUBLIC_API bool JS::EnqueueMicroTask(JSContext* cx, 1127 const JS::GenericMicroTask& entry) { 1128 JS_LOG(mtq, Info, "Enqueue of non JS MT"); 1129 1130 return cx->microTaskQueues->enqueueRegularMicroTask(cx, entry); 1131 } 1132 1133 JS_PUBLIC_API bool JS::EnqueueDebugMicroTask( 1134 JSContext* cx, const JS::GenericMicroTask& entry) { 1135 JS_LOG(mtq, Info, "Enqueue of non JS MT"); 1136 1137 return cx->microTaskQueues->enqueueDebugMicroTask(cx, entry); 1138 } 1139 1140 JS_PUBLIC_API bool JS::PrependMicroTask(JSContext* cx, 1141 const JS::GenericMicroTask& entry) { 1142 JS_LOG(mtq, Info, "Prepend job to MTQ"); 1143 1144 return cx->microTaskQueues->prependRegularMicroTask(cx, entry); 1145 } 1146 1147 JS_PUBLIC_API JS::GenericMicroTask JS::DequeueNextMicroTask(JSContext* cx) { 1148 return cx->microTaskQueues->popFront(); 1149 } 1150 1151 JS_PUBLIC_API JS::GenericMicroTask JS::DequeueNextDebuggerMicroTask( 1152 JSContext* cx) { 1153 return cx->microTaskQueues->popDebugFront(); 1154 } 1155 1156 JS_PUBLIC_API bool JS::HasAnyMicroTasks(JSContext* cx) { 1157 return !cx->microTaskQueues->empty(); 1158 } 1159 1160 JS_PUBLIC_API bool JS::HasDebuggerMicroTasks(JSContext* cx) { 1161 return !cx->microTaskQueues->debugMicroTaskQueue.empty(); 1162 } 1163 1164 // Concrete implementation of the saved queue. 1165 struct SavedMicroTaskQueueImpl : public JS::SavedMicroTaskQueue { 1166 explicit SavedMicroTaskQueueImpl(JSContext* cx) : savedQueues(cx) { 1167 savedQueues = js::MakeUnique<js::MicroTaskQueueSet>(cx); 1168 std::swap(cx->microTaskQueues.get(), savedQueues.get()); 1169 } 1170 ~SavedMicroTaskQueueImpl() override = default; 1171 JS::PersistentRooted<js::UniquePtr<js::MicroTaskQueueSet>> savedQueues; 1172 }; 1173 1174 JS_PUBLIC_API js::UniquePtr<JS::SavedMicroTaskQueue> JS::SaveMicroTaskQueue( 1175 JSContext* cx) { 1176 auto saved = js::MakeUnique<SavedMicroTaskQueueImpl>(cx); 1177 if (!saved) { 1178 ReportOutOfMemory(cx); 1179 return nullptr; 1180 } 1181 return saved; 1182 } 1183 1184 JS_PUBLIC_API void JS::RestoreMicroTaskQueue( 1185 JSContext* cx, js::UniquePtr<JS::SavedMicroTaskQueue> savedQueue) { 1186 MOZ_ASSERT(cx->microTaskQueues->empty(), "Don't drop jobs on the floor"); 1187 1188 // There's only one impl, so we know this is safe. 1189 SavedMicroTaskQueueImpl* savedQueueImpl = 1190 static_cast<SavedMicroTaskQueueImpl*>(savedQueue.get()); 1191 std::swap(savedQueueImpl->savedQueues.get(), cx->microTaskQueues.get()); 1192 } 1193 1194 JS_PUBLIC_API size_t JS::GetRegularMicroTaskCount(JSContext* cx) { 1195 return cx->microTaskQueues->microTaskQueue.length(); 1196 } 1197 1198 JS_PUBLIC_API bool JS::HasRegularMicroTasks(JSContext* cx) { 1199 return !cx->microTaskQueues->microTaskQueue.empty(); 1200 } 1201 1202 JS_PUBLIC_API JS::GenericMicroTask JS::DequeueNextRegularMicroTask( 1203 JSContext* cx) { 1204 auto& queue = cx->microTaskQueues->microTaskQueue; 1205 if (!queue.empty()) { 1206 JS::GenericMicroTask p = queue.front(); 1207 queue.popFront(); 1208 return p; 1209 } 1210 return JS::NullValue(); 1211 } 1212 1213 mozilla::GenericErrorResult<OOM> JSContext::alreadyReportedOOM() { 1214 MOZ_ASSERT(isThrowingOutOfMemory()); 1215 return mozilla::Err(JS::OOM()); 1216 } 1217 1218 mozilla::GenericErrorResult<JS::Error> JSContext::alreadyReportedError() { 1219 return mozilla::Err(JS::Error()); 1220 } 1221 1222 JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options) 1223 : RootingContext(runtime ? &runtime->gc.nursery() : nullptr), 1224 runtime_(runtime), 1225 options_(this, options), 1226 measuringExecutionTimeEnabled_(this, false), 1227 jitActivation(this, nullptr), 1228 isolate(this, nullptr), 1229 activation_(this, nullptr), 1230 profilingActivation_(nullptr), 1231 noExecuteDebuggerTop(this, nullptr), 1232 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI 1233 inUnsafeCallWithABI(this, false), 1234 hasAutoUnsafeCallWithABI(this, false), 1235 #endif 1236 #ifdef DEBUG 1237 liveArraySortDataInstances(this, 0), 1238 #endif 1239 #ifdef JS_SIMULATOR 1240 simulator_(this, nullptr), 1241 #endif 1242 dtoaState(this, nullptr), 1243 suppressGC(this, 0), 1244 #ifdef FUZZING_JS_FUZZILLI 1245 executionHash(1), 1246 executionHashInputs(0), 1247 #endif 1248 #ifdef DEBUG 1249 noNurseryAllocationCheck(this, 0), 1250 disableStrictProxyCheckingCount(this, 0), 1251 #endif 1252 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) 1253 runningOOMTest(this, false), 1254 #endif 1255 inUnsafeRegion(this, 0), 1256 generationalDisabled(this, 0), 1257 compactingDisabledCount(this, 0), 1258 #ifdef DEBUG 1259 regExpSearcherLastLimit(this, RegExpSearcherLastLimitSentinel), 1260 #else 1261 regExpSearcherLastLimit(this, 0), 1262 #endif 1263 isEvaluatingModule(this, 0), 1264 frontendCollectionPool_(this), 1265 suppressProfilerSampling(false), 1266 tempLifoAlloc_(this, (size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE, 1267 js::MallocArena), 1268 debuggerMutations(this, 0), 1269 status(this, JS::ExceptionStatus::None), 1270 unwrappedException_(this), 1271 unwrappedExceptionStack_(this), 1272 #ifdef DEBUG 1273 hadResourceExhaustion_(this, false), 1274 hadUncatchableException_(this, false), 1275 #endif 1276 reportGranularity(this, JS_DEFAULT_JITREPORT_GRANULARITY), 1277 resolvingList(this, nullptr), 1278 #ifdef DEBUG 1279 enteredPolicy(this, nullptr), 1280 #endif 1281 generatingError(this, false), 1282 cycleDetectorVector_(this, this), 1283 data(nullptr), 1284 asyncStackForNewActivations_(this), 1285 asyncCauseForNewActivations(this, nullptr), 1286 asyncCallIsExplicit(this, false), 1287 interruptCallbacks_(this), 1288 interruptCallbackDisabled(this, false), 1289 interruptBits_(0), 1290 jitStackLimit(JS::NativeStackLimitMin), 1291 jitStackLimitNoInterrupt(this, JS::NativeStackLimitMin), 1292 jobQueue(this, nullptr), 1293 internalJobQueue(this), 1294 canSkipEnqueuingJobs(this, false), 1295 promiseRejectionTrackerCallback(this, nullptr), 1296 promiseRejectionTrackerCallbackData(this, nullptr), 1297 oomStackTraceBuffer_(this, nullptr), 1298 oomStackTraceBufferValid_(this, false), 1299 bypassCSPForDebugger(this, false), 1300 insideExclusiveDebuggerOnEval(this, nullptr), 1301 microTaskQueues(this) { 1302 MOZ_ASSERT(static_cast<JS::RootingContext*>(this) == 1303 JS::RootingContext::get(this)); 1304 1305 if (JS::Prefs::experimental_capture_oom_stack_trace()) { 1306 // Allocate pre-allocated buffer for OOM stack traces 1307 oomStackTraceBuffer_ = 1308 static_cast<char*>(js_calloc(OOMStackTraceBufferSize)); 1309 } 1310 } 1311 1312 JSContext::~JSContext() { 1313 #ifdef DEBUG 1314 // Clear the initialized_ first, so that ProtectedData checks will allow us to 1315 // destroy this context even if the runtime is already gone. 1316 initialized_ = false; 1317 #endif 1318 1319 /* Free the stuff hanging off of cx. */ 1320 MOZ_ASSERT(!resolvingList); 1321 1322 // Ensure we didn't leak memory for the ArraySortData vector. 1323 MOZ_ASSERT(liveArraySortDataInstances == 0); 1324 1325 if (dtoaState) { 1326 DestroyDtoaState(dtoaState); 1327 } 1328 1329 fx.destroyInstance(); 1330 1331 #ifdef JS_SIMULATOR 1332 js::jit::Simulator::Destroy(simulator_); 1333 #endif 1334 1335 if (isolate) { 1336 irregexp::DestroyIsolate(isolate.ref()); 1337 } 1338 1339 // Free the pre-allocated OOM stack trace buffer 1340 if (oomStackTraceBuffer_) { 1341 js_free(oomStackTraceBuffer_); 1342 } 1343 1344 TlsContext.set(nullptr); 1345 } 1346 1347 void JSContext::unsetOOMStackTrace() { oomStackTraceBufferValid_ = false; } 1348 1349 const char* JSContext::getOOMStackTrace() const { 1350 if (!oomStackTraceBufferValid_ || !oomStackTraceBuffer_) { 1351 return nullptr; 1352 } 1353 return oomStackTraceBuffer_; 1354 } 1355 1356 bool JSContext::hasOOMStackTrace() const { return oomStackTraceBufferValid_; } 1357 1358 void JSContext::captureOOMStackTrace() { 1359 // Clear any existing stack trace 1360 oomStackTraceBufferValid_ = false; 1361 1362 if (!oomStackTraceBuffer_) { 1363 return; // Buffer not available 1364 } 1365 1366 // Write directly to pre-allocated buffer to avoid any memory allocation 1367 FixedBufferPrinter fbp(oomStackTraceBuffer_, OOMStackTraceBufferSize); 1368 js::DumpBacktrace(this, fbp); 1369 MOZ_ASSERT(strlen(oomStackTraceBuffer_) < OOMStackTraceBufferSize); 1370 1371 oomStackTraceBufferValid_ = true; 1372 } 1373 1374 void JSContext::setRuntime(JSRuntime* rt) { 1375 MOZ_ASSERT(!resolvingList); 1376 MOZ_ASSERT(!compartment()); 1377 MOZ_ASSERT(!activation()); 1378 MOZ_ASSERT(!unwrappedException_.ref().initialized()); 1379 MOZ_ASSERT(!unwrappedExceptionStack_.ref().initialized()); 1380 MOZ_ASSERT(!asyncStackForNewActivations_.ref().initialized()); 1381 1382 runtime_ = rt; 1383 } 1384 1385 #if defined(NIGHTLY_BUILD) 1386 static bool IsOutOfMemoryException(JSContext* cx, const Value& v) { 1387 return v == StringValue(cx->names().out_of_memory_); 1388 } 1389 #endif 1390 1391 void JSContext::setPendingException(HandleValue v, Handle<SavedFrame*> stack) { 1392 #if defined(NIGHTLY_BUILD) 1393 do { 1394 // Do not intercept exceptions if we are already 1395 // in the exception interceptor. That would lead 1396 // to infinite recursion. 1397 if (this->runtime()->errorInterception.isExecuting) { 1398 break; 1399 } 1400 1401 // Check whether we have an interceptor at all. 1402 if (!this->runtime()->errorInterception.interceptor) { 1403 break; 1404 } 1405 1406 // Don't report OOM exceptions. The interceptor isn't interested in those 1407 // and they can confuse the interceptor because OOM can be thrown when we 1408 // are not in a realm (atom allocation, for example). 1409 if (IsOutOfMemoryException(this, v)) { 1410 break; 1411 } 1412 1413 // Make sure that we do not call the interceptor from within 1414 // the interceptor. 1415 this->runtime()->errorInterception.isExecuting = true; 1416 1417 // The interceptor must be infallible. 1418 const mozilla::DebugOnly<bool> wasExceptionPending = 1419 this->isExceptionPending(); 1420 this->runtime()->errorInterception.interceptor->interceptError(this, v); 1421 MOZ_ASSERT(wasExceptionPending == this->isExceptionPending()); 1422 1423 this->runtime()->errorInterception.isExecuting = false; 1424 } while (false); 1425 #endif // defined(NIGHTLY_BUILD) 1426 1427 // overRecursed_ is set after the fact by ReportOverRecursed. 1428 this->status = JS::ExceptionStatus::Throwing; 1429 this->unwrappedException() = v; 1430 this->unwrappedExceptionStack() = stack; 1431 } 1432 1433 void JSContext::setPendingException(HandleValue value, 1434 ShouldCaptureStack captureStack) { 1435 Rooted<SavedFrame*> nstack(this); 1436 if (captureStack == ShouldCaptureStack::Always || 1437 realm()->shouldCaptureStackForThrow()) { 1438 RootedObject stack(this); 1439 if (!CaptureStack(this, &stack)) { 1440 clearPendingException(); 1441 } 1442 if (stack) { 1443 nstack = &stack->as<SavedFrame>(); 1444 } 1445 } 1446 setPendingException(value, nstack); 1447 } 1448 1449 bool JSContext::getPendingException(MutableHandleValue rval) { 1450 MOZ_ASSERT(isExceptionPending()); 1451 1452 RootedValue exception(this, unwrappedException()); 1453 if (zone()->isAtomsZone()) { 1454 rval.set(exception); 1455 return true; 1456 } 1457 1458 Rooted<SavedFrame*> stack(this, unwrappedExceptionStack()); 1459 JS::ExceptionStatus prevStatus = status; 1460 clearPendingException(); 1461 if (!compartment()->wrap(this, &exception)) { 1462 return false; 1463 } 1464 this->check(exception); 1465 setPendingException(exception, stack); 1466 status = prevStatus; 1467 1468 rval.set(exception); 1469 return true; 1470 } 1471 1472 bool JSContext::getPendingExceptionStack(MutableHandleValue rval) { 1473 MOZ_ASSERT(isExceptionPending()); 1474 1475 Rooted<SavedFrame*> exceptionStack(this, unwrappedExceptionStack()); 1476 if (!exceptionStack) { 1477 rval.setNull(); 1478 return true; 1479 } 1480 if (zone()->isAtomsZone()) { 1481 rval.setObject(*exceptionStack); 1482 return true; 1483 } 1484 1485 RootedValue stack(this, ObjectValue(*exceptionStack)); 1486 RootedValue exception(this, unwrappedException()); 1487 JS::ExceptionStatus prevStatus = status; 1488 clearPendingException(); 1489 if (!compartment()->wrap(this, &exception) || 1490 !compartment()->wrap(this, &stack)) { 1491 return false; 1492 } 1493 this->check(stack); 1494 setPendingException(exception, exceptionStack); 1495 status = prevStatus; 1496 1497 rval.set(stack); 1498 return true; 1499 } 1500 1501 SavedFrame* JSContext::getPendingExceptionStack() { 1502 return unwrappedExceptionStack(); 1503 } 1504 1505 #ifdef DEBUG 1506 const JS::Value& JSContext::getPendingExceptionUnwrapped() { 1507 MOZ_ASSERT(isExceptionPending()); 1508 return unwrappedException(); 1509 } 1510 #endif 1511 1512 bool JSContext::isClosingGenerator() { 1513 return isExceptionPending() && 1514 unwrappedException().isMagic(JS_GENERATOR_CLOSING); 1515 } 1516 1517 bool JSContext::isThrowingDebuggeeWouldRun() { 1518 return isExceptionPending() && unwrappedException().isObject() && 1519 unwrappedException().toObject().is<ErrorObject>() && 1520 unwrappedException().toObject().as<ErrorObject>().type() == 1521 JSEXN_DEBUGGEEWOULDRUN; 1522 } 1523 1524 bool JSContext::isRuntimeCodeGenEnabled( 1525 JS::RuntimeCode kind, JS::Handle<JSString*> codeString, 1526 JS::CompilationType compilationType, 1527 JS::Handle<JS::StackGCVector<JSString*>> parameterStrings, 1528 JS::Handle<JSString*> bodyString, 1529 JS::Handle<JS::StackGCVector<JS::Value>> parameterArgs, 1530 JS::Handle<JS::Value> bodyArg, bool* outCanCompileStrings) { 1531 // Make sure that the CSP callback is installed and that it permits runtime 1532 // code generation. 1533 if (JSCSPEvalChecker allows = 1534 runtime()->securityCallbacks->contentSecurityPolicyAllows) { 1535 return allows(this, kind, codeString, compilationType, parameterStrings, 1536 bodyString, parameterArgs, bodyArg, outCanCompileStrings); 1537 } 1538 1539 // Default implementation from the "Dynamic Code Brand Checks" spec. 1540 // https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostensurecancompilestrings 1541 *outCanCompileStrings = true; 1542 return true; 1543 } 1544 1545 bool JSContext::getCodeForEval(HandleObject code, 1546 JS::MutableHandle<JSString*> outCode) { 1547 if (JSCodeForEvalOp gets = runtime()->securityCallbacks->codeForEvalGets) { 1548 return gets(this, code, outCode); 1549 } 1550 // Default implementation from the "Dynamic Code Brand Checks" spec. 1551 // https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostgetcodeforeval 1552 outCode.set(nullptr); 1553 return true; 1554 } 1555 1556 size_t JSContext::sizeOfExcludingThis( 1557 mozilla::MallocSizeOf mallocSizeOf) const { 1558 /* 1559 * There are other JSContext members that could be measured; the following 1560 * ones have been found by DMD to be worth measuring. More stuff may be 1561 * added later. 1562 */ 1563 return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf) + 1564 irregexp::IsolateSizeOfIncludingThis(isolate, mallocSizeOf); 1565 } 1566 1567 size_t JSContext::sizeOfIncludingThis( 1568 mozilla::MallocSizeOf mallocSizeOf) const { 1569 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 1570 } 1571 1572 #ifdef DEBUG 1573 bool JSContext::inAtomsZone() const { return zone_->isAtomsZone(); } 1574 #endif 1575 1576 void JSContext::trace(JSTracer* trc) { 1577 cycleDetectorVector().trace(trc); 1578 geckoProfiler().trace(trc); 1579 if (isolate) { 1580 irregexp::TraceIsolate(trc, isolate.ref()); 1581 } 1582 #ifdef ENABLE_WASM_JSPI 1583 wasm().trace(trc); 1584 #endif 1585 } 1586 1587 JS::NativeStackLimit JSContext::stackLimitForJitCode(JS::StackKind kind) { 1588 #ifdef JS_SIMULATOR 1589 return simulator()->stackLimit(); 1590 #else 1591 return stackLimit(kind); 1592 #endif 1593 } 1594 1595 void JSContext::resetJitStackLimit() { 1596 // Note that, for now, we use the untrusted limit for ion. This is fine, 1597 // because it's the most conservative limit, and if we hit it, we'll bail 1598 // out of ion into the interpreter, which will do a proper recursion check. 1599 #ifdef JS_SIMULATOR 1600 jitStackLimit = jit::Simulator::StackLimit(); 1601 #else 1602 jitStackLimit = nativeStackLimit[JS::StackForUntrustedScript]; 1603 #endif 1604 jitStackLimitNoInterrupt = jitStackLimit; 1605 } 1606 1607 void JSContext::initJitStackLimit() { 1608 resetJitStackLimit(); 1609 wasm_.initStackLimit(this); 1610 } 1611 1612 JSScript* JSContext::currentScript(jsbytecode** ppc, 1613 AllowCrossRealm allowCrossRealm) { 1614 if (ppc) { 1615 *ppc = nullptr; 1616 } 1617 1618 // Fast path: there are no JS frames on the stack if there's no activation. 1619 if (!activation()) { 1620 return nullptr; 1621 } 1622 1623 FrameIter iter(this); 1624 if (iter.done() || !iter.hasScript()) { 1625 return nullptr; 1626 } 1627 1628 JSScript* script = iter.script(); 1629 if (allowCrossRealm == AllowCrossRealm::DontAllow && 1630 script->realm() != realm()) { 1631 return nullptr; 1632 } 1633 1634 if (ppc) { 1635 *ppc = iter.pc(); 1636 } 1637 return script; 1638 } 1639 1640 #ifdef JS_CRASH_DIAGNOSTICS 1641 void ContextChecks::check(AbstractFramePtr frame, int argIndex) { 1642 if (frame) { 1643 check(frame.realm(), argIndex); 1644 } 1645 } 1646 #endif 1647 1648 void AutoEnterOOMUnsafeRegion::crash_impl(const char* reason) { 1649 char msgbuf[1024]; 1650 js::NoteIntentionalCrash(); 1651 SprintfLiteral(msgbuf, "[unhandlable oom] %s", reason); 1652 #ifndef DEBUG 1653 // In non-DEBUG builds MOZ_CRASH normally doesn't print to stderr so we have 1654 // to do this explicitly (the jit-test allow-unhandlable-oom annotation and 1655 // fuzzers depend on it). 1656 fprintf(stderr, "Hit MOZ_CRASH(%s) at %s:%d\n", msgbuf, __FILE__, __LINE__); 1657 #endif 1658 MOZ_CRASH_UNSAFE(msgbuf); 1659 } 1660 1661 mozilla::Atomic<AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback, 1662 mozilla::Relaxed> 1663 AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback(nullptr); 1664 1665 void AutoEnterOOMUnsafeRegion::crash_impl(size_t size, const char* reason) { 1666 { 1667 JS::AutoSuppressGCAnalysis suppress; 1668 if (annotateOOMSizeCallback) { 1669 annotateOOMSizeCallback(size); 1670 } 1671 } 1672 crash_impl(reason); 1673 } 1674 1675 void ExternalValueArray::trace(JSTracer* trc) { 1676 if (Value* vp = begin()) { 1677 TraceRootRange(trc, length(), vp, "js::ExternalValueArray"); 1678 } 1679 } 1680 1681 #ifdef MOZ_EXECUTION_TRACING 1682 1683 bool JSContext::enableExecutionTracing() { 1684 if (!executionTracer_) { 1685 for (RealmsIter realm(runtime()); !realm.done(); realm.next()) { 1686 if (realm->debuggerObservesCoverage()) { 1687 JS_ReportErrorNumberASCII( 1688 this, GetErrorMessage, nullptr, 1689 JSMSG_DEBUG_EXCLUSIVE_EXECUTION_TRACE_COVERAGE); 1690 return false; 1691 } 1692 } 1693 1694 executionTracer_ = js::MakeUnique<ExecutionTracer>(); 1695 1696 if (!executionTracer_) { 1697 return false; 1698 } 1699 1700 if (!executionTracer_->init()) { 1701 executionTracer_ = nullptr; 1702 return false; 1703 } 1704 1705 for (RealmsIter realm(runtime()); !realm.done(); realm.next()) { 1706 if (realm->isSystem()) { 1707 continue; 1708 } 1709 realm->enableExecutionTracing(); 1710 } 1711 } 1712 1713 executionTracerSuspended_ = false; 1714 return true; 1715 } 1716 1717 void JSContext::cleanUpExecutionTracingState() { 1718 MOZ_ASSERT(executionTracer_); 1719 1720 for (RealmsIter realm(runtime()); !realm.done(); realm.next()) { 1721 if (realm->isSystem()) { 1722 continue; 1723 } 1724 realm->disableExecutionTracing(); 1725 } 1726 1727 caches().tracingCaches.clearAll(); 1728 } 1729 1730 void JSContext::disableExecutionTracing() { 1731 if (executionTracer_) { 1732 cleanUpExecutionTracingState(); 1733 executionTracer_ = nullptr; 1734 } 1735 } 1736 1737 void JSContext::suspendExecutionTracing() { 1738 if (executionTracer_) { 1739 cleanUpExecutionTracingState(); 1740 executionTracerSuspended_ = true; 1741 } 1742 } 1743 1744 #endif 1745 1746 #ifdef JS_CHECK_UNSAFE_CALL_WITH_ABI 1747 1748 AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness) 1749 : cx_(TlsContext.get()), 1750 nested_(cx_ ? cx_->hasAutoUnsafeCallWithABI : false), 1751 nogc(cx_) { 1752 if (!cx_) { 1753 // This is a helper thread doing Ion or Wasm compilation - nothing to do. 1754 return; 1755 } 1756 switch (strictness) { 1757 case UnsafeABIStrictness::NoExceptions: 1758 MOZ_ASSERT(!JS_IsExceptionPending(cx_)); 1759 checkForPendingException_ = true; 1760 break; 1761 case UnsafeABIStrictness::AllowPendingExceptions: 1762 checkForPendingException_ = !JS_IsExceptionPending(cx_); 1763 break; 1764 } 1765 1766 cx_->hasAutoUnsafeCallWithABI = true; 1767 } 1768 1769 AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI() { 1770 if (!cx_) { 1771 return; 1772 } 1773 MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI); 1774 if (!nested_) { 1775 cx_->hasAutoUnsafeCallWithABI = false; 1776 cx_->inUnsafeCallWithABI = false; 1777 } 1778 MOZ_ASSERT_IF(checkForPendingException_, !JS_IsExceptionPending(cx_)); 1779 } 1780 1781 #endif // JS_CHECK_UNSAFE_CALL_WITH_ABI 1782 1783 #ifdef __wasi__ 1784 JS_PUBLIC_API void js::IncWasiRecursionDepth(JSContext* cx) { 1785 ++JS::RootingContext::get(cx)->wasiRecursionDepth; 1786 } 1787 1788 JS_PUBLIC_API void js::DecWasiRecursionDepth(JSContext* cx) { 1789 MOZ_ASSERT(JS::RootingContext::get(cx)->wasiRecursionDepth > 0); 1790 --JS::RootingContext::get(cx)->wasiRecursionDepth; 1791 } 1792 1793 JS_PUBLIC_API bool js::CheckWasiRecursionLimit(JSContext* cx) { 1794 // WASI has two limits: 1795 // 1) The stack pointer in linear memory that grows to zero. See 1796 // --stack-first in js/src/shell/moz.build. 1797 // 2) The JS::RootingContext::wasiRecursionDepth that counts recursion depth. 1798 // Here we should check both. 1799 if (JS::RootingContext::get(cx)->wasiRecursionDepth >= 1800 JS::RootingContext::wasiRecursionDepthLimit) { 1801 return false; 1802 } 1803 return true; 1804 } 1805 #endif // __wasi__