Skip to content

Commit 8e172d8

Browse files
authored
Merge pull request #4414 from owen-jones-diffblue/fix/must-not-throw-annotations
Fix MustNotThrow annotations and add invariant to avoid exception handling in __CPROVER_initialize
2 parents 7d597c9 + 58ded62 commit 8e172d8

File tree

13 files changed

+83
-9
lines changed

13 files changed

+83
-9
lines changed
Binary file not shown.

jbmc/regression/jbmc/class-fields/java/lang/Class.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public class Class {
44

55
public Integer field;
66

7+
@org.cprover.MustNotThrow
78
protected void cproverNondetInitialize() {
89
org.cprover.CProver.assume(field == null);
910
}
Binary file not shown.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.cprover;
2+
3+
public final class CProver
4+
{
5+
public static final boolean enableAssume=true;
6+
7+
public static void assume(boolean condition)
8+
{
9+
if(enableAssume)
10+
{
11+
throw new RuntimeException(
12+
"Cannot execute program with CProver.assume()");
13+
}
14+
}
15+
}
Binary file not shown.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.cprover;
2+
3+
/**
4+
* This can be added to methods to indicate they aren't allowed to throw
5+
* exceptions. JBMC and related tools will truncate any execution path on which
6+
* they do with an ASSUME FALSE instruction.
7+
*/
8+
public @interface MustNotThrow { }
Binary file not shown.

jbmc/regression/jbmc/class-literals/java/lang/Class.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class Class {
1212
private boolean isMemberClass;
1313
private boolean isEnum;
1414

15+
@org.cprover.MustNotThrow
1516
public void cproverInitializeClassLiteral(
1617
String name,
1718
boolean isAnnotation,
Binary file not shown.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.cprover;
2+
3+
public final class CProver
4+
{
5+
public static final boolean enableAssume=true;
6+
7+
public static void assume(boolean condition)
8+
{
9+
if(enableAssume)
10+
{
11+
throw new RuntimeException(
12+
"Cannot execute program with CProver.assume()");
13+
}
14+
}
15+
}
Binary file not shown.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.cprover;
2+
3+
/**
4+
* This can be added to methods to indicate they aren't allowed to throw
5+
* exceptions. JBMC and related tools will truncate any execution path on which
6+
* they do with an ASSUME FALSE instruction.
7+
*/
8+
public @interface MustNotThrow { }

jbmc/src/java_bytecode/remove_exceptions.cpp

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ Date: December 2016
3030

3131
#include <analyses/uncaught_exceptions_analysis.h>
3232

33+
#include <linking/static_lifetime_init.h>
34+
3335
#include "java_types.h"
3436

3537
/// Lowers high-level exception descriptions into low-level operations suitable
@@ -119,6 +121,13 @@ class remove_exceptionst
119121
bool remove_added_instanceof;
120122
message_handlert &message_handler;
121123

124+
enum class instrumentation_resultt
125+
{
126+
DID_NOTHING,
127+
ADDED_CODE_WITHOUT_MAY_THROW,
128+
ADDED_CODE_WITH_MAY_THROW,
129+
};
130+
122131
symbol_exprt get_inflight_exception_global();
123132

124133
bool function_or_callees_may_throw(const goto_programt &) const;
@@ -148,7 +157,7 @@ class remove_exceptionst
148157
const stack_catcht &,
149158
const std::vector<symbol_exprt> &);
150159

151-
bool instrument_function_call(
160+
instrumentation_resultt instrument_function_call(
152161
const irep_idt &function_identifier,
153162
goto_programt &goto_program,
154163
const goto_programt::targett &,
@@ -207,7 +216,7 @@ bool remove_exceptionst::function_or_callees_may_throw(
207216
/// Translates an exception landing-pad into instructions that copy the
208217
/// in-flight exception pointer to a nominated expression, then clear the
209218
/// in-flight exception (i.e. null the pointer), hence marking it caught.
210-
/// \param goto_program: body of the function containing this landingpad
219+
/// \param [out] goto_program: body of the function containing this landingpad
211220
/// instruction
212221
/// \param instr_it: iterator pointing to the landingpad instruction.
213222
/// Will be overwritten.
@@ -421,7 +430,8 @@ bool remove_exceptionst::instrument_throw(
421430

422431
/// instruments each function call that may escape exceptions with conditional
423432
/// GOTOS to the corresponding exception handlers
424-
bool remove_exceptionst::instrument_function_call(
433+
remove_exceptionst::instrumentation_resultt
434+
remove_exceptionst::instrument_function_call(
425435
const irep_idt &function_identifier,
426436
goto_programt &goto_program,
427437
const goto_programt::targett &instr_it,
@@ -455,6 +465,8 @@ bool remove_exceptionst::instrument_function_call(
455465
goto_program.insert_after(
456466
instr_it,
457467
goto_programt::make_assumption(no_exception_currently_in_flight));
468+
469+
return instrumentation_resultt::ADDED_CODE_WITHOUT_MAY_THROW;
458470
}
459471
else
460472
{
@@ -468,12 +480,12 @@ bool remove_exceptionst::instrument_function_call(
468480
next_it,
469481
no_exception_currently_in_flight,
470482
instr_it->source_location));
471-
}
472483

473-
return true;
484+
return instrumentation_resultt::ADDED_CODE_WITH_MAY_THROW;
485+
}
474486
}
475487

476-
return false;
488+
return instrumentation_resultt::DID_NOTHING;
477489
}
478490

479491
/// instruments throws, function calls that may escape exceptions and exception
@@ -494,6 +506,7 @@ void remove_exceptionst::instrument_exceptions(
494506
function_or_callees_may_throw(goto_program);
495507

496508
bool did_something = false;
509+
bool added_goto_instruction = false;
497510

498511
Forall_goto_program_instructions(instr_it, goto_program)
499512
{
@@ -577,16 +590,29 @@ void remove_exceptionst::instrument_exceptions(
577590
}
578591
else if(instr_it->type==THROW)
579592
{
580-
did_something |= instrument_throw(
593+
did_something = instrument_throw(
581594
function_identifier, goto_program, instr_it, stack_catch, locals);
582595
}
583596
else if(instr_it->type==FUNCTION_CALL)
584597
{
585-
did_something |= instrument_function_call(
586-
function_identifier, goto_program, instr_it, stack_catch, locals);
598+
instrumentation_resultt result =
599+
instrument_function_call(
600+
function_identifier, goto_program, instr_it, stack_catch, locals);
601+
did_something = result != instrumentation_resultt::DID_NOTHING;
602+
added_goto_instruction =
603+
result == instrumentation_resultt::ADDED_CODE_WITH_MAY_THROW;
587604
}
588605
}
589606

607+
// INITIALIZE_FUNCTION should not contain any exception handling branches for
608+
// two reasons: (1) with symex-driven lazy loading it means that code that
609+
// references @inflight_exception might be generated before
610+
// @inflight_exception is initialized; (2) symex can analyze
611+
// INITIALIZE_FUNCTION much faster if it doesn't contain any branching.
612+
INVARIANT(
613+
function_identifier != INITIALIZE_FUNCTION || !added_goto_instruction,
614+
INITIALIZE_FUNCTION " should not contain any exception handling branches");
615+
590616
if(did_something)
591617
remove_skip(goto_program);
592618
}

0 commit comments

Comments
 (0)