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