/src/serenity/Userland/Libraries/LibWeb/WebAssembly/Memory.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org> |
3 | | * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <LibJS/Runtime/Realm.h> |
9 | | #include <LibJS/Runtime/VM.h> |
10 | | #include <LibWasm/Types.h> |
11 | | #include <LibWeb/Bindings/Intrinsics.h> |
12 | | #include <LibWeb/Bindings/MemoryPrototype.h> |
13 | | #include <LibWeb/WebAssembly/Memory.h> |
14 | | #include <LibWeb/WebAssembly/WebAssembly.h> |
15 | | |
16 | | namespace Web::WebAssembly { |
17 | | |
18 | | JS_DEFINE_ALLOCATOR(Memory); |
19 | | |
20 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<Memory>> Memory::construct_impl(JS::Realm& realm, MemoryDescriptor& descriptor) |
21 | 0 | { |
22 | 0 | auto& vm = realm.vm(); |
23 | |
|
24 | 0 | Wasm::Limits limits { descriptor.initial, move(descriptor.maximum) }; |
25 | 0 | Wasm::MemoryType memory_type { move(limits) }; |
26 | |
|
27 | 0 | auto& cache = Detail::get_cache(realm); |
28 | 0 | auto address = cache.abstract_machine().store().allocate(memory_type); |
29 | 0 | if (!address.has_value()) |
30 | 0 | return vm.throw_completion<JS::TypeError>("Wasm Memory allocation failed"sv); |
31 | | |
32 | 0 | auto memory_object = vm.heap().allocate<Memory>(realm, realm, *address); |
33 | 0 | cache.abstract_machine().store().get(*address)->successful_grow_hook = [memory_object] { |
34 | 0 | MUST(memory_object->reset_the_memory_buffer()); |
35 | 0 | }; |
36 | |
|
37 | 0 | return memory_object; |
38 | 0 | } |
39 | | |
40 | | Memory::Memory(JS::Realm& realm, Wasm::MemoryAddress address) |
41 | 0 | : Bindings::PlatformObject(realm) |
42 | 0 | , m_address(address) |
43 | 0 | { |
44 | 0 | } |
45 | | |
46 | | void Memory::initialize(JS::Realm& realm) |
47 | 0 | { |
48 | 0 | Base::initialize(realm); |
49 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE_WITH_CUSTOM_NAME(Memory, WebAssembly.Memory); |
50 | 0 | } |
51 | | |
52 | | void Memory::visit_edges(Visitor& visitor) |
53 | 0 | { |
54 | 0 | Base::visit_edges(visitor); |
55 | 0 | visitor.visit(m_buffer); |
56 | 0 | } |
57 | | |
58 | | // https://webassembly.github.io/spec/js-api/#dom-memory-grow |
59 | | WebIDL::ExceptionOr<u32> Memory::grow(u32 delta) |
60 | 0 | { |
61 | 0 | auto& vm = this->vm(); |
62 | |
|
63 | 0 | auto& context = Detail::get_cache(realm()); |
64 | 0 | auto* memory = context.abstract_machine().store().get(address()); |
65 | 0 | if (!memory) |
66 | 0 | return vm.throw_completion<JS::RangeError>("Could not find the memory instance to grow"sv); |
67 | | |
68 | 0 | auto previous_size = memory->size() / Wasm::Constants::page_size; |
69 | 0 | if (!memory->grow(delta * Wasm::Constants::page_size, Wasm::MemoryInstance::GrowType::No, Wasm::MemoryInstance::InhibitGrowCallback::Yes)) |
70 | 0 | return vm.throw_completion<JS::RangeError>("Memory.grow() grows past the stated limit of the memory instance"sv); |
71 | | |
72 | 0 | TRY(reset_the_memory_buffer()); |
73 | |
|
74 | 0 | return previous_size; |
75 | 0 | } |
76 | | |
77 | | // https://webassembly.github.io/spec/js-api/#reset-the-memory-buffer |
78 | | WebIDL::ExceptionOr<void> Memory::reset_the_memory_buffer() |
79 | 0 | { |
80 | 0 | if (!m_buffer) |
81 | 0 | return {}; |
82 | | |
83 | 0 | auto& vm = this->vm(); |
84 | 0 | auto& realm = *vm.current_realm(); |
85 | |
|
86 | 0 | MUST(JS::detach_array_buffer(vm, *m_buffer, JS::PrimitiveString::create(vm, "WebAssembly.Memory"_string))); |
87 | |
|
88 | 0 | auto buffer = TRY(create_a_memory_buffer(vm, realm, m_address)); |
89 | 0 | m_buffer = buffer; |
90 | |
|
91 | 0 | return {}; |
92 | 0 | } |
93 | | |
94 | | // https://webassembly.github.io/spec/js-api/#dom-memory-buffer |
95 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> Memory::buffer() const |
96 | 0 | { |
97 | 0 | auto& vm = this->vm(); |
98 | 0 | auto& realm = *vm.current_realm(); |
99 | |
|
100 | 0 | if (!m_buffer) |
101 | 0 | m_buffer = TRY(create_a_memory_buffer(vm, realm, m_address)); |
102 | |
|
103 | 0 | return JS::NonnullGCPtr(*m_buffer); |
104 | 0 | } |
105 | | |
106 | | // https://webassembly.github.io/spec/js-api/#create-a-memory-buffer |
107 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> Memory::create_a_memory_buffer(JS::VM& vm, JS::Realm& realm, Wasm::MemoryAddress address) |
108 | 0 | { |
109 | 0 | auto& context = Detail::get_cache(realm); |
110 | 0 | auto* memory = context.abstract_machine().store().get(address); |
111 | 0 | if (!memory) |
112 | 0 | return vm.throw_completion<JS::RangeError>("Could not find the memory instance"sv); |
113 | | |
114 | 0 | auto array_buffer = JS::ArrayBuffer::create(realm, &memory->data()); |
115 | 0 | array_buffer->set_detach_key(JS::PrimitiveString::create(vm, "WebAssembly.Memory"_string)); |
116 | |
|
117 | 0 | return JS::NonnullGCPtr(*array_buffer); |
118 | 0 | } |
119 | | |
120 | | } |