Bug 1781008 - Part 1: Move ReportOverRecursed implementation to JSContext and ErrorContext. r=bthrall
authorTooru Fujisawa <arai_a@mac.com>
Sat, 30 Jul 2022 04:45:20 +0000
changeset 625569 9c5f59e32d1941269f982ff1c3b09bb63626c376
parent 625568 c6adbe1e71951db7944d3e16531c87fd125408eb
child 625570 dd0966996f42dcd4d548eeb40a7ec7673ff5de57
push id167065
push userarai_a@mac.com
push dateSat, 30 Jul 2022 04:50:14 +0000
treeherderautoland@1d0a625cf1be [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbthrall
bugs1781008
milestone105.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1781008 - Part 1: Move ReportOverRecursed implementation to JSContext and ErrorContext. r=bthrall Depends on D152778 Differential Revision: https://phabricator.services.mozilla.com/D152779
js/public/friend/StackLimits.h
js/src/vm/ErrorContext.cpp
js/src/vm/ErrorContext.h
js/src/vm/JSContext.cpp
js/src/vm/JSContext.h
--- a/js/public/friend/StackLimits.h
+++ b/js/public/friend/StackLimits.h
@@ -26,16 +26,18 @@ struct JS_PUBLIC_API JSContext;
 #    define JS_STACK_GROWTH_DIRECTION (1)
 #  else
 #    define JS_STACK_GROWTH_DIRECTION (-1)
 #  endif
 #endif
 
 namespace js {
 
+class ErrorContext;
+
 // AutoCheckRecursionLimit can be used to check whether we're close to using up
 // the C++ stack.
 //
 // Typical usage is like this:
 //
 //   AutoCheckRecursionLimit recursion(cx);
 //   if (!recursion.check(cx)) {
 //     return false;
@@ -102,16 +104,17 @@ class MOZ_RAII AutoCheckRecursionLimit {
       JSContext* cx) const;
 
   [[nodiscard]] MOZ_ALWAYS_INLINE bool checkSystem(JSContext* cx) const;
   [[nodiscard]] MOZ_ALWAYS_INLINE bool checkSystemDontReport(
       JSContext* cx) const;
 };
 
 extern MOZ_COLD JS_PUBLIC_API void ReportOverRecursed(JSContext* maybecx);
+extern MOZ_COLD JS_PUBLIC_API void ReportOverRecursed(ErrorContext* ec);
 
 MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkLimitImpl(uintptr_t limit,
                                                                void* sp) const {
   JS_STACK_OOM_POSSIBLY_FAIL();
 
 #ifdef __wasi__
   // WASI has two limits:
   // 1) The stack pointer in linear memory that grows to zero. See --stack-first
--- a/js/src/vm/ErrorContext.cpp
+++ b/js/src/vm/ErrorContext.cpp
@@ -29,16 +29,18 @@ void* MainThreadErrorContext::onOutOfMem
                                             void* reallocPtr) {
   return cx_->onOutOfMemory(allocFunc, arena, nbytes, reallocPtr);
 }
 
 void MainThreadErrorContext::onAllocationOverflow() {
   return cx_->reportAllocationOverflow();
 }
 
+void MainThreadErrorContext::onOverRecursed() { cx_->onOverRecursed(); }
+
 const JSErrorFormatString* MainThreadErrorContext::gcSafeCallback(
     JSErrorCallback callback, void* userRef, const unsigned errorNumber) {
   gc::AutoSuppressGC suppressGC(cx_);
   return callback(userRef, errorNumber);
 }
 
 void MainThreadErrorContext::reportError(CompileError* err) {
   // On the main thread, report the error immediately.
@@ -75,16 +77,18 @@ void* OffThreadErrorContext::onOutOfMemo
   return nullptr;
 }
 
 void OffThreadErrorContext::onAllocationOverflow() {
   // TODO Bug 1780599 - Currently allocation overflows are not reported for
   // helper threads; see js::reportAllocationOverflow()
 }
 
+void OffThreadErrorContext::onOverRecursed() { errors_.overRecursed = true; }
+
 const JSErrorFormatString* OffThreadErrorContext::gcSafeCallback(
     JSErrorCallback callback, void* userRef, const unsigned errorNumber) {
   return callback(userRef, errorNumber);
 }
 
 void OffThreadErrorContext::reportError(CompileError* err) {
   // When compiling off thread, save the error so that the thread finishing the
   // parse can report it later.
--- a/js/src/vm/ErrorContext.h
+++ b/js/src/vm/ErrorContext.h
@@ -47,16 +47,17 @@ class ErrorContext {
   // Report CompileErrors
   virtual void reportError(js::CompileError* err) = 0;
   virtual void reportWarning(js::CompileError* err) = 0;
 
   // Report ErrorAllocator errors
   virtual void* onOutOfMemory(js::AllocFunction allocFunc, arena_id_t arena,
                               size_t nbytes, void* reallocPtr = nullptr) = 0;
   virtual void onAllocationOverflow() = 0;
+  virtual void onOverRecursed() = 0;
 
   virtual const JSErrorFormatString* gcSafeCallback(
       JSErrorCallback callback, void* userRef, const unsigned errorNumber) = 0;
 
   // Status of errors reported to this ErrorContext
   virtual bool hadOutOfMemory() const = 0;
   virtual bool hadOverRecursed() const = 0;
   virtual bool hadErrors() const = 0;
@@ -72,16 +73,17 @@ class MainThreadErrorContext : public Er
   // Report CompileErrors
   void reportError(js::CompileError* err) override;
   void reportWarning(js::CompileError* err) override;
 
   // Report ErrorAllocator errors
   void* onOutOfMemory(js::AllocFunction allocFunc, arena_id_t arena,
                       size_t nbytes, void* reallocPtr = nullptr) override;
   void onAllocationOverflow() override;
+  void onOverRecursed() override;
 
   const JSErrorFormatString* gcSafeCallback(
       JSErrorCallback callback, void* userRef,
       const unsigned errorNumber) override;
 
   // Status of errors reported to this ErrorContext
   bool hadOutOfMemory() const override;
   bool hadOverRecursed() const override;
@@ -103,16 +105,17 @@ class OffThreadErrorContext : public Err
   // Report CompileErrors
   void reportError(js::CompileError* err) override;
   void reportWarning(js::CompileError* err) override;
 
   // Report ErrorAllocator errors
   void* onOutOfMemory(js::AllocFunction allocFunc, arena_id_t arena,
                       size_t nbytes, void* reallocPtr = nullptr) override;
   void onAllocationOverflow() override;
+  void onOverRecursed() override;
 
   const JSErrorFormatString* gcSafeCallback(
       JSErrorCallback callback, void* userRef,
       const unsigned errorNumber) override;
 
   // Status of errors reported to this ErrorContext
   bool hadOutOfMemory() const override { return errors_.outOfMemory; }
   bool hadOverRecursed() const override { return errors_.overRecursed; }
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -303,48 +303,65 @@ JS_PUBLIC_API void js::ReportOutOfMemory
   MOZ_ASSERT(cx->status == JS::ExceptionStatus::Throwing);
   cx->status = JS::ExceptionStatus::OutOfMemory;
 
 #ifdef DEBUG
   cx->hadNondeterministicException_ = true;
 #endif
 }
 
-JS_PUBLIC_API void js::ReportOverRecursed(JSContext* maybecx) {
+static void MaybeReportOverRecursedForDifferentialTesting() {
   /*
    * We cannot make stack depth deterministic across different
    * implementations (e.g. JIT vs. interpreter will differ in
    * their maximum stack depth).
    * However, we can detect externally when we hit the maximum
    * stack depth which is useful for external testing programs
    * like fuzzers.
    */
   if (js::SupportDifferentialTesting()) {
     fprintf(stderr, "ReportOverRecursed called\n");
   }
+}
 
-  if (maybecx) {
-    if (maybecx->isHelperThreadContext()) {
-      maybecx->addPendingOverRecursed();
-    } else {
-      // Try to construct an over-recursed error and then update the exception
-      // status to `OverRecursed`. Creating the error can fail, so check there
-      // is a reasonable looking exception pending before updating status.
-      JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr,
-                                JSMSG_OVER_RECURSED);
-      if (maybecx->isExceptionPending() && !maybecx->isThrowingOutOfMemory()) {
-        MOZ_ASSERT(maybecx->unwrappedException().isObject());
-        MOZ_ASSERT(maybecx->status == JS::ExceptionStatus::Throwing);
-        maybecx->status = JS::ExceptionStatus::OverRecursed;
-      }
+void JSContext::onOverRecursed() {
+  if (isHelperThreadContext()) {
+    addPendingOverRecursed();
+  } else {
+    // Try to construct an over-recursed error and then update the exception
+    // status to `OverRecursed`. Creating the error can fail, so check there
+    // is a reasonable looking exception pending before updating status.
+    JS_ReportErrorNumberASCII(this, GetErrorMessage, nullptr,
+                              JSMSG_OVER_RECURSED);
+    if (isExceptionPending() && !isThrowingOutOfMemory()) {
+      MOZ_ASSERT(unwrappedException().isObject());
+      MOZ_ASSERT(status == JS::ExceptionStatus::Throwing);
+      status = JS::ExceptionStatus::OverRecursed;
     }
+  }
+
 #ifdef DEBUG
-    maybecx->hadNondeterministicException_ = true;
+  hadNondeterministicException_ = true;
 #endif
+}
+
+JS_PUBLIC_API void js::ReportOverRecursed(JSContext* maybecx) {
+  MaybeReportOverRecursedForDifferentialTesting();
+
+  if (!maybecx) {
+    return;
   }
+
+  maybecx->onOverRecursed();
+}
+
+JS_PUBLIC_API void js::ReportOverRecursed(ErrorContext* ec) {
+  MaybeReportOverRecursedForDifferentialTesting();
+
+  ec->onOverRecursed();
 }
 
 void js::ReportOversizedAllocation(JSContext* cx, const unsigned errorNumber) {
   // The JIT may optimize away allocations if it determines that they aren't
   // used. This can affect whether we throw an exception when the size of an
   // allocation exceeds implementation-defined limits (eg JSString::MAX_LENGTH).
   // These errors aren't interesting for the purposes of differential fuzzing.
   // We print a message so that fuzzers can detect this case. To simplify
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -256,16 +256,18 @@ struct JS_PUBLIC_API JSContext : public 
                       size_t nbytes, void* reallocPtr = nullptr) {
     if (isHelperThreadContext()) {
       addPendingOutOfMemory();
       return nullptr;
     }
     return runtime_->onOutOfMemory(allocFunc, arena, nbytes, reallocPtr, this);
   }
 
+  void onOverRecursed();
+
   /* Clear the pending exception (if any) due to OOM. */
   void recoverFromOutOfMemory();
 
   void reportAllocationOverflow() { js::ReportAllocationOverflow(this); }
 
   // Accessors for immutable runtime data.
   JSAtomState& names() { return *runtime_->commonNames; }
   js::StaticStrings& staticStrings() { return *runtime_->staticStrings; }