1 //===-- REPL.cpp ----------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Expression/REPL.h"
10 #include "lldb/Core/Debugger.h"
11 #include "lldb/Core/PluginManager.h"
12 #include "lldb/Core/StreamFile.h"
13 #include "lldb/Expression/ExpressionVariable.h"
14 #include "lldb/Expression/UserExpression.h"
15 #include "lldb/Host/HostInfo.h"
16 #include "lldb/Interpreter/CommandInterpreter.h"
17 #include "lldb/Interpreter/CommandReturnObject.h"
18 #include "lldb/Target/Thread.h"
19 #include "lldb/Utility/AnsiTerminal.h"
23 using namespace lldb_private;
25 REPL::REPL(LLVMCastKind kind, Target &target) : m_target(target), m_kind(kind) {
26 // Make sure all option values have sane defaults
27 Debugger &debugger = m_target.GetDebugger();
28 auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext();
29 m_format_options.OptionParsingStarting(&exe_ctx);
30 m_varobj_options.OptionParsingStarting(&exe_ctx);
33 REPL::~REPL() = default;
35 lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language,
36 Debugger *debugger, Target *target,
37 const char *repl_options) {
41 while (REPLCreateInstance create_instance =
42 PluginManager::GetREPLCreateCallbackAtIndex(idx++)) {
43 ret = (*create_instance)(err, language, debugger, target, repl_options);
52 std::string REPL::GetSourcePath() {
53 ConstString file_basename = GetSourceFileBasename();
54 FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
55 if (tmpdir_file_spec) {
56 tmpdir_file_spec.GetFilename() = file_basename;
57 m_repl_source_path = tmpdir_file_spec.GetPath();
59 tmpdir_file_spec = FileSpec("/tmp");
60 tmpdir_file_spec.AppendPathComponent(file_basename.GetStringRef());
63 return tmpdir_file_spec.GetPath();
66 lldb::IOHandlerSP REPL::GetIOHandler() {
67 if (!m_io_handler_sp) {
68 Debugger &debugger = m_target.GetDebugger();
69 m_io_handler_sp = std::make_shared<IOHandlerEditline>(
70 debugger, IOHandler::Type::REPL,
71 "lldb-repl", // Name of input reader for history
72 llvm::StringRef("> "), // prompt
73 llvm::StringRef(". "), // Continuation prompt
75 true, // The REPL prompt is always colored
79 // Don't exit if CTRL+C is pressed
80 static_cast<IOHandlerEditline *>(m_io_handler_sp.get())
81 ->SetInterruptExits(false);
83 if (m_io_handler_sp->GetIsInteractive() &&
84 m_io_handler_sp->GetIsRealTerminal()) {
85 m_indent_str.assign(debugger.GetTabSize(), ' ');
86 m_enable_auto_indent = debugger.GetAutoIndent();
89 m_enable_auto_indent = false;
92 return m_io_handler_sp;
95 void REPL::IOHandlerActivated(IOHandler &io_handler, bool interactive) {
96 lldb::ProcessSP process_sp = m_target.GetProcessSP();
97 if (process_sp && process_sp->IsAlive())
99 lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
100 error_sp->Printf("REPL requires a running target process.\n");
101 io_handler.SetIsDone(true);
104 bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; }
106 void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) {
109 const char *REPL::IOHandlerGetFixIndentationCharacters() {
110 return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr);
113 ConstString REPL::IOHandlerGetControlSequence(char ch) {
115 return ConstString(":quit\n");
116 return ConstString();
119 const char *REPL::IOHandlerGetCommandPrefix() { return ":"; }
121 const char *REPL::IOHandlerGetHelpPrologue() {
122 return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter. "
123 "Valid statements, expressions, and declarations are immediately "
124 "compiled and executed.\n\n"
125 "The complete set of LLDB debugging commands are also available as "
126 "described below. Commands "
127 "must be prefixed with a colon at the REPL prompt (:quit for "
128 "example.) Typing just a colon "
129 "followed by return will switch to the LLDB prompt.\n\n";
132 bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) {
133 // Check for meta command
134 const size_t num_lines = lines.GetSize();
135 if (num_lines == 1) {
136 const char *first_line = lines.GetStringAtIndex(0);
137 if (first_line[0] == ':')
138 return true; // Meta command is a single line where that starts with ':'
141 // Check if REPL input is done
142 std::string source_string(lines.CopyList());
143 return SourceIsComplete(source_string);
146 int REPL::CalculateActualIndentation(const StringList &lines) {
147 std::string last_line = lines[lines.GetSize() - 1];
149 int actual_indent = 0;
150 for (char &ch : last_line) {
156 return actual_indent;
159 int REPL::IOHandlerFixIndentation(IOHandler &io_handler,
160 const StringList &lines,
161 int cursor_position) {
162 if (!m_enable_auto_indent)
165 if (!lines.GetSize()) {
169 int tab_size = io_handler.GetDebugger().GetTabSize();
171 lldb::offset_t desired_indent =
172 GetDesiredIndentation(lines, cursor_position, tab_size);
174 int actual_indent = REPL::CalculateActualIndentation(lines);
176 if (desired_indent == LLDB_INVALID_OFFSET)
179 return (int)desired_indent - actual_indent;
182 void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {
183 lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
184 lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
185 bool extra_line = false;
186 bool did_quit = false;
189 m_code.AppendString("");
190 static_cast<IOHandlerEditline &>(io_handler)
191 .SetBaseLineNumber(m_code.GetSize() + 1);
193 Debugger &debugger = m_target.GetDebugger();
194 CommandInterpreter &ci = debugger.GetCommandInterpreter();
195 extra_line = ci.GetSpaceReplPrompts();
197 ExecutionContext exe_ctx(m_target.GetProcessSP()
203 lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
205 if (code[0] == ':') {
209 if (!llvm::StringRef(code).trim().empty()) {
210 // "lldb" was followed by arguments, so just execute the command dump
213 // Turn off prompt on quit in case the user types ":quit"
214 const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
215 if (saved_prompt_on_quit)
216 ci.SetPromptOnQuit(false);
218 // Execute the command
219 CommandReturnObject result(debugger.GetUseColor());
220 result.SetImmediateOutputStream(output_sp);
221 result.SetImmediateErrorStream(error_sp);
222 ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
224 if (saved_prompt_on_quit)
225 ci.SetPromptOnQuit(true);
227 if (result.GetStatus() == lldb::eReturnStatusQuit) {
229 io_handler.SetIsDone(true);
230 if (debugger.CheckTopIOHandlerTypes(
231 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
232 // We typed "quit" or an alias to quit so we need to check if the
233 // command interpreter is above us and tell it that it is done as
234 // well so we don't drop back into the command interpreter if we
236 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
238 io_handler_sp->SetIsDone(true);
242 // ":" was followed by no arguments, so push the LLDB command prompt
243 if (debugger.CheckTopIOHandlerTypes(
244 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
245 // If the user wants to get back to the command interpreter and the
246 // command interpreter is what launched the REPL, then just let the
247 // REPL exit and fall back to the command interpreter.
248 io_handler.SetIsDone(true);
250 // The REPL wasn't launched the by the command interpreter, it is the
251 // base IOHandler, so we need to get the command interpreter and
252 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
254 io_handler_sp->SetIsDone(false);
255 debugger.RunIOHandlerAsync(ci.GetIOHandler());
260 // Unwind any expression we might have been running in case our REPL
261 // expression crashed and the user was looking around
262 if (m_dedicated_repl_mode) {
263 Thread *thread = exe_ctx.GetThreadPtr();
264 if (thread && thread->UnwindInnermostExpression().Success()) {
265 thread->SetSelectedFrameByIndex(0, false);
266 exe_ctx.SetFrameSP(thread->GetSelectedFrame());
270 const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
272 EvaluateExpressionOptions expr_options = m_expr_options;
273 expr_options.SetCoerceToId(m_varobj_options.use_objc);
274 expr_options.SetKeepInMemory(true);
275 expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
276 expr_options.SetGenerateDebugInfo(true);
277 expr_options.SetREPLEnabled(true);
278 expr_options.SetColorizeErrors(colorize_err);
279 expr_options.SetPoundLine(m_repl_source_path.c_str(),
280 m_code.GetSize() + 1);
282 expr_options.SetLanguage(GetLanguage());
284 PersistentExpressionState *persistent_state =
285 m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
286 if (!persistent_state)
289 const size_t var_count_before = persistent_state->GetSize();
291 const char *expr_prefix = nullptr;
292 lldb::ValueObjectSP result_valobj_sp;
294 lldb::ExpressionResults execution_results =
295 UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(),
296 expr_prefix, result_valobj_sp, error,
297 nullptr); // fixed expression
299 // CommandInterpreter &ci = debugger.GetCommandInterpreter();
301 if (process_sp && process_sp->IsAlive()) {
302 bool add_to_code = true;
303 bool handled = false;
304 if (result_valobj_sp) {
305 lldb::Format format = m_format_options.GetFormat();
307 if (result_valobj_sp->GetError().Success()) {
308 handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
309 } else if (result_valobj_sp->GetError().GetError() ==
310 UserExpression::kNoResult) {
311 if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) {
312 error_sp->PutCString("(void)\n");
318 if (debugger.GetPrintDecls()) {
319 for (size_t vi = var_count_before, ve = persistent_state->GetSize();
321 lldb::ExpressionVariableSP persistent_var_sp =
322 persistent_state->GetVariableAtIndex(vi);
323 lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
325 PrintOneVariable(debugger, output_sp, valobj_sp,
326 persistent_var_sp.get());
331 bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
332 switch (execution_results) {
333 case lldb::eExpressionSetupError:
334 case lldb::eExpressionParseError:
337 case lldb::eExpressionDiscarded:
338 error_sp->Printf("%s\n", error.AsCString());
341 case lldb::eExpressionCompleted:
343 case lldb::eExpressionInterrupted:
345 error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
346 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
348 error_sp->Printf("Execution interrupted. ");
350 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
351 error_sp->Printf("Enter code to recover and continue.\nEnter LLDB "
352 "commands to investigate (type :help for "
356 case lldb::eExpressionHitBreakpoint:
357 // Breakpoint was hit, drop into LLDB command interpreter
359 error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
360 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
362 output_sp->Printf("Execution stopped at breakpoint. ");
364 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
365 output_sp->Printf("Enter LLDB commands to investigate (type help "
366 "for assistance.)\n");
368 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
370 io_handler_sp->SetIsDone(false);
371 debugger.RunIOHandlerAsync(ci.GetIOHandler());
376 case lldb::eExpressionTimedOut:
377 error_sp->Printf("error: timeout\n");
378 if (error.AsCString())
379 error_sp->Printf("error: %s\n", error.AsCString());
381 case lldb::eExpressionResultUnavailable:
382 // Shoulnd't happen???
383 error_sp->Printf("error: could not fetch result -- %s\n",
386 case lldb::eExpressionStoppedForDebug:
387 // Shoulnd't happen???
388 error_sp->Printf("error: stopped for debug -- %s\n",
391 case lldb::eExpressionThreadVanished:
392 // Shoulnd't happen???
393 error_sp->Printf("error: expression thread vanished -- %s\n",
400 const uint32_t new_default_line = m_code.GetSize() + 1;
402 m_code.SplitIntoLines(code);
404 // Update our code on disk
405 if (!m_repl_source_path.empty()) {
406 auto file = FileSystem::Instance().Open(
407 FileSpec(m_repl_source_path),
408 File::eOpenOptionWrite | File::eOpenOptionTruncate |
409 File::eOpenOptionCanCreate,
410 lldb::eFilePermissionsFileDefault);
412 std::string code(m_code.CopyList());
413 code.append(1, '\n');
414 size_t bytes_written = code.size();
415 file.get()->Write(code.c_str(), bytes_written);
418 std::string message = llvm::toString(file.takeError());
419 error_sp->Printf("error: couldn't open %s: %s\n",
420 m_repl_source_path.c_str(), message.c_str());
423 // Now set the default file and line to the REPL source file
424 m_target.GetSourceManager().SetDefaultFileAndLine(
425 FileSpec(m_repl_source_path), new_default_line);
427 static_cast<IOHandlerEditline &>(io_handler)
428 .SetBaseLineNumber(m_code.GetSize() + 1);
431 output_sp->Printf("\n");
436 // Don't complain about the REPL process going away if we are in the
437 // process of quitting.
438 if (!did_quit && (!process_sp || !process_sp->IsAlive())) {
440 "error: REPL process is no longer alive, exiting REPL\n");
441 io_handler.SetIsDone(true);
446 void REPL::IOHandlerComplete(IOHandler &io_handler,
447 CompletionRequest &request) {
448 // Complete an LLDB command if the first character is a colon...
449 if (request.GetRawLine().startswith(":")) {
450 Debugger &debugger = m_target.GetDebugger();
452 // auto complete LLDB commands
453 llvm::StringRef new_line = request.GetRawLine().drop_front();
454 CompletionResult sub_result;
455 CompletionRequest sub_request(new_line, request.GetRawCursorPos() - 1,
457 debugger.GetCommandInterpreter().HandleCompletion(sub_request);
458 StringList matches, descriptions;
459 sub_result.GetMatches(matches);
460 // Prepend command prefix that was excluded in the completion request.
461 if (request.GetCursorIndex() == 0)
462 for (auto &match : matches)
463 match.insert(0, 1, ':');
464 sub_result.GetDescriptions(descriptions);
465 request.AddCompletions(matches, descriptions);
469 // Strip spaces from the line and see if we had only spaces
470 if (request.GetRawLine().trim().empty()) {
471 // Only spaces on this line, so just indent
472 request.AddCompletion(m_indent_str);
476 std::string current_code;
477 current_code.append(m_code.CopyList());
479 IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
480 const StringList *current_lines = editline.GetCurrentLines();
482 const uint32_t current_line_idx = editline.GetCurrentLineIndex();
484 if (current_line_idx < current_lines->GetSize()) {
485 for (uint32_t i = 0; i < current_line_idx; ++i) {
486 const char *line_cstr = current_lines->GetStringAtIndex(i);
488 current_code.append("\n");
489 current_code.append(line_cstr);
495 current_code.append("\n");
496 current_code += request.GetRawLine();
498 CompleteCode(current_code, request);
501 bool QuitCommandOverrideCallback(void *baton, const char **argv) {
502 Target *target = (Target *)baton;
503 lldb::ProcessSP process_sp(target->GetProcessSP());
505 process_sp->Destroy(false);
506 process_sp->GetTarget().GetDebugger().ClearIOHandlers();
511 Status REPL::RunLoop() {
514 error = DoInitialization();
515 m_repl_source_path = GetSourcePath();
517 if (!error.Success())
520 Debugger &debugger = m_target.GetDebugger();
522 lldb::IOHandlerSP io_handler_sp(GetIOHandler());
524 FileSpec save_default_file;
525 uint32_t save_default_line = 0;
527 if (!m_repl_source_path.empty()) {
528 // Save the current default file and line
529 m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file,
533 debugger.RunIOHandlerAsync(io_handler_sp);
535 // Check if we are in dedicated REPL mode where LLDB was start with the "--
536 // repl" option from the command line. Currently we know this by checking if
537 // the debugger already has a IOHandler thread.
538 if (!debugger.HasIOHandlerThread()) {
539 // The debugger doesn't have an existing IOHandler thread, so this must be
540 // dedicated REPL mode...
541 m_dedicated_repl_mode = true;
542 debugger.StartIOHandlerThread();
543 llvm::StringRef command_name_str("quit");
544 CommandObject *cmd_obj =
545 debugger.GetCommandInterpreter().GetCommandObjectForCommand(
548 assert(command_name_str.empty());
549 cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target);
553 // Wait for the REPL command interpreter to get popped
554 io_handler_sp->WaitForPop();
556 if (m_dedicated_repl_mode) {
557 // If we were in dedicated REPL mode we would have started the IOHandler
558 // thread, and we should kill our process
559 lldb::ProcessSP process_sp = m_target.GetProcessSP();
560 if (process_sp && process_sp->IsAlive())
561 process_sp->Destroy(false);
563 // Wait for the IO handler thread to exit (TODO: don't do this if the IO
564 // handler thread already exists...)
565 debugger.JoinIOHandlerThread();
568 // Restore the default file and line
569 if (save_default_file && save_default_line != 0)
570 m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file,