mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 22:57:44 +00:00
LibWeb: Implement the WebAssembly Memory object and Memory imports
This commit is contained in:
parent
eb5c92d4e2
commit
5c90c389c3
7 changed files with 257 additions and 56 deletions
|
@ -221,6 +221,8 @@ set(SOURCES
|
||||||
UIEvents/EventNames.cpp
|
UIEvents/EventNames.cpp
|
||||||
UIEvents/MouseEvent.cpp
|
UIEvents/MouseEvent.cpp
|
||||||
URLEncoder.cpp
|
URLEncoder.cpp
|
||||||
|
WebAssembly/WebAssemblyMemoryConstructor.cpp
|
||||||
|
WebAssembly/WebAssemblyMemoryPrototype.cpp
|
||||||
WebAssembly/WebAssemblyObject.cpp
|
WebAssembly/WebAssemblyObject.cpp
|
||||||
WebAssembly/WebAssemblyObjectPrototype.cpp
|
WebAssembly/WebAssemblyObjectPrototype.cpp
|
||||||
WebContentClient.cpp
|
WebContentClient.cpp
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* 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/WebAssemblyMemoryConstructor.h>
|
||||||
|
#include <LibWeb/WebAssembly/WebAssemblyMemoryPrototype.h>
|
||||||
|
#include <LibWeb/WebAssembly/WebAssemblyObject.h>
|
||||||
|
|
||||||
|
namespace Web::Bindings {
|
||||||
|
|
||||||
|
WebAssemblyMemoryConstructor::WebAssemblyMemoryConstructor(JS::GlobalObject& global_object)
|
||||||
|
: NativeFunction(*global_object.function_prototype())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WebAssemblyMemoryConstructor::~WebAssemblyMemoryConstructor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::Value WebAssemblyMemoryConstructor::call()
|
||||||
|
{
|
||||||
|
vm().throw_exception<JS::TypeError>(global_object(), JS::ErrorType::ConstructorWithoutNew, "WebAssemblyMemory");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::Value WebAssemblyMemoryConstructor::construct(Function&)
|
||||||
|
{
|
||||||
|
auto& vm = this->vm();
|
||||||
|
auto& global_object = this->global_object();
|
||||||
|
|
||||||
|
auto descriptor = vm.argument(0).to_object(global_object);
|
||||||
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto initial_value = descriptor->get_own_property("initial", {}, JS::AllowSideEffects::No);
|
||||||
|
auto maximum_value = descriptor->get_own_property("maximum", {}, JS::AllowSideEffects::No);
|
||||||
|
|
||||||
|
if (initial_value.is_empty()) {
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Number");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto initial = initial_value.to_u32(global_object);
|
||||||
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
Optional<u32> maximum;
|
||||||
|
|
||||||
|
if (!maximum_value.is_empty()) {
|
||||||
|
maximum = maximum_value.to_u32(global_object);
|
||||||
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto address = WebAssemblyObject::s_abstract_machine.store().allocate(Wasm::MemoryType { Wasm::Limits { initial, maximum } });
|
||||||
|
if (!address.has_value()) {
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, "Wasm Memory allocation failed");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm.heap().allocate<WebAssemblyMemoryObject>(global_object, global_object, *address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebAssemblyMemoryConstructor::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<WebAssemblyMemoryPrototype>("WebAssembly.Memory"));
|
||||||
|
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 WebAssemblyMemoryConstructor : public JS::NativeFunction {
|
||||||
|
JS_OBJECT(WebAssemblyMemoryConstructor, JS::NativeFunction);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WebAssemblyMemoryConstructor(JS::GlobalObject&);
|
||||||
|
virtual void initialize(JS::GlobalObject&) override;
|
||||||
|
virtual ~WebAssemblyMemoryConstructor() override;
|
||||||
|
|
||||||
|
virtual JS::Value call() override;
|
||||||
|
virtual JS::Value construct(JS::Function& new_target) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual bool has_constructor() const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/TypedArray.h>
|
||||||
|
#include <LibWeb/WebAssembly/WebAssemblyMemoryPrototype.h>
|
||||||
|
#include <LibWeb/WebAssembly/WebAssemblyObject.h>
|
||||||
|
|
||||||
|
namespace Web::Bindings {
|
||||||
|
|
||||||
|
void WebAssemblyMemoryPrototype::initialize(JS::GlobalObject& global_object)
|
||||||
|
{
|
||||||
|
Object::initialize(global_object);
|
||||||
|
define_native_property("buffer", buffer_getter, nullptr);
|
||||||
|
define_native_function("grow", grow);
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_DEFINE_NATIVE_FUNCTION(WebAssemblyMemoryPrototype::grow)
|
||||||
|
{
|
||||||
|
auto page_count = 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<WebAssemblyMemoryObject>(this_object)) {
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Memory");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto* memory_object = static_cast<WebAssemblyMemoryObject*>(this_object);
|
||||||
|
auto address = memory_object->address();
|
||||||
|
auto* memory = WebAssemblyObject::s_abstract_machine.store().get(address);
|
||||||
|
if (!memory)
|
||||||
|
return JS::js_undefined();
|
||||||
|
|
||||||
|
auto previous_size = memory->size() / Wasm::Constants::page_size;
|
||||||
|
if (!memory->grow(page_count * Wasm::Constants::page_size)) {
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, "Memory.grow() grows past the stated limit of the memory instance");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return JS::Value(static_cast<u32>(previous_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_DEFINE_NATIVE_GETTER(WebAssemblyMemoryPrototype::buffer_getter)
|
||||||
|
{
|
||||||
|
auto* this_object = vm.this_value(global_object).to_object(global_object);
|
||||||
|
if (!this_object || !is<WebAssemblyMemoryObject>(this_object)) {
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Memory");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto* memory_object = static_cast<WebAssemblyMemoryObject*>(this_object);
|
||||||
|
auto address = memory_object->address();
|
||||||
|
auto* memory = WebAssemblyObject::s_abstract_machine.store().get(address);
|
||||||
|
if (!memory)
|
||||||
|
return JS::js_undefined();
|
||||||
|
|
||||||
|
auto array_buffer = JS::ArrayBuffer::create(global_object, &memory->data());
|
||||||
|
array_buffer->set_detach_key(JS::js_string(vm, "WebAssembly.Memory"));
|
||||||
|
return array_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WebAssemblyMemoryConstructor.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 WebAssemblyMemoryPrototype final : public JS::Object {
|
||||||
|
JS_OBJECT(WebAssemblyMemoryPrototype, JS::Object);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WebAssemblyMemoryPrototype(JS::GlobalObject& global_object)
|
||||||
|
: JS::Object(global_object)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void initialize(JS::GlobalObject&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(grow);
|
||||||
|
JS_DECLARE_NATIVE_GETTER(buffer_getter);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "WebAssemblyMemoryPrototype.h"
|
||||||
#include <AK/ScopeGuard.h>
|
#include <AK/ScopeGuard.h>
|
||||||
#include <LibJS/Runtime/Array.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/ArrayBuffer.h>
|
#include <LibJS/Runtime/ArrayBuffer.h>
|
||||||
|
@ -31,6 +32,15 @@ void WebAssemblyObject::initialize(JS::GlobalObject& global_object)
|
||||||
define_native_function("validate", validate, 1);
|
define_native_function("validate", validate, 1);
|
||||||
define_native_function("compile", compile, 1);
|
define_native_function("compile", compile, 1);
|
||||||
define_native_function("instantiate", instantiate, 1);
|
define_native_function("instantiate", instantiate, 1);
|
||||||
|
|
||||||
|
auto& vm = global_object.vm();
|
||||||
|
|
||||||
|
auto& window = static_cast<WindowObject&>(global_object);
|
||||||
|
auto& memory_constructor = window.ensure_web_prototype<WebAssemblyMemoryConstructor>("WebAssembly.Memory");
|
||||||
|
memory_constructor.define_property(vm.names.name, js_string(vm, "WebAssembly.Memory"), JS::Attribute::Configurable);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullOwnPtrVector<WebAssemblyObject::CompiledWebAssemblyModule> WebAssemblyObject::s_compiled_modules;
|
NonnullOwnPtrVector<WebAssemblyObject::CompiledWebAssemblyModule> WebAssemblyObject::s_compiled_modules;
|
||||||
|
@ -213,6 +223,45 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
|
||||||
|
|
||||||
resolved_imports.set(import_name, Wasm::ExternValue { Wasm::FunctionAddress { *address } });
|
resolved_imports.set(import_name, Wasm::ExternValue { Wasm::FunctionAddress { *address } });
|
||||||
},
|
},
|
||||||
|
[&](Wasm::GlobalType const& type) {
|
||||||
|
Optional<Wasm::GlobalAddress> address;
|
||||||
|
// https://webassembly.github.io/spec/js-api/#read-the-imports step 5.1
|
||||||
|
if (import_.is_number() || import_.is_bigint()) {
|
||||||
|
if (import_.is_number() && type.type().kind() == Wasm::ValueType::I64) {
|
||||||
|
// FIXME: Throw a LinkError instead.
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, "LinkError: Import resolution attempted to cast a Number to a BigInteger");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (import_.is_bigint() && type.type().kind() != Wasm::ValueType::I64) {
|
||||||
|
// FIXME: Throw a LinkError instead.
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, "LinkError: Import resolution attempted to cast a BigInteger to a Number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto cast_value = to_webassembly_value(import_, type.type(), global_object);
|
||||||
|
if (!cast_value.has_value())
|
||||||
|
return;
|
||||||
|
address = s_abstract_machine.store().allocate({ type.type(), false }, cast_value.release_value());
|
||||||
|
} else {
|
||||||
|
// FIXME: https://webassembly.github.io/spec/js-api/#read-the-imports step 5.2
|
||||||
|
// if v implements Global
|
||||||
|
// let globaladdr be v.[[Global]]
|
||||||
|
|
||||||
|
// FIXME: Throw a LinkError instead
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, "LinkError: Invalid value for global type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved_imports.set(import_name, Wasm::ExternValue { *address });
|
||||||
|
},
|
||||||
|
[&](Wasm::MemoryType const&) {
|
||||||
|
if (!import_.is_object() || !is<WebAssemblyMemoryObject>(import_.as_object())) {
|
||||||
|
// FIXME: Throw a LinkError instead
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, "LinkError: Expected an instance of WebAssembly.Memory for a memory import");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto address = static_cast<WebAssemblyMemoryObject const&>(import_.as_object()).address();
|
||||||
|
resolved_imports.set(import_name, Wasm::ExternValue { address });
|
||||||
|
},
|
||||||
[&](const auto&) {
|
[&](const auto&) {
|
||||||
// FIXME: Implement these.
|
// FIXME: Implement these.
|
||||||
dbgln("Unimplemented import of non-function attempted");
|
dbgln("Unimplemented import of non-function attempted");
|
||||||
|
@ -420,59 +469,9 @@ void WebAssemblyInstanceObject::visit_edges(Cell::Visitor& visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
WebAssemblyMemoryObject::WebAssemblyMemoryObject(JS::GlobalObject& global_object, Wasm::MemoryAddress address)
|
WebAssemblyMemoryObject::WebAssemblyMemoryObject(JS::GlobalObject& global_object, Wasm::MemoryAddress address)
|
||||||
: JS::Object(global_object)
|
: Object(static_cast<WindowObject&>(global_object).ensure_web_prototype<WebAssemblyMemoryPrototype>(class_name()))
|
||||||
, m_address(address)
|
, m_address(address)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebAssemblyMemoryObject::initialize(JS::GlobalObject& global_object)
|
|
||||||
{
|
|
||||||
Object::initialize(global_object);
|
|
||||||
define_native_function("grow", grow, 1);
|
|
||||||
define_native_property("buffer", buffer, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_DEFINE_NATIVE_FUNCTION(WebAssemblyMemoryObject::grow)
|
|
||||||
{
|
|
||||||
auto page_count = 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<WebAssemblyMemoryObject>(this_object)) {
|
|
||||||
vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Memory");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
auto* memory_object = static_cast<WebAssemblyMemoryObject*>(this_object);
|
|
||||||
auto address = memory_object->m_address;
|
|
||||||
auto* memory = WebAssemblyObject::s_abstract_machine.store().get(address);
|
|
||||||
if (!memory)
|
|
||||||
return JS::js_undefined();
|
|
||||||
|
|
||||||
auto previous_size = memory->size() / Wasm::Constants::page_size;
|
|
||||||
if (!memory->grow(page_count * Wasm::Constants::page_size)) {
|
|
||||||
vm.throw_exception<JS::TypeError>(global_object, "Memory.grow() grows past the stated limit of the memory instance");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return JS::Value(static_cast<u32>(previous_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_DEFINE_NATIVE_GETTER(WebAssemblyMemoryObject::buffer)
|
|
||||||
{
|
|
||||||
auto* this_object = vm.this_value(global_object).to_object(global_object);
|
|
||||||
if (!this_object || !is<WebAssemblyMemoryObject>(this_object)) {
|
|
||||||
vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Memory");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
auto* memory_object = static_cast<WebAssemblyMemoryObject*>(this_object);
|
|
||||||
auto address = memory_object->m_address;
|
|
||||||
auto* memory = WebAssemblyObject::s_abstract_machine.store().get(address);
|
|
||||||
if (!memory)
|
|
||||||
return JS::js_undefined();
|
|
||||||
|
|
||||||
auto array_buffer = JS::ArrayBuffer::create(global_object, &memory->data());
|
|
||||||
array_buffer->set_detach_key(JS::js_string(vm, "WebAssembly.Memory"));
|
|
||||||
return array_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,19 +95,15 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
class WebAssemblyMemoryObject final : public JS::Object {
|
class WebAssemblyMemoryObject final : public JS::Object {
|
||||||
JS_OBJECT(WebAssemblyModuleObject, JS::Object);
|
JS_OBJECT(WebAssemblyMemoryObject, JS::Object);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit WebAssemblyMemoryObject(JS::GlobalObject&, Wasm::MemoryAddress);
|
explicit WebAssemblyMemoryObject(JS::GlobalObject&, Wasm::MemoryAddress);
|
||||||
virtual void initialize(JS::GlobalObject&) override;
|
|
||||||
virtual ~WebAssemblyMemoryObject() override = default;
|
virtual ~WebAssemblyMemoryObject() override = default;
|
||||||
|
|
||||||
auto address() const { return m_address; }
|
auto address() const { return m_address; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JS_DECLARE_NATIVE_FUNCTION(grow);
|
|
||||||
JS_DECLARE_NATIVE_GETTER(buffer);
|
|
||||||
|
|
||||||
Wasm::MemoryAddress m_address;
|
Wasm::MemoryAddress m_address;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue