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