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.
This commit is contained in:
Ali Mohammad Pur 2025-04-22 09:48:26 +02:00 committed by Andrew Kaster
parent 7b2a427430
commit 51bab5b186
Notes: github-actions[bot] 2025-04-22 14:45:25 +00:00
11 changed files with 176 additions and 111 deletions

View file

@ -239,6 +239,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
}
BytecodeInterpreter interpreter(m_stack_info);
auto handle = register_scoped(interpreter);
for (auto& entry : module.global_section().entries()) {
Configuration config { m_store };
@ -250,9 +251,9 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
entry.expression(),
1,
});
auto result = config.execute(interpreter).assert_wasm_result();
auto result = config.execute(interpreter);
if (result.is_trap())
return InstantiationError { ByteString::formatted("Global value construction trapped: {}", result.trap().reason) };
return InstantiationError { "Global instantiation trapped", move(result.trap()) };
global_values.append(result.values().first());
}
@ -271,9 +272,9 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
entry,
entry.instructions().size(),
});
auto result = config.execute(interpreter).assert_wasm_result();
auto result = config.execute(interpreter);
if (result.is_trap())
return InstantiationError { ByteString::formatted("Element construction trapped: {}", result.trap().reason) };
return InstantiationError { "Element section initialisation trapped", move(result.trap()) };
for (auto& value : result.values()) {
auto reference = value.to<Reference>();
@ -306,9 +307,9 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
active_ptr->expression,
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<i32>();
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<Ex
data.offset,
1,
});
auto result = config.execute(interpreter).assert_wasm_result();
auto result = config.execute(interpreter);
if (result.is_trap())
return InstantiationError { ByteString::formatted("Data section initialisation trapped: {}", result.trap().reason) };
return InstantiationError { "Data section initialisation trapped", move(result.trap()) };
size_t offset = result.values().first().to<u64>();
if (main_module_instance.memories().size() <= data.index.value()) {
return InstantiationError {
@ -391,7 +392,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
}
auto result = invoke(functions[index.value()], {});
if (result.is_trap())
return InstantiationError { ByteString::formatted("Start function trapped: {}", result.trap().reason) };
return InstantiationError { "Start function trapped", move(result.trap()) };
}
return InstantiationResult { move(main_module_instance_pointer) };
@ -491,6 +492,7 @@ Optional<InstantiationError> AbstractMachine::allocate_all_final_phase(Module co
Result AbstractMachine::invoke(FunctionAddress address, Vector<Value> 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);
}
}

View file

