mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
LibWasm+LibWeb: Partially resolve memory exports
This allows the JS side to access the wasm memory, assuming it's exported by the module. This can be used to draw stuff on the wasm side and display them from the js side, for example :^)
This commit is contained in:
parent
4a459d2430
commit
cf8b75c2e5
Notes:
sideshowbarker
2024-07-18 17:23:16 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/cf8b75c2e57 Pull-request: https://github.com/SerenityOS/serenity/pull/7395 Reviewed-by: https://github.com/linusg
6 changed files with 173 additions and 49 deletions
|
@ -16,8 +16,17 @@ namespace Wasm {
|
||||||
|
|
||||||
#define TRAP_IF_NOT(x) \
|
#define TRAP_IF_NOT(x) \
|
||||||
do { \
|
do { \
|
||||||
if (trap_if_not(x)) \
|
if (trap_if_not(x)) { \
|
||||||
|
dbgln_if(WASM_TRACE_DEBUG, "Trapped because {} failed, at line {}", #x, __LINE__); \
|
||||||
return; \
|
return; \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define TRAP_IF_NOT_NORETURN(x) \
|
||||||
|
do { \
|
||||||
|
if (trap_if_not(x)) { \
|
||||||
|
dbgln_if(WASM_TRACE_DEBUG, "Trapped because {} failed, at line {}", #x, __LINE__); \
|
||||||
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
void Interpreter::interpret(Configuration& configuration)
|
void Interpreter::interpret(Configuration& configuration)
|
||||||
|
@ -56,9 +65,8 @@ void Interpreter::branch_to_label(Configuration& configuration, LabelIndex index
|
||||||
configuration.stack().pop();
|
configuration.stack().pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push results in reverse
|
for (auto& result : results)
|
||||||
for (size_t i = results.size(); i > 0; --i)
|
configuration.stack().push(move(result));
|
||||||
configuration.stack().push(move(static_cast<Vector<NonnullOwnPtr<Value>>&>(results)[i - 1]));
|
|
||||||
|
|
||||||
configuration.ip() = label->continuation();
|
configuration.ip() = label->continuation();
|
||||||
}
|
}
|
||||||
|
@ -269,13 +277,12 @@ struct ConvertToRaw<double> {
|
||||||
Vector<NonnullOwnPtr<Value>> Interpreter::pop_values(Configuration& configuration, size_t count)
|
Vector<NonnullOwnPtr<Value>> Interpreter::pop_values(Configuration& configuration, size_t count)
|
||||||
{
|
{
|
||||||
Vector<NonnullOwnPtr<Value>> results;
|
Vector<NonnullOwnPtr<Value>> results;
|
||||||
// Pop results in order
|
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
auto top_of_stack = configuration.stack().pop();
|
auto top_of_stack = configuration.stack().pop();
|
||||||
if (auto value = top_of_stack.get_pointer<NonnullOwnPtr<Value>>())
|
if (auto value = top_of_stack.get_pointer<NonnullOwnPtr<Value>>())
|
||||||
results.append(move(*value));
|
results.prepend(move(*value));
|
||||||
else
|
else
|
||||||
trap_if_not(value);
|
TRAP_IF_NOT_NORETURN(value);
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
@ -361,9 +368,8 @@ void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push results in reverse
|
for (auto& result : results)
|
||||||
for (size_t i = 1; i < results.size() + 1; ++i)
|
configuration.stack().push(move(result));
|
||||||
configuration.stack().push(move(static_cast<Vector<NonnullOwnPtr<Value>>&>(results)[results.size() - i]));
|
|
||||||
|
|
||||||
if (instruction.opcode() == Instructions::structured_end)
|
if (instruction.opcode() == Instructions::structured_end)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -222,6 +222,7 @@ set(SOURCES
|
||||||
UIEvents/MouseEvent.cpp
|
UIEvents/MouseEvent.cpp
|
||||||
URLEncoder.cpp
|
URLEncoder.cpp
|
||||||
WebAssembly/WebAssemblyObject.cpp
|
WebAssembly/WebAssemblyObject.cpp
|
||||||
|
WebAssembly/WebAssemblyObjectPrototype.cpp
|
||||||
WebContentClient.cpp
|
WebContentClient.cpp
|
||||||
XHR/EventNames.cpp
|
XHR/EventNames.cpp
|
||||||
XHR/XMLHttpRequest.cpp
|
XHR/XMLHttpRequest.cpp
|
||||||
|
|
|
@ -4,11 +4,13 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/ScopeGuard.h>
|
||||||
#include <LibJS/Runtime/Array.h>
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/ArrayBuffer.h>
|
#include <LibJS/Runtime/ArrayBuffer.h>
|
||||||
#include <LibJS/Runtime/BigInt.h>
|
#include <LibJS/Runtime/BigInt.h>
|
||||||
#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/WebAssembly/WebAssemblyObject.h>
|
#include <LibWeb/WebAssembly/WebAssemblyObject.h>
|
||||||
|
|
||||||
namespace Web::Bindings {
|
namespace Web::Bindings {
|
||||||
|
@ -57,6 +59,11 @@ static Result<size_t, JS::Value> parse_module(JS::GlobalObject& global_object, J
|
||||||
}
|
}
|
||||||
InputMemoryStream stream { *bytes };
|
InputMemoryStream stream { *bytes };
|
||||||
auto module_result = Wasm::Module::parse(stream);
|
auto module_result = Wasm::Module::parse(stream);
|
||||||
|
ScopeGuard drain_errors {
|
||||||
|
[&] {
|
||||||
|
stream.handle_any_error();
|
||||||
|
}
|
||||||
|
};
|
||||||
if (module_result.is_error()) {
|
if (module_result.is_error()) {
|
||||||
// FIXME: Throw CompileError instead.
|
// FIXME: Throw CompileError instead.
|
||||||
auto error = JS::TypeError::create(global_object, Wasm::parse_error_to_string(module_result.error()));
|
auto error = JS::TypeError::create(global_object, Wasm::parse_error_to_string(module_result.error()));
|
||||||
|
@ -192,7 +199,11 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyObject::instantiate)
|
||||||
},
|
},
|
||||||
[&](const auto&) {
|
[&](const auto&) {
|
||||||
// FIXME: Implement these.
|
// FIXME: Implement these.
|
||||||
|
dbgln("Unimplemented import of non-function attempted");
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, "LinkError: Not Implemented");
|
||||||
});
|
});
|
||||||
|
if (vm.exception())
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (take_exception_and_reject_if_needed())
|
if (take_exception_and_reject_if_needed())
|
||||||
|
@ -231,7 +242,7 @@ WebAssemblyModuleObject::WebAssemblyModuleObject(JS::GlobalObject& global_object
|
||||||
}
|
}
|
||||||
|
|
||||||
WebAssemblyInstanceObject::WebAssemblyInstanceObject(JS::GlobalObject& global_object, size_t index)
|
WebAssemblyInstanceObject::WebAssemblyInstanceObject(JS::GlobalObject& global_object, size_t index)
|
||||||
: Object(*global_object.object_prototype())
|
: Object(static_cast<WindowObject&>(global_object).ensure_web_prototype<WebAssemblyInstancePrototype>(class_name()))
|
||||||
, m_index(index)
|
, m_index(index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -341,12 +352,6 @@ JS::NativeFunction* create_native_function(Wasm::FunctionAddress address, String
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebAssemblyInstancePrototype::initialize(JS::GlobalObject& global_object)
|
|
||||||
{
|
|
||||||
Object::initialize(global_object);
|
|
||||||
define_native_property("exports", exports_getter, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebAssemblyInstanceObject::initialize(JS::GlobalObject& global_object)
|
void WebAssemblyInstanceObject::initialize(JS::GlobalObject& global_object)
|
||||||
{
|
{
|
||||||
Object::initialize(global_object);
|
Object::initialize(global_object);
|
||||||
|
@ -361,6 +366,11 @@ void WebAssemblyInstanceObject::initialize(JS::GlobalObject& global_object)
|
||||||
auto function = create_native_function(address, export_.name(), global_object);
|
auto function = create_native_function(address, export_.name(), global_object);
|
||||||
m_exports_object->define_property(export_.name(), function);
|
m_exports_object->define_property(export_.name(), function);
|
||||||
},
|
},
|
||||||
|
[&](const Wasm::MemoryAddress& address) {
|
||||||
|
// FIXME: Cache this.
|
||||||
|
auto memory = heap().allocate<WebAssemblyMemoryObject>(global_object, global_object, address);
|
||||||
|
m_exports_object->define_property(export_.name(), memory);
|
||||||
|
},
|
||||||
[&](const auto&) {
|
[&](const auto&) {
|
||||||
// FIXME: Implement other exports!
|
// FIXME: Implement other exports!
|
||||||
});
|
});
|
||||||
|
@ -369,24 +379,64 @@ void WebAssemblyInstanceObject::initialize(JS::GlobalObject& global_object)
|
||||||
m_exports_object->set_integrity_level(IntegrityLevel::Frozen);
|
m_exports_object->set_integrity_level(IntegrityLevel::Frozen);
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_DEFINE_NATIVE_GETTER(WebAssemblyInstancePrototype::exports_getter)
|
|
||||||
{
|
|
||||||
auto this_value = vm.this_value(global_object);
|
|
||||||
auto this_object = this_value.to_object(global_object);
|
|
||||||
if (vm.exception())
|
|
||||||
return {};
|
|
||||||
if (!is<WebAssemblyInstanceObject>(this_object)) {
|
|
||||||
vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAn, "WebAssemblyInstance");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
auto object = static_cast<WebAssemblyInstanceObject*>(this_object);
|
|
||||||
return object->m_exports_object;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebAssemblyInstanceObject::visit_edges(Cell::Visitor& visitor)
|
void WebAssemblyInstanceObject::visit_edges(Cell::Visitor& visitor)
|
||||||
{
|
{
|
||||||
Object::visit_edges(visitor);
|
Object::visit_edges(visitor);
|
||||||
visitor.visit(m_exports_object);
|
visitor.visit(m_exports_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebAssemblyMemoryObject::WebAssemblyMemoryObject(JS::GlobalObject& global_object, Wasm::MemoryAddress address)
|
||||||
|
: JS::Object(global_object)
|
||||||
|
, 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();
|
||||||
|
|
||||||
|
return JS::ArrayBuffer::create(global_object, &memory->data());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <LibJS/Runtime/Object.h>
|
#include <LibJS/Runtime/Object.h>
|
||||||
#include <LibWasm/AbstractMachine/AbstractMachine.h>
|
#include <LibWasm/AbstractMachine/AbstractMachine.h>
|
||||||
#include <LibWeb/Forward.h>
|
#include <LibWeb/Forward.h>
|
||||||
|
#include <LibWeb/WebAssembly/WebAssemblyObjectPrototype.h>
|
||||||
|
|
||||||
namespace Web::Bindings {
|
namespace Web::Bindings {
|
||||||
|
|
||||||
|
@ -58,21 +59,6 @@ private:
|
||||||
size_t m_index { 0 };
|
size_t m_index { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class WebAssemblyInstancePrototype final : public JS::Object {
|
|
||||||
JS_OBJECT(WebAssemblyInstancePrototype, JS::Object);
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit WebAssemblyInstancePrototype(JS::GlobalObject& global_object)
|
|
||||||
: JS::Object(global_object)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void initialize(JS::GlobalObject&) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
JS_DECLARE_NATIVE_GETTER(exports_getter);
|
|
||||||
};
|
|
||||||
|
|
||||||
class WebAssemblyInstanceObject final : public JS::Object {
|
class WebAssemblyInstanceObject final : public JS::Object {
|
||||||
JS_OBJECT(WebAssemblyInstanceObject, JS::Object);
|
JS_OBJECT(WebAssemblyInstanceObject, JS::Object);
|
||||||
|
|
||||||
|
@ -93,4 +79,21 @@ private:
|
||||||
JS::Object* m_exports_object { nullptr };
|
JS::Object* m_exports_object { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class WebAssemblyMemoryObject final : public JS::Object {
|
||||||
|
JS_OBJECT(WebAssemblyModuleObject, JS::Object);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WebAssemblyMemoryObject(JS::GlobalObject&, Wasm::MemoryAddress);
|
||||||
|
virtual void initialize(JS::GlobalObject&) override;
|
||||||
|
virtual ~WebAssemblyMemoryObject() override = default;
|
||||||
|
|
||||||
|
auto address() const { return m_address; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(grow);
|
||||||
|
JS_DECLARE_NATIVE_GETTER(buffer);
|
||||||
|
|
||||||
|
Wasm::MemoryAddress m_address;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibWeb/WebAssembly/WebAssemblyObject.h>
|
||||||
|
#include <LibWeb/WebAssembly/WebAssemblyObjectPrototype.h>
|
||||||
|
|
||||||
|
namespace Web::Bindings {
|
||||||
|
|
||||||
|
void WebAssemblyInstancePrototype::initialize(JS::GlobalObject& global_object)
|
||||||
|
{
|
||||||
|
Object::initialize(global_object);
|
||||||
|
define_native_property("exports", exports_getter, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_DEFINE_NATIVE_GETTER(WebAssemblyInstancePrototype::exports_getter)
|
||||||
|
{
|
||||||
|
auto this_value = vm.this_value(global_object);
|
||||||
|
auto this_object = this_value.to_object(global_object);
|
||||||
|
if (vm.exception())
|
||||||
|
return {};
|
||||||
|
if (!is<WebAssemblyInstanceObject>(this_object)) {
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAn, "WebAssemblyInstance");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto object = static_cast<WebAssemblyInstanceObject*>(this_object);
|
||||||
|
return object->m_exports_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
|
#include <LibJS/Runtime/Object.h>
|
||||||
|
#include <LibJS/Runtime/VM.h>
|
||||||
|
#include <LibWeb/Forward.h>
|
||||||
|
|
||||||
|
namespace Web::Bindings {
|
||||||
|
|
||||||
|
class WebAssemblyInstancePrototype final : public JS::Object {
|
||||||
|
JS_OBJECT(WebAssemblyInstancePrototype, JS::Object);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WebAssemblyInstancePrototype(JS::GlobalObject& global_object)
|
||||||
|
: JS::Object(global_object)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void initialize(JS::GlobalObject&) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
JS_DECLARE_NATIVE_GETTER(exports_getter);
|
||||||
|
static JS::Handle<WebAssemblyInstancePrototype> s_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue