xref: /openbsd-src/gnu/llvm/lldb/source/Expression/REPL.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- REPL.cpp ----------------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Expression/REPL.h"
10061da546Spatrick #include "lldb/Core/Debugger.h"
11061da546Spatrick #include "lldb/Core/PluginManager.h"
12061da546Spatrick #include "lldb/Core/StreamFile.h"
13061da546Spatrick #include "lldb/Expression/ExpressionVariable.h"
14061da546Spatrick #include "lldb/Expression/UserExpression.h"
15061da546Spatrick #include "lldb/Host/HostInfo.h"
16061da546Spatrick #include "lldb/Interpreter/CommandInterpreter.h"
17061da546Spatrick #include "lldb/Interpreter/CommandReturnObject.h"
18061da546Spatrick #include "lldb/Target/Thread.h"
19061da546Spatrick #include "lldb/Utility/AnsiTerminal.h"
20061da546Spatrick 
21061da546Spatrick #include <memory>
22061da546Spatrick 
23061da546Spatrick using namespace lldb_private;
24061da546Spatrick 
REPL(LLVMCastKind kind,Target & target)25061da546Spatrick REPL::REPL(LLVMCastKind kind, Target &target) : m_target(target), m_kind(kind) {
26061da546Spatrick   // Make sure all option values have sane defaults
27061da546Spatrick   Debugger &debugger = m_target.GetDebugger();
28*f6aab3d8Srobert   debugger.SetShowProgress(false);
29061da546Spatrick   auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext();
30061da546Spatrick   m_format_options.OptionParsingStarting(&exe_ctx);
31061da546Spatrick   m_varobj_options.OptionParsingStarting(&exe_ctx);
32061da546Spatrick }
33061da546Spatrick 
34061da546Spatrick REPL::~REPL() = default;
35061da546Spatrick 
Create(Status & err,lldb::LanguageType language,Debugger * debugger,Target * target,const char * repl_options)36061da546Spatrick lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language,
37061da546Spatrick                           Debugger *debugger, Target *target,
38061da546Spatrick                           const char *repl_options) {
39061da546Spatrick   uint32_t idx = 0;
40061da546Spatrick   lldb::REPLSP ret;
41061da546Spatrick 
42061da546Spatrick   while (REPLCreateInstance create_instance =
43*f6aab3d8Srobert              PluginManager::GetREPLCreateCallbackAtIndex(idx)) {
44*f6aab3d8Srobert     LanguageSet supported_languages =
45*f6aab3d8Srobert         PluginManager::GetREPLSupportedLanguagesAtIndex(idx++);
46*f6aab3d8Srobert     if (!supported_languages[language])
47*f6aab3d8Srobert       continue;
48061da546Spatrick     ret = (*create_instance)(err, language, debugger, target, repl_options);
49061da546Spatrick     if (ret) {
50061da546Spatrick       break;
51061da546Spatrick     }
52061da546Spatrick   }
53061da546Spatrick 
54061da546Spatrick   return ret;
55061da546Spatrick }
56061da546Spatrick 
GetSourcePath()57061da546Spatrick std::string REPL::GetSourcePath() {
58061da546Spatrick   ConstString file_basename = GetSourceFileBasename();
59061da546Spatrick   FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
60061da546Spatrick   if (tmpdir_file_spec) {
61*f6aab3d8Srobert     tmpdir_file_spec.SetFilename(file_basename);
62061da546Spatrick     m_repl_source_path = tmpdir_file_spec.GetPath();
63061da546Spatrick   } else {
64061da546Spatrick     tmpdir_file_spec = FileSpec("/tmp");
65dda28197Spatrick     tmpdir_file_spec.AppendPathComponent(file_basename.GetStringRef());
66061da546Spatrick   }
67061da546Spatrick 
68061da546Spatrick   return tmpdir_file_spec.GetPath();
69061da546Spatrick }
70061da546Spatrick 
GetIOHandler()71061da546Spatrick lldb::IOHandlerSP REPL::GetIOHandler() {
72061da546Spatrick   if (!m_io_handler_sp) {
73061da546Spatrick     Debugger &debugger = m_target.GetDebugger();
74061da546Spatrick     m_io_handler_sp = std::make_shared<IOHandlerEditline>(
75061da546Spatrick         debugger, IOHandler::Type::REPL,
76061da546Spatrick         "lldb-repl",           // Name of input reader for history
77061da546Spatrick         llvm::StringRef("> "), // prompt
78061da546Spatrick         llvm::StringRef(". "), // Continuation prompt
79061da546Spatrick         true,                  // Multi-line
80061da546Spatrick         true,                  // The REPL prompt is always colored
81061da546Spatrick         1,                     // Line number
82*f6aab3d8Srobert         *this);
83061da546Spatrick 
84061da546Spatrick     // Don't exit if CTRL+C is pressed
85061da546Spatrick     static_cast<IOHandlerEditline *>(m_io_handler_sp.get())
86061da546Spatrick         ->SetInterruptExits(false);
87061da546Spatrick 
88061da546Spatrick     if (m_io_handler_sp->GetIsInteractive() &&
89061da546Spatrick         m_io_handler_sp->GetIsRealTerminal()) {
90061da546Spatrick       m_indent_str.assign(debugger.GetTabSize(), ' ');
91061da546Spatrick       m_enable_auto_indent = debugger.GetAutoIndent();
92061da546Spatrick     } else {
93061da546Spatrick       m_indent_str.clear();
94061da546Spatrick       m_enable_auto_indent = false;
95061da546Spatrick     }
96061da546Spatrick   }
97061da546Spatrick   return m_io_handler_sp;
98061da546Spatrick }
99061da546Spatrick 
IOHandlerActivated(IOHandler & io_handler,bool interactive)100061da546Spatrick void REPL::IOHandlerActivated(IOHandler &io_handler, bool interactive) {
101061da546Spatrick   lldb::ProcessSP process_sp = m_target.GetProcessSP();
102061da546Spatrick   if (process_sp && process_sp->IsAlive())
103061da546Spatrick     return;
104061da546Spatrick   lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
105061da546Spatrick   error_sp->Printf("REPL requires a running target process.\n");
106061da546Spatrick   io_handler.SetIsDone(true);
107061da546Spatrick }
108061da546Spatrick 
IOHandlerInterrupt(IOHandler & io_handler)109061da546Spatrick bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; }
110061da546Spatrick 
IOHandlerInputInterrupted(IOHandler & io_handler,std::string & line)111061da546Spatrick void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) {
112061da546Spatrick }
113061da546Spatrick 
IOHandlerGetFixIndentationCharacters()114061da546Spatrick const char *REPL::IOHandlerGetFixIndentationCharacters() {
115061da546Spatrick   return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr);
116061da546Spatrick }
117061da546Spatrick 
IOHandlerGetControlSequence(char ch)118061da546Spatrick ConstString REPL::IOHandlerGetControlSequence(char ch) {
119061da546Spatrick   if (ch == 'd')
120061da546Spatrick     return ConstString(":quit\n");
121061da546Spatrick   return ConstString();
122061da546Spatrick }
123061da546Spatrick 
IOHandlerGetCommandPrefix()124061da546Spatrick const char *REPL::IOHandlerGetCommandPrefix() { return ":"; }
125061da546Spatrick 
IOHandlerGetHelpPrologue()126061da546Spatrick const char *REPL::IOHandlerGetHelpPrologue() {
127061da546Spatrick   return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter.  "
128061da546Spatrick          "Valid statements, expressions, and declarations are immediately "
129061da546Spatrick          "compiled and executed.\n\n"
130061da546Spatrick          "The complete set of LLDB debugging commands are also available as "
131be691f3bSpatrick          "described below.\n\nCommands "
132061da546Spatrick          "must be prefixed with a colon at the REPL prompt (:quit for "
133061da546Spatrick          "example.)  Typing just a colon "
134be691f3bSpatrick          "followed by return will switch to the LLDB prompt.\n\n"
135be691f3bSpatrick          "Type “< path” to read in code from a text file “path”.\n\n";
136061da546Spatrick }
137061da546Spatrick 
IOHandlerIsInputComplete(IOHandler & io_handler,StringList & lines)138061da546Spatrick bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) {
139061da546Spatrick   // Check for meta command
140061da546Spatrick   const size_t num_lines = lines.GetSize();
141061da546Spatrick   if (num_lines == 1) {
142061da546Spatrick     const char *first_line = lines.GetStringAtIndex(0);
143061da546Spatrick     if (first_line[0] == ':')
144061da546Spatrick       return true; // Meta command is a single line where that starts with ':'
145061da546Spatrick   }
146061da546Spatrick 
147061da546Spatrick   // Check if REPL input is done
148061da546Spatrick   std::string source_string(lines.CopyList());
149061da546Spatrick   return SourceIsComplete(source_string);
150061da546Spatrick }
151061da546Spatrick 
CalculateActualIndentation(const StringList & lines)152061da546Spatrick int REPL::CalculateActualIndentation(const StringList &lines) {
153061da546Spatrick   std::string last_line = lines[lines.GetSize() - 1];
154061da546Spatrick 
155061da546Spatrick   int actual_indent = 0;
156061da546Spatrick   for (char &ch : last_line) {
157061da546Spatrick     if (ch != ' ')
158061da546Spatrick       break;
159061da546Spatrick     ++actual_indent;
160061da546Spatrick   }
161061da546Spatrick 
162061da546Spatrick   return actual_indent;
163061da546Spatrick }
164061da546Spatrick 
IOHandlerFixIndentation(IOHandler & io_handler,const StringList & lines,int cursor_position)165061da546Spatrick int REPL::IOHandlerFixIndentation(IOHandler &io_handler,
166061da546Spatrick                                   const StringList &lines,
167061da546Spatrick                                   int cursor_position) {
168061da546Spatrick   if (!m_enable_auto_indent)
169061da546Spatrick     return 0;
170061da546Spatrick 
171061da546Spatrick   if (!lines.GetSize()) {
172061da546Spatrick     return 0;
173061da546Spatrick   }
174061da546Spatrick 
175061da546Spatrick   int tab_size = io_handler.GetDebugger().GetTabSize();
176061da546Spatrick 
177061da546Spatrick   lldb::offset_t desired_indent =
178061da546Spatrick       GetDesiredIndentation(lines, cursor_position, tab_size);
179061da546Spatrick 
180061da546Spatrick   int actual_indent = REPL::CalculateActualIndentation(lines);
181061da546Spatrick 
182061da546Spatrick   if (desired_indent == LLDB_INVALID_OFFSET)
183061da546Spatrick     return 0;
184061da546Spatrick 
185061da546Spatrick   return (int)desired_indent - actual_indent;
186061da546Spatrick }
187061da546Spatrick 
ReadCode(const std::string & path,std::string & code,lldb::StreamFileSP & error_sp)188be691f3bSpatrick static bool ReadCode(const std::string &path, std::string &code,
189be691f3bSpatrick                      lldb::StreamFileSP &error_sp) {
190be691f3bSpatrick   auto &fs = FileSystem::Instance();
191be691f3bSpatrick   llvm::Twine pathTwine(path);
192be691f3bSpatrick   if (!fs.Exists(pathTwine)) {
193be691f3bSpatrick     error_sp->Printf("no such file at path '%s'\n", path.c_str());
194be691f3bSpatrick     return false;
195be691f3bSpatrick   }
196be691f3bSpatrick   if (!fs.Readable(pathTwine)) {
197be691f3bSpatrick     error_sp->Printf("could not read file at path '%s'\n", path.c_str());
198be691f3bSpatrick     return false;
199be691f3bSpatrick   }
200be691f3bSpatrick   const size_t file_size = fs.GetByteSize(pathTwine);
201be691f3bSpatrick   const size_t max_size = code.max_size();
202be691f3bSpatrick   if (file_size > max_size) {
203be691f3bSpatrick     error_sp->Printf("file at path '%s' too large: "
204be691f3bSpatrick                      "file_size = %zu, max_size = %zu\n",
205be691f3bSpatrick                      path.c_str(), file_size, max_size);
206be691f3bSpatrick     return false;
207be691f3bSpatrick   }
208be691f3bSpatrick   auto data_sp = fs.CreateDataBuffer(pathTwine);
209be691f3bSpatrick   if (data_sp == nullptr) {
210be691f3bSpatrick     error_sp->Printf("could not create buffer for file at path '%s'\n",
211be691f3bSpatrick                      path.c_str());
212be691f3bSpatrick     return false;
213be691f3bSpatrick   }
214be691f3bSpatrick   code.assign((const char *)data_sp->GetBytes(), data_sp->GetByteSize());
215be691f3bSpatrick   return true;
216be691f3bSpatrick }
217be691f3bSpatrick 
IOHandlerInputComplete(IOHandler & io_handler,std::string & code)218061da546Spatrick void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {
219061da546Spatrick   lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
220061da546Spatrick   lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
221061da546Spatrick   bool extra_line = false;
222061da546Spatrick   bool did_quit = false;
223061da546Spatrick 
224061da546Spatrick   if (code.empty()) {
225061da546Spatrick     m_code.AppendString("");
226061da546Spatrick     static_cast<IOHandlerEditline &>(io_handler)
227061da546Spatrick         .SetBaseLineNumber(m_code.GetSize() + 1);
228061da546Spatrick   } else {
229061da546Spatrick     Debugger &debugger = m_target.GetDebugger();
230061da546Spatrick     CommandInterpreter &ci = debugger.GetCommandInterpreter();
231061da546Spatrick     extra_line = ci.GetSpaceReplPrompts();
232061da546Spatrick 
233061da546Spatrick     ExecutionContext exe_ctx(m_target.GetProcessSP()
234061da546Spatrick                                  ->GetThreadList()
235061da546Spatrick                                  .GetSelectedThread()
236061da546Spatrick                                  ->GetSelectedFrame()
237061da546Spatrick                                  .get());
238061da546Spatrick 
239061da546Spatrick     lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
240061da546Spatrick 
241061da546Spatrick     if (code[0] == ':') {
242061da546Spatrick       // Meta command
243061da546Spatrick       // Strip the ':'
244061da546Spatrick       code.erase(0, 1);
245061da546Spatrick       if (!llvm::StringRef(code).trim().empty()) {
246061da546Spatrick         // "lldb" was followed by arguments, so just execute the command dump
247061da546Spatrick         // the results
248061da546Spatrick 
249061da546Spatrick         // Turn off prompt on quit in case the user types ":quit"
250061da546Spatrick         const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
251061da546Spatrick         if (saved_prompt_on_quit)
252061da546Spatrick           ci.SetPromptOnQuit(false);
253061da546Spatrick 
254061da546Spatrick         // Execute the command
255dda28197Spatrick         CommandReturnObject result(debugger.GetUseColor());
256061da546Spatrick         result.SetImmediateOutputStream(output_sp);
257061da546Spatrick         result.SetImmediateErrorStream(error_sp);
258061da546Spatrick         ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
259061da546Spatrick 
260061da546Spatrick         if (saved_prompt_on_quit)
261061da546Spatrick           ci.SetPromptOnQuit(true);
262061da546Spatrick 
263061da546Spatrick         if (result.GetStatus() == lldb::eReturnStatusQuit) {
264061da546Spatrick           did_quit = true;
265061da546Spatrick           io_handler.SetIsDone(true);
266061da546Spatrick           if (debugger.CheckTopIOHandlerTypes(
267061da546Spatrick                   IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
268061da546Spatrick             // We typed "quit" or an alias to quit so we need to check if the
269061da546Spatrick             // command interpreter is above us and tell it that it is done as
270061da546Spatrick             // well so we don't drop back into the command interpreter if we
271061da546Spatrick             // have already quit
272061da546Spatrick             lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
273061da546Spatrick             if (io_handler_sp)
274061da546Spatrick               io_handler_sp->SetIsDone(true);
275061da546Spatrick           }
276061da546Spatrick         }
277061da546Spatrick       } else {
278061da546Spatrick         // ":" was followed by no arguments, so push the LLDB command prompt
279061da546Spatrick         if (debugger.CheckTopIOHandlerTypes(
280061da546Spatrick                 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
281061da546Spatrick           // If the user wants to get back to the command interpreter and the
282061da546Spatrick           // command interpreter is what launched the REPL, then just let the
283061da546Spatrick           // REPL exit and fall back to the command interpreter.
284061da546Spatrick           io_handler.SetIsDone(true);
285061da546Spatrick         } else {
286061da546Spatrick           // The REPL wasn't launched the by the command interpreter, it is the
287061da546Spatrick           // base IOHandler, so we need to get the command interpreter and
288061da546Spatrick           lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
289061da546Spatrick           if (io_handler_sp) {
290061da546Spatrick             io_handler_sp->SetIsDone(false);
291dda28197Spatrick             debugger.RunIOHandlerAsync(ci.GetIOHandler());
292061da546Spatrick           }
293061da546Spatrick         }
294061da546Spatrick       }
295061da546Spatrick     } else {
296be691f3bSpatrick       if (code[0] == '<') {
297be691f3bSpatrick         // User wants to read code from a file.
298be691f3bSpatrick         // Interpret rest of line as a literal path.
299be691f3bSpatrick         auto path = llvm::StringRef(code.substr(1)).trim().str();
300be691f3bSpatrick         if (!ReadCode(path, code, error_sp)) {
301be691f3bSpatrick           return;
302be691f3bSpatrick         }
303be691f3bSpatrick       }
304be691f3bSpatrick 
305061da546Spatrick       // Unwind any expression we might have been running in case our REPL
306061da546Spatrick       // expression crashed and the user was looking around
307061da546Spatrick       if (m_dedicated_repl_mode) {
308061da546Spatrick         Thread *thread = exe_ctx.GetThreadPtr();
309061da546Spatrick         if (thread && thread->UnwindInnermostExpression().Success()) {
310061da546Spatrick           thread->SetSelectedFrameByIndex(0, false);
311061da546Spatrick           exe_ctx.SetFrameSP(thread->GetSelectedFrame());
312061da546Spatrick         }
313061da546Spatrick       }
314061da546Spatrick 
315061da546Spatrick       const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
316061da546Spatrick 
317061da546Spatrick       EvaluateExpressionOptions expr_options = m_expr_options;
318061da546Spatrick       expr_options.SetCoerceToId(m_varobj_options.use_objc);
319061da546Spatrick       expr_options.SetKeepInMemory(true);
320061da546Spatrick       expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
321061da546Spatrick       expr_options.SetGenerateDebugInfo(true);
322061da546Spatrick       expr_options.SetREPLEnabled(true);
323061da546Spatrick       expr_options.SetColorizeErrors(colorize_err);
324061da546Spatrick       expr_options.SetPoundLine(m_repl_source_path.c_str(),
325061da546Spatrick                                 m_code.GetSize() + 1);
326061da546Spatrick 
327061da546Spatrick       expr_options.SetLanguage(GetLanguage());
328061da546Spatrick 
329061da546Spatrick       PersistentExpressionState *persistent_state =
330061da546Spatrick           m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
331061da546Spatrick       if (!persistent_state)
332061da546Spatrick         return;
333061da546Spatrick 
334061da546Spatrick       const size_t var_count_before = persistent_state->GetSize();
335061da546Spatrick 
336061da546Spatrick       const char *expr_prefix = nullptr;
337061da546Spatrick       lldb::ValueObjectSP result_valobj_sp;
338061da546Spatrick       Status error;
339061da546Spatrick       lldb::ExpressionResults execution_results =
340061da546Spatrick           UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(),
341061da546Spatrick                                    expr_prefix, result_valobj_sp, error,
342dda28197Spatrick                                    nullptr); // fixed expression
343061da546Spatrick 
344061da546Spatrick       // CommandInterpreter &ci = debugger.GetCommandInterpreter();
345061da546Spatrick 
346061da546Spatrick       if (process_sp && process_sp->IsAlive()) {
347061da546Spatrick         bool add_to_code = true;
348061da546Spatrick         bool handled = false;
349061da546Spatrick         if (result_valobj_sp) {
350061da546Spatrick           lldb::Format format = m_format_options.GetFormat();
351061da546Spatrick 
352061da546Spatrick           if (result_valobj_sp->GetError().Success()) {
353061da546Spatrick             handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
354061da546Spatrick           } else if (result_valobj_sp->GetError().GetError() ==
355061da546Spatrick                      UserExpression::kNoResult) {
356061da546Spatrick             if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) {
357061da546Spatrick               error_sp->PutCString("(void)\n");
358061da546Spatrick               handled = true;
359061da546Spatrick             }
360061da546Spatrick           }
361061da546Spatrick         }
362061da546Spatrick 
363061da546Spatrick         if (debugger.GetPrintDecls()) {
364061da546Spatrick           for (size_t vi = var_count_before, ve = persistent_state->GetSize();
365061da546Spatrick                vi != ve; ++vi) {
366061da546Spatrick             lldb::ExpressionVariableSP persistent_var_sp =
367061da546Spatrick                 persistent_state->GetVariableAtIndex(vi);
368061da546Spatrick             lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
369061da546Spatrick 
370061da546Spatrick             PrintOneVariable(debugger, output_sp, valobj_sp,
371061da546Spatrick                              persistent_var_sp.get());
372061da546Spatrick           }
373061da546Spatrick         }
374061da546Spatrick 
375061da546Spatrick         if (!handled) {
376061da546Spatrick           bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
377061da546Spatrick           switch (execution_results) {
378061da546Spatrick           case lldb::eExpressionSetupError:
379061da546Spatrick           case lldb::eExpressionParseError:
380061da546Spatrick             add_to_code = false;
381*f6aab3d8Srobert             [[fallthrough]];
382061da546Spatrick           case lldb::eExpressionDiscarded:
383061da546Spatrick             error_sp->Printf("%s\n", error.AsCString());
384061da546Spatrick             break;
385061da546Spatrick 
386061da546Spatrick           case lldb::eExpressionCompleted:
387061da546Spatrick             break;
388061da546Spatrick           case lldb::eExpressionInterrupted:
389061da546Spatrick             if (useColors) {
390061da546Spatrick               error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
391061da546Spatrick               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
392061da546Spatrick             }
393061da546Spatrick             error_sp->Printf("Execution interrupted. ");
394061da546Spatrick             if (useColors)
395061da546Spatrick               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
396061da546Spatrick             error_sp->Printf("Enter code to recover and continue.\nEnter LLDB "
397061da546Spatrick                              "commands to investigate (type :help for "
398061da546Spatrick                              "assistance.)\n");
399061da546Spatrick             break;
400061da546Spatrick 
401061da546Spatrick           case lldb::eExpressionHitBreakpoint:
402061da546Spatrick             // Breakpoint was hit, drop into LLDB command interpreter
403061da546Spatrick             if (useColors) {
404061da546Spatrick               error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
405061da546Spatrick               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
406061da546Spatrick             }
407061da546Spatrick             output_sp->Printf("Execution stopped at breakpoint.  ");
408061da546Spatrick             if (useColors)
409061da546Spatrick               error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
410061da546Spatrick             output_sp->Printf("Enter LLDB commands to investigate (type help "
411061da546Spatrick                               "for assistance.)\n");
412061da546Spatrick             {
413061da546Spatrick               lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
414061da546Spatrick               if (io_handler_sp) {
415061da546Spatrick                 io_handler_sp->SetIsDone(false);
416dda28197Spatrick                 debugger.RunIOHandlerAsync(ci.GetIOHandler());
417061da546Spatrick               }
418061da546Spatrick             }
419061da546Spatrick             break;
420061da546Spatrick 
421061da546Spatrick           case lldb::eExpressionTimedOut:
422061da546Spatrick             error_sp->Printf("error: timeout\n");
423061da546Spatrick             if (error.AsCString())
424061da546Spatrick               error_sp->Printf("error: %s\n", error.AsCString());
425061da546Spatrick             break;
426061da546Spatrick           case lldb::eExpressionResultUnavailable:
427061da546Spatrick             // Shoulnd't happen???
428061da546Spatrick             error_sp->Printf("error: could not fetch result -- %s\n",
429061da546Spatrick                              error.AsCString());
430061da546Spatrick             break;
431061da546Spatrick           case lldb::eExpressionStoppedForDebug:
432061da546Spatrick             // Shoulnd't happen???
433061da546Spatrick             error_sp->Printf("error: stopped for debug -- %s\n",
434061da546Spatrick                              error.AsCString());
435061da546Spatrick             break;
436dda28197Spatrick           case lldb::eExpressionThreadVanished:
437dda28197Spatrick             // Shoulnd't happen???
438dda28197Spatrick             error_sp->Printf("error: expression thread vanished -- %s\n",
439dda28197Spatrick                              error.AsCString());
440dda28197Spatrick             break;
441061da546Spatrick           }
442061da546Spatrick         }
443061da546Spatrick 
444061da546Spatrick         if (add_to_code) {
445061da546Spatrick           const uint32_t new_default_line = m_code.GetSize() + 1;
446061da546Spatrick 
447061da546Spatrick           m_code.SplitIntoLines(code);
448061da546Spatrick 
449061da546Spatrick           // Update our code on disk
450061da546Spatrick           if (!m_repl_source_path.empty()) {
451061da546Spatrick             auto file = FileSystem::Instance().Open(
452061da546Spatrick                 FileSpec(m_repl_source_path),
453*f6aab3d8Srobert                 File::eOpenOptionWriteOnly | File::eOpenOptionTruncate |
454061da546Spatrick                     File::eOpenOptionCanCreate,
455061da546Spatrick                 lldb::eFilePermissionsFileDefault);
456061da546Spatrick             if (file) {
457061da546Spatrick               std::string code(m_code.CopyList());
458061da546Spatrick               code.append(1, '\n');
459061da546Spatrick               size_t bytes_written = code.size();
460061da546Spatrick               file.get()->Write(code.c_str(), bytes_written);
461061da546Spatrick               file.get()->Close();
462061da546Spatrick             } else {
463061da546Spatrick               std::string message = llvm::toString(file.takeError());
464061da546Spatrick               error_sp->Printf("error: couldn't open %s: %s\n",
465061da546Spatrick                                m_repl_source_path.c_str(), message.c_str());
466061da546Spatrick             }
467061da546Spatrick 
468061da546Spatrick             // Now set the default file and line to the REPL source file
469061da546Spatrick             m_target.GetSourceManager().SetDefaultFileAndLine(
470061da546Spatrick                 FileSpec(m_repl_source_path), new_default_line);
471061da546Spatrick           }
472061da546Spatrick           static_cast<IOHandlerEditline &>(io_handler)
473061da546Spatrick               .SetBaseLineNumber(m_code.GetSize() + 1);
474061da546Spatrick         }
475061da546Spatrick         if (extra_line) {
476061da546Spatrick           output_sp->Printf("\n");
477061da546Spatrick         }
478061da546Spatrick       }
479061da546Spatrick     }
480061da546Spatrick 
481061da546Spatrick     // Don't complain about the REPL process going away if we are in the
482061da546Spatrick     // process of quitting.
483061da546Spatrick     if (!did_quit && (!process_sp || !process_sp->IsAlive())) {
484061da546Spatrick       error_sp->Printf(
485061da546Spatrick           "error: REPL process is no longer alive, exiting REPL\n");
486061da546Spatrick       io_handler.SetIsDone(true);
487061da546Spatrick     }
488061da546Spatrick   }
489061da546Spatrick }
490061da546Spatrick 
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)491061da546Spatrick void REPL::IOHandlerComplete(IOHandler &io_handler,
492061da546Spatrick                              CompletionRequest &request) {
493061da546Spatrick   // Complete an LLDB command if the first character is a colon...
494061da546Spatrick   if (request.GetRawLine().startswith(":")) {
495061da546Spatrick     Debugger &debugger = m_target.GetDebugger();
496061da546Spatrick 
497061da546Spatrick     // auto complete LLDB commands
498061da546Spatrick     llvm::StringRef new_line = request.GetRawLine().drop_front();
499061da546Spatrick     CompletionResult sub_result;
500061da546Spatrick     CompletionRequest sub_request(new_line, request.GetRawCursorPos() - 1,
501061da546Spatrick                                   sub_result);
502061da546Spatrick     debugger.GetCommandInterpreter().HandleCompletion(sub_request);
503061da546Spatrick     StringList matches, descriptions;
504061da546Spatrick     sub_result.GetMatches(matches);
505dda28197Spatrick     // Prepend command prefix that was excluded in the completion request.
506dda28197Spatrick     if (request.GetCursorIndex() == 0)
507dda28197Spatrick       for (auto &match : matches)
508dda28197Spatrick         match.insert(0, 1, ':');
509061da546Spatrick     sub_result.GetDescriptions(descriptions);
510061da546Spatrick     request.AddCompletions(matches, descriptions);
511061da546Spatrick     return;
512061da546Spatrick   }
513061da546Spatrick 
514061da546Spatrick   // Strip spaces from the line and see if we had only spaces
515061da546Spatrick   if (request.GetRawLine().trim().empty()) {
516061da546Spatrick     // Only spaces on this line, so just indent
517061da546Spatrick     request.AddCompletion(m_indent_str);
518061da546Spatrick     return;
519061da546Spatrick   }
520061da546Spatrick 
521061da546Spatrick   std::string current_code;
522061da546Spatrick   current_code.append(m_code.CopyList());
523061da546Spatrick 
524061da546Spatrick   IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
525061da546Spatrick   const StringList *current_lines = editline.GetCurrentLines();
526061da546Spatrick   if (current_lines) {
527061da546Spatrick     const uint32_t current_line_idx = editline.GetCurrentLineIndex();
528061da546Spatrick 
529061da546Spatrick     if (current_line_idx < current_lines->GetSize()) {
530061da546Spatrick       for (uint32_t i = 0; i < current_line_idx; ++i) {
531061da546Spatrick         const char *line_cstr = current_lines->GetStringAtIndex(i);
532061da546Spatrick         if (line_cstr) {
533061da546Spatrick           current_code.append("\n");
534061da546Spatrick           current_code.append(line_cstr);
535061da546Spatrick         }
536061da546Spatrick       }
537061da546Spatrick     }
538061da546Spatrick   }
539061da546Spatrick 
540061da546Spatrick   current_code.append("\n");
541061da546Spatrick   current_code += request.GetRawLine();
542061da546Spatrick 
543dda28197Spatrick   CompleteCode(current_code, request);
544061da546Spatrick }
545061da546Spatrick 
QuitCommandOverrideCallback(void * baton,const char ** argv)546061da546Spatrick bool QuitCommandOverrideCallback(void *baton, const char **argv) {
547061da546Spatrick   Target *target = (Target *)baton;
548061da546Spatrick   lldb::ProcessSP process_sp(target->GetProcessSP());
549061da546Spatrick   if (process_sp) {
550061da546Spatrick     process_sp->Destroy(false);
551061da546Spatrick     process_sp->GetTarget().GetDebugger().ClearIOHandlers();
552061da546Spatrick   }
553061da546Spatrick   return false;
554061da546Spatrick }
555061da546Spatrick 
RunLoop()556061da546Spatrick Status REPL::RunLoop() {
557061da546Spatrick   Status error;
558061da546Spatrick 
559061da546Spatrick   error = DoInitialization();
560061da546Spatrick   m_repl_source_path = GetSourcePath();
561061da546Spatrick 
562061da546Spatrick   if (!error.Success())
563061da546Spatrick     return error;
564061da546Spatrick 
565061da546Spatrick   Debugger &debugger = m_target.GetDebugger();
566061da546Spatrick 
567061da546Spatrick   lldb::IOHandlerSP io_handler_sp(GetIOHandler());
568061da546Spatrick 
569061da546Spatrick   FileSpec save_default_file;
570061da546Spatrick   uint32_t save_default_line = 0;
571061da546Spatrick 
572061da546Spatrick   if (!m_repl_source_path.empty()) {
573061da546Spatrick     // Save the current default file and line
574061da546Spatrick     m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file,
575061da546Spatrick                                                       save_default_line);
576061da546Spatrick   }
577061da546Spatrick 
578dda28197Spatrick   debugger.RunIOHandlerAsync(io_handler_sp);
579061da546Spatrick 
580061da546Spatrick   // Check if we are in dedicated REPL mode where LLDB was start with the "--
581061da546Spatrick   // repl" option from the command line. Currently we know this by checking if
582061da546Spatrick   // the debugger already has a IOHandler thread.
583061da546Spatrick   if (!debugger.HasIOHandlerThread()) {
584061da546Spatrick     // The debugger doesn't have an existing IOHandler thread, so this must be
585061da546Spatrick     // dedicated REPL mode...
586061da546Spatrick     m_dedicated_repl_mode = true;
587061da546Spatrick     debugger.StartIOHandlerThread();
588061da546Spatrick     llvm::StringRef command_name_str("quit");
589061da546Spatrick     CommandObject *cmd_obj =
590061da546Spatrick         debugger.GetCommandInterpreter().GetCommandObjectForCommand(
591061da546Spatrick             command_name_str);
592061da546Spatrick     if (cmd_obj) {
593061da546Spatrick       assert(command_name_str.empty());
594061da546Spatrick       cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target);
595061da546Spatrick     }
596061da546Spatrick   }
597061da546Spatrick 
598061da546Spatrick   // Wait for the REPL command interpreter to get popped
599061da546Spatrick   io_handler_sp->WaitForPop();
600061da546Spatrick 
601061da546Spatrick   if (m_dedicated_repl_mode) {
602061da546Spatrick     // If we were in dedicated REPL mode we would have started the IOHandler
603061da546Spatrick     // thread, and we should kill our process
604061da546Spatrick     lldb::ProcessSP process_sp = m_target.GetProcessSP();
605061da546Spatrick     if (process_sp && process_sp->IsAlive())
606061da546Spatrick       process_sp->Destroy(false);
607061da546Spatrick 
608061da546Spatrick     // Wait for the IO handler thread to exit (TODO: don't do this if the IO
609061da546Spatrick     // handler thread already exists...)
610061da546Spatrick     debugger.JoinIOHandlerThread();
611061da546Spatrick   }
612061da546Spatrick 
613061da546Spatrick   // Restore the default file and line
614061da546Spatrick   if (save_default_file && save_default_line != 0)
615061da546Spatrick     m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file,
616061da546Spatrick                                                       save_default_line);
617061da546Spatrick   return error;
618061da546Spatrick }
619