From 40624a085c03fb6d41834885d41f7158ab72950e Mon Sep 17 00:00:00 2001 From: Aleksandr Urakov Date: Tue, 5 Feb 2019 09:14:36 +0000 Subject: [PATCH] [Expressions] Add support of expressions evaluation in some object's context Summary: This patch adds support of expression evaluation in a context of some object. Consider the following example: ``` struct S { int a = 11; int b = 12; }; int main() { S s; int a = 1; int b = 2; // We have stopped here return 0; } ``` This patch allows to do something like that: ``` lldb.frame.FindVariable("s").EvaluateExpression("a + b") ``` and the result will be `33` (not `3`) because fields `a` and `b` of `s` will be used (not locals `a` and `b`). This is achieved by replacing of `this` type and object for the expression. This has some limitations: an expression can be evaluated only for values located in the debuggee process memory (they must have an address of `eAddressTypeLoad` type). Reviewers: teemperor, clayborg, jingham, zturner, labath, davide, spyffe, serge-sans-paille Reviewed By: jingham Subscribers: abidh, lldb-commits, leonid.mashinskiy Tags: #lldb Differential Revision: https://reviews.llvm.org/D55318 llvm-svn: 353149 --- lldb/include/lldb/API/SBValue.h | 7 + .../include/lldb/Expression/ExpressionSourceCode.h | 3 +- lldb/include/lldb/Expression/UserExpression.h | 13 +- lldb/include/lldb/Symbol/ClangASTContext.h | 3 +- lldb/include/lldb/Symbol/TypeSystem.h | 3 +- lldb/include/lldb/Target/Target.h | 6 +- .../context-object-objc/Makefile | 6 + .../context-object-objc/TestContextObjectObjc.py | 78 +++++++++++ .../expression_command/context-object-objc/main.m | 47 +++++++ .../expression_command/context-object/Makefile | 5 + .../context-object/TestContextObject.py | 145 +++++++++++++++++++++ .../expression_command/context-object/main.cpp | 46 +++++++ lldb/scripts/interface/SBValue.i | 14 +- lldb/source/API/SBValue.cpp | 81 ++++++++++++ lldb/source/Breakpoint/BreakpointLocation.cpp | 2 +- lldb/source/Breakpoint/Watchpoint.cpp | 3 +- lldb/source/Commands/CommandObjectExpression.cpp | 2 +- lldb/source/Expression/ExpressionSourceCode.cpp | 16 ++- lldb/source/Expression/UserExpression.cpp | 20 ++- .../Clang/ClangExpressionDeclMap.cpp | 40 +++++- .../Clang/ClangExpressionDeclMap.h | 15 ++- .../ExpressionParser/Clang/ClangUserExpression.cpp | 47 ++++++- .../ExpressionParser/Clang/ClangUserExpression.h | 18 ++- .../Clang/ClangUtilityFunction.cpp | 3 +- lldb/source/Symbol/ClangASTContext.cpp | 5 +- lldb/source/Target/Target.cpp | 12 +- 26 files changed, 596 insertions(+), 44 deletions(-) create mode 100644 lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/Makefile create mode 100644 lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/TestContextObjectObjc.py create mode 100644 lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/main.m create mode 100644 lldb/packages/Python/lldbsuite/test/expression_command/context-object/Makefile create mode 100644 lldb/packages/Python/lldbsuite/test/expression_command/context-object/TestContextObject.py create mode 100644 lldb/packages/Python/lldbsuite/test/expression_command/context-object/main.cpp diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h index 444557a..21364cf 100644 --- a/lldb/include/lldb/API/SBValue.h +++ b/lldb/include/lldb/API/SBValue.h @@ -306,6 +306,13 @@ public: bool GetExpressionPath(lldb::SBStream &description, bool qualify_cxx_base_classes); + lldb::SBValue EvaluateExpression(const char *expr) const; + lldb::SBValue EvaluateExpression(const char *expr, + const SBExpressionOptions &options) const; + lldb::SBValue EvaluateExpression(const char *expr, + const SBExpressionOptions &options, + const char *name) const; + SBValue(const lldb::ValueObjectSP &value_sp); //------------------------------------------------------------------ diff --git a/lldb/include/lldb/Expression/ExpressionSourceCode.h b/lldb/include/lldb/Expression/ExpressionSourceCode.h index bb5b29a..f8785ba 100644 --- a/lldb/include/lldb/Expression/ExpressionSourceCode.h +++ b/lldb/include/lldb/Expression/ExpressionSourceCode.h @@ -36,7 +36,8 @@ public: const char *GetName() const { return m_name.c_str(); } bool GetText(std::string &text, lldb::LanguageType wrapping_language, - bool static_method, ExecutionContext &exe_ctx) const; + bool static_method, ExecutionContext &exe_ctx, + bool add_locals) const; // Given a string returned by GetText, find the beginning and end of the body // passed to CreateWrapped. Return true if the bounds could be found. This diff --git a/lldb/include/lldb/Expression/UserExpression.h b/lldb/include/lldb/Expression/UserExpression.h index e2fa5b3f..ea72c51 100644 --- a/lldb/include/lldb/Expression/UserExpression.h +++ b/lldb/include/lldb/Expression/UserExpression.h @@ -272,6 +272,16 @@ public: /// @param[out] jit_module_sp_ptr /// If non-nullptr, used to persist the generated IR module. /// + /// @param[in] ctx_obj + /// If specified, then the expression will be evaluated in the context of + /// this object. It means that the context object's address will be + /// treated as `this` for the expression (the expression will be + /// evaluated as if it was inside of a method of the context object's + /// class, and its `this` parameter were pointing to the context object). + /// The parameter makes sense for class and union types only. + /// Currently there is a limitation: the context object must be located + /// in the debuggee process' memory (and have the load address). + /// /// @result /// A Process::ExpressionResults value. eExpressionCompleted for /// success. @@ -281,7 +291,8 @@ public: llvm::StringRef expr_cstr, llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp, Status &error, uint32_t line_offset = 0, std::string *fixed_expression = nullptr, - lldb::ModuleSP *jit_module_sp_ptr = nullptr); + lldb::ModuleSP *jit_module_sp_ptr = nullptr, + ValueObject *ctx_obj = nullptr); static const Status::ValueType kNoResult = 0x1001; ///< ValueObject::GetError() returns this if there is no result diff --git a/lldb/include/lldb/Symbol/ClangASTContext.h b/lldb/include/lldb/Symbol/ClangASTContext.h index 50c8d4b..b574391 100644 --- a/lldb/include/lldb/Symbol/ClangASTContext.h +++ b/lldb/include/lldb/Symbol/ClangASTContext.h @@ -1060,7 +1060,8 @@ public: GetUserExpression(llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, Expression::ResultType desired_type, - const EvaluateExpressionOptions &options) override; + const EvaluateExpressionOptions &options, + ValueObject *ctx_obj) override; FunctionCaller *GetFunctionCaller(const CompilerType &return_type, const Address &function_address, diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index 520916f..2e1d830 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -449,7 +449,8 @@ public: GetUserExpression(llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, Expression::ResultType desired_type, - const EvaluateExpressionOptions &options) { + const EvaluateExpressionOptions &options, + ValueObject *ctx_obj) { return nullptr; } diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index adc4795..0b67100 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -1043,7 +1043,8 @@ public: UserExpression *GetUserExpressionForLanguage( llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, Expression::ResultType desired_type, - const EvaluateExpressionOptions &options, Status &error); + const EvaluateExpressionOptions &options, + ValueObject *ctx_obj, Status &error); // Creates a FunctionCaller for the given language, the rest of the // parameters have the same meaning as for the FunctionCaller constructor. @@ -1107,7 +1108,8 @@ public: llvm::StringRef expression, ExecutionContextScope *exe_scope, lldb::ValueObjectSP &result_valobj_sp, const EvaluateExpressionOptions &options = EvaluateExpressionOptions(), - std::string *fixed_expression = nullptr); + std::string *fixed_expression = nullptr, + ValueObject *ctx_obj = nullptr); lldb::ExpressionVariableSP GetPersistentVariable(const ConstString &name); diff --git a/lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/Makefile b/lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/Makefile new file mode 100644 index 0000000..b659274 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../make + +OBJC_SOURCES := main.m + +include $(LEVEL)/Makefile.rules +LDFLAGS += -framework Foundation diff --git a/lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/TestContextObjectObjc.py b/lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/TestContextObjectObjc.py new file mode 100644 index 0000000..4ae4fd8 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/TestContextObjectObjc.py @@ -0,0 +1,78 @@ +""" +Tests expression evaluation in context of an objc class. +""" + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + +class ContextObjectObjcTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipUnlessDarwin + def test_context_object_objc(self): + """Tests expression evaluation in context of an objc class.""" + self.build() + + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, '// Break here', self.main_source_spec) + frame = thread.GetFrameAtIndex(0) + + # + # Test objc class variable + # + + obj_val = frame.FindVariable("objcClass") + self.assertTrue(obj_val.IsValid()) + obj_val = obj_val.Dereference() + self.assertTrue(obj_val.IsValid()) + + # Test an empty expression evaluation + value = obj_val.EvaluateExpression("") + self.assertFalse(value.IsValid()) + self.assertFalse(value.GetError().Success()) + + # Test retrieving of a field (not a local with the same name) + value = obj_val.EvaluateExpression("field") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 1111) + + # Test if the self pointer is properly evaluated + + # Test retrieving of an objcClass's property through the self pointer + value = obj_val.EvaluateExpression("self.property") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 2222) + + # Test objcClass's methods evaluation through the self pointer + value = obj_val.EvaluateExpression("[self method]") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 3333) + + # Test if we can use a computation result reference object correctly + + obj_val = frame.EvaluateExpression("[ObjcClass createNew]") + self.assertTrue(obj_val.IsValid()) + obj_val = obj_val.Dereference() + self.assertTrue(obj_val.IsValid()) + + # Test an expression evaluation on it + value = obj_val.EvaluateExpression("1") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + + # Test retrieving of a field on it + value = obj_val.EvaluateExpression("field") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 1111) + + def setUp(self): + TestBase.setUp(self) + + self.main_source = "main.m" + self.main_source_spec = lldb.SBFileSpec(self.main_source) diff --git a/lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/main.m b/lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/main.m new file mode 100644 index 0000000..5c495b2 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/expression_command/context-object-objc/main.m @@ -0,0 +1,47 @@ +#import + +@interface ObjcClass : NSObject { + int field; +} + +@property int property; + ++(ObjcClass*)createNew; + +-(id)init; + +-(int)method; + +@end + +@implementation ObjcClass + ++(ObjcClass*)createNew { + return [ObjcClass new]; +} + +-(id)init { + self = [super init]; + if (self) { + field = 1111; + _property = 2222; + } + return self; +} + +-(int)method { + return 3333; +} + +@end + +int main() +{ + @autoreleasepool { + ObjcClass* objcClass = [ObjcClass new]; + + int field = 4444; + + return 0; // Break here + } +} diff --git a/lldb/packages/Python/lldbsuite/test/expression_command/context-object/Makefile b/lldb/packages/Python/lldbsuite/test/expression_command/context-object/Makefile new file mode 100644 index 0000000..8a7102e --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/expression_command/context-object/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/expression_command/context-object/TestContextObject.py b/lldb/packages/Python/lldbsuite/test/expression_command/context-object/TestContextObject.py new file mode 100644 index 0000000..02b8162 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/expression_command/context-object/TestContextObject.py @@ -0,0 +1,145 @@ +""" +Tests expression evaluation in context of an object. +""" + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class ContextObjectTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_context_object(self): + """Tests expression evaluation in context of an object.""" + self.build() + + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, '// Break here', self.main_source_spec) + frame = thread.GetFrameAtIndex(0) + + # + # Test C++ struct variable + # + + obj_val = frame.FindVariable("cpp_struct") + self.assertTrue(obj_val.IsValid()) + + # Test an empty expression evaluation + value = obj_val.EvaluateExpression("") + self.assertFalse(value.IsValid()) + self.assertFalse(value.GetError().Success()) + + # Test retrieveing of a field (not a local with the same name) + value = obj_val.EvaluateExpression("field") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 1111) + + # Test functions evaluation + value = obj_val.EvaluateExpression("function()") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 2222) + + # Test that we retrieve the right global + value = obj_val.EvaluateExpression("global.field") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 1111) + + # + # Test C++ union variable + # + + obj_val = frame.FindVariable("cpp_union") + self.assertTrue(obj_val.IsValid()) + + # Test retrieveing of a field + value = obj_val.EvaluateExpression("field_int") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 5555) + + # + # Test C++ scalar + # + + obj_val = frame.FindVariable("cpp_scalar") + self.assertTrue(obj_val.IsValid()) + + # Test an expression evaluation + value = obj_val.EvaluateExpression("1") + self.assertFalse(value.IsValid()) + self.assertFalse(value.GetError().Success()) + + # + # Test C++ array + # + + obj_val = frame.FindVariable("cpp_array") + self.assertTrue(obj_val.IsValid()) + + # Test an expression evaluation + value = obj_val.EvaluateExpression("1") + self.assertFalse(value.IsValid()) + self.assertFalse(value.GetError().Success()) + + # Test retrieveing of an element's field + value = obj_val.GetValueForExpressionPath("[7]").EvaluateExpression("field") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 1111) + + # + # Test C++ pointer + # + + obj_val = frame.FindVariable("cpp_pointer") + self.assertTrue(obj_val.IsValid()) + + # Test an expression evaluation + value = obj_val.EvaluateExpression("1") + self.assertFalse(value.IsValid()) + self.assertFalse(value.GetError().Success()) + + # Test retrieveing of a dereferenced object's field + value = obj_val.Dereference().EvaluateExpression("field") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 1111) + + # + # Test C++ computation result + # + + obj_val = frame.EvaluateExpression("cpp_namespace::GetCppStruct()") + self.assertTrue(obj_val.IsValid()) + + # Test an expression evaluation + value = obj_val.EvaluateExpression("1") + self.assertTrue(value.IsValid()) + self.assertFalse(value.GetError().Success()) + + # + # Test C++ computation result located in debuggee memory + # + + obj_val = frame.EvaluateExpression("cpp_namespace::GetCppStructPtr()") + self.assertTrue(obj_val.IsValid()) + + # Test an expression evaluation + value = obj_val.EvaluateExpression("1") + self.assertFalse(value.IsValid()) + self.assertFalse(value.GetError().Success()) + + # Test retrieveing of a dereferenced object's field + value = obj_val.Dereference().EvaluateExpression("field") + self.assertTrue(value.IsValid()) + self.assertTrue(value.GetError().Success()) + self.assertEqual(value.GetValueAsSigned(), 1111) + + def setUp(self): + TestBase.setUp(self) + + self.main_source = "main.cpp" + self.main_source_spec = lldb.SBFileSpec(self.main_source) diff --git a/lldb/packages/Python/lldbsuite/test/expression_command/context-object/main.cpp b/lldb/packages/Python/lldbsuite/test/expression_command/context-object/main.cpp new file mode 100644 index 0000000..098b608 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/expression_command/context-object/main.cpp @@ -0,0 +1,46 @@ +namespace cpp_namespace { + struct CppStruct { + int field = 1111; + + int function() { + return 2222; + } + }; + + union CppUnion { + char field_char; + short field_short; + int field_int; + }; + + CppStruct GetCppStruct() { + return CppStruct(); + } + + CppStruct global; + + CppStruct *GetCppStructPtr() { + return &global; + } +} + +int global = 3333; + +int main() +{ + cpp_namespace::CppStruct cpp_struct = cpp_namespace::GetCppStruct(); + cpp_struct.function(); + + int field = 4444; + + cpp_namespace::CppUnion cpp_union; + cpp_union.field_int = 5555; + + int cpp_scalar = 6666; + + cpp_namespace::CppStruct cpp_array[16]; + + cpp_namespace::CppStruct *cpp_pointer = cpp_namespace::GetCppStructPtr(); + + return 0; // Break here +} diff --git a/lldb/scripts/interface/SBValue.i b/lldb/scripts/interface/SBValue.i index f620417..99352d8 100644 --- a/lldb/scripts/interface/SBValue.i +++ b/lldb/scripts/interface/SBValue.i @@ -448,7 +448,19 @@ public: ) GetExpressionPath; bool GetExpressionPath (lldb::SBStream &description, bool qualify_cxx_base_classes); - + + lldb::SBValue + EvaluateExpression(const char *expr) const; + + lldb::SBValue + EvaluateExpression(const char *expr, + const SBExpressionOptions &options) const; + + lldb::SBValue + EvaluateExpression(const char *expr, + const SBExpressionOptions &options, + const char *name) const; + %pythoncode %{ def __get_dynamic__ (self): '''Helper function for the "SBValue.dynamic" property.''' diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp index 31606af..b83a4ef 100644 --- a/lldb/source/API/SBValue.cpp +++ b/lldb/source/API/SBValue.cpp @@ -1294,6 +1294,87 @@ bool SBValue::GetExpressionPath(SBStream &description, return false; } +lldb::SBValue SBValue::EvaluateExpression(const char* expr) const { + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (!value_sp) + return SBValue(); + + lldb::TargetSP target_sp = value_sp->GetTargetSP(); + if (!target_sp) + return SBValue(); + + lldb::SBExpressionOptions options; + options.SetFetchDynamicValue(target_sp->GetPreferDynamicValue()); + options.SetUnwindOnError(true); + options.SetIgnoreBreakpoints(true); + + return EvaluateExpression(expr, options, nullptr); +} + +lldb::SBValue +SBValue::EvaluateExpression(const char *expr, + const SBExpressionOptions &options) const { + return EvaluateExpression(expr, options, nullptr); +} + +lldb::SBValue SBValue::EvaluateExpression(const char *expr, + const SBExpressionOptions &options, + const char *name) const { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); + + if (!expr || expr[0] == '\0') { + LLDB_LOG(log, + "SBValue::EvaluateExpression called with an empty expression"); + return SBValue(); + } + + LLDB_LOG(log, "SBValue()::EvaluateExpression (expr=\"{0}\")...", expr); + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (!value_sp) { + LLDB_LOG(log, "SBValue::EvaluateExpression () => error: could not " + "reconstruct value object for this SBValue"); + return SBValue(); + } + + lldb::TargetSP target_sp = value_sp->GetTargetSP(); + if (!target_sp) { + LLDB_LOG( + log, + "SBValue::EvaluateExpression () => error: could not retrieve target"); + return SBValue(); + } + + std::lock_guard guard(target_sp->GetAPIMutex()); + ExecutionContext exe_ctx(target_sp.get()); + + StackFrame *frame = exe_ctx.GetFramePtr(); + if (!frame) { + LLDB_LOG(log, "SBValue::EvaluateExpression () => error: could not retrieve " + "current stack frame"); + return SBValue(); + } + + ValueObjectSP res_val_sp; + ExpressionResults expr_res = target_sp->EvaluateExpression( + expr, frame, res_val_sp, options.ref(), nullptr, value_sp.get()); + + if (name) + res_val_sp->SetName(ConstString(name)); + + LLDB_LOG(log, + "SBValue(Name=\"{0}\")::EvaluateExpression (expr=\"{1}\") => " + "SBValue(Success={2}) (execution result={3})", + value_sp->GetName(), expr, res_val_sp->GetError().Success(), + expr_res); + + SBValue result; + result.SetSP(res_val_sp, options.GetFetchDynamicValue()); + return result; +} + bool SBValue::GetDescription(SBStream &description) { Stream &strm = description.ref(); diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp index c337fe6..99a9982 100644 --- a/lldb/source/Breakpoint/BreakpointLocation.cpp +++ b/lldb/source/Breakpoint/BreakpointLocation.cpp @@ -255,7 +255,7 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx, m_user_expression_sp.reset(GetTarget().GetUserExpressionForLanguage( condition_text, llvm::StringRef(), language, Expression::eResultTypeAny, - EvaluateExpressionOptions(), error)); + EvaluateExpressionOptions(), nullptr, error)); if (error.Fail()) { if (log) log->Printf("Error getting condition expression: %s.", diff --git a/lldb/source/Breakpoint/Watchpoint.cpp b/lldb/source/Breakpoint/Watchpoint.cpp index 8ff8744..77e9863 100644 --- a/lldb/source/Breakpoint/Watchpoint.cpp +++ b/lldb/source/Breakpoint/Watchpoint.cpp @@ -279,7 +279,8 @@ void Watchpoint::SetCondition(const char *condition) { Status error; m_condition_ap.reset(m_target.GetUserExpressionForLanguage( condition, llvm::StringRef(), lldb::eLanguageTypeUnknown, - UserExpression::eResultTypeAny, EvaluateExpressionOptions(), error)); + UserExpression::eResultTypeAny, EvaluateExpressionOptions(), + nullptr, error)); if (error.Fail()) { // FIXME: Log something... m_condition_ap.reset(); diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index e04e0b7..c85e54c 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -363,7 +363,7 @@ int CommandObjectExpression::HandleCompletion(CompletionRequest &request) { Status error; lldb::UserExpressionSP expr(target->GetUserExpressionForLanguage( code, llvm::StringRef(), language, UserExpression::eResultTypeAny, - options, error)); + options, nullptr, error)); if (error.Fail()) return 0; diff --git a/lldb/source/Expression/ExpressionSourceCode.cpp b/lldb/source/Expression/ExpressionSourceCode.cpp index d19321a..a146472 100644 --- a/lldb/source/Expression/ExpressionSourceCode.cpp +++ b/lldb/source/Expression/ExpressionSourceCode.cpp @@ -178,7 +178,8 @@ static void AddLocalVariableDecls(const lldb::VariableListSP &var_list_sp, bool ExpressionSourceCode::GetText(std::string &text, lldb::LanguageType wrapping_language, bool static_method, - ExecutionContext &exe_ctx) const { + ExecutionContext &exe_ctx, + bool add_locals) const { const char *target_specific_defines = "typedef signed char BOOL;\n"; std::string module_macros; @@ -251,12 +252,13 @@ bool ExpressionSourceCode::GetText(std::string &text, } } - ConstString object_name; - if (Language::LanguageIsCPlusPlus(frame->GetLanguage())) { - if (target->GetInjectLocalVariables(&exe_ctx)) { - lldb::VariableListSP var_list_sp = - frame->GetInScopeVariableList(false, true); - AddLocalVariableDecls(var_list_sp, lldb_local_var_decls); + if (add_locals) { + if (Language::LanguageIsCPlusPlus(frame->GetLanguage())) { + if (target->GetInjectLocalVariables(&exe_ctx)) { + lldb::VariableListSP var_list_sp = + frame->GetInScopeVariableList(false, true); + AddLocalVariableDecls(var_list_sp, lldb_local_var_decls); + } } } } diff --git a/lldb/source/Expression/UserExpression.cpp b/lldb/source/Expression/UserExpression.cpp index 87e560b..4fb5617 100644 --- a/lldb/source/Expression/UserExpression.cpp +++ b/lldb/source/Expression/UserExpression.cpp @@ -140,10 +140,22 @@ lldb::ExpressionResults UserExpression::Evaluate( ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options, llvm::StringRef expr, llvm::StringRef prefix, lldb::ValueObjectSP &result_valobj_sp, Status &error, uint32_t line_offset, - std::string *fixed_expression, lldb::ModuleSP *jit_module_sp_ptr) { + std::string *fixed_expression, lldb::ModuleSP *jit_module_sp_ptr, + ValueObject *ctx_obj) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + if (ctx_obj) { + static unsigned const ctx_type_mask = + lldb::TypeFlags::eTypeIsClass | lldb::TypeFlags::eTypeIsStructUnion; + if (!(ctx_obj->GetTypeInfo() & ctx_type_mask)) { + LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a context object of " + "an invalid type, can't run expressions."); + error.SetErrorString("a context object of an invalid type passed"); + return lldb::eExpressionSetupError; + } + } + lldb_private::ExecutionPolicy execution_policy = options.GetExecutionPolicy(); lldb::LanguageType language = options.GetLanguage(); const ResultType desired_type = options.DoesCoerceToId() @@ -208,7 +220,8 @@ lldb::ExpressionResults UserExpression::Evaluate( lldb::UserExpressionSP user_expression_sp( target->GetUserExpressionForLanguage(expr, full_prefix, language, - desired_type, options, error)); + desired_type, options, ctx_obj, + error)); if (error.Fail()) { if (log) log->Printf("== [UserExpression::Evaluate] Getting expression: %s ==", @@ -253,7 +266,8 @@ lldb::ExpressionResults UserExpression::Evaluate( lldb::UserExpressionSP fixed_expression_sp( target->GetUserExpressionForLanguage(fixed_expression->c_str(), full_prefix, language, - desired_type, options, error)); + desired_type, options, ctx_obj, + error)); DiagnosticManager fixed_diagnostic_manager; parse_success = fixed_expression_sp->Parse( fixed_diagnostic_manager, exe_ctx, execution_policy, diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp index 745341b..6e7f11f 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -65,10 +65,12 @@ const char *g_lldb_local_vars_namespace_cstr = "$__lldb_local_vars"; ClangExpressionDeclMap::ClangExpressionDeclMap( bool keep_result_in_memory, Materializer::PersistentVariableDelegate *result_delegate, - ExecutionContext &exe_ctx) + ExecutionContext &exe_ctx, + ValueObject *ctx_obj) : ClangASTSource(exe_ctx.GetTargetSP()), m_found_entities(), m_struct_members(), m_keep_result_in_memory(keep_result_in_memory), - m_result_delegate(result_delegate), m_parser_vars(), m_struct_vars() { + m_result_delegate(result_delegate), m_parser_vars(), m_struct_vars(), + m_ctx_obj(ctx_obj) { EnableStructVars(); } @@ -927,6 +929,21 @@ void ClangExpressionDeclMap::FindExternalVisibleDecls( static ConstString g_lldb_class_name("$__lldb_class"); if (name == g_lldb_class_name) { + if (m_ctx_obj) { + Status status; + lldb::ValueObjectSP ctx_obj_ptr = m_ctx_obj->AddressOf(status); + if (!ctx_obj_ptr || status.Fail()) + return; + + AddThisType(context, TypeFromUser(m_ctx_obj->GetCompilerType()), + current_id); + + m_struct_vars->m_object_pointer_type = + TypeFromUser(ctx_obj_ptr->GetCompilerType()); + + return; + } + // Clang is looking for the type of "this" if (frame == NULL) @@ -1019,6 +1036,21 @@ void ClangExpressionDeclMap::FindExternalVisibleDecls( static ConstString g_lldb_objc_class_name("$__lldb_objc_class"); if (name == g_lldb_objc_class_name) { + if (m_ctx_obj) { + Status status; + lldb::ValueObjectSP ctx_obj_ptr = m_ctx_obj->AddressOf(status); + if (!ctx_obj_ptr || status.Fail()) + return; + + AddOneType(context, TypeFromUser(m_ctx_obj->GetCompilerType()), + current_id); + + m_struct_vars->m_object_pointer_type = + TypeFromUser(ctx_obj_ptr->GetCompilerType()); + + return; + } + // Clang is looking for the type of "*self" if (!frame) @@ -2124,7 +2156,7 @@ void ClangExpressionDeclMap::AddOneFunction(NameSearchContext &context, } void ClangExpressionDeclMap::AddThisType(NameSearchContext &context, - TypeFromUser &ut, + const TypeFromUser &ut, unsigned int current_id) { CompilerType copied_clang_type = GuardedCopyType(ut); @@ -2198,7 +2230,7 @@ void ClangExpressionDeclMap::AddThisType(NameSearchContext &context, } void ClangExpressionDeclMap::AddOneType(NameSearchContext &context, - TypeFromUser &ut, + const TypeFromUser &ut, unsigned int current_id) { CompilerType copied_clang_type = GuardedCopyType(ut); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h index d860979..ac6690d 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h @@ -72,11 +72,16 @@ public: /// /// @param[in] exe_ctx /// The execution context to use when parsing. + /// + /// @param[in] ctx_obj + /// If not empty, then expression is evaluated in context of this object. + /// See the comment to `UserExpression::Evaluate` for details. //------------------------------------------------------------------ ClangExpressionDeclMap( bool keep_result_in_memory, Materializer::PersistentVariableDelegate *result_delegate, - ExecutionContext &exe_ctx); + ExecutionContext &exe_ctx, + ValueObject *ctx_obj); //------------------------------------------------------------------ /// Destructor @@ -343,6 +348,10 @@ private: Materializer::PersistentVariableDelegate *m_result_delegate; ///< If non-NULL, used to report expression results to ///ClangUserExpression. + ValueObject *m_ctx_obj; ///< If not empty, then expression is + ///evaluated in context of this object. + ///For details see the comment to + ///`UserExpression::Evaluate`. //---------------------------------------------------------------------- /// The following values should not live beyond parsing @@ -581,7 +590,7 @@ private: /// @param[in] type /// The type that needs to be created. //------------------------------------------------------------------ - void AddOneType(NameSearchContext &context, TypeFromUser &type, + void AddOneType(NameSearchContext &context, const TypeFromUser &type, unsigned int current_id); //------------------------------------------------------------------ @@ -594,7 +603,7 @@ private: /// @param[in] type /// The type for *this. //------------------------------------------------------------------ - void AddThisType(NameSearchContext &context, TypeFromUser &type, + void AddThisType(NameSearchContext &context, const TypeFromUser &type, unsigned int current_id); //------------------------------------------------------------------ diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp index e7e9ba9..f967237 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -59,13 +59,15 @@ using namespace lldb_private; ClangUserExpression::ClangUserExpression( ExecutionContextScope &exe_scope, llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, - ResultType desired_type, const EvaluateExpressionOptions &options) + ResultType desired_type, const EvaluateExpressionOptions &options, + ValueObject *ctx_obj) : LLVMUserExpression(exe_scope, expr, prefix, language, desired_type, options), m_type_system_helper(*m_target_wp.lock().get(), options.GetExecutionPolicy() == eExecutionPolicyTopLevel), - m_result_delegate(exe_scope.CalculateTarget()) { + m_result_delegate(exe_scope.CalculateTarget()), + m_ctx_obj(ctx_obj) { switch (m_language) { case lldb::eLanguageTypeC_plus_plus: m_allow_cxx = true; @@ -130,7 +132,27 @@ void ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Status &err) { return; } - if (clang::CXXMethodDecl *method_decl = + if (m_ctx_obj) { + switch (m_ctx_obj->GetObjectRuntimeLanguage()) { + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC89: + case lldb::eLanguageTypeC99: + case lldb::eLanguageTypeC11: + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeC_plus_plus_03: + case lldb::eLanguageTypeC_plus_plus_11: + case lldb::eLanguageTypeC_plus_plus_14: + m_in_cplusplus_method = true; + break; + case lldb::eLanguageTypeObjC: + case lldb::eLanguageTypeObjC_plus_plus: + m_in_objectivec_method = true; + break; + default: + break; + } + m_needs_object_ptr = true; + } else if (clang::CXXMethodDecl *method_decl = ClangASTContext::DeclContextGetAsCXXMethodDecl(decl_context)) { if (m_allow_cxx && method_decl->isInstance()) { if (m_enforce_valid_object) { @@ -396,7 +418,8 @@ void ClangUserExpression::UpdateLanguageForExpr( m_expr_lang = lldb::eLanguageTypeC; if (!source_code->GetText(m_transformed_text, m_expr_lang, - m_in_static_method, exe_ctx)) { + m_in_static_method, exe_ctx, + !m_ctx_obj)) { diagnostic_manager.PutString(eDiagnosticSeverityError, "couldn't construct expression body"); return; @@ -733,7 +756,15 @@ bool ClangUserExpression::AddArguments(ExecutionContext &exe_ctx, Status object_ptr_error; - object_ptr = GetObjectPointer(frame_sp, object_name, object_ptr_error); + if (m_ctx_obj) { + AddressType address_type; + object_ptr = m_ctx_obj->GetAddressOf(false, &address_type); + if (object_ptr == LLDB_INVALID_ADDRESS || + address_type != eAddressTypeLoad) + object_ptr_error.SetErrorString("Can't get context object's " + "debuggee address"); + } else + object_ptr = GetObjectPointer(frame_sp, object_name, object_ptr_error); if (!object_ptr_error.Success()) { exe_ctx.GetTargetRef().GetDebugger().GetAsyncOutputStream()->Printf( @@ -776,9 +807,11 @@ lldb::ExpressionVariableSP ClangUserExpression::GetResultAfterDematerialization( void ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap( ExecutionContext &exe_ctx, Materializer::PersistentVariableDelegate &delegate, - bool keep_result_in_memory) { + bool keep_result_in_memory, + ValueObject *ctx_obj) { m_expr_decl_map_up.reset( - new ClangExpressionDeclMap(keep_result_in_memory, &delegate, exe_ctx)); + new ClangExpressionDeclMap(keep_result_in_memory, &delegate, exe_ctx, + ctx_obj)); } clang::ASTConsumer * diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h index 836d44e..2f9a723 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h @@ -61,7 +61,8 @@ public: void ResetDeclMap(ExecutionContext &exe_ctx, Materializer::PersistentVariableDelegate &result_delegate, - bool keep_result_in_memory); + bool keep_result_in_memory, + ValueObject *ctx_obj); //------------------------------------------------------------------ /// Return the object that the parser should allow to access ASTs. May be @@ -105,11 +106,17 @@ public: /// @param[in] desired_type /// If not eResultTypeAny, the type to use for the expression /// result. + /// + /// @param[in] ctx_obj + /// The object (if any) in which context the expression + /// must be evaluated. For details see the comment to + /// `UserExpression::Evaluate`. //------------------------------------------------------------------ ClangUserExpression(ExecutionContextScope &exe_scope, llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, ResultType desired_type, - const EvaluateExpressionOptions &options); + const EvaluateExpressionOptions &options, + ValueObject *ctx_obj); ~ClangUserExpression() override; @@ -153,7 +160,8 @@ public: Materializer::PersistentVariableDelegate &result_delegate, bool keep_result_in_memory) { m_type_system_helper.ResetDeclMap(exe_ctx, result_delegate, - keep_result_in_memory); + keep_result_in_memory, + m_ctx_obj); } lldb::ExpressionVariableSP @@ -204,6 +212,10 @@ private: /// were not able to calculate this position. llvm::Optional m_user_expression_start_pos; ResultDelegate m_result_delegate; + + /// The object (if any) in which context the expression is evaluated. + /// See the comment to `UserExpression::Evaluate` for details. + ValueObject *m_ctx_obj; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp index 210ed3c..c15bfa5 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp @@ -156,5 +156,6 @@ bool ClangUtilityFunction::Install(DiagnosticManager &diagnostic_manager, void ClangUtilityFunction::ClangUtilityFunctionHelper::ResetDeclMap( ExecutionContext &exe_ctx, bool keep_result_in_memory) { m_expr_decl_map_up.reset( - new ClangExpressionDeclMap(keep_result_in_memory, nullptr, exe_ctx)); + new ClangExpressionDeclMap(keep_result_in_memory, nullptr, exe_ctx, + nullptr)); } diff --git a/lldb/source/Symbol/ClangASTContext.cpp b/lldb/source/Symbol/ClangASTContext.cpp index 5d22afa..cc30e63 100644 --- a/lldb/source/Symbol/ClangASTContext.cpp +++ b/lldb/source/Symbol/ClangASTContext.cpp @@ -10317,13 +10317,14 @@ ClangASTContextForExpressions::ClangASTContextForExpressions(Target &target) UserExpression *ClangASTContextForExpressions::GetUserExpression( llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, Expression::ResultType desired_type, - const EvaluateExpressionOptions &options) { + const EvaluateExpressionOptions &options, + ValueObject *ctx_obj) { TargetSP target_sp = m_target_wp.lock(); if (!target_sp) return nullptr; return new ClangUserExpression(*target_sp.get(), expr, prefix, language, - desired_type, options); + desired_type, options, ctx_obj); } FunctionCaller *ClangASTContextForExpressions::GetFunctionCaller( diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 0cd1457..bc0bdae 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -2199,7 +2199,8 @@ Target::GetPersistentExpressionStateForLanguage(lldb::LanguageType language) { UserExpression *Target::GetUserExpressionForLanguage( llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, Expression::ResultType desired_type, - const EvaluateExpressionOptions &options, Status &error) { + const EvaluateExpressionOptions &options, + ValueObject *ctx_obj, Status &error) { Status type_system_error; TypeSystem *type_system = @@ -2215,7 +2216,7 @@ UserExpression *Target::GetUserExpressionForLanguage( } user_expr = type_system->GetUserExpression(expr, prefix, language, - desired_type, options); + desired_type, options, ctx_obj); if (!user_expr) error.SetErrorStringWithFormat( "Could not create an expression for language %s", @@ -2356,7 +2357,8 @@ Target *Target::GetTargetFromContexts(const ExecutionContext *exe_ctx_ptr, ExpressionResults Target::EvaluateExpression( llvm::StringRef expr, ExecutionContextScope *exe_scope, lldb::ValueObjectSP &result_valobj_sp, - const EvaluateExpressionOptions &options, std::string *fixed_expression) { + const EvaluateExpressionOptions &options, std::string *fixed_expression, + ValueObject *ctx_obj) { result_valobj_sp.reset(); ExpressionResults execution_results = eExpressionSetupError; @@ -2397,7 +2399,9 @@ ExpressionResults Target::EvaluateExpression( execution_results = UserExpression::Evaluate(exe_ctx, options, expr, prefix, result_valobj_sp, error, 0, // Line Number - fixed_expression); + fixed_expression, + nullptr, // Module + ctx_obj); } m_suppress_stop_hooks = old_suppress_value; -- 2.7.4