js_repl.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /*
  2. * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
  4. * Copyright (c) 2020-2022, Ali Mohammad Pur <mpfard@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <LibCore/ArgsParser.h>
  9. #include <LibCore/ConfigFile.h>
  10. #include <LibCore/StandardPaths.h>
  11. #include <LibCore/System.h>
  12. #include <LibJS/AST.h>
  13. #include <LibJS/Bytecode/BasicBlock.h>
  14. #include <LibJS/Bytecode/Generator.h>
  15. #include <LibJS/Bytecode/Interpreter.h>
  16. #include <LibJS/Console.h>
  17. #include <LibJS/Interpreter.h>
  18. #include <LibJS/Parser.h>
  19. #include <LibJS/Print.h>
  20. #include <LibJS/Runtime/ConsoleObject.h>
  21. #include <LibJS/Runtime/JSONObject.h>
  22. #include <LibJS/Runtime/StringPrototype.h>
  23. #include <LibJS/SourceTextModule.h>
  24. #include <LibLine/Editor.h>
  25. #include <LibMain/Main.h>
  26. #include <LibTextCodec/Decoder.h>
  27. #include <fcntl.h>
  28. #include <signal.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <unistd.h>
  32. #ifndef AK_OS_EMSCRIPTEN
  33. # error "This program is for Emscripten only"
  34. #endif
  35. #include <emscripten.h>
  36. class ReplConsoleClient;
  37. RefPtr<JS::VM> g_vm;
  38. OwnPtr<JS::Interpreter> g_interpreter;
  39. OwnPtr<ReplConsoleClient> g_console_client;
  40. JS::Handle<JS::Value> g_last_value = JS::make_handle(JS::js_undefined());
  41. EM_JS(void, user_display, (char const* string, u32 length), { globalDisplayToUser(UTF8ToString(string, length)); });
  42. template<typename... Args>
  43. void display(CheckedFormatString<Args...> format_string, Args const&... args)
  44. {
  45. auto string = DeprecatedString::formatted(format_string.view(), args...);
  46. user_display(string.characters(), string.length());
  47. }
  48. template<typename... Args>
  49. void displayln(CheckedFormatString<Args...> format_string, Args const&... args)
  50. {
  51. display(format_string.view(), args...);
  52. user_display("\n", 1);
  53. }
  54. void displayln() { user_display("\n", 1); }
  55. class UserDisplayStream final : public Stream {
  56. virtual ErrorOr<Bytes> read_some(Bytes) override { return Error::from_string_view("Not readable"sv); };
  57. virtual ErrorOr<size_t> write_some(ReadonlyBytes bytes) override
  58. {
  59. user_display(bit_cast<char const*>(bytes.data()), bytes.size());
  60. return bytes.size();
  61. }
  62. virtual bool is_eof() const override { return true; }
  63. virtual bool is_open() const override { return true; }
  64. virtual void close() override { }
  65. };
  66. ErrorOr<void> print(JS::Value value)
  67. {
  68. UserDisplayStream stream;
  69. JS::PrintContext print_context {
  70. .vm = *g_vm,
  71. .stream = stream,
  72. .strip_ansi = true,
  73. };
  74. return JS::print(value, print_context);
  75. }
  76. class ReplObject final : public JS::GlobalObject {
  77. JS_OBJECT(ReplObject, JS::GlobalObject);
  78. public:
  79. ReplObject(JS::Realm& realm)
  80. : GlobalObject(realm)
  81. {
  82. }
  83. virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
  84. virtual ~ReplObject() override = default;
  85. private:
  86. JS_DECLARE_NATIVE_FUNCTION(last_value_getter);
  87. JS_DECLARE_NATIVE_FUNCTION(print);
  88. };
  89. static bool s_dump_ast = false;
  90. static bool s_run_bytecode = false;
  91. static bool s_as_module = false;
  92. static bool s_print_last_result = false;
  93. static ErrorOr<bool> parse_and_run(JS::Interpreter& interpreter, StringView source, StringView source_name)
  94. {
  95. enum class ReturnEarly {
  96. No,
  97. Yes,
  98. };
  99. JS::ThrowCompletionOr<JS::Value> result { JS::js_undefined() };
  100. auto run_script_or_module = [&](auto& script_or_module) -> ErrorOr<ReturnEarly> {
  101. if (s_dump_ast)
  102. script_or_module->parse_node().dump(0);
  103. if (s_run_bytecode) {
  104. JS::Bytecode::Interpreter bytecode_interpreter(interpreter.realm());
  105. result = bytecode_interpreter.run(*script_or_module);
  106. } else {
  107. result = interpreter.run(*script_or_module);
  108. }
  109. return ReturnEarly::No;
  110. };
  111. if (!s_as_module) {
  112. auto script_or_error = JS::Script::parse(source, interpreter.realm(), source_name);
  113. if (script_or_error.is_error()) {
  114. auto error = script_or_error.error()[0];
  115. auto hint = error.source_location_hint(source);
  116. if (!hint.is_empty())
  117. displayln("{}", hint);
  118. auto error_string = TRY(error.to_string());
  119. displayln("{}", error_string);
  120. result = interpreter.vm().throw_completion<JS::SyntaxError>(move(error_string));
  121. } else {
  122. auto return_early = TRY(run_script_or_module(script_or_error.value()));
  123. if (return_early == ReturnEarly::Yes)
  124. return true;
  125. }
  126. } else {
  127. auto module_or_error = JS::SourceTextModule::parse(source, interpreter.realm(), source_name);
  128. if (module_or_error.is_error()) {
  129. auto error = module_or_error.error()[0];
  130. auto hint = error.source_location_hint(source);
  131. if (!hint.is_empty())
  132. displayln("{}", hint);
  133. auto error_string = TRY(error.to_string());
  134. displayln("{}", error_string);
  135. result = interpreter.vm().throw_completion<JS::SyntaxError>(move(error_string));
  136. } else {
  137. auto return_early = TRY(run_script_or_module(module_or_error.value()));
  138. if (return_early == ReturnEarly::Yes)
  139. return true;
  140. }
  141. }
  142. auto handle_exception = [&](JS::Value thrown_value) {
  143. display("Uncaught exception: ");
  144. (void)print(thrown_value);
  145. if (!thrown_value.is_object() || !is<JS::Error>(thrown_value.as_object()))
  146. return;
  147. auto& traceback = static_cast<JS::Error const&>(thrown_value.as_object()).traceback();
  148. if (traceback.size() > 1) {
  149. unsigned repetitions = 0;
  150. for (size_t i = 0; i < traceback.size(); ++i) {
  151. auto& traceback_frame = traceback[i];
  152. if (i + 1 < traceback.size()) {
  153. auto& next_traceback_frame = traceback[i + 1];
  154. if (next_traceback_frame.function_name == traceback_frame.function_name) {
  155. repetitions++;
  156. continue;
  157. }
  158. }
  159. if (repetitions > 4) {
  160. // If more than 5 (1 + >4) consecutive function calls with the same name, print
  161. // the name only once and show the number of repetitions instead. This prevents
  162. // printing ridiculously large call stacks of recursive functions.
  163. displayln(" -> {}", traceback_frame.function_name);
  164. displayln(" {} more calls", repetitions);
  165. } else {
  166. for (size_t j = 0; j < repetitions + 1; ++j)
  167. displayln(" -> {}", traceback_frame.function_name);
  168. }
  169. repetitions = 0;
  170. }
  171. }
  172. };
  173. if (!result.is_error())
  174. g_last_value = JS::make_handle(result.value());
  175. if (result.is_error()) {
  176. VERIFY(result.throw_completion().value().has_value());
  177. handle_exception(*result.release_error().value());
  178. return false;
  179. }
  180. if (s_print_last_result) {
  181. (void)print(result.value());
  182. display("\n");
  183. }
  184. return true;
  185. }
  186. JS::ThrowCompletionOr<void> ReplObject::initialize(JS::Realm& realm)
  187. {
  188. MUST_OR_THROW_OOM(Base::initialize(realm));
  189. define_direct_property("global", this, JS::Attribute::Enumerable);
  190. u8 attr = JS::Attribute::Configurable | JS::Attribute::Writable | JS::Attribute::Enumerable;
  191. define_native_function(realm, "print", print, 1, attr);
  192. define_native_accessor(
  193. realm,
  194. "_",
  195. [](JS::VM&) {
  196. return g_last_value.value();
  197. },
  198. [](JS::VM& vm) -> JS::ThrowCompletionOr<JS::Value> {
  199. auto& global_object = vm.get_global_object();
  200. VERIFY(is<ReplObject>(global_object));
  201. displayln("Disable writing last value to '_'");
  202. // We must delete first otherwise this setter gets called recursively.
  203. TRY(global_object.internal_delete(JS::PropertyKey { "_" }));
  204. auto value = vm.argument(0);
  205. TRY(global_object.internal_set(JS::PropertyKey { "_" }, value, &global_object));
  206. return value;
  207. },
  208. attr);
  209. return {};
  210. }
  211. JS_DEFINE_NATIVE_FUNCTION(ReplObject::print)
  212. {
  213. auto result = ::print(vm.argument(0));
  214. if (result.is_error())
  215. return g_vm->throw_completion<JS::InternalError>(TRY_OR_THROW_OOM(*g_vm, String::formatted("Failed to print value: {}", result.error())));
  216. displayln();
  217. return JS::js_undefined();
  218. }
  219. class ReplConsoleClient final : public JS::ConsoleClient {
  220. public:
  221. ReplConsoleClient(JS::Console& console)
  222. : ConsoleClient(console)
  223. {
  224. }
  225. virtual void clear() override
  226. {
  227. display("FIXME: clear");
  228. m_group_stack_depth = 0;
  229. }
  230. virtual void end_group() override
  231. {
  232. if (m_group_stack_depth > 0)
  233. m_group_stack_depth--;
  234. }
  235. // 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer
  236. virtual JS::ThrowCompletionOr<JS::Value> printer(JS::Console::LogLevel log_level, PrinterArguments arguments) override
  237. {
  238. DeprecatedString indent = DeprecatedString::repeated(" "sv, m_group_stack_depth);
  239. if (log_level == JS::Console::LogLevel::Trace) {
  240. auto trace = arguments.get<JS::Console::Trace>();
  241. StringBuilder builder;
  242. if (!trace.label.is_empty())
  243. builder.appendff("{}{}\n", indent, trace.label);
  244. for (auto& function_name : trace.stack)
  245. builder.appendff("{}-> {}\n", indent, function_name);
  246. displayln("{}", builder.string_view());
  247. return JS::js_undefined();
  248. }
  249. if (log_level == JS::Console::LogLevel::Group || log_level == JS::Console::LogLevel::GroupCollapsed) {
  250. auto group = arguments.get<JS::Console::Group>();
  251. displayln("{}{}", indent, group.label);
  252. m_group_stack_depth++;
  253. return JS::js_undefined();
  254. }
  255. auto output = DeprecatedString::join(' ', arguments.get<JS::MarkedVector<JS::Value>>());
  256. switch (log_level) {
  257. case JS::Console::LogLevel::Debug:
  258. displayln("{}{}", indent, output);
  259. break;
  260. case JS::Console::LogLevel::Error:
  261. case JS::Console::LogLevel::Assert:
  262. displayln("{}{}", indent, output);
  263. break;
  264. case JS::Console::LogLevel::Info:
  265. displayln("{}(i) {}", indent, output);
  266. break;
  267. case JS::Console::LogLevel::Log:
  268. displayln("{}{}", indent, output);
  269. break;
  270. case JS::Console::LogLevel::Warn:
  271. case JS::Console::LogLevel::CountReset:
  272. displayln("{}{}", indent, output);
  273. break;
  274. default:
  275. displayln("{}{}", indent, output);
  276. break;
  277. }
  278. return JS::js_undefined();
  279. }
  280. private:
  281. int m_group_stack_depth { 0 };
  282. };
  283. extern "C" int initialize_repl(char const* time_zone)
  284. {
  285. if (time_zone)
  286. setenv("TZ", time_zone, 1);
  287. g_vm = MUST(JS::VM::create());
  288. g_vm->enable_default_host_import_module_dynamically_hook();
  289. // NOTE: These will print out both warnings when using something like Promise.reject().catch(...) -
  290. // which is, as far as I can tell, correct - a promise is created, rejected without handler, and a
  291. // handler then attached to it. The Node.js REPL doesn't warn in this case, so it's something we
  292. // might want to revisit at a later point and disable warnings for promises created this way.
  293. g_vm->on_promise_unhandled_rejection = [](auto& promise) {
  294. display("WARNING: A promise was rejected without any handlers");
  295. display(" (result: ");
  296. (void)print(promise.result());
  297. displayln(")");
  298. };
  299. g_vm->on_promise_rejection_handled = [](auto& promise) {
  300. display("WARNING: A handler was added to an already rejected promise");
  301. display(" (result: ");
  302. (void)print(promise.result());
  303. displayln(")");
  304. };
  305. OwnPtr<JS::Interpreter> interpreter;
  306. s_print_last_result = true;
  307. interpreter = JS::Interpreter::create<ReplObject>(*g_vm);
  308. auto console_object = interpreter->realm().intrinsics().console_object();
  309. g_console_client = make<ReplConsoleClient>(console_object->console());
  310. console_object->console().set_client(*g_console_client);
  311. g_interpreter = move(interpreter);
  312. return 0;
  313. }
  314. extern "C" bool execute(char const* source)
  315. {
  316. if (auto result = parse_and_run(*g_interpreter, { source, strlen(source) }, "REPL"sv); result.is_error()) {
  317. displayln("{}", result.error());
  318. return false;
  319. } else {
  320. return result.value();
  321. }
  322. }