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:
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