LibWasm+LibWeb: Properly track module lifetime with function refs
This commit is contained in:
parent
a33e148339
commit
a36f6abedb
10 changed files with 103 additions and 36 deletions
|
|
@ -248,7 +248,12 @@ FunctionInstance* Store::get(FunctionAddress address)
|
|||
auto value = address.value();
|
||||
if (m_functions.size() <= value)
|
||||
return nullptr;
|
||||
return &m_functions[value];
|
||||
auto& instance = m_functions[value];
|
||||
if (auto const* wasm = instance.get_pointer<WasmFunction>()) {
|
||||
if (!wasm->try_module())
|
||||
return nullptr;
|
||||
}
|
||||
return &instance;
|
||||
}
|
||||
|
||||
Module const* Store::get_module_for(Wasm::FunctionAddress address)
|
||||
|
|
@ -259,6 +264,14 @@ Module const* Store::get_module_for(Wasm::FunctionAddress address)
|
|||
return function->get<WasmFunction>().module_ref().ptr();
|
||||
}
|
||||
|
||||
RefPtr<ModuleInstance const> Store::get_module_instance_for(FunctionAddress address)
|
||||
{
|
||||
auto* function = get(address);
|
||||
if (!function || function->has<HostFunction>())
|
||||
return nullptr;
|
||||
return function->get<WasmFunction>().try_module();
|
||||
}
|
||||
|
||||
TableInstance* Store::get(TableAddress address)
|
||||
{
|
||||
auto value = address.value();
|
||||
|
|
@ -337,7 +350,7 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
|
|||
if (auto result = validate(const_cast<Module&>(module)); result.is_error())
|
||||
return InstantiationError { ByteString::formatted("Validation failed: {}", result.error()) };
|
||||
|
||||
auto main_module_instance_pointer = make<ModuleInstance>();
|
||||
auto main_module_instance_pointer = adopt_ref(*new ModuleInstance);
|
||||
main_module_instance_pointer->cached_minimum_call_record_allocation_size = module.minimum_call_record_allocation_size();
|
||||
auto& main_module_instance = *main_module_instance_pointer;
|
||||
|
||||
|
|
@ -345,7 +358,8 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
|
|||
|
||||
Vector<Value> global_values;
|
||||
Vector<Vector<Reference>> elements;
|
||||
ModuleInstance auxiliary_instance;
|
||||
auto auxiliary_instance_ptr = adopt_ref(*new ModuleInstance);
|
||||
auto& auxiliary_instance = *auxiliary_instance_ptr;
|
||||
|
||||
auxiliary_instance.cached_minimum_call_record_allocation_size = module.minimum_call_record_allocation_size();
|
||||
|
||||
|
|
@ -453,7 +467,8 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
|
|||
if (result.is_trap())
|
||||
return InstantiationError { "Global instantiation trapped", move(result.trap()) };
|
||||
global_values.append(result.values().first());
|
||||
auxiliary_instance.globals().append(m_store.allocate(entry.type(), result.values().first()).release_value());
|
||||
auto addr = m_store.allocate(entry.type(), result.values().first()).release_value();
|
||||
auxiliary_instance.globals().append(addr);
|
||||
}
|
||||
|
||||
if (auto result = allocate_all_initial_phase(module, main_module_instance, externs, global_values, module_functions); result.has_value())
|
||||
|
|
@ -520,8 +535,12 @@ InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<Ex
|
|||
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] = *it;
|
||||
for (auto it = elem_instance->references().begin(); it < elem_instance->references().end(); ++i, ++it) {
|
||||
RefPtr<ModuleInstance const> anchor;
|
||||
if (auto const* func = it->ref().template get_pointer<Reference::Func>())
|
||||
anchor = m_store.get_module_instance_for(func->address);
|
||||
table_instance->set_element(i + d, *it, move(anchor));
|
||||
}
|
||||
// Drop element
|
||||
*m_store.get(main_module_instance.elements()[current_index]) = ElementInstance(elem_instance->type(), {});
|
||||
}
|
||||
|
|
@ -611,14 +630,16 @@ Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module
|
|||
|
||||
for (auto& table : module.table_section().tables()) {
|
||||
auto table_address = m_store.allocate(table.type());
|
||||
if (table_address.has_value())
|
||||
if (table_address.has_value()) {
|
||||
module_instance.tables().append(*table_address);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& memory : module.memory_section().memories()) {
|
||||
auto memory_address = m_store.allocate(memory.type());
|
||||
if (memory_address.has_value())
|
||||
if (memory_address.has_value()) {
|
||||
module_instance.memories().append(*memory_address);
|
||||
}
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/StackInfo.h>
|
||||
#include <AK/UFixedBigInt.h>
|
||||
#include <AK/Weakable.h>
|
||||
#include <LibWasm/Export.h>
|
||||
#include <LibWasm/Types.h>
|
||||
|
||||
|
|
@ -302,6 +303,8 @@ struct InstantiationError {
|
|||
|
||||
using ExternValue = Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, TagAddress>;
|
||||
|
||||
class Store;
|
||||
|
||||
class ExportInstance {
|
||||
public:
|
||||
explicit ExportInstance(ByteString name, ExternValue value)
|
||||
|
|
@ -318,7 +321,8 @@ private:
|
|||
ExternValue m_value;
|
||||
};
|
||||
|
||||
class ModuleInstance {
|
||||
class WASM_API ModuleInstance : public RefCounted<ModuleInstance>
|
||||
, public Weakable<ModuleInstance> {
|
||||
public:
|
||||
explicit ModuleInstance(
|
||||
Vector<TypeSection::Type> 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, size_t minimum_call_record_allocation_size)
|
||||
|
|
@ -379,21 +383,23 @@ public:
|
|||
explicit WasmFunction(FunctionType const& type, ModuleInstance const& instance, Module const& module, CodeSection::Code const& code)
|
||||
: m_type(type)
|
||||
, m_module(module.make_weak_ptr())
|
||||
, m_module_instance(instance)
|
||||
, m_code(code)
|
||||
, m_module_instance(instance.make_weak_ptr<ModuleInstance const>())
|
||||
, m_code(&code)
|
||||
{
|
||||
}
|
||||
|
||||
auto& type() const { return m_type; }
|
||||
auto& module() const { return m_module_instance; }
|
||||
auto& code() const { return m_code; }
|
||||
// Callers must have already verified the module is alive (e.g., via Store::get() returning non-null).
|
||||
ModuleInstance const& module() const { return *m_module_instance.strong_ref(); }
|
||||
RefPtr<ModuleInstance const> try_module() const { return m_module_instance.strong_ref(); }
|
||||
auto& code() const { return *m_code; }
|
||||
RefPtr<Module const> module_ref() const { return m_module.strong_ref(); }
|
||||
|
||||
private:
|
||||
FunctionType m_type;
|
||||
WeakPtr<Module const> m_module;
|
||||
ModuleInstance const& m_module_instance;
|
||||
CodeSection::Code const& m_code;
|
||||
WeakPtr<ModuleInstance const> m_module_instance;
|
||||
CodeSection::Code const* m_code;
|
||||
};
|
||||
|
||||
class HostFunction {
|
||||
|
|
@ -423,13 +429,24 @@ public:
|
|||
: m_elements(move(elements))
|
||||
, m_type(type)
|
||||
{
|
||||
m_module_anchors.resize(m_elements.size());
|
||||
}
|
||||
|
||||
auto& elements() const { return m_elements; }
|
||||
auto& elements() { return m_elements; }
|
||||
auto& type() const { return m_type; }
|
||||
|
||||
bool grow(u32 size_to_grow, Reference const& fill_value)
|
||||
// MUST use this if a function reference can be stored in the table
|
||||
void set_element(size_t index, Reference ref, RefPtr<ModuleInstance const> module_anchor = {})
|
||||
{
|
||||
m_elements[index] = move(ref);
|
||||
m_module_anchors[index] = move(module_anchor);
|
||||
}
|
||||
|
||||
// Strong ref pinning the element's defining ModuleInstance (null for non-Func).
|
||||
RefPtr<ModuleInstance const> module_anchor_at(size_t index) const { return m_module_anchors[index]; }
|
||||
|
||||
bool grow(u32 size_to_grow, Reference const& fill_value, RefPtr<ModuleInstance const> fill_module_anchor = {})
|
||||
{
|
||||
if (size_to_grow == 0)
|
||||
return true;
|
||||
|
|
@ -444,8 +461,12 @@ public:
|
|||
auto previous_size = m_elements.size();
|
||||
if (m_elements.try_resize(new_size).is_error())
|
||||
return false;
|
||||
for (size_t i = previous_size; i < m_elements.size(); ++i)
|
||||
if (m_module_anchors.try_resize(new_size).is_error())
|
||||
return false;
|
||||
for (size_t i = previous_size; i < m_elements.size(); ++i) {
|
||||
m_elements[i] = fill_value;
|
||||
m_module_anchors[i] = fill_module_anchor;
|
||||
}
|
||||
|
||||
m_type = TableType { m_type.element_type(), Limits(m_type.limits().address_type(), m_type.limits().min() + size_to_grow, m_type.limits().max()) };
|
||||
|
||||
|
|
@ -454,6 +475,7 @@ public:
|
|||
|
||||
private:
|
||||
Vector<Reference> m_elements;
|
||||
Vector<RefPtr<ModuleInstance const>> m_module_anchors;
|
||||
TableType m_type;
|
||||
};
|
||||
|
||||
|
|
@ -637,6 +659,7 @@ public:
|
|||
Optional<ExceptionAddress> allocate(TagInstance const&, Vector<Value>);
|
||||
|
||||
Module const* get_module_for(FunctionAddress);
|
||||
RefPtr<ModuleInstance const> get_module_instance_for(FunctionAddress); // Obtains strong ref for module.
|
||||
FunctionInstance* get(FunctionAddress);
|
||||
TableInstance* get(TableAddress);
|
||||
MemoryInstance* get(MemoryAddress);
|
||||
|
|
@ -746,7 +769,7 @@ private:
|
|||
bool m_owns_locals { false };
|
||||
};
|
||||
|
||||
using InstantiationResult = AK::ErrorOr<NonnullOwnPtr<ModuleInstance>, InstantiationError>;
|
||||
using InstantiationResult = AK::ErrorOr<NonnullRefPtr<ModuleInstance>, InstantiationError>;
|
||||
|
||||
struct HostVisitOps {
|
||||
Function<void(ExternallyManagedTrap&)> visit_trap;
|
||||
|
|
|
|||
|
|
@ -2779,8 +2779,13 @@ HANDLE_INSTRUCTION(table_init)
|
|||
TRAP_IN_LOOP_IF_NOT(!checked_source_offset.has_overflow() && checked_source_offset <= (u32)element->references().size());
|
||||
TRAP_IN_LOOP_IF_NOT(!checked_destination_offset.has_overflow() && checked_destination_offset <= (u32)table->elements().size());
|
||||
|
||||
for (u32 i = 0; i < count; ++i)
|
||||
table->elements()[destination_offset + i] = element->references()[source_offset + i];
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
auto const& ref = element->references()[source_offset + i];
|
||||
RefPtr<ModuleInstance const> anchor;
|
||||
if (auto const* func = ref.ref().template get_pointer<Reference::Func>())
|
||||
anchor = configuration.store().get_module_instance_for(func->address);
|
||||
table->set_element(destination_offset + i, ref, move(anchor));
|
||||
}
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
|
|
@ -2809,13 +2814,15 @@ HANDLE_INSTRUCTION(table_copy)
|
|||
|
||||
if (destination_offset <= source_offset) {
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
auto value = source_instance->elements()[source_offset + i];
|
||||
destination_instance->elements()[destination_offset + i] = value;
|
||||
destination_instance->set_element(destination_offset + i,
|
||||
source_instance->elements()[source_offset + i],
|
||||
source_instance->module_anchor_at(source_offset + i));
|
||||
}
|
||||
} else {
|
||||
for (u32 i = count - 1; i != NumericLimits<u32>::max(); --i) {
|
||||
auto value = source_instance->elements()[source_offset + i];
|
||||
destination_instance->elements()[destination_offset + i] = value;
|
||||
destination_instance->set_element(destination_offset + i,
|
||||
source_instance->elements()[source_offset + i],
|
||||
source_instance->module_anchor_at(source_offset + i));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2838,8 +2845,15 @@ HANDLE_INSTRUCTION(table_fill)
|
|||
checked_offset += count;
|
||||
TRAP_IN_LOOP_IF_NOT(!checked_offset.has_overflow() && checked_offset <= (u32)table->elements().size());
|
||||
|
||||
for (u32 i = 0; i < count; ++i)
|
||||
table->elements()[start + i] = value.template to<Reference>();
|
||||
// Don't leak the RefPtr to the sibling call.
|
||||
{
|
||||
auto ref = value.template to<Reference>();
|
||||
RefPtr<ModuleInstance const> anchor;
|
||||
if (auto const* func = ref.ref().template get_pointer<Reference::Func>())
|
||||
anchor = configuration.store().get_module_instance_for(func->address);
|
||||
for (u32 i = 0; i < count; ++i)
|
||||
table->set_element(start + i, ref, anchor);
|
||||
}
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
|
|
@ -2854,7 +2868,13 @@ HANDLE_INSTRUCTION(table_set)
|
|||
auto address = configuration.frame().module().tables()[table_index.value()];
|
||||
auto table = configuration.store().get(address);
|
||||
TRAP_IN_LOOP_IF_NOT(index < table->elements().size());
|
||||
table->elements()[index] = ref.template to<Reference>();
|
||||
{
|
||||
auto reference = ref.template to<Reference>();
|
||||
RefPtr<ModuleInstance const> anchor;
|
||||
if (auto const* func = reference.ref().template get_pointer<Reference::Func>())
|
||||
anchor = configuration.store().get_module_instance_for(func->address);
|
||||
table->set_element(index, reference, move(anchor));
|
||||
}
|
||||
TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@ i32 wasm_cl_call_indirect(void* interp_ptr, void* config_ptr, i32 table_idx, i32
|
|||
auto const& module = config.frame().module();
|
||||
auto table_address = module.tables()[table_idx];
|
||||
auto* table_instance = config.store().get(table_address);
|
||||
if (element_index < 0 || static_cast<size_t>(element_index) >= table_instance->elements().size())
|
||||
if (!table_instance || element_index < 0 || static_cast<size_t>(element_index) >= table_instance->elements().size())
|
||||
return interpreter.set_trap(Trap::from_string("Table index out of bounds"));
|
||||
|
||||
auto& element = table_instance->elements()[element_index];
|
||||
|
|
@ -416,7 +416,10 @@ i32 wasm_cl_call_indirect(void* interp_ptr, void* config_ptr, i32 table_idx, i32
|
|||
return interpreter.set_trap(Trap::from_string("Table element is not a function reference"));
|
||||
|
||||
auto address = element.ref().get<Reference::Func>().address;
|
||||
auto const& type_actual = config.store().get(address)->visit([](auto& f) -> decltype(auto) { return f.type(); });
|
||||
auto* function = config.store().get(address);
|
||||
if (!function)
|
||||
return interpreter.set_trap(Trap::from_string("Indirect call to freed function"));
|
||||
auto const& type_actual = function->visit([](auto& f) -> decltype(auto) { return f.type(); });
|
||||
auto const& type_expected = module.types()[type_idx].unsafe_function();
|
||||
if (type_actual.parameters() != type_expected.parameters() || type_actual.results() != type_expected.results())
|
||||
return interpreter.set_trap(Trap::from_string("Indirect call type mismatch"));
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ WebIDL::ExceptionOr<GC::Ref<Instance>> Instance::construct_impl(JS::Realm& realm
|
|||
return realm.create<Instance>(realm, move(module_instance));
|
||||
}
|
||||
|
||||
Instance::Instance(JS::Realm& realm, NonnullOwnPtr<Wasm::ModuleInstance> module_instance)
|
||||
Instance::Instance(JS::Realm& realm, NonnullRefPtr<Wasm::ModuleInstance> module_instance)
|
||||
: Bindings::PlatformObject(realm)
|
||||
, m_exports(Object::create(realm, nullptr))
|
||||
, m_module_instance(move(module_instance))
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ public:
|
|||
Wasm::ModuleInstance const* module_instance() const { return m_module_instance.ptr(); }
|
||||
|
||||
private:
|
||||
Instance(JS::Realm&, NonnullOwnPtr<Wasm::ModuleInstance>);
|
||||
Instance(JS::Realm&, NonnullRefPtr<Wasm::ModuleInstance>);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
GC::Ref<Object> m_exports;
|
||||
NonnullOwnPtr<Wasm::ModuleInstance> m_module_instance;
|
||||
NonnullRefPtr<Wasm::ModuleInstance> m_module_instance;
|
||||
HashMap<Wasm::FunctionAddress, GC::Ptr<JS::FunctionObject>> m_function_instances;
|
||||
HashMap<Wasm::TableAddress, GC::Ptr<WebAssembly::Table>> m_table_instances;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ Wasm::HostFunction create_host_function(JS::VM& vm, JS::FunctionObject& function
|
|||
};
|
||||
}
|
||||
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS::VM& vm, Wasm::Module const& module, GC::Ptr<JS::Object> import_object)
|
||||
JS::ThrowCompletionOr<NonnullRefPtr<Wasm::ModuleInstance>> instantiate_module(JS::VM& vm, Wasm::Module const& module, GC::Ptr<JS::Object> import_object)
|
||||
{
|
||||
Wasm::Linker linker { module };
|
||||
auto& cache = get_cache(*vm.current_realm());
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ private:
|
|||
|
||||
WebAssemblyCache& get_cache(JS::Realm&);
|
||||
|
||||
JS::ThrowCompletionOr<NonnullOwnPtr<Wasm::ModuleInstance>> instantiate_module(JS::VM&, Wasm::Module const&, GC::Ptr<JS::Object> import_object);
|
||||
JS::ThrowCompletionOr<NonnullRefPtr<Wasm::ModuleInstance>> instantiate_module(JS::VM&, Wasm::Module const&, GC::Ptr<JS::Object> import_object);
|
||||
JS::ThrowCompletionOr<NonnullRefPtr<CompiledWebAssemblyModule>> compile_a_webassembly_module(JS::VM&, ByteBuffer);
|
||||
JS::NativeFunction* create_native_function(JS::VM&, Wasm::FunctionAddress address, Utf16FlyString name, Instance* instance = nullptr);
|
||||
JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM&, JS::Value value, Wasm::ValueType const& type);
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ private:
|
|||
static HashMap<Wasm::Linker::Name, Wasm::ExternValue> s_spec_test_namespace;
|
||||
static Wasm::AbstractMachine m_machine;
|
||||
RefPtr<Wasm::Module> m_module;
|
||||
OwnPtr<Wasm::ModuleInstance> m_module_instance;
|
||||
RefPtr<Wasm::ModuleInstance> m_module_instance;
|
||||
};
|
||||
|
||||
GC_DEFINE_ALLOCATOR(WebAssemblyModule);
|
||||
|
|
|
|||
|
|
@ -619,7 +619,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|||
|
||||
Core::EventLoop main_loop;
|
||||
// First, resolve the linked modules
|
||||
Vector<NonnullOwnPtr<Wasm::ModuleInstance>> linked_instances;
|
||||
Vector<NonnullRefPtr<Wasm::ModuleInstance>> linked_instances;
|
||||
Vector<NonnullRefPtr<Wasm::Module>> linked_modules;
|
||||
for (auto& name : modules_to_link_in) {
|
||||
auto parse_result = parse(name);
|
||||
|
|
|
|||
Loading…
Reference in a new issue