diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:26:05 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-01-02 19:26:05 +0000 |
commit | 14f1b3e8826ce43b978db93a62d1166055db5394 (patch) | |
tree | 0a00ad8d3498783fe0193f3b656bca17c4c8697d /source/Expression/REPL.cpp | |
parent | 4ee8c119c71a06dcad1e0fecc8c675e480e59337 (diff) | |
download | src-14f1b3e8826ce43b978db93a62d1166055db5394.tar.gz src-14f1b3e8826ce43b978db93a62d1166055db5394.zip |
Vendor import of lldb trunk r290819:vendor/lldb/lldb-trunk-r290819
Notes
Notes:
svn path=/vendor/lldb/dist/; revision=311128
svn path=/vendor/lldb/lldb-trunk-r290819/; revision=311129; tag=vendor/lldb/lldb-trunk-r290819
Diffstat (limited to 'source/Expression/REPL.cpp')
-rw-r--r-- | source/Expression/REPL.cpp | 1083 |
1 files changed, 509 insertions, 574 deletions
diff --git a/source/Expression/REPL.cpp b/source/Expression/REPL.cpp index 30f256002fc8..e404537562b7 100644 --- a/source/Expression/REPL.cpp +++ b/source/Expression/REPL.cpp @@ -11,11 +11,11 @@ // C++ Includes // Other libraries and framework includes // Project includes +#include "lldb/Expression/REPL.h" #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" @@ -26,625 +26,560 @@ 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(LLVMCastKind kind, Target &target) : m_target(target), m_kind(kind) { + // Make sure all option values have sane defaults + Debugger &debugger = m_target.GetDebugger(); + auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext(); + m_format_options.OptionParsingStarting(&exe_ctx); + m_varobj_options.OptionParsingStarting(&exe_ctx); + m_command_options.OptionParsingStarting(&exe_ctx); + + // 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; - } +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; + } + + 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(); +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; - } - +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 + llvm::StringRef("> "), // prompt + llvm::StringRef(". "), // 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; + } + 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); +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; -} +bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; } -void -REPL::IOHandlerInputInterrupted (IOHandler &io_handler, - std::string &line) -{ +void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) { } -const char * -REPL::IOHandlerGetFixIndentationCharacters() -{ - return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr); +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(); +ConstString REPL::IOHandlerGetControlSequence(char ch) { + if (ch == 'd') + return ConstString(":quit\n"); + return ConstString(); } -const char * -REPL::IOHandlerGetCommandPrefix () -{ - return ":"; -} +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"; +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); +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 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 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; +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()); - } - } - } +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 - { - // 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 - nullptr, // Fixed Expression - &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; - LLVM_FALLTHROUGH; - 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"); - } + } 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.SetTimeout(std::chrono::microseconds(m_command_options.timeout)); + else + expr_options.SetTimeout(llvm::None); + + 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 + nullptr, // Fixed Expression + &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; } + } } - - // 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); + + 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()); + } } - } -} -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) + if (!handled) { + bool useColors = error_sp->GetFile().GetIsTerminalWithColors(); + switch (execution_results) { + case lldb::eExpressionSetupError: + case lldb::eExpressionParseError: + add_to_code = false; + LLVM_FALLTHROUGH; + 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"); { - const char *line_cstr = current_lines->GetStringAtIndex(i); - if (line_cstr) - { - current_code.append("\n"); - current_code.append (line_cstr); - } + 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, false), new_default_line); + } + static_cast<IOHandlerEditline &>(io_handler) + .SetBaseLineNumber(m_code.GetSize() + 1); + } + if (extra_line) { + fprintf(output_sp->GetFile().GetStream(), "\n"); + } + } } - - 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(); + // 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); } - return false; + } } -Error -REPL::RunLoop () -{ - Error error; - - error = DoInitialization(); - m_repl_source_path = GetSourcePath(); - - if (!error.Success()) - return error; - +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(); - - 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); + + // 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); } + } } - - // 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); + } + + 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(); + llvm::StringRef 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; } |