js_repl.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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_as_module = false;
  91. static bool s_print_last_result = false;
  92. static ErrorOr<bool> parse_and_run(JS::Interpreter& interpreter, StringView source, StringView source_name)
  93. {
  94. enum class ReturnEarly {
  95. No,
  96. Yes,
  97. };
  98. JS::ThrowCompletionOr<JS::Value> result { JS::js_undefined() };
  99. auto run_script_or_module = [&](auto& script_or_module) -> ErrorOr<ReturnEarly> {
  100. if (s_dump_ast)
  101. script_or_module->parse_node().dump(0);
  102. if (auto* bytecode_interpreter = g_vm->bytecode_interpreter_if_exists()) {
  103. result = bytecode_interpreter->run(*script_or_module);
  104. } else {
  105. result = interpreter.run(*script_or_module);
  106. }
  107. return ReturnEarly::No;
  108. };
  109. if (!s_as_module) {
  110. auto script_or_error = JS::Script::parse(source, interpreter.realm(), source_name);
  111. if (script_or_error.is_error()) {
  112. auto error = script_or_error.error()[0];
  113. auto hint = error.source_location_hint(source);
  114. if (!hint.is_empty())
  115. displayln("{}", hint);
  116. auto error_string = TRY(error.to_string());
  117. displayln("{}", error_string);
  118. result = interpreter.vm().throw_completion<JS::SyntaxError>(move(error_string));
  119. } else {
  120. auto return_early = TRY(run_script_or_module(script_or_error.value()));
  121. if (return_early == ReturnEarly::Yes)
  122. return true;
  123. }
  124. } else {
  125. auto module_or_error = JS::SourceTextModule::parse(source, interpreter.realm(), source_name);
  126. if (module_or_error.is_error()) {
  127. auto error = module_or_error.error()[0];
  128. auto hint = error.source_location_hint(source);
  129. if (!hint.is_empty())
  130. displayln("{}", hint);
  131. auto error_string = TRY(error.to_string());
  132. displayln("{}", error_string);
  133. result = interpreter.vm().throw_completion<JS::SyntaxError>(move(error_string));
  134. } else {
  135. auto return_early = TRY(run_script_or_module(module_or_error.value()));
  136. if (return_early == ReturnEarly::Yes)
  137. return true;
  138. }
  139. }
  140. auto handle_exception = [&](JS::Value thrown_value) {
  141. display("Uncaught exception: ");
  142. (void)print(thrown_value);
  143. if (!thrown_value.is_object() || !is<JS::Error>(thrown_value.as_object()))
  144. return;
  145. auto& traceback = static_cast<JS::Error const&>(thrown_value.as_object()).traceback();
  146. if (traceback.size() > 1) {
  147. unsigned repetitions = 0;
  148. for (size_t i = 0; i < traceback.size(); ++i) {
  149. auto& traceback_frame = traceback[i];
  150. if (i + 1 < traceback.size()) {
  151. auto& next_traceback_frame = traceback[i + 1];
  152. if (next_traceback_frame.function_name == traceback_frame.function_name) {
  153. repetitions++;
  154. continue;
  155. }
  156. }
  157. if (repetitions > 4) {
  158. // If more than 5 (1 + >4) consecutive function calls with the same name, print
  159. // the name only once and show the number of repetitions instead. This prevents
  160. // printing ridiculously large call stacks of recursive functions.
  161. displayln(" -> {}", traceback_frame.function_name);
  162. displayln(" {} more calls", repetitions);
  163. } else {
  164. for (size_t j = 0; j < repetitions + 1; ++j)
  165. displayln(" -> {}", traceback_frame.function_name);
  166. }
  167. repetitions = 0;
  168. }
  169. }
  170. };
  171. if (!result.is_error())
  172. g_last_value = JS::make_handle(result.value());
  173. if (result.is_error()) {
  174. VERIFY(result.throw_completion().value().has_value());
  175. handle_exception(*result.release_error().value());
  176. return false;
  177. }
  178. if (s_print_last_result) {
  179. (void)print(result.value());
  180. display("\n");
  181. }
  182. return true;
  183. }
  184. JS::ThrowCompletionOr<void> ReplObject::initialize(JS::Realm& realm)
  185. {
  186. MUST_OR_THROW_OOM(Base::initialize(realm));
  187. define_direct_property("global", this, JS::Attribute::Enumerable);
  188. u8 attr = JS::Attribute::Configurable | JS::Attribute::Writable | JS::Attribute::Enumerable;
  189. define_native_function(realm, "print", print, 1, attr);
  190. define_native_accessor(
  191. realm,
  192. "_",
  193. [](JS::VM&) {
  194. return g_last_value.value();
  195. },
  196. [](JS::VM& vm) -> JS::ThrowCompletionOr<JS::Value> {
  197. auto& global_object = vm.get_global_object();
  198. VERIFY(is<ReplObject>(global_object));
  199. displayln("Disable writing last value to '_'");
  200. // We must delete first otherwise this setter gets called recursively.
  201. TRY(global_object.internal_delete(JS::PropertyKey { "_" }));
  202. auto value = vm.argument(0);
  203. TRY(global_object.internal_set(JS::PropertyKey { "_" }, value, &global_object));
  204. return value;
  205. },
  206. attr);
  207. return {};
  208. }
  209. JS_DEFINE_NATIVE_FUNCTION(ReplObject::print)
  210. {
  211. auto result = ::print(vm.argument(0));
  212. if (result.is_error())
  213. return g_vm->throw_completion<JS::InternalError>(TRY_OR_THROW_OOM(*g_vm, String::formatted("Failed to print value: {}", result.error())));
  214. displayln();
  215. return JS::js_undefined();
  216. }
  217. class ReplConsoleClient final : public JS::ConsoleClient {
  218. public:
  219. ReplConsoleClient(JS::Console& console)
  220. : ConsoleClient(console)
  221. {
  222. }
  223. virtual void clear() override
  224. {
  225. display("FIXME: clear");
  226. m_group_stack_depth = 0;
  227. }
  228. virtual void end_group() override
  229. {
  230. if (m_group_stack_depth > 0)
  231. m_group_stack_depth--;
  232. }
  233. // 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer
  234. virtual JS::ThrowCompletionOr<JS::Value> printer(JS::Console::LogLevel log_level, PrinterArguments arguments) override
  235. {
  236. DeprecatedString indent = DeprecatedString::repeated(" "sv, m_group_stack_depth);
  237. if (log_level == JS::Console::LogLevel::Trace) {
  238. auto trace = arguments.get<JS::Console::Trace>();
  239. StringBuilder builder;
  240. if (!trace.label.is_empty())
  241. builder.appendff("{}{}\n", indent, trace.label);
  242. for (auto& function_name : trace.stack)
  243. builder.appendff("{}-> {}\n", indent, function_name);
  244. displayln("{}", builder.string_view());
  245. return JS::js_undefined();
  246. }
  247. if (log_level == JS::Console::LogLevel::Group || log_level == JS::Console::LogLevel::GroupCollapsed) {
  248. auto group = arguments.get<JS::Console::Group>();
  249. displayln("{}{}", indent, group.label);
  250. m_group_stack_depth++;
  251. return JS::js_undefined();
  252. }
  253. auto output = DeprecatedString::join(' ', arguments.get<JS::MarkedVector<JS::Value>>());
  254. switch (log_level) {
  255. case JS::Console::LogLevel::Debug:
  256. displayln("{}{}", indent, output);
  257. break;
  258. case JS::Console::LogLevel::Error:
  259. case JS::Console::LogLevel::Assert:
  260. displayln("{}{}", indent, output);
  261. break;
  262. case JS::Console::LogLevel::Info:
  263. displayln("{}(i) {}", indent, output);
  264. break;
  265. case JS::Console::LogLevel::Log:
  266. displayln("{}{}", indent, output);
  267. break;
  268. case JS::Console::LogLevel::Warn:
  269. case JS::Console::LogLevel::CountReset:
  270. displayln("{}{}", indent, output);
  271. break;
  272. default:
  273. displayln("{}{}", indent, output);
  274. break;
  275. }
  276. return JS::js_undefined();
  277. }
  278. private:
  279. int m_group_stack_depth { 0 };
  280. };
  281. extern "C" int initialize_repl(char const* time_zone)
  282. {
  283. if (time_zone)
  284. setenv("TZ", time_zone, 1);
  285. g_vm = MUST(JS::VM::create());
  286. g_vm->enable_default_host_import_module_dynamically_hook();
  287. // NOTE: These will print out both warnings when using something like Promise.reject().catch(...) -
  288. // which is, as far as I can tell, correct - a promise is created, rejected without handler, and a
  289. // handler then attached to it. The Node.js REPL doesn't warn in this case, so it's something we
  290. // might want to revisit at a later point and disable warnings for promises created this way.
  291. g_vm->on_promise_unhandled_rejection = [](auto& promise) {
  292. display("WARNING: A promise was rejected without any handlers");
  293. display(" (result: ");
  294. (void)print(promise.result());
  295. displayln(")");
  296. };
  297. g_vm->on_promise_rejection_handled = [](auto& promise) {
  298. display("WARNING: A handler was added to an already rejected promise");
  299. display(" (result: ");
  300. (void)print(promise.result());
  301. displayln(")");
  302. };
  303. OwnPtr<JS::Interpreter> interpreter;
  304. s_print_last_result = true;
  305. interpreter = JS::Interpreter::create<ReplObject>(*g_vm);
  306. auto console_object = interpreter->realm().intrinsics().console_object();
  307. g_console_client = make<ReplConsoleClient>(console_object->console());
  308. console_object->console().set_client(*g_console_client);
  309. g_interpreter = move(interpreter);
  310. return 0;
  311. }
  312. extern "C" bool execute(char const* source)
  313. {
  314. if (auto result = parse_and_run(*g_interpreter, { source, strlen(source) }, "REPL"sv); result.is_error()) {
  315. displayln("{}", result.error());
  316. return false;
  317. } else {
  318. return result.value();
  319. }
  320. }