Skip to content

Commit ab60852

Browse files
committed
Add org.cprover.MustNotThrow method attribute
This asserts that a particular method must not throw exceptions, intended for use with internal methods such as cproverNondetInitialize, which must not propagate exceptions to their callsite. In such a case, instead of an exception dispatch table we introduce an assumption checking one was not in fact thrown.
1 parent 774060b commit ab60852

File tree

8 files changed

+56
-10
lines changed

8 files changed

+56
-10
lines changed
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
public class Test {
2+
3+
@org.cprover.MustNotThrow
4+
public static void mustNotThrow() {
5+
throw new RuntimeException("Oh dear");
6+
}
7+
8+
public static void main() {
9+
try {
10+
mustNotThrow();
11+
}
12+
catch(Throwable e) {
13+
assert false;
14+
}
15+
}
16+
17+
}
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.cprover;
2+
3+
public @interface MustNotThrow { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
Test.class
3+
--function Test.main
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

jbmc/src/java_bytecode/java_bytecode_convert_method.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,13 @@ void java_bytecode_convert_method_lazy(
406406
// Not used in jbmc at present, but other codebases that use jbmc as a library
407407
// use this information.
408408
method_symbol.type.set(ID_C_abstract, m.is_abstract);
409+
410+
if(java_bytecode_parse_treet::find_annotation(
411+
m.annotations, "java::org.cprover.MustNotThrow"))
412+
{
413+
method_symbol.type.set(ID_C_must_not_throw, true);
414+
}
415+
409416
symbol_table.add(method_symbol);
410417
}
411418

jbmc/src/java_bytecode/remove_exceptions.cpp

+20-10
Original file line numberDiff line numberDiff line change
@@ -432,19 +432,29 @@ bool remove_exceptionst::instrument_function_call(
432432

433433
if(function_may_throw(callee_id))
434434
{
435-
add_exception_dispatch_sequence(
436-
goto_program, instr_it, stack_catch, locals);
437-
438-
// add a null check (so that instanceof can be applied)
439-
equal_exprt eq_null(
435+
equal_exprt no_exception_currently_in_flight(
440436
get_inflight_exception_global(),
441437
null_pointer_exprt(pointer_type(empty_typet())));
442438

443-
goto_programt::targett t_null=goto_program.insert_after(instr_it);
444-
t_null->make_goto(next_it);
445-
t_null->source_location=instr_it->source_location;
446-
t_null->function=instr_it->function;
447-
t_null->guard=eq_null;
439+
if(symbol_table.lookup_ref(callee_id).type.get_bool(ID_C_must_not_throw))
440+
{
441+
// Function is annotated must-not-throw, but we can't prove that here.
442+
// Insert an ASSUME(@inflight_exception == null):
443+
goto_programt::targett assume_null = goto_program.insert_after(instr_it);
444+
assume_null->make_assumption(no_exception_currently_in_flight);
445+
}
446+
else
447+
{
448+
add_exception_dispatch_sequence(
449+
goto_program, instr_it, stack_catch, locals);
450+
451+
// add a null check (so that instanceof can be applied)
452+
goto_programt::targett t_null=goto_program.insert_after(instr_it);
453+
t_null->make_goto(next_it);
454+
t_null->source_location=instr_it->source_location;
455+
t_null->function=instr_it->function;
456+
t_null->guard=no_exception_currently_in_flight;
457+
}
448458

449459
return true;
450460
}

src/util/irep_ids.def

+1
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ IREP_ID_ONE(bits_per_byte)
670670
IREP_ID_TWO(C_abstract, #abstract)
671671
IREP_ID_ONE(synthetic)
672672
IREP_ID_ONE(interface)
673+
IREP_ID_TWO(C_must_not_throw, #must_not_throw)
673674

674675
// Projects depending on this code base that wish to extend the list of
675676
// available ids should provide a file local_irep_ids.h in their source tree and

0 commit comments

Comments
 (0)