WebKit Bugzilla
Attachment 371816 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-20190610204416.patch (text/plain), 86.96 KB, created by
Justin Michaud
on 2019-06-10 20:44:17 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Justin Michaud
Created:
2019-06-10 20:44:17 PDT
Size:
86.96 KB
patch
obsolete
>Subversion Revision: 246154 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index cef4c61afa48b3c77aad39df021a5fc6599ee9d6..72cacbc2aba16826ce0894d34e4be126a65cf0fd 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,84 @@ >+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 NOBODY (OOPS!). >+ >+ Add support for funcref in parameters, globals, and in table.get/set. When converting a JSValue to >+ a funcref (née 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. >+ >+ * 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-06 Michael Catanzaro <mcatanzaro@igalia.com> > > aarch64: âJSC::ARM64Assembler::LinkRecord::<unnamed union>::RealTypes::m_compareRegisterâ is too small to hold all values of âJSC::ARM64Assembler::RegisterIDâ {aka âenum JSC::ARM64Registers::RegisterIDâ} >diff --git a/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp b/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >index adb33f5404780b54d1a9e8111249878c37c5ae73..ce09878fab425e195559d07bd6ff4eb9cfc1d030 100644 >--- a/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >+++ b/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >@@ -42,6 +42,7 @@ > #include "BinarySwitch.h" > #include "DisallowMacroScratchRegisterUsage.h" > #include "JSCInlines.h" >+#include "JSWebAssemblyHelpers.h" > #include "JSWebAssemblyInstance.h" > #include "ScratchRegisterAllocator.h" > #include "VirtualRegister.h" >@@ -234,6 +235,7 @@ 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); >@@ -370,6 +372,7 @@ private: > return g32(); > case Type::I64: > case Type::Anyref: >+ case Type::Anyfunc: > return g64(); > case Type::F32: > return f32(); >@@ -554,6 +557,7 @@ private: > return Move32; > case Type::I64: > case Type::Anyref: >+ case Type::Anyfunc: > return Move; > case Type::F32: > return MoveFloat; >@@ -799,6 +803,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 +889,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 +924,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,6 +960,22 @@ auto AirIRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result) > return { }; > } > >+auto AirIRGenerator::addRefFunc(uint32_t index, ExpressionType& result) -> PartialResult >+{ >+ // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >+ result = tmpForType(Type::Anyfunc); >+ >+ uint64_t (*doRefFunc)(Instance*, uint32_t) = [] (Instance* instance, uint32_t index) -> uint64_t { >+ JSValue val = instance->getFunctionWrapper(index); >+ ASSERT(val.isFunction(*instance->owner<JSObject>()->vm())); >+ return JSValue::encode(val); >+ }; >+ >+ emitCCall(doRefFunc, result, instanceValue(), addConstant(Type::I32, index)); >+ >+ return { }; >+} >+ > auto AirIRGenerator::addTableGet(ExpressionType& idx, ExpressionType& result) -> PartialResult > { > // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >@@ -976,10 +999,26 @@ auto AirIRGenerator::addTableSet(ExpressionType& idx, ExpressionType& value) -> > ASSERT(idx.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)); >+ void (*doSet)(Instance*, uint32_t, uint64_t value) = [] (Instance* instance, uint32_t idx, uint64_t encValue) -> void { >+ JSValue value = JSValue::decode(encValue); >+ if (instance->table()->type() == TableElementType::Anyref) >+ instance->table()->set(idx, value); >+ else if (instance->table()->type() == TableElementType::Funcref) { >+ WebAssemblyFunction* wasmFunction; >+ WebAssemblyWrapperFunction* wasmWrapperFunction; >+ >+ if (isWebAssemblyHostFunction(*instance->owner<JSObject>()->vm(), value, wasmFunction, wasmWrapperFunction)) { >+ ASSERT(!!wasmFunction || !!wasmWrapperFunction); >+ if (wasmFunction) >+ instance->table()->asFuncrefTable()->setFunction(idx, jsCast<JSObject*>(value), wasmFunction->importableFunction(), &wasmFunction->instance()->instance()); >+ else >+ instance->table()->asFuncrefTable()->setFunction(idx, jsCast<JSObject*>(value), wasmWrapperFunction->importableFunction(), &wasmWrapperFunction->instance()->instance()); >+ } else if (!value.isFunction(*instance->owner<JSObject>()->vm())) >+ instance->table()->clear(idx); >+ else >+ ASSERT_NOT_REACHED(); >+ } else >+ ASSERT_NOT_REACHED(); > }; > > emitCCall(doSet, TypedTmp(), instanceValue(), idx, value); >@@ -1103,7 +1142,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 +1662,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..8e9039ab3611e1ea428c015907ceb9188e961b92 100644 >--- a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >+++ b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >@@ -49,6 +49,7 @@ > #include "B3WasmBoundsCheckValue.h" > #include "DisallowMacroScratchRegisterUsage.h" > #include "JSCInlines.h" >+#include "JSWebAssemblyHelpers.h" > #include "JSWebAssemblyInstance.h" > #include "ScratchRegisterAllocator.h" > #include "VirtualRegister.h" >@@ -187,6 +188,7 @@ 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); >@@ -538,7 +540,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 { }; >@@ -582,10 +584,26 @@ auto B3IRGenerator::addTableGet(ExpressionType& idx, ExpressionType& result) -> > auto B3IRGenerator::addTableSet(ExpressionType& idx, ExpressionType& value) -> 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)); >+ void (*doSet)(Instance*, uint32_t, uint64_t value) = [] (Instance* instance, uint32_t idx, uint64_t encValue) -> void { >+ JSValue value = JSValue::decode(encValue); >+ if (instance->table()->type() == TableElementType::Anyref) >+ instance->table()->set(idx, value); >+ else if (instance->table()->type() == TableElementType::Funcref) { >+ WebAssemblyFunction* wasmFunction; >+ WebAssemblyWrapperFunction* wasmWrapperFunction; >+ >+ if (isWebAssemblyHostFunction(*instance->owner<JSObject>()->vm(), value, wasmFunction, wasmWrapperFunction)) { >+ ASSERT(!!wasmFunction || !!wasmWrapperFunction); >+ if (wasmFunction) >+ instance->table()->asFuncrefTable()->setFunction(idx, jsCast<JSObject*>(value), wasmFunction->importableFunction(), &wasmFunction->instance()->instance()); >+ else >+ instance->table()->asFuncrefTable()->setFunction(idx, jsCast<JSObject*>(value), wasmWrapperFunction->importableFunction(), &wasmWrapperFunction->instance()->instance()); >+ } else if (!value.isFunction(*instance->owner<JSObject>()->vm())) >+ instance->table()->clear(idx); >+ else >+ ASSERT_NOT_REACHED(); >+ } else >+ ASSERT_NOT_REACHED(); > }; > > m_currentBlock->appendNew<CCallValue>(m_proc, B3::Void, origin(), >@@ -595,6 +613,22 @@ auto B3IRGenerator::addTableSet(ExpressionType& idx, ExpressionType& value) -> P > return { }; > } > >+auto B3IRGenerator::addRefFunc(uint32_t index, ExpressionType& result) -> PartialResult >+{ >+ // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >+ uint64_t (*doRefFunc)(Instance*, uint32_t) = [] (Instance* instance, uint32_t index) -> uint64_t { >+ JSValue val = instance->getFunctionWrapper(index); >+ ASSERT(val.isFunction(*instance->owner<JSObject>()->vm())); >+ return JSValue::encode(val); >+ }; >+ >+ result = m_currentBlock->appendNew<CCallValue>(m_proc, B3::Int64, origin(), >+ m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(doRefFunc, B3CCallPtrTag)), >+ instanceValue(), addConstant(Type::I32, index)); >+ >+ return { }; >+} >+ > auto B3IRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult > { > ASSERT(m_locals[index]); >@@ -679,7 +713,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..1e0207f10020e466bc33aeb0ba1f5d7a3a0d7143 100644 >--- a/Source/JavaScriptCore/wasm/WasmBBQPlan.cpp >+++ b/Source/JavaScriptCore/wasm/WasmBBQPlan.cpp >@@ -286,7 +286,8 @@ void BBQPlan::compileFunctions(CompilationEffort effort) > > m_wasmInternalFunctions[functionIndex] = WTFMove(*parseAndCompileResult); > >- if (m_exportedFunctionIndices.contains(functionIndex)) { >+ // TODO check if used in ref.func >+ if (m_exportedFunctionIndices.contains(functionIndex) || true) { > 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..f00f8f181e640d8e83f86b2d9dc1c6c3258d2ce7 100644 >--- a/Source/JavaScriptCore/wasm/WasmExceptionType.h >+++ b/Source/JavaScriptCore/wasm/WasmExceptionType.h >@@ -42,7 +42,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..80a314ff757f073b0b91c805a309b92566bd321f 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 > }; > >diff --git a/Source/JavaScriptCore/wasm/WasmFunctionParser.h b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >index dfae8f2cf3ad2da18aa88bb26bd558cc7d8476de..260be6d5c9dbab9f9f9ce67e70f27b98a6a607db 100644 >--- a/Source/JavaScriptCore/wasm/WasmFunctionParser.h >+++ b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >@@ -301,7 +301,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > > 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..8603f957d0dc641e46c410c67563e47f1c7ae617 100644 >--- a/Source/JavaScriptCore/wasm/WasmInstance.cpp >+++ b/Source/JavaScriptCore/wasm/WasmInstance.cpp >@@ -41,12 +41,18 @@ size_t globalMemoryByteSize(Module& module) > { > return (Checked<size_t>(module.moduleInformation().globals.size()) * sizeof(Register)).unsafeGet(); > } >+ >+size_t functionWrapperByteSize(Module& module) >+{ >+ return (Checked<size_t>(module.moduleInformation().functionIndexSpaceSize()) * sizeof(WriteBarrier<Unknown>)).unsafeGet(); >+} > } > > Instance::Instance(Context* context, Ref<Module>&& module, EntryFrame** pointerToTopEntryFrame, void** pointerToActualStackLimit, StoreTopCallFrameCallback&& storeTopCallFrame) > : m_context(context) > , m_module(WTFMove(module)) > , m_globals(MallocPtr<GlobalValue>::malloc(globalMemoryByteSize(m_module.get()))) >+ , m_functionWrappers(MallocPtr<WriteBarrier<Unknown>>::malloc(functionWrapperByteSize(m_module.get()))) > , m_globalsToMark(m_module.get().moduleInformation().globals.size()) > , m_pointerToTopEntryFrame(pointerToTopEntryFrame) > , m_pointerToActualStackLimit(pointerToActualStackLimit) >@@ -55,9 +61,12 @@ Instance::Instance(Context* context, Ref<Module>&& module, EntryFrame** pointerT > { > for (unsigned i = 0; i < m_numImportFunctions; ++i) > 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) >+ new (&m_globals.get()[i].anyref) WriteBarrier<Unknown>(UndefinedWriteBarrierTag); >+ for (unsigned i = 0; i < m_module->moduleInformation().functionIndexSpaceSize(); ++i) >+ new (&m_functionWrappers.get()[i]) WriteBarrier<Unknown>(UndefinedWriteBarrierTag); > 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 +89,18 @@ 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 >+{ >+ return m_functionWrappers.get()[i].get(); >+} >+ >+void Instance::setFunctionWrapper(unsigned i, JSValue value) >+{ >+ ASSERT(m_owner); >+ ASSERT(value.isFunction(*owner<JSWebAssemblyInstance>()->vm())); >+ m_functionWrappers.get()[i].set(*owner<JSWebAssemblyInstance>()->vm(), owner<JSWebAssemblyInstance>(), value); >+} >+ > } } // namespace JSC::Wasm > > #endif // ENABLE(WEBASSEMBLY) >diff --git a/Source/JavaScriptCore/wasm/WasmInstance.h b/Source/JavaScriptCore/wasm/WasmInstance.h >index 5d1fcf3f9d3b82bd0981a20cccd544e7807ce4d7..015ae1abb53609749137697442f0779d8b652b04 100644 >--- a/Source/JavaScriptCore/wasm/WasmInstance.h >+++ b/Source/JavaScriptCore/wasm/WasmInstance.h >@@ -91,6 +91,8 @@ 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; >+ 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 +161,7 @@ private: > uint64_t primitive; > }; > MallocPtr<GlobalValue> m_globals; >+ MallocPtr<WriteBarrier<Unknown>> 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 7e45deea83d6e5c9b66646b9c2194aef779d84fb..29f3a5d173d859cdff7c8060b1760b8ca2954115 100644 >--- a/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >+++ b/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >@@ -290,9 +290,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)); > } >@@ -478,11 +480,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 6619dfbfd03e50a335475ca7df352be120651fed..22972a64043e344cb3d02d9a3b924f043c43d1f4 100644 >--- a/Source/JavaScriptCore/wasm/WasmValidate.cpp >+++ b/Source/JavaScriptCore/wasm/WasmValidate.cpp >@@ -102,6 +102,7 @@ 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); >@@ -178,18 +179,20 @@ auto Validate::addArguments(const Signature& signature) -> Result > > auto Validate::addTableGet(ExpressionType& idx, ExpressionType& result) -> Result > { >- result = Type::Anyref; >+ auto type = m_module.tableInformation.type() == TableElementType::Funcref ? Type::Anyfunc : Type::Anyref; >+ result = type; > 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); > > return { }; > } > > auto Validate::addTableSet(ExpressionType& idx, ExpressionType& value) -> Result > { >+ auto type = m_module.tableInformation.type() == TableElementType::Funcref ? Type::Anyfunc : Type::Anyref; > 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); >+ 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 { }; > } >@@ -197,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"); >+ m_module.addReferencedFunction(index); > > return { }; > } >@@ -223,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 { }; > } > >@@ -361,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 { }; >@@ -374,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..e80aafea4aa6d9cd897b552a476de705cd18df36 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)); >- }); >+ emitWasmToJSException(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..20b60fc7115abd2a7626747dee5c1a9749067035 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp >@@ -91,11 +91,11 @@ 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))); >- } >+ >+ for (unsigned i = 0; i < thisObject->instance().module().moduleInformation().functionIndexSpaceSize(); ++i) >+ visitor.appendUnbarriered(thisObject->instance().getFunctionWrapper(i)); > } > > 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..5c7b006516f62e1498297ac942432f4b092185da 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 emitWasmToJSException(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..de3e7cbf4d4186fe0f9d77a3e13785e8cabc5a65 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 emitWasmToJSException(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..85f725ee8fe321017c2242e5883db16ad57d4bc1 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,33 @@ 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(); >+ }; >+ CCallHelpers::JumpList done; >+ 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)); >+ }); >+ >+ done.append(jit.branchTest32(CCallHelpers::Zero, GPRInfo::returnValueGPR)); >+ >+ jit.move(CCallHelpers::TrustedImmPtr(&instance()->instance()), GPRInfo::argumentGPR2); >+ emitWasmToJSException(jit, GPRInfo::argumentGPR2, Wasm::ExceptionType::FuncrefNotWasm); >+ done.link(&jit); >+ >+ FALLTHROUGH; >+ } > case Wasm::Anyref: { > jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchGPR); > >@@ -466,13 +498,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..b371ec2069ff55880bee267d6ba80fbfdf1d8de9 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"))); >+ 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 = jsNull(); >+ >+ if ((wrapper = m_instance->instance().getFunctionWrapper(index)).isFunction(vm)) >+ 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 59fc2acae67b97158f9c225f6b888e13a953f0fe..31bb99ed02e9664288892516289fb6e0e23990e2 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 NOBODY (OOPS!). >+ >+ * 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-05 Justin Michaud <justin_michaud@apple.com> > > [WASM-References] Add support for Anyref tables, Table.get and Table.set (for Anyref only). >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..e15a21069ca2c18e80166da6adcac0c5a9f4ae8b >--- /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 (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