forked from OSchip/llvm-project
Added the concept of a Read-Eval-Print-Loop to LLDB.
A REPL takes over the command line and typically treats input as source code. REPLs can also do code completion. The REPL class allows its subclasses to implement the language-specific functionality without having to know about the IOHandler-specific internals. Also added a PluginManager-based way of getting to a REPL given a language and a target. Also brought in some utility code and expression options that are useful for REPLs, such as line offsets for expressions, ANSI terminal coloring of errors, and a few IOHandler convenience functions. llvm-svn: 250753
This commit is contained in:
parent
cc25301092
commit
6681041d70
|
@ -203,6 +203,10 @@ public:
|
|||
|
||||
bool
|
||||
IsTopIOHandler (const lldb::IOHandlerSP& reader_sp);
|
||||
|
||||
bool
|
||||
CheckTopIOHandlerTypes (IOHandler::Type top_type,
|
||||
IOHandler::Type second_top_type);
|
||||
|
||||
void
|
||||
PrintAsync (const char *s, size_t len, bool is_stdout);
|
||||
|
@ -321,6 +325,24 @@ public:
|
|||
bool
|
||||
GetAutoOneLineSummaries () const;
|
||||
|
||||
bool
|
||||
GetAutoIndent () const;
|
||||
|
||||
bool
|
||||
SetAutoIndent (bool b);
|
||||
|
||||
bool
|
||||
GetPrintDecls () const;
|
||||
|
||||
bool
|
||||
SetPrintDecls (bool b);
|
||||
|
||||
uint32_t
|
||||
GetTabSize () const;
|
||||
|
||||
bool
|
||||
SetTabSize (uint32_t tab_size);
|
||||
|
||||
bool
|
||||
GetEscapeNonPrintables () const;
|
||||
|
||||
|
@ -362,6 +384,7 @@ public:
|
|||
protected:
|
||||
|
||||
friend class CommandInterpreter;
|
||||
friend class REPL;
|
||||
|
||||
bool
|
||||
StartEventHandlerThread();
|
||||
|
@ -372,12 +395,18 @@ protected:
|
|||
static lldb::thread_result_t
|
||||
EventHandlerThread (lldb::thread_arg_t arg);
|
||||
|
||||
bool
|
||||
HasIOHandlerThread();
|
||||
|
||||
bool
|
||||
StartIOHandlerThread();
|
||||
|
||||
void
|
||||
StopIOHandlerThread();
|
||||
|
||||
void
|
||||
JoinIOHandlerThread();
|
||||
|
||||
static lldb::thread_result_t
|
||||
IOHandlerThread (lldb::thread_arg_t arg);
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace lldb_private {
|
|||
Confirm,
|
||||
Curses,
|
||||
Expression,
|
||||
REPL,
|
||||
ProcessIO,
|
||||
PythonInterpreter,
|
||||
PythonCode,
|
||||
|
|
|
@ -443,6 +443,23 @@ public:
|
|||
static TypeSystemEnumerateSupportedLanguages
|
||||
GetTypeSystemEnumerateSupportedLanguagesCallbackForPluginName (const ConstString &name);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// REPL
|
||||
//------------------------------------------------------------------
|
||||
static bool
|
||||
RegisterPlugin (const ConstString &name,
|
||||
const char *description,
|
||||
REPLCreateInstance create_callback);
|
||||
|
||||
static bool
|
||||
UnregisterPlugin (REPLCreateInstance create_callback);
|
||||
|
||||
static REPLCreateInstance
|
||||
GetREPLCreateCallbackAtIndex (uint32_t idx);
|
||||
|
||||
static REPLCreateInstance
|
||||
GetREPLCreateCallbackForPluginName (const ConstString &name);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Some plug-ins might register a DebuggerInitializeCallback
|
||||
// callback when registering the plug-in. After a new Debugger
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
//===-- REPL.h --------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
#ifndef lldb_REPL_h
|
||||
#define lldb_REPL_h
|
||||
|
||||
#include "lldb/Interpreter/OptionGroupFormat.h"
|
||||
#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
|
||||
#include "lldb/../../source/Commands/CommandObjectExpression.h"
|
||||
|
||||
namespace lldb_private
|
||||
{
|
||||
|
||||
class REPL : public IOHandlerDelegate
|
||||
{
|
||||
public:
|
||||
//----------------------------------------------------------------------
|
||||
// See TypeSystem.h for how to add subclasses to this.
|
||||
//----------------------------------------------------------------------
|
||||
enum LLVMCastKind {
|
||||
eKindClang,
|
||||
eKindSwift,
|
||||
eKindGo,
|
||||
kNumKinds
|
||||
};
|
||||
|
||||
LLVMCastKind getKind() const { return m_kind; }
|
||||
|
||||
REPL(LLVMCastKind kind, Target &target);
|
||||
|
||||
virtual ~REPL();
|
||||
|
||||
static lldb::REPLSP
|
||||
Create (lldb::LanguageType language, Target *target);
|
||||
|
||||
void
|
||||
SetFormatOptions (const OptionGroupFormat &options)
|
||||
{
|
||||
m_format_options = options;
|
||||
}
|
||||
|
||||
void
|
||||
SetValueObjectDisplayOptions (const OptionGroupValueObjectDisplay &options)
|
||||
{
|
||||
m_varobj_options = options;
|
||||
}
|
||||
|
||||
void
|
||||
SetCommandOptions (const CommandObjectExpression::CommandOptions &options)
|
||||
{
|
||||
m_command_options = options;
|
||||
}
|
||||
|
||||
void
|
||||
SetCompilerOptions (const char *options)
|
||||
{
|
||||
if (options)
|
||||
m_compiler_options = options;
|
||||
}
|
||||
|
||||
lldb::IOHandlerSP
|
||||
GetIOHandler ();
|
||||
|
||||
Error
|
||||
RunLoop ();
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// IOHandler::Delegate functions
|
||||
//------------------------------------------------------------------
|
||||
void
|
||||
IOHandlerActivated (IOHandler &io_handler) override;
|
||||
|
||||
bool
|
||||
IOHandlerInterrupt (IOHandler &io_handler) override;
|
||||
|
||||
void
|
||||
IOHandlerInputInterrupted (IOHandler &io_handler,
|
||||
std::string &line) override;
|
||||
|
||||
const char *
|
||||
IOHandlerGetFixIndentationCharacters () override;
|
||||
|
||||
ConstString
|
||||
IOHandlerGetControlSequence (char ch) override;
|
||||
|
||||
const char *
|
||||
IOHandlerGetCommandPrefix () override;
|
||||
|
||||
const char *
|
||||
IOHandlerGetHelpPrologue () override;
|
||||
|
||||
bool
|
||||
IOHandlerIsInputComplete (IOHandler &io_handler,
|
||||
StringList &lines) override;
|
||||
|
||||
int
|
||||
IOHandlerFixIndentation (IOHandler &io_handler,
|
||||
const StringList &lines,
|
||||
int cursor_position) override;
|
||||
|
||||
void
|
||||
IOHandlerInputComplete (IOHandler &io_handler,
|
||||
std::string &line) override;
|
||||
|
||||
int
|
||||
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) override;
|
||||
|
||||
private:
|
||||
std::string
|
||||
GetSourcePath();
|
||||
|
||||
protected:
|
||||
static int
|
||||
CalculateActualIndentation (const StringList &lines);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Subclasses should override these functions to implement a functional REPL.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
virtual Error
|
||||
DoInitialization () = 0;
|
||||
|
||||
virtual ConstString
|
||||
GetSourceFileBasename () = 0;
|
||||
|
||||
virtual const char *
|
||||
GetAutoIndentCharacters () = 0;
|
||||
|
||||
virtual bool
|
||||
SourceIsComplete (const std::string &source) = 0;
|
||||
|
||||
virtual lldb::offset_t
|
||||
GetDesiredIndentation (const StringList &lines,
|
||||
int cursor_position,
|
||||
int tab_size) = 0; // LLDB_INVALID_OFFSET means no change
|
||||
|
||||
virtual lldb::LanguageType
|
||||
GetLanguage () = 0;
|
||||
|
||||
virtual bool
|
||||
PrintOneVariable (Debugger &debugger,
|
||||
lldb::StreamFileSP &output_sp,
|
||||
lldb::ValueObjectSP &valobj_sp,
|
||||
ExpressionVariable *var = nullptr) = 0;
|
||||
|
||||
virtual int
|
||||
CompleteCode(const std::string ¤t_code,
|
||||
StringList &matches) = 0;
|
||||
|
||||
OptionGroupFormat m_format_options = OptionGroupFormat(lldb::eFormatDefault);
|
||||
OptionGroupValueObjectDisplay m_varobj_options;
|
||||
CommandObjectExpression::CommandOptions m_command_options;
|
||||
std::string m_compiler_options;
|
||||
|
||||
bool m_enable_auto_indent = true;
|
||||
std::string m_indent_str; // Use this string for each level of indentation
|
||||
std::string m_current_indent_str;
|
||||
uint32_t m_current_indent_level = 0;
|
||||
|
||||
std::string m_repl_source_path;
|
||||
bool m_dedicated_repl_mode = false;
|
||||
|
||||
StringList m_code; // All accumulated REPL statements are saved here
|
||||
|
||||
Target &m_target;
|
||||
lldb::IOHandlerSP m_io_handler_sp;
|
||||
LLVMCastKind m_kind;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* REPL_h */
|
|
@ -273,10 +273,16 @@ public:
|
|||
/// @param[in,out] result_valobj_sp
|
||||
/// If execution is successful, the result valobj is placed here.
|
||||
///
|
||||
/// @param[out]
|
||||
/// @param[out] error
|
||||
/// Filled in with an error in case the expression evaluation
|
||||
/// fails to parse, run, or evaluated.
|
||||
///
|
||||
/// @param[in] line_offset
|
||||
/// The offset of the first line of the expression from the "beginning" of a virtual source file used for error reporting and debug info.
|
||||
///
|
||||
/// @param[out] jit_module_sp_ptr
|
||||
/// If non-NULL, used to persist the generated IR module.
|
||||
///
|
||||
/// @result
|
||||
/// A Process::ExpressionResults value. eExpressionCompleted for success.
|
||||
//------------------------------------------------------------------
|
||||
|
@ -286,7 +292,9 @@ public:
|
|||
const char *expr_cstr,
|
||||
const char *expr_prefix,
|
||||
lldb::ValueObjectSP &result_valobj_sp,
|
||||
Error &error);
|
||||
Error &error,
|
||||
uint32_t line_offset = 0,
|
||||
lldb::ModuleSP *jit_module_sp_ptr = NULL);
|
||||
|
||||
static const Error::ValueType kNoResult = 0x1001; ///< ValueObject::GetError() returns this if there is no result from the expression.
|
||||
protected:
|
||||
|
|
|
@ -494,6 +494,9 @@ public:
|
|||
//------------------------------------------------------------------
|
||||
bool
|
||||
GetIsRealTerminal ();
|
||||
|
||||
bool
|
||||
GetIsTerminalWithColors ();
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Output printf formatted output to the stream.
|
||||
|
@ -546,6 +549,7 @@ protected:
|
|||
bool m_own_stream;
|
||||
LazyBool m_is_interactive;
|
||||
LazyBool m_is_real_terminal;
|
||||
LazyBool m_supports_colors;
|
||||
};
|
||||
|
||||
} // namespace lldb_private
|
||||
|
|
|
@ -659,6 +659,9 @@ public:
|
|||
return m_stopped_for_crash;
|
||||
}
|
||||
|
||||
bool
|
||||
GetSpaceReplPrompts () const;
|
||||
|
||||
protected:
|
||||
friend class Debugger;
|
||||
|
||||
|
|
|
@ -441,6 +441,18 @@ public:
|
|||
m_generate_debug_info = b;
|
||||
}
|
||||
|
||||
bool
|
||||
GetColorizeErrors () const
|
||||
{
|
||||
return m_ansi_color_errors;
|
||||
}
|
||||
|
||||
void
|
||||
SetColorizeErrors (bool b)
|
||||
{
|
||||
m_ansi_color_errors = b;
|
||||
}
|
||||
|
||||
bool
|
||||
GetTrapExceptions() const
|
||||
{
|
||||
|
@ -453,6 +465,18 @@ public:
|
|||
m_trap_exceptions = b;
|
||||
}
|
||||
|
||||
bool
|
||||
GetREPLEnabled() const
|
||||
{
|
||||
return m_repl;
|
||||
}
|
||||
|
||||
void
|
||||
SetREPLEnabled (bool b)
|
||||
{
|
||||
m_repl = b;
|
||||
}
|
||||
|
||||
void
|
||||
SetCancelCallback (lldb::ExpressionCancelCallback callback, void *baton)
|
||||
{
|
||||
|
@ -468,6 +492,37 @@ public:
|
|||
else
|
||||
return m_cancel_callback (phase, m_cancel_callback_baton);
|
||||
}
|
||||
|
||||
// Allows the expression contents to be remapped to point to the specified file and line
|
||||
// using #line directives.
|
||||
void
|
||||
SetPoundLine (const char *path, uint32_t line) const
|
||||
{
|
||||
if (path && path[0])
|
||||
{
|
||||
m_pound_line_file = path;
|
||||
m_pound_line_line = line;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pound_line_file.clear();
|
||||
m_pound_line_line = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
GetPoundLineFilePath () const
|
||||
{
|
||||
if (m_pound_line_file.empty())
|
||||
return NULL;
|
||||
return m_pound_line_file.c_str();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GetPoundLineLine () const
|
||||
{
|
||||
return m_pound_line_line;
|
||||
}
|
||||
|
||||
void
|
||||
SetResultIsInternal (bool b)
|
||||
|
@ -493,13 +548,20 @@ private:
|
|||
bool m_stop_others;
|
||||
bool m_debug;
|
||||
bool m_trap_exceptions;
|
||||
bool m_repl;
|
||||
bool m_generate_debug_info;
|
||||
bool m_ansi_color_errors;
|
||||
bool m_result_is_internal;
|
||||
lldb::DynamicValueType m_use_dynamic;
|
||||
uint32_t m_timeout_usec;
|
||||
uint32_t m_one_thread_timeout_usec;
|
||||
lldb::ExpressionCancelCallback m_cancel_callback;
|
||||
void *m_cancel_callback_baton;
|
||||
// If m_pound_line_file is not empty and m_pound_line_line is non-zero,
|
||||
// use #line %u "%s" before the expression content to remap where the source
|
||||
// originates
|
||||
mutable std::string m_pound_line_file;
|
||||
mutable uint32_t m_pound_line_line;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -1504,8 +1566,11 @@ public:
|
|||
|
||||
lldb::SearchFilterSP
|
||||
GetSearchFilterForModuleAndCUList (const FileSpecList *containingModules, const FileSpecList *containingSourceFiles);
|
||||
|
||||
lldb::REPLSP
|
||||
GetREPL (lldb::LanguageType, bool can_create);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
//------------------------------------------------------------------
|
||||
// Member variables.
|
||||
//------------------------------------------------------------------
|
||||
|
@ -1528,6 +1593,9 @@ protected:
|
|||
PathMappingList m_image_search_paths;
|
||||
TypeSystemMap m_scratch_type_system_map;
|
||||
|
||||
typedef std::map<lldb::LanguageType, lldb::REPLSP> REPLMap;
|
||||
REPLMap m_repl_map;
|
||||
|
||||
lldb::ClangASTImporterUP m_ast_importer_ap;
|
||||
lldb::ClangModulesDeclVendorUP m_clang_modules_decl_vendor_ap;
|
||||
|
||||
|
|
|
@ -41,8 +41,13 @@
|
|||
#define ANSI_CTRL_CONCEAL 8
|
||||
#define ANSI_CTRL_CROSSED_OUT 9
|
||||
|
||||
#define ANSI_ESC_START "\033["
|
||||
#define ANSI_ESC_END "m"
|
||||
#define ANSI_ESC_START "\033["
|
||||
#define ANSI_ESC_END "m"
|
||||
|
||||
#define ANSI_STR(s) #s
|
||||
#define ANSI_DEF_STR(s) ANSI_STR(s)
|
||||
|
||||
#define ANSI_ESCAPE1(s) ANSI_ESC_START ANSI_DEF_STR(s) ANSI_ESC_END
|
||||
|
||||
#define ANSI_1_CTRL(ctrl1) "\033["##ctrl1 ANSI_ESC_END
|
||||
#define ANSI_2_CTRL(ctrl1,ctrl2) "\033["##ctrl1";"##ctrl2 ANSI_ESC_END
|
||||
|
|
|
@ -183,6 +183,7 @@ class RegisterLocation;
|
|||
class RegisterLocationList;
|
||||
class RegisterValue;
|
||||
class RegularExpression;
|
||||
class REPL;
|
||||
class Scalar;
|
||||
class ScriptInterpreter;
|
||||
class ScriptInterpreterLocker;
|
||||
|
@ -382,6 +383,7 @@ namespace lldb {
|
|||
typedef std::shared_ptr<lldb_private::Queue> QueueSP;
|
||||
typedef std::weak_ptr<lldb_private::Queue> QueueWP;
|
||||
typedef std::shared_ptr<lldb_private::QueueItem> QueueItemSP;
|
||||
typedef std::shared_ptr<lldb_private::REPL> REPLSP;
|
||||
typedef std::shared_ptr<lldb_private::ScriptSummaryFormat> ScriptSummaryFormatSP;
|
||||
typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
|
||||
typedef std::unique_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterUP;
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace lldb_private
|
|||
typedef lldb::InstrumentationRuntimeType (*InstrumentationRuntimeGetType) ();
|
||||
typedef lldb::InstrumentationRuntimeSP (*InstrumentationRuntimeCreateInstance) (const lldb::ProcessSP &process_sp);
|
||||
typedef lldb::TypeSystemSP (*TypeSystemCreateInstance) (lldb::LanguageType language, Module *module, Target *target);
|
||||
typedef lldb::REPLSP (*REPLCreateInstance) (lldb::LanguageType language, Target *target);
|
||||
typedef void (*TypeSystemEnumerateSupportedLanguages) (std::set<lldb::LanguageType> &languages_for_types, std::set<lldb::LanguageType> &languages_for_expressions);
|
||||
typedef int (*ComparisonFunction)(const void *, const void *);
|
||||
typedef void (*DebuggerInitializeCallback)(Debugger &debugger);
|
||||
|
|
|
@ -668,6 +668,7 @@
|
|||
449ACC98197DEA0B008D175E /* FastDemangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 449ACC96197DE9EC008D175E /* FastDemangle.cpp */; };
|
||||
490A36C0180F0E6F00BA31F8 /* PlatformWindows.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 490A36BD180F0E6F00BA31F8 /* PlatformWindows.cpp */; };
|
||||
490A966B1628C3BF00F0002E /* SBDeclaration.h in Headers */ = {isa = PBXBuildFile; fileRef = 9452573816262CEF00325455 /* SBDeclaration.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
4939EA8D1BD56B6D00084382 /* REPL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4939EA8C1BD56B6D00084382 /* REPL.cpp */; };
|
||||
494260DA14579144003C1C78 /* VerifyDecl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 494260D914579144003C1C78 /* VerifyDecl.cpp */; };
|
||||
4959511F1A1BC4BC00F6F8FC /* ClangModulesDeclVendor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4959511E1A1BC4BC00F6F8FC /* ClangModulesDeclVendor.cpp */; };
|
||||
4966DCC4148978A10028481B /* ClangExternalASTSourceCommon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4966DCC3148978A10028481B /* ClangExternalASTSourceCommon.cpp */; };
|
||||
|
@ -2254,6 +2255,8 @@
|
|||
491193501226386000578B7F /* ASTStructExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ASTStructExtractor.cpp; path = ExpressionParser/Clang/ASTStructExtractor.cpp; sourceTree = "<group>"; };
|
||||
49307AAD11DEA4D90081F992 /* IRForTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = IRForTarget.cpp; path = ExpressionParser/Clang/IRForTarget.cpp; sourceTree = "<group>"; };
|
||||
49307AB111DEA4F20081F992 /* IRForTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IRForTarget.h; path = ExpressionParser/Clang/IRForTarget.h; sourceTree = "<group>"; };
|
||||
4939EA8B1BD56B3700084382 /* REPL.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = REPL.h; path = include/lldb/Expression/REPL.h; sourceTree = "<group>"; };
|
||||
4939EA8C1BD56B6D00084382 /* REPL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = REPL.cpp; path = source/Expression/REPL.cpp; sourceTree = "<group>"; };
|
||||
494260D7145790D5003C1C78 /* VerifyDecl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = VerifyDecl.h; path = include/lldb/Symbol/VerifyDecl.h; sourceTree = "<group>"; };
|
||||
494260D914579144003C1C78 /* VerifyDecl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VerifyDecl.cpp; path = source/Symbol/VerifyDecl.cpp; sourceTree = "<group>"; };
|
||||
49445C2512245E3600C11A81 /* ClangExpressionParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangExpressionParser.cpp; path = ExpressionParser/Clang/ClangExpressionParser.cpp; sourceTree = "<group>"; };
|
||||
|
@ -4447,6 +4450,8 @@
|
|||
496B01581406DE8900F830D5 /* IRInterpreter.cpp */,
|
||||
49DCF6FF170E6FD90092F75E /* Materializer.h */,
|
||||
49DCF700170E70120092F75E /* Materializer.cpp */,
|
||||
4939EA8B1BD56B3700084382 /* REPL.h */,
|
||||
4939EA8C1BD56B6D00084382 /* REPL.cpp */,
|
||||
);
|
||||
name = Expression;
|
||||
sourceTree = "<group>";
|
||||
|
@ -6371,6 +6376,7 @@
|
|||
2689005F13353E0E00698AC0 /* ClangFunctionCaller.cpp in Sources */,
|
||||
2689006013353E0E00698AC0 /* ClangExpressionDeclMap.cpp in Sources */,
|
||||
2689006113353E0E00698AC0 /* ClangExpressionParser.cpp in Sources */,
|
||||
4939EA8D1BD56B6D00084382 /* REPL.cpp in Sources */,
|
||||
B5EFAE861AE53B1D007059F3 /* RegisterContextFreeBSD_arm.cpp in Sources */,
|
||||
2689006313353E0E00698AC0 /* ClangPersistentVariables.cpp in Sources */,
|
||||
2689006413353E0E00698AC0 /* ClangUserExpression.cpp in Sources */,
|
||||
|
|
|
@ -156,6 +156,9 @@ g_properties[] =
|
|||
{ "use-external-editor", OptionValue::eTypeBoolean , true, false, NULL, NULL, "Whether to use an external editor or not." },
|
||||
{ "use-color", OptionValue::eTypeBoolean , true, true , NULL, NULL, "Whether to use Ansi color codes or not." },
|
||||
{ "auto-one-line-summaries", OptionValue::eTypeBoolean , true, true, NULL, NULL, "If true, LLDB will automatically display small structs in one-liner format (default: true)." },
|
||||
{ "auto-indent", OptionValue::eTypeBoolean , true, true , NULL, NULL, "If true, LLDB will auto indent/outdent code. Currently only supported in the REPL (default: true)." },
|
||||
{ "print-decls", OptionValue::eTypeBoolean , true, true , NULL, NULL, "If true, LLDB will print the values of variables declared in an expression. Currently only supported in the REPL (default: true)." },
|
||||
{ "tab-size", OptionValue::eTypeUInt64 , true, 4 , NULL, NULL, "The tab size to use when indenting code in multi-line input mode (default: 4)." },
|
||||
{ "escape-non-printables", OptionValue::eTypeBoolean , true, true, NULL, NULL, "If true, LLDB will automatically escape non-printable and escape characters when formatting strings." },
|
||||
{ NULL, OptionValue::eTypeInvalid , true, 0 , NULL, NULL, NULL }
|
||||
};
|
||||
|
@ -177,6 +180,9 @@ enum
|
|||
ePropertyUseExternalEditor,
|
||||
ePropertyUseColor,
|
||||
ePropertyAutoOneLineSummaries,
|
||||
ePropertyAutoIndent,
|
||||
ePropertyPrintDecls,
|
||||
ePropertyTabSize,
|
||||
ePropertyEscapeNonPrintables
|
||||
};
|
||||
|
||||
|
@ -392,6 +398,49 @@ Debugger::GetEscapeNonPrintables () const
|
|||
return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, true);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::GetAutoIndent () const
|
||||
{
|
||||
const uint32_t idx = ePropertyAutoIndent;
|
||||
return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, true);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::SetAutoIndent (bool b)
|
||||
{
|
||||
const uint32_t idx = ePropertyAutoIndent;
|
||||
return m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::GetPrintDecls () const
|
||||
{
|
||||
const uint32_t idx = ePropertyPrintDecls;
|
||||
return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, true);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::SetPrintDecls (bool b)
|
||||
{
|
||||
const uint32_t idx = ePropertyPrintDecls;
|
||||
return m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Debugger::GetTabSize () const
|
||||
{
|
||||
const uint32_t idx = ePropertyTabSize;
|
||||
return m_collection_sp->GetPropertyAtIndexAsUInt64 (NULL, idx, g_properties[idx].default_uint_value);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::SetTabSize (uint32_t tab_size)
|
||||
{
|
||||
const uint32_t idx = ePropertyTabSize;
|
||||
return m_collection_sp->SetPropertyAtIndexAsUInt64 (NULL, idx, tab_size);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Debugger
|
||||
|
||||
//const DebuggerPropertiesSP &
|
||||
|
@ -919,6 +968,12 @@ Debugger::IsTopIOHandler (const lldb::IOHandlerSP& reader_sp)
|
|||
return m_input_reader_stack.IsTop (reader_sp);
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::CheckTopIOHandlerTypes (IOHandler::Type top_type, IOHandler::Type second_top_type)
|
||||
{
|
||||
return m_input_reader_stack.CheckTopIOHandlerTypes (top_type, second_top_type);
|
||||
}
|
||||
|
||||
void
|
||||
Debugger::PrintAsync (const char *s, size_t len, bool is_stdout)
|
||||
{
|
||||
|
@ -1683,6 +1738,12 @@ Debugger::IOHandlerThread (lldb::thread_arg_t arg)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::HasIOHandlerThread()
|
||||
{
|
||||
return m_io_handler_thread.IsJoinable();
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::StartIOHandlerThread()
|
||||
{
|
||||
|
@ -1706,6 +1767,17 @@ Debugger::StopIOHandlerThread()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Debugger::JoinIOHandlerThread()
|
||||
{
|
||||
if (HasIOHandlerThread())
|
||||
{
|
||||
thread_result_t result;
|
||||
m_io_handler_thread.Join(&result);
|
||||
m_io_handler_thread = LLDB_INVALID_HOST_THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
Target *
|
||||
Debugger::GetDummyTarget()
|
||||
{
|
||||
|
|
|
@ -2648,6 +2648,106 @@ PluginManager::GetTypeSystemEnumerateSupportedLanguagesCallbackForPluginName (co
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#pragma mark REPL
|
||||
|
||||
struct REPLInstance
|
||||
{
|
||||
REPLInstance() :
|
||||
name(),
|
||||
description(),
|
||||
create_callback(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
ConstString name;
|
||||
std::string description;
|
||||
REPLCreateInstance create_callback;
|
||||
};
|
||||
|
||||
typedef std::vector<REPLInstance> REPLInstances;
|
||||
|
||||
static Mutex &
|
||||
GetREPLMutex ()
|
||||
{
|
||||
static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
|
||||
return g_instances_mutex;
|
||||
}
|
||||
|
||||
static REPLInstances &
|
||||
GetREPLInstances ()
|
||||
{
|
||||
static REPLInstances g_instances;
|
||||
return g_instances;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginManager::RegisterPlugin (const ConstString &name,
|
||||
const char *description,
|
||||
REPLCreateInstance create_callback)
|
||||
{
|
||||
if (create_callback)
|
||||
{
|
||||
REPLInstance instance;
|
||||
assert ((bool)name);
|
||||
instance.name = name;
|
||||
if (description && description[0])
|
||||
instance.description = description;
|
||||
instance.create_callback = create_callback;
|
||||
Mutex::Locker locker (GetREPLMutex ());
|
||||
GetREPLInstances ().push_back (instance);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginManager::UnregisterPlugin (REPLCreateInstance create_callback)
|
||||
{
|
||||
if (create_callback)
|
||||
{
|
||||
Mutex::Locker locker (GetREPLMutex ());
|
||||
REPLInstances &instances = GetREPLInstances ();
|
||||
|
||||
REPLInstances::iterator pos, end = instances.end();
|
||||
for (pos = instances.begin(); pos != end; ++ pos)
|
||||
{
|
||||
if (pos->create_callback == create_callback)
|
||||
{
|
||||
instances.erase(pos);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
REPLCreateInstance
|
||||
PluginManager::GetREPLCreateCallbackAtIndex (uint32_t idx)
|
||||
{
|
||||
Mutex::Locker locker (GetREPLMutex ());
|
||||
REPLInstances &instances = GetREPLInstances ();
|
||||
if (idx < instances.size())
|
||||
return instances[idx].create_callback;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
REPLCreateInstance
|
||||
PluginManager::GetREPLCreateCallbackForPluginName (const ConstString &name)
|
||||
{
|
||||
if (name)
|
||||
{
|
||||
Mutex::Locker locker (GetREPLMutex ());
|
||||
REPLInstances &instances = GetREPLInstances ();
|
||||
|
||||
REPLInstances::iterator pos, end = instances.end();
|
||||
for (pos = instances.begin(); pos != end; ++ pos)
|
||||
{
|
||||
if (name == pos->name)
|
||||
return pos->create_callback;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#pragma mark PluginManager
|
||||
|
||||
void
|
||||
|
|
|
@ -0,0 +1,650 @@
|
|||
//===-- REPL.cpp ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lldb/Core/Debugger.h"
|
||||
#include "lldb/Core/PluginManager.h"
|
||||
#include "lldb/Core/StreamFile.h"
|
||||
#include "lldb/Expression/ExpressionVariable.h"
|
||||
#include "lldb/Expression/REPL.h"
|
||||
#include "lldb/Expression/UserExpression.h"
|
||||
#include "lldb/Host/HostInfo.h"
|
||||
#include "lldb/Interpreter/CommandInterpreter.h"
|
||||
#include "lldb/Interpreter/CommandReturnObject.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
#include "lldb/Target/Thread.h"
|
||||
#include "lldb/Utility/AnsiTerminal.h"
|
||||
|
||||
using namespace lldb_private;
|
||||
|
||||
lldb::REPLSP
|
||||
REPL::Create(lldb::LanguageType language, Target *target)
|
||||
{
|
||||
uint32_t idx = 0;
|
||||
lldb::REPLSP ret;
|
||||
|
||||
while (REPLCreateInstance create_instance = PluginManager::GetREPLCreateCallbackAtIndex(idx++))
|
||||
{
|
||||
ret = (*create_instance)(language, target);
|
||||
if (ret)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 = std::move(tmpdir_file_spec.GetPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpdir_file_spec = FileSpec("/tmp", false);
|
||||
tmpdir_file_spec.AppendPathComponent(file_basename.AsCString());
|
||||
}
|
||||
|
||||
return tmpdir_file_spec.GetPath();
|
||||
}
|
||||
|
||||
REPL::~REPL()
|
||||
{
|
||||
}
|
||||
|
||||
lldb::IOHandlerSP
|
||||
REPL::GetIOHandler()
|
||||
{
|
||||
if (!m_io_handler_sp)
|
||||
{
|
||||
Debugger &debugger = m_target.GetDebugger();
|
||||
m_io_handler_sp.reset (new IOHandlerEditline (debugger,
|
||||
IOHandler::Type::REPL,
|
||||
"lldb-repl", // Name of input reader for history
|
||||
"> ", // prompt
|
||||
". ", // Continuation prompt
|
||||
true, // Multi-line
|
||||
true, // The REPL prompt is always colored
|
||||
1, // Line number
|
||||
*this));
|
||||
|
||||
// Don't exit if CTRL+C is pressed
|
||||
static_cast<IOHandlerEditline *>(m_io_handler_sp.get())->SetInterruptExits(false);
|
||||
|
||||
if (m_io_handler_sp->GetIsInteractive() && m_io_handler_sp->GetIsRealTerminal())
|
||||
{
|
||||
m_indent_str.assign (debugger.GetTabSize(), ' ');
|
||||
m_enable_auto_indent = debugger.GetAutoIndent();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_indent_str.clear();
|
||||
m_enable_auto_indent = false;
|
||||
}
|
||||
|
||||
}
|
||||
return m_io_handler_sp;
|
||||
}
|
||||
|
||||
void
|
||||
REPL::IOHandlerActivated (IOHandler &io_handler)
|
||||
{
|
||||
lldb::ProcessSP process_sp = m_target.GetProcessSP();
|
||||
if (process_sp && process_sp->IsAlive())
|
||||
return;
|
||||
lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile());
|
||||
error_sp->Printf("REPL requires a running target process.\n");
|
||||
io_handler.SetIsDone(true);
|
||||
}
|
||||
|
||||
bool
|
||||
REPL::IOHandlerInterrupt (IOHandler &io_handler)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
REPL::IOHandlerInputInterrupted (IOHandler &io_handler,
|
||||
std::string &line)
|
||||
{
|
||||
}
|
||||
|
||||
const char *
|
||||
REPL::IOHandlerGetFixIndentationCharacters ()
|
||||
{
|
||||
if (m_enable_auto_indent)
|
||||
return GetAutoIndentCharacters();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ConstString
|
||||
REPL::IOHandlerGetControlSequence (char ch)
|
||||
{
|
||||
if (ch == 'd')
|
||||
return ConstString(":quit\n");
|
||||
return ConstString();
|
||||
}
|
||||
|
||||
const char *
|
||||
REPL::IOHandlerGetCommandPrefix ()
|
||||
{
|
||||
return ":";
|
||||
}
|
||||
|
||||
const char *
|
||||
REPL::IOHandlerGetHelpPrologue ()
|
||||
{
|
||||
return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter. "
|
||||
"Valid statements, expressions, and declarations are immediately compiled and executed.\n\n"
|
||||
"The complete set of LLDB debugging commands are also available as described below. Commands "
|
||||
"must be prefixed with a colon at the REPL prompt (:quit for example.) Typing just a colon "
|
||||
"followed by return will switch to the LLDB prompt.\n\n";
|
||||
}
|
||||
|
||||
bool
|
||||
REPL::IOHandlerIsInputComplete (IOHandler &io_handler,
|
||||
StringList &lines)
|
||||
{
|
||||
// Check for meta command
|
||||
const size_t num_lines = lines.GetSize();
|
||||
if (num_lines == 1)
|
||||
{
|
||||
const char *first_line = lines.GetStringAtIndex(0);
|
||||
if (first_line[0] == ':')
|
||||
return true; // Meta command is a single line where that starts with ':'
|
||||
}
|
||||
|
||||
// Check if REPL input is done
|
||||
std::string source_string (lines.CopyList());
|
||||
return SourceIsComplete(source_string);
|
||||
}
|
||||
|
||||
int
|
||||
REPL::CalculateActualIndentation (const StringList &lines)
|
||||
{
|
||||
std::string last_line = lines[lines.GetSize() - 1];
|
||||
|
||||
int actual_indent = 0;
|
||||
for (char &ch : last_line)
|
||||
{
|
||||
if (ch != ' ') break;
|
||||
++actual_indent;
|
||||
}
|
||||
|
||||
return actual_indent;
|
||||
}
|
||||
|
||||
int
|
||||
REPL::IOHandlerFixIndentation (IOHandler &io_handler,
|
||||
const StringList &lines,
|
||||
int cursor_position)
|
||||
{
|
||||
if (!m_enable_auto_indent) return 0;
|
||||
|
||||
if (!lines.GetSize())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tab_size = io_handler.GetDebugger().GetTabSize();
|
||||
|
||||
lldb::offset_t desired_indent = GetDesiredIndentation(lines,
|
||||
cursor_position,
|
||||
tab_size);
|
||||
|
||||
int actual_indent = REPL::CalculateActualIndentation(lines);
|
||||
|
||||
if (desired_indent == LLDB_INVALID_OFFSET)
|
||||
return 0;
|
||||
|
||||
return (int)desired_indent - actual_indent;
|
||||
}
|
||||
|
||||
void
|
||||
REPL::IOHandlerInputComplete (IOHandler &io_handler, std::string &code)
|
||||
{
|
||||
lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFile());
|
||||
lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile());
|
||||
bool extra_line = false;
|
||||
bool did_quit = false;
|
||||
|
||||
if (code.empty())
|
||||
{
|
||||
m_code.AppendString("");
|
||||
static_cast<IOHandlerEditline &>(io_handler).SetBaseLineNumber(m_code.GetSize()+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debugger &debugger = m_target.GetDebugger();
|
||||
CommandInterpreter &ci = debugger.GetCommandInterpreter();
|
||||
extra_line = ci.GetSpaceReplPrompts();
|
||||
|
||||
ExecutionContext exe_ctx (m_target.GetProcessSP()->GetThreadList().GetSelectedThread()->GetSelectedFrame().get());
|
||||
|
||||
lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
|
||||
|
||||
if (code[0] == ':')
|
||||
{
|
||||
// Meta command
|
||||
// Strip the ':'
|
||||
code.erase(0, 1);
|
||||
if (Args::StripSpaces (code))
|
||||
{
|
||||
// "lldb" was followed by arguments, so just execute the command dump the results
|
||||
|
||||
// Turn off prompt on quit in case the user types ":quit"
|
||||
const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
|
||||
if (saved_prompt_on_quit)
|
||||
ci.SetPromptOnQuit(false);
|
||||
|
||||
// Execute the command
|
||||
CommandReturnObject result;
|
||||
result.SetImmediateOutputStream(output_sp);
|
||||
result.SetImmediateErrorStream(error_sp);
|
||||
ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
|
||||
|
||||
if (saved_prompt_on_quit)
|
||||
ci.SetPromptOnQuit(true);
|
||||
|
||||
if (result.GetStatus() == lldb::eReturnStatusQuit)
|
||||
{
|
||||
did_quit = true;
|
||||
io_handler.SetIsDone(true);
|
||||
if (debugger.CheckTopIOHandlerTypes(IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter))
|
||||
{
|
||||
// We typed "quit" or an alias to quit so we need to check if the
|
||||
// command interpreter is above us and tell it that it is done as well
|
||||
// so we don't drop back into the command interpreter if we have already
|
||||
// quit
|
||||
lldb::IOHandlerSP io_handler_sp (ci.GetIOHandler());
|
||||
if (io_handler_sp)
|
||||
io_handler_sp->SetIsDone(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ":" was followed by no arguments, so push the LLDB command prompt
|
||||
if (debugger.CheckTopIOHandlerTypes(IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter))
|
||||
{
|
||||
// If the user wants to get back to the command interpreter and the
|
||||
// command interpreter is what launched the REPL, then just let the
|
||||
// REPL exit and fall back to the command interpreter.
|
||||
io_handler.SetIsDone(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The REPL wasn't launched the by the command interpreter, it is the
|
||||
// base IOHandler, so we need to get the command interpreter and
|
||||
lldb::IOHandlerSP io_handler_sp (ci.GetIOHandler());
|
||||
if (io_handler_sp)
|
||||
{
|
||||
io_handler_sp->SetIsDone(false);
|
||||
debugger.PushIOHandler(ci.GetIOHandler());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unwind any expression we might have been running in case our REPL
|
||||
// expression crashed and the user was looking around
|
||||
if (m_dedicated_repl_mode)
|
||||
{
|
||||
Thread *thread = exe_ctx.GetThreadPtr();
|
||||
if (thread && thread->UnwindInnermostExpression().Success())
|
||||
{
|
||||
thread->SetSelectedFrameByIndex(0, false);
|
||||
exe_ctx.SetFrameSP(thread->GetSelectedFrame());
|
||||
}
|
||||
}
|
||||
|
||||
const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
|
||||
|
||||
EvaluateExpressionOptions expr_options;
|
||||
expr_options.SetCoerceToId(m_varobj_options.use_objc);
|
||||
expr_options.SetUnwindOnError(m_command_options.unwind_on_error);
|
||||
expr_options.SetIgnoreBreakpoints (m_command_options.ignore_breakpoints);
|
||||
expr_options.SetKeepInMemory(true);
|
||||
expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
|
||||
expr_options.SetTryAllThreads(m_command_options.try_all_threads);
|
||||
expr_options.SetGenerateDebugInfo(true);
|
||||
expr_options.SetREPLEnabled (true);
|
||||
expr_options.SetColorizeErrors(colorize_err);
|
||||
expr_options.SetPoundLine(m_repl_source_path.c_str(), m_code.GetSize() + 1);
|
||||
if (m_command_options.timeout > 0)
|
||||
expr_options.SetTimeoutUsec(m_command_options.timeout);
|
||||
else
|
||||
expr_options.SetTimeoutUsec(0);
|
||||
|
||||
expr_options.SetLanguage(GetLanguage());
|
||||
|
||||
PersistentExpressionState *persistent_state = m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
|
||||
|
||||
const size_t var_count_before = persistent_state->GetSize();
|
||||
|
||||
const char *expr_prefix = NULL;
|
||||
lldb::ValueObjectSP result_valobj_sp;
|
||||
Error error;
|
||||
lldb::ModuleSP jit_module_sp;
|
||||
lldb::ExpressionResults execution_results = UserExpression::Evaluate (exe_ctx,
|
||||
expr_options,
|
||||
code.c_str(),
|
||||
expr_prefix,
|
||||
result_valobj_sp,
|
||||
error,
|
||||
0, // Line offset
|
||||
&jit_module_sp);
|
||||
|
||||
//CommandInterpreter &ci = debugger.GetCommandInterpreter();
|
||||
|
||||
if (process_sp && process_sp->IsAlive())
|
||||
{
|
||||
bool add_to_code = true;
|
||||
bool handled = false;
|
||||
if (result_valobj_sp)
|
||||
{
|
||||
lldb::Format format = m_format_options.GetFormat();
|
||||
|
||||
if (result_valobj_sp->GetError().Success())
|
||||
{
|
||||
handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
|
||||
}
|
||||
else if (result_valobj_sp->GetError().GetError() == UserExpression::kNoResult)
|
||||
{
|
||||
if (format != lldb::eFormatVoid && debugger.GetNotifyVoid())
|
||||
{
|
||||
error_sp->PutCString("(void)\n");
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debugger.GetPrintDecls())
|
||||
{
|
||||
for (size_t vi = var_count_before, ve = persistent_state->GetSize();
|
||||
vi != ve;
|
||||
++vi)
|
||||
{
|
||||
lldb::ExpressionVariableSP persistent_var_sp = persistent_state->GetVariableAtIndex(vi);
|
||||
lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
|
||||
|
||||
PrintOneVariable(debugger, output_sp, valobj_sp, persistent_var_sp.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
|
||||
switch (execution_results)
|
||||
{
|
||||
case lldb::eExpressionSetupError:
|
||||
case lldb::eExpressionParseError:
|
||||
add_to_code = false;
|
||||
// Fall through
|
||||
case lldb::eExpressionDiscarded:
|
||||
error_sp->Printf("%s\n", error.AsCString());
|
||||
break;
|
||||
|
||||
case lldb::eExpressionCompleted:
|
||||
break;
|
||||
case lldb::eExpressionInterrupted:
|
||||
if (useColors) {
|
||||
error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
|
||||
error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
|
||||
}
|
||||
error_sp->Printf("Execution interrupted. ");
|
||||
if (useColors) error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
|
||||
error_sp->Printf("Enter code to recover and continue.\nEnter LLDB commands to investigate (type :help for assistance.)\n");
|
||||
break;
|
||||
|
||||
case lldb::eExpressionHitBreakpoint:
|
||||
// Breakpoint was hit, drop into LLDB command interpreter
|
||||
if (useColors) {
|
||||
error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
|
||||
error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
|
||||
}
|
||||
output_sp->Printf("Execution stopped at breakpoint. ");
|
||||
if (useColors) error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
|
||||
output_sp->Printf("Enter LLDB commands to investigate (type help for assistance.)\n");
|
||||
{
|
||||
lldb::IOHandlerSP io_handler_sp (ci.GetIOHandler());
|
||||
if (io_handler_sp)
|
||||
{
|
||||
io_handler_sp->SetIsDone(false);
|
||||
debugger.PushIOHandler(ci.GetIOHandler());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case lldb::eExpressionTimedOut:
|
||||
error_sp->Printf("error: timeout\n");
|
||||
if (error.AsCString())
|
||||
error_sp->Printf("error: %s\n", error.AsCString());
|
||||
break;
|
||||
case lldb::eExpressionResultUnavailable:
|
||||
// Shoulnd't happen???
|
||||
error_sp->Printf("error: could not fetch result -- %s\n", error.AsCString());
|
||||
break;
|
||||
case lldb::eExpressionStoppedForDebug:
|
||||
// Shoulnd't happen???
|
||||
error_sp->Printf("error: stopped for debug -- %s\n", error.AsCString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (add_to_code)
|
||||
{
|
||||
const uint32_t new_default_line = m_code.GetSize() + 1;
|
||||
|
||||
m_code.SplitIntoLines(code);
|
||||
|
||||
// Update our code on disk
|
||||
if (!m_repl_source_path.empty())
|
||||
{
|
||||
lldb_private::File file (m_repl_source_path.c_str(),
|
||||
File::eOpenOptionWrite | File::eOpenOptionTruncate | File::eOpenOptionCanCreate,
|
||||
lldb::eFilePermissionsFileDefault);
|
||||
std::string code (m_code.CopyList());
|
||||
code.append(1, '\n');
|
||||
size_t bytes_written = code.size();
|
||||
file.Write(code.c_str(), bytes_written);
|
||||
file.Close();
|
||||
|
||||
// Now set the default file and line to the REPL source file
|
||||
m_target.GetSourceManager().SetDefaultFileAndLine(FileSpec(m_repl_source_path.c_str(), false), new_default_line);
|
||||
}
|
||||
static_cast<IOHandlerEditline &>(io_handler).SetBaseLineNumber(m_code.GetSize()+1);
|
||||
}
|
||||
if (extra_line)
|
||||
{
|
||||
fprintf(output_sp->GetFile().GetStream(), "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't complain about the REPL process going away if we are in the process of quitting.
|
||||
if (!did_quit && (!process_sp || !process_sp->IsAlive()))
|
||||
{
|
||||
error_sp->Printf("error: REPL process is no longer alive, exiting REPL\n");
|
||||
io_handler.SetIsDone(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
REPL::IOHandlerComplete (IOHandler &io_handler,
|
||||
const char *current_line,
|
||||
const char *cursor,
|
||||
const char *last_char,
|
||||
int skip_first_n_matches,
|
||||
int max_matches,
|
||||
StringList &matches)
|
||||
{
|
||||
matches.Clear();
|
||||
|
||||
llvm::StringRef line (current_line, cursor - current_line);
|
||||
|
||||
// Complete an LLDB command if the first character is a colon...
|
||||
if (!line.empty() && line[0] == ':')
|
||||
{
|
||||
Debugger &debugger = m_target.GetDebugger();
|
||||
|
||||
// auto complete LLDB commands
|
||||
const char *lldb_current_line = line.substr(1).data();
|
||||
return debugger.GetCommandInterpreter().HandleCompletion (lldb_current_line,
|
||||
cursor,
|
||||
last_char,
|
||||
skip_first_n_matches,
|
||||
max_matches,
|
||||
matches);
|
||||
}
|
||||
|
||||
// Strip spaces from the line and see if we had only spaces
|
||||
line = line.ltrim();
|
||||
if (line.empty())
|
||||
{
|
||||
// Only spaces on this line, so just indent
|
||||
matches.AppendString(m_indent_str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string current_code;
|
||||
current_code.append(m_code.CopyList());
|
||||
|
||||
IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
|
||||
const StringList *current_lines = editline.GetCurrentLines();
|
||||
if (current_lines)
|
||||
{
|
||||
const uint32_t current_line_idx = editline.GetCurrentLineIndex();
|
||||
|
||||
if (current_line_idx < current_lines->GetSize())
|
||||
{
|
||||
for (uint32_t i=0; i<current_line_idx; ++i)
|
||||
{
|
||||
const char *line_cstr = current_lines->GetStringAtIndex(i);
|
||||
if (line_cstr)
|
||||
{
|
||||
current_code.append("\n");
|
||||
current_code.append (line_cstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor > current_line)
|
||||
{
|
||||
current_code.append("\n");
|
||||
current_code.append(current_line, cursor - current_line);
|
||||
}
|
||||
|
||||
return CompleteCode(current_code, matches);
|
||||
}
|
||||
|
||||
bool
|
||||
QuitCommandOverrideCallback(void *baton, const char **argv)
|
||||
{
|
||||
Target *target = (Target *)baton;
|
||||
lldb::ProcessSP process_sp (target->GetProcessSP());
|
||||
if (process_sp)
|
||||
{
|
||||
process_sp->Destroy(false);
|
||||
process_sp->GetTarget().GetDebugger().ClearIOHandlers();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Error
|
||||
REPL::RunLoop ()
|
||||
{
|
||||
Error error;
|
||||
|
||||
error = DoInitialization();
|
||||
m_repl_source_path = GetSourcePath();
|
||||
|
||||
if (!error.Success())
|
||||
return error;
|
||||
|
||||
Debugger &debugger = m_target.GetDebugger();
|
||||
|
||||
lldb::IOHandlerSP io_handler_sp (GetIOHandler());
|
||||
|
||||
FileSpec save_default_file;
|
||||
uint32_t save_default_line = 0;
|
||||
|
||||
if (!m_repl_source_path.empty())
|
||||
{
|
||||
// Save the current default file and line
|
||||
m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file, save_default_line);
|
||||
}
|
||||
|
||||
debugger.PushIOHandler(io_handler_sp);
|
||||
|
||||
// Check if we are in dedicated REPL mode where LLDB was start with the "--repl" option
|
||||
// from the command line. Currently we know this by checking if the debugger already
|
||||
// has a IOHandler thread.
|
||||
if (!debugger.HasIOHandlerThread())
|
||||
{
|
||||
// The debugger doesn't have an existing IOHandler thread, so this must be
|
||||
// dedicated REPL mode...
|
||||
m_dedicated_repl_mode = true;
|
||||
debugger.StartIOHandlerThread();
|
||||
std::string command_name_str ("quit");
|
||||
CommandObject *cmd_obj = debugger.GetCommandInterpreter().GetCommandObjectForCommand(command_name_str);
|
||||
if (cmd_obj)
|
||||
{
|
||||
assert(command_name_str.empty());
|
||||
cmd_obj->SetOverrideCallback (QuitCommandOverrideCallback, &m_target);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the REPL command interpreter to get popped
|
||||
io_handler_sp->WaitForPop();
|
||||
|
||||
if (m_dedicated_repl_mode)
|
||||
{
|
||||
// If we were in dedicated REPL mode we would have started the
|
||||
// IOHandler thread, and we should kill our process
|
||||
lldb::ProcessSP process_sp = m_target.GetProcessSP();
|
||||
if (process_sp && process_sp->IsAlive())
|
||||
process_sp->Destroy(false);
|
||||
|
||||
// Wait for the IO handler thread to exit (TODO: don't do this if the IO handler thread already exists...)
|
||||
debugger.JoinIOHandlerThread();
|
||||
}
|
||||
|
||||
// Restore the default file and line
|
||||
if (save_default_file && save_default_line != 0)
|
||||
m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file, save_default_line);
|
||||
return error;
|
||||
}
|
|
@ -465,7 +465,9 @@ UserExpression::Evaluate (ExecutionContext &exe_ctx,
|
|||
const char *expr_cstr,
|
||||
const char *expr_prefix,
|
||||
lldb::ValueObjectSP &result_valobj_sp,
|
||||
Error &error)
|
||||
Error &error,
|
||||
uint32_t line_offset,
|
||||
lldb::ModuleSP *jit_module_sp_ptr)
|
||||
{
|
||||
Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP));
|
||||
|
||||
|
@ -567,6 +569,10 @@ UserExpression::Evaluate (ExecutionContext &exe_ctx,
|
|||
}
|
||||
else
|
||||
{
|
||||
// If a pointer to a lldb::ModuleSP was passed in, return the JIT'ed module if one was created
|
||||
if (jit_module_sp_ptr && user_expression_sp->m_execution_unit_sp)
|
||||
*jit_module_sp_ptr = user_expression_sp->m_execution_unit_sp->GetJITModule();
|
||||
|
||||
lldb::ExpressionVariableSP expr_result;
|
||||
|
||||
if (execution_policy == eExecutionPolicyNever &&
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include "llvm/Support/Process.h" // for llvm::sys::Process::FileDescriptorHasColors()
|
||||
|
||||
#include "lldb/Core/DataBufferHeap.h"
|
||||
#include "lldb/Core/Error.h"
|
||||
#include "lldb/Core/Log.h"
|
||||
|
@ -1051,7 +1053,11 @@ File::CalculateInteractiveAndTerminal ()
|
|||
if (::ioctl (fd, TIOCGWINSZ, &window_size) == 0)
|
||||
{
|
||||
if (window_size.ws_col > 0)
|
||||
{
|
||||
m_is_real_terminal = eLazyBoolYes;
|
||||
if (llvm::sys::Process::FileDescriptorHasColors(fd))
|
||||
m_supports_colors = eLazyBoolYes;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -1074,3 +1080,11 @@ File::GetIsRealTerminal ()
|
|||
return m_is_real_terminal == eLazyBoolYes;
|
||||
}
|
||||
|
||||
bool
|
||||
File::GetIsTerminalWithColors ()
|
||||
{
|
||||
if (m_supports_colors == eLazyBoolCalculate)
|
||||
CalculateInteractiveAndTerminal();
|
||||
return m_supports_colors == eLazyBoolYes;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ g_properties[] =
|
|||
{ "expand-regex-aliases", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true, regular expression alias commands will show the expanded command that will be executed. This can be used to debug new regular expression alias commands." },
|
||||
{ "prompt-on-quit", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, LLDB will prompt you before quitting if there are any live processes being debugged. If false, LLDB will quit without asking in any case." },
|
||||
{ "stop-command-source-on-error", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, LLDB will stop running a 'command source' script upon encountering an error." },
|
||||
{ "space-repl-prompts", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true, blank lines will be printed between between REPL submissions." },
|
||||
{ nullptr , OptionValue::eTypeInvalid, true, 0 , nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -92,7 +93,8 @@ enum
|
|||
{
|
||||
ePropertyExpandRegexAliases = 0,
|
||||
ePropertyPromptOnQuit = 1,
|
||||
ePropertyStopCmdSourceOnError = 2
|
||||
ePropertyStopCmdSourceOnError = 2,
|
||||
eSpaceReplPrompts = 3
|
||||
};
|
||||
|
||||
ConstString &
|
||||
|
@ -167,6 +169,13 @@ CommandInterpreter::GetStopCmdSourceOnError () const
|
|||
return m_collection_sp->GetPropertyAtIndexAsBoolean (nullptr, idx, g_properties[idx].default_uint_value != 0);
|
||||
}
|
||||
|
||||
bool
|
||||
CommandInterpreter::GetSpaceReplPrompts () const
|
||||
{
|
||||
const uint32_t idx = eSpaceReplPrompts;
|
||||
return m_collection_sp->GetPropertyAtIndexAsBoolean (nullptr, idx, g_properties[idx].default_uint_value != 0);
|
||||
}
|
||||
|
||||
void
|
||||
CommandInterpreter::Initialize ()
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "lldb/Core/StreamString.h"
|
||||
#include "lldb/Core/Timer.h"
|
||||
#include "lldb/Core/ValueObject.h"
|
||||
#include "lldb/Expression/REPL.h"
|
||||
#include "lldb/Expression/UserExpression.h"
|
||||
#include "Plugins/ExpressionParser/Clang/ClangASTSource.h"
|
||||
#include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h"
|
||||
|
@ -211,6 +212,37 @@ Target::GetProcessSP () const
|
|||
return m_process_sp;
|
||||
}
|
||||
|
||||
lldb::REPLSP
|
||||
Target::GetREPL (lldb::LanguageType language, bool can_create)
|
||||
{
|
||||
if (language == eLanguageTypeUnknown)
|
||||
{
|
||||
return REPLSP(); // must provide a language
|
||||
}
|
||||
|
||||
REPLMap::iterator pos = m_repl_map.find(language);
|
||||
|
||||
if (pos != m_repl_map.end())
|
||||
{
|
||||
return pos->second;
|
||||
}
|
||||
|
||||
if (!can_create)
|
||||
{
|
||||
return lldb::REPLSP();
|
||||
}
|
||||
|
||||
lldb::REPLSP ret = REPL::Create(language, this);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
m_repl_map[language] = ret;
|
||||
return m_repl_map[language];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Target::Destroy()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue