LibWasm: Clean up module sections API

Remove `for_each_section_of_type` in favor of making the module's
sections defined as distinct fields. This means it is no longer possible
to have two of the same section (which is invalid in WebAssembly, for
anything other than custom sections).
This commit is contained in:
Diego Frias 2024-07-29 19:56:00 -07:00 committed by Ali Mohammad Pur
parent 9f24176cac
commit 23cfee2205
Notes: github-actions[bot] 2024-08-01 10:20:10 +00:00
7 changed files with 469 additions and 541 deletions

View file

@ -146,75 +146,67 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
auto main_module_instance_pointer = make<ModuleInstance>();
auto& main_module_instance = *main_module_instance_pointer;
Optional<InstantiationResult> instantiation_result;
module.for_each_section_of_type<TypeSection>([&](TypeSection const& section) {
main_module_instance.types() = section.types();
});
main_module_instance.types() = module.type_section().types();
Vector<Value> global_values;
Vector<Vector<Reference>> elements;
ModuleInstance auxiliary_instance;
module.for_each_section_of_type<ImportSection>([&](ImportSection const& section) {
for (auto [i, import_] : enumerate(section.imports())) {
auto extern_ = externs.at(i);
auto invalid = import_.description().visit(
[&](MemoryType const& mem_type) -> Optional<ByteString> {
if (!extern_.has<MemoryAddress>())
return "Expected memory import"sv;
auto other_mem_type = m_store.get(extern_.get<MemoryAddress>())->type();
if (other_mem_type.limits().is_subset_of(mem_type.limits()))
return {};
return ByteString::formatted("Memory import and extern do not match: {}-{} vs {}-{}", mem_type.limits().min(), mem_type.limits().max(), other_mem_type.limits().min(), other_mem_type.limits().max());
},
[&](TableType const& table_type) -> Optional<ByteString> {
if (!extern_.has<TableAddress>())
return "Expected table import"sv;
auto other_table_type = m_store.get(extern_.get<TableAddress>())->type();
if (table_type.element_type() == other_table_type.element_type()
&& other_table_type.limits().is_subset_of(table_type.limits()))
return {};
return ByteString::formatted("Table import and extern do not match: {}-{} vs {}-{}", table_type.limits().min(), table_type.limits().max(), other_table_type.limits().min(), other_table_type.limits().max());
},
[&](GlobalType const& global_type) -> Optional<ByteString> {
if (!extern_.has<GlobalAddress>())
return "Expected global import"sv;
auto other_global_type = m_store.get(extern_.get<GlobalAddress>())->type();
if (global_type.type() == other_global_type.type()
&& global_type.is_mutable() == other_global_type.is_mutable())
return {};
return "Global import and extern do not match"sv;
},
[&](FunctionType const& type) -> Optional<ByteString> {
if (!extern_.has<FunctionAddress>())
return "Expected function import"sv;
auto other_type = m_store.get(extern_.get<FunctionAddress>())->visit([&](WasmFunction const& wasm_func) { return wasm_func.type(); }, [&](HostFunction const& host_func) { return host_func.type(); });
if (type.results() != other_type.results())
return ByteString::formatted("Function import and extern do not match, results: {} vs {}", type.results(), other_type.results());
if (type.parameters() != other_type.parameters())
return ByteString::formatted("Function import and extern do not match, parameters: {} vs {}", type.parameters(), other_type.parameters());
for (auto [i, import_] : enumerate(module.import_section().imports())) {
auto extern_ = externs.at(i);
auto invalid = import_.description().visit(
[&](MemoryType const& mem_type) -> Optional<ByteString> {
if (!extern_.has<MemoryAddress>())
return "Expected memory import"sv;
auto other_mem_type = m_store.get(extern_.get<MemoryAddress>())->type();
if (other_mem_type.limits().is_subset_of(mem_type.limits()))
return {};
},
[&](TypeIndex type_index) -> Optional<ByteString> {
if (!extern_.has<FunctionAddress>())
return "Expected function import"sv;
auto other_type = m_store.get(extern_.get<FunctionAddress>())->visit([&](WasmFunction const& wasm_func) { return wasm_func.type(); }, [&](HostFunction const& host_func) { return host_func.type(); });
auto& type = module.type(type_index);
if (type.results() != other_type.results())
return ByteString::formatted("Function import and extern do not match, results: {} vs {}", type.results(), other_type.results());
if (type.parameters() != other_type.parameters())
return ByteString::formatted("Function import and extern do not match, parameters: {} vs {}", type.parameters(), other_type.parameters());
return ByteString::formatted("Memory import and extern do not match: {}-{} vs {}-{}", mem_type.limits().min(), mem_type.limits().max(), other_mem_type.limits().min(), other_mem_type.limits().max());
},
[&](TableType const& table_type) -> Optional<ByteString> {
if (!extern_.has<TableAddress>())
return "Expected table import"sv;
auto other_table_type = m_store.get(extern_.get<TableAddress>())->type();
if (table_type.element_type() == other_table_type.element_type()
&& other_table_type.limits().is_subset_of(table_type.limits()))
return {};
});
if (invalid.has_value())
instantiation_result = InstantiationError { ByteString::formatted("{}::{}: {}", import_.module(), import_.name(), invalid.release_value()) };
}
});
if (instantiation_result.has_value())
return instantiation_result.release_value();
return ByteString::formatted("Table import and extern do not match: {}-{} vs {}-{}", table_type.limits().min(), table_type.limits().max(), other_table_type.limits().min(), other_table_type.limits().max());
},
[&](GlobalType const& global_type) -> Optional<ByteString> {
if (!extern_.has<GlobalAddress>())
return "Expected global import"sv;
auto other_global_type = m_store.get(extern_.get<GlobalAddress>())->type();
if (global_type.type() == other_global_type.type()
&& global_type.is_mutable() == other_global_type.is_mutable())
return {};
return "Global import and extern do not match"sv;
},
[&](FunctionType const& type) -> Optional<ByteString> {
if (!extern_.has<FunctionAddress>())
return "Expected function import"sv;
auto other_type = m_store.get(extern_.get<FunctionAddress>())->visit([&](WasmFunction const& wasm_func) { return wasm_func.type(); }, [&](HostFunction const& host_func) { return host_func.type(); });
if (type.results() != other_type.results())
return ByteString::formatted("Function import and extern do not match, results: {} vs {}", type.results(), other_type.results());
if (type.parameters() != other_type.parameters())
return ByteString::formatted("Function import and extern do not match, parameters: {} vs {}", type.parameters(), other_type.parameters());
return {};
},
[&](TypeIndex type_index) -> Optional<ByteString> {
if (!extern_.has<FunctionAddress>())
return "Expected function import"sv;
auto other_type = m_store.get(extern_.get<FunctionAddress>())->visit([&](WasmFunction const& wasm_func) { return wasm_func.type(); }, [&](HostFunction const& host_func) { return host_func.type(); });
auto& type = module.type_section().types()[type_index.value()];
if (type.results() != other_type.results())
return ByteString::formatted("Function import and extern do not match, results: {} vs {}", type.results(), other_type.results());
if (type.parameters() != other_type.parameters())
return ByteString::formatted("Function import and extern do not match, parameters: {} vs {}", type.parameters(), other_type.parameters());
return {};
});
if (invalid.has_value())
return InstantiationError { ByteString::formatted("{}::{}: {}", import_.module(), import_.name(), invalid.release_value()) };
}
for (auto& entry : externs) {
if (auto* ptr = entry.get_pointer<GlobalAddress>())
@ -223,239 +215,179 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
auxiliary_instance.functions().append(*ptr);
}
FunctionSection const* function_section { nullptr };
module.for_each_section_of_type<FunctionSection>([&](FunctionSection const& section) { function_section = &section; });
Vector<FunctionAddress> module_functions;
if (function_section)
module_functions.ensure_capacity(function_section->types().size());
module_functions.ensure_capacity(module.function_section().types().size());
module.for_each_section_of_type<CodeSection>([&](auto& code_section) {
size_t i = 0;
for (auto& code : code_section.functions()) {
auto type_index = function_section->types()[i];
auto address = m_store.allocate(main_module_instance, code, type_index);
VERIFY(address.has_value());
auxiliary_instance.functions().append(*address);
module_functions.append(*address);
++i;
}
});
size_t i = 0;
for (auto& code : module.code_section().functions()) {
auto type_index = module.function_section().types()[i];
auto address = m_store.allocate(main_module_instance, code, type_index);
VERIFY(address.has_value());
auxiliary_instance.functions().append(*address);
module_functions.append(*address);
++i;
}
BytecodeInterpreter interpreter(m_stack_info);
module.for_each_section_of_type<GlobalSection>([&](auto& global_section) {
for (auto& entry : global_section.entries()) {
for (auto& entry : module.global_section().entries()) {
Configuration config { m_store };
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
auxiliary_instance,
Vector<Value> {},
entry.expression(),
1,
});
auto result = config.execute(interpreter).assert_wasm_result();
if (result.is_trap())
return InstantiationError { ByteString::formatted("Global value construction trapped: {}", result.trap().reason) };
global_values.append(result.values().first());
}
if (auto result = allocate_all_initial_phase(module, main_module_instance, externs, global_values, module_functions); result.has_value())
return result.release_value();
for (auto& segment : module.element_section().segments()) {
Vector<Reference> references;
for (auto& entry : segment.init) {
Configuration config { m_store };
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
auxiliary_instance,
Vector<Value> {},
entry.expression(),
1,
entry,
entry.instructions().size(),
});
auto result = config.execute(interpreter).assert_wasm_result();
if (result.is_trap())
instantiation_result = InstantiationError { ByteString::formatted("Global value construction trapped: {}", result.trap().reason) };
else
global_values.append(result.values().first());
return InstantiationError { ByteString::formatted("Element construction trapped: {}", result.trap().reason) };
for (auto& value : result.values()) {
auto reference = value.to<Reference>();
references.append(reference.release_value());
}
}
});
elements.append(move(references));
}
if (instantiation_result.has_value())
return instantiation_result.release_value();
if (auto result = allocate_all_initial_phase(module, main_module_instance, externs, global_values, module_functions); result.has_value())
if (auto result = allocate_all_final_phase(module, main_module_instance, elements); result.has_value())
return result.release_value();
module.for_each_section_of_type<ElementSection>([&](ElementSection const& section) {
for (auto& segment : section.segments()) {
Vector<Reference> references;
for (auto& entry : segment.init) {
size_t index = 0;
for (auto& segment : module.element_section().segments()) {
auto current_index = index;
++index;
auto active_ptr = segment.mode.get_pointer<ElementSection::Active>();
auto elem_instance = m_store.get(main_module_instance.elements()[current_index]);
if (!active_ptr) {
if (segment.mode.has<ElementSection::Declarative>())
*elem_instance = ElementInstance(elem_instance->type(), {});
continue;
}
Configuration config { m_store };
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
auxiliary_instance,
Vector<Value> {},
active_ptr->expression,
1,
});
auto result = config.execute(interpreter).assert_wasm_result();
if (result.is_trap())
return InstantiationError { ByteString::formatted("Element section initialisation trapped: {}", result.trap().reason) };
auto d = result.values().first().to<i32>();
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" };
if (!table_instance || !elem_instance)
return InstantiationError { "Invalid element referenced by active element segment" };
Checked<size_t> total_size = elem_instance->references().size();
total_size.saturating_add(d.value());
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;
// Drop element
*m_store.get(main_module_instance.elements()[current_index]) = ElementInstance(elem_instance->type(), {});
}
for (auto& segment : module.data_section().data()) {
Optional<InstantiationError> result = segment.value().visit(
[&](DataSection::Data::Active const& data) -> Optional<InstantiationError> {
Configuration config { m_store };
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
auxiliary_instance,
Vector<Value> {},
entry,
entry.instructions().size(),
data.offset,
1,
});
auto result = config.execute(interpreter).assert_wasm_result();
if (result.is_trap()) {
instantiation_result = InstantiationError { ByteString::formatted("Element construction trapped: {}", result.trap().reason) };
return IterationDecision::Continue;
if (result.is_trap())
return InstantiationError { ByteString::formatted("Data section initialisation trapped: {}", result.trap().reason) };
size_t offset = TRY(result.values().first().value().visit(
[&](auto const& value) { return ErrorOr<size_t, InstantiationError> { value }; },
[&](u128 const&) { return ErrorOr<size_t, InstantiationError> { InstantiationError { "Data segment offset returned a vector type"sv } }; },
[&](Reference const&) { return ErrorOr<size_t, InstantiationError> { InstantiationError { "Data segment offset returned a reference type"sv } }; }));
if (main_module_instance.memories().size() <= data.index.value()) {
return InstantiationError {
ByteString::formatted("Data segment referenced out-of-bounds memory ({}) of max {} entries",
data.index.value(), main_module_instance.memories().size())
};
}
for (auto& value : result.values()) {
if (!value.type().is_reference()) {
instantiation_result = InstantiationError { "Evaluated element entry is not a reference" };
return IterationDecision::Continue;
}
auto reference = value.to<Reference>();
if (!reference.has_value()) {
instantiation_result = InstantiationError { "Evaluated element entry does not contain a reference" };
return IterationDecision::Continue;
}
// FIXME: type-check the reference.
references.append(reference.release_value());
auto maybe_data_address = m_store.allocate_data(data.init);
if (!maybe_data_address.has_value()) {
return InstantiationError { "Failed to allocate a data instance for an active data segment"sv };
}
}
elements.append(move(references));
}
main_module_instance.datas().append(*maybe_data_address);
return IterationDecision::Continue;
});
if (instantiation_result.has_value())
return instantiation_result.release_value();
if (auto result = allocate_all_final_phase(module, main_module_instance, elements); result.has_value())
return result.release_value();
module.for_each_section_of_type<ElementSection>([&](ElementSection const& section) {
size_t index = 0;
for (auto& segment : section.segments()) {
auto current_index = index;
++index;
auto active_ptr = segment.mode.get_pointer<ElementSection::Active>();
auto elem_instance = m_store.get(main_module_instance.elements()[current_index]);
if (!active_ptr) {
if (segment.mode.has<ElementSection::Declarative>())
*elem_instance = ElementInstance(elem_instance->type(), {});
continue;
}
Configuration config { m_store };
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
auxiliary_instance,
Vector<Value> {},
active_ptr->expression,
1,
});
auto result = config.execute(interpreter).assert_wasm_result();
if (result.is_trap()) {
instantiation_result = InstantiationError { ByteString::formatted("Element section initialisation trapped: {}", result.trap().reason) };
return IterationDecision::Break;
}
auto d = result.values().first().to<i32>();
if (!d.has_value()) {
instantiation_result = InstantiationError { "Element section initialisation returned invalid table initial offset" };
return IterationDecision::Break;
}
auto table_instance = m_store.get(main_module_instance.tables()[active_ptr->index.value()]);
if (current_index >= main_module_instance.elements().size()) {
instantiation_result = InstantiationError { "Invalid element referenced by active element segment" };
return IterationDecision::Break;
}
if (!table_instance || !elem_instance) {
instantiation_result = InstantiationError { "Invalid element referenced by active element segment" };
return IterationDecision::Break;
}
Checked<size_t> total_size = elem_instance->references().size();
total_size.saturating_add(d.value());
if (total_size.value() > table_instance->elements().size()) {
instantiation_result = InstantiationError { "Table instantiation out of bounds" };
return IterationDecision::Break;
}
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;
}
// Drop element
*m_store.get(main_module_instance.elements()[current_index]) = ElementInstance(elem_instance->type(), {});
}
return IterationDecision::Continue;
});
if (instantiation_result.has_value())
return instantiation_result.release_value();
module.for_each_section_of_type<DataSection>([&](DataSection const& data_section) {
for (auto& segment : data_section.data()) {
segment.value().visit(
[&](DataSection::Data::Active const& data) {
Configuration config { m_store };
if (m_should_limit_instruction_count)
config.enable_instruction_count_limit();
config.set_frame(Frame {
auxiliary_instance,
Vector<Value> {},
data.offset,
1,
});
auto result = config.execute(interpreter).assert_wasm_result();
if (result.is_trap()) {
instantiation_result = InstantiationError { ByteString::formatted("Data section initialisation trapped: {}", result.trap().reason) };
return;
}
size_t offset = 0;
result.values().first().value().visit(
[&](auto const& value) { offset = value; },
[&](u128 const&) { instantiation_result = InstantiationError { "Data segment offset returned a vector type"sv }; },
[&](Reference const&) { instantiation_result = InstantiationError { "Data segment offset returned a reference"sv }; });
if (instantiation_result.has_value() && instantiation_result->is_error())
return;
if (main_module_instance.memories().size() <= data.index.value()) {
instantiation_result = InstantiationError {
ByteString::formatted("Data segment referenced out-of-bounds memory ({}) of max {} entries",
data.index.value(), main_module_instance.memories().size())
};
return;
}
auto maybe_data_address = m_store.allocate_data(data.init);
if (!maybe_data_address.has_value()) {
instantiation_result = InstantiationError { "Failed to allocate a data instance for an active data segment"sv };
return;
}
main_module_instance.datas().append(*maybe_data_address);
auto address = main_module_instance.memories()[data.index.value()];
auto instance = m_store.get(address);
Checked<size_t> checked_offset = data.init.size();
checked_offset += offset;
if (checked_offset.has_overflow() || checked_offset > instance->size()) {
instantiation_result = InstantiationError {
ByteString::formatted("Data segment attempted to write to out-of-bounds memory ({}) in memory of size {}",
offset, instance->size())
};
return;
}
if (data.init.is_empty())
return;
auto address = main_module_instance.memories()[data.index.value()];
auto instance = m_store.get(address);
Checked<size_t> checked_offset = data.init.size();
checked_offset += offset;
if (checked_offset.has_overflow() || checked_offset > instance->size()) {
return InstantiationError {
ByteString::formatted("Data segment attempted to write to out-of-bounds memory ({}) in memory of size {}",
offset, instance->size())
};
}
if (!data.init.is_empty())
instance->data().overwrite(offset, data.init.data(), data.init.size());
},
[&](DataSection::Data::Passive const& passive) {
auto maybe_data_address = m_store.allocate_data(passive.init);
if (!maybe_data_address.has_value()) {
instantiation_result = InstantiationError { "Failed to allocate a data instance for a passive data segment"sv };
return;
}
main_module_instance.datas().append(*maybe_data_address);
});
}
});
return {};
},
[&](DataSection::Data::Passive const& passive) -> Optional<InstantiationError> {
auto maybe_data_address = m_store.allocate_data(passive.init);
if (!maybe_data_address.has_value()) {
return InstantiationError { "Failed to allocate a data instance for a passive data segment"sv };
}
main_module_instance.datas().append(*maybe_data_address);
return {};
});
if (result.has_value())
return result.release_value();
}
module.for_each_section_of_type<StartSection>([&](StartSection const& section) {
if (module.start_section().function().has_value()) {
auto& functions = main_module_instance.functions();
auto index = section.function().index();
auto index = module.start_section().function()->index();
if (functions.size() <= index.value()) {
instantiation_result = InstantiationError { ByteString::formatted("Start section function referenced invalid index {} of max {} entries", index.value(), functions.size()) };
return;
return InstantiationError { ByteString::formatted("Start section function referenced invalid index {} of max {} entries", index.value(), functions.size()) };
}
auto result = invoke(functions[index.value()], {});
if (result.is_trap())
instantiation_result = InstantiationError { ByteString::formatted("Start function trapped: {}", result.trap().reason) };
});
if (instantiation_result.has_value())
return instantiation_result.release_value();
return InstantiationError { ByteString::formatted("Start function trapped: {}", result.trap().reason) };
}
return InstantiationResult { move(main_module_instance_pointer) };
}
@ -476,86 +408,77 @@ Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module
// FIXME: What if this fails?
module.for_each_section_of_type<TableSection>([&](TableSection const& section) {
for (auto& table : section.tables()) {
auto table_address = m_store.allocate(table.type());
VERIFY(table_address.has_value());
module_instance.tables().append(*table_address);
}
});
for (auto& table : module.table_section().tables()) {
auto table_address = m_store.allocate(table.type());
VERIFY(table_address.has_value());
module_instance.tables().append(*table_address);
}
module.for_each_section_of_type<MemorySection>([&](MemorySection const& section) {
for (auto& memory : section.memories()) {
auto memory_address = m_store.allocate(memory.type());
VERIFY(memory_address.has_value());
module_instance.memories().append(*memory_address);
}
});
for (auto& memory : module.memory_section().memories()) {
auto memory_address = m_store.allocate(memory.type());
VERIFY(memory_address.has_value());
module_instance.memories().append(*memory_address);
}
module.for_each_section_of_type<GlobalSection>([&](GlobalSection const& section) {
size_t index = 0;
for (auto& entry : section.entries()) {
auto address = m_store.allocate(entry.type(), move(global_values[index]));
VERIFY(address.has_value());
module_instance.globals().append(*address);
index++;
}
});
module.for_each_section_of_type<ExportSection>([&](ExportSection const& section) {
for (auto& entry : section.entries()) {
Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, Empty> address {};
entry.description().visit(
[&](FunctionIndex const& index) {
if (module_instance.functions().size() > index.value())
address = FunctionAddress { module_instance.functions()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.functions().size());
},
[&](TableIndex const& index) {
if (module_instance.tables().size() > index.value())
address = TableAddress { module_instance.tables()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.tables().size());
},
[&](MemoryIndex const& index) {
if (module_instance.memories().size() > index.value())
address = MemoryAddress { module_instance.memories()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.memories().size());
},
[&](GlobalIndex const& index) {
if (module_instance.globals().size() > index.value())
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());
});
size_t index = 0;
for (auto& entry : module.global_section().entries()) {
auto address = m_store.allocate(entry.type(), move(global_values[index]));
VERIFY(address.has_value());
module_instance.globals().append(*address);
index++;
}
if (address.has<Empty>()) {
result = InstantiationError { "An export could not be resolved" };
continue;
}
module_instance.exports().append(ExportInstance {
entry.name(),
move(address).downcast<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress>(),
for (auto& entry : module.export_section().entries()) {
Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, Empty> address {};
entry.description().visit(
[&](FunctionIndex const& index) {
if (module_instance.functions().size() > index.value())
address = FunctionAddress { module_instance.functions()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.functions().size());
},
[&](TableIndex const& index) {
if (module_instance.tables().size() > index.value())
address = TableAddress { module_instance.tables()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.tables().size());
},
[&](MemoryIndex const& index) {
if (module_instance.memories().size() > index.value())
address = MemoryAddress { module_instance.memories()[index.value()] };
else
dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.memories().size());
},
[&](GlobalIndex const& index) {
if (module_instance.globals().size() > index.value())
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());
});
if (address.has<Empty>()) {
result = InstantiationError { "An export could not be resolved" };
continue;
}
});
module_instance.exports().append(ExportInstance {
entry.name(),
move(address).downcast<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress>(),
});
}
return result;
}
Optional<InstantiationError> AbstractMachine::allocate_all_final_phase(Module const& module, ModuleInstance& module_instance, Vector<Vector<Reference>>& elements)
{
module.for_each_section_of_type<ElementSection>([&](ElementSection const& section) {
size_t index = 0;
for (auto& segment : section.segments()) {
auto address = m_store.allocate(segment.type, move(elements[index]));
VERIFY(address.has_value());
module_instance.elements().append(*address);
index++;
}
});
size_t index = 0;
for (auto& segment : module.element_section().segments()) {
auto address = m_store.allocate(segment.type, move(elements[index]));
VERIFY(address.has_value());
module_instance.elements().append(*address);
index++;
}
return {};
}
@ -642,21 +565,10 @@ void Linker::populate()
if (!m_ordered_imports.is_empty())
return;
// There better be at most one import section!
bool already_seen_an_import_section = false;
m_module.for_each_section_of_type<ImportSection>([&](ImportSection const& section) {
if (already_seen_an_import_section) {
if (!m_error.has_value())
m_error = LinkError {};
m_error->other_errors.append(LinkError::InvalidImportedModule);
return;
}
already_seen_an_import_section = true;
for (auto& import_ : section.imports()) {
m_ordered_imports.append({ import_.module(), import_.name(), import_.description() });
m_unresolved_imports.set(m_ordered_imports.last());
}
});
for (auto& import_ : m_module.import_section().imports()) {
m_ordered_imports.append({ import_.module(), import_.name(), import_.description() });
m_unresolved_imports.set(m_ordered_imports.last());
}
}
}

