Skip to content

Commit b76857c

Browse files
Rewrite do_exception_handling
Make the control-flow in the code understandable Fix bugs caused by handlers in the exception table not being sorted so that ones that start and end at the same positions are all next to each other
1 parent e9674ac commit b76857c

File tree

2 files changed

+66
-93
lines changed

2 files changed

+66
-93
lines changed

jbmc/regression/jbmc/exceptions28/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
test.class
33
--unwind 10
44
^\[java::test.main:\(\[Ljava/lang/String;\)V\.1\] line 7 no uncaught exception: FAILURE$

jbmc/src/java_bytecode/java_bytecode_convert_method.cpp

Lines changed: 65 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -2335,105 +2335,78 @@ codet &java_bytecode_convert_methodt::do_exception_handling(
23352335
const java_bytecode_convert_methodt::methodt &method,
23362336
const std::set<method_offsett> &working_set,
23372337
method_offsett cur_pc,
2338-
codet &c)
2338+
codet &code)
23392339
{
2340-
std::vector<irep_idt> exception_ids;
2341-
std::vector<irep_idt> handler_labels;
2342-
2343-
// for each try-catch add a CATCH-PUSH instruction
2344-
// each CATCH-PUSH records a list of all the handler labels
2345-
// together with a list of all the exception ids
2346-
2347-
// be aware of different try-catch blocks with the same starting pc
2348-
method_offsett pos = 0;
2349-
method_offsett end_pc = 0;
2350-
while(pos < method.exception_table.size())
2340+
// For each try-catch add a CATCH-PUSH instruction
2341+
// Each CATCH-PUSH records a list of all the handler labels together with a
2342+
// list of all the exception ids
2343+
2344+
// Gather all try-catch blocks that have cur_pc as the starting pc
2345+
typedef std::vector<std::reference_wrapper<
2346+
const java_bytecode_convert_methodt::methodt::exceptiont>> exceptionst;
2347+
std::map<std::size_t, exceptionst> exceptions_by_end;
2348+
for(const java_bytecode_convert_methodt::methodt::exceptiont &exception
2349+
: method.exception_table)
23512350
{
2352-
// check if this is the beginning of a try block
2353-
for(; pos < method.exception_table.size(); ++pos)
2354-
{
2355-
// unexplored try-catch?
2356-
if(cur_pc == method.exception_table[pos].start_pc && end_pc == 0)
2357-
{
2358-
end_pc = method.exception_table[pos].end_pc;
2359-
}
2360-
2361-
// currently explored try-catch?
2362-
if(
2363-
cur_pc == method.exception_table[pos].start_pc &&
2364-
method.exception_table[pos].end_pc == end_pc)
2365-
{
2366-
exception_ids.push_back(
2367-
method.exception_table[pos].catch_type.get_identifier());
2368-
// record the exception handler in the CATCH-PUSH
2369-
// instruction by generating a label corresponding to
2370-
// the handler's pc
2371-
handler_labels.push_back(
2372-
label(std::to_string(method.exception_table[pos].handler_pc)));
2373-
}
2374-
else
2375-
break;
2376-
}
2377-
2378-
// add the actual PUSH-CATCH instruction
2379-
if(!exception_ids.empty())
2380-
{
2381-
code_push_catcht catch_push;
2382-
INVARIANT(
2383-
exception_ids.size() == handler_labels.size(),
2384-
"Exception tags and handler labels should be 1-to-1");
2385-
code_push_catcht::exception_listt &exception_list =
2386-
catch_push.exception_list();
2387-
for(size_t i = 0; i < exception_ids.size(); ++i)
2388-
{
2389-
exception_list.push_back(
2390-
code_push_catcht::exception_list_entryt(
2391-
exception_ids[i], handler_labels[i]));
2392-
}
2393-
2394-
c = code_blockt({catch_push, c});
2395-
}
2396-
else
2397-
{
2398-
// advance
2399-
++pos;
2400-
}
2401-
2402-
// reset
2403-
end_pc = 0;
2404-
exception_ids.clear();
2405-
handler_labels.clear();
2351+
if(exception.start_pc == cur_pc)
2352+
exceptions_by_end[exception.end_pc].push_back(exception);
2353+
}
2354+
for(const auto &exceptions : exceptions_by_end)
2355+
{
2356+
code_push_catcht catch_push;
2357+
code_push_catcht::exception_listt &exception_list =
2358+
catch_push.exception_list();
2359+
for(const java_bytecode_convert_methodt::methodt::exceptiont &exception
2360+
: exceptions.second)
2361+
{
2362+
exception_list.emplace_back(
2363+
exception.catch_type.get_identifier(),
2364+
// Record the exception handler in the CATCH-PUSH instruction by
2365+
// generating a label corresponding to the handler's pc
2366+
label(std::to_string(exception.handler_pc)));
2367+
}
2368+
// Prepend the CATCH-PUSH instruction
2369+
code = code_blockt({ catch_push, code });
24062370
}
24072371

2408-
// next add the CATCH-POP instructions
2409-
size_t start_pc = 0;
2410-
// as the first try block may have start_pc 0, we
2411-
// must track it separately
2412-
bool first_handler = false;
2413-
// check if this is the end of a try block
2372+
// Next add the CATCH-POP instructions
2373+
// exception_row.end_pc is exclusive so append CATCH-POP instructions if this
2374+
// is the instruction before it.
2375+
// Dangerously, we assume that the lexically next bit of code after the try
2376+
// block (the catch block) is:
2377+
// a) the next code in the bytecode and therefore its start address matches
2378+
// the exclusive end position of the try block (probably a valid assumption
2379+
// for non-obfuscated bytecode generated by most compilers)
2380+
// b) the successor of a previous instruction and therefore in the working
2381+
// set (not a valid assumption when there aren't any throwing statements in
2382+
// the try block)
2383+
// We are therefore going to find all exception handlers that end at our
2384+
// earliest successor
2385+
if(working_set.empty())
2386+
{
2387+
// There was no throw in the try block
2388+
// TODO: This will result in non-balanced PUSH and POPs
2389+
return code;
2390+
}
2391+
const method_offsett &working_set_elt = *working_set.begin();
2392+
if(cur_pc >= working_set_elt)
2393+
{
2394+
// This just fails if we have a back-edge here
2395+
// TODO: This will result in non-balanced PUSH and POPs
2396+
return code;
2397+
}
2398+
// Count the distinct handler start positions that end at this location
2399+
std::set<std::size_t> start_positions;
24142400
for(const auto &exception_row : method.exception_table)
24152401
{
2416-
// add the CATCH-POP before the end of the try block
2417-
if(
2418-
cur_pc < exception_row.end_pc && !working_set.empty() &&
2419-
*working_set.begin() == exception_row.end_pc)
2420-
{
2421-
// have we already added a CATCH-POP for the current try-catch?
2422-
// (each row corresponds to a handler)
2423-
if(exception_row.start_pc != start_pc || !first_handler)
2424-
{
2425-
if(!first_handler)
2426-
first_handler = true;
2427-
// remember the beginning of the try-catch so that we don't add
2428-
// another CATCH-POP for the same try-catch
2429-
start_pc = exception_row.start_pc;
2430-
// add CATCH_POP instruction
2431-
code_pop_catcht catch_pop;
2432-
c = code_blockt({c, catch_pop});
2433-
}
2434-
}
2402+
// Check if working_set_elt is the end of a try block
2403+
if(working_set_elt == exception_row.end_pc)
2404+
start_positions.insert(exception_row.start_pc);
24352405
}
2436-
return c;
2406+
for(std::size_t handler = 0; handler < start_positions.size(); ++handler)
2407+
// Append a CATCH-POP instruction before the end of the try block
2408+
code = code_blockt({ code, code_pop_catcht() });
2409+
return code;
24372410
}
24382411

24392412
code_blockt java_bytecode_convert_methodt::convert_multianewarray(

0 commit comments

Comments
 (0)