1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 15:57:35 +00:00

LibWeb+LibWasm: Implement and use the "reset the Memory buffer" steps

This implements the memory object cache and its "reset on grow"
semantics, as the web depends on the exact behaviour.
This commit is contained in:
Ali Mohammad Pur 2023-03-29 01:31:51 +03:30 committed by Andreas Kling
parent 14fb6372c3
commit 64da05a96d
3 changed files with 55 additions and 5 deletions

View file

@ -398,7 +398,12 @@ public:
auto& data() const { return m_data; } auto& data() const { return m_data; }
auto& data() { return m_data; } auto& data() { return m_data; }
bool grow(size_t size_to_grow) enum class InhibitGrowCallback {
No,
Yes,
};
bool grow(size_t size_to_grow, InhibitGrowCallback inhibit_callback = InhibitGrowCallback::No)
{ {
if (size_to_grow == 0) if (size_to_grow == 0)
return true; return true;
@ -416,9 +421,17 @@ public:
m_size = new_size; m_size = new_size;
// The spec requires that we zero out everything on grow // The spec requires that we zero out everything on grow
__builtin_memset(m_data.offset_pointer(previous_size), 0, size_to_grow); __builtin_memset(m_data.offset_pointer(previous_size), 0, size_to_grow);
// NOTE: This exists because wasm-js-api wants to execute code after a successful grow,
// See [this issue](https://github.com/WebAssembly/spec/issues/1635) for more details.
if (inhibit_callback == InhibitGrowCallback::No && successful_grow_hook)
successful_grow_hook();
return true; return true;
} }
Function<void()> successful_grow_hook;
private: private:
explicit MemoryInstance(MemoryType const& type) explicit MemoryInstance(MemoryType const& type)
: m_type(type) : m_type(type)

View file

@ -25,7 +25,12 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Memory>> Memory::construct_impl(JS::Realm&
if (!address.has_value()) if (!address.has_value())
return vm.throw_completion<JS::TypeError>("Wasm Memory allocation failed"sv); return vm.throw_completion<JS::TypeError>("Wasm Memory allocation failed"sv);
return MUST_OR_THROW_OOM(vm.heap().allocate<Memory>(realm, realm, *address)); auto memory_object = MUST_OR_THROW_OOM(vm.heap().allocate<Memory>(realm, realm, *address));
Detail::s_abstract_machine.store().get(*address)->successful_grow_hook = [memory_object] {
MUST(memory_object->reset_the_memory_buffer());
};
return memory_object;
} }
Memory::Memory(JS::Realm& realm, Wasm::MemoryAddress address) Memory::Memory(JS::Realm& realm, Wasm::MemoryAddress address)
@ -52,26 +57,54 @@ WebIDL::ExceptionOr<u32> Memory::grow(u32 delta)
return vm.throw_completion<JS::RangeError>("Could not find the memory instance to grow"sv); return vm.throw_completion<JS::RangeError>("Could not find the memory instance to grow"sv);
auto previous_size = memory->size() / Wasm::Constants::page_size; auto previous_size = memory->size() / Wasm::Constants::page_size;
if (!memory->grow(delta * Wasm::Constants::page_size)) if (!memory->grow(delta * Wasm::Constants::page_size, Wasm::MemoryInstance::InhibitGrowCallback::Yes))
return vm.throw_completion<JS::RangeError>("Memory.grow() grows past the stated limit of the memory instance"sv); return vm.throw_completion<JS::RangeError>("Memory.grow() grows past the stated limit of the memory instance"sv);
TRY(reset_the_memory_buffer());
return previous_size; return previous_size;
} }
// https://webassembly.github.io/spec/js-api/#reset-the-memory-buffer
WebIDL::ExceptionOr<void> Memory::reset_the_memory_buffer()
{
if (!m_buffer)
return {};
auto& vm = this->vm();
auto& realm = *vm.current_realm();
MUST(JS::detach_array_buffer(vm, *m_buffer, MUST_OR_THROW_OOM(JS::PrimitiveString::create(vm, "WebAssembly.Memory"sv))));
auto buffer = TRY(create_a_memory_buffer(vm, realm, m_address));
m_buffer = buffer;
return {};
}
// https://webassembly.github.io/spec/js-api/#dom-memory-buffer // https://webassembly.github.io/spec/js-api/#dom-memory-buffer
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> Memory::buffer() const WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> Memory::buffer() const
{ {
auto& vm = this->vm(); auto& vm = this->vm();
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
auto* memory = Detail::s_abstract_machine.store().get(address()); if (!m_buffer)
m_buffer = TRY(create_a_memory_buffer(vm, realm, m_address));
return JS::NonnullGCPtr(*m_buffer);
}
// https://webassembly.github.io/spec/js-api/#create-a-memory-buffer
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> Memory::create_a_memory_buffer(JS::VM& vm, JS::Realm& realm, Wasm::MemoryAddress address)
{
auto* memory = Detail::s_abstract_machine.store().get(address);
if (!memory) if (!memory)
return vm.throw_completion<JS::RangeError>("Could not find the memory instance"sv); return vm.throw_completion<JS::RangeError>("Could not find the memory instance"sv);
auto array_buffer = JS::ArrayBuffer::create(realm, &memory->data()); auto array_buffer = JS::ArrayBuffer::create(realm, &memory->data());
array_buffer->set_detach_key(MUST_OR_THROW_OOM(JS::PrimitiveString::create(vm, "WebAssembly.Memory"sv))); array_buffer->set_detach_key(MUST_OR_THROW_OOM(JS::PrimitiveString::create(vm, "WebAssembly.Memory"sv)));
return array_buffer; return JS::NonnullGCPtr(*array_buffer);
} }
} }

View file

@ -38,7 +38,11 @@ private:
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override; virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
WebIDL::ExceptionOr<void> reset_the_memory_buffer();
static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> create_a_memory_buffer(JS::VM&, JS::Realm&, Wasm::MemoryAddress);
Wasm::MemoryAddress m_address; Wasm::MemoryAddress m_address;
mutable JS::GCPtr<JS::ArrayBuffer> m_buffer;
}; };
} }