summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/WebAssembly
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2021-09-04 04:40:57 +0430
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2021-09-05 15:42:50 +0430
commit09dd397160a0081568f964c04db0c55db6cb52d5 (patch)
tree7affb4067fc7352aaf829c871118183bc70689f1 /Userland/Libraries/LibWeb/WebAssembly
parentd52a26de3f069eea9d947c9659a522afd615cbec (diff)
downloadserenity-09dd397160a0081568f964c04db0c55db6cb52d5.zip
LibWeb+LibWasm: Implement the WebAssembly.Table object
Diffstat (limited to 'Userland/Libraries/LibWeb/WebAssembly')
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp33
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableConstructor.cpp124
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableConstructor.h28
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableObject.cpp18
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableObject.h30
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTablePrototype.cpp145
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTablePrototype.h36
7 files changed, 413 insertions, 1 deletions
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
index d3be78d352..aabe0d27c6 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
@@ -9,6 +9,8 @@
#include "WebAssemblyModuleConstructor.h"
#include "WebAssemblyModuleObject.h"
#include "WebAssemblyModulePrototype.h"
+#include "WebAssemblyTableObject.h"
+#include "WebAssemblyTablePrototype.h"
#include <AK/ScopeGuard.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayBuffer.h>
@@ -57,6 +59,12 @@ void WebAssemblyObject::initialize(JS::GlobalObject& global_object)
auto& module_prototype = window.ensure_web_prototype<WebAssemblyModulePrototype>("WebAssemblyModulePrototype");
module_prototype.define_direct_property(vm.names.constructor, &module_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
define_direct_property("Module", &module_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
+
+ auto& table_constructor = window.ensure_web_constructor<WebAssemblyTableConstructor>("WebAssembly.Table");
+ table_constructor.define_direct_property(vm.names.name, js_string(vm, "WebAssembly.Table"), JS::Attribute::Configurable);
+ auto& table_prototype = window.ensure_web_prototype<WebAssemblyTablePrototype>("WebAssemblyTablePrototype");
+ table_prototype.define_direct_property(vm.names.constructor, &table_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
+ define_direct_property("Table", &table_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
}
NonnullOwnPtrVector<WebAssemblyObject::CompiledWebAssemblyModule> WebAssemblyObject::s_compiled_modules;
@@ -249,6 +257,15 @@ Result<size_t, JS::Value> WebAssemblyObject::instantiate_module(Wasm::Module con
auto address = static_cast<WebAssemblyMemoryObject const&>(import_.as_object()).address();
resolved_imports.set(import_name, Wasm::ExternValue { address });
},
+ [&](Wasm::TableType const&) {
+ if (!import_.is_object() || !is<WebAssemblyTableObject>(import_.as_object())) {
+ // FIXME: Throw a LinkError instead
+ vm.throw_exception<JS::TypeError>(global_object, "LinkError: Expected an instance of WebAssembly.Table for a table import");
+ return;
+ }
+ auto address = static_cast<WebAssemblyTableObject 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");
@@ -399,8 +416,22 @@ Optional<Wasm::Value> to_webassembly_value(JS::Value value, const Wasm::ValueTyp
return Wasm::Value { static_cast<float>(number) };
}
case Wasm::ValueType::FunctionReference:
+ case Wasm::ValueType::NullFunctionReference: {
+ if (value.is_null())
+ return Wasm::Value { Wasm::ValueType(Wasm::ValueType::NullExternReference), 0ull };
+
+ if (value.is_function()) {
+ auto& function = value.as_function();
+ for (auto& entry : WebAssemblyObject::s_global_cache.function_instances) {
+ if (entry.value == &function)
+ return Wasm::Value { Wasm::Reference { Wasm::Reference::Func { entry.key } } };
+ }
+ }
+
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAn, "Exported function");
+ return {};
+ }
case Wasm::ValueType::ExternReference:
- case Wasm::ValueType::NullFunctionReference:
case Wasm::ValueType::NullExternReference:
TODO();
}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableConstructor.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableConstructor.cpp
new file mode 100644
index 0000000000..480d2dea51
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableConstructor.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/TypedArray.h>
+#include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/WebAssembly/WebAssemblyObject.h>
+#include <LibWeb/WebAssembly/WebAssemblyTableConstructor.h>
+#include <LibWeb/WebAssembly/WebAssemblyTableObject.h>
+#include <LibWeb/WebAssembly/WebAssemblyTablePrototype.h>
+
+namespace Web::Bindings {
+
+WebAssemblyTableConstructor::WebAssemblyTableConstructor(JS::GlobalObject& global_object)
+ : NativeFunction(*global_object.function_prototype())
+{
+}
+
+WebAssemblyTableConstructor::~WebAssemblyTableConstructor()
+{
+}
+
+JS::Value WebAssemblyTableConstructor::call()
+{
+ vm().throw_exception<JS::TypeError>(global_object(), JS::ErrorType::ConstructorWithoutNew, "WebAssembly.Table");
+ return {};
+}
+
+JS::Value WebAssemblyTableConstructor::construct(FunctionObject&)
+{
+ auto& vm = this->vm();
+ auto& global_object = this->global_object();
+
+ auto descriptor = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+
+ auto element_value = descriptor->get("element");
+ if (vm.exception())
+ return {};
+ if (!element_value.is_string()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::InvalidHint, element_value.to_string_without_side_effects());
+ return {};
+ }
+ auto& element = element_value.as_string().string();
+
+ Optional<Wasm::ValueType> reference_type;
+ if (element == "anyfunc"sv)
+ reference_type = Wasm::ValueType(Wasm::ValueType::FunctionReference);
+ else if (element == "externref"sv)
+ reference_type = Wasm::ValueType(Wasm::ValueType::ExternReference);
+
+ if (!reference_type.has_value()) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::InvalidHint, element);
+ return {};
+ }
+
+ auto initial_value = descriptor->get("initial");
+ if (vm.exception())
+ return {};
+ auto maximum_value = descriptor->get("maximum");
+ if (vm.exception())
+ return {};
+
+ auto initial = initial_value.to_u32(global_object);
+ if (vm.exception())
+ return {};
+
+ Optional<u32> maximum;
+
+ if (!maximum_value.is_undefined()) {
+ maximum = maximum_value.to_u32(global_object);
+ if (vm.exception())
+ return {};
+ }
+
+ if (maximum.has_value() && maximum.value() < initial) {
+ vm.throw_exception<JS::RangeError>(global_object, "maximum should be larger than or equal to initial");
+ return {};
+ }
+
+ auto value_value = descriptor->get("value");
+ if (vm.exception())
+ return {};
+
+ auto reference_value = [&]() -> Optional<Wasm::Value> {
+ if (value_value.is_empty() || value_value.is_undefined())
+ return Wasm::Value(*reference_type, 0ull);
+
+ return to_webassembly_value(value_value, *reference_type, global_object);
+ }();
+
+ if (!reference_value.has_value())
+ return {};
+
+ auto& reference = reference_value->value().get<Wasm::Reference>();
+
+ auto address = WebAssemblyObject::s_abstract_machine.store().allocate(Wasm::TableType { *reference_type, Wasm::Limits { initial, maximum } });
+ if (!address.has_value()) {
+ vm.throw_exception<JS::TypeError>(global_object, "Wasm Table allocation failed");
+ return {};
+ }
+
+ auto& table = *WebAssemblyObject::s_abstract_machine.store().get(*address);
+ for (auto& element : table.elements())
+ element = reference;
+
+ return vm.heap().allocate<WebAssemblyTableObject>(global_object, global_object, *address);
+}
+
+void WebAssemblyTableConstructor::initialize(JS::GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ auto& window = static_cast<WindowObject&>(global_object);
+
+ NativeFunction::initialize(global_object);
+ define_direct_property(vm.names.prototype, &window.ensure_web_prototype<WebAssemblyTablePrototype>("WebAssemblyTablePrototype"), 0);
+ define_direct_property(vm.names.length, JS::Value(1), JS::Attribute::Configurable);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableConstructor.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableConstructor.h
new file mode 100644
index 0000000000..56536a4d58
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableConstructor.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 WebAssemblyTableConstructor : public JS::NativeFunction {
+ JS_OBJECT(WebAssemblyTableConstructor, JS::NativeFunction);
+
+public:
+ explicit WebAssemblyTableConstructor(JS::GlobalObject&);
+ virtual void initialize(JS::GlobalObject&) override;
+ virtual ~WebAssemblyTableConstructor() override;
+
+ virtual JS::Value call() override;
+ virtual JS::Value construct(JS::FunctionObject& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableObject.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableObject.cpp
new file mode 100644
index 0000000000..f01f9e9cd9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableObject.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "WebAssemblyTablePrototype.h"
+#include <LibWeb/WebAssembly/WebAssemblyTableObject.h>
+
+namespace Web::Bindings {
+
+WebAssemblyTableObject::WebAssemblyTableObject(JS::GlobalObject& global_object, Wasm::TableAddress address)
+ : Object(static_cast<WindowObject&>(global_object).ensure_web_prototype<WebAssemblyTablePrototype>("WebAssemblyTablePrototype"))
+ , m_address(address)
+{
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableObject.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableObject.h
new file mode 100644
index 0000000000..bb2bbd2cd9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTableObject.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+#include <LibWasm/AbstractMachine/AbstractMachine.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/WebAssembly/WebAssemblyInstanceObjectPrototype.h>
+#include <LibWeb/WebAssembly/WebAssemblyObject.h>
+
+namespace Web::Bindings {
+
+class WebAssemblyTableObject final : public JS::Object {
+ JS_OBJECT(WebAssemblyTableObject, Object);
+
+public:
+ explicit WebAssemblyTableObject(JS::GlobalObject&, Wasm::TableAddress);
+ virtual ~WebAssemblyTableObject() override = default;
+
+ Wasm::TableAddress address() const { return m_address; }
+
+private:
+ Wasm::TableAddress m_address;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTablePrototype.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTablePrototype.cpp
new file mode 100644
index 0000000000..161d413fdf
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTablePrototype.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Runtime/TypedArray.h>
+#include <LibWeb/WebAssembly/WebAssemblyTableObject.h>
+#include <LibWeb/WebAssembly/WebAssemblyTablePrototype.h>
+
+namespace Web::Bindings {
+
+void WebAssemblyTablePrototype::initialize(JS::GlobalObject& global_object)
+{
+ Object::initialize(global_object);
+ define_native_accessor("length", length_getter, {}, JS::Attribute::Enumerable | JS::Attribute::Configurable);
+ define_native_function("grow", grow, 1, JS::Attribute::Writable | JS::Attribute::Enumerable | JS::Attribute::Configurable);
+ define_native_function("get", get, 1, JS::Attribute::Writable | JS::Attribute::Enumerable | JS::Attribute::Configurable);
+ define_native_function("set", set, 1, JS::Attribute::Writable | JS::Attribute::Enumerable | JS::Attribute::Configurable);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WebAssemblyTablePrototype::grow)
+{
+ auto delta = 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<WebAssemblyTableObject>(this_object)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "WebAssembly.Table");
+ return {};
+ }
+ auto* table_object = static_cast<WebAssemblyTableObject*>(this_object);
+ auto address = table_object->address();
+ auto* table = WebAssemblyObject::s_abstract_machine.store().get(address);
+ if (!table)
+ return JS::js_undefined();
+
+ auto initial_size = table->elements().size();
+ auto value_value = vm.argument(1);
+ auto reference_value = [&]() -> Optional<Wasm::Value> {
+ if (value_value.is_empty() || value_value.is_undefined())
+ return Wasm::Value(table->type().element_type(), 0ull);
+
+ return to_webassembly_value(value_value, table->type().element_type(), global_object);
+ }();
+
+ if (!reference_value.has_value())
+ return {};
+
+ auto& reference = reference_value->value().get<Wasm::Reference>();
+
+ if (!table->grow(delta, reference)) {
+ vm.throw_exception<JS::RangeError>(global_object, "Failed to grow table");
+ return {};
+ }
+
+ return JS::Value(static_cast<u32>(initial_size));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WebAssemblyTablePrototype::get)
+{
+ auto index = 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<WebAssemblyTableObject>(this_object)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "WebAssembly.Table");
+ return {};
+ }
+ auto* table_object = static_cast<WebAssemblyTableObject*>(this_object);
+ auto address = table_object->address();
+ auto* table = WebAssemblyObject::s_abstract_machine.store().get(address);
+ if (!table)
+ return JS::js_undefined();
+
+ if (table->elements().size() <= index) {
+ vm.throw_exception<JS::RangeError>(global_object, "Table element index out of range");
+ return {};
+ }
+
+ auto& ref = table->elements()[index];
+ if (!ref.has_value())
+ return JS::js_undefined();
+
+ Wasm::Value wasm_value { ref.value() };
+ return to_js_value(wasm_value, global_object);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WebAssemblyTablePrototype::set)
+{
+ auto index = 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<WebAssemblyTableObject>(this_object)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "WebAssembly.Table");
+ return {};
+ }
+ auto* table_object = static_cast<WebAssemblyTableObject*>(this_object);
+ auto address = table_object->address();
+ auto* table = WebAssemblyObject::s_abstract_machine.store().get(address);
+ if (!table)
+ return JS::js_undefined();
+
+ if (table->elements().size() <= index) {
+ vm.throw_exception<JS::RangeError>(global_object, "Table element index out of range");
+ return {};
+ }
+
+ auto value_value = vm.argument(1);
+ auto reference_value = [&]() -> Optional<Wasm::Value> {
+ if (value_value.is_empty() || value_value.is_undefined())
+ return Wasm::Value(table->type().element_type(), 0ull);
+
+ return to_webassembly_value(value_value, table->type().element_type(), global_object);
+ }();
+
+ if (!reference_value.has_value())
+ return {};
+
+ auto& reference = reference_value->value().get<Wasm::Reference>();
+ table->elements()[index] = reference;
+
+ return JS::js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WebAssemblyTablePrototype::length_getter)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object || !is<WebAssemblyTableObject>(this_object)) {
+ vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "WebAssembly.Table");
+ return {};
+ }
+ auto* table_object = static_cast<WebAssemblyTableObject*>(this_object);
+ auto address = table_object->address();
+ auto* table = WebAssemblyObject::s_abstract_machine.store().get(address);
+ if (!table)
+ return JS::js_undefined();
+
+ return JS::Value(table->elements().size());
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTablePrototype.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTablePrototype.h
new file mode 100644
index 0000000000..e3075c63ae
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyTablePrototype.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include "WebAssemblyTableConstructor.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 WebAssemblyTablePrototype final : public JS::Object {
+ JS_OBJECT(WebAssemblyTablePrototype, JS::Object);
+
+public:
+ explicit WebAssemblyTablePrototype(JS::GlobalObject& global_object)
+ : JS::Object(global_object)
+ {
+ }
+
+ virtual void initialize(JS::GlobalObject& global_object) override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(grow);
+ JS_DECLARE_NATIVE_FUNCTION(get);
+ JS_DECLARE_NATIVE_FUNCTION(set);
+ JS_DECLARE_NATIVE_FUNCTION(length_getter);
+};
+
+}