Memory.cpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  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. #include <LibJS/Runtime/Realm.h>
  8. #include <LibJS/Runtime/VM.h>
  9. #include <LibWasm/Types.h>
  10. #include <LibWeb/Bindings/Intrinsics.h>
  11. #include <LibWeb/Bindings/MemoryPrototype.h>
  12. #include <LibWeb/WebAssembly/Memory.h>
  13. #include <LibWeb/WebAssembly/WebAssembly.h>
  14. namespace Web::WebAssembly {
  15. JS_DEFINE_ALLOCATOR(Memory);
  16. WebIDL::ExceptionOr<JS::NonnullGCPtr<Memory>> Memory::construct_impl(JS::Realm& realm, MemoryDescriptor& descriptor)
  17. {
  18. auto& vm = realm.vm();
  19. Wasm::Limits limits { descriptor.initial, move(descriptor.maximum) };
  20. Wasm::MemoryType memory_type { move(limits) };
  21. auto& cache = Detail::get_cache(realm);
  22. auto address = cache.abstract_machine().store().allocate(memory_type);
  23. if (!address.has_value())
  24. return vm.throw_completion<JS::TypeError>("Wasm Memory allocation failed"sv);
  25. auto memory_object = vm.heap().allocate<Memory>(realm, realm, *address);
  26. cache.abstract_machine().store().get(*address)->successful_grow_hook = [memory_object] {
  27. MUST(memory_object->reset_the_memory_buffer());
  28. };
  29. return memory_object;
  30. }
  31. Memory::Memory(JS::Realm& realm, Wasm::MemoryAddress address)
  32. : Bindings::PlatformObject(realm)
  33. , m_address(address)
  34. {
  35. }
  36. void Memory::initialize(JS::Realm& realm)
  37. {
  38. Base::initialize(realm);
  39. WEB_SET_PROTOTYPE_FOR_INTERFACE_WITH_CUSTOM_NAME(Memory, WebAssembly.Memory);
  40. }
  41. void Memory::visit_edges(Visitor& visitor)
  42. {
  43. Base::visit_edges(visitor);
  44. visitor.visit(m_buffer);
  45. }
  46. // https://webassembly.github.io/spec/js-api/#dom-memory-grow
  47. WebIDL::ExceptionOr<u32> Memory::grow(u32 delta)
  48. {
  49. auto& vm = this->vm();
  50. auto& context = Detail::get_cache(realm());
  51. auto* memory = context.abstract_machine().store().get(address());
  52. if (!memory)
  53. return vm.throw_completion<JS::RangeError>("Could not find the memory instance to grow"sv);
  54. auto previous_size = memory->size() / Wasm::Constants::page_size;
  55. if (!memory->grow(delta * Wasm::Constants::page_size, Wasm::MemoryInstance::GrowType::No, Wasm::MemoryInstance::InhibitGrowCallback::Yes))
  56. return vm.throw_completion<JS::RangeError>("Memory.grow() grows past the stated limit of the memory instance"sv);
  57. TRY(reset_the_memory_buffer());
  58. return previous_size;
  59. }
  60. // https://webassembly.github.io/spec/js-api/#reset-the-memory-buffer
  61. WebIDL::ExceptionOr<void> Memory::reset_the_memory_buffer()
  62. {
  63. if (!m_buffer)
  64. return {};
  65. auto& vm = this->vm();
  66. auto& realm = *vm.current_realm();
  67. MUST(JS::detach_array_buffer(vm, *m_buffer, JS::PrimitiveString::create(vm, "WebAssembly.Memory"_string)));
  68. auto buffer = TRY(create_a_memory_buffer(vm, realm, m_address));
  69. m_buffer = buffer;
  70. return {};
  71. }
  72. // https://webassembly.github.io/spec/js-api/#dom-memory-buffer
  73. WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> Memory::buffer() const
  74. {
  75. auto& vm = this->vm();
  76. auto& realm = *vm.current_realm();
  77. if (!m_buffer)
  78. m_buffer = TRY(create_a_memory_buffer(vm, realm, m_address));
  79. return JS::NonnullGCPtr(*m_buffer);
  80. }
  81. // https://webassembly.github.io/spec/js-api/#create-a-memory-buffer
  82. WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> Memory::create_a_memory_buffer(JS::VM& vm, JS::Realm& realm, Wasm::MemoryAddress address)
  83. {
  84. auto& context = Detail::get_cache(realm);
  85. auto* memory = context.abstract_machine().store().get(address);
  86. if (!memory)
  87. return vm.throw_completion<JS::RangeError>("Could not find the memory instance"sv);
  88. auto array_buffer = JS::ArrayBuffer::create(realm, &memory->data());
  89. array_buffer->set_detach_key(JS::PrimitiveString::create(vm, "WebAssembly.Memory"_string));
  90. return JS::NonnullGCPtr(*array_buffer);
  91. }
  92. }