mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 20:57:35 +00:00
LibWasm: Implement a very basic linker
This will simply "link" any given module instances and produce a list of external values that can be used to instantiate a module. Note that this is extremely basic and cannot resolve circular dependencies, and depends on the instance order.
This commit is contained in:
parent
3283c8a495
commit
35b3ae26ed
3 changed files with 206 additions and 15 deletions
|
@ -283,4 +283,86 @@ Result AbstractMachine::invoke(FunctionAddress address, Vector<Value> arguments)
|
|||
return Configuration { m_store }.call(address, move(arguments));
|
||||
}
|
||||
|
||||
void Linker::link(const ModuleInstance& instance)
|
||||
{
|
||||
populate();
|
||||
if (m_unresolved_imports.is_empty())
|
||||
return;
|
||||
|
||||
HashTable<Name> resolved_imports;
|
||||
for (auto& import_ : m_unresolved_imports) {
|
||||
auto it = instance.exports().find_if([&](auto& export_) { return export_.name() == import_.name; });
|
||||
if (!it.is_end()) {
|
||||
resolved_imports.set(import_);
|
||||
m_resolved_imports.set(import_, it->value());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& entry : resolved_imports)
|
||||
m_unresolved_imports.remove(entry);
|
||||
}
|
||||
|
||||
void Linker::link(const HashMap<Linker::Name, ExternValue>& exports)
|
||||
{
|
||||
populate();
|
||||
if (m_unresolved_imports.is_empty())
|
||||
return;
|
||||
|
||||
HashTable<Name> resolved_imports;
|
||||
for (auto& import_ : m_unresolved_imports) {
|
||||
auto export_ = exports.get(import_);
|
||||
if (export_.has_value()) {
|
||||
resolved_imports.set(import_);
|
||||
m_resolved_imports.set(import_, export_.value());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& entry : resolved_imports)
|
||||
m_unresolved_imports.remove(entry);
|
||||
}
|
||||
|
||||
AK::Result<Vector<ExternValue>, LinkError> Linker::finish()
|
||||
{
|
||||
populate();
|
||||
if (!m_unresolved_imports.is_empty()) {
|
||||
if (!m_error.has_value())
|
||||
m_error = LinkError {};
|
||||
for (auto& entry : m_unresolved_imports)
|
||||
m_error->missing_imports.append(entry.name);
|
||||
return *m_error;
|
||||
}
|
||||
|
||||
if (m_error.has_value())
|
||||
return *m_error;
|
||||
|
||||
// Result must be in the same order as the module imports
|
||||
Vector<ExternValue> exports;
|
||||
exports.ensure_capacity(m_ordered_imports.size());
|
||||
for (auto& import_ : m_ordered_imports)
|
||||
exports.unchecked_append(*m_resolved_imports.get(import_));
|
||||
return exports;
|
||||
}
|
||||
|
||||
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>([&](const ImportSection& 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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Result.h>
|
||||
#include <LibWasm/Types.h>
|
||||
|
@ -15,6 +17,13 @@ namespace Wasm {
|
|||
struct InstantiationError {
|
||||
String error { "Unknown error" };
|
||||
};
|
||||
struct LinkError {
|
||||
enum OtherErrors {
|
||||
InvalidImportedModule,
|
||||
};
|
||||
Vector<String> missing_imports;
|
||||
Vector<OtherErrors> other_errors;
|
||||
};
|
||||
|
||||
TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, FunctionAddress);
|
||||
TYPEDEF_DISTINCT_NUMERIC_GENERAL(u64, true, true, false, false, false, true, ExternAddress);
|
||||
|
@ -436,4 +445,42 @@ private:
|
|||
Store m_store;
|
||||
};
|
||||
|
||||
class Linker {
|
||||
public:
|
||||
struct Name {
|
||||
String module;
|
||||
String name;
|
||||
ImportSection::Import::ImportDesc type;
|
||||
};
|
||||
|
||||
explicit Linker(const Module& module)
|
||||
: m_module(module)
|
||||
{
|
||||
}
|
||||
|
||||
// Link a module, the import 'module name' is ignored with this.
|
||||
void link(const ModuleInstance&);
|
||||
|
||||
// Link a bunch of qualified values, also matches 'module name'.
|
||||
void link(const HashMap<Name, ExternValue>&);
|
||||
|
||||
AK::Result<Vector<ExternValue>, LinkError> finish();
|
||||
|
||||
private:
|
||||
void populate();
|
||||
|
||||
const Module& m_module;
|
||||
HashMap<Name, ExternValue> m_resolved_imports;
|
||||
HashTable<Name> m_unresolved_imports;
|
||||
Vector<Name> m_ordered_imports;
|
||||
Optional<LinkError> m_error;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Wasm::Linker::Name> : public AK::GenericTraits<Wasm::Linker::Name> {
|
||||
static constexpr bool is_trivial() { return false; }
|
||||
static unsigned hash(const Wasm::Linker::Name& entry) { return pair_int_hash(entry.module.hash(), entry.name.hash()); }
|
||||
static bool equals(const Wasm::Linker::Name& a, const Wasm::Linker::Name& b) { return a.name == b.name && a.module == b.module; }
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue