180814287SRaphael Isemann //===-- REPL.cpp ----------------------------------------------------------===//
26681041dSSean Callanan //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66681041dSSean Callanan //
76681041dSSean Callanan //===----------------------------------------------------------------------===//
86681041dSSean Callanan 
9b9c1b51eSKate Stone #include "lldb/Expression/REPL.h"
106681041dSSean Callanan #include "lldb/Core/Debugger.h"
116681041dSSean Callanan #include "lldb/Core/PluginManager.h"
126681041dSSean Callanan #include "lldb/Core/StreamFile.h"
136681041dSSean Callanan #include "lldb/Expression/ExpressionVariable.h"
146681041dSSean Callanan #include "lldb/Expression/UserExpression.h"
156681041dSSean Callanan #include "lldb/Host/HostInfo.h"
166681041dSSean Callanan #include "lldb/Interpreter/CommandInterpreter.h"
176681041dSSean Callanan #include "lldb/Interpreter/CommandReturnObject.h"
186681041dSSean Callanan #include "lldb/Target/Thread.h"
196681041dSSean Callanan #include "lldb/Utility/AnsiTerminal.h"
206681041dSSean Callanan 
21796ac80bSJonas Devlieghere #include <memory>
22796ac80bSJonas Devlieghere 
236681041dSSean Callanan using namespace lldb_private;
246681041dSSean Callanan 
REPL(LLVMCastKind kind,Target & target)25b9c1b51eSKate Stone REPL::REPL(LLVMCastKind kind, Target &target) : m_target(target), m_kind(kind) {
26adb5b1dfSEugene Zelenko   // Make sure all option values have sane defaults
27adb5b1dfSEugene Zelenko   Debugger &debugger = m_target.GetDebugger();
28cbcb3bceSJonas Devlieghere   debugger.SetShowProgress(false);
29e1cfbc79STodd Fiala   auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext();
30e1cfbc79STodd Fiala   m_format_options.OptionParsingStarting(&exe_ctx);
31e1cfbc79STodd Fiala   m_varobj_options.OptionParsingStarting(&exe_ctx);
32adb5b1dfSEugene Zelenko }
33adb5b1dfSEugene Zelenko 
34adb5b1dfSEugene Zelenko REPL::~REPL() = default;
35adb5b1dfSEugene Zelenko 
Create(Status & err,lldb::LanguageType language,Debugger * debugger,Target * target,const char * repl_options)3697206d57SZachary Turner lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language,
37b9c1b51eSKate Stone                           Debugger *debugger, Target *target,
38b9c1b51eSKate Stone                           const char *repl_options) {
396681041dSSean Callanan   uint32_t idx = 0;
406681041dSSean Callanan   lldb::REPLSP ret;
416681041dSSean Callanan 
42b9c1b51eSKate Stone   while (REPLCreateInstance create_instance =
43100863ccSJonas Devlieghere              PluginManager::GetREPLCreateCallbackAtIndex(idx)) {
44100863ccSJonas Devlieghere     LanguageSet supported_languages =
45100863ccSJonas Devlieghere         PluginManager::GetREPLSupportedLanguagesAtIndex(idx++);
46100863ccSJonas Devlieghere     if (!supported_languages[language])
47100863ccSJonas Devlieghere       continue;
483b682de6SSean Callanan     ret = (*create_instance)(err, language, debugger, target, repl_options);
49b9c1b51eSKate Stone     if (ret) {
506681041dSSean Callanan       break;
516681041dSSean Callanan     }
526681041dSSean Callanan   }
536681041dSSean Callanan 
546681041dSSean Callanan   return ret;
556681041dSSean Callanan }
566681041dSSean Callanan 
GetSourcePath()57b9c1b51eSKate Stone std::string REPL::GetSourcePath() {
586681041dSSean Callanan   ConstString file_basename = GetSourceFileBasename();
5960f028ffSPavel Labath   FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
6060f028ffSPavel Labath   if (tmpdir_file_spec) {
61*1b4b12a3SNico Weber     tmpdir_file_spec.GetFilename() = file_basename;
62adb5b1dfSEugene Zelenko     m_repl_source_path = tmpdir_file_spec.GetPath();
63b9c1b51eSKate Stone   } else {
648f3be7a3SJonas Devlieghere     tmpdir_file_spec = FileSpec("/tmp");
65642bc15dSRaphael Isemann     tmpdir_file_spec.AppendPathComponent(file_basename.GetStringRef());
666681041dSSean Callanan   }
676681041dSSean Callanan 
686681041dSSean Callanan   return tmpdir_file_spec.GetPath();
696681041dSSean Callanan }
706681041dSSean Callanan 
GetIOHandler()71b9c1b51eSKate Stone lldb::IOHandlerSP REPL::GetIOHandler() {
72b9c1b51eSKate Stone   if (!m_io_handler_sp) {
736681041dSSean Callanan     Debugger &debugger = m_target.GetDebugger();
74796ac80bSJonas Devlieghere     m_io_handler_sp = std::make_shared<IOHandlerEditline>(
75796ac80bSJonas Devlieghere         debugger, IOHandler::Type::REPL,
766681041dSSean Callanan         "lldb-repl",           // Name of input reader for history
77514d8cd8SZachary Turner         llvm::StringRef("> "), // prompt
78514d8cd8SZachary Turner         llvm::StringRef(". "), // Continuation prompt
796681041dSSean Callanan         true,                  // Multi-line
806681041dSSean Callanan         true,                  // The REPL prompt is always colored
816681041dSSean Callanan         1,                     // Line number
82d77c2e09SJonas Devlieghere         *this, nullptr);
836681041dSSean Callanan 
846681041dSSean Callanan     // Don't exit if CTRL+C is pressed
85b9c1b51eSKate Stone     static_cast<IOHandlerEditline *>(m_io_handler_sp.get())
86b9c1b51eSKate Stone         ->SetInterruptExits(false);
876681041dSSean Callanan 
88b9c1b51eSKate Stone     if (m_io_handler_sp->GetIsInteractive() &&
89b9c1b51eSKate Stone         m_io_handler_sp->GetIsRealTerminal()) {
906681041dSSean Callanan       m_indent_str.assign(debugger.GetTabSize(), ' ');
916681041dSSean Callanan       m_enable_auto_indent = debugger.GetAutoIndent();
92b9c1b51eSKate Stone     } else {
936681041dSSean Callanan       m_indent_str.clear();
946681041dSSean Callanan       m_enable_auto_indent = false;
956681041dSSean Callanan     }
966681041dSSean Callanan   }
976681041dSSean Callanan   return m_io_handler_sp;
986681041dSSean Callanan }
996681041dSSean Callanan 
IOHandlerActivated(IOHandler & io_handler,bool interactive)1000affb582SDave Lee void REPL::IOHandlerActivated(IOHandler &io_handler, bool interactive) {
1016681041dSSean Callanan   lldb::ProcessSP process_sp = m_target.GetProcessSP();
1026681041dSSean Callanan   if (process_sp && process_sp->IsAlive())
1036681041dSSean Callanan     return;
1047ca15ba7SLawrence D'Anna   lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
1056681041dSSean Callanan   error_sp->Printf("REPL requires a running target process.\n");
1066681041dSSean Callanan   io_handler.SetIsDone(true);
1076681041dSSean Callanan }
1086681041dSSean Callanan 
IOHandlerInterrupt(IOHandler & io_handler)109b9c1b51eSKate Stone bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; }
110b9c1b51eSKate Stone 
IOHandlerInputInterrupted(IOHandler & io_handler,std::string & line)111b9c1b51eSKate Stone void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) {
1126681041dSSean Callanan }
1136681041dSSean Callanan 
IOHandlerGetFixIndentationCharacters()114b9c1b51eSKate Stone const char *REPL::IOHandlerGetFixIndentationCharacters() {
115adb5b1dfSEugene Zelenko   return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr);
1166681041dSSean Callanan }
1176681041dSSean Callanan 
IOHandlerGetControlSequence(char ch)118b9c1b51eSKate Stone ConstString REPL::IOHandlerGetControlSequence(char ch) {
1196681041dSSean Callanan   if (ch == 'd')
1206681041dSSean Callanan     return ConstString(":quit\n");
1216681041dSSean Callanan   return ConstString();
1226681041dSSean Callanan }
1236681041dSSean Callanan 
IOHandlerGetCommandPrefix()124b9c1b51eSKate Stone const char *REPL::IOHandlerGetCommandPrefix() { return ":"; }
1256681041dSSean Callanan 
IOHandlerGetHelpPrologue()126b9c1b51eSKate Stone const char *REPL::IOHandlerGetHelpPrologue() {
1276681041dSSean Callanan   return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter.  "
128b9c1b51eSKate Stone          "Valid statements, expressions, and declarations are immediately "
129b9c1b51eSKate Stone          "compiled and executed.\n\n"
130b9c1b51eSKate Stone          "The complete set of LLDB debugging commands are also available as "
131f723d193SPatrick Beard          "described below.\n\nCommands "
132b9c1b51eSKate Stone          "must be prefixed with a colon at the REPL prompt (:quit for "
133b9c1b51eSKate Stone          "example.)  Typing just a colon "
134f723d193SPatrick Beard          "followed by return will switch to the LLDB prompt.\n\n"
135f723d193SPatrick Beard          "Type “< path” to read in code from a text file “path”.\n\n";
1366681041dSSean Callanan }
1376681041dSSean Callanan 
IOHandlerIsInputComplete(IOHandler & io_handler,StringList & lines)138b9c1b51eSKate Stone bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) {
1396681041dSSean Callanan   // Check for meta command
1406681041dSSean Callanan   const size_t num_lines = lines.GetSize();
141b9c1b51eSKate Stone   if (num_lines == 1) {
1426681041dSSean Callanan     const char *first_line = lines.GetStringAtIndex(0);
1436681041dSSean Callanan     if (first_line[0] == ':')
1446681041dSSean Callanan       return true; // Meta command is a single line where that starts with ':'
1456681041dSSean Callanan   }
1466681041dSSean Callanan 
1476681041dSSean Callanan   // Check if REPL input is done
1486681041dSSean Callanan   std::string source_string(lines.CopyList());
1496681041dSSean Callanan   return SourceIsComplete(source_string);
1506681041dSSean Callanan }
1516681041dSSean Callanan 
CalculateActualIndentation(const StringList & lines)152b9c1b51eSKate Stone int REPL::CalculateActualIndentation(const StringList &lines) {
1536681041dSSean Callanan   std::string last_line = lines[lines.GetSize() - 1];
1546681041dSSean Callanan 
1556681041dSSean Callanan   int actual_indent = 0;
156b9c1b51eSKate Stone   for (char &ch : last_line) {
157b9c1b51eSKate Stone     if (ch != ' ')
158b9c1b51eSKate Stone       break;
1596681041dSSean Callanan     ++actual_indent;
1606681041dSSean Callanan   }
1616681041dSSean Callanan 
1626681041dSSean Callanan   return actual_indent;
1636681041dSSean Callanan }
1646681041dSSean Callanan 
IOHandlerFixIndentation(IOHandler & io_handler,const StringList & lines,int cursor_position)165b9c1b51eSKate Stone int REPL::IOHandlerFixIndentation(IOHandler &io_handler,
1666681041dSSean Callanan                                   const StringList &lines,
167b9c1b51eSKate Stone                                   int cursor_position) {
168b9c1b51eSKate Stone   if (!m_enable_auto_indent)
169b9c1b51eSKate Stone     return 0;
1706681041dSSean Callanan 
171b9c1b51eSKate Stone   if (!lines.GetSize()) {
1726681041dSSean Callanan     return 0;
1736681041dSSean Callanan   }
1746681041dSSean Callanan 
1756681041dSSean Callanan   int tab_size = io_handler.GetDebugger().GetTabSize();
1766681041dSSean Callanan 
177b9c1b51eSKate Stone   lldb::offset_t desired_indent =
178b9c1b51eSKate Stone       GetDesiredIndentation(lines, cursor_position, tab_size);
1796681041dSSean Callanan 
1806681041dSSean Callanan   int actual_indent = REPL::CalculateActualIndentation(lines);
1816681041dSSean Callanan 
1826681041dSSean Callanan   if (desired_indent == LLDB_INVALID_OFFSET)
1836681041dSSean Callanan     return 0;
1846681041dSSean Callanan 
1856681041dSSean Callanan   return (int)desired_indent - actual_indent;
1866681041dSSean Callanan }
1876681041dSSean Callanan 
ReadCode(const std::string & path,std::string & code,lldb::StreamFileSP & error_sp)188f723d193SPatrick Beard static bool ReadCode(const std::string &path, std::string &code,
189f723d193SPatrick Beard                      lldb::StreamFileSP &error_sp) {
190f723d193SPatrick Beard   auto &fs = FileSystem::Instance();
191f723d193SPatrick Beard   llvm::Twine pathTwine(path);
192f723d193SPatrick Beard   if (!fs.Exists(pathTwine)) {
193f723d193SPatrick Beard     error_sp->Printf("no such file at path '%s'\n", path.c_str());
194f723d193SPatrick Beard     return false;
195f723d193SPatrick Beard   }
196f723d193SPatrick Beard   if (!fs.Readable(pathTwine)) {
197f723d193SPatrick Beard     error_sp->Printf("could not read file at path '%s'\n", path.c_str());
198f723d193SPatrick Beard     return false;
199f723d193SPatrick Beard   }
200f723d193SPatrick Beard   const size_t file_size = fs.GetByteSize(pathTwine);
201f723d193SPatrick Beard   const size_t max_size = code.max_size();
202f723d193SPatrick Beard   if (file_size > max_size) {
203f723d193SPatrick Beard     error_sp->Printf("file at path '%s' too large: "
204c1403228SEric Christopher                      "file_size = %zu, max_size = %zu\n",
205f723d193SPatrick Beard                      path.c_str(), file_size, max_size);
206f723d193SPatrick Beard     return false;
207f723d193SPatrick Beard   }
208f723d193SPatrick Beard   auto data_sp = fs.CreateDataBuffer(pathTwine);
209f723d193SPatrick Beard   if (data_sp == nullptr) {
210f723d193SPatrick Beard     error_sp->Printf("could not create buffer for file at path '%s'\n",
211f723d193SPatrick Beard                      path.c_str());
212f723d193SPatrick Beard     return false;
213f723d193SPatrick Beard   }
214f723d193SPatrick Beard   code.assign((const char *)data_sp->GetBytes(), data_sp->GetByteSize());
215f723d193SPatrick Beard   return true;
216f723d193SPatrick Beard }
217f723d193SPatrick Beard 
IOHandlerInputComplete(IOHandler & io_handler,std::string & code)218b9c1b51eSKate Stone void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {
2197ca15ba7SLawrence D'Anna   lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
2207ca15ba7SLawrence D'Anna   lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
2216681041dSSean Callanan   bool extra_line = false;
2226681041dSSean Callanan   bool did_quit = false;
2236681041dSSean Callanan 
224b9c1b51eSKate Stone   if (code.empty()) {
2256681041dSSean Callanan     m_code.AppendString("");
226b9c1b51eSKate Stone     static_cast<IOHandlerEditline &>(io_handler)
227b9c1b51eSKate Stone         .SetBaseLineNumber(m_code.GetSize() + 1);
228b9c1b51eSKate Stone   } else {
2296681041dSSean Callanan     Debugger &debugger = m_target.GetDebugger();
2306681041dSSean Callanan     CommandInterpreter &ci = debugger.GetCommandInterpreter();
2316681041dSSean Callanan     extra_line = ci.GetSpaceReplPrompts();
2326681041dSSean Callanan 
233b9c1b51eSKate Stone     ExecutionContext exe_ctx(m_target.GetProcessSP()
234b9c1b51eSKate Stone                                  ->GetThreadList()
235b9c1b51eSKate Stone                                  .GetSelectedThread()
236b9c1b51eSKate Stone                                  ->GetSelectedFrame()
237b9c1b51eSKate Stone                                  .get());
2386681041dSSean Callanan 
2396681041dSSean Callanan     lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
2406681041dSSean Callanan 
241b9c1b51eSKate Stone     if (code[0] == ':') {
2426681041dSSean Callanan       // Meta command
2436681041dSSean Callanan       // Strip the ':'
2446681041dSSean Callanan       code.erase(0, 1);
2457841e80eSRaphael Isemann       if (!llvm::StringRef(code).trim().empty()) {
246b9c1b51eSKate Stone         // "lldb" was followed by arguments, so just execute the command dump
247b9c1b51eSKate Stone         // the results
2486681041dSSean Callanan 
2496681041dSSean Callanan         // Turn off prompt on quit in case the user types ":quit"
2506681041dSSean Callanan         const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
2516681041dSSean Callanan         if (saved_prompt_on_quit)
2526681041dSSean Callanan           ci.SetPromptOnQuit(false);
2536681041dSSean Callanan 
2546681041dSSean Callanan         // Execute the command
255de019b88SJonas Devlieghere         CommandReturnObject result(debugger.GetUseColor());
2566681041dSSean Callanan         result.SetImmediateOutputStream(output_sp);
2576681041dSSean Callanan         result.SetImmediateErrorStream(error_sp);
2586681041dSSean Callanan         ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
2596681041dSSean Callanan 
2606681041dSSean Callanan         if (saved_prompt_on_quit)
2616681041dSSean Callanan           ci.SetPromptOnQuit(true);
2626681041dSSean Callanan 
263b9c1b51eSKate Stone         if (result.GetStatus() == lldb::eReturnStatusQuit) {
2646681041dSSean Callanan           did_quit = true;
2656681041dSSean Callanan           io_handler.SetIsDone(true);
266b9c1b51eSKate Stone           if (debugger.CheckTopIOHandlerTypes(
267b9c1b51eSKate Stone                   IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
2686681041dSSean Callanan             // We typed "quit" or an alias to quit so we need to check if the
269b9c1b51eSKate Stone             // command interpreter is above us and tell it that it is done as
27005097246SAdrian Prantl             // well so we don't drop back into the command interpreter if we
27105097246SAdrian Prantl             // have already quit
2726681041dSSean Callanan             lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
2736681041dSSean Callanan             if (io_handler_sp)
2746681041dSSean Callanan               io_handler_sp->SetIsDone(true);
2756681041dSSean Callanan           }
2766681041dSSean Callanan         }
277b9c1b51eSKate Stone       } else {
2786681041dSSean Callanan         // ":" was followed by no arguments, so push the LLDB command prompt
279b9c1b51eSKate Stone         if (debugger.CheckTopIOHandlerTypes(
280b9c1b51eSKate Stone                 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
2816681041dSSean Callanan           // If the user wants to get back to the command interpreter and the
2826681041dSSean Callanan           // command interpreter is what launched the REPL, then just let the
2836681041dSSean Callanan           // REPL exit and fall back to the command interpreter.
2846681041dSSean Callanan           io_handler.SetIsDone(true);
285b9c1b51eSKate Stone         } else {
2866681041dSSean Callanan           // The REPL wasn't launched the by the command interpreter, it is the
2876681041dSSean Callanan           // base IOHandler, so we need to get the command interpreter and
2886681041dSSean Callanan           lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
289b9c1b51eSKate Stone           if (io_handler_sp) {
2906681041dSSean Callanan             io_handler_sp->SetIsDone(false);
2917ce2de2cSJonas Devlieghere             debugger.RunIOHandlerAsync(ci.GetIOHandler());
2926681041dSSean Callanan           }
2936681041dSSean Callanan         }
2946681041dSSean Callanan       }
295b9c1b51eSKate Stone     } else {
296f723d193SPatrick Beard       if (code[0] == '<') {
297f723d193SPatrick Beard         // User wants to read code from a file.
298f723d193SPatrick Beard         // Interpret rest of line as a literal path.
299f723d193SPatrick Beard         auto path = llvm::StringRef(code.substr(1)).trim().str();
300f723d193SPatrick Beard         if (!ReadCode(path, code, error_sp)) {
301f723d193SPatrick Beard           return;
302f723d193SPatrick Beard         }
303f723d193SPatrick Beard       }
304f723d193SPatrick Beard 
3056681041dSSean Callanan       // Unwind any expression we might have been running in case our REPL
3066681041dSSean Callanan       // expression crashed and the user was looking around
307b9c1b51eSKate Stone       if (m_dedicated_repl_mode) {
3086681041dSSean Callanan         Thread *thread = exe_ctx.GetThreadPtr();
309b9c1b51eSKate Stone         if (thread && thread->UnwindInnermostExpression().Success()) {
3106681041dSSean Callanan           thread->SetSelectedFrameByIndex(0, false);
3116681041dSSean Callanan           exe_ctx.SetFrameSP(thread->GetSelectedFrame());
3126681041dSSean Callanan         }
3136681041dSSean Callanan       }
3146681041dSSean Callanan 
3156681041dSSean Callanan       const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
3166681041dSSean Callanan 
317c5bfa3daSJonas Devlieghere       EvaluateExpressionOptions expr_options = m_expr_options;
3186681041dSSean Callanan       expr_options.SetCoerceToId(m_varobj_options.use_objc);
3196681041dSSean Callanan       expr_options.SetKeepInMemory(true);
3206681041dSSean Callanan       expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
3216681041dSSean Callanan       expr_options.SetGenerateDebugInfo(true);
3226681041dSSean Callanan       expr_options.SetREPLEnabled(true);
3236681041dSSean Callanan       expr_options.SetColorizeErrors(colorize_err);
324b9c1b51eSKate Stone       expr_options.SetPoundLine(m_repl_source_path.c_str(),
325b9c1b51eSKate Stone                                 m_code.GetSize() + 1);
3266681041dSSean Callanan 
3276681041dSSean Callanan       expr_options.SetLanguage(GetLanguage());
3286681041dSSean Callanan 
329b9c1b51eSKate Stone       PersistentExpressionState *persistent_state =
330b9c1b51eSKate Stone           m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
331e9331a56SAdrian Prantl       if (!persistent_state)
332e9331a56SAdrian Prantl         return;
3336681041dSSean Callanan 
3346681041dSSean Callanan       const size_t var_count_before = persistent_state->GetSize();
3356681041dSSean Callanan 
336adb5b1dfSEugene Zelenko       const char *expr_prefix = nullptr;
3376681041dSSean Callanan       lldb::ValueObjectSP result_valobj_sp;
33897206d57SZachary Turner       Status error;
339b9c1b51eSKate Stone       lldb::ExpressionResults execution_results =
340b9c1b51eSKate Stone           UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(),
341b9c1b51eSKate Stone                                    expr_prefix, result_valobj_sp, error,
342381e81a0SAlex Langford                                    nullptr); // fixed expression
3436681041dSSean Callanan 
3446681041dSSean Callanan       // CommandInterpreter &ci = debugger.GetCommandInterpreter();
3456681041dSSean Callanan 
346b9c1b51eSKate Stone       if (process_sp && process_sp->IsAlive()) {
3476681041dSSean Callanan         bool add_to_code = true;
3486681041dSSean Callanan         bool handled = false;
349b9c1b51eSKate Stone         if (result_valobj_sp) {
3506681041dSSean Callanan           lldb::Format format = m_format_options.GetFormat();
3516681041dSSean Callanan 
352b9c1b51eSKate Stone           if (result_valobj_sp->GetError().Success()) {
3536681041dSSean Callanan             handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
354b9c1b51eSKate Stone           } else if (result_valobj_sp->GetError().GetError() ==
355a35912daSKrasimir Georgiev                      UserExpression::kNoResult) {
356b9c1b51eSKate Stone             if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) {
3576681041dSSean Callanan               error_sp->PutCString("(void)\n");
3586681041dSSean Callanan               handled = true;
3596681041dSSean Callanan             }
3606681041dSSean Callanan           }
3616681041dSSean Callanan         }
3626681041dSSean Callanan 
363b9c1b51eSKate Stone         if (debugger.GetPrintDecls()) {
3646681041dSSean Callanan           for (size_t vi = var_count_before, ve = persistent_state->GetSize();
365b9c1b51eSKate Stone                vi != ve; ++vi) {
366b9c1b51eSKate Stone             lldb::ExpressionVariableSP persistent_var_sp =
367b9c1b51eSKate Stone                 persistent_state->GetVariableAtIndex(vi);
3686681041dSSean Callanan             lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
3696681041dSSean Callanan 
370b9c1b51eSKate Stone             PrintOneVariable(debugger, output_sp, valobj_sp,
371b9c1b51eSKate Stone                              persistent_var_sp.get());
3726681041dSSean Callanan           }
3736681041dSSean Callanan         }
3746681041dSSean Callanan 
375b9c1b51eSKate Stone         if (!handled) {
3766681041dSSean Callanan           bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
377b9c1b51eSKate Stone           switch (execution_results) {
3786681041dSSean Callanan           case lldb::eExpressionSetupError:
3796681041dSSean Callanan           case lldb::eExpressionParseError:
3806681041dSSean Callanan             add_to_code = false;
38162e0681aSJason Molenda             LLVM_FALLTHROUGH;
3826681041dSSean Callanan           case lldb::eExpressionDiscarded:
3836681041dSSean Callanan             error_sp->Printf("%s\n", error.AsCString());
3846681041dSSean Callanan             break;
3856681041dSSean Callanan 
3866681041dSSean Callanan           case lldb::eExpressionCompleted:
3876681041dSSean Callanan             break;
3886681041dSSean Callanan           case lldb::eExpressionInterrupted:
3896681041dSSean Callanan             if (useColors) {
3906681041dSSean Callanan               error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
3916681041dSSean Callanan               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
3926681041dSSean Callanan             }
3936681041dSSean Callanan             error_sp->Printf("Execution interrupted. ");
394b9c1b51eSKate Stone             if (useColors)
395b9c1b51eSKate Stone               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
396b9c1b51eSKate Stone             error_sp->Printf("Enter code to recover and continue.\nEnter LLDB "
397b9c1b51eSKate Stone                              "commands to investigate (type :help for "
398b9c1b51eSKate Stone                              "assistance.)\n");
3996681041dSSean Callanan             break;
4006681041dSSean Callanan 
4016681041dSSean Callanan           case lldb::eExpressionHitBreakpoint:
4026681041dSSean Callanan             // Breakpoint was hit, drop into LLDB command interpreter
4036681041dSSean Callanan             if (useColors) {
4046681041dSSean Callanan               error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
4056681041dSSean Callanan               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
4066681041dSSean Callanan             }
4076681041dSSean Callanan             output_sp->Printf("Execution stopped at breakpoint.  ");
408b9c1b51eSKate Stone             if (useColors)
409b9c1b51eSKate Stone               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
410b9c1b51eSKate Stone             output_sp->Printf("Enter LLDB commands to investigate (type help "
411b9c1b51eSKate Stone                               "for assistance.)\n");
4126681041dSSean Callanan             {
4136681041dSSean Callanan               lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
414b9c1b51eSKate Stone               if (io_handler_sp) {
4156681041dSSean Callanan                 io_handler_sp->SetIsDone(false);
4167ce2de2cSJonas Devlieghere                 debugger.RunIOHandlerAsync(ci.GetIOHandler());
4176681041dSSean Callanan               }
4186681041dSSean Callanan             }
4196681041dSSean Callanan             break;
4206681041dSSean Callanan 
4216681041dSSean Callanan           case lldb::eExpressionTimedOut:
4226681041dSSean Callanan             error_sp->Printf("error: timeout\n");
4236681041dSSean Callanan             if (error.AsCString())
4246681041dSSean Callanan               error_sp->Printf("error: %s\n", error.AsCString());
4256681041dSSean Callanan             break;
4266681041dSSean Callanan           case lldb::eExpressionResultUnavailable:
4276681041dSSean Callanan             // Shoulnd't happen???
428b9c1b51eSKate Stone             error_sp->Printf("error: could not fetch result -- %s\n",
429b9c1b51eSKate Stone                              error.AsCString());
4306681041dSSean Callanan             break;
4316681041dSSean Callanan           case lldb::eExpressionStoppedForDebug:
4326681041dSSean Callanan             // Shoulnd't happen???
433b9c1b51eSKate Stone             error_sp->Printf("error: stopped for debug -- %s\n",
434b9c1b51eSKate Stone                              error.AsCString());
4356681041dSSean Callanan             break;
4368a6333efSJonas Devlieghere           case lldb::eExpressionThreadVanished:
4378a6333efSJonas Devlieghere             // Shoulnd't happen???
4388a6333efSJonas Devlieghere             error_sp->Printf("error: expression thread vanished -- %s\n",
4398a6333efSJonas Devlieghere                              error.AsCString());
4408a6333efSJonas Devlieghere             break;
4416681041dSSean Callanan           }
4426681041dSSean Callanan         }
4436681041dSSean Callanan 
444b9c1b51eSKate Stone         if (add_to_code) {
4456681041dSSean Callanan           const uint32_t new_default_line = m_code.GetSize() + 1;
4466681041dSSean Callanan 
4476681041dSSean Callanan           m_code.SplitIntoLines(code);
4486681041dSSean Callanan 
4496681041dSSean Callanan           // Update our code on disk
450b9c1b51eSKate Stone           if (!m_repl_source_path.empty()) {
4517ca15ba7SLawrence D'Anna             auto file = FileSystem::Instance().Open(
4527ca15ba7SLawrence D'Anna                 FileSpec(m_repl_source_path),
45314735cabSMichał Górny                 File::eOpenOptionWriteOnly | File::eOpenOptionTruncate |
454b9c1b51eSKate Stone                     File::eOpenOptionCanCreate,
4556681041dSSean Callanan                 lldb::eFilePermissionsFileDefault);
4562fce1137SLawrence D'Anna             if (file) {
4576681041dSSean Callanan               std::string code(m_code.CopyList());
4586681041dSSean Callanan               code.append(1, '\n');
4596681041dSSean Callanan               size_t bytes_written = code.size();
4602fce1137SLawrence D'Anna               file.get()->Write(code.c_str(), bytes_written);
4612fce1137SLawrence D'Anna               file.get()->Close();
4622fce1137SLawrence D'Anna             } else {
4632fce1137SLawrence D'Anna               std::string message = llvm::toString(file.takeError());
4642fce1137SLawrence D'Anna               error_sp->Printf("error: couldn't open %s: %s\n",
4652fce1137SLawrence D'Anna                                m_repl_source_path.c_str(), message.c_str());
4662fce1137SLawrence D'Anna             }
4676681041dSSean Callanan 
4686681041dSSean Callanan             // Now set the default file and line to the REPL source file
469b9c1b51eSKate Stone             m_target.GetSourceManager().SetDefaultFileAndLine(
4708f3be7a3SJonas Devlieghere                 FileSpec(m_repl_source_path), new_default_line);
4716681041dSSean Callanan           }
472b9c1b51eSKate Stone           static_cast<IOHandlerEditline &>(io_handler)
473b9c1b51eSKate Stone               .SetBaseLineNumber(m_code.GetSize() + 1);
4746681041dSSean Callanan         }
475b9c1b51eSKate Stone         if (extra_line) {
4765da2bc22SLawrence D'Anna           output_sp->Printf("\n");
4776681041dSSean Callanan         }
4786681041dSSean Callanan       }
4796681041dSSean Callanan     }
4806681041dSSean Callanan 
48105097246SAdrian Prantl     // Don't complain about the REPL process going away if we are in the
48205097246SAdrian Prantl     // process of quitting.
483b9c1b51eSKate Stone     if (!did_quit && (!process_sp || !process_sp->IsAlive())) {
484b9c1b51eSKate Stone       error_sp->Printf(
485b9c1b51eSKate Stone           "error: REPL process is no longer alive, exiting REPL\n");
4866681041dSSean Callanan       io_handler.SetIsDone(true);
4876681041dSSean Callanan     }
4886681041dSSean Callanan   }
4896681041dSSean Callanan }
4906681041dSSean Callanan 
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)491ae34ed2cSRaphael Isemann void REPL::IOHandlerComplete(IOHandler &io_handler,
492ae34ed2cSRaphael Isemann                              CompletionRequest &request) {
4936681041dSSean Callanan   // Complete an LLDB command if the first character is a colon...
4942fc20f65SRaphael Isemann   if (request.GetRawLine().startswith(":")) {
4956681041dSSean Callanan     Debugger &debugger = m_target.GetDebugger();
4966681041dSSean Callanan 
4976681041dSSean Callanan     // auto complete LLDB commands
4982fc20f65SRaphael Isemann     llvm::StringRef new_line = request.GetRawLine().drop_front();
4992fc20f65SRaphael Isemann     CompletionResult sub_result;
5002fc20f65SRaphael Isemann     CompletionRequest sub_request(new_line, request.GetRawCursorPos() - 1,
5012fc20f65SRaphael Isemann                                   sub_result);
502ae34ed2cSRaphael Isemann     debugger.GetCommandInterpreter().HandleCompletion(sub_request);
5032fc20f65SRaphael Isemann     StringList matches, descriptions;
5042fc20f65SRaphael Isemann     sub_result.GetMatches(matches);
5053faec833SMartin Svensson     // Prepend command prefix that was excluded in the completion request.
5063faec833SMartin Svensson     if (request.GetCursorIndex() == 0)
5073faec833SMartin Svensson       for (auto &match : matches)
5083faec833SMartin Svensson         match.insert(0, 1, ':');
5092fc20f65SRaphael Isemann     sub_result.GetDescriptions(descriptions);
5102fc20f65SRaphael Isemann     request.AddCompletions(matches, descriptions);
511ae34ed2cSRaphael Isemann     return;
5126681041dSSean Callanan   }
5136681041dSSean Callanan 
5146681041dSSean Callanan   // Strip spaces from the line and see if we had only spaces
5157f9ac337SRaphael Isemann   if (request.GetRawLine().trim().empty()) {
5166681041dSSean Callanan     // Only spaces on this line, so just indent
5172fc20f65SRaphael Isemann     request.AddCompletion(m_indent_str);
518ae34ed2cSRaphael Isemann     return;
5196681041dSSean Callanan   }
5206681041dSSean Callanan 
5216681041dSSean Callanan   std::string current_code;
5226681041dSSean Callanan   current_code.append(m_code.CopyList());
5236681041dSSean Callanan 
5246681041dSSean Callanan   IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
5256681041dSSean Callanan   const StringList *current_lines = editline.GetCurrentLines();
526b9c1b51eSKate Stone   if (current_lines) {
5276681041dSSean Callanan     const uint32_t current_line_idx = editline.GetCurrentLineIndex();
5286681041dSSean Callanan 
529b9c1b51eSKate Stone     if (current_line_idx < current_lines->GetSize()) {
530b9c1b51eSKate Stone       for (uint32_t i = 0; i < current_line_idx; ++i) {
5316681041dSSean Callanan         const char *line_cstr = current_lines->GetStringAtIndex(i);
532b9c1b51eSKate Stone         if (line_cstr) {
5336681041dSSean Callanan           current_code.append("\n");
5346681041dSSean Callanan           current_code.append(line_cstr);
5356681041dSSean Callanan         }
5366681041dSSean Callanan       }
5376681041dSSean Callanan     }
5386681041dSSean Callanan   }
5396681041dSSean Callanan 
5406681041dSSean Callanan   current_code.append("\n");
5417f9ac337SRaphael Isemann   current_code += request.GetRawLine();
5426681041dSSean Callanan 
543a497e1b5SRaphael Isemann   CompleteCode(current_code, request);
5446681041dSSean Callanan }
5456681041dSSean Callanan 
QuitCommandOverrideCallback(void * baton,const char ** argv)546b9c1b51eSKate Stone bool QuitCommandOverrideCallback(void *baton, const char **argv) {
5476681041dSSean Callanan   Target *target = (Target *)baton;
5486681041dSSean Callanan   lldb::ProcessSP process_sp(target->GetProcessSP());
549b9c1b51eSKate Stone   if (process_sp) {
5506681041dSSean Callanan     process_sp->Destroy(false);
5516681041dSSean Callanan     process_sp->GetTarget().GetDebugger().ClearIOHandlers();
5526681041dSSean Callanan   }
5536681041dSSean Callanan   return false;
5546681041dSSean Callanan }
5556681041dSSean Callanan 
RunLoop()55697206d57SZachary Turner Status REPL::RunLoop() {
55797206d57SZachary Turner   Status error;
5586681041dSSean Callanan 
5596681041dSSean Callanan   error = DoInitialization();
5606681041dSSean Callanan   m_repl_source_path = GetSourcePath();
5616681041dSSean Callanan 
5626681041dSSean Callanan   if (!error.Success())
5636681041dSSean Callanan     return error;
5646681041dSSean Callanan 
5656681041dSSean Callanan   Debugger &debugger = m_target.GetDebugger();
5666681041dSSean Callanan 
5676681041dSSean Callanan   lldb::IOHandlerSP io_handler_sp(GetIOHandler());
5686681041dSSean Callanan 
5696681041dSSean Callanan   FileSpec save_default_file;
5706681041dSSean Callanan   uint32_t save_default_line = 0;
5716681041dSSean Callanan 
572b9c1b51eSKate Stone   if (!m_repl_source_path.empty()) {
5736681041dSSean Callanan     // Save the current default file and line
574b9c1b51eSKate Stone     m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file,
575b9c1b51eSKate Stone                                                       save_default_line);
5766681041dSSean Callanan   }
5776681041dSSean Callanan 
5787ce2de2cSJonas Devlieghere   debugger.RunIOHandlerAsync(io_handler_sp);
5796681041dSSean Callanan 
58005097246SAdrian Prantl   // Check if we are in dedicated REPL mode where LLDB was start with the "--
58105097246SAdrian Prantl   // repl" option from the command line. Currently we know this by checking if
58205097246SAdrian Prantl   // the debugger already has a IOHandler thread.
583b9c1b51eSKate Stone   if (!debugger.HasIOHandlerThread()) {
5846681041dSSean Callanan     // The debugger doesn't have an existing IOHandler thread, so this must be
5856681041dSSean Callanan     // dedicated REPL mode...
5866681041dSSean Callanan     m_dedicated_repl_mode = true;
5876681041dSSean Callanan     debugger.StartIOHandlerThread();
588a01bccdbSZachary Turner     llvm::StringRef command_name_str("quit");
589b9c1b51eSKate Stone     CommandObject *cmd_obj =
590b9c1b51eSKate Stone         debugger.GetCommandInterpreter().GetCommandObjectForCommand(
591b9c1b51eSKate Stone             command_name_str);
592b9c1b51eSKate Stone     if (cmd_obj) {
5936681041dSSean Callanan       assert(command_name_str.empty());
5946681041dSSean Callanan       cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target);
5956681041dSSean Callanan     }
5966681041dSSean Callanan   }
5976681041dSSean Callanan 
5986681041dSSean Callanan   // Wait for the REPL command interpreter to get popped
5996681041dSSean Callanan   io_handler_sp->WaitForPop();
6006681041dSSean Callanan 
601b9c1b51eSKate Stone   if (m_dedicated_repl_mode) {
60205097246SAdrian Prantl     // If we were in dedicated REPL mode we would have started the IOHandler
60305097246SAdrian Prantl     // thread, and we should kill our process
6046681041dSSean Callanan     lldb::ProcessSP process_sp = m_target.GetProcessSP();
6056681041dSSean Callanan     if (process_sp && process_sp->IsAlive())
6066681041dSSean Callanan       process_sp->Destroy(false);
6076681041dSSean Callanan 
608b9c1b51eSKate Stone     // Wait for the IO handler thread to exit (TODO: don't do this if the IO
609b9c1b51eSKate Stone     // handler thread already exists...)
6106681041dSSean Callanan     debugger.JoinIOHandlerThread();
6116681041dSSean Callanan   }
6126681041dSSean Callanan 
6136681041dSSean Callanan   // Restore the default file and line
6146681041dSSean Callanan   if (save_default_file && save_default_line != 0)
615b9c1b51eSKate Stone     m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file,
616b9c1b51eSKate Stone                                                       save_default_line);
6176681041dSSean Callanan   return error;
6186681041dSSean Callanan }
619