summaryrefslogtreecommitdiffstats
path: root/js/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/wasm')
-rw-r--r--js/src/wasm/AsmJS.cpp6
-rw-r--r--js/src/wasm/WasmAnyRef.h7
-rw-r--r--js/src/wasm/WasmBCClass.h3
-rw-r--r--js/src/wasm/WasmBaselineCompile.cpp100
-rw-r--r--js/src/wasm/WasmBuiltins.cpp24
-rw-r--r--js/src/wasm/WasmCodegenTypes.h60
-rw-r--r--js/src/wasm/WasmFrame.h27
-rw-r--r--js/src/wasm/WasmGC.h2
-rw-r--r--js/src/wasm/WasmGcObject.cpp31
-rw-r--r--js/src/wasm/WasmInstance.cpp7
-rw-r--r--js/src/wasm/WasmInstance.h2
-rw-r--r--js/src/wasm/WasmJS.cpp25
-rw-r--r--js/src/wasm/WasmModule.cpp4
-rw-r--r--js/src/wasm/WasmStubs.cpp15
-rw-r--r--js/src/wasm/WasmTypeDef.cpp15
-rw-r--r--js/src/wasm/WasmTypeDef.h2
-rw-r--r--js/src/wasm/WasmValidate.cpp43
-rw-r--r--js/src/wasm/WasmValidate.h9
-rw-r--r--js/src/wasm/WasmValue.h6
19 files changed, 227 insertions, 161 deletions
diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
index 11a0c2b23d..1e79880360 100644
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -6914,8 +6914,8 @@ static bool GetImports(JSContext* cx, const AsmJSMetadata& metadata,
return true;
}
-static bool TryInstantiate(JSContext* cx, CallArgs args, const Module& module,
- const AsmJSMetadata& metadata,
+static bool TryInstantiate(JSContext* cx, const CallArgs& args,
+ const Module& module, const AsmJSMetadata& metadata,
MutableHandle<WasmInstanceObject*> instanceObj,
MutableHandleObject exportObj) {
HandleValue globalVal = args.get(0);
@@ -6956,7 +6956,7 @@ static bool TryInstantiate(JSContext* cx, CallArgs args, const Module& module,
return true;
}
-static bool HandleInstantiationFailure(JSContext* cx, CallArgs args,
+static bool HandleInstantiationFailure(JSContext* cx, const CallArgs& args,
const AsmJSMetadata& metadata) {
using js::frontend::FunctionSyntaxKind;
diff --git a/js/src/wasm/WasmAnyRef.h b/js/src/wasm/WasmAnyRef.h
index 1675a9fa8d..cbd3fe75e0 100644
--- a/js/src/wasm/WasmAnyRef.h
+++ b/js/src/wasm/WasmAnyRef.h
@@ -208,16 +208,19 @@ class AnyRef {
// losslessly represent all i31 values.
static AnyRef fromUint32Truncate(uint32_t value) {
// See 64-bit GPRs carrying 32-bit values invariants in MacroAssember.h
-#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM64)
+#if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_X64) || \
+ defined(JS_CODEGEN_ARM64)
// Truncate the value to the 31-bit value size.
uintptr_t wideValue = uintptr_t(value & 0x7FFFFFFF);
#elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \
defined(JS_CODEGEN_RISCV64)
// Sign extend the value to the native pointer size.
uintptr_t wideValue = uintptr_t(int64_t((uint64_t(value) << 33)) >> 33);
-#else
+#elif !defined(JS_64BIT)
// Transfer 32-bit value as is.
uintptr_t wideValue = (uintptr_t)value;
+#else
+# error "unknown architecture"
#endif
// Left shift the value by 1, truncating the high bit.
diff --git a/js/src/wasm/WasmBCClass.h b/js/src/wasm/WasmBCClass.h
index 844ae3381a..52a73d195c 100644
--- a/js/src/wasm/WasmBCClass.h
+++ b/js/src/wasm/WasmBCClass.h
@@ -1402,9 +1402,6 @@ struct BaseCompiler final {
// Used for common setup for catch and catch_all.
void emitCatchSetup(LabelKind kind, Control& tryCatch,
const ResultType& resultType);
- // Helper function used to generate landing pad code for the special
- // case in which `delegate` jumps to a function's body block.
- [[nodiscard]] bool emitBodyDelegateThrowPad();
[[nodiscard]] bool emitTry();
[[nodiscard]] bool emitTryTable();
diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp
index cb0fbde6ec..dbd5bf11d4 100644
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -3780,12 +3780,6 @@ bool BaseCompiler::emitEnd() {
return false;
}
doReturn(ContinuationKind::Fallthrough);
- // This is emitted here after `doReturn` to avoid being executed in the
- // normal return path of a function, and instead only when a `delegate`
- // jumps to it.
- if (!emitBodyDelegateThrowPad()) {
- return false;
- }
iter_.popEnd();
MOZ_ASSERT(iter_.controlStackEmpty());
return iter_.endFunction(iter_.end());
@@ -4108,9 +4102,6 @@ bool BaseCompiler::emitTryTable() {
Label skipLandingPad;
masm.jump(&skipLandingPad);
- // Bind the otherLabel so that delegate can target this
- masm.bind(&controlItem().otherLabel);
-
StackHeight prePadHeight = fr.stackHeight();
uint32_t padOffset = masm.currentOffset();
uint32_t padStackHeight = masm.framePushed();
@@ -4496,30 +4487,6 @@ bool BaseCompiler::emitCatchAll() {
return pushBlockResults(exnResult);
}
-bool BaseCompiler::emitBodyDelegateThrowPad() {
- Control& block = controlItem();
-
- // Only emit a landing pad if a `delegate` has generated a jump to here.
- if (block.otherLabel.used()) {
- StackHeight savedHeight = fr.stackHeight();
- fr.setStackHeight(block.stackHeight);
- masm.bind(&block.otherLabel);
-
- // A try-delegate jumps immediately to its delegated try block, so we are
- // responsible to unpack the exception and rethrow it.
- RegRef exn;
- RegRef tag;
- consumePendingException(RegPtr(InstanceReg), &exn, &tag);
- freeRef(tag);
- if (!throwFrom(exn)) {
- return false;
- }
- fr.setStackHeight(savedHeight);
- }
-
- return true;
-}
-
bool BaseCompiler::emitDelegate() {
uint32_t relativeDepth;
ResultType resultType;
@@ -4529,47 +4496,17 @@ bool BaseCompiler::emitDelegate() {
return false;
}
- Control& tryDelegate = controlItem();
-
- // End the try branch like a plain catch block without exception ref handling.
- if (deadCode_) {
- fr.resetStackHeight(tryDelegate.stackHeight, resultType);
- popValueStackTo(tryDelegate.stackSize);
- } else {
- MOZ_ASSERT(stk_.length() == tryDelegate.stackSize + resultType.length());
- popBlockResults(resultType, tryDelegate.stackHeight,
- ContinuationKind::Jump);
- freeResultRegisters(resultType);
- masm.jump(&tryDelegate.label);
- MOZ_ASSERT(!tryDelegate.deadOnArrival);
+ if (!endBlock(resultType)) {
+ return false;
}
- deadCode_ = tryDelegate.deadOnArrival;
-
- if (deadCode_) {
+ if (controlItem().deadOnArrival) {
return true;
}
- // Create an exception landing pad that immediately branches to the landing
- // pad of the delegated try block.
- masm.bind(&tryDelegate.otherLabel);
-
- StackHeight savedHeight = fr.stackHeight();
- fr.setStackHeight(tryDelegate.stackHeight);
-
// Mark the end of the try body. This may insert a nop.
finishTryNote(controlItem().tryNoteIndex);
- // The landing pad begins at this point
- TryNoteVector& tryNotes = masm.tryNotes();
- TryNote& tryNote = tryNotes[controlItem().tryNoteIndex];
- tryNote.setLandingPad(masm.currentOffset(), masm.framePushed());
-
- // Store the Instance that was left in InstanceReg by the exception
- // handling mechanism, that is this frame's Instance but with the exception
- // filled in Instance::pendingException.
- fr.storeInstancePtr(InstanceReg);
-
// If the target block is a non-try block, skip over it and find the next
// try block or the very last block (to re-throw out of the function).
Control& lastBlock = controlOutermost();
@@ -4579,22 +4516,24 @@ bool BaseCompiler::emitDelegate() {
relativeDepth++;
}
Control& target = controlItem(relativeDepth);
-
- popBlockResults(ResultType::Empty(), target.stackHeight,
- ContinuationKind::Jump);
- masm.jump(&target.otherLabel);
-
- fr.setStackHeight(savedHeight);
-
- // Where the try branch jumps to, if it's not dead.
- if (tryDelegate.label.used()) {
- masm.bind(&tryDelegate.label);
+ TryNoteVector& tryNotes = masm.tryNotes();
+ TryNote& delegateTryNote = tryNotes[controlItem().tryNoteIndex];
+
+ if (&target == &lastBlock) {
+ // A delegate targeting the function body block means that any exception
+ // in this try needs to be propagated to the caller function. We use the
+ // delegate code offset of `0` as that will be in the prologue and cannot
+ // have a try note.
+ delegateTryNote.setDelegate(0);
+ } else {
+ // Delegate to one byte inside the beginning of the target try note, as
+ // that's when matches hit. Try notes are guaranteed to not be empty either
+ // and so this will not miss either.
+ const TryNote& targetTryNote = tryNotes[target.tryNoteIndex];
+ delegateTryNote.setDelegate(targetTryNote.tryBodyBegin() + 1);
}
- captureResultRegisters(resultType);
- bceSafe_ = tryDelegate.bceSafeOnExit;
-
- return pushBlockResults(resultType);
+ return true;
}
bool BaseCompiler::endTryCatch(ResultType type) {
@@ -4634,7 +4573,6 @@ bool BaseCompiler::endTryCatch(ResultType type) {
// Create landing pad for all catch handlers in this block.
// When used for a catchless try block, this will generate a landing pad
// with no handlers and only the fall-back rethrow.
- masm.bind(&tryCatch.otherLabel);
// The stack height also needs to be set not for a block result, but for the
// entry to the exception handlers. This is reset again below for the join.
diff --git a/js/src/wasm/WasmBuiltins.cpp b/js/src/wasm/WasmBuiltins.cpp
index 7b03494bcd..f0773583ac 100644
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -628,6 +628,22 @@ static WasmExceptionObject* GetOrWrapWasmException(JitActivation* activation,
return nullptr;
}
+static const wasm::TryNote* FindNonDelegateTryNote(const wasm::Code& code,
+ const uint8_t* pc,
+ Tier* tier) {
+ const wasm::TryNote* tryNote = code.lookupTryNote((void*)pc, tier);
+ while (tryNote && tryNote->isDelegate()) {
+ const wasm::CodeTier& codeTier = code.codeTier(*tier);
+ pc = codeTier.segment().base() + tryNote->delegateOffset();
+ const wasm::TryNote* delegateTryNote = code.lookupTryNote((void*)pc, tier);
+ MOZ_RELEASE_ASSERT(delegateTryNote == nullptr ||
+ delegateTryNote->tryBodyBegin() <
+ tryNote->tryBodyBegin());
+ tryNote = delegateTryNote;
+ }
+ return tryNote;
+}
+
// Unwind the entire activation in response to a thrown exception. This function
// is responsible for notifying the debugger of each unwound frame. The return
// value is the new stack address which the calling stub will set to the sp
@@ -674,10 +690,10 @@ bool wasm::HandleThrow(JSContext* cx, WasmFrameIter& iter,
// Only look for an exception handler if there's a catchable exception.
if (wasmExn) {
+ Tier tier;
const wasm::Code& code = iter.instance()->code();
const uint8_t* pc = iter.resumePCinCurrentFrame();
- Tier tier;
- const wasm::TryNote* tryNote = code.lookupTryNote((void*)pc, &tier);
+ const wasm::TryNote* tryNote = FindNonDelegateTryNote(code, pc, &tier);
if (tryNote) {
#ifdef ENABLE_WASM_TAIL_CALLS
@@ -751,8 +767,8 @@ bool wasm::HandleThrow(JSContext* cx, WasmFrameIter& iter,
// Assert that any pending exception escaping to non-wasm code is not a
// wrapper exception object
#ifdef DEBUG
- Rooted<Value> pendingException(cx);
- if (cx->isExceptionPending() && cx->getPendingException(&pendingException)) {
+ if (cx->isExceptionPending()) {
+ Rooted<Value> pendingException(cx, cx->getPendingExceptionUnwrapped());
MOZ_ASSERT_IF(pendingException.isObject() &&
pendingException.toObject().is<WasmExceptionObject>(),
!pendingException.toObject()
diff --git a/js/src/wasm/WasmCodegenTypes.h b/js/src/wasm/WasmCodegenTypes.h
index 590572ae8a..7e54ad15f7 100644
--- a/js/src/wasm/WasmCodegenTypes.h
+++ b/js/src/wasm/WasmCodegenTypes.h
@@ -671,20 +671,30 @@ struct TryNote {
// Sentinel value to detect a try note that has not been given a try body.
static const uint32_t BEGIN_NONE = UINT32_MAX;
+ // Sentinel value used in `entryPointOrIsDelegate_`.
+ static const uint32_t IS_DELEGATE = UINT32_MAX;
+
// Begin code offset of the try body.
uint32_t begin_;
// Exclusive end code offset of the try body.
uint32_t end_;
- // The code offset of the landing pad.
- uint32_t entryPoint_;
- // Track offset from frame of stack pointer.
- uint32_t framePushed_;
+ // Either a marker that this is a 'delegate' or else the code offset of the
+ // landing pad to jump to.
+ uint32_t entryPointOrIsDelegate_;
+ // If this is a delegate, then this is the code offset to delegate to,
+ // otherwise this is the offset from the frame pointer of the stack pointer
+ // to use when jumping to the landing pad.
+ uint32_t framePushedOrDelegateOffset_;
- WASM_CHECK_CACHEABLE_POD(begin_, end_, entryPoint_, framePushed_);
+ WASM_CHECK_CACHEABLE_POD(begin_, end_, entryPointOrIsDelegate_,
+ framePushedOrDelegateOffset_);
public:
explicit TryNote()
- : begin_(BEGIN_NONE), end_(0), entryPoint_(0), framePushed_(0) {}
+ : begin_(BEGIN_NONE),
+ end_(0),
+ entryPointOrIsDelegate_(0),
+ framePushedOrDelegateOffset_(0) {}
// Returns whether a try note has been assigned a range for the try body.
bool hasTryBody() const { return begin_ != BEGIN_NONE; }
@@ -700,11 +710,27 @@ struct TryNote {
return offset > begin_ && offset <= end_;
}
+ // Check if the unwinder should delegate the handling of this try note to the
+ // try note given at the delegate offset.
+ bool isDelegate() const { return entryPointOrIsDelegate_ == IS_DELEGATE; }
+
+ // The code offset to delegate the handling of this try note to.
+ uint32_t delegateOffset() const {
+ MOZ_ASSERT(isDelegate());
+ return framePushedOrDelegateOffset_;
+ }
+
// The code offset of the entry to the landing pad.
- uint32_t landingPadEntryPoint() const { return entryPoint_; }
+ uint32_t landingPadEntryPoint() const {
+ MOZ_ASSERT(!isDelegate());
+ return entryPointOrIsDelegate_;
+ }
// The stack frame pushed amount at the entry to the landing pad.
- uint32_t landingPadFramePushed() const { return framePushed_; }
+ uint32_t landingPadFramePushed() const {
+ MOZ_ASSERT(!isDelegate());
+ return framePushedOrDelegateOffset_;
+ }
// Set the beginning of the try body.
void setTryBodyBegin(uint32_t begin) {
@@ -722,17 +748,29 @@ struct TryNote {
MOZ_ASSERT(end_ > begin_);
}
+ // Mark this try note as a delegate, requesting the unwinder to use the try
+ // note found at the delegate offset.
+ void setDelegate(uint32_t delegateOffset) {
+ entryPointOrIsDelegate_ = IS_DELEGATE;
+ framePushedOrDelegateOffset_ = delegateOffset;
+ }
+
// Set the entry point and frame pushed of the landing pad.
void setLandingPad(uint32_t entryPoint, uint32_t framePushed) {
- entryPoint_ = entryPoint;
- framePushed_ = framePushed;
+ MOZ_ASSERT(!isDelegate());
+ entryPointOrIsDelegate_ = entryPoint;
+ framePushedOrDelegateOffset_ = framePushed;
}
// Adjust all code offsets in this try note by a delta.
void offsetBy(uint32_t offset) {
begin_ += offset;
end_ += offset;
- entryPoint_ += offset;
+ if (isDelegate()) {
+ framePushedOrDelegateOffset_ += offset;
+ } else {
+ entryPointOrIsDelegate_ += offset;
+ }
}
bool operator<(const TryNote& other) const {
diff --git a/js/src/wasm/WasmFrame.h b/js/src/wasm/WasmFrame.h
index 23e1c4f49c..a2fdba2b75 100644
--- a/js/src/wasm/WasmFrame.h
+++ b/js/src/wasm/WasmFrame.h
@@ -346,14 +346,16 @@ static_assert(!std::is_polymorphic_v<Frame>, "Frame doesn't need a vtable.");
static_assert(sizeof(Frame) == 2 * sizeof(void*),
"Frame is a two pointer structure");
-// Note that sizeof(FrameWithInstances) does not account for ShadowStackSpace.
-// Use FrameWithInstances::sizeOf() if you are not incorporating
-// ShadowStackSpace through other means (eg the ABIArgIter).
-
-class FrameWithInstances : public Frame {
+class FrameWithShadowStackSpace : public Frame {
+ protected:
// `ShadowStackSpace` bytes will be allocated here on Win64, at higher
// addresses than Frame and at lower addresses than the instance fields.
+ uint8_t shadowStackSpace_[js::jit::ShadowStackSpace];
+};
+class FrameWithInstances
+ : public std::conditional_t<js::jit::ShadowStackSpace >= 1,
+ FrameWithShadowStackSpace, Frame> {
// The instance area MUST be two pointers exactly.
Instance* calleeInstance_;
Instance* callerInstance_;
@@ -362,21 +364,17 @@ class FrameWithInstances : public Frame {
Instance* calleeInstance() { return calleeInstance_; }
Instance* callerInstance() { return callerInstance_; }
- constexpr static uint32_t sizeOf() {
- return sizeof(wasm::FrameWithInstances) + js::jit::ShadowStackSpace;
- }
-
constexpr static uint32_t sizeOfInstanceFields() {
- return sizeof(wasm::FrameWithInstances) - sizeof(wasm::Frame);
+ return sizeof(wasm::FrameWithInstances) - sizeof(wasm::Frame) -
+ js::jit::ShadowStackSpace;
}
constexpr static uint32_t sizeOfInstanceFieldsAndShadowStack() {
- return sizeOfInstanceFields() + js::jit::ShadowStackSpace;
+ return sizeof(wasm::FrameWithInstances) - sizeof(wasm::Frame);
}
constexpr static uint32_t calleeInstanceOffset() {
- return offsetof(FrameWithInstances, calleeInstance_) +
- js::jit::ShadowStackSpace;
+ return offsetof(FrameWithInstances, calleeInstance_);
}
constexpr static uint32_t calleeInstanceOffsetWithoutFrame() {
@@ -384,8 +382,7 @@ class FrameWithInstances : public Frame {
}
constexpr static uint32_t callerInstanceOffset() {
- return offsetof(FrameWithInstances, callerInstance_) +
- js::jit::ShadowStackSpace;
+ return offsetof(FrameWithInstances, callerInstance_);
}
constexpr static uint32_t callerInstanceOffsetWithoutFrame() {
diff --git a/js/src/wasm/WasmGC.h b/js/src/wasm/WasmGC.h
index b502f2f40e..57e823b3c5 100644
--- a/js/src/wasm/WasmGC.h
+++ b/js/src/wasm/WasmGC.h
@@ -91,7 +91,7 @@ struct StackMapHeader {
// Add 16 words to account for the size of FrameWithInstances including any
// shadow stack (at worst 8 words total), and then a little headroom in case
// the argument area had to be aligned.
- static_assert(FrameWithInstances::sizeOf() / sizeof(void*) <= 8);
+ static_assert(sizeof(FrameWithInstances) / sizeof(void*) <= 8);
static_assert(maxFrameOffsetFromTop >=
(MaxParams * MaxParamSize / sizeof(void*)) + 16,
"limited size of the offset field");
diff --git a/js/src/wasm/WasmGcObject.cpp b/js/src/wasm/WasmGcObject.cpp
index bcab5fe275..3ccb08d381 100644
--- a/js/src/wasm/WasmGcObject.cpp
+++ b/js/src/wasm/WasmGcObject.cpp
@@ -410,8 +410,6 @@ void WasmArrayObject::obj_finalize(JS::GCContext* gcx, JSObject* object) {
/* static */
size_t WasmArrayObject::obj_moved(JSObject* obj, JSObject* old) {
- MOZ_ASSERT(!IsInsideNursery(obj));
-
// Moving inline arrays requires us to update the data pointer.
WasmArrayObject& arrayObj = obj->as<WasmArrayObject>();
WasmArrayObject& oldArrayObj = old->as<WasmArrayObject>();
@@ -423,24 +421,18 @@ size_t WasmArrayObject::obj_moved(JSObject* obj, JSObject* old) {
MOZ_ASSERT(arrayObj.isDataInline() == oldArrayObj.isDataInline());
if (IsInsideNursery(old)) {
+ Nursery& nursery = obj->runtimeFromMainThread()->gc.nursery();
// It's been tenured.
- MOZ_ASSERT(obj->isTenured());
if (!arrayObj.isDataInline()) {
- // Tell the nursery that the trailer is no longer associated with an
- // object in the nursery, since the object has been moved to the tenured
- // heap.
- Nursery& nursery = obj->runtimeFromMainThread()->gc.nursery();
- nursery.unregisterTrailer(arrayObj.dataHeader());
- // Tell the tenured-heap accounting machinery that the trailer is now
- // associated with the tenured heap.
const TypeDef& typeDef = arrayObj.typeDef();
MOZ_ASSERT(typeDef.isArrayType());
size_t trailerSize = calcStorageBytes(
typeDef.arrayType().elementType_.size(), arrayObj.numElements_);
// Ensured by WasmArrayObject::createArrayOOL.
MOZ_RELEASE_ASSERT(trailerSize <= size_t(MaxArrayPayloadBytes));
- AddCellMemory(&arrayObj, trailerSize + TrailerBlockOverhead,
- MemoryUse::WasmTrailerBlock);
+ nursery.trackTrailerOnPromotion(arrayObj.dataHeader(), obj, trailerSize,
+ TrailerBlockOverhead,
+ MemoryUse::WasmTrailerBlock);
}
}
@@ -563,16 +555,9 @@ void WasmStructObject::obj_finalize(JS::GCContext* gcx, JSObject* object) {
/* static */
size_t WasmStructObject::obj_moved(JSObject* obj, JSObject* old) {
// See also, corresponding comments in WasmArrayObject::obj_moved.
- MOZ_ASSERT(!IsInsideNursery(obj));
if (IsInsideNursery(old)) {
- // It's been tenured.
- MOZ_ASSERT(obj->isTenured());
- WasmStructObject& structObj = obj->as<WasmStructObject>();
- // WasmStructObject::classForTypeDef ensures we only get called for
- // structs with OOL data. Hence:
- MOZ_ASSERT(structObj.outlineData_);
Nursery& nursery = obj->runtimeFromMainThread()->gc.nursery();
- nursery.unregisterTrailer(structObj.outlineData_);
+ WasmStructObject& structObj = obj->as<WasmStructObject>();
const TypeDef& typeDef = structObj.typeDef();
MOZ_ASSERT(typeDef.isStructType());
uint32_t totalBytes = typeDef.structType().size_;
@@ -580,9 +565,11 @@ size_t WasmStructObject::obj_moved(JSObject* obj, JSObject* old) {
WasmStructObject::getDataByteSizes(totalBytes, &inlineBytes, &outlineBytes);
MOZ_ASSERT(inlineBytes == WasmStructObject_MaxInlineBytes);
MOZ_ASSERT(outlineBytes > 0);
- AddCellMemory(&structObj, outlineBytes + TrailerBlockOverhead,
- MemoryUse::WasmTrailerBlock);
+ nursery.trackTrailerOnPromotion(structObj.outlineData_, obj, outlineBytes,
+ TrailerBlockOverhead,
+ MemoryUse::WasmTrailerBlock);
}
+
return 0;
}
diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp
index d025c02c16..606601581d 100644
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -2937,7 +2937,8 @@ static bool EnsureEntryStubs(const Instance& instance, uint32_t funcIndex,
}
static bool GetInterpEntryAndEnsureStubs(JSContext* cx, Instance& instance,
- uint32_t funcIndex, CallArgs args,
+ uint32_t funcIndex,
+ const CallArgs& args,
void** interpEntry,
const FuncType** funcType) {
const FuncExport* funcExport;
@@ -3099,8 +3100,8 @@ class MOZ_RAII ReturnToJSResultCollector {
}
};
-bool Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args,
- CoercionLevel level) {
+bool Instance::callExport(JSContext* cx, uint32_t funcIndex,
+ const CallArgs& args, CoercionLevel level) {
if (memory0Base_) {
// If there has been a moving grow, this Instance should have been notified.
MOZ_RELEASE_ASSERT(memoryBase(0).unwrap() == memory0Base_);
diff --git a/js/src/wasm/WasmInstance.h b/js/src/wasm/WasmInstance.h
index 074c6212df..0e4f9745b7 100644
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -364,7 +364,7 @@ class alignas(16) Instance {
// value in args.rval.
[[nodiscard]] bool callExport(JSContext* cx, uint32_t funcIndex,
- CallArgs args,
+ const CallArgs& args,
CoercionLevel level = CoercionLevel::Spec);
// Exception handling support
diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp
index 2eb5e355d9..d987ecec29 100644
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -123,6 +123,10 @@ static bool ThrowBadImportArg(JSContext* cx) {
static bool ThrowBadImportType(JSContext* cx, const CacheableName& field,
const char* str) {
UniqueChars fieldQuoted = field.toQuotedString(cx);
+ if (!fieldQuoted) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_IMPORT_TYPE, fieldQuoted.get(), str);
return false;
@@ -178,6 +182,10 @@ bool js::wasm::GetImports(JSContext* cx, const Module& module,
if (!importModuleValue.isObject()) {
UniqueChars moduleQuoted = import.module.toQuotedString(cx);
+ if (!moduleQuoted) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_IMPORT_FIELD,
moduleQuoted.get());
@@ -256,6 +264,10 @@ bool js::wasm::GetImports(JSContext* cx, const Module& module,
if (obj->resultType() != tags[index].type->resultType()) {
UniqueChars fieldQuoted = import.field.toQuotedString(cx);
UniqueChars moduleQuoted = import.module.toQuotedString(cx);
+ if (!fieldQuoted || !moduleQuoted) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_TAG_SIG, moduleQuoted.get(),
fieldQuoted.get());
@@ -1005,8 +1017,9 @@ static bool IsModuleObject(JSObject* obj, const Module** module) {
return true;
}
-static bool GetModuleArg(JSContext* cx, CallArgs args, uint32_t numRequired,
- const char* name, const Module** module) {
+static bool GetModuleArg(JSContext* cx, const CallArgs& args,
+ uint32_t numRequired, const char* name,
+ const Module** module) {
if (!args.requireAtLeast(cx, name, numRequired)) {
return false;
}
@@ -4514,8 +4527,8 @@ static bool EnsurePromiseSupport(JSContext* cx) {
return true;
}
-static bool GetBufferSource(JSContext* cx, CallArgs callArgs, const char* name,
- MutableBytes* bytecode) {
+static bool GetBufferSource(JSContext* cx, const CallArgs& callArgs,
+ const char* name, MutableBytes* bytecode) {
if (!callArgs.requireAtLeast(cx, name, 1)) {
return false;
}
@@ -4576,7 +4589,7 @@ static bool WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
-static bool GetInstantiateArgs(JSContext* cx, CallArgs callArgs,
+static bool GetInstantiateArgs(JSContext* cx, const CallArgs& callArgs,
MutableHandleObject firstArg,
MutableHandleObject importObj,
MutableHandleValue featureOptions) {
@@ -5089,7 +5102,7 @@ const JSClass ResolveResponseClosure::class_ = {
&ResolveResponseClosure::classOps_,
};
-static ResolveResponseClosure* ToResolveResponseClosure(CallArgs args) {
+static ResolveResponseClosure* ToResolveResponseClosure(const CallArgs& args) {
return &args.callee()
.as<JSFunction>()
.getExtendedSlot(0)
diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp
index a297e81ad3..406c16462b 100644
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -486,6 +486,10 @@ bool Module::instantiateFunctions(JSContext* cx,
const Import& import = FindImportFunction(imports_, i);
UniqueChars importModuleName = import.module.toQuotedString(cx);
UniqueChars importFieldName = import.field.toQuotedString(cx);
+ if (!importFieldName || !importModuleName) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_IMPORT_SIG,
importModuleName.get(), importFieldName.get());
diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp
index 83a18c9992..dfaa898744 100644
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -1633,9 +1633,10 @@ static void FillArgumentArrayForInterpExit(MacroAssembler& masm,
const FuncType& funcType,
unsigned argOffset,
Register scratch) {
- // This is FrameWithInstances::sizeOf() - ShadowStackSpace because the latter
+ // This is `sizeof(FrameWithInstances) - ShadowStackSpace` because the latter
// is accounted for by the ABIArgIter.
- const unsigned offsetFromFPToCallerStackArgs = sizeof(FrameWithInstances);
+ const unsigned offsetFromFPToCallerStackArgs =
+ sizeof(FrameWithInstances) - jit::ShadowStackSpace;
GenPrintf(DebugChannel::Import, masm, "wasm-import[%u]; arguments ",
funcImportIndex);
@@ -1729,9 +1730,10 @@ static void FillArgumentArrayForJitExit(MacroAssembler& masm, Register instance,
Register scratch2, Label* throwLabel) {
MOZ_ASSERT(scratch != scratch2);
- // This is FrameWithInstances::sizeOf() - ShadowStackSpace because the latter
+ // This is `sizeof(FrameWithInstances) - ShadowStackSpace` because the latter
// is accounted for by the ABIArgIter.
- const unsigned offsetFromFPToCallerStackArgs = sizeof(FrameWithInstances);
+ const unsigned offsetFromFPToCallerStackArgs =
+ sizeof(FrameWithInstances) - jit::ShadowStackSpace;
// This loop does not root the values that are being constructed in
// for the arguments. Allocations that are generated by code either
@@ -2473,9 +2475,10 @@ bool wasm::GenerateBuiltinThunk(MacroAssembler& masm, ABIFunctionType abiType,
// Copy out and convert caller arguments, if needed.
- // This is FrameWithInstances::sizeOf() - ShadowStackSpace because the latter
+ // This is `sizeof(FrameWithInstances) - ShadowStackSpace` because the latter
// is accounted for by the ABIArgIter.
- unsigned offsetFromFPToCallerStackArgs = sizeof(FrameWithInstances);
+ unsigned offsetFromFPToCallerStackArgs =
+ sizeof(FrameWithInstances) - jit::ShadowStackSpace;
Register scratch = ABINonArgReturnReg0;
for (ABIArgIter i(args); !i.done(); i++) {
if (i->argInRegister()) {
diff --git a/js/src/wasm/WasmTypeDef.cpp b/js/src/wasm/WasmTypeDef.cpp
index 42367c0cb2..ee005681c5 100644
--- a/js/src/wasm/WasmTypeDef.cpp
+++ b/js/src/wasm/WasmTypeDef.cpp
@@ -364,6 +364,21 @@ bool StructType::init() {
return true;
}
+/* static */
+bool StructType::createImmutable(const ValTypeVector& types,
+ StructType* struct_) {
+ StructFieldVector fields;
+ if (!fields.resize(types.length())) {
+ return false;
+ }
+ for (size_t i = 0; i < types.length(); i++) {
+ fields[i].type = StorageType(types[i].packed());
+ fields[i].isMutable = false;
+ }
+ *struct_ = StructType(std::move(fields));
+ return struct_->init();
+}
+
size_t StructType::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
return fields_.sizeOfExcludingThis(mallocSizeOf);
}
diff --git a/js/src/wasm/WasmTypeDef.h b/js/src/wasm/WasmTypeDef.h
index 3426647095..a0d44e647b 100644
--- a/js/src/wasm/WasmTypeDef.h
+++ b/js/src/wasm/WasmTypeDef.h
@@ -371,6 +371,8 @@ class StructType {
return true;
}
+ static bool createImmutable(const ValTypeVector& types, StructType* struct_);
+
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
WASM_DECLARE_FRIEND_SERIALIZE(StructType);
};
diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp
index 98a1423a41..d67967fa41 100644
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -40,6 +40,49 @@ using mozilla::CheckedInt32;
using mozilla::IsUtf8;
using mozilla::Span;
+// Module environment helpers.
+
+bool ModuleEnvironment::addDefinedFunc(
+ ValTypeVector&& params, ValTypeVector&& results, bool declareForRef,
+ Maybe<CacheableName>&& optionalExportedName) {
+ uint32_t typeIndex = types->length();
+ FuncType funcType(std::move(params), std::move(results));
+ if (!types->addType(std::move(funcType))) {
+ return false;
+ }
+
+ FuncDesc funcDesc = FuncDesc(&(*types)[typeIndex].funcType(), typeIndex);
+ uint32_t funcIndex = funcs.length();
+ if (!funcs.append(funcDesc)) {
+ return false;
+ }
+ if (declareForRef) {
+ declareFuncExported(funcIndex, true, true);
+ }
+ if (optionalExportedName.isSome()) {
+ if (!exports.emplaceBack(std::move(optionalExportedName.ref()), funcIndex,
+ DefinitionKind::Function)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ModuleEnvironment::addImportedFunc(ValTypeVector&& params,
+ ValTypeVector&& results,
+ CacheableName&& importModName,
+ CacheableName&& importFieldName) {
+ MOZ_ASSERT(numFuncImports == funcs.length());
+ if (!addDefinedFunc(std::move(params), std::move(results), false,
+ mozilla::Nothing())) {
+ return false;
+ }
+ numFuncImports++;
+ return imports.emplaceBack(std::move(importModName),
+ std::move(importFieldName),
+ DefinitionKind::Function);
+}
+
// Misc helpers.
bool wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals) {
diff --git a/js/src/wasm/WasmValidate.h b/js/src/wasm/WasmValidate.h
index 8ba08fd088..f8d712b3b3 100644
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -191,6 +191,15 @@ struct ModuleEnvironment {
MOZ_ASSERT(tagIndex < tags.length());
return tagsOffsetStart + tagIndex * sizeof(TagInstanceData);
}
+
+ bool addDefinedFunc(
+ ValTypeVector&& params, ValTypeVector&& results,
+ bool declareForRef = false,
+ Maybe<CacheableName>&& optionalExportedName = mozilla::Nothing());
+
+ bool addImportedFunc(ValTypeVector&& params, ValTypeVector&& results,
+ CacheableName&& importModName,
+ CacheableName&& importFieldName);
};
// ElemSegmentFlags provides methods for decoding and encoding the flags field
diff --git a/js/src/wasm/WasmValue.h b/js/src/wasm/WasmValue.h
index 79e20285b9..9a5442fc75 100644
--- a/js/src/wasm/WasmValue.h
+++ b/js/src/wasm/WasmValue.h
@@ -224,9 +224,9 @@ class LitVal {
Cell& cell() { return cell_; }
const Cell& cell() const { return cell_; }
- // Updates the type of the LitVal. Does not check that the type is valid for the
- // actual value, so make sure the type is definitely correct via validation or
- // something.
+ // Updates the type of the LitVal. Does not check that the type is valid for
+ // the actual value, so make sure the type is definitely correct via
+ // validation or something.
void unsafeSetType(ValType type) { type_ = type; }
uint32_t i32() const {