Selaa lähdekoodia

LibWeb: Add the WebAssembly.Instance constructor

Ali Mohammad Pur 4 vuotta sitten
vanhempi
commit
8acc8339d1

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -222,6 +222,7 @@ set(SOURCES
     UIEvents/EventNames.cpp
     UIEvents/MouseEvent.cpp
     URLEncoder.cpp
+    WebAssembly/WebAssemblyInstanceConstructor.cpp
     WebAssembly/WebAssemblyInstanceObject.cpp
     WebAssembly/WebAssemblyInstanceObjectPrototype.cpp
     WebAssembly/WebAssemblyMemoryConstructor.cpp

+ 65 - 0
Userland/Libraries/LibWeb/WebAssembly/WebAssemblyInstanceConstructor.cpp

@@ -0,0 +1,65 @@
+/*
+ * 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/WebAssemblyInstanceConstructor.h>
+#include <LibWeb/WebAssembly/WebAssemblyInstanceObject.h>
+#include <LibWeb/WebAssembly/WebAssemblyInstanceObjectPrototype.h>
+#include <LibWeb/WebAssembly/WebAssemblyObject.h>
+
+namespace Web::Bindings {
+
+WebAssemblyInstanceConstructor::WebAssemblyInstanceConstructor(JS::GlobalObject& global_object)
+    : NativeFunction(*global_object.function_prototype())
+{
+}
+
+WebAssemblyInstanceConstructor::~WebAssemblyInstanceConstructor()
+{
+}
+
+JS::Value WebAssemblyInstanceConstructor::call()
+{
+    vm().throw_exception<JS::TypeError>(global_object(), JS::ErrorType::ConstructorWithoutNew, "WebAssemblyInstance");
+    return {};
+}
+
+JS::Value WebAssemblyInstanceConstructor::construct(FunctionObject&)
+{
+    auto& vm = this->vm();
+    auto& global_object = this->global_object();
+
+    auto module_argument = vm.argument(0).to_object(global_object);
+    if (vm.exception())
+        return {};
+
+    if (!is<WebAssemblyModuleObject>(module_argument)) {
+        vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "WebAssembly.Module");
+        return {};
+    }
+
+    auto& module_object = static_cast<WebAssemblyModuleObject&>(*module_argument);
+    auto result = WebAssemblyObject::instantiate_module(module_object.module(), vm, global_object);
+    if (result.is_error()) {
+        vm.throw_exception(global_object, result.release_error());
+        return {};
+    }
+    return heap().allocate<WebAssemblyInstanceObject>(global_object, global_object, result.value());
+}
+
+void WebAssemblyInstanceConstructor::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<WebAssemblyInstancePrototype>("WebAssemblyInstancePrototype"));
+    define_property(vm.names.length, JS::Value(1), JS::Attribute::Configurable);
+}
+
+}

+ 28 - 0
Userland/Libraries/LibWeb/WebAssembly/WebAssemblyInstanceConstructor.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 WebAssemblyInstanceConstructor : public JS::NativeFunction {
+    JS_OBJECT(WebAssemblyInstanceConstructor, JS::NativeFunction);
+
+public:
+    explicit WebAssemblyInstanceConstructor(JS::GlobalObject&);
+    virtual void initialize(JS::GlobalObject&) override;
+    virtual ~WebAssemblyInstanceConstructor() override;
+
+    virtual JS::Value call() override;
+    virtual JS::Value construct(JS::FunctionObject& new_target) override;
+
+private:
+    virtual bool has_constructor() const override { return true; }
+};
+
+}

+ 1 - 1
Userland/Libraries/LibWeb/WebAssembly/WebAssemblyInstanceObject.cpp

@@ -18,7 +18,7 @@
 namespace Web::Bindings {
 
 WebAssemblyInstanceObject::WebAssemblyInstanceObject(JS::GlobalObject& global_object, size_t index)
-    : Object(static_cast<Web::Bindings::WindowObject&>(global_object).ensure_web_prototype<WebAssemblyInstancePrototype>(class_name()))
+    : Object(static_cast<Web::Bindings::WindowObject&>(global_object).ensure_web_prototype<WebAssemblyInstancePrototype>("WebAssemblyInstancePrototype"))
     , m_index(index)
 {
 }

+ 76 - 49
Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp

@@ -13,6 +13,7 @@
 #include <LibJS/Runtime/TypedArray.h>
 #include <LibWasm/AbstractMachine/Interpreter.h>
 #include <LibWeb/Bindings/WindowObject.h>
+#include <LibWeb/WebAssembly/WebAssemblyInstanceConstructor.h>
 #include <LibWeb/WebAssembly/WebAssemblyObject.h>
 
 namespace Web::Bindings {
@@ -38,6 +39,12 @@ void WebAssemblyObject::initialize(JS::GlobalObject& global_object)
     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);
+
+    auto& instance_constructor = window.ensure_web_constructor<WebAssemblyInstanceConstructor>("WebAssembly.Instance");
+    instance_constructor.define_property(vm.names.name, js_string(vm, "WebAssembly.Instance"), JS::Attribute::Configurable);
+    auto& instance_prototype = window.ensure_web_prototype<WebAssemblyInstancePrototype>("WebAssemblyInstancePrototype");
+    instance_prototype.define_property(vm.names.constructor, &instance_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
+    define_property("Instance", &instance_constructor);
 }
 
 NonnullOwnPtrVector<WebAssemblyObject::CompiledWebAssemblyModule> WebAssemblyObject::s_compiled_modules;
@@ -119,49 +126,17 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::compile)
     return promise;
 }
 
-JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
+Result<size_t, JS::Value> WebAssemblyObject::instantiate_module(Wasm::Module const& module, JS::VM& vm, JS::GlobalObject& global_object)
 {
-    // FIXME: This shouldn't block!
-    auto buffer = vm.argument(0).to_object(global_object);
-    auto promise = JS::Promise::create(global_object);
-    auto take_exception_and_reject_if_needed = [&] {
-        if (vm.exception()) {
-            auto rejection_value = vm.exception()->value();
-            vm.clear_exception();
-            promise->reject(rejection_value);
-            return true;
-        }
-
-        return false;
-    };
-
-    if (take_exception_and_reject_if_needed())
-        return promise;
-
-    const Wasm::Module* module { nullptr };
-    if (is<JS::ArrayBuffer>(buffer) || is<JS::TypedArrayBase>(buffer)) {
-        auto result = parse_module(global_object, buffer);
-        if (result.is_error()) {
-            promise->reject(result.error());
-            return promise;
-        }
-        module = &WebAssemblyObject::s_compiled_modules.at(result.value()).module;
-    } else if (is<WebAssemblyModuleObject>(buffer)) {
-        module = &static_cast<WebAssemblyModuleObject*>(buffer)->module();
-    } else {
-        auto error = JS::TypeError::create(global_object, String::formatted("{} is not an ArrayBuffer or a Module", buffer->class_name()));
-        promise->reject(error);
-        return promise;
-    }
-    VERIFY(module);
-
-    Wasm::Linker linker { *module };
+    Wasm::Linker linker { module };
     HashMap<Wasm::Linker::Name, Wasm::ExternValue> resolved_imports;
     auto import_argument = vm.argument(1);
     if (!import_argument.is_undefined()) {
         [[maybe_unused]] auto import_object = import_argument.to_object(global_object);
-        if (take_exception_and_reject_if_needed())
-            return promise;
+        if (auto exception = vm.exception()) {
+            vm.clear_exception();
+            return exception->value();
+        }
 
         dbgln("Trying to resolve stuff because import object was specified");
         for (const Wasm::Linker::Name& import_name : linker.unresolved_imports()) {
@@ -179,7 +154,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
             import_name.type.visit(
                 [&](Wasm::TypeIndex index) {
                     dbgln("Trying to resolve a function {}::{}, type index {}", import_name.module, import_name.name, index.value());
-                    auto& type = module->type(index);
+                    auto& type = module.type(index);
                     // FIXME: IsCallable()
                     if (!import_.is_function())
                         return;
@@ -268,8 +243,10 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
                 break;
         }
 
-        if (take_exception_and_reject_if_needed())
-            return promise;
+        if (auto exception = vm.exception()) {
+            vm.clear_exception();
+            return exception->value();
+        }
     }
 
     linker.link(resolved_imports);
@@ -279,22 +256,72 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
         StringBuilder builder;
         builder.append("LinkError: Missing ");
         builder.join(' ', link_result.error().missing_imports);
-        auto error = JS::TypeError::create(global_object, builder.build());
-        promise->reject(error);
-        return promise;
+        return JS::Value(JS::TypeError::create(global_object, builder.build()));
     }
 
-    auto instance_result = s_abstract_machine.instantiate(*module, link_result.release_value());
+    auto instance_result = s_abstract_machine.instantiate(module, link_result.release_value());
     if (instance_result.is_error()) {
         // FIXME: Throw a LinkError instead.
-        auto error = JS::TypeError::create(global_object, instance_result.error().error);
-        promise->reject(error);
-        return promise;
+        return JS::Value(JS::TypeError::create(global_object, instance_result.error().error));
     }
 
     s_instantiated_modules.append(instance_result.release_value());
     s_module_caches.empend();
-    promise->fulfill(vm.heap().allocate<WebAssemblyInstanceObject>(global_object, global_object, s_instantiated_modules.size() - 1));
+    return s_instantiated_modules.size() - 1;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
+{
+    // FIXME: This shouldn't block!
+    auto buffer = vm.argument(0).to_object(global_object);
+    auto promise = JS::Promise::create(global_object);
+    bool should_return_module = false;
+    auto take_exception_and_reject_if_needed = [&] {
+        if (vm.exception()) {
+            auto rejection_value = vm.exception()->value();
+            vm.clear_exception();
+            promise->reject(rejection_value);
+            return true;
+        }
+
+        return false;
+    };
+
+    if (take_exception_and_reject_if_needed())
+        return promise;
+
+    const Wasm::Module* module { nullptr };
+    if (is<JS::ArrayBuffer>(buffer) || is<JS::TypedArrayBase>(buffer)) {
+        auto result = parse_module(global_object, buffer);
+        if (result.is_error()) {
+            promise->reject(result.error());
+            return promise;
+        }
+        module = &WebAssemblyObject::s_compiled_modules.at(result.value()).module;
+        should_return_module = true;
+    } else if (is<WebAssemblyModuleObject>(buffer)) {
+        module = &static_cast<WebAssemblyModuleObject*>(buffer)->module();
+    } else {
+        auto error = JS::TypeError::create(global_object, String::formatted("{} is not an ArrayBuffer or a Module", buffer->class_name()));
+        promise->reject(error);
+        return promise;
+    }
+    VERIFY(module);
+
+    auto result = instantiate_module(*module, vm, global_object);
+    if (result.is_error()) {
+        promise->reject(result.release_error());
+    } else {
+        auto instance_object = vm.heap().allocate<WebAssemblyInstanceObject>(global_object, global_object, result.value());
+        if (should_return_module) {
+            auto object = JS::Object::create(global_object, nullptr);
+            object->put("module", vm.heap().allocate<WebAssemblyModuleObject>(global_object, global_object, s_compiled_modules.size() - 1));
+            object->put("instance", instance_object);
+            promise->fulfill(object);
+        } else {
+            promise->fulfill(instance_object);
+        }
+    }
     return promise;
 }
 

+ 2 - 0
Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h

@@ -28,6 +28,8 @@ public:
 
     virtual void visit_edges(Cell::Visitor&) override;
 
+    static Result<size_t, JS::Value> instantiate_module(Wasm::Module const&, JS::VM&, JS::GlobalObject&);
+
     struct CompiledWebAssemblyModule {
         explicit CompiledWebAssemblyModule(Wasm::Module&& module)
             : module(move(module))