View file

@ -16,111 +16,79 @@ namespace Wasm {
ErrorOr<void, ValidationError> Validator::validate(Module& module)
{
ErrorOr<void, ValidationError> result {};
// Pre-emptively make invalid. The module will be set to `Valid` at the end
// of validation.
module.set_validation_status(Module::ValidationStatus::Invalid, {});
// Note: The spec performs this after populating the context, but there's no real reason to do so,
// as this has no dependency.
HashTable<StringView> seen_export_names;
module.for_each_section_of_type<ExportSection>([&result, &seen_export_names](ExportSection const& section) {
if (result.is_error())
return;
for (auto& export_ : section.entries()) {
if (seen_export_names.try_set(export_.name()).release_value_but_fixme_should_propagate_errors() != AK::HashSetResult::InsertedNewEntry) {
result = Errors::duplicate_export_name(export_.name());
return;
}
}
});
if (result.is_error()) {
module.set_validation_status(Module::ValidationStatus::Invalid, {});
return result;
}
for (auto& export_ : module.export_section().entries())
if (seen_export_names.try_set(export_.name()).release_value_but_fixme_should_propagate_errors() != AK::HashSetResult::InsertedNewEntry)
return Errors::duplicate_export_name(export_.name());
m_context = {};
module.for_each_section_of_type<TypeSection>([this](TypeSection const& section) {
m_context.types.extend(section.types());
});
m_context.types.extend(module.type_section().types());
m_context.data_count = module.data_count_section().count();
module.for_each_section_of_type<DataCountSection>([this](DataCountSection const& section) {
m_context.data_count = section.count();
});
module.for_each_section_of_type<ImportSection>([&](ImportSection const& section) {
for (auto& import_ : section.imports()) {
import_.description().visit(
[this, &result](TypeIndex const& index) {
if (m_context.types.size() > index.value())
m_context.functions.append(m_context.types[index.value()]);
else
result = Errors::invalid("TypeIndex"sv);
m_context.imported_function_count++;
},
[this](FunctionType const& type) {
m_context.functions.append(type);
m_context.imported_function_count++;
},
[this](TableType const& type) { m_context.tables.append(type); },
[this](MemoryType const& type) { m_context.memories.append(type); },
[this](GlobalType const& type) {
m_globals_without_internal_globals.append(type);
m_context.globals.append(type);
});
}
});
if (result.is_error()) {
module.set_validation_status(Module::ValidationStatus::Invalid, {});
return result;
for (auto& import_ : module.import_section().imports()) {
TRY(import_.description().visit(
[&](TypeIndex const& index) -> ErrorOr<void, ValidationError> {
if (m_context.types.size() > index.value())
m_context.functions.append(m_context.types[index.value()]);
else
return Errors::invalid("TypeIndex"sv);
m_context.imported_function_count++;
return {};
},
[&](FunctionType const& type) -> ErrorOr<void, ValidationError> {
m_context.functions.append(type);
m_context.imported_function_count++;
return {};
},
[&](TableType const& type) -> ErrorOr<void, ValidationError> {
m_context.tables.append(type);
return {};
},
[&](MemoryType const& type) -> ErrorOr<void, ValidationError> {
m_context.memories.append(type);
return {};
},
[&](GlobalType const& type) -> ErrorOr<void, ValidationError> {
m_globals_without_internal_globals.append(type);
m_context.globals.append(type);
return {};
}));
}
CodeSection const* code_section { nullptr };
module.for_each_section_of_type<CodeSection>([&](auto& section) { code_section = &section; });
module.for_each_section_of_type<FunctionSection>([&](FunctionSection const& section) {
if ((!code_section && !section.types().is_empty()) || (code_section && code_section->functions().size() != section.types().size())) {
result = Errors::invalid("FunctionSection"sv);
return;
}
m_context.functions.ensure_capacity(section.types().size() + m_context.functions.size());
for (auto& index : section.types()) {
if (m_context.types.size() > index.value()) {
m_context.functions.append(m_context.types[index.value()]);
} else {
result = Errors::invalid("TypeIndex"sv);
break;
}
}
});
if (result.is_error()) {
module.set_validation_status(Module::ValidationStatus::Invalid, {});
return result;
}
if (module.code_section().functions().size() != module.function_section().types().size())
return Errors::invalid("FunctionSection"sv);
module.for_each_section_of_type<TableSection>([this](TableSection const& section) {
m_context.tables.ensure_capacity(m_context.tables.size() + section.tables().size());
for (auto& table : section.tables())
m_context.tables.append(table.type());
});
m_context.functions.ensure_capacity(module.function_section().types().size() + m_context.functions.size());
for (auto& index : module.function_section().types())
if (m_context.types.size() > index.value())
m_context.functions.append(m_context.types[index.value()]);
else
return Errors::invalid("TypeIndex"sv);
module.for_each_section_of_type<MemorySection>([this](MemorySection const& section) {
m_context.memories.ensure_capacity(m_context.memories.size() + section.memories().size());
for (auto& memory : section.memories())
m_context.memories.append(memory.type());
});
m_context.tables.ensure_capacity(m_context.tables.size() + module.table_section().tables().size());
for (auto& table : module.table_section().tables())
m_context.tables.append(table.type());
module.for_each_section_of_type<GlobalSection>([this](GlobalSection const& section) {
m_context.globals.ensure_capacity(m_context.globals.size() + section.entries().size());
for (auto& global : section.entries())
m_context.globals.append(global.type());
});
module.for_each_section_of_type<ElementSection>([this](ElementSection const& section) {
m_context.elements.ensure_capacity(section.segments().size());
for (auto& segment : section.segments())
m_context.elements.append(segment.type);
});
module.for_each_section_of_type<DataSection>([this](DataSection const& section) {
m_context.datas.resize(section.data().size());
});
m_context.memories.ensure_capacity(m_context.memories.size() + module.memory_section().memories().size());
for (auto& memory : module.memory_section().memories())
m_context.memories.append(memory.type());
m_context.globals.ensure_capacity(m_context.globals.size() + module.global_section().entries().size());
for (auto& global : module.global_section().entries())
m_context.globals.append(global.type());
m_context.elements.ensure_capacity(module.element_section().segments().size());
for (auto& segment : module.element_section().segments())
m_context.elements.append(segment.type);
m_context.datas.resize(module.data_section().data().size());
// 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
@ -134,44 +102,28 @@ ErrorOr<void, ValidationError> Validator::validate(Module& module)
}
}
};
module.for_each_section_of_type<ExportSection>([&](ExportSection const& section) {
for (auto& export_ : section.entries()) {
if (!export_.description().has<FunctionIndex>())
continue;
auto index = export_.description().get<FunctionIndex>();
m_context.references->tree.insert(index.value(), index);
}
});
module.for_each_section_of_type<ElementSection>([&](ElementSection const& section) {
for (auto& segment : section.segments()) {
for (auto& expression : segment.init)
scan_expression_for_function_indices(expression);
}
});
module.for_each_section_of_type<GlobalSection>([&](GlobalSection const& section) {
for (auto& segment : section.entries())
scan_expression_for_function_indices(segment.expression());
});
bool seen_start_section = false;
module.for_each_section_of_type<StartSection>([&](StartSection const&) {
if (seen_start_section)
result = Errors::multiple_start_sections();
seen_start_section = true;
});
if (result.is_error()) {
module.set_validation_status(Module::ValidationStatus::Invalid, {});
return result;
for (auto& export_ : module.export_section().entries()) {
if (!export_.description().has<FunctionIndex>())
continue;
auto index = export_.description().get<FunctionIndex>();
m_context.references->tree.insert(index.value(), index);
}
for (auto& segment : module.element_section().segments()) {
for (auto& expression : segment.init)
scan_expression_for_function_indices(expression);
}
for (auto& segment : module.global_section().entries())
scan_expression_for_function_indices(segment.expression());
for (auto& section : module.sections()) {
section.visit([this, &result](auto& section) {
result = validate(section);
});
if (result.is_error()) {
module.set_validation_status(Module::ValidationStatus::Invalid, {});
return result;
}
}
TRY(validate(module.import_section()));
TRY(validate(module.export_section()));
TRY(validate(module.start_section()));
TRY(validate(module.data_section()));
TRY(validate(module.element_section()));
TRY(validate(module.global_section()));
TRY(validate(module.memory_section()));
TRY(validate(module.table_section()));
TRY(validate(module.code_section()));
module.set_validation_status(Module::ValidationStatus::Valid, {});
return {};
@ -193,8 +145,10 @@ ErrorOr<void, ValidationError> Validator::validate(ExportSection const& section)
ErrorOr<void, ValidationError> Validator::validate(StartSection const& section)
{
TRY(validate(section.function().index()));
FunctionType const& type = m_context.functions[section.function().index().value()];
if (!section.function().has_value())
return {};
TRY(validate(section.function()->index()));
FunctionType const& type = m_context.functions[section.function()->index().value()];
if (!type.parameters().is_empty() || !type.results().is_empty())
return Errors::invalid("start function signature"sv);
return {};

View file

@ -1227,60 +1227,66 @@ ParseResult<Module> Module::parse(Stream& stream)
if (Bytes { buf, 4 } != wasm_version.span())
return with_eof_check(stream, ParseError::InvalidModuleVersion);
Vector<AnySection> sections;
auto last_section_id = CustomSection::section_id;
Module module;
while (!stream.is_eof()) {
auto section_id = TRY_READ(stream, u8, ParseError::ExpectedIndex);
size_t section_size = TRY_READ(stream, LEB128<u32>, ParseError::ExpectedSize);
auto section_stream = ConstrainedStream { MaybeOwned<Stream>(stream), section_size };
if (section_id != CustomSection::section_id && section_id == last_section_id)
return ParseError::DuplicateSection;
switch (section_id) {
case CustomSection::section_id:
sections.append(TRY(CustomSection::parse(section_stream)));
module.custom_sections().append(TRY(CustomSection::parse(section_stream)));
break;
case TypeSection::section_id:
sections.append(TRY(TypeSection::parse(section_stream)));
module.type_section() = TRY(TypeSection::parse(section_stream));
break;
case ImportSection::section_id:
sections.append(TRY(ImportSection::parse(section_stream)));
module.import_section() = TRY(ImportSection::parse(section_stream));
break;
case FunctionSection::section_id:
sections.append(TRY(FunctionSection::parse(section_stream)));
module.function_section() = TRY(FunctionSection::parse(section_stream));
break;
case TableSection::section_id:
sections.append(TRY(TableSection::parse(section_stream)));
module.table_section() = TRY(TableSection::parse(section_stream));
break;
case MemorySection::section_id:
sections.append(TRY(MemorySection::parse(section_stream)));
module.memory_section() = TRY(MemorySection::parse(section_stream));
break;
case GlobalSection::section_id:
sections.append(TRY(GlobalSection::parse(section_stream)));
module.global_section() = TRY(GlobalSection::parse(section_stream));
break;
case ExportSection::section_id:
sections.append(TRY(ExportSection::parse(section_stream)));
module.export_section() = TRY(ExportSection::parse(section_stream));
break;
case StartSection::section_id:
sections.append(TRY(StartSection::parse(section_stream)));
module.start_section() = TRY(StartSection::parse(section_stream));
break;
case ElementSection::section_id:
sections.append(TRY(ElementSection::parse(section_stream)));
module.element_section() = TRY(ElementSection::parse(section_stream));
break;
case CodeSection::section_id:
sections.append(TRY(CodeSection::parse(section_stream)));
module.code_section() = TRY(CodeSection::parse(section_stream));
break;
case DataSection::section_id:
sections.append(TRY(DataSection::parse(section_stream)));
module.data_section() = TRY(DataSection::parse(section_stream));
break;
case DataCountSection::section_id:
sections.append(TRY(DataCountSection::parse(section_stream)));
module.data_count_section() = TRY(DataCountSection::parse(section_stream));
break;
default:
return ParseError::InvalidIndex;
}
if (section_id != CustomSection::section_id)
last_section_id = section_id;
if (section_stream.remaining() != 0)
return ParseError::SectionSizeMismatch;
}
return Module { move(sections) };
return module;
}
ByteString parse_error_to_byte_string(ParseError error)
@ -1326,6 +1332,8 @@ ByteString parse_error_to_byte_string(ParseError error)
return "A parsed string was not valid UTF-8";
case ParseError::UnknownInstruction:
return "A parsed instruction was not known to this parser";
case ParseError::DuplicateSection:
return "Two sections of the same type were encountered";
}
return "Unknown error";
}

