xref: /llvm-project/lldb/test/API/lang/objc/objc-dynamic-value/TestObjCDynamicValue.py (revision 80fcecb13c388ff087a27a4b0e7ca3dd8c98eaa4)
1"""
2Use lldb Python API to test dynamic values in ObjC
3"""
4
5
6import lldb
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10
11
12class ObjCDynamicValueTestCase(TestBase):
13    def setUp(self):
14        # Call super's setUp().
15        TestBase.setUp(self)
16
17        # Find the line number to break for main.c.
18
19        self.source_name = "dynamic-value.m"
20        self.set_property_line = line_number(
21            self.source_name,
22            "// This is the line in setProperty, make sure we step to here.",
23        )
24        self.handle_SourceBase = line_number(
25            self.source_name, "// Break here to check dynamic values."
26        )
27        self.main_before_setProperty_line = line_number(
28            self.source_name, "// Break here to see if we can step into real method."
29        )
30
31    @add_test_categories(["pyapi"])
32    @expectedFailureDarwin("llvm.org/pr20271 rdar://18684107")
33    def test_get_objc_dynamic_vals(self):
34        """Test fetching ObjC dynamic values."""
35        if self.getArchitecture() == "i386":
36            # rdar://problem/9946499
37            self.skipTest("Dynamic types for ObjC V1 runtime not implemented")
38
39        self.build()
40        exe = self.getBuildArtifact("a.out")
41
42        # Create a target from the debugger.
43
44        target = self.dbg.CreateTarget(exe)
45        self.assertTrue(target, VALID_TARGET)
46
47        # Set up our breakpoints:
48
49        handle_SourceBase_bkpt = target.BreakpointCreateByLocation(
50            self.source_name, self.handle_SourceBase
51        )
52        self.assertTrue(
53            handle_SourceBase_bkpt and handle_SourceBase_bkpt.GetNumLocations() == 1,
54            VALID_BREAKPOINT,
55        )
56
57        main_before_setProperty_bkpt = target.BreakpointCreateByLocation(
58            self.source_name, self.main_before_setProperty_line
59        )
60        self.assertTrue(
61            main_before_setProperty_bkpt
62            and main_before_setProperty_bkpt.GetNumLocations() == 1,
63            VALID_BREAKPOINT,
64        )
65
66        # Now launch the process, and do not stop at the entry point.
67        process = target.LaunchSimple(None, None, self.get_process_working_directory())
68
69        self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
70
71        threads = lldbutil.get_threads_stopped_at_breakpoint(
72            process, main_before_setProperty_bkpt
73        )
74        self.assertEqual(len(threads), 1)
75        thread = threads[0]
76
77        #
78        #  At this point, myObserver has a Source pointer that is actually a KVO swizzled SourceDerived
79        #  make sure we can get that properly:
80
81        frame = thread.GetFrameAtIndex(0)
82        myObserver = frame.FindVariable("myObserver", lldb.eDynamicCanRunTarget)
83        self.assertTrue(myObserver)
84        myObserver_source = myObserver.GetChildMemberWithName(
85            "_source", lldb.eDynamicCanRunTarget
86        )
87        self.examine_SourceDerived_ptr(myObserver_source)
88
89        #
90        # Make sure a static value can be correctly turned into a dynamic
91        # value.
92
93        frame = thread.GetFrameAtIndex(0)
94        myObserver_static = frame.FindVariable("myObserver", lldb.eNoDynamicValues)
95        self.assertTrue(myObserver_static)
96        myObserver = myObserver_static.GetDynamicValue(lldb.eDynamicCanRunTarget)
97        myObserver_source = myObserver.GetChildMemberWithName(
98            "_source", lldb.eDynamicCanRunTarget
99        )
100        self.examine_SourceDerived_ptr(myObserver_source)
101
102        # The "frame var" code uses another path to get into children, so let's
103        # make sure that works as well:
104
105        result = lldb.SBCommandReturnObject()
106
107        self.expect(
108            "frame var -d run-target myObserver->_source",
109            "frame var finds its way into a child member",
110            patterns=["\(SourceDerived \*\)"],
111        )
112
113        # check that our ObjC GetISA() does a good job at hiding KVO swizzled
114        # classes
115
116        self.expect(
117            "frame var -d run-target myObserver->_source -T",
118            "the KVO-ed class is hidden",
119            substrs=["SourceDerived"],
120        )
121
122        self.expect(
123            "frame var -d run-target myObserver->_source -T",
124            "the KVO-ed class is hidden",
125            matching=False,
126            substrs=["NSKVONotify"],
127        )
128
129        # This test is not entirely related to the main thrust of this test case, but since we're here,
130        # try stepping into setProperty, and make sure we get into the version
131        # in Source:
132
133        thread.StepInto()
134
135        threads = lldbutil.get_stopped_threads(process, lldb.eStopReasonPlanComplete)
136        self.assertEqual(len(threads), 1)
137        line_entry = threads[0].GetFrameAtIndex(0).GetLineEntry()
138
139        self.assertEqual(line_entry.GetLine(), self.set_property_line)
140        self.assertEqual(line_entry.GetFileSpec().GetFilename(), self.source_name)
141
142        # Okay, back to the main business.  Continue to the handle_SourceBase
143        # and make sure we get the correct dynamic value.
144
145        threads = lldbutil.continue_to_breakpoint(process, handle_SourceBase_bkpt)
146        self.assertEqual(len(threads), 1)
147        thread = threads[0]
148
149        frame = thread.GetFrameAtIndex(0)
150
151        # Get "object" using FindVariable:
152
153        noDynamic = lldb.eNoDynamicValues
154        useDynamic = lldb.eDynamicCanRunTarget
155
156        object_static = frame.FindVariable("object", noDynamic)
157        object_dynamic = frame.FindVariable("object", useDynamic)
158
159        # Delete this object to make sure that this doesn't cause havoc with
160        # the dynamic object that depends on it.
161        del object_static
162
163        self.examine_SourceDerived_ptr(object_dynamic)
164
165        # Get "this" using FindValue, make sure that works too:
166        object_static = frame.FindValue(
167            "object", lldb.eValueTypeVariableArgument, noDynamic
168        )
169        object_dynamic = frame.FindValue(
170            "object", lldb.eValueTypeVariableArgument, useDynamic
171        )
172        del object_static
173        self.examine_SourceDerived_ptr(object_dynamic)
174
175        # Get "this" using the EvaluateExpression:
176        object_static = frame.EvaluateExpression("object", noDynamic)
177        object_dynamic = frame.EvaluateExpression("object", useDynamic)
178        del object_static
179        self.examine_SourceDerived_ptr(object_dynamic)
180
181        # Continue again to the handle_SourceBase and make sure we get the correct dynamic value.
182        # This one looks exactly the same, but in fact this is an "un-KVO'ed" version of SourceBase, so
183        # its isa pointer points to SourceBase not NSKVOSourceBase or
184        # whatever...
185
186        threads = lldbutil.continue_to_breakpoint(process, handle_SourceBase_bkpt)
187        self.assertEqual(len(threads), 1)
188        thread = threads[0]
189
190        frame = thread.GetFrameAtIndex(0)
191
192        # Get "object" using FindVariable:
193
194        object_static = frame.FindVariable("object", noDynamic)
195        object_dynamic = frame.FindVariable("object", useDynamic)
196
197        # Delete this object to make sure that this doesn't cause havoc with
198        # the dynamic object that depends on it.
199        del object_static
200
201        self.examine_SourceDerived_ptr(object_dynamic)
202
203    def examine_SourceDerived_ptr(self, object):
204        self.assertTrue(object)
205        self.assertNotEqual(object.GetTypeName().find("SourceDerived"), -1)
206        derivedValue = object.GetChildMemberWithName("_derivedValue")
207        self.assertTrue(derivedValue)
208        self.assertEqual(int(derivedValue.GetValue(), 0), 30)
209