diff options
Diffstat (limited to 'js/src/wasm')
-rw-r--r-- | js/src/wasm/AsmJS.cpp | 6 | ||||
-rw-r--r-- | js/src/wasm/WasmAnyRef.h | 7 | ||||
-rw-r--r-- | js/src/wasm/WasmBCClass.h | 3 | ||||
-rw-r--r-- | js/src/wasm/WasmBaselineCompile.cpp | 100 | ||||
-rw-r--r-- | js/src/wasm/WasmBuiltins.cpp | 24 | ||||
-rw-r--r-- | js/src/wasm/WasmCodegenTypes.h | 60 | ||||
-rw-r--r-- | js/src/wasm/WasmFrame.h | 27 | ||||
-rw-r--r-- | js/src/wasm/WasmGC.h | 2 | ||||
-rw-r--r-- | js/src/wasm/WasmGcObject.cpp | 31 | ||||
-rw-r--r-- | js/src/wasm/WasmInstance.cpp | 7 | ||||
-rw-r--r-- | js/src/wasm/WasmInstance.h | 2 | ||||
-rw-r--r-- | js/src/wasm/WasmJS.cpp | 25 | ||||
-rw-r--r-- | js/src/wasm/WasmModule.cpp | 4 | ||||
-rw-r--r-- | js/src/wasm/WasmStubs.cpp | 15 | ||||
-rw-r--r-- | js/src/wasm/WasmTypeDef.cpp | 15 | ||||
-rw-r--r-- | js/src/wasm/WasmTypeDef.h | 2 | ||||
-rw-r--r-- | js/src/wasm/WasmValidate.cpp | 43 | ||||
-rw-r--r-- | js/src/wasm/WasmValidate.h | 9 | ||||
-rw-r--r-- | js/src/wasm/WasmValue.h | 6 |
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 { |