Skip to content

Commit 40624a0

Browse files
author
Aleksandr Urakov
committed
[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
1 parent de5220e commit 40624a0

File tree

26 files changed

+596
-44
lines changed

26 files changed

+596
-44
lines changed

lldb/include/lldb/API/SBValue.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,13 @@ class LLDB_API SBValue {
306306
bool GetExpressionPath(lldb::SBStream &description,
307307
bool qualify_cxx_base_classes);
308308

309+
lldb::SBValue EvaluateExpression(const char *expr) const;
310+
lldb::SBValue EvaluateExpression(const char *expr,
311+
const SBExpressionOptions &options) const;
312+
lldb::SBValue EvaluateExpression(const char *expr,
313+
const SBExpressionOptions &options,
314+
const char *name) const;
315+
309316
SBValue(const lldb::ValueObjectSP &value_sp);
310317

311318
//------------------------------------------------------------------

lldb/include/lldb/Expression/ExpressionSourceCode.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ class ExpressionSourceCode {
3636
const char *GetName() const { return m_name.c_str(); }
3737

3838
bool GetText(std::string &text, lldb::LanguageType wrapping_language,
39-
bool static_method, ExecutionContext &exe_ctx) const;
39+
bool static_method, ExecutionContext &exe_ctx,
40+
bool add_locals) const;
4041

4142
// Given a string returned by GetText, find the beginning and end of the body
4243
// passed to CreateWrapped. Return true if the bounds could be found. This

lldb/include/lldb/Expression/UserExpression.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,16 @@ class UserExpression : public Expression {
272272
/// @param[out] jit_module_sp_ptr
273273
/// If non-nullptr, used to persist the generated IR module.
274274
///
275+
/// @param[in] ctx_obj
276+
/// If specified, then the expression will be evaluated in the context of
277+
/// this object. It means that the context object's address will be
278+
/// treated as `this` for the expression (the expression will be
279+
/// evaluated as if it was inside of a method of the context object's
280+
/// class, and its `this` parameter were pointing to the context object).
281+
/// The parameter makes sense for class and union types only.
282+
/// Currently there is a limitation: the context object must be located
283+
/// in the debuggee process' memory (and have the load address).
284+
///
275285
/// @result
276286
/// A Process::ExpressionResults value. eExpressionCompleted for
277287
/// success.
@@ -281,7 +291,8 @@ class UserExpression : public Expression {
281291
llvm::StringRef expr_cstr, llvm::StringRef expr_prefix,
282292
lldb::ValueObjectSP &result_valobj_sp, Status &error,
283293
uint32_t line_offset = 0, std::string *fixed_expression = nullptr,
284-
lldb::ModuleSP *jit_module_sp_ptr = nullptr);
294+
lldb::ModuleSP *jit_module_sp_ptr = nullptr,
295+
ValueObject *ctx_obj = nullptr);
285296

286297
static const Status::ValueType kNoResult =
287298
0x1001; ///< ValueObject::GetError() returns this if there is no result

lldb/include/lldb/Symbol/ClangASTContext.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,8 @@ class ClangASTContextForExpressions : public ClangASTContext {
10601060
GetUserExpression(llvm::StringRef expr, llvm::StringRef prefix,
10611061
lldb::LanguageType language,
10621062
Expression::ResultType desired_type,
1063-
const EvaluateExpressionOptions &options) override;
1063+
const EvaluateExpressionOptions &options,
1064+
ValueObject *ctx_obj) override;
10641065

10651066
FunctionCaller *GetFunctionCaller(const CompilerType &return_type,
10661067
const Address &function_address,

lldb/include/lldb/Symbol/TypeSystem.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,8 @@ class TypeSystem : public PluginInterface {
449449
GetUserExpression(llvm::StringRef expr, llvm::StringRef prefix,
450450
lldb::LanguageType language,
451451
Expression::ResultType desired_type,
452-
const EvaluateExpressionOptions &options) {
452+
const EvaluateExpressionOptions &options,
453+
ValueObject *ctx_obj) {
453454
return nullptr;
454455
}
455456

lldb/include/lldb/Target/Target.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,8 @@ class Target : public std::enable_shared_from_this<Target>,
10431043
UserExpression *GetUserExpressionForLanguage(
10441044
llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language,
10451045
Expression::ResultType desired_type,
1046-
const EvaluateExpressionOptions &options, Status &error);
1046+
const EvaluateExpressionOptions &options,
1047+
ValueObject *ctx_obj, Status &error);
10471048

10481049
// Creates a FunctionCaller for the given language, the rest of the
10491050
// parameters have the same meaning as for the FunctionCaller constructor.
@@ -1107,7 +1108,8 @@ class Target : public std::enable_shared_from_this<Target>,
11071108
llvm::StringRef expression, ExecutionContextScope *exe_scope,
11081109
lldb::ValueObjectSP &result_valobj_sp,
11091110
const EvaluateExpressionOptions &options = EvaluateExpressionOptions(),
1110-
std::string *fixed_expression = nullptr);
1111+
std::string *fixed_expression = nullptr,
1112+
ValueObject *ctx_obj = nullptr);
11111113

11121114
lldb::ExpressionVariableSP GetPersistentVariable(const ConstString &name);
11131115

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
LEVEL = ../../make
2+
3+
OBJC_SOURCES := main.m
4+
5+
include $(LEVEL)/Makefile.rules
6+
LDFLAGS += -framework Foundation
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
Tests expression evaluation in context of an objc class.
3+
"""
4+
5+
import lldb
6+
import lldbsuite.test.lldbutil as lldbutil
7+
from lldbsuite.test.decorators import *
8+
from lldbsuite.test.lldbtest import *
9+
10+
class ContextObjectObjcTestCase(TestBase):
11+
12+
mydir = TestBase.compute_mydir(__file__)
13+
14+
@skipUnlessDarwin
15+
def test_context_object_objc(self):
16+
"""Tests expression evaluation in context of an objc class."""
17+
self.build()
18+
19+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, '// Break here', self.main_source_spec)
20+
frame = thread.GetFrameAtIndex(0)
21+
22+
#
23+
# Test objc class variable
24+
#
25+
26+
obj_val = frame.FindVariable("objcClass")
27+
self.assertTrue(obj_val.IsValid())
28+
obj_val = obj_val.Dereference()
29+
self.assertTrue(obj_val.IsValid())
30+
31+
# Test an empty expression evaluation
32+
value = obj_val.EvaluateExpression("")
33+
self.assertFalse(value.IsValid())
34+
self.assertFalse(value.GetError().Success())
35+
36+
# Test retrieving of a field (not a local with the same name)
37+
value = obj_val.EvaluateExpression("field")
38+
self.assertTrue(value.IsValid())
39+
self.assertTrue(value.GetError().Success())
40+
self.assertEqual(value.GetValueAsSigned(), 1111)
41+
42+
# Test if the self pointer is properly evaluated
43+
44+
# Test retrieving of an objcClass's property through the self pointer
45+
value = obj_val.EvaluateExpression("self.property")
46+
self.assertTrue(value.IsValid())
47+
self.assertTrue(value.GetError().Success())
48+
self.assertEqual(value.GetValueAsSigned(), 2222)
49+
50+
# Test objcClass's methods evaluation through the self pointer
51+
value = obj_val.EvaluateExpression("[self method]")
52+
self.assertTrue(value.IsValid())
53+
self.assertTrue(value.GetError().Success())
54+
self.assertEqual(value.GetValueAsSigned(), 3333)
55+
56+
# Test if we can use a computation result reference object correctly
57+
58+
obj_val = frame.EvaluateExpression("[ObjcClass createNew]")
59+
self.assertTrue(obj_val.IsValid())
60+
obj_val = obj_val.Dereference()
61+
self.assertTrue(obj_val.IsValid())
62+
63+
# Test an expression evaluation on it
64+
value = obj_val.EvaluateExpression("1")
65+
self.assertTrue(value.IsValid())
66+
self.assertTrue(value.GetError().Success())
67+
68+
# Test retrieving of a field on it
69+
value = obj_val.EvaluateExpression("field")
70+
self.assertTrue(value.IsValid())
71+
self.assertTrue(value.GetError().Success())
72+
self.assertEqual(value.GetValueAsSigned(), 1111)
73+
74+
def setUp(self):
75+
TestBase.setUp(self)
76+
77+
self.main_source = "main.m"
78+
self.main_source_spec = lldb.SBFileSpec(self.main_source)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface ObjcClass : NSObject {
4+
int field;
5+
}
6+
7+
@property int property;
8+
9+
+(ObjcClass*)createNew;
10+
11+
-(id)init;
12+
13+
-(int)method;
14+
15+
@end
16+
17+
@implementation ObjcClass
18+
19+
+(ObjcClass*)createNew {
20+
return [ObjcClass new];
21+
}
22+
23+
-(id)init {
24+
self = [super init];
25+
if (self) {
26+
field = 1111;
27+
_property = 2222;
28+
}
29+
return self;
30+
}
31+
32+
-(int)method {
33+
return 3333;
34+
}
35+
36+
@end
37+
38+
int main()
39+
{
40+
@autoreleasepool {
41+
ObjcClass* objcClass = [ObjcClass new];
42+
43+
int field = 4444;
44+
45+
return 0; // Break here
46+
}
47+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
LEVEL = ../../make
2+
3+
CXX_SOURCES := main.cpp
4+
5+
include $(LEVEL)/Makefile.rules
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
"""
2+
Tests expression evaluation in context of an object.
3+
"""
4+
5+
import lldb
6+
import lldbsuite.test.lldbutil as lldbutil
7+
from lldbsuite.test.lldbtest import *
8+
9+
class ContextObjectTestCase(TestBase):
10+
11+
mydir = TestBase.compute_mydir(__file__)
12+
13+
def test_context_object(self):
14+
"""Tests expression evaluation in context of an object."""
15+
self.build()
16+
17+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, '// Break here', self.main_source_spec)
18+
frame = thread.GetFrameAtIndex(0)
19+
20+
#
21+
# Test C++ struct variable
22+
#
23+
24+
obj_val = frame.FindVariable("cpp_struct")
25+
self.assertTrue(obj_val.IsValid())
26+
27+
# Test an empty expression evaluation
28+
value = obj_val.EvaluateExpression("")
29+
self.assertFalse(value.IsValid())
30+
self.assertFalse(value.GetError().Success())
31+
32+
# Test retrieveing of a field (not a local with the same name)
33+
value = obj_val.EvaluateExpression("field")
34+
self.assertTrue(value.IsValid())
35+
self.assertTrue(value.GetError().Success())
36+
self.assertEqual(value.GetValueAsSigned(), 1111)
37+
38+
# Test functions evaluation
39+
value = obj_val.EvaluateExpression("function()")
40+
self.assertTrue(value.IsValid())
41+
self.assertTrue(value.GetError().Success())
42+
self.assertEqual(value.GetValueAsSigned(), 2222)
43+
44+
# Test that we retrieve the right global
45+
value = obj_val.EvaluateExpression("global.field")
46+
self.assertTrue(value.IsValid())
47+
self.assertTrue(value.GetError().Success())
48+
self.assertEqual(value.GetValueAsSigned(), 1111)
49+
50+
#
51+
# Test C++ union variable
52+
#
53+
54+
obj_val = frame.FindVariable("cpp_union")
55+
self.assertTrue(obj_val.IsValid())
56+
57+
# Test retrieveing of a field
58+
value = obj_val.EvaluateExpression("field_int")
59+
self.assertTrue(value.IsValid())
60+
self.assertTrue(value.GetError().Success())
61+
self.assertEqual(value.GetValueAsSigned(), 5555)
62+
63+
#
64+
# Test C++ scalar
65+
#
66+
67+
obj_val = frame.FindVariable("cpp_scalar")
68+
self.assertTrue(obj_val.IsValid())
69+
70+
# Test an expression evaluation
71+
value = obj_val.EvaluateExpression("1")
72+
self.assertFalse(value.IsValid())
73+
self.assertFalse(value.GetError().Success())
74+
75+
#
76+
# Test C++ array
77+
#
78+
79+
obj_val = frame.FindVariable("cpp_array")
80+
self.assertTrue(obj_val.IsValid())
81+
82+
# Test an expression evaluation
83+
value = obj_val.EvaluateExpression("1")
84+
self.assertFalse(value.IsValid())
85+
self.assertFalse(value.GetError().Success())
86+
87+
# Test retrieveing of an element's field
88+
value = obj_val.GetValueForExpressionPath("[7]").EvaluateExpression("field")
89+
self.assertTrue(value.IsValid())
90+
self.assertTrue(value.GetError().Success())
91+
self.assertEqual(value.GetValueAsSigned(), 1111)
92+
93+
#
94+
# Test C++ pointer
95+
#
96+
97+
obj_val = frame.FindVariable("cpp_pointer")
98+
self.assertTrue(obj_val.IsValid())
99+
100+
# Test an expression evaluation
101+
value = obj_val.EvaluateExpression("1")
102+
self.assertFalse(value.IsValid())
103+
self.assertFalse(value.GetError().Success())
104+
105+
# Test retrieveing of a dereferenced object's field
106+
value = obj_val.Dereference().EvaluateExpression("field")
107+
self.assertTrue(value.IsValid())
108+
self.assertTrue(value.GetError().Success())
109+
self.assertEqual(value.GetValueAsSigned(), 1111)
110+
111+
#
112+
# Test C++ computation result
113+
#
114+
115+
obj_val = frame.EvaluateExpression("cpp_namespace::GetCppStruct()")
116+
self.assertTrue(obj_val.IsValid())
117+
118+
# Test an expression evaluation
119+
value = obj_val.EvaluateExpression("1")
120+
self.assertTrue(value.IsValid())
121+
self.assertFalse(value.GetError().Success())
122+
123+
#
124+
# Test C++ computation result located in debuggee memory
125+
#
126+
127+
obj_val = frame.EvaluateExpression("cpp_namespace::GetCppStructPtr()")
128+
self.assertTrue(obj_val.IsValid())
129+
130+
# Test an expression evaluation
131+
value = obj_val.EvaluateExpression("1")
132+
self.assertFalse(value.IsValid())
133+
self.assertFalse(value.GetError().Success())
134+
135+
# Test retrieveing of a dereferenced object's field
136+
value = obj_val.Dereference().EvaluateExpression("field")
137+
self.assertTrue(value.IsValid())
138+
self.assertTrue(value.GetError().Success())
139+
self.assertEqual(value.GetValueAsSigned(), 1111)
140+
141+
def setUp(self):
142+
TestBase.setUp(self)
143+
144+
self.main_source = "main.cpp"
145+
self.main_source_spec = lldb.SBFileSpec(self.main_source)

0 commit comments

Comments
 (0)