mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 21:37:34 +00:00
LibWeb: Add the WebAssembly.Instance constructor
This commit is contained in:
parent
de4cbc8f08
commit
8acc8339d1
6 changed files with 173 additions and 50 deletions
|
@ -222,6 +222,7 @@ set(SOURCES
|
||||||
UIEvents/EventNames.cpp
|
UIEvents/EventNames.cpp
|
||||||
UIEvents/MouseEvent.cpp
|
UIEvents/MouseEvent.cpp
|
||||||
URLEncoder.cpp
|
URLEncoder.cpp
|
||||||
|
WebAssembly/WebAssemblyInstanceConstructor.cpp
|
||||||
WebAssembly/WebAssemblyInstanceObject.cpp
|
WebAssembly/WebAssemblyInstanceObject.cpp
|
||||||
WebAssembly/WebAssemblyInstanceObjectPrototype.cpp
|
WebAssembly/WebAssemblyInstanceObjectPrototype.cpp
|
||||||
WebAssembly/WebAssemblyMemoryConstructor.cpp
|
WebAssembly/WebAssemblyMemoryConstructor.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -18,7 +18,7 @@
|
||||||
namespace Web::Bindings {
|
namespace Web::Bindings {
|
||||||
|
|
||||||
WebAssemblyInstanceObject::WebAssemblyInstanceObject(JS::GlobalObject& global_object, size_t index)
|
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)
|
, m_index(index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <LibJS/Runtime/TypedArray.h>
|
#include <LibJS/Runtime/TypedArray.h>
|
||||||
#include <LibWasm/AbstractMachine/Interpreter.h>
|
#include <LibWasm/AbstractMachine/Interpreter.h>
|
||||||
#include <LibWeb/Bindings/WindowObject.h>
|
#include <LibWeb/Bindings/WindowObject.h>
|
||||||
|
#include <LibWeb/WebAssembly/WebAssemblyInstanceConstructor.h>
|
||||||
#include <LibWeb/WebAssembly/WebAssemblyObject.h>
|
#include <LibWeb/WebAssembly/WebAssemblyObject.h>
|
||||||
|
|
||||||
namespace Web::Bindings {
|
namespace Web::Bindings {
|
||||||
|
@ -38,6 +39,12 @@ void WebAssemblyObject::initialize(JS::GlobalObject& global_object)
|
||||||
auto& memory_prototype = window.ensure_web_prototype<WebAssemblyMemoryPrototype>("WebAssemblyMemoryPrototype");
|
auto& memory_prototype = window.ensure_web_prototype<WebAssemblyMemoryPrototype>("WebAssemblyMemoryPrototype");
|
||||||
memory_prototype.define_property(vm.names.constructor, &memory_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
|
memory_prototype.define_property(vm.names.constructor, &memory_constructor, JS::Attribute::Writable | JS::Attribute::Configurable);
|
||||||
define_property("Memory", &memory_constructor);
|
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;
|
NonnullOwnPtrVector<WebAssemblyObject::CompiledWebAssemblyModule> WebAssemblyObject::s_compiled_modules;
|
||||||
|
@ -119,49 +126,17 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::compile)
|
||||||
return promise;
|
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!
|
Wasm::Linker linker { module };
|
||||||
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 };
|
|
||||||
HashMap<Wasm::Linker::Name, Wasm::ExternValue> resolved_imports;
|
HashMap<Wasm::Linker::Name, Wasm::ExternValue> resolved_imports;
|
||||||
auto import_argument = vm.argument(1);
|
auto import_argument = vm.argument(1);
|
||||||
if (!import_argument.is_undefined()) {
|
if (!import_argument.is_undefined()) {
|
||||||
[[maybe_unused]] auto import_object = import_argument.to_object(global_object);
|
[[maybe_unused]] auto import_object = import_argument.to_object(global_object);
|
||||||
if (take_exception_and_reject_if_needed())
|
if (auto exception = vm.exception()) {
|
||||||
return promise;
|
vm.clear_exception();
|
||||||
|
return exception->value();
|
||||||
|
}
|
||||||
|
|
||||||
dbgln("Trying to resolve stuff because import object was specified");
|
dbgln("Trying to resolve stuff because import object was specified");
|
||||||
for (const Wasm::Linker::Name& import_name : linker.unresolved_imports()) {
|
for (const Wasm::Linker::Name& import_name : linker.unresolved_imports()) {
|
||||||
|
@ -179,7 +154,7 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
|
||||||
import_name.type.visit(
|
import_name.type.visit(
|
||||||
[&](Wasm::TypeIndex index) {
|
[&](Wasm::TypeIndex index) {
|
||||||
dbgln("Trying to resolve a function {}::{}, type index {}", import_name.module, import_name.name, index.value());
|
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()
|
// FIXME: IsCallable()
|
||||||
if (!import_.is_function())
|
if (!import_.is_function())
|
||||||
return;
|
return;
|
||||||
|
@ -268,8 +243,10 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (take_exception_and_reject_if_needed())
|
if (auto exception = vm.exception()) {
|
||||||
return promise;
|
vm.clear_exception();
|
||||||
|
return exception->value();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
linker.link(resolved_imports);
|
linker.link(resolved_imports);
|
||||||
|
@ -279,22 +256,72 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
builder.append("LinkError: Missing ");
|
builder.append("LinkError: Missing ");
|
||||||
builder.join(' ', link_result.error().missing_imports);
|
builder.join(' ', link_result.error().missing_imports);
|
||||||
auto error = JS::TypeError::create(global_object, builder.build());
|
return JS::Value(JS::TypeError::create(global_object, builder.build()));
|
||||||
promise->reject(error);
|
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
if (instance_result.is_error()) {
|
||||||
// FIXME: Throw a LinkError instead.
|
// FIXME: Throw a LinkError instead.
|
||||||
auto error = JS::TypeError::create(global_object, instance_result.error().error);
|
return JS::Value(JS::TypeError::create(global_object, instance_result.error().error));
|
||||||
promise->reject(error);
|
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s_instantiated_modules.append(instance_result.release_value());
|
s_instantiated_modules.append(instance_result.release_value());
|
||||||
s_module_caches.empend();
|
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;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ public:
|
||||||
|
|
||||||
virtual void visit_edges(Cell::Visitor&) override;
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
|
static Result<size_t, JS::Value> instantiate_module(Wasm::Module const&, JS::VM&, JS::GlobalObject&);
|
||||||
|
|
||||||
struct CompiledWebAssemblyModule {
|
struct CompiledWebAssemblyModule {
|
||||||
explicit CompiledWebAssemblyModule(Wasm::Module&& module)
|
explicit CompiledWebAssemblyModule(Wasm::Module&& module)
|
||||||
: module(move(module))
|
: module(move(module))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue