xref: /llvm-project/lldb/test/API/lang/objc/objc-stepping/TestObjCStepping.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1"""Test stepping through ObjC method dispatch in various forms."""
2
3import lldb
4from lldbsuite.test.decorators import *
5from lldbsuite.test.lldbtest import *
6from lldbsuite.test import lldbutil
7
8
9class TestObjCStepping(TestBase):
10    def setUp(self):
11        # Call super's setUp().
12        TestBase.setUp(self)
13        # Find the line numbers that we will step to in main:
14        self.main_source = "stepping-tests.m"
15        self.source_randomMethod_line = line_number(
16            self.main_source, "// Source randomMethod start line."
17        )
18        self.sourceBase_randomMethod_line = line_number(
19            self.main_source, "// SourceBase randomMethod start line."
20        )
21        self.source_returnsStruct_start_line = line_number(
22            self.main_source, "// Source returnsStruct start line."
23        )
24        self.sourceBase_returnsStruct_start_line = line_number(
25            self.main_source, "// SourceBase returnsStruct start line."
26        )
27        self.stepped_past_nil_line = line_number(
28            self.main_source, "// Step over nil should stop here."
29        )
30
31    @add_test_categories(["pyapi", "basic_process"])
32    def test_with_python_api(self):
33        """Test stepping through ObjC method dispatch in various forms."""
34        self.build()
35        exe = self.getBuildArtifact("a.out")
36
37        target = self.dbg.CreateTarget(exe)
38        self.assertTrue(target, VALID_TARGET)
39
40        self.main_source_spec = lldb.SBFileSpec(self.main_source)
41
42        breakpoints_to_disable = []
43
44        break1 = target.BreakpointCreateBySourceRegex(
45            "// Set first breakpoint here.", self.main_source_spec
46        )
47        self.assertTrue(break1, VALID_BREAKPOINT)
48        breakpoints_to_disable.append(break1)
49
50        break2 = target.BreakpointCreateBySourceRegex(
51            "// Set second breakpoint here.", self.main_source_spec
52        )
53        self.assertTrue(break2, VALID_BREAKPOINT)
54        breakpoints_to_disable.append(break2)
55
56        break3 = target.BreakpointCreateBySourceRegex(
57            "// Set third breakpoint here.", self.main_source_spec
58        )
59        self.assertTrue(break3, VALID_BREAKPOINT)
60        breakpoints_to_disable.append(break3)
61
62        break4 = target.BreakpointCreateBySourceRegex(
63            "// Set fourth breakpoint here.", self.main_source_spec
64        )
65        self.assertTrue(break4, VALID_BREAKPOINT)
66        breakpoints_to_disable.append(break4)
67
68        break5 = target.BreakpointCreateBySourceRegex(
69            "// Set fifth breakpoint here.", self.main_source_spec
70        )
71        self.assertTrue(break5, VALID_BREAKPOINT)
72        breakpoints_to_disable.append(break5)
73
74        break_returnStruct_call_super = target.BreakpointCreateBySourceRegex(
75            "// Source returnsStruct call line.", self.main_source_spec
76        )
77        self.assertTrue(break_returnStruct_call_super, VALID_BREAKPOINT)
78        breakpoints_to_disable.append(break_returnStruct_call_super)
79
80        break_step_nil = target.BreakpointCreateBySourceRegex(
81            "// Set nil step breakpoint here.", self.main_source_spec
82        )
83        self.assertTrue(break_step_nil, VALID_BREAKPOINT)
84
85        # Now launch the process, and do not stop at entry point.
86        process = target.LaunchSimple(None, None, self.get_process_working_directory())
87
88        self.assertTrue(process, PROCESS_IS_VALID)
89
90        # The stop reason of the thread should be breakpoint.
91        threads = lldbutil.get_threads_stopped_at_breakpoint(process, break1)
92        if len(threads) != 1:
93            self.fail("Failed to stop at breakpoint 1.")
94
95        thread = threads[0]
96
97        mySource = thread.GetFrameAtIndex(0).FindVariable("mySource")
98        self.assertTrue(mySource, "Found mySource local variable.")
99        mySource_isa = mySource.GetChildMemberWithName("isa")
100        self.assertTrue(mySource_isa, "Found mySource->isa local variable.")
101        className = mySource_isa.GetSummary()
102
103        if self.TraceOn():
104            print(mySource_isa)
105
106        # Lets delete mySource so we can check that after stepping a child variable
107        # with no parent persists and is useful.
108        del mySource
109
110        # Now step in, that should leave us in the Source randomMethod:
111        thread.StepInto()
112        line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()
113        self.assertEqual(
114            line_number,
115            self.source_randomMethod_line,
116            "Stepped into Source randomMethod.",
117        )
118
119        # Now step in again, through the super call, and that should leave us
120        # in the SourceBase randomMethod:
121        thread.StepInto()
122        line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()
123        self.assertEqual(
124            line_number,
125            self.sourceBase_randomMethod_line,
126            "Stepped through super into SourceBase randomMethod.",
127        )
128
129        threads = lldbutil.continue_to_breakpoint(process, break2)
130        self.assertEqual(len(threads), 1, "Continued to second breakpoint in main.")
131
132        # Again, step in twice gets us to a stret method and a stret super
133        # call:
134        thread = threads[0]
135        thread.StepInto()
136        line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()
137        self.assertEqual(
138            line_number,
139            self.source_returnsStruct_start_line,
140            "Stepped into Source returnsStruct.",
141        )
142
143        threads = lldbutil.continue_to_breakpoint(
144            process, break_returnStruct_call_super
145        )
146        self.assertEqual(
147            len(threads), 1, "Stepped to the call super line in Source returnsStruct."
148        )
149        thread = threads[0]
150
151        thread.StepInto()
152        line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()
153        self.assertEqual(
154            line_number,
155            self.sourceBase_returnsStruct_start_line,
156            "Stepped through super into SourceBase returnsStruct.",
157        )
158
159        # Cool now continue to get past the call that initializes the Observer, and then do our steps in again to see that
160        # we can find our way when we're stepping through a KVO swizzled
161        # object.
162
163        threads = lldbutil.continue_to_breakpoint(process, break3)
164        self.assertEqual(
165            len(threads),
166            1,
167            "Continued to third breakpoint in main, our object should now be swizzled.",
168        )
169
170        newClassName = mySource_isa.GetSummary()
171
172        if self.TraceOn():
173            print("className is %s, newClassName is %s" % (className, newClassName))
174            print(mySource_isa)
175
176        self.assertNotEqual(
177            newClassName, className, "The isa did indeed change, swizzled!"
178        )
179
180        # Now step in, that should leave us in the Source randomMethod:
181        thread = threads[0]
182        thread.StepInto()
183        line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()
184        self.assertEqual(
185            line_number,
186            self.source_randomMethod_line,
187            "Stepped into Source randomMethod in swizzled object.",
188        )
189
190        # Now step in again, through the super call, and that should leave us
191        # in the SourceBase randomMethod:
192        thread.StepInto()
193        line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()
194        self.assertEqual(
195            line_number,
196            self.sourceBase_randomMethod_line,
197            "Stepped through super into SourceBase randomMethod in swizzled object.",
198        )
199
200        threads = lldbutil.continue_to_breakpoint(process, break4)
201        self.assertEqual(len(threads), 1, "Continued to fourth breakpoint in main.")
202        thread = threads[0]
203
204        # Again, step in twice gets us to a stret method and a stret super
205        # call:
206        thread.StepInto()
207        line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()
208        self.assertEqual(
209            line_number,
210            self.source_returnsStruct_start_line,
211            "Stepped into Source returnsStruct in swizzled object.",
212        )
213
214        threads = lldbutil.continue_to_breakpoint(
215            process, break_returnStruct_call_super
216        )
217        self.assertEqual(
218            len(threads),
219            1,
220            "Stepped to the call super line in Source returnsStruct - second time.",
221        )
222        thread = threads[0]
223
224        thread.StepInto()
225        line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()
226        self.assertEqual(
227            line_number,
228            self.sourceBase_returnsStruct_start_line,
229            "Stepped through super into SourceBase returnsStruct in swizzled object.",
230        )
231
232        for bkpt in breakpoints_to_disable:
233            bkpt.SetEnabled(False)
234
235        threads = lldbutil.continue_to_breakpoint(process, break_step_nil)
236        self.assertEqual(len(threads), 1, "Continued to step nil breakpoint.")
237
238        thread.StepInto()
239        line_number = thread.GetFrameAtIndex(0).GetLineEntry().GetLine()
240        self.assertEqual(
241            line_number,
242            self.stepped_past_nil_line,
243            "Step in over dispatch to nil stepped over.",
244        )
245