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