Skip to content

Commit 01be3da

Browse files
committed
Fix for nondet replacement on a direct return (pre-remove returns)
If remove returns hasn't been run and the nondet method call was a direct return - return nondetWithoutNull() - an assertion was hit because it couldn't find the destination assignment to add the nondet value too. This change just adds that particular situation in, saying if we can't find an assignment it's likely to be a return and then attempts to look for that. The logic is also very slightly modified to replace the code of the target instruction instead of destroying it, creating a new one and inserting that directly afterwards.
1 parent 5b8897e commit 01be3da

File tree

5 files changed

+251
-69
lines changed

5 files changed

+251
-69
lines changed

jbmc/src/java_bytecode/replace_java_nondet.cpp

+61-26
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,25 @@ static bool is_assignment_from(
152152
is_typecast_with_id(rhs, identifier);
153153
}
154154

155+
/// Return whether the instruction is a return, and the rhs is a symbol or
156+
/// typecast expression with the specified identifier.
157+
/// \param instr: A goto program instruction.
158+
/// \param identifier: Some identifier.
159+
/// \return True if the expression is a typecast with one operand, and the
160+
/// typecast's identifier matches the specified identifier.
161+
static bool is_return_with_variable(
162+
const goto_programt::instructiont &instr,
163+
const irep_idt &identifier)
164+
{
165+
if(!instr.is_return())
166+
{
167+
return false;
168+
}
169+
const auto &rhs = to_code_return(instr.code).return_value();
170+
return is_symbol_with_id(rhs, identifier) ||
171+
is_typecast_with_id(rhs, identifier);
172+
}
173+
155174
/// Given an iterator into a list of instructions, modify the list to replace
156175
/// 'nondet' library functions with CBMC-native nondet expressions, and return
157176
/// an iterator to the next instruction to check. It's important to note that
@@ -166,7 +185,10 @@ static bool is_assignment_from(
166185
/// obj = (<type-of-obj>)return_tmp0;
167186
///
168187
/// We're going to replace all of these lines with
169-
/// return_tmp0 = NONDET(<type-of-obj>)
188+
/// obj = NONDET(<type-of-obj>)
189+
///
190+
/// In the situation of a direct return, the end result should be:
191+
/// return NONDET(<type-of-obj>)
170192
/// \param goto_program: The goto program to modify.
171193
/// \param target: A single step of the goto program which may be erased and
172194
/// replaced.
@@ -225,47 +247,60 @@ static goto_programt::targett check_and_replace_target(
225247
// Look for the assignment of the temporary return variable into our target
226248
// variable.
227249
const auto end = goto_program.instructions.end();
228-
auto assignment_instruction = std::find_if(
250+
auto target_instruction = std::find_if(
229251
next_instr,
230252
end,
231253
[&return_identifier](const goto_programt::instructiont &instr) {
232254
return is_assignment_from(instr, return_identifier);
233255
});
234256

235-
INVARIANT(
236-
assignment_instruction != end,
237-
"failed to find assignment of the temporary return variable into our "
238-
"target variable");
239-
240-
// Assume that the LHS of *this* assignment is the actual nondet variable
241-
const auto &code_assign = to_code_assign(assignment_instruction->code);
242-
const auto nondet_var = code_assign.lhs();
243-
const auto source_loc = target->source_location;
257+
// If we can't find an assign, it might be a direct return.
258+
if(target_instruction == end)
259+
{
260+
target_instruction = std::find_if(
261+
next_instr,
262+
end,
263+
[&return_identifier](const goto_programt::instructiont &instr) {
264+
return is_return_with_variable(instr, return_identifier);
265+
});
266+
}
244267

245-
// Erase from the nondet function call to the assignment
246-
const auto after_matching_assignment = std::next(assignment_instruction);
247268
INVARIANT(
248-
after_matching_assignment != end,
249-
"goto_program missing END_FUNCTION instruction");
269+
target_instruction != end,
270+
"failed to find return of the temporary return variable or assignment of "
271+
"the temporary return variable into a target variable");
250272

251273
std::for_each(
252-
target, after_matching_assignment, [](goto_programt::instructiont &instr) {
274+
target, target_instruction, [](goto_programt::instructiont &instr) {
253275
instr.make_skip();
254276
});
255277

256-
const auto inserted = goto_program.insert_before(after_matching_assignment);
257-
inserted->make_assignment();
258-
side_effect_expr_nondett inserted_expr(nondet_var.type());
259-
inserted_expr.set_nullable(
260-
instr_info.get_nullable_type() ==
261-
nondet_instruction_infot::is_nullablet::TRUE);
262-
inserted->code = code_assignt(nondet_var, inserted_expr);
263-
inserted->code.add_source_location() = source_loc;
264-
inserted->source_location = source_loc;
278+
if(target_instruction->is_return())
279+
{
280+
const auto &nondet_var =
281+
to_code_return(target_instruction->code).return_value();
282+
283+
side_effect_expr_nondett inserted_expr(nondet_var.type());
284+
inserted_expr.set_nullable(
285+
instr_info.get_nullable_type() ==
286+
nondet_instruction_infot::is_nullablet::TRUE);
287+
target_instruction->code = code_returnt(inserted_expr);
288+
}
289+
else if(target_instruction->is_assign())
290+
{
291+
// Assume that the LHS of *this* assignment is the actual nondet variable
292+
const auto &nondet_var = to_code_assign(target_instruction->code).lhs();
293+
294+
side_effect_expr_nondett inserted_expr(nondet_var.type());
295+
inserted_expr.set_nullable(
296+
instr_info.get_nullable_type() ==
297+
nondet_instruction_infot::is_nullablet::TRUE);
298+
target_instruction->code = code_assignt(nondet_var, inserted_expr);
299+
}
265300

266301
goto_program.update();
267302

268-
return after_matching_assignment;
303+
return std::next(target_instruction);
269304
}
270305

271306
/// Checks each instruction in the goto program to see whether it is a method
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
public class Main {
2+
public void replaceNondetAssignment() {
3+
Object temp = org.cprover.CProver.nondetWithoutNull();
4+
}
5+
6+
public void replaceNondetAssignmentUnbox() {
7+
int temp = org.cprover.CProver.nondetWithoutNull();
8+
}
9+
10+
public void replaceNondetAssignmentImplicitCast() {
11+
Integer temp = org.cprover.CProver.nondetWithoutNull();
12+
}
13+
14+
public void replaceNondetAssignmentExplicitCast() {
15+
Integer temp = (Integer)org.cprover.CProver.nondetWithoutNull();
16+
}
17+
18+
public void replaceNondetAssignmentAddition() {
19+
int temp = ((int)org.cprover.CProver.nondetWithoutNull()) + 3;
20+
}
21+
22+
public void replaceNondetUnused() {
23+
org.cprover.CProver.nondetWithoutNull();
24+
}
25+
26+
public int replaceNondetReturnUnboxed() {
27+
return org.cprover.CProver.nondetWithoutNull();
28+
}
29+
30+
public Object replaceNondetReturn() {
31+
return org.cprover.CProver.nondetWithoutNull();
32+
}
33+
34+
public Integer replaceNondetReturnWithImplicitCast() {
35+
return org.cprover.CProver.nondetWithoutNull();
36+
}
37+
38+
public Integer replaceNondetReturnWithExplicitCast() {
39+
return (Integer)org.cprover.CProver.nondetWithoutNull();
40+
}
41+
42+
public Integer replaceNondetReturnAddition() {
43+
return ((int)org.cprover.CProver.nondetWithoutNull()) + 3;
44+
}
45+
}

0 commit comments

Comments
 (0)