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