| """ 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.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| @skipIfWasm # no expression evaluation | |
| 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="const int", result_value="5") | |
| self.expect_expr("local2", result_type="const 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"), | |
| ) |