mirror of
https://github.com/RGBCube/serenity
synced 2025-05-28 16:35:08 +00:00
LibWeb+LibWasm: Implement the WebAssembly.Table object
This commit is contained in:
parent
d52a26de3f
commit
09dd397160
9 changed files with 433 additions and 1 deletions
|
@ -301,6 +301,23 @@ public:
|
|||
auto& elements() { return m_elements; }
|
||||
auto& type() const { return m_type; }
|
||||
|
||||
bool grow(size_t size_to_grow, Reference const& fill_value)
|
||||
{
|
||||
if (size_to_grow == 0)
|
||||
return true;
|
||||
auto new_size = m_elements.size() + size_to_grow;
|
||||
if (auto max = m_type.limits().max(); max.has_value()) {
|
||||
if (max.value() < new_size)
|
||||
return false;
|
||||
}
|
||||
auto previous_size = m_elements.size();
|
||||
if (!m_elements.try_resize(new_size))
|
||||
return false;
|
||||
for (size_t i = previous_size; i < m_elements.size(); ++i)
|
||||
m_elements[i] = fill_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Vector<Optional<Reference>> m_elements;
|
||||
TableType const& m_type;
|
||||
|
|
|
@ -231,6 +231,9 @@ set(SOURCES
|
|||
WebAssembly/WebAssemblyModuleConstructor.cpp
|
||||
WebAssembly/WebAssemblyModuleObject.cpp
|
||||
WebAssembly/WebAssemblyObject.cpp
|
||||
WebAssembly/WebAssemblyTableConstructor.cpp
|
||||
WebAssembly/WebAssemblyTableObject.cpp
|
||||
WebAssembly/WebAssemblyTablePrototype.cpp
|
||||
WebContentClient.cpp
|
||||
XHR/EventNames.cpp
|
||||
XHR/XMLHttpRequest.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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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; }
|
||||
};
|
||||
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue