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