Переглянути джерело

Utilities: Add support for loading the WASI module to wasm

Ali Mohammad Pur 2 роки тому
батько
коміт
6d48719069
1 змінених файлів з 66 додано та 0 видалено
  1. 66 0
      Userland/Utilities/wasm.cpp

+ 66 - 0
Userland/Utilities/wasm.cpp

@@ -7,6 +7,7 @@
 
 #include <AK/MemoryStream.h>
 #include <LibCore/ArgsParser.h>
+#include <LibCore/DeprecatedFile.h>
 #include <LibCore/File.h>
 #include <LibCore/MappedFile.h>
 #include <LibLine/Editor.h>
@@ -15,6 +16,7 @@
 #include <LibWasm/AbstractMachine/BytecodeInterpreter.h>
 #include <LibWasm/Printer/Printer.h>
 #include <LibWasm/Types.h>
+#include <LibWasm/Wasi.h>
 #include <signal.h>
 #include <unistd.h>
 
@@ -278,9 +280,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     bool debug = false;
     bool export_all_imports = false;
     bool shell_mode = false;
+    bool wasi = false;
     DeprecatedString exported_function_to_execute;
     Vector<u64> values_to_push;
     Vector<DeprecatedString> modules_to_link_in;
+    Vector<StringView> args_if_wasi;
+    Vector<StringView> wasi_preopened_mappings;
 
     Core::ArgsParser parser;
     parser.add_positional_argument(filename, "File name to parse", "file");
@@ -290,6 +295,21 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     parser.add_option(exported_function_to_execute, "Attempt to execute the named exported function from the module (implies -i)", "execute", 'e', "name");
     parser.add_option(export_all_imports, "Export noop functions corresponding to imports", "export-noop", 0);
     parser.add_option(shell_mode, "Launch a REPL in the module's context (implies -i)", "shell", 's');
+    parser.add_option(wasi, "Enable WASI", "wasi", 'w');
+    parser.add_option(Core::ArgsParser::Option {
+        .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
+        .help_string = "Directory mappings to expose via WASI",
+        .long_name = "wasi-map-dir",
+        .short_name = 0,
+        .value_name = "path[:path]",
+        .accept_value = [&](StringView str) {
+            if (!str.is_empty()) {
+                wasi_preopened_mappings.append(str);
+                return true;
+            }
+            return false;
+        },
+    });
     parser.add_option(Core::ArgsParser::Option {
         .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
         .help_string = "Extra modules to link with, use to resolve imports",
@@ -318,6 +338,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
             return false;
         },
     });
+    parser.add_positional_argument(args_if_wasi, "Arguments to pass to the WASI module", "args", Core::ArgsParser::Required::No);
     parser.parse(arguments);
 
     if (shell_mode) {
@@ -351,6 +372,34 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
 
     if (attempt_instantiate) {
         Wasm::AbstractMachine machine;
+        Optional<Wasm::Wasi::Implementation> wasi_impl;
+
+        if (wasi) {
+            wasi_impl.emplace(Wasm::Wasi::Implementation::Details {
+                .provide_arguments = [&] {
+                    Vector<String> strings;
+                    for (auto& string : args_if_wasi)
+                        strings.append(String::from_utf8(string).release_value_but_fixme_should_propagate_errors());
+                    return strings; },
+                .provide_environment = {},
+                .provide_preopened_directories = [&] {
+                    Vector<Wasm::Wasi::Implementation::MappedPath> paths;
+                    for (auto& string : wasi_preopened_mappings) {
+                        auto split_index = string.find(':');
+                        if (split_index.has_value()) {
+                            LexicalPath host_path { Core::DeprecatedFile::real_path_for(string.substring_view(0, *split_index)) };
+                            LexicalPath mapped_path { string.substring_view(*split_index + 1) };
+                            paths.append({move(host_path), move(mapped_path)});
+                        } else {
+                            LexicalPath host_path { Core::DeprecatedFile::real_path_for(string) };
+                            LexicalPath mapped_path { string };
+                            paths.append({move(host_path), move(mapped_path)});
+                        }
+                    }
+                    return paths; },
+            });
+        }
+
         Core::EventLoop main_loop;
         if (debug) {
             g_line_editor = Line::Editor::construct();
@@ -389,6 +438,23 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         for (auto& instance : linked_instances)
             linker.link(*instance);
 
+        if (wasi) {
+            HashMap<Wasm::Linker::Name, Wasm::ExternValue> wasi_exports;
+            for (auto& entry : linker.unresolved_imports()) {
+                if (entry.module != "wasi_snapshot_preview1"sv)
+                    continue;
+                auto function = wasi_impl->function_by_name(entry.name);
+                if (function.is_error()) {
+                    dbgln("wasi function {} not implemented :(", entry.name);
+                    continue;
+                }
+                auto address = machine.store().allocate(function.release_value());
+                wasi_exports.set(entry, *address);
+            }
+
+            linker.link(wasi_exports);
+        }
+
         if (export_all_imports) {
             HashMap<Wasm::Linker::Name, Wasm::ExternValue> exports;
             for (auto& entry : linker.unresolved_imports()) {