diff options
author | Ali Mohammad Pur <ali.mpfard@gmail.com> | 2021-09-04 04:40:57 +0430 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2021-09-05 15:42:50 +0430 |
commit | 09dd397160a0081568f964c04db0c55db6cb52d5 (patch) | |
tree | 7affb4067fc7352aaf829c871118183bc70689f1 /Userland/Libraries/LibWeb/WebAssembly | |
parent | d52a26de3f069eea9d947c9659a522afd615cbec (diff) | |
download | serenity-09dd397160a0081568f964c04db0c55db6cb52d5.zip |
LibWeb+LibWasm: Implement the WebAssembly.Table object
Diffstat (limited to 'Userland/Libraries/LibWeb/WebAssembly')
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); +}; + +} |