| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2021-06-03 10:46:30 +02:00
										 |  |  |  * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> | 
					
						
							| 
									
										
										
										
											2022-05-05 20:50:32 +02:00
										 |  |  |  * Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org> | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |  * Copyright (c) 2020-2022, Ali Mohammad Pur <mpfard@serenityos.org> | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-12 10:51:41 +01:00
										 |  |  | #include <LibCore/ArgsParser.h>
 | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  | #include <LibCore/ConfigFile.h>
 | 
					
						
							| 
									
										
										
										
											2020-10-25 23:36:06 +00:00
										 |  |  | #include <LibCore/StandardPaths.h>
 | 
					
						
							| 
									
										
										
										
											2022-09-14 15:19:11 +01:00
										 |  |  | #include <LibCore/Stream.h>
 | 
					
						
							| 
									
										
										
										
											2021-11-23 10:59:50 +01:00
										 |  |  | #include <LibCore/System.h>
 | 
					
						
							| 
									
										
										
										
											2021-06-09 06:49:58 +04:30
										 |  |  | #include <LibJS/Bytecode/BasicBlock.h>
 | 
					
						
							| 
									
										
										
										
											2021-06-03 10:46:30 +02:00
										 |  |  | #include <LibJS/Bytecode/Generator.h>
 | 
					
						
							|  |  |  | #include <LibJS/Bytecode/Interpreter.h>
 | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  | #include <LibJS/Console.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | #include <LibJS/Interpreter.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-11 19:27:43 +01:00
										 |  |  | #include <LibJS/Parser.h>
 | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  | #include <LibJS/Print.h>
 | 
					
						
							| 
									
										
										
										
											2022-08-28 14:20:06 +01:00
										 |  |  | #include <LibJS/Runtime/ConsoleObject.h>
 | 
					
						
							| 
									
										
										
										
											2021-09-16 21:48:07 +02:00
										 |  |  | #include <LibJS/Runtime/JSONObject.h>
 | 
					
						
							| 
									
										
										
										
											2022-03-07 18:11:13 +01:00
										 |  |  | #include <LibJS/Runtime/StringPrototype.h>
 | 
					
						
							| 
									
										
										
										
											2022-01-16 13:16:04 +01:00
										 |  |  | #include <LibJS/SourceTextModule.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-31 13:34:57 +02:00
										 |  |  | #include <LibLine/Editor.h>
 | 
					
						
							| 
									
										
										
										
											2021-11-22 19:25:30 +00:00
										 |  |  | #include <LibMain/Main.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 09:31:04 -04:00
										 |  |  | #include <LibTextCodec/Decoder.h>
 | 
					
						
							| 
									
										
										
										
											2021-01-12 19:21:59 +01:00
										 |  |  | #include <fcntl.h>
 | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:10 +04:30
										 |  |  | #include <signal.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | #include <stdio.h>
 | 
					
						
							| 
									
										
										
										
											2021-03-12 17:29:37 +01:00
										 |  |  | #include <unistd.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  | RefPtr<JS::VM> g_vm; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | Vector<DeprecatedString> g_repl_statements; | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  | JS::Handle<JS::Value> g_last_value = JS::make_handle(JS::js_undefined()); | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-17 16:52:26 +01:00
										 |  |  | class ReplObject final : public JS::GlobalObject { | 
					
						
							|  |  |  |     JS_OBJECT(ReplObject, JS::GlobalObject); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | public: | 
					
						
							| 
									
										
										
										
											2022-08-01 20:27:20 +02:00
										 |  |  |     ReplObject(JS::Realm& realm) | 
					
						
							|  |  |  |         : GlobalObject(realm) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-28 14:42:50 +01:00
										 |  |  |     virtual void initialize(JS::Realm&) override; | 
					
						
							| 
									
										
										
										
											2021-05-26 01:01:07 +01:00
										 |  |  |     virtual ~ReplObject() override = default; | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							| 
									
										
										
										
											2021-10-29 17:13:18 -04:00
										 |  |  |     JS_DECLARE_NATIVE_FUNCTION(exit_interpreter); | 
					
						
							|  |  |  |     JS_DECLARE_NATIVE_FUNCTION(repl_help); | 
					
						
							|  |  |  |     JS_DECLARE_NATIVE_FUNCTION(save_to_file); | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  |     JS_DECLARE_NATIVE_FUNCTION(load_ini); | 
					
						
							| 
									
										
										
										
											2021-10-29 17:13:18 -04:00
										 |  |  |     JS_DECLARE_NATIVE_FUNCTION(load_json); | 
					
						
							| 
									
										
										
										
											2022-01-16 02:05:32 +01:00
										 |  |  |     JS_DECLARE_NATIVE_FUNCTION(last_value_getter); | 
					
						
							| 
									
										
										
										
											2022-02-14 12:04:50 +03:30
										 |  |  |     JS_DECLARE_NATIVE_FUNCTION(print); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-26 00:50:18 +02:00
										 |  |  | class ScriptObject final : public JS::GlobalObject { | 
					
						
							|  |  |  |     JS_OBJECT(ScriptObject, JS::GlobalObject); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2022-08-01 20:27:20 +02:00
										 |  |  |     ScriptObject(JS::Realm& realm) | 
					
						
							|  |  |  |         : JS::GlobalObject(realm) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-28 14:42:50 +01:00
										 |  |  |     virtual void initialize(JS::Realm&) override; | 
					
						
							| 
									
										
										
										
											2021-05-26 01:01:07 +01:00
										 |  |  |     virtual ~ScriptObject() override = default; | 
					
						
							| 
									
										
										
										
											2021-05-26 00:50:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  |     JS_DECLARE_NATIVE_FUNCTION(load_ini); | 
					
						
							| 
									
										
										
										
											2021-10-29 17:13:18 -04:00
										 |  |  |     JS_DECLARE_NATIVE_FUNCTION(load_json); | 
					
						
							| 
									
										
										
										
											2022-02-14 12:04:50 +03:30
										 |  |  |     JS_DECLARE_NATIVE_FUNCTION(print); | 
					
						
							| 
									
										
										
										
											2021-05-26 00:50:18 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  | static bool s_dump_ast = false; | 
					
						
							| 
									
										
										
										
											2021-06-03 10:46:30 +02:00
										 |  |  | static bool s_run_bytecode = false; | 
					
						
							| 
									
										
										
										
											2021-06-13 20:40:20 +04:30
										 |  |  | static bool s_opt_bytecode = false; | 
					
						
							| 
									
										
										
										
											2021-08-14 17:30:37 +02:00
										 |  |  | static bool s_as_module = false; | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  | static bool s_print_last_result = false; | 
					
						
							| 
									
										
										
										
											2021-11-23 22:46:28 +02:00
										 |  |  | static bool s_strip_ansi = false; | 
					
						
							| 
									
										
										
										
											2021-11-23 23:02:19 +02:00
										 |  |  | static bool s_disable_source_location_hints = false; | 
					
						
							| 
									
										
										
										
											2020-05-26 15:04:39 +04:30
										 |  |  | static RefPtr<Line::Editor> s_editor; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | static DeprecatedString s_history_path = DeprecatedString::formatted("{}/.js-history", Core::StandardPaths::home_directory()); | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  | static int s_repl_line_level = 0; | 
					
						
							| 
									
										
										
										
											2020-05-25 16:57:07 +04:30
										 |  |  | static bool s_fail_repl = false; | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  | static ErrorOr<void> print(JS::Value value, Core::Stream::Stream& stream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     JS::PrintContext print_context { .vm = *g_vm, .stream = stream, .strip_ansi = s_strip_ansi }; | 
					
						
							|  |  |  |     return JS::print(value, print_context); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum class PrintTarget { | 
					
						
							|  |  |  |     StandardError, | 
					
						
							|  |  |  |     StandardOutput, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ErrorOr<void> print(JS::Value value, PrintTarget target = PrintTarget::StandardOutput) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto stream = TRY(target == PrintTarget::StandardError ? Core::Stream::File::standard_error() : Core::Stream::File::standard_output()); | 
					
						
							|  |  |  |     return print(value, *stream); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | static DeprecatedString prompt_for_level(int level) | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  | { | 
					
						
							|  |  |  |     static StringBuilder prompt_builder; | 
					
						
							|  |  |  |     prompt_builder.clear(); | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |     prompt_builder.append("> "sv); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |     for (auto i = 0; i < level; ++i) | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |         prompt_builder.append("    "sv); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |     return prompt_builder.build(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | static DeprecatedString read_next_piece() | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | { | 
					
						
							|  |  |  |     StringBuilder piece; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-30 21:42:11 +04:30
										 |  |  |     auto line_level_delta_for_next_line { 0 }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |     do { | 
					
						
							| 
									
										
										
										
											2020-05-25 16:57:07 +04:30
										 |  |  |         auto line_result = s_editor->get_line(prompt_for_level(s_repl_line_level)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-30 21:42:11 +04:30
										 |  |  |         line_level_delta_for_next_line = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-25 16:57:07 +04:30
										 |  |  |         if (line_result.is_error()) { | 
					
						
							|  |  |  |             s_fail_repl = true; | 
					
						
							|  |  |  |             return ""; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto& line = line_result.value(); | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |         s_editor->add_to_history(line); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         piece.append(line); | 
					
						
							| 
									
										
										
										
											2021-06-20 13:58:57 -07:00
										 |  |  |         piece.append('\n'); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |         auto lexer = JS::Lexer(line); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-30 21:42:11 +04:30
										 |  |  |         enum { | 
					
						
							|  |  |  |             NotInLabelOrObjectKey, | 
					
						
							|  |  |  |             InLabelOrObjectKeyIdentifier, | 
					
						
							|  |  |  |             InLabelOrObjectKey | 
					
						
							|  |  |  |         } label_state { NotInLabelOrObjectKey }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |         for (JS::Token token = lexer.next(); token.type() != JS::TokenType::Eof; token = lexer.next()) { | 
					
						
							|  |  |  |             switch (token.type()) { | 
					
						
							|  |  |  |             case JS::TokenType::BracketOpen: | 
					
						
							|  |  |  |             case JS::TokenType::CurlyOpen: | 
					
						
							|  |  |  |             case JS::TokenType::ParenOpen: | 
					
						
							| 
									
										
										
										
											2020-05-30 21:42:11 +04:30
										 |  |  |                 label_state = NotInLabelOrObjectKey; | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |                 s_repl_line_level++; | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             case JS::TokenType::BracketClose: | 
					
						
							|  |  |  |             case JS::TokenType::CurlyClose: | 
					
						
							|  |  |  |             case JS::TokenType::ParenClose: | 
					
						
							| 
									
										
										
										
											2020-05-30 21:42:11 +04:30
										 |  |  |                 label_state = NotInLabelOrObjectKey; | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |                 s_repl_line_level--; | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2020-05-30 21:42:11 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |             case JS::TokenType::Identifier: | 
					
						
							|  |  |  |             case JS::TokenType::StringLiteral: | 
					
						
							|  |  |  |                 if (label_state == NotInLabelOrObjectKey) | 
					
						
							|  |  |  |                     label_state = InLabelOrObjectKeyIdentifier; | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                     label_state = NotInLabelOrObjectKey; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             case JS::TokenType::Colon: | 
					
						
							|  |  |  |                 if (label_state == InLabelOrObjectKeyIdentifier) | 
					
						
							|  |  |  |                     label_state = InLabelOrObjectKey; | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                     label_state = NotInLabelOrObjectKey; | 
					
						
							|  |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |             default: | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-05-30 21:42:11 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |         if (label_state == InLabelOrObjectKey) { | 
					
						
							|  |  |  |             // If there's a label or object literal key at the end of this line,
 | 
					
						
							|  |  |  |             // prompt for more lines but do not change the line level.
 | 
					
						
							|  |  |  |             line_level_delta_for_next_line += 1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } while (s_repl_line_level + line_level_delta_for_next_line > 0); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |     return piece.to_deprecated_string(); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  | static bool write_to_file(DeprecatedString const& path) | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-01-12 19:21:59 +01:00
										 |  |  |     int fd = open(path.characters(), O_WRONLY | O_CREAT | O_TRUNC, 0666); | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |     for (size_t i = 0; i < g_repl_statements.size(); i++) { | 
					
						
							|  |  |  |         auto line = g_repl_statements[i]; | 
					
						
							|  |  |  |         if (line.length() && i != g_repl_statements.size() - 1) { | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  |             ssize_t nwritten = write(fd, line.characters(), line.length()); | 
					
						
							|  |  |  |             if (nwritten < 0) { | 
					
						
							|  |  |  |                 close(fd); | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |         if (i != g_repl_statements.size() - 1) { | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  |             char ch = '\n'; | 
					
						
							|  |  |  |             ssize_t nwritten = write(fd, &ch, 1); | 
					
						
							|  |  |  |             if (nwritten != 1) { | 
					
						
							|  |  |  |                 perror("write"); | 
					
						
							|  |  |  |                 close(fd); | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     close(fd); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  | static ErrorOr<bool> parse_and_run(JS::Interpreter& interpreter, StringView source, StringView source_name) | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-01-16 13:16:04 +01:00
										 |  |  |     enum class ReturnEarly { | 
					
						
							|  |  |  |         No, | 
					
						
							|  |  |  |         Yes, | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-16 13:16:04 +01:00
										 |  |  |     JS::ThrowCompletionOr<JS::Value> result { JS::js_undefined() }; | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-24 12:43:05 +03:30
										 |  |  |     auto run_script_or_module = [&](auto& script_or_module) { | 
					
						
							| 
									
										
										
										
											2022-01-16 13:16:04 +01:00
										 |  |  |         if (s_dump_ast) | 
					
						
							| 
									
										
										
										
											2022-01-24 12:43:05 +03:30
										 |  |  |             script_or_module->parse_node().dump(0); | 
					
						
							| 
									
										
										
										
											2022-01-08 21:28:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-24 13:34:46 +02:00
										 |  |  |         if (JS::Bytecode::g_dump_bytecode || s_run_bytecode) { | 
					
						
							| 
									
										
										
										
											2022-02-12 19:54:08 +03:30
										 |  |  |             auto executable_result = JS::Bytecode::Generator::generate(script_or_module->parse_node()); | 
					
						
							|  |  |  |             if (executable_result.is_error()) { | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |                 result = g_vm->throw_completion<JS::InternalError>(executable_result.error().to_deprecated_string()); | 
					
						
							| 
									
										
										
										
											2022-02-12 19:54:08 +03:30
										 |  |  |                 return ReturnEarly::No; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             auto executable = executable_result.release_value(); | 
					
						
							| 
									
										
										
										
											2022-01-31 13:25:39 +01:00
										 |  |  |             executable->name = source_name; | 
					
						
							| 
									
										
										
										
											2021-06-13 20:40:20 +04:30
										 |  |  |             if (s_opt_bytecode) { | 
					
						
							| 
									
										
										
										
											2022-11-02 14:27:43 +01:00
										 |  |  |                 auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(JS::Bytecode::Interpreter::OptimizationLevel::Optimize); | 
					
						
							| 
									
										
										
										
											2022-01-31 13:25:39 +01:00
										 |  |  |                 passes.perform(*executable); | 
					
						
							| 
									
										
										
										
											2021-06-13 20:40:20 +04:30
										 |  |  |                 dbgln("Optimisation passes took {}us", passes.elapsed()); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-24 13:34:46 +02:00
										 |  |  |             if (JS::Bytecode::g_dump_bytecode) | 
					
						
							| 
									
										
										
										
											2022-01-31 13:25:39 +01:00
										 |  |  |                 executable->dump(); | 
					
						
							| 
									
										
										
										
											2021-06-10 21:04:12 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (s_run_bytecode) { | 
					
						
							| 
									
										
										
										
											2022-08-22 19:35:23 +01:00
										 |  |  |                 JS::Bytecode::Interpreter bytecode_interpreter(interpreter.realm()); | 
					
						
							| 
									
										
										
										
											2022-04-02 11:53:13 +04:30
										 |  |  |                 auto result_or_error = bytecode_interpreter.run_and_return_frame(*executable, nullptr); | 
					
						
							|  |  |  |                 if (result_or_error.value.is_error()) | 
					
						
							|  |  |  |                     result = result_or_error.value.release_error(); | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                     result = result_or_error.frame->registers[0]; | 
					
						
							| 
									
										
										
										
											2021-06-10 21:04:32 +02:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2022-01-16 13:16:04 +01:00
										 |  |  |                 return ReturnEarly::Yes; | 
					
						
							| 
									
										
										
										
											2021-06-10 21:04:12 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2022-01-24 12:43:05 +03:30
										 |  |  |             result = interpreter.run(*script_or_module); | 
					
						
							| 
									
										
										
										
											2022-01-16 13:16:04 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return ReturnEarly::No; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!s_as_module) { | 
					
						
							|  |  |  |         auto script_or_error = JS::Script::parse(source, interpreter.realm(), source_name); | 
					
						
							|  |  |  |         if (script_or_error.is_error()) { | 
					
						
							|  |  |  |             auto error = script_or_error.error()[0]; | 
					
						
							|  |  |  |             auto hint = error.source_location_hint(source); | 
					
						
							|  |  |  |             if (!hint.is_empty()) | 
					
						
							|  |  |  |                 outln("{}", hint); | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |             outln("{}", error.to_deprecated_string()); | 
					
						
							|  |  |  |             result = interpreter.vm().throw_completion<JS::SyntaxError>(error.to_deprecated_string()); | 
					
						
							| 
									
										
										
										
											2022-01-16 13:16:04 +01:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2022-01-24 12:43:05 +03:30
										 |  |  |             auto return_early = run_script_or_module(script_or_error.value()); | 
					
						
							| 
									
										
										
										
											2022-01-16 13:16:04 +01:00
										 |  |  |             if (return_early == ReturnEarly::Yes) | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         auto module_or_error = JS::SourceTextModule::parse(source, interpreter.realm(), source_name); | 
					
						
							|  |  |  |         if (module_or_error.is_error()) { | 
					
						
							|  |  |  |             auto error = module_or_error.error()[0]; | 
					
						
							|  |  |  |             auto hint = error.source_location_hint(source); | 
					
						
							|  |  |  |             if (!hint.is_empty()) | 
					
						
							|  |  |  |                 outln("{}", hint); | 
					
						
							| 
									
										
										
										
											2022-12-06 01:12:49 +00:00
										 |  |  |             outln(error.to_deprecated_string()); | 
					
						
							|  |  |  |             result = interpreter.vm().throw_completion<JS::SyntaxError>(error.to_deprecated_string()); | 
					
						
							| 
									
										
										
										
											2022-01-16 13:16:04 +01:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2022-01-24 12:43:05 +03:30
										 |  |  |             auto return_early = run_script_or_module(module_or_error.value()); | 
					
						
							| 
									
										
										
										
											2022-01-16 13:16:04 +01:00
										 |  |  |             if (return_early == ReturnEarly::Yes) | 
					
						
							|  |  |  |                 return true; | 
					
						
							| 
									
										
										
										
											2021-06-10 21:04:12 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |     auto handle_exception = [&](JS::Value thrown_value) -> ErrorOr<void> { | 
					
						
							|  |  |  |         warnln("Uncaught exception: "); | 
					
						
							|  |  |  |         TRY(print(thrown_value, PrintTarget::StandardError)); | 
					
						
							|  |  |  |         warnln(); | 
					
						
							| 
									
										
										
										
											2022-02-07 15:12:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (!thrown_value.is_object() || !is<JS::Error>(thrown_value.as_object())) | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2022-02-07 15:12:41 +01:00
										 |  |  |         auto& traceback = static_cast<JS::Error const&>(thrown_value.as_object()).traceback(); | 
					
						
							| 
									
										
										
										
											2021-04-24 18:15:02 +02:00
										 |  |  |         if (traceback.size() > 1) { | 
					
						
							| 
									
										
										
										
											2020-11-08 12:58:49 +00:00
										 |  |  |             unsigned repetitions = 0; | 
					
						
							| 
									
										
										
										
											2021-04-24 18:15:02 +02:00
										 |  |  |             for (size_t i = 0; i < traceback.size(); ++i) { | 
					
						
							|  |  |  |                 auto& traceback_frame = traceback[i]; | 
					
						
							|  |  |  |                 if (i + 1 < traceback.size()) { | 
					
						
							|  |  |  |                     auto& next_traceback_frame = traceback[i + 1]; | 
					
						
							|  |  |  |                     if (next_traceback_frame.function_name == traceback_frame.function_name) { | 
					
						
							|  |  |  |                         repetitions++; | 
					
						
							|  |  |  |                         continue; | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2020-11-08 12:58:49 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  |                 if (repetitions > 4) { | 
					
						
							|  |  |  |                     // If more than 5 (1 + >4) consecutive function calls with the same name, print
 | 
					
						
							|  |  |  |                     // the name only once and show the number of repetitions instead. This prevents
 | 
					
						
							|  |  |  |                     // printing ridiculously large call stacks of recursive functions.
 | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |                     warnln(" -> {}", traceback_frame.function_name); | 
					
						
							|  |  |  |                     warnln(" {} more calls", repetitions); | 
					
						
							| 
									
										
										
										
											2020-11-08 12:58:49 +00:00
										 |  |  |                 } else { | 
					
						
							|  |  |  |                     for (size_t j = 0; j < repetitions + 1; ++j) | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |                         warnln(" -> {}", traceback_frame.function_name); | 
					
						
							| 
									
										
										
										
											2020-11-08 12:58:49 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  |                 repetitions = 0; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-06-02 14:12:04 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |         return {}; | 
					
						
							| 
									
										
										
										
											2021-01-29 09:16:06 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-16 02:05:32 +01:00
										 |  |  |     if (!result.is_error()) | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |         g_last_value = JS::make_handle(result.value()); | 
					
						
							| 
									
										
										
										
											2022-01-16 02:05:32 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 21:28:27 +01:00
										 |  |  |     if (result.is_error()) { | 
					
						
							| 
									
										
										
										
											2022-02-07 15:12:41 +01:00
										 |  |  |         VERIFY(result.throw_completion().value().has_value()); | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |         TRY(handle_exception(*result.release_error().value())); | 
					
						
							| 
									
										
										
										
											2021-04-12 00:08:28 +02:00
										 |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2021-01-29 09:16:06 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  | 
 | 
					
						
							|  |  |  |     if (s_print_last_result) { | 
					
						
							|  |  |  |         TRY(print(result.value())); | 
					
						
							|  |  |  |         warnln(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 19:24:32 +01:00
										 |  |  | static JS::ThrowCompletionOr<JS::Value> load_ini_impl(JS::VM& vm) | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-08-21 19:24:32 +01:00
										 |  |  |     auto& realm = *vm.current_realm(); | 
					
						
							| 
									
										
										
										
											2022-08-16 00:20:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 14:00:56 +01:00
										 |  |  |     auto filename = TRY(vm.argument(0).to_string(vm)); | 
					
						
							| 
									
										
										
										
											2022-09-14 15:19:11 +01:00
										 |  |  |     auto file_or_error = Core::Stream::File::open(filename, Core::Stream::OpenMode::Read); | 
					
						
							|  |  |  |     if (file_or_error.is_error()) | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         return vm.throw_completion<JS::Error>(DeprecatedString::formatted("Failed to open '{}': {}", filename, file_or_error.error())); | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-14 15:19:11 +01:00
										 |  |  |     auto config_file = MUST(Core::ConfigFile::open(filename, file_or_error.release_value())); | 
					
						
							| 
									
										
										
										
											2022-08-27 00:54:55 +01:00
										 |  |  |     auto* object = JS::Object::create(realm, realm.intrinsics().object_prototype()); | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  |     for (auto const& group : config_file->groups()) { | 
					
						
							| 
									
										
										
										
											2022-08-27 00:54:55 +01:00
										 |  |  |         auto* group_object = JS::Object::create(realm, realm.intrinsics().object_prototype()); | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  |         for (auto const& key : config_file->keys(group)) { | 
					
						
							|  |  |  |             auto entry = config_file->read_entry(group, key); | 
					
						
							| 
									
										
										
										
											2022-12-06 22:17:27 +00:00
										 |  |  |             group_object->define_direct_property(key, JS::PrimitiveString::create(vm, move(entry)), JS::Attribute::Enumerable | JS::Attribute::Configurable | JS::Attribute::Writable); | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         object->define_direct_property(group, group_object, JS::Attribute::Enumerable | JS::Attribute::Configurable | JS::Attribute::Writable); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return object; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 19:24:32 +01:00
										 |  |  | static JS::ThrowCompletionOr<JS::Value> load_json_impl(JS::VM& vm) | 
					
						
							| 
									
										
										
										
											2021-09-16 21:48:07 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-08-21 14:00:56 +01:00
										 |  |  |     auto filename = TRY(vm.argument(0).to_string(vm)); | 
					
						
							| 
									
										
										
										
											2022-09-14 15:19:11 +01:00
										 |  |  |     auto file_or_error = Core::Stream::File::open(filename, Core::Stream::OpenMode::Read); | 
					
						
							|  |  |  |     if (file_or_error.is_error()) | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         return vm.throw_completion<JS::Error>(DeprecatedString::formatted("Failed to open '{}': {}", filename, file_or_error.error())); | 
					
						
							| 
									
										
										
										
											2022-09-14 15:19:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-11 17:49:00 +01:00
										 |  |  |     auto file_contents_or_error = file_or_error.value()->read_until_eof(); | 
					
						
							| 
									
										
										
										
											2022-09-14 15:19:11 +01:00
										 |  |  |     if (file_contents_or_error.is_error()) | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         return vm.throw_completion<JS::Error>(DeprecatedString::formatted("Failed to read '{}': {}", filename, file_contents_or_error.error())); | 
					
						
							| 
									
										
										
										
											2022-09-14 15:19:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto json = JsonValue::from_string(file_contents_or_error.value()); | 
					
						
							| 
									
										
										
										
											2021-11-15 01:46:51 +01:00
										 |  |  |     if (json.is_error()) | 
					
						
							| 
									
										
										
										
											2022-08-16 20:33:17 +01:00
										 |  |  |         return vm.throw_completion<JS::SyntaxError>(JS::ErrorType::JsonMalformed); | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 19:24:32 +01:00
										 |  |  |     return JS::JSONObject::parse_json_value(vm, json.value()); | 
					
						
							| 
									
										
										
										
											2021-09-16 21:48:07 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-28 14:42:50 +01:00
										 |  |  | void ReplObject::initialize(JS::Realm& realm) | 
					
						
							| 
									
										
										
										
											2020-04-18 13:18:06 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-08-28 14:42:50 +01:00
										 |  |  |     Base::initialize(realm); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-06 02:15:08 +03:00
										 |  |  |     define_direct_property("global", this, JS::Attribute::Enumerable); | 
					
						
							| 
									
										
										
										
											2021-07-06 01:12:54 +03:00
										 |  |  |     u8 attr = JS::Attribute::Configurable | JS::Attribute::Writable | JS::Attribute::Enumerable; | 
					
						
							| 
									
										
										
										
											2022-08-22 21:47:35 +01:00
										 |  |  |     define_native_function(realm, "exit", exit_interpreter, 0, attr); | 
					
						
							|  |  |  |     define_native_function(realm, "help", repl_help, 0, attr); | 
					
						
							|  |  |  |     define_native_function(realm, "save", save_to_file, 1, attr); | 
					
						
							|  |  |  |     define_native_function(realm, "loadINI", load_ini, 1, attr); | 
					
						
							|  |  |  |     define_native_function(realm, "loadJSON", load_json, 1, attr); | 
					
						
							|  |  |  |     define_native_function(realm, "print", print, 1, attr); | 
					
						
							| 
									
										
										
										
											2022-01-16 02:05:32 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     define_native_accessor( | 
					
						
							| 
									
										
										
										
											2022-08-22 21:47:35 +01:00
										 |  |  |         realm, | 
					
						
							| 
									
										
										
										
											2022-01-16 02:05:32 +01:00
										 |  |  |         "_", | 
					
						
							| 
									
										
										
										
											2022-08-22 11:48:08 +01:00
										 |  |  |         [](JS::VM&) { | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |             return g_last_value.value(); | 
					
						
							| 
									
										
										
										
											2022-01-16 02:05:32 +01:00
										 |  |  |         }, | 
					
						
							| 
									
										
										
										
											2022-08-22 11:48:08 +01:00
										 |  |  |         [](JS::VM& vm) -> JS::ThrowCompletionOr<JS::Value> { | 
					
						
							|  |  |  |             auto& global_object = vm.get_global_object(); | 
					
						
							| 
									
										
										
										
											2022-01-16 02:05:32 +01:00
										 |  |  |             VERIFY(is<ReplObject>(global_object)); | 
					
						
							|  |  |  |             outln("Disable writing last value to '_'"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // We must delete first otherwise this setter gets called recursively.
 | 
					
						
							|  |  |  |             TRY(global_object.internal_delete(JS::PropertyKey { "_" })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             auto value = vm.argument(0); | 
					
						
							|  |  |  |             TRY(global_object.internal_set(JS::PropertyKey { "_" }, value, &global_object)); | 
					
						
							|  |  |  |             return value; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         attr); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-29 17:13:18 -04:00
										 |  |  | JS_DEFINE_NATIVE_FUNCTION(ReplObject::save_to_file) | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-09-27 18:36:49 +02:00
										 |  |  |     if (!vm.argument_count()) | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  |         return JS::Value(false); | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     DeprecatedString save_path = vm.argument(0).to_string_without_side_effects(); | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |     if (write_to_file(save_path)) { | 
					
						
							| 
									
										
										
										
											2020-04-02 11:52:40 -04:00
										 |  |  |         return JS::Value(true); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return JS::Value(false); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-13 14:04:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-29 17:13:18 -04:00
										 |  |  | JS_DEFINE_NATIVE_FUNCTION(ReplObject::exit_interpreter) | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-09-27 18:36:49 +02:00
										 |  |  |     if (!vm.argument_count()) | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |         exit(0); | 
					
						
							| 
									
										
										
										
											2022-08-21 14:00:56 +01:00
										 |  |  |     exit(TRY(vm.argument(0).to_number(vm)).as_double()); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-13 14:04:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-29 17:13:18 -04:00
										 |  |  | JS_DEFINE_NATIVE_FUNCTION(ReplObject::repl_help) | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |     warnln("REPL commands:"); | 
					
						
							|  |  |  |     warnln("    exit(code): exit the REPL with specified code. Defaults to 0."); | 
					
						
							|  |  |  |     warnln("    help(): display this menu"); | 
					
						
							|  |  |  |     warnln("    loadINI(file): load the given file as INI."); | 
					
						
							|  |  |  |     warnln("    loadJSON(file): load the given file as JSON."); | 
					
						
							|  |  |  |     warnln("    print(value): pretty-print the given JS value."); | 
					
						
							|  |  |  |     warnln("    save(file): write REPL input history to the given file. For example: save(\"foo.txt\")"); | 
					
						
							| 
									
										
										
										
											2020-04-13 21:08:54 +01:00
										 |  |  |     return JS::js_undefined(); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  | JS_DEFINE_NATIVE_FUNCTION(ReplObject::load_ini) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-08-21 19:24:32 +01:00
										 |  |  |     return load_ini_impl(vm); | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-29 17:13:18 -04:00
										 |  |  | JS_DEFINE_NATIVE_FUNCTION(ReplObject::load_json) | 
					
						
							| 
									
										
										
										
											2021-09-16 21:48:07 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-08-21 19:24:32 +01:00
										 |  |  |     return load_json_impl(vm); | 
					
						
							| 
									
										
										
										
											2021-09-16 21:48:07 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-14 12:04:50 +03:30
										 |  |  | JS_DEFINE_NATIVE_FUNCTION(ReplObject::print) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |     auto result = ::print(vm.argument(0)); | 
					
						
							|  |  |  |     if (result.is_error()) | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         return g_vm->throw_completion<JS::InternalError>(DeprecatedString::formatted("Failed to print value: {}", result.error())); | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  | 
 | 
					
						
							|  |  |  |     outln(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-14 12:04:50 +03:30
										 |  |  |     return JS::js_undefined(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-28 14:42:50 +01:00
										 |  |  | void ScriptObject::initialize(JS::Realm& realm) | 
					
						
							| 
									
										
										
										
											2021-05-26 00:50:18 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-08-28 14:42:50 +01:00
										 |  |  |     Base::initialize(realm); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-06 02:15:08 +03:00
										 |  |  |     define_direct_property("global", this, JS::Attribute::Enumerable); | 
					
						
							| 
									
										
										
										
											2021-07-06 01:12:54 +03:00
										 |  |  |     u8 attr = JS::Attribute::Configurable | JS::Attribute::Writable | JS::Attribute::Enumerable; | 
					
						
							| 
									
										
										
										
											2022-08-22 21:47:35 +01:00
										 |  |  |     define_native_function(realm, "loadINI", load_ini, 1, attr); | 
					
						
							|  |  |  |     define_native_function(realm, "loadJSON", load_json, 1, attr); | 
					
						
							|  |  |  |     define_native_function(realm, "print", print, 1, attr); | 
					
						
							| 
									
										
										
										
											2021-05-26 00:50:18 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  | JS_DEFINE_NATIVE_FUNCTION(ScriptObject::load_ini) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-08-21 19:24:32 +01:00
										 |  |  |     return load_ini_impl(vm); | 
					
						
							| 
									
										
										
										
											2022-06-06 18:51:02 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-29 17:13:18 -04:00
										 |  |  | JS_DEFINE_NATIVE_FUNCTION(ScriptObject::load_json) | 
					
						
							| 
									
										
										
										
											2021-09-16 21:48:07 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-08-21 19:24:32 +01:00
										 |  |  |     return load_json_impl(vm); | 
					
						
							| 
									
										
										
										
											2021-09-16 21:48:07 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-14 12:04:50 +03:30
										 |  |  | JS_DEFINE_NATIVE_FUNCTION(ScriptObject::print) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |     auto result = ::print(vm.argument(0)); | 
					
						
							|  |  |  |     if (result.is_error()) | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         return g_vm->throw_completion<JS::InternalError>(DeprecatedString::formatted("Failed to print value: {}", result.error())); | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  | 
 | 
					
						
							|  |  |  |     outln(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-14 12:04:50 +03:30
										 |  |  |     return JS::js_undefined(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  | static ErrorOr<void> repl(JS::Interpreter& interpreter) | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-05-25 16:57:07 +04:30
										 |  |  |     while (!s_fail_repl) { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         DeprecatedString piece = read_next_piece(); | 
					
						
							| 
									
										
										
										
											2022-03-07 18:11:13 +01:00
										 |  |  |         if (Utf8View { piece }.trim(JS::whitespace_characters).is_empty()) | 
					
						
							| 
									
										
										
										
											2020-03-31 13:29:42 +02:00
										 |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2022-03-07 18:11:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |         g_repl_statements.append(piece); | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |         TRY(parse_and_run(interpreter, piece, "REPL"sv)); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-09 21:58:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 17:59:16 +01:00
										 |  |  | static Function<void()> interrupt_interpreter; | 
					
						
							| 
									
										
										
										
											2020-08-10 23:48:37 +02:00
										 |  |  | static void sigint_handler() | 
					
						
							| 
									
										
										
										
											2020-04-24 17:59:16 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     interrupt_interpreter(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  | class ReplConsoleClient final : public JS::ConsoleClient { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     ReplConsoleClient(JS::Console& console) | 
					
						
							|  |  |  |         : ConsoleClient(console) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 19:24:17 +00:00
										 |  |  |     virtual void clear() override | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-11-28 17:00:20 +03:30
										 |  |  |         out("\033[3J\033[H\033[2J"); | 
					
						
							| 
									
										
										
										
											2021-12-22 12:32:15 +00:00
										 |  |  |         m_group_stack_depth = 0; | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  |         fflush(stdout); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-04-18 17:08:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-22 12:32:15 +00:00
										 |  |  |     virtual void end_group() override | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-12-22 12:32:15 +00:00
										 |  |  |         if (m_group_stack_depth > 0) | 
					
						
							|  |  |  |             m_group_stack_depth--; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 2.3. Printer(logLevel, args[, options]), https://console.spec.whatwg.org/#printer
 | 
					
						
							|  |  |  |     virtual JS::ThrowCompletionOr<JS::Value> printer(JS::Console::LogLevel log_level, PrinterArguments arguments) override | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         DeprecatedString indent = DeprecatedString::repeated("  "sv, m_group_stack_depth); | 
					
						
							| 
									
										
										
										
											2021-12-22 12:32:15 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-10 13:48:38 +00:00
										 |  |  |         if (log_level == JS::Console::LogLevel::Trace) { | 
					
						
							|  |  |  |             auto trace = arguments.get<JS::Console::Trace>(); | 
					
						
							|  |  |  |             StringBuilder builder; | 
					
						
							|  |  |  |             if (!trace.label.is_empty()) | 
					
						
							| 
									
										
										
										
											2021-12-22 12:32:15 +00:00
										 |  |  |                 builder.appendff("{}\033[36;1m{}\033[0m\n", indent, trace.label); | 
					
						
							| 
									
										
										
										
											2021-12-10 13:48:38 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             for (auto& function_name : trace.stack) | 
					
						
							| 
									
										
										
										
											2021-12-22 12:32:15 +00:00
										 |  |  |                 builder.appendff("{}-> {}\n", indent, function_name); | 
					
						
							| 
									
										
										
										
											2021-12-10 13:48:38 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 17:00:20 +03:30
										 |  |  |             outln("{}", builder.string_view()); | 
					
						
							| 
									
										
										
										
											2021-12-10 13:48:38 +00:00
										 |  |  |             return JS::js_undefined(); | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-04-18 17:08:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-22 12:32:15 +00:00
										 |  |  |         if (log_level == JS::Console::LogLevel::Group || log_level == JS::Console::LogLevel::GroupCollapsed) { | 
					
						
							|  |  |  |             auto group = arguments.get<JS::Console::Group>(); | 
					
						
							| 
									
										
										
										
											2022-11-28 17:00:20 +03:30
										 |  |  |             outln("{}\033[36;1m{}\033[0m", indent, group.label); | 
					
						
							| 
									
										
										
										
											2021-12-22 12:32:15 +00:00
										 |  |  |             m_group_stack_depth++; | 
					
						
							|  |  |  |             return JS::js_undefined(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         auto output = DeprecatedString::join(' ', arguments.get<JS::MarkedVector<JS::Value>>()); | 
					
						
							| 
									
										
										
										
											2022-10-15 22:38:31 +02:00
										 |  |  | #ifdef AK_OS_SERENITY
 | 
					
						
							| 
									
										
										
										
											2021-12-10 12:26:25 +00:00
										 |  |  |         m_console.output_debug_message(log_level, output); | 
					
						
							| 
									
										
										
										
											2022-10-15 22:38:31 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2021-12-10 12:26:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         switch (log_level) { | 
					
						
							|  |  |  |         case JS::Console::LogLevel::Debug: | 
					
						
							| 
									
										
										
										
											2022-11-28 17:00:20 +03:30
										 |  |  |             outln("{}\033[36;1m{}\033[0m", indent, output); | 
					
						
							| 
									
										
										
										
											2021-12-10 12:26:25 +00:00
										 |  |  |             break; | 
					
						
							|  |  |  |         case JS::Console::LogLevel::Error: | 
					
						
							| 
									
										
										
										
											2021-12-08 19:43:29 +00:00
										 |  |  |         case JS::Console::LogLevel::Assert: | 
					
						
							| 
									
										
										
										
											2022-11-28 17:00:20 +03:30
										 |  |  |             outln("{}\033[31;1m{}\033[0m", indent, output); | 
					
						
							| 
									
										
										
										
											2021-12-10 12:26:25 +00:00
										 |  |  |             break; | 
					
						
							|  |  |  |         case JS::Console::LogLevel::Info: | 
					
						
							| 
									
										
										
										
											2022-11-28 17:00:20 +03:30
										 |  |  |             outln("{}(i) {}", indent, output); | 
					
						
							| 
									
										
										
										
											2021-12-10 12:26:25 +00:00
										 |  |  |             break; | 
					
						
							|  |  |  |         case JS::Console::LogLevel::Log: | 
					
						
							| 
									
										
										
										
											2022-11-28 17:00:20 +03:30
										 |  |  |             outln("{}{}", indent, output); | 
					
						
							| 
									
										
										
										
											2021-12-10 12:26:25 +00:00
										 |  |  |             break; | 
					
						
							|  |  |  |         case JS::Console::LogLevel::Warn: | 
					
						
							| 
									
										
										
										
											2021-12-08 19:12:06 +00:00
										 |  |  |         case JS::Console::LogLevel::CountReset: | 
					
						
							| 
									
										
										
										
											2022-11-28 17:00:20 +03:30
										 |  |  |             outln("{}\033[33;1m{}\033[0m", indent, output); | 
					
						
							| 
									
										
										
										
											2021-12-10 12:26:25 +00:00
										 |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							| 
									
										
										
										
											2022-11-28 17:00:20 +03:30
										 |  |  |             outln("{}{}", indent, output); | 
					
						
							| 
									
										
										
										
											2021-12-10 12:26:25 +00:00
										 |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return JS::js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-12-22 12:32:15 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     int m_group_stack_depth { 0 }; | 
					
						
							| 
									
										
										
										
											2020-05-04 16:09:05 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 19:25:30 +00:00
										 |  |  | ErrorOr<int> serenity_main(Main::Arguments arguments) | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-12-29 13:10:12 -05:00
										 |  |  |     TRY(Core::System::pledge("stdio rpath wpath cpath tty sigaction")); | 
					
						
							| 
									
										
										
										
											2021-11-22 20:41:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-16 19:18:46 +01:00
										 |  |  |     bool gc_on_every_allocation = false; | 
					
						
							| 
									
										
										
										
											2020-04-30 22:37:50 +02:00
										 |  |  |     bool disable_syntax_highlight = false; | 
					
						
							| 
									
										
										
										
											2022-02-09 07:53:49 -05:00
										 |  |  |     StringView evaluate_script; | 
					
						
							| 
									
										
										
										
											2021-11-26 22:32:37 +01:00
										 |  |  |     Vector<StringView> script_paths; | 
					
						
							| 
									
										
										
										
											2020-03-12 10:51:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Core::ArgsParser args_parser; | 
					
						
							| 
									
										
										
										
											2020-12-05 16:22:58 +01:00
										 |  |  |     args_parser.set_general_help("This is a JavaScript interpreter."); | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |     args_parser.add_option(s_dump_ast, "Dump the AST", "dump-ast", 'A'); | 
					
						
							| 
									
										
										
										
											2021-10-24 13:34:46 +02:00
										 |  |  |     args_parser.add_option(JS::Bytecode::g_dump_bytecode, "Dump the bytecode", "dump-bytecode", 'd'); | 
					
						
							| 
									
										
										
										
											2021-06-03 10:46:30 +02:00
										 |  |  |     args_parser.add_option(s_run_bytecode, "Run the bytecode", "run-bytecode", 'b'); | 
					
						
							| 
									
										
										
										
											2021-06-13 20:40:20 +04:30
										 |  |  |     args_parser.add_option(s_opt_bytecode, "Optimize the bytecode", "optimize-bytecode", 'p'); | 
					
						
							| 
									
										
										
										
											2021-08-14 17:30:37 +02:00
										 |  |  |     args_parser.add_option(s_as_module, "Treat as module", "as-module", 'm'); | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |     args_parser.add_option(s_print_last_result, "Print last result", "print-last-result", 'l'); | 
					
						
							| 
									
										
										
										
											2022-02-09 07:53:49 -05:00
										 |  |  |     args_parser.add_option(s_strip_ansi, "Disable ANSI colors", "disable-ansi-colors", 'i'); | 
					
						
							| 
									
										
										
										
											2021-11-23 23:02:19 +02:00
										 |  |  |     args_parser.add_option(s_disable_source_location_hints, "Disable source location hints", "disable-source-location-hints", 'h'); | 
					
						
							| 
									
										
										
										
											2020-03-16 19:18:46 +01:00
										 |  |  |     args_parser.add_option(gc_on_every_allocation, "GC on every allocation", "gc-on-every-allocation", 'g'); | 
					
						
							| 
									
										
										
										
											2020-04-30 22:37:50 +02:00
										 |  |  |     args_parser.add_option(disable_syntax_highlight, "Disable live syntax highlighting", "no-syntax-highlight", 's'); | 
					
						
							| 
									
										
										
										
											2022-02-09 07:53:49 -05:00
										 |  |  |     args_parser.add_option(evaluate_script, "Evaluate argument as a script", "evaluate", 'c', "script"); | 
					
						
							| 
									
										
										
										
											2021-06-16 13:55:34 +00:00
										 |  |  |     args_parser.add_positional_argument(script_paths, "Path to script files", "scripts", Core::ArgsParser::Required::No); | 
					
						
							| 
									
										
										
										
											2021-11-22 19:03:05 -05:00
										 |  |  |     args_parser.parse(arguments); | 
					
						
							| 
									
										
										
										
											2020-03-12 10:51:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-30 22:37:50 +02:00
										 |  |  |     bool syntax_highlight = !disable_syntax_highlight; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |     g_vm = JS::VM::create(); | 
					
						
							|  |  |  |     g_vm->enable_default_host_import_module_dynamically_hook(); | 
					
						
							| 
									
										
										
										
											2022-01-18 19:39:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 22:14:12 +02:00
										 |  |  |     // NOTE: These will print out both warnings when using something like Promise.reject().catch(...) -
 | 
					
						
							|  |  |  |     // which is, as far as I can tell, correct - a promise is created, rejected without handler, and a
 | 
					
						
							|  |  |  |     // handler then attached to it. The Node.js REPL doesn't warn in this case, so it's something we
 | 
					
						
							|  |  |  |     // might want to revisit at a later point and disable warnings for promises created this way.
 | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |     g_vm->on_promise_unhandled_rejection = [](auto& promise) { | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |         warn("WARNING: A promise was rejected without any handlers"); | 
					
						
							|  |  |  |         warn(" (result: "); | 
					
						
							|  |  |  |         (void)print(promise.result(), PrintTarget::StandardError); | 
					
						
							|  |  |  |         warnln(")"); | 
					
						
							| 
									
										
										
										
											2021-04-01 22:14:12 +02:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |     g_vm->on_promise_rejection_handled = [](auto& promise) { | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |         warn("WARNING: A handler was added to an already rejected promise"); | 
					
						
							|  |  |  |         warn(" (result: "); | 
					
						
							|  |  |  |         (void)print(promise.result(), PrintTarget::StandardError); | 
					
						
							|  |  |  |         warnln(")"); | 
					
						
							| 
									
										
										
										
											2021-04-01 22:14:12 +02:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2020-04-24 17:59:16 +01:00
										 |  |  |     OwnPtr<JS::Interpreter> interpreter; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-07 15:12:41 +01:00
										 |  |  |     // FIXME: Figure out some way to interrupt the interpreter now that vm.exception() is gone.
 | 
					
						
							| 
									
										
										
										
											2020-04-24 17:59:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-09 07:53:49 -05:00
										 |  |  |     if (evaluate_script.is_empty() && script_paths.is_empty()) { | 
					
						
							| 
									
										
										
										
											2020-05-14 16:29:24 +01:00
										 |  |  |         s_print_last_result = true; | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |         interpreter = JS::Interpreter::create<ReplObject>(*g_vm); | 
					
						
							| 
									
										
										
										
											2022-08-28 14:20:06 +01:00
										 |  |  |         auto& console_object = *interpreter->realm().intrinsics().console_object(); | 
					
						
							|  |  |  |         ReplConsoleClient console_client(console_object.console()); | 
					
						
							|  |  |  |         console_object.console().set_client(console_client); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |         interpreter->heap().set_should_collect_on_every_allocation(gc_on_every_allocation); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-03 13:17:33 +02:00
										 |  |  |         auto& global_environment = interpreter->realm().global_environment(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 15:04:39 +04:30
										 |  |  |         s_editor = Line::Editor::construct(); | 
					
						
							| 
									
										
										
										
											2020-10-25 23:36:06 +00:00
										 |  |  |         s_editor->load_history(s_history_path); | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:10 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |         signal(SIGINT, [](int) { | 
					
						
							| 
									
										
										
										
											2020-04-29 00:31:22 +04:30
										 |  |  |             if (!s_editor->is_editing()) | 
					
						
							|  |  |  |                 sigint_handler(); | 
					
						
							| 
									
										
										
										
											2020-10-25 23:36:06 +00:00
										 |  |  |             s_editor->save_history(s_history_path); | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:10 +04:30
										 |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |         s_editor->on_display_refresh = [syntax_highlight](Line::Editor& editor) { | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |             auto stylize = [&](Line::Span span, Line::Style styles) { | 
					
						
							|  |  |  |                 if (syntax_highlight) | 
					
						
							|  |  |  |                     editor.stylize(span, styles); | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             editor.strip_styles(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 15:19:30 +01:00
										 |  |  |             size_t open_indents = s_repl_line_level; | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 13:00:30 -04:00
										 |  |  |             auto line = editor.line(); | 
					
						
							|  |  |  |             JS::Lexer lexer(line); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |             bool indenters_starting_line = true; | 
					
						
							|  |  |  |             for (JS::Token token = lexer.next(); token.type() != JS::TokenType::Eof; token = lexer.next()) { | 
					
						
							| 
									
										
										
										
											2021-09-01 16:13:53 +00:00
										 |  |  |                 auto length = Utf8View { token.value() }.length(); | 
					
						
							| 
									
										
										
										
											2022-03-06 10:32:15 +03:30
										 |  |  |                 auto start = token.offset(); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 auto end = start + length; | 
					
						
							|  |  |  |                 if (indenters_starting_line) { | 
					
						
							|  |  |  |                     if (token.type() != JS::TokenType::ParenClose && token.type() != JS::TokenType::BracketClose && token.type() != JS::TokenType::CurlyClose) { | 
					
						
							|  |  |  |                         indenters_starting_line = false; | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         --open_indents; | 
					
						
							| 
									
										
										
										
											2020-04-05 18:33:12 +04:30
										 |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:
- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter
This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:
- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most
Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.
I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
											
										 
											2020-10-04 22:28:59 +01:00
										 |  |  |                 switch (token.category()) { | 
					
						
							|  |  |  |                 case JS::TokenCategory::Invalid: | 
					
						
							| 
									
										
										
										
											2021-09-01 16:13:53 +00:00
										 |  |  |                     stylize({ start, end, Line::Span::CodepointOriented }, { Line::Style::Foreground(Line::Style::XtermColor::Red), Line::Style::Underline }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							| 
									
										
											  
											
												LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:
- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter
This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:
- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most
Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.
I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
											
										 
											2020-10-04 22:28:59 +01:00
										 |  |  |                 case JS::TokenCategory::Number: | 
					
						
							| 
									
										
										
										
											2021-09-01 16:13:53 +00:00
										 |  |  |                     stylize({ start, end, Line::Span::CodepointOriented }, { Line::Style::Foreground(Line::Style::XtermColor::Magenta) }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							| 
									
										
											  
											
												LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:
- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter
This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:
- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most
Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.
I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
											
										 
											2020-10-04 22:28:59 +01:00
										 |  |  |                 case JS::TokenCategory::String: | 
					
						
							| 
									
										
										
										
											2021-09-01 16:13:53 +00:00
										 |  |  |                     stylize({ start, end, Line::Span::CodepointOriented }, { Line::Style::Foreground(Line::Style::XtermColor::Green), Line::Style::Bold }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							| 
									
										
											  
											
												LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:
- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter
This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:
- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most
Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.
I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
											
										 
											2020-10-04 22:28:59 +01:00
										 |  |  |                 case JS::TokenCategory::Punctuation: | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							| 
									
										
											  
											
												LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:
- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter
This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:
- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most
Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.
I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
											
										 
											2020-10-04 22:28:59 +01:00
										 |  |  |                 case JS::TokenCategory::Operator: | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							| 
									
										
											  
											
												LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:
- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter
This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:
- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most
Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.
I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
											
										 
											2020-10-04 22:28:59 +01:00
										 |  |  |                 case JS::TokenCategory::Keyword: | 
					
						
							|  |  |  |                     switch (token.type()) { | 
					
						
							|  |  |  |                     case JS::TokenType::BoolLiteral: | 
					
						
							|  |  |  |                     case JS::TokenType::NullLiteral: | 
					
						
							| 
									
										
										
										
											2021-09-01 16:13:53 +00:00
										 |  |  |                         stylize({ start, end, Line::Span::CodepointOriented }, { Line::Style::Foreground(Line::Style::XtermColor::Yellow), Line::Style::Bold }); | 
					
						
							| 
									
										
											  
											
												LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:
- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter
This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:
- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most
Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.
I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
											
										 
											2020-10-04 22:28:59 +01:00
										 |  |  |                         break; | 
					
						
							|  |  |  |                     default: | 
					
						
							| 
									
										
										
										
											2021-09-01 16:13:53 +00:00
										 |  |  |                         stylize({ start, end, Line::Span::CodepointOriented }, { Line::Style::Foreground(Line::Style::XtermColor::Blue), Line::Style::Bold }); | 
					
						
							| 
									
										
											  
											
												LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:
- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter
This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:
- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most
Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.
I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
											
										 
											2020-10-04 22:28:59 +01:00
										 |  |  |                         break; | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							| 
									
										
											  
											
												LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:
- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter
This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:
- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most
Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.
I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
											
										 
											2020-10-04 22:28:59 +01:00
										 |  |  |                 case JS::TokenCategory::ControlKeyword: | 
					
						
							| 
									
										
										
										
											2021-09-01 16:13:53 +00:00
										 |  |  |                     stylize({ start, end, Line::Span::CodepointOriented }, { Line::Style::Foreground(Line::Style::XtermColor::Cyan), Line::Style::Italic }); | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                     break; | 
					
						
							| 
									
										
											  
											
												LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:
- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter
This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:
- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most
Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.
I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
											
										 
											2020-10-04 22:28:59 +01:00
										 |  |  |                 case JS::TokenCategory::Identifier: | 
					
						
							| 
									
										
										
										
											2021-09-01 16:13:53 +00:00
										 |  |  |                     stylize({ start, end, Line::Span::CodepointOriented }, { Line::Style::Foreground(Line::Style::XtermColor::White), Line::Style::Bold }); | 
					
						
							| 
									
										
										
										
											2021-07-05 19:17:08 +02:00
										 |  |  |                     break; | 
					
						
							| 
									
										
										
										
											2020-04-09 08:00:45 +04:30
										 |  |  |                 default: | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             editor.set_prompt(prompt_for_level(open_indents)); | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-03 13:17:33 +02:00
										 |  |  |         auto complete = [&interpreter, &global_environment](Line::Editor const& editor) -> Vector<Line::CompletionSuggestion> { | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |             auto line = editor.line(editor.cursor()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             JS::Lexer lexer { line }; | 
					
						
							|  |  |  |             enum { | 
					
						
							|  |  |  |                 Initial, | 
					
						
							|  |  |  |                 CompleteVariable, | 
					
						
							|  |  |  |                 CompleteNullProperty, | 
					
						
							|  |  |  |                 CompleteProperty, | 
					
						
							|  |  |  |             } mode { Initial }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             StringView variable_name; | 
					
						
							|  |  |  |             StringView property_name; | 
					
						
							| 
									
										
										
										
											2020-04-11 19:02:15 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |             // we're only going to complete either
 | 
					
						
							|  |  |  |             //    - <N>
 | 
					
						
							|  |  |  |             //        where N is part of the name of a variable
 | 
					
						
							|  |  |  |             //    - <N>.<P>
 | 
					
						
							|  |  |  |             //        where N is the complete name of a variable and
 | 
					
						
							|  |  |  |             //        P is part of the name of one of its properties
 | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |             auto js_token = lexer.next(); | 
					
						
							|  |  |  |             for (; js_token.type() != JS::TokenType::Eof; js_token = lexer.next()) { | 
					
						
							|  |  |  |                 switch (mode) { | 
					
						
							|  |  |  |                 case CompleteVariable: | 
					
						
							|  |  |  |                     switch (js_token.type()) { | 
					
						
							|  |  |  |                     case JS::TokenType::Period: | 
					
						
							|  |  |  |                         // ...<name> <dot>
 | 
					
						
							|  |  |  |                         mode = CompleteNullProperty; | 
					
						
							|  |  |  |                         break; | 
					
						
							|  |  |  |                     default: | 
					
						
							|  |  |  |                         // not a dot, reset back to initial
 | 
					
						
							|  |  |  |                         mode = Initial; | 
					
						
							|  |  |  |                         break; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case CompleteNullProperty: | 
					
						
							|  |  |  |                     if (js_token.is_identifier_name()) { | 
					
						
							|  |  |  |                         // ...<name> <dot> <name>
 | 
					
						
							|  |  |  |                         mode = CompleteProperty; | 
					
						
							|  |  |  |                         property_name = js_token.value(); | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         mode = Initial; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case CompleteProperty: | 
					
						
							|  |  |  |                     // something came after the property access, reset to initial
 | 
					
						
							|  |  |  |                 case Initial: | 
					
						
							| 
									
										
										
										
											2021-07-16 02:33:09 +04:30
										 |  |  |                     if (js_token.type() == JS::TokenType::Identifier) { | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                         // ...<name>...
 | 
					
						
							|  |  |  |                         mode = CompleteVariable; | 
					
						
							|  |  |  |                         variable_name = js_token.value(); | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         mode = Initial; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             bool last_token_has_trivia = js_token.trivia().length() > 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (mode == CompleteNullProperty) { | 
					
						
							|  |  |  |                 mode = CompleteProperty; | 
					
						
							| 
									
										
										
										
											2022-07-11 17:32:29 +00:00
										 |  |  |                 property_name = ""sv; | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                 last_token_has_trivia = false; // <name> <dot> [tab] is sensible to complete.
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (mode == Initial || last_token_has_trivia) | 
					
						
							|  |  |  |                 return {}; // we do not know how to complete this
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-19 18:32:28 +04:30
										 |  |  |             Vector<Line::CompletionSuggestion> results; | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 00:55:02 +01:00
										 |  |  |             Function<void(JS::Shape const&, StringView)> list_all_properties = [&results, &list_all_properties](JS::Shape const& shape, auto property_pattern) { | 
					
						
							| 
									
										
										
										
											2021-07-06 23:58:50 +01:00
										 |  |  |                 for (auto const& descriptor : shape.property_table()) { | 
					
						
							| 
									
										
										
										
											2020-07-07 21:38:46 -07:00
										 |  |  |                     if (!descriptor.key.is_string()) | 
					
						
							|  |  |  |                         continue; | 
					
						
							|  |  |  |                     auto key = descriptor.key.as_string(); | 
					
						
							|  |  |  |                     if (key.view().starts_with(property_pattern)) { | 
					
						
							|  |  |  |                         Line::CompletionSuggestion completion { key, Line::CompletionSuggestion::ForSearch }; | 
					
						
							| 
									
										
										
										
											2020-05-02 16:01:09 +01:00
										 |  |  |                         if (!results.contains_slow(completion)) { // hide duplicates
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |                             results.append(DeprecatedString(key)); | 
					
						
							| 
									
										
										
										
											2022-02-28 17:28:47 +03:30
										 |  |  |                             results.last().invariant_offset = property_pattern.length(); | 
					
						
							| 
									
										
										
										
											2020-04-11 19:02:15 +04:30
										 |  |  |                         } | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-07-06 23:58:50 +01:00
										 |  |  |                 if (auto const* prototype = shape.prototype()) { | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |                     list_all_properties(prototype->shape(), property_pattern); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |             switch (mode) { | 
					
						
							|  |  |  |             case CompleteProperty: { | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |                 auto reference_or_error = g_vm->resolve_binding(variable_name, &global_environment); | 
					
						
							| 
									
										
										
										
											2022-01-04 22:54:31 +01:00
										 |  |  |                 if (reference_or_error.is_error()) | 
					
						
							|  |  |  |                     return {}; | 
					
						
							| 
									
										
										
										
											2022-08-21 15:39:13 +01:00
										 |  |  |                 auto value_or_error = reference_or_error.value().get_value(*g_vm); | 
					
						
							| 
									
										
										
										
											2022-01-04 22:54:31 +01:00
										 |  |  |                 if (value_or_error.is_error()) | 
					
						
							|  |  |  |                     return {}; | 
					
						
							|  |  |  |                 auto variable = value_or_error.value(); | 
					
						
							|  |  |  |                 VERIFY(!variable.is_empty()); | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  | 
 | 
					
						
							|  |  |  |                 if (!variable.is_object()) | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                     break; | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 14:00:56 +01:00
										 |  |  |                 auto const* object = MUST(variable.to_object(*g_vm)); | 
					
						
							| 
									
										
										
										
											2021-07-06 23:58:50 +01:00
										 |  |  |                 auto const& shape = object->shape(); | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                 list_all_properties(shape, property_name); | 
					
						
							|  |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |             case CompleteVariable: { | 
					
						
							| 
									
										
										
										
											2022-08-22 19:00:49 +01:00
										 |  |  |                 auto const& variable = interpreter->realm().global_object(); | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                 list_all_properties(variable.shape(), variable_name); | 
					
						
							| 
									
										
										
										
											2021-10-03 13:20:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-07 14:02:37 +01:00
										 |  |  |                 for (auto const& name : global_environment.declarative_record().bindings()) { | 
					
						
							| 
									
										
										
										
											2022-02-28 17:28:47 +03:30
										 |  |  |                     if (name.starts_with(variable_name)) { | 
					
						
							| 
									
										
										
										
											2021-10-03 13:20:15 +02:00
										 |  |  |                         results.empend(name); | 
					
						
							| 
									
										
										
										
											2022-02-28 17:28:47 +03:30
										 |  |  |                         results.last().invariant_offset = variable_name.length(); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-10-03 13:20:15 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             default: | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |                 VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 13:39:55 +04:30
										 |  |  |             return results; | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2020-05-19 08:42:01 +04:30
										 |  |  |         s_editor->on_tab_complete = move(complete); | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |         TRY(repl(*interpreter)); | 
					
						
							| 
									
										
										
										
											2020-10-25 23:36:06 +00:00
										 |  |  |         s_editor->save_history(s_history_path); | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2022-06-30 19:34:38 +01:00
										 |  |  |         interpreter = JS::Interpreter::create<ScriptObject>(*g_vm); | 
					
						
							| 
									
										
										
										
											2022-08-28 14:20:06 +01:00
										 |  |  |         auto& console_object = *interpreter->realm().intrinsics().console_object(); | 
					
						
							|  |  |  |         ReplConsoleClient console_client(console_object.console()); | 
					
						
							|  |  |  |         console_object.console().set_client(console_client); | 
					
						
							| 
									
										
										
										
											2020-04-01 15:20:32 -04:00
										 |  |  |         interpreter->heap().set_should_collect_on_every_allocation(gc_on_every_allocation); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-24 17:59:16 +01:00
										 |  |  |         signal(SIGINT, [](int) { | 
					
						
							|  |  |  |             sigint_handler(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-16 13:55:34 +00:00
										 |  |  |         StringBuilder builder; | 
					
						
							| 
									
										
										
										
											2022-02-09 07:53:49 -05:00
										 |  |  |         StringView source_name; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (evaluate_script.is_empty()) { | 
					
						
							|  |  |  |             if (script_paths.size() > 1) | 
					
						
							|  |  |  |                 warnln("Warning: Multiple files supplied, this will concatenate the sources and resolve modules as if it was the first file"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (auto& path : script_paths) { | 
					
						
							| 
									
										
										
										
											2022-09-14 15:19:11 +01:00
										 |  |  |                 auto file = TRY(Core::Stream::File::open(path, Core::Stream::OpenMode::Read)); | 
					
						
							| 
									
										
										
										
											2022-12-11 17:49:00 +01:00
										 |  |  |                 auto file_contents = TRY(file->read_until_eof()); | 
					
						
							| 
									
										
										
										
											2022-02-09 07:53:49 -05:00
										 |  |  |                 auto source = StringView { file_contents }; | 
					
						
							| 
									
										
										
										
											2022-04-03 09:31:04 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if (Utf8View { file_contents }.validate()) { | 
					
						
							|  |  |  |                     builder.append(source); | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     auto* decoder = TextCodec::decoder_for("windows-1252"); | 
					
						
							|  |  |  |                     VERIFY(decoder); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     auto utf8_source = TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, source); | 
					
						
							|  |  |  |                     builder.append(utf8_source); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-02-09 07:53:49 -05:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             source_name = script_paths[0]; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             builder.append(evaluate_script); | 
					
						
							|  |  |  |             source_name = "eval"sv; | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-17 18:47:32 +01:00
										 |  |  |         // We resolve modules as if it is the first file
 | 
					
						
							| 
									
										
										
										
											2021-10-24 14:33:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 07:50:51 +03:30
										 |  |  |         if (!TRY(parse_and_run(*interpreter, builder.string_view(), source_name))) | 
					
						
							| 
									
										
										
										
											2020-04-13 02:05:21 +02:00
										 |  |  |             return 1; | 
					
						
							| 
									
										
										
										
											2020-03-26 01:10:29 +03:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-08 19:59:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  |     return 0; | 
					
						
							|  |  |  | } |