| 
									
										
										
										
											2021-04-26 12:48:13 +04:30
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org> | 
					
						
							| 
									
										
										
										
											2022-01-08 18:41:38 +01:00
										 |  |  |  * Copyright (c) 2022, the SerenityOS developers. | 
					
						
							| 
									
										
										
										
											2021-04-26 12:48:13 +04:30
										 |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <LibCore/ArgsParser.h>
 | 
					
						
							|  |  |  | #include <LibCore/File.h>
 | 
					
						
							|  |  |  | #include <LibCore/FileStream.h>
 | 
					
						
							| 
									
										
										
										
											2022-10-24 04:01:40 +03:30
										 |  |  | #include <LibCore/MappedFile.h>
 | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  | #include <LibLine/Editor.h>
 | 
					
						
							| 
									
										
										
										
											2022-01-08 18:41:38 +01:00
										 |  |  | #include <LibMain/Main.h>
 | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  | #include <LibWasm/AbstractMachine/AbstractMachine.h>
 | 
					
						
							| 
									
										
										
										
											2021-06-04 21:54:20 +10:00
										 |  |  | #include <LibWasm/AbstractMachine/BytecodeInterpreter.h>
 | 
					
						
							| 
									
										
										
										
											2021-04-27 22:13:01 +04:30
										 |  |  | #include <LibWasm/Printer/Printer.h>
 | 
					
						
							| 
									
										
										
										
											2021-04-26 12:48:13 +04:30
										 |  |  | #include <LibWasm/Types.h>
 | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  | #include <signal.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RefPtr<Line::Editor> g_line_editor; | 
					
						
							|  |  |  | static auto g_stdout = Core::OutputFileStream::standard_error(); | 
					
						
							|  |  |  | static Wasm::Printer g_printer { g_stdout }; | 
					
						
							|  |  |  | static bool g_continue { false }; | 
					
						
							|  |  |  | static void (*old_signal)(int); | 
					
						
							| 
									
										
										
										
											2021-05-24 02:04:58 +04:30
										 |  |  | static Wasm::DebuggerBytecodeInterpreter g_interpreter; | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  | static void sigint_handler(int) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!g_continue) { | 
					
						
							|  |  |  |         signal(SIGINT, old_signal); | 
					
						
							|  |  |  |         kill(getpid(), SIGINT); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     g_continue = false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-04 03:42:11 +04:30
										 |  |  | static bool post_interpret_hook(Wasm::Configuration&, Wasm::InstructionPointer& ip, Wasm::Instruction const& instr, Wasm::Interpreter const& interpreter) | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  | { | 
					
						
							|  |  |  |     if (interpreter.did_trap()) { | 
					
						
							|  |  |  |         g_continue = false; | 
					
						
							| 
									
										
										
										
											2021-06-01 20:08:24 +04:30
										 |  |  |         warnln("Trapped when executing ip={}", ip); | 
					
						
							|  |  |  |         g_printer.print(instr); | 
					
						
							| 
									
										
										
										
											2021-07-01 17:03:17 +04:30
										 |  |  |         warnln("Trap reason: {}", interpreter.trap_reason()); | 
					
						
							|  |  |  |         const_cast<Wasm::Interpreter&>(interpreter).clear_trap(); | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-04 03:42:11 +04:30
										 |  |  | static bool pre_interpret_hook(Wasm::Configuration& config, Wasm::InstructionPointer& ip, Wasm::Instruction const& instr) | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  | { | 
					
						
							|  |  |  |     static bool always_print_stack = false; | 
					
						
							|  |  |  |     static bool always_print_instruction = false; | 
					
						
							|  |  |  |     if (always_print_stack) | 
					
						
							|  |  |  |         config.dump_stack(); | 
					
						
							|  |  |  |     if (always_print_instruction) { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         g_stdout.write(DeprecatedString::formatted("{:0>4} ", ip.value()).bytes()); | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |         g_printer.print(instr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (g_continue) | 
					
						
							|  |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     g_stdout.write(DeprecatedString::formatted("{:0>4} ", ip.value()).bytes()); | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |     g_printer.print(instr); | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     DeprecatedString last_command = ""; | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |     for (;;) { | 
					
						
							|  |  |  |         auto result = g_line_editor->get_line("> "); | 
					
						
							|  |  |  |         if (result.is_error()) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         auto str = result.release_value(); | 
					
						
							|  |  |  |         g_line_editor->add_to_history(str); | 
					
						
							|  |  |  |         if (str.is_empty()) | 
					
						
							|  |  |  |             str = last_command; | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             last_command = str; | 
					
						
							|  |  |  |         auto args = str.split_view(' '); | 
					
						
							|  |  |  |         if (args.is_empty()) | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         auto& cmd = args[0]; | 
					
						
							| 
									
										
										
										
											2021-06-09 16:43:47 +04:30
										 |  |  |         if (cmd.is_one_of("h", "help")) { | 
					
						
							|  |  |  |             warnln("Wasm shell commands"); | 
					
						
							|  |  |  |             warnln("Toplevel:"); | 
					
						
							|  |  |  |             warnln("- [s]tep                     Run one instruction"); | 
					
						
							|  |  |  |             warnln("- next                       Alias for step"); | 
					
						
							|  |  |  |             warnln("- [c]ontinue                 Execute until a trap or the program exit point"); | 
					
						
							|  |  |  |             warnln("- [p]rint <args...>          Print various things (see section on print)"); | 
					
						
							|  |  |  |             warnln("- call <fn> <args...>        Call the function <fn> with the given arguments"); | 
					
						
							|  |  |  |             warnln("- set <args...>              Set shell option (see section on settings)"); | 
					
						
							|  |  |  |             warnln("- unset <args...>            Unset shell option (see section on settings)"); | 
					
						
							|  |  |  |             warnln("- [h]elp                     Print this help"); | 
					
						
							|  |  |  |             warnln(); | 
					
						
							|  |  |  |             warnln("Print:"); | 
					
						
							|  |  |  |             warnln("- print [s]tack              Print the contents of the stack, including frames and labels"); | 
					
						
							|  |  |  |             warnln("- print [[m]em]ory <index>   Print the contents of the memory identified by <index>"); | 
					
						
							|  |  |  |             warnln("- print [[i]nstr]uction      Print the current instruction"); | 
					
						
							|  |  |  |             warnln("- print [[f]unc]tion <index> Print the function identified by <index>"); | 
					
						
							|  |  |  |             warnln(); | 
					
						
							|  |  |  |             warnln("Settings:"); | 
					
						
							|  |  |  |             warnln("- set print stack            Make the shell print the stack on every instruction executed"); | 
					
						
							|  |  |  |             warnln("- set print [instr]uction    Make the shell print the instruction that will be executed next"); | 
					
						
							|  |  |  |             warnln(); | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |         if (cmd.is_one_of("s", "step", "next")) { | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (cmd.is_one_of("p", "print")) { | 
					
						
							|  |  |  |             if (args.size() < 2) { | 
					
						
							|  |  |  |                 warnln("Print what?"); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             auto& what = args[1]; | 
					
						
							|  |  |  |             if (what.is_one_of("s", "stack")) { | 
					
						
							|  |  |  |                 config.dump_stack(); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (what.is_one_of("m", "mem", "memory")) { | 
					
						
							|  |  |  |                 if (args.size() < 3) { | 
					
						
							|  |  |  |                     warnln("print what memory?"); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 auto value = args[2].to_uint<u64>(); | 
					
						
							|  |  |  |                 if (!value.has_value()) { | 
					
						
							|  |  |  |                     warnln("invalid memory index {}", args[2]); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 auto mem = config.store().get(Wasm::MemoryAddress(value.value())); | 
					
						
							|  |  |  |                 if (!mem) { | 
					
						
							|  |  |  |                     warnln("invalid memory index {} (not found)", args[2]); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-06-17 13:12:58 +04:30
										 |  |  |                 warnln("{:>32hex-dump}", mem->data().bytes()); | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (what.is_one_of("i", "instr", "instruction")) { | 
					
						
							|  |  |  |                 g_printer.print(instr); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (what.is_one_of("f", "func", "function")) { | 
					
						
							|  |  |  |                 if (args.size() < 3) { | 
					
						
							|  |  |  |                     warnln("print what function?"); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 auto value = args[2].to_uint<u64>(); | 
					
						
							|  |  |  |                 if (!value.has_value()) { | 
					
						
							|  |  |  |                     warnln("invalid function index {}", args[2]); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 auto fn = config.store().get(Wasm::FunctionAddress(value.value())); | 
					
						
							|  |  |  |                 if (!fn) { | 
					
						
							|  |  |  |                     warnln("invalid function index {} (not found)", args[2]); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (auto* fn_value = fn->get_pointer<Wasm::HostFunction>()) { | 
					
						
							|  |  |  |                     warnln("Host function at {:p}", &fn_value->function()); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (auto* fn_value = fn->get_pointer<Wasm::WasmFunction>()) { | 
					
						
							|  |  |  |                     g_printer.print(fn_value->code()); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (cmd == "call"sv) { | 
					
						
							|  |  |  |             if (args.size() < 2) { | 
					
						
							|  |  |  |                 warnln("call what?"); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             Optional<Wasm::FunctionAddress> address; | 
					
						
							|  |  |  |             auto index = args[1].to_uint<u64>(); | 
					
						
							|  |  |  |             if (index.has_value()) { | 
					
						
							| 
									
										
										
										
											2021-05-23 21:42:19 +04:30
										 |  |  |                 address = config.frame().module().functions()[index.value()]; | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |             } else { | 
					
						
							|  |  |  |                 auto& name = args[1]; | 
					
						
							| 
									
										
										
										
											2021-05-23 21:42:19 +04:30
										 |  |  |                 for (auto& export_ : config.frame().module().exports()) { | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |                     if (export_.name() == name) { | 
					
						
							|  |  |  |                         if (auto addr = export_.value().get_pointer<Wasm::FunctionAddress>()) { | 
					
						
							|  |  |  |                             address = *addr; | 
					
						
							|  |  |  |                             break; | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!address.has_value()) { | 
					
						
							|  |  |  |             failed_to_find:; | 
					
						
							|  |  |  |                 warnln("Could not find a function {}", args[1]); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             auto fn = config.store().get(*address); | 
					
						
							|  |  |  |             if (!fn) | 
					
						
							|  |  |  |                 goto failed_to_find; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             auto type = fn->visit([&](auto& value) { return value.type(); }); | 
					
						
							|  |  |  |             if (type.parameters().size() + 2 != args.size()) { | 
					
						
							|  |  |  |                 warnln("Expected {} arguments for call, but found only {}", type.parameters().size(), args.size() - 2); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             Vector<u64> values_to_push; | 
					
						
							|  |  |  |             Vector<Wasm::Value> values; | 
					
						
							|  |  |  |             for (size_t index = 2; index < args.size(); ++index) | 
					
						
							|  |  |  |                 values_to_push.append(args[index].to_uint().value_or(0)); | 
					
						
							|  |  |  |             for (auto& param : type.parameters()) | 
					
						
							|  |  |  |                 values.append(Wasm::Value { param, values_to_push.take_last() }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-24 02:04:58 +04:30
										 |  |  |             Wasm::Result result { Wasm::Trap {} }; | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2021-05-24 21:24:28 +04:30
										 |  |  |                 Wasm::BytecodeInterpreter::CallFrameHandle handle { g_interpreter, config }; | 
					
						
							| 
									
										
										
										
											2021-05-24 02:04:58 +04:30
										 |  |  |                 result = config.call(g_interpreter, *address, move(values)); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |             if (result.is_trap()) | 
					
						
							| 
									
										
										
										
											2021-07-01 17:03:17 +04:30
										 |  |  |                 warnln("Execution trapped: {}", result.trap().reason); | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |             if (!result.values().is_empty()) | 
					
						
							|  |  |  |                 warnln("Returned:"); | 
					
						
							|  |  |  |             for (auto& value : result.values()) { | 
					
						
							| 
									
										
										
										
											2021-06-04 03:30:09 +04:30
										 |  |  |                 g_stdout.write("  -> "sv.bytes()); | 
					
						
							|  |  |  |                 g_printer.print(value); | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |             } | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (cmd.is_one_of("set", "unset")) { | 
					
						
							|  |  |  |             auto value = !cmd.starts_with('u'); | 
					
						
							|  |  |  |             if (args.size() < 3) { | 
					
						
							|  |  |  |                 warnln("(un)set what (to what)?"); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (args[1] == "print"sv) { | 
					
						
							|  |  |  |                 if (args[2] == "stack"sv) | 
					
						
							|  |  |  |                     always_print_stack = value; | 
					
						
							|  |  |  |                 else if (args[2].is_one_of("instr", "instruction")) | 
					
						
							|  |  |  |                     always_print_instruction = value; | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                     warnln("Unknown print category '{}'", args[2]); | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             warnln("Unknown set category '{}'", args[1]); | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (cmd.is_one_of("c", "continue")) { | 
					
						
							|  |  |  |             g_continue = true; | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         warnln("Command not understood: {}", cmd); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-04-26 12:48:13 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 00:55:02 +01:00
										 |  |  | static Optional<Wasm::Module> parse(StringView filename) | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-10-24 04:01:40 +03:30
										 |  |  |     auto result = Core::MappedFile::map(filename); | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  |     if (result.is_error()) { | 
					
						
							|  |  |  |         warnln("Failed to open {}: {}", filename, result.error()); | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-24 04:01:40 +03:30
										 |  |  |     InputMemoryStream stream { ReadonlyBytes { result.value()->data(), result.value()->size() } }; | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  |     auto parse_result = Wasm::Module::parse(stream); | 
					
						
							|  |  |  |     if (parse_result.is_error()) { | 
					
						
							|  |  |  |         warnln("Something went wrong, either the file is invalid, or there's a bug with LibWasm!"); | 
					
						
							|  |  |  |         warnln("The parse error was {}", Wasm::parse_error_to_string(parse_result.error())); | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return parse_result.release_value(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-04 03:42:11 +04:30
										 |  |  | static void print_link_error(Wasm::LinkError const& error) | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-06-04 03:42:11 +04:30
										 |  |  |     for (auto const& missing : error.missing_imports) | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  |         warnln("Missing import '{}'", missing); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 18:41:38 +01:00
										 |  |  | ErrorOr<int> serenity_main(Main::Arguments arguments) | 
					
						
							| 
									
										
										
										
											2021-04-26 12:48:13 +04:30
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-07-11 20:42:03 +00:00
										 |  |  |     StringView filename; | 
					
						
							| 
									
										
										
										
											2021-04-27 22:13:01 +04:30
										 |  |  |     bool print = false; | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |     bool attempt_instantiate = false; | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |     bool debug = false; | 
					
						
							| 
									
										
										
										
											2021-06-04 03:33:10 +04:30
										 |  |  |     bool export_all_imports = false; | 
					
						
							| 
									
										
										
										
											2021-06-09 16:43:47 +04:30
										 |  |  |     bool shell_mode = false; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     DeprecatedString exported_function_to_execute; | 
					
						
							| 
									
										
										
										
											2021-05-01 03:19:01 +04:30
										 |  |  |     Vector<u64> values_to_push; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     Vector<DeprecatedString> modules_to_link_in; | 
					
						
							| 
									
										
										
										
											2021-04-26 12:48:13 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |     Core::ArgsParser parser; | 
					
						
							|  |  |  |     parser.add_positional_argument(filename, "File name to parse", "file"); | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |     parser.add_option(debug, "Open a debugger", "debug", 'd'); | 
					
						
							| 
									
										
										
										
											2021-04-27 22:13:01 +04:30
										 |  |  |     parser.add_option(print, "Print the parsed module", "print", 'p'); | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |     parser.add_option(attempt_instantiate, "Attempt to instantiate the module", "instantiate", 'i'); | 
					
						
							| 
									
										
										
										
											2021-05-01 03:19:01 +04:30
										 |  |  |     parser.add_option(exported_function_to_execute, "Attempt to execute the named exported function from the module (implies -i)", "execute", 'e', "name"); | 
					
						
							| 
									
										
										
										
											2021-06-04 03:33:10 +04:30
										 |  |  |     parser.add_option(export_all_imports, "Export noop functions corresponding to imports", "export-noop", 0); | 
					
						
							| 
									
										
										
										
											2021-06-09 16:43:47 +04:30
										 |  |  |     parser.add_option(shell_mode, "Launch a REPL in the module's context (implies -i)", "shell", 's'); | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  |     parser.add_option(Core::ArgsParser::Option { | 
					
						
							| 
									
										
										
										
											2022-07-12 22:13:38 +02:00
										 |  |  |         .argument_mode = Core::ArgsParser::OptionArgumentMode::Required, | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  |         .help_string = "Extra modules to link with, use to resolve imports", | 
					
						
							|  |  |  |         .long_name = "link", | 
					
						
							|  |  |  |         .short_name = 'l', | 
					
						
							|  |  |  |         .value_name = "file", | 
					
						
							| 
									
										
										
										
											2021-06-04 03:42:11 +04:30
										 |  |  |         .accept_value = [&](char const* str) { | 
					
						
							| 
									
										
										
										
											2022-07-11 19:53:29 +00:00
										 |  |  |             if (auto v = StringView { str, strlen(str) }; !v.is_empty()) { | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  |                 modules_to_link_in.append(v); | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-05-01 03:19:01 +04:30
										 |  |  |     parser.add_option(Core::ArgsParser::Option { | 
					
						
							| 
									
										
										
										
											2022-07-12 22:13:38 +02:00
										 |  |  |         .argument_mode = Core::ArgsParser::OptionArgumentMode::Required, | 
					
						
							| 
									
										
										
										
											2021-05-01 03:19:01 +04:30
										 |  |  |         .help_string = "Supply arguments to the function (default=0) (expects u64, casts to required type)", | 
					
						
							|  |  |  |         .long_name = "arg", | 
					
						
							|  |  |  |         .short_name = 0, | 
					
						
							|  |  |  |         .value_name = "u64", | 
					
						
							| 
									
										
										
										
											2021-06-04 03:42:11 +04:30
										 |  |  |         .accept_value = [&](char const* str) -> bool { | 
					
						
							| 
									
										
										
										
											2022-07-11 19:53:29 +00:00
										 |  |  |             if (auto v = StringView { str, strlen(str) }.to_uint<u64>(); v.has_value()) { | 
					
						
							| 
									
										
										
										
											2021-05-01 03:19:01 +04:30
										 |  |  |                 values_to_push.append(v.value()); | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-01-08 18:41:38 +01:00
										 |  |  |     parser.parse(arguments); | 
					
						
							| 
									
										
										
										
											2021-04-26 12:48:13 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-09 16:43:47 +04:30
										 |  |  |     if (shell_mode) { | 
					
						
							|  |  |  |         debug = true; | 
					
						
							|  |  |  |         attempt_instantiate = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!shell_mode && debug && exported_function_to_execute.is_empty()) { | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |         warnln("Debug what? (pass -e fn)"); | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-09 16:43:47 +04:30
										 |  |  |     if (debug || shell_mode) { | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |         old_signal = signal(SIGINT, sigint_handler); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-01 03:19:01 +04:30
										 |  |  |     if (!exported_function_to_execute.is_empty()) | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |         attempt_instantiate = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  |     auto parse_result = parse(filename); | 
					
						
							| 
									
										
										
										
											2021-06-04 03:33:10 +04:30
										 |  |  |     if (!parse_result.has_value()) | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |     if (print && !attempt_instantiate) { | 
					
						
							| 
									
										
										
										
											2021-04-27 22:13:01 +04:30
										 |  |  |         auto out_stream = Core::OutputFileStream::standard_output(); | 
					
						
							|  |  |  |         Wasm::Printer printer(out_stream); | 
					
						
							|  |  |  |         printer.print(parse_result.value()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |     if (attempt_instantiate) { | 
					
						
							|  |  |  |         Wasm::AbstractMachine machine; | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |         Core::EventLoop main_loop; | 
					
						
							|  |  |  |         if (debug) { | 
					
						
							|  |  |  |             g_line_editor = Line::Editor::construct(); | 
					
						
							| 
									
										
										
										
											2021-05-24 02:04:58 +04:30
										 |  |  |             g_interpreter.pre_interpret_hook = pre_interpret_hook; | 
					
						
							|  |  |  |             g_interpreter.post_interpret_hook = post_interpret_hook; | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-06-09 16:43:47 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  |         // First, resolve the linked modules
 | 
					
						
							|  |  |  |         NonnullOwnPtrVector<Wasm::ModuleInstance> linked_instances; | 
					
						
							|  |  |  |         Vector<Wasm::Module> linked_modules; | 
					
						
							|  |  |  |         for (auto& name : modules_to_link_in) { | 
					
						
							|  |  |  |             auto parse_result = parse(name); | 
					
						
							|  |  |  |             if (!parse_result.has_value()) { | 
					
						
							|  |  |  |                 warnln("Failed to parse linked module '{}'", name); | 
					
						
							|  |  |  |                 return 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             linked_modules.append(parse_result.release_value()); | 
					
						
							|  |  |  |             Wasm::Linker linker { linked_modules.last() }; | 
					
						
							|  |  |  |             for (auto& instance : linked_instances) | 
					
						
							|  |  |  |                 linker.link(instance); | 
					
						
							|  |  |  |             auto link_result = linker.finish(); | 
					
						
							|  |  |  |             if (link_result.is_error()) { | 
					
						
							|  |  |  |                 warnln("Linking imported module '{}' failed", name); | 
					
						
							|  |  |  |                 print_link_error(link_result.error()); | 
					
						
							|  |  |  |                 return 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             auto instantiation_result = machine.instantiate(linked_modules.last(), link_result.release_value()); | 
					
						
							|  |  |  |             if (instantiation_result.is_error()) { | 
					
						
							|  |  |  |                 warnln("Instantiation of imported module '{}' failed: {}", name, instantiation_result.error().error); | 
					
						
							|  |  |  |                 return 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             linked_instances.append(instantiation_result.release_value()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Wasm::Linker linker { parse_result.value() }; | 
					
						
							|  |  |  |         for (auto& instance : linked_instances) | 
					
						
							|  |  |  |             linker.link(instance); | 
					
						
							| 
									
										
										
										
											2021-06-04 03:33:10 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |         if (export_all_imports) { | 
					
						
							|  |  |  |             HashMap<Wasm::Linker::Name, Wasm::ExternValue> exports; | 
					
						
							|  |  |  |             for (auto& entry : linker.unresolved_imports()) { | 
					
						
							|  |  |  |                 if (!entry.type.has<Wasm::TypeIndex>()) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 auto type = parse_result.value().type(entry.type.get<Wasm::TypeIndex>()); | 
					
						
							|  |  |  |                 auto address = machine.store().allocate(Wasm::HostFunction( | 
					
						
							|  |  |  |                     [name = entry.name, type = type](auto&, auto& arguments) -> Wasm::Result { | 
					
						
							|  |  |  |                         StringBuilder argument_builder; | 
					
						
							|  |  |  |                         bool first = true; | 
					
						
							|  |  |  |                         for (auto& argument : arguments) { | 
					
						
							|  |  |  |                             DuplexMemoryStream stream; | 
					
						
							|  |  |  |                             Wasm::Printer { stream }.print(argument); | 
					
						
							|  |  |  |                             if (first) | 
					
						
							|  |  |  |                                 first = false; | 
					
						
							|  |  |  |                             else | 
					
						
							|  |  |  |                                 argument_builder.append(", "sv); | 
					
						
							| 
									
										
										
										
											2021-09-10 23:43:39 +02:00
										 |  |  |                             ByteBuffer buffer = stream.copy_into_contiguous_buffer(); | 
					
						
							|  |  |  |                             argument_builder.append(StringView(buffer).trim_whitespace()); | 
					
						
							| 
									
										
										
										
											2021-06-04 03:33:10 +04:30
										 |  |  |                         } | 
					
						
							|  |  |  |                         dbgln("[wasm runtime] Stub function {} was called with the following arguments: {}", name, argument_builder.to_string()); | 
					
						
							|  |  |  |                         Vector<Wasm::Value> result; | 
					
						
							|  |  |  |                         result.ensure_capacity(type.results().size()); | 
					
						
							|  |  |  |                         for (auto& result_type : type.results()) | 
					
						
							|  |  |  |                             result.append(Wasm::Value { result_type, 0ull }); | 
					
						
							|  |  |  |                         return Wasm::Result { move(result) }; | 
					
						
							|  |  |  |                     }, | 
					
						
							|  |  |  |                     type)); | 
					
						
							|  |  |  |                 exports.set(entry, *address); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             linker.link(exports); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-10 17:26:17 +04:30
										 |  |  |         auto link_result = linker.finish(); | 
					
						
							|  |  |  |         if (link_result.is_error()) { | 
					
						
							|  |  |  |             warnln("Linking main module failed"); | 
					
						
							|  |  |  |             print_link_error(link_result.error()); | 
					
						
							|  |  |  |             return 1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         auto result = machine.instantiate(parse_result.value(), link_result.release_value()); | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |         if (result.is_error()) { | 
					
						
							|  |  |  |             warnln("Module instantiation failed: {}", result.error().error); | 
					
						
							|  |  |  |             return 1; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-05-10 15:40:49 +04:30
										 |  |  |         auto module_instance = result.release_value(); | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-09 16:43:47 +04:30
										 |  |  |         auto launch_repl = [&] { | 
					
						
							|  |  |  |             Wasm::Configuration config { machine.store() }; | 
					
						
							|  |  |  |             Wasm::Expression expression { {} }; | 
					
						
							|  |  |  |             config.set_frame(Wasm::Frame { | 
					
						
							|  |  |  |                 *module_instance, | 
					
						
							|  |  |  |                 Vector<Wasm::Value> {}, | 
					
						
							|  |  |  |                 expression, | 
					
						
							|  |  |  |                 0, | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             Wasm::Instruction instr { Wasm::Instructions::nop }; | 
					
						
							|  |  |  |             Wasm::InstructionPointer ip { 0 }; | 
					
						
							|  |  |  |             g_continue = false; | 
					
						
							|  |  |  |             pre_interpret_hook(config, ip, instr); | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |         auto stream = Core::OutputFileStream::standard_output(); | 
					
						
							| 
									
										
										
										
											2021-06-04 03:42:11 +04:30
										 |  |  |         auto print_func = [&](auto const& address) { | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |             Wasm::FunctionInstance* fn = machine.store().get(address); | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |             stream.write(DeprecatedString::formatted("- Function with address {}, ptr = {}\n", address.value(), fn).bytes()); | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |             if (fn) { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |                 stream.write(DeprecatedString::formatted("    wasm function? {}\n", fn->has<Wasm::WasmFunction>()).bytes()); | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |                 fn->visit( | 
					
						
							| 
									
										
										
										
											2021-06-04 03:42:11 +04:30
										 |  |  |                     [&](Wasm::WasmFunction const& func) { | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |                         Wasm::Printer printer { stream, 3 }; | 
					
						
							|  |  |  |                         stream.write("    type:\n"sv.bytes()); | 
					
						
							|  |  |  |                         printer.print(func.type()); | 
					
						
							|  |  |  |                         stream.write("    code:\n"sv.bytes()); | 
					
						
							|  |  |  |                         printer.print(func.code()); | 
					
						
							|  |  |  |                     }, | 
					
						
							| 
									
										
										
										
											2021-06-04 03:42:11 +04:30
										 |  |  |                     [](Wasm::HostFunction const&) {}); | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |             } | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         if (print) { | 
					
						
							|  |  |  |             // Now, let's dump the functions!
 | 
					
						
							| 
									
										
										
										
											2021-05-11 04:44:59 +04:30
										 |  |  |             for (auto& address : module_instance->functions()) { | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |                 print_func(address); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-09 16:43:47 +04:30
										 |  |  |         if (shell_mode) { | 
					
						
							|  |  |  |             launch_repl(); | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-01 03:19:01 +04:30
										 |  |  |         if (!exported_function_to_execute.is_empty()) { | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |             Optional<Wasm::FunctionAddress> run_address; | 
					
						
							|  |  |  |             Vector<Wasm::Value> values; | 
					
						
							| 
									
										
										
										
											2021-05-11 04:44:59 +04:30
										 |  |  |             for (auto& entry : module_instance->exports()) { | 
					
						
							| 
									
										
										
										
											2021-05-01 03:19:01 +04:30
										 |  |  |                 if (entry.name() == exported_function_to_execute) { | 
					
						
							|  |  |  |                     if (auto addr = entry.value().get_pointer<Wasm::FunctionAddress>()) | 
					
						
							|  |  |  |                         run_address = *addr; | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (!run_address.has_value()) { | 
					
						
							| 
									
										
										
										
											2021-05-01 03:19:01 +04:30
										 |  |  |                 warnln("No such exported function, sorry :("); | 
					
						
							|  |  |  |                 return 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             auto instance = machine.store().get(*run_address); | 
					
						
							|  |  |  |             VERIFY(instance); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (instance->has<Wasm::HostFunction>()) { | 
					
						
							|  |  |  |                 warnln("Exported function is a host function, cannot run that yet"); | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |                 return 1; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-05-01 03:19:01 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |             for (auto& param : instance->get<Wasm::WasmFunction>().type().parameters()) { | 
					
						
							|  |  |  |                 if (values_to_push.is_empty()) | 
					
						
							|  |  |  |                     values.append(Wasm::Value { param, 0ull }); | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                     values.append(Wasm::Value { param, values_to_push.take_last() }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (print) { | 
					
						
							|  |  |  |                 outln("Executing "); | 
					
						
							|  |  |  |                 print_func(*run_address); | 
					
						
							|  |  |  |                 outln(); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-24 02:04:58 +04:30
										 |  |  |             auto result = machine.invoke(g_interpreter, run_address.value(), move(values)); | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-09 16:43:47 +04:30
										 |  |  |             if (debug) | 
					
						
							|  |  |  |                 launch_repl(); | 
					
						
							| 
									
										
										
										
											2021-05-21 21:10:44 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-13 00:36:50 +04:30
										 |  |  |             if (result.is_trap()) { | 
					
						
							|  |  |  |                 warnln("Execution trapped: {}", result.trap().reason); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 if (!result.values().is_empty()) | 
					
						
							|  |  |  |                     warnln("Returned:"); | 
					
						
							|  |  |  |                 for (auto& value : result.values()) { | 
					
						
							|  |  |  |                     Wasm::Printer printer { stream }; | 
					
						
							|  |  |  |                     g_stdout.write("  -> "sv.bytes()); | 
					
						
							|  |  |  |                     g_printer.print(value); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-05-01 01:08:51 +04:30
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-26 12:48:13 +04:30
										 |  |  |     return 0; | 
					
						
							|  |  |  | } |