Skip to content

Commit 6c9f05e

Browse files
committed
Fixes to exception handling behaviour
Corrected the ordering of exception catching and added tests to verify the correct behaviour. Previously, the universal catch would be the one on the outermost try (should be the innermost try). Also, a throw can never throw directly past this catch, so there is no need to include these alternatives (they will appear in the rethrow of the finally).
1 parent 8360233 commit 6c9f05e

File tree

11 files changed

+172
-14
lines changed

11 files changed

+172
-14
lines changed
189 Bytes
Binary file not shown.
164 Bytes
Binary file not shown.
780 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
test.class
3+
^VERIFICATION SUCCESSFUL$
4+
--
5+
^warning: ignoring
6+
--
7+
This test verifies that catches are executed in the correct order
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
class A extends RuntimeException {}
2+
class B extends A {}
3+
4+
// This test verifies that catches are executed in the correct order
5+
public class test {
6+
public static void main (String args[]) {
7+
int i = 0;
8+
try {
9+
try {
10+
throw new A();
11+
}
12+
catch(A exc) {
13+
i++;
14+
throw new B();
15+
}
16+
finally
17+
{
18+
// Make sure A threw into the catch
19+
assert(i==1);
20+
i++;
21+
}
22+
}
23+
catch(B exc) {
24+
// Make sure finally was executed
25+
assert(i==2);
26+
i++;
27+
}
28+
// Make sure B was rethrown by finally
29+
assert(i==3);
30+
}
31+
}
189 Bytes
Binary file not shown.
164 Bytes
Binary file not shown.
1.04 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
test.class
3+
VERIFICATION SUCCESSFUL
4+
--
5+
^warning: ignoring
6+
--
7+
Tests embedded try-catch-finally to ensure the catching order is correct
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import org.cprover.CProver;
2+
class A extends RuntimeException {}
3+
class B extends A {}
4+
5+
// This test verifies that catches are executed in the correct order
6+
public class test {
7+
public static void main (String args[]) {
8+
int i = 0;
9+
int j = 0;
10+
try {
11+
try {
12+
try {
13+
if (CProver.nondetBoolean()) throw new A();
14+
else throw new B();
15+
}
16+
catch(B exc) {
17+
i++;
18+
}
19+
catch(A exc) {
20+
i++;
21+
throw new B();
22+
}
23+
finally
24+
{
25+
// Make sure A threw into the catch
26+
assert(i==1);
27+
i++;
28+
}
29+
assert(i==2);
30+
// Account for the rethrow in finally
31+
j++;
32+
}
33+
catch(B exc) {
34+
// Make sure finally was executed
35+
assert(i==2);
36+
i++;
37+
throw new B();
38+
}
39+
finally
40+
{
41+
assert ((i+j) == 3);
42+
}
43+
// Account for the rethrow in finally
44+
j++;
45+
}
46+
catch(B exc)
47+
{
48+
i++;
49+
}
50+
// Make sure B was rethrown by finally when A was thrown
51+
// or if B was thrown there was no rethrow
52+
assert(i+j == 4);
53+
}
54+
}

src/goto-programs/remove_exceptions.cpp

+72-14
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ class remove_exceptionst
107107
const goto_programt::targett &,
108108
bool may_catch);
109109

