WebKit Bugzilla
Attachment 372380 Details for
Bug 198760
: [WASM-References] Add support for multiple tables
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-198760-20190618135913.patch (text/plain), 100.34 KB, created by
Justin Michaud
on 2019-06-18 13:59:14 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Justin Michaud
Created:
2019-06-18 13:59:14 PDT
Size:
100.34 KB
patch
obsolete
>Subversion Revision: 246516 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index fd0e8bfabf8df96a9c4eb9e42fc3af10d2415109..9283943d01ae968aec73c1a2e0d06bb1454fb2c3 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,119 @@ >+2019-06-17 Justin Michaud <justin_michaud@apple.com> >+ >+ [WASM-References] Add support for multiple tables >+ https://bugs.webkit.org/show_bug.cgi?id=198760 >+ >+ Reviewed by Saam Barati. >+ >+ Support multiple wasm tables. We turn tableInformation into a tables array, and update all of the >+ existing users to give a table index. >+ >+ * wasm/WasmAirIRGenerator.cpp: >+ (JSC::Wasm::AirIRGenerator::AirIRGenerator): >+ (JSC::Wasm::AirIRGenerator::addTableGet): >+ (JSC::Wasm::AirIRGenerator::addTableSet): >+ (JSC::Wasm::AirIRGenerator::addCallIndirect): >+ * wasm/WasmB3IRGenerator.cpp: >+ (JSC::Wasm::B3IRGenerator::B3IRGenerator): >+ (JSC::Wasm::B3IRGenerator::addTableGet): >+ (JSC::Wasm::B3IRGenerator::addTableSet): >+ (JSC::Wasm::B3IRGenerator::addCallIndirect): >+ * wasm/WasmExceptionType.h: >+ * wasm/WasmFormat.h: >+ (JSC::Wasm::Element::Element): >+ * wasm/WasmFunctionParser.h: >+ (JSC::Wasm::FunctionParser<Context>::parseExpression): >+ (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression): >+ * wasm/WasmInstance.cpp: >+ (JSC::Wasm::Instance::Instance): >+ (JSC::Wasm::Instance::create): >+ (JSC::Wasm::Instance::extraMemoryAllocated const): >+ (JSC::Wasm::Instance::table): >+ (JSC::Wasm::Instance::setTable): >+ * wasm/WasmInstance.h: >+ (JSC::Wasm::Instance::updateCachedMemory): >+ (JSC::Wasm::Instance::offsetOfGlobals): >+ (JSC::Wasm::Instance::offsetOfTablePtr): >+ (JSC::Wasm::Instance::allocationSize): >+ (JSC::Wasm::Instance::table): Deleted. >+ (JSC::Wasm::Instance::setTable): Deleted. >+ (JSC::Wasm::Instance::offsetOfTable): Deleted. >+ * wasm/WasmModuleInformation.h: >+ (JSC::Wasm::ModuleInformation::tableCount const): >+ * wasm/WasmSectionParser.cpp: >+ (JSC::Wasm::SectionParser::parseImport): >+ (JSC::Wasm::SectionParser::parseTableHelper): >+ (JSC::Wasm::SectionParser::parseTable): >+ (JSC::Wasm::SectionParser::parseElement): >+ * wasm/WasmTable.h: >+ (JSC::Wasm::Table::owner const): >+ * wasm/WasmValidate.cpp: >+ (JSC::Wasm::Validate::addTableGet): >+ (JSC::Wasm::Validate::addTableSet): >+ (JSC::Wasm::Validate::addCallIndirect): >+ * wasm/js/JSWebAssemblyInstance.cpp: >+ (JSC::JSWebAssemblyInstance::JSWebAssemblyInstance): >+ (JSC::JSWebAssemblyInstance::visitChildren): >+ * wasm/js/JSWebAssemblyInstance.h: >+ * wasm/js/WebAssemblyModuleRecord.cpp: >+ (JSC::WebAssemblyModuleRecord::link): >+ (JSC::WebAssemblyModuleRecord::evaluate): >+ * wasm/wasm.json: >+ >+ * wasm/WasmAirIRGenerator.cpp: >+ (JSC::Wasm::AirIRGenerator::AirIRGenerator): >+ (JSC::Wasm::AirIRGenerator::addTableGet): >+ (JSC::Wasm::AirIRGenerator::addTableSet): >+ (JSC::Wasm::AirIRGenerator::addCallIndirect): >+ * wasm/WasmB3IRGenerator.cpp: >+ (JSC::Wasm::B3IRGenerator::B3IRGenerator): >+ (JSC::Wasm::B3IRGenerator::addTableGet): >+ (JSC::Wasm::B3IRGenerator::addTableSet): >+ (JSC::Wasm::B3IRGenerator::addCallIndirect): >+ * wasm/WasmFormat.h: >+ (JSC::Wasm::Element::Element): >+ * wasm/WasmFunctionParser.h: >+ (JSC::Wasm::FunctionParser<Context>::parseExpression): >+ (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression): >+ * wasm/WasmInstance.cpp: >+ (JSC::Wasm::Instance::Instance): >+ (JSC::Wasm::Instance::create): >+ (JSC::Wasm::Instance::extraMemoryAllocated const): >+ (JSC::Wasm::Instance::table): >+ (JSC::Wasm::Instance::setTable): >+ (JSC::Wasm::getWasmTableElement): >+ (JSC::Wasm::setWasmTableElement): >+ * wasm/WasmInstance.h: >+ (JSC::Wasm::Instance::updateCachedMemory): >+ (JSC::Wasm::Instance::offsetOfGlobals): >+ (JSC::Wasm::Instance::offsetOfTablePtr): >+ (JSC::Wasm::Instance::allocationSize): >+ (JSC::Wasm::Instance::table): Deleted. >+ (JSC::Wasm::Instance::setTable): Deleted. >+ (JSC::Wasm::Instance::offsetOfTable): Deleted. >+ * wasm/WasmLimits.h: >+ * wasm/WasmModuleInformation.h: >+ (JSC::Wasm::ModuleInformation::tableCount const): >+ * wasm/WasmSectionParser.cpp: >+ (JSC::Wasm::SectionParser::parseImport): >+ (JSC::Wasm::SectionParser::parseTableHelper): >+ (JSC::Wasm::SectionParser::parseTable): >+ (JSC::Wasm::SectionParser::parseElement): >+ * wasm/WasmTable.h: >+ (JSC::Wasm::Table::owner const): >+ * wasm/WasmValidate.cpp: >+ (JSC::Wasm::Validate::addTableGet): >+ (JSC::Wasm::Validate::addTableSet): >+ (JSC::Wasm::Validate::addCallIndirect): >+ * wasm/js/JSWebAssemblyInstance.cpp: >+ (JSC::JSWebAssemblyInstance::JSWebAssemblyInstance): >+ (JSC::JSWebAssemblyInstance::visitChildren): >+ * wasm/js/JSWebAssemblyInstance.h: >+ * wasm/js/WebAssemblyModuleRecord.cpp: >+ (JSC::WebAssemblyModuleRecord::link): >+ (JSC::WebAssemblyModuleRecord::evaluate): >+ * wasm/wasm.json: >+ > 2019-06-17 Justin Michaud <justin_michaud@apple.com> > > Validate that table element type is funcref if using an element section >diff --git a/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp b/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >index ab304a55d36b358422b1a07d2a44b000df522de7..eff7e3227bf9df24c21623edf893f5c9338d77b0 100644 >--- a/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >+++ b/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >@@ -237,8 +237,8 @@ public: > PartialResult WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result); > > // Tables >- PartialResult WARN_UNUSED_RETURN addTableGet(ExpressionType& index, ExpressionType& result); >- PartialResult WARN_UNUSED_RETURN addTableSet(ExpressionType& index, ExpressionType& value); >+ PartialResult WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType& index, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType& index, ExpressionType& value); > > // Locals > PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); >@@ -277,7 +277,7 @@ public: > > // Calls > PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, ExpressionType& result); >- PartialResult WARN_UNUSED_RETURN addCallIndirect(const Signature&, Vector<ExpressionType>& args, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addCallIndirect(unsigned tableIndex, const Signature&, Vector<ExpressionType>& args, ExpressionType& result); > PartialResult WARN_UNUSED_RETURN addUnreachable(); > > PartialResult addShift(Type, B3::Air::Opcode, ExpressionType value, ExpressionType shift, ExpressionType& result); >@@ -626,6 +626,7 @@ private: > } > > uint32_t m_maxNumJSCallArguments { 0 }; >+ unsigned m_numImportFunctions; > > B3::PatchpointSpecial* m_patchpointSpecial { nullptr }; > }; >@@ -681,6 +682,7 @@ AirIRGenerator::AirIRGenerator(const ModuleInformation& info, B3::Procedure& pro > , m_proc(procedure) > , m_code(m_proc.code()) > , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls) >+ , m_numImportFunctions(info.importFunctionCount()) > { > m_currentBlock = m_code.addBlock(); > m_rootBlock = m_currentBlock; >@@ -968,14 +970,14 @@ auto AirIRGenerator::addRefFunc(uint32_t index, ExpressionType& result) -> Parti > return { }; > } > >-auto AirIRGenerator::addTableGet(ExpressionType& index, ExpressionType& result) -> PartialResult >+auto AirIRGenerator::addTableGet(unsigned tableIndex, 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); >+ emitCCall(&getWasmTableElement, result, instanceValue(), addConstant(Type::I32, tableIndex), index); > emitCheck([&] { > return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::Zero), result, result); > }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { >@@ -985,7 +987,7 @@ auto AirIRGenerator::addTableGet(ExpressionType& index, ExpressionType& result) > return { }; > } > >-auto AirIRGenerator::addTableSet(ExpressionType& index, ExpressionType& value) -> PartialResult >+auto AirIRGenerator::addTableSet(unsigned tableIndex, ExpressionType& index, ExpressionType& value) -> PartialResult > { > // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. > ASSERT(index.tmp()); >@@ -993,7 +995,7 @@ auto AirIRGenerator::addTableSet(ExpressionType& index, ExpressionType& value) - > ASSERT(value.tmp()); > > auto shouldThrow = g32(); >- emitCCall(&setWasmTableElement, shouldThrow, instanceValue(), index, value); >+ emitCCall(&setWasmTableElement, shouldThrow, instanceValue(), addConstant(Type::I32, tableIndex), index, value); > > emitCheck([&] { > return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::Zero), shouldThrow, shouldThrow); >@@ -1893,11 +1895,12 @@ auto AirIRGenerator::addCall(uint32_t functionIndex, const Signature& signature, > return { }; > } > >-auto AirIRGenerator::addCallIndirect(const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult >+auto AirIRGenerator::addCallIndirect(unsigned tableIndex, const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult > { > ExpressionType calleeIndex = args.takeLast(); > ASSERT(signature.argumentCount() == args.size()); >- ASSERT(m_info.tableInformation.type() == TableElementType::Funcref); >+ ASSERT(m_info.tableCount() > tableIndex); >+ ASSERT(m_info.tables[tableIndex].type() == TableElementType::Funcref); > > m_makesCalls = true; > // Note: call indirect can call either WebAssemblyFunction or WebAssemblyWrapperFunction. Because >@@ -1912,12 +1915,16 @@ auto AirIRGenerator::addCallIndirect(const Signature& signature, Vector<Expressi > ExpressionType instancesBuffer = g64(); > ExpressionType callableFunctionBufferLength = g64(); > { >- RELEASE_ASSERT(Arg::isValidAddrForm(Instance::offsetOfTable(), B3::Width64)); > RELEASE_ASSERT(Arg::isValidAddrForm(FuncRefTable::offsetOfFunctions(), B3::Width64)); > RELEASE_ASSERT(Arg::isValidAddrForm(FuncRefTable::offsetOfInstances(), B3::Width64)); > RELEASE_ASSERT(Arg::isValidAddrForm(FuncRefTable::offsetOfLength(), B3::Width64)); > >- append(Move, Arg::addr(instanceValue(), Instance::offsetOfTable()), callableFunctionBufferLength); >+ if (UNLIKELY(!Arg::isValidAddrForm(Instance::offsetOfTablePtr(m_numImportFunctions, tableIndex), B3::Width64))) { >+ append(Move, Arg::bigImm(Instance::offsetOfTablePtr(m_numImportFunctions, tableIndex)), callableFunctionBufferLength); >+ append(Add64, instanceValue(), callableFunctionBufferLength); >+ append(Move, Arg::addr(callableFunctionBufferLength), callableFunctionBufferLength); >+ } else >+ append(Move, Arg::addr(instanceValue(), Instance::offsetOfTablePtr(m_numImportFunctions, tableIndex)), callableFunctionBufferLength); > append(Move, Arg::addr(callableFunctionBufferLength, FuncRefTable::offsetOfFunctions()), callableFunctionBuffer); > append(Move, Arg::addr(callableFunctionBufferLength, FuncRefTable::offsetOfInstances()), instancesBuffer); > append(Move32, Arg::addr(callableFunctionBufferLength, Table::offsetOfLength()), callableFunctionBufferLength); >diff --git a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >index 7760974843ecce2f1e9b1705925e984aaccba293..1eb24652ae4ed6b3290719f7ed533692d511d2f6 100644 >--- a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >+++ b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >@@ -190,8 +190,8 @@ public: > PartialResult WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result); > > // Tables >- PartialResult WARN_UNUSED_RETURN addTableGet(ExpressionType& index, ExpressionType& result); >- PartialResult WARN_UNUSED_RETURN addTableSet(ExpressionType& index, ExpressionType& value); >+ PartialResult WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType& index, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType& index, ExpressionType& value); > > // Locals > PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); >@@ -230,7 +230,7 @@ public: > > // Calls > PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, ExpressionType& result); >- PartialResult WARN_UNUSED_RETURN addCallIndirect(const Signature&, Vector<ExpressionType>& args, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addCallIndirect(unsigned tableIndex, const Signature&, Vector<ExpressionType>& args, ExpressionType& result); > PartialResult WARN_UNUSED_RETURN addUnreachable(); > > void dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack); >@@ -292,6 +292,7 @@ private: > } > > uint32_t m_maxNumJSCallArguments { 0 }; >+ unsigned m_numImportFunctions; > }; > > // Memory accesses in WebAssembly have unsigned 32-bit offsets, whereas they have signed 32-bit offsets in B3. >@@ -343,6 +344,7 @@ B3IRGenerator::B3IRGenerator(const ModuleInformation& info, Procedure& procedure > , m_proc(procedure) > , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls) > , m_constantInsertionValues(m_proc) >+ , m_numImportFunctions(info.importFunctionCount()) > { > m_currentBlock = m_proc.addBlock(); > >@@ -566,12 +568,12 @@ auto B3IRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result) > return { }; > } > >-auto B3IRGenerator::addTableGet(ExpressionType& index, ExpressionType& result) -> PartialResult >+auto B3IRGenerator::addTableGet(unsigned tableIndex, ExpressionType& index, ExpressionType& result) -> PartialResult > { > // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. > result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(Anyref), origin(), > m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(&getWasmTableElement, B3CCallPtrTag)), >- instanceValue(), index); >+ instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex), index); > > { > CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), >@@ -585,12 +587,12 @@ auto B3IRGenerator::addTableGet(ExpressionType& index, ExpressionType& result) - > return { }; > } > >-auto B3IRGenerator::addTableSet(ExpressionType& index, ExpressionType& value) -> PartialResult >+auto B3IRGenerator::addTableSet(unsigned tableIndex, 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); >+ instanceValue(), m_currentBlock->appendNew<Const32Value>(m_proc, origin(), tableIndex), index, value); > > { > CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(), >@@ -1298,7 +1300,7 @@ auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature& signature, > return { }; > } > >-auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult >+auto B3IRGenerator::addCallIndirect(unsigned tableIndex, const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult > { > ExpressionType calleeIndex = args.takeLast(); > ASSERT(signature.argumentCount() == args.size()); >@@ -1315,7 +1317,7 @@ auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<Expressio > ExpressionType mask; > { > ExpressionType table = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), >- instanceValue(), safeCast<int32_t>(Instance::offsetOfTable())); >+ instanceValue(), safeCast<int32_t>(Instance::offsetOfTablePtr(m_numImportFunctions, tableIndex))); > callableFunctionBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), > table, safeCast<int32_t>(FuncRefTable::offsetOfFunctions())); > instancesBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), >diff --git a/Source/JavaScriptCore/wasm/WasmFormat.h b/Source/JavaScriptCore/wasm/WasmFormat.h >index 7c034417a13f261b8475c8edc4274fa80518b9eb..4db28a4e2b6e9d552281688fa9faddf893e5fb9c 100644 >--- a/Source/JavaScriptCore/wasm/WasmFormat.h >+++ b/Source/JavaScriptCore/wasm/WasmFormat.h >@@ -213,10 +213,12 @@ struct Segment { > }; > > struct Element { >- Element(I32InitExpr offset) >- : offset(offset) >+ Element(uint32_t tableIndex, I32InitExpr offset) >+ : tableIndex(tableIndex) >+ , offset(offset) > { } > >+ uint32_t tableIndex; > I32InitExpr offset; > Vector<uint32_t> functionIndices; > }; >diff --git a/Source/JavaScriptCore/wasm/WasmFunctionParser.h b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >index 59eee2622de2685932c51c42fbe8c1993f84e95f..72505428a9ee4560597ee71cd6e3b4efb950201a 100644 >--- a/Source/JavaScriptCore/wasm/WasmFunctionParser.h >+++ b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >@@ -283,19 +283,23 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > > case TableGet: { > WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >+ unsigned tableIndex; >+ WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index"); > ExpressionType result, index; > WASM_TRY_POP_EXPRESSION_STACK_INTO(index, "table.get"); >- WASM_TRY_ADD_TO_CONTEXT(addTableGet(index, result)); >+ WASM_TRY_ADD_TO_CONTEXT(addTableGet(tableIndex, index, result)); > m_expressionStack.append(result); > return { }; > } > > case TableSet: { > WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >+ unsigned tableIndex; >+ WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index"); > ExpressionType val, index; > WASM_TRY_POP_EXPRESSION_STACK_INTO(val, "table.set"); > WASM_TRY_POP_EXPRESSION_STACK_INTO(index, "table.set"); >- WASM_TRY_ADD_TO_CONTEXT(addTableSet(index, val)); >+ WASM_TRY_ADD_TO_CONTEXT(addTableSet(tableIndex, index, val)); > return { }; > } > >@@ -396,13 +400,13 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > > case CallIndirect: { > uint32_t signatureIndex; >- uint8_t reserved; >- WASM_PARSER_FAIL_IF(!m_info.tableInformation, "call_indirect is only valid when a table is defined or imported"); >+ uint32_t tableIndex; >+ WASM_PARSER_FAIL_IF(!m_info.tableCount(), "call_indirect is only valid when a table is defined or imported"); > WASM_PARSER_FAIL_IF(!parseVarUInt32(signatureIndex), "can't get call_indirect's signature index"); >- WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't get call_indirect's reserved byte"); >- WASM_PARSER_FAIL_IF(reserved, "call_indirect's 'reserved' varuint1 must be 0x0"); >+ WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't get call_indirect's table index"); >+ WASM_PARSER_FAIL_IF(tableIndex >= m_info.tableCount(), "call_indirect's table index ", tableIndex, " invalid, limit is ", m_info.tableCount()); > WASM_PARSER_FAIL_IF(m_info.usedSignatures.size() <= signatureIndex, "call_indirect's signature index ", signatureIndex, " exceeds known signatures ", m_info.usedSignatures.size()); >- WASM_PARSER_FAIL_IF(m_info.tableInformation.type() != TableElementType::Funcref, "call_indirect is only valid when a table has type anyfunc"); >+ WASM_PARSER_FAIL_IF(m_info.tables[tableIndex].type() != TableElementType::Funcref, "call_indirect is only valid when a table has type anyfunc"); > > const Signature& calleeSignature = m_info.usedSignatures[signatureIndex].get(); > size_t argumentCount = calleeSignature.argumentCount() + 1; // Add the callee's index. >@@ -416,7 +420,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > m_expressionStack.shrink(firstArgumentIndex); > > ExpressionType result = Context::emptyExpression(); >- WASM_TRY_ADD_TO_CONTEXT(addCallIndirect(calleeSignature, args, result)); >+ WASM_TRY_ADD_TO_CONTEXT(addCallIndirect(tableIndex, calleeSignature, args, result)); > > if (result != Context::emptyExpression()) > m_expressionStack.append(result); >@@ -631,9 +635,9 @@ auto FunctionParser<Context>::parseUnreachableExpression() -> PartialResult > > case CallIndirect: { > uint32_t unused; >- uint8_t unused2; >+ uint32_t unused2; > WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get call_indirect's signature index in unreachable context"); >- WASM_PARSER_FAIL_IF(!parseVarUInt1(unused2), "can't get call_indirect's reserved byte in unreachable context"); >+ WASM_PARSER_FAIL_IF(!parseVarUInt32(unused2), "can't get call_indirect's reserved byte in unreachable context"); > return { }; > } > >@@ -685,7 +689,11 @@ auto FunctionParser<Context>::parseUnreachableExpression() -> PartialResult > } > > case TableGet: >- case TableSet: >+ case TableSet: { >+ unsigned tableIndex; >+ WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't parse table index"); >+ FALLTHROUGH; >+ } > case RefIsNull: > case RefNull: { > WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >diff --git a/Source/JavaScriptCore/wasm/WasmInstance.cpp b/Source/JavaScriptCore/wasm/WasmInstance.cpp >index 52bf0f2b3310d8badb02d43995001d43f1bf2c80..d5d3aab14ecf25ef417748937903214b33056e36 100644 >--- a/Source/JavaScriptCore/wasm/WasmInstance.cpp >+++ b/Source/JavaScriptCore/wasm/WasmInstance.cpp >@@ -61,18 +61,19 @@ Instance::Instance(Context* context, Ref<Module>&& module, EntryFrame** pointerT > if (isSubtype(m_module.get().moduleInformation().globals[i].type, Anyref)) > m_globalsToMark.set(i); > } >+ memset(bitwise_cast<char*>(this) + offsetOfTablePtr(m_numImportFunctions, 0), 0, m_module->moduleInformation().tableCount() * sizeof(Table*)); > } > > Ref<Instance> Instance::create(Context* context, Ref<Module>&& module, EntryFrame** pointerToTopEntryFrame, void** pointerToActualStackLimit, StoreTopCallFrameCallback&& storeTopCallFrame) > { >- return adoptRef(*new (NotNull, fastMalloc(allocationSize(module->moduleInformation().importFunctionCount()))) Instance(context, WTFMove(module), pointerToTopEntryFrame, pointerToActualStackLimit, WTFMove(storeTopCallFrame))); >+ return adoptRef(*new (NotNull, fastMalloc(allocationSize(module->moduleInformation().importFunctionCount(), module->moduleInformation().tableCount()))) Instance(context, WTFMove(module), pointerToTopEntryFrame, pointerToActualStackLimit, WTFMove(storeTopCallFrame))); > } > > Instance::~Instance() { } > > size_t Instance::extraMemoryAllocated() const > { >- return globalMemoryByteSize(m_module.get()) + allocationSize(m_numImportFunctions); >+ return globalMemoryByteSize(m_module.get()) + allocationSize(m_numImportFunctions, m_module->moduleInformation().tableCount()); > } > > void Instance::setGlobal(unsigned i, JSValue value) >@@ -99,42 +100,58 @@ void Instance::setFunctionWrapper(unsigned i, JSValue value) > ASSERT(getFunctionWrapper(i) == value); > } > >-EncodedJSValue getWasmTableElement(Instance* instance, int32_t signedIndex) >+Table* Instance::table(unsigned i) > { >+ RELEASE_ASSERT(i < m_module->moduleInformation().tableCount()); >+ return *bitwise_cast<Table**>(bitwise_cast<char*>(this) + offsetOfTablePtr(m_numImportFunctions, i)); >+} >+ >+void Instance::setTable(unsigned i, Ref<Table>&& table) >+{ >+ RELEASE_ASSERT(i < m_module->moduleInformation().tableCount()); >+ if (auto* table = this->table(i)) >+ table->deref(); >+ *bitwise_cast<Table**>(bitwise_cast<char*>(this) + offsetOfTablePtr(m_numImportFunctions, i)) = &table.leakRef(); >+} >+ >+EncodedJSValue getWasmTableElement(Instance* instance, unsigned tableIndex, int32_t signedIndex) >+{ >+ ASSERT(tableIndex < instance->module().moduleInformation().tableCount()); > if (signedIndex < 0) > return 0; > > uint32_t index = signedIndex; >- if (index >= instance->table()->length()) >+ if (index >= instance->table(tableIndex)->length()) > return 0; > >- return JSValue::encode(instance->table()->get(index)); >+ return JSValue::encode(instance->table(tableIndex)->get(index)); > } > >-bool setWasmTableElement(Instance* instance, int32_t signedIndex, EncodedJSValue encValue) >+bool setWasmTableElement(Instance* instance, unsigned tableIndex, int32_t signedIndex, EncodedJSValue encValue) > { >+ ASSERT(tableIndex < instance->module().moduleInformation().tableCount()); > if (signedIndex < 0) > return false; > > uint32_t index = signedIndex; >- if (index >= instance->table()->length()) >+ if (index >= instance->table(tableIndex)->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) { >+ if (instance->table(tableIndex)->type() == Wasm::TableElementType::Anyref) >+ instance->table(tableIndex)->set(index, value); >+ else if (instance->table(tableIndex)->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()); >+ instance->table(tableIndex)->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()); >+ instance->table(tableIndex)->asFuncrefTable()->setFunction(index, jsCast<JSObject*>(value), wasmWrapperFunction->importableFunction(), &wasmWrapperFunction->instance()->instance()); > } else if (value.isNull()) >- instance->table()->clear(index); >+ instance->table(tableIndex)->clear(index); > else > ASSERT_NOT_REACHED(); > } else >diff --git a/Source/JavaScriptCore/wasm/WasmInstance.h b/Source/JavaScriptCore/wasm/WasmInstance.h >index 7a23e562824f81e35860ee7ab0d9858af3b3b279..012cd4573aa84f96470cb07198c7a70bb0f3a7c4 100644 >--- a/Source/JavaScriptCore/wasm/WasmInstance.h >+++ b/Source/JavaScriptCore/wasm/WasmInstance.h >@@ -41,8 +41,8 @@ namespace JSC { namespace Wasm { > struct Context; > class Instance; > >-EncodedJSValue getWasmTableElement(Instance*, int32_t); >-bool setWasmTableElement(Instance*, int32_t, EncodedJSValue encValue); >+EncodedJSValue getWasmTableElement(Instance*, unsigned, int32_t); >+bool setWasmTableElement(Instance*, unsigned, int32_t, EncodedJSValue encValue); > EncodedJSValue doWasmRefFunc(Instance*, uint32_t); > > class Instance : public ThreadSafeRefCounted<Instance>, public CanMakeWeakPtr<Instance> { >@@ -70,7 +70,8 @@ public: > Module& module() { return m_module.get(); } > CodeBlock* codeBlock() { return m_codeBlock.get(); } > Memory* memory() { return m_memory.get(); } >- Table* table() { return m_table.get(); } >+ Table* table(unsigned); >+ void setTable(unsigned, Ref<Table>&&); > > void* cachedMemory() const { return m_cachedMemory.getMayBeNull(cachedMemorySize()); } > size_t cachedMemorySize() const { return m_cachedMemorySize; } >@@ -88,7 +89,6 @@ public: > m_cachedMemorySize = memory()->size(); > } > } >- void setTable(Ref<Table>&& table) { m_table = WTFMove(table); } > > int32_t loadI32Global(unsigned i) const { return m_globals.get()[i].primitive; } > int64_t loadI64Global(unsigned i) const { return m_globals.get()[i].primitive; } >@@ -103,7 +103,6 @@ public: > > static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(Instance, m_memory); } > static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(Instance, m_globals); } >- static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(Instance, m_table); } > static ptrdiff_t offsetOfCachedMemory() { return OBJECT_OFFSETOF(Instance, m_cachedMemory); } > static ptrdiff_t offsetOfCachedMemorySize() { return OBJECT_OFFSETOF(Instance, m_cachedMemorySize); } > static ptrdiff_t offsetOfPointerToTopEntryFrame() { return OBJECT_OFFSETOF(Instance, m_pointerToTopEntryFrame); } >@@ -122,7 +121,7 @@ public: > } > > // Tail accessors. >- static size_t offsetOfTail() { return WTF::roundUpToMultipleOf<sizeof(uint64_t)>(sizeof(Instance)); } >+ static constexpr size_t offsetOfTail() { return WTF::roundUpToMultipleOf<sizeof(uint64_t)>(sizeof(Instance)); } > struct ImportFunctionInfo { > // Target instance and entrypoint are only set for wasm->wasm calls, and are otherwise nullptr. The embedder-specific logic occurs through import function. > Instance* targetInstance { nullptr }; >@@ -142,6 +141,9 @@ public: > static size_t offsetOfImportFunction(size_t importFunctionNum) { return offsetOfTail() + importFunctionNum * sizeof(ImportFunctionInfo) + OBJECT_OFFSETOF(ImportFunctionInfo, importFunction); } > template<typename T> T* importFunction(unsigned importFunctionNum) { return reinterpret_cast<T*>(&importFunctionInfo(importFunctionNum)->importFunction); } > >+ static_assert(sizeof(ImportFunctionInfo) == WTF::roundUpToMultipleOf<sizeof(uint64_t)>(sizeof(ImportFunctionInfo)), "We rely on this for the alignment to be correct"); >+ static constexpr size_t offsetOfTablePtr(unsigned numImportFunctions, unsigned i) { return offsetOfTail() + sizeof(ImportFunctionInfo) * numImportFunctions + sizeof(Table*) * i; } >+ > void storeTopCallFrame(void* callFrame) > { > m_storeTopCallFrame(callFrame); >@@ -150,9 +152,9 @@ public: > private: > Instance(Context*, Ref<Module>&&, EntryFrame**, void**, StoreTopCallFrameCallback&&); > >- static size_t allocationSize(Checked<size_t> numImportFunctions) >+ static size_t allocationSize(Checked<size_t> numImportFunctions, Checked<size_t> numTables) > { >- return (offsetOfTail() + sizeof(ImportFunctionInfo) * numImportFunctions).unsafeGet(); >+ return (offsetOfTail() + sizeof(ImportFunctionInfo) * numImportFunctions + sizeof(Table*) * numTables).unsafeGet(); > } > void* m_owner { nullptr }; // In a JS embedding, this is a JSWebAssemblyInstance*. > Context* m_context { nullptr }; >@@ -161,7 +163,6 @@ private: > Ref<Module> m_module; > RefPtr<CodeBlock> m_codeBlock; > RefPtr<Memory> m_memory; >- RefPtr<Table> m_table; > > union GlobalValue { > WriteBarrier<Unknown> anyref; >diff --git a/Source/JavaScriptCore/wasm/WasmLimits.h b/Source/JavaScriptCore/wasm/WasmLimits.h >index c6f6cdcbbcdcd785e35f642c7e880d889fdb38aa..cf8b44603681123ecc25cfc372498b5f5b58c986 100644 >--- a/Source/JavaScriptCore/wasm/WasmLimits.h >+++ b/Source/JavaScriptCore/wasm/WasmLimits.h >@@ -51,6 +51,7 @@ constexpr size_t maxFunctionLocals = 50000; > constexpr size_t maxFunctionParams = 1000; > > constexpr size_t maxTableEntries = 10000000; >+constexpr unsigned maxTables = 1000000; > > } } // namespace JSC::Wasm > >diff --git a/Source/JavaScriptCore/wasm/WasmModuleInformation.h b/Source/JavaScriptCore/wasm/WasmModuleInformation.h >index 88f66cb639159c369d9dde60a1fce6da5d171289..268f88bb10bf7485fa65165524e704b659987252 100644 >--- a/Source/JavaScriptCore/wasm/WasmModuleInformation.h >+++ b/Source/JavaScriptCore/wasm/WasmModuleInformation.h >@@ -65,7 +65,7 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> { > // Currently, our wasm implementation allows only one memory and table. > // If we need to remove this limitation, we would have MemoryInformation and TableInformation in the Vectors. > uint32_t memoryCount() const { return memory ? 1 : 0; } >- uint32_t tableCount() const { return tableInformation ? 1 : 0; } >+ uint32_t tableCount() const { return tables.size(); } > > const BitVector& referencedFunctions() const { return m_referencedFunctions; } > void addReferencedFunction(unsigned index) const { m_referencedFunctions.set(index); } >@@ -83,7 +83,7 @@ struct ModuleInformation : public ThreadSafeRefCounted<ModuleInformation> { > Optional<uint32_t> startFunctionIndexSpace; > Vector<Segment::Ptr> data; > Vector<Element> elements; >- TableInformation tableInformation; >+ Vector<TableInformation> tables; > Vector<Global> globals; > unsigned firstInternalGlobal { 0 }; > Vector<CustomSection> customSections; >diff --git a/Source/JavaScriptCore/wasm/WasmSectionParser.cpp b/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >index c80314dfda003feb2b5f07ca9fa99cd8883dea70..ef51216752a1265e1024d37e9289ec48baffdee4 100644 >--- a/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >+++ b/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >@@ -119,6 +119,7 @@ auto SectionParser::parseImport() -> PartialResult > } > case ExternalKind::Table: { > bool isImport = true; >+ kindIndex = m_info->tables.size(); > PartialResult result = parseTableHelper(isImport); > if (UNLIKELY(!result)) > return makeUnexpected(WTFMove(result.error())); >@@ -193,7 +194,7 @@ auto SectionParser::parseResizableLimits(uint32_t& initial, Optional<uint32_t>& > > auto SectionParser::parseTableHelper(bool isImport) -> PartialResult > { >- WASM_PARSER_FAIL_IF(m_info->tableCount() > 0, "Cannot have more than one Table for now"); >+ WASM_PARSER_FAIL_IF(m_info->tableCount() >= maxTables, "Table count of ", m_info->tableCount(), " is too big, maximum ", maxTables); > > int8_t type; > WASM_PARSER_FAIL_IF(!parseInt7(type), "can't parse Table type"); >@@ -209,7 +210,7 @@ auto SectionParser::parseTableHelper(bool isImport) -> PartialResult > ASSERT(!maximum || *maximum >= initial); > > TableElementType tableType = type == Wasm::Anyfunc ? TableElementType::Funcref : TableElementType::Anyref; >- m_info->tableInformation = TableInformation(initial, maximum, isImport, tableType); >+ m_info->tables.append(TableInformation(initial, maximum, isImport, tableType)); > > return { }; > } >@@ -218,15 +219,13 @@ auto SectionParser::parseTable() -> PartialResult > { > uint32_t count; > WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Table's count"); >- WASM_PARSER_FAIL_IF(count > 1, "Table count of ", count, " is invalid, at most 1 is allowed for now"); > >- if (!count) >- return { }; >- >- bool isImport = false; >- PartialResult result = parseTableHelper(isImport); >- if (UNLIKELY(!result)) >- return makeUnexpected(WTFMove(result.error())); >+ for (unsigned i = 0; i < count; ++i) { >+ bool isImport = false; >+ PartialResult result = parseTableHelper(isImport); >+ if (UNLIKELY(!result)) >+ return makeUnexpected(WTFMove(result.error())); >+ } > > return { }; > } >@@ -379,16 +378,16 @@ auto SectionParser::parseElement() -> PartialResult > > WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't get ", elementNum, "th Element table index"); > WASM_PARSER_FAIL_IF(tableIndex >= m_info->tableCount(), "Element section for Table ", tableIndex, " exceeds available Table ", m_info->tableCount()); >- WASM_PARSER_FAIL_IF(m_info->tableInformation.type() != TableElementType::Funcref, "Table ", tableIndex, " must have type 'anyfunc' to have an element section"); >+ WASM_PARSER_FAIL_IF(m_info->tables[tableIndex].type() != TableElementType::Funcref, "Table ", tableIndex, " must have type 'anyfunc' to have an element section"); > Type initExprType; > WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, initExprBits, initExprType)); > WASM_PARSER_FAIL_IF(initExprType != I32, "Element init_expr must produce an i32"); > WASM_PARSER_FAIL_IF(!parseVarUInt32(indexCount), "can't get ", elementNum, "th index count for Element section"); > WASM_PARSER_FAIL_IF(indexCount == std::numeric_limits<uint32_t>::max(), "Element section's ", elementNum, "th index count is too big ", indexCount); > >- ASSERT(!!m_info->tableInformation); >+ ASSERT(!!m_info->tables[tableIndex]); > >- Element element(makeI32InitExpr(initOpcode, initExprBits)); >+ Element element(tableIndex, makeI32InitExpr(initOpcode, initExprBits)); > WASM_PARSER_FAIL_IF(!element.functionIndices.tryReserveCapacity(indexCount), "can't allocate memory for ", indexCount, " Element indices"); > > for (unsigned index = 0; index < indexCount; ++index) { >diff --git a/Source/JavaScriptCore/wasm/WasmTable.h b/Source/JavaScriptCore/wasm/WasmTable.h >index b1e2b5df4df1c645cf9b3302ea587a07feec8d6c..d6aac5b5582da420cd41634e0e532bc52548a939 100644 >--- a/Source/JavaScriptCore/wasm/WasmTable.h >+++ b/Source/JavaScriptCore/wasm/WasmTable.h >@@ -57,6 +57,7 @@ public: > static uint32_t allocatedLength(uint32_t length); > uint32_t mask() const { return m_mask; } > >+ template<typename T> T* owner() const { return reinterpret_cast<T*>(m_owner); } > void setOwner(JSObject* owner) > { > ASSERT(!m_owner); >diff --git a/Source/JavaScriptCore/wasm/WasmValidate.cpp b/Source/JavaScriptCore/wasm/WasmValidate.cpp >index 7f1ac61ee8a24cd1e918b8bdeaccb8dbe4348e98..bd21d80ef1a53f379b15c708821e8c6765dbdfa4 100644 >--- a/Source/JavaScriptCore/wasm/WasmValidate.cpp >+++ b/Source/JavaScriptCore/wasm/WasmValidate.cpp >@@ -106,8 +106,8 @@ public: > Result WARN_UNUSED_RETURN addRefFunc(uint32_t index, ExpressionType& result); > > // Tables >- Result WARN_UNUSED_RETURN addTableGet(ExpressionType& index, ExpressionType& result); >- Result WARN_UNUSED_RETURN addTableSet(ExpressionType& index, ExpressionType& value); >+ Result WARN_UNUSED_RETURN addTableGet(unsigned, ExpressionType& index, ExpressionType& result); >+ Result WARN_UNUSED_RETURN addTableSet(unsigned, ExpressionType& index, ExpressionType& value); > > // Locals > Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); >@@ -148,7 +148,7 @@ public: > > // Calls > Result WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature&, const Vector<ExpressionType>& args, ExpressionType& result); >- Result WARN_UNUSED_RETURN addCallIndirect(const Signature&, const Vector<ExpressionType>& args, ExpressionType& result); >+ Result WARN_UNUSED_RETURN addCallIndirect(unsigned tableIndex, const Signature&, const Vector<ExpressionType>& args, ExpressionType& result); > > ALWAYS_INLINE void didKill(ExpressionType) { } > >@@ -178,21 +178,22 @@ auto Validate::addArguments(const Signature& signature) -> Result > return { }; > } > >-auto Validate::addTableGet(ExpressionType& index, ExpressionType& result) -> Result >+auto Validate::addTableGet(unsigned tableIndex, ExpressionType& index, ExpressionType& result) -> Result > { >- result = m_module.tableInformation.wasmType(); >+ WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount()); >+ result = m_module.tables[tableIndex].wasmType(); > WASM_VALIDATOR_FAIL_IF(Type::I32 != index, "table.get index to type ", index, " expected ", Type::I32); > > return { }; > } > >-auto Validate::addTableSet(ExpressionType& index, ExpressionType& value) -> Result >+auto Validate::addTableSet(unsigned tableIndex, ExpressionType& index, ExpressionType& value) -> Result > { >- auto type = m_module.tableInformation.wasmType(); >+ WASM_VALIDATOR_FAIL_IF(tableIndex >= m_module.tableCount(), "table index ", tableIndex, " is invalid, limit is ", m_module.tableCount()); >+ auto type = m_module.tables[tableIndex].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"); >+ RELEASE_ASSERT(m_module.tables[tableIndex].type() == TableElementType::Anyref || m_module.tables[tableIndex].type() == TableElementType::Funcref); > > return { }; > } >@@ -379,9 +380,10 @@ auto Validate::addCall(unsigned, const Signature& signature, const Vector<Expres > return { }; > } > >-auto Validate::addCallIndirect(const Signature& signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result >+auto Validate::addCallIndirect(unsigned tableIndex, const Signature& signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result > { >- WASM_VALIDATOR_FAIL_IF(m_module.tableInformation.type() != TableElementType::Funcref, "Table must have type Anyfunc to call"); >+ RELEASE_ASSERT(tableIndex < m_module.tableCount()); >+ RELEASE_ASSERT(m_module.tables[tableIndex].type() == TableElementType::Funcref); > const auto argumentCount = signature.argumentCount(); > WASM_VALIDATOR_FAIL_IF(argumentCount != args.size() - 1, "arity mismatch in call_indirect, got ", args.size() - 1, " arguments, expected ", argumentCount); > >diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp >index 323427fd8a0af24ce29364fe404b1670a683b597..99afa1ac6743e928fb559c9827ae4822afcb3e31 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp >@@ -53,6 +53,7 @@ JSWebAssemblyInstance::JSWebAssemblyInstance(VM& vm, Structure* structure, Ref<W > : Base(vm, structure) > , m_instance(WTFMove(instance)) > , m_vm(&vm) >+ , m_tables(m_instance->module().moduleInformation().tableCount()) > { > for (unsigned i = 0; i < this->instance().numImportFunctions(); ++i) > new (this->instance().importFunction<WriteBarrier<JSObject>>(i)) WriteBarrier<JSObject>(); >@@ -85,7 +86,8 @@ void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor) > visitor.append(thisObject->m_codeBlock); > visitor.append(thisObject->m_moduleNamespaceObject); > visitor.append(thisObject->m_memory); >- visitor.append(thisObject->m_table); >+ for (unsigned i = 0; i < thisObject->instance().module().moduleInformation().tableCount(); ++i) >+ visitor.append(thisObject->m_tables[i]); > visitor.append(thisObject->m_callee); > visitor.reportExtraMemoryVisited(thisObject->m_instance->extraMemoryAllocated()); > for (unsigned i = 0; i < thisObject->instance().numImportFunctions(); ++i) >diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h >index 21784a939d8eb47e8f5246e2663ef367704928e5..e1631fcbb1833e018ef7c4d6178091824d4e6128 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h >@@ -71,11 +71,13 @@ public: > } > Wasm::MemoryMode memoryMode() { return memory()->memory().mode(); } > >- JSWebAssemblyTable* table() { return m_table.get(); } >- void setTable(VM& vm, JSWebAssemblyTable* value) { >- ASSERT(!table()); >- m_table.set(vm, this, value); >- instance().setTable(makeRef(*table()->table())); >+ JSWebAssemblyTable* table(unsigned i) { return m_tables[i].get(); } >+ void setTable(VM& vm, uint32_t index, JSWebAssemblyTable* value) >+ { >+ ASSERT(index < m_tables.size()); >+ ASSERT(!table(index)); >+ m_tables[index].set(vm, this, value); >+ instance().setTable(index, makeRef(*table(index)->table())); > } > > JSWebAssemblyModule* module() const { return m_module.get(); } >@@ -98,7 +100,7 @@ private: > WriteBarrier<JSWebAssemblyCodeBlock> m_codeBlock; > WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject; > WriteBarrier<JSWebAssemblyMemory> m_memory; >- WriteBarrier<JSWebAssemblyTable> m_table; >+ Vector<WriteBarrier<JSWebAssemblyTable>> m_tables; > WriteBarrier<WebAssemblyToJSCallee> m_callee; > }; > >diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >index 12b1cef2df46879a2c7f1b371f4c59399bec6613..052d085896895b9d942b4890beb35609ab735f7f 100644 >--- a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >@@ -113,8 +113,6 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > return makeString(before, " ", String::fromUTF8(import.module), ":", String::fromUTF8(import.field), " ", after); > }; > >- bool hasTableImport = false; >- > for (const auto& import : moduleInformation.imports) { > // Validation and linking other than Wasm::ExternalKind::Function is already done in JSWebAssemblyInstance. > // Eventually we will move all the linking code in JSWebAssemblyInstance here and remove this switch statement. >@@ -264,20 +262,18 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > } > > case Wasm::ExternalKind::Table: { >- RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure. > // 7. Otherwise (i is a table import): >- hasTableImport = true; > JSWebAssemblyTable* table = jsDynamicCast<JSWebAssemblyTable*>(vm, value); > // i. If v is not a WebAssembly.Table object, throw a WebAssembly.LinkError. > if (!table) > return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "is not an instance of WebAssembly.Table"))); > >- uint32_t expectedInitial = moduleInformation.tableInformation.initial(); >+ uint32_t expectedInitial = moduleInformation.tables[import.kindIndex].initial(); > uint32_t actualInitial = table->length(); > if (actualInitial < expectedInitial) > return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "provided an 'initial' that is too small"))); > >- if (Optional<uint32_t> expectedMaximum = moduleInformation.tableInformation.maximum()) { >+ if (Optional<uint32_t> expectedMaximum = moduleInformation.tables[import.kindIndex].maximum()) { > Optional<uint32_t> actualMaximum = table->maximum(); > if (!actualMaximum) > return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "does not have a 'maximum' but the module requires that it does"))); >@@ -285,14 +281,14 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Imported Table", "'maximum' is larger than the module's expected 'maximum'"))); > } > >- auto expectedType = moduleInformation.tableInformation.type(); >+ auto expectedType = moduleInformation.tables[import.kindIndex].type(); > auto actualType = table->table()->type(); > if (expectedType != actualType) > return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "provided a 'type' that is wrong"))); > > // ii. Append v to tables. > // iii. Append v.[[Table]] to imports. >- m_instance->setTable(vm, table); >+ m_instance->setTable(vm, import.kindIndex, table); > RETURN_IF_EXCEPTION(scope, void()); > break; > } >@@ -302,16 +298,16 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > } > } > >- { >- if (!!moduleInformation.tableInformation && moduleInformation.tableInformation.isImport()) { >+ for (unsigned i = 0; i < moduleInformation.tableCount(); ++i) { >+ if (moduleInformation.tables[i].isImport()) { > // We should either have a Table import or we should have thrown an exception. >- RELEASE_ASSERT(hasTableImport); >+ RELEASE_ASSERT(m_instance->table(i)); > } > >- if (!!moduleInformation.tableInformation && !hasTableImport) { >- RELEASE_ASSERT(!moduleInformation.tableInformation.isImport()); >+ if (!m_instance->table(i)) { >+ RELEASE_ASSERT(!moduleInformation.tables[i].isImport()); > // We create a Table when it's a Table definition. >- RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum(), moduleInformation.tableInformation.type()); >+ RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(moduleInformation.tables[i].initial(), moduleInformation.tables[i].maximum(), moduleInformation.tables[i].type()); > if (!wasmTable) > return exception(createJSWebAssemblyLinkError(exec, vm, "couldn't create Table")); > JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, globalObject->webAssemblyTableStructure(), wasmTable.releaseNonNull()); >@@ -319,7 +315,7 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > // If it's defined to be too large, we should have thrown a validation error. > scope.assertNoException(); > ASSERT(table); >- m_instance->setTable(vm, table); >+ m_instance->setTable(vm, i, table); > RETURN_IF_EXCEPTION(scope, void()); > } > } >@@ -397,10 +393,8 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > } > case Wasm::ExternalKind::Table: { > // This should be guaranteed by module verification. >- RELEASE_ASSERT(m_instance->table()); >- ASSERT(exp.kindIndex == 0); >- >- exportedValue = m_instance->table(); >+ RELEASE_ASSERT(m_instance->table(exp.kindIndex)); >+ exportedValue = m_instance->table(exp.kindIndex); > break; > } > case Wasm::ExternalKind::Memory: { >@@ -486,7 +480,6 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec) > Wasm::Module& module = m_instance->instance().module(); > Wasm::CodeBlock* codeBlock = m_instance->instance().codeBlock(); > const Wasm::ModuleInformation& moduleInformation = module.moduleInformation(); >- JSWebAssemblyTable* table = m_instance->table(); > > const Vector<Wasm::Segment::Ptr>& data = moduleInformation.data; > >@@ -498,16 +491,16 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec) > // Also, it could be that a table wasn't imported, or that the table > // imported wasn't compatible. However, those should error out before > // getting here. >- ASSERT(!!table); >+ ASSERT(!!m_instance->table(element.tableIndex)); > > if (!element.functionIndices.size()) > continue; > >- uint32_t tableIndex = element.offset.isGlobalImport() >+ uint32_t elementIndex = element.offset.isGlobalImport() > ? static_cast<uint32_t>(m_instance->instance().loadI32Global(element.offset.globalImportIndex())) > : element.offset.constValue(); > >- fn(element, tableIndex); >+ fn(element, element.tableIndex, elementIndex); > > if (exception) > break; >@@ -531,9 +524,9 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec) > }; > > // Validation of all element ranges comes before all Table and Memory initialization. >- forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex) { >- uint64_t lastWrittenIndex = static_cast<uint64_t>(tableIndex) + static_cast<uint64_t>(element.functionIndices.size()) - 1; >- if (UNLIKELY(lastWrittenIndex >= table->length())) >+ forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex, uint32_t elementIndex) { >+ uint64_t lastWrittenIndex = static_cast<uint64_t>(elementIndex) + static_cast<uint64_t>(element.functionIndices.size()) - 1; >+ if (UNLIKELY(lastWrittenIndex >= m_instance->table(tableIndex)->length())) > exception = JSValue(throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, "Element is trying to set an out of bounds table index"_s))); > }); > >@@ -552,7 +545,7 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec) > return exception.value(); > > JSGlobalObject* globalObject = m_instance->globalObject(vm); >- forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex) { >+ forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex, uint32_t elementIndex) { > for (uint32_t i = 0; i < element.functionIndices.size(); ++i) { > // FIXME: This essentially means we're exporting an import. > // We need a story here. We need to create a WebAssemblyFunction >@@ -568,14 +561,14 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec) > // Because a WebAssemblyWrapperFunction can never wrap another WebAssemblyWrapperFunction, > // the only type this could be is WebAssemblyFunction. > RELEASE_ASSERT(wasmFunction); >- table->set(tableIndex, wasmFunction); >- ++tableIndex; >+ m_instance->table(tableIndex)->set(elementIndex, wasmFunction); >+ ++elementIndex; > continue; > } > >- table->set(tableIndex, >+ m_instance->table(tableIndex)->set(elementIndex, > WebAssemblyWrapperFunction::create(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, functionIndex, m_instance.get(), signatureIndex)); >- ++tableIndex; >+ ++elementIndex; > continue; > } > >@@ -589,8 +582,8 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec) > WebAssemblyFunction* function = WebAssemblyFunction::create( > vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), String(), m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex); > >- table->set(tableIndex, function); >- ++tableIndex; >+ m_instance->table(tableIndex)->set(elementIndex, function); >+ ++elementIndex; > } > }); > >diff --git a/Source/JavaScriptCore/wasm/wasm.json b/Source/JavaScriptCore/wasm/wasm.json >index 75285d6fb700901ca5275ea3fd75bb18d30e413b..96e8a1d207729900d39518b10965e6c576d0f828 100644 >--- a/Source/JavaScriptCore/wasm/wasm.json >+++ b/Source/JavaScriptCore/wasm/wasm.json >@@ -67,10 +67,10 @@ > "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" }, > "get_global": { "category": "special", "value": 35, "return": ["any"], "parameter": [], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "read a global variable" }, > "set_global": { "category": "special", "value": 36, "return": [], "parameter": ["any"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "write a global variable" }, >- "table.get": { "category": "special", "value": 37, "return": ["anyref"], "parameter": ["i32"], "immediate": [], "description": "get a table value" }, >- "table.set": { "category": "special", "value": 38, "return": [], "parameter": ["i32", "anyref"], "immediate": [], "description": "set a table value" }, >+ "table.get": { "category": "special", "value": 37, "return": ["anyref"], "parameter": ["i32"], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "get a table value" }, >+ "table.set": { "category": "special", "value": 38, "return": [], "parameter": ["i32", "anyref"], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "set a table value" }, > "call": { "category": "call", "value": 16, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "function_index", "type": "varuint32"}], "description": "call a function by its index" }, >- "call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}, {"name": "reserved", "type": "varuint1"}], "description": "call a function indirect with an expected signature" }, >+ "call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}, {"name": "table_index","type": "varuint32"}],"description": "call a function indirect with an expected signature" }, > "i32.load8_s": { "category": "memory", "value": 44, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, > "i32.load8_u": { "category": "memory", "value": 45, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, > "i32.load16_s": { "category": "memory", "value": 46, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index 82b2721fdba9251897e6e9cb13057715d7463195..16db89d26367303e69bb6fb08438f603d1a5f3c6 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,83 @@ >+2019-06-17 Justin Michaud <justin_michaud@apple.com> >+ >+ [WASM-References] Add support for multiple tables >+ https://bugs.webkit.org/show_bug.cgi?id=198760 >+ >+ Reviewed by Saam Barati. >+ >+ * wasm/Builder.js: >+ * wasm/js-api/call-indirect.js: >+ (const.oneTable): >+ (const.multiTable): >+ (multiTable): >+ (multiTable.Polyphic2Import): >+ (multiTable.VirtualImport): >+ (const.wasmModuleWhichImportJS): Deleted. >+ (const.makeTable): Deleted. >+ (): Deleted. >+ (Polyphic2Import): Deleted. >+ (VirtualImport): Deleted. >+ * wasm/js-api/table.js: >+ (new.WebAssembly.Module): >+ (assert.throws): >+ (assertBadTableImport): >+ (assert.truthy): >+ (assert.throws.new.WebAssembly.Module.builder.WebAssembly): Deleted. >+ * wasm/references/anyref_table.js: >+ * wasm/references/anyref_table_import.js: >+ (makeImport): >+ (string_appeared_here.fullGC.assert.eq.1.exports.get_tbl.makeImport): >+ (string_appeared_here.fullGC.assert.eq.1.exports.get_tbl): >+ * wasm/references/multitable.js: Added. >+ (assert.throws.1.exports.set_tbl0): >+ (assert.throws): >+ (assert.eq): >+ * wasm/references/validation.js: >+ (assert.throws.new.WebAssembly.Module.bin): >+ (assert.throws): >+ * wasm/spec-tests/imports.wast.js: >+ * wasm/wasm.json: >+ >+ * wasm/Builder.js: >+ * wasm/js-api/call-indirect.js: >+ (const.oneTable): >+ (const.multiTable): >+ (multiTable): >+ (multiTable.Polyphic2Import): >+ (multiTable.VirtualImport): >+ (const.wasmModuleWhichImportJS): Deleted. >+ (const.makeTable): Deleted. >+ (): Deleted. >+ (Polyphic2Import): Deleted. >+ (VirtualImport): Deleted. >+ * wasm/js-api/table.js: >+ (new.WebAssembly.Module): >+ (assert.throws): >+ (assertBadTableImport): >+ (assert.truthy): >+ (assert.throws.new.WebAssembly.Module.builder.WebAssembly): Deleted. >+ * wasm/references/anyref_table.js: >+ * wasm/references/anyref_table_import.js: >+ (makeImport): >+ (string_appeared_here.fullGC.assert.eq.1.exports.get_tbl.makeImport): >+ (string_appeared_here.fullGC.assert.eq.1.exports.get_tbl): >+ * wasm/references/func_ref.js: >+ (GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly.fun): Deleted. >+ (GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly.assert.throws): Deleted. >+ (GetLocal.0.I32Const.0.TableSet.End.End.WebAssembly): Deleted. >+ * wasm/references/multitable.js: Added. >+ (assert.throws.1.exports.set_tbl0): >+ (assert.throws): >+ (assert.eq): >+ (string_appeared_here.tableInsanity): >+ (I32Const.0.GetLocal.0.TableSet.1.End.End.WebAssembly.): >+ (I32Const.0.GetLocal.0.TableSet.1.End.End.WebAssembly): >+ * wasm/references/validation.js: >+ (assert.throws.new.WebAssembly.Module.bin): >+ (assert.throws): >+ * wasm/spec-tests/imports.wast.js: >+ * wasm/wasm.json: >+ > 2019-06-17 Justin Michaud <justin_michaud@apple.com> > > Validate that table element type is funcref if using an element section >diff --git a/JSTests/wasm/Builder.js b/JSTests/wasm/Builder.js >index 65f1f477a98b8620fccd2aa8f8c1109e0af67866..976377f7b3d913d4b8db1f8a7596e73d2791cb47 100644 >--- a/JSTests/wasm/Builder.js >+++ b/JSTests/wasm/Builder.js >@@ -306,6 +306,7 @@ const _checkImms = (op, imms, expectedImms, ret) => { > case "target_count": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421 > case "target_table": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421 > case "reserved": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421 >+ case "table_index": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421 > default: throw new Error(`Implementation problem: unhandled immediate "${expect.name}" on "${op}"`); > } > } >diff --git a/JSTests/wasm/js-api/call-indirect.js b/JSTests/wasm/js-api/call-indirect.js >index 01cd42f9b79a921b1bbaa5ab576fc0d910438328..2fafcc192b120539c7aa4551d443daae7bd81972 100644 >--- a/JSTests/wasm/js-api/call-indirect.js >+++ b/JSTests/wasm/js-api/call-indirect.js >@@ -1,7 +1,7 @@ > import * as assert from '../assert.js'; > import Builder from '../Builder.js'; > >-const wasmModuleWhichImportJS = () => { >+const oneTable = () => { > const builder = (new Builder()) > .Type().End() > .Import() >@@ -31,6 +31,39 @@ const wasmModuleWhichImportJS = () => { > return module; > }; > >+const multiTable = () => { >+ const builder = (new Builder()) >+ .Type().End() >+ .Import() >+ .Function("imp", "func", { params: ["i32"] }) >+ .Table("imp", "table0", { initial: 0, maximum: 0, element: "anyfunc"}) >+ .Table("imp", "table", { initial: 1, maximum: 1, element: "anyfunc"}) >+ .End() >+ .Function().End() >+ .Export() >+ .Function("changeCounter") >+ .Function("callFunc") >+ .End() >+ .Code() >+ .Function("changeCounter", { params: ["i32", "i32"] }) >+ .I32Const(42) >+ .GetLocal(0) >+ .I32Add() >+ .GetLocal(1) >+ .CallIndirect(0, 1) // Calls table[0](param[0] + 42). >+ .End() >+ .Function("callFunc", { params: ["i32"] }) >+ .GetLocal(0) >+ .Call(0) // Calls func(param[0] + 42) >+ .End() >+ .End(); >+ const bin = builder.WebAssembly().get(); >+ const module = new WebAssembly.Module(bin); >+ return module; >+}; >+ >+for (const wasmModuleWhichImportJS of [oneTable, multiTable]) { >+ > const makeTable = () => { > return new WebAssembly.Table({initial: 1, maximum: 1, element: "anyfunc"}); > }; >@@ -40,7 +73,7 @@ const makeTable = () => { > const counterSetter = v => counter = v; > const table = makeTable(); > const module = wasmModuleWhichImportJS(); >- const instance = new WebAssembly.Instance(module, { imp: { func: counterSetter, table} }); >+ const instance = new WebAssembly.Instance(module, { imp: { func: counterSetter, table, table0: new WebAssembly.Table({initial: 0, maximum: 0, element: "anyfunc"}) } }); > table.set(0, instance.exports.callFunc); > for (let i = 0; i < 4096; ++i) { > // Invoke this a bunch of times to make sure the IC in the wasm -> JS stub works correctly. >@@ -57,11 +90,11 @@ const makeTable = () => { > const module = wasmModuleWhichImportJS(); > > const tableA = makeTable(); >- const instanceA = new WebAssembly.Instance(module, { imp: { func: counterASetter, table: tableA} }); >+ const instanceA = new WebAssembly.Instance(module, { imp: { func: counterASetter, table: tableA, table0: new WebAssembly.Table({initial: 0, maximum: 0, element: "anyfunc"}) } }); > tableA.set(0, instanceA.exports.callFunc); > > const tableB = makeTable(); >- const instanceB = new WebAssembly.Instance(module, { imp: { func: counterBSetter, table: tableB} }); >+ const instanceB = new WebAssembly.Instance(module, { imp: { func: counterBSetter, table: tableB, table0: new WebAssembly.Table({initial: 0, maximum: 0, element: "anyfunc"}) } }); > tableB.set(0, instanceB.exports.callFunc); > for (let i = 0; i < 2048; ++i) { > instanceA.exports.changeCounter(i, 0); >@@ -93,7 +126,7 @@ const makeTable = () => { > let instances = []; > for (let i = 0; i < num; ++i) { > let table = makeTable(); >- instances[i] = new WebAssembly.Instance(module, { imp: { func: counterSetters[i], table} }); >+ instances[i] = new WebAssembly.Instance(module, { imp: { func: counterSetters[i], table, table0: new WebAssembly.Table({initial: 0, maximum: 0, element: "anyfunc"}) } }); > table.set(0, instances[i].exports.callFunc); > } > for (let i = 0; i < 2048; ++i) { >@@ -104,3 +137,5 @@ const makeTable = () => { > } > } > })(); >+ >+} >diff --git a/JSTests/wasm/js-api/table.js b/JSTests/wasm/js-api/table.js >index bbca89ec34f71912084f6309aac2d64550a43bd1..1b844431ce65f91c4375325d5cae53dc8bc243c7 100644 >--- a/JSTests/wasm/js-api/table.js >+++ b/JSTests/wasm/js-api/table.js >@@ -13,7 +13,7 @@ import * as assert from '../assert.js'; > .End() > .Code() > .End(); >- assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 34: Cannot have more than one Table for now"); >+ new WebAssembly.Module(builder.WebAssembly().get()) > } > > { >@@ -38,7 +38,7 @@ import * as assert from '../assert.js'; > .End() > .Code() > .End(); >- assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 17: Table count of 2 is invalid, at most 1 is allowed for now (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"); >+ new WebAssembly.Module(builder.WebAssembly().get()) > } > > { >@@ -73,7 +73,28 @@ import * as assert from '../assert.js'; > .CallIndirect(0, 1) > .End() > .End(); >- assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 6: call_indirect's 'reserved' varuint1 must be 0x0, in function at index 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"); >+ assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 6: call_indirect's table index 1 invalid, limit is 1, in function at index 0 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"); >+} >+ >+{ >+ const builder = new Builder() >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial:20, element:"anyfunc"}) >+ .Table({initial:20, element:"anyfunc"}) >+ .End() >+ .Export() >+ .Function("foo") >+ .End() >+ .Code() >+ .Function("foo", {params: ["i32"]}) >+ .GetLocal(0) >+ .GetLocal(0) >+ .CallIndirect(0, 1) >+ .End() >+ .End(); >+ new WebAssembly.Module(builder.WebAssembly().get()) > } > > { >@@ -186,7 +207,7 @@ function assertBadTableImport(tableDescription, message) { > .Function().End() > .Code() > .End(); >- assert.throws(() => new WebAssembly.Module(builder.WebAssembly().get()), WebAssembly.CompileError, "WebAssembly.Module doesn't parse at byte 39: Cannot have more than one Table for now"); >+ new WebAssembly.Module(builder.WebAssembly().get()) > } > > >@@ -320,3 +341,26 @@ assert.throws(() => WebAssembly.Table.prototype.grow(undefined), TypeError, `exp > assert.eq(instance.exports.table.length, 20); > assert.truthy(instance.exports.table instanceof WebAssembly.Table); > } >+ >+{ >+ const builder = new Builder() >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 0, maximum: 1, element: "anyfunc"}) >+ .Table({initial: 20, maximum: 30, element: "anyfunc"}) >+ .End() >+ .Export() >+ .Table("table0", 0) >+ .Table("table", 1) >+ .Table("table2", 1) >+ .End() >+ .Code().End(); >+ >+ const module = new WebAssembly.Module(builder.WebAssembly().get()); >+ const instance = new WebAssembly.Instance(module); >+ assert.eq(instance.exports.table, instance.exports.table2); >+ assert.eq(instance.exports.table.length, 20); >+ assert.eq(instance.exports.table0.length, 0); >+ assert.truthy(instance.exports.table instanceof WebAssembly.Table); >+} >diff --git a/JSTests/wasm/references/anyref_table.js b/JSTests/wasm/references/anyref_table.js >index b2bcaecbebdfd3683b3178d42e585060bb99be20..bc95f67da16fe652c96c669265c6644886ee8674 100644 >--- a/JSTests/wasm/references/anyref_table.js >+++ b/JSTests/wasm/references/anyref_table.js >@@ -18,12 +18,12 @@ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) > .Function("set_tbl", { params: ["anyref"], ret: "void" }) > .I32Const(0) > .GetLocal(0) >- .TableSet() >+ .TableSet(0) > .End() > > .Function("get_tbl", { params: [], ret: "anyref" }) > .I32Const(0) >- .TableGet() >+ .TableGet(0) > .End() > > .Function("tbl_is_null", { params: [], ret: "i32" }) >diff --git a/JSTests/wasm/references/anyref_table_import.js b/JSTests/wasm/references/anyref_table_import.js >index 88cab7c23fe7b40ad69ec236d4bb69d23518b63e..1742bb208c0a82424270ed5b90ff0a6b40875301 100644 >--- a/JSTests/wasm/references/anyref_table_import.js >+++ b/JSTests/wasm/references/anyref_table_import.js >@@ -22,12 +22,12 @@ import Builder from '../Builder.js'; > .Function("set_tbl", { params: ["anyref"], ret: "void" }) > .I32Const(0) > .GetLocal(0) >- .TableSet() >+ .TableSet(0) > .End() > > .Function("get_tbl", { params: [], ret: "anyref" }) > .I32Const(0) >- .TableGet() >+ .TableGet(0) > .End() > > .Function("tbl_is_null", { params: [], ret: "i32" }) >@@ -88,12 +88,12 @@ import Builder from '../Builder.js'; > .Function("set_tbl", { params: ["anyref"], ret: "void" }) > .I32Const(0) > .GetLocal(0) >- .TableSet() >+ .TableSet(0) > .End() > > .Function("get_tbl", { params: [], ret: "anyref" }) > .I32Const(0) >- .TableGet() >+ .TableGet(0) > .End() > > .Function("tbl_is_null", { params: [], ret: "i32" }) >@@ -127,12 +127,12 @@ import Builder from '../Builder.js'; > .Function("set_tbl", { params: ["anyref"], ret: "void" }) > .I32Const(0) > .GetLocal(0) >- .TableSet() >+ .TableSet(0) > .End() > > .Function("get_tbl", { params: [], ret: "anyref" }) > .I32Const(0) >- .TableGet() >+ .TableGet(0) > .End() > > .Function("tbl_is_null", { params: [], ret: "i32" }) >diff --git a/JSTests/wasm/references/func_ref.js b/JSTests/wasm/references/func_ref.js >index 35e06e4658387283c58335b9b66f77853f9bb102..36a79b6f34223267de4ee4d42b76715659a4968b 100644 >--- a/JSTests/wasm/references/func_ref.js >+++ b/JSTests/wasm/references/func_ref.js >@@ -257,7 +257,7 @@ assert.throws(() => new WebAssembly.Module((new Builder()) > .Function("h", { params: ["i32"], ret: "void" }) > .GetLocal(0) > .I32Const(0) >- .TableSet() >+ .TableSet(0) > .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')"); > >@@ -267,6 +267,7 @@ assert.throws(() => new WebAssembly.Module((new Builder()) > .Type().End() > .Function().End() > .Table() >+ .Table({initial: 0, element: "anyref"}) > .Table({initial: 1, element: "anyfunc"}) > .End() > .Global() >@@ -291,11 +292,11 @@ assert.throws(() => new WebAssembly.Module((new Builder()) > .Function("call_glob", { params: ["i32"], ret: "i32" }) > .I32Const(0) > .GetGlobal(0) >- .TableSet() >+ .TableSet(1) > > .GetLocal(0) > .I32Const(0) >- .CallIndirect(2,0) >+ .CallIndirect(2,1) > .End() > > .Function("ret_20", { params: ["i32"], ret: "i32" }) >@@ -328,6 +329,7 @@ assert.throws(() => new WebAssembly.Module((new Builder()) > .Type().End() > .Function().End() > .Table() >+ .Table({initial: 0, element: "anyref"}) > .Table({initial: 1, element: "anyfunc"}) > .End() > .Export() >@@ -338,12 +340,12 @@ assert.throws(() => new WebAssembly.Module((new Builder()) > .Function("set", { params: ["anyfunc"], ret: "void" }) > .I32Const(0) > .GetLocal(0) >- .TableSet() >+ .TableSet(1) > .End() > > .Function("get", { params: [], ret: "anyfunc" }) > .I32Const(0) >- .TableGet() >+ .TableGet(1) > .End() > .End().WebAssembly().get())); > >@@ -405,7 +407,7 @@ for (let importedFun of [function(i) { return i; }, makeAnyfuncIdent()]) { > .GetLocal(0) > .I32Const(0) > .RefFunc(0) >- .TableSet() >+ .TableSet(0) > .I32Const(0) > .CallIndirect(0, 0) > .End() >@@ -414,7 +416,7 @@ for (let importedFun of [function(i) { return i; }, makeAnyfuncIdent()]) { > .RefFunc(1) > .I32Const(0) > .RefFunc(0) >- .TableSet() >+ .TableSet(0) > .I32Const(0) > .CallIndirect(0, 0) > .End() >diff --git a/JSTests/wasm/references/multitable.js b/JSTests/wasm/references/multitable.js >new file mode 100644 >index 0000000000000000000000000000000000000000..371c2f04d98d03fa362b24023856a8c924f12d98 >--- /dev/null >+++ b/JSTests/wasm/references/multitable.js >@@ -0,0 +1,460 @@ >+import * as assert from '../assert.js'; >+import Builder from '../Builder.js'; >+ >+{ >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 0, maximum: 0, element: "anyref"}) >+ .Table({initial: 20, maximum: 30, element: "anyref"}) >+ .End() >+ .Export() >+ .Function("set_tbl") >+ .Function("get_tbl") >+ .Function("get_tbl0") >+ .Function("set_tbl0") >+ .Table("tbl", 1) >+ .End() >+ .Code() >+ .Function("set_tbl", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet(1) >+ .End() >+ >+ .Function("get_tbl", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet(1) >+ .End() >+ >+ .Function("get_tbl0", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet(0) >+ .End() >+ >+ .Function("set_tbl0", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet(0) >+ .End() >+ .End().WebAssembly().get())); >+ >+ fullGC() >+ >+ assert.eq($1.exports.get_tbl(), null) >+ >+ $1.exports.set_tbl("hi") >+ fullGC() >+ assert.eq($1.exports.get_tbl(), "hi") >+ assert.eq($1.exports.tbl.get(0), "hi") >+ assert.eq($1.exports.tbl.get(1), null) >+ >+ assert.throws(() => $1.exports.get_tbl0(), Error, "Out of bounds table access (evaluating 'func(...args)')"); >+ assert.throws(() => $1.exports.set_tbl0(null), Error, "Out of bounds table access (evaluating 'func(...args)')"); >+} >+ >+{ >+ const tbl = new WebAssembly.Table({initial:0, element:"anyref"}); >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Import() >+ .Table("imp", "tbl", {initial: 0, element: "anyref"}) >+ .End() >+ .Function().End() >+ .Table() >+ .Table({initial: 20, maximum: 30, element: "anyref"}) >+ .End() >+ .Export() >+ .Function("set_tbl") >+ .Function("get_tbl") >+ .Function("get_tbl0") >+ .Function("set_tbl0") >+ .Table("tbl", 1) >+ .End() >+ .Code() >+ .Function("set_tbl", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet(1) >+ .End() >+ >+ .Function("get_tbl", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet(1) >+ .End() >+ >+ .Function("get_tbl0", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet(0) >+ .End() >+ >+ .Function("set_tbl0", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet(0) >+ .End() >+ .End().WebAssembly().get()), { imp: { tbl }}); >+ >+ fullGC() >+ >+ assert.eq($1.exports.get_tbl(), null) >+ >+ $1.exports.set_tbl("hi") >+ fullGC() >+ assert.eq($1.exports.get_tbl(), "hi") >+ assert.eq($1.exports.tbl.get(0), "hi") >+ assert.eq($1.exports.tbl.get(1), null) >+ >+ assert.throws(() => $1.exports.get_tbl0(), Error, "Out of bounds table access (evaluating 'func(...args)')"); >+ assert.throws(() => $1.exports.set_tbl0(null), Error, "Out of bounds table access (evaluating 'func(...args)')"); >+} >+ >+{ >+ const tbl = new WebAssembly.Table({initial:1, element:"anyref"}); >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Import() >+ .Table("imp", "tbl", {initial: 1, element: "anyref"}) >+ .End() >+ .Function().End() >+ .Table() >+ .Table({initial: 20, maximum: 30, element: "anyref"}) >+ .End() >+ .Export() >+ .Function("set_tbl") >+ .Function("get_tbl") >+ .Function("get_tbl0") >+ .Function("set_tbl0") >+ .Table("tbl", 1) >+ .End() >+ .Code() >+ .Function("set_tbl", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet(1) >+ .End() >+ >+ .Function("get_tbl", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet(1) >+ .End() >+ >+ .Function("get_tbl0", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet(0) >+ .End() >+ >+ .Function("set_tbl0", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet(0) >+ .End() >+ .End().WebAssembly().get()), { imp: { tbl }}); >+ >+ fullGC() >+ >+ assert.eq($1.exports.get_tbl(), null) >+ >+ $1.exports.set_tbl("hi") >+ fullGC() >+ $1.exports.set_tbl0(null) >+ assert.eq($1.exports.get_tbl(), "hi") >+ assert.eq($1.exports.get_tbl0(), null) >+ assert.eq($1.exports.tbl.get(0), "hi") >+ assert.eq($1.exports.tbl.get(1), null) >+ assert.eq(tbl.get(0), null) >+} >+ >+{ >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 30, element: "anyfunc"}) >+ .Table({initial: 2, maximum: 30, element: "anyfunc"}) >+ .End() >+ .Export() >+ .Function("call_tbl0") >+ .Function("call_tbl1") >+ .Function("ret42") >+ .Function("ret1337") >+ .Function("ret256") >+ .End() >+ .Element() >+ .Element({tableIndex: 1, offset: 0, functionIndices: [2]}) >+ .End() >+ .Code() >+ .Function("call_tbl0", { params: ["i32"], ret: "i32" }) >+ .GetLocal(0) >+ .CallIndirect(1,0) >+ .End() >+ >+ .Function("call_tbl1", { params: ["i32"], ret: "i32" }) >+ .GetLocal(0) >+ .CallIndirect(1,1) >+ .End() >+ >+ .Function("ret42", { params: [], ret: "i32" }) >+ .I32Const(42) >+ .End() >+ >+ .Function("ret1337", { params: [], ret: "i32" }) >+ .I32Const(1337) >+ .End() >+ >+ .Function("ret256", { params: [], ret: "i32" }) >+ .I32Const(256) >+ .End() >+ .End().WebAssembly().get())); >+ >+ fullGC() >+ >+ assert.eq($1.exports.call_tbl1(0), 42) >+ assert.throws(() => $1.exports.call_tbl0(0), Error, "call_indirect to a null table entry (evaluating 'func(...args)')") >+ assert.throws(() => $1.exports.call_tbl1(1), Error, "call_indirect to a null table entry (evaluating 'func(...args)')") >+} >+ >+{ >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 30, element: "anyfunc"}) >+ .Table({initial: 2, maximum: 30, element: "anyfunc"}) >+ .End() >+ .Export() >+ .Function("call_tbl0") >+ .Function("call_tbl1") >+ .Function("ret42") >+ .Function("ret1337") >+ .Function("ret256") >+ .End() >+ .Element() >+ .Element({tableIndex: 1, offset: 0, functionIndices: [2]}) >+ .Element({tableIndex: 0, offset: 0, functionIndices: [3]}) >+ .Element({tableIndex: 1, offset: 1, functionIndices: [4]}) >+ .End() >+ .Code() >+ .Function("call_tbl0", { params: ["i32"], ret: "i32" }) >+ .GetLocal(0) >+ .CallIndirect(1,0) >+ .End() >+ >+ .Function("call_tbl1", { params: ["i32"], ret: "i32" }) >+ .GetLocal(0) >+ .CallIndirect(1,1) >+ .End() >+ >+ .Function("ret42", { params: [], ret: "i32" }) >+ .I32Const(42) >+ .End() >+ >+ .Function("ret1337", { params: [], ret: "i32" }) >+ .I32Const(1337) >+ .End() >+ >+ .Function("ret256", { params: [], ret: "i32" }) >+ .I32Const(256) >+ .End() >+ .End().WebAssembly().get())); >+ >+ fullGC() >+ >+ assert.eq($1.exports.call_tbl1(0), 42) >+ assert.eq($1.exports.call_tbl0(0), 1337) >+ assert.eq($1.exports.call_tbl1(1), 256) >+ assert.throws(() => $1.exports.call_tbl0(1), Error, "call_indirect to a null table entry (evaluating 'func(...args)')") >+ assert.throws(() => $1.exports.call_tbl0(2), Error, "call_indirect to a null table entry (evaluating 'func(...args)')") >+ assert.throws(() => $1.exports.call_tbl1(2), Error, "Out of bounds call_indirect (evaluating 'func(...args)')") >+} >+ assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 3, element: "anyfunc"}) >+ .Table({initial: 2, maximum: 2, element: "anyfunc"}) >+ .End() >+ .Element() >+ .Element({tableIndex: 1, offset: 0, functionIndices: [0]}) >+ .Element({tableIndex: 0, offset: 0, functionIndices: [0]}) >+ .Element({tableIndex: 1, offset: 2, functionIndices: [0]}) >+ .End() >+ .Code() >+ .Function("ret42", { params: [], ret: "i32" }) >+ .I32Const(42) >+ .End() >+ .End().WebAssembly().get())), Error, "Element is trying to set an out of bounds table index (evaluating 'new WebAssembly.Instance')") >+ >+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 3, element: "anyref"}) >+ .Table({initial: 2, maximum: 3, element: "anyfunc"}) >+ .End() >+ .Element() >+ .Element({tableIndex: 1, offset: 0, functionIndices: [0]}) >+ .Element({tableIndex: 0, offset: 0, functionIndices: [0]}) >+ .Element({tableIndex: 1, offset: 2, functionIndices: [0]}) >+ .End() >+ .Code() >+ .Function("ret42", { params: [], ret: "i32" }) >+ .I32Const(42) >+ .End() >+ .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't parse at byte 40: Table 0 must have type 'anyfunc' to have an element section (evaluating 'new WebAssembly.Module')") >+ >+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 3, element: "anyref"}) >+ .Table({initial: 2, maximum: 3, element: "anyfunc"}) >+ .End() >+ .Code() >+ .Function("fun", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet(2) >+ .End() >+ .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't validate: table index 2 is invalid, limit is 2, in function at index 0 (evaluating 'new WebAssembly.Module')") >+ >+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 3, element: "anyref"}) >+ .Table({initial: 2, maximum: 3, element: "anyfunc"}) >+ .End() >+ .Code() >+ .Function("fun", { params: [], ret: "void" }) >+ .I32Const(0) >+ .RefNull() >+ .TableSet(2) >+ .End() >+ .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't validate: table index 2 is invalid, limit is 2, in function at index 0 (evaluating 'new WebAssembly.Module')") >+ >+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 3, element: "anyref"}) >+ .Table({initial: 2, maximum: 3, element: "anyfunc"}) >+ .End() >+ .Code() >+ .Function("fun", { params: [], ret: "void" }) >+ .CallIndirect(0, 2) >+ .End() >+ .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't parse at byte 4: call_indirect's table index 2 invalid, limit is 2, in function at index 0 (evaluating 'new WebAssembly.Module')") >+ >+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 3, element: "anyref"}) >+ .Table({initial: 2, maximum: 3, element: "anyfunc"}) >+ .End() >+ .Code() >+ .Function("fun", { params: [], ret: "void" }) >+ .CallIndirect(0,0) >+ .End() >+ .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't parse at byte 4: call_indirect is only valid when a table has type anyfunc, in function at index 0 (evaluating 'new WebAssembly.Module')") >+ >+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 3, element: "anyref"}) >+ .Table({initial: 2, maximum: 3, element: "anyfunc"}) >+ .End() >+ .Code() >+ .Function("fun", { params: [], ret: "void" }) >+ .RefNull() >+ .TableGet(0) >+ .End() >+ .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't validate: table.get index to type Anyfunc expected I32, in function at index 0 (evaluating 'new WebAssembly.Module')") >+ >+ >+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 3, element: "anyref"}) >+ .Table({initial: 2, maximum: 3, element: "anyfunc"}) >+ .End() >+ .Code() >+ .Function("fun", { params: [], ret: "void" }) >+ .RefNull() >+ .RefNull() >+ .TableSet(0) >+ .End() >+ .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't validate: table.set index to type Anyfunc expected I32, in function at index 0 (evaluating 'new WebAssembly.Module')") >+ >+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Function().End() >+ .Table() >+ .Table({initial: 3, maximum: 3, element: "anyref"}) >+ .Table({initial: 2, maximum: 3, element: "anyfunc"}) >+ .End() >+ .Code() >+ .Function("fun", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet(1) >+ .End() >+ .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't validate: table.set value to type Anyref expected Anyfunc, in function at index 0 (evaluating 'new WebAssembly.Module')") >+ >+function tableInsanity(num, b) { >+ b = b.Import() >+ for (let i=0; i<100000-1; ++i) >+ b = b.Function("imp", "ref", { params: [], ret: "void" }) >+ b = b.End().Function().End().Table() >+ for (let i=0; i<num; ++i) >+ b = b.Table({initial: 0, maximum: 3, element: "anyref"}) >+ return b >+} >+ >+assert.throws(() => new WebAssembly.Instance(new WebAssembly.Module(tableInsanity(1000000, (new Builder()) >+ .Type().End()) >+ .Table({initial: 3, maximum: 3, element: "anyref"}) >+ .End() >+ .Code() >+ .Function("fun", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet(1) >+ .End() >+ .End().WebAssembly().get())), Error, "WebAssembly.Module doesn't parse at byte 5000027: Table count of 1000000 is too big, maximum 1000000 (evaluating 'new WebAssembly.Module')") >+{ >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module(tableInsanity(1000000-2, (new Builder()) >+ .Type().End()) >+ .Table({initial: 3, maximum: 3, element: "anyfunc"}) >+ .Table({initial: 3, maximum: 3, element: "anyref"}) >+ .End() >+ .Export() >+ .Function("set_tbl") >+ .Function("get_tbl") >+ .Function("call") >+ .End() >+ .Element() >+ .Element({tableIndex: 1000000-2, offset: 0, functionIndices: [0]}) >+ .End() >+ .Code() >+ .Function("set_tbl", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet(1000000-1) >+ .End() >+ .Function("get_tbl", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet(1000000-1) >+ .End() >+ .Function("call", { params: [], ret: "void" }) >+ .I32Const(0) >+ .CallIndirect(0, 1000000-2) >+ .End() >+ .End().WebAssembly().get()), { imp: { ref: function () {} } }) >+ $1.exports.set_tbl("hi") >+ assert.eq($1.exports.get_tbl(), "hi") >+ $1.exports.call() >+} >diff --git a/JSTests/wasm/references/validation.js b/JSTests/wasm/references/validation.js >index 8f816369a74e89e49e6909df445550fd0f66c216..7f7d18427b75d37052ad64845e3715c1caedee3d 100644 >--- a/JSTests/wasm/references/validation.js >+++ b/JSTests/wasm/references/validation.js >@@ -24,6 +24,9 @@ import Builder from '../Builder.js'; > { > const builder = (new Builder()) > .Type().End() >+ .Import() >+ .Table("imp", "tbl", {initial: 2, element: "anyfunc"}) >+ .End() > .Function().End() > .Export() > .Function("j") >@@ -32,14 +35,14 @@ import Builder from '../Builder.js'; > .Function("j", { params: [], ret: "void" }) > .I32Const(0) > .I32Const(0) >- .TableSet() >+ .TableSet(0) > .End() > .End(); > > const bin = builder.WebAssembly(); > bin.trim(); > >- assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: table.set value to type I32 expected 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 I32 expected Anyfunc, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')"); > } > > { >@@ -56,7 +59,7 @@ import Builder from '../Builder.js'; > .Function("j", { params: ["anyref"], ret: "void" }) > .I32Const(0) > .GetLocal(0) >- .TableSet() >+ .TableSet(0) > .End() > .End(); > >@@ -79,7 +82,7 @@ import Builder from '../Builder.js'; > .Code() > .Function("j", { params: [], ret: "anyref" }) > .I32Const(0) >- .TableGet() >+ .TableGet(0) > .End() > .End(); > >diff --git a/JSTests/wasm/spec-tests/imports.wast.js b/JSTests/wasm/spec-tests/imports.wast.js >index 48d140966a4f758367080297a4c03caa74833531..6b3faafad96f7ebef4298d429abd91b0bfa8668b 100644 >--- a/JSTests/wasm/spec-tests/imports.wast.js >+++ b/JSTests/wasm/spec-tests/imports.wast.js >@@ -188,15 +188,6 @@ assert_trap(() => call($14, "call", [3])); > // imports.wast:290 > assert_trap(() => call($14, "call", [100])); > >-// imports.wast:293 >-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x8d\x80\x80\x80\x00\x02\x00\x00\x01\x70\x00\x0a\x00\x00\x01\x70\x00\x0a"); >- >-// imports.wast:297 >-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x87\x80\x80\x80\x00\x01\x00\x00\x01\x70\x00\x0a\x04\x84\x80\x80\x80\x00\x01\x70\x00\x0a"); >- >-// imports.wast:301 >-assert_invalid("\x00\x61\x73\x6d\x01\x00\x00\x00\x04\x87\x80\x80\x80\x00\x02\x70\x00\x0a\x70\x00\x0a"); >- > // imports.wast:306 > let $15 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x02\x97\x80\x80\x80\x00\x01\x04\x74\x65\x73\x74\x0c\x74\x61\x62\x6c\x65\x2d\x31\x30\x2d\x69\x6e\x66\x01\x70\x00\x0a"); > >diff --git a/JSTests/wasm/wasm.json b/JSTests/wasm/wasm.json >index 64ca0b5b208d91b2bf7865575712473d5b7250a3..45308fbbd57532f5eba13956caacd055934acbaf 100644 >--- a/JSTests/wasm/wasm.json >+++ b/JSTests/wasm/wasm.json >@@ -67,10 +67,10 @@ > "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" }, > "get_global": { "category": "special", "value": 35, "return": ["any"], "parameter": [], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "read a global variable" }, > "set_global": { "category": "special", "value": 36, "return": [], "parameter": ["any"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "write a global variable" }, >- "table.get": { "category": "special", "value": 37, "return": ["anyref"], "parameter": ["i32"], "immediate": [], "description": "get a table value" }, >- "table.set": { "category": "special", "value": 38, "return": [], "parameter": ["i32", "anyref"], "immediate": [], "description": "set a table value" }, >+ "table.get": { "category": "special", "value": 37, "return": ["anyref"], "parameter": ["i32"], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "get a table value" }, >+ "table.set": { "category": "special", "value": 38, "return": [], "parameter": ["i32", "anyref"], "immediate": [{"name": "table_index", "type": "varuint32"}], "description": "set a table value" }, > "call": { "category": "call", "value": 16, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "function_index", "type": "varuint32"}], "description": "call a function by its index" }, >- "call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}, {"name": "reserved", "type": "varuint1"}], "description": "call a function indirect with an expected signature" }, >+ "call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}, {"name": "table_index","type": "varuint32"}],"description": "call a function indirect with an expected signature" }, > "i32.load8_s": { "category": "memory", "value": 44, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, > "i32.load8_u": { "category": "memory", "value": 45, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, > "i32.load16_s": { "category": "memory", "value": 46, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
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 198760
:
372080
|
372175
|
372266
|
372313
|
372347
|
372376
|
372380
|
372381