diff --git a/Tests/LibWasm/test-wasm.cpp b/Tests/LibWasm/test-wasm.cpp index 0ecd294ec6..ecd89f8932 100644 --- a/Tests/LibWasm/test-wasm.cpp +++ b/Tests/LibWasm/test-wasm.cpp @@ -190,6 +190,12 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke) case Wasm::ValueType::Kind::ExternReference: arguments.append(Wasm::Value(Wasm::ExternAddress { static_cast(value) })); break; + case Wasm::ValueType::Kind::NullFunctionReference: + arguments.append(Wasm::Value(Wasm::Value::Null { Wasm::ValueType(Wasm::ValueType::Kind::FunctionReference) })); + break; + case Wasm::ValueType::Kind::NullExternReference: + arguments.append(Wasm::Value(Wasm::Value::Null { Wasm::ValueType(Wasm::ValueType::Kind::ExternReference) })); + break; } } @@ -206,6 +212,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke) result.values().first().value().visit( [&](const auto& value) { return_value = JS::Value(static_cast(value)); }, [&](const Wasm::FunctionAddress& index) { return_value = JS::Value(static_cast(index.value())); }, - [&](const Wasm::ExternAddress& index) { return_value = JS::Value(static_cast(index.value())); }); + [&](const Wasm::ExternAddress& index) { return_value = JS::Value(static_cast(index.value())); }, + [&](const Wasm::Value::Null&) { return_value = JS::js_null(); }); return return_value; } diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp index 2daca7ab14..592c90223c 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp @@ -152,7 +152,8 @@ InstantiationResult AbstractMachine::instantiate(const Module& module, Vectoris_error()) return; if (main_module_instance.memories().size() <= data.index.value()) { diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h index 98db12d9ab..b7b49b664b 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h +++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h @@ -45,7 +45,11 @@ public: { } - using AnyValueType = Variant; + struct Null { + ValueType type; + }; + + using AnyValueType = Variant; explicit Value(AnyValueType value) : m_value(move(value)) , m_type(ValueType::I32) @@ -62,6 +66,8 @@ public: m_type = ValueType { ValueType::FunctionReference }; else if (m_value.has()) m_type = ValueType { ValueType::ExternReference }; + else if (m_value.has()) + m_type = ValueType { m_value.get().type.kind() == ValueType::ExternReference ? ValueType::NullExternReference : ValueType::NullFunctionReference }; else VERIFY_NOT_REACHED(); } @@ -90,6 +96,14 @@ public: case ValueType::Kind::F64: m_value = bit_cast(raw_value); break; + case ValueType::Kind::NullFunctionReference: + VERIFY(raw_value == 0); + m_value = Null { ValueType(ValueType::Kind::FunctionReference) }; + break; + case ValueType::Kind::NullExternReference: + VERIFY(raw_value == 0); + m_value = Null { ValueType(ValueType::Kind::ExternReference) }; + break; default: VERIFY_NOT_REACHED(); } @@ -139,6 +153,10 @@ public: [&](const ExternAddress& address) { if constexpr (IsSame) result = address; + }, + [&](const Null& null) { + if constexpr (IsSame) + result = null; }); return result; } diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp index 4a27fcbd49..fcfd14ddee 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp @@ -85,6 +85,8 @@ void Configuration::dump_stack() v.value().visit([](const T& v) { if constexpr (IsIntegral || IsFloatingPoint) dbgln(" {}", v); + else if constexpr (IsSame) + dbgln(" *null"); else dbgln(" *{}", v.value()); }); @@ -95,6 +97,8 @@ void Configuration::dump_stack() local.value().visit([](const T& v) { if constexpr (IsIntegral || IsFloatingPoint) dbgln(" {}", v); + else if constexpr (IsSame) + dbgln(" *null"); else dbgln(" *{}", v.value()); }); diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp index efeec9a304..dbce68e6e7 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp @@ -590,10 +590,29 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi } case Instructions::table_get.value(): case Instructions::table_set.value(): - case Instructions::ref_null.value(): - case Instructions::ref_func.value(): - case Instructions::ref_is_null.value(): goto unimplemented; + case Instructions::ref_null.value(): { + auto type = instruction.arguments().get(); + TRAP_IF_NOT(type.is_reference()); + configuration.stack().push(Value(Value::Null { type })); + return; + }; + case Instructions::ref_func.value(): { + auto index = instruction.arguments().get().value(); + auto& functions = configuration.frame().module().functions(); + TRAP_IF_NOT(functions.size() > index); + auto address = functions[index]; + configuration.stack().push(Value(ValueType(ValueType::FunctionReference), address.value())); + return; + } + case Instructions::ref_is_null.value(): { + auto top = configuration.stack().peek().get_pointer(); + TRAP_IF_NOT(top); + TRAP_IF_NOT(top->type().is_reference()); + auto is_null = top->to().has_value(); + configuration.stack().peek() = Value(ValueType(ValueType::I32), static_cast(is_null ? 1 : 0)); + return; + } case Instructions::drop.value(): configuration.stack().pop(); return; diff --git a/Userland/Libraries/LibWasm/Types.h b/Userland/Libraries/LibWasm/Types.h index 9134af6272..bb0299b38a 100644 --- a/Userland/Libraries/LibWasm/Types.h +++ b/Userland/Libraries/LibWasm/Types.h @@ -170,6 +170,8 @@ public: F64, FunctionReference, ExternReference, + NullFunctionReference, + NullExternReference, }; explicit ValueType(Kind kind) @@ -177,7 +179,7 @@ public: { } - auto is_reference() const { return m_kind == ExternReference || m_kind == FunctionReference; } + auto is_reference() const { return m_kind == ExternReference || m_kind == FunctionReference || m_kind == NullExternReference || m_kind == NullFunctionReference; } auto is_numeric() const { return !is_reference(); } auto kind() const { return m_kind; } @@ -198,6 +200,10 @@ public: return "funcref"; case ExternReference: return "externref"; + case NullFunctionReference: + return "ref.null externref"; + case NullExternReference: + return "ref.null funcref"; } VERIFY_NOT_REACHED(); } diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp index a8e8f82c8c..b4bd033dc4 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp +++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp @@ -262,7 +262,10 @@ JS::Value to_js_value(Wasm::Value& wasm_value, JS::GlobalObject& global_object) case Wasm::ValueType::FunctionReference: // FIXME: What's the name of a function reference that isn't exported? return create_native_function(wasm_value.to().value(), "FIXME_IHaveNoIdeaWhatThisShouldBeCalled", global_object); + case Wasm::ValueType::NullFunctionReference: + return JS::js_null(); case Wasm::ValueType::ExternReference: + case Wasm::ValueType::NullExternReference: TODO(); } VERIFY_NOT_REACHED(); @@ -304,6 +307,8 @@ Optional to_webassembly_value(JS::Value value, const Wasm::ValueTyp } case Wasm::ValueType::FunctionReference: case Wasm::ValueType::ExternReference: + case Wasm::ValueType::NullFunctionReference: + case Wasm::ValueType::NullExternReference: TODO(); } diff --git a/Userland/Utilities/wasm.cpp b/Userland/Utilities/wasm.cpp index f9a2d97dc5..5b1d7f786d 100644 --- a/Userland/Utilities/wasm.cpp +++ b/Userland/Utilities/wasm.cpp @@ -202,9 +202,11 @@ static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPoi warnln("Returned:"); for (auto& value : result.values()) { auto str = value.value().visit( - [&](const auto& value) { + [&](const T& value) { if constexpr (requires { value.value(); }) return String::formatted(" -> addr{} ", value.value()); + else if constexpr (IsSame) + return String::formatted(" ->addr(null)"); else return String::formatted(" -> {} ", value); }); @@ -460,9 +462,11 @@ int main(int argc, char* argv[]) warnln("Returned:"); for (auto& value : result.values()) { value.value().visit( - [&](const auto& value) { + [&](const T& value) { if constexpr (requires { value.value(); }) out(" -> addr{} ", value.value()); + else if constexpr (IsSame) + out(" ->addr(null)"); else out(" -> {} ", value); });