xref: /freebsd-src/contrib/llvm-project/lldb/source/Expression/REPL.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
15ffd83dbSDimitry Andric //===-- REPL.cpp ----------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Expression/REPL.h"
100b57cec5SDimitry Andric #include "lldb/Core/Debugger.h"
110b57cec5SDimitry Andric #include "lldb/Core/PluginManager.h"
120b57cec5SDimitry Andric #include "lldb/Expression/ExpressionVariable.h"
130b57cec5SDimitry Andric #include "lldb/Expression/UserExpression.h"
140b57cec5SDimitry Andric #include "lldb/Host/HostInfo.h"
15*5f757f3fSDimitry Andric #include "lldb/Host/StreamFile.h"
160b57cec5SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
170b57cec5SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h"
180b57cec5SDimitry Andric #include "lldb/Target/Thread.h"
190b57cec5SDimitry Andric #include "lldb/Utility/AnsiTerminal.h"
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric #include <memory>
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric using namespace lldb_private;
240b57cec5SDimitry Andric 
2506c3fb27SDimitry Andric char REPL::ID;
2606c3fb27SDimitry Andric 
REPL(Target & target)2706c3fb27SDimitry Andric REPL::REPL(Target &target) : m_target(target) {
280b57cec5SDimitry Andric   // Make sure all option values have sane defaults
290b57cec5SDimitry Andric   Debugger &debugger = m_target.GetDebugger();
3081ad6265SDimitry Andric   debugger.SetShowProgress(false);
310b57cec5SDimitry Andric   auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext();
320b57cec5SDimitry Andric   m_format_options.OptionParsingStarting(&exe_ctx);
330b57cec5SDimitry Andric   m_varobj_options.OptionParsingStarting(&exe_ctx);
340b57cec5SDimitry Andric }
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric REPL::~REPL() = default;
370b57cec5SDimitry Andric 
Create(Status & err,lldb::LanguageType language,Debugger * debugger,Target * target,const char * repl_options)380b57cec5SDimitry Andric lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language,
390b57cec5SDimitry Andric                           Debugger *debugger, Target *target,
400b57cec5SDimitry Andric                           const char *repl_options) {
410b57cec5SDimitry Andric   uint32_t idx = 0;
420b57cec5SDimitry Andric   lldb::REPLSP ret;
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric   while (REPLCreateInstance create_instance =
450eae32dcSDimitry Andric              PluginManager::GetREPLCreateCallbackAtIndex(idx)) {
460eae32dcSDimitry Andric     LanguageSet supported_languages =
470eae32dcSDimitry Andric         PluginManager::GetREPLSupportedLanguagesAtIndex(idx++);
480eae32dcSDimitry Andric     if (!supported_languages[language])
490eae32dcSDimitry Andric       continue;
500b57cec5SDimitry Andric     ret = (*create_instance)(err, language, debugger, target, repl_options);
510b57cec5SDimitry Andric     if (ret) {
520b57cec5SDimitry Andric       break;
530b57cec5SDimitry Andric     }
540b57cec5SDimitry Andric   }
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric   return ret;
570b57cec5SDimitry Andric }
580b57cec5SDimitry Andric 
GetSourcePath()590b57cec5SDimitry Andric std::string REPL::GetSourcePath() {
6006c3fb27SDimitry Andric   llvm::StringRef file_basename = GetSourceFileBasename();
610b57cec5SDimitry Andric   FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
620b57cec5SDimitry Andric   if (tmpdir_file_spec) {
63bdd1243dSDimitry Andric     tmpdir_file_spec.SetFilename(file_basename);
640b57cec5SDimitry Andric     m_repl_source_path = tmpdir_file_spec.GetPath();
650b57cec5SDimitry Andric   } else {
660b57cec5SDimitry Andric     tmpdir_file_spec = FileSpec("/tmp");
6706c3fb27SDimitry Andric     tmpdir_file_spec.AppendPathComponent(file_basename);
680b57cec5SDimitry Andric   }
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric   return tmpdir_file_spec.GetPath();
710b57cec5SDimitry Andric }
720b57cec5SDimitry Andric 
GetIOHandler()730b57cec5SDimitry Andric lldb::IOHandlerSP REPL::GetIOHandler() {
740b57cec5SDimitry Andric   if (!m_io_handler_sp) {
750b57cec5SDimitry Andric     Debugger &debugger = m_target.GetDebugger();
760b57cec5SDimitry Andric     m_io_handler_sp = std::make_shared<IOHandlerEditline>(
770b57cec5SDimitry Andric         debugger, IOHandler::Type::REPL,
780b57cec5SDimitry Andric         "lldb-repl",           // Name of input reader for history
790b57cec5SDimitry Andric         llvm::StringRef("> "), // prompt
800b57cec5SDimitry Andric         llvm::StringRef(". "), // Continuation prompt
810b57cec5SDimitry Andric         true,                  // Multi-line
820b57cec5SDimitry Andric         true,                  // The REPL prompt is always colored
830b57cec5SDimitry Andric         1,                     // Line number
84bdd1243dSDimitry Andric         *this);
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric     // Don't exit if CTRL+C is pressed
870b57cec5SDimitry Andric     static_cast<IOHandlerEditline *>(m_io_handler_sp.get())
880b57cec5SDimitry Andric         ->SetInterruptExits(false);
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric     if (m_io_handler_sp->GetIsInteractive() &&
910b57cec5SDimitry Andric         m_io_handler_sp->GetIsRealTerminal()) {
920b57cec5SDimitry Andric       m_indent_str.assign(debugger.GetTabSize(), ' ');
930b57cec5SDimitry Andric       m_enable_auto_indent = debugger.GetAutoIndent();
940b57cec5SDimitry Andric     } else {
950b57cec5SDimitry Andric       m_indent_str.clear();
960b57cec5SDimitry Andric       m_enable_auto_indent = false;
970b57cec5SDimitry Andric     }
980b57cec5SDimitry Andric   }
990b57cec5SDimitry Andric   return m_io_handler_sp;
1000b57cec5SDimitry Andric }
1010b57cec5SDimitry Andric 
IOHandlerActivated(IOHandler & io_handler,bool interactive)1020b57cec5SDimitry Andric void REPL::IOHandlerActivated(IOHandler &io_handler, bool interactive) {
1030b57cec5SDimitry Andric   lldb::ProcessSP process_sp = m_target.GetProcessSP();
1040b57cec5SDimitry Andric   if (process_sp && process_sp->IsAlive())
1050b57cec5SDimitry Andric     return;
1069dba64beSDimitry Andric   lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
1070b57cec5SDimitry Andric   error_sp->Printf("REPL requires a running target process.\n");
1080b57cec5SDimitry Andric   io_handler.SetIsDone(true);
1090b57cec5SDimitry Andric }
1100b57cec5SDimitry Andric 
IOHandlerInterrupt(IOHandler & io_handler)1110b57cec5SDimitry Andric bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; }
1120b57cec5SDimitry Andric 
IOHandlerInputInterrupted(IOHandler & io_handler,std::string & line)1130b57cec5SDimitry Andric void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) {
1140b57cec5SDimitry Andric }
1150b57cec5SDimitry Andric 
IOHandlerGetFixIndentationCharacters()1160b57cec5SDimitry Andric const char *REPL::IOHandlerGetFixIndentationCharacters() {
1170b57cec5SDimitry Andric   return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr);
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric 
IOHandlerGetControlSequence(char ch)12006c3fb27SDimitry Andric llvm::StringRef REPL::IOHandlerGetControlSequence(char ch) {
12106c3fb27SDimitry Andric   static constexpr llvm::StringLiteral control_sequence(":quit\n");
1220b57cec5SDimitry Andric   if (ch == 'd')
12306c3fb27SDimitry Andric     return control_sequence;
12406c3fb27SDimitry Andric   return {};
1250b57cec5SDimitry Andric }
1260b57cec5SDimitry Andric 
IOHandlerGetCommandPrefix()1270b57cec5SDimitry Andric const char *REPL::IOHandlerGetCommandPrefix() { return ":"; }
1280b57cec5SDimitry Andric 
IOHandlerGetHelpPrologue()1290b57cec5SDimitry Andric const char *REPL::IOHandlerGetHelpPrologue() {
1300b57cec5SDimitry Andric   return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter.  "
1310b57cec5SDimitry Andric          "Valid statements, expressions, and declarations are immediately "
1320b57cec5SDimitry Andric          "compiled and executed.\n\n"
1330b57cec5SDimitry Andric          "The complete set of LLDB debugging commands are also available as "
134e8d8bef9SDimitry Andric          "described below.\n\nCommands "
1350b57cec5SDimitry Andric          "must be prefixed with a colon at the REPL prompt (:quit for "
1360b57cec5SDimitry Andric          "example.)  Typing just a colon "
137e8d8bef9SDimitry Andric          "followed by return will switch to the LLDB prompt.\n\n"
138e8d8bef9SDimitry Andric          "Type “< path” to read in code from a text file “path”.\n\n";
1390b57cec5SDimitry Andric }
1400b57cec5SDimitry Andric 
IOHandlerIsInputComplete(IOHandler & io_handler,StringList & lines)1410b57cec5SDimitry Andric bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) {
1420b57cec5SDimitry Andric   // Check for meta command
1430b57cec5SDimitry Andric   const size_t num_lines = lines.GetSize();
1440b57cec5SDimitry Andric   if (num_lines == 1) {
1450b57cec5SDimitry Andric     const char *first_line = lines.GetStringAtIndex(0);
1460b57cec5SDimitry Andric     if (first_line[0] == ':')
1470b57cec5SDimitry Andric       return true; // Meta command is a single line where that starts with ':'
1480b57cec5SDimitry Andric   }
1490b57cec5SDimitry Andric 
1500b57cec5SDimitry Andric   // Check if REPL input is done
1510b57cec5SDimitry Andric   std::string source_string(lines.CopyList());
1520b57cec5SDimitry Andric   return SourceIsComplete(source_string);
1530b57cec5SDimitry Andric }
1540b57cec5SDimitry Andric 
CalculateActualIndentation(const StringList & lines)1550b57cec5SDimitry Andric int REPL::CalculateActualIndentation(const StringList &lines) {
1560b57cec5SDimitry Andric   std::string last_line = lines[lines.GetSize() - 1];
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric   int actual_indent = 0;
1590b57cec5SDimitry Andric   for (char &ch : last_line) {
1600b57cec5SDimitry Andric     if (ch != ' ')
1610b57cec5SDimitry Andric       break;
1620b57cec5SDimitry Andric     ++actual_indent;
1630b57cec5SDimitry Andric   }
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric   return actual_indent;
1660b57cec5SDimitry Andric }
1670b57cec5SDimitry Andric 
IOHandlerFixIndentation(IOHandler & io_handler,const StringList & lines,int cursor_position)1680b57cec5SDimitry Andric int REPL::IOHandlerFixIndentation(IOHandler &io_handler,
1690b57cec5SDimitry Andric                                   const StringList &lines,
1700b57cec5SDimitry Andric                                   int cursor_position) {
1710b57cec5SDimitry Andric   if (!m_enable_auto_indent)
1720b57cec5SDimitry Andric     return 0;
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric   if (!lines.GetSize()) {
1750b57cec5SDimitry Andric     return 0;
1760b57cec5SDimitry Andric   }
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric   int tab_size = io_handler.GetDebugger().GetTabSize();
1790b57cec5SDimitry Andric 
1800b57cec5SDimitry Andric   lldb::offset_t desired_indent =
1810b57cec5SDimitry Andric       GetDesiredIndentation(lines, cursor_position, tab_size);
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric   int actual_indent = REPL::CalculateActualIndentation(lines);
1840b57cec5SDimitry Andric 
1850b57cec5SDimitry Andric   if (desired_indent == LLDB_INVALID_OFFSET)
1860b57cec5SDimitry Andric     return 0;
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric   return (int)desired_indent - actual_indent;
1890b57cec5SDimitry Andric }
1900b57cec5SDimitry Andric 
ReadCode(const std::string & path,std::string & code,lldb::StreamFileSP & error_sp)191e8d8bef9SDimitry Andric static bool ReadCode(const std::string &path, std::string &code,
192e8d8bef9SDimitry Andric                      lldb::StreamFileSP &error_sp) {
193e8d8bef9SDimitry Andric   auto &fs = FileSystem::Instance();
194e8d8bef9SDimitry Andric   llvm::Twine pathTwine(path);
195e8d8bef9SDimitry Andric   if (!fs.Exists(pathTwine)) {
196e8d8bef9SDimitry Andric     error_sp->Printf("no such file at path '%s'\n", path.c_str());
197e8d8bef9SDimitry Andric     return false;
198e8d8bef9SDimitry Andric   }
199e8d8bef9SDimitry Andric   if (!fs.Readable(pathTwine)) {
200e8d8bef9SDimitry Andric     error_sp->Printf("could not read file at path '%s'\n", path.c_str());
201e8d8bef9SDimitry Andric     return false;
202e8d8bef9SDimitry Andric   }
203e8d8bef9SDimitry Andric   const size_t file_size = fs.GetByteSize(pathTwine);
204e8d8bef9SDimitry Andric   const size_t max_size = code.max_size();
205e8d8bef9SDimitry Andric   if (file_size > max_size) {
206e8d8bef9SDimitry Andric     error_sp->Printf("file at path '%s' too large: "
207e8d8bef9SDimitry Andric                      "file_size = %zu, max_size = %zu\n",
208e8d8bef9SDimitry Andric                      path.c_str(), file_size, max_size);
209e8d8bef9SDimitry Andric     return false;
210e8d8bef9SDimitry Andric   }
211e8d8bef9SDimitry Andric   auto data_sp = fs.CreateDataBuffer(pathTwine);
212e8d8bef9SDimitry Andric   if (data_sp == nullptr) {
213e8d8bef9SDimitry Andric     error_sp->Printf("could not create buffer for file at path '%s'\n",
214e8d8bef9SDimitry Andric                      path.c_str());
215e8d8bef9SDimitry Andric     return false;
216e8d8bef9SDimitry Andric   }
217e8d8bef9SDimitry Andric   code.assign((const char *)data_sp->GetBytes(), data_sp->GetByteSize());
218e8d8bef9SDimitry Andric   return true;
219e8d8bef9SDimitry Andric }
220e8d8bef9SDimitry Andric 
IOHandlerInputComplete(IOHandler & io_handler,std::string & code)2210b57cec5SDimitry Andric void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {
2229dba64beSDimitry Andric   lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
2239dba64beSDimitry Andric   lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
2240b57cec5SDimitry Andric   bool extra_line = false;
2250b57cec5SDimitry Andric   bool did_quit = false;
2260b57cec5SDimitry Andric 
2270b57cec5SDimitry Andric   if (code.empty()) {
2280b57cec5SDimitry Andric     m_code.AppendString("");
2290b57cec5SDimitry Andric     static_cast<IOHandlerEditline &>(io_handler)
2300b57cec5SDimitry Andric         .SetBaseLineNumber(m_code.GetSize() + 1);
2310b57cec5SDimitry Andric   } else {
2320b57cec5SDimitry Andric     Debugger &debugger = m_target.GetDebugger();
2330b57cec5SDimitry Andric     CommandInterpreter &ci = debugger.GetCommandInterpreter();
2340b57cec5SDimitry Andric     extra_line = ci.GetSpaceReplPrompts();
2350b57cec5SDimitry Andric 
2360b57cec5SDimitry Andric     ExecutionContext exe_ctx(m_target.GetProcessSP()
2370b57cec5SDimitry Andric                                  ->GetThreadList()
2380b57cec5SDimitry Andric                                  .GetSelectedThread()
23906c3fb27SDimitry Andric                                  ->GetSelectedFrame(DoNoSelectMostRelevantFrame)
2400b57cec5SDimitry Andric                                  .get());
2410b57cec5SDimitry Andric 
2420b57cec5SDimitry Andric     lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
2430b57cec5SDimitry Andric 
2440b57cec5SDimitry Andric     if (code[0] == ':') {
2450b57cec5SDimitry Andric       // Meta command
2460b57cec5SDimitry Andric       // Strip the ':'
2470b57cec5SDimitry Andric       code.erase(0, 1);
2489dba64beSDimitry Andric       if (!llvm::StringRef(code).trim().empty()) {
2490b57cec5SDimitry Andric         // "lldb" was followed by arguments, so just execute the command dump
2500b57cec5SDimitry Andric         // the results
2510b57cec5SDimitry Andric 
2520b57cec5SDimitry Andric         // Turn off prompt on quit in case the user types ":quit"
2530b57cec5SDimitry Andric         const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
2540b57cec5SDimitry Andric         if (saved_prompt_on_quit)
2550b57cec5SDimitry Andric           ci.SetPromptOnQuit(false);
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric         // Execute the command
2585ffd83dbSDimitry Andric         CommandReturnObject result(debugger.GetUseColor());
2590b57cec5SDimitry Andric         result.SetImmediateOutputStream(output_sp);
2600b57cec5SDimitry Andric         result.SetImmediateErrorStream(error_sp);
2610b57cec5SDimitry Andric         ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
2620b57cec5SDimitry Andric 
2630b57cec5SDimitry Andric         if (saved_prompt_on_quit)
2640b57cec5SDimitry Andric           ci.SetPromptOnQuit(true);
2650b57cec5SDimitry Andric 
2660b57cec5SDimitry Andric         if (result.GetStatus() == lldb::eReturnStatusQuit) {
2670b57cec5SDimitry Andric           did_quit = true;
2680b57cec5SDimitry Andric           io_handler.SetIsDone(true);
2690b57cec5SDimitry Andric           if (debugger.CheckTopIOHandlerTypes(
2700b57cec5SDimitry Andric                   IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
2710b57cec5SDimitry Andric             // We typed "quit" or an alias to quit so we need to check if the
2720b57cec5SDimitry Andric             // command interpreter is above us and tell it that it is done as
2730b57cec5SDimitry Andric             // well so we don't drop back into the command interpreter if we
2740b57cec5SDimitry Andric             // have already quit
2750b57cec5SDimitry Andric             lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
2760b57cec5SDimitry Andric             if (io_handler_sp)
2770b57cec5SDimitry Andric               io_handler_sp->SetIsDone(true);
2780b57cec5SDimitry Andric           }
2790b57cec5SDimitry Andric         }
2800b57cec5SDimitry Andric       } else {
2810b57cec5SDimitry Andric         // ":" was followed by no arguments, so push the LLDB command prompt
2820b57cec5SDimitry Andric         if (debugger.CheckTopIOHandlerTypes(
2830b57cec5SDimitry Andric                 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
2840b57cec5SDimitry Andric           // If the user wants to get back to the command interpreter and the
2850b57cec5SDimitry Andric           // command interpreter is what launched the REPL, then just let the
2860b57cec5SDimitry Andric           // REPL exit and fall back to the command interpreter.
2870b57cec5SDimitry Andric           io_handler.SetIsDone(true);
2880b57cec5SDimitry Andric         } else {
2890b57cec5SDimitry Andric           // The REPL wasn't launched the by the command interpreter, it is the
2900b57cec5SDimitry Andric           // base IOHandler, so we need to get the command interpreter and
2910b57cec5SDimitry Andric           lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
2920b57cec5SDimitry Andric           if (io_handler_sp) {
2930b57cec5SDimitry Andric             io_handler_sp->SetIsDone(false);
2945ffd83dbSDimitry Andric             debugger.RunIOHandlerAsync(ci.GetIOHandler());
2950b57cec5SDimitry Andric           }
2960b57cec5SDimitry Andric         }
2970b57cec5SDimitry Andric       }
2980b57cec5SDimitry Andric     } else {
299e8d8bef9SDimitry Andric       if (code[0] == '<') {
300e8d8bef9SDimitry Andric         // User wants to read code from a file.
301e8d8bef9SDimitry Andric         // Interpret rest of line as a literal path.
302e8d8bef9SDimitry Andric         auto path = llvm::StringRef(code.substr(1)).trim().str();
303e8d8bef9SDimitry Andric         if (!ReadCode(path, code, error_sp)) {
304e8d8bef9SDimitry Andric           return;
305e8d8bef9SDimitry Andric         }
306e8d8bef9SDimitry Andric       }
307e8d8bef9SDimitry Andric 
3080b57cec5SDimitry Andric       // Unwind any expression we might have been running in case our REPL
3090b57cec5SDimitry Andric       // expression crashed and the user was looking around
3100b57cec5SDimitry Andric       if (m_dedicated_repl_mode) {
3110b57cec5SDimitry Andric         Thread *thread = exe_ctx.GetThreadPtr();
3120b57cec5SDimitry Andric         if (thread && thread->UnwindInnermostExpression().Success()) {
3130b57cec5SDimitry Andric           thread->SetSelectedFrameByIndex(0, false);
31406c3fb27SDimitry Andric           exe_ctx.SetFrameSP(
31506c3fb27SDimitry Andric               thread->GetSelectedFrame(DoNoSelectMostRelevantFrame));
3160b57cec5SDimitry Andric         }
3170b57cec5SDimitry Andric       }
3180b57cec5SDimitry Andric 
3190b57cec5SDimitry Andric       const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
3200b57cec5SDimitry Andric 
3210b57cec5SDimitry Andric       EvaluateExpressionOptions expr_options = m_expr_options;
3220b57cec5SDimitry Andric       expr_options.SetCoerceToId(m_varobj_options.use_objc);
3230b57cec5SDimitry Andric       expr_options.SetKeepInMemory(true);
3240b57cec5SDimitry Andric       expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
3250b57cec5SDimitry Andric       expr_options.SetGenerateDebugInfo(true);
3260b57cec5SDimitry Andric       expr_options.SetREPLEnabled(true);
3270b57cec5SDimitry Andric       expr_options.SetColorizeErrors(colorize_err);
3280b57cec5SDimitry Andric       expr_options.SetPoundLine(m_repl_source_path.c_str(),
3290b57cec5SDimitry Andric                                 m_code.GetSize() + 1);
3300b57cec5SDimitry Andric 
3310b57cec5SDimitry Andric       expr_options.SetLanguage(GetLanguage());
3320b57cec5SDimitry Andric 
3330b57cec5SDimitry Andric       PersistentExpressionState *persistent_state =
3340b57cec5SDimitry Andric           m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
335480093f4SDimitry Andric       if (!persistent_state)
336480093f4SDimitry Andric         return;
3370b57cec5SDimitry Andric 
3380b57cec5SDimitry Andric       const size_t var_count_before = persistent_state->GetSize();
3390b57cec5SDimitry Andric 
3400b57cec5SDimitry Andric       const char *expr_prefix = nullptr;
3410b57cec5SDimitry Andric       lldb::ValueObjectSP result_valobj_sp;
3420b57cec5SDimitry Andric       Status error;
3430b57cec5SDimitry Andric       lldb::ExpressionResults execution_results =
3440b57cec5SDimitry Andric           UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(),
3450b57cec5SDimitry Andric                                    expr_prefix, result_valobj_sp, error,
3465ffd83dbSDimitry Andric                                    nullptr); // fixed expression
3470b57cec5SDimitry Andric 
34806c3fb27SDimitry Andric       if (llvm::Error err = OnExpressionEvaluated(exe_ctx, code, expr_options,
34906c3fb27SDimitry Andric                                                   execution_results,
35006c3fb27SDimitry Andric                                                   result_valobj_sp, error)) {
35106c3fb27SDimitry Andric         *error_sp << llvm::toString(std::move(err)) << "\n";
35206c3fb27SDimitry Andric       } else if (process_sp && process_sp->IsAlive()) {
3530b57cec5SDimitry Andric         bool add_to_code = true;
3540b57cec5SDimitry Andric         bool handled = false;
3550b57cec5SDimitry Andric         if (result_valobj_sp) {
3560b57cec5SDimitry Andric           lldb::Format format = m_format_options.GetFormat();
3570b57cec5SDimitry Andric 
3580b57cec5SDimitry Andric           if (result_valobj_sp->GetError().Success()) {
3590b57cec5SDimitry Andric             handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
3600b57cec5SDimitry Andric           } else if (result_valobj_sp->GetError().GetError() ==
3610b57cec5SDimitry Andric                      UserExpression::kNoResult) {
3620b57cec5SDimitry Andric             if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) {
3630b57cec5SDimitry Andric               error_sp->PutCString("(void)\n");
3640b57cec5SDimitry Andric               handled = true;
3650b57cec5SDimitry Andric             }
3660b57cec5SDimitry Andric           }
3670b57cec5SDimitry Andric         }
3680b57cec5SDimitry Andric 
3690b57cec5SDimitry Andric         if (debugger.GetPrintDecls()) {
3700b57cec5SDimitry Andric           for (size_t vi = var_count_before, ve = persistent_state->GetSize();
3710b57cec5SDimitry Andric                vi != ve; ++vi) {
3720b57cec5SDimitry Andric             lldb::ExpressionVariableSP persistent_var_sp =
3730b57cec5SDimitry Andric                 persistent_state->GetVariableAtIndex(vi);
3740b57cec5SDimitry Andric             lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
3750b57cec5SDimitry Andric 
3760b57cec5SDimitry Andric             PrintOneVariable(debugger, output_sp, valobj_sp,
3770b57cec5SDimitry Andric                              persistent_var_sp.get());
3780b57cec5SDimitry Andric           }
3790b57cec5SDimitry Andric         }
3800b57cec5SDimitry Andric 
3810b57cec5SDimitry Andric         if (!handled) {
3820b57cec5SDimitry Andric           bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
3830b57cec5SDimitry Andric           switch (execution_results) {
3840b57cec5SDimitry Andric           case lldb::eExpressionSetupError:
3850b57cec5SDimitry Andric           case lldb::eExpressionParseError:
3860b57cec5SDimitry Andric             add_to_code = false;
387bdd1243dSDimitry Andric             [[fallthrough]];
3880b57cec5SDimitry Andric           case lldb::eExpressionDiscarded:
3890b57cec5SDimitry Andric             error_sp->Printf("%s\n", error.AsCString());
3900b57cec5SDimitry Andric             break;
3910b57cec5SDimitry Andric 
3920b57cec5SDimitry Andric           case lldb::eExpressionCompleted:
3930b57cec5SDimitry Andric             break;
3940b57cec5SDimitry Andric           case lldb::eExpressionInterrupted:
3950b57cec5SDimitry Andric             if (useColors) {
3960b57cec5SDimitry Andric               error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
3970b57cec5SDimitry Andric               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
3980b57cec5SDimitry Andric             }
3990b57cec5SDimitry Andric             error_sp->Printf("Execution interrupted. ");
4000b57cec5SDimitry Andric             if (useColors)
4010b57cec5SDimitry Andric               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
4020b57cec5SDimitry Andric             error_sp->Printf("Enter code to recover and continue.\nEnter LLDB "
4030b57cec5SDimitry Andric                              "commands to investigate (type :help for "
4040b57cec5SDimitry Andric                              "assistance.)\n");
4050b57cec5SDimitry Andric             break;
4060b57cec5SDimitry Andric 
4070b57cec5SDimitry Andric           case lldb::eExpressionHitBreakpoint:
4080b57cec5SDimitry Andric             // Breakpoint was hit, drop into LLDB command interpreter
4090b57cec5SDimitry Andric             if (useColors) {
4100b57cec5SDimitry Andric               error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
4110b57cec5SDimitry Andric               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
4120b57cec5SDimitry Andric             }
4130b57cec5SDimitry Andric             output_sp->Printf("Execution stopped at breakpoint.  ");
4140b57cec5SDimitry Andric             if (useColors)
4150b57cec5SDimitry Andric               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
4160b57cec5SDimitry Andric             output_sp->Printf("Enter LLDB commands to investigate (type help "
4170b57cec5SDimitry Andric                               "for assistance.)\n");
4180b57cec5SDimitry Andric             {
4190b57cec5SDimitry Andric               lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
4200b57cec5SDimitry Andric               if (io_handler_sp) {
4210b57cec5SDimitry Andric                 io_handler_sp->SetIsDone(false);
4225ffd83dbSDimitry Andric                 debugger.RunIOHandlerAsync(ci.GetIOHandler());
4230b57cec5SDimitry Andric               }
4240b57cec5SDimitry Andric             }
4250b57cec5SDimitry Andric             break;
4260b57cec5SDimitry Andric 
4270b57cec5SDimitry Andric           case lldb::eExpressionTimedOut:
4280b57cec5SDimitry Andric             error_sp->Printf("error: timeout\n");
4290b57cec5SDimitry Andric             if (error.AsCString())
4300b57cec5SDimitry Andric               error_sp->Printf("error: %s\n", error.AsCString());
4310b57cec5SDimitry Andric             break;
4320b57cec5SDimitry Andric           case lldb::eExpressionResultUnavailable:
4330b57cec5SDimitry Andric             // Shoulnd't happen???
4340b57cec5SDimitry Andric             error_sp->Printf("error: could not fetch result -- %s\n",
4350b57cec5SDimitry Andric                              error.AsCString());
4360b57cec5SDimitry Andric             break;
4370b57cec5SDimitry Andric           case lldb::eExpressionStoppedForDebug:
4380b57cec5SDimitry Andric             // Shoulnd't happen???
4390b57cec5SDimitry Andric             error_sp->Printf("error: stopped for debug -- %s\n",
4400b57cec5SDimitry Andric                              error.AsCString());
4410b57cec5SDimitry Andric             break;
4425ffd83dbSDimitry Andric           case lldb::eExpressionThreadVanished:
4435ffd83dbSDimitry Andric             // Shoulnd't happen???
4445ffd83dbSDimitry Andric             error_sp->Printf("error: expression thread vanished -- %s\n",
4455ffd83dbSDimitry Andric                              error.AsCString());
4465ffd83dbSDimitry Andric             break;
4470b57cec5SDimitry Andric           }
4480b57cec5SDimitry Andric         }
4490b57cec5SDimitry Andric 
4500b57cec5SDimitry Andric         if (add_to_code) {
4510b57cec5SDimitry Andric           const uint32_t new_default_line = m_code.GetSize() + 1;
4520b57cec5SDimitry Andric 
4530b57cec5SDimitry Andric           m_code.SplitIntoLines(code);
4540b57cec5SDimitry Andric 
4550b57cec5SDimitry Andric           // Update our code on disk
4560b57cec5SDimitry Andric           if (!m_repl_source_path.empty()) {
4579dba64beSDimitry Andric             auto file = FileSystem::Instance().Open(
4589dba64beSDimitry Andric                 FileSpec(m_repl_source_path),
459349cc55cSDimitry Andric                 File::eOpenOptionWriteOnly | File::eOpenOptionTruncate |
4600b57cec5SDimitry Andric                     File::eOpenOptionCanCreate,
4610b57cec5SDimitry Andric                 lldb::eFilePermissionsFileDefault);
4629dba64beSDimitry Andric             if (file) {
4630b57cec5SDimitry Andric               std::string code(m_code.CopyList());
4640b57cec5SDimitry Andric               code.append(1, '\n');
4650b57cec5SDimitry Andric               size_t bytes_written = code.size();
4669dba64beSDimitry Andric               file.get()->Write(code.c_str(), bytes_written);
4679dba64beSDimitry Andric               file.get()->Close();
4689dba64beSDimitry Andric             } else {
4699dba64beSDimitry Andric               std::string message = llvm::toString(file.takeError());
4709dba64beSDimitry Andric               error_sp->Printf("error: couldn't open %s: %s\n",
4719dba64beSDimitry Andric                                m_repl_source_path.c_str(), message.c_str());
4729dba64beSDimitry Andric             }
4730b57cec5SDimitry Andric 
4740b57cec5SDimitry Andric             // Now set the default file and line to the REPL source file
4750b57cec5SDimitry Andric             m_target.GetSourceManager().SetDefaultFileAndLine(
4760b57cec5SDimitry Andric                 FileSpec(m_repl_source_path), new_default_line);
4770b57cec5SDimitry Andric           }
4780b57cec5SDimitry Andric           static_cast<IOHandlerEditline &>(io_handler)
4790b57cec5SDimitry Andric               .SetBaseLineNumber(m_code.GetSize() + 1);
4800b57cec5SDimitry Andric         }
4810b57cec5SDimitry Andric         if (extra_line) {
4829dba64beSDimitry Andric           output_sp->Printf("\n");
4830b57cec5SDimitry Andric         }
4840b57cec5SDimitry Andric       }
4850b57cec5SDimitry Andric     }
4860b57cec5SDimitry Andric 
4870b57cec5SDimitry Andric     // Don't complain about the REPL process going away if we are in the
4880b57cec5SDimitry Andric     // process of quitting.
4890b57cec5SDimitry Andric     if (!did_quit && (!process_sp || !process_sp->IsAlive())) {
4900b57cec5SDimitry Andric       error_sp->Printf(
4910b57cec5SDimitry Andric           "error: REPL process is no longer alive, exiting REPL\n");
4920b57cec5SDimitry Andric       io_handler.SetIsDone(true);
4930b57cec5SDimitry Andric     }
4940b57cec5SDimitry Andric   }
4950b57cec5SDimitry Andric }
4960b57cec5SDimitry Andric 
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)4979dba64beSDimitry Andric void REPL::IOHandlerComplete(IOHandler &io_handler,
4989dba64beSDimitry Andric                              CompletionRequest &request) {
4990b57cec5SDimitry Andric   // Complete an LLDB command if the first character is a colon...
500*5f757f3fSDimitry Andric   if (request.GetRawLine().starts_with(":")) {
5010b57cec5SDimitry Andric     Debugger &debugger = m_target.GetDebugger();
5020b57cec5SDimitry Andric 
5030b57cec5SDimitry Andric     // auto complete LLDB commands
5049dba64beSDimitry Andric     llvm::StringRef new_line = request.GetRawLine().drop_front();
5059dba64beSDimitry Andric     CompletionResult sub_result;
5069dba64beSDimitry Andric     CompletionRequest sub_request(new_line, request.GetRawCursorPos() - 1,
5079dba64beSDimitry Andric                                   sub_result);
5089dba64beSDimitry Andric     debugger.GetCommandInterpreter().HandleCompletion(sub_request);
5099dba64beSDimitry Andric     StringList matches, descriptions;
5109dba64beSDimitry Andric     sub_result.GetMatches(matches);
5115ffd83dbSDimitry Andric     // Prepend command prefix that was excluded in the completion request.
5125ffd83dbSDimitry Andric     if (request.GetCursorIndex() == 0)
5135ffd83dbSDimitry Andric       for (auto &match : matches)
5145ffd83dbSDimitry Andric         match.insert(0, 1, ':');
5159dba64beSDimitry Andric     sub_result.GetDescriptions(descriptions);
5169dba64beSDimitry Andric     request.AddCompletions(matches, descriptions);
5179dba64beSDimitry Andric     return;
5180b57cec5SDimitry Andric   }
5190b57cec5SDimitry Andric 
5200b57cec5SDimitry Andric   // Strip spaces from the line and see if we had only spaces
5219dba64beSDimitry Andric   if (request.GetRawLine().trim().empty()) {
5220b57cec5SDimitry Andric     // Only spaces on this line, so just indent
5239dba64beSDimitry Andric     request.AddCompletion(m_indent_str);
5249dba64beSDimitry Andric     return;
5250b57cec5SDimitry Andric   }
5260b57cec5SDimitry Andric 
5270b57cec5SDimitry Andric   std::string current_code;
5280b57cec5SDimitry Andric   current_code.append(m_code.CopyList());
5290b57cec5SDimitry Andric 
5300b57cec5SDimitry Andric   IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
531*5f757f3fSDimitry Andric   StringList current_lines = editline.GetCurrentLines();
5320b57cec5SDimitry Andric   const uint32_t current_line_idx = editline.GetCurrentLineIndex();
5330b57cec5SDimitry Andric 
534*5f757f3fSDimitry Andric   if (current_line_idx < current_lines.GetSize()) {
5350b57cec5SDimitry Andric     for (uint32_t i = 0; i < current_line_idx; ++i) {
536*5f757f3fSDimitry Andric       const char *line_cstr = current_lines.GetStringAtIndex(i);
5370b57cec5SDimitry Andric       if (line_cstr) {
5380b57cec5SDimitry Andric         current_code.append("\n");
5390b57cec5SDimitry Andric         current_code.append(line_cstr);
5400b57cec5SDimitry Andric       }
5410b57cec5SDimitry Andric     }
5420b57cec5SDimitry Andric   }
5430b57cec5SDimitry Andric 
5440b57cec5SDimitry Andric   current_code.append("\n");
5459dba64beSDimitry Andric   current_code += request.GetRawLine();
5460b57cec5SDimitry Andric 
5475ffd83dbSDimitry Andric   CompleteCode(current_code, request);
5480b57cec5SDimitry Andric }
5490b57cec5SDimitry Andric 
QuitCommandOverrideCallback(void * baton,const char ** argv)5500b57cec5SDimitry Andric bool QuitCommandOverrideCallback(void *baton, const char **argv) {
5510b57cec5SDimitry Andric   Target *target = (Target *)baton;
5520b57cec5SDimitry Andric   lldb::ProcessSP process_sp(target->GetProcessSP());
5530b57cec5SDimitry Andric   if (process_sp) {
5540b57cec5SDimitry Andric     process_sp->Destroy(false);
5550b57cec5SDimitry Andric     process_sp->GetTarget().GetDebugger().ClearIOHandlers();
5560b57cec5SDimitry Andric   }
5570b57cec5SDimitry Andric   return false;
5580b57cec5SDimitry Andric }
5590b57cec5SDimitry Andric 
RunLoop()5600b57cec5SDimitry Andric Status REPL::RunLoop() {
5610b57cec5SDimitry Andric   Status error;
5620b57cec5SDimitry Andric 
5630b57cec5SDimitry Andric   error = DoInitialization();
5640b57cec5SDimitry Andric   m_repl_source_path = GetSourcePath();
5650b57cec5SDimitry Andric 
5660b57cec5SDimitry Andric   if (!error.Success())
5670b57cec5SDimitry Andric     return error;
5680b57cec5SDimitry Andric 
5690b57cec5SDimitry Andric   Debugger &debugger = m_target.GetDebugger();
5700b57cec5SDimitry Andric 
5710b57cec5SDimitry Andric   lldb::IOHandlerSP io_handler_sp(GetIOHandler());
5720b57cec5SDimitry Andric 
5730b57cec5SDimitry Andric   FileSpec save_default_file;
5740b57cec5SDimitry Andric   uint32_t save_default_line = 0;
5750b57cec5SDimitry Andric 
5760b57cec5SDimitry Andric   if (!m_repl_source_path.empty()) {
5770b57cec5SDimitry Andric     // Save the current default file and line
5780b57cec5SDimitry Andric     m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file,
5790b57cec5SDimitry Andric                                                       save_default_line);
5800b57cec5SDimitry Andric   }
5810b57cec5SDimitry Andric 
5825ffd83dbSDimitry Andric   debugger.RunIOHandlerAsync(io_handler_sp);
5830b57cec5SDimitry Andric 
5840b57cec5SDimitry Andric   // Check if we are in dedicated REPL mode where LLDB was start with the "--
5850b57cec5SDimitry Andric   // repl" option from the command line. Currently we know this by checking if
5860b57cec5SDimitry Andric   // the debugger already has a IOHandler thread.
5870b57cec5SDimitry Andric   if (!debugger.HasIOHandlerThread()) {
5880b57cec5SDimitry Andric     // The debugger doesn't have an existing IOHandler thread, so this must be
5890b57cec5SDimitry Andric     // dedicated REPL mode...
5900b57cec5SDimitry Andric     m_dedicated_repl_mode = true;
5910b57cec5SDimitry Andric     debugger.StartIOHandlerThread();
5920b57cec5SDimitry Andric     llvm::StringRef command_name_str("quit");
5930b57cec5SDimitry Andric     CommandObject *cmd_obj =
5940b57cec5SDimitry Andric         debugger.GetCommandInterpreter().GetCommandObjectForCommand(
5950b57cec5SDimitry Andric             command_name_str);
5960b57cec5SDimitry Andric     if (cmd_obj) {
5970b57cec5SDimitry Andric       assert(command_name_str.empty());
5980b57cec5SDimitry Andric       cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target);
5990b57cec5SDimitry Andric     }
6000b57cec5SDimitry Andric   }
6010b57cec5SDimitry Andric 
6020b57cec5SDimitry Andric   // Wait for the REPL command interpreter to get popped
6030b57cec5SDimitry Andric   io_handler_sp->WaitForPop();
6040b57cec5SDimitry Andric 
6050b57cec5SDimitry Andric   if (m_dedicated_repl_mode) {
6060b57cec5SDimitry Andric     // If we were in dedicated REPL mode we would have started the IOHandler
6070b57cec5SDimitry Andric     // thread, and we should kill our process
6080b57cec5SDimitry Andric     lldb::ProcessSP process_sp = m_target.GetProcessSP();
6090b57cec5SDimitry Andric     if (process_sp && process_sp->IsAlive())
6100b57cec5SDimitry Andric       process_sp->Destroy(false);
6110b57cec5SDimitry Andric 
6120b57cec5SDimitry Andric     // Wait for the IO handler thread to exit (TODO: don't do this if the IO
6130b57cec5SDimitry Andric     // handler thread already exists...)
6140b57cec5SDimitry Andric     debugger.JoinIOHandlerThread();
6150b57cec5SDimitry Andric   }
6160b57cec5SDimitry Andric 
6170b57cec5SDimitry Andric   // Restore the default file and line
6180b57cec5SDimitry Andric   if (save_default_file && save_default_line != 0)
6190b57cec5SDimitry Andric     m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file,
6200b57cec5SDimitry Andric                                                       save_default_line);
6210b57cec5SDimitry Andric   return error;
6220b57cec5SDimitry Andric }
623