1"""
2Test calling a function that throws an ObjC exception, make sure that it doesn't propagate the exception.
3"""
4
5
6
7import lldb
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbutil
11
12
13class ExprCommandWithThrowTestCase(TestBase):
14
15    mydir = TestBase.compute_mydir(__file__)
16
17    def setUp(self):
18        # Call super's setUp().
19        TestBase.setUp(self)
20
21        self.main_source = "call-throws.m"
22        self.main_source_spec = lldb.SBFileSpec(self.main_source)
23
24    @add_test_categories(["objc"])
25    def test(self):
26        """Test calling a function that throws and ObjC exception."""
27        self.build()
28        self.call_function()
29
30    def check_after_call(self):
31        # Check that we are back where we were before:
32        frame = self.thread.GetFrameAtIndex(0)
33        self.assertTrue(
34            self.orig_frame_pc == frame.GetPC(),
35            "Restored the zeroth frame correctly")
36
37    def call_function(self):
38        """Test calling function that throws."""
39        (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
40                                   'I am about to throw.', self.main_source_spec)
41
42        options = lldb.SBExpressionOptions()
43        options.SetUnwindOnError(True)
44
45        frame = self.thread.GetFrameAtIndex(0)
46        # Store away the PC to check that the functions unwind to the right
47        # place after calls
48        self.orig_frame_pc = frame.GetPC()
49
50        value = frame.EvaluateExpression("[my_class callMeIThrow]", options)
51        self.assertTrue(value.IsValid())
52        self.assertEquals(value.GetError().Success(), False)
53
54        self.check_after_call()
55
56        # Okay, now try with a breakpoint in the called code in the case where
57        # we are ignoring breakpoint hits.
58        handler_bkpt = target.BreakpointCreateBySourceRegex(
59            "I felt like it", self.main_source_spec)
60        self.assertTrue(handler_bkpt.GetNumLocations() > 0)
61        options.SetIgnoreBreakpoints(True)
62        options.SetUnwindOnError(True)
63
64        value = frame.EvaluateExpression("[my_class callMeIThrow]", options)
65
66        self.assertTrue(
67            value.IsValid() and value.GetError().Success() == False)
68        self.check_after_call()
69
70        # Now set the ObjC language breakpoint and make sure that doesn't
71        # interfere with the call:
72        exception_bkpt = target.BreakpointCreateForException(
73            lldb.eLanguageTypeObjC, False, True)
74        self.assertTrue(exception_bkpt.GetNumLocations() > 0)
75
76        options.SetIgnoreBreakpoints(True)
77        options.SetUnwindOnError(True)
78
79        value = frame.EvaluateExpression("[my_class callMeIThrow]", options)
80
81        self.assertTrue(
82            value.IsValid() and value.GetError().Success() == False)
83        self.check_after_call()
84
85        # Now turn off exception trapping, and call a function that catches the exceptions,
86        # and make sure the function actually completes, and we get the right
87        # value:
88        options.SetTrapExceptions(False)
89        value = frame.EvaluateExpression("[my_class iCatchMyself]", options)
90        self.assertTrue(value.IsValid())
91        self.assertSuccess(value.GetError())
92        self.assertEquals(value.GetValueAsUnsigned(), 57)
93        self.check_after_call()
94        options.SetTrapExceptions(True)
95
96        # Now set this unwind on error to false, and make sure that we stop
97        # where the exception was thrown
98        options.SetUnwindOnError(False)
99        value = frame.EvaluateExpression("[my_class callMeIThrow]", options)
100
101        self.assertTrue(
102            value.IsValid() and value.GetError().Success() == False)
103        self.check_after_call()
104