summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/WebAssembly
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2021-06-21 08:42:58 +0430
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2021-06-22 00:26:25 +0430
commit5c90c389c3cbce761d711bd2b5a635be196e48a4 (patch)
treefe4acdef37c4e01eec4c49514417fb0a7adabcfa /Userland/Libraries/LibWeb/WebAssembly
parenteb5c92d4e20d66ea13a1daeb48599b46359f60dd (diff)
downloadserenity-5c90c389c3cbce761d711bd2b5a635be196e48a4.zip
LibWeb: Implement the WebAssembly Memory object and Memory imports
Diffstat (limited to 'Userland/Libraries/LibWeb/WebAssembly')
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryConstructor.cpp79
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryConstructor.h28
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryPrototype.cpp63
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryPrototype.h34
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp101
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h6
6 files changed, 255 insertions, 56 deletions
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryConstructor.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryConstructor.cpp
new file mode 100644
index 0000000000..4a8ce4a786
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryConstructor.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/WebAssembly/WebAssemblyMemoryConstructor.h>
+#include <LibWeb/WebAssembly/WebAssemblyMemoryPrototype.h>
+#include <LibWeb/WebAssembly/WebAssemblyObject.h>
+
+namespace Web::Bindings {
+
+WebAssemblyMemoryConstructor::WebAssemblyMemoryConstructor(JS::GlobalObject& global_object)
+ : NativeFunction(*global_object.function_prototype())
+{
+}
+
+WebAssemblyMemoryConstructor::~WebAssemblyMemoryConstructor()
+{
+}
+
+JS::Value WebAssemblyMemoryConstructor::call()
+{
+ vm().throw_exception<JS::TypeError>(global_object(), JS::ErrorType::ConstructorWithoutNew, "WebAssemblyMemory");
+ return {};
+}
+
+JS::Value WebAssemblyMemoryConstructor::construct(Function&)
+{
+ auto& vm = this->vm();
+ auto& global_object = this->global_object();
+
+ auto descriptor = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+
+ auto initial_value = descriptor->get_own_property("initial", {}, JS::AllowSideEffects::No);
+ auto maximum_value = descriptor->get_own_property("maximum", {}, JS::AllowSideEffects::No);
+
+ if (initial_value.is_empty()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Number");
+ return {};
+ }
+
+ auto initial = initial_value.to_u32(global_object);
+ if (vm.exception())
+ return {};
+
+ Optional<u32> maximum;
+
+ if (!maximum_value.is_empty()) {
+ maximum = maximum_value.to_u32(global_object);
+ if (vm.exception())
+ return {};
+ }
+
+ auto address = WebAssemblyObject::s_abstract_machine.store().allocate(Wasm::MemoryType { Wasm::Limits { initial, maximum } });
+ if (!address.has_value()) {
+ vm.throw_exception<JS::TypeError>(global_object, "Wasm Memory allocation failed");
+ return {};
+ }
+
+ return vm.heap().allocate<WebAssemblyMemoryObject>(global_object, global_object, *address);
+}
+
+void WebAssemblyMemoryConstructor::initialize(JS::GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ auto& window = static_cast<WindowObject&>(global_object);
+
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, &window.ensure_web_prototype<WebAssemblyMemoryPrototype>("WebAssembly.Memory"));
+ define_property(vm.names.length, JS::Value(1), JS::Attribute::Configurable);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryConstructor.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryConstructor.h
new file mode 100644
index 0000000000..ecf8896449
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryConstructor.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/NativeFunction.h>
+
+namespace Web::Bindings {
+
+class WebAssemblyMemoryConstructor : public JS::NativeFunction {
+ JS_OBJECT(WebAssemblyMemoryConstructor, JS::NativeFunction);
+
+public:
+ explicit WebAssemblyMemoryConstructor(JS::GlobalObject&);
+ virtual void initialize(JS::GlobalObject&) override;
+ virtual ~WebAssemblyMemoryConstructor() override;
+
+ virtual JS::Value call() override;
+ virtual JS::Value construct(JS::Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryPrototype.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryPrototype.cpp
new file mode 100644
index 0000000000..c5f32d0591
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryPrototype.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Runtime/TypedArray.h>
+#include <LibWeb/WebAssembly/WebAssemblyMemoryPrototype.h>
+#include <LibWeb/WebAssembly/WebAssemblyObject.h>
+
+namespace Web::Bindings {
+
+void WebAssemblyMemoryPrototype::initialize(JS::GlobalObject& global_object)
+{
+ Object::initialize(global_object);
+ define_native_property("buffer", buffer_getter, nullptr);
+ define_native_function("grow", grow);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WebAssemblyMemoryPrototype::grow)
+{
+ auto page_count = vm.argument(0).to_u32(global_object);
+ if (vm.exception())
+ return {};
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object || !is<WebAssemblyMemoryObject>(this_object)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Memory");
+ return {};
+ }
+ auto* memory_object = static_cast<WebAssemblyMemoryObject*>(this_object);
+ auto address = memory_object->address();
+ auto* memory = WebAssemblyObject::s_abstract_machine.store().get(address);
+ if (!memory)
+ return JS::js_undefined();
+
+ auto previous_size = memory->size() / Wasm::Constants::page_size;
+ if (!memory->grow(page_count * Wasm::Constants::page_size)) {
+ vm.throw_exception<JS::TypeError>(global_object, "Memory.grow() grows past the stated limit of the memory instance");
+ return {};
+ }
+
+ return JS::Value(static_cast<u32>(previous_size));
+}
+
+JS_DEFINE_NATIVE_GETTER(WebAssemblyMemoryPrototype::buffer_getter)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object || !is<WebAssemblyMemoryObject>(this_object)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Memory");
+ return {};
+ }
+ auto* memory_object = static_cast<WebAssemblyMemoryObject*>(this_object);
+ auto address = memory_object->address();
+ auto* memory = WebAssemblyObject::s_abstract_machine.store().get(address);
+ if (!memory)
+ return JS::js_undefined();
+
+ auto array_buffer = JS::ArrayBuffer::create(global_object, &memory->data());
+ array_buffer->set_detach_key(JS::js_string(vm, "WebAssembly.Memory"));
+ return array_buffer;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryPrototype.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryPrototype.h
new file mode 100644
index 0000000000..bcfbb1e88c
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyMemoryPrototype.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include "WebAssemblyMemoryConstructor.h"
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/VM.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::Bindings {
+
+class WebAssemblyMemoryPrototype final : public JS::Object {
+ JS_OBJECT(WebAssemblyMemoryPrototype, JS::Object);
+
+public:
+ explicit WebAssemblyMemoryPrototype(JS::GlobalObject& global_object)
+ : JS::Object(global_object)
+ {
+ }
+
+ virtual void initialize(JS::GlobalObject&) override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(grow);
+ JS_DECLARE_NATIVE_GETTER(buffer_getter);
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
index a0ffd7e035..e593564458 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include "WebAssemblyMemoryPrototype.h"
#include <AK/ScopeGuard.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayBuffer.h>
@@ -31,6 +32,15 @@ void WebAssemblyObject::initialize(JS::GlobalObject& global_object)
define_native_function("validate", validate, 1);
define_native_function("compile", compile, 1);
define_native_function("instantiate", instantiate, 1);
+
+ auto& vm = global_object.vm();
+
+ auto& window = static_cast<WindowObject&>(global_object);
+ auto& memory_constructor = window.ensure_web_prototype<WebAssemblyMemoryConstructor>("WebAssembly.Memory");
+ memory_constructor.define_property(vm.names.name, js_string(vm, "WebAssembly.Memory"), JS::Attribute::Configurable);
+ auto& memory_prototype = window.ensure_web_prototype<WebAssemblyMemoryPrototype>("WebAssemblyMemoryPrototype");
+ memory_prototype.define_property(vm.names.constructor, &memory_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
+ define_property("Memory", &memory_constructor);
}
NonnullOwnPtrVector<WebAssemblyObject::CompiledWebAssemblyModule> WebAssemblyObject::s_compiled_modules;
@@ -213,6 +223,45 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
resolved_imports.set(import_name, Wasm::ExternValue { Wasm::FunctionAddress { *address } });
},
+ [&](Wasm::GlobalType const& type) {
+ Optional<Wasm::GlobalAddress> address;
+ // https://webassembly.github.io/spec/js-api/#read-the-imports step 5.1
+ if (import_.is_number() || import_.is_bigint()) {
+ if (import_.is_number() && type.type().kind() == Wasm::ValueType::I64) {
+ // FIXME: Throw a LinkError instead.
+ vm.throw_exception<JS::TypeError>(global_object, "LinkError: Import resolution attempted to cast a Number to a BigInteger");
+ return;
+ }
+ if (import_.is_bigint() && type.type().kind() != Wasm::ValueType::I64) {
+ // FIXME: Throw a LinkError instead.
+ vm.throw_exception<JS::TypeError>(global_object, "LinkError: Import resolution attempted to cast a BigInteger to a Number");
+ return;
+ }
+ auto cast_value = to_webassembly_value(import_, type.type(), global_object);
+ if (!cast_value.has_value())
+ return;
+ address = s_abstract_machine.store().allocate({ type.type(), false }, cast_value.release_value());
+ } else {
+ // FIXME: https://webassembly.github.io/spec/js-api/#read-the-imports step 5.2
+ // if v implements Global
+ // let globaladdr be v.[[Global]]
+
+ // FIXME: Throw a LinkError instead
+ vm.throw_exception<JS::TypeError>(global_object, "LinkError: Invalid value for global type");
+ return;
+ }
+
+ resolved_imports.set(import_name, Wasm::ExternValue { *address });
+ },
+ [&](Wasm::MemoryType const&) {
+ if (!import_.is_object() || !is<WebAssemblyMemoryObject>(import_.as_object())) {
+ // FIXME: Throw a LinkError instead
+ vm.throw_exception<JS::TypeError>(global_object, "LinkError: Expected an instance of WebAssembly.Memory for a memory import");
+ return;
+ }
+ auto address = static_cast<WebAssemblyMemoryObject const&>(import_.as_object()).address();
+ resolved_imports.set(import_name, Wasm::ExternValue { address });
+ },
[&](const auto&) {
// FIXME: Implement these.
dbgln("Unimplemented import of non-function attempted");
@@ -420,59 +469,9 @@ void WebAssemblyInstanceObject::visit_edges(Cell::Visitor& visitor)
}
WebAssemblyMemoryObject::WebAssemblyMemoryObject(JS::GlobalObject& global_object, Wasm::MemoryAddress address)
- : JS::Object(global_object)
+ : Object(static_cast<WindowObject&>(global_object).ensure_web_prototype<WebAssemblyMemoryPrototype>(class_name()))
, m_address(address)
{
}
-void WebAssemblyMemoryObject::initialize(JS::GlobalObject& global_object)
-{
- Object::initialize(global_object);
- define_native_function("grow", grow, 1);
- define_native_property("buffer", buffer, nullptr);
-}
-
-JS_DEFINE_NATIVE_FUNCTION(WebAssemblyMemoryObject::grow)
-{
- auto page_count = vm.argument(0).to_u32(global_object);
- if (vm.exception())
- return {};
- auto* this_object = vm.this_value(global_object).to_object(global_object);
- if (!this_object || !is<WebAssemblyMemoryObject>(this_object)) {
- vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Memory");
- return {};
- }
- auto* memory_object = static_cast<WebAssemblyMemoryObject*>(this_object);
- auto address = memory_object->m_address;
- auto* memory = WebAssemblyObject::s_abstract_machine.store().get(address);
- if (!memory)
- return JS::js_undefined();
-
- auto previous_size = memory->size() / Wasm::Constants::page_size;
- if (!memory->grow(page_count * Wasm::Constants::page_size)) {
- vm.throw_exception<JS::TypeError>(global_object, "Memory.grow() grows past the stated limit of the memory instance");
- return {};
- }
-
- return JS::Value(static_cast<u32>(previous_size));
-}
-
-JS_DEFINE_NATIVE_GETTER(WebAssemblyMemoryObject::buffer)
-{
- auto* this_object = vm.this_value(global_object).to_object(global_object);
- if (!this_object || !is<WebAssemblyMemoryObject>(this_object)) {
- vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Memory");
- return {};
- }
- auto* memory_object = static_cast<WebAssemblyMemoryObject*>(this_object);
- auto address = memory_object->m_address;
- auto* memory = WebAssemblyObject::s_abstract_machine.store().get(address);
- if (!memory)
- return JS::js_undefined();
-
- auto array_buffer = JS::ArrayBuffer::create(global_object, &memory->data());
- array_buffer->set_detach_key(JS::js_string(vm, "WebAssembly.Memory"));
- return array_buffer;
-}
-
}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
index 3758ab9d39..99f762c859 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
@@ -95,19 +95,15 @@ private:
};
class WebAssemblyMemoryObject final : public JS::Object {
- JS_OBJECT(WebAssemblyModuleObject, JS::Object);
+ JS_OBJECT(WebAssemblyMemoryObject, JS::Object);
public:
explicit WebAssemblyMemoryObject(JS::GlobalObject&, Wasm::MemoryAddress);
- virtual void initialize(JS::GlobalObject&) override;
virtual ~WebAssemblyMemoryObject() override = default;
auto address() const { return m_address; }
private:
- JS_DECLARE_NATIVE_FUNCTION(grow);
- JS_DECLARE_NATIVE_GETTER(buffer);
-
Wasm::MemoryAddress m_address;
};