tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 54a523d23d99d6f787d79104dafa9830b3c24e8e
parent 6136e2ffd1df61f4a73e9c297befb5845cbf5996
Author: Ryan Hunt <rhunt@eqrion.net>
Date:   Thu, 18 Dec 2025 16:45:25 +0000

Bug 2002625 - wasm: Merge SuspenderContext and wasm::Context. r=yury

We don't gain much by having SuspenderContext separate from
wasm::Context, and will make future patches harder. This
patch merges them.

One side effect is that SuspenderObject is now a public
interface in WasmPI.h. This is required for other
changes and isn't unusual.

Differential Revision: https://phabricator.services.mozilla.com/D274183

Diffstat:
Mjs/src/jit/JitFrames.cpp | 2+-
Mjs/src/vm/JSContext.cpp | 2+-
Mjs/src/wasm/WasmContext.cpp | 42+++++++++++++++++++++++++++++++++++++++---
Mjs/src/wasm/WasmContext.h | 29++++++++++-------------------
Mjs/src/wasm/WasmPI.cpp | 206+++++++++++++++++++++----------------------------------------------------------
Mjs/src/wasm/WasmPI.h | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
6 files changed, 185 insertions(+), 194 deletions(-)

diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp @@ -1485,7 +1485,7 @@ void TraceJitActivations(JSContext* cx, JSTracer* trc) { TraceJitActivation(trc, activations->asJit()); } #ifdef ENABLE_WASM_JSPI - cx->wasm().promiseIntegration.traceRoots(trc); + cx->wasm().traceRoots(trc); #endif } diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp @@ -1536,7 +1536,7 @@ void JSContext::trace(JSTracer* trc) { irregexp::TraceIsolate(trc, isolate.ref()); } #ifdef ENABLE_WASM_JSPI - wasm().promiseIntegration.trace(trc); + wasm().trace(trc); #endif } diff --git a/js/src/wasm/WasmContext.cpp b/js/src/wasm/WasmContext.cpp @@ -19,8 +19,11 @@ #include "wasm/WasmContext.h" #include "js/friend/StackLimits.h" +#include "js/TracingAPI.h" #include "vm/JSContext.h" +#include "wasm/WasmPI.h" + using namespace js::wasm; Context::Context() @@ -29,12 +32,21 @@ Context::Context() stackLimit(JS::NativeStackLimitMin) #ifdef ENABLE_WASM_JSPI , + activeSuspender_(nullptr), onSuspendableStack(0), - suspendableStacksCount(0) + suspendableStacksCount(0), + suspendedStacks_() #endif { } +Context::~Context() { +#ifdef ENABLE_WASM_JSPI + MOZ_ASSERT(activeSuspender_ == nullptr); + MOZ_ASSERT(suspendedStacks_.isEmpty()); +#endif +} + void Context::initStackLimit(JSContext* cx) { // The wasm stack limit is the same as the jit stack limit. We also don't // use the stack limit for triggering interrupts. @@ -42,6 +54,32 @@ void Context::initStackLimit(JSContext* cx) { } #ifdef ENABLE_WASM_JSPI +SuspenderObject* Context::activeSuspender() { return activeSuspender_; } + +void Context::setActiveSuspender(SuspenderObject* obj) { + activeSuspender_.set(obj); +} + +void Context::trace(JSTracer* trc) { + if (activeSuspender_) { + TraceEdge(trc, &activeSuspender_, "suspender"); + } +} + +void Context::traceRoots(JSTracer* trc) { + // The suspendedStacks_ contains suspended stacks frames that need to be + // traced only during minor GC. The major GC tracing is happening via + // SuspenderObject::trace. + // Non-suspended stack frames are traced as part of TraceJitActivations. + if (!trc->isTenuringTracer()) { + return; + } + gc::AssertRootMarkingPhase(trc); + for (const SuspenderObjectData& data : suspendedStacks_) { + TraceSuspendableStack(trc, data); + } +} + void Context::enterSuspendableStack(JS::NativeStackLimit newStackLimit) { MOZ_ASSERT(onSuspendableStack == 0); onSuspendableStack = 1; @@ -53,9 +91,7 @@ void Context::leaveSuspendableStack(JSContext* cx) { onSuspendableStack = 0; initStackLimit(cx); } -#endif -#ifdef ENABLE_WASM_JSPI bool js::IsSuspendableStackActive(JSContext* cx) { return cx->wasm().onSuspendableStack != 0; } diff --git a/js/src/wasm/WasmContext.h b/js/src/wasm/WasmContext.h @@ -30,26 +30,8 @@ namespace js::wasm { #ifdef ENABLE_WASM_JSPI - class SuspenderObject; class SuspenderObjectData; - -class SuspenderContext { - private: - HeapPtr<SuspenderObject*> activeSuspender_; - // Using double-linked list to avoid allocation in the JIT code. - mozilla::DoublyLinkedList<SuspenderObjectData> suspendedStacks_; - - public: - SuspenderContext(); - ~SuspenderContext(); - SuspenderObject* activeSuspender(); - void setActiveSuspender(SuspenderObject* obj); - void trace(JSTracer* trc); - void traceRoots(JSTracer* trc); - - friend class SuspenderObject; -}; #endif // ENABLE_WASM_JSPI // wasm::Context lives in JSContext and contains the wasm-related per-context @@ -58,6 +40,7 @@ class SuspenderContext { class Context { public: Context(); + ~Context(); static constexpr size_t offsetOfStackLimit() { return offsetof(Context, stackLimit); @@ -71,8 +54,14 @@ class Context { void initStackLimit(JSContext* cx); #ifdef ENABLE_WASM_JSPI + SuspenderObject* activeSuspender(); + void setActiveSuspender(SuspenderObject* obj); + void enterSuspendableStack(JS::NativeStackLimit newStackLimit); void leaveSuspendableStack(JSContext* cx); + + void trace(JSTracer* trc); + void traceRoots(JSTracer* trc); #endif // Used by wasm::EnsureThreadSignalHandlers(cx) to install thread signal @@ -86,11 +75,13 @@ class Context { JS::NativeStackLimit stackLimit; #ifdef ENABLE_WASM_JSPI + HeapPtr<SuspenderObject*> activeSuspender_; // Boolean value set to true when the top wasm frame is currently executed on // a suspendable stack. Aligned to int32_t to be used on JIT code. int32_t onSuspendableStack; mozilla::Atomic<uint32_t> suspendableStacksCount; - SuspenderContext promiseIntegration; + // Using double-linked list to avoid allocation in the JIT code. + mozilla::DoublyLinkedList<SuspenderObjectData> suspendedStacks_; #endif }; diff --git a/js/src/wasm/WasmPI.cpp b/js/src/wasm/WasmPI.cpp @@ -166,28 +166,6 @@ const size_t WRAPPED_FN_SLOT = 1; const size_t CONTINUE_ON_SUSPENDABLE_SLOT = 1; const size_t PROMISE_SLOT = 2; -SuspenderContext::SuspenderContext() - : activeSuspender_(nullptr), suspendedStacks_() {} - -SuspenderContext::~SuspenderContext() { - MOZ_ASSERT(activeSuspender_ == nullptr); - MOZ_ASSERT(suspendedStacks_.isEmpty()); -} - -SuspenderObject* SuspenderContext::activeSuspender() { - return activeSuspender_; -} - -void SuspenderContext::setActiveSuspender(SuspenderObject* obj) { - activeSuspender_.set(obj); -} - -void SuspenderContext::trace(JSTracer* trc) { - if (activeSuspender_) { - TraceEdge(trc, &activeSuspender_, "suspender"); - } -} - static JitActivation* FindSuspendableStackActivation( JSTracer* trc, const SuspenderObjectData& data) { // The jitActivation.refNoCheck() can be used since during trace/marking @@ -210,8 +188,7 @@ static JitActivation* FindSuspendableStackActivation( MOZ_CRASH("Suspendable stack activation not found"); } -static void TraceSuspendableStack(JSTracer* trc, - const SuspenderObjectData& data) { +void TraceSuspendableStack(JSTracer* trc, const SuspenderObjectData& data) { void* exitFP = data.suspendableExitFP(); MOZ_ASSERT(data.traceable()); @@ -245,20 +222,6 @@ static void TraceSuspendableStack(JSTracer* trc, } } -void SuspenderContext::traceRoots(JSTracer* trc) { - // The suspendedStacks_ contains suspended stacks frames that need to be - // traced only during minor GC. The major GC tracing is happening via - // SuspenderObject::trace. - // Non-suspended stack frames are traced as part of TraceJitActivations. - if (!trc->isTenuringTracer()) { - return; - } - gc::AssertRootMarkingPhase(trc); - for (const SuspenderObjectData& data : suspendedStacks_) { - TraceSuspendableStack(trc, data); - } -} - static_assert(JS_STACK_GROWTH_DIRECTION < 0, "JS-PI implemented only for native stacks that grows towards 0"); @@ -274,114 +237,54 @@ static void DecrementSuspendableStacksCount(JSContext* cx) { } } -class SuspenderObject : public NativeObject { - public: - static const JSClass class_; - - enum { DataSlot, PromisingPromiseSlot, SuspendingReturnTypeSlot, SlotCount }; - - enum class ReturnType : int32_t { Unknown, Promise, Exception }; - - static SuspenderObject* create(JSContext* cx) { - for (;;) { - uint32_t currentCount = cx->wasm().suspendableStacksCount; - if (currentCount >= SuspendableStacksMaxCount) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_JSPI_SUSPENDER_LIMIT); - return nullptr; - } - if (cx->wasm().suspendableStacksCount.compareExchange(currentCount, - currentCount + 1)) { - break; - } - // Failed to increment suspendableStacksCount, repeat. - } - - Rooted<SuspenderObject*> suspender( - cx, NewBuiltinClassInstance<SuspenderObject>(cx)); - if (!suspender) { - DecrementSuspendableStacksCount(cx); - return nullptr; - } - - void* stackMemory = js_malloc(SuspendableStackPlusRedZoneSize); - if (!stackMemory) { - DecrementSuspendableStacksCount(cx); - ReportOutOfMemory(cx); +SuspenderObject* SuspenderObject::create(JSContext* cx) { + for (;;) { + uint32_t currentCount = cx->wasm().suspendableStacksCount; + if (currentCount >= SuspendableStacksMaxCount) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_JSPI_SUSPENDER_LIMIT); return nullptr; } - - SuspenderObjectData* data = js_new<SuspenderObjectData>(stackMemory); - if (!data) { - js_free(stackMemory); - DecrementSuspendableStacksCount(cx); - ReportOutOfMemory(cx); - return nullptr; + if (cx->wasm().suspendableStacksCount.compareExchange(currentCount, + currentCount + 1)) { + break; } - MOZ_RELEASE_ASSERT(data->state() != SuspenderState::Moribund); - - suspender->initReservedSlot(DataSlot, PrivateValue(data)); - suspender->initReservedSlot(PromisingPromiseSlot, NullValue()); - suspender->initReservedSlot(SuspendingReturnTypeSlot, - Int32Value(int32_t(ReturnType::Unknown))); - return suspender; - } - - PromiseObject* promisingPromise() const { - return &getReservedSlot(PromisingPromiseSlot) - .toObject() - .as<PromiseObject>(); + // Failed to increment suspendableStacksCount, repeat. } - void setPromisingPromise(Handle<PromiseObject*> promise) { - setReservedSlot(PromisingPromiseSlot, ObjectOrNullValue(promise)); - } - - ReturnType suspendingReturnType() const { - return ReturnType(getReservedSlot(SuspendingReturnTypeSlot).toInt32()); - } - - void setSuspendingReturnType(ReturnType type) { - // The SuspendingReturnTypeSlot will change after result is defined, - // and becomes invalid after GetSuspendingPromiseResult. The assert is - // checking if the result was processed by GetSuspendingPromiseResult. - MOZ_ASSERT((type == ReturnType::Unknown) != - (suspendingReturnType() == ReturnType::Unknown)); - - setReservedSlot(SuspendingReturnTypeSlot, Int32Value(int32_t(type))); + Rooted<SuspenderObject*> suspender( + cx, NewBuiltinClassInstance<SuspenderObject>(cx)); + if (!suspender) { + DecrementSuspendableStacksCount(cx); + return nullptr; } - JS::NativeStackLimit getStackMemoryLimit() { - return JS::NativeStackLimit(data()->stackMemory()) + SuspendableRedZoneSize; + void* stackMemory = js_malloc(SuspendableStackPlusRedZoneSize); + if (!stackMemory) { + DecrementSuspendableStacksCount(cx); + ReportOutOfMemory(cx); + return nullptr; } - SuspenderState state() { return data()->state(); } - - inline bool hasData() { return !getReservedSlot(DataSlot).isUndefined(); } - - inline SuspenderObjectData* data() { - return static_cast<SuspenderObjectData*>( - getReservedSlot(DataSlot).toPrivate()); + SuspenderObjectData* data = js_new<SuspenderObjectData>(stackMemory); + if (!data) { + js_free(stackMemory); + DecrementSuspendableStacksCount(cx); + ReportOutOfMemory(cx); + return nullptr; } + MOZ_RELEASE_ASSERT(data->state() != SuspenderState::Moribund); - void setMoribund(JSContext* cx); - void setActive(JSContext* cx); - void setSuspended(JSContext* cx); - - void enter(JSContext* cx); - void suspend(JSContext* cx); - void resume(JSContext* cx); - void leave(JSContext* cx); - - // Modifies frames to inject the suspendable stack back into the main one. - void forwardToSuspendable(); - - private: - static const JSClassOps classOps_; + suspender->initReservedSlot(DataSlot, PrivateValue(data)); + suspender->initReservedSlot(PromisingPromiseSlot, NullValue()); + suspender->initReservedSlot(SuspendingReturnTypeSlot, + Int32Value(int32_t(ReturnType::Unknown))); + return suspender; +} - static void finalize(JS::GCContext* gcx, JSObject* obj); - static void trace(JSTracer* trc, JSObject* obj); -}; +JS::NativeStackLimit SuspenderObject::getStackMemoryLimit() { + return JS::NativeStackLimit(data()->stackMemory()) + SuspendableRedZoneSize; +} static_assert(SuspenderObjectDataSlot == SuspenderObject::DataSlot); @@ -416,7 +319,7 @@ void SuspenderObject::finalize(JS::GCContext* gcx, JSObject* obj) { } else { // Cleaning stack memory and removing from suspendableStacks_. data->releaseStackMemory(); - if (SuspenderContext* scx = data->suspendedBy()) { + if (Context* scx = data->suspendedBy()) { scx->suspendedStacks_.remove(data); } } @@ -449,9 +352,7 @@ void SuspenderObject::setMoribund(JSContext* cx) { data->setState(SuspenderState::Moribund); data->releaseStackMemory(); DecrementSuspendableStacksCount(cx); - MOZ_ASSERT( - !cx->wasm().promiseIntegration.suspendedStacks_.ElementProbablyInList( - data)); + MOZ_ASSERT(!cx->wasm().suspendedStacks_.ElementProbablyInList(data)); } void SuspenderObject::setActive(JSContext* cx) { @@ -472,7 +373,7 @@ void SuspenderObject::setSuspended(JSContext* cx) { void SuspenderObject::enter(JSContext* cx) { MOZ_ASSERT(state() == SuspenderState::Initial); - cx->wasm().promiseIntegration.setActiveSuspender(this); + cx->wasm().setActiveSuspender(this); setActive(cx); # ifdef DEBUG cx->runtime()->jitRuntime()->disallowArbitraryCode(); @@ -482,9 +383,9 @@ void SuspenderObject::enter(JSContext* cx) { void SuspenderObject::suspend(JSContext* cx) { MOZ_ASSERT(state() == SuspenderState::Active); setSuspended(cx); - cx->wasm().promiseIntegration.suspendedStacks_.pushFront(data()); - data()->setSuspendedBy(&cx->wasm().promiseIntegration); - cx->wasm().promiseIntegration.setActiveSuspender(nullptr); + cx->wasm().suspendedStacks_.pushFront(data()); + data()->setSuspendedBy(&cx->wasm()); + cx->wasm().setActiveSuspender(nullptr); # ifdef DEBUG cx->runtime()->jitRuntime()->clearDisallowArbitraryCode(); # endif @@ -506,13 +407,13 @@ void SuspenderObject::suspend(JSContext* cx) { void SuspenderObject::resume(JSContext* cx) { MOZ_ASSERT(state() == SuspenderState::Suspended); - cx->wasm().promiseIntegration.setActiveSuspender(this); + cx->wasm().setActiveSuspender(this); setActive(cx); data()->setSuspendedBy(nullptr); // Use barrier because object is being removed from the suspendable stack // from roots. gc::PreWriteBarrier(this); - cx->wasm().promiseIntegration.suspendedStacks_.remove(data()); + cx->wasm().suspendedStacks_.remove(data()); # ifdef DEBUG cx->runtime()->jitRuntime()->disallowArbitraryCode(); # endif @@ -534,7 +435,7 @@ void SuspenderObject::resume(JSContext* cx) { } void SuspenderObject::leave(JSContext* cx) { - cx->wasm().promiseIntegration.setActiveSuspender(nullptr); + cx->wasm().setActiveSuspender(nullptr); # ifdef DEBUG cx->runtime()->jitRuntime()->clearDisallowArbitraryCode(); # endif @@ -563,11 +464,10 @@ void SuspenderObject::forwardToSuspendable() { } bool CallOnMainStack(JSContext* cx, CallOnMainStackFn fn, void* data) { - Rooted<SuspenderObject*> suspender( - cx, cx->wasm().promiseIntegration.activeSuspender()); + Rooted<SuspenderObject*> suspender(cx, cx->wasm().activeSuspender()); SuspenderObjectData* stacks = suspender->data(); - cx->wasm().promiseIntegration.setActiveSuspender(nullptr); + cx->wasm().setActiveSuspender(nullptr); MOZ_ASSERT(suspender->state() == SuspenderState::Active); suspender->setSuspended(cx); @@ -906,7 +806,7 @@ bool CallOnMainStack(JSContext* cx, CallOnMainStackFn fn, void* data) { bool ok = (res & 255) != 0; // need only low byte suspender->setActive(cx); - cx->wasm().promiseIntegration.setActiveSuspender(suspender); + cx->wasm().setActiveSuspender(suspender); # undef INLINED_ASM # undef CHECK_OFFSETS @@ -916,9 +816,9 @@ bool CallOnMainStack(JSContext* cx, CallOnMainStackFn fn, void* data) { } static void CleanupActiveSuspender(JSContext* cx) { - SuspenderObject* suspender = cx->wasm().promiseIntegration.activeSuspender(); + SuspenderObject* suspender = cx->wasm().activeSuspender(); MOZ_ASSERT(suspender); - cx->wasm().promiseIntegration.setActiveSuspender(nullptr); + cx->wasm().setActiveSuspender(nullptr); suspender->setMoribund(cx); } @@ -1765,7 +1665,7 @@ static bool WasmPIPromisingFunction(JSContext* cx, unsigned argc, Value* vp) { // During an exception the stack was unwound -- time to release resources. // At this point, the suspender might be null, if that's the case // don't try to clean up. - if (cx->wasm().promiseIntegration.activeSuspender() != nullptr) { + if (cx->wasm().activeSuspender() != nullptr) { CleanupActiveSuspender(cx); } @@ -1845,7 +1745,7 @@ JSFunction* WasmPromisingFunctionCreate(JSContext* cx, HandleObject func, SuspenderObject* CurrentSuspender(Instance* instance, int32_t reserved) { MOZ_ASSERT(SASigCurrentSuspender.failureMode == FailureMode::FailOnNullPtr); JSContext* cx = instance->cx(); - SuspenderObject* suspender = cx->wasm().promiseIntegration.activeSuspender(); + SuspenderObject* suspender = cx->wasm().activeSuspender(); if (!suspender) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_JSPI_INVALID_STATE); diff --git a/js/src/wasm/WasmPI.h b/js/src/wasm/WasmPI.h @@ -22,6 +22,8 @@ #include "mozilla/DoublyLinkedList.h" // for DoublyLinkedListElement #include "js/TypeDecls.h" +#include "vm/NativeObject.h" +#include "vm/PromiseObject.h" #include "wasm/WasmAnyRef.h" #include "wasm/WasmTypeDef.h" @@ -104,13 +106,13 @@ namespace js { -class PromiseObject; class WasmStructObject; namespace wasm { -class SuspenderContext; -class SuspenderObject; +class Context; + +#ifdef ENABLE_WASM_JSPI static const uint32_t SuspenderObjectDataSlot = 0; @@ -149,14 +151,14 @@ class SuspenderObjectData SuspenderState state_; // Identify context that is holding suspended stack, otherwise nullptr. - SuspenderContext* suspendedBy_; + Context* suspendedBy_; -#if defined(_WIN32) +# if defined(_WIN32) // The storage of main stack limits during stack switching. // See updateTibFields and restoreTibFields below. void* savedStackBase_; void* savedStackLimit_; -#endif +# endif public: explicit SuspenderObjectData(void* stackMemory); @@ -169,10 +171,8 @@ class SuspenderObjectData state_ == SuspenderState::Suspended; } inline bool hasStackEntry() const { return suspendedBy_ != nullptr; } - inline SuspenderContext* suspendedBy() const { return suspendedBy_; } - void setSuspendedBy(SuspenderContext* suspendedBy) { - suspendedBy_ = suspendedBy; - } + inline Context* suspendedBy() const { return suspendedBy_; } + void setSuspendedBy(Context* suspendedBy) { suspendedBy_ = suspendedBy; } bool hasFramePointer(void* fp) const { return (uintptr_t)stackMemory_ <= (uintptr_t)fp && @@ -193,17 +193,17 @@ class SuspenderObjectData void releaseStackMemory(); -#if defined(_WIN32) +# if defined(_WIN32) void updateTIBStackFields(); void restoreTIBStackFields(); -#endif +# endif -#if defined(JS_SIMULATOR_ARM64) || defined(JS_SIMULATOR_ARM) || \ - defined(JS_SIMULATOR_RISCV64) || defined(JS_SIMULATOR_LOONG64) || \ - defined(JS_SIMULATOR_MIPS64) +# if defined(JS_SIMULATOR_ARM64) || defined(JS_SIMULATOR_ARM) || \ + defined(JS_SIMULATOR_RISCV64) || defined(JS_SIMULATOR_LOONG64) || \ + defined(JS_SIMULATOR_MIPS64) void switchSimulatorToMain(); void switchSimulatorToSuspendable(); -#endif +# endif static constexpr size_t offsetOfMainFP() { return offsetof(SuspenderObjectData, mainFP_); @@ -234,7 +234,69 @@ class SuspenderObjectData } }; -#ifdef ENABLE_WASM_JSPI +class SuspenderObject : public NativeObject { + public: + static const JSClass class_; + + enum { DataSlot, PromisingPromiseSlot, SuspendingReturnTypeSlot, SlotCount }; + + enum class ReturnType : int32_t { Unknown, Promise, Exception }; + + static SuspenderObject* create(JSContext* cx); + + PromiseObject* promisingPromise() const { + return &getReservedSlot(PromisingPromiseSlot) + .toObject() + .as<PromiseObject>(); + } + + void setPromisingPromise(Handle<PromiseObject*> promise) { + setReservedSlot(PromisingPromiseSlot, ObjectOrNullValue(promise)); + } + + ReturnType suspendingReturnType() const { + return ReturnType(getReservedSlot(SuspendingReturnTypeSlot).toInt32()); + } + + void setSuspendingReturnType(ReturnType type) { + // The SuspendingReturnTypeSlot will change after result is defined, + // and becomes invalid after GetSuspendingPromiseResult. The assert is + // checking if the result was processed by GetSuspendingPromiseResult. + MOZ_ASSERT((type == ReturnType::Unknown) != + (suspendingReturnType() == ReturnType::Unknown)); + + setReservedSlot(SuspendingReturnTypeSlot, Int32Value(int32_t(type))); + } + + JS::NativeStackLimit getStackMemoryLimit(); + + SuspenderState state() { return data()->state(); } + + inline bool hasData() { return !getReservedSlot(DataSlot).isUndefined(); } + + inline SuspenderObjectData* data() { + return static_cast<SuspenderObjectData*>( + getReservedSlot(DataSlot).toPrivate()); + } + + void setMoribund(JSContext* cx); + void setActive(JSContext* cx); + void setSuspended(JSContext* cx); + + void enter(JSContext* cx); + void suspend(JSContext* cx); + void resume(JSContext* cx); + void leave(JSContext* cx); + + // Modifies frames to inject the suspendable stack back into the main one. + void forwardToSuspendable(); + + private: + static const JSClassOps classOps_; + + static void finalize(JS::GCContext* gcx, JSObject* obj); + static void trace(JSTracer* trc, JSObject* obj); +}; using CallOnMainStackFn = bool (*)(void* data); bool CallOnMainStack(JSContext* cx, CallOnMainStackFn fn, void* data); @@ -273,6 +335,8 @@ int32_t SetPromisingPromiseResults(Instance* instance, void UpdateSuspenderState(Instance* instance, SuspenderObject* suspender, UpdateSuspenderStateAction action); +void TraceSuspendableStack(JSTracer* trc, const SuspenderObjectData& data); + #endif // ENABLE_WASM_JSPI } // namespace wasm