110+
goto_programt::targett find_universal_exception(
111+
const remove_exceptionst::stack_catcht &stack_catch,
112+
goto_programt &goto_program,
113+
std::size_t &universal_try,
114+
std::size_t &universal_catch);
115+
110116
void add_exception_dispatch_sequence(
111117
goto_programt &goto_program,
112118
const goto_programt::targett &instr_it,
@@ -229,6 +235,55 @@ void remove_exceptionst::instrument_exception_handler(
229235
instr_it->make_skip();
230236
}
231237

238+
/// Find the innermost universal exception handler for the current
239+
/// program location which may throw (i.e. the first catch of type
240+
/// any in the innermost try that contains such). We record this one
241+
/// because no handler after it can possibly catch.
242+
/// The context is contained in stack_catch which is a stack of all the tries
243+
/// which contain the current program location in their bodies. Each of these
244+
/// in turn contains a list of all possible catches for that try giving the
245+
/// type of exception they catch and the location of the handler.
246+
/// This function returns the indices of the try and the catch within that try
247+
/// as well as the location of the handler.
248+
/// The first loop is in forward order because the insertion reverses the order
249+
/// we note that try1{ try2 {} catch2c {} catch2d {}} catch1a() {} catch1b{}
250+
/// must catch in the following order: 2c 2d 1a 1b hence the numerical index
251+
/// is reversed whereas the letter ordering remains the same.
252+
/// @param stack_catch exception table
253+
/// @param goto_program program being evaluated
254+
/// @param[out] universal_try returns the try block
255+
/// corresponding to the desired exception handler
256+
/// @param[out] universal_catch returns the catch block
257+
/// corresponding to the exception desired exception handler
258+
/// @return the desired exception handler
259+
goto_programt::targett remove_exceptionst::find_universal_exception(
260+
const remove_exceptionst::stack_catcht &stack_catch,
261+
goto_programt &goto_program,
262+
std::size_t &universal_try,
263+
std::size_t &universal_catch)
264+
{
265+
for(std::size_t i=stack_catch.size(); i>0;)
266+
{
267+
i--;
268+
for(std::size_t j=0; j<stack_catch[i].size(); ++j)
269+
{
270+
if(stack_catch[i][j].first.empty())
271+
{
272+
// Record the position of the default behaviour as any further catches
273+
// will not capture the throw
274+
universal_try=i;
275+
universal_catch=j;
276+
277+
// Universal handler. Highest on the stack takes
278+
// precedence, so overwrite any we've already seen:
279+
return stack_catch[i][j].second;
280+
}
281+
}
282+
}
283+
// Unless we have a universal exception handler, jump to end of function
284+
return goto_program.get_end_function();
285+
}
286+
232287
/// Emit the code:
233288
/// if (exception instanceof ExnA) then goto handlerA
234289
/// else if (exception instanceof ExnB) then goto handlerB
@@ -244,10 +299,6 @@ void remove_exceptionst::add_exception_dispatch_sequence(
244299
const remove_exceptionst::stack_catcht &stack_catch,
245300
const std::vector<exprt> &locals)
246301
{
247-
// Unless we have a universal exception handler, jump to end of function
248-
// if not caught:
249-
goto_programt::targett default_target=goto_program.get_end_function();
250-
251302
// Jump to the universal handler or function end, as appropriate.
252303
// This will appear after the GOTO-based dynamic dispatch below
253304
goto_programt::targett default_dispatch=goto_program.insert_after(instr_it);
@@ -259,21 +310,28 @@ void remove_exceptionst::add_exception_dispatch_sequence(
259310
symbol_exprt exc_thrown =
260311
get_inflight_exception_global();
261312

313+
std::size_t default_try=0;
314+
std::size_t default_catch=(!stack_catch.empty()) ? stack_catch[0].size() : 0;
315+
316+
goto_programt::targett default_target=
317+
find_universal_exception(stack_catch, goto_program,
318+
default_try, default_catch);
319+
262320
// add GOTOs implementing the dynamic dispatch of the
263-
// exception handlers
264-
for(std::size_t i=stack_catch.size(); i-->0;)
321+
// exception handlers.
322+
// The first loop is in forward order because the insertion reverses the order
323+
// we note that try1{ try2 {} catch2c {} catch2d {}} catch1a() {} catch1b{}
324+
// must catch in the following order: 2c 2d 1a 1b hence the numerical index
325+
// is reversed whereas the letter ordering remains the same.
326+
for(std::size_t i=default_try; i<stack_catch.size(); i++)
265327
{
266-
for(std::size_t j=stack_catch[i].size(); j-->0;)
328+
for(std::size_t j=(i==default_try) ? default_catch : stack_catch[i].size();
329+
j>0;)
267330
{
331+
j--;
268332
goto_programt::targett new_state_pc=
269333
stack_catch[i][j].second;
270-
if(stack_catch[i][j].first.empty())
271-
{
272-
// Universal handler. Highest on the stack takes
273-
// precedence, so overwrite any we've already seen:
274-
default_target=new_state_pc;
275-
}
276-
else
334+
if(!stack_catch[i][j].first.empty())
277335
{
278336
// Normal exception handler, make an instanceof check.
279337
goto_programt::targett t_exc=goto_program.insert_after(instr_it);

0 commit comments

Comments
 (0)