| """ Test that evaluating expressions from within C++ lambdas works |
| Particularly, we test the case of capturing "this" and |
| using members of the captured object in expression evaluation |
| while we're on a breakpoint inside a lambda. |
| """ |
| |
| |
| import lldb |
| from lldbsuite.test.lldbtest import * |
| |
| |
| class ExprInsideLambdaTestCase(TestBase): |
| def expectExprError(self, expr: str, expected: str): |
| frame = self.thread.GetFrameAtIndex(0) |
| value = frame.EvaluateExpression(expr) |
| errmsg = value.GetError().GetCString() |
| self.assertIn(expected, errmsg) |
| |
| def test_expr_inside_lambda(self): |
| """Test that lldb evaluating expressions inside lambda expressions works correctly.""" |
| self.build() |
| (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint( |
| self, "break here", lldb.SBFileSpec("main.cpp") |
| ) |
| |
| # Inside 'Foo::method' |
| |
| # Check access to captured 'this' |
| self.expect_expr("class_var", result_type="int", result_value="109") |
| self.expect_expr("this->class_var", result_type="int", result_value="109") |
| |
| # Check that captured shadowed variables take preference over the |
| # corresponding member variable |
| self.expect_expr("shadowed", result_type="int", result_value="5") |
| self.expect_expr("this->shadowed", result_type="int", result_value="-137") |
| |
| # Check access to local captures |
| self.expect_expr("local_var", result_type="int", result_value="137") |
| self.expect_expr("*class_ptr", result_type="int", result_value="137") |
| |
| # Check access to base class variables |
| self.expect_expr("base_var", result_type="int", result_value="14") |
| self.expect_expr("base_base_var", result_type="int", result_value="11") |
| |
| # Check access to global variable |
| self.expect_expr("global_var", result_type="int", result_value="-5") |
| |
| # Check access to multiple captures/member variables |
| self.expect_expr( |
| "(shadowed + this->shadowed) * (base_base_var + local_var - class_var)", |
| result_type="int", |
| result_value="-5148", |
| ) |
| |
| # Check base-class function call |
| self.expect_expr("baz_virt()", result_type="int", result_value="2") |
| self.expect_expr("base_var", result_type="int", result_value="14") |
| self.expect_expr("this->shadowed", result_type="int", result_value="-1") |
| |
| # 'p this' should yield 'struct Foo*' |
| frame = self.thread.GetFrameAtIndex(0) |
| outer_class_addr = frame.GetValueForVariablePath("this->this") |
| self.expect_expr("this", result_value=outer_class_addr.GetValue()) |
| |
| lldbutil.continue_to_breakpoint(process, bkpt) |
| |
| # Inside 'nested_lambda' |
| |
| # Check access to captured 'this'. Should still be 'struct Foo*' |
| self.expect_expr("class_var", result_type="int", result_value="109") |
| self.expect_expr("global_var", result_type="int", result_value="-5") |
| self.expect_expr("this", result_value=outer_class_addr.GetValue()) |
| |
| # Check access to captures |
| self.expect_expr("lambda_local_var", result_type="int", result_value="5") |
| self.expect_expr("local_var", result_type="int", result_value="137") |
| |
| # Check access to variable in previous frame which we didn't capture |
| self.expectExprError("local_var_copy", "use of undeclared identifier") |
| |
| lldbutil.continue_to_breakpoint(process, bkpt) |
| |
| # By-ref mutates source variable |
| self.expect_expr("lambda_local_var", result_type="int", result_value="0") |
| |
| # By-value doesn't mutate source variable |
| self.expect_expr("local_var_copy", result_type="int", result_value="136") |
| self.expect_expr("local_var", result_type="int", result_value="137") |
| |
| lldbutil.continue_to_breakpoint(process, bkpt) |
| |
| # Inside 'LocalLambdaClass::inner_method' |
| |
| # Check access to captured 'this' |
| self.expect_expr("lambda_class_local", result_type="int", result_value="-12345") |
| self.expect_expr( |
| "this->lambda_class_local", result_type="int", result_value="-12345" |
| ) |
| self.expect_expr("outer_ptr->class_var", result_type="int", result_value="109") |
| |
| # 'p this' should yield 'struct LocalLambdaClass*' |
| frame = self.thread.GetFrameAtIndex(0) |
| local_class_addr = frame.GetValueForVariablePath("this->this") |
| self.assertNotEqual(local_class_addr, outer_class_addr) |
| self.expect_expr("this", result_value=local_class_addr.GetValue()) |
| |
| # Can still access global variable |
| self.expect_expr("global_var", result_type="int", result_value="-5") |
| |
| # Check access to outer top-level structure's members |
| self.expectExprError( |
| "class_var", |
| ("use of non-static data member" " 'class_var' of 'Foo' from nested type"), |
| ) |
| |
| self.expectExprError( |
| "base_var", ("use of non-static data member" " 'base_var'") |
| ) |
| |
| self.expectExprError( |
| "local_var", |
| ( |
| "use of non-static data member 'local_var'" |
| " of '(unnamed class)' from nested type 'LocalLambdaClass'" |
| ), |
| ) |
| |
| # Inside non_capturing_method |
| lldbutil.continue_to_breakpoint(process, bkpt) |
| self.expect_expr("local", result_type="int", result_value="5") |
| self.expect_expr("local2", result_type="int", result_value="10") |
| self.expect_expr("local2 * local", result_type="int", result_value="50") |
| |
| self.expectExprError( |
| "class_var", |
| ("use of non-static data member" " 'class_var' of 'Foo' from nested type"), |
| ) |