WebKit Bugzilla
Attachment 372257 Details for
Bug 198157
: [WASM-References] Add support for Funcref in parameters and return types
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-198157-20190617105948.patch (text/plain), 91.45 KB, created by
Justin Michaud
on 2019-06-17 10:59:49 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Justin Michaud
Created:
2019-06-17 10:59:49 PDT
Size:
91.45 KB
patch
obsolete
>Subversion Revision: 246498 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index b74032480082f43d2f7bb2a9977ec944b05f9bb2..9fe06278712a412f024a36b6bb846be262e52692 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,86 @@ >+2019-06-10 Justin Michaud <justin_michaud@apple.com> >+ >+ [WASM-References] Add support for Funcref in parameters and return types >+ https://bugs.webkit.org/show_bug.cgi?id=198157 >+ >+ Reviewed by Yusuke Suzuki. >+ >+ Add support for funcref in parameters, globals, and in table.get/set. When converting a JSValue to >+ a funcref (nee anyfunc), we first make sure it is an exported wasm function or null. >+ >+ We also add support for Ref.func. Anywhere a Ref.func is used, (statically) we construct a JS wrapper >+ for it so that we never need to construct JSValues when handling references. This should make threads >+ easier to implement. >+ >+ Finally, we add some missing bounds checks for table.get/set. >+ >+ * wasm/WasmAirIRGenerator.cpp: >+ (JSC::Wasm::AirIRGenerator::tmpForType): >+ (JSC::Wasm::AirIRGenerator::moveOpForValueType): >+ (JSC::Wasm::AirIRGenerator::AirIRGenerator): >+ (JSC::Wasm::AirIRGenerator::addLocal): >+ (JSC::Wasm::AirIRGenerator::addConstant): >+ (JSC::Wasm::AirIRGenerator::addRefFunc): >+ (JSC::Wasm::AirIRGenerator::addTableSet): >+ (JSC::Wasm::AirIRGenerator::setGlobal): >+ (JSC::Wasm::AirIRGenerator::addReturn): >+ * wasm/WasmB3IRGenerator.cpp: >+ (JSC::Wasm::B3IRGenerator::addLocal): >+ (JSC::Wasm::B3IRGenerator::addTableSet): >+ (JSC::Wasm::B3IRGenerator::addRefFunc): >+ (JSC::Wasm::B3IRGenerator::setGlobal): >+ * wasm/WasmBBQPlan.cpp: >+ (JSC::Wasm::BBQPlan::compileFunctions): >+ * wasm/WasmCallingConvention.h: >+ (JSC::Wasm::CallingConventionAir::marshallArgument const): >+ (JSC::Wasm::CallingConventionAir::setupCall const): >+ * wasm/WasmExceptionType.h: >+ * wasm/WasmFormat.h: >+ (JSC::Wasm::isValueType): >+ (JSC::Wasm::isSubtype): >+ * wasm/WasmFunctionParser.h: >+ (JSC::Wasm::FunctionParser<Context>::parseExpression): >+ (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression): >+ * wasm/WasmInstance.cpp: >+ (JSC::Wasm::Instance::Instance): >+ (JSC::Wasm::Instance::getFunctionWrapper const): >+ (JSC::Wasm::Instance::setFunctionWrapper): >+ * wasm/WasmInstance.h: >+ * wasm/WasmModuleInformation.h: >+ (JSC::Wasm::ModuleInformation::referencedFunctions const): >+ (JSC::Wasm::ModuleInformation::addReferencedFunction const): >+ * wasm/WasmSectionParser.cpp: >+ (JSC::Wasm::SectionParser::parseGlobal): >+ (JSC::Wasm::SectionParser::parseInitExpr): >+ * wasm/WasmValidate.cpp: >+ (JSC::Wasm::Validate::addTableGet): >+ (JSC::Wasm::Validate::addTableSet): >+ (JSC::Wasm::Validate::addRefIsNull): >+ (JSC::Wasm::Validate::addRefFunc): >+ (JSC::Wasm::Validate::setLocal): >+ (JSC::Wasm::Validate::addCall): >+ (JSC::Wasm::Validate::addCallIndirect): >+ * wasm/js/JSToWasm.cpp: >+ (JSC::Wasm::createJSToWasmWrapper): >+ * wasm/js/JSWebAssemblyHelpers.h: >+ (JSC::isWebAssemblyHostFunction): >+ * wasm/js/JSWebAssemblyInstance.cpp: >+ (JSC::JSWebAssemblyInstance::visitChildren): >+ * wasm/js/JSWebAssemblyRuntimeError.cpp: >+ (JSC::createJSWebAssemblyRuntimeError): >+ * wasm/js/JSWebAssemblyRuntimeError.h: >+ * wasm/js/WasmToJS.cpp: >+ (JSC::Wasm::handleBadI64Use): >+ (JSC::Wasm::wasmToJS): >+ (JSC::Wasm::emitWasmToJSException): >+ * wasm/js/WasmToJS.h: >+ * wasm/js/WebAssemblyFunction.cpp: >+ (JSC::callWebAssemblyFunction): >+ (JSC::WebAssemblyFunction::jsCallEntrypointSlow): >+ * wasm/js/WebAssemblyModuleRecord.cpp: >+ (JSC::WebAssemblyModuleRecord::link): >+ * wasm/wasm.json: >+ > 2019-06-16 Darin Adler <darin@apple.com> > > Rename AtomicString to AtomString >diff --git a/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp b/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >index adb33f5404780b54d1a9e8111249878c37c5ae73..ab304a55d36b358422b1a07d2a44b000df522de7 100644 >--- a/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >+++ b/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >@@ -234,10 +234,11 @@ public: > > // References > PartialResult WARN_UNUSED_RETURN addRefIsNull(ExpressionType& value, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result); > > // Tables >- PartialResult WARN_UNUSED_RETURN addTableGet(ExpressionType& idx, ExpressionType& result); >- PartialResult WARN_UNUSED_RETURN addTableSet(ExpressionType& idx, ExpressionType& value); >+ PartialResult WARN_UNUSED_RETURN addTableGet(ExpressionType& index, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addTableSet(ExpressionType& index, ExpressionType& value); > > // Locals > PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); >@@ -370,6 +371,7 @@ private: > return g32(); > case Type::I64: > case Type::Anyref: >+ case Type::Anyfunc: > return g64(); > case Type::F32: > return f32(); >@@ -554,6 +556,7 @@ private: > return Move32; > case Type::I64: > case Type::Anyref: >+ case Type::Anyfunc: > return Move; > case Type::F32: > return MoveFloat; >@@ -799,6 +802,7 @@ AirIRGenerator::AirIRGenerator(const ModuleInformation& info, B3::Procedure& pro > break; > case Type::I64: > case Type::Anyref: >+ case Type::Anyfunc: > append(Move, arg, m_locals[i]); > break; > case Type::F32: >@@ -884,6 +888,7 @@ auto AirIRGenerator::addLocal(Type type, uint32_t count) -> PartialResult > m_locals.uncheckedAppend(local); > switch (type) { > case Type::Anyref: >+ case Type::Anyfunc: > append(Move, Arg::imm(JSValue::encode(jsNull())), local); > break; > case Type::I32: >@@ -918,6 +923,7 @@ auto AirIRGenerator::addConstant(BasicBlock* block, Type type, uint64_t value) - > case Type::I32: > case Type::I64: > case Type::Anyref: >+ case Type::Anyfunc: > append(block, Move, Arg::bigImm(value), result); > break; > case Type::F32: >@@ -953,36 +959,47 @@ auto AirIRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result) > return { }; > } > >-auto AirIRGenerator::addTableGet(ExpressionType& idx, ExpressionType& result) -> PartialResult >+auto AirIRGenerator::addRefFunc(uint32_t index, ExpressionType& result) -> PartialResult > { > // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >- ASSERT(idx.tmp()); >- ASSERT(idx.type() == Type::I32); >- result = tmpForType(Type::Anyref); >+ result = tmpForType(Type::Anyfunc); >+ emitCCall(&doWasmRefFunc, result, instanceValue(), addConstant(Type::I32, index)); > >- uint64_t (*doGet)(Instance*, int32_t) = [] (Instance* instance, int32_t idx) -> uint64_t { >- return JSValue::encode(instance->table()->get(idx)); >- }; >+ return { }; >+} > >- emitCCall(doGet, result, instanceValue(), idx); >+auto AirIRGenerator::addTableGet(ExpressionType& index, ExpressionType& result) -> PartialResult >+{ >+ // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >+ ASSERT(index.tmp()); >+ ASSERT(index.type() == Type::I32); >+ result = tmpForType(Type::Anyref); >+ >+ emitCCall(&getWasmTableElement, result, instanceValue(), index); >+ emitCheck([&] { >+ return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::Zero), result, result); >+ }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { >+ this->emitThrowException(jit, ExceptionType::OutOfBoundsTableAccess); >+ }); > > return { }; > } > >-auto AirIRGenerator::addTableSet(ExpressionType& idx, ExpressionType& value) -> PartialResult >+auto AirIRGenerator::addTableSet(ExpressionType& index, ExpressionType& value) -> PartialResult > { > // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >- ASSERT(idx.tmp()); >- ASSERT(idx.type() == Type::I32); >+ ASSERT(index.tmp()); >+ ASSERT(index.type() == Type::I32); > ASSERT(value.tmp()); > >- void (*doSet)(Instance*, int32_t, uint64_t value) = [] (Instance* instance, int32_t idx, uint64_t value) -> void { >- // FIXME: We need to box wasm Funcrefs once they are supported here. >- // <https://bugs.webkit.org/show_bug.cgi?id=198157> >- instance->table()->set(idx, JSValue::decode(value)); >- }; >+ auto shouldThrow = g32(); >+ emitCCall(&setWasmTableElement, shouldThrow, instanceValue(), index, value); > >- emitCCall(doSet, TypedTmp(), instanceValue(), idx, value); >+ emitCheck([&] { >+ return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::Zero), shouldThrow, shouldThrow); >+ }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { >+ this->emitThrowException(jit, ExceptionType::OutOfBoundsTableAccess); >+ }); > > return { }; > } >@@ -1103,7 +1120,7 @@ auto AirIRGenerator::setGlobal(uint32_t index, ExpressionType value) -> PartialR > append(moveOpForValueType(type), value, Arg::addr(temp)); > } > >- if (type == Anyref) >+ if (isSubtype(type, Anyref)) > emitWriteBarrierForJSWrapper(); > > return { }; >@@ -1623,6 +1640,7 @@ auto AirIRGenerator::addReturn(const ControlData& data, const ExpressionList& re > break; > case Type::I64: > case Type::Anyref: >+ case Type::Anyfunc: > append(Move, returnValues[0], returnValueGPR); > append(Ret64, returnValueGPR); > break; >diff --git a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >index 55bd2d8b5fe66196a5e2d5d2962ed16a9626a94d..7760974843ecce2f1e9b1705925e984aaccba293 100644 >--- a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >+++ b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >@@ -187,10 +187,11 @@ public: > > // References > PartialResult WARN_UNUSED_RETURN addRefIsNull(ExpressionType& value, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result); > > // Tables >- PartialResult WARN_UNUSED_RETURN addTableGet(ExpressionType& idx, ExpressionType& result); >- PartialResult WARN_UNUSED_RETURN addTableSet(ExpressionType& idx, ExpressionType& value); >+ PartialResult WARN_UNUSED_RETURN addTableGet(ExpressionType& index, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addTableSet(ExpressionType& index, ExpressionType& value); > > // Locals > PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); >@@ -538,7 +539,7 @@ auto B3IRGenerator::addLocal(Type type, uint32_t count) -> PartialResult > for (uint32_t i = 0; i < count; ++i) { > Variable* local = m_proc.addVariable(toB3Type(type)); > m_locals.uncheckedAppend(local); >- auto val = type == Anyref ? JSValue::encode(jsNull()) : 0; >+ auto val = isSubtype(type, Anyref) ? JSValue::encode(jsNull()) : 0; > m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), local, constant(toB3Type(type), val, Origin())); > } > return { }; >@@ -565,32 +566,51 @@ auto B3IRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result) > return { }; > } > >-auto B3IRGenerator::addTableGet(ExpressionType& idx, ExpressionType& result) -> PartialResult >+auto B3IRGenerator::addTableGet(ExpressionType& index, ExpressionType& result) -> PartialResult > { > // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >- uint64_t (*doGet)(Instance*, int32_t) = [] (Instance* instance, int32_t idx) -> uint64_t { >- return JSValue::encode(instance->table()->get(idx)); >- }; >- > result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(Anyref), origin(), >- m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(doGet, B3CCallPtrTag)), >- instanceValue(), idx); >+ m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(&getWasmTableElement, B3CCallPtrTag)), >+ instanceValue(), index); >+ >+ { >+ CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), >+ m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), result, m_currentBlock->appendNew<Const64Value>(m_proc, origin(), 0))); >+ >+ check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { >+ this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess); >+ }); >+ } > > return { }; > } > >-auto B3IRGenerator::addTableSet(ExpressionType& idx, ExpressionType& value) -> PartialResult >+auto B3IRGenerator::addTableSet(ExpressionType& index, ExpressionType& value) -> PartialResult >+{ >+ // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >+ auto shouldThrow = m_currentBlock->appendNew<CCallValue>(m_proc, B3::Int32, origin(), >+ m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(&setWasmTableElement, B3CCallPtrTag)), >+ instanceValue(), index, value); >+ >+ { >+ CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), >+ m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), shouldThrow, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0))); >+ >+ check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { >+ this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess); >+ }); >+ } >+ >+ return { }; >+} >+ >+auto B3IRGenerator::addRefFunc(uint32_t index, ExpressionType& result) -> PartialResult > { > // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >- void (*doSet)(Instance*, int32_t, uint64_t value) = [] (Instance* instance, int32_t idx, uint64_t value) -> void { >- // FIXME: We need to box wasm Funcrefs once they are supported here. >- // <https://bugs.webkit.org/show_bug.cgi?id=198157> >- instance->table()->set(idx, JSValue::decode(value)); >- }; > >- m_currentBlock->appendNew<CCallValue>(m_proc, B3::Void, origin(), >- m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(doSet, B3CCallPtrTag)), >- instanceValue(), idx, value); >+ result = m_currentBlock->appendNew<CCallValue>(m_proc, B3::Int64, origin(), >+ m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(&doWasmRefFunc, B3CCallPtrTag)), >+ instanceValue(), addConstant(Type::I32, index)); > > return { }; > } >@@ -679,7 +699,7 @@ auto B3IRGenerator::setGlobal(uint32_t index, ExpressionType value) -> PartialRe > Value* globalsArray = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), instanceValue(), safeCast<int32_t>(Instance::offsetOfGlobals())); > m_currentBlock->appendNew<MemoryValue>(m_proc, Store, origin(), value, globalsArray, safeCast<int32_t>(index * sizeof(Register))); > >- if (m_info.globals[index].type == Anyref) >+ if (isSubtype(m_info.globals[index].type, Anyref)) > emitWriteBarrierForJSWrapper(); > > return { }; >diff --git a/Source/JavaScriptCore/wasm/WasmBBQPlan.cpp b/Source/JavaScriptCore/wasm/WasmBBQPlan.cpp >index 0b38f9d596bfc07bcb672bc2a17b71aadc1c49aa..65cde662ceae1c52ae7639878165460beceee1b9 100644 >--- a/Source/JavaScriptCore/wasm/WasmBBQPlan.cpp >+++ b/Source/JavaScriptCore/wasm/WasmBBQPlan.cpp >@@ -286,7 +286,7 @@ void BBQPlan::compileFunctions(CompilationEffort effort) > > m_wasmInternalFunctions[functionIndex] = WTFMove(*parseAndCompileResult); > >- if (m_exportedFunctionIndices.contains(functionIndex)) { >+ if (m_exportedFunctionIndices.contains(functionIndex) || m_moduleInformation->referencedFunctions().contains(functionIndex)) { > auto locker = holdLock(m_lock); > auto result = m_embedderToWasmInternalFunctions.add(functionIndex, m_createEmbedderWrapper(m_compilationContexts[functionIndex], signature, &m_unlinkedWasmToWasmCalls[functionIndex], m_moduleInformation.get(), m_mode, functionIndex)); > ASSERT_UNUSED(result, result.isNewEntry); >diff --git a/Source/JavaScriptCore/wasm/WasmCallingConvention.h b/Source/JavaScriptCore/wasm/WasmCallingConvention.h >index 10eb8cd4e7336e0ef24dc9be504b7507ce83c752..f7be38c67bee9eb35332113b23e6b243de94cdce 100644 >--- a/Source/JavaScriptCore/wasm/WasmCallingConvention.h >+++ b/Source/JavaScriptCore/wasm/WasmCallingConvention.h >@@ -236,6 +236,7 @@ private: > case Type::I32: > case Type::I64: > case Type::Anyref: >+ case Wasm::Anyfunc: > marshallArgumentImpl(m_gprArgs, gpArgumentCount, stackOffset, regFunc, stackFunc); > break; > case Type::F32: >@@ -301,6 +302,7 @@ public: > case Type::I32: > case Type::I64: > case Type::Anyref: >+ case Wasm::Anyfunc: > patchpoint->resultConstraint = B3::ValueRep::reg(GPRInfo::returnValueGPR); > break; > default: >diff --git a/Source/JavaScriptCore/wasm/WasmExceptionType.h b/Source/JavaScriptCore/wasm/WasmExceptionType.h >index d446c0ed1b4dd9f66ff5938265a066e0dc92d611..932861ca9dd1e5ab8373956143045d17535003bc 100644 >--- a/Source/JavaScriptCore/wasm/WasmExceptionType.h >+++ b/Source/JavaScriptCore/wasm/WasmExceptionType.h >@@ -33,6 +33,7 @@ namespace Wasm { > > #define FOR_EACH_EXCEPTION(macro) \ > macro(OutOfBoundsMemoryAccess, "Out of bounds memory access") \ >+ macro(OutOfBoundsTableAccess, "Out of bounds table access") \ > macro(OutOfBoundsCallIndirect, "Out of bounds call_indirect") \ > macro(NullTableEntry, "call_indirect to a null table entry") \ > macro(BadSignature, "call_indirect to a signature that does not match") \ >@@ -42,7 +43,8 @@ namespace Wasm { > macro(IntegerOverflow, "Integer overflow") \ > macro(StackOverflow, "Stack overflow") \ > macro(I64ArgumentType, "WebAssembly function with an i64 argument can't be called from JavaScript") \ >- macro(I64ReturnType, "WebAssembly function that returns i64 can't be called from JavaScript") >+ macro(I64ReturnType, "WebAssembly function that returns i64 can't be called from JavaScript") \ >+ macro(FuncrefNotWasm, "Anyfunc must be an exported wasm function") > > enum class ExceptionType : uint32_t { > #define MAKE_ENUM(enumName, error) enumName, >diff --git a/Source/JavaScriptCore/wasm/WasmFormat.h b/Source/JavaScriptCore/wasm/WasmFormat.h >index 6292250f25eea9490c43a4ebd46d2e25e0c36c95..7c034417a13f261b8475c8edc4274fa80518b9eb 100644 >--- a/Source/JavaScriptCore/wasm/WasmFormat.h >+++ b/Source/JavaScriptCore/wasm/WasmFormat.h >@@ -68,12 +68,20 @@ inline bool isValueType(Type type) > case F64: > return true; > case Anyref: >+ case Anyfunc: > return Options::useWebAssemblyReferences(); > default: > break; > } > return false; > } >+ >+inline bool isSubtype(Type sub, Type parent) >+{ >+ if (sub == parent) >+ return true; >+ return sub == Anyfunc && parent == Anyref; >+} > > enum class ExternalKind : uint8_t { > // FIXME auto-generate this. https://bugs.webkit.org/show_bug.cgi?id=165231 >@@ -138,6 +146,7 @@ struct Global { > enum InitializationType { > IsImport, > FromGlobalImport, >+ FromRefFunc, > FromExpression > }; > >@@ -234,6 +243,7 @@ public: > uint32_t initial() const { return m_initial; } > Optional<uint32_t> maximum() const { return m_maximum; } > TableElementType type() const { return m_type; } >+ Wasm::Type wasmType() const { return m_type == TableElementType::Funcref ? Type::Anyfunc : Type::Anyref; } > > private: > uint32_t m_initial; >diff --git a/Source/JavaScriptCore/wasm/WasmFunctionParser.h b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >index dfae8f2cf3ad2da18aa88bb26bd558cc7d8476de..59eee2622de2685932c51c42fbe8c1993f84e95f 100644 >--- a/Source/JavaScriptCore/wasm/WasmFunctionParser.h >+++ b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >@@ -283,25 +283,25 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > > case TableGet: { > WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >- ExpressionType result, idx; >- WASM_TRY_POP_EXPRESSION_STACK_INTO(idx, "table.get"); >- WASM_TRY_ADD_TO_CONTEXT(addTableGet(idx, result)); >+ ExpressionType result, index; >+ WASM_TRY_POP_EXPRESSION_STACK_INTO(index, "table.get"); >+ WASM_TRY_ADD_TO_CONTEXT(addTableGet(index, result)); > m_expressionStack.append(result); > return { }; > } > > case TableSet: { > WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >- ExpressionType val, idx; >+ ExpressionType val, index; > WASM_TRY_POP_EXPRESSION_STACK_INTO(val, "table.set"); >- WASM_TRY_POP_EXPRESSION_STACK_INTO(idx, "table.set"); >- WASM_TRY_ADD_TO_CONTEXT(addTableSet(idx, val)); >+ WASM_TRY_POP_EXPRESSION_STACK_INTO(index, "table.set"); >+ WASM_TRY_ADD_TO_CONTEXT(addTableSet(index, val)); > return { }; > } > > case RefNull: { > WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >- m_expressionStack.append(m_context.addConstant(Anyref, JSValue::encode(jsNull()))); >+ m_expressionStack.append(m_context.addConstant(Anyfunc, JSValue::encode(jsNull()))); > return { }; > } > >@@ -314,6 +314,17 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > return { }; > } > >+ case RefFunc: { >+ uint32_t index; >+ ExpressionType result; >+ WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >+ WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for ref.func"); >+ >+ WASM_TRY_ADD_TO_CONTEXT(addRefFunc(index, result)); >+ m_expressionStack.append(result); >+ return { }; >+ } >+ > case GetLocal: { > uint32_t index; > ExpressionType result; >@@ -681,6 +692,13 @@ auto FunctionParser<Context>::parseUnreachableExpression() -> PartialResult > return { }; > } > >+ case RefFunc: { >+ uint32_t unused; >+ WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get immediate for ", m_currentOpcode, " in unreachable context"); >+ WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >+ return { }; >+ } >+ > case GrowMemory: > case CurrentMemory: { > uint8_t reserved; >diff --git a/Source/JavaScriptCore/wasm/WasmInstance.cpp b/Source/JavaScriptCore/wasm/WasmInstance.cpp >index 37059968e0b7854d27336f1937ec258a370b4966..52bf0f2b3310d8badb02d43995001d43f1bf2c80 100644 >--- a/Source/JavaScriptCore/wasm/WasmInstance.cpp >+++ b/Source/JavaScriptCore/wasm/WasmInstance.cpp >@@ -29,6 +29,7 @@ > #if ENABLE(WEBASSEMBLY) > > #include "JSCInlines.h" >+#include "JSWebAssemblyHelpers.h" > #include "JSWebAssemblyInstance.h" > #include "Register.h" > #include "WasmModuleInformation.h" >@@ -57,7 +58,7 @@ Instance::Instance(Context* context, Ref<Module>&& module, EntryFrame** pointerT > new (importFunctionInfo(i)) ImportFunctionInfo(); > memset(static_cast<void*>(m_globals.get()), 0, globalMemoryByteSize(m_module.get())); > for (unsigned i = 0; i < m_module->moduleInformation().globals.size(); ++i) { >- if (m_module.get().moduleInformation().globals[i].type == Anyref) >+ if (isSubtype(m_module.get().moduleInformation().globals[i].type, Anyref)) > m_globalsToMark.set(i); > } > } >@@ -80,6 +81,75 @@ void Instance::setGlobal(unsigned i, JSValue value) > m_globals.get()[i].anyref.set(*owner<JSWebAssemblyInstance>()->vm(), owner<JSWebAssemblyInstance>(), value); > } > >+JSValue Instance::getFunctionWrapper(unsigned i) const >+{ >+ JSValue value = m_functionWrappers.get(i).get(); >+ if (value.isEmpty()) >+ return jsNull(); >+ return value; >+} >+ >+void Instance::setFunctionWrapper(unsigned i, JSValue value) >+{ >+ ASSERT(m_owner); >+ ASSERT(value.isFunction(*owner<JSWebAssemblyInstance>()->vm())); >+ ASSERT(!m_functionWrappers.contains(i)); >+ auto locker = holdLock(owner<JSWebAssemblyInstance>()->cellLock()); >+ m_functionWrappers.set(i, WriteBarrier<Unknown>(*owner<JSWebAssemblyInstance>()->vm(), owner<JSWebAssemblyInstance>(), value)); >+ ASSERT(getFunctionWrapper(i) == value); >+} >+ >+EncodedJSValue getWasmTableElement(Instance* instance, int32_t signedIndex) >+{ >+ if (signedIndex < 0) >+ return 0; >+ >+ uint32_t index = signedIndex; >+ if (index >= instance->table()->length()) >+ return 0; >+ >+ return JSValue::encode(instance->table()->get(index)); >+} >+ >+bool setWasmTableElement(Instance* instance, int32_t signedIndex, EncodedJSValue encValue) >+{ >+ if (signedIndex < 0) >+ return false; >+ >+ uint32_t index = signedIndex; >+ if (index >= instance->table()->length()) >+ return false; >+ >+ JSValue value = JSValue::decode(encValue); >+ if (instance->table()->type() == Wasm::TableElementType::Anyref) >+ instance->table()->set(index, value); >+ else if (instance->table()->type() == Wasm::TableElementType::Funcref) { >+ WebAssemblyFunction* wasmFunction; >+ WebAssemblyWrapperFunction* wasmWrapperFunction; >+ >+ if (isWebAssemblyHostFunction(*instance->owner<JSObject>()->vm(), value, wasmFunction, wasmWrapperFunction)) { >+ ASSERT(!!wasmFunction || !!wasmWrapperFunction); >+ if (wasmFunction) >+ instance->table()->asFuncrefTable()->setFunction(index, jsCast<JSObject*>(value), wasmFunction->importableFunction(), &wasmFunction->instance()->instance()); >+ else >+ instance->table()->asFuncrefTable()->setFunction(index, jsCast<JSObject*>(value), wasmWrapperFunction->importableFunction(), &wasmWrapperFunction->instance()->instance()); >+ } else if (value.isNull()) >+ instance->table()->clear(index); >+ else >+ ASSERT_NOT_REACHED(); >+ } else >+ ASSERT_NOT_REACHED(); >+ >+ return true; >+} >+ >+EncodedJSValue doWasmRefFunc(Instance* instance, uint32_t index) >+{ >+ JSValue value = instance->getFunctionWrapper(index); >+ ASSERT(value.isFunction(*instance->owner<JSObject>()->vm())); >+ return JSValue::encode(value); >+} >+ > } } // namespace JSC::Wasm > > #endif // ENABLE(WEBASSEMBLY) >diff --git a/Source/JavaScriptCore/wasm/WasmInstance.h b/Source/JavaScriptCore/wasm/WasmInstance.h >index 5d1fcf3f9d3b82bd0981a20cccd544e7807ce4d7..7a23e562824f81e35860ee7ab0d9858af3b3b279 100644 >--- a/Source/JavaScriptCore/wasm/WasmInstance.h >+++ b/Source/JavaScriptCore/wasm/WasmInstance.h >@@ -39,10 +39,16 @@ > namespace JSC { namespace Wasm { > > struct Context; >+class Instance; >+ >+EncodedJSValue getWasmTableElement(Instance*, int32_t); >+bool setWasmTableElement(Instance*, int32_t, EncodedJSValue encValue); >+EncodedJSValue doWasmRefFunc(Instance*, uint32_t); > > class Instance : public ThreadSafeRefCounted<Instance>, public CanMakeWeakPtr<Instance> { > public: > using StoreTopCallFrameCallback = WTF::Function<void(void*)>; >+ using FunctionWrapperMap = HashMap<uint32_t, WriteBarrier<Unknown>, IntHash<uint32_t>, WTF::UnsignedWithZeroKeyHashTraits<uint32_t>>; > > static Ref<Instance> create(Context*, Ref<Module>&&, EntryFrame** pointerToTopEntryFrame, void** pointerToActualStackLimit, StoreTopCallFrameCallback&&); > >@@ -91,6 +97,9 @@ public: > void setGlobal(unsigned i, int64_t bits) { m_globals.get()[i].primitive = bits; } > void setGlobal(unsigned, JSValue); > const BitVector& globalsToMark() { return m_globalsToMark; } >+ JSValue getFunctionWrapper(unsigned) const; >+ typename FunctionWrapperMap::ValuesConstIteratorRange functionWrappers() const { return m_functionWrappers.values(); } >+ void setFunctionWrapper(unsigned, JSValue); > > static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(Instance, m_memory); } > static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(Instance, m_globals); } >@@ -159,6 +168,7 @@ private: > uint64_t primitive; > }; > MallocPtr<GlobalValue> m_globals; >+ FunctionWrapperMap m_functionWrappers; > BitVector m_globalsToMark; > EntryFrame** m_pointerToTopEntryFrame { nullptr }; > void** m_pointerToActualStackLimit { nullptr }; >diff --git a/Source/JavaScriptCore/wasm/WasmModuleInformation.h b/Source/JavaScriptCore/wasm/WasmModuleInformation.h >index ad5eb9526ac7ac3be437fc623df0bd7dc20807d2..88f66cb639159c369d9dde60a1fce6da5d171289 100644 >--- a/Source/JavaScriptCore/wasm/WasmModuleInformation.h >+++ b/Source/JavaScriptCore/wasm/WasmModuleInformation.h >@@ -29,6 +29,7 @@ > > #include "WasmFormat.h" > >+#include <wtf/BitVector.h> > #include <wtf/Optional.h> > > namespace JSC { namespace Wasm { >@@ -66,6 +67,9 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> { > uint32_t memoryCount() const { return memory ? 1 : 0; } > uint32_t tableCount() const { return tableInformation ? 1 : 0; } > >+ const BitVector& referencedFunctions() const { return m_referencedFunctions; } >+ void addReferencedFunction(unsigned index) const { m_referencedFunctions.set(index); } >+ > Vector<Import> imports; > Vector<SignatureIndex> importFunctionSignatureIndices; > Vector<SignatureIndex> internalFunctionSignatureIndices; >@@ -84,6 +88,8 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> { > unsigned firstInternalGlobal { 0 }; > Vector<CustomSection> customSections; > Ref<NameSection> nameSection; >+ >+ mutable BitVector m_referencedFunctions; > }; > > >diff --git a/Source/JavaScriptCore/wasm/WasmSectionParser.cpp b/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >index 30b6f3ced9df5626222627253c24684a3c8b4943..254e826a80309440cd257f3d746b70219cd43436 100644 >--- a/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >+++ b/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >@@ -291,9 +291,11 @@ auto SectionParser::parseGlobal() -> PartialResult > WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, global.initialBitsOrImportNumber, typeForInitOpcode)); > if (initOpcode == GetGlobal) > global.initializationType = Global::FromGlobalImport; >+ else if (initOpcode == RefFunc) >+ global.initializationType = Global::FromRefFunc; > else > global.initializationType = Global::FromExpression; >- WASM_PARSER_FAIL_IF(typeForInitOpcode != global.type, "Global init_expr opcode of type ", typeForInitOpcode, " doesn't match global's type ", global.type); >+ WASM_PARSER_FAIL_IF(!isSubtype(typeForInitOpcode, global.type), "Global init_expr opcode of type ", typeForInitOpcode, " doesn't match global's type ", global.type); > > m_info->globals.uncheckedAppend(WTFMove(global)); > } >@@ -479,11 +481,21 @@ auto SectionParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber, > } > > case RefNull: { >- resultType = Anyref; >+ resultType = Anyfunc; > bitsOrImportNumber = JSValue::encode(jsNull()); > break; > } > >+ case RefFunc: { >+ uint32_t index; >+ WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get ref.func index"); >+ WASM_PARSER_FAIL_IF(index >= m_info->functions.size(), "ref.func index", index, " exceeds the number of functions ", m_info->functions.size()); >+ >+ resultType = Anyfunc; >+ bitsOrImportNumber = index; >+ break; >+ } >+ > default: > WASM_PARSER_FAIL_IF(true, "unknown init_expr opcode ", opcode); > } >diff --git a/Source/JavaScriptCore/wasm/WasmValidate.cpp b/Source/JavaScriptCore/wasm/WasmValidate.cpp >index a4b78e458df4598e0ccda61fa94ec919a29afcf4..7f1ac61ee8a24cd1e918b8bdeaccb8dbe4348e98 100644 >--- a/Source/JavaScriptCore/wasm/WasmValidate.cpp >+++ b/Source/JavaScriptCore/wasm/WasmValidate.cpp >@@ -103,10 +103,11 @@ public: > > // References > Result WARN_UNUSED_RETURN addRefIsNull(ExpressionType& value, ExpressionType& result); >+ Result WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result); > > // Tables >- Result WARN_UNUSED_RETURN addTableGet(ExpressionType& idx, ExpressionType& result); >- Result WARN_UNUSED_RETURN addTableSet(ExpressionType& idx, ExpressionType& value); >+ Result WARN_UNUSED_RETURN addTableGet(ExpressionType& index, ExpressionType& result); >+ Result WARN_UNUSED_RETURN addTableSet(ExpressionType& index, ExpressionType& value); > > // Locals > Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); >@@ -177,20 +178,21 @@ auto Validate::addArguments(const Signature& signature) -> Result > return { }; > } > >-auto Validate::addTableGet(ExpressionType& idx, ExpressionType& result) -> Result >+auto Validate::addTableGet(ExpressionType& index, ExpressionType& result) -> Result > { >- result = Type::Anyref; >- WASM_VALIDATOR_FAIL_IF(Type::I32 != idx, "table.get index to type ", idx, " expected ", Type::I32); >- WASM_VALIDATOR_FAIL_IF(TableElementType::Anyref != m_module.tableInformation.type(), "table.get expects the table to have type ", Type::Anyref); >+ result = m_module.tableInformation.wasmType(); >+ WASM_VALIDATOR_FAIL_IF(Type::I32 != index, "table.get index to type ", index, " expected ", Type::I32); > > return { }; > } > >-auto Validate::addTableSet(ExpressionType& idx, ExpressionType& value) -> Result >+auto Validate::addTableSet(ExpressionType& index, ExpressionType& value) -> Result > { >- WASM_VALIDATOR_FAIL_IF(Type::I32 != idx, "table.set index to type ", idx, " expected ", Type::I32); >- WASM_VALIDATOR_FAIL_IF(Type::Anyref != value, "table.set value to type ", value, " expected ", Type::Anyref); >- WASM_VALIDATOR_FAIL_IF(TableElementType::Anyref != m_module.tableInformation.type(), "table.set expects the table to have type ", Type::Anyref); >+ auto type = m_module.tableInformation.wasmType(); >+ WASM_VALIDATOR_FAIL_IF(Type::I32 != index, "table.set index to type ", index, " expected ", Type::I32); >+ WASM_VALIDATOR_FAIL_IF(!isSubtype(value, type), "table.set value to type ", value, " expected ", type); >+ WASM_VALIDATOR_FAIL_IF(m_module.tableInformation.type() != TableElementType::Anyref >+ && m_module.tableInformation.type() != TableElementType::Funcref, "table.set expects the table to have type anyref or anyfunc"); > > return { }; > } >@@ -198,7 +200,16 @@ auto Validate::addTableSet(ExpressionType& idx, ExpressionType& value) -> Result > auto Validate::addRefIsNull(ExpressionType& value, ExpressionType& result) -> Result > { > result = Type::I32; >- WASM_VALIDATOR_FAIL_IF(Type::Anyref != value, "ref.is_null to type ", value, " expected ", Type::Anyref); >+ WASM_VALIDATOR_FAIL_IF(!isSubtype(value, Type::Anyref), "ref.is_null to type ", value, " expected ", Type::Anyref); >+ >+ return { }; >+} >+ >+auto Validate::addRefFunc(uint32_t index, ExpressionType& result) -> Result >+{ >+ result = Type::Anyfunc; >+ WASM_VALIDATOR_FAIL_IF(index >= m_module.functionIndexSpaceSize(), "ref.func index ", index, " is too large, max is ", m_module.functionIndexSpaceSize()); >+ m_module.addReferencedFunction(index); > > return { }; > } >@@ -224,7 +235,7 @@ auto Validate::setLocal(uint32_t index, ExpressionType value) -> Result > { > ExpressionType localType; > WASM_FAIL_IF_HELPER_FAILS(getLocal(index, localType)); >- WASM_VALIDATOR_FAIL_IF(localType != value, "set_local to type ", value, " expected ", localType); >+ WASM_VALIDATOR_FAIL_IF(!isSubtype(value, localType), "set_local to type ", value, " expected ", localType); > return { }; > } > >@@ -362,7 +373,7 @@ auto Validate::addCall(unsigned, const Signature& signature, const Vector<Expres > WASM_VALIDATOR_FAIL_IF(signature.argumentCount() != args.size(), "arity mismatch in call, got ", args.size(), " arguments, expected ", signature.argumentCount()); > > for (unsigned i = 0; i < args.size(); ++i) >- WASM_VALIDATOR_FAIL_IF(args[i] != signature.argument(i), "argument type mismatch in call, got ", args[i], ", expected ", signature.argument(i)); >+ WASM_VALIDATOR_FAIL_IF(!isSubtype(args[i], signature.argument(i)), "argument type mismatch in call, got ", args[i], ", expected ", signature.argument(i)); > > result = signature.returnType(); > return { }; >@@ -375,7 +386,7 @@ auto Validate::addCallIndirect(const Signature& signature, const Vector<Expressi > WASM_VALIDATOR_FAIL_IF(argumentCount != args.size() - 1, "arity mismatch in call_indirect, got ", args.size() - 1, " arguments, expected ", argumentCount); > > for (unsigned i = 0; i < argumentCount; ++i) >- WASM_VALIDATOR_FAIL_IF(args[i] != signature.argument(i), "argument type mismatch in call_indirect, got ", args[i], ", expected ", signature.argument(i)); >+ WASM_VALIDATOR_FAIL_IF(!isSubtype(args[i], signature.argument(i)), "argument type mismatch in call_indirect, got ", args[i], ", expected ", signature.argument(i)); > > WASM_VALIDATOR_FAIL_IF(args.last() != I32, "non-i32 call_indirect index ", args.last()); > >diff --git a/Source/JavaScriptCore/wasm/js/JSToWasm.cpp b/Source/JavaScriptCore/wasm/js/JSToWasm.cpp >index 26313e84269657fee9b6cbd73b57058c445fb5b8..685295789e36b7cfb5d6b908f4f514c72ccdd13f 100644 >--- a/Source/JavaScriptCore/wasm/js/JSToWasm.cpp >+++ b/Source/JavaScriptCore/wasm/js/JSToWasm.cpp >@@ -31,6 +31,7 @@ > #include "CCallHelpers.h" > #include "DisallowMacroScratchRegisterUsage.h" > #include "JSCInlines.h" >+#include "JSWebAssemblyHelpers.h" > #include "JSWebAssemblyInstance.h" > #include "JSWebAssemblyRuntimeError.h" > #include "MaxFrameExtentForSlowPathCall.h" >@@ -83,6 +84,7 @@ std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& comp > FALLTHROUGH; > case Wasm::I32: > case Wasm::Anyref: >+ case Wasm::Anyfunc: > if (numGPRs >= wasmCallingConvention().m_gprArgs.size()) > totalFrameSize += sizeof(void*); > ++numGPRs; >@@ -122,20 +124,7 @@ std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& comp > jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR2, JSWebAssemblyInstance::offsetOfInstance()), GPRInfo::argumentGPR2); > } > >- jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR2, Instance::offsetOfPointerToTopEntryFrame()), GPRInfo::argumentGPR0); >- jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0), GPRInfo::argumentGPR0); >- jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(GPRInfo::argumentGPR0); >- jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); >- jit.move(CCallHelpers::TrustedImm32(static_cast<int32_t>(argumentsIncludeI64 ? ExceptionType::I64ArgumentType : ExceptionType::I64ReturnType)), GPRInfo::argumentGPR1); >- >- CCallHelpers::Call call = jit.call(OperationPtrTag); >- >- jit.jump(GPRInfo::returnValueGPR, ExceptionHandlerPtrTag); >- jit.breakpoint(); // We should not reach this. >- >- jit.addLinkTask([=] (LinkBuffer& linkBuffer) { >- linkBuffer.link(call, FunctionPtr<OperationPtrTag>(wasmToJSException)); >- }); >+ emitThrowWasmToJSException(jit, GPRInfo::argumentGPR2, argumentsIncludeI64 ? ExceptionType::I64ArgumentType : ExceptionType::I64ReturnType); > return result; > } > >@@ -165,6 +154,7 @@ std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& comp > switch (signature.argument(i)) { > case Wasm::I32: > case Wasm::I64: >+ case Wasm::Anyfunc: > case Wasm::Anyref: > if (numGPRs >= wasmCallingConvention().m_gprArgs.size()) { > if (signature.argument(i) == Wasm::I32) { >@@ -250,7 +240,7 @@ std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& comp > jit.moveTrustedValue(jsUndefined(), JSValueRegs { GPRInfo::returnValueGPR }); > break; > case Wasm::Anyref: >- // FIXME: We need to box wasm Funcrefs once they are supported here. >+ case Wasm::Anyfunc: > break; > case Wasm::I32: > jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR); >@@ -268,7 +258,6 @@ std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& comp > } > case Wasm::I64: > case Wasm::Func: >- case Wasm::Anyfunc: > jit.breakpoint(); > break; > default: >diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h >index ea292a22f64e385ced233b8f4cbac5c8c08c6e1f..13d0a0947ee1df0eea1360f460d2bd4c655470ef 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h >@@ -122,7 +122,7 @@ ALWAYS_INLINE bool isWebAssemblyHostFunction(VM& vm, JSValue value, WebAssemblyF > } > > >-ALWAYS_INLINE bool isWebAssemblyHostFunction(VM& vm, JSObject* object) >+ALWAYS_INLINE bool isWebAssemblyHostFunction(VM& vm, JSValue object) > { > WebAssemblyFunction* unused; > WebAssemblyWrapperFunction* unused2; >diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp >index 666bf47f1fe23302fc30fd4e1dc73945ea4b3d3f..323427fd8a0af24ce29364fe404b1670a683b597 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp >@@ -91,11 +91,12 @@ void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor) > for (unsigned i = 0; i < thisObject->instance().numImportFunctions(); ++i) > visitor.append(*thisObject->instance().importFunction<WriteBarrier<JSObject>>(i)); // This also keeps the functions' JSWebAssemblyInstance alive. > >- for (size_t i : thisObject->instance().globalsToMark()) { >- // FIXME: We need to box wasm Funcrefs once they are supported here. >- // <https://bugs.webkit.org/show_bug.cgi?id=198157> >+ for (size_t i : thisObject->instance().globalsToMark()) > visitor.appendUnbarriered(JSValue::decode(thisObject->instance().loadI64Global(i))); >- } >+ >+ auto locker = holdLock(cell->cellLock()); >+ for (auto& wrapper : thisObject->instance().functionWrappers()) >+ visitor.appendUnbarriered(wrapper.get()); > } > > void JSWebAssemblyInstance::finalizeCreation(VM& vm, ExecState* exec, Ref<Wasm::CodeBlock>&& wasmCodeBlock, JSObject* importObject, Wasm::CreationMode creationMode) >diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.cpp >index 963c282e8b88df23ba3997b1d7d1508cd2cf88c1..8ab2f903acac15b13a1de39247c055052edcf379 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.cpp >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.cpp >@@ -48,6 +48,12 @@ JSWebAssemblyRuntimeError::JSWebAssemblyRuntimeError(VM& vm, Structure* structur > > const ClassInfo JSWebAssemblyRuntimeError::s_info = { "WebAssembly.RuntimeError", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWebAssemblyRuntimeError) }; > >+JSObject* createJSWebAssemblyRuntimeError(ExecState* exec, VM& vm, const String& message) >+{ >+ ASSERT(!message.isEmpty()); >+ JSGlobalObject* globalObject = exec->lexicalGlobalObject(); >+ return JSWebAssemblyRuntimeError::create(exec, vm, globalObject->webAssemblyRuntimeErrorStructure(), message); >+} > > } // namespace JSC > >diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.h >index 68d3ad1465b1af8e6e116936fa1bc92f6ea651d5..861f472f3db77097ea3bcc24c92463a9c11b8e2f 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.h >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.h >@@ -43,6 +43,8 @@ protected: > JSWebAssemblyRuntimeError(VM&, Structure*); > }; > >+JSObject* createJSWebAssemblyRuntimeError(ExecState*, VM&, const String&); >+ > } // namespace JSC > > #endif // ENABLE(WEBASSEMBLY) >diff --git a/Source/JavaScriptCore/wasm/js/WasmToJS.cpp b/Source/JavaScriptCore/wasm/js/WasmToJS.cpp >index 5bff9cc6d0441f0efcf21b9dcf988107fb5688bb..8e718ea600d718af5500909437619ad170ee5424 100644 >--- a/Source/JavaScriptCore/wasm/js/WasmToJS.cpp >+++ b/Source/JavaScriptCore/wasm/js/WasmToJS.cpp >@@ -32,6 +32,7 @@ > #include "FrameTracers.h" > #include "JITExceptions.h" > #include "JSCInlines.h" >+#include "JSWebAssemblyHelpers.h" > #include "JSWebAssemblyInstance.h" > #include "JSWebAssemblyRuntimeError.h" > #include "LinkBuffer.h" >@@ -65,7 +66,6 @@ static Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> handleBa > switch (argType) { > case Void: > case Func: >- case Anyfunc: > RELEASE_ASSERT_NOT_REACHED(); > > case I64: { >@@ -162,10 +162,10 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm > switch (argType) { > case Void: > case Func: >- case Anyfunc: > case I64: > RELEASE_ASSERT_NOT_REACHED(); > case Anyref: >+ case Anyfunc: > case I32: { > GPRReg gprReg; > if (marshalledGPRs < wasmCC.m_gprArgs.size()) >@@ -237,14 +237,17 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm > switch (argType) { > case Void: > case Func: >- case Anyfunc: > case I64: > RELEASE_ASSERT_NOT_REACHED(); > case I32: > arg = jsNumber(static_cast<int32_t>(buffer[argNum])); > break; >+ case Anyfunc: { >+ arg = JSValue::decode(buffer[argNum]); >+ ASSERT(isWebAssemblyHostFunction(*vm, arg) || arg.isNull()); >+ break; >+ } > case Anyref: >- // FIXME: We need to box wasm Funcrefs once they are supported here. > arg = JSValue::decode(buffer[argNum]); > break; > case F32: >@@ -268,7 +271,6 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm > uint64_t realResult; > switch (signature.returnType()) { > case Func: >- case Anyfunc: > case I64: > RELEASE_ASSERT_NOT_REACHED(); > break; >@@ -278,6 +280,11 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm > realResult = static_cast<uint64_t>(static_cast<uint32_t>(result.toInt32(exec))); > break; > } >+ case Anyfunc: { >+ realResult = JSValue::encode(result); >+ ASSERT(result.isFunction(*vm) || result.isNull()); >+ break; >+ } > case Anyref: { > realResult = JSValue::encode(result); > break; >@@ -371,10 +378,10 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm > switch (argType) { > case Void: > case Func: >- case Anyfunc: > case I64: > RELEASE_ASSERT_NOT_REACHED(); // Handled above. > case Anyref: >+ case Anyfunc: > case I32: { > GPRReg gprReg; > if (marshalledGPRs < wasmCC.m_gprArgs.size()) >@@ -390,7 +397,6 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm > jit.zeroExtend32ToPtr(gprReg, gprReg); // Clear non-int32 and non-tag bits. > jit.boxInt32(gprReg, JSValueRegs(gprReg), DoNotHaveTagRegisters); > } >- // FIXME: We need to box wasm Funcrefs once they are supported here. > jit.store64(gprReg, calleeFrame.withOffset(calleeFrameOffset)); > calleeFrameOffset += sizeof(Register); > break; >@@ -441,10 +447,10 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm > switch (argType) { > case Void: > case Func: >- case Anyfunc: > case I64: > RELEASE_ASSERT_NOT_REACHED(); // Handled above. > case Anyref: >+ case Anyfunc: > case I32: > // Skipped: handled above. > if (marshalledGPRs >= wasmCC.m_gprArgs.size()) >@@ -519,7 +525,6 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm > // Discard. > break; > case Func: >- case Anyfunc: > // For the JavaScript embedding, imports with these types in their signature return are a WebAssembly.Module validation error. > RELEASE_ASSERT_NOT_REACHED(); > break; >@@ -553,6 +558,7 @@ Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM* vm > done.link(&jit); > break; > } >+ case Anyfunc: > case Anyref: > break; > case F32: { >@@ -695,6 +701,25 @@ void* wasmToJSException(ExecState* exec, Wasm::ExceptionType type, Instance* was > return vm.targetMachinePCForThrow; > } > >+void emitThrowWasmToJSException(CCallHelpers& jit, GPRReg wasmInstance, Wasm::ExceptionType type) >+{ >+ ASSERT(wasmInstance != GPRInfo::argumentGPR0); >+ jit.loadPtr(CCallHelpers::Address(wasmInstance, Wasm::Instance::offsetOfPointerToTopEntryFrame()), GPRInfo::argumentGPR0); >+ jit.loadPtr(CCallHelpers::Address(GPRInfo::argumentGPR0), GPRInfo::argumentGPR0); >+ jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(GPRInfo::argumentGPR0); >+ jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); >+ jit.move(CCallHelpers::TrustedImm32(static_cast<int32_t>(type)), GPRInfo::argumentGPR1); >+ >+ CCallHelpers::Call call = jit.call(OperationPtrTag); >+ >+ jit.jump(GPRInfo::returnValueGPR, ExceptionHandlerPtrTag); >+ jit.breakpoint(); // We should not reach this. >+ >+ jit.addLinkTask([=] (LinkBuffer& linkBuffer) { >+ linkBuffer.link(call, FunctionPtr<OperationPtrTag>(Wasm::wasmToJSException)); >+ }); >+} >+ > } } // namespace JSC::Wasm > > #endif // ENABLE(WEBASSEMBLY) >diff --git a/Source/JavaScriptCore/wasm/js/WasmToJS.h b/Source/JavaScriptCore/wasm/js/WasmToJS.h >index e0c74c33836f052f60fe874e93a0c71d683f861e..5445800b48724c7728a39e62b72b007c61b878b4 100644 >--- a/Source/JavaScriptCore/wasm/js/WasmToJS.h >+++ b/Source/JavaScriptCore/wasm/js/WasmToJS.h >@@ -45,6 +45,7 @@ class Instance; > Expected<MacroAssemblerCodeRef<WasmEntryPtrTag>, BindingFailure> wasmToJS(VM*, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex, unsigned importIndex); > > void* wasmToJSException(ExecState*, Wasm::ExceptionType, Instance*); >+void emitThrowWasmToJSException(CCallHelpers&, GPRReg wasmInstance, Wasm::ExceptionType); > > } } // namespace JSC::Wasm > >diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp >index 91b5b8388520a142aae09c52dc3fc8538e17bcff..d89715042605d73e3c7b4532b6399704c3f93558 100644 >--- a/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp >+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp >@@ -84,6 +84,11 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec) > case Wasm::I32: > arg = JSValue::decode(arg.toInt32(exec)); > break; >+ case Wasm::Anyfunc: { >+ if (!isWebAssemblyHostFunction(vm, arg) && !arg.isNull()) >+ return JSValue::encode(throwException(exec, scope, createJSWebAssemblyRuntimeError(exec, vm, "Anyfunc must be an exported wasm function"))); >+ break; >+ } > case Wasm::Anyref: > break; > case Wasm::I64: >@@ -97,7 +102,6 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec) > break; > case Wasm::Void: > case Wasm::Func: >- case Wasm::Anyfunc: > RELEASE_ASSERT_NOT_REACHED(); > } > RETURN_IF_EXCEPTION(scope, encodedJSValue()); >@@ -228,6 +232,7 @@ MacroAssemblerCodePtr<JSEntryPtrTag> WebAssemblyFunction::jsCallEntrypointSlow() > argumentsIncludeI64 = true; > break; > case Wasm::Anyref: >+ case Wasm::Anyfunc: > case Wasm::I32: > if (numGPRs >= Wasm::wasmCallingConvention().m_gprArgs.size()) > totalFrameSize += sizeof(CPURegister); >@@ -303,6 +308,27 @@ MacroAssemblerCodePtr<JSEntryPtrTag> WebAssemblyFunction::jsCallEntrypointSlow() > ++numGPRs; > } > break; >+ case Wasm::Anyfunc: { >+ // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >+ bool (*shouldThrow)(Wasm::Instance*, JSValue) = [] (Wasm::Instance* wasmInstance, JSValue arg) -> bool { >+ JSWebAssemblyInstance* instance = wasmInstance->owner<JSWebAssemblyInstance>(); >+ JSGlobalObject* globalObject = instance->globalObject(); >+ VM& vm = globalObject->vm(); >+ return !isWebAssemblyHostFunction(vm, arg) && !arg.isNull(); >+ }; >+ jit.move(CCallHelpers::TrustedImmPtr(&instance()->instance()), GPRInfo::argumentGPR0); >+ jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), GPRInfo::argumentGPR1); >+ jit.setupArguments<decltype(shouldThrow)>(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1); >+ auto call = jit.call(OperationPtrTag); >+ >+ jit.addLinkTask([=] (LinkBuffer& linkBuffer) { >+ linkBuffer.link(call, FunctionPtr<OperationPtrTag>(shouldThrow)); >+ }); >+ >+ slowPath.append(jit.branchTest32(CCallHelpers::NonZero, GPRInfo::returnValueGPR)); >+ >+ FALLTHROUGH; >+ } > case Wasm::Anyref: { > jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchGPR); > >@@ -466,13 +492,11 @@ MacroAssemblerCodePtr<JSEntryPtrTag> WebAssemblyFunction::jsCallEntrypointSlow() > isNaN.link(&jit); > break; > } >- case Wasm::Anyref: { >- // FIXME: We need to box wasm Funcrefs once they are supported here. >+ case Wasm::Anyfunc: >+ case Wasm::Anyref: > break; >- } > case Wasm::I64: > case Wasm::Func: >- case Wasm::Anyfunc: > return nullptr; > default: > break; >diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >index 4f801e576927ff66ee50c650890efe021707b8f2..12b1cef2df46879a2c7f1b371f4c59399bec6613 100644 >--- a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >@@ -235,10 +235,15 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > // ii. If the global_type of i is i64 or Type(v) is not Number, throw a WebAssembly.LinkError. > if (moduleInformation.globals[import.kindIndex].type == Wasm::I64) > return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "imported global", "cannot be an i64"))); >- if (moduleInformation.globals[import.kindIndex].type != Wasm::Anyref && !value.isNumber()) >+ if (!isSubtype(moduleInformation.globals[import.kindIndex].type, Wasm::Anyref) && !value.isNumber()) > return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "imported global", "must be a number"))); > // iii. Append ToWebAssemblyValue(v) to imports. > switch (moduleInformation.globals[import.kindIndex].type) { >+ case Wasm::Anyfunc: >+ if (!isWebAssemblyHostFunction(vm, value) && !value.isNull()) >+ return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "imported global", "must be a wasm exported function or null"))); >+ m_instance->instance().setGlobal(import.kindIndex, value); >+ break; > case Wasm::Anyref: > m_instance->instance().setGlobal(import.kindIndex, value); > break; >@@ -319,6 +324,47 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > } > } > >+ unsigned functionImportCount = codeBlock->functionImportCount(); >+ auto makeFunctionWrapper = [&] (const String& field, uint32_t index) -> JSValue { >+ // If we already made a wrapper, do not make a new one. >+ JSValue wrapper = m_instance->instance().getFunctionWrapper(index); >+ >+ if (!wrapper.isNull()) >+ return wrapper; >+ >+ // 1. If e is a closure c: >+ // i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func. >+ // ii. (Note: At most one wrapper is created for any closure, so func is unique, even if there are multiple occurrances in the list. Moreover, if the item was an import that is already an Exported Function Exotic Object, then the original function object will be found. For imports that are regular JS functions, a new wrapper will be created.) >+ if (index < functionImportCount) { >+ JSObject* functionImport = m_instance->instance().importFunction<WriteBarrier<JSObject>>(index)->get(); >+ if (isWebAssemblyHostFunction(vm, functionImport)) >+ wrapper = functionImport; >+ else { >+ Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(index); >+ wrapper = WebAssemblyWrapperFunction::create(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, index, m_instance.get(), signatureIndex); >+ } >+ } else { >+ // iii. Otherwise: >+ // a. Let func be an Exported Function Exotic Object created from c. >+ // b. Append func to funcs. >+ // c. Return func. >+ Wasm::Callee& embedderEntrypointCallee = codeBlock->embedderEntrypointCalleeFromFunctionIndexSpace(index); >+ Wasm::WasmToWasmImportableFunction::LoadLocation entrypointLoadLocation = codeBlock->entrypointLoadLocationFromFunctionIndexSpace(index); >+ Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(index); >+ const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex); >+ WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), field, m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex); >+ wrapper = function; >+ } >+ >+ ASSERT(wrapper.isFunction(vm)); >+ m_instance->instance().setFunctionWrapper(index, wrapper); >+ >+ return wrapper; >+ }; >+ >+ for (auto index : moduleInformation.referencedFunctions()) >+ makeFunctionWrapper("Referenced function", index); >+ > // Globals > { > for (size_t globalIndex = moduleInformation.firstInternalGlobal; globalIndex < moduleInformation.globals.size(); ++globalIndex) { >@@ -327,13 +373,16 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > if (global.initializationType == Wasm::Global::FromGlobalImport) { > ASSERT(global.initialBitsOrImportNumber < moduleInformation.firstInternalGlobal); > m_instance->instance().setGlobal(globalIndex, m_instance->instance().loadI64Global(global.initialBitsOrImportNumber)); >+ } else if (global.initializationType == Wasm::Global::FromRefFunc) { >+ ASSERT(global.initialBitsOrImportNumber < moduleInformation.functionIndexSpaceSize()); >+ ASSERT(makeFunctionWrapper("Global init expr", global.initialBitsOrImportNumber).isFunction(vm)); >+ m_instance->instance().setGlobal(globalIndex, JSValue::encode(makeFunctionWrapper("Global init expr", global.initialBitsOrImportNumber))); > } else > m_instance->instance().setGlobal(globalIndex, global.initialBitsOrImportNumber); > } > } > > SymbolTable* exportSymbolTable = module->exportSymbolTable(); >- unsigned functionImportCount = codeBlock->functionImportCount(); > > // Let exports be a list of (string, JS value) pairs that is mapped from each external value e in instance.exports as follows: > JSModuleEnvironment* moduleEnvironment = JSModuleEnvironment::create(vm, globalObject, nullptr, exportSymbolTable, JSValue(), this); >@@ -341,30 +390,9 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > JSValue exportedValue; > switch (exp.kind) { > case Wasm::ExternalKind::Function: { >- // 1. If e is a closure c: >- // i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func. >- // ii. (Note: At most one wrapper is created for any closure, so func is unique, even if there are multiple occurrances in the list. Moreover, if the item was an import that is already an Exported Function Exotic Object, then the original function object will be found. For imports that are regular JS functions, a new wrapper will be created.) >- if (exp.kindIndex < functionImportCount) { >- unsigned functionIndex = exp.kindIndex; >- JSObject* functionImport = m_instance->instance().importFunction<WriteBarrier<JSObject>>(functionIndex)->get(); >- if (isWebAssemblyHostFunction(vm, functionImport)) >- exportedValue = functionImport; >- else { >- Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(functionIndex); >- exportedValue = WebAssemblyWrapperFunction::create(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, functionIndex, m_instance.get(), signatureIndex); >- } >- } else { >- // iii. Otherwise: >- // a. Let func be an Exported Function Exotic Object created from c. >- // b. Append func to funcs. >- // c. Return func. >- Wasm::Callee& embedderEntrypointCallee = codeBlock->embedderEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex); >- Wasm::WasmToWasmImportableFunction::LoadLocation entrypointLoadLocation = codeBlock->entrypointLoadLocationFromFunctionIndexSpace(exp.kindIndex); >- Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(exp.kindIndex); >- const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex); >- WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), String::fromUTF8(exp.field), m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex); >- exportedValue = function; >- } >+ exportedValue = makeFunctionWrapper(String::fromUTF8(exp.field), exp.kindIndex); >+ ASSERT(exportedValue.isFunction(vm)); >+ ASSERT(makeFunctionWrapper(String::fromUTF8(exp.field), exp.kindIndex) == exportedValue); > break; > } > case Wasm::ExternalKind::Table: { >@@ -388,8 +416,7 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > // Return ToJSValue(v). > switch (global.type) { > case Wasm::Anyref: >- // FIXME: We need to box wasm Funcrefs once they are supported here. >- // <https://bugs.webkit.org/show_bug.cgi?id=198157> >+ case Wasm::Anyfunc: > exportedValue = JSValue::decode(m_instance->instance().loadI64Global(exp.kindIndex)); > break; > >diff --git a/Source/JavaScriptCore/wasm/wasm.json b/Source/JavaScriptCore/wasm/wasm.json >index 30bbc74723783922edbbf634993490f48b7b7e02..75285d6fb700901ca5275ea3fd75bb18d30e413b 100644 >--- a/Source/JavaScriptCore/wasm/wasm.json >+++ b/Source/JavaScriptCore/wasm/wasm.json >@@ -11,13 +11,13 @@ > "i64": { "type": "varint7", "value": -2, "b3type": "B3::Int64" }, > "f32": { "type": "varint7", "value": -3, "b3type": "B3::Float" }, > "f64": { "type": "varint7", "value": -4, "b3type": "B3::Double" }, >- "anyfunc": { "type": "varint7", "value": -16, "b3type": "B3::Void" }, >+ "anyfunc": { "type": "varint7", "value": -16, "b3type": "B3::Int64" }, > "anyref": { "type": "varint7", "value": -17, "b3type": "B3::Int64" }, > "func": { "type": "varint7", "value": -32, "b3type": "B3::Void" }, > "void": { "type": "varint7", "value": -64, "b3type": "B3::Void" } > }, >- "value_type": ["i32", "i64", "f32", "f64", "anyref"], >- "block_type": ["i32", "i64", "f32", "f64", "void", "anyref"], >+ "value_type": ["i32", "i64", "f32", "f64", "anyref", "anyfunc"], >+ "block_type": ["i32", "i64", "f32", "f64", "void", "anyref", "anyfunc"], > "elem_type": ["anyfunc","anyref"], > "external_kind": { > "Function": { "type": "uint8", "value": 0 }, >@@ -59,8 +59,9 @@ > "i64.const": { "category": "special", "value": 66, "return": ["i64"], "parameter": [], "immediate": [{"name": "value", "type": "varint64"}], "description": "a constant value interpreted as i64" }, > "f64.const": { "category": "special", "value": 68, "return": ["f64"], "parameter": [], "immediate": [{"name": "value", "type": "double"}], "description": "a constant value interpreted as f64" }, > "f32.const": { "category": "special", "value": 67, "return": ["f32"], "parameter": [], "immediate": [{"name": "value", "type": "float"}], "description": "a constant value interpreted as f32" }, >- "ref.null": { "category": "special", "value": 208, "return": ["anyref"], "parameter": [], "immediate": [], "description": "a constant null reference" }, >+ "ref.null": { "category": "special", "value": 208, "return": ["anyfunc"], "parameter": [], "immediate": [], "description": "a constant null reference" }, > "ref.is_null": { "category": "special", "value": 209, "return": ["i32"], "parameter": ["anyref"], "immediate": [], "description": "determine if a reference is null" }, >+ "ref.func": { "category": "special", "value": 210, "return": ["anyfunc"], "parameter": [], "immediate": [{"name": "function_index", "type": "varuint32"}], "description": "return a reference to the function at the given index" }, > "get_local": { "category": "special", "value": 32, "return": ["any"], "parameter": [], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "read a local variable or parameter" }, > "set_local": { "category": "special", "value": 33, "return": [], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter" }, > "tee_local": { "category": "special", "value": 34, "return": ["any"], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter and return the same value" }, >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index ffc1cad5bc4233323539cc4c865aa1518a89983f..94f56adccc6686ee55c06658e49b42f12a7e4c9b 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,33 @@ >+2019-06-10 Justin Michaud <justin_michaud@apple.com> >+ >+ [WASM-References] Add support for Funcref in parameters and return types >+ https://bugs.webkit.org/show_bug.cgi?id=198157 >+ >+ Reviewed by Yusuke Suzuki. >+ >+ * wasm/Builder.js: >+ (export.default.Builder.prototype._registerSectionBuilders.const.section.in.WASM.description.section.switch.section.case.string_appeared_here.this.section): >+ * wasm/references/anyref_globals.js: >+ * wasm/references/func_ref.js: Added. >+ (fullGC.gc.makeExportedFunction): >+ (makeExportedIdent): >+ (makeAnyfuncIdent): >+ (fun): >+ (assert.eq.instance.exports.fix.fun): >+ (assert.eq.instance.exports.fix): >+ (string_appeared_here.End.End.Function.End.Code.End.WebAssembly.imp.ref): >+ (string_appeared_here.End.End.Function.End.Code.End.WebAssembly): >+ (GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly.fun): >+ (GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly.assert.throws): >+ (GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly): >+ (assert.throws): >+ (assert.throws.doTest): >+ (let.importedFun.of): >+ (makeAnyfuncIdent.fun): >+ * wasm/references/validation.js: >+ (assert.throws): >+ * wasm/wasm.json: >+ > 2019-06-16 Yusuke Suzuki <ysuzuki@apple.com> > > [JSC] Grown region of WasmTable should be initialized with null >diff --git a/JSTests/wasm/Builder.js b/JSTests/wasm/Builder.js >index 7e7c1b7dc223c883e10e6f373bcdd3c6c29d511b..65f1f477a98b8620fccd2aa8f8c1109e0af67866 100644 >--- a/JSTests/wasm/Builder.js >+++ b/JSTests/wasm/Builder.js >@@ -536,6 +536,10 @@ export default class Builder { > s.data.push({ type, op: "get_global", mutability: _normalizeMutability(mutability), initValue }); > return _errorHandlingProxyFor(globalBuilder); > }, >+ RefFunc: (type, initValue, mutability) => { >+ s.data.push({ type, op: "ref.func", mutability: _normalizeMutability(mutability), initValue }); >+ return _errorHandlingProxyFor(globalBuilder); >+ }, > RefNull: (type, mutability) => { > s.data.push({ type, op: "ref.null", mutability: _normalizeMutability(mutability) }); > return _errorHandlingProxyFor(globalBuilder); >diff --git a/JSTests/wasm/references/anyref_globals.js b/JSTests/wasm/references/anyref_globals.js >index 184457a33fecb0b08ade13decfcf19e7d33089ae..2d3ca7bc605f7974cfef25814e89ae584b053130 100644 >--- a/JSTests/wasm/references/anyref_globals.js >+++ b/JSTests/wasm/references/anyref_globals.js >@@ -56,7 +56,9 @@ assert.eq($1.exports.get_glob(), null) > > const obj = { test: "hi" } > >-$1.exports.set_glob(obj); assert.eq($1.exports.get_glob(), obj); >+assert.throws(() => $1.exports.expglob2 = null, TypeError, "Attempted to assign to readonly property.") >+ >+$1.exports.set_glob(obj); assert.eq($1.exports.get_glob(), obj); assert.eq($1.exports.expglob2, "hi") > $1.exports.set_glob(5); assert.eq($1.exports.get_glob(), 5) > $1.exports.set_glob(null); assert.eq($1.exports.get_glob(), null) > $1.exports.set_glob("hi"); assert.eq($1.exports.get_glob(), "hi") >diff --git a/JSTests/wasm/references/func_ref.js b/JSTests/wasm/references/func_ref.js >new file mode 100644 >index 0000000000000000000000000000000000000000..35e06e4658387283c58335b9b66f77853f9bb102 >--- /dev/null >+++ b/JSTests/wasm/references/func_ref.js >@@ -0,0 +1,447 @@ >+import * as assert from '../assert.js'; >+import Builder from '../Builder.js'; >+ >+fullGC() >+gc() >+ >+function makeExportedFunction(i) { >+ const builder = (new Builder()) >+ .Type().End() >+ .Function().End() >+ .Export() >+ .Function("h") >+ .End() >+ .Code() >+ .Function("h", { params: [], ret: "i32" }, []) >+ .I32Const(i) >+ .End() >+ .End(); >+ >+ const bin = builder.WebAssembly().get(); >+ const module = new WebAssembly.Module(bin); >+ const instance = new WebAssembly.Instance(module); >+ >+ return instance.exports.h >+} >+ >+function makeExportedIdent() { >+ const builder = (new Builder()) >+ .Type().End() >+ .Function().End() >+ .Export() >+ .Function("h") >+ .End() >+ .Code() >+ .Function("h", { params: ["i32"], ret: "i32" }, []) >+ .GetLocal(0) >+ .End() >+ .End(); >+ >+ const bin = builder.WebAssembly().get(); >+ const module = new WebAssembly.Module(bin); >+ const instance = new WebAssembly.Instance(module); >+ >+ return instance.exports.h >+} >+ >+function makeAnyfuncIdent() { >+ const builder = (new Builder()) >+ .Type().End() >+ .Function().End() >+ .Export() >+ .Function("h") >+ .End() >+ .Code() >+ .Function("h", { params: ["anyfunc"], ret: "anyfunc" }, []) >+ .GetLocal(0) >+ .End() >+ .End(); >+ >+ const bin = builder.WebAssembly().get(); >+ const module = new WebAssembly.Module(bin); >+ const instance = new WebAssembly.Instance(module); >+ >+ return instance.exports.h >+} >+ >+{ >+ const myfun = makeExportedFunction(1337); >+ function fun() { >+ return 41; >+ } >+ >+ const builder = (new Builder()) >+ .Type().End() >+ .Function().End() >+ .Export() >+ .Function("h") >+ .Function("i") >+ .Function("get_h") >+ .Function("fix") >+ .Function("get_not_exported") >+ .Function("local_read") >+ .End() >+ .Code() >+ .Function("h", { params: ["anyfunc"], ret: "anyref" }, ["anyref"]) >+ .GetLocal(0) >+ .SetLocal(1) >+ .GetLocal(1) >+ .End() >+ >+ .Function("i", { params: ["anyfunc"], ret: "anyfunc" }, ["anyfunc"]) >+ .GetLocal(0) >+ .SetLocal(1) >+ .GetLocal(1) >+ .End() >+ >+ .Function("get_h", { params: [], ret: "anyfunc" }, ["anyfunc"]) >+ .I32Const(0) >+ .RefFunc(0) >+ .SetLocal(0) >+ .If("anyfunc") >+ .Block("anyfunc", (b) => >+ b.GetLocal(0) >+ ) >+ .Else() >+ .Block("anyfunc", (b) => >+ b.GetLocal(0) >+ ) >+ .End() >+ .End() >+ >+ .Function("fix", { params: [], ret: "anyfunc" }, []) >+ .RefFunc(3) >+ .End() >+ >+ .Function("get_not_exported", { params: [], ret: "anyfunc" }, []) >+ .RefFunc(5) >+ .End() >+ >+ .Function("ret_42", { params: [], ret: "i32" }, []) >+ .I32Const(42) >+ .End() >+ >+ .Function("local_read", { params: [], ret: "i32" }, ["anyfunc"]) >+ .GetLocal(0) >+ .RefIsNull() >+ .End() >+ .End(); >+ >+ const bin = builder.WebAssembly().get(); >+ const module = new WebAssembly.Module(bin); >+ const instance = new WebAssembly.Instance(module); >+ fullGC(); >+ >+ assert.eq(instance.exports.local_read(), 1) >+ assert.eq(instance.exports.h(null), null) >+ >+ assert.throws(() => instance.exports.h(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ assert.eq(instance.exports.h(myfun), myfun) >+ assert.throws(() => instance.exports.h(5), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ assert.throws(() => instance.exports.h(undefined), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ >+ assert.eq(instance.exports.i(null), null) >+ assert.eq(instance.exports.i(myfun), myfun) >+ assert.throws(() => instance.exports.i(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ assert.throws(() => instance.exports.i(5), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ >+ assert.throws(() => instance.exports.get_h()(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ assert.eq(instance.exports.get_h()(null), null) >+ assert.eq(instance.exports.get_h()(myfun), myfun) >+ assert.throws(() => instance.exports.get_h()(5), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ >+ assert.eq(instance.exports.get_not_exported()(), 42) >+ >+ assert.eq(instance.exports.fix()(), instance.exports.fix()); >+ assert.eq(instance.exports.fix(), instance.exports.fix); >+} >+ >+// Globals >+ >+{ >+ const myfun = makeExportedFunction(42); >+ function fun() { >+ return 41; >+ } >+ >+ const $1 = (() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Import() >+ .Global().Anyfunc("imp", "ref", "immutable").End() >+ .End() >+ .Function().End() >+ .Global() >+ .RefNull("anyfunc", "mutable") >+ .RefNull("anyfunc", "immutable") >+ .GetGlobal("anyfunc", 0, "mutable") >+ .RefFunc("anyfunc", 2, "immutable") >+ .End() >+ .Export() >+ .Function("set_glob") >+ .Function("get_glob") >+ .Function("glob_is_null") >+ .Function("set_glob_null") >+ .Function("get_import") >+ .Global("expglob", 2) >+ .Global("expglob2", 0) >+ .Global("exp_glob_is_null", 4) >+ .End() >+ .Code() >+ .Function("set_glob", { params: ["anyfunc"], ret: "void" }) >+ .GetLocal(0) >+ .SetGlobal(1) >+ .End() >+ >+ .Function("get_glob", { params: [], ret: "anyfunc" }) >+ .GetGlobal(1) >+ .End() >+ >+ .Function("glob_is_null", { params: [], ret: "i32" }) >+ .Call(1) >+ .RefIsNull() >+ .End() >+ >+ .Function("set_glob_null", { params: [], ret: "void" }) >+ .RefNull() >+ .Call(0) >+ .End() >+ >+ .Function("get_import", { params: [], ret: "anyfunc" }) >+ .GetGlobal(0) >+ .End() >+ .End().WebAssembly().get()), { imp: { ref: makeExportedFunction(1337) } }))(); >+ >+ fullGC(); >+ >+ assert.eq($1.exports.get_import()(), 1337) >+ assert.eq($1.exports.expglob, null) >+ assert.eq($1.exports.expglob2(), 1337) >+ assert.eq($1.exports.exp_glob_is_null, $1.exports.glob_is_null); >+ assert.eq($1.exports.get_glob(), null) >+ >+ $1.exports.set_glob(myfun); assert.eq($1.exports.get_glob(), myfun); assert.eq($1.exports.get_glob()(), 42); assert.eq($1.exports.expglob2(), 1337) >+ $1.exports.set_glob(null); assert.eq($1.exports.get_glob(), null) >+ $1.exports.set_glob(myfun); assert.eq($1.exports.get_glob()(), 42); >+ >+ assert.throws(() => $1.exports.set_glob(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ >+ assert.eq($1.exports.glob_is_null(), 0) >+ $1.exports.set_glob_null(); assert.eq($1.exports.get_glob(), null) >+ assert.eq($1.exports.glob_is_null(), 1) >+} >+ >+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Import() >+ .Global().Anyfunc("imp", "ref", "immutable").End() >+ .End() >+ .Function().End() >+ .Code().End().WebAssembly().get()), { imp: { ref: function() { return "hi" } } }), Error, "imported global imp:ref must be a wasm exported function or null (evaluating 'new WebAssembly.Instance')"); >+ >+assert.throws(() => new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Code() >+ .Function("h", { params: ["anyfunc"], ret: "anyref" }) >+ .GetLocal(0) >+ .End() >+ .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't validate: control flow returns with unexpected type, in function at index 0 (evaluating 'new WebAssembly.Module')"); >+ >+assert.throws(() => new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 1, element: "anyfunc"}) >+ .End() >+ .Code() >+ .Function("h", { params: ["i32"], ret: "void" }) >+ .GetLocal(0) >+ .I32Const(0) >+ .TableSet() >+ .End() >+ .End().WebAssembly().get()), Error, "WebAssembly.Module doesn't validate: table.set value to type I32 expected Anyfunc, in function at index 0 (evaluating 'new WebAssembly.Module')"); >+ >+// Tables >+{ >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 1, element: "anyfunc"}) >+ .End() >+ .Global() >+ .RefNull("anyfunc", "mutable") >+ .End() >+ .Export() >+ .Function("set_glob") >+ .Function("get_glob") >+ .Function("call_glob") >+ .Function("ret_20") >+ .End() >+ .Code() >+ .Function("set_glob", { params: ["anyfunc"], ret: "void" }) >+ .GetLocal(0) >+ .SetGlobal(0) >+ .End() >+ >+ .Function("get_glob", { params: [], ret: "anyfunc" }) >+ .GetGlobal(0) >+ .End() >+ >+ .Function("call_glob", { params: ["i32"], ret: "i32" }) >+ .I32Const(0) >+ .GetGlobal(0) >+ .TableSet() >+ >+ .GetLocal(0) >+ .I32Const(0) >+ .CallIndirect(2,0) >+ .End() >+ >+ .Function("ret_20", { params: ["i32"], ret: "i32" }) >+ .I32Const(20) >+ .End() >+ .End().WebAssembly().get())); >+ >+ const myfun = makeExportedFunction(1337); >+ function fun(i) { >+ return 41; >+ } >+ const ident = makeExportedIdent(); >+ >+ $1.exports.set_glob($1.exports.ret_20); assert.eq($1.exports.get_glob(), $1.exports.ret_20); assert.eq($1.exports.call_glob(42), 20) >+ $1.exports.set_glob(null); assert.eq($1.exports.get_glob(), null); assert.throws(() => $1.exports.call_glob(42), Error, "call_indirect to a null table entry (evaluating 'func(...args)')") >+ $1.exports.set_glob(ident); assert.eq($1.exports.get_glob(), ident); assert.eq($1.exports.call_glob(42), 42) >+ >+ assert.throws(() => $1.exports.set_glob(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ $1.exports.set_glob(myfun); assert.eq($1.exports.get_glob(), myfun); assert.throws(() => $1.exports.call_glob(42), Error, "call_indirect to a signature that does not match (evaluating 'func(...args)')") >+ >+ for (let i=0; i<1000; ++i) { >+ assert.throws(() => $1.exports.set_glob(function() {}), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')"); >+ } >+} >+ >+// Table set/get >+ >+{ >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 1, element: "anyfunc"}) >+ .End() >+ .Export() >+ .Function("set") >+ .Function("get") >+ .End() >+ .Code() >+ .Function("set", { params: ["anyfunc"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet() >+ .End() >+ >+ .Function("get", { params: [], ret: "anyfunc" }) >+ .I32Const(0) >+ .TableGet() >+ .End() >+ .End().WebAssembly().get())); >+ >+ function doSet() { >+ const myfun = makeExportedFunction(444); >+ for (let i=0; i<1000; ++i) { >+ $1.exports.set(myfun); >+ } >+ $1.exports.set(myfun); assert.eq($1.exports.get(), myfun); assert.eq($1.exports.get()(), 444); >+ } >+ >+ function doTest(j,k, l) { >+ fullGC(); >+ let garbage = { val: "hi", val2: 5, arr: [] } >+ for (let i=0; i<100; ++i) garbage.arr += ({ field: i + j + k + l }) >+ fullGC(); >+ >+ for (let i=0; i<100; ++i) { >+ assert.eq($1.exports.get()(), 444); >+ fullGC(); >+ } >+ } >+ >+ >+ doSet() >+ doTest(0,0,0) >+} >+ >+// Wasm->JS Calls >+ >+for (let importedFun of [function(i) { return i; }, makeAnyfuncIdent()]) { >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Import() >+ .Function("imp", "h", { params: ["anyfunc"], ret: "anyfunc" }) >+ .End() >+ .Function().End() >+ .Table() >+ .Table({initial: 1, element: "anyfunc"}) >+ .End() >+ .Export() >+ .Function("test1") >+ .Function("test2") >+ .Function("test3") >+ .Function("test4") >+ .End() >+ .Code() >+ .Function("test1", { params: ["anyfunc"], ret: "anyfunc" }) >+ .GetLocal(0) >+ .Call(0) >+ .End() >+ >+ .Function("test2", { params: [], ret: "anyfunc" }) >+ .RefFunc(1) >+ .Call(0) >+ .End() >+ >+ .Function("test3", { params: ["anyfunc"], ret: "anyfunc" }) >+ .GetLocal(0) >+ .I32Const(0) >+ .RefFunc(0) >+ .TableSet() >+ .I32Const(0) >+ .CallIndirect(0, 0) >+ .End() >+ >+ .Function("test4", { params: [], ret: "anyfunc" }) >+ .RefFunc(1) >+ .I32Const(0) >+ .RefFunc(0) >+ .TableSet() >+ .I32Const(0) >+ .CallIndirect(0, 0) >+ .End() >+ .End().WebAssembly().get()), { imp: { h: importedFun } }); >+ >+ const myfun = makeExportedFunction(1337); >+ function fun(i) { >+ return 41; >+ } >+ >+ for (let test of [$1.exports.test1, $1.exports.test3]) { >+ assert.eq(test(myfun), myfun) >+ assert.eq(test(myfun)(), 1337) >+ assert.throws(() => test(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ >+ for (let i=0; i<1000; ++i) { >+ assert.throws(() => test(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ } >+ } >+ >+ for (let test of [$1.exports.test2, $1.exports.test4]) { >+ assert.eq(test(), $1.exports.test1) >+ assert.eq(test()(myfun), myfun) >+ assert.throws(() => test()(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ >+ for (let i=0; i<1000; ++i) { >+ assert.throws(() => test()(fun), Error, "Anyfunc must be an exported wasm function (evaluating 'func(...args)')") >+ } >+ } >+} >diff --git a/JSTests/wasm/references/validation.js b/JSTests/wasm/references/validation.js >index d38c265bfff610ad494884f9a77a6373c84e52a3..8f816369a74e89e49e6909df445550fd0f66c216 100644 >--- a/JSTests/wasm/references/validation.js >+++ b/JSTests/wasm/references/validation.js >@@ -63,7 +63,7 @@ import Builder from '../Builder.js'; > const bin = builder.WebAssembly(); > bin.trim(); > >- assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: table.set expects the table to have type Anyref, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')"); >+ assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: table.set value to type Anyref expected Anyfunc, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')"); > } > > { >@@ -86,7 +86,7 @@ import Builder from '../Builder.js'; > const bin = builder.WebAssembly(); > bin.trim(); > >- assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: table.get expects the table to have type Anyref, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')"); >+ assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: control flow returns with unexpected type, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')"); > } > > { >diff --git a/JSTests/wasm/wasm.json b/JSTests/wasm/wasm.json >index 30bbc74723783922edbbf634993490f48b7b7e02..64ca0b5b208d91b2bf7865575712473d5b7250a3 100644 >--- a/JSTests/wasm/wasm.json >+++ b/JSTests/wasm/wasm.json >@@ -11,13 +11,13 @@ > "i64": { "type": "varint7", "value": -2, "b3type": "B3::Int64" }, > "f32": { "type": "varint7", "value": -3, "b3type": "B3::Float" }, > "f64": { "type": "varint7", "value": -4, "b3type": "B3::Double" }, >- "anyfunc": { "type": "varint7", "value": -16, "b3type": "B3::Void" }, >+ "anyfunc": { "type": "varint7", "value": -16, "b3type": "B3::Int64" }, > "anyref": { "type": "varint7", "value": -17, "b3type": "B3::Int64" }, > "func": { "type": "varint7", "value": -32, "b3type": "B3::Void" }, > "void": { "type": "varint7", "value": -64, "b3type": "B3::Void" } > }, >- "value_type": ["i32", "i64", "f32", "f64", "anyref"], >- "block_type": ["i32", "i64", "f32", "f64", "void", "anyref"], >+ "value_type": ["i32", "i64", "f32", "f64", "anyref", "anyfunc"], >+ "block_type": ["i32", "i64", "f32", "f64", "void", "anyref", "anyfunc"], > "elem_type": ["anyfunc","anyref"], > "external_kind": { > "Function": { "type": "uint8", "value": 0 }, >@@ -59,8 +59,9 @@ > "i64.const": { "category": "special", "value": 66, "return": ["i64"], "parameter": [], "immediate": [{"name": "value", "type": "varint64"}], "description": "a constant value interpreted as i64" }, > "f64.const": { "category": "special", "value": 68, "return": ["f64"], "parameter": [], "immediate": [{"name": "value", "type": "double"}], "description": "a constant value interpreted as f64" }, > "f32.const": { "category": "special", "value": 67, "return": ["f32"], "parameter": [], "immediate": [{"name": "value", "type": "float"}], "description": "a constant value interpreted as f32" }, >- "ref.null": { "category": "special", "value": 208, "return": ["anyref"], "parameter": [], "immediate": [], "description": "a constant null reference" }, >+ "ref.null": { "category": "special", "value": 208, "return": ["anyfunc"], "parameter": [], "immediate": [], "description": "a constant null reference" }, > "ref.is_null": { "category": "special", "value": 209, "return": ["i32"], "parameter": ["anyref"], "immediate": [], "description": "determine if a reference is null" }, >+ "ref.func": { "category": "special", "value": 210, "return": ["anyfunc"], "parameter": [], "immediate": [{"name": "function_index", "type": "varuint32"}], "description": "return a reference to the function at the given index" }, > "get_local": { "category": "special", "value": 32, "return": ["any"], "parameter": [], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "read a local variable or parameter" }, > "set_local": { "category": "special", "value": 33, "return": [], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter" }, > "tee_local": { "category": "special", "value": 34, "return": ["any"], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter and return the same value" },
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 198157
:
371816
|
372095
|
372170
|
372256
| 372257