mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
LibWasm: Implement parsing/validation for proposal exception-handling
Actual execution traps for now.
This commit is contained in:
parent
8138c2f48b
commit
d99f663b1a
|
|
@ -76,6 +76,20 @@ Optional<ElementAddress> Store::allocate(ValueType const& type, Vector<Reference
|
|||
return address;
|
||||
}
|
||||
|
||||
Optional<TagAddress> Store::allocate(FunctionType const& type, TagType::Flags flags)
|
||||
{
|
||||
TagAddress address { m_tags.size() };
|
||||
m_tags.append({ type, flags });
|
||||
return address;
|
||||
}
|
||||
|
||||
Optional<ExceptionAddress> Store::allocate(TagInstance const& tag_instance, Vector<Value> params)
|
||||
{
|
||||
ExceptionAddress address { m_exceptions.size() };
|
||||
m_exceptions.append(ExceptionInstance { tag_instance, move(params) });
|
||||
return address;
|
||||
}
|
||||
|
||||
FunctionInstance* Store::get(FunctionAddress address)
|
||||
{
|
||||
auto value = address.value();
|
||||
|
|
@ -132,6 +146,22 @@ DataInstance* Store::get(DataAddress address)
|
|||
return &m_datas[value];
|
||||
}
|
||||
|
||||
TagInstance* Store::get(TagAddress address)
|
||||
{
|
||||
auto value = address.value();
|
||||
if (m_tags.size() <= value)
|
||||
return nullptr;
|
||||
return &m_tags[value];
|
||||
}
|
||||
|
||||
ExceptionInstance* Store::get(ExceptionAddress address)
|
||||
{
|
||||
auto value = address.value();
|
||||
if (m_exceptions.size() <= value)
|
||||
return nullptr;
|
||||
return &m_exceptions[value];
|
||||
}
|
||||
|
||||
ErrorOr<void, ValidationError> AbstractMachine::validate(Module& module)
|
||||
{
|
||||
if (module.validation_status() != Module::ValidationStatus::Unchecked) {
|
||||
|
|
@ -203,6 +233,19 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
|
|||
return ByteString::formatted("Function import and extern do not match, parameters: {} vs {}", type.parameters(), other_type.parameters());
|
||||
return {};
|
||||
},
|
||||
[&](TagType const& type) -> Optional<ByteString> {
|
||||
if (!extern_.has<TagAddress>())
|
||||
return "Expected tag import"sv;
|
||||
auto other_tag_instance = m_store.get(extern_.get<TagAddress>());
|
||||
if (other_tag_instance->flags() != type.flags())
|
||||
return "Tag import and extern do not match"sv;
|
||||
|
||||
auto& this_type = module.type_section().types()[type.type().value()];
|
||||
|
||||
if (other_tag_instance->type().parameters() != this_type.parameters())
|
||||
return "Tag import and extern do not match"sv;
|
||||
return {};
|
||||
},
|
||||
[&](TypeIndex type_index) -> Optional<ByteString> {
|
||||
if (!extern_.has<FunctionAddress>())
|
||||
return "Expected function import"sv;
|
||||
|
|
@ -407,7 +450,8 @@ Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module
|
|||
[&](FunctionAddress const& address) { module_instance.functions().append(address); },
|
||||
[&](TableAddress const& address) { module_instance.tables().append(address); },
|
||||
[&](MemoryAddress const& address) { module_instance.memories().append(address); },
|
||||
[&](GlobalAddress const& address) { module_instance.globals().append(address); });
|
||||
[&](GlobalAddress const& address) { module_instance.globals().append(address); },
|
||||
[&](TagAddress const& address) { module_instance.tags().append(address); });
|
||||
}
|
||||
|
||||
module_instance.functions().extend(own_functions);
|
||||
|
|
@ -434,8 +478,15 @@ Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module
|
|||
index++;
|
||||
}
|
||||
|
||||
for (auto& entry : module.tag_section().tags()) {
|
||||
auto& type = module.type_section().types()[entry.type().value()];
|
||||
auto address = m_store.allocate(type, entry.flags());
|
||||
VERIFY(address.has_value());
|
||||
module_instance.tags().append(*address);
|
||||
}
|
||||
|
||||
for (auto& entry : module.export_section().entries()) {
|
||||
Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, Empty> address {};
|
||||
Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, TagAddress, Empty> address {};
|
||||
entry.description().visit(
|
||||
[&](FunctionIndex const& index) {
|
||||
if (module_instance.functions().size() > index.value())
|
||||
|
|
@ -460,6 +511,12 @@ Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module
|
|||
address = GlobalAddress { module_instance.globals()[index.value()] };
|
||||
else
|
||||
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.globals().size());
|
||||
},
|
||||
[&](TagIndex const& index) {
|
||||
if (module_instance.tags().size() > index.value())
|
||||
address = TagAddress { module_instance.tags()[index.value()] };
|
||||
else
|
||||
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.tags().size());
|
||||
});
|
||||
|
||||
if (address.has<Empty>()) {
|
||||
|
|
@ -469,7 +526,7 @@ Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module
|
|||
|
||||
module_instance.exports().append(ExportInstance {
|
||||
entry.name(),
|
||||
move(address).downcast<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress>(),
|
||||
move(address).downcast<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, TagAddress>(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, GlobalAddress, Arithmetic, Comparison,
|
|||
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, ElementAddress, Arithmetic, Comparison, Increment);
|
||||
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, DataAddress, Arithmetic, Comparison, Increment);
|
||||
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, MemoryAddress, Arithmetic, Comparison, Increment);
|
||||
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, TagAddress, Arithmetic, Comparison, Increment);
|
||||
AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, ExceptionAddress, Arithmetic, Comparison, Increment);
|
||||
|
||||
// FIXME: These should probably be made generic/virtual if/when we decide to do something more
|
||||
// fancy than just a dumb interpreter.
|
||||
|
|
@ -53,8 +55,11 @@ public:
|
|||
struct Extern {
|
||||
ExternAddress address;
|
||||
};
|
||||
struct Exception {
|
||||
ExceptionAddress address;
|
||||
};
|
||||
|
||||
using RefType = Variant<Null, Func, Extern>;
|
||||
using RefType = Variant<Null, Func, Extern, Exception>;
|
||||
explicit Reference(RefType ref)
|
||||
: m_ref(move(ref))
|
||||
{
|
||||
|
|
@ -90,6 +95,10 @@ public:
|
|||
// ref.null externref
|
||||
m_value = u128(0, 3);
|
||||
break;
|
||||
case ValueType::ExceptionReference:
|
||||
// ref.null exnref
|
||||
m_value = u128(0, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,10 +145,14 @@ public:
|
|||
// 1: externref
|
||||
// 2: null funcref
|
||||
// 3: null externref
|
||||
// 4: null exnref
|
||||
// 5: exnref
|
||||
ref.ref().visit(
|
||||
[&](Reference::Func const& func) { m_value = u128(bit_cast<u64>(func.address), bit_cast<u64>(func.source_module.ptr())); },
|
||||
[&](Reference::Extern const& func) { m_value = u128(bit_cast<u64>(func.address), 1); },
|
||||
[&](Reference::Null const& null) { m_value = u128(0, null.type.kind() == ValueType::Kind::FunctionReference ? 2 : 3); });
|
||||
[&](Reference::Null const& null) { m_value = u128(0, null.type.kind() == ValueType::Kind::FunctionReference ? 2 : null.type.kind() == ValueType::Kind::ExceptionReference ? 4
|
||||
: 3); },
|
||||
[&](Reference::Exception const& exn) { m_value = u128(bit_cast<u64>(exn.address), 5); });
|
||||
}
|
||||
|
||||
template<SameAs<u128> T>
|
||||
|
|
@ -184,6 +197,10 @@ public:
|
|||
return Reference { Reference::Null { ValueType(ValueType::Kind::FunctionReference) } };
|
||||
case 3:
|
||||
return Reference { Reference::Null { ValueType(ValueType::Kind::ExternReference) } };
|
||||
case 4:
|
||||
return Reference { Reference::Null { ValueType(ValueType::Kind::ExceptionReference) } };
|
||||
case 5:
|
||||
return Reference { Reference::Exception { bit_cast<ExceptionAddress>(m_value.low()) } };
|
||||
}
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
|
|
@ -273,7 +290,7 @@ struct InstantiationError {
|
|||
InstantiationErrorSource source { InstantiationErrorSource::Linking };
|
||||
};
|
||||
|
||||
using ExternValue = Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress>;
|
||||
using ExternValue = Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, TagAddress>;
|
||||
|
||||
class ExportInstance {
|
||||
public:
|
||||
|
|
@ -296,13 +313,16 @@ public:
|
|||
explicit ModuleInstance(
|
||||
Vector<FunctionType> types, Vector<FunctionAddress> function_addresses, Vector<TableAddress> table_addresses,
|
||||
Vector<MemoryAddress> memory_addresses, Vector<GlobalAddress> global_addresses, Vector<DataAddress> data_addresses,
|
||||
Vector<TagAddress> tag_addresses, Vector<TagType> tag_types,
|
||||
Vector<ExportInstance> exports)
|
||||
: m_types(move(types))
|
||||
, m_tag_types(move(tag_types))
|
||||
, m_functions(move(function_addresses))
|
||||
, m_tables(move(table_addresses))
|
||||
, m_memories(move(memory_addresses))
|
||||
, m_globals(move(global_addresses))
|
||||
, m_datas(move(data_addresses))
|
||||
, m_tags(move(tag_addresses))
|
||||
, m_exports(move(exports))
|
||||
{
|
||||
}
|
||||
|
|
@ -317,6 +337,8 @@ public:
|
|||
auto& elements() const { return m_elements; }
|
||||
auto& datas() const { return m_datas; }
|
||||
auto& exports() const { return m_exports; }
|
||||
auto& tags() const { return m_tags; }
|
||||
auto& tag_types() const { return m_tag_types; }
|
||||
|
||||
auto& types() { return m_types; }
|
||||
auto& functions() { return m_functions; }
|
||||
|
|
@ -326,15 +348,19 @@ public:
|
|||
auto& elements() { return m_elements; }
|
||||
auto& datas() { return m_datas; }
|
||||
auto& exports() { return m_exports; }
|
||||
auto& tags() { return m_tags; }
|
||||
auto& tag_types() { return m_tag_types; }
|
||||
|
||||
private:
|
||||
Vector<FunctionType> m_types;
|
||||
Vector<TagType> m_tag_types;
|
||||
Vector<FunctionAddress> m_functions;
|
||||
Vector<TableAddress> m_tables;
|
||||
Vector<MemoryAddress> m_memories;
|
||||
Vector<GlobalAddress> m_globals;
|
||||
Vector<ElementAddress> m_elements;
|
||||
Vector<DataAddress> m_datas;
|
||||
Vector<TagAddress> m_tags;
|
||||
Vector<ExportInstance> m_exports;
|
||||
};
|
||||
|
||||
|
|
@ -552,6 +578,38 @@ private:
|
|||
Vector<Reference> m_references;
|
||||
};
|
||||
|
||||
class TagInstance {
|
||||
public:
|
||||
TagInstance(FunctionType const& type, TagType::Flags flags)
|
||||
: m_type(type)
|
||||
, m_flags(flags)
|
||||
{
|
||||
}
|
||||
|
||||
auto& type() const { return m_type; }
|
||||
auto flags() const { return m_flags; }
|
||||
|
||||
private:
|
||||
FunctionType m_type;
|
||||
TagType::Flags m_flags;
|
||||
};
|
||||
|
||||
class ExceptionInstance {
|
||||
public:
|
||||
explicit ExceptionInstance(TagInstance const& type, Vector<Value> params)
|
||||
: m_type(type)
|
||||
, m_params(move(params))
|
||||
{
|
||||
}
|
||||
|
||||
auto& type() const { return m_type; }
|
||||
auto& params() const { return m_params; }
|
||||
|
||||
private:
|
||||
TagInstance m_type;
|
||||
Vector<Value> m_params;
|
||||
};
|
||||
|
||||
class WASM_API Store {
|
||||
public:
|
||||
Store() = default;
|
||||
|
|
@ -563,6 +621,8 @@ public:
|
|||
Optional<DataAddress> allocate_data(Vector<u8>);
|
||||
Optional<GlobalAddress> allocate(GlobalType const&, Value);
|
||||
Optional<ElementAddress> allocate(ValueType const&, Vector<Reference>);
|
||||
Optional<TagAddress> allocate(FunctionType const&, TagType::Flags);
|
||||
Optional<ExceptionAddress> allocate(TagInstance const&, Vector<Value>);
|
||||
|
||||
Module const* get_module_for(FunctionAddress);
|
||||
FunctionInstance* get(FunctionAddress);
|
||||
|
|
@ -571,6 +631,8 @@ public:
|
|||
GlobalInstance* get(GlobalAddress);
|
||||
DataInstance* get(DataAddress);
|
||||
ElementInstance* get(ElementAddress);
|
||||
TagInstance* get(TagAddress);
|
||||
ExceptionInstance* get(ExceptionAddress);
|
||||
|
||||
MemoryInstance* unsafe_get(MemoryAddress address) { return &m_memories.data()[address.value()]; }
|
||||
|
||||
|
|
@ -581,6 +643,8 @@ private:
|
|||
Vector<GlobalInstance> m_globals;
|
||||
Vector<ElementInstance> m_elements;
|
||||
Vector<DataInstance> m_datas;
|
||||
Vector<TagInstance> m_tags;
|
||||
Vector<ExceptionInstance> m_exceptions;
|
||||
};
|
||||
|
||||
class Label {
|
||||
|
|
|
|||
|
|
@ -3754,6 +3754,36 @@ HANDLE_INSTRUCTION(i32x4_relaxed_dot_i8x16_i7x16_add_s)
|
|||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(throw_ref)
|
||||
{
|
||||
interpreter.set_trap("Not Implemented: Proposal 'Exception-handling'"sv);
|
||||
return Outcome::Return;
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(throw_)
|
||||
{
|
||||
{
|
||||
auto tag_address = configuration.frame().module().tags()[instruction->arguments().get<TagIndex>().value()];
|
||||
auto& tag_instance = *configuration.store().get(tag_address);
|
||||
auto& type = tag_instance.type();
|
||||
auto values = Vector<Value>(configuration.value_stack().span().slice_from_end(type.parameters().size()));
|
||||
configuration.value_stack().shrink(configuration.value_stack().size() - type.parameters().size());
|
||||
auto exception_address = configuration.store().allocate(tag_instance, move(values));
|
||||
if (!exception_address.has_value()) {
|
||||
interpreter.set_trap("Out of memory"sv);
|
||||
return Outcome::Return;
|
||||
}
|
||||
configuration.value_stack().append(Value(Reference { Reference::Exception { *exception_address } }));
|
||||
}
|
||||
TAILCALL return InstructionHandler<Instructions::throw_ref.value()>::operator()<HasDynamicInsnLimit, Continue>(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
HANDLE_INSTRUCTION(try_table)
|
||||
{
|
||||
interpreter.set_trap("Not Implemented: Proposal 'Exception-handling'"sv);
|
||||
return Outcome::Return;
|
||||
}
|
||||
|
||||
template<u64 opcode, bool HasDynamicInsnLimit, typename Continue, typename... Args>
|
||||
constexpr static auto handle_instruction(Args&&... a)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ ErrorOr<void, ValidationError> Validator::validate(Module& module)
|
|||
m_globals_without_internal_globals.append(type);
|
||||
m_context.globals.append(type);
|
||||
return {};
|
||||
},
|
||||
[&](TagType const&) -> ErrorOr<void, ValidationError> {
|
||||
m_context.tags.append(import_.description().get<TagType>());
|
||||
return {};
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -89,6 +93,10 @@ ErrorOr<void, ValidationError> Validator::validate(Module& module)
|
|||
|
||||
m_context.datas.resize(module.data_section().data().size());
|
||||
|
||||
m_context.tags.ensure_capacity(m_context.tags.size() + module.tag_section().tags().size());
|
||||
for (auto& tag : module.tag_section().tags())
|
||||
m_context.tags.append(TagType(tag.type(), tag.flags()));
|
||||
|
||||
// We need to build the set of declared functions to check that `ref.func` uses a specific set of predetermined functions, found in:
|
||||
// - Element initializer expressions
|
||||
// - Global initializer expressions
|
||||
|
|
@ -277,6 +285,17 @@ ErrorOr<void, ValidationError> Validator::validate(MemoryType const& type)
|
|||
return validate(type.limits(), 1 << 16);
|
||||
}
|
||||
|
||||
ErrorOr<void, ValidationError> Validator::validate(Wasm::TagType const& tag_type)
|
||||
{
|
||||
// The function type t1^n -> t2^m must be valid
|
||||
TRY(validate(tag_type.type()));
|
||||
auto& type = m_context.types[tag_type.type().value()];
|
||||
// The type sequence t2^m must be empty
|
||||
if (!type.results().is_empty())
|
||||
return Errors::invalid("TagType"sv);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<FunctionType, ValidationError> Validator::validate(BlockType const& type)
|
||||
{
|
||||
if (type.kind() == BlockType::Index) {
|
||||
|
|
@ -2030,6 +2049,85 @@ VALIDATE_INSTRUCTION(if_)
|
|||
return {};
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/exception-handling/core/valid/instructions.html#xref-syntax-instructions-syntax-instr-control-mathsf-throw-x
|
||||
VALIDATE_INSTRUCTION(throw_)
|
||||
{
|
||||
auto tag_index = instruction.arguments().get<TagIndex>();
|
||||
TRY(validate(tag_index));
|
||||
|
||||
auto tag_type = m_context.tags[tag_index.value()];
|
||||
auto& type = m_context.types[tag_type.type().value()];
|
||||
if (!type.results().is_empty())
|
||||
return Errors::invalid("throw type"sv, "empty"sv, type.results());
|
||||
|
||||
for (auto const& parameter : type.parameters().in_reverse())
|
||||
TRY(stack.take(parameter));
|
||||
|
||||
m_frames.last().unreachable = true;
|
||||
stack.resize(m_frames.last().initial_size);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/exception-handling/core/valid/instructions.html#xref-syntax-instructions-syntax-instr-control-mathsf-throw-ref
|
||||
VALIDATE_INSTRUCTION(throw_ref)
|
||||
{
|
||||
TRY(stack.take<ValueType::ExceptionReference>());
|
||||
m_frames.last().unreachable = true;
|
||||
stack.resize(m_frames.last().initial_size);
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://webassembly.github.io/exception-handling/core/valid/instructions.html#xref-syntax-instructions-syntax-instr-control-mathsf-try-table-xref-syntax-instructions-syntax-blocktype-mathit-blocktype-xref-syntax-instructions-syntax-catch-mathit-catch-ast-xref-syntax-instructions-syntax-instr-mathit-instr-ast-xref-syntax-instructions-syntax-instr-control-mathsf-end
|
||||
// https://webassembly.github.io/exception-handling/core/valid/instructions.html#xref-syntax-instructions-syntax-instr-control-mathsf-catch-x-l
|
||||
// https://webassembly.github.io/exception-handling/core/valid/instructions.html#xref-syntax-instructions-syntax-instr-control-mathsf-catch-ref-x-l
|
||||
// https://webassembly.github.io/exception-handling/core/valid/instructions.html#xref-syntax-instructions-syntax-instr-control-mathsf-catch-all-l
|
||||
// https://webassembly.github.io/exception-handling/core/valid/instructions.html#xref-syntax-instructions-syntax-instr-control-mathsf-catch-all-ref-l
|
||||
VALIDATE_INSTRUCTION(try_table)
|
||||
{
|
||||
auto& args = instruction.arguments().get<Instruction::TryTableArgs>();
|
||||
auto block_type = TRY(validate(args.try_.block_type));
|
||||
for (auto& catch_ : args.catches) {
|
||||
auto label = catch_.target_label();
|
||||
TRY(validate(label));
|
||||
auto& target_label_type = m_frames[(m_frames.size() - 1) - label.value()].labels();
|
||||
|
||||
if (auto tag = catch_.matching_tag_index(); tag.has_value()) {
|
||||
TRY(validate(tag.value()));
|
||||
auto tag_type = m_context.tags[tag->value()];
|
||||
auto& type = m_context.types[tag_type.type().value()];
|
||||
if (!type.results().is_empty())
|
||||
return Errors::invalid("catch type"sv, "empty"sv, type.results());
|
||||
|
||||
Span<ValueType const> parameters_to_check = type.parameters().span();
|
||||
if (catch_.is_ref()) {
|
||||
// catch_ref x l
|
||||
auto& parameters = type.parameters();
|
||||
if (parameters.is_empty() || parameters.last().kind() != ValueType::ExceptionReference)
|
||||
return Errors::invalid("catch_ref type"sv, "[..., exnref]"sv, parameters);
|
||||
parameters_to_check = parameters_to_check.slice(0, parameters.size() - 1);
|
||||
} else {
|
||||
// catch x l
|
||||
// (noop here)
|
||||
}
|
||||
|
||||
if (parameters_to_check != target_label_type.span())
|
||||
return Errors::non_conforming_types("catch"sv, parameters_to_check, target_label_type.span());
|
||||
} else {
|
||||
if (catch_.is_ref()) {
|
||||
// catch_all_ref l
|
||||
if (target_label_type.size() != 1 || target_label_type[0].kind() != ValueType::ExceptionReference)
|
||||
return Errors::invalid("catch_all_ref type"sv, "[exnref]"sv, target_label_type);
|
||||
} else {
|
||||
// catch_all l
|
||||
if (!target_label_type.is_empty())
|
||||
return Errors::invalid("catch_all type"sv, "empty"sv, target_label_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
VALIDATE_INSTRUCTION(br)
|
||||
{
|
||||
auto label = instruction.arguments().get<LabelIndex>();
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ struct Context {
|
|||
COWVector<ValueType> elements;
|
||||
COWVector<bool> datas;
|
||||
COWVector<ValueType> locals;
|
||||
COWVector<TagType> tags;
|
||||
Optional<u32> data_count;
|
||||
RefPtr<RefRBTree> references { make_ref_counted<RefRBTree>() };
|
||||
size_t imported_function_count { 0 };
|
||||
|
|
@ -68,6 +69,7 @@ public:
|
|||
ErrorOr<void, ValidationError> validate(MemorySection const&);
|
||||
ErrorOr<void, ValidationError> validate(TableSection const&);
|
||||
ErrorOr<void, ValidationError> validate(CodeSection const&);
|
||||
ErrorOr<void, ValidationError> validate(TagSection const&);
|
||||
ErrorOr<void, ValidationError> validate(FunctionSection const&) { return {}; }
|
||||
ErrorOr<void, ValidationError> validate(DataCountSection const&) { return {}; }
|
||||
ErrorOr<void, ValidationError> validate(TypeSection const&) { return {}; }
|
||||
|
|
@ -136,6 +138,13 @@ public:
|
|||
return Errors::invalid("TableIndex"sv);
|
||||
}
|
||||
|
||||
ErrorOr<void, ValidationError> validate(TagIndex index) const
|
||||
{
|
||||
if (index.value() < m_context.tags.size())
|
||||
return {};
|
||||
return Errors::invalid("TagIndex"sv);
|
||||
}
|
||||
|
||||
enum class FrameKind {
|
||||
Block,
|
||||
Loop,
|
||||
|
|
@ -293,6 +302,7 @@ public:
|
|||
ErrorOr<void, ValidationError> validate(TableType const&);
|
||||
ErrorOr<void, ValidationError> validate(MemoryType const&);
|
||||
ErrorOr<void, ValidationError> validate(GlobalType const&) { return {}; }
|
||||
ErrorOr<void, ValidationError> validate(TagType const&);
|
||||
|
||||
// Proposal 'memory64'
|
||||
ErrorOr<void, ValidationError> take_memory_address(Stack& stack, MemoryType const& memory, Instruction::MemoryArgument const& arg)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ static constexpr auto extern_function_tag = 0x00;
|
|||
static constexpr auto extern_table_tag = 0x01;
|
||||
static constexpr auto extern_memory_tag = 0x02;
|
||||
static constexpr auto extern_global_tag = 0x03;
|
||||
static constexpr auto extern_tag_tag = 0x04; // Proposal "exception-handling"
|
||||
|
||||
static constexpr auto page_size = 64 * KiB;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ namespace Instructions {
|
|||
M(loop, 0x03, 0, -1) \
|
||||
M(if_, 0x04, 1, -1) \
|
||||
M(structured_else, 0x05, -1, -1) \
|
||||
M(throw_, 0x08, -1, -1) \
|
||||
M(throw_ref, 0x0a, 1, -1) \
|
||||
M(structured_end, 0x0b, -1, -1) \
|
||||
M(br, 0x0c, 0, -1) \
|
||||
M(br_if, 0x0d, 1, -1) \
|
||||
|
|
@ -34,6 +36,7 @@ namespace Instructions {
|
|||
M(drop, 0x1a, 1, 0) \
|
||||
M(select, 0x1b, 3, 1) \
|
||||
M(select_typed, 0x1c, 3, 1) \
|
||||
M(try_table, 0x1f, 0, 0) \
|
||||
M(local_get, 0x20, 0, 1) \
|
||||
M(local_set, 0x21, 1, 0) \
|
||||
M(local_tee, 0x22, 1, 1) \
|
||||
|
|
|
|||
|
|
@ -202,6 +202,16 @@ ParseResult<GlobalType> GlobalType::parse(ConstrainedStream& stream)
|
|||
return GlobalType { type_result, mutable_ == 0x01 };
|
||||
}
|
||||
|
||||
ParseResult<TagType> TagType::parse(ConstrainedStream& stream)
|
||||
{
|
||||
ScopeLogger<WASM_BINPARSER_DEBUG> logger("TagType"sv);
|
||||
auto flags = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
|
||||
if (flags != 0)
|
||||
return ParseError::InvalidTag;
|
||||
auto index = TRY(GenericIndexParser<TypeIndex>::parse(stream));
|
||||
return TagType { index, static_cast<TagType::Flags>(flags) };
|
||||
}
|
||||
|
||||
ParseResult<BlockType> BlockType::parse(ConstrainedStream& stream)
|
||||
{
|
||||
ScopeLogger<WASM_BINPARSER_DEBUG> logger("BlockType"sv);
|
||||
|
|
@ -231,6 +241,38 @@ ParseResult<BlockType> BlockType::parse(ConstrainedStream& stream)
|
|||
return BlockType { TypeIndex(index_value) };
|
||||
}
|
||||
|
||||
ParseResult<Catch> Catch::parse(ConstrainedStream& stream)
|
||||
{
|
||||
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Catch"sv);
|
||||
auto kind = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
|
||||
switch (kind) {
|
||||
case 0: {
|
||||
// catch x l
|
||||
auto tag_index = TRY(GenericIndexParser<TagIndex>::parse(stream));
|
||||
auto label_index = TRY(GenericIndexParser<LabelIndex>::parse(stream));
|
||||
return Catch { false, tag_index, label_index };
|
||||
}
|
||||
case 1: {
|
||||
// catch_ref x l
|
||||
auto tag_index = TRY(GenericIndexParser<TagIndex>::parse(stream));
|
||||
auto label_index = TRY(GenericIndexParser<LabelIndex>::parse(stream));
|
||||
return Catch { true, tag_index, label_index };
|
||||
}
|
||||
case 2: {
|
||||
// catch_all l
|
||||
auto label_index = TRY(GenericIndexParser<LabelIndex>::parse(stream));
|
||||
return Catch { false, {}, label_index };
|
||||
}
|
||||
case 3: {
|
||||
// catch_all_ref l
|
||||
auto label_index = TRY(GenericIndexParser<LabelIndex>::parse(stream));
|
||||
return Catch { true, {}, label_index };
|
||||
}
|
||||
default:
|
||||
return ParseError::InvalidTag;
|
||||
}
|
||||
}
|
||||
|
||||
ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
|
||||
{
|
||||
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Instruction"sv);
|
||||
|
|
@ -249,6 +291,19 @@ ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
|
|||
opcode, StructuredInstructionArgs { block_type, {}, {} }
|
||||
};
|
||||
}
|
||||
case Instructions::try_table.value(): {
|
||||
// try_table block_type (catch*) (instruction*) end
|
||||
auto block_type = TRY(BlockType::parse(stream));
|
||||
auto catch_types = TRY(parse_vector<Catch>(stream));
|
||||
auto structured_args = StructuredInstructionArgs { block_type, {}, {} };
|
||||
return Instruction {
|
||||
opcode, TryTableArgs { move(structured_args), move(catch_types) }
|
||||
};
|
||||
}
|
||||
case Instructions::throw_.value(): {
|
||||
auto tag_index = TRY(GenericIndexParser<TagIndex>::parse(stream));
|
||||
return Instruction { opcode, tag_index };
|
||||
}
|
||||
case Instructions::br.value():
|
||||
case Instructions::br_if.value(): {
|
||||
// branches with a single label immediate
|
||||
|
|
@ -384,6 +439,7 @@ ParseResult<Instruction> Instruction::parse(ConstrainedStream& stream)
|
|||
auto index = TRY(GenericIndexParser<FunctionIndex>::parse(stream));
|
||||
return Instruction { opcode, index };
|
||||
}
|
||||
case Instructions::throw_ref.value():
|
||||
case Instructions::structured_end.value():
|
||||
case Instructions::structured_else.value():
|
||||
case Instructions::ref_is_null.value():
|
||||
|
|
@ -944,6 +1000,8 @@ ParseResult<ImportSection::Import> ImportSection::Import::parse(ConstrainedStrea
|
|||
return parse_with_type<MemoryType>(stream, module, name);
|
||||
case Constants::extern_global_tag:
|
||||
return parse_with_type<GlobalType>(stream, module, name);
|
||||
case Constants::extern_tag_tag:
|
||||
return parse_with_type<TagType>(stream, module, name);
|
||||
default:
|
||||
return ParseError::InvalidTag;
|
||||
}
|
||||
|
|
@ -1012,6 +1070,7 @@ ParseResult<Expression> Expression::parse(ConstrainedStream& stream, Optional<si
|
|||
case Instructions::block.value():
|
||||
case Instructions::loop.value():
|
||||
case Instructions::if_.value():
|
||||
case Instructions::try_table.value():
|
||||
stack.append(ip);
|
||||
break;
|
||||
case Instructions::structured_end.value(): {
|
||||
|
|
@ -1019,10 +1078,16 @@ ParseResult<Expression> Expression::parse(ConstrainedStream& stream, Optional<si
|
|||
instructions.empend(Instructions::synthetic_end_expression); // Synthetic noop to mark the end of the expression.
|
||||
return Expression { move(instructions) };
|
||||
}
|
||||
auto entry = stack.take_last();
|
||||
auto& args = instructions[entry.value()].arguments().get<Instruction::StructuredInstructionArgs>();
|
||||
// Patch the end_ip of the last structured instruction
|
||||
args.end_ip = ip + (args.else_ip.has_value() ? 1 : 0);
|
||||
auto entry = stack.take_last();
|
||||
instructions[entry.value()].arguments().visit(
|
||||
[&](Instruction::StructuredInstructionArgs& args) {
|
||||
args.end_ip = ip + (args.else_ip.has_value() ? 1 : 0);
|
||||
},
|
||||
[&](Instruction::TryTableArgs& args) {
|
||||
args.try_.end_ip = ip + 1;
|
||||
},
|
||||
[](auto&) { VERIFY_NOT_REACHED(); });
|
||||
break;
|
||||
}
|
||||
case Instructions::structured_else.value(): {
|
||||
|
|
@ -1075,6 +1140,8 @@ ParseResult<ExportSection::Export> ExportSection::Export::parse(ConstrainedStrea
|
|||
return Export { name, ExportDesc { MemoryIndex { index } } };
|
||||
case Constants::extern_global_tag:
|
||||
return Export { name, ExportDesc { GlobalIndex { index } } };
|
||||
case Constants::extern_tag_tag:
|
||||
return Export { name, ExportDesc { TagIndex { index } } };
|
||||
default:
|
||||
return ParseError::InvalidTag;
|
||||
}
|
||||
|
|
@ -1256,6 +1323,25 @@ ParseResult<DataCountSection> DataCountSection::parse(ConstrainedStream& stream)
|
|||
return DataCountSection { value };
|
||||
}
|
||||
|
||||
ParseResult<TagSection> TagSection::parse(ConstrainedStream& stream)
|
||||
{
|
||||
ScopeLogger<WASM_BINPARSER_DEBUG> logger("TagSection"sv);
|
||||
// https://webassembly.github.io/exception-handling/core/binary/modules.html#binary-tagsec
|
||||
auto tags = TRY(parse_vector<Tag>(stream));
|
||||
return TagSection { move(tags) };
|
||||
}
|
||||
|
||||
ParseResult<TagSection::Tag> TagSection::Tag::parse(ConstrainedStream& stream)
|
||||
{
|
||||
// https://webassembly.github.io/exception-handling/core/binary/modules.html#binary-tagsec
|
||||
ScopeLogger<WASM_BINPARSER_DEBUG> logger("Tag"sv);
|
||||
auto flag = TRY_READ(stream, u8, ParseError::ExpectedKindTag);
|
||||
if (flag != 0)
|
||||
return ParseError::InvalidTag; // currently the only valid flag is 0
|
||||
auto type_index = TRY(GenericIndexParser<TypeIndex>::parse(stream));
|
||||
return TagSection::Tag { type_index, static_cast<TagSection::Tag::Flags>(flag) };
|
||||
}
|
||||
|
||||
ParseResult<SectionId> SectionId::parse(Stream& stream)
|
||||
{
|
||||
u8 id = TRY_READ(stream, u8, ParseError::ExpectedIndex);
|
||||
|
|
@ -1286,6 +1372,8 @@ ParseResult<SectionId> SectionId::parse(Stream& stream)
|
|||
return SectionId(SectionIdKind::Data);
|
||||
case 0x0c:
|
||||
return SectionId(SectionIdKind::DataCount);
|
||||
case 0x0d:
|
||||
return SectionId(SectionIdKind::Tag);
|
||||
default:
|
||||
return ParseError::InvalidIndex;
|
||||
}
|
||||
|
|
@ -1357,14 +1445,15 @@ ParseResult<NonnullRefPtr<Module>> Module::parse(Stream& stream)
|
|||
case SectionId::SectionIdKind::DataCount:
|
||||
module.data_count_section() = TRY(DataCountSection::parse(section_stream));
|
||||
break;
|
||||
case SectionId::SectionIdKind::Tag:
|
||||
module.tag_section() = TRY(TagSection::parse(section_stream));
|
||||
break;
|
||||
default:
|
||||
return ParseError::InvalidIndex;
|
||||
}
|
||||
if (section_id.kind() != SectionId::SectionIdKind::Custom) {
|
||||
if (section_id.kind() < last_section_id)
|
||||
return ParseError::SectionOutOfOrder;
|
||||
last_section_id = section_id.kind();
|
||||
}
|
||||
if (!section_id.can_appear_after(last_section_id))
|
||||
return ParseError::SectionOutOfOrder;
|
||||
last_section_id = section_id.kind();
|
||||
if (section_stream.remaining() != 0)
|
||||
return ParseError::SectionSizeMismatch;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,7 +252,8 @@ void Printer::print(Wasm::ExportSection::Export const& entry)
|
|||
[this](FunctionIndex const& index) { print("(function index {})\n", index.value()); },
|
||||
[this](TableIndex const& index) { print("(table index {})\n", index.value()); },
|
||||
[this](MemoryIndex const& index) { print("(memory index {})\n", index.value()); },
|
||||
[this](GlobalIndex const& index) { print("(global index {})\n", index.value()); });
|
||||
[this](GlobalIndex const& index) { print("(global index {})\n", index.value()); },
|
||||
[this](TagIndex const& index) { print("(tag index {})\n", index.value()); });
|
||||
}
|
||||
print_indent();
|
||||
print(")\n");
|
||||
|
|
@ -398,6 +399,18 @@ void Printer::print(Wasm::GlobalType const& type)
|
|||
print(")\n");
|
||||
}
|
||||
|
||||
void Printer::print(Wasm::TagType const& type)
|
||||
{
|
||||
print_indent();
|
||||
print("(type tag\n");
|
||||
{
|
||||
TemporaryChange change { m_indent, m_indent + 1 };
|
||||
print(type.type());
|
||||
}
|
||||
print_indent();
|
||||
print(")\n");
|
||||
}
|
||||
|
||||
void Printer::print(Wasm::ImportSection const& section)
|
||||
{
|
||||
if (section.imports().is_empty())
|
||||
|
|
@ -432,6 +445,35 @@ void Printer::print(Wasm::ImportSection::Import const& import)
|
|||
print(")\n");
|
||||
}
|
||||
|
||||
void Printer::print(Wasm::TagSection const& section)
|
||||
{
|
||||
// (section tag\n[ ](tag type)*)
|
||||
if (section.tags().is_empty())
|
||||
return;
|
||||
|
||||
print_indent();
|
||||
print("(section tag\n");
|
||||
{
|
||||
TemporaryChange change { m_indent, m_indent + 1 };
|
||||
for (auto& tag : section.tags())
|
||||
print(tag);
|
||||
}
|
||||
print_indent();
|
||||
print(")\n");
|
||||
}
|
||||
|
||||
void Printer::print(Wasm::TagSection::Tag const& tag)
|
||||
{
|
||||
print_indent();
|
||||
print("(tag (type index {}))\n", tag.type().value());
|
||||
}
|
||||
|
||||
void Printer::print(Wasm::TypeIndex const& index)
|
||||
{
|
||||
print_indent();
|
||||
print("(type index {})\n", index.value());
|
||||
}
|
||||
|
||||
void Printer::print(Wasm::Instruction const& instruction)
|
||||
{
|
||||
print_indent();
|
||||
|
|
@ -472,6 +514,16 @@ void Printer::print(Wasm::Instruction const& instruction)
|
|||
print_indent();
|
||||
print("(else {}) (end {}))", args.else_ip.has_value() ? ByteString::number(args.else_ip->value()) : "(none)", args.end_ip.value());
|
||||
},
|
||||
[&](Instruction::TryTableArgs const& args) {
|
||||
print("(try_table ");
|
||||
print(args.try_.block_type);
|
||||
print(" (catches\n");
|
||||
TemporaryChange change { m_indent, m_indent + 1 };
|
||||
for (auto& catch_ : args.catches)
|
||||
print(catch_);
|
||||
print_indent();
|
||||
print(") (end {}))", args.try_.end_ip.value());
|
||||
},
|
||||
[&](Instruction::TableBranchArgs const& args) {
|
||||
print("(table_branch");
|
||||
for (auto& label : args.labels)
|
||||
|
|
@ -491,6 +543,24 @@ void Printer::print(Wasm::Instruction const& instruction)
|
|||
}
|
||||
}
|
||||
|
||||
void Printer::print(Catch const& catch_)
|
||||
{
|
||||
print_indent();
|
||||
StringBuilder name_builder;
|
||||
name_builder.appendff("catch{}{}", catch_.matching_tag_index().has_value() ? ""sv : "_all"sv, catch_.is_ref() ? "_ref"sv : ""sv);
|
||||
print("({} ", name_builder.string_view());
|
||||
if (auto index = catch_.matching_tag_index(); index.has_value())
|
||||
print("(tag index {})", index.value());
|
||||
print("\n");
|
||||
{
|
||||
TemporaryChange change { m_indent, m_indent + 1 };
|
||||
print_indent();
|
||||
print("(label index {})\n", catch_.target_label().value());
|
||||
}
|
||||
print_indent();
|
||||
print(")\n");
|
||||
}
|
||||
|
||||
void Printer::print(Wasm::Limits const& limits)
|
||||
{
|
||||
print_indent();
|
||||
|
|
@ -566,6 +636,7 @@ void Printer::print(Wasm::Module const& module)
|
|||
print(module.function_section());
|
||||
print(module.table_section());
|
||||
print(module.memory_section());
|
||||
print(module.tag_section());
|
||||
print(module.global_section());
|
||||
print(module.export_section());
|
||||
print(module.start_section());
|
||||
|
|
@ -682,9 +753,11 @@ void Printer::print(Wasm::Value const& value, Wasm::ValueType const& type)
|
|||
break;
|
||||
case ValueType::FunctionReference:
|
||||
case ValueType::ExternReference:
|
||||
case ValueType::ExceptionReference:
|
||||
print("addr({})",
|
||||
value.to<Reference>().ref().visit(
|
||||
[](Wasm::Reference::Null const&) { return ByteString("null"); },
|
||||
[](Wasm::Reference::Exception const&) { return ByteString("exception"); },
|
||||
[](auto const& ref) { return ByteString::number(ref.address.value()); }));
|
||||
break;
|
||||
}
|
||||
|
|
@ -705,6 +778,7 @@ void Printer::print(Wasm::Reference const& value)
|
|||
"addr({})\n",
|
||||
value.ref().visit(
|
||||
[](Wasm::Reference::Null const&) { return ByteString("null"); },
|
||||
[](Wasm::Reference::Exception const&) { return ByteString("exception"); },
|
||||
[](auto const& ref) { return ByteString::number(ref.address.value()); }));
|
||||
}
|
||||
|
||||
|
|
@ -716,6 +790,9 @@ HashMap<Wasm::OpCode, ByteString> Wasm::Names::instruction_names {
|
|||
{ Instructions::block, "block" },
|
||||
{ Instructions::loop, "loop" },
|
||||
{ Instructions::if_, "if" },
|
||||
{ Instructions::try_table, "try_table" },
|
||||
{ Instructions::throw_, "throw" },
|
||||
{ Instructions::throw_ref, "throw_ref" },
|
||||
{ Instructions::br, "br" },
|
||||
{ Instructions::br_if, "br.if" },
|
||||
{ Instructions::br_table, "br.table" },
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ struct WASM_API Printer {
|
|||
void print(Wasm::GlobalType const&);
|
||||
void print(Wasm::ImportSection const&);
|
||||
void print(Wasm::ImportSection::Import const&);
|
||||
void print(Wasm::TagSection const&);
|
||||
void print(Wasm::TagSection::Tag const&);
|
||||
void print(Wasm::Instruction const&);
|
||||
void print(Wasm::Limits const&);
|
||||
void print(Wasm::Locals const&);
|
||||
|
|
@ -59,8 +61,11 @@ struct WASM_API Printer {
|
|||
void print(Wasm::TableType const&);
|
||||
void print(Wasm::TypeSection const&);
|
||||
void print(Wasm::ValueType const&);
|
||||
void print(Wasm::TagType const&);
|
||||
void print(Wasm::Value const&);
|
||||
void print(Wasm::Value const&, ValueType const&);
|
||||
void print(Wasm::Catch const&);
|
||||
void print(Wasm::TypeIndex const&);
|
||||
|
||||
private:
|
||||
void print_indent();
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, FunctionIndex);
|
|||
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, TableIndex);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, ElementIndex);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, MemoryIndex);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, TagIndex);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, LocalIndex);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, GlobalIndex);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(size_t, LabelIndex);
|
||||
|
|
@ -167,6 +168,7 @@ public:
|
|||
V128,
|
||||
FunctionReference,
|
||||
ExternReference,
|
||||
ExceptionReference,
|
||||
};
|
||||
|
||||
explicit ValueType(Kind kind)
|
||||
|
|
@ -200,6 +202,8 @@ public:
|
|||
return "funcref";
|
||||
case ExternReference:
|
||||
return "externref";
|
||||
case ExceptionReference:
|
||||
return "exnref";
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
|
@ -336,6 +340,29 @@ private:
|
|||
bool m_is_mutable { false };
|
||||
};
|
||||
|
||||
// https://webassembly.github.io/exception-handling/core/binary/types.html#tag-types
|
||||
class TagType {
|
||||
public:
|
||||
enum Flags : u8 {
|
||||
None = 0
|
||||
};
|
||||
|
||||
TagType(TypeIndex type, Flags flags)
|
||||
: m_flags(flags)
|
||||
, m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
auto& type() const { return m_type; }
|
||||
auto flags() const { return m_flags; }
|
||||
|
||||
static ParseResult<TagType> parse(ConstrainedStream& stream);
|
||||
|
||||
private:
|
||||
Flags m_flags { None };
|
||||
TypeIndex m_type;
|
||||
};
|
||||
|
||||
// https://webassembly.github.io/spec/core/bikeshed/#binary-blocktype
|
||||
class BlockType {
|
||||
public:
|
||||
|
|
@ -386,6 +413,35 @@ private:
|
|||
};
|
||||
};
|
||||
|
||||
// Proposal "exception-handling"
|
||||
// https://webassembly.github.io/exception-handling/core/binary/instructions.html
|
||||
class Catch {
|
||||
public:
|
||||
Catch(bool ref, TagIndex index, LabelIndex label) // catch[_ref] x l
|
||||
: m_matching_tag_index(index)
|
||||
, m_target_label(label)
|
||||
, m_is_ref(ref)
|
||||
{
|
||||
}
|
||||
|
||||
explicit Catch(bool ref, LabelIndex label) // catch_all[_ref] l
|
||||
: m_target_label(label)
|
||||
, m_is_ref(ref)
|
||||
{
|
||||
}
|
||||
|
||||
auto& matching_tag_index() const { return m_matching_tag_index; }
|
||||
auto& target_label() const { return m_target_label; }
|
||||
auto is_ref() const { return m_is_ref; }
|
||||
|
||||
static ParseResult<Catch> parse(ConstrainedStream& stream);
|
||||
|
||||
private:
|
||||
Optional<TagIndex> m_matching_tag_index; // None for catch_all
|
||||
LabelIndex m_target_label;
|
||||
bool m_is_ref = false; // true if catch*_ref
|
||||
};
|
||||
|
||||
// https://webassembly.github.io/spec/core/bikeshed/#binary-instr
|
||||
// https://webassembly.github.io/spec/core/bikeshed/#reference-instructions%E2%91%A6
|
||||
// https://webassembly.github.io/spec/core/bikeshed/#parametric-instructions%E2%91%A6
|
||||
|
|
@ -457,6 +513,12 @@ public:
|
|||
MemoryIndex memory_index;
|
||||
};
|
||||
|
||||
// Proposal "exception-handling"
|
||||
struct TryTableArgs {
|
||||
StructuredInstructionArgs try_; // "else" unused.
|
||||
Vector<Catch> catches;
|
||||
};
|
||||
|
||||
struct ShuffleArgument {
|
||||
explicit ShuffleArgument(u8 (&lanes)[16])
|
||||
: lanes {
|
||||
|
|
@ -509,6 +571,7 @@ private:
|
|||
ElementIndex,
|
||||
FunctionIndex,
|
||||
GlobalIndex,
|
||||
TagIndex,
|
||||
IndirectCallArgs,
|
||||
LabelIndex,
|
||||
LaneIndex,
|
||||
|
|
@ -524,6 +587,7 @@ private:
|
|||
TableElementArgs,
|
||||
TableIndex,
|
||||
TableTableArgs,
|
||||
TryTableArgs,
|
||||
ValueType,
|
||||
Vector<ValueType>,
|
||||
double,
|
||||
|
|
@ -568,6 +632,17 @@ struct CompiledInstructions {
|
|||
bool direct = false; // true if all dispatches contain handler_ptr, otherwise false and all contain instruction_opcode.
|
||||
};
|
||||
|
||||
template<Enum auto... Vs>
|
||||
consteval auto as_ordered()
|
||||
{
|
||||
using Type = CommonType<decltype(to_underlying(Vs))...>;
|
||||
Array<Type, sizeof...(Vs)> result;
|
||||
[&]<Type... Is>(IntegerSequence<Type, Is...>) {
|
||||
(void)((result[to_underlying(Vs)] = Is), ...);
|
||||
}(MakeIntegerSequence<Type, static_cast<Type>(sizeof...(Vs))>());
|
||||
return result;
|
||||
}
|
||||
|
||||
struct SectionId {
|
||||
public:
|
||||
enum class SectionIdKind : u8 {
|
||||
|
|
@ -584,14 +659,44 @@ public:
|
|||
DataCount,
|
||||
Code,
|
||||
Data,
|
||||
Tag,
|
||||
};
|
||||
|
||||
constexpr inline static auto section_order = as_ordered<
|
||||
SectionIdKind::Type,
|
||||
SectionIdKind::Import,
|
||||
SectionIdKind::Function,
|
||||
SectionIdKind::Table,
|
||||
SectionIdKind::Memory,
|
||||
SectionIdKind::Tag,
|
||||
SectionIdKind::Global,
|
||||
SectionIdKind::Export,
|
||||
SectionIdKind::Start,
|
||||
SectionIdKind::Element,
|
||||
SectionIdKind::DataCount,
|
||||
SectionIdKind::Code,
|
||||
SectionIdKind::Data,
|
||||
SectionIdKind::Custom>();
|
||||
|
||||
explicit SectionId(SectionIdKind kind)
|
||||
: m_kind(kind)
|
||||
{
|
||||
}
|
||||
|
||||
SectionIdKind kind() const { return m_kind; }
|
||||
bool can_appear_after(SectionIdKind other) const
|
||||
{
|
||||
if (kind() == SectionIdKind::Custom || other == SectionIdKind::Custom)
|
||||
return true;
|
||||
|
||||
auto index = section_order[to_underlying(kind())];
|
||||
auto other_index = section_order[to_underlying(other)];
|
||||
return index >= other_index;
|
||||
}
|
||||
|
||||
SectionIdKind kind() const
|
||||
{
|
||||
return m_kind;
|
||||
}
|
||||
|
||||
static ParseResult<SectionId> parse(Stream& stream);
|
||||
|
||||
|
|
@ -638,7 +743,7 @@ class ImportSection {
|
|||
public:
|
||||
class Import {
|
||||
public:
|
||||
using ImportDesc = Variant<TypeIndex, TableType, MemoryType, GlobalType, FunctionType>;
|
||||
using ImportDesc = Variant<TypeIndex, TableType, MemoryType, GlobalType, FunctionType, TagType>;
|
||||
Import(ByteString module, ByteString name, ImportDesc description)
|
||||
: m_module(move(module))
|
||||
, m_name(move(name))
|
||||
|
|
@ -826,7 +931,7 @@ private:
|
|||
|
||||
class ExportSection {
|
||||
private:
|
||||
using ExportDesc = Variant<FunctionIndex, TableIndex, MemoryIndex, GlobalIndex>;
|
||||
using ExportDesc = Variant<FunctionIndex, TableIndex, MemoryIndex, GlobalIndex, TagIndex>;
|
||||
|
||||
public:
|
||||
class Export {
|
||||
|
|
@ -1059,6 +1164,43 @@ private:
|
|||
Optional<u32> m_count;
|
||||
};
|
||||
|
||||
class TagSection {
|
||||
public:
|
||||
class Tag {
|
||||
public:
|
||||
using Flags = TagType::Flags;
|
||||
|
||||
Tag(TypeIndex type, Flags flags)
|
||||
: m_type(type)
|
||||
, m_flags(flags)
|
||||
{
|
||||
}
|
||||
|
||||
auto type() const { return m_type; }
|
||||
auto flags() const { return m_flags; }
|
||||
|
||||
static ParseResult<Tag> parse(ConstrainedStream& stream);
|
||||
|
||||
private:
|
||||
TypeIndex m_type;
|
||||
Flags m_flags { Flags::None };
|
||||
};
|
||||
|
||||
TagSection() = default;
|
||||
|
||||
explicit TagSection(Vector<Tag> tags)
|
||||
: m_tags(move(tags))
|
||||
{
|
||||
}
|
||||
|
||||
auto& tags() const { return m_tags; }
|
||||
|
||||
static ParseResult<TagSection> parse(ConstrainedStream& stream);
|
||||
|
||||
private:
|
||||
Vector<Tag> m_tags;
|
||||
};
|
||||
|
||||
class WASM_API Module : public RefCounted<Module>
|
||||
, public Weakable<Module> {
|
||||
public:
|
||||
|
|
@ -1099,6 +1241,8 @@ public:
|
|||
auto& data_section() const { return m_data_section; }
|
||||
auto& data_count_section() { return m_data_count_section; }
|
||||
auto& data_count_section() const { return m_data_count_section; }
|
||||
auto& tag_section() { return m_tag_section; }
|
||||
auto& tag_section() const { return m_tag_section; }
|
||||
|
||||
void set_validation_status(ValidationStatus status, Badge<Validator>) { set_validation_status(status); }
|
||||
ValidationStatus validation_status() const { return m_validation_status; }
|
||||
|
|
@ -1123,6 +1267,7 @@ private:
|
|||
CodeSection m_code_section;
|
||||
DataSection m_data_section;
|
||||
DataCountSection m_data_count_section;
|
||||
TagSection m_tag_section;
|
||||
|
||||
ValidationStatus m_validation_status { ValidationStatus::Unchecked };
|
||||
Optional<ByteString> m_validation_error;
|
||||
|
|
|
|||
|
|
@ -617,6 +617,8 @@ JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM& vm, JS::Value va
|
|||
cache.add_extern_value(extern_addr, value);
|
||||
return Wasm::Value { Wasm::Reference { Wasm::Reference::Extern { extern_addr } } };
|
||||
}
|
||||
case Wasm::ValueType::ExceptionReference:
|
||||
return Wasm::Value(Wasm::ValueType { Wasm::ValueType::Kind::ExceptionReference });
|
||||
case Wasm::ValueType::V128:
|
||||
return vm.throw_completion<JS::TypeError>("Cannot convert a vector value to a javascript value"sv);
|
||||
}
|
||||
|
|
@ -636,6 +638,8 @@ Wasm::Value default_webassembly_value(JS::VM& vm, Wasm::ValueType type)
|
|||
return Wasm::Value(type);
|
||||
case Wasm::ValueType::ExternReference:
|
||||
return MUST(to_webassembly_value(vm, JS::js_undefined(), type));
|
||||
case Wasm::ValueType::ExceptionReference:
|
||||
return Wasm::Value(type);
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
|
@ -680,6 +684,7 @@ JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value, Wasm::ValueType type)
|
|||
return value.release_value();
|
||||
}
|
||||
case Wasm::ValueType::V128:
|
||||
case Wasm::ValueType::ExceptionReference:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
|
|
|
|||
|
|
@ -314,9 +314,11 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::get_export)
|
|||
}
|
||||
case Wasm::ValueType::FunctionReference:
|
||||
case Wasm::ValueType::ExternReference:
|
||||
case Wasm::ValueType::ExceptionReference:
|
||||
auto ref = global->value().to<Wasm::Reference>();
|
||||
return ref.ref().visit(
|
||||
[&](Wasm::Reference::Null const&) -> JS::Value { return JS::js_null(); },
|
||||
[](Wasm::Reference::Exception const&) -> JS::Value { return JS::js_undefined(); },
|
||||
[&](auto const& ref) -> JS::Value { return JS::Value(static_cast<double>(ref.address.value())); });
|
||||
}
|
||||
}
|
||||
|
|
@ -398,6 +400,12 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
|
|||
}
|
||||
arguments.append(Wasm::Value(Wasm::Reference { Wasm::Reference::Extern { static_cast<u64>(double_value) } }));
|
||||
break;
|
||||
case Wasm::ValueType::Kind::ExceptionReference:
|
||||
if (argument.is_null())
|
||||
arguments.append(Wasm::Value(Wasm::Reference { Wasm::Reference::Null { Wasm::ValueType(Wasm::ValueType::Kind::ExceptionReference) } }));
|
||||
else
|
||||
return vm.throw_completion<JS::TypeError>("Exception references are not supported"sv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -431,7 +439,9 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke)
|
|||
}
|
||||
case Wasm::ValueType::FunctionReference:
|
||||
case Wasm::ValueType::ExternReference:
|
||||
return (value.to<Wasm::Reference>()).ref().visit([&](Wasm::Reference::Null) { return JS::js_null(); }, [&](auto const& ref) { return JS::Value(static_cast<double>(ref.address.value())); });
|
||||
return (value.to<Wasm::Reference>()).ref().visit([&](Wasm::Reference::Null) { return JS::js_null(); }, [&](Wasm::Reference::Exception) { return JS::Value(); }, [&](auto const& ref) { return JS::Value(static_cast<double>(ref.address.value())); });
|
||||
case Wasm::ValueType::ExceptionReference:
|
||||
return JS::js_null();
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ static ErrorOr<ParsedValue> parse_value(StringView spec)
|
|||
case Wasm::ValueType::V128:
|
||||
case Wasm::ValueType::FunctionReference:
|
||||
case Wasm::ValueType::ExternReference:
|
||||
case Wasm::ValueType::ExceptionReference:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
last_value = parsed.value.value();
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user