WebKit Bugzilla
Attachment 371221 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-20190603154411.patch (text/plain), 70.41 KB, created by
Justin Michaud
on 2019-06-03 15:44:12 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Justin Michaud
Created:
2019-06-03 15:44:12 PDT
Size:
70.41 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..24ea2541f53f6a52b80fac81d1c116bcf6734f62 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,38 @@ auto AirIRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result) > return { }; > } > >+auto AirIRGenerator::addTableGet(ExpressionType& idx, ExpressionType& result) -> PartialResult >+{ >+ 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 >+{ >+ 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 +1878,7 @@ auto AirIRGenerator::addCallIndirect(const Signature& signature, Vector<Expressi > { > ExpressionType calleeIndex = args.takeLast(); > ASSERT(signature.argumentCount() == args.size()); >+ ASSERT(m_info.tableInformation.type() == Wasm::Anyfunc); > > m_makesCalls = true; > // Note: call indirect can call either WebAssemblyFunction or WebAssemblyWrapperFunction. Because >@@ -1856,13 +1894,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..8179851db084acf99c8c1caf8bbbb5f64d833fd0 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,34 @@ auto B3IRGenerator::addRefIsNull(ExpressionType& value, ExpressionType& result) > return { }; > } > >+auto B3IRGenerator::addTableGet(ExpressionType& idx, ExpressionType& result) -> PartialResult >+{ >+ 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 >+{ >+ 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 +1296,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..b802ce543136fdb0c7d01a59e3945f3575fd5646 100644 >--- a/Source/JavaScriptCore/wasm/WasmFormat.h >+++ b/Source/JavaScriptCore/wasm/WasmFormat.h >@@ -214,11 +214,12 @@ public: > ASSERT(!*this); > } > >- TableInformation(uint32_t initial, Optional<uint32_t> maximum, bool isImport) >+ TableInformation(uint32_t initial, Optional<uint32_t> maximum, bool isImport, int8_t type) > : m_initial(initial) > , m_maximum(maximum) > , m_isImport(isImport) > , m_isValid(true) >+ , m_type(type) > { > ASSERT(*this); > } >@@ -227,12 +228,14 @@ public: > bool isImport() const { return m_isImport; } > uint32_t initial() const { return m_initial; } > Optional<uint32_t> maximum() const { return m_maximum; } >+ int32_t type() const { return m_type; } > > private: > uint32_t m_initial; > Optional<uint32_t> m_maximum; > bool m_isImport { false }; > bool m_isValid { false }; >+ int8_t m_type { Wasm::Anyfunc }; > }; > > struct CustomSection { >diff --git a/Source/JavaScriptCore/wasm/WasmFunctionParser.h b/Source/JavaScriptCore/wasm/WasmFunctionParser.h >index 175056b58e721f7442d48c73966de3301195807b..742329bd2280efddb523d274e3ff0f635547a473 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() != Wasm::Anyfunc, "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..f3610d4e673a378094d9a51b22c97b2dde0d98b3 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,7 @@ auto SectionParser::parseTableHelper(bool isImport) -> PartialResult > > ASSERT(!maximum || *maximum >= initial); > >- m_info->tableInformation = TableInformation(initial, maximum, isImport); >+ m_info->tableInformation = TableInformation(initial, maximum, isImport, type); > > return { }; > } >diff --git a/Source/JavaScriptCore/wasm/WasmTable.cpp b/Source/JavaScriptCore/wasm/WasmTable.cpp >index e5c608cb1f028c8644399c59a07d23790d4e37a9..898257a2730905698e4ad2d8369d6042a372ca2d 100644 >--- a/Source/JavaScriptCore/wasm/WasmTable.cpp >+++ b/Source/JavaScriptCore/wasm/WasmTable.cpp >@@ -47,18 +47,8 @@ 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, int32_t type) >+ : m_type(type) > { > setLength(initial); > m_maximum = maximum; >@@ -66,21 +56,33 @@ Table::Table(uint32_t initial, Optional<uint32_t> maximum) > > // 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, int32_t type) >+{ >+ if (!isValidLength(initial)) >+ return nullptr; >+ if (type == Wasm::Anyfunc) >+ return adoptRef(new (NotNull, fastMalloc(sizeof(FuncRefTable))) FuncRefTable(initial, maximum)); >+ if (type == Wasm::Anyref) >+ return adoptRef(new (NotNull, fastMalloc(sizeof(Table))) Table(initial, maximum, Wasm::Anyref)); >+ ASSERT_NOT_REACHED(); >+ return nullptr; >+} >+ > Optional<uint32_t> Table::grow(uint32_t delta) > { > if (delta == 0) > return length(); > >+ ConcurrentJSLocker locker(m_lock); >+ > using Checked = Checked<uint32_t, RecordOverflow>; > Checked newLengthChecked = length(); > newLengthChecked += delta; >@@ -93,42 +95,79 @@ Optional<uint32_t> Table::grow(uint32_t delta) > if (!isValidLength(newLength)) > return WTF::nullopt; > >- auto checkedGrow = [&] (auto& container) { >- if (newLengthChecked.unsafeGet() > allocatedLength(m_length)) { >- Checked reallocSizeChecked = allocatedLength(newLengthChecked.unsafeGet()); >- reallocSizeChecked *= sizeof(*container.get()); >- uint32_t reallocSize; >- if (reallocSizeChecked.safeGet(reallocSize) == CheckedState::DidOverflow) >- return false; >- // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425 >- container.realloc(reallocSize); >- } >- for (uint32_t i = m_length; i < allocatedLength(newLength); ++i) >- new (&container.get()[i]) std::remove_reference_t<decltype(*container.get())>(); >- return true; >- }; >- >- if (!checkedGrow(m_importableFunctions)) >- return WTF::nullopt; >- if (!checkedGrow(m_instances)) >+ if (m_type == Wasm::Anyfunc) { >+ auto& subThis = *static_cast<FuncRefTable*>(this); >+ if (!checkedGrow(m_length, newLength, subThis.m_importableFunctions)) >+ return WTF::nullopt; >+ if (!checkedGrow(m_length, newLength, subThis.m_instances)) >+ return WTF::nullopt; >+ } >+ >+ if (!checkedGrow(m_length, newLength, 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 (m_type == Wasm::Anyfunc) { >+ auto& subThis = *static_cast<FuncRefTable*>(this); >+ subThis.m_importableFunctions.get()[index & m_mask] = WasmToWasmImportableFunction(); >+ ASSERT(subThis.m_importableFunctions.get()[index & m_mask].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code. >+ subThis.m_instances.get()[index & m_mask] = nullptr; >+ } >+ m_jsValues.get()[index & m_mask].setStartingValue(jsNull()); > } > >-void Table::setFunction(uint32_t index, WasmToWasmImportableFunction function, Instance* instance) >+void Table::set(uint32_t index, JSValue value) > { > RELEASE_ASSERT(index < length()); >+ RELEASE_ASSERT(m_owner); >+ RELEASE_ASSERT(m_type == Wasm::Anyref); >+ clear(index); >+ m_jsValues.get()[index & m_mask].set(*m_owner->vm(), m_owner, value); >+} >+ >+JSValue Table::get(uint32_t index) >+{ >+ RELEASE_ASSERT(index < length()); >+ RELEASE_ASSERT(m_owner); >+ return m_jsValues.get()[index & m_mask].get(); >+} >+ >+void Table::visitChildren(SlotVisitor& visitor) >+{ >+ ConcurrentJSLocker locker(m_lock); >+ for (unsigned i = 0; i < m_length; ++i) >+ visitor.append(m_jsValues.get()[i]); >+} >+ >+FuncRefTable::FuncRefTable(uint32_t initial, Optional<uint32_t> maximum) >+ : Table(initial, maximum, Wasm::Anyfunc) >+{ >+ // 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 FuncRefTable::setFunction(uint32_t index, JSObject* optionalWrapper, WasmToWasmImportableFunction function, Instance* instance) >+{ >+ RELEASE_ASSERT(index < length()); >+ clear(index); >+ if (optionalWrapper) { >+ RELEASE_ASSERT(m_owner); >+ 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..d106647660900dbe17da94d8b6ec6b1fe4535e5d 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> >@@ -40,36 +41,80 @@ class Instance; > > class Table : public ThreadSafeRefCounted<Table> { > public: >- static RefPtr<Table> tryCreate(uint32_t initial, Optional<uint32_t> maximum); >+ static RefPtr<Table> tryCreate(uint32_t initial, Optional<uint32_t> maximum, int32_t type); > >- JS_EXPORT_PRIVATE ~Table(); >+ JS_EXPORT_PRIVATE ~Table() = default; >+ >+ template<typename T> >+ static inline bool checkedGrow(uint32_t length, uint32_t newLength, T& container) WARN_UNUSED_RETURN { >+ if (newLength > allocatedLength(length)) { >+ Checked<uint32_t, RecordOverflow> reallocSizeChecked = allocatedLength(newLength); >+ reallocSizeChecked *= sizeof(*container.get()); >+ uint32_t reallocSize; >+ if (reallocSizeChecked.safeGet(reallocSize) == CheckedState::DidOverflow) >+ return false; >+ // FIXME this over-allocates and could be smarter about not committing all of that memory https://bugs.webkit.org/show_bug.cgi?id=181425 >+ container.realloc(reallocSize); >+ } >+ for (uint32_t i = length; i < allocatedLength(newLength); ++i) >+ new (&container.get()[i]) std::remove_reference_t<decltype(*container.get())>(); >+ return true; >+ } > > 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; } >+ int32_t type() const { return m_type; } > static bool isValidLength(uint32_t length) { return length < maxTableEntries; } > >-private: >- Table(uint32_t initial, Optional<uint32_t> maximum); >+ void setOwner(JSObject* instance) { m_owner = instance; } >+ void clear(uint32_t); >+ void set(uint32_t, JSValue); >+ JSValue get(uint32_t); >+ >+ Optional<uint32_t> grow(uint32_t delta); >+ >+ void visitChildren(SlotVisitor&); >+ >+protected: >+ Table(uint32_t initial, Optional<uint32_t> maximum, int32_t type); >+ WTF_MAKE_NONCOPYABLE(Table); > > void setLength(uint32_t); > >- 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; >+ int32_t m_type; > Optional<uint32_t> m_maximum; >+ >+ MallocPtr<WriteBarrier<Unknown>> m_jsValues; >+ JSObject* m_owner; >+ >+ ConcurrentJSLock m_lock; >+}; >+ >+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); >+ >+ 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; >+ >+ friend class Table; > }; > > } } // namespace JSC::Wasm >diff --git a/Source/JavaScriptCore/wasm/WasmValidate.cpp b/Source/JavaScriptCore/wasm/WasmValidate.cpp >index fd54162008409da36f9694d38912c8efc91428b5..36c77b72b77b37bb72046a6b7b3a0254e8cc2713 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(Type::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(Type::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() != Wasm::Anyfunc, "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..3a9b286adc0bc673f13a7496036618e1ff72cfb1 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.cpp >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.cpp >@@ -60,16 +60,11 @@ 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) > { >+ m_table->setOwner(this); > Base::finishCreation(vm); > ASSERT(inherits(vm, info())); > } >@@ -85,55 +80,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(VM&, uint32_t index, JSValue value) > { > RELEASE_ASSERT(index < length()); >- return m_jsFunctions.get()[index & m_table->mask()].get(); >+ RELEASE_ASSERT(m_table->type() == Wasm::Anyref); >+ m_table->set(index, value); > } > >-void JSWebAssemblyTable::clearFunction(uint32_t index) >+void JSWebAssemblyTable::set(VM&, 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->type() == Wasm::Anyfunc); >+ 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(VM&, 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->type() == Wasm::Anyfunc); >+ 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..f32df9420bc62f1f45979226995a04797d0595a9 100644 >--- a/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h >+++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h >@@ -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(VM&, uint32_t, WebAssemblyFunction*); >+ void set(VM&, uint32_t, WebAssemblyWrapperFunction*); >+ void set(VM&, 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..bc44055a7ebf923e709ecd7374f028ff1a5f7505 100644 >--- a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp >@@ -301,7 +301,8 @@ 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()); >+ RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum(), >+ moduleInformation.tableInformation.type()); > if (!wasmTable) > return exception(createJSWebAssemblyLinkError(exec, vm, "couldn't create Table")); > JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, globalObject->webAssemblyTableStructure(), wasmTable.releaseNonNull()); >@@ -536,12 +537,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(vm, tableIndex, wasmFunction); > ++tableIndex; > continue; > } > >- table->setFunction(vm, tableIndex, >+ table->set(vm, tableIndex, > WebAssemblyWrapperFunction::create(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, functionIndex, m_instance.get(), signatureIndex)); > ++tableIndex; > continue; >@@ -557,7 +558,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(vm, tableIndex, function); > ++tableIndex; > } > }); >diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp >index 1a2eaa7ab2622d31400bccf99a0617b7e2e372bd..d409dcfbbf4d4ca695b8b17724841954ac1bc5f8 100644 >--- a/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp >+++ b/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp >@@ -58,14 +58,16 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyTable(ExecState* exec) > memoryDescriptor = jsCast<JSObject*>(argument); > } > >+ int32_t 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" && elementString != "anyref") >+ return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, "WebAssembly.Table expects its 'element' field to be the string 'anyfunc' or 'anyref'"_s))); >+ type = elementString == "anyfunc" ? Wasm::Anyfunc : Wasm::Anyref; > } > > Identifier initialIdent = Identifier::fromString(&vm, "initial"); >@@ -90,7 +92,7 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyTable(ExecState* exec) > } > } > >- RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(initial, maximum); >+ RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(initial, maximum, type); > if (!wasmTable) { > return JSValue::encode(throwException(exec, throwScope, > createRangeError(exec, "couldn't create Table"_s))); >diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.cpp >index f48178374fd81135f0988d910219dd4896809f44..86b9d2a87b50b5142957608dc4b48f078356d73a 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,15 +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()->type() == Wasm::Anyfunc) { >+ 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(vm, index, wasmFunction); >+ else >+ table->set(vm, index, wasmWrapperFunction); >+ } >+ } else { >+ table->set(vm, 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..e83601d9c62c58e082f75ffce50200d1ad21b76a >--- /dev/null >+++ b/JSTests/wasm/references/validation.js >@@ -0,0 +1,90 @@ >+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())')"); >+} >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