Skip to content

Commit 61f14d8

Browse files
author
Thomas Kiley
authored
Merge pull request diffblue#1962 from owen-jones-diffblue/owen-jones-diffblue/simplify-replace-java-nondet
Make replace_java_nondet() work even if remove_returns() hasn't been run
2 parents fdee7e8 + 048c188 commit 61f14d8

File tree

4 files changed

+187
-20
lines changed

4 files changed

+187
-20
lines changed

src/goto-programs/replace_java_nondet.cpp

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,19 @@ static bool is_assignment_from(
143143

144144
/// Given an iterator into a list of instructions, modify the list to replace
145145
/// 'nondet' library functions with CBMC-native nondet expressions, and return
146-
/// an iterator to the next instruction to check.
146+
/// an iterator to the next instruction to check. It's important to note that
147+
/// this method also deals with the fact that in the GOTO program it assigns
148+
/// function return values to temporary variables, performs validation and then
149+
/// assigns the value into the actual variable.
150+
///
151+
/// Example:
152+
///
153+
/// return_tmp0=org.cprover.CProver.nondetWithoutNull:()Ljava/lang/Object;();
154+
/// ... Various validations of type and value here.
155+
/// obj = (<type-of-obj>)return_tmp0;
156+
///
157+
/// We're going to replace all of these lines with
158+
/// return_tmp0 = NONDET(<type-of-obj>)
147159
/// \param goto_program: The goto program to modify.
148160
/// \param target: A single step of the goto program which may be erased and
149161
/// replaced.
@@ -161,40 +173,68 @@ static goto_programt::targett check_and_replace_target(
161173
return next_instr;
162174
}
163175

164-
// Look at the next instruction, ensure that it is an assignment
165-
assert(next_instr->is_assign());
166-
// Get the name of the LHS of the assignment
167-
const auto &next_instr_assign_lhs=to_code_assign(next_instr->code).lhs();
168-
if(!(next_instr_assign_lhs.id()==ID_symbol &&
169-
!next_instr_assign_lhs.has_operands()))
176+
// If we haven't removed returns yet, our function call will have a variable
177+
// on its left hand side.
178+
const bool remove_returns_not_run =
179+
to_code_function_call(target->code).lhs().is_not_nil();
180+
181+
irep_idt return_identifier;
182+
if(remove_returns_not_run)
170183
{
171-
return next_instr;
184+
return_identifier =
185+
to_symbol_expr(to_code_function_call(target->code).lhs())
186+
.get_identifier();
172187
}
173-
const auto return_identifier=
174-
to_symbol_expr(next_instr_assign_lhs).get_identifier();
188+
else
189+
{
190+
// If not, we need to look at the next line instead.
191+
DATA_INVARIANT(
192+
next_instr->is_assign(),
193+
"the code_function_callt for a nondet-returning library function should "
194+
"either have a variable for the return value in its lhs() or the next "
195+
"instruction should be an assignment of the return value to a temporary "
196+
"variable");
197+
const exprt &return_value_assignment =
198+
to_code_assign(next_instr->code).lhs();
199+
200+
// If the assignment is null, return.
201+
if(
202+
!(return_value_assignment.id() == ID_symbol &&
203+
!return_value_assignment.has_operands()))
204+
{
205+
return next_instr;
206+
}
175207

176-
auto &instructions=goto_program.instructions;
177-
const auto end=instructions.end();
208+
// Otherwise it's the temporary variable.
209+
return_identifier =
210+
to_symbol_expr(return_value_assignment).get_identifier();
211+
}
178212

179-
// Look for an instruction where this name is on the RHS of an assignment
180-
const auto matching_assignment=std::find_if(
213+
// Look for the assignment of the temporary return variable into our target
214+
// variable.
215+
const auto end = goto_program.instructions.end();
216+
auto assignment_instruction = std::find_if(
181217
next_instr,
182218
end,
183-
[&return_identifier](const goto_programt::instructiont &instr)
184-
{
219+
[&return_identifier](const goto_programt::instructiont &instr) {
185220
return is_assignment_from(instr, return_identifier);
186221
});
187222

188-
assert(matching_assignment!=end);
223+
INVARIANT(
224+
assignment_instruction != end,
225+
"failed to find assignment of the temporary return variable into our "
226+
"target variable");
189227

190228
// Assume that the LHS of *this* assignment is the actual nondet variable
191-
const auto &code_assign=to_code_assign(matching_assignment->code);
229+
const auto &code_assign = to_code_assign(assignment_instruction->code);
192230
const auto nondet_var=code_assign.lhs();
193231
const auto source_loc=target->source_location;
194232

195233
// Erase from the nondet function call to the assignment
196-
const auto after_matching_assignment=std::next(matching_assignment);
197-
assert(after_matching_assignment!=end);
234+
const auto after_matching_assignment = std::next(assignment_instruction);
235+
INVARIANT(
236+
after_matching_assignment != end,
237+
"goto_program missing END_FUNCTION instruction");
198238

199239
std::for_each(
200240
target,
434 Bytes
Binary file not shown.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*******************************************************************\
2+
3+
Module: Tests for the replace nondet pass to make sure it works both
4+
when remove_returns has been run before and after the call.
5+
6+
Author: Diffblue Ltd.
7+
8+
\*******************************************************************/
9+
10+
#include <testing-utils/catch.hpp>
11+
#include <testing-utils/load_java_class.h>
12+
13+
#include <goto-programs/goto_convert_functions.h>
14+
#include <goto-programs/remove_virtual_functions.h>
15+
#include <util/config.h>
16+
#include <goto-instrument/cover.h>
17+
#include <util/options.h>
18+
#include <iostream>
19+
#include <goto-programs/remove_returns.h>
20+
#include <goto-programs/replace_java_nondet.h>
21+
#include <goto-programs/remove_instanceof.h>
22+
23+
void validate_method_removal(
24+
std::list<goto_programt::instructiont> instructions)
25+
{
26+
bool method_removed = true, replacement_nondet_exists = false;
27+
28+
// Quick loop to check that our method call has been replaced.
29+
for(const auto &inst : instructions)
30+
{
31+
if(inst.is_assign())
32+
{
33+
const code_assignt &assignment = to_code_assign(inst.code);
34+
if(assignment.rhs().id() == ID_side_effect)
35+
{
36+
const side_effect_exprt &see = to_side_effect_expr(assignment.rhs());
37+
if(see.get_statement() == ID_nondet)
38+
{
39+
replacement_nondet_exists = true;
40+
}
41+
}
42+
}
43+
44+
if(inst.is_function_call())
45+
{
46+
const code_function_callt &function_call =
47+
to_code_function_call(inst.code);
48+
49+
// Small check to make sure the instruction is a symbol.
50+
if(function_call.function().id() != ID_symbol)
51+
continue;
52+
53+
const irep_idt function_id =
54+
to_symbol_expr(function_call.function()).get_identifier();
55+
if(
56+
function_id ==
57+
"java::org.cprover.CProver.nondetWithoutNull:()"
58+
"Ljava/lang/Object;")
59+
{
60+
method_removed = false;
61+
}
62+
}
63+
}
64+
65+
REQUIRE(method_removed);
66+
REQUIRE(replacement_nondet_exists);
67+
}
68+
69+
TEST_CASE(
70+
"Load class with a generated java nondet method call, run remove returns "
71+
"both before and after the nondet statements have been removed, check "
72+
"results are as expected.",
73+
"[core][java_bytecode][replace_nondet]")
74+
{
75+
GIVEN("A class with a call to CProver.nondetWithoutNull()")
76+
{
77+
symbol_tablet raw_symbol_table = load_java_class(
78+
"Main", "./java_bytecode/java_replace_nondet", "Main.replaceNondet");
79+
80+
journalling_symbol_tablet symbol_table =
81+
journalling_symbol_tablet::wrap(raw_symbol_table);
82+
83+
null_message_handlert null_output;
84+
goto_functionst functions;
85+
goto_convert(symbol_table, functions, null_output);
86+
87+
const std::string function_name = "java::Main.replaceNondet:()V";
88+
goto_functionst::goto_functiont &goto_function =
89+
functions.function_map.at(function_name);
90+
91+
goto_model_functiont model_function(
92+
symbol_table, functions, function_name, goto_function);
93+
94+
remove_instanceof(goto_function, symbol_table);
95+
96+
remove_virtual_functions(model_function);
97+
98+
WHEN("Remove returns is called before java nondet.")
99+
{
100+
remove_returns(model_function, [](const irep_idt &) { return false; });
101+
102+
replace_java_nondet(functions);
103+
104+
THEN("The nondet method call should have been removed.")
105+
{
106+
validate_method_removal(goto_function.body.instructions);
107+
}
108+
}
109+
110+
WHEN("Remove returns is called after java nondet.")
111+
{
112+
replace_java_nondet(functions);
113+
114+
remove_returns(model_function, [](const irep_idt &) { return false; });
115+
116+
THEN("The nondet method call should have been removed.")
117+
{
118+
validate_method_removal(goto_function.body.instructions);
119+
}
120+
}
121+
}
122+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public class Main {
2+
public void replaceNondet() {
3+
Object test = org.cprover.CProver.nondetWithoutNull();
4+
}
5+
}

0 commit comments

Comments
 (0)