summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibWeb/CMakeLists.txt1
-rw-r--r--Userland/Libraries/LibWeb/HTML/Window.cpp8
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp139
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h27
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssembly.idl17
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp148
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h15
-rw-r--r--Userland/Libraries/LibWeb/idl_files.cmake1
8 files changed, 186 insertions, 170 deletions
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt
index 9c53a5e023..79d0db46f4 100644
--- a/Userland/Libraries/LibWeb/CMakeLists.txt
+++ b/Userland/Libraries/LibWeb/CMakeLists.txt
@@ -447,6 +447,7 @@ set(SOURCES
WebAssembly/Memory.cpp
WebAssembly/Module.cpp
WebAssembly/Table.cpp
+ WebAssembly/WebAssembly.cpp
WebAssembly/WebAssemblyObject.cpp
WebDriver/Capabilities.cpp
WebDriver/Client.cpp
diff --git a/Userland/Libraries/LibWeb/HTML/Window.cpp b/Userland/Libraries/LibWeb/HTML/Window.cpp
index 88a78e22b0..9527cc8c5c 100644
--- a/Userland/Libraries/LibWeb/HTML/Window.cpp
+++ b/Userland/Libraries/LibWeb/HTML/Window.cpp
@@ -53,7 +53,6 @@
#include <LibWeb/Page/Page.h>
#include <LibWeb/RequestIdleCallback/IdleDeadline.h>
#include <LibWeb/Selection/Selection.h>
-#include <LibWeb/WebAssembly/WebAssemblyObject.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
namespace Web::HTML {
@@ -747,13 +746,6 @@ WebIDL::ExceptionOr<void> Window::initialize_web_interfaces(Badge<WindowEnvironm
MUST_OR_THROW_OOM(Bindings::WindowGlobalMixin::initialize(realm, *this));
- // https://webidl.spec.whatwg.org/#define-the-global-property-references
- // 5. For every namespace namespace that is exposed in realm:
- // 1. Let id be namespace’s identifier.
- // 3. Let namespaceObject be the result of creating a namespace object for namespace in realm.
- // 3. Perform CreateMethodProperty(target, id, namespaceObject).
- create_method_property("WebAssembly", MUST_OR_THROW_OOM(heap().allocate<Bindings::WebAssemblyObject>(realm, realm)));
-
return {};
}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp
new file mode 100644
index 0000000000..f5445f08bd
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/ScopeGuard.h>
+#include <LibJS/Runtime/NativeFunction.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/Promise.h>
+#include <LibJS/Runtime/VM.h>
+#include <LibWasm/AbstractMachine/AbstractMachine.h>
+#include <LibWasm/AbstractMachine/Validator.h>
+#include <LibWeb/WebAssembly/Instance.h>
+#include <LibWeb/WebAssembly/Memory.h>
+#include <LibWeb/WebAssembly/Module.h>
+#include <LibWeb/WebAssembly/Table.h>
+#include <LibWeb/WebAssembly/WebAssembly.h>
+#include <LibWeb/WebAssembly/WebAssemblyObject.h>
+
+namespace Web::WebAssembly {
+
+void visit_edges(JS::Cell::Visitor& visitor)
+{
+ for (auto& entry : Bindings::WebAssemblyObject::s_global_cache.function_instances)
+ visitor.visit(entry.value);
+
+ for (auto& module_cache : Bindings::WebAssemblyObject::s_module_caches) {
+ for (auto& entry : module_cache.function_instances)
+ visitor.visit(entry.value);
+ for (auto& entry : module_cache.memory_instances)
+ visitor.visit(entry.value);
+ for (auto& entry : module_cache.table_instances)
+ visitor.visit(entry.value);
+ }
+}
+
+// https://webassembly.github.io/spec/js-api/#dom-webassembly-validate
+bool validate(JS::VM& vm, JS::Handle<JS::Object>& bytes)
+{
+ // 1. Let stableBytes be a copy of the bytes held by the buffer bytes.
+ // Note: There's no need to copy the bytes here as the buffer data cannot change while we're compiling the module.
+
+ // 2. Compile stableBytes as a WebAssembly module and store the results as module.
+ auto maybe_module = Bindings::parse_module(vm, bytes.cell());
+
+ // 3. If module is error, return false.
+ if (maybe_module.is_error())
+ return false;
+
+ // Drop the module from the cache, we're never going to refer to it.
+ ScopeGuard drop_from_cache { [&] { (void)Bindings::WebAssemblyObject::s_compiled_modules.take_last(); } };
+
+ // 3 continued - our "compile" step is lazy with validation, explicitly do the validation.
+ if (Bindings::WebAssemblyObject::s_abstract_machine.validate(Bindings::WebAssemblyObject::s_compiled_modules[maybe_module.value()]->module).is_error())
+ return false;
+
+ // 4. Return true.
+ return true;
+}
+
+// https://webassembly.github.io/spec/js-api/#dom-webassembly-compile
+WebIDL::ExceptionOr<JS::Value> compile(JS::VM& vm, JS::Handle<JS::Object>& bytes)
+{
+ auto& realm = *vm.current_realm();
+
+ // FIXME: This shouldn't block!
+ auto module = Bindings::parse_module(vm, bytes.cell());
+ auto promise = JS::Promise::create(realm);
+
+ if (module.is_error()) {
+ promise->reject(*module.release_error().value());
+ } else {
+ auto module_object = MUST_OR_THROW_OOM(vm.heap().allocate<Module>(realm, realm, module.release_value()));
+ promise->fulfill(module_object);
+ }
+
+ return promise;
+}
+
+// https://webassembly.github.io/spec/js-api/#dom-webassembly-instantiate
+WebIDL::ExceptionOr<JS::Value> instantiate(JS::VM& vm, JS::Handle<JS::Object>& bytes, Optional<JS::Handle<JS::Object>>& import_object)
+{
+ // FIXME: Implement the importObject parameter.
+ (void)import_object;
+
+ auto& realm = *vm.current_realm();
+
+ // FIXME: This shouldn't block!
+ auto module = Bindings::parse_module(vm, bytes.cell());
+ auto promise = JS::Promise::create(realm);
+
+ if (module.is_error()) {
+ promise->reject(*module.release_error().value());
+ return promise;
+ }
+
+ auto const& compiled_module = Bindings::WebAssemblyObject::s_compiled_modules.at(module.release_value())->module;
+ auto result = Bindings::WebAssemblyObject::instantiate_module(vm, compiled_module);
+
+ if (result.is_error()) {
+ promise->reject(*result.release_error().value());
+ } else {
+ auto module_object = MUST_OR_THROW_OOM(vm.heap().allocate<Module>(realm, realm, Bindings::WebAssemblyObject::s_compiled_modules.size() - 1));
+ auto instance_object = MUST_OR_THROW_OOM(vm.heap().allocate<Instance>(realm, realm, result.release_value()));
+
+ auto object = JS::Object::create(realm, nullptr);
+ object->define_direct_property("module", module_object, JS::default_attributes);
+ object->define_direct_property("instance", instance_object, JS::default_attributes);
+ promise->fulfill(object);
+ }
+
+ return promise;
+}
+
+// https://webassembly.github.io/spec/js-api/#dom-webassembly-instantiate-moduleobject-importobject
+WebIDL::ExceptionOr<JS::Value> instantiate(JS::VM& vm, Module const& module_object, Optional<JS::Handle<JS::Object>>& import_object)
+{
+ // FIXME: Implement the importObject parameter.
+ (void)import_object;
+
+ auto& realm = *vm.current_realm();
+ auto promise = JS::Promise::create(realm);
+
+ auto const& compiled_module = module_object.module();
+ auto result = Bindings::WebAssemblyObject::instantiate_module(vm, compiled_module);
+
+ if (result.is_error()) {
+ promise->reject(*result.release_error().value());
+ } else {
+ auto instance_object = MUST_OR_THROW_OOM(vm.heap().allocate<Instance>(realm, realm, result.release_value()));
+ promise->fulfill(instance_object);
+ }
+
+ return promise;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h
new file mode 100644
index 0000000000..782f1d73a2
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Optional.h>
+#include <LibJS/Forward.h>
+#include <LibJS/Heap/Handle.h>
+#include <LibJS/Runtime/Value.h>
+#include <LibWeb/Bindings/ExceptionOrUtils.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::WebAssembly {
+
+void visit_edges(JS::Cell::Visitor&);
+
+bool validate(JS::VM&, JS::Handle<JS::Object>& bytes);
+WebIDL::ExceptionOr<JS::Value> compile(JS::VM&, JS::Handle<JS::Object>& bytes);
+
+WebIDL::ExceptionOr<JS::Value> instantiate(JS::VM&, JS::Handle<JS::Object>& bytes, Optional<JS::Handle<JS::Object>>& import_object);
+WebIDL::ExceptionOr<JS::Value> instantiate(JS::VM&, Module const& module_object, Optional<JS::Handle<JS::Object>>& import_object);
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.idl b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.idl
new file mode 100644
index 0000000000..593cd3f32d
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssembly.idl
@@ -0,0 +1,17 @@
+#import <WebAssembly/Instance.idl>
+#import <WebAssembly/Module.idl>
+
+dictionary WebAssemblyInstantiatedSource {
+ required Module module;
+ required Instance instance;
+};
+
+// https://webassembly.github.io/spec/js-api/#webassembly-namespace
+[Exposed=*, WithGCVistor]
+namespace WebAssembly {
+ boolean validate(BufferSource bytes);
+ Promise<Module> compile(BufferSource bytes);
+
+ Promise<WebAssemblyInstantiatedSource> instantiate(BufferSource bytes, optional object importObject);
+ Promise<Instance> instantiate(Module moduleObject, optional object importObject);
+};
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
index 9ae7b5a66b..135600f6c7 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
@@ -28,86 +28,12 @@
namespace Web::Bindings {
-WebAssemblyObject::WebAssemblyObject(JS::Realm& realm)
- : Object(ConstructWithPrototypeTag::Tag, *realm.intrinsics().object_prototype())
-{
- s_abstract_machine.enable_instruction_count_limit();
-}
-
-JS::ThrowCompletionOr<void> WebAssemblyObject::initialize(JS::Realm& realm)
-{
- MUST_OR_THROW_OOM(Object::initialize(realm));
-
- u8 attr = JS::Attribute::Configurable | JS::Attribute::Writable | JS::Attribute::Enumerable;
- define_native_function(realm, "validate", validate, 1, attr);
- define_native_function(realm, "compile", compile, 1, attr);
- define_native_function(realm, "instantiate", instantiate, 1, attr);
-
- auto& memory_constructor = Bindings::ensure_web_constructor<MemoryPrototype>(realm, "WebAssembly.Memory"sv);
- define_direct_property("Memory", &memory_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
-
- auto& instance_constructor = Bindings::ensure_web_constructor<InstancePrototype>(realm, "WebAssembly.Instance"sv);
- define_direct_property("Instance", &instance_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
-
- auto& module_constructor = Bindings::ensure_web_constructor<ModulePrototype>(realm, "WebAssembly.Module"sv);
- define_direct_property("Module", &module_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
-
- auto& table_constructor = Bindings::ensure_web_constructor<TablePrototype>(realm, "WebAssembly.Table"sv);
- define_direct_property("Table", &table_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
-
- return {};
-}
-
Vector<NonnullOwnPtr<WebAssemblyObject::CompiledWebAssemblyModule>> WebAssemblyObject::s_compiled_modules;
Vector<NonnullOwnPtr<Wasm::ModuleInstance>> WebAssemblyObject::s_instantiated_modules;
Vector<WebAssemblyObject::ModuleCache> WebAssemblyObject::s_module_caches;
WebAssemblyObject::GlobalModuleCache WebAssemblyObject::s_global_cache;
Wasm::AbstractMachine WebAssemblyObject::s_abstract_machine;
-void WebAssemblyObject::visit_edges(Visitor& visitor)
-{
- Base::visit_edges(visitor);
-
- for (auto& entry : s_global_cache.function_instances)
- visitor.visit(entry.value);
- for (auto& module_cache : s_module_caches) {
- for (auto& entry : module_cache.function_instances)
- visitor.visit(entry.value);
- for (auto& entry : module_cache.memory_instances)
- visitor.visit(entry.value);
- for (auto& entry : module_cache.table_instances)
- visitor.visit(entry.value);
- }
-}
-
-JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::validate)
-{
- // 1. Let stableBytes be a copy of the bytes held by the buffer bytes.
- // Note: There's no need to copy the bytes here as the buffer data cannot change while we're compiling the module.
- auto buffer = TRY(vm.argument(0).to_object(vm));
-
- // 2. Compile stableBytes as a WebAssembly module and store the results as module.
- auto maybe_module = parse_module(vm, buffer);
-
- // 3. If module is error, return false.
- if (maybe_module.is_error())
- return JS::Value(false);
-
- // Drop the module from the cache, we're never going to refer to it.
- ScopeGuard drop_from_cache {
- [&] {
- (void)s_compiled_modules.take_last();
- }
- };
-
- // 3 continued - our "compile" step is lazy with validation, explicitly do the validation.
- if (s_abstract_machine.validate(s_compiled_modules[maybe_module.value()]->module).is_error())
- return JS::Value(false);
-
- // 4. Return true.
- return JS::Value(true);
-}
-
JS::ThrowCompletionOr<size_t> parse_module(JS::VM& vm, JS::Object* buffer_object)
{
ReadonlyBytes data;
@@ -139,30 +65,6 @@ JS::ThrowCompletionOr<size_t> parse_module(JS::VM& vm, JS::Object* buffer_object
return WebAssemblyObject::s_compiled_modules.size() - 1;
}
-JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::compile)
-{
- auto& realm = *vm.current_realm();
-
- // FIXME: This shouldn't block!
- auto buffer_or_error = vm.argument(0).to_object(vm);
- JS::Value rejection_value;
- if (buffer_or_error.is_error())
- rejection_value = *buffer_or_error.throw_completion().value();
-
- auto promise = JS::Promise::create(realm);
- if (!rejection_value.is_empty()) {
- promise->reject(rejection_value);
- return promise;
- }
- auto* buffer = buffer_or_error.release_value();
- auto result = parse_module(vm, buffer);
- if (result.is_error())
- promise->reject(*result.release_error().value());
- else
- promise->fulfill(MUST_OR_THROW_OOM(vm.heap().allocate<WebAssembly::Module>(realm, realm, result.release_value())));
- return promise;
-}
-
JS::ThrowCompletionOr<size_t> WebAssemblyObject::instantiate_module(JS::VM& vm, Wasm::Module const& module)
{
Wasm::Linker linker { module };
@@ -309,56 +211,6 @@ JS::ThrowCompletionOr<size_t> WebAssemblyObject::instantiate_module(JS::VM& vm,
return s_instantiated_modules.size() - 1;
}
-JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
-{
- auto& realm = *vm.current_realm();
-
- // FIXME: This shouldn't block!
- auto buffer_or_error = vm.argument(0).to_object(vm);
- auto promise = JS::Promise::create(realm);
- bool should_return_module = false;
- if (buffer_or_error.is_error()) {
- auto rejection_value = *buffer_or_error.throw_completion().value();
- promise->reject(rejection_value);
- return promise;
- }
- auto* buffer = buffer_or_error.release_value();
-
- Wasm::Module const* module { nullptr };
- if (is<JS::ArrayBuffer>(buffer) || is<JS::TypedArrayBase>(buffer)) {
- auto result = parse_module(vm, buffer);
- if (result.is_error()) {
- promise->reject(*result.release_error().value());
- return promise;
- }
- module = &WebAssemblyObject::s_compiled_modules.at(result.release_value())->module;
- should_return_module = true;
- } else if (is<WebAssembly::Module>(buffer)) {
- module = &static_cast<WebAssembly::Module*>(buffer)->module();
- } else {
- auto error = JS::TypeError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted("{} is not an ArrayBuffer or a Module", buffer->class_name())));
- promise->reject(error);
- return promise;
- }
- VERIFY(module);
-
- auto result = instantiate_module(vm, *module);
- if (result.is_error()) {
- promise->reject(*result.release_error().value());
- } else {
- auto instance_object = MUST_OR_THROW_OOM(vm.heap().allocate<WebAssembly::Instance>(realm, realm, result.release_value()));
- if (should_return_module) {
- auto object = JS::Object::create(realm, nullptr);
- object->define_direct_property("module", MUST_OR_THROW_OOM(vm.heap().allocate<WebAssembly::Module>(realm, realm, s_compiled_modules.size() - 1)), JS::default_attributes);
- object->define_direct_property("instance", instance_object, JS::default_attributes);
- promise->fulfill(object);
- } else {
- promise->fulfill(instance_object);
- }
- }
- return promise;
-}
-
JS::Value to_js_value(JS::VM& vm, Wasm::Value& wasm_value)
{
auto& realm = *vm.current_realm();
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
index bf465f56bb..9f627fbbe6 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
@@ -17,16 +17,8 @@ JS::NativeFunction* create_native_function(JS::VM&, Wasm::FunctionAddress addres
JS::Value to_js_value(JS::VM&, Wasm::Value& wasm_value);
JS::ThrowCompletionOr<Wasm::Value> to_webassembly_value(JS::VM&, JS::Value value, Wasm::ValueType const& type);
-class WebAssemblyObject final : public JS::Object {
- JS_OBJECT(WebAssemblyObject, JS::Object);
-
+class WebAssemblyObject final {
public:
- explicit WebAssemblyObject(JS::Realm&);
- virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
- virtual ~WebAssemblyObject() override = default;
-
- virtual void visit_edges(Cell::Visitor&) override;
-
static JS::ThrowCompletionOr<size_t> instantiate_module(JS::VM&, Wasm::Module const&);
struct CompiledWebAssemblyModule {
@@ -57,11 +49,6 @@ public:
static GlobalModuleCache s_global_cache;
static Wasm::AbstractMachine s_abstract_machine;
-
-private:
- JS_DECLARE_NATIVE_FUNCTION(validate);
- JS_DECLARE_NATIVE_FUNCTION(compile);
- JS_DECLARE_NATIVE_FUNCTION(instantiate);
};
}
diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake
index 1fe22bee79..e84c90cb3b 100644
--- a/Userland/Libraries/LibWeb/idl_files.cmake
+++ b/Userland/Libraries/LibWeb/idl_files.cmake
@@ -204,6 +204,7 @@ libweb_js_bindings(WebAssembly/Instance)
libweb_js_bindings(WebAssembly/Memory)
libweb_js_bindings(WebAssembly/Module)
libweb_js_bindings(WebAssembly/Table)
+libweb_js_bindings(WebAssembly/WebAssembly NAMESPACE)
libweb_js_bindings(WebGL/WebGLContextEvent)
libweb_js_bindings(WebGL/WebGLRenderingContext)
libweb_js_bindings(WebIDL/DOMException)