summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/WebAssembly
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2021-05-17 21:42:56 +0430
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2021-05-26 15:34:13 +0430
commitcf8b75c2e572a413b6865c2aec6efb10c50c2179 (patch)
tree7eb0a9d62c5f6fa3cd25e0db0183cf21416d9d95 /Userland/Libraries/LibWeb/WebAssembly
parent4a459d24305ae61e6f84980510d7ab069c0bc51e (diff)
downloadserenity-cf8b75c2e572a413b6865c2aec6efb10c50c2179.zip
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 :^)
Diffstat (limited to 'Userland/Libraries/LibWeb/WebAssembly')
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp84
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h33
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObjectPrototype.cpp32
-rw-r--r--Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObjectPrototype.h32
4 files changed, 149 insertions, 32 deletions
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
index 1fcf7ef73c..a8e8f82c8c 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp
@@ -4,11 +4,13 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include <AK/ScopeGuard.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibWasm/AbstractMachine/Interpreter.h>
+#include <LibWeb/Bindings/WindowObject.h>
#include <LibWeb/WebAssembly/WebAssemblyObject.h>
namespace Web::Bindings {
@@ -57,6 +59,11 @@ static Result<size_t, JS::Value> parse_module(JS::GlobalObject& global_object, J
}
InputMemoryStream stream { *bytes };
auto module_result = Wasm::Module::parse(stream);
+ ScopeGuard drain_errors {
+ [&] {
+ stream.handle_any_error();
+ }
+ };
if (module_result.is_error()) {
// FIXME: Throw CompileError instead.
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&) {
// 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())
@@ -231,7 +242,7 @@ WebAssemblyModuleObject::WebAssemblyModuleObject(JS::GlobalObject& global_object
}
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)
{
}
@@ -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)
{
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);
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&) {
// FIXME: Implement other exports!
});
@@ -369,24 +379,64 @@ void WebAssemblyInstanceObject::initialize(JS::GlobalObject& global_object)
m_exports_object->set_integrity_level(IntegrityLevel::Frozen);
}
-JS_DEFINE_NATIVE_GETTER(WebAssemblyInstancePrototype::exports_getter)
+void WebAssemblyInstanceObject::visit_edges(Cell::Visitor& visitor)
+{
+ Object::visit_edges(visitor);
+ visitor.visit(m_exports_object);
+}
+
+WebAssemblyMemoryObject::WebAssemblyMemoryObject(JS::GlobalObject& global_object, Wasm::MemoryAddress address)
+ : JS::Object(global_object)
+ , m_address(address)
{
- auto this_value = vm.this_value(global_object);
- auto this_object = this_value.to_object(global_object);
+}
+
+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 {};
- if (!is<WebAssemblyInstanceObject>(this_object)) {
- vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAn, "WebAssemblyInstance");
+ 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 {};
}
- auto object = static_cast<WebAssemblyInstanceObject*>(this_object);
- return object->m_exports_object;
+
+ return JS::Value(static_cast<u32>(previous_size));
}
-void WebAssemblyInstanceObject::visit_edges(Cell::Visitor& visitor)
+JS_DEFINE_NATIVE_GETTER(WebAssemblyMemoryObject::buffer)
{
- Object::visit_edges(visitor);
- visitor.visit(m_exports_object);
+ 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());
}
}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
index 35b3aa3094..173cf4df7d 100644
--- a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.h
@@ -9,6 +9,7 @@
#include <LibJS/Runtime/Object.h>
#include <LibWasm/AbstractMachine/AbstractMachine.h>
#include <LibWeb/Forward.h>
+#include <LibWeb/WebAssembly/WebAssemblyObjectPrototype.h>
namespace Web::Bindings {
@@ -58,21 +59,6 @@ private:
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 {
JS_OBJECT(WebAssemblyInstanceObject, JS::Object);
@@ -93,4 +79,21 @@ private:
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;
+};
+
}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObjectPrototype.cpp b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObjectPrototype.cpp
new file mode 100644
index 0000000000..978c2dcad9
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObjectPrototype.cpp
@@ -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;
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObjectPrototype.h b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObjectPrototype.h
new file mode 100644
index 0000000000..b65759598b
--- /dev/null
+++ b/Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObjectPrototype.h
@@ -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;
+};
+
+}