diff --git a/Libraries/LibWasm/AbstractMachine/AbstractMachine.h b/Libraries/LibWasm/AbstractMachine/AbstractMachine.h index 6e0fffc041..db2d42b377 100644 --- a/Libraries/LibWasm/AbstractMachine/AbstractMachine.h +++ b/Libraries/LibWasm/AbstractMachine/AbstractMachine.h @@ -103,6 +103,7 @@ public: // ref.null exnref m_value = u128(0, 4); break; + case ValueType::TypeUseReference: case ValueType::UnsupportedHeapReference: // ref.null (todo) m_value = u128(0, 5); diff --git a/Libraries/LibWasm/AbstractMachine/Validator.cpp b/Libraries/LibWasm/AbstractMachine/Validator.cpp index b2f8e62618..46b5df782c 100644 --- a/Libraries/LibWasm/AbstractMachine/Validator.cpp +++ b/Libraries/LibWasm/AbstractMachine/Validator.cpp @@ -313,6 +313,7 @@ ErrorOr Validator::validate(TagSection const& section) ErrorOr Validator::validate(TableType const& type) { + TRY(validate(type.element_type())); Optional bound = type.limits().address_type() == AddressType::I64 ? Optional {} : (1ull << 32) - 1; return validate(type.limits(), bound); } @@ -339,6 +340,33 @@ ErrorOr Validator::validate(Wasm::TagType const& tag_type return {}; } +ErrorOr Validator::validate(ValueType const& type) +{ + if (type.is_typeuse()) { + TRY(validate(type.unsafe_typeindex())); + } + + return {}; +} + +ErrorOr Validator::validate(FunctionType const& type) +{ + for (auto param : type.parameters()) { + TRY(validate(param)); + } + + for (auto param : type.results()) { + TRY(validate(param)); + } + + return {}; +} + +ErrorOr Validator::validate(GlobalType const& type) +{ + return validate(type.type()); +} + ErrorOr Validator::validate(BlockType const& type) { if (type.kind() == BlockType::Index) { diff --git a/Libraries/LibWasm/AbstractMachine/Validator.h b/Libraries/LibWasm/AbstractMachine/Validator.h index fcdaba5608..5836796b5c 100644 --- a/Libraries/LibWasm/AbstractMachine/Validator.h +++ b/Libraries/LibWasm/AbstractMachine/Validator.h @@ -302,11 +302,12 @@ public: // Types ErrorOr validate(Limits const&, Optional bound); // n <= bound && m? <= bound ErrorOr validate(BlockType const&); - ErrorOr validate(FunctionType const&) { return {}; } + ErrorOr validate(FunctionType const&); ErrorOr validate(TableType const&); ErrorOr validate(MemoryType const&); - ErrorOr validate(GlobalType const&) { return {}; } + ErrorOr validate(GlobalType const&); ErrorOr validate(TagType const&); + ErrorOr validate(ValueType const&); // Proposal 'memory64' ErrorOr take_memory_address(Stack& stack, MemoryType const& memory, Instruction::MemoryArgument const& arg) @@ -407,7 +408,7 @@ struct AK::Formatter : public AK::Formatter format(FormatBuilder& builder, Wasm::Validator::StackEntry const& value) { if (value.is_known) - return Formatter::format(builder, Wasm::ValueType::kind_name(value.concrete_type.kind())); + return Formatter::format(builder, value.concrete_type.kind_name()); return Formatter::format(builder, ""sv); } @@ -425,7 +426,7 @@ template<> struct AK::Formatter : public AK::Formatter { ErrorOr format(FormatBuilder& builder, Wasm::ValueType const& value) { - return Formatter::format(builder, Wasm::ValueType::kind_name(value.kind())); + return Formatter::format(builder, value.kind_name()); } }; diff --git a/Libraries/LibWasm/Parser/Parser.cpp b/Libraries/LibWasm/Parser/Parser.cpp index 4769dbf5b3..975db4eaa7 100644 --- a/Libraries/LibWasm/Parser/Parser.cpp +++ b/Libraries/LibWasm/Parser/Parser.cpp @@ -99,6 +99,45 @@ static ParseResult parse_name(ConstrainedStream& stream) return string; } +static ParseResult parse_reference_type(Stream& stream, u8 tag) +{ + switch (tag) { + case Constants::function_reference_tag: + return ValueType(ValueType::FunctionReference); + case Constants::extern_reference_tag: + return ValueType(ValueType::ExternReference); + case Constants::array_reference_tag: + case Constants::struct_reference_tag: + case Constants::i31_reference_tag: + case Constants::eq_reference_tag: + case Constants::any_reference_tag: + case Constants::none_reference_tag: + case Constants::noextern_reference_tag: + case Constants::nofunc_reference_tag: + case Constants::noexn_heap_reference_tag: + // FIXME: Implement these when we support wasm-gc properly. + return ValueType(ValueType::UnsupportedHeapReference); + case Constants::nullable_reference_tag_tag: + case Constants::non_nullable_reference_tag_tag: { + tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag); + return parse_reference_type(stream, tag); + } + default: { + ReconsumableStream new_stream { stream }; + new_stream.unread({ &tag, 1 }); + + // FIXME: should be an i33. Right now, we're missing a potential last bit at + // the end. See https://webassembly.github.io/spec/core/bikeshed/#heap-types%E2%91%A6 + i32 type_index = TRY_READ(new_stream, LEB128, ParseError::ExpectedIndex); + if (type_index < 0) { + return with_eof_check(stream, ParseError::InvalidIndex); + } + + return ValueType(ValueType::TypeUseReference, TypeIndex(type_index)); + } + } +} + ParseResult ValueType::parse(Stream& stream) { ScopeLogger logger("ValueType"sv); @@ -115,28 +154,8 @@ ParseResult ValueType::parse(Stream& stream) return ValueType(F64); case Constants::v128_tag: return ValueType(V128); - case Constants::function_reference_tag: - return ValueType(FunctionReference); - case Constants::extern_reference_tag: - return ValueType(ExternReference); - case Constants::array_reference_tag: - case Constants::struct_reference_tag: - case Constants::i31_reference_tag: - case Constants::eq_reference_tag: - case Constants::any_reference_tag: - case Constants::none_reference_tag: - case Constants::noextern_reference_tag: - case Constants::nofunc_reference_tag: - case Constants::noexn_heap_reference_tag: - // FIXME: Implement these when we support wasm-gc properly. - return ValueType(UnsupportedHeapReference); - case Constants::nullable_reference_tag_tag: - case Constants::non_nullable_reference_tag_tag: - tag = TRY_READ(stream, u8, ParseError::ExpectedKindTag); - (void)tag; - return ValueType(UnsupportedHeapReference); default: - return ParseError::InvalidTag; + return parse_reference_type(stream, tag); } } @@ -253,25 +272,14 @@ ParseResult BlockType::parse(ConstrainedStream& stream) if (kind == Constants::empty_block_tag) return BlockType {}; - { - FixedMemoryStream value_stream { ReadonlyBytes { &kind, 1 } }; - if (auto value_type = ValueType::parse(value_stream); !value_type.is_error()) - return BlockType { value_type.release_value() }; + ReconsumableStream value_stream { stream }; + value_stream.unread({ &kind, 1 }); + auto value_type = TRY(ValueType::parse(value_stream)); + if (value_type.is_typeuse()) { + return BlockType { value_type.unsafe_typeindex() }; } - ReconsumableStream new_stream { stream }; - new_stream.unread({ &kind, 1 }); - - // FIXME: should be an i33. Right now, we're missing a potential last bit at - // the end. See https://webassembly.github.io/spec/core/binary/instructions.html#binary-blocktype - i32 index_value = TRY_READ(new_stream, LEB128, ParseError::ExpectedIndex); - - if (index_value < 0) { - dbgln("Invalid type index {}", index_value); - return with_eof_check(stream, ParseError::InvalidIndex); - } - - return BlockType { TypeIndex(index_value) }; + return BlockType { value_type }; } ParseResult Catch::parse(ConstrainedStream& stream) diff --git a/Libraries/LibWasm/Printer/Printer.cpp b/Libraries/LibWasm/Printer/Printer.cpp index bbdeb72ea1..8fb1c1804c 100644 --- a/Libraries/LibWasm/Printer/Printer.cpp +++ b/Libraries/LibWasm/Printer/Printer.cpp @@ -770,7 +770,7 @@ void Printer::print(Wasm::FieldType const& type) void Printer::print(Wasm::ValueType const& type) { print_indent(); - print("(type {})\n", ValueType::kind_name(type.kind())); + print("(type {})\n", type.kind_name()); } void Printer::print(Wasm::Value const& value, Wasm::ValueType const& type) @@ -801,6 +801,9 @@ void Printer::print(Wasm::Value const& value, Wasm::ValueType const& type) [](Wasm::Reference::Exception const&) { return ByteString("exception"); }, [](auto const& ref) { return ByteString::number(ref.address.value()); })); break; + case ValueType::TypeUseReference: + print("unsupported-type-use-ref({})", type.unsafe_typeindex()); + break; case ValueType::UnsupportedHeapReference: print("unsupported-heap-ref"); break; diff --git a/Libraries/LibWasm/Types.h b/Libraries/LibWasm/Types.h index d7458c7330..c6cec2778e 100644 --- a/Libraries/LibWasm/Types.h +++ b/Libraries/LibWasm/Types.h @@ -173,6 +173,7 @@ public: FunctionReference, ExternReference, ExceptionReference, + TypeUseReference, UnsupportedHeapReference, // Stub for wasm-gc proposal's reference types. }; @@ -181,18 +182,28 @@ public: { } + template + explicit ValueType(Kind kind, T argument) + : m_kind(kind) + , m_argument(move(argument)) + { + } + bool operator==(ValueType const&) const = default; - auto is_reference() const { return m_kind == ExternReference || m_kind == FunctionReference || m_kind == UnsupportedHeapReference; } + auto is_reference() const { return m_kind == ExternReference || m_kind == FunctionReference || m_kind == TypeUseReference || m_kind == UnsupportedHeapReference; } auto is_vector() const { return m_kind == V128; } auto is_numeric() const { return !is_reference() && !is_vector(); } + auto is_typeuse() const { return m_kind == TypeUseReference; } auto kind() const { return m_kind; } + auto unsafe_typeindex() const { return m_argument.unsafe_get(); } + static ParseResult parse(Stream& stream); - static ByteString kind_name(Kind kind) + ByteString kind_name() const { - switch (kind) { + switch (m_kind) { case I32: return "i32"; case I64: @@ -209,6 +220,8 @@ public: return "externref"; case ExceptionReference: return "exnref"; + case TypeUseReference: + return ByteString::formatted("ref null {}", unsafe_typeindex().value()); case UnsupportedHeapReference: return "todo.heapref"; } @@ -217,6 +230,7 @@ public: private: Kind m_kind; + Variant m_argument; }; // https://webassembly.github.io/spec/core/bikeshed/#result-types%E2%91%A2 diff --git a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp index 8e4c95b61f..8453149ae9 100644 --- a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp +++ b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp @@ -690,6 +690,7 @@ JS::ThrowCompletionOr to_webassembly_value(JS::VM& vm, JS::Value va return Wasm::Value(Wasm::ValueType { Wasm::ValueType::Kind::ExceptionReference }); case Wasm::ValueType::V128: return vm.throw_completion("Cannot convert a vector value to a javascript value"sv); + case Wasm::ValueType::TypeUseReference: case Wasm::ValueType::UnsupportedHeapReference: return vm.throw_completion("Unsupported heap reference"sv); } @@ -711,6 +712,8 @@ Wasm::Value default_webassembly_value(JS::VM& vm, Wasm::ValueType type) return MUST(to_webassembly_value(vm, JS::js_undefined(), type)); case Wasm::ValueType::ExceptionReference: return Wasm::Value(type); + case Wasm::ValueType::TypeUseReference: + return Wasm::Value(type); case Wasm::ValueType::UnsupportedHeapReference: return Wasm::Value(type); } @@ -758,6 +761,7 @@ JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value, Wasm::ValueType type) } case Wasm::ValueType::V128: case Wasm::ValueType::ExceptionReference: + case Wasm::ValueType::TypeUseReference: case Wasm::ValueType::UnsupportedHeapReference: VERIFY_NOT_REACHED(); } diff --git a/Meta/wasm_unimplemented_tests.txt b/Meta/wasm_unimplemented_tests.txt index 0196d9d57f..871158b535 100644 --- a/Meta/wasm_unimplemented_tests.txt +++ b/Meta/wasm_unimplemented_tests.txt @@ -130,6 +130,9 @@ module try_table.2 module try_table.5 module type-canon.0 module type-canon.1 +module type-equivalence.0 +module type-equivalence.1 +module type-equivalence.13 module type-equivalence.14 module type-equivalence.15 module type-equivalence.16 @@ -139,6 +142,7 @@ module type-equivalence.19 module type-equivalence.2 module type-equivalence.20 module type-equivalence.21 +module type-equivalence.3 module type-equivalence.4 module type-equivalence.5 module type-equivalence.7 diff --git a/Tests/LibWasm/test-wasm.cpp b/Tests/LibWasm/test-wasm.cpp index dee25b4d70..2b6812ef0f 100644 --- a/Tests/LibWasm/test-wasm.cpp +++ b/Tests/LibWasm/test-wasm.cpp @@ -334,6 +334,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::get_export) [](Wasm::Reference::Exception const&) -> JS::Value { return JS::js_undefined(); }, [&](auto const& ref) -> JS::Value { return JS::Value(static_cast(ref.address.value())); }); } + case Wasm::ValueType::TypeUseReference: case Wasm::ValueType::UnsupportedHeapReference: return vm.throw_completion("Unsupported heap reference"sv); } @@ -422,6 +423,12 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke) else return vm.throw_completion("Exception references are not supported"sv); break; + case Wasm::ValueType::Kind::TypeUseReference: + if (argument.is_null()) + arguments.append(Wasm::Value(Wasm::Reference { Wasm::Reference::Null { Wasm::ValueType(Wasm::ValueType::Kind::TypeUseReference, param.unsafe_typeindex()) } })); + else + return vm.throw_completion("GC Heap references are not supported"sv); + break; case Wasm::ValueType::Kind::UnsupportedHeapReference: return vm.throw_completion("GC Heap references are not supported"sv); } @@ -460,6 +467,8 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke) return (value.to()).ref().visit([&](Wasm::Reference::Null) { return JS::js_null(); }, [&](Wasm::Reference::Exception) { return JS::Value(); }, [&](auto const& ref) { return JS::Value(static_cast(ref.address.value())); }); case Wasm::ValueType::ExceptionReference: return JS::js_null(); + case Wasm::ValueType::TypeUseReference: + return JS::js_null(); case Wasm::ValueType::UnsupportedHeapReference: return vm.throw_completion("Unsupported heap reference"sv); } diff --git a/Utilities/wasm.cpp b/Utilities/wasm.cpp index cec01b6ba8..346ec600fa 100644 --- a/Utilities/wasm.cpp +++ b/Utilities/wasm.cpp @@ -234,6 +234,7 @@ static ErrorOr parse_value(StringView spec) case Wasm::ValueType::FunctionReference: case Wasm::ValueType::ExternReference: case Wasm::ValueType::ExceptionReference: + case Wasm::ValueType::TypeUseReference: case Wasm::ValueType::UnsupportedHeapReference: VERIFY_NOT_REACHED(); } @@ -343,7 +344,7 @@ ErrorOr ladybird_main(Main::Arguments arguments) } auto fn_name = lexer.consume_until(is_any_of("(=:"sv)); struct Arg { - Wasm::ValueType::Kind type; + Wasm::ValueType type; StringView name; }; Vector formal_params; @@ -354,24 +355,24 @@ ErrorOr ladybird_main(Main::Arguments arguments) warnln("Invalid JS export argument name in '{}'", str); return false; } - auto type = Wasm::ValueType::I32; + auto type_kind = Wasm::ValueType::I32; if (lexer.consume_specific(':')) { if (lexer.consume_specific("i32"sv)) { - type = Wasm::ValueType::I32; + type_kind = Wasm::ValueType::I32; } else if (lexer.consume_specific("i64"sv)) { - type = Wasm::ValueType::I64; + type_kind = Wasm::ValueType::I64; } else if (lexer.consume_specific("f32"sv)) { - type = Wasm::ValueType::F32; + type_kind = Wasm::ValueType::F32; } else if (lexer.consume_specific("f64"sv)) { - type = Wasm::ValueType::F64; + type_kind = Wasm::ValueType::F64; } else if (lexer.consume_specific("v128"sv)) { - type = Wasm::ValueType::V128; + type_kind = Wasm::ValueType::V128; } else { warnln("Invalid JS export argument type in '{}'", str); return false; } } - formal_params.append(Arg { type, name }); + formal_params.append(Arg { Wasm::ValueType(type_kind), name }); lexer.consume_specific(','); } } @@ -451,7 +452,7 @@ ErrorOr ladybird_main(Main::Arguments arguments) return Wasm::Trap { ByteString("Not enough arguments") }; } auto& arg = args[i]; - switch (type) { + switch (type.kind()) { case Wasm::ValueType::I32: js_args.append(JS::Value(arg.to())); break; @@ -471,7 +472,7 @@ ErrorOr ladybird_main(Main::Arguments arguments) break; } default: - warnln("Unsupported argument type '{}' for JS export function '{}'", Wasm::ValueType::kind_name(type), name); + warnln("Unsupported argument type '{}' for JS export function '{}'", type.kind_name(), name); return Wasm::Trap { ByteString("Unsupported argument type") }; } } @@ -864,7 +865,7 @@ ErrorOr ladybird_main(Main::Arguments arguments) } 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 {}", param.kind_name(), values_to_push.last().type.kind_name()); return 1; } }