650 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			650 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- REPL.cpp ------------------------------------------------*- C++ -*-===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| // C Includes
 | |
| // C++ Includes
 | |
| // Other libraries and framework includes
 | |
| // Project includes
 | |
| #include "lldb/Core/Debugger.h"
 | |
| #include "lldb/Core/PluginManager.h"
 | |
| #include "lldb/Core/StreamFile.h"
 | |
| #include "lldb/Expression/ExpressionVariable.h"
 | |
| #include "lldb/Expression/REPL.h"
 | |
| #include "lldb/Expression/UserExpression.h"
 | |
| #include "lldb/Host/HostInfo.h"
 | |
| #include "lldb/Interpreter/CommandInterpreter.h"
 | |
| #include "lldb/Interpreter/CommandReturnObject.h"
 | |
| #include "lldb/Target/Target.h"
 | |
| #include "lldb/Target/Thread.h"
 | |
| #include "lldb/Utility/AnsiTerminal.h"
 | |
| 
 | |
| using namespace lldb_private;
 | |
| 
 | |
| REPL::REPL(LLVMCastKind kind, Target &target) :
 | |
|     m_target(target),
 | |
|     m_kind(kind)
 | |
| {
 | |
|     // Make sure all option values have sane defaults
 | |
|     Debugger &debugger = m_target.GetDebugger();
 | |
|     CommandInterpreter &ci = debugger.GetCommandInterpreter();
 | |
|     m_format_options.OptionParsingStarting(ci);
 | |
|     m_varobj_options.OptionParsingStarting(ci);
 | |
|     m_command_options.OptionParsingStarting(ci);
 | |
|     
 | |
|     // Default certain settings for REPL regardless of the global settings.
 | |
|     m_command_options.unwind_on_error = false;
 | |
|     m_command_options.ignore_breakpoints = false;
 | |
|     m_command_options.debug = false;
 | |
| }
 | |
| 
 | |
| REPL::~REPL() = default;
 | |
| 
 | |
| lldb::REPLSP
 | |
| REPL::Create(Error &err, lldb::LanguageType language, Debugger *debugger, Target *target, const char *repl_options)
 | |
