diff --git a/Tests/LibWasm/test-wasm.cpp b/Tests/LibWasm/test-wasm.cpp index abc8226ea9e..7560cac1155 100644 --- a/Tests/LibWasm/test-wasm.cpp +++ b/Tests/LibWasm/test-wasm.cpp @@ -295,16 +295,27 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::get_export) if (auto ptr = value.get_pointer()) return JS::Value(static_cast(ptr->value())); if (auto v = value.get_pointer()) { - return m_machine.store().get(*v)->value().value().visit( - [&](auto const& value) -> JS::Value { return JS::Value(static_cast(value)); }, - [&](i32 value) { return JS::Value(static_cast(value)); }, - [&](i64 value) -> JS::Value { return JS::BigInt::create(vm, Crypto::SignedBigInteger { value }); }, - [&](u128 value) -> JS::Value { return JS::BigInt::create(vm, Crypto::SignedBigInteger::import_data(bit_cast(&value), sizeof(value))); }, - [&](Wasm::Reference const& reference) -> JS::Value { - return reference.ref().visit( - [&](Wasm::Reference::Null const&) -> JS::Value { return JS::js_null(); }, - [&](auto const& ref) -> JS::Value { return JS::Value(static_cast(ref.address.value())); }); - }); + auto global = m_machine.store().get(*v); + switch (global->type().type().kind()) { + case Wasm::ValueType::I32: + return JS::Value(static_cast(global->value().to())); + case Wasm::ValueType::I64: + return JS::BigInt::create(vm, Crypto::SignedBigInteger { global->value().to() }); + case Wasm::ValueType::F32: + return JS::Value(static_cast(global->value().to())); + case Wasm::ValueType::F64: + return JS::Value(global->value().to()); + case Wasm::ValueType::V128: { + auto value = global->value().to(); + return JS::BigInt::create(vm, Crypto::SignedBigInteger::import_data(bit_cast(&value), sizeof(u128))); + } + case Wasm::ValueType::FunctionReference: + case Wasm::ValueType::ExternReference: + auto ref = global->value().to(); + return ref.ref().visit( + [&](Wasm::Reference::Null const&) -> JS::Value { return JS::js_null(); }, + [&](auto const& ref) -> JS::Value { return JS::Value(static_cast(ref.address.value())); }); + } } return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("'{}' does not refer to a function or a global", name))); } @@ -336,14 +347,14 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke) double_value = TRY(argument.to_double(vm)); switch (param.kind()) { case Wasm::ValueType::Kind::I32: - arguments.append(Wasm::Value(param, static_cast(double_value))); + arguments.append(Wasm::Value(static_cast(double_value))); break; case Wasm::ValueType::Kind::I64: if (argument.is_bigint()) { auto value = TRY(argument.to_bigint_int64(vm)); - arguments.append(Wasm::Value(param, value)); + arguments.append(Wasm::Value(value)); } else { - arguments.append(Wasm::Value(param, static_cast(double_value))); + arguments.append(Wasm::Value(static_cast(double_value))); } break; case Wasm::ValueType::Kind::F32: @@ -352,9 +363,9 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke) case Wasm::ValueType::Kind::F64: if (argument.is_bigint()) { auto value = TRY(argument.to_bigint_uint64(vm)); - arguments.append(Wasm::Value(param, bit_cast(value))); + arguments.append(Wasm::Value(bit_cast(value))); } else { - arguments.append(Wasm::Value(param, double_value)); + arguments.append(Wasm::Value(double_value)); } break; case Wasm::ValueType::Kind::V128: { @@ -385,6 +396,7 @@ 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))); @@ -395,30 +407,36 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke) if (result.values().is_empty()) return JS::js_null(); - auto to_js_value = [&](Wasm::Value const& value) { - return value.value().visit( - // For floating point values, we're testing with their bit representation, so we bit_cast them - [](f32 value) { return JS::Value(static_cast(bit_cast(value))); }, - [&](f64 value) { return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger { Crypto::UnsignedBigInteger { bit_cast(value) } })); }, - [](i32 value) { return JS::Value(static_cast(value)); }, - [&](i64 value) { return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger { value })); }, - [&](u128 value) { - // FIXME: remove the MUST here - auto buf = MUST(JS::ArrayBuffer::create(*vm.current_realm(), 16)); - memcpy(buf->buffer().data(), value.bytes().data(), 16); - return JS::Value(buf); - }, - [](Wasm::Reference const& reference) { - return reference.ref().visit( - [](Wasm::Reference::Null const&) { return JS::js_null(); }, - [](auto const& ref) { return JS::Value(static_cast(ref.address.value())); }); - }); + auto to_js_value = [&](Wasm::Value const& value, Wasm::ValueType type) { + switch (type.kind()) { + case Wasm::ValueType::I32: + return JS::Value(static_cast(value.to())); + case Wasm::ValueType::I64: + return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger { value.to() })); + case Wasm::ValueType::F32: + return JS::Value(static_cast(bit_cast(value.to()))); + case Wasm::ValueType::F64: + return JS::Value(JS::BigInt::create(vm, Crypto::SignedBigInteger { Crypto::UnsignedBigInteger { bit_cast(value.to()) } })); + case Wasm::ValueType::V128: { + u128 val = value.to(); + // FIXME: remove the MUST here + auto buf = MUST(JS::ArrayBuffer::create(*vm.current_realm(), 16)); + memcpy(buf->buffer().data(), val.bytes().data(), 16); + return JS::Value(buf); + } + case Wasm::ValueType::FunctionReference: + case Wasm::ValueType::ExternReference: + return (value.to()).ref().visit([&](Wasm::Reference::Null) { return JS::js_null(); }, [&](auto const& ref) { return JS::Value(static_cast(ref.address.value())); }); + } + VERIFY_NOT_REACHED(); }; if (result.values().size() == 1) - return to_js_value(result.values().first()); + return to_js_value(result.values().first(), functype.results().first()); + size_t i = 0; return JS::Array::create_from(*vm.current_realm(), result.values(), [&](Wasm::Value value) { - return to_js_value(value); + auto value_type = type->results()[i++]; + return to_js_value(value, value_type); }); } diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp index e450332a6de..f3d5922ef9e 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp @@ -55,7 +55,7 @@ Optional Store::allocate(MemoryType const& type) Optional Store::allocate(GlobalType const& type, Value value) { GlobalAddress address { m_globals.size() }; - m_globals.append(GlobalInstance { move(value), type.is_mutable() }); + m_globals.append(GlobalInstance { value, type.is_mutable(), type.type() }); return address; } @@ -138,7 +138,6 @@ ErrorOr AbstractMachine::validate(Module& module) return {}; } - InstantiationResult AbstractMachine::instantiate(Module const& module, Vector externs) { if (auto result = validate(const_cast(module)); result.is_error()) @@ -267,7 +266,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector(); - references.append(reference.release_value()); + references.append(reference); } } elements.append(move(references)); @@ -300,8 +299,6 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector(); - if (!d.has_value()) - return InstantiationError { "Element section initialisation returned invalid table initial offset" }; auto table_instance = m_store.get(main_module_instance.tables()[active_ptr->index.value()]); if (current_index >= main_module_instance.elements().size()) return InstantiationError { "Invalid element referenced by active element segment" }; @@ -309,14 +306,14 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector total_size = elem_instance->references().size(); - total_size.saturating_add(d.value()); + total_size.saturating_add(d); if (total_size.value() > table_instance->elements().size()) return InstantiationError { "Table instantiation out of bounds" }; size_t i = 0; for (auto it = elem_instance->references().begin(); it < elem_instance->references().end(); ++i, ++it) - table_instance->elements()[i + d.value()] = *it; + table_instance->elements()[i + d] = *it; // Drop element *m_store.get(main_module_instance.elements()[current_index]) = ElementInstance(elem_instance->type(), {}); } @@ -336,10 +333,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector { value }; }, - [&](u128 const&) { return ErrorOr { InstantiationError { "Data segment offset returned a vector type"sv } }; }, - [&](Reference const&) { return ErrorOr { InstantiationError { "Data segment offset returned a reference type"sv } }; })); + size_t offset = result.values().first().to(); if (main_module_instance.memories().size() <= data.index.value()) { return InstantiationError { ByteString::formatted("Data segment referenced out-of-bounds memory ({}) of max {} entries", @@ -570,5 +564,4 @@ void Linker::populate() m_unresolved_imports.set(m_ordered_imports.last()); } } - } diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h index 32971a4b30d..d3112fe37d9 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h +++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h @@ -75,45 +75,57 @@ private: class Value { public: Value() - : m_value(0) - { - } - - using AnyValueType = Variant; - explicit Value(AnyValueType value) - : m_value(move(value)) + : m_value(u128()) { } template - requires(sizeof(T) == sizeof(u64)) explicit Value(ValueType type, T raw_value) - : m_value(0) + requires(sizeof(T) == sizeof(u64)) explicit Value(T raw_value) + : m_value(u128(bit_cast(raw_value), 0)) { - switch (type.kind()) { - case ValueType::Kind::ExternReference: - m_value = Reference { Reference::Extern { { bit_cast(raw_value) } } }; - break; - case ValueType::Kind::FunctionReference: - m_value = Reference { Reference::Func { { bit_cast(raw_value) } } }; - break; - case ValueType::Kind::I32: - m_value = static_cast(bit_cast(raw_value)); - break; - case ValueType::Kind::I64: - m_value = static_cast(bit_cast(raw_value)); - break; - case ValueType::Kind::F32: - m_value = static_cast(bit_cast(raw_value)); - break; - case ValueType::Kind::F64: - m_value = bit_cast(raw_value); - break; - case ValueType::Kind::V128: - m_value = u128(0ull, bit_cast(raw_value)); - break; - default: - VERIFY_NOT_REACHED(); - } + } + + template + requires(sizeof(T) == sizeof(u32)) explicit Value(T raw_value) + : m_value(u128(static_cast(bit_cast(raw_value)), 0)) + { + } + + template + requires(sizeof(T) == sizeof(u8) && Signed) explicit Value(T raw_value) + : m_value(u128(static_cast(bit_cast(raw_value)), 0)) + { + } + + template + requires(sizeof(T) == sizeof(u8) && Unsigned) explicit Value(T raw_value) + : m_value(u128(static_cast(bit_cast(raw_value)), 0)) + { + } + + template + requires(sizeof(T) == sizeof(u16) && Signed) explicit Value(T raw_value) + : m_value(u128(static_cast(bit_cast(raw_value)), 0)) + { + } + + template + requires(sizeof(T) == sizeof(u16) && Unsigned) explicit Value(T raw_value) + : m_value(u128(static_cast(bit_cast(raw_value)), 0)) + { + } + + explicit Value(Reference ref) + { + // Reference variant is encoded in the high storage of the u128: + // 0: funcref + // 1: externref + // 2: null funcref + // 3: null externref + ref.ref().visit( + [&](Reference::Func const& func) { m_value = u128(bit_cast(func.address), 0); }, + [&](Reference::Extern const& func) { m_value = u128(bit_cast(func.address), 1); }, + [&](Reference::Null const& null) { m_value = u128(0, null.type.kind() == ValueType::Kind::FunctionReference ? 2 : 3); }); } template T> @@ -128,63 +140,53 @@ public: ALWAYS_INLINE Value& operator=(Value const& value) = default; template - ALWAYS_INLINE Optional to() const + ALWAYS_INLINE T to() const { - Optional result; - m_value.visit( - [&](auto value) { - if constexpr (IsSame || (!IsFloatingPoint && IsSame>)) { - result = static_cast(value); - } else if constexpr (!IsFloatingPoint && IsConvertible) { - // NOTE: No implicit vector <-> scalar conversion. - if constexpr (!IsSame) { - if (AK::is_within_range(value)) - result = static_cast(value); - } - } - }, - [&](u128 value) { - if constexpr (IsSame) - result = value; - }, - [&](Reference const& value) { - if constexpr (IsSame) { - result = value; - } else if constexpr (IsSame) { - if (auto ptr = value.ref().template get_pointer()) - result = *ptr; - } else if constexpr (IsSame) { - if (auto ptr = value.ref().template get_pointer()) - result = *ptr; - } else if constexpr (IsSame) { - if (auto ptr = value.ref().template get_pointer()) - result = *ptr; - } - }); - return result; + if constexpr (IsSame) { + return m_value; + } + if constexpr (IsSame) { + u32 low = m_value.low() & 0xFFFFFFFF; + return low; + } + if constexpr (IsSame) { + u32 low = m_value.low() & 0xFFFFFFFF; + return bit_cast(low); + } + if constexpr (IsSame) { + return bit_cast(m_value.low()); + } + if constexpr (IsSame) { + return bit_cast(m_value.low()); + } + if constexpr (IsSame) { + u32 low = m_value.low() & 0xFFFFFFFF; + return bit_cast(low); + } + if constexpr (IsSame) { + return bit_cast(m_value.low()); + } + if constexpr (IsSame) { + switch (m_value.high()) { + case 0: + return Reference { Reference::Func(bit_cast(m_value.low())) }; + case 1: + return Reference { Reference::Extern(bit_cast(m_value.low())) }; + case 2: + return Reference { Reference::Null(ValueType(ValueType::Kind::FunctionReference)) }; + case 3: + return Reference { Reference::Null(ValueType(ValueType::Kind::ExternReference)) }; + default: + VERIFY_NOT_REACHED(); + } + } + VERIFY_NOT_REACHED(); } - ValueType type() const - { - return ValueType(m_value.visit( - [](i32) { return ValueType::Kind::I32; }, - [](i64) { return ValueType::Kind::I64; }, - [](float) { return ValueType::Kind::F32; }, - [](double) { return ValueType::Kind::F64; }, - [](u128) { return ValueType::Kind::V128; }, - [&](Reference const& type) { - return type.ref().visit( - [](Reference::Func const&) { return ValueType::Kind::FunctionReference; }, - [](Reference::Null const& null_type) { - return null_type.type.kind(); - }, - [](Reference::Extern const&) { return ValueType::Kind::ExternReference; }); - })); - } auto& value() const { return m_value; } private: - AnyValueType m_value; + u128 m_value; }; struct Trap { @@ -481,15 +483,16 @@ private: class GlobalInstance { public: - explicit GlobalInstance(Value value, bool is_mutable) + explicit GlobalInstance(Value value, bool is_mutable, ValueType type) : m_mutable(is_mutable) - , m_value(move(value)) + , m_value(value) + , m_type(type) { } auto is_mutable() const { return m_mutable; } auto& value() const { return m_value; } - GlobalType type() const { return { m_value.type(), is_mutable() }; } + GlobalType type() const { return { m_type, is_mutable() }; } void set_value(Value value) { VERIFY(is_mutable()); @@ -499,6 +502,7 @@ public: private: bool m_mutable { false }; Value m_value; + ValueType m_type; }; class DataInstance { diff --git a/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp b/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp index 50ce2f43dfa..9e441cb0f94 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp @@ -75,7 +75,7 @@ void BytecodeInterpreter::load_and_push(Configuration& configuration, Instructio auto& address = configuration.frame().module().memories()[arg.memory_index.value()]; auto memory = configuration.store().get(address); auto& entry = configuration.value_stack().last(); - auto base = *entry.to(); + 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" }; @@ -100,7 +100,7 @@ void BytecodeInterpreter::load_and_push_mxn(Configuration& configuration, Instru auto& address = configuration.frame().module().memories()[arg.memory_index.value()]; auto memory = configuration.store().get(address); auto& entry = configuration.value_stack().last(); - auto base = *entry.to(); + 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" }; @@ -127,8 +127,8 @@ void BytecodeInterpreter::load_and_push_lane_n(Configuration& configuration, Ins auto memarg_and_lane = instruction.arguments().get(); auto& address = configuration.frame().module().memories()[memarg_and_lane.memory.memory_index.value()]; auto memory = configuration.store().get(address); - auto vector = *configuration.value_stack().take_last().to(); - auto base = *configuration.value_stack().take_last().to(); + auto vector = configuration.value_stack().take_last().to(); + 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" }; @@ -146,7 +146,7 @@ void BytecodeInterpreter::load_and_push_zero_n(Configuration& configuration, Ins auto memarg_and_lane = instruction.arguments().get(); auto& address = configuration.frame().module().memories()[memarg_and_lane.memory_index.value()]; auto memory = configuration.store().get(address); - auto base = *configuration.value_stack().take_last().to(); + 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" }; @@ -165,7 +165,7 @@ void BytecodeInterpreter::load_and_push_m_splat(Configuration& configuration, In auto& address = configuration.frame().module().memories()[arg.memory_index.value()]; auto memory = configuration.store().get(address); auto& entry = configuration.value_stack().last(); - auto base = *entry.to(); + 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" }; @@ -212,7 +212,7 @@ void BytecodeInterpreter::pop_and_push_m_splat(Wasm::Configuration& configuratio using PopT = Conditional, NativeType<64>>; using ReadT = NativeType; auto entry = configuration.value_stack().last(); - auto value = static_cast(*entry.to()); + auto value = static_cast(entry.to()); dbgln_if(WASM_TRACE_DEBUG, "stack({}) -> splat({})", value, M); set_top_m_splat(configuration, value); } @@ -220,7 +220,7 @@ void BytecodeInterpreter::pop_and_push_m_splat(Wasm::Configuration& configuratio template typename SetSign, typename VectorType> VectorType BytecodeInterpreter::pop_vector(Configuration& configuration) { - return bit_cast(*configuration.value_stack().take_last().to()); + return bit_cast(configuration.value_stack().take_last().to()); } void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address) @@ -268,7 +268,7 @@ void BytecodeInterpreter::binary_numeric_operation(Configuration& configuration, auto& lhs_entry = configuration.value_stack().last(); auto lhs = lhs_entry.to(); PushType result; - auto call_result = Operator { forward(args)... }(lhs.value(), rhs.value()); + auto call_result = Operator { forward(args)... }(lhs, rhs); if constexpr (IsSpecializationOf) { if (call_result.is_error()) { trap_if_not(false, call_result.error()); @@ -287,7 +287,7 @@ void BytecodeInterpreter::unary_operation(Configuration& configuration, Args&&.. { auto& entry = configuration.value_stack().last(); auto value = entry.to(); - auto call_result = Operator { forward(args)... }(*value); + auto call_result = Operator { forward(args)... }(value); PushType result; if constexpr (IsSpecializationOf) { if (call_result.is_error()) { @@ -337,19 +337,19 @@ void BytecodeInterpreter::pop_and_store(Configuration& configuration, Instructio { auto& memarg = instruction.arguments().get(); auto entry = configuration.value_stack().take_last(); - auto value = ConvertToRaw {}(*entry.to()); + auto value = ConvertToRaw {}(entry.to()); dbgln_if(WASM_TRACE_DEBUG, "stack({}) -> temporary({}b)", value, sizeof(StoreT)); auto base = configuration.value_stack().take_last().to(); - store_to_memory(configuration, memarg, { &value, sizeof(StoreT) }, *base); + store_to_memory(configuration, memarg, { &value, sizeof(StoreT) }, base); } template void BytecodeInterpreter::pop_and_store_lane_n(Configuration& configuration, Instruction const& instruction) { auto& memarg_and_lane = instruction.arguments().get(); - auto vector = *configuration.value_stack().take_last().to(); + auto vector = configuration.value_stack().take_last().to(); auto src = bit_cast(&vector) + memarg_and_lane.lane * N / 8; - auto base = *configuration.value_stack().take_last().to(); + auto base = configuration.value_stack().take_last().to(); store_to_memory(configuration, memarg_and_lane.memory, { src, N / 8 }, base); } @@ -422,16 +422,16 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con return; } case Instructions::i32_const.value(): - configuration.value_stack().append(Value(ValueType { ValueType::I32 }, static_cast(instruction.arguments().get()))); + configuration.value_stack().append(Value(instruction.arguments().get())); return; case Instructions::i64_const.value(): - configuration.value_stack().append(Value(ValueType { ValueType::I64 }, instruction.arguments().get())); + configuration.value_stack().append(Value(instruction.arguments().get())); return; case Instructions::f32_const.value(): - configuration.value_stack().append(Value(Value::AnyValueType(instruction.arguments().get()))); + configuration.value_stack().append(Value(instruction.arguments().get())); return; case Instructions::f64_const.value(): - configuration.value_stack().append(Value(Value::AnyValueType(instruction.arguments().get()))); + configuration.value_stack().append(Value(instruction.arguments().get())); return; case Instructions::block.value(): { size_t arity = 0; @@ -513,13 +513,13 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con return branch_to_label(configuration, instruction.arguments().get()); case Instructions::br_if.value(): { auto cond = configuration.value_stack().take_last().to(); - if (*cond == 0) + if (cond == 0) return; return branch_to_label(configuration, instruction.arguments().get()); } case Instructions::br_table.value(): { auto& arguments = instruction.arguments().get(); - auto i = *configuration.value_stack().take_last().to(); + auto i = configuration.value_stack().take_last().to(); if (i >= arguments.labels.size()) { return branch_to_label(configuration, arguments.default_); @@ -538,12 +538,12 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con auto table_address = configuration.frame().module().tables()[args.table.value()]; auto table_instance = configuration.store().get(table_address); auto index = configuration.value_stack().take_last().to(); - TRAP_IF_NOT(index.value() >= 0); - TRAP_IF_NOT(static_cast(index.value()) < table_instance->elements().size()); - auto element = table_instance->elements()[index.value()]; + TRAP_IF_NOT(index >= 0); + TRAP_IF_NOT(static_cast(index) < table_instance->elements().size()); + auto element = table_instance->elements()[index]; TRAP_IF_NOT(element.ref().has()); auto address = element.ref().get().address; - dbgln_if(WASM_TRACE_DEBUG, "call_indirect({} -> {})", index.value(), address.value()); + dbgln_if(WASM_TRACE_DEBUG, "call_indirect({} -> {})", index, address.value()); call_address(configuration, address); return; } @@ -636,8 +636,8 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con i32 old_pages = instance->size() / Constants::page_size; auto& entry = configuration.value_stack().last(); auto new_pages = entry.to(); - dbgln_if(WASM_TRACE_DEBUG, "memory.grow({}), previously {} pages...", *new_pages, old_pages); - if (instance->grow(new_pages.value() * Constants::page_size)) + dbgln_if(WASM_TRACE_DEBUG, "memory.grow({}), previously {} pages...", new_pages, old_pages); + if (instance->grow(new_pages * Constants::page_size)) entry = Value((i32)old_pages); else entry = Value((i32)-1); @@ -648,9 +648,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con auto& args = instruction.arguments().get(); auto address = configuration.frame().module().memories()[args.memory_index.value()]; auto instance = configuration.store().get(address); - auto count = configuration.value_stack().take_last().to().value(); - u8 value = static_cast(configuration.value_stack().take_last().to().value()); - auto destination_offset = configuration.value_stack().take_last().to().value(); + auto count = configuration.value_stack().take_last().to(); + u8 value = static_cast(configuration.value_stack().take_last().to()); + auto destination_offset = configuration.value_stack().take_last().to(); TRAP_IF_NOT(static_cast(destination_offset + count) <= instance->data().size()); @@ -670,9 +670,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con auto source_instance = configuration.store().get(source_address); auto destination_instance = configuration.store().get(destination_address); - auto count = configuration.value_stack().take_last().to().value(); - auto source_offset = configuration.value_stack().take_last().to().value(); - auto destination_offset = configuration.value_stack().take_last().to().value(); + auto count = configuration.value_stack().take_last().to(); + auto source_offset = configuration.value_stack().take_last().to(); + auto destination_offset = configuration.value_stack().take_last().to(); Checked source_position = source_offset; source_position.saturating_add(count); @@ -706,9 +706,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con auto& data = *configuration.store().get(data_address); auto memory_address = configuration.frame().module().memories()[args.memory_index.value()]; auto memory = configuration.store().get(memory_address); - auto count = *configuration.value_stack().take_last().to(); - auto source_offset = *configuration.value_stack().take_last().to(); - auto destination_offset = *configuration.value_stack().take_last().to(); + auto count = configuration.value_stack().take_last().to(); + auto source_offset = configuration.value_stack().take_last().to(); + auto destination_offset = configuration.value_stack().take_last().to(); Checked source_position = source_offset; source_position.saturating_add(count); @@ -747,9 +747,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con auto table = configuration.store().get(table_address); auto element_address = configuration.frame().module().elements()[args.element_index.value()]; auto element = configuration.store().get(element_address); - auto count = *configuration.value_stack().take_last().to(); - auto source_offset = *configuration.value_stack().take_last().to(); - auto destination_offset = *configuration.value_stack().take_last().to(); + auto count = configuration.value_stack().take_last().to(); + auto source_offset = configuration.value_stack().take_last().to(); + auto destination_offset = configuration.value_stack().take_last().to(); Checked checked_source_offset = source_offset; Checked checked_destination_offset = destination_offset; @@ -769,9 +769,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con auto source_instance = configuration.store().get(source_address); auto destination_instance = configuration.store().get(destination_address); - auto count = configuration.value_stack().take_last().to().value(); - auto source_offset = configuration.value_stack().take_last().to().value(); - auto destination_offset = configuration.value_stack().take_last().to().value(); + auto count = configuration.value_stack().take_last().to(); + auto source_offset = configuration.value_stack().take_last().to(); + auto destination_offset = configuration.value_stack().take_last().to(); Checked source_position = source_offset; source_position.saturating_add(count); @@ -801,9 +801,9 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con auto table_index = instruction.arguments().get(); auto address = configuration.frame().module().tables()[table_index.value()]; auto table = configuration.store().get(address); - auto count = *configuration.value_stack().take_last().to(); - auto value = *configuration.value_stack().take_last().to(); - auto start = *configuration.value_stack().take_last().to(); + auto count = configuration.value_stack().take_last().to(); + auto value = configuration.value_stack().take_last().to(); + auto start = configuration.value_stack().take_last().to(); Checked checked_offset = start; checked_offset += count; @@ -814,8 +814,8 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con return; } case Instructions::table_set.value(): { - auto ref = *configuration.value_stack().take_last().to(); - auto index = (size_t)(*configuration.value_stack().take_last().to()); + auto ref = configuration.value_stack().take_last().to(); + auto index = (size_t)(configuration.value_stack().take_last().to()); auto table_index = instruction.arguments().get(); auto address = configuration.frame().module().tables()[table_index.value()]; auto table = configuration.store().get(address); @@ -824,7 +824,7 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con return; } case Instructions::table_get.value(): { - auto index = (size_t)(*configuration.value_stack().take_last().to()); + auto index = (size_t)(configuration.value_stack().take_last().to()); auto table_index = instruction.arguments().get(); auto address = configuration.frame().module().tables()[table_index.value()]; auto table = configuration.store().get(address); @@ -834,8 +834,8 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con return; } case Instructions::table_grow.value(): { - auto size = *configuration.value_stack().take_last().to(); - auto fill_value = *configuration.value_stack().take_last().to(); + auto size = configuration.value_stack().take_last().to(); + auto fill_value = configuration.value_stack().take_last().to(); auto table_index = instruction.arguments().get(); auto address = configuration.frame().module().tables()[table_index.value()]; auto table = configuration.store().get(address); @@ -864,12 +864,12 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con auto index = instruction.arguments().get().value(); auto& functions = configuration.frame().module().functions(); auto address = functions[index]; - configuration.value_stack().append(Value(ValueType(ValueType::FunctionReference), address.value())); + configuration.value_stack().append(Value(address.value())); return; } case Instructions::ref_is_null.value(): { - auto ref = configuration.value_stack().take_last().to(); - configuration.value_stack().append(Value(static_cast(ref.has_value() ? 1 : 0))); + auto ref = configuration.value_stack().take_last().to(); + configuration.value_stack().append(Value(static_cast(ref.ref().has() ? 1 : 0))); return; } case Instructions::drop.value(): @@ -879,10 +879,10 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con case Instructions::select_typed.value(): { // Note: The type seems to only be used for validation. auto value = configuration.value_stack().take_last().to(); - dbgln_if(WASM_TRACE_DEBUG, "select({})", value.value()); + dbgln_if(WASM_TRACE_DEBUG, "select({})", value); auto rhs = configuration.value_stack().take_last(); auto& lhs = configuration.value_stack().last(); - lhs = value.value() != 0 ? lhs : rhs; + lhs = value != 0 ? lhs : rhs; return; } case Instructions::i32_eqz.value(): @@ -1579,15 +1579,15 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con case Instructions::v128_andnot.value(): return binary_numeric_operation(configuration); case Instructions::v128_bitselect.value(): { - auto mask = *configuration.value_stack().take_last().to(); - auto false_vector = *configuration.value_stack().take_last().to(); - auto true_vector = *configuration.value_stack().take_last().to(); + auto mask = configuration.value_stack().take_last().to(); + auto false_vector = configuration.value_stack().take_last().to(); + auto true_vector = configuration.value_stack().take_last().to(); u128 result = (true_vector & mask) | (false_vector & ~mask); configuration.value_stack().append(Value(result)); return; } case Instructions::v128_any_true.value(): { - auto vector = *configuration.value_stack().take_last().to(); + auto vector = configuration.value_stack().take_last().to(); configuration.value_stack().append(Value(static_cast(vector != 0))); return; } diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp index b0e79818936..28c8d91917a 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp @@ -28,7 +28,7 @@ Result Configuration::call(Interpreter& interpreter, FunctionAddress address, Ve locals.ensure_capacity(locals.size() + wasm_function->code().func().locals().size()); for (auto& local : wasm_function->code().func().locals()) { for (size_t i = 0; i < local.n(); ++i) - locals.empend(local.type(), 0ull); + locals.append(Value()); } set_frame(Frame { diff --git a/Userland/Libraries/LibWasm/Printer/Printer.cpp b/Userland/Libraries/LibWasm/Printer/Printer.cpp index b7dfee0065a..b842472aa4b 100644 --- a/Userland/Libraries/LibWasm/Printer/Printer.cpp +++ b/Userland/Libraries/LibWasm/Printer/Printer.cpp @@ -654,24 +654,41 @@ void Printer::print(Wasm::ValueType const& type) print("(type {})\n", ValueType::kind_name(type.kind())); } +void Printer::print(Wasm::Value const& value, Wasm::ValueType const& type) +{ + print_indent(); + switch (type.kind()) { + case ValueType::I32: + print(ByteString::formatted("{}", value.to())); + break; + case ValueType::I64: + print(ByteString::formatted("{}", value.to())); + break; + case ValueType::F32: + print(ByteString::formatted("{}", value.to())); + break; + case ValueType::F64: + print(ByteString::formatted("{}", value.to())); + break; + case ValueType::V128: + print(ByteString::formatted("v128({:x})", value.value())); + break; + case ValueType::FunctionReference: + case ValueType::ExternReference: + print(ByteString::formatted("addr({})", + value.to().ref().visit( + [](Wasm::Reference::Null const&) { return ByteString("null"); }, + [](auto const& ref) { return ByteString::number(ref.address.value()); }))); + break; + } + TemporaryChange change { m_indent, 0 }; +} + void Printer::print(Wasm::Value const& value) { print_indent(); - print("{} ", value.value().visit([&](T const& value) { - if constexpr (IsSame) { - return ByteString::formatted( - "addr({})", - value.ref().visit( - [](Wasm::Reference::Null const&) { return ByteString("null"); }, - [](auto const& ref) { return ByteString::number(ref.address.value()); })); - } else if constexpr (IsSame) { - return ByteString::formatted("v128({:x})", value); - } else { - return ByteString::formatted("{}", value); - } - })); + print("{:x}", value.value()); TemporaryChange change { m_indent, 0 }; - print(value.type()); } void Printer::print(Wasm::Reference const& value) diff --git a/Userland/Libraries/LibWasm/Printer/Printer.h b/Userland/Libraries/LibWasm/Printer/Printer.h index 3be6f367824..a3926fcc860 100644 --- a/Userland/Libraries/LibWasm/Printer/Printer.h +++ b/Userland/Libraries/LibWasm/Printer/Printer.h @@ -59,6 +59,7 @@ struct Printer { void print(Wasm::TypeSection const&); void print(Wasm::ValueType const&); void print(Wasm::Value const&); + void print(Wasm::Value const&, ValueType const&); private: void print_indent(); diff --git a/Userland/Libraries/LibWasm/WASI/Wasi.cpp b/Userland/Libraries/LibWasm/WASI/Wasi.cpp index c5e6c2f50ce..f77c5e74b47 100644 --- a/Userland/Libraries/LibWasm/WASI/Wasi.cpp +++ b/Userland/Libraries/LibWasm/WASI/Wasi.cpp @@ -68,7 +68,7 @@ CompatibleValue to_compatible_value(Wasm::Value const& value) { using Type = typename ToCompatibleValue::Type; // Note: the type can't be something else, we've already checked before through the function type's runtime checker. - auto converted_value = *value.template to(); + auto converted_value = value.template to(); return { .value = converted_value }; } @@ -868,7 +868,7 @@ static Array address_spans(Span values, Configuration& configur Array result; auto memory = configuration.store().get(MemoryAddress { 0 })->data().span(); for (size_t i = 0; i < N; ++i) - result[i] = memory.slice(*values[i].to()); + result[i] = memory.slice(values[i].to()); return result; } @@ -1027,7 +1027,7 @@ struct InvocationOf { auto value = result.release_value(); if constexpr (IsSpecializationOf) { if (value.is_error()) - return Wasm::Result { Vector { Value { ValueType(ValueType::I32), static_cast(to_underlying(value.error().value())) } } }; + return Wasm::Result { Vector { Value { static_cast(to_underlying(value.error().value())) } } }; } if constexpr (!IsVoid) { @@ -1040,7 +1040,7 @@ struct InvocationOf { } } // Return value is errno, we have nothing to return. - return Wasm::Result { Vector { Value(ValueType(ValueType::I32), 0ull) } }; + return Wasm::Result { Vector { Value() } }; }, FunctionType { move(arguments_types), @@ -1227,20 +1227,6 @@ FDFlags fd_flags_of(struct stat const&) } namespace AK { -template<> -struct Formatter : AK::Formatter { - ErrorOr format(FormatBuilder& builder, Wasm::Value const& value) - { - return value.value().visit( - [&](Wasm::Reference const&) { - return Formatter::format(builder, "({}) &r"sv, Wasm::ValueType::kind_name(value.type().kind())); - }, - [&](auto const& v) { - return Formatter::format(builder, "({}) {}"sv, Wasm::ValueType::kind_name(value.type().kind()), v); - }); - } -}; - template<> struct Formatter : AK::Formatter { ErrorOr format(FormatBuilder& builder, Wasm::Wasi::Errno const& value) diff --git a/Userland/Libraries/LibWeb/WebAssembly/Table.cpp b/Userland/Libraries/LibWeb/WebAssembly/Table.cpp index 5fa0661e829..50941c8739d 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/Table.cpp +++ b/Userland/Libraries/LibWeb/WebAssembly/Table.cpp @@ -32,7 +32,7 @@ static Wasm::ValueType table_kind_to_value_type(Bindings::TableKind kind) static JS::ThrowCompletionOr value_to_reference(JS::VM& vm, JS::Value value, Wasm::ValueType const& reference_type) { if (value.is_undefined()) - return Wasm::Value(reference_type, 0ull); + return Wasm::Value(); return Detail::to_webassembly_value(vm, value, reference_type); } @@ -51,7 +51,7 @@ WebIDL::ExceptionOr> Table::construct_impl(JS::Realm& re if (!address.has_value()) return vm.throw_completion("Wasm Table allocation failed"sv); - auto const& reference = reference_value.value().get(); + auto const& reference = reference_value.to(); auto& table = *cache.abstract_machine().store().get(*address); for (auto& element : table.elements()) element = reference; @@ -84,7 +84,7 @@ WebIDL::ExceptionOr Table::grow(u32 delta, JS::Value value) auto initial_size = table->elements().size(); auto reference_value = TRY(value_to_reference(vm, value, table->type().element_type())); - auto const& reference = reference_value.value().get(); + auto const& reference = reference_value.to(); if (!table->grow(delta, reference)) return vm.throw_completion("Failed to grow table"sv); @@ -108,7 +108,7 @@ WebIDL::ExceptionOr Table::get(u32 index) const auto& ref = table->elements()[index]; Wasm::Value wasm_value { ref }; - return Detail::to_js_value(vm, wasm_value); + return Detail::to_js_value(vm, wasm_value, table->type().element_type()); } // https://webassembly.github.io/spec/js-api/#dom-table-set @@ -125,7 +125,7 @@ WebIDL::ExceptionOr Table::set(u32 index, JS::Value value) return vm.throw_completion("Table element index out of range"sv); auto reference_value = TRY(value_to_reference(vm, value, table->type().element_type())); - auto const& reference = reference_value.value().get(); + auto const& reference = reference_value.to(); table->elements()[index] = reference; diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp index b243a2f3089..94901a5c1df 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp +++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp @@ -193,8 +193,11 @@ JS::ThrowCompletionOr> instantiate_module(JS Wasm::HostFunction host_function { [&](auto&, auto& arguments) -> Wasm::Result { JS::MarkedVector argument_values { vm.heap() }; - for (auto& entry : arguments) - argument_values.append(to_js_value(vm, entry)); + size_t index = 0; + for (auto& entry : arguments) { + argument_values.append(to_js_value(vm, entry, type.parameters()[index])); + ++index; + } auto result = TRY(JS::call(vm, function, JS::js_undefined(), argument_values.span())); if (type.results().is_empty()) @@ -378,11 +381,13 @@ JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress add return JS::js_undefined(); if (result.values().size() == 1) - return to_js_value(vm, result.values().first()); + return to_js_value(vm, result.values().first(), type.results().first()); - return JS::Value(JS::Array::create_from(realm, result.values(), [&](Wasm::Value value) { - return to_js_value(vm, value); - })); + VERIFY_NOT_REACHED(); + // TODO + /*return JS::Value(JS::Array::create_from(realm, result.values(), [&](Wasm::Value value) {*/ + /* return to_js_value(vm, value);*/ + /*}));*/ }); cache.add_function_instance(address, function); @@ -417,7 +422,7 @@ JS::ThrowCompletionOr to_webassembly_value(JS::VM& vm, JS::Value va } case Wasm::ValueType::FunctionReference: { if (value.is_null()) - return Wasm::Value { Wasm::ValueType(Wasm::ValueType::FunctionReference), 0ull }; + return Wasm::Value(); if (value.is_function()) { auto& function = value.as_function(); @@ -439,20 +444,20 @@ JS::ThrowCompletionOr to_webassembly_value(JS::VM& vm, JS::Value va VERIFY_NOT_REACHED(); } -JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value) +JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value, Wasm::ValueType type) { auto& realm = *vm.current_realm(); - switch (wasm_value.type().kind()) { + switch (type.kind()) { case Wasm::ValueType::I64: - return realm.heap().allocate(realm, ::Crypto::SignedBigInteger { wasm_value.to().value() }); + return realm.heap().allocate(realm, ::Crypto::SignedBigInteger { wasm_value.to() }); case Wasm::ValueType::I32: - return JS::Value(wasm_value.to().value()); + return JS::Value(wasm_value.to()); case Wasm::ValueType::F64: - return JS::Value(wasm_value.to().value()); + return JS::Value(wasm_value.to()); case Wasm::ValueType::F32: - return JS::Value(static_cast(wasm_value.to().value())); + return JS::Value(static_cast(wasm_value.to())); case Wasm::ValueType::FunctionReference: { - auto ref_ = *wasm_value.to(); + auto ref_ = wasm_value.to(); if (ref_.ref().has()) return JS::js_null(); auto address = ref_.ref().get().address; diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h index 47782f9f031..78bbf3a6f5a 100644 --- a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h +++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h @@ -62,7 +62,7 @@ JS::ThrowCompletionOr> instantiate_module(JS JS::ThrowCompletionOr> parse_module(JS::VM&, JS::Object* buffer); JS::NativeFunction* create_native_function(JS::VM&, Wasm::FunctionAddress address, ByteString const& name, Instance* instance = nullptr); JS::ThrowCompletionOr to_webassembly_value(JS::VM&, JS::Value value, Wasm::ValueType const& type); -JS::Value to_js_value(JS::VM&, Wasm::Value& wasm_value); +JS::Value to_js_value(JS::VM&, Wasm::Value& wasm_value, Wasm::ValueType type); extern HashMap, WebAssemblyCache> s_caches; diff --git a/Userland/Utilities/wasm.cpp b/Userland/Utilities/wasm.cpp index 56e6b7dcc8c..9e374eb6823 100644 --- a/Userland/Utilities/wasm.cpp +++ b/Userland/Utilities/wasm.cpp @@ -31,6 +31,11 @@ static void (*old_signal)(int); static StackInfo g_stack_info; static Wasm::DebuggerBytecodeInterpreter g_interpreter(g_stack_info); +struct ParsedValue { + Wasm::Value value; + Wasm::ValueType type; +}; + static void sigint_handler(int) { if (!g_continue) { @@ -81,7 +86,7 @@ static Optional convert_to_uint_from_hex(StringView string) return value; } -static ErrorOr parse_value(StringView spec) +static ErrorOr parse_value(StringView spec) { constexpr auto is_sep = [](char c) { return is_ascii_space(c) || c == ':'; }; // Scalar: 'T.const[:\s]v' (i32.const 42) @@ -127,42 +132,63 @@ static ErrorOr parse_value(StringView spec) lexer.ignore_while(is_sep); // The rest of the string is the value auto text = lexer.consume_all(); - return parse_u128(text); + return ParsedValue { + .value = TRY(parse_u128(text)), + .type = Wasm::ValueType(Wasm::ValueType::Kind::V128) + }; } if (lexer.consume_specific("i8.const"sv)) { lexer.ignore_while(is_sep); auto text = lexer.consume_all(); - return parse_scalar.operator()(text); + return ParsedValue { + .value = TRY(parse_scalar.operator()(text)), + .type = Wasm::ValueType(Wasm::ValueType::Kind::I32) + }; } if (lexer.consume_specific("i16.const"sv)) { lexer.ignore_while(is_sep); auto text = lexer.consume_all(); - return parse_scalar.operator()(text); + return ParsedValue { + .value = TRY(parse_scalar.operator()(text)), + .type = Wasm::ValueType(Wasm::ValueType::Kind::I32) + }; } if (lexer.consume_specific("i32.const"sv)) { lexer.ignore_while(is_sep); auto text = lexer.consume_all(); - return parse_scalar.operator()(text); + return ParsedValue { + .value = TRY(parse_scalar.operator()(text)), + .type = Wasm::ValueType(Wasm::ValueType::Kind::I32) + }; } if (lexer.consume_specific("i64.const"sv)) { lexer.ignore_while(is_sep); auto text = lexer.consume_all(); - return parse_scalar.operator()(text); + return ParsedValue { + .value = TRY(parse_scalar.operator()(text)), + .type = Wasm::ValueType(Wasm::ValueType::Kind::I64) + }; } if (lexer.consume_specific("f32.const"sv)) { lexer.ignore_while(is_sep); auto text = lexer.consume_all(); - return parse_scalar.operator()(text); + return ParsedValue { + .value = TRY(parse_scalar.operator()(text)), + .type = Wasm::ValueType(Wasm::ValueType::Kind::F32) + }; } if (lexer.consume_specific("f64.const"sv)) { lexer.ignore_while(is_sep); auto text = lexer.consume_all(); - return parse_scalar.operator()(text); + return ParsedValue { + .value = TRY(parse_scalar.operator()(text)), + .type = Wasm::ValueType(Wasm::ValueType::Kind::F64) + }; } if (lexer.consume_specific("v("sv)) { - Vector values; + Vector values; for (;;) { lexer.ignore_while(is_sep); if (lexer.consume_specific(")"sv)) @@ -181,9 +207,9 @@ static ErrorOr parse_value(StringView spec) if (values.is_empty()) return Error::from_string_literal("Empty vector"); - auto element_type = values.first().type(); + auto element_type = values.first().type; for (auto& value : values) { - if (value.type() != element_type) + if (value.type != element_type) return Error::from_string_literal("Mixed types in vector"); } @@ -191,24 +217,25 @@ static ErrorOr parse_value(StringView spec) unsigned width = 0; u128 result = 0; u128 last_value = 0; - for (auto& value : values) { + for (auto& parsed : values) { if (total_size >= 128) return Error::from_string_literal("Vector too large"); - width = value.value().visit( - [&](Integral auto x) { - last_value = u128(x, 0); - return sizeof(x); - }, - [&](float x) { - last_value = u128(bit_cast(x), 0); - return sizeof(x); - }, - [&](double x) { - last_value = u128(bit_cast(x), 0); - return sizeof(x); - }, - [&](auto) -> size_t { VERIFY_NOT_REACHED(); }); + switch (parsed.type.kind()) { + case Wasm::ValueType::F32: + case Wasm::ValueType::I32: + width = sizeof(u32); + break; + case Wasm::ValueType::F64: + case Wasm::ValueType::I64: + width = sizeof(u64); + break; + case Wasm::ValueType::V128: + case Wasm::ValueType::FunctionReference: + case Wasm::ValueType::ExternReference: + VERIFY_NOT_REACHED(); + } + last_value = parsed.value.value(); result |= last_value << total_size; total_size += width * 8; @@ -222,7 +249,10 @@ static ErrorOr parse_value(StringView spec) total_size += width * 8; } - return Wasm::Value { result }; + return ParsedValue { + .value = Wasm::Value { result }, + .type = Wasm::ValueType(Wasm::ValueType::Kind::V128) + }; } return Error::from_string_literal("Invalid value"); @@ -390,7 +420,7 @@ static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPoi warnln("Expected {} arguments for call, but found only {}", type.parameters().size(), args.size() - 2); continue; } - Vector values_to_push; + Vector values_to_push; Vector values; auto ok = true; for (size_t index = 2; index < args.size(); ++index) { @@ -406,12 +436,12 @@ static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPoi continue; for (auto& param : type.parameters()) { auto v = values_to_push.take_last(); - if (v.type() != param) { - warnln("Type mismatch in argument: expected {}, but got {}", Wasm::ValueType::kind_name(param.kind()), Wasm::ValueType::kind_name(v.type().kind())); + if (v.type != param) { + warnln("Type mismatch in argument: expected {}, but got {}", Wasm::ValueType::kind_name(param.kind()), Wasm::ValueType::kind_name(v.type.kind())); ok = false; break; } - values.append(v); + values.append(v.value); } if (!ok) continue; @@ -426,9 +456,11 @@ static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPoi } else { if (!result.values().is_empty()) warnln("Returned:"); + size_t index = 0; for (auto& value : result.values()) { g_stdout->write_until_depleted(" -> "sv.bytes()).release_value_but_fixme_should_propagate_errors(); - g_printer->print(value); + g_printer->print(value, type.results()[index]); + ++index; } } continue; @@ -492,7 +524,7 @@ ErrorOr serenity_main(Main::Arguments arguments) bool shell_mode = false; bool wasi = false; ByteString exported_function_to_execute; - Vector values_to_push; + Vector values_to_push; Vector modules_to_link_in; Vector args_if_wasi; Vector wasi_preopened_mappings; @@ -677,9 +709,11 @@ ErrorOr serenity_main(Main::Arguments arguments) [name = entry.name, type = type](auto&, auto& arguments) -> Wasm::Result { StringBuilder argument_builder; bool first = true; + size_t index = 0; for (auto& argument : arguments) { AllocatingMemoryStream stream; - Wasm::Printer { stream }.print(argument); + auto value_type = type.parameters()[index]; + Wasm::Printer { stream }.print(argument, value_type); if (first) first = false; else @@ -687,12 +721,13 @@ ErrorOr serenity_main(Main::Arguments arguments) auto buffer = ByteBuffer::create_uninitialized(stream.used_buffer_size()).release_value_but_fixme_should_propagate_errors(); stream.read_until_filled(buffer).release_value_but_fixme_should_propagate_errors(); argument_builder.append(StringView(buffer).trim_whitespace()); + ++index; } dbgln("[wasm runtime] Stub function {} was called with the following arguments: {}", name, argument_builder.to_byte_string()); Vector result; result.ensure_capacity(type.results().size()); - for (auto& result_type : type.results()) - result.append(Wasm::Value { result_type, 0ull }); + for (size_t i = 0; i < type.results().size(); ++i) + result.append(Wasm::Value()); return Wasm::Result { move(result) }; }, type, @@ -783,11 +818,11 @@ ErrorOr serenity_main(Main::Arguments arguments) for (auto& param : instance->get().type().parameters()) { if (values_to_push.is_empty()) { - values.append(Wasm::Value { param, 0ull }); - } else if (param == values_to_push.last().type()) { - values.append(values_to_push.take_last()); + values.append(Wasm::Value()); + } else if (param == values_to_push.last().type) { + values.append(values_to_push.take_last().value); } else { - warnln("Type mismatch in argument: expected {}, but got {}", Wasm::ValueType::kind_name(param.kind()), Wasm::ValueType::kind_name(values_to_push.last().type().kind())); + warnln("Type mismatch in argument: expected {}, but got {}", Wasm::ValueType::kind_name(param.kind()), Wasm::ValueType::kind_name(values_to_push.last().type.kind())); return 1; } } @@ -810,9 +845,12 @@ ErrorOr serenity_main(Main::Arguments arguments) } else { if (!result.values().is_empty()) warnln("Returned:"); + auto result_type = instance->get().type().results(); + size_t index = 0; for (auto& value : result.values()) { g_stdout->write_until_depleted(" -> "sv.bytes()).release_value_but_fixme_should_propagate_errors(); - g_printer->print(value); + g_printer->print(value, result_type[index]); + ++index; } } }