1"""
2Test calling a function that throws an ObjC exception, make sure that it doesn't propagate the exception.
3"""
4
5import unittest2
6import lldb
7import lldbutil
8from lldbtest import *
9
10class ExprCommandWithThrowTestCase(TestBase):
11
12    mydir = os.path.join("expression_command", "call-throws")
13
14    def setUp(self):
15        # Call super's setUp().
16        TestBase.setUp(self)
17
18        self.main_source = "call-throws.m"
19        self.main_source_spec = lldb.SBFileSpec (self.main_source)
20
21
22    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
23    @dsym_test
24    def test_with_dsym(self):
25        """Test calling a function that throws and ObjC exception."""
26        self.buildDsym()
27        self.call_function()
28
29    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin due to ObjC test case")
30    @dwarf_test
31    def test_with_dwarf(self):
32        """Test calling a function that throws and ObjC exception."""
33        self.buildDwarf()
34        self.call_function()
35
36    def check_after_call (self):
37        # Check that we are back where we were before:
38        frame = self.thread.GetFrameAtIndex(0)
39        self.assertTrue (self.orig_frame_pc == frame.GetPC(), "Restored the zeroth frame correctly")
40
41
42    def call_function(self):
43        """Test calling function that throws."""
44        exe_name = "a.out"
45        exe = os.path.join(os.getcwd(), exe_name)
46
47        target = self.dbg.CreateTarget(exe)
48        self.assertTrue(target, VALID_TARGET)
49
50        breakpoint = target.BreakpointCreateBySourceRegex('I am about to throw.',self.main_source_spec)
51        self.assertTrue(breakpoint.GetNumLocations() > 0, VALID_BREAKPOINT)
52
53        # Launch the process, and do not stop at the entry point.
54        process = target.LaunchSimple(None, None, os.getcwd())
55
56        self.assertTrue(process, PROCESS_IS_VALID)
57
58        # Frame #0 should be at our breakpoint.
59        threads = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint)
60
61        self.assertTrue(len(threads) == 1)
62        self.thread = threads[0]
63
64        options = lldb.SBExpressionOptions()
65        options.SetUnwindOnError(True)
66
67        frame = self.thread.GetFrameAtIndex(0)
68        # Store away the PC to check that the functions unwind to the right place after calls
69        self.orig_frame_pc = frame.GetPC()
70
71        value = frame.EvaluateExpression ("[my_class callMeIThrow]", options)
72        self.assertTrue (value.IsValid())
73        self.assertTrue (value.GetError().Success() == False)
74
75        self.check_after_call()
76
77        # Okay, now try with a breakpoint in the called code in the case where
78        # we are ignoring breakpoint hits.
79        handler_bkpt = target.BreakpointCreateBySourceRegex("I felt like it", self.main_source_spec)
80        self.assertTrue (handler_bkpt.GetNumLocations() > 0)
81        options.SetIgnoreBreakpoints(True)
82        options.SetUnwindOnError(True)
83
84        value = frame.EvaluateExpression ("[my_class callMeIThrow]", options)
85
86        self.assertTrue (value.IsValid() and value.GetError().Success() == False)
87        self.check_after_call()
88
89        # Now set the ObjC language breakpoint and make sure that doesn't interfere with the call:
90        exception_bkpt = target.BreakpointCreateForException (lldb.eLanguageTypeObjC, False, True)
91        self.assertTrue(exception_bkpt.GetNumLocations() > 0)
92
93        options.SetIgnoreBreakpoints(True)
94        options.SetUnwindOnError(True)
95
96        value = frame.EvaluateExpression ("[my_class callMeIThrow]", options)
97
98        self.assertTrue (value.IsValid() and value.GetError().Success() == False)
99        self.check_after_call()
100
101        # Now set this unwind on error to false, and make sure that we stop where the exception was thrown
102        options.SetUnwindOnError(False)
103        value = frame.EvaluateExpression ("[my_class callMeIThrow]", options)
104
105
106        self.assertTrue (value.IsValid() and value.GetError().Success() == False)
107        self.check_after_call()
108
109if __name__ == '__main__':
110    import atexit
111    lldb.SBDebugger.Initialize()
112    atexit.register(lambda: lldb.SBDebugger.Terminate())
113    unittest2.main()
114