1 //===-- REPL.cpp ----------------------------------------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
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"
20 
21 #include <memory>
22 
23 using namespace lldb_private;
24 
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);
31 }
32 
33 REPL::~REPL() = default;
34 
35 lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language,
36                           Debugger *debugger, Target *target,
37                           const char *repl_options) {
38   uint32_t idx = 0;
39   lldb::REPLSP ret;
40 
41   while (REPLCreateInstance create_instance =
42              PluginManager::GetREPLCreateCallbackAtIndex(idx++)) {
43     ret = (*create_instance)(err, language, debugger, target, repl_options);
44     if (ret) {
45       break;
46     }
47   }
48 
49   return ret;
50 }
51 
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();
58   } else {
59     tmpdir_file_spec = FileSpec("/tmp");
60     tmpdir_file_spec.AppendPathComponent(file_basename.GetStringRef());
61   }
62 
63   return tmpdir_file_spec.GetPath();
64 }
65 
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
74         true,                  // Multi-line
75         true,                  // The REPL prompt is always colored
76         1,                     // Line number
77         *this, nullptr);
78 
79     // Don't exit if CTRL+C is pressed
80     static_cast<IOHandlerEditline *>(m_io_handler_sp.get())
81         ->SetInterruptExits(false);
82 
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();
87     } else {
88       m_indent_str.clear();
89       m_enable_auto_indent = false;
90     }
91   }
92   return m_io_handler_sp;
93 }
94 
95 void REPL::IOHandlerActivated(IOHandler &io_handler, bool interactive) {
96   lldb::ProcessSP process_sp = m_target.GetProcessSP();
97   if (process_sp && process_sp->IsAlive())
98     return;
99   lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
100   error_sp->Printf("REPL requires a running target process.\n");
101   io_handler.SetIsDone(true);
102 }
103 
104 bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; }
105 
106 void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) {
107 }
108 
109 const char *REPL::IOHandlerGetFixIndentationCharacters() {
110   return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr);
111 }
112 
113 ConstString REPL::IOHandlerGetControlSequence(char ch) {
114   if (ch == 'd')
115     return ConstString(":quit\n");
116   return ConstString();
117 }
118 
119 const char *REPL::IOHandlerGetCommandPrefix() { return ":"; }
120 
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.\n\nCommands "
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"
130          "Type “< path” to read in code from a text file “path”.\n\n";
131 }
132 
133 bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) {
134   // Check for meta command
135   const size_t num_lines = lines.GetSize();
136   if (num_lines == 1) {
137     const char *first_line = lines.GetStringAtIndex(0);
138     if (first_line[0] == ':')
139       return true; // Meta command is a single line where that starts with ':'
140   }
141 
142   // Check if REPL input is done
143   std::string source_string(lines.CopyList());
144   return SourceIsComplete(source_string);
145 }
146 
147 int REPL::CalculateActualIndentation(const StringList &lines) {
148   std::string last_line = lines[lines.GetSize() - 1];
149 
150   int actual_indent = 0;
151   for (char &ch : last_line) {
152     if (ch != ' ')
153       break;
154     ++actual_indent;
155   }
156 
157   return actual_indent;
158 }
159 
160 int REPL::IOHandlerFixIndentation(IOHandler &io_handler,
161                                   const StringList &lines,
162                                   int cursor_position) {
163   if (!m_enable_auto_indent)
164     return 0;
165 
166   if (!lines.GetSize()) {
167     return 0;
168   }
169 
170   int tab_size = io_handler.GetDebugger().GetTabSize();
171 
172   lldb::offset_t desired_indent =
173       GetDesiredIndentation(lines, cursor_position, tab_size);
174 
175   int actual_indent = REPL::CalculateActualIndentation(lines);
176 
177   if (desired_indent == LLDB_INVALID_OFFSET)
178     return 0;
179 
180   return (int)desired_indent - actual_indent;
181 }
182 
183 static bool ReadCode(const std::string &path, std::string &code,
184                      lldb::StreamFileSP &error_sp) {
185   auto &fs = FileSystem::Instance();
186   llvm::Twine pathTwine(path);
187   if (!fs.Exists(pathTwine)) {
188     error_sp->Printf("no such file at path '%s'\n", path.c_str());
189     return false;
190   }
191   if (!fs.Readable(pathTwine)) {
192     error_sp->Printf("could not read file at path '%s'\n", path.c_str());
193     return false;
194   }
195   const size_t file_size = fs.GetByteSize(pathTwine);
196   const size_t max_size = code.max_size();
197   if (file_size > max_size) {
198     error_sp->Printf("file at path '%s' too large: "
199                      "file_size = %zu, max_size = %zu\n",
200                      path.c_str(), file_size, max_size);
201     return false;
202   }
203   auto data_sp = fs.CreateDataBuffer(pathTwine);
204   if (data_sp == nullptr) {
205     error_sp->Printf("could not create buffer for file at path '%s'\n",
206                      path.c_str());
207     return false;
208   }
209   code.assign((const char *)data_sp->GetBytes(), data_sp->GetByteSize());
210   return true;
211 }
212 
213 void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {
214   lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
215   lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
216   bool extra_line = false;
217   bool did_quit = false;
218 
219   if (code.empty()) {
220     m_code.AppendString("");
221     static_cast<IOHandlerEditline &>(io_handler)
222         .SetBaseLineNumber(m_code.GetSize() + 1);
223   } else {
224     Debugger &debugger = m_target.GetDebugger();
225     CommandInterpreter &ci = debugger.GetCommandInterpreter();
226     extra_line = ci.GetSpaceReplPrompts();
227 
228     ExecutionContext exe_ctx(m_target.GetProcessSP()
229                                  ->GetThreadList()
230                                  .GetSelectedThread()
231                                  ->GetSelectedFrame()
232                                  .get());
233 
234     lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
235 
236     if (code[0] == ':') {
237       // Meta command
238       // Strip the ':'
239       code.erase(0, 1);
240       if (!llvm::StringRef(code).trim().empty()) {
241         // "lldb" was followed by arguments, so just execute the command dump
242         // the results
243 
244         // Turn off prompt on quit in case the user types ":quit"
245         const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
246         if (saved_prompt_on_quit)
247           ci.SetPromptOnQuit(false);
248 
249         // Execute the command
250         CommandReturnObject result(debugger.GetUseColor());
251         result.SetImmediateOutputStream(output_sp);
252         result.SetImmediateErrorStream(error_sp);
253         ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
254 
255         if (saved_prompt_on_quit)
256           ci.SetPromptOnQuit(true);
257 
258         if (result.GetStatus() == lldb::eReturnStatusQuit) {
259           did_quit = true;
260           io_handler.SetIsDone(true);
261           if (debugger.CheckTopIOHandlerTypes(
262                   IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
263             // We typed "quit" or an alias to quit so we need to check if the
264             // command interpreter is above us and tell it that it is done as
265             // well so we don't drop back into the command interpreter if we
266             // have already quit
267             lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
268             if (io_handler_sp)
269               io_handler_sp->SetIsDone(true);
270           }
271         }
272       } else {
273         // ":" was followed by no arguments, so push the LLDB command prompt
274         if (debugger.CheckTopIOHandlerTypes(
275                 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
276           // If the user wants to get back to the command interpreter and the
277           // command interpreter is what launched the REPL, then just let the
278           // REPL exit and fall back to the command interpreter.
279           io_handler.SetIsDone(true);
280         } else {
281           // The REPL wasn't launched the by the command interpreter, it is the
282           // base IOHandler, so we need to get the command interpreter and
283           lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
284           if (io_handler_sp) {
285             io_handler_sp->SetIsDone(false);
286             debugger.RunIOHandlerAsync(ci.GetIOHandler());
287           }
288         }
289       }
290     } else {
291       if (code[0] == '<') {
292         // User wants to read code from a file.
293         // Interpret rest of line as a literal path.
294         auto path = llvm::StringRef(code.substr(1)).trim().str();
295         if (!ReadCode(path, code, error_sp)) {
296           return;
297         }
298       }
299 
300       // Unwind any expression we might have been running in case our REPL
301       // expression crashed and the user was looking around
302       if (m_dedicated_repl_mode) {
303         Thread *thread = exe_ctx.GetThreadPtr();
304         if (thread && thread->UnwindInnermostExpression().Success()) {
305           thread->SetSelectedFrameByIndex(0, false);
306           exe_ctx.SetFrameSP(thread->GetSelectedFrame());
307         }
308       }
309 
310       const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
311 
312       EvaluateExpressionOptions expr_options = m_expr_options;
313       expr_options.SetCoerceToId(m_varobj_options.use_objc);
314       expr_options.SetKeepInMemory(true);
315       expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
316       expr_options.SetGenerateDebugInfo(true);
317       expr_options.SetREPLEnabled(true);
318       expr_options.SetColorizeErrors(colorize_err);
319       expr_options.SetPoundLine(m_repl_source_path.c_str(),
320                                 m_code.GetSize() + 1);
321 
322       expr_options.SetLanguage(GetLanguage());
323 
324       PersistentExpressionState *persistent_state =
325           m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
326       if (!persistent_state)
327         return;
328 
329       const size_t var_count_before = persistent_state->GetSize();
330 
331       const char *expr_prefix = nullptr;
332       lldb::ValueObjectSP result_valobj_sp;
333       Status error;
334       lldb::ExpressionResults execution_results =
335           UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(),
336                                    expr_prefix, result_valobj_sp, error,
337                                    nullptr); // fixed expression
338 
339       // CommandInterpreter &ci = debugger.GetCommandInterpreter();
340 
341       if (process_sp && process_sp->IsAlive()) {
342         bool add_to_code = true;
343         bool handled = false;
344         if (result_valobj_sp) {
345           lldb::Format format = m_format_options.GetFormat();
346 
347           if (result_valobj_sp->GetError().Success()) {
348             handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
349           } else if (result_valobj_sp->GetError().GetError() ==
350                      UserExpression::kNoResult) {
351             if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) {
352               error_sp->PutCString("(void)\n");
353               handled = true;
354             }
355           }
356         }
357 
358         if (debugger.GetPrintDecls()) {
359           for (size_t vi = var_count_before, ve = persistent_state->GetSize();
360                vi != ve; ++vi) {
361             lldb::ExpressionVariableSP persistent_var_sp =
362                 persistent_state->GetVariableAtIndex(vi);
363             lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
364 
365             PrintOneVariable(debugger, output_sp, valobj_sp,
366                              persistent_var_sp.get());
367           }
368         }
369 
370         if (!handled) {
371           bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
372           switch (execution_results) {
373           case lldb::eExpressionSetupError:
374           case lldb::eExpressionParseError:
375             add_to_code = false;
376             LLVM_FALLTHROUGH;
377           case lldb::eExpressionDiscarded:
378             error_sp->Printf("%s\n", error.AsCString());
379             break;
380 
381           case lldb::eExpressionCompleted:
382             break;
383           case lldb::eExpressionInterrupted:
384             if (useColors) {
385               error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
386               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
387             }
388             error_sp->Printf("Execution interrupted. ");
389             if (useColors)
390               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
391             error_sp->Printf("Enter code to recover and continue.\nEnter LLDB "
392                              "commands to investigate (type :help for "
393                              "assistance.)\n");
394             break;
395 
396           case lldb::eExpressionHitBreakpoint:
397             // Breakpoint was hit, drop into LLDB command interpreter
398             if (useColors) {
399               error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
400               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
401             }
402             output_sp->Printf("Execution stopped at breakpoint.  ");
403             if (useColors)
404               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
405             output_sp->Printf("Enter LLDB commands to investigate (type help "
406                               "for assistance.)\n");
407             {
408               lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
409               if (io_handler_sp) {
410                 io_handler_sp->SetIsDone(false);
411                 debugger.RunIOHandlerAsync(ci.GetIOHandler());
412               }
413             }
414             break;
415 
416           case lldb::eExpressionTimedOut:
417             error_sp->Printf("error: timeout\n");
418             if (error.AsCString())
419               error_sp->Printf("error: %s\n", error.AsCString());
420             break;
421           case lldb::eExpressionResultUnavailable:
422             // Shoulnd't happen???
423             error_sp->Printf("error: could not fetch result -- %s\n",
424                              error.AsCString());
425             break;
426           case lldb::eExpressionStoppedForDebug:
427             // Shoulnd't happen???
428             error_sp->Printf("error: stopped for debug -- %s\n",
429                              error.AsCString());
430             break;
431           case lldb::eExpressionThreadVanished:
432             // Shoulnd't happen???
433             error_sp->Printf("error: expression thread vanished -- %s\n",
434                              error.AsCString());
435             break;
436           }
437         }
438 
439         if (add_to_code) {
440           const uint32_t new_default_line = m_code.GetSize() + 1;
441 
442           m_code.SplitIntoLines(code);
443 
444           // Update our code on disk
445           if (!m_repl_source_path.empty()) {
446             auto file = FileSystem::Instance().Open(
447                 FileSpec(m_repl_source_path),
448                 File::eOpenOptionWrite | File::eOpenOptionTruncate |
449                     File::eOpenOptionCanCreate,
450                 lldb::eFilePermissionsFileDefault);
451             if (file) {
452               std::string code(m_code.CopyList());
453               code.append(1, '\n');
454               size_t bytes_written = code.size();
455               file.get()->Write(code.c_str(), bytes_written);
456               file.get()->Close();
457             } else {
458               std::string message = llvm::toString(file.takeError());
459               error_sp->Printf("error: couldn't open %s: %s\n",
460                                m_repl_source_path.c_str(), message.c_str());
461             }
462 
463             // Now set the default file and line to the REPL source file
464             m_target.GetSourceManager().SetDefaultFileAndLine(
465                 FileSpec(m_repl_source_path), new_default_line);
466           }
467           static_cast<IOHandlerEditline &>(io_handler)
468               .SetBaseLineNumber(m_code.GetSize() + 1);
469         }
470         if (extra_line) {
471           output_sp->Printf("\n");
472         }
473       }
474     }
475 
476     // Don't complain about the REPL process going away if we are in the
477     // process of quitting.
478     if (!did_quit && (!process_sp || !process_sp->IsAlive())) {
479       error_sp->Printf(
480           "error: REPL process is no longer alive, exiting REPL\n");
481       io_handler.SetIsDone(true);
482     }
483   }
484 }
485 
486 void REPL::IOHandlerComplete(IOHandler &io_handler,
487                              CompletionRequest &request) {
488   // Complete an LLDB command if the first character is a colon...
489   if (request.GetRawLine().startswith(":")) {
490     Debugger &debugger = m_target.GetDebugger();
491 
492     // auto complete LLDB commands
493     llvm::StringRef new_line = request.GetRawLine().drop_front();
494     CompletionResult sub_result;
495     CompletionRequest sub_request(new_line, request.GetRawCursorPos() - 1,
496                                   sub_result);
497     debugger.GetCommandInterpreter().HandleCompletion(sub_request);
498     StringList matches, descriptions;
499     sub_result.GetMatches(matches);
500     // Prepend command prefix that was excluded in the completion request.
501     if (request.GetCursorIndex() == 0)
502       for (auto &match : matches)
503         match.insert(0, 1, ':');
504     sub_result.GetDescriptions(descriptions);
505     request.AddCompletions(matches, descriptions);
506     return;
507   }
508 
509   // Strip spaces from the line and see if we had only spaces
510   if (request.GetRawLine().trim().empty()) {
511     // Only spaces on this line, so just indent
512     request.AddCompletion(m_indent_str);
513     return;
514   }
515 
516   std::string current_code;
517   current_code.append(m_code.CopyList());
518 
519   IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
520   const StringList *current_lines = editline.GetCurrentLines();
521   if (current_lines) {
522     const uint32_t current_line_idx = editline.GetCurrentLineIndex();
523 
524     if (current_line_idx < current_lines->GetSize()) {
525       for (uint32_t i = 0; i < current_line_idx; ++i) {
526         const char *line_cstr = current_lines->GetStringAtIndex(i);
527         if (line_cstr) {
528           current_code.append("\n");
529           current_code.append(line_cstr);
530         }
531       }
532     }
533   }
534 
535   current_code.append("\n");
536   current_code += request.GetRawLine();
537 
538   CompleteCode(current_code, request);
539 }
540 
541 bool QuitCommandOverrideCallback(void *baton, const char **argv) {
542   Target *target = (Target *)baton;
543   lldb::ProcessSP process_sp(target->GetProcessSP());
544   if (process_sp) {
545     process_sp->Destroy(false);
546     process_sp->GetTarget().GetDebugger().ClearIOHandlers();
547   }
548   return false;
549 }
550 
551 Status REPL::RunLoop() {
552   Status error;
553 
554   error = DoInitialization();
555   m_repl_source_path = GetSourcePath();
556 
557   if (!error.Success())
558     return error;
559 
560   Debugger &debugger = m_target.GetDebugger();
561 
562   lldb::IOHandlerSP io_handler_sp(GetIOHandler());
563 
564   FileSpec save_default_file;
565   uint32_t save_default_line = 0;
566 
567   if (!m_repl_source_path.empty()) {
568     // Save the current default file and line
569     m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file,
570                                                       save_default_line);
571   }
572 
573   debugger.RunIOHandlerAsync(io_handler_sp);
574 
575   // Check if we are in dedicated REPL mode where LLDB was start with the "--
576   // repl" option from the command line. Currently we know this by checking if
577   // the debugger already has a IOHandler thread.
578   if (!debugger.HasIOHandlerThread()) {
579     // The debugger doesn't have an existing IOHandler thread, so this must be
580     // dedicated REPL mode...
581     m_dedicated_repl_mode = true;
582     debugger.StartIOHandlerThread();
583     llvm::StringRef command_name_str("quit");
584     CommandObject *cmd_obj =
585         debugger.GetCommandInterpreter().GetCommandObjectForCommand(
586             command_name_str);
587     if (cmd_obj) {
588       assert(command_name_str.empty());
589       cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target);
590     }
591   }
592 
593   // Wait for the REPL command interpreter to get popped
594   io_handler_sp->WaitForPop();
595 
596   if (m_dedicated_repl_mode) {
597     // If we were in dedicated REPL mode we would have started the IOHandler
598     // thread, and we should kill our process
599     lldb::ProcessSP process_sp = m_target.GetProcessSP();
600     if (process_sp && process_sp->IsAlive())
601       process_sp->Destroy(false);
602 
603     // Wait for the IO handler thread to exit (TODO: don't do this if the IO
604     // handler thread already exists...)
605     debugger.JoinIOHandlerThread();
606   }
607 
608   // Restore the default file and line
609   if (save_default_file && save_default_line != 0)
610     m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file,
611                                                       save_default_line);
612   return error;
613 }
614