View file

@ -64,6 +64,8 @@ void Printer::print(Wasm::BlockType const& type)
void Printer::print(Wasm::CodeSection const& section)
{
if (section.functions().is_empty())
return;
print_indent();
print("(section code\n");
{
@ -97,6 +99,8 @@ void Printer::print(Wasm::CustomSection const& section)
void Printer::print(Wasm::DataCountSection const& section)
{
if (!section.count().has_value())
return;
print_indent();
print("(section data count\n");
if (section.count().has_value()) {
@ -110,6 +114,8 @@ void Printer::print(Wasm::DataCountSection const& section)
void Printer::print(Wasm::DataSection const& section)
{
if (section.data().is_empty())
return;
print_indent();
print("(section data\n");
{
@ -163,6 +169,8 @@ void Printer::print(Wasm::DataSection::Data const& data)
void Printer::print(Wasm::ElementSection const& section)
{
if (section.segments().is_empty())
return;
print_indent();
print("(section element\n");
{
@ -218,6 +226,8 @@ void Printer::print(Wasm::ElementSection::Element const& element)
void Printer::print(Wasm::ExportSection const& section)
{
if (section.entries().is_empty())
return;
print_indent();
print("(section export\n");
{
@ -282,6 +292,8 @@ void Printer::print(Wasm::CodeSection::Func const& func)
void Printer::print(Wasm::FunctionSection const& section)
{
if (section.types().is_empty())
return;
print_indent();
print("(section function\n");
{
@ -329,6 +341,8 @@ void Printer::print(Wasm::FunctionType const& type)
void Printer::print(Wasm::GlobalSection const& section)
{
if (section.entries().is_empty())
return;
print_indent();
print("(section global\n");
{
@ -384,6 +398,8 @@ void Printer::print(Wasm::GlobalType const& type)
void Printer::print(Wasm::ImportSection const& section)
{
if (section.imports().is_empty())
return;
print_indent();
print("(section import\n");
{
@ -493,6 +509,8 @@ void Printer::print(Wasm::Locals const& local)
void Printer::print(Wasm::MemorySection const& section)
{
if (section.memories().is_empty())
return;
print_indent();
print("(section memory\n");
{
@ -534,8 +552,20 @@ void Printer::print(Wasm::Module const& module)
{
TemporaryChange change { m_indent, m_indent + 1 };
print("(module\n");
for (auto& section : module.sections())
section.visit([this](auto const& value) { print(value); });
for (auto& custom_section : module.custom_sections())
print(custom_section);
print(module.type_section());
print(module.import_section());
print(module.function_section());
print(module.table_section());
print(module.memory_section());
print(module.global_section());
print(module.export_section());
print(module.start_section());
print(module.element_section());
print(module.code_section());
print(module.data_section());
print(module.data_count_section());
}
print_indent();
print(")\n");
@ -543,11 +573,13 @@ void Printer::print(Wasm::Module const& module)
void Printer::print(Wasm::StartSection const& section)
{
if (!section.function().has_value())
return;
print_indent();
print("(section start\n");
{
TemporaryChange change { m_indent, m_indent + 1 };
print(section.function());
print(*section.function());
}
print_indent();
print(")\n");
@ -561,6 +593,8 @@ void Printer::print(Wasm::StartSection::StartFunction const& function)
void Printer::print(Wasm::TableSection const& section)
{
if (section.tables().is_empty())
return;
print_indent();
print("(section table\n");
{
@ -601,6 +635,8 @@ void Printer::print(Wasm::TableType const& type)
void Printer::print(Wasm::TypeSection const& section)
{
if (section.types().is_empty())
return;
print_indent();
print("(section type\n");
{

View file

@ -56,6 +56,7 @@ enum class ParseError {
OutOfMemory,
SectionSizeMismatch,
InvalidUtf8,
DuplicateSection,
};
ByteString parse_error_to_byte_string(ParseError);
@ -522,6 +523,8 @@ class TypeSection {
public:
static constexpr u8 section_id = 1;
TypeSection() = default;
explicit TypeSection(Vector<FunctionType> types)
: m_types(move(types))
{
@ -569,6 +572,8 @@ public:
public:
static constexpr u8 section_id = 2;
ImportSection() = default;
explicit ImportSection(Vector<Import> imports)
: m_imports(move(imports))
{
@ -586,6 +591,8 @@ class FunctionSection {
public:
static constexpr u8 section_id = 3;
FunctionSection() = default;
explicit FunctionSection(Vector<TypeIndex> types)
: m_types(move(types))
{
@ -619,6 +626,8 @@ public:
public:
static constexpr u8 section_id = 4;
TableSection() = default;
explicit TableSection(Vector<Table> tables)
: m_tables(move(tables))
{
@ -652,6 +661,8 @@ public:
public:
static constexpr u8 section_id = 5;
MemorySection() = default;
explicit MemorySection(Vector<Memory> memories)
: m_memories(move(memories))
{
@ -703,6 +714,8 @@ public:
public:
static constexpr u8 section_id = 6;
GlobalSection() = default;
explicit GlobalSection(Vector<Global> entries)
: m_entries(move(entries))
{
@ -741,6 +754,8 @@ public:
static constexpr u8 section_id = 7;
ExportSection() = default;
explicit ExportSection(Vector<Export> entries)
: m_entries(move(entries))
{
@ -773,7 +788,9 @@ public:
static constexpr u8 section_id = 8;
explicit StartSection(StartFunction func)
StartSection() = default;
explicit StartSection(Optional<StartFunction> func)
: m_function(move(func))
{
}
@ -783,7 +800,7 @@ public:
static ParseResult<StartSection> parse(Stream& stream);
private:
StartFunction m_function;
Optional<StartFunction> m_function;
};
class ElementSection {
@ -807,6 +824,8 @@ public:
static constexpr u8 section_id = 9;
ElementSection() = default;
explicit ElementSection(Vector<Element> segs)
: m_segments(move(segs))
{
@ -879,6 +898,8 @@ public:
static constexpr u8 section_id = 10;
CodeSection() = default;
explicit CodeSection(Vector<Code> funcs)
: m_functions(move(funcs))
{
@ -921,6 +942,8 @@ public:
static constexpr u8 section_id = 11;
DataSection() = default;
explicit DataSection(Vector<Data> data)
: m_data(move(data))
{
@ -938,6 +961,8 @@ class DataCountSection {
public:
static constexpr u8 section_id = 12;
DataCountSection() = default;
explicit DataCountSection(Optional<u32> count)
: m_count(move(count))
{
@ -959,57 +984,37 @@ public:
Valid,
};
using AnySection = Variant<
CustomSection,
TypeSection,
ImportSection,
FunctionSection,
TableSection,
MemorySection,
GlobalSection,
ExportSection,
StartSection,
ElementSection,
CodeSection,
DataSection,
DataCountSection>;
static constexpr Array<u8, 4> wasm_magic { 0, 'a', 's', 'm' };
static constexpr Array<u8, 4> wasm_version { 1, 0, 0, 0 };
explicit Module(Vector<AnySection> sections)
: m_sections(move(sections))
{
}
Module() = default;
auto& sections() const { return m_sections; }
auto& type(TypeIndex index) const
{
FunctionType const* type = nullptr;
for_each_section_of_type<TypeSection>([&](TypeSection const& section) {
type = &section.types().at(index.value());
});
VERIFY(type != nullptr);
return *type;
}
template<typename T, typename Callback>
void for_each_section_of_type(Callback&& callback) const
{
for (auto& section : m_sections) {
if (auto ptr = section.get_pointer<T>())
callback(*ptr);
}
}
template<typename T, typename Callback>
void for_each_section_of_type(Callback&& callback)
{
for (auto& section : m_sections) {
if (auto ptr = section.get_pointer<T>())
callback(*ptr);
}
}
auto& custom_sections() { return m_custom_sections; }
auto& custom_sections() const { return m_custom_sections; }
auto& type_section() const { return m_type_section; }
auto& type_section() { return m_type_section; }
auto& import_section() const { return m_import_section; }
auto& import_section() { return m_import_section; }
auto& function_section() { return m_function_section; }
auto& function_section() const { return m_function_section; }
auto& table_section() { return m_table_section; }
auto& table_section() const { return m_table_section; }
auto& memory_section() { return m_memory_section; }
auto& memory_section() const { return m_memory_section; }
auto& global_section() { return m_global_section; }
auto& global_section() const { return m_global_section; }
auto& export_section() { return m_export_section; }
auto& export_section() const { return m_export_section; }
auto& start_section() { return m_start_section; }
auto& start_section() const { return m_start_section; }
auto& element_section() { return m_element_section; }
auto& element_section() const { return m_element_section; }
auto& code_section() { return m_code_section; }
auto& code_section() const { return m_code_section; }
auto& data_section() { return m_data_section; }
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; }
void set_validation_status(ValidationStatus status, Badge<Validator>) { set_validation_status(status); }
ValidationStatus validation_status() const { return m_validation_status; }
@ -1021,7 +1026,20 @@ public:
private:
void set_validation_status(ValidationStatus status) { m_validation_status = status; }
Vector<AnySection> m_sections;
Vector<CustomSection> m_custom_sections;
TypeSection m_type_section;
ImportSection m_import_section;
FunctionSection m_function_section;
TableSection m_table_section;
MemorySection m_memory_section;
GlobalSection m_global_section;
ExportSection m_export_section;
StartSection m_start_section;
ElementSection m_element_section;
CodeSection m_code_section;
DataSection m_data_section;
DataCountSection m_data_count_section;
ValidationStatus m_validation_status { ValidationStatus::Unchecked };
Optional<ByteString> m_validation_error;
};

View file

@ -182,7 +182,7 @@ JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS
TRY(import_name.type.visit(
[&](Wasm::TypeIndex index) -> JS::ThrowCompletionOr<void> {
dbgln("Trying to resolve a function {}::{}, type index {}", import_name.module, import_name.name, index.value());
auto& type = module.type(index);
auto& type = module.type_section().types()[index.value()];
// FIXME: IsCallable()
if (!import_.is_function())
return {};

View file

@ -672,7 +672,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
for (auto& entry : linker.unresolved_imports()) {
if (!entry.type.has<Wasm::TypeIndex>())
continue;
auto type = parse_result.value().type(entry.type.get<Wasm::TypeIndex>());
auto type = parse_result.value().type_section().types()[entry.type.get<Wasm::TypeIndex>().value()];
auto address = machine.store().allocate(Wasm::HostFunction(
[name = entry.name, type = type](auto&, auto& arguments) -> Wasm::Result {
StringBuilder argument_builder;