WebKit Bugzilla
Attachment 371242 Details for
Bug 198398
: [WASM-References] Add support for Anyref tables, Table.get and Table.set (for Anyref only).
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-198398-20190603204000.patch (text/plain), 71.65 KB, created by
Justin Michaud
on 2019-06-03 20:40:01 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Justin Michaud
Created:
2019-06-03 20:40:01 PDT
Size:
71.65 KB
patch
obsolete
>Subversion Revision: 245993 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 2a775655d2c6f0ab030aebf499b211319c2f7089..e713178b5b368b2df8db617f8fab1870baf4a7f9 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,81 @@ >+2019-05-31 Justin Michaud <justin_michaud@apple.com> >+ >+ [WASM-References] Add support for Anyref tables, Table.get and Table.set (for Anyref only). >+ https://bugs.webkit.org/show_bug.cgi?id=198398 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Create a new table subtype called FuncRefTable (note: Anyfunc was renamed to Funcref in the references spec). >+ Table now write-barriers and visits its children's wrapper objects. FuncRefTable caches some extra data to >+ support calling from wasm. A JSWebAssemblyTable is required to set an anyref element, but this is only because >+ we need to write barrier it (so it should not restrict how we implement threads). This patch does, however, >+ restrict the implementation of function references to require every Ref.func to have an associated wrapper. This >+ can be done statically, so this too should not restrict our threads implementation. >+ >+ * wasm/WasmAirIRGenerator.cpp: >+ (JSC::Wasm::AirIRGenerator::addTableGet): >+ (JSC::Wasm::AirIRGenerator::addTableSet): >+ (JSC::Wasm::AirIRGenerator::addCallIndirect): >+ * wasm/WasmB3IRGenerator.cpp: >+ (JSC::Wasm::B3IRGenerator::addLocal): >+ (JSC::Wasm::B3IRGenerator::addTableGet): >+ (JSC::Wasm::B3IRGenerator::addTableSet): >+ (JSC::Wasm::B3IRGenerator::addCallIndirect): >+ * wasm/WasmFormat.h: >+ (JSC::Wasm::TableInformation::TableInformation): >+ (JSC::Wasm::TableInformation::type const): >+ * wasm/WasmFunctionParser.h: >+ (JSC::Wasm::FunctionParser<Context>::parseExpression): >+ (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression): >+ * wasm/WasmSectionParser.cpp: >+ (JSC::Wasm::SectionParser::parseTableHelper): >+ * wasm/WasmTable.cpp: >+ (JSC::Wasm::Table::Table): >+ (JSC::Wasm::Table::tryCreate): >+ (JSC::Wasm::Table::grow): >+ (JSC::Wasm::Table::clear): >+ (JSC::Wasm::Table::set): >+ (JSC::Wasm::Table::get): >+ (JSC::Wasm::Table::visitChildren): >+ (JSC::Wasm::FuncRefTable::FuncRefTable): >+ (JSC::Wasm::FuncRefTable::setFunction): >+ (JSC::Wasm::Table::~Table): Deleted. >+ (JSC::Wasm::Table::clearFunction): Deleted. >+ (JSC::Wasm::Table::setFunction): Deleted. >+ * wasm/WasmTable.h: >+ (JSC::Wasm::Table::length const): >+ (JSC::Wasm::Table::type const): >+ (JSC::Wasm::Table::setOwner): >+ (JSC::Wasm::FuncRefTable::offsetOfFunctions): >+ (JSC::Wasm::FuncRefTable::offsetOfInstances): >+ (JSC::Wasm::Table::offsetOfFunctions): Deleted. >+ (JSC::Wasm::Table::offsetOfInstances): Deleted. >+ * wasm/WasmValidate.cpp: >+ (JSC::Wasm::Validate::addTableGet): >+ (JSC::Wasm::Validate::addTableSet): >+ (JSC::Wasm::Validate::addCallIndirect): >+ * wasm/js/JSWebAssemblyTable.cpp: >+ (JSC::JSWebAssemblyTable::JSWebAssemblyTable): >+ (JSC::JSWebAssemblyTable::finishCreation): >+ (JSC::JSWebAssemblyTable::visitChildren): >+ (JSC::JSWebAssemblyTable::grow): >+ (JSC::JSWebAssemblyTable::get): >+ (JSC::JSWebAssemblyTable::set): >+ (JSC::JSWebAssemblyTable::clear): >+ (JSC::JSWebAssemblyTable::getFunction): Deleted. >+ (JSC::JSWebAssemblyTable::clearFunction): Deleted. >+ (JSC::JSWebAssemblyTable::setFunction): Deleted. >+ * wasm/js/JSWebAssemblyTable.h: >+ * wasm/js/WebAssemblyModuleRecord.cpp: >+ (JSC::WebAssemblyModuleRecord::link): >+ (JSC::WebAssemblyModuleRecord::evaluate): >+ * wasm/js/WebAssemblyTableConstructor.cpp: >+ (JSC::constructJSWebAssemblyTable): >+ * wasm/js/WebAssemblyTablePrototype.cpp: >+ (JSC::webAssemblyTableProtoFuncGet): >+ (JSC::webAssemblyTableProtoFuncSet): >+ * wasm/wasm.json: >+ > 2019-05-31 Don Olmstead <don.olmstead@sony.com> > > [CMake] Add WebKit::WTF target >diff --git a/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp b/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >index 3f51d0b0fc42797341e864f807d527fc2446c0c1..57b3df810e4b12f9e73ac4b14780b1649d873cb2 100644 >--- a/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >+++ b/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp >@@ -232,8 +232,13 @@ public: > ExpressionType addConstant(Type, uint64_t); > ExpressionType addConstant(BasicBlock*, Type, uint64_t); > >+ // References > PartialResult WARN_UNUSED_RETURN addRefIsNull(ExpressionType& value, ExpressionType& result); > >+ // Tables >+ PartialResult WARN_UNUSED_RETURN addTableGet(ExpressionType& idx, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addTableSet(ExpressionType& idx, ExpressionType& value); >+ > // Locals > PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); > PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value); >@@ -949,6 +954,40 @@ auto AirIRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result) > return { }; > } > >+auto AirIRGenerator::addTableGet(ExpressionType& idx, ExpressionType& result) -> PartialResult >+{ >+ // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >+ ASSERT(idx.tmp()); >+ ASSERT(idx.type() == Type::I32); >+ result = tmpForType(Type::Anyref); >+ >+ uint64_t (*doGet)(Instance*, int32_t) = [] (Instance* instance, int32_t idx) -> uint64_t { >+ return JSValue::encode(instance->table()->get(idx)); >+ }; >+ >+ emitCCall(doGet, result, instanceValue(), idx); >+ >+ return { }; >+} >+ >+auto AirIRGenerator::addTableSet(ExpressionType& idx, ExpressionType& value) -> PartialResult >+{ >+ // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >+ ASSERT(idx.tmp()); >+ ASSERT(idx.type() == Type::I32); >+ ASSERT(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)); >+ }; >+ >+ emitCCall(doSet, TypedTmp(), instanceValue(), idx, value); >+ >+ return { }; >+} >+ > auto AirIRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult > { > ASSERT(m_locals[index].tmp()); >@@ -1841,6 +1880,7 @@ auto AirIRGenerator::addCallIndirect(const Signature& signature, Vector<Expressi > { > ExpressionType calleeIndex = args.takeLast(); > ASSERT(signature.argumentCount() == args.size()); >+ ASSERT(m_info.tableInformation.type() == TableElementType::Funcref); > > m_makesCalls = true; > // Note: call indirect can call either WebAssemblyFunction or WebAssemblyWrapperFunction. Because >@@ -1856,13 +1896,13 @@ auto AirIRGenerator::addCallIndirect(const Signature& signature, Vector<Expressi > ExpressionType callableFunctionBufferLength = g64(); > { > RELEASE_ASSERT(Arg::isValidAddrForm(Instance::offsetOfTable(), B3::Width64)); >- RELEASE_ASSERT(Arg::isValidAddrForm(Table::offsetOfFunctions(), B3::Width64)); >- RELEASE_ASSERT(Arg::isValidAddrForm(Table::offsetOfInstances(), B3::Width64)); >- RELEASE_ASSERT(Arg::isValidAddrForm(Table::offsetOfLength(), 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); >- append(Move, Arg::addr(callableFunctionBufferLength, Table::offsetOfFunctions()), callableFunctionBuffer); >- append(Move, Arg::addr(callableFunctionBufferLength, Table::offsetOfInstances()), instancesBuffer); >+ 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 5e87f4bafb4d72b37d3f892561f25515403b9be7..2226ffc72c9382cc26fb3ebbe09429760ef44ab7 100644 >--- a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >+++ b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp >@@ -185,8 +185,13 @@ public: > PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t); > ExpressionType addConstant(Type, uint64_t); > >+ // References > PartialResult WARN_UNUSED_RETURN addRefIsNull(ExpressionType& value, ExpressionType& result); > >+ // Tables >+ PartialResult WARN_UNUSED_RETURN addTableGet(ExpressionType& idx, ExpressionType& result); >+ PartialResult WARN_UNUSED_RETURN addTableSet(ExpressionType& idx, ExpressionType& value); >+ > // Locals > PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); > PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value); >@@ -561,6 +566,36 @@ auto B3IRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result) > return { }; > } > >+auto B3IRGenerator::addTableGet(ExpressionType& idx, ExpressionType& result) -> PartialResult >+{ >+ // FIXME: Emit this inline <https://bugs.webkit.org/show_bug.cgi?id=198506>. >+ uint64_t (*doGet)(Instance*, int32_t) = [] (Instance* instance, int32_t idx) -> uint64_t { >+ return JSValue::encode(instance->table()->get(idx)); >+ }; >+ >+ result = m_currentBlock->appendNew<CCallValue>(m_proc, toB3Type(Anyref), origin(), >+ m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(doGet, B3CCallPtrTag)), >+ instanceValue(), idx); >+ >+ return { }; >+} >+ >+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)); >+ }; >+ >+ m_currentBlock->appendNew<CCallValue>(m_proc, B3::Void, origin(), >+ m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunctionPtr<void*>(doSet, B3CCallPtrTag)), >+ instanceValue(), idx, value); >+ >+ return { }; >+} >+ > auto B3IRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult > { > ASSERT(m_locals[index]); >@@ -1263,9 +1298,9 @@ auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<Expressio > ExpressionType table = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), > instanceValue(), safeCast<int32_t>(Instance::offsetOfTable())); > callableFunctionBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), >- table, safeCast<int32_t>(Table::offsetOfFunctions())); >+ table, safeCast<int32_t>(FuncRefTable::offsetOfFunctions())); > instancesBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), >- table, safeCast<int32_t>(Table::offsetOfInstances())); >+ table, safeCast<int32_t>(FuncRefTable::offsetOfInstances())); > callableFunctionBufferLength = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin(), > table, safeCast<int32_t>(Table::offsetOfLength())); > mask = m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin(), >diff --git a/Source/JavaScriptCore/wasm/WasmFormat.h b/Source/JavaScriptCore/wasm/WasmFormat.h >index b7622644163bf78f5ed380519d43836b51483e15..d0e6d5e3c9ec7574861c85831fb936bb0c937e3a 100644 >--- a/Source/JavaScriptCore/wasm/WasmFormat.h >+++ b/Source/JavaScriptCore/wasm/WasmFormat.h >@@ -54,6 +54,11 @@ namespace Wasm { > struct CompilationContext; > struct ModuleInformation; > >+enum class TableElementType { >+ Anyref, >+ Funcref >+}; >+ > inline bool isValueType(Type type) > { > switch (type) { >@@ -214,11 +219,12 @@ public: > ASSERT(!*this); > } > >- TableInformation(uint32_t initial, Optional<uint32_t> maximum, bool isImport) >+ TableInformation(uint32_t initial, Optional<uint32_t> maximum, bool isImport, TableElementType type) > : m_initial(initial) > , m_maximum(maximum) > , m_isImport(isImport) > , m_isValid(true) >+ , m_type(type) > { > ASSERT(*this); > } >@@ -227,12 +233,14 @@ public: > bool isImport() const { return m_isImport; } > uint32_t initial() const { return m_initial; } > Optional<uint32_t> maximum() const { return m_maximum; } >+ TableElementType type() const { return m_type; } > > private: > uint32_t m_initial; > Optional<uint32_t> m_maximum; > bool m_isImport { false }; > bool m_isValid { false }; >+ TableElementType m_type; > }; > > struct CustomSection { >diff --git a/Source/JavaScriptCore/wasm/WasmFunctionParser.h b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >index 175056b58e721f7442d48c73966de3301195807b..dfae8f2cf3ad2da18aa88bb26bd558cc7d8476de 100644 >--- a/Source/JavaScriptCore/wasm/WasmFunctionParser.h >+++ b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >@@ -281,6 +281,24 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > return { }; > } > >+ case TableGet: { >+ WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >+ ExpressionType result, idx; >+ WASM_TRY_POP_EXPRESSION_STACK_INTO(idx, "table.get"); >+ WASM_TRY_ADD_TO_CONTEXT(addTableGet(idx, result)); >+ m_expressionStack.append(result); >+ return { }; >+ } >+ >+ case TableSet: { >+ WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >+ ExpressionType val, idx; >+ WASM_TRY_POP_EXPRESSION_STACK_INTO(val, "table.set"); >+ WASM_TRY_POP_EXPRESSION_STACK_INTO(idx, "table.set"); >+ WASM_TRY_ADD_TO_CONTEXT(addTableSet(idx, val)); >+ return { }; >+ } >+ > case RefNull: { > WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); > m_expressionStack.append(m_context.addConstant(Anyref, JSValue::encode(jsNull()))); >@@ -373,6 +391,7 @@ auto FunctionParser<Context>::parseExpression() -> PartialResult > 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(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"); > > const Signature& calleeSignature = m_info.usedSignatures[signatureIndex].get(); > size_t argumentCount = calleeSignature.argumentCount() + 1; // Add the callee's index. >@@ -654,16 +673,14 @@ auto FunctionParser<Context>::parseUnreachableExpression() -> PartialResult > return { }; > } > >+ case TableGet: >+ case TableSet: >+ case RefIsNull: > case RefNull: { > WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); > return { }; > } > >- case RefIsNull: { >- WASM_PARSER_FAIL_IF(!Options::useWebAssemblyReferences(), "references are not enabled"); >- return { }; >- } >- > case GrowMemory: > case CurrentMemory: { > uint8_t reserved; >diff --git a/Source/JavaScriptCore/wasm/WasmSectionParser.cpp b/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >index 6b940f7364e434d3f33dfda819a97c6f94b0731d..7e45deea83d6e5c9b66646b9c2194aef779d84fb 100644 >--- a/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >+++ b/Source/JavaScriptCore/wasm/WasmSectionParser.cpp >@@ -196,7 +196,7 @@ auto SectionParser::parseTableHelper(bool isImport) -> PartialResult > > int8_t type; > WASM_PARSER_FAIL_IF(!parseInt7(type), "can't parse Table type"); >- WASM_PARSER_FAIL_IF(type != Wasm::Anyfunc, "Table type should be anyfunc, got ", type); >+ WASM_PARSER_FAIL_IF(type != Wasm::Anyfunc && type != Wasm::Anyref, "Table type should be anyfunc or anyref, got ", type); > > uint32_t initial; > Optional<uint32_t> maximum; >@@ -207,7 +207,8 @@ auto SectionParser::parseTableHelper(bool isImport) -> PartialResult > > ASSERT(!maximum || *maximum >= initial); > >- m_info->tableInformation = TableInformation(initial, maximum, isImport); >+ TableElementType tableType = type == Wasm::Anyfunc ? TableElementType::Funcref : TableElementType::Anyref; >+ m_info->tableInformation = TableInformation(initial, maximum, isImport, tableType); > > return { }; > } >diff --git a/Source/JavaScriptCore/wasm/WasmTable.cpp b/Source/JavaScriptCore/wasm/WasmTable.cpp >index e5c608cb1f028c8644399c59a07d23790d4e37a9..d84b736ce0864bdc99af9dd0c749df40b04591fe 100644 >--- a/Source/JavaScriptCore/wasm/WasmTable.cpp >+++ b/Source/JavaScriptCore/wasm/WasmTable.cpp >@@ -47,32 +47,34 @@ void Table::setLength(uint32_t length) > ASSERT(m_mask == WTF::maskForSize(allocatedLength(length))); > } > >-RefPtr<Table> Table::tryCreate(uint32_t initial, Optional<uint32_t> maximum) >-{ >- if (!isValidLength(initial)) >- return nullptr; >- return adoptRef(new (NotNull, fastMalloc(sizeof(Table))) Table(initial, maximum)); >-} >- >-Table::~Table() >-{ >-} >- >-Table::Table(uint32_t initial, Optional<uint32_t> maximum) >+Table::Table(uint32_t initial, Optional<uint32_t> maximum, JSObject* owner, TableElementType type) >+ : m_type(type) >+ , m_maximum(maximum) >+ , m_owner(owner) > { >+ ASSERT(m_owner); > setLength(initial); >- m_maximum = maximum; > ASSERT(!m_maximum || *m_maximum >= m_length); > > // FIXME: It might be worth trying to pre-allocate maximum here. The spec recommends doing so. > // But for now, we're not doing that. >- m_importableFunctions = MallocPtr<WasmToWasmImportableFunction>::malloc((sizeof(WasmToWasmImportableFunction) * Checked<size_t>(allocatedLength(m_length))).unsafeGet()); > // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425 >- m_instances = MallocPtr<Instance*>::malloc((sizeof(Instance*) * Checked<size_t>(allocatedLength(m_length))).unsafeGet()); >+ m_jsValues = MallocPtr<WriteBarrier<Unknown>>::malloc((sizeof(WriteBarrier<Unknown>) * Checked<size_t>(allocatedLength(m_length))).unsafeGet()); > for (uint32_t i = 0; i < allocatedLength(m_length); ++i) { >- new (&m_importableFunctions.get()[i]) WasmToWasmImportableFunction(); >- ASSERT(m_importableFunctions.get()[i].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code. >- m_instances.get()[i] = nullptr; >+ new (&m_jsValues.get()[i]) WriteBarrier<Unknown>(); >+ m_jsValues.get()[i].setStartingValue(jsNull()); >+ } >+} >+ >+RefPtr<Table> Table::tryCreate(uint32_t initial, Optional<uint32_t> maximum, JSObject* owner, TableElementType type) >+{ >+ if (!isValidLength(initial)) >+ return nullptr; >+ switch (type) { >+ case TableElementType::Funcref: >+ return adoptRef(new FuncRefTable(initial, maximum, owner)); >+ case TableElementType::Anyref: >+ return adoptRef(new Table(initial, maximum, owner)); > } > } > >@@ -81,6 +83,8 @@ Optional<uint32_t> Table::grow(uint32_t delta) > if (delta == 0) > return length(); > >+ auto locker = holdLock(m_owner->cellLock()); >+ > using Checked = Checked<uint32_t, RecordOverflow>; > Checked newLengthChecked = length(); > newLengthChecked += delta; >@@ -108,27 +112,75 @@ Optional<uint32_t> Table::grow(uint32_t delta) > return true; > }; > >- if (!checkedGrow(m_importableFunctions)) >- return WTF::nullopt; >- if (!checkedGrow(m_instances)) >+ if (isFuncrefTable()) { >+ auto& funcRefTable = *static_cast<FuncRefTable*>(this); >+ if (!checkedGrow(funcRefTable.m_importableFunctions)) >+ return WTF::nullopt; >+ if (!checkedGrow(funcRefTable.m_instances)) >+ return WTF::nullopt; >+ } >+ >+ if (!checkedGrow(m_jsValues)) > return WTF::nullopt; > > setLength(newLength); >- > return newLength; > } > >-void Table::clearFunction(uint32_t index) >+void Table::clear(uint32_t index) > { > RELEASE_ASSERT(index < length()); >- m_importableFunctions.get()[index & m_mask] = WasmToWasmImportableFunction(); >- ASSERT(m_importableFunctions.get()[index & m_mask].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code. >- m_instances.get()[index & m_mask] = nullptr; >+ if (isFuncrefTable()) { >+ auto& funcRefTable = *static_cast<FuncRefTable*>(this); >+ funcRefTable.m_importableFunctions.get()[index & m_mask] = WasmToWasmImportableFunction(); >+ ASSERT(funcRefTable.m_importableFunctions.get()[index & m_mask].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code. >+ funcRefTable.m_instances.get()[index & m_mask] = nullptr; >+ } >+ m_jsValues.get()[index & m_mask].setStartingValue(jsNull()); >+} >+ >+void Table::set(uint32_t index, JSValue value) >+{ >+ RELEASE_ASSERT(index < length()); >+ RELEASE_ASSERT(isAnyrefTable()); >+ clear(index); >+ m_jsValues.get()[index & m_mask].set(*m_owner->vm(), m_owner, value); >+} >+ >+JSValue Table::get(uint32_t index) const >+{ >+ RELEASE_ASSERT(index < length()); >+ return m_jsValues.get()[index & m_mask].get(); >+} >+ >+void Table::visitChildren(SlotVisitor& visitor) >+{ >+ auto locker = holdLock(m_owner->cellLock()); >+ for (unsigned i = 0; i < m_length; ++i) >+ visitor.append(m_jsValues.get()[i]); >+} >+ >+FuncRefTable::FuncRefTable(uint32_t initial, Optional<uint32_t> maximum, JSObject* owner) >+ : Table(initial, maximum, owner, TableElementType::Funcref) >+{ >+ // FIXME: It might be worth trying to pre-allocate maximum here. The spec recommends doing so. >+ // But for now, we're not doing that. >+ m_importableFunctions = MallocPtr<WasmToWasmImportableFunction>::malloc((sizeof(WasmToWasmImportableFunction) * Checked<size_t>(allocatedLength(m_length))).unsafeGet()); >+ // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425 >+ m_instances = MallocPtr<Instance*>::malloc((sizeof(Instance*) * Checked<size_t>(allocatedLength(m_length))).unsafeGet()); >+ for (uint32_t i = 0; i < allocatedLength(m_length); ++i) { >+ new (&m_importableFunctions.get()[i]) WasmToWasmImportableFunction(); >+ ASSERT(m_importableFunctions.get()[i].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code. >+ m_instances.get()[i] = nullptr; >+ } > } > >-void Table::setFunction(uint32_t index, WasmToWasmImportableFunction function, Instance* instance) >+void FuncRefTable::setFunction(uint32_t index, JSObject* optionalWrapper, WasmToWasmImportableFunction function, Instance* instance) > { > RELEASE_ASSERT(index < length()); >+ clear(index); >+ if (optionalWrapper) >+ m_jsValues.get()[index & m_mask].set(*m_owner->vm(), m_owner, optionalWrapper); > m_importableFunctions.get()[index & m_mask] = function; > m_instances.get()[index & m_mask] = instance; > } >diff --git a/Source/JavaScriptCore/wasm/WasmTable.h b/Source/JavaScriptCore/wasm/WasmTable.h >index 7e3004135cdc22642f5a98a57bc27924e849d97c..f10718b336735f417e4536ea1e04669ac3363752 100644 >--- a/Source/JavaScriptCore/wasm/WasmTable.h >+++ b/Source/JavaScriptCore/wasm/WasmTable.h >@@ -29,6 +29,7 @@ > > #include "WasmFormat.h" > #include "WasmLimits.h" >+#include "WriteBarrier.h" > #include <wtf/MallocPtr.h> > #include <wtf/Optional.h> > #include <wtf/Ref.h> >@@ -39,37 +40,66 @@ namespace JSC { namespace Wasm { > class Instance; > > class Table : public ThreadSafeRefCounted<Table> { >+ WTF_MAKE_NONCOPYABLE(Table); >+ WTF_MAKE_FAST_ALLOCATED(Table); > public: >- static RefPtr<Table> tryCreate(uint32_t initial, Optional<uint32_t> maximum); >+ static RefPtr<Table> tryCreate(uint32_t initial, Optional<uint32_t> maximum, JSObject* owner, TableElementType); > >- JS_EXPORT_PRIVATE ~Table(); >+ JS_EXPORT_PRIVATE ~Table() = default; > > Optional<uint32_t> maximum() const { return m_maximum; } > uint32_t length() const { return m_length; } >- Optional<uint32_t> grow(uint32_t delta) WARN_UNUSED_RETURN; >- void clearFunction(uint32_t); >- void setFunction(uint32_t, WasmToWasmImportableFunction, Instance*); > >- static ptrdiff_t offsetOfFunctions() { return OBJECT_OFFSETOF(Table, m_importableFunctions); } >- static ptrdiff_t offsetOfInstances() { return OBJECT_OFFSETOF(Table, m_instances); } > static ptrdiff_t offsetOfLength() { return OBJECT_OFFSETOF(Table, m_length); } > static ptrdiff_t offsetOfMask() { return OBJECT_OFFSETOF(Table, m_mask); } > > static uint32_t allocatedLength(uint32_t length); > uint32_t mask() const { return m_mask; } >+ TableElementType type() const { return m_type; } >+ bool isFuncrefTable() const { return m_type == TableElementType::Funcref; } >+ bool isAnyrefTable() const { return m_type == TableElementType::Anyref; } >+ > static bool isValidLength(uint32_t length) { return length < maxTableEntries; } > >-private: >- Table(uint32_t initial, Optional<uint32_t> maximum); >+ void clear(uint32_t); >+ void set(uint32_t, JSValue); >+ JSValue get(uint32_t) const; >+ >+ Optional<uint32_t> grow(uint32_t delta); >+ >+ void visitChildren(SlotVisitor&); >+ >+protected: >+ Table(uint32_t initial, Optional<uint32_t> maximum, JSObject* owner, TableElementType = TableElementType::Anyref); > > void setLength(uint32_t); > >+ uint32_t m_length; >+ uint32_t m_mask; >+ const TableElementType m_type; >+ const Optional<uint32_t> m_maximum; >+ >+ MallocPtr<WriteBarrier<Unknown>> m_jsValues; >+ JSObject* const m_owner; >+}; >+ >+class FuncRefTable : public Table { >+public: >+ JS_EXPORT_PRIVATE ~FuncRefTable() = default; >+ >+ void setFunction(uint32_t, JSObject*, WasmToWasmImportableFunction, Instance*); >+ >+ static ptrdiff_t offsetOfFunctions() { return OBJECT_OFFSETOF(FuncRefTable, m_importableFunctions); } >+ static ptrdiff_t offsetOfInstances() { return OBJECT_OFFSETOF(FuncRefTable, m_instances); } >+ >+private: >+ FuncRefTable(uint32_t initial, Optional<uint32_t> maximum, JSObject* owner); >+ > MallocPtr<WasmToWasmImportableFunction> m_importableFunctions; > // call_indirect needs to do an Instance check to potentially context switch when calling a function to another instance. We can hold raw pointers to Instance here because the embedder ensures that Table keeps all the instances alive. We couldn't hold a Ref here because it would cause cycles. > MallocPtr<Instance*> m_instances; >- uint32_t m_length; >- uint32_t m_mask; >- Optional<uint32_t> m_maximum; >+ >+ friend class Table; > }; > > } } // namespace JSC::Wasm >diff --git a/Source/JavaScriptCore/wasm/WasmValidate.cpp b/Source/JavaScriptCore/wasm/WasmValidate.cpp >index fd54162008409da36f9694d38912c8efc91428b5..6619dfbfd03e50a335475ca7df352be120651fed 100644 >--- a/Source/JavaScriptCore/wasm/WasmValidate.cpp >+++ b/Source/JavaScriptCore/wasm/WasmValidate.cpp >@@ -100,8 +100,13 @@ public: > Result WARN_UNUSED_RETURN addLocal(Type, uint32_t); > ExpressionType addConstant(Type type, uint64_t) { return type; } > >+ // References > Result WARN_UNUSED_RETURN addRefIsNull(ExpressionType& value, ExpressionType& result); > >+ // Tables >+ Result WARN_UNUSED_RETURN addTableGet(ExpressionType& idx, ExpressionType& result); >+ Result WARN_UNUSED_RETURN addTableSet(ExpressionType& idx, ExpressionType& value); >+ > // Locals > Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); > Result WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value); >@@ -171,6 +176,24 @@ auto Validate::addArguments(const Signature& signature) -> Result > return { }; > } > >+auto Validate::addTableGet(ExpressionType& idx, ExpressionType& result) -> Result >+{ >+ result = Type::Anyref; >+ WASM_VALIDATOR_FAIL_IF(Type::I32 != idx, "table.get index to type ", idx, " expected ", Type::I32); >+ WASM_VALIDATOR_FAIL_IF(TableElementType::Anyref != m_module.tableInformation.type(), "table.get expects the table to have type ", Type::Anyref); >+ >+ return { }; >+} >+ >+auto Validate::addTableSet(ExpressionType& idx, ExpressionType& value) -> Result >+{ >+ WASM_VALIDATOR_FAIL_IF(Type::I32 != idx, "table.set index to type ", idx, " expected ", Type::I32); >+ WASM_VALIDATOR_FAIL_IF(Type::Anyref != value, "table.set value to type ", value, " expected ", Type::Anyref); >+ WASM_VALIDATOR_FAIL_IF(TableElementType::Anyref != m_module.tableInformation.type(), "table.set expects the table to have type ", Type::Anyref); >+ >+ return { }; >+} >+ > auto Validate::addRefIsNull(ExpressionType& value, ExpressionType& result) -> Result > { > result = Type::I32; >@@ -346,6 +369,7 @@ auto Validate::addCall(unsigned, const Signature& signature, const Vector<Expres > > auto Validate::addCallIndirect(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"); > 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/JSWebAssemblyTable.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.cpp >index 5be49dd76ad4e3e4b40f971289cf15b7b6b269d7..a1e07d950b7f23a4f3f5b2beba2d8e74ebb40bc8 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.cpp >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.cpp >@@ -36,7 +36,7 @@ namespace JSC { > > const ClassInfo JSWebAssemblyTable::s_info = { "WebAssembly.Table", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWebAssemblyTable) }; > >-JSWebAssemblyTable* JSWebAssemblyTable::create(ExecState* exec, VM& vm, Structure* structure, Ref<Wasm::Table>&& table) >+JSWebAssemblyTable* JSWebAssemblyTable::tryCreate(ExecState* exec, VM& vm, Structure* structure, uint32_t initial, Optional<uint32_t> maximum, Wasm::TableElementType type) > { > auto throwScope = DECLARE_THROW_SCOPE(vm); > auto* globalObject = exec->lexicalGlobalObject(); >@@ -46,7 +46,13 @@ JSWebAssemblyTable* JSWebAssemblyTable::create(ExecState* exec, VM& vm, Structur > return nullptr; > } > >- auto* instance = new (NotNull, allocateCell<JSWebAssemblyTable>(vm.heap)) JSWebAssemblyTable(vm, structure, WTFMove(table)); >+ void* cell = allocateCell<JSWebAssemblyTable>(vm.heap); >+ auto table = Wasm::Table::tryCreate(initial, maximum, (JSObject*) cell, type); >+ >+ if (!table) >+ return nullptr; >+ >+ auto* instance = new (NotNull, cell) JSWebAssemblyTable(vm, structure, table.releaseNonNull()); > instance->finishCreation(vm); > return instance; > } >@@ -60,12 +66,6 @@ JSWebAssemblyTable::JSWebAssemblyTable(VM& vm, Structure* structure, Ref<Wasm::T > : Base(vm, structure) > , m_table(WTFMove(table)) > { >- // FIXME: It might be worth trying to pre-allocate maximum here. The spec recommends doing so. >- // But for now, we're not doing that. >- // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425 >- m_jsFunctions = MallocPtr<WriteBarrier<JSObject>>::malloc((sizeof(WriteBarrier<JSObject>) * Checked<size_t>(allocatedLength())).unsafeGet()); >- for (uint32_t i = 0; i < allocatedLength(); ++i) >- new(&m_jsFunctions.get()[i]) WriteBarrier<JSObject>(); > } > > void JSWebAssemblyTable::finishCreation(VM& vm) >@@ -85,55 +85,49 @@ void JSWebAssemblyTable::visitChildren(JSCell* cell, SlotVisitor& visitor) > ASSERT_GC_OBJECT_INHERITS(thisObject, info()); > > Base::visitChildren(thisObject, visitor); >- >- for (unsigned i = 0; i < thisObject->length(); ++i) >- visitor.append(thisObject->m_jsFunctions.get()[i]); >+ thisObject->table()->visitChildren(visitor); > } > > bool JSWebAssemblyTable::grow(uint32_t delta) > { > if (delta == 0) > return true; >+ return !!m_table->grow(delta); >+} > >- size_t oldLength = length(); >- >- auto grew = m_table->grow(delta); >- if (!grew) >- return false; >- >- size_t newLength = grew.value(); >- if (newLength > m_table->allocatedLength(oldLength)) >- // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425 >- m_jsFunctions.realloc((sizeof(WriteBarrier<JSObject>) * Checked<size_t>(m_table->allocatedLength(newLength))).unsafeGet()); >- >- for (size_t i = oldLength; i < m_table->allocatedLength(newLength); ++i) >- new (&m_jsFunctions.get()[i]) WriteBarrier<JSObject>(); >- >- return true; >+JSValue JSWebAssemblyTable::get(uint32_t index) >+{ >+ RELEASE_ASSERT(index < length()); >+ return m_table->get(index); > } > >-JSObject* JSWebAssemblyTable::getFunction(uint32_t index) >+void JSWebAssemblyTable::set(uint32_t index, JSValue value) > { > RELEASE_ASSERT(index < length()); >- return m_jsFunctions.get()[index & m_table->mask()].get(); >+ RELEASE_ASSERT(m_table->isAnyrefTable()); >+ m_table->set(index, value); > } > >-void JSWebAssemblyTable::clearFunction(uint32_t index) >+void JSWebAssemblyTable::set(uint32_t index, WebAssemblyFunction* function) > { >- m_table->clearFunction(index); >- m_jsFunctions.get()[index & m_table->mask()] = WriteBarrier<JSObject>(); >+ RELEASE_ASSERT(index < length()); >+ RELEASE_ASSERT(m_table->isAnyrefTable()); >+ auto& subThis = *static_cast<Wasm::FuncRefTable*>(&m_table.get()); >+ subThis.setFunction(index, function, function->importableFunction(), &function->instance()->instance()); > } > >-void JSWebAssemblyTable::setFunction(VM& vm, uint32_t index, WebAssemblyFunction* function) >+void JSWebAssemblyTable::set(uint32_t index, WebAssemblyWrapperFunction* function) > { >- m_table->setFunction(index, function->importableFunction(), &function->instance()->instance()); >- m_jsFunctions.get()[index & m_table->mask()].set(vm, this, function); >+ RELEASE_ASSERT(index < length()); >+ RELEASE_ASSERT(m_table->isFuncrefTable()); >+ auto& subThis = *static_cast<Wasm::FuncRefTable*>(&m_table.get()); >+ subThis.setFunction(index, function, function->importableFunction(), &function->instance()->instance()); > } > >-void JSWebAssemblyTable::setFunction(VM& vm, uint32_t index, WebAssemblyWrapperFunction* function) >+void JSWebAssemblyTable::clear(uint32_t index) > { >- m_table->setFunction(index, function->importableFunction(), &function->instance()->instance()); >- m_jsFunctions.get()[index & m_table->mask()].set(vm, this, function); >+ RELEASE_ASSERT(index < length()); >+ m_table->clear(index); > } > > } // namespace JSC >diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h >index d2f3eac2598b0e9bfba338271756235ac4cacb86..e242833c96c9af5f8a7fc5b49bbee09ea3ffb544 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h >@@ -42,7 +42,7 @@ class JSWebAssemblyTable final : public JSDestructibleObject { > public: > typedef JSDestructibleObject Base; > >- static JSWebAssemblyTable* create(ExecState*, VM&, Structure*, Ref<Wasm::Table>&&); >+ static JSWebAssemblyTable* tryCreate(ExecState*, VM&, Structure*, uint32_t initial, Optional<uint32_t> maximum, Wasm::TableElementType); > static Structure* createStructure(VM&, JSGlobalObject*, JSValue); > > DECLARE_INFO; >@@ -52,10 +52,11 @@ public: > uint32_t length() const { return m_table->length(); } > uint32_t allocatedLength() const { return m_table->allocatedLength(length()); } > bool grow(uint32_t delta) WARN_UNUSED_RETURN; >- JSObject* getFunction(uint32_t); >- void clearFunction(uint32_t); >- void setFunction(VM&, uint32_t, WebAssemblyFunction*); >- void setFunction(VM&, uint32_t, WebAssemblyWrapperFunction*); >+ JSValue get(uint32_t); >+ void set(uint32_t, WebAssemblyFunction*); >+ void set(uint32_t, WebAssemblyWrapperFunction*); >+ void set(uint32_t, JSValue); >+ void clear(uint32_t); > > Wasm::Table* table() { return m_table.ptr(); } > >@@ -66,7 +67,6 @@ private: > static void visitChildren(JSCell*, SlotVisitor&); > > Ref<Wasm::Table> m_table; >- MallocPtr<WriteBarrier<JSObject>> m_jsFunctions; > }; > > } // namespace JSC >diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >index 5c04d4ed5ac5d8d2c61eed28d773b1c2176fa4fe..15f1ad68f8991dcc9874bbab8abb5cc962529e50 100644 >--- a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >@@ -301,10 +301,12 @@ void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObj > if (!!moduleInformation.tableInformation && !hasTableImport) { > RELEASE_ASSERT(!moduleInformation.tableInformation.isImport()); > // We create a Table when it's a Table definition. >- RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum()); >- if (!wasmTable) >+ JSWebAssemblyTable* table = JSWebAssemblyTable::tryCreate(exec, vm, globalObject->webAssemblyTableStructure(), moduleInformation.tableInformation.initial(), >+ moduleInformation.tableInformation.maximum(), moduleInformation.tableInformation.type()); >+ >+ if (!table) > return exception(createJSWebAssemblyLinkError(exec, vm, "couldn't create Table")); >- JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, globalObject->webAssemblyTableStructure(), wasmTable.releaseNonNull()); >+ > // We should always be able to allocate a JSWebAssemblyTable we've defined. > // If it's defined to be too large, we should have thrown a validation error. > scope.assertNoException(); >@@ -536,12 +538,12 @@ 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->setFunction(vm, tableIndex, wasmFunction); >+ table->set(tableIndex, wasmFunction); > ++tableIndex; > continue; > } > >- table->setFunction(vm, tableIndex, >+ table->set(tableIndex, > WebAssemblyWrapperFunction::create(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, functionIndex, m_instance.get(), signatureIndex)); > ++tableIndex; > continue; >@@ -557,7 +559,7 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec) > WebAssemblyFunction* function = WebAssemblyFunction::create( > vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), String(), m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex); > >- table->setFunction(vm, tableIndex, function); >+ table->set(tableIndex, function); > ++tableIndex; > } > }); >diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp >index 1a2eaa7ab2622d31400bccf99a0617b7e2e372bd..79a9b44087347d60d410affd3cb8e0e2102e85c8 100644 >--- a/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp >+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp >@@ -58,14 +58,19 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyTable(ExecState* exec) > memoryDescriptor = jsCast<JSObject*>(argument); > } > >+ Wasm::TableElementType type; > { > Identifier elementIdent = Identifier::fromString(&vm, "element"); > JSValue elementValue = memoryDescriptor->get(exec, elementIdent); > RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); > String elementString = elementValue.toWTFString(exec); > RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); >- if (elementString != "anyfunc") >- return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, "WebAssembly.Table expects its 'element' field to be the string 'anyfunc'"_s))); >+ if (elementString == "anyfunc") >+ type = Wasm::TableElementType::Funcref; >+ else if (elementString == "anyref") >+ type = Wasm::TableElementType::Anyref; >+ else >+ return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, "WebAssembly.Table expects its 'element' field to be the string 'anyfunc' or 'anyref'"_s))); > } > > Identifier initialIdent = Identifier::fromString(&vm, "initial"); >@@ -90,13 +95,13 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyTable(ExecState* exec) > } > } > >- RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(initial, maximum); >+ auto* wasmTable = JSWebAssemblyTable::tryCreate(exec, vm, exec->lexicalGlobalObject()->webAssemblyTableStructure(), initial, maximum, type); > if (!wasmTable) { > return JSValue::encode(throwException(exec, throwScope, > createRangeError(exec, "couldn't create Table"_s))); > } > >- RELEASE_AND_RETURN(throwScope, JSValue::encode(JSWebAssemblyTable::create(exec, vm, exec->lexicalGlobalObject()->webAssemblyTableStructure(), wasmTable.releaseNonNull()))); >+ RELEASE_AND_RETURN(throwScope, JSValue::encode(wasmTable)); > } > > static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyTable(ExecState* exec) >diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.cpp >index f48178374fd81135f0988d910219dd4896809f44..b64825837a8262833786c8c7a2e0e36f248a19f5 100644 >--- a/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.cpp >+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.cpp >@@ -109,9 +109,7 @@ static EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncGet(ExecState* exec > if (index >= table->length()) > return JSValue::encode(throwException(exec, throwScope, createRangeError(exec, "WebAssembly.Table.prototype.get expects an integer less than the length of the table"_s))); > >- if (JSObject* result = table->getFunction(index)) >- return JSValue::encode(result); >- return JSValue::encode(jsNull()); >+ return JSValue::encode(table->get(index)); > } > > static EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncSet(ExecState* exec) >@@ -123,10 +121,6 @@ static EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncSet(ExecState* exec > RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); > > JSValue value = exec->argument(1); >- WebAssemblyFunction* wasmFunction; >- WebAssemblyWrapperFunction* wasmWrapperFunction; >- if (!value.isNull() && !isWebAssemblyHostFunction(vm, value, wasmFunction, wasmWrapperFunction)) >- return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, "WebAssembly.Table.prototype.set expects the second argument to be null or an instance of WebAssembly.Function"_s))); > > uint32_t index = toNonWrappingUint32(exec, exec->argument(0)); > RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); >@@ -134,16 +128,24 @@ static EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncSet(ExecState* exec > if (index >= table->length()) > return JSValue::encode(throwException(exec, throwScope, createRangeError(exec, "WebAssembly.Table.prototype.set expects an integer less than the length of the table"_s))); > >- if (value.isNull()) >- table->clearFunction(index); >- else { >- ASSERT(value.isObject() && isWebAssemblyHostFunction(vm, jsCast<JSObject*>(value), wasmFunction, wasmWrapperFunction)); >- ASSERT(!!wasmFunction || !!wasmWrapperFunction); >- if (wasmFunction) >- table->setFunction(vm, index, wasmFunction); >- else >- table->setFunction(vm, index, wasmWrapperFunction); >- } >+ if (table->table()->isFuncrefTable()) { >+ WebAssemblyFunction* wasmFunction; >+ WebAssemblyWrapperFunction* wasmWrapperFunction; >+ if (!value.isNull() && !isWebAssemblyHostFunction(vm, value, wasmFunction, wasmWrapperFunction)) >+ return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, "WebAssembly.Table.prototype.set expects the second argument to be null or an instance of WebAssembly.Function"_s))); >+ >+ if (value.isNull()) >+ table->clear(index); >+ else { >+ ASSERT(value.isObject() && isWebAssemblyHostFunction(vm, jsCast<JSObject*>(value), wasmFunction, wasmWrapperFunction)); >+ ASSERT(!!wasmFunction || !!wasmWrapperFunction); >+ if (wasmFunction) >+ table->set(index, wasmFunction); >+ else >+ table->set(index, wasmWrapperFunction); >+ } >+ } else >+ table->set(index, value); > > return JSValue::encode(jsUndefined()); > } >diff --git a/Source/JavaScriptCore/wasm/wasm.json b/Source/JavaScriptCore/wasm/wasm.json >index e61aa74c338660e4b5f16a354d52d151060092b0..30bbc74723783922edbbf634993490f48b7b7e02 100644 >--- a/Source/JavaScriptCore/wasm/wasm.json >+++ b/Source/JavaScriptCore/wasm/wasm.json >@@ -18,7 +18,7 @@ > }, > "value_type": ["i32", "i64", "f32", "f64", "anyref"], > "block_type": ["i32", "i64", "f32", "f64", "void", "anyref"], >- "elem_type": ["anyfunc"], >+ "elem_type": ["anyfunc","anyref"], > "external_kind": { > "Function": { "type": "uint8", "value": 0 }, > "Table": { "type": "uint8", "value": 1 }, >@@ -66,6 +66,8 @@ > "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" }, > "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" }, > "i32.load8_s": { "category": "memory", "value": 44, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, >diff --git a/Tools/MiniBrowser/MiniBrowser.entitlements b/Tools/MiniBrowser/MiniBrowser.entitlements >index c9ea376468a649976c9ae38f299a325dcb4dadb9..4b1f54e2531df8e370f685d95f33feb88872ba3a 100644 >GIT binary patch >delta 171 >zcmcb}dV*OZsURn_xWvHV8Y2@k3o9EtJ0}-Ad%S?$#Ers}IhnYb;{_B!Oj{;CAk$@X >zB9jYKyujoHCXLB~jN(8c*@+uvfs_GI(ZuhvT%4SN`ir=E;ssPEH!-S$bWh&RsOZ4S >w$-u!N%3#J2#jud!5hD|$DWe&qIin?`4Py#p2V*A#7%(wHXa;^L4W$^n013t)_5c6? > >literal 721 >zcma))K~DlP5QWe1E7rZY)tfN|A}l6Ef+S#!$8L9khHl%mvn>1X?Fu3$sGOR#?|U=z >zrhKq3b+Q8=lr_Edj^3sT7-<V-*1hy~`9$v1LBGpyvX{~FeLhZ{Rsoaw>u@?5B`IO- >z(K!u_Wy>s?PbZ6I5<M6j&(b8_Ai4*}>bj<)RZ(fnRv6gaTL&JSX*44dpam9bEOT)G >z(N4TAl*E3w!?)1%qs7rIfULa2h8D0>5@;22&RR)SqOXAcfvl8<5DD-kfCh<BP!FOY >z*68MH7lhJTFMLB_FTr7qkd{Jjh_-fHIl9(17n&GR!pT#3FWMG6JfM#upAEDSE8!K5 >z2MoQfLwuzR@a=G%9n}}XH^`qA(ivcF{WfBmfYO>Xh_kP{zl9hVM1n9yZuh5N|4Y#v >q3R0;R7`z(06x(4TA`ezq2*e58Tv*641ok}{XNS+h+2_~kcRv82l=fW! > >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index 4aad59508d3d9aaea81dfef84e9078ea95e6282a..969887bacae4349f061171145782ec9060e38614 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,24 @@ >+2019-05-31 Justin Michaud <justin_michaud@apple.com> >+ >+ [WASM-References] Add support for Anyref tables, Table.get and Table.set (for Anyref only). >+ https://bugs.webkit.org/show_bug.cgi?id=198398 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * wasm/references/anyref_table.js: Added. >+ (string_appeared_here.doGCSet): >+ (doGCTest): >+ (doGCSet.doGCTest.let.count.0.doBarrierSet): >+ * wasm/references/anyref_table_import.js: Added. >+ (makeImport): >+ (string_appeared_here.fullGC.assert.eq.1.exports.get_tbl.makeImport): >+ (string_appeared_here.fullGC.assert.eq.1.exports.get_tbl): >+ * wasm/references/is_null_error.js: Removed. >+ * wasm/references/validation.js: Added. >+ (assert.throws.new.WebAssembly.Module.bin): >+ (assert.throws): >+ * wasm/wasm.json: >+ > 2019-05-30 Tadeu Zagallo <tzagallo@apple.com> and Yusuke Suzuki <ysuzuki@apple.com> > > [JSC] Implement op_wide16 / op_wide32 and introduce 16bit version bytecode >diff --git a/JSTests/wasm/js-api/table.js b/JSTests/wasm/js-api/table.js >index cf68b440495f6be6f8b27947712456d7ca7c8c6f..bbca89ec34f71912084f6309aac2d64550a43bd1 100644 >--- a/JSTests/wasm/js-api/table.js >+++ b/JSTests/wasm/js-api/table.js >@@ -132,29 +132,29 @@ function assertBadTableImport(tableDescription, message) { > { > let badDescriptions = [ > [{initial: 10, element: "i32"}, >- "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >- "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], >+ "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc or anyref, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >+ "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc or anyref, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], > [{initial: 10, element: "f32"}, >- "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >- "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], >+ "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc or anyref, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >+ "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc or anyref, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], > [{initial: 10, element: "f64"}, >- "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >- "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], >+ "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc or anyref, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >+ "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc or anyref, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], > [{initial: 10, element: "i64"}, >- "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >- "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], >+ "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc or anyref, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >+ "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc or anyref, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], > [{initial: 10, maximum: 20, element: "i32"}, >- "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >- "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], >+ "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc or anyref, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >+ "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc or anyref, got -1 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], > [{initial: 10, maximum: 20, element: "f32"}, >- "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >- "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], >+ "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc or anyref, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >+ "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc or anyref, got -3 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], > [{initial: 10, maximum: 20, element: "f64"}, >- "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >- "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], >+ "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc or anyref, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >+ "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc or anyref, got -4 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], > [{initial: 10, maximum: 20, element: "i64"}, >- "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >- "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], >+ "WebAssembly.Module doesn't parse at byte 18: Table type should be anyfunc or anyref, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >+ "WebAssembly.Module doesn't parse at byte 26: Table type should be anyfunc or anyref, got -2 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')"], > > [{initial: 10, maximum: 9, element: "anyfunc"}, > "WebAssembly.Module doesn't parse at byte 21: resizable limits has a initial page count of 10 which is greater than its maximum 9 (evaluating 'new WebAssembly.Module(builder.WebAssembly().get())')", >diff --git a/JSTests/wasm/references/anyref_table.js b/JSTests/wasm/references/anyref_table.js >new file mode 100644 >index 0000000000000000000000000000000000000000..8a0c293e55693aa201ba3bbfe1d0ce65d82a591d >--- /dev/null >+++ b/JSTests/wasm/references/anyref_table.js >@@ -0,0 +1,100 @@ >+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: 20, maximum: 30, element: "anyref"}) >+ .End() >+ .Export() >+ .Function("set_tbl") >+ .Function("get_tbl") >+ .Function("tbl_is_null") >+ .Function("set_tbl_null") >+ .Table("tbl", 0) >+ .End() >+ .Code() >+ .Function("set_tbl", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet() >+ .End() >+ >+ .Function("get_tbl", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet() >+ .End() >+ >+ .Function("tbl_is_null", { params: [], ret: "i32" }) >+ .Call(1) >+ .RefIsNull() >+ .End() >+ >+ .Function("set_tbl_null", { params: [], ret: "void" }) >+ .RefNull() >+ .Call(0) >+ .End() >+ .End().WebAssembly().get())); >+ >+fullGC() >+ >+assert.eq($1.exports.get_tbl(), null) >+assert.eq($1.exports.tbl_is_null(), 1) >+ >+$1.exports.set_tbl("hi") >+fullGC() >+assert.eq($1.exports.get_tbl(), "hi") >+assert.eq($1.exports.tbl_is_null(), 0) >+ >+assert.eq($1.exports.tbl.get(0), "hi") >+assert.eq($1.exports.tbl.get(1), null) >+ >+$1.exports.tbl.set(0, { test: "test" }); >+fullGC() >+assert.eq($1.exports.get_tbl().test, "test") >+ >+assert.eq($1.exports.tbl.grow(10), 20) >+assert.eq($1.exports.tbl.grow(0), 30) >+assert.eq($1.exports.get_tbl().test, "test") >+fullGC() >+assert.eq($1.exports.get_tbl().test, "test") >+ >+function doGCSet() { >+ fullGC() >+ $1.exports.set_tbl({ test: -1 }) >+ fullGC() >+} >+ >+function doGCTest() { >+ for (let i=0; i<1000; ++i) { >+ assert.eq($1.exports.get_tbl().test, -1) >+ fullGC() >+ } >+} >+ >+doGCSet() >+doGCTest() >+ >+let count = 0 >+ >+function doBarrierSet() { >+ ++count >+ $1.exports.set_tbl({ test: -count }) >+} >+ >+function doBarrierTest() { >+ let garbage = { val: "hi", val2: 5, arr: [] } >+ for (let i=0; i<100; ++i) garbage.arr += ({ field: i }) >+ >+ for (let j=0; j<1000; ++j) { >+ assert.eq($1.exports.get_tbl().test, -count) >+ edenGC() >+ } >+} >+ >+for (let i=0; i<5; ++i) { >+ doBarrierSet() >+ doBarrierTest() >+ doBarrierTest() >+} >diff --git a/JSTests/wasm/references/anyref_table_import.js b/JSTests/wasm/references/anyref_table_import.js >new file mode 100644 >index 0000000000000000000000000000000000000000..10fb43510bb87743c6c8baabffc117958aa959aa >--- /dev/null >+++ b/JSTests/wasm/references/anyref_table_import.js >@@ -0,0 +1,176 @@ >+import * as assert from '../assert.js'; >+import Builder from '../Builder.js'; >+ >+{ >+ function makeImport() { >+ const tbl = new WebAssembly.Table({initial:2, element:"anyref"}); >+ >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Import() >+ .Table("imp", "tbl", {initial: 2, element: "anyref"}) >+ .End() >+ .Function().End() >+ .Export() >+ .Function("set_tbl") >+ .Function("get_tbl") >+ .Function("tbl_is_null") >+ .Function("set_tbl_null") >+ .Table("tbl", 0) >+ .End() >+ .Code() >+ .Function("set_tbl", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet() >+ .End() >+ >+ .Function("get_tbl", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet() >+ .End() >+ >+ .Function("tbl_is_null", { params: [], ret: "i32" }) >+ .Call(1) >+ .RefIsNull() >+ .End() >+ >+ .Function("set_tbl_null", { params: [], ret: "void" }) >+ .RefNull() >+ .Call(0) >+ .End() >+ .End().WebAssembly().get()), { imp: { tbl }}); >+ fullGC() >+ return $1 >+ } >+ >+ const $1 = makeImport() >+ fullGC() >+ >+ assert.eq($1.exports.get_tbl(), null) >+ assert.eq($1.exports.tbl_is_null(), 1) >+ >+ $1.exports.set_tbl("hi") >+ fullGC() >+ assert.eq($1.exports.get_tbl(), "hi") >+ assert.eq($1.exports.tbl_is_null(), 0) >+ >+ assert.eq($1.exports.tbl.get(0), "hi") >+ assert.eq($1.exports.tbl.get(1), null) >+ >+ $1.exports.tbl.set(0, { test: "test" }); >+ fullGC() >+ assert.eq($1.exports.get_tbl().test, "test") >+ >+ assert.eq($1.exports.tbl.grow(10), 2) >+ assert.eq($1.exports.tbl.grow(0), 12) >+ assert.eq($1.exports.get_tbl().test, "test") >+ fullGC() >+ assert.eq($1.exports.get_tbl().test, "test") >+} >+ >+{ >+ function makeImport() { >+ const $1 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Import() >+ .Table("imp", "tbl", {initial: 2, element: "anyref"}) >+ .End() >+ .Function().End() >+ .Export() >+ .Function("set_tbl") >+ .Function("get_tbl") >+ .Function("tbl_is_null") >+ .Function("set_tbl_null") >+ .Table("tbl", 0) >+ .End() >+ .Code() >+ .Function("set_tbl", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet() >+ .End() >+ >+ .Function("get_tbl", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet() >+ .End() >+ >+ .Function("tbl_is_null", { params: [], ret: "i32" }) >+ .Call(1) >+ .RefIsNull() >+ .End() >+ >+ .Function("set_tbl_null", { params: [], ret: "void" }) >+ .RefNull() >+ .Call(0) >+ .End() >+ .End().WebAssembly().get()), { imp: { tbl: new WebAssembly.Table({initial:2, element:"anyref"}) }}); >+ fullGC() >+ >+ $1.exports.tbl.set(0, { test: "test" }); >+ >+ const $2 = new WebAssembly.Instance(new WebAssembly.Module((new Builder()) >+ .Type().End() >+ .Import() >+ .Table("imp", "tbl", {initial: 2, element: "anyref"}) >+ .End() >+ .Function().End() >+ .Export() >+ .Function("set_tbl") >+ .Function("get_tbl") >+ .Function("tbl_is_null") >+ .Function("set_tbl_null") >+ .Table("tbl", 0) >+ .End() >+ .Code() >+ .Function("set_tbl", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet() >+ .End() >+ >+ .Function("get_tbl", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet() >+ .End() >+ >+ .Function("tbl_is_null", { params: [], ret: "i32" }) >+ .Call(1) >+ .RefIsNull() >+ .End() >+ >+ .Function("set_tbl_null", { params: [], ret: "void" }) >+ .RefNull() >+ .Call(0) >+ .End() >+ .End().WebAssembly().get()), { imp: { tbl: $1.exports.tbl }}); >+ fullGC() >+ >+ return $2 >+ } >+ >+ const $1 = makeImport() >+ fullGC() >+ >+ assert.eq($1.exports.get_tbl().test, "test") >+ assert.eq($1.exports.tbl_is_null(), 0) >+ >+ $1.exports.set_tbl("hi") >+ fullGC() >+ assert.eq($1.exports.get_tbl(), "hi") >+ assert.eq($1.exports.tbl_is_null(), 0) >+ >+ assert.eq($1.exports.tbl.get(0), "hi") >+ assert.eq($1.exports.tbl.get(1), null) >+ >+ $1.exports.tbl.set(0, { test: "test" }); >+ fullGC() >+ assert.eq($1.exports.get_tbl().test, "test") >+ >+ assert.eq($1.exports.tbl.grow(10), 2) >+ assert.eq($1.exports.tbl.grow(0), 12) >+ assert.eq($1.exports.get_tbl().test, "test") >+ fullGC() >+ assert.eq($1.exports.get_tbl().test, "test") >+} >diff --git a/JSTests/wasm/references/is_null_error.js b/JSTests/wasm/references/is_null_error.js >deleted file mode 100644 >index 915b36ad99614f0f0f5b87c94235724d16955c8b..0000000000000000000000000000000000000000 >--- a/JSTests/wasm/references/is_null_error.js >+++ /dev/null >@@ -1,22 +0,0 @@ >-import * as assert from '../assert.js'; >-import Builder from '../Builder.js'; >- >-{ >- const builder = (new Builder()) >- .Type().End() >- .Function().End() >- .Export() >- .Function("j") >- .End() >- .Code() >- .Function("j", { params: [], ret: "i32" }) >- .I32Const(0) >- .RefIsNull() >- .End() >- .End(); >- >- const bin = builder.WebAssembly(); >- bin.trim(); >- >- assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: ref.is_null to type I32 expected Anyref, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')"); >-} >diff --git a/JSTests/wasm/references/validation.js b/JSTests/wasm/references/validation.js >new file mode 100644 >index 0000000000000000000000000000000000000000..d38c265bfff610ad494884f9a77a6373c84e52a3 >--- /dev/null >+++ b/JSTests/wasm/references/validation.js >@@ -0,0 +1,94 @@ >+import * as assert from '../assert.js'; >+import Builder from '../Builder.js'; >+ >+{ >+ const builder = (new Builder()) >+ .Type().End() >+ .Function().End() >+ .Export() >+ .Function("j") >+ .End() >+ .Code() >+ .Function("j", { params: [], ret: "i32" }) >+ .I32Const(0) >+ .RefIsNull() >+ .End() >+ .End(); >+ >+ const bin = builder.WebAssembly(); >+ bin.trim(); >+ >+ assert.throws(() => new WebAssembly.Module(bin.get()), WebAssembly.CompileError, "WebAssembly.Module doesn't validate: ref.is_null to type I32 expected Anyref, in function at index 0 (evaluating 'new WebAssembly.Module(bin.get())')"); >+} >+ >+{ >+ const builder = (new Builder()) >+ .Type().End() >+ .Function().End() >+ .Export() >+ .Function("j") >+ .End() >+ .Code() >+ .Function("j", { params: [], ret: "void" }) >+ .I32Const(0) >+ .I32Const(0) >+ .TableSet() >+ .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())')"); >+} >+ >+{ >+ const builder = (new Builder()) >+ .Type().End() >+ .Import() >+ .Table("imp", "tbl", {initial: 2, element: "anyfunc"}) >+ .End() >+ .Function().End() >+ .Export() >+ .Function("j") >+ .End() >+ .Code() >+ .Function("j", { params: ["anyref"], ret: "void" }) >+ .I32Const(0) >+ .GetLocal(0) >+ .TableSet() >+ .End() >+ .End(); >+ >+ 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())')"); >+} >+ >+{ >+ const builder = (new Builder()) >+ .Type().End() >+ .Import() >+ .Table("imp", "tbl", {initial: 2, element: "anyfunc"}) >+ .End() >+ .Function().End() >+ .Export() >+ .Function("j") >+ .End() >+ .Code() >+ .Function("j", { params: [], ret: "anyref" }) >+ .I32Const(0) >+ .TableGet() >+ .End() >+ .End(); >+ >+ 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.Table({initial:2, element:"i32"}), TypeError, "WebAssembly.Table expects its 'element' field to be the string 'anyfunc' or 'anyref'"); >+} >diff --git a/JSTests/wasm/wasm.json b/JSTests/wasm/wasm.json >index e61aa74c338660e4b5f16a354d52d151060092b0..30bbc74723783922edbbf634993490f48b7b7e02 100644 >--- a/JSTests/wasm/wasm.json >+++ b/JSTests/wasm/wasm.json >@@ -18,7 +18,7 @@ > }, > "value_type": ["i32", "i64", "f32", "f64", "anyref"], > "block_type": ["i32", "i64", "f32", "f64", "void", "anyref"], >- "elem_type": ["anyfunc"], >+ "elem_type": ["anyfunc","anyref"], > "external_kind": { > "Function": { "type": "uint8", "value": 0 }, > "Table": { "type": "uint8", "value": 1 }, >@@ -66,6 +66,8 @@ > "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" }, > "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" }, > "i32.load8_s": { "category": "memory", "value": 44, "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 198398
:
370997
|
371001
|
371009
|
371019
|
371029
|
371221
|
371242
|
371245
|
371246
|
371248
|
371303
|
371467