summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h15
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/Memory.cpp41
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/Memory.h4
3 files changed, 55 insertions, 5 deletions
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h
index b08120ffd2..4229198994 100644
--- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h
+++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h
@@ -398,7 +398,12 @@ public:
auto& data() const { 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)
return true;
@@ -416,9 +421,17 @@ public:
m_size = new_size;
// The spec requires that we zero out everything on 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;
}
+ Function<void()> successful_grow_hook;
+
private:
explicit MemoryInstance(MemoryType const& type)
: m_type(type)
diff --git a/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp b/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp
index a07a1dc244..c7ff4a5121 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp
+++ b/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp
@@ -25,7 +25,12 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Memory>> Memory::construct_impl(JS::Realm&
if (!address.has_value())
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)
@@ -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);
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);
+ TRY(reset_the_memory_buffer());
+
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
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> Memory::buffer() const
{
auto& vm = this->vm();
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)
return vm.throw_completion<JS::RangeError>("Could not find the memory instance"sv);
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)));
- return array_buffer;
+ return JS::NonnullGCPtr(*array_buffer);
}
}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/Memory.h b/Userland/Libraries/LibWeb/WebAssembly/Memory.h
index 5ead1b2f6c..f8b63ee960 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/Memory.h
+++ b/Userland/Libraries/LibWeb/WebAssembly/Memory.h
@@ -38,7 +38,11 @@ private:
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;
+ mutable JS::GCPtr<JS::ArrayBuffer> m_buffer;
};
}