diff options
6 files changed, 169 insertions, 45 deletions
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp index 27d458b11c..6e789c8671 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp @@ -14,10 +14,19 @@ namespace Wasm { -#define TRAP_IF_NOT(x) \ - do { \ - if (trap_if_not(x)) \ - return; \ +#define TRAP_IF_NOT(x) \ + do { \ + if (trap_if_not(x)) { \ + dbgln_if(WASM_TRACE_DEBUG, "Trapped because {} failed, at line {}", #x, __LINE__); \ + 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) void Interpreter::interpret(Configuration& configuration) @@ -56,9 +65,8 @@ void Interpreter::branch_to_label(Configuration& configuration, LabelIndex index configuration.stack().pop(); } - // Push results in reverse - for (size_t i = results.size(); i > 0; --i) - configuration.stack().push(move(static_cast<Vector<NonnullOwnPtr<Value>>&>(results)[i - 1])); + for (auto& result : results) + configuration.stack().push(move(result)); 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>> results; - // Pop results in order for (size_t i = 0; i < count; ++i) { auto top_of_stack = configuration.stack().pop(); if (auto value = top_of_stack.get_pointer<NonnullOwnPtr<Value>>()) - results.append(move(*value)); + results.prepend(move(*value)); else - trap_if_not(value); + TRAP_IF_NOT_NORETURN(value); } return results; } @@ -361,9 +368,8 @@ void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip break; } - // Push results in reverse - for (size_t i = 1; i < results.size() + 1; ++i) - configuration.stack().push(move(static_cast<Vector<NonnullOwnPtr<Value>>&>(results)[results.size() - i])); + for (auto& result : results) + configuration.stack().push(move(result)); if (instruction.opcode() == Instructions::structured_end) return; diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index f9d370f7bb..bc9123ed83 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -222,6 +222,7 @@ set(SOURCES UIEvents/MouseEvent.cpp URLEncoder.cpp WebAssembly/WebAssemblyObject.cpp + WebAssembly/WebAssemblyObjectPrototype.cpp WebContentClient.cpp XHR/EventNames.cpp XHR/XMLHttpRequest.cpp 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; +}; + +} |