@ -6,6 +6,7 @@
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/Function.h>
#include <AK/HashMap.h>
#include <AK/HashTable.h>
@ -14,18 +15,13 @@
#include <AK/UFixedBigInt.h>
#include <LibWasm/Types.h>
// NOTE: Special case for Wasm::Result.
#include <LibJS/Runtime/Completion.h>
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<u8, 64> data;
template<typename T>
T const& unsafe_external_object_as() const
{
static_assert(sizeof(T) <= sizeof(data), "Object size is too large for ExternallyManagedTrap");
return *reinterpret_cast<T const*>(data.data());
}
};
// A variant of Result that does not include external reasons for error (JS::Completion, for now).
class PureResult {
public:
explicit PureResult(Vector<Value> values)
: m_result(move(values))
struct Trap {
Variant<ByteString, ExternallyManagedTrap> data;
ByteString format() const
{
if (auto const* ptr = data.get_pointer<ByteString>())
return *ptr;
return "<Externally managed Trap Data>";
}
PureResult(Trap trap)
: m_result(move(trap))
template<typename T>
static Trap from_external_object(T&& object)
{
static_assert(sizeof(T) <= sizeof(ExternallyManagedTrap::data), "Object size is too large for ExternallyManagedTrap");
static_assert(IsTriviallyCopyable<T>, "Object must be trivially copyable");
static_assert(IsTriviallyDestructible<T>, "Object must be trivially destructible");
ExternallyManagedTrap externally_managed_trap;
new (externally_managed_trap.data.data()) T(forward<T>(object));
return Trap { externally_managed_trap };
}
auto is_trap() const { return m_result.has<Trap>(); }
auto& values() const { return m_result.get<Vector<Value>>(); }
auto& values() { return m_result.get<Vector<Value>>(); }
auto& trap() const { return m_result.get<Trap>(); }
auto& trap() { return m_result.get<Trap>(); }
private:
friend class Result;
explicit PureResult(Variant<Vector<Value>, Trap>&& result)
: m_result(move(result))
static Trap from_string(ByteString string)
{
return Trap { move(string) };
}
Variant<Vector<Value>, Trap> m_result;
};
class Result {
@ -243,34 +245,24 @@ public:
{
}
Result(JS::Completion completion)
: m_result(move(completion))
{
VERIFY(m_result.get<JS::Completion>().is_abrupt());
}
Result(PureResult&& result)
: m_result(result.m_result.downcast<decltype(m_result)>())
{
}
auto is_trap() const { return m_result.has<Trap>(); }
auto is_completion() const { return m_result.has<JS::Completion>(); }
auto& values() const { return m_result.get<Vector<Value>>(); }
auto& values() { return m_result.get<Vector<Value>>(); }
auto& trap() const { return m_result.get<Trap>(); }
auto& trap() { return m_result.get<Trap>(); }
auto& completion() { return m_result.get<JS::Completion>(); }
auto& completion() const { return m_result.get<JS::Completion>(); }
PureResult assert_wasm_result() &&
{
VERIFY(!is_completion());
return PureResult(move(m_result).downcast<Vector<Value>, Trap>());
}
private:
Variant<Vector<Value>, Trap, JS::Completion> m_result;
explicit Result(Variant<Vector<Value>, Trap>&& result)
: m_result(move(result))
{
}
Variant<Vector<Value>, Trap> m_result;
};
struct InstantiationError {
ByteString error { "Unknown error" };
Optional<Trap> relevant_trap {};
};
using ExternValue = Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress>;
@ -628,6 +620,10 @@ private:
using InstantiationResult = AK::ErrorOr<NonnullOwnPtr<ModuleInstance>, InstantiationError>;
struct HostVisitOps {
Function<void(ExternallyManagedTrap&)> 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<InstantiationError> allocate_all_initial_phase(Module const&, ModuleInstance&, Vector<ExternValue>&, Vector<Value>& global_values, Vector<FunctionAddress>& own_functions);
Optional<InstantiationError> allocate_all_final_phase(Module const&, ModuleInstance&, Vector<Vector<Reference>>& elements);
Store m_store;
StackInfo m_stack_info;
HashTable<Interpreter*> m_active_interpreters;
bool m_should_limit_instruction_count { false };
};

View file

@ -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<i32>();
u64 instance_address = static_cast<u64>(bit_cast<u32>(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<i32>();
u64 instance_address = static_cast<u64>(bit_cast<u32>(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<u32>();
u64 instance_address = static_cast<u64>(bit_cast<u32>(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<u32>();
u64 instance_address = static_cast<u64>(bit_cast<u32>(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<i32>();
u64 instance_address = static_cast<u64>(bit_cast<u32>(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<WasmFunction>()) {
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<LittleEndian<T>>();
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<float>(ReadonlyBytes data)
FixedMemoryStream stream { data };
auto raw_value_or_error = stream.read_value<LittleEndian<u32>>();
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<float>(static_cast<u32>(raw_value));
}
@ -398,7 +393,7 @@ double BytecodeInterpreter::read_value<double>(ReadonlyBytes data)
FixedMemoryStream stream { data };
auto raw_value_or_error = stream.read_value<LittleEndian<u64>>();
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<double>(static_cast<u64>(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;
}
}

View file

@ -22,14 +22,17 @@ struct BytecodeInterpreter : public Interpreter {
virtual ~BytecodeInterpreter() override = default;
virtual bool did_trap() const final { return !m_trap.has<Empty>(); }
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<Trap>();
}
virtual void clear_trap() final { m_trap = Empty {}; }
virtual void visit_external_resources(HostVisitOps const& host) override
{
if (auto ptr = m_trap.get_pointer<Trap>())
if (auto data = ptr->data.get_pointer<ExternallyManagedTrap>())
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<Empty>();
}
Variant<Trap, JS::Completion, Empty> m_trap;
Variant<Trap, Empty> m_trap;
StackInfo const& m_stack_info;
};

View file

@ -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<WasmFunction>()) {
Vector<Value> 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<Value> results;
results.ensure_capacity(frame().arity());

View file

@ -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&) { }
};
}

View file

@ -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)

View file

@ -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<JS::Completion>();
visitor.visit(completion.value());
} });
}
}
@ -156,6 +160,32 @@ WebIDL::ExceptionOr<GC::Ref<WebIDL::Promise>> 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<decltype(_temporary_result.release_value())>, \
"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<JS::InternalError>((vm).error_message(::JS::VM::ErrorMessage::OutOfMemory))); \
} \
static_assert(!::AK::Detail::IsLvalueReference<decltype(_temporary_result.release_value())>, \
"Do not return a reference from a fallible expression"); \
_temporary_result.release_value(); \
})
JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS::VM& vm, Wasm::Module const& module, GC::Ptr<JS::Object> import_object)
{
Wasm::Linker linker { module };
@ -216,28 +246,28 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> 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<Wasm::Value> {} };
if (type.results().size() == 1)
return Wasm::Result { Vector<Wasm::Value> { TRY(to_webassembly_value(vm, result, type.results().first())) } };
return Wasm::Result { Vector<Wasm::Value> { 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::TypeError>(JS::ErrorType::NotIterable, result.to_string_without_side_effects());
return Wasm::Trap::from_external_object(vm.throw_completion<JS::TypeError>(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<JS::TypeError>(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<JS::TypeError>(ByteString::formatted("Invalid number of return values for multi-value wasm return of {} objects", type.results().size())));
Vector<Wasm::Value> 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<JS::TypeError>(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<Wasm::ExternallyManagedTrap>())
return ptr->unsafe_external_object_as<JS::Completion>();
return vm.throw_completion<JS::TypeError>(TRY_OR_THROW_OOM(vm, String::formatted("Wasm execution trapped (WIP): {}", result.trap().format())));
}
if (result.values().is_empty())
return JS::js_undefined();

View file

@ -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)

View file

@ -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<JS::TypeError>(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<Wasm::ExternallyManagedTrap>())
return ptr->unsafe_external_object_as<JS::Completion>();
return vm.throw_completion<JS::TypeError>(TRY_OR_THROW_OOM(vm, String::formatted("Execution trapped: {}", result.trap().format())));
}
if (result.values().is_empty())
return JS::js_null();

View file

@ -20,6 +20,7 @@
#include <LibWasm/Printer/Printer.h>
#include <LibWasm/Types.h>
#include <LibWasm/Wasi.h>
#include <math.h>
#include <signal.h>
#include <unistd.h>
@ -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<Wasm::Interpreter&>(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<int> 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<i32>().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<i32>().value_or(-1);
warnln("Execution trapped: {}", trap_reason);
} else {
if (!result.values().is_empty())
warnln("Returned:");