From 51bab5b186b349e8fd1d1d96dde0b823d8b313f0 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Tue, 22 Apr 2025 09:48:26 +0200 Subject: [PATCH] LibWasm: Make traps hold on to externally-managed data ...instead of specially handling JS::Completion. This makes it possible for LibWeb/LibJS to have full control over how these things are made, stored, and visited (whenever). Fixes an issue where we couldn't roundtrip a JS exception through Wasm. --- .../AbstractMachine/AbstractMachine.cpp | 26 ++-- .../LibWasm/AbstractMachine/AbstractMachine.h | 119 +++++++++++------- .../AbstractMachine/BytecodeInterpreter.cpp | 33 +++-- .../AbstractMachine/BytecodeInterpreter.h | 17 +-- .../LibWasm/AbstractMachine/Configuration.cpp | 4 +- .../LibWasm/AbstractMachine/Interpreter.h | 3 +- Libraries/LibWasm/CMakeLists.txt | 2 +- Libraries/LibWeb/WebAssembly/WebAssembly.cpp | 53 ++++++-- Meta/Lagom/CMakeLists.txt | 2 +- Tests/LibWasm/test-wasm.cpp | 10 +- Utilities/wasm.cpp | 18 +-- 11 files changed, 176 insertions(+), 111 deletions(-) diff --git a/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp b/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp index f9202554318..34f5bb018fb 100644 --- a/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp +++ b/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp @@ -239,6 +239,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector(); @@ -306,9 +307,9 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vectorexpression, 1, }); - auto result = config.execute(interpreter).assert_wasm_result(); + auto result = config.execute(interpreter); if (result.is_trap()) - return InstantiationError { ByteString::formatted("Element section initialisation trapped: {}", result.trap().reason) }; + return InstantiationError { "Element section initialisation trapped", move(result.trap()) }; auto d = result.values().first().to(); auto table_instance = m_store.get(main_module_instance.tables()[active_ptr->index.value()]); if (current_index >= main_module_instance.elements().size()) @@ -341,9 +342,9 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector(); if (main_module_instance.memories().size() <= data.index.value()) { return InstantiationError { @@ -391,7 +392,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector AbstractMachine::allocate_all_final_phase(Module co Result AbstractMachine::invoke(FunctionAddress address, Vector arguments) { BytecodeInterpreter interpreter(m_stack_info); + auto handle = register_scoped(interpreter); return invoke(interpreter, address, move(arguments)); } @@ -575,4 +577,10 @@ void Linker::populate() m_unresolved_imports.set(m_ordered_imports.last()); } } + +void AbstractMachine::visit_external_resources(HostVisitOps const& host) +{ + for (auto interpreter_ptr : m_active_interpreters) + interpreter_ptr->visit_external_resources(host); +} } diff --git a/Libraries/LibWasm/AbstractMachine/AbstractMachine.h b/Libraries/LibWasm/AbstractMachine/AbstractMachine.h index 5bfdabd36ea..96a66a75e2c 100644 --- a/Libraries/LibWasm/AbstractMachine/AbstractMachine.h +++ b/Libraries/LibWasm/AbstractMachine/AbstractMachine.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include @@ -14,18 +15,13 @@ #include #include -// NOTE: Special case for Wasm::Result. -#include - namespace Wasm { class Configuration; class Result; struct Interpreter; +struct Trap; -struct InstantiationError { - ByteString error { "Unknown error" }; -}; struct LinkError { enum OtherErrors { InvalidImportedModule, @@ -198,37 +194,43 @@ private: u128 m_value; }; -struct Trap { - ByteString reason; +struct ExternallyManagedTrap { + Array data; + + template + T const& unsafe_external_object_as() const + { + static_assert(sizeof(T) <= sizeof(data), "Object size is too large for ExternallyManagedTrap"); + return *reinterpret_cast(data.data()); + } }; -// A variant of Result that does not include external reasons for error (JS::Completion, for now). -class PureResult { -public: - explicit PureResult(Vector values) - : m_result(move(values)) +struct Trap { + Variant data; + + ByteString format() const { + if (auto const* ptr = data.get_pointer()) + return *ptr; + return ""; } - PureResult(Trap trap) - : m_result(move(trap)) + template + static Trap from_external_object(T&& object) { + static_assert(sizeof(T) <= sizeof(ExternallyManagedTrap::data), "Object size is too large for ExternallyManagedTrap"); + static_assert(IsTriviallyCopyable, "Object must be trivially copyable"); + static_assert(IsTriviallyDestructible, "Object must be trivially destructible"); + + ExternallyManagedTrap externally_managed_trap; + new (externally_managed_trap.data.data()) T(forward(object)); + return Trap { externally_managed_trap }; } - auto is_trap() const { return m_result.has(); } - auto& values() const { return m_result.get>(); } - auto& values() { return m_result.get>(); } - auto& trap() const { return m_result.get(); } - auto& trap() { return m_result.get(); } - -private: - friend class Result; - explicit PureResult(Variant, Trap>&& result) - : m_result(move(result)) + static Trap from_string(ByteString string) { + return Trap { move(string) }; } - - Variant, Trap> m_result; }; class Result { @@ -243,34 +245,24 @@ public: { } - Result(JS::Completion completion) - : m_result(move(completion)) - { - VERIFY(m_result.get().is_abrupt()); - } - - Result(PureResult&& result) - : m_result(result.m_result.downcast()) - { - } - auto is_trap() const { return m_result.has(); } - auto is_completion() const { return m_result.has(); } auto& values() const { return m_result.get>(); } auto& values() { return m_result.get>(); } auto& trap() const { return m_result.get(); } auto& trap() { return m_result.get(); } - auto& completion() { return m_result.get(); } - auto& completion() const { return m_result.get(); } - - PureResult assert_wasm_result() && - { - VERIFY(!is_completion()); - return PureResult(move(m_result).downcast, Trap>()); - } private: - Variant, Trap, JS::Completion> m_result; + explicit Result(Variant, Trap>&& result) + : m_result(move(result)) + { + } + + Variant, Trap> m_result; +}; + +struct InstantiationError { + ByteString error { "Unknown error" }; + Optional relevant_trap {}; }; using ExternValue = Variant; @@ -628,6 +620,10 @@ private: using InstantiationResult = AK::ErrorOr, InstantiationError>; +struct HostVisitOps { + Function visit_trap; +}; + class AbstractMachine { public: explicit AbstractMachine() = default; @@ -644,11 +640,38 @@ public: void enable_instruction_count_limit() { m_should_limit_instruction_count = true; } + void visit_external_resources(HostVisitOps const&); + private: + class InterpreterHandle { + public: + explicit InterpreterHandle(AbstractMachine& machine, Interpreter& interpreter) + : m_machine(machine) + , m_interpreter(interpreter) + { + m_machine.m_active_interpreters.set(&m_interpreter); + } + + ~InterpreterHandle() + { + m_machine.m_active_interpreters.remove(&m_interpreter); + } + + private: + AbstractMachine& m_machine; + Interpreter& m_interpreter; + }; + + [[nodiscard]] InterpreterHandle register_scoped(Interpreter& interpreter) + { + return InterpreterHandle(*this, interpreter); + } + Optional allocate_all_initial_phase(Module const&, ModuleInstance&, Vector&, Vector& global_values, Vector& own_functions); Optional allocate_all_final_phase(Module const&, ModuleInstance&, Vector>& elements); Store m_store; StackInfo m_stack_info; + HashTable m_active_interpreters; bool m_should_limit_instruction_count { false }; }; diff --git a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp index 7e61accaaac..63ab2feec78 100644 --- a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp +++ b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp @@ -42,7 +42,7 @@ void BytecodeInterpreter::interpret(Configuration& configuration) while (current_ip_value < max_ip_value) { if (should_limit_instruction_count) { if (executed_instructions++ >= Constants::max_allowed_executed_instructions_per_call) [[unlikely]] { - m_trap = Trap { "Exceeded maximum allowed number of instructions" }; + m_trap = Trap::from_string("Exceeded maximum allowed number of instructions"); return; } } @@ -78,7 +78,7 @@ void BytecodeInterpreter::load_and_push(Configuration& configuration, Instructio auto base = entry.to(); u64 instance_address = static_cast(bit_cast(base)) + arg.offset; if (instance_address + sizeof(ReadType) > memory->size()) { - m_trap = Trap { "Memory access out of bounds" }; + m_trap = Trap::from_string("Memory access out of bounds"); dbgln("LibWasm: Memory access out of bounds (expected {} to be less than or equal to {})", instance_address + sizeof(ReadType), memory->size()); return; } @@ -103,7 +103,7 @@ void BytecodeInterpreter::load_and_push_mxn(Configuration& configuration, Instru auto base = entry.to(); u64 instance_address = static_cast(bit_cast(base)) + arg.offset; if (instance_address + M * N / 8 > memory->size()) { - m_trap = Trap { "Memory access out of bounds" }; + m_trap = Trap::from_string("Memory access out of bounds"); dbgln("LibWasm: Memory access out of bounds (expected {} to be less than or equal to {})", instance_address + M * N / 8, memory->size()); return; } @@ -131,7 +131,7 @@ void BytecodeInterpreter::load_and_push_lane_n(Configuration& configuration, Ins auto base = configuration.value_stack().take_last().to(); u64 instance_address = static_cast(bit_cast(base)) + memarg_and_lane.memory.offset; if (instance_address + N / 8 > memory->size()) { - m_trap = Trap { "Memory access out of bounds" }; + m_trap = Trap::from_string("Memory access out of bounds"); return; } auto slice = memory->data().bytes().slice(instance_address, N / 8); @@ -149,7 +149,7 @@ void BytecodeInterpreter::load_and_push_zero_n(Configuration& configuration, Ins auto base = configuration.value_stack().take_last().to(); u64 instance_address = static_cast(bit_cast(base)) + memarg_and_lane.offset; if (instance_address + N / 8 > memory->size()) { - m_trap = Trap { "Memory access out of bounds" }; + m_trap = Trap::from_string("Memory access out of bounds"); return; } auto slice = memory->data().bytes().slice(instance_address, N / 8); @@ -168,7 +168,7 @@ void BytecodeInterpreter::load_and_push_m_splat(Configuration& configuration, In auto base = entry.to(); u64 instance_address = static_cast(bit_cast(base)) + arg.offset; if (instance_address + M / 8 > memory->size()) { - m_trap = Trap { "Memory access out of bounds" }; + m_trap = Trap::from_string("Memory access out of bounds"); dbgln("LibWasm: Memory access out of bounds (expected {} to be less than or equal to {})", instance_address + M / 8, memory->size()); return; } @@ -238,7 +238,7 @@ void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAdd configuration.value_stack().remove(configuration.value_stack().size() - span.size(), span.size()); - Result result { Trap { ""sv } }; + Result result { Trap::from_string("") }; if (instance->has()) { CallFrameHandle handle { *this, configuration }; result = configuration.call(*this, address, move(args)); @@ -251,11 +251,6 @@ void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAdd return; } - if (result.is_completion()) { - m_trap = move(result.completion()); - return; - } - configuration.value_stack().ensure_capacity(configuration.value_stack().size() + result.values().size()); for (auto& entry : result.values().in_reverse()) configuration.value_stack().unchecked_append(entry); @@ -361,7 +356,7 @@ void BytecodeInterpreter::store_to_memory(Configuration& configuration, Instruct Checked addition { instance_address }; addition += data.size(); if (addition.has_overflow() || addition.value() > memory->size()) { - m_trap = Trap { "Memory access out of bounds" }; + m_trap = Trap::from_string("Memory access out of bounds"); dbgln("LibWasm: Memory access out of bounds (expected 0 <= {} and {} <= {})", instance_address, instance_address + data.size(), memory->size()); return; } @@ -376,7 +371,7 @@ T BytecodeInterpreter::read_value(ReadonlyBytes data) auto value_or_error = stream.read_value>(); if (value_or_error.is_error()) { dbgln("Read from {} failed", data.data()); - m_trap = Trap { "Read from memory failed" }; + m_trap = Trap::from_string("Read from memory failed"); } return value_or_error.release_value(); } @@ -387,7 +382,7 @@ float BytecodeInterpreter::read_value(ReadonlyBytes data) FixedMemoryStream stream { data }; auto raw_value_or_error = stream.read_value>(); if (raw_value_or_error.is_error()) - m_trap = Trap { "Read from memory failed" }; + m_trap = Trap::from_string("Read from memory failed"); auto raw_value = raw_value_or_error.release_value(); return bit_cast(static_cast(raw_value)); } @@ -398,7 +393,7 @@ double BytecodeInterpreter::read_value(ReadonlyBytes data) FixedMemoryStream stream { data }; auto raw_value_or_error = stream.read_value>(); if (raw_value_or_error.is_error()) - m_trap = Trap { "Read from memory failed" }; + m_trap = Trap::from_string("Read from memory failed"); auto raw_value = raw_value_or_error.release_value(); return bit_cast(static_cast(raw_value)); } @@ -409,7 +404,7 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con switch (instruction.opcode().value()) { case Instructions::unreachable.value(): - m_trap = Trap { "Unreachable" }; + m_trap = Trap::from_string("Unreachable"); return; case Instructions::nop.value(): return; @@ -1659,7 +1654,7 @@ void DebuggerBytecodeInterpreter::interpret_instruction(Configuration& configura if (pre_interpret_hook) { auto result = pre_interpret_hook(configuration, ip, instruction); if (!result) { - m_trap = Trap { "Trapped by user request" }; + m_trap = Trap::from_string("Trapped by user request"); return; } } @@ -1669,7 +1664,7 @@ void DebuggerBytecodeInterpreter::interpret_instruction(Configuration& configura if (post_interpret_hook) { auto result = post_interpret_hook(configuration, ip, instruction, *this); if (!result) { - m_trap = Trap { "Trapped by user request" }; + m_trap = Trap::from_string("Trapped by user request"); return; } } diff --git a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h index 319ec4408a0..fcb9127ab47 100644 --- a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h +++ b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h @@ -22,14 +22,17 @@ struct BytecodeInterpreter : public Interpreter { virtual ~BytecodeInterpreter() override = default; virtual bool did_trap() const final { return !m_trap.has(); } - virtual ByteString trap_reason() const final + virtual Trap trap() const final { - return m_trap.visit( - [](Empty) -> ByteString { VERIFY_NOT_REACHED(); }, - [](Trap const& trap) { return trap.reason; }, - [](JS::Completion const& completion) { return completion.value().to_string_without_side_effects().to_byte_string(); }); + return m_trap.get(); } virtual void clear_trap() final { m_trap = Empty {}; } + virtual void visit_external_resources(HostVisitOps const& host) override + { + if (auto ptr = m_trap.get_pointer()) + if (auto data = ptr->data.get_pointer()) + host.visit_trap(*data); + } struct CallFrameHandle { explicit CallFrameHandle(BytecodeInterpreter& interpreter, Configuration& configuration) @@ -82,11 +85,11 @@ protected: ALWAYS_INLINE bool trap_if_not(bool value, StringView reason) { if (!value) - m_trap = Trap { reason }; + m_trap = Trap { ByteString(reason) }; return !m_trap.has(); } - Variant m_trap; + Variant m_trap; StackInfo const& m_stack_info; }; diff --git a/Libraries/LibWasm/AbstractMachine/Configuration.cpp b/Libraries/LibWasm/AbstractMachine/Configuration.cpp index fab66867ced..17fd1ee2f41 100644 --- a/Libraries/LibWasm/AbstractMachine/Configuration.cpp +++ b/Libraries/LibWasm/AbstractMachine/Configuration.cpp @@ -22,7 +22,7 @@ Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Ve { auto* function = m_store.get(address); if (!function) - return Trap {}; + return Trap::from_string("Attempt to call nonexistent function by address"); if (auto* wasm_function = function->get_pointer()) { Vector locals = move(arguments); locals.ensure_capacity(locals.size() + wasm_function->code().func().locals().size()); @@ -50,7 +50,7 @@ Result Configuration::execute(Interpreter& interpreter) { interpreter.interpret(*this); if (interpreter.did_trap()) - return Trap { interpreter.trap_reason() }; + return interpreter.trap(); Vector results; results.ensure_capacity(frame().arity()); diff --git a/Libraries/LibWasm/AbstractMachine/Interpreter.h b/Libraries/LibWasm/AbstractMachine/Interpreter.h index cce7ad5b3a7..122ed2b95b8 100644 --- a/Libraries/LibWasm/AbstractMachine/Interpreter.h +++ b/Libraries/LibWasm/AbstractMachine/Interpreter.h @@ -13,9 +13,10 @@ namespace Wasm { struct Interpreter { virtual ~Interpreter() = default; virtual void interpret(Configuration&) = 0; + virtual Trap trap() const = 0; virtual bool did_trap() const = 0; - virtual ByteString trap_reason() const = 0; virtual void clear_trap() = 0; + virtual void visit_external_resources(HostVisitOps const&) { } }; } diff --git a/Libraries/LibWasm/CMakeLists.txt b/Libraries/LibWasm/CMakeLists.txt index d120f3aee18..478cc5cd56b 100644 --- a/Libraries/LibWasm/CMakeLists.txt +++ b/Libraries/LibWasm/CMakeLists.txt @@ -12,6 +12,6 @@ if (NOT WIN32) endif() serenity_lib(LibWasm wasm) -target_link_libraries(LibWasm PRIVATE LibCore LibGC LibJS) +target_link_libraries(LibWasm PRIVATE LibCore) include(wasm_spec_tests) diff --git a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp index a9520929dd0..c68b7b549c5 100644 --- a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp +++ b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp @@ -59,6 +59,10 @@ void visit_edges(JS::Object& object, JS::Cell::Visitor& visitor) visitor.visit(cache.imported_objects()); visitor.visit(cache.extern_values()); visitor.visit(cache.global_instances()); + cache.abstract_machine().visit_external_resources({ .visit_trap = [&visitor](Wasm::ExternallyManagedTrap const& trap) { + auto& completion = trap.unsafe_external_object_as(); + visitor.visit(completion.value()); + } }); } } @@ -156,6 +160,32 @@ WebIDL::ExceptionOr> instantiate_streaming(JS::VM& vm, namespace Detail { +#define TRY_OR_RETURN_TRAP(...) \ + ({ \ + /* Ignore -Wshadow to allow nesting the macro. */ \ + AK_IGNORE_DIAGNOSTIC("-Wshadow", \ + auto&& _temporary_result = (__VA_ARGS__)); \ + static_assert(!::AK::Detail::IsLvalueReference, \ + "Do not return a reference from a fallible expression"); \ + if (_temporary_result.is_error()) [[unlikely]] \ + return Wasm::Trap::from_external_object(_temporary_result.release_error()); \ + _temporary_result.release_value(); \ + }) + +#define TRY_OR_RETURN_OOM_TRAP(vm, ...) \ + ({ \ + /* Ignore -Wshadow to allow nesting the macro. */ \ + AK_IGNORE_DIAGNOSTIC("-Wshadow", \ + auto&& _temporary_result = (__VA_ARGS__)); \ + if (_temporary_result.is_error()) { \ + VERIFY(_temporary_result.error().code() == ENOMEM); \ + return Wasm::Trap::from_external_object((vm).throw_completion((vm).error_message(::JS::VM::ErrorMessage::OutOfMemory))); \ + } \ + static_assert(!::AK::Detail::IsLvalueReference, \ + "Do not return a reference from a fallible expression"); \ + _temporary_result.release_value(); \ + }) + JS::ThrowCompletionOr> instantiate_module(JS::VM& vm, Wasm::Module const& module, GC::Ptr import_object) { Wasm::Linker linker { module }; @@ -216,28 +246,28 @@ JS::ThrowCompletionOr> instantiate_module(JS ++index; } - auto result = TRY(JS::call(vm, function, JS::js_undefined(), argument_values.span())); + auto result = TRY_OR_RETURN_TRAP(JS::call(vm, function, JS::js_undefined(), argument_values.span())); if (type.results().is_empty()) return Wasm::Result { Vector {} }; if (type.results().size() == 1) - return Wasm::Result { Vector { TRY(to_webassembly_value(vm, result, type.results().first())) } }; + return Wasm::Result { Vector { TRY_OR_RETURN_TRAP(to_webassembly_value(vm, result, type.results().first())) } }; - auto method = TRY(result.get_method(vm, vm.names.iterator)); + auto method = TRY_OR_RETURN_TRAP(result.get_method(vm, vm.names.iterator)); if (method == JS::js_undefined()) - return vm.throw_completion(JS::ErrorType::NotIterable, result.to_string_without_side_effects()); + return Wasm::Trap::from_external_object(vm.throw_completion(JS::ErrorType::NotIterable, result.to_string_without_side_effects())); - auto values = TRY(JS::iterator_to_list(vm, TRY(JS::get_iterator_from_method(vm, result, *method)))); + auto values = TRY_OR_RETURN_TRAP(JS::iterator_to_list(vm, TRY_OR_RETURN_TRAP(JS::get_iterator_from_method(vm, result, *method)))); if (values.size() != type.results().size()) - return vm.throw_completion(ByteString::formatted("Invalid number of return values for multi-value wasm return of {} objects", type.results().size())); + return Wasm::Trap::from_external_object(vm.throw_completion(ByteString::formatted("Invalid number of return values for multi-value wasm return of {} objects", type.results().size()))); Vector wasm_values; - TRY_OR_THROW_OOM(vm, wasm_values.try_ensure_capacity(values.size())); + TRY_OR_RETURN_OOM_TRAP(vm, wasm_values.try_ensure_capacity(values.size())); size_t i = 0; for (auto& value : values) - wasm_values.append(TRY(to_webassembly_value(vm, value, type.results()[i++]))); + wasm_values.append(TRY_OR_RETURN_TRAP(to_webassembly_value(vm, value, type.results()[i++]))); return Wasm::Result { move(wasm_values) }; }, @@ -415,8 +445,11 @@ JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress add auto& cache = get_cache(realm); auto result = cache.abstract_machine().invoke(address, move(values)); // FIXME: Use the convoluted mapping of errors defined in the spec. - if (result.is_trap()) - return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("Wasm execution trapped (WIP): {}", result.trap().reason))); + if (result.is_trap()) { + if (auto ptr = result.trap().data.get_pointer()) + return ptr->unsafe_external_object_as(); + return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("Wasm execution trapped (WIP): {}", result.trap().format()))); + } if (result.values().is_empty()) return JS::js_undefined(); diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 42aacb44ffb..d343477b3ab 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -471,7 +471,7 @@ if (ASSERT_FAIL_HAS_INT OR EMSCRIPTEN) target_compile_definitions(test262-runner PRIVATE ASSERT_FAIL_HAS_INT) endif() -lagom_utility(wasm SOURCES ../../Utilities/wasm.cpp LIBS LibFileSystem LibWasm LibLine LibMain LibJS) +lagom_utility(wasm SOURCES ../../Utilities/wasm.cpp LIBS LibFileSystem LibWasm LibLine LibMain) lagom_utility(xml SOURCES ../../Utilities/xml.cpp LIBS LibFileSystem LibMain LibXML LibURL) include(CTest) diff --git a/Tests/LibWasm/test-wasm.cpp b/Tests/LibWasm/test-wasm.cpp index 39171d6d005..9fec682b05f 100644 --- a/Tests/LibWasm/test-wasm.cpp +++ b/Tests/LibWasm/test-wasm.cpp @@ -401,11 +401,11 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke) auto functype = WebAssemblyModule::machine().store().get(function_address)->visit([&](auto& func) { return func.type(); }); auto result = WebAssemblyModule::machine().invoke(function_address, arguments); - if (result.is_trap()) - return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("Execution trapped: {}", result.trap().reason))); - - if (result.is_completion()) - return result.completion(); + if (result.is_trap()) { + if (auto ptr = result.trap().data.get_pointer()) + return ptr->unsafe_external_object_as(); + return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("Execution trapped: {}", result.trap().format()))); + } if (result.values().is_empty()) return JS::js_null(); diff --git a/Utilities/wasm.cpp b/Utilities/wasm.cpp index 1f64387c72b..38eb6a31170 100644 --- a/Utilities/wasm.cpp +++ b/Utilities/wasm.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -264,7 +265,7 @@ static bool post_interpret_hook(Wasm::Configuration&, Wasm::InstructionPointer& g_continue = false; warnln("Trapped when executing ip={}", ip); g_printer->print(instr); - warnln("Trap reason: {}", interpreter.trap_reason()); + warnln("Trap reason: {}", interpreter.trap().format()); const_cast(interpreter).clear_trap(); } return true; @@ -446,13 +447,13 @@ static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPoi if (!ok) continue; - Wasm::Result result { Wasm::Trap {} }; + Wasm::Result result { Wasm::Trap::from_string("") }; { Wasm::BytecodeInterpreter::CallFrameHandle handle { g_interpreter, config }; - result = config.call(g_interpreter, *address, move(values)).assert_wasm_result(); + result = config.call(g_interpreter, *address, move(values)); } if (result.is_trap()) { - warnln("Execution trapped: {}", result.trap().reason); + warnln("Execution trapped: {}", result.trap().format()); } else { if (!result.values().is_empty()) warnln("Returned:"); @@ -833,15 +834,16 @@ ErrorOr serenity_main(Main::Arguments arguments) outln(); } - auto result = machine.invoke(g_interpreter, run_address.value(), move(values)).assert_wasm_result(); + auto result = machine.invoke(g_interpreter, run_address.value(), move(values)); if (debug) launch_repl(); if (result.is_trap()) { - if (result.trap().reason.starts_with("exit:"sv)) - return -result.trap().reason.substring_view(5).to_number().value_or(-1); - warnln("Execution trapped: {}", result.trap().reason); + auto trap_reason = result.trap().format(); + if (trap_reason.starts_with("exit:"sv)) + return -trap_reason.substring_view(5).to_number().value_or(-1); + warnln("Execution trapped: {}", trap_reason); } else { if (!result.values().is_empty()) warnln("Returned:");