| {
 | |
|     uint32_t idx = 0;
 | |
|     lldb::REPLSP ret;
 | |
|     
 | |
|     while (REPLCreateInstance create_instance = PluginManager::GetREPLCreateCallbackAtIndex(idx++))
 | |
|     {
 | |
|         ret = (*create_instance)(err, language, debugger, target, repl_options);
 | |
|         if (ret)
 | |
|         {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| std::string
 | |
| REPL::GetSourcePath()
 | |
| {
 | |
|     ConstString file_basename = GetSourceFileBasename();
 | |
|     
 | |
|     FileSpec tmpdir_file_spec;
 | |
|     if (HostInfo::GetLLDBPath (lldb::ePathTypeLLDBTempSystemDir, tmpdir_file_spec))
 | |
|     {
 | |
|         tmpdir_file_spec.GetFilename().SetCString(file_basename.AsCString());
 | |
|         m_repl_source_path = tmpdir_file_spec.GetPath();
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         tmpdir_file_spec = FileSpec("/tmp", false);
 | |
|         tmpdir_file_spec.AppendPathComponent(file_basename.AsCString());
 | |
|     }
 | |
|     
 | |
|     return tmpdir_file_spec.GetPath();
 | |
| }
 | |
| 
 | |
| lldb::IOHandlerSP
 | |
| REPL::GetIOHandler()
 | |
| {
 | |
|     if (!m_io_handler_sp)
 | |
|     {
 | |
|         Debugger &debugger = m_target.GetDebugger();
 | |
|         m_io_handler_sp.reset (new IOHandlerEditline (debugger,
 | |
|                                                       IOHandler::Type::REPL,
 | |
|                                                       "lldb-repl",     // Name of input reader for history
 | |
|                                                       "> ",             // prompt
 | |
|                                                       ". ",             // Continuation prompt
 | |
|                                                       true,             // Multi-line
 | |
|                                                       true,             // The REPL prompt is always colored
 | |
|                                                       1,                // Line number
 | |
|                                                       *this));
 | |
|         
 | |
|         // Don't exit if CTRL+C is pressed
 | |
|         static_cast<IOHandlerEditline *>(m_io_handler_sp.get())->SetInterruptExits(false);
 | |
|         
 | |
|         if (m_io_handler_sp->GetIsInteractive() && m_io_handler_sp->GetIsRealTerminal())
 | |
|         {
 | |
|             m_indent_str.assign (debugger.GetTabSize(), ' ');
 | |
|             m_enable_auto_indent = debugger.GetAutoIndent();
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             m_indent_str.clear();
 | |
|             m_enable_auto_indent = false;
 | |
|         }
 | |
|         
 | |
|     }
 | |
|     return m_io_handler_sp;
 | |
| }
 | |
| 
 | |
| void
 | |
| REPL::IOHandlerActivated (IOHandler &io_handler)
 | |
| {
 | |
|     lldb::ProcessSP process_sp = m_target.GetProcessSP();
 | |
|     if (process_sp && process_sp->IsAlive())
 | |
|         return;
 | |
|     lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile());
 | |
|     error_sp->Printf("REPL requires a running target process.\n");
 | |
|     io_handler.SetIsDone(true);
 | |
| }
 | |
| 
 | |
| bool
 | |
| REPL::IOHandlerInterrupt (IOHandler &io_handler)
 | |
| {
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| void
 | |
| REPL::IOHandlerInputInterrupted (IOHandler &io_handler,
 | |
|                                  std::string &line)
 | |
| {
 | |
| }
 | |
| 
 | |
| const char *
 | |
| REPL::IOHandlerGetFixIndentationCharacters()
 | |
| {
 | |
|     return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr);
 | |
| }
 | |
| 
 | |
| ConstString
 | |
| REPL::IOHandlerGetControlSequence (char ch)
 | |
| {
 | |
|     if (ch == 'd')
 | |
|         return ConstString(":quit\n");
 | |
|     return ConstString();
 | |
| }
 | |
| 
 | |
| const char *
 | |
| REPL::IOHandlerGetCommandPrefix ()
 | |
| {
 | |
|     return ":";
 | |
| }
 | |
| 
 | |
| const char *
 | |
| REPL::IOHandlerGetHelpPrologue ()
 | |
| {
 | |
|     return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter.  "
 | |
|     "Valid statements, expressions, and declarations are immediately compiled and executed.\n\n"
 | |
|     "The complete set of LLDB debugging commands are also available as described below.  Commands "
 | |
|     "must be prefixed with a colon at the REPL prompt (:quit for example.)  Typing just a colon "
 | |
|     "followed by return will switch to the LLDB prompt.\n\n";
 | |
| }
 | |
| 
 | |
| bool
 | |
| REPL::IOHandlerIsInputComplete (IOHandler &io_handler,
 | |
|                                 StringList &lines)
 | |
| {
 | |
|     // Check for meta command
 | |
|     const size_t num_lines = lines.GetSize();
 | |
|     if (num_lines == 1)
 | |
|     {
 | |
|         const char *first_line = lines.GetStringAtIndex(0);
 | |
|         if (first_line[0] == ':')
 | |
|             return true; // Meta command is a single line where that starts with ':'
 | |
|     }
 | |
|     
 | |
|     // Check if REPL input is done
 | |
|     std::string source_string (lines.CopyList());
 | |
|     return SourceIsComplete(source_string);
 | |
| }
 | |
| 
 | |
| int
 | |
| REPL::CalculateActualIndentation (const StringList &lines)
 | |
| {
 | |
|     std::string last_line = lines[lines.GetSize() - 1];
 | |
| 
 | |
|     int actual_indent = 0;
 | |
|     for (char &ch : last_line)
 | |
|     {
 | |
|         if (ch != ' ') break;
 | |
|         ++actual_indent;
 | |
|     }
 | |
|     
 | |
|     return actual_indent;
 | |
| }
 | |
| 
 | |
| int
 | |
| REPL::IOHandlerFixIndentation (IOHandler &io_handler,
 | |
|                                const StringList &lines,
 | |
|                                int cursor_position)
 | |
| {
 | |
|     if (!m_enable_auto_indent) return 0;
 | |
|     
 | |
|     if (!lines.GetSize())
 | |
|     {
 | |
|         return 0;
 | |
|     }
 | |
|     
 | |
|     int tab_size = io_handler.GetDebugger().GetTabSize();
 | |
| 
 | |
|     lldb::offset_t desired_indent = GetDesiredIndentation(lines,
 | |
|                                                           cursor_position,
 | |
|                                                           tab_size);
 | |
|     
 | |
|     int actual_indent = REPL::CalculateActualIndentation(lines);
 | |
|     
 | |
|     if (desired_indent == LLDB_INVALID_OFFSET)
 | |
|         return 0;
 | |
|     
 | |
|     return (int)desired_indent - actual_indent;
 | |
| }
 | |
| 
 | |
| void
 | |
| REPL::IOHandlerInputComplete (IOHandler &io_handler, std::string &code)
 | |
| {
 | |
|     lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFile());
 | |
|     lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile());
 | |
|     bool extra_line = false;
 | |
|     bool did_quit = false;
 | |
|     
 | |
|     if (code.empty())
 | |
|     {
 | |
|         m_code.AppendString("");
 | |
|         static_cast<IOHandlerEditline &>(io_handler).SetBaseLineNumber(m_code.GetSize()+1);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         Debugger &debugger = m_target.GetDebugger();
 | |
|         CommandInterpreter &ci = debugger.GetCommandInterpreter();
 | |
|         extra_line = ci.GetSpaceReplPrompts();
 | |
|         
 | |
|         ExecutionContext exe_ctx (m_target.GetProcessSP()->GetThreadList().GetSelectedThread()->GetSelectedFrame().get());
 | |
|         
 | |
|         lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
 | |
|         
 | |
|         if (code[0] == ':')
 | |
|         {
 | |
|             // Meta command
 | |
|             // Strip the ':'
 | |
|             code.erase(0, 1);
 | |
|             if (Args::StripSpaces (code))
 | |
|             {
 | |
|                 // "lldb" was followed by arguments, so just execute the command dump the results
 | |
|                 
 | |
|                 // Turn off prompt on quit in case the user types ":quit"
 | |
|                 const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
 | |
|                 if (saved_prompt_on_quit)
 | |
|                     ci.SetPromptOnQuit(false);
 | |
|                 
 | |
|                 // Execute the command
 | |
|                 CommandReturnObject result;
 | |
|                 result.SetImmediateOutputStream(output_sp);
 | |
|                 result.SetImmediateErrorStream(error_sp);
 | |
|                 ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
 | |
|                 
 | |
|                 if (saved_prompt_on_quit)
 | |
|                     ci.SetPromptOnQuit(true);
 | |
|                 
 | |
|                 if (result.GetStatus() == lldb::eReturnStatusQuit)
 | |
|                 {
 | |
|                     did_quit = true;
 | |
|                     io_handler.SetIsDone(true);
 | |
|                     if (debugger.CheckTopIOHandlerTypes(IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter))
 | |
|                     {
 | |
|                         // We typed "quit" or an alias to quit so we need to check if the
 | |
|                         // command interpreter is above us and tell it that it is done as well
 | |
|                         // so we don't drop back into the command interpreter if we have already
 | |
|                         // quit
 | |
|                         lldb::IOHandlerSP io_handler_sp (ci.GetIOHandler());
 | |
|                         if (io_handler_sp)
 | |
|                             io_handler_sp->SetIsDone(true);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // ":" was followed by no arguments, so push the LLDB command prompt
 | |
|                 if (debugger.CheckTopIOHandlerTypes(IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter))
 | |
|                 {
 | |
|                     // If the user wants to get back to the command interpreter and the
 | |
|                     // command interpreter is what launched the REPL, then just let the
 | |
|                     // REPL exit and fall back to the command interpreter.
 | |
|                     io_handler.SetIsDone(true);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // The REPL wasn't launched the by the command interpreter, it is the
 | |
|                     // base IOHandler, so we need to get the command interpreter and
 | |
|                     lldb::IOHandlerSP io_handler_sp (ci.GetIOHandler());
 | |
|                     if (io_handler_sp)
 | |
|                     {
 | |
|                         io_handler_sp->SetIsDone(false);
 | |
|                         debugger.PushIOHandler(ci.GetIOHandler());
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             // Unwind any expression we might have been running in case our REPL
 | |
|             // expression crashed and the user was looking around
 | |
|             if (m_dedicated_repl_mode)
 | |
|             {
 | |
|                 Thread *thread = exe_ctx.GetThreadPtr();
 | |
|                 if (thread && thread->UnwindInnermostExpression().Success())
 | |
|                 {
 | |
|                     thread->SetSelectedFrameByIndex(0, false);
 | |
|                     exe_ctx.SetFrameSP(thread->GetSelectedFrame());
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
 | |
|             
 | |
|             EvaluateExpressionOptions expr_options;
 | |
|             expr_options.SetCoerceToId(m_varobj_options.use_objc);
 | |
|             expr_options.SetUnwindOnError(m_command_options.unwind_on_error);
 | |
|             expr_options.SetIgnoreBreakpoints (m_command_options.ignore_breakpoints);
 | |
|             expr_options.SetKeepInMemory(true);
 | |
|             expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
 | |
|             expr_options.SetTryAllThreads(m_command_options.try_all_threads);
 | |
|             expr_options.SetGenerateDebugInfo(true);
 | |
|             expr_options.SetREPLEnabled (true);
 | |
|             expr_options.SetColorizeErrors(colorize_err);
 | |
|             expr_options.SetPoundLine(m_repl_source_path.c_str(), m_code.GetSize() + 1);
 | |
|             if (m_command_options.timeout > 0)
 | |
|                 expr_options.SetTimeoutUsec(m_command_options.timeout);
 | |
|             else
 | |
|                 expr_options.SetTimeoutUsec(0);
 | |
|             
 | |
|             expr_options.SetLanguage(GetLanguage());
 | |
|             
 | |
|             PersistentExpressionState *persistent_state = m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
 | |
|             
 | |
|             const size_t var_count_before = persistent_state->GetSize();
 | |
|             
 | |
|             const char *expr_prefix = nullptr;
 | |
|             lldb::ValueObjectSP result_valobj_sp;
 | |
|             Error error;
 | |
|             lldb::ModuleSP jit_module_sp;
 | |
|             lldb::ExpressionResults execution_results = UserExpression::Evaluate (exe_ctx,
 | |
|                                                                                   expr_options,
 | |
|                                                                                   code.c_str(),
 | |
|                                                                                   expr_prefix,
 | |
|                                                                                   result_valobj_sp,
 | |
|                                                                                   error,
 | |
|                                                                                   0, // Line offset
 | |
|                                                                                   &jit_module_sp);
 | |
|             
 | |
|             //CommandInterpreter &ci = debugger.GetCommandInterpreter();
 | |
|             
 | |
|             if (process_sp && process_sp->IsAlive())
 | |
|             {
 | |
|                 bool add_to_code = true;
 | |
|                 bool handled = false;
 | |
|                 if (result_valobj_sp)
 | |
|                 {
 | |
|                     lldb::Format format = m_format_options.GetFormat();
 | |
|                     
 | |
|                     if (result_valobj_sp->GetError().Success())
 | |
|                     {
 | |
|                         handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
 | |
|                     }
 | |
|                     else if (result_valobj_sp->GetError().GetError() == UserExpression::kNoResult)
 | |
|                     {
 | |
|                         if (format != lldb::eFormatVoid && debugger.GetNotifyVoid())
 | |
|                         {
 | |
|                             error_sp->PutCString("(void)\n");
 | |
|                             handled = true;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 
 | |
|                 if (debugger.GetPrintDecls())
 | |
|                 {
 | |
|                     for (size_t vi = var_count_before, ve = persistent_state->GetSize();
 | |
|                          vi != ve;
 | |
|                          ++vi)
 | |
|                     {
 | |
|                         lldb::ExpressionVariableSP persistent_var_sp = persistent_state->GetVariableAtIndex(vi);
 | |
|                         lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
 | |
|                         
 | |
|                         PrintOneVariable(debugger, output_sp, valobj_sp, persistent_var_sp.get());
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 if (!handled)
 | |
|                 {
 | |
|                     bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
 | |
|                     switch (execution_results)
 | |
|                     {
 | |
|                         case lldb::eExpressionSetupError:
 | |
|                         case lldb::eExpressionParseError:
 | |
|                             add_to_code = false;
 | |
|                             // Fall through
 | |
|                         case lldb::eExpressionDiscarded:
 | |
|                             error_sp->Printf("%s\n", error.AsCString());
 | |
|                             break;
 | |
|                             
 | |
|                         case lldb::eExpressionCompleted:
 | |
|                             break;
 | |
|                         case lldb::eExpressionInterrupted:
 | |
|                             if (useColors) {
 | |
|                                 error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
 | |
|                                 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
 | |
|                             }
 | |
|                             error_sp->Printf("Execution interrupted. ");
 | |
|                             if (useColors) error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
 | |
|                             error_sp->Printf("Enter code to recover and continue.\nEnter LLDB commands to investigate (type :help for assistance.)\n");
 | |
|                             break;
 | |
|                             
 | |
|                         case lldb::eExpressionHitBreakpoint:
 | |
|                             // Breakpoint was hit, drop into LLDB command interpreter
 | |
|                             if (useColors) {
 | |
|                                 error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
 | |
|                                 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
 | |
|                             }
 | |
|                             output_sp->Printf("Execution stopped at breakpoint.  ");
 | |
|                             if (useColors) error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
 | |
|                             output_sp->Printf("Enter LLDB commands to investigate (type help for assistance.)\n");
 | |
|                         {
 | |
|                             lldb::IOHandlerSP io_handler_sp (ci.GetIOHandler());
 | |
|                             if (io_handler_sp)
 | |
|                             {
 | |
|                                 io_handler_sp->SetIsDone(false);
 | |
|                                 debugger.PushIOHandler(ci.GetIOHandler());
 | |
|                             }
 | |
|                         }
 | |
|                             break;
 | |
|                             
 | |
|                         case lldb::eExpressionTimedOut:
 | |
|                             error_sp->Printf("error: timeout\n");
 | |
|                             if (error.AsCString())
 | |
|                                 error_sp->Printf("error: %s\n", error.AsCString());
 | |
|                             break;
 | |
|                         case lldb::eExpressionResultUnavailable:
 | |
|                             // Shoulnd't happen???
 | |
|                             error_sp->Printf("error: could not fetch result -- %s\n", error.AsCString());
 | |
|                             break;
 | |
|                         case lldb::eExpressionStoppedForDebug:
 | |
|                             // Shoulnd't happen???
 | |
|                             error_sp->Printf("error: stopped for debug -- %s\n", error.AsCString());
 | |
|                             break;
 | |
|                     }
 | |
|                 }
 | |
|                 
 | |
|                 if (add_to_code)
 | |
|                 {
 | |
|                     const uint32_t new_default_line = m_code.GetSize() + 1;
 | |
|                     
 | |
|                     m_code.SplitIntoLines(code);
 | |
|                     
 | |
|                     // Update our code on disk
 | |
|                     if (!m_repl_source_path.empty())
 | |
|                     {
 | |
|                         lldb_private::File file (m_repl_source_path.c_str(),
 | |
|                                                  File::eOpenOptionWrite | File::eOpenOptionTruncate | File::eOpenOptionCanCreate,
 | |
|                                                  lldb::eFilePermissionsFileDefault);
 | |
|                         std::string code (m_code.CopyList());
 | |
|                         code.append(1, '\n');
 | |
|                         size_t bytes_written = code.size();
 | |
|                         file.Write(code.c_str(), bytes_written);
 | |
|                         file.Close();
 | |
|                         
 | |
|                         // Now set the default file and line to the REPL source file
 | |
|                         m_target.GetSourceManager().SetDefaultFileAndLine(FileSpec(m_repl_source_path.c_str(), false), new_default_line);
 | |
|                     }
 | |
|                     static_cast<IOHandlerEditline &>(io_handler).SetBaseLineNumber(m_code.GetSize()+1);
 | |
|                 }
 | |
|                 if (extra_line)
 | |
|                 {
 | |
|                     fprintf(output_sp->GetFile().GetStream(), "\n");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Don't complain about the REPL process going away if we are in the process of quitting.
 | |
|         if (!did_quit && (!process_sp || !process_sp->IsAlive()))
 | |
|         {
 | |
|             error_sp->Printf("error: REPL process is no longer alive, exiting REPL\n");
 | |
|             io_handler.SetIsDone(true);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| int
 | |
| REPL::IOHandlerComplete (IOHandler &io_handler,
 | |
|                          const char *current_line,
 | |
|                          const char *cursor,
 | |
|                          const char *last_char,
 | |
|                          int skip_first_n_matches,
 | |
|                          int max_matches,
 | |
|                          StringList &matches)
 | |
| {
 | |
|     matches.Clear();
 | |
|     
 | |
|     llvm::StringRef line (current_line, cursor - current_line);
 | |
|     
 | |
|     // Complete an LLDB command if the first character is a colon...
 | |
|     if (!line.empty() && line[0] == ':')
 | |
|     {
 | |
|         Debugger &debugger = m_target.GetDebugger();
 | |
|         
 | |
|         // auto complete LLDB commands
 | |
|         const char *lldb_current_line = line.substr(1).data();
 | |
|         return debugger.GetCommandInterpreter().HandleCompletion (lldb_current_line,
 | |
|                                                                   cursor,
 | |
|                                                                   last_char,
 | |
|                                                                   skip_first_n_matches,
 | |
|                                                                   max_matches,
 | |
|                                                                   matches);
 | |
|     }
 | |
|     
 | |
|     // Strip spaces from the line and see if we had only spaces
 | |
|     line = line.ltrim();
 | |
|     if (line.empty())
 | |
|     {
 | |
|         // Only spaces on this line, so just indent
 | |
|         matches.AppendString(m_indent_str);
 | |
|         return 1;
 | |
|     }
 | |
|     
 | |
|     std::string current_code;
 | |
|     current_code.append(m_code.CopyList());
 | |
|     
 | |
|     IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
 | |
|     const StringList *current_lines = editline.GetCurrentLines();
 | |
|     if (current_lines)
 | |
|     {
 | |
|         const uint32_t current_line_idx = editline.GetCurrentLineIndex();
 | |
|         
 | |
|         if (current_line_idx < current_lines->GetSize())
 | |
|         {
 | |
|             for (uint32_t i=0; i<current_line_idx; ++i)
 | |
|             {
 | |
|                 const char *line_cstr = current_lines->GetStringAtIndex(i);
 | |
|                 if (line_cstr)
 | |
|                 {
 | |
|                     current_code.append("\n");
 | |
|                     current_code.append (line_cstr);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     if (cursor > current_line)
 | |
|     {
 | |
|         current_code.append("\n");
 | |
|         current_code.append(current_line, cursor - current_line);
 | |
|     }
 | |
|     
 | |
|     return CompleteCode(current_code, matches);
 | |
| }
 | |
| 
 | |
| bool
 | |
| QuitCommandOverrideCallback(void *baton, const char **argv)
 | |
| {
 | |
|     Target *target = (Target *)baton;
 | |
|     lldb::ProcessSP process_sp (target->GetProcessSP());
 | |
|     if (process_sp)
 | |
|     {
 | |
|         process_sp->Destroy(false);
 | |
|         process_sp->GetTarget().GetDebugger().ClearIOHandlers();
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| Error
 | |
| REPL::RunLoop ()
 | |
| {
 | |
|     Error error;
 | |
|     
 | |
|     error = DoInitialization();
 | |
|     m_repl_source_path = GetSourcePath();
 | |
|     
 | |
|     if (!error.Success())
 | |
|         return error;
 | |
|     
 | |
|     Debugger &debugger = m_target.GetDebugger();
 | |
|     
 | |
|     lldb::IOHandlerSP io_handler_sp (GetIOHandler());
 | |
|     
 | |
|     FileSpec save_default_file;
 | |
|     uint32_t save_default_line = 0;
 | |
|     
 | |
|     if (!m_repl_source_path.empty())
 | |
|     {
 | |
|         // Save the current default file and line
 | |
|         m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file, save_default_line);
 | |
|     }
 | |
|     
 | |
|     debugger.PushIOHandler(io_handler_sp);
 | |
|     
 | |
|     // Check if we are in dedicated REPL mode where LLDB was start with the "--repl" option
 | |
|     // from the command line. Currently we know this by checking if the debugger already
 | |
|     // has a IOHandler thread.
 | |
|     if (!debugger.HasIOHandlerThread())
 | |
|     {
 | |
|         // The debugger doesn't have an existing IOHandler thread, so this must be
 | |
|         // dedicated REPL mode...
 | |
|         m_dedicated_repl_mode = true;
 | |
|         debugger.StartIOHandlerThread();
 | |
|         std::string command_name_str ("quit");
 | |
|         CommandObject *cmd_obj = debugger.GetCommandInterpreter().GetCommandObjectForCommand(command_name_str);
 | |
|         if (cmd_obj)
 | |
|         {
 | |
|             assert(command_name_str.empty());
 | |
|             cmd_obj->SetOverrideCallback (QuitCommandOverrideCallback, &m_target);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     // Wait for the REPL command interpreter to get popped
 | |
|     io_handler_sp->WaitForPop();
 | |
|     
 | |
|     if (m_dedicated_repl_mode)
 | |
|     {
 | |
|         // If we were in dedicated REPL mode we would have started the
 | |
|         // IOHandler thread, and we should kill our process
 | |
|         lldb::ProcessSP process_sp = m_target.GetProcessSP();
 | |
|         if (process_sp && process_sp->IsAlive())
 | |
|             process_sp->Destroy(false);
 | |
|         
 | |
|         // Wait for the IO handler thread to exit (TODO: don't do this if the IO handler thread already exists...)
 | |
|         debugger.JoinIOHandlerThread();
 | |
|     }
 | |
|     
 | |
|     // Restore the default file and line
 | |
|     if (save_default_file && save_default_line != 0)
 | |
|         m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file, save_default_line);
 | |
|     return error;
 | |
| }
 |