15ffd83dbSDimitry Andric //===-- REPL.cpp ----------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "lldb/Expression/REPL.h"
100b57cec5SDimitry Andric #include "lldb/Core/Debugger.h"
110b57cec5SDimitry Andric #include "lldb/Core/PluginManager.h"
120b57cec5SDimitry Andric #include "lldb/Expression/ExpressionVariable.h"
130b57cec5SDimitry Andric #include "lldb/Expression/UserExpression.h"
140b57cec5SDimitry Andric #include "lldb/Host/HostInfo.h"
15*5f757f3fSDimitry Andric #include "lldb/Host/StreamFile.h"
160b57cec5SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
170b57cec5SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h"
180b57cec5SDimitry Andric #include "lldb/Target/Thread.h"
190b57cec5SDimitry Andric #include "lldb/Utility/AnsiTerminal.h"
200b57cec5SDimitry Andric
210b57cec5SDimitry Andric #include <memory>
220b57cec5SDimitry Andric
230b57cec5SDimitry Andric using namespace lldb_private;
240b57cec5SDimitry Andric
2506c3fb27SDimitry Andric char REPL::ID;
2606c3fb27SDimitry Andric
REPL(Target & target)2706c3fb27SDimitry Andric REPL::REPL(Target &target) : m_target(target) {
280b57cec5SDimitry Andric // Make sure all option values have sane defaults
290b57cec5SDimitry Andric Debugger &debugger = m_target.GetDebugger();
3081ad6265SDimitry Andric debugger.SetShowProgress(false);
310b57cec5SDimitry Andric auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext();
320b57cec5SDimitry Andric m_format_options.OptionParsingStarting(&exe_ctx);
330b57cec5SDimitry Andric m_varobj_options.OptionParsingStarting(&exe_ctx);
340b57cec5SDimitry Andric }
350b57cec5SDimitry Andric
360b57cec5SDimitry Andric REPL::~REPL() = default;
370b57cec5SDimitry Andric
Create(Status & err,lldb::LanguageType language,Debugger * debugger,Target * target,const char * repl_options)380b57cec5SDimitry Andric lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language,
390b57cec5SDimitry Andric Debugger *debugger, Target *target,
400b57cec5SDimitry Andric const char *repl_options) {
410b57cec5SDimitry Andric uint32_t idx = 0;
420b57cec5SDimitry Andric lldb::REPLSP ret;
430b57cec5SDimitry Andric
440b57cec5SDimitry Andric while (REPLCreateInstance create_instance =
450eae32dcSDimitry Andric PluginManager::GetREPLCreateCallbackAtIndex(idx)) {
460eae32dcSDimitry Andric LanguageSet supported_languages =
470eae32dcSDimitry Andric PluginManager::GetREPLSupportedLanguagesAtIndex(idx++);
480eae32dcSDimitry Andric if (!supported_languages[language])
490eae32dcSDimitry Andric continue;
500b57cec5SDimitry Andric ret = (*create_instance)(err, language, debugger, target, repl_options);
510b57cec5SDimitry Andric if (ret) {
520b57cec5SDimitry Andric break;
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric }
550b57cec5SDimitry Andric
560b57cec5SDimitry Andric return ret;
570b57cec5SDimitry Andric }
580b57cec5SDimitry Andric
GetSourcePath()590b57cec5SDimitry Andric std::string REPL::GetSourcePath() {
6006c3fb27SDimitry Andric llvm::StringRef file_basename = GetSourceFileBasename();
610b57cec5SDimitry Andric FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
620b57cec5SDimitry Andric if (tmpdir_file_spec) {
63bdd1243dSDimitry Andric tmpdir_file_spec.SetFilename(file_basename);
640b57cec5SDimitry Andric m_repl_source_path = tmpdir_file_spec.GetPath();
650b57cec5SDimitry Andric } else {
660b57cec5SDimitry Andric tmpdir_file_spec = FileSpec("/tmp");
6706c3fb27SDimitry Andric tmpdir_file_spec.AppendPathComponent(file_basename);
680b57cec5SDimitry Andric }
690b57cec5SDimitry Andric
700b57cec5SDimitry Andric return tmpdir_file_spec.GetPath();
710b57cec5SDimitry Andric }
720b57cec5SDimitry Andric
GetIOHandler()730b57cec5SDimitry Andric lldb::IOHandlerSP REPL::GetIOHandler() {
740b57cec5SDimitry Andric if (!m_io_handler_sp) {
750b57cec5SDimitry Andric Debugger &debugger = m_target.GetDebugger();
760b57cec5SDimitry Andric m_io_handler_sp = std::make_shared<IOHandlerEditline>(
770b57cec5SDimitry Andric debugger, IOHandler::Type::REPL,
780b57cec5SDimitry Andric "lldb-repl", // Name of input reader for history
790b57cec5SDimitry Andric llvm::StringRef("> "), // prompt
800b57cec5SDimitry Andric llvm::StringRef(". "), // Continuation prompt
810b57cec5SDimitry Andric true, // Multi-line
820b57cec5SDimitry Andric true, // The REPL prompt is always colored
830b57cec5SDimitry Andric 1, // Line number
84bdd1243dSDimitry Andric *this);
850b57cec5SDimitry Andric
860b57cec5SDimitry Andric // Don't exit if CTRL+C is pressed
870b57cec5SDimitry Andric static_cast<IOHandlerEditline *>(m_io_handler_sp.get())
880b57cec5SDimitry Andric ->SetInterruptExits(false);
890b57cec5SDimitry Andric
900b57cec5SDimitry Andric if (m_io_handler_sp->GetIsInteractive() &&
910b57cec5SDimitry Andric m_io_handler_sp->GetIsRealTerminal()) {
920b57cec5SDimitry Andric m_indent_str.assign(debugger.GetTabSize(), ' ');
930b57cec5SDimitry Andric m_enable_auto_indent = debugger.GetAutoIndent();
940b57cec5SDimitry Andric } else {
950b57cec5SDimitry Andric m_indent_str.clear();
960b57cec5SDimitry Andric m_enable_auto_indent = false;
970b57cec5SDimitry Andric }
980b57cec5SDimitry Andric }
990b57cec5SDimitry Andric return m_io_handler_sp;
1000b57cec5SDimitry Andric }
1010b57cec5SDimitry Andric
IOHandlerActivated(IOHandler & io_handler,bool interactive)1020b57cec5SDimitry Andric void REPL::IOHandlerActivated(IOHandler &io_handler, bool interactive) {
1030b57cec5SDimitry Andric lldb::ProcessSP process_sp = m_target.GetProcessSP();
1040b57cec5SDimitry Andric if (process_sp && process_sp->IsAlive())
1050b57cec5SDimitry Andric return;
1069dba64beSDimitry Andric lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
1070b57cec5SDimitry Andric error_sp->Printf("REPL requires a running target process.\n");
1080b57cec5SDimitry Andric io_handler.SetIsDone(true);
1090b57cec5SDimitry Andric }
1100b57cec5SDimitry Andric
IOHandlerInterrupt(IOHandler & io_handler)1110b57cec5SDimitry Andric bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; }
1120b57cec5SDimitry Andric
IOHandlerInputInterrupted(IOHandler & io_handler,std::string & line)1130b57cec5SDimitry Andric void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) {
1140b57cec5SDimitry Andric }
1150b57cec5SDimitry Andric
IOHandlerGetFixIndentationCharacters()1160b57cec5SDimitry Andric const char *REPL::IOHandlerGetFixIndentationCharacters() {
1170b57cec5SDimitry Andric return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr);
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric
IOHandlerGetControlSequence(char ch)12006c3fb27SDimitry Andric llvm::StringRef REPL::IOHandlerGetControlSequence(char ch) {
12106c3fb27SDimitry Andric static constexpr llvm::StringLiteral control_sequence(":quit\n");
1220b57cec5SDimitry Andric if (ch == 'd')
12306c3fb27SDimitry Andric return control_sequence;
12406c3fb27SDimitry Andric return {};
1250b57cec5SDimitry Andric }
1260b57cec5SDimitry Andric
IOHandlerGetCommandPrefix()1270b57cec5SDimitry Andric const char *REPL::IOHandlerGetCommandPrefix() { return ":"; }
1280b57cec5SDimitry Andric
IOHandlerGetHelpPrologue()1290b57cec5SDimitry Andric const char *REPL::IOHandlerGetHelpPrologue() {
1300b57cec5SDimitry Andric return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter. "
1310b57cec5SDimitry Andric "Valid statements, expressions, and declarations are immediately "
1320b57cec5SDimitry Andric "compiled and executed.\n\n"
1330b57cec5SDimitry Andric "The complete set of LLDB debugging commands are also available as "
134e8d8bef9SDimitry Andric "described below.\n\nCommands "
1350b57cec5SDimitry Andric "must be prefixed with a colon at the REPL prompt (:quit for "
1360b57cec5SDimitry Andric "example.) Typing just a colon "
137e8d8bef9SDimitry Andric "followed by return will switch to the LLDB prompt.\n\n"
138e8d8bef9SDimitry Andric "Type “< path” to read in code from a text file “path”.\n\n";
1390b57cec5SDimitry Andric }
1400b57cec5SDimitry Andric
IOHandlerIsInputComplete(IOHandler & io_handler,StringList & lines)1410b57cec5SDimitry Andric bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) {
1420b57cec5SDimitry Andric // Check for meta command
1430b57cec5SDimitry Andric const size_t num_lines = lines.GetSize();
1440b57cec5SDimitry Andric if (num_lines == 1) {
1450b57cec5SDimitry Andric const char *first_line = lines.GetStringAtIndex(0);
1460b57cec5SDimitry Andric if (first_line[0] == ':')
1470b57cec5SDimitry Andric return true; // Meta command is a single line where that starts with ':'
1480b57cec5SDimitry Andric }
1490b57cec5SDimitry Andric
1500b57cec5SDimitry Andric // Check if REPL input is done
1510b57cec5SDimitry Andric std::string source_string(lines.CopyList());
1520b57cec5SDimitry Andric return SourceIsComplete(source_string);
1530b57cec5SDimitry Andric }
1540b57cec5SDimitry Andric
CalculateActualIndentation(const StringList & lines)1550b57cec5SDimitry Andric int REPL::CalculateActualIndentation(const StringList &lines) {
1560b57cec5SDimitry Andric std::string last_line = lines[lines.GetSize() - 1];
1570b57cec5SDimitry Andric
1580b57cec5SDimitry Andric int actual_indent = 0;
1590b57cec5SDimitry Andric for (char &ch : last_line) {
1600b57cec5SDimitry Andric if (ch != ' ')
1610b57cec5SDimitry Andric break;
1620b57cec5SDimitry Andric ++actual_indent;
1630b57cec5SDimitry Andric }
1640b57cec5SDimitry Andric
1650b57cec5SDimitry Andric return actual_indent;
1660b57cec5SDimitry Andric }
1670b57cec5SDimitry Andric
IOHandlerFixIndentation(IOHandler & io_handler,const StringList & lines,int cursor_position)1680b57cec5SDimitry Andric int REPL::IOHandlerFixIndentation(IOHandler &io_handler,
1690b57cec5SDimitry Andric const StringList &lines,
1700b57cec5SDimitry Andric int cursor_position) {
1710b57cec5SDimitry Andric if (!m_enable_auto_indent)
1720b57cec5SDimitry Andric return 0;
1730b57cec5SDimitry Andric
1740b57cec5SDimitry Andric if (!lines.GetSize()) {
1750b57cec5SDimitry Andric return 0;
1760b57cec5SDimitry Andric }
1770b57cec5SDimitry Andric
1780b57cec5SDimitry Andric int tab_size = io_handler.GetDebugger().GetTabSize();
1790b57cec5SDimitry Andric
1800b57cec5SDimitry Andric lldb::offset_t desired_indent =
1810b57cec5SDimitry Andric GetDesiredIndentation(lines, cursor_position, tab_size);
1820b57cec5SDimitry Andric
1830b57cec5SDimitry Andric int actual_indent = REPL::CalculateActualIndentation(lines);
1840b57cec5SDimitry Andric
1850b57cec5SDimitry Andric if (desired_indent == LLDB_INVALID_OFFSET)
1860b57cec5SDimitry Andric return 0;
1870b57cec5SDimitry Andric
1880b57cec5SDimitry Andric return (int)desired_indent - actual_indent;
1890b57cec5SDimitry Andric }
1900b57cec5SDimitry Andric
ReadCode(const std::string & path,std::string & code,lldb::StreamFileSP & error_sp)191e8d8bef9SDimitry Andric static bool ReadCode(const std::string &path, std::string &code,
192e8d8bef9SDimitry Andric lldb::StreamFileSP &error_sp) {
193e8d8bef9SDimitry Andric auto &fs = FileSystem::Instance();
194e8d8bef9SDimitry Andric llvm::Twine pathTwine(path);
195e8d8bef9SDimitry Andric if (!fs.Exists(pathTwine)) {
196e8d8bef9SDimitry Andric error_sp->Printf("no such file at path '%s'\n", path.c_str());
197e8d8bef9SDimitry Andric return false;
198e8d8bef9SDimitry Andric }
199e8d8bef9SDimitry Andric if (!fs.Readable(pathTwine)) {
200e8d8bef9SDimitry Andric error_sp->Printf("could not read file at path '%s'\n", path.c_str());
201e8d8bef9SDimitry Andric return false;
202e8d8bef9SDimitry Andric }
203e8d8bef9SDimitry Andric const size_t file_size = fs.GetByteSize(pathTwine);
204e8d8bef9SDimitry Andric const size_t max_size = code.max_size();
205e8d8bef9SDimitry Andric if (file_size > max_size) {
206e8d8bef9SDimitry Andric error_sp->Printf("file at path '%s' too large: "
207e8d8bef9SDimitry Andric "file_size = %zu, max_size = %zu\n",
208e8d8bef9SDimitry Andric path.c_str(), file_size, max_size);
209e8d8bef9SDimitry Andric return false;
210e8d8bef9SDimitry Andric }
211e8d8bef9SDimitry Andric auto data_sp = fs.CreateDataBuffer(pathTwine);
212e8d8bef9SDimitry Andric if (data_sp == nullptr) {
213e8d8bef9SDimitry Andric error_sp->Printf("could not create buffer for file at path '%s'\n",
214e8d8bef9SDimitry Andric path.c_str());
215e8d8bef9SDimitry Andric return false;
216e8d8bef9SDimitry Andric }
217e8d8bef9SDimitry Andric code.assign((const char *)data_sp->GetBytes(), data_sp->GetByteSize());
218e8d8bef9SDimitry Andric return true;
219e8d8bef9SDimitry Andric }
220e8d8bef9SDimitry Andric
IOHandlerInputComplete(IOHandler & io_handler,std::string & code)2210b57cec5SDimitry Andric void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {
2229dba64beSDimitry Andric lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
2239dba64beSDimitry Andric lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFileSP());
2240b57cec5SDimitry Andric bool extra_line = false;
2250b57cec5SDimitry Andric bool did_quit = false;
2260b57cec5SDimitry Andric
2270b57cec5SDimitry Andric if (code.empty()) {
2280b57cec5SDimitry Andric m_code.AppendString("");
2290b57cec5SDimitry Andric static_cast<IOHandlerEditline &>(io_handler)
2300b57cec5SDimitry Andric .SetBaseLineNumber(m_code.GetSize() + 1);
2310b57cec5SDimitry Andric } else {
2320b57cec5SDimitry Andric Debugger &debugger = m_target.GetDebugger();
2330b57cec5SDimitry Andric CommandInterpreter &ci = debugger.GetCommandInterpreter();
2340b57cec5SDimitry Andric extra_line = ci.GetSpaceReplPrompts();
2350b57cec5SDimitry Andric
2360b57cec5SDimitry Andric ExecutionContext exe_ctx(m_target.GetProcessSP()
2370b57cec5SDimitry Andric ->GetThreadList()
2380b57cec5SDimitry Andric .GetSelectedThread()
23906c3fb27SDimitry Andric ->GetSelectedFrame(DoNoSelectMostRelevantFrame)
2400b57cec5SDimitry Andric .get());
2410b57cec5SDimitry Andric
2420b57cec5SDimitry Andric lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
2430b57cec5SDimitry Andric
2440b57cec5SDimitry Andric if (code[0] == ':') {
2450b57cec5SDimitry Andric // Meta command
2460b57cec5SDimitry Andric // Strip the ':'
2470b57cec5SDimitry Andric code.erase(0, 1);
2489dba64beSDimitry Andric if (!llvm::StringRef(code).trim().empty()) {
2490b57cec5SDimitry Andric // "lldb" was followed by arguments, so just execute the command dump
2500b57cec5SDimitry Andric // the results
2510b57cec5SDimitry Andric
2520b57cec5SDimitry Andric // Turn off prompt on quit in case the user types ":quit"
2530b57cec5SDimitry Andric const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
2540b57cec5SDimitry Andric if (saved_prompt_on_quit)
2550b57cec5SDimitry Andric ci.SetPromptOnQuit(false);
2560b57cec5SDimitry Andric
2570b57cec5SDimitry Andric // Execute the command
2585ffd83dbSDimitry Andric CommandReturnObject result(debugger.GetUseColor());
2590b57cec5SDimitry Andric result.SetImmediateOutputStream(output_sp);
2600b57cec5SDimitry Andric result.SetImmediateErrorStream(error_sp);
2610b57cec5SDimitry Andric ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
2620b57cec5SDimitry Andric
2630b57cec5SDimitry Andric if (saved_prompt_on_quit)
2640b57cec5SDimitry Andric ci.SetPromptOnQuit(true);
2650b57cec5SDimitry Andric
2660b57cec5SDimitry Andric if (result.GetStatus() == lldb::eReturnStatusQuit) {
2670b57cec5SDimitry Andric did_quit = true;
2680b57cec5SDimitry Andric io_handler.SetIsDone(true);
2690b57cec5SDimitry Andric if (debugger.CheckTopIOHandlerTypes(
2700b57cec5SDimitry Andric IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
2710b57cec5SDimitry Andric // We typed "quit" or an alias to quit so we need to check if the
2720b57cec5SDimitry Andric // command interpreter is above us and tell it that it is done as
2730b57cec5SDimitry Andric // well so we don't drop back into the command interpreter if we
2740b57cec5SDimitry Andric // have already quit
2750b57cec5SDimitry Andric lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
2760b57cec5SDimitry Andric if (io_handler_sp)
2770b57cec5SDimitry Andric io_handler_sp->SetIsDone(true);
2780b57cec5SDimitry Andric }
2790b57cec5SDimitry Andric }
2800b57cec5SDimitry Andric } else {
2810b57cec5SDimitry Andric // ":" was followed by no arguments, so push the LLDB command prompt
2820b57cec5SDimitry Andric if (debugger.CheckTopIOHandlerTypes(
2830b57cec5SDimitry Andric IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
2840b57cec5SDimitry Andric // If the user wants to get back to the command interpreter and the
2850b57cec5SDimitry Andric // command interpreter is what launched the REPL, then just let the
2860b57cec5SDimitry Andric // REPL exit and fall back to the command interpreter.
2870b57cec5SDimitry Andric io_handler.SetIsDone(true);
2880b57cec5SDimitry Andric } else {
2890b57cec5SDimitry Andric // The REPL wasn't launched the by the command interpreter, it is the
2900b57cec5SDimitry Andric // base IOHandler, so we need to get the command interpreter and
2910b57cec5SDimitry Andric lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
2920b57cec5SDimitry Andric if (io_handler_sp) {
2930b57cec5SDimitry Andric io_handler_sp->SetIsDone(false);
2945ffd83dbSDimitry Andric debugger.RunIOHandlerAsync(ci.GetIOHandler());
2950b57cec5SDimitry Andric }
2960b57cec5SDimitry Andric }
2970b57cec5SDimitry Andric }
2980b57cec5SDimitry Andric } else {
299e8d8bef9SDimitry Andric if (code[0] == '<') {
300e8d8bef9SDimitry Andric // User wants to read code from a file.
301e8d8bef9SDimitry Andric // Interpret rest of line as a literal path.
302e8d8bef9SDimitry Andric auto path = llvm::StringRef(code.substr(1)).trim().str();
303e8d8bef9SDimitry Andric if (!ReadCode(path, code, error_sp)) {
304e8d8bef9SDimitry Andric return;
305e8d8bef9SDimitry Andric }
306e8d8bef9SDimitry Andric }
307e8d8bef9SDimitry Andric
3080b57cec5SDimitry Andric // Unwind any expression we might have been running in case our REPL
3090b57cec5SDimitry Andric // expression crashed and the user was looking around
3100b57cec5SDimitry Andric if (m_dedicated_repl_mode) {
3110b57cec5SDimitry Andric Thread *thread = exe_ctx.GetThreadPtr();
3120b57cec5SDimitry Andric if (thread && thread->UnwindInnermostExpression().Success()) {
3130b57cec5SDimitry Andric thread->SetSelectedFrameByIndex(0, false);
31406c3fb27SDimitry Andric exe_ctx.SetFrameSP(
31506c3fb27SDimitry Andric thread->GetSelectedFrame(DoNoSelectMostRelevantFrame));
3160b57cec5SDimitry Andric }
3170b57cec5SDimitry Andric }
3180b57cec5SDimitry Andric
3190b57cec5SDimitry Andric const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
3200b57cec5SDimitry Andric
3210b57cec5SDimitry Andric EvaluateExpressionOptions expr_options = m_expr_options;
3220b57cec5SDimitry Andric expr_options.SetCoerceToId(m_varobj_options.use_objc);
3230b57cec5SDimitry Andric expr_options.SetKeepInMemory(true);
3240b57cec5SDimitry Andric expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
3250b57cec5SDimitry Andric expr_options.SetGenerateDebugInfo(true);
3260b57cec5SDimitry Andric expr_options.SetREPLEnabled(true);
3270b57cec5SDimitry Andric expr_options.SetColorizeErrors(colorize_err);
3280b57cec5SDimitry Andric expr_options.SetPoundLine(m_repl_source_path.c_str(),
3290b57cec5SDimitry Andric m_code.GetSize() + 1);
3300b57cec5SDimitry Andric
3310b57cec5SDimitry Andric expr_options.SetLanguage(GetLanguage());
3320b57cec5SDimitry Andric
3330b57cec5SDimitry Andric PersistentExpressionState *persistent_state =
3340b57cec5SDimitry Andric m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
335480093f4SDimitry Andric if (!persistent_state)
336480093f4SDimitry Andric return;
3370b57cec5SDimitry Andric
3380b57cec5SDimitry Andric const size_t var_count_before = persistent_state->GetSize();
3390b57cec5SDimitry Andric
3400b57cec5SDimitry Andric const char *expr_prefix = nullptr;
3410b57cec5SDimitry Andric lldb::ValueObjectSP result_valobj_sp;
3420b57cec5SDimitry Andric Status error;
3430b57cec5SDimitry Andric lldb::ExpressionResults execution_results =
3440b57cec5SDimitry Andric UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(),
3450b57cec5SDimitry Andric expr_prefix, result_valobj_sp, error,
3465ffd83dbSDimitry Andric nullptr); // fixed expression
3470b57cec5SDimitry Andric
34806c3fb27SDimitry Andric if (llvm::Error err = OnExpressionEvaluated(exe_ctx, code, expr_options,
34906c3fb27SDimitry Andric execution_results,
35006c3fb27SDimitry Andric result_valobj_sp, error)) {
35106c3fb27SDimitry Andric *error_sp << llvm::toString(std::move(err)) << "\n";
35206c3fb27SDimitry Andric } else if (process_sp && process_sp->IsAlive()) {
3530b57cec5SDimitry Andric bool add_to_code = true;
3540b57cec5SDimitry Andric bool handled = false;
3550b57cec5SDimitry Andric if (result_valobj_sp) {
3560b57cec5SDimitry Andric lldb::Format format = m_format_options.GetFormat();
3570b57cec5SDimitry Andric
3580b57cec5SDimitry Andric if (result_valobj_sp->GetError().Success()) {
3590b57cec5SDimitry Andric handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
3600b57cec5SDimitry Andric } else if (result_valobj_sp->GetError().GetError() ==
3610b57cec5SDimitry Andric UserExpression::kNoResult) {
3620b57cec5SDimitry Andric if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) {
3630b57cec5SDimitry Andric error_sp->PutCString("(void)\n");
3640b57cec5SDimitry Andric handled = true;
3650b57cec5SDimitry Andric }
3660b57cec5SDimitry Andric }
3670b57cec5SDimitry Andric }
3680b57cec5SDimitry Andric
3690b57cec5SDimitry Andric if (debugger.GetPrintDecls()) {
3700b57cec5SDimitry Andric for (size_t vi = var_count_before, ve = persistent_state->GetSize();
3710b57cec5SDimitry Andric vi != ve; ++vi) {
3720b57cec5SDimitry Andric lldb::ExpressionVariableSP persistent_var_sp =
3730b57cec5SDimitry Andric persistent_state->GetVariableAtIndex(vi);
3740b57cec5SDimitry Andric lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
3750b57cec5SDimitry Andric
3760b57cec5SDimitry Andric PrintOneVariable(debugger, output_sp, valobj_sp,
3770b57cec5SDimitry Andric persistent_var_sp.get());
3780b57cec5SDimitry Andric }
3790b57cec5SDimitry Andric }
3800b57cec5SDimitry Andric
3810b57cec5SDimitry Andric if (!handled) {
3820b57cec5SDimitry Andric bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
3830b57cec5SDimitry Andric switch (execution_results) {
3840b57cec5SDimitry Andric case lldb::eExpressionSetupError:
3850b57cec5SDimitry Andric case lldb::eExpressionParseError:
3860b57cec5SDimitry Andric add_to_code = false;
387bdd1243dSDimitry Andric [[fallthrough]];
3880b57cec5SDimitry Andric case lldb::eExpressionDiscarded:
3890b57cec5SDimitry Andric error_sp->Printf("%s\n", error.AsCString());
3900b57cec5SDimitry Andric break;
3910b57cec5SDimitry Andric
3920b57cec5SDimitry Andric case lldb::eExpressionCompleted:
3930b57cec5SDimitry Andric break;
3940b57cec5SDimitry Andric case lldb::eExpressionInterrupted:
3950b57cec5SDimitry Andric if (useColors) {
3960b57cec5SDimitry Andric error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
3970b57cec5SDimitry Andric error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
3980b57cec5SDimitry Andric }
3990b57cec5SDimitry Andric error_sp->Printf("Execution interrupted. ");
4000b57cec5SDimitry Andric if (useColors)
4010b57cec5SDimitry Andric error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
4020b57cec5SDimitry Andric error_sp->Printf("Enter code to recover and continue.\nEnter LLDB "
4030b57cec5SDimitry Andric "commands to investigate (type :help for "
4040b57cec5SDimitry Andric "assistance.)\n");
4050b57cec5SDimitry Andric break;
4060b57cec5SDimitry Andric
4070b57cec5SDimitry Andric case lldb::eExpressionHitBreakpoint:
4080b57cec5SDimitry Andric // Breakpoint was hit, drop into LLDB command interpreter
4090b57cec5SDimitry Andric if (useColors) {
4100b57cec5SDimitry Andric error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
4110b57cec5SDimitry Andric error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
4120b57cec5SDimitry Andric }
4130b57cec5SDimitry Andric output_sp->Printf("Execution stopped at breakpoint. ");
4140b57cec5SDimitry Andric if (useColors)
4150b57cec5SDimitry Andric error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
4160b57cec5SDimitry Andric output_sp->Printf("Enter LLDB commands to investigate (type help "
4170b57cec5SDimitry Andric "for assistance.)\n");
4180b57cec5SDimitry Andric {
4190b57cec5SDimitry Andric lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
4200b57cec5SDimitry Andric if (io_handler_sp) {
4210b57cec5SDimitry Andric io_handler_sp->SetIsDone(false);
4225ffd83dbSDimitry Andric debugger.RunIOHandlerAsync(ci.GetIOHandler());
4230b57cec5SDimitry Andric }
4240b57cec5SDimitry Andric }
4250b57cec5SDimitry Andric break;
4260b57cec5SDimitry Andric
4270b57cec5SDimitry Andric case lldb::eExpressionTimedOut:
4280b57cec5SDimitry Andric error_sp->Printf("error: timeout\n");
4290b57cec5SDimitry Andric if (error.AsCString())
4300b57cec5SDimitry Andric error_sp->Printf("error: %s\n", error.AsCString());
4310b57cec5SDimitry Andric break;
4320b57cec5SDimitry Andric case lldb::eExpressionResultUnavailable:
4330b57cec5SDimitry Andric // Shoulnd't happen???
4340b57cec5SDimitry Andric error_sp->Printf("error: could not fetch result -- %s\n",
4350b57cec5SDimitry Andric error.AsCString());
4360b57cec5SDimitry Andric break;
4370b57cec5SDimitry Andric case lldb::eExpressionStoppedForDebug:
4380b57cec5SDimitry Andric // Shoulnd't happen???
4390b57cec5SDimitry Andric error_sp->Printf("error: stopped for debug -- %s\n",
4400b57cec5SDimitry Andric error.AsCString());
4410b57cec5SDimitry Andric break;
4425ffd83dbSDimitry Andric case lldb::eExpressionThreadVanished:
4435ffd83dbSDimitry Andric // Shoulnd't happen???
4445ffd83dbSDimitry Andric error_sp->Printf("error: expression thread vanished -- %s\n",
4455ffd83dbSDimitry Andric error.AsCString());
4465ffd83dbSDimitry Andric break;
4470b57cec5SDimitry Andric }
4480b57cec5SDimitry Andric }
4490b57cec5SDimitry Andric
4500b57cec5SDimitry Andric if (add_to_code) {
4510b57cec5SDimitry Andric const uint32_t new_default_line = m_code.GetSize() + 1;
4520b57cec5SDimitry Andric
4530b57cec5SDimitry Andric m_code.SplitIntoLines(code);
4540b57cec5SDimitry Andric
4550b57cec5SDimitry Andric // Update our code on disk
4560b57cec5SDimitry Andric if (!m_repl_source_path.empty()) {
4579dba64beSDimitry Andric auto file = FileSystem::Instance().Open(
4589dba64beSDimitry Andric FileSpec(m_repl_source_path),
459349cc55cSDimitry Andric File::eOpenOptionWriteOnly | File::eOpenOptionTruncate |
4600b57cec5SDimitry Andric File::eOpenOptionCanCreate,
4610b57cec5SDimitry Andric lldb::eFilePermissionsFileDefault);
4629dba64beSDimitry Andric if (file) {
4630b57cec5SDimitry Andric std::string code(m_code.CopyList());
4640b57cec5SDimitry Andric code.append(1, '\n');
4650b57cec5SDimitry Andric size_t bytes_written = code.size();
4669dba64beSDimitry Andric file.get()->Write(code.c_str(), bytes_written);
4679dba64beSDimitry Andric file.get()->Close();
4689dba64beSDimitry Andric } else {
4699dba64beSDimitry Andric std::string message = llvm::toString(file.takeError());
4709dba64beSDimitry Andric error_sp->Printf("error: couldn't open %s: %s\n",
4719dba64beSDimitry Andric m_repl_source_path.c_str(), message.c_str());
4729dba64beSDimitry Andric }
4730b57cec5SDimitry Andric
4740b57cec5SDimitry Andric // Now set the default file and line to the REPL source file
4750b57cec5SDimitry Andric m_target.GetSourceManager().SetDefaultFileAndLine(
4760b57cec5SDimitry Andric FileSpec(m_repl_source_path), new_default_line);
4770b57cec5SDimitry Andric }
4780b57cec5SDimitry Andric static_cast<IOHandlerEditline &>(io_handler)
4790b57cec5SDimitry Andric .SetBaseLineNumber(m_code.GetSize() + 1);
4800b57cec5SDimitry Andric }
4810b57cec5SDimitry Andric if (extra_line) {
4829dba64beSDimitry Andric output_sp->Printf("\n");
4830b57cec5SDimitry Andric }
4840b57cec5SDimitry Andric }
4850b57cec5SDimitry Andric }
4860b57cec5SDimitry Andric
4870b57cec5SDimitry Andric // Don't complain about the REPL process going away if we are in the
4880b57cec5SDimitry Andric // process of quitting.
4890b57cec5SDimitry Andric if (!did_quit && (!process_sp || !process_sp->IsAlive())) {
4900b57cec5SDimitry Andric error_sp->Printf(
4910b57cec5SDimitry Andric "error: REPL process is no longer alive, exiting REPL\n");
4920b57cec5SDimitry Andric io_handler.SetIsDone(true);
4930b57cec5SDimitry Andric }
4940b57cec5SDimitry Andric }
4950b57cec5SDimitry Andric }
4960b57cec5SDimitry Andric
IOHandlerComplete(IOHandler & io_handler,CompletionRequest & request)4979dba64beSDimitry Andric void REPL::IOHandlerComplete(IOHandler &io_handler,
4989dba64beSDimitry Andric CompletionRequest &request) {
4990b57cec5SDimitry Andric // Complete an LLDB command if the first character is a colon...
500*5f757f3fSDimitry Andric if (request.GetRawLine().starts_with(":")) {
5010b57cec5SDimitry Andric Debugger &debugger = m_target.GetDebugger();
5020b57cec5SDimitry Andric
5030b57cec5SDimitry Andric // auto complete LLDB commands
5049dba64beSDimitry Andric llvm::StringRef new_line = request.GetRawLine().drop_front();
5059dba64beSDimitry Andric CompletionResult sub_result;
5069dba64beSDimitry Andric CompletionRequest sub_request(new_line, request.GetRawCursorPos() - 1,
5079dba64beSDimitry Andric sub_result);
5089dba64beSDimitry Andric debugger.GetCommandInterpreter().HandleCompletion(sub_request);
5099dba64beSDimitry Andric StringList matches, descriptions;
5109dba64beSDimitry Andric sub_result.GetMatches(matches);
5115ffd83dbSDimitry Andric // Prepend command prefix that was excluded in the completion request.
5125ffd83dbSDimitry Andric if (request.GetCursorIndex() == 0)
5135ffd83dbSDimitry Andric for (auto &match : matches)
5145ffd83dbSDimitry Andric match.insert(0, 1, ':');
5159dba64beSDimitry Andric sub_result.GetDescriptions(descriptions);
5169dba64beSDimitry Andric request.AddCompletions(matches, descriptions);
5179dba64beSDimitry Andric return;
5180b57cec5SDimitry Andric }
5190b57cec5SDimitry Andric
5200b57cec5SDimitry Andric // Strip spaces from the line and see if we had only spaces
5219dba64beSDimitry Andric if (request.GetRawLine().trim().empty()) {
5220b57cec5SDimitry Andric // Only spaces on this line, so just indent
5239dba64beSDimitry Andric request.AddCompletion(m_indent_str);
5249dba64beSDimitry Andric return;
5250b57cec5SDimitry Andric }
5260b57cec5SDimitry Andric
5270b57cec5SDimitry Andric std::string current_code;
5280b57cec5SDimitry Andric current_code.append(m_code.CopyList());
5290b57cec5SDimitry Andric
5300b57cec5SDimitry Andric IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
531*5f757f3fSDimitry Andric StringList current_lines = editline.GetCurrentLines();
5320b57cec5SDimitry Andric const uint32_t current_line_idx = editline.GetCurrentLineIndex();
5330b57cec5SDimitry Andric
534*5f757f3fSDimitry Andric if (current_line_idx < current_lines.GetSize()) {
5350b57cec5SDimitry Andric for (uint32_t i = 0; i < current_line_idx; ++i) {
536*5f757f3fSDimitry Andric const char *line_cstr = current_lines.GetStringAtIndex(i);
5370b57cec5SDimitry Andric if (line_cstr) {
5380b57cec5SDimitry Andric current_code.append("\n");
5390b57cec5SDimitry Andric current_code.append(line_cstr);
5400b57cec5SDimitry Andric }
5410b57cec5SDimitry Andric }
5420b57cec5SDimitry Andric }
5430b57cec5SDimitry Andric
5440b57cec5SDimitry Andric current_code.append("\n");
5459dba64beSDimitry Andric current_code += request.GetRawLine();
5460b57cec5SDimitry Andric
5475ffd83dbSDimitry Andric CompleteCode(current_code, request);
5480b57cec5SDimitry Andric }
5490b57cec5SDimitry Andric
QuitCommandOverrideCallback(void * baton,const char ** argv)5500b57cec5SDimitry Andric bool QuitCommandOverrideCallback(void *baton, const char **argv) {
5510b57cec5SDimitry Andric Target *target = (Target *)baton;
5520b57cec5SDimitry Andric lldb::ProcessSP process_sp(target->GetProcessSP());
5530b57cec5SDimitry Andric if (process_sp) {
5540b57cec5SDimitry Andric process_sp->Destroy(false);
5550b57cec5SDimitry Andric process_sp->GetTarget().GetDebugger().ClearIOHandlers();
5560b57cec5SDimitry Andric }
5570b57cec5SDimitry Andric return false;
5580b57cec5SDimitry Andric }
5590b57cec5SDimitry Andric
RunLoop()5600b57cec5SDimitry Andric Status REPL::RunLoop() {
5610b57cec5SDimitry Andric Status error;
5620b57cec5SDimitry Andric
5630b57cec5SDimitry Andric error = DoInitialization();
5640b57cec5SDimitry Andric m_repl_source_path = GetSourcePath();
5650b57cec5SDimitry Andric
5660b57cec5SDimitry Andric if (!error.Success())
5670b57cec5SDimitry Andric return error;
5680b57cec5SDimitry Andric
5690b57cec5SDimitry Andric Debugger &debugger = m_target.GetDebugger();
5700b57cec5SDimitry Andric
5710b57cec5SDimitry Andric lldb::IOHandlerSP io_handler_sp(GetIOHandler());
5720b57cec5SDimitry Andric
5730b57cec5SDimitry Andric FileSpec save_default_file;
5740b57cec5SDimitry Andric uint32_t save_default_line = 0;
5750b57cec5SDimitry Andric
5760b57cec5SDimitry Andric if (!m_repl_source_path.empty()) {
5770b57cec5SDimitry Andric // Save the current default file and line
5780b57cec5SDimitry Andric m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file,
5790b57cec5SDimitry Andric save_default_line);
5800b57cec5SDimitry Andric }
5810b57cec5SDimitry Andric
5825ffd83dbSDimitry Andric debugger.RunIOHandlerAsync(io_handler_sp);
5830b57cec5SDimitry Andric
5840b57cec5SDimitry Andric // Check if we are in dedicated REPL mode where LLDB was start with the "--
5850b57cec5SDimitry Andric // repl" option from the command line. Currently we know this by checking if
5860b57cec5SDimitry Andric // the debugger already has a IOHandler thread.
5870b57cec5SDimitry Andric if (!debugger.HasIOHandlerThread()) {
5880b57cec5SDimitry Andric // The debugger doesn't have an existing IOHandler thread, so this must be
5890b57cec5SDimitry Andric // dedicated REPL mode...
5900b57cec5SDimitry Andric m_dedicated_repl_mode = true;
5910b57cec5SDimitry Andric debugger.StartIOHandlerThread();
5920b57cec5SDimitry Andric llvm::StringRef command_name_str("quit");
5930b57cec5SDimitry Andric CommandObject *cmd_obj =
5940b57cec5SDimitry Andric debugger.GetCommandInterpreter().GetCommandObjectForCommand(
5950b57cec5SDimitry Andric command_name_str);
5960b57cec5SDimitry Andric if (cmd_obj) {
5970b57cec5SDimitry Andric assert(command_name_str.empty());
5980b57cec5SDimitry Andric cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target);
5990b57cec5SDimitry Andric }
6000b57cec5SDimitry Andric }
6010b57cec5SDimitry Andric
6020b57cec5SDimitry Andric // Wait for the REPL command interpreter to get popped
6030b57cec5SDimitry Andric io_handler_sp->WaitForPop();
6040b57cec5SDimitry Andric
6050b57cec5SDimitry Andric if (m_dedicated_repl_mode) {
6060b57cec5SDimitry Andric // If we were in dedicated REPL mode we would have started the IOHandler
6070b57cec5SDimitry Andric // thread, and we should kill our process
6080b57cec5SDimitry Andric lldb::ProcessSP process_sp = m_target.GetProcessSP();
6090b57cec5SDimitry Andric if (process_sp && process_sp->IsAlive())
6100b57cec5SDimitry Andric process_sp->Destroy(false);
6110b57cec5SDimitry Andric
6120b57cec5SDimitry Andric // Wait for the IO handler thread to exit (TODO: don't do this if the IO
6130b57cec5SDimitry Andric // handler thread already exists...)
6140b57cec5SDimitry Andric debugger.JoinIOHandlerThread();
6150b57cec5SDimitry Andric }
6160b57cec5SDimitry Andric
6170b57cec5SDimitry Andric // Restore the default file and line
6180b57cec5SDimitry Andric if (save_default_file && save_default_line != 0)
6190b57cec5SDimitry Andric m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file,
6200b57cec5SDimitry Andric save_default_line);
6210b57cec5SDimitry Andric return error;
6220b57cec5SDimitry Andric }
623