199451b44SJordan Rupprecht"""
299451b44SJordan RupprechtUse lldb Python API to test dynamic values in ObjC
399451b44SJordan Rupprecht"""
499451b44SJordan Rupprecht
599451b44SJordan Rupprecht
699451b44SJordan Rupprecht
799451b44SJordan Rupprechtimport lldb
899451b44SJordan Rupprechtfrom lldbsuite.test.decorators import *
999451b44SJordan Rupprechtfrom lldbsuite.test.lldbtest import *
1099451b44SJordan Rupprechtfrom lldbsuite.test import lldbutil
1199451b44SJordan Rupprecht
1299451b44SJordan Rupprecht
1399451b44SJordan Rupprechtclass ObjCDynamicValueTestCase(TestBase):
1499451b44SJordan Rupprecht
1599451b44SJordan Rupprecht    def setUp(self):
1699451b44SJordan Rupprecht        # Call super's setUp().
1799451b44SJordan Rupprecht        TestBase.setUp(self)
1899451b44SJordan Rupprecht
1999451b44SJordan Rupprecht        # Find the line number to break for main.c.
2099451b44SJordan Rupprecht
2199451b44SJordan Rupprecht        self.source_name = 'dynamic-value.m'
2299451b44SJordan Rupprecht        self.set_property_line = line_number(
2399451b44SJordan Rupprecht            self.source_name,
2499451b44SJordan Rupprecht            '// This is the line in setProperty, make sure we step to here.')
2599451b44SJordan Rupprecht        self.handle_SourceBase = line_number(
2699451b44SJordan Rupprecht            self.source_name, '// Break here to check dynamic values.')
2799451b44SJordan Rupprecht        self.main_before_setProperty_line = line_number(
2899451b44SJordan Rupprecht            self.source_name, '// Break here to see if we can step into real method.')
2999451b44SJordan Rupprecht
3099451b44SJordan Rupprecht    @add_test_categories(['pyapi'])
3199451b44SJordan Rupprecht    @expectedFailureDarwin("llvm.org/pr20271 rdar://18684107")
3299451b44SJordan Rupprecht    def test_get_objc_dynamic_vals(self):
3399451b44SJordan Rupprecht        """Test fetching ObjC dynamic values."""
3499451b44SJordan Rupprecht        if self.getArchitecture() == 'i386':
3599451b44SJordan Rupprecht            # rdar://problem/9946499
3699451b44SJordan Rupprecht            self.skipTest("Dynamic types for ObjC V1 runtime not implemented")
3799451b44SJordan Rupprecht
3899451b44SJordan Rupprecht        self.build()
3999451b44SJordan Rupprecht        exe = self.getBuildArtifact("a.out")
4099451b44SJordan Rupprecht
4199451b44SJordan Rupprecht        # Create a target from the debugger.
4299451b44SJordan Rupprecht
4399451b44SJordan Rupprecht        target = self.dbg.CreateTarget(exe)
4499451b44SJordan Rupprecht        self.assertTrue(target, VALID_TARGET)
4599451b44SJordan Rupprecht
4699451b44SJordan Rupprecht        # Set up our breakpoints:
4799451b44SJordan Rupprecht
4899451b44SJordan Rupprecht        handle_SourceBase_bkpt = target.BreakpointCreateByLocation(
4999451b44SJordan Rupprecht            self.source_name, self.handle_SourceBase)
5099451b44SJordan Rupprecht        self.assertTrue(handle_SourceBase_bkpt and
5199451b44SJordan Rupprecht                        handle_SourceBase_bkpt.GetNumLocations() == 1,
5299451b44SJordan Rupprecht                        VALID_BREAKPOINT)
5399451b44SJordan Rupprecht
5499451b44SJordan Rupprecht        main_before_setProperty_bkpt = target.BreakpointCreateByLocation(
5599451b44SJordan Rupprecht            self.source_name, self.main_before_setProperty_line)
5699451b44SJordan Rupprecht        self.assertTrue(main_before_setProperty_bkpt and
5799451b44SJordan Rupprecht                        main_before_setProperty_bkpt.GetNumLocations() == 1,
5899451b44SJordan Rupprecht                        VALID_BREAKPOINT)
5999451b44SJordan Rupprecht
6099451b44SJordan Rupprecht        # Now launch the process, and do not stop at the entry point.
6199451b44SJordan Rupprecht        process = target.LaunchSimple(
6299451b44SJordan Rupprecht            None, None, self.get_process_working_directory())
6399451b44SJordan Rupprecht
64*ce825e46SJonas Devlieghere        self.assertState(process.GetState(), lldb.eStateStopped,
6599451b44SJordan Rupprecht                         PROCESS_STOPPED)
6699451b44SJordan Rupprecht
6799451b44SJordan Rupprecht        threads = lldbutil.get_threads_stopped_at_breakpoint(
6899451b44SJordan Rupprecht            process, main_before_setProperty_bkpt)
69b3a0c4d7SRaphael Isemann        self.assertEquals(len(threads), 1)
7099451b44SJordan Rupprecht        thread = threads[0]
7199451b44SJordan Rupprecht
7299451b44SJordan Rupprecht        #
7399451b44SJordan Rupprecht        #  At this point, myObserver has a Source pointer that is actually a KVO swizzled SourceDerived
7499451b44SJordan Rupprecht        #  make sure we can get that properly:
7599451b44SJordan Rupprecht
7699451b44SJordan Rupprecht        frame = thread.GetFrameAtIndex(0)
7799451b44SJordan Rupprecht        myObserver = frame.FindVariable(
7899451b44SJordan Rupprecht            'myObserver', lldb.eDynamicCanRunTarget)
7999451b44SJordan Rupprecht        self.assertTrue(myObserver)
8099451b44SJordan Rupprecht        myObserver_source = myObserver.GetChildMemberWithName(
8199451b44SJordan Rupprecht            '_source', lldb.eDynamicCanRunTarget)
8299451b44SJordan Rupprecht        self.examine_SourceDerived_ptr(myObserver_source)
8399451b44SJordan Rupprecht
8499451b44SJordan Rupprecht        #
8599451b44SJordan Rupprecht        # Make sure a static value can be correctly turned into a dynamic
8699451b44SJordan Rupprecht        # value.
8799451b44SJordan Rupprecht
8899451b44SJordan Rupprecht        frame = thread.GetFrameAtIndex(0)
8999451b44SJordan Rupprecht        myObserver_static = frame.FindVariable(
9099451b44SJordan Rupprecht            'myObserver', lldb.eNoDynamicValues)
9199451b44SJordan Rupprecht        self.assertTrue(myObserver_static)
9299451b44SJordan Rupprecht        myObserver = myObserver_static.GetDynamicValue(
9399451b44SJordan Rupprecht            lldb.eDynamicCanRunTarget)
9499451b44SJordan Rupprecht        myObserver_source = myObserver.GetChildMemberWithName(
9599451b44SJordan Rupprecht            '_source', lldb.eDynamicCanRunTarget)
9699451b44SJordan Rupprecht        self.examine_SourceDerived_ptr(myObserver_source)
9799451b44SJordan Rupprecht
9899451b44SJordan Rupprecht        # The "frame var" code uses another path to get into children, so let's
9999451b44SJordan Rupprecht        # make sure that works as well:
10099451b44SJordan Rupprecht
10199451b44SJordan Rupprecht        result = lldb.SBCommandReturnObject()
10299451b44SJordan Rupprecht
10399451b44SJordan Rupprecht        self.expect(
10499451b44SJordan Rupprecht            'frame var -d run-target myObserver->_source',
10599451b44SJordan Rupprecht            'frame var finds its way into a child member',
10699451b44SJordan Rupprecht            patterns=['\(SourceDerived \*\)'])
10799451b44SJordan Rupprecht
10899451b44SJordan Rupprecht        # check that our ObjC GetISA() does a good job at hiding KVO swizzled
10999451b44SJordan Rupprecht        # classes
11099451b44SJordan Rupprecht
11199451b44SJordan Rupprecht        self.expect(
11299451b44SJordan Rupprecht            'frame var -d run-target myObserver->_source -T',
11399451b44SJordan Rupprecht            'the KVO-ed class is hidden',
11499451b44SJordan Rupprecht            substrs=['SourceDerived'])
11599451b44SJordan Rupprecht
11699451b44SJordan Rupprecht        self.expect(
11799451b44SJordan Rupprecht            'frame var -d run-target myObserver->_source -T',
11899451b44SJordan Rupprecht            'the KVO-ed class is hidden',
11999451b44SJordan Rupprecht            matching=False,
12099451b44SJordan Rupprecht            substrs=['NSKVONotify'])
12199451b44SJordan Rupprecht
12299451b44SJordan Rupprecht        # This test is not entirely related to the main thrust of this test case, but since we're here,
12399451b44SJordan Rupprecht        # try stepping into setProperty, and make sure we get into the version
12499451b44SJordan Rupprecht        # in Source:
12599451b44SJordan Rupprecht
12699451b44SJordan Rupprecht        thread.StepInto()
12799451b44SJordan Rupprecht
12899451b44SJordan Rupprecht        threads = lldbutil.get_stopped_threads(
12999451b44SJordan Rupprecht            process, lldb.eStopReasonPlanComplete)
130b3a0c4d7SRaphael Isemann        self.assertEquals(len(threads), 1)
13199451b44SJordan Rupprecht        line_entry = threads[0].GetFrameAtIndex(0).GetLineEntry()
13299451b44SJordan Rupprecht
13399451b44SJordan Rupprecht        self.assertEqual(line_entry.GetLine(), self.set_property_line)
13499451b44SJordan Rupprecht        self.assertEqual(
13599451b44SJordan Rupprecht            line_entry.GetFileSpec().GetFilename(),
13699451b44SJordan Rupprecht            self.source_name)
13799451b44SJordan Rupprecht
13899451b44SJordan Rupprecht        # Okay, back to the main business.  Continue to the handle_SourceBase
13999451b44SJordan Rupprecht        # and make sure we get the correct dynamic value.
14099451b44SJordan Rupprecht
14199451b44SJordan Rupprecht        threads = lldbutil.continue_to_breakpoint(
14299451b44SJordan Rupprecht            process, handle_SourceBase_bkpt)
143b3a0c4d7SRaphael Isemann        self.assertEquals(len(threads), 1)
14499451b44SJordan Rupprecht        thread = threads[0]
14599451b44SJordan Rupprecht
14699451b44SJordan Rupprecht        frame = thread.GetFrameAtIndex(0)
14799451b44SJordan Rupprecht
14899451b44SJordan Rupprecht        # Get "object" using FindVariable:
14999451b44SJordan Rupprecht
15099451b44SJordan Rupprecht        noDynamic = lldb.eNoDynamicValues
15199451b44SJordan Rupprecht        useDynamic = lldb.eDynamicCanRunTarget
15299451b44SJordan Rupprecht
15399451b44SJordan Rupprecht        object_static = frame.FindVariable('object', noDynamic)
15499451b44SJordan Rupprecht        object_dynamic = frame.FindVariable('object', useDynamic)
15599451b44SJordan Rupprecht
15699451b44SJordan Rupprecht        # Delete this object to make sure that this doesn't cause havoc with
15799451b44SJordan Rupprecht        # the dynamic object that depends on it.
15899451b44SJordan Rupprecht        del (object_static)
15999451b44SJordan Rupprecht
16099451b44SJordan Rupprecht        self.examine_SourceDerived_ptr(object_dynamic)
16199451b44SJordan Rupprecht
16299451b44SJordan Rupprecht        # Get "this" using FindValue, make sure that works too:
16399451b44SJordan Rupprecht        object_static = frame.FindValue(
16499451b44SJordan Rupprecht            'object', lldb.eValueTypeVariableArgument, noDynamic)
16599451b44SJordan Rupprecht        object_dynamic = frame.FindValue(
16699451b44SJordan Rupprecht            'object', lldb.eValueTypeVariableArgument, useDynamic)
16799451b44SJordan Rupprecht        del (object_static)
16899451b44SJordan Rupprecht        self.examine_SourceDerived_ptr(object_dynamic)
16999451b44SJordan Rupprecht
17099451b44SJordan Rupprecht        # Get "this" using the EvaluateExpression:
17199451b44SJordan Rupprecht        object_static = frame.EvaluateExpression('object', noDynamic)
17299451b44SJordan Rupprecht        object_dynamic = frame.EvaluateExpression('object', useDynamic)
17399451b44SJordan Rupprecht        del (object_static)
17499451b44SJordan Rupprecht        self.examine_SourceDerived_ptr(object_dynamic)
17599451b44SJordan Rupprecht
17699451b44SJordan Rupprecht        # Continue again to the handle_SourceBase and make sure we get the correct dynamic value.
17799451b44SJordan Rupprecht        # This one looks exactly the same, but in fact this is an "un-KVO'ed" version of SourceBase, so
17899451b44SJordan Rupprecht        # its isa pointer points to SourceBase not NSKVOSourceBase or
17999451b44SJordan Rupprecht        # whatever...
18099451b44SJordan Rupprecht
18199451b44SJordan Rupprecht        threads = lldbutil.continue_to_breakpoint(
18299451b44SJordan Rupprecht            process, handle_SourceBase_bkpt)
183b3a0c4d7SRaphael Isemann        self.assertEquals(len(threads), 1)
18499451b44SJordan Rupprecht        thread = threads[0]
18599451b44SJordan Rupprecht
18699451b44SJordan Rupprecht        frame = thread.GetFrameAtIndex(0)
18799451b44SJordan Rupprecht
18899451b44SJordan Rupprecht        # Get "object" using FindVariable:
18999451b44SJordan Rupprecht
19099451b44SJordan Rupprecht        object_static = frame.FindVariable('object', noDynamic)
19199451b44SJordan Rupprecht        object_dynamic = frame.FindVariable('object', useDynamic)
19299451b44SJordan Rupprecht
19399451b44SJordan Rupprecht        # Delete this object to make sure that this doesn't cause havoc with
19499451b44SJordan Rupprecht        # the dynamic object that depends on it.
19599451b44SJordan Rupprecht        del (object_static)
19699451b44SJordan Rupprecht
19799451b44SJordan Rupprecht        self.examine_SourceDerived_ptr(object_dynamic)
19899451b44SJordan Rupprecht
19999451b44SJordan Rupprecht    def examine_SourceDerived_ptr(self, object):
20099451b44SJordan Rupprecht        self.assertTrue(object)
201b3a0c4d7SRaphael Isemann        self.assertNotEqual(object.GetTypeName().find('SourceDerived'), -1)
20299451b44SJordan Rupprecht        derivedValue = object.GetChildMemberWithName('_derivedValue')
20399451b44SJordan Rupprecht        self.assertTrue(derivedValue)
204b3a0c4d7SRaphael Isemann        self.assertEquals(int(derivedValue.GetValue(), 0), 30)
205