Skip to content

Commit 45eae64

Browse files
author
owen-jones-diffblue
authored
Merge pull request diffblue#2505 from owen-jones-diffblue/owen-jones-diffblue/fix/convert-nondet
Make convert_java_nondet more general
2 parents 70887e2 + 6d9e805 commit 45eae64

File tree

2 files changed

+192
-71
lines changed

2 files changed

+192
-71
lines changed

jbmc/src/java_bytecode/convert_java_nondet.cpp

+125-64
Original file line numberDiff line numberDiff line change
@@ -22,90 +22,141 @@ Author: Reuben Thomas, [email protected]
2222

2323
#include "java_object_factory.h" // gen_nondet_init
2424

25+
/// Returns true if `expr` is a nondet pointer that isn't a function pointer or
26+
/// a void* pointer as these can be meaningfully non-det initalized.
27+
static bool is_nondet_pointer(exprt expr)
28+
{
29+
// If the expression type doesn't have a subtype then I guess it's primitive
30+
// and we do not want to convert it. If the type is a ptr-to-void or a
31+
// function pointer then we also do not want to convert it.
32+
const typet &type = expr.type();
33+
const bool non_void_non_function_pointer = type.id() == ID_pointer &&
34+
type.subtype().id() != ID_empty &&
35+
type.subtype().id() != ID_code;
36+
return can_cast_expr<side_effect_expr_nondett>(expr) &&
37+
non_void_non_function_pointer;
38+
}
39+
40+
/// Populate `instructions` with goto instructions to nondet init
41+
/// `aux_symbol_expr`
42+
static goto_programt get_gen_nondet_init_instructions(
43+
const symbol_exprt &aux_symbol_expr,
44+
const source_locationt &source_loc,
45+
symbol_table_baset &symbol_table,
46+
message_handlert &message_handler,
47+
const object_factory_parameterst &object_factory_parameters,
48+
const irep_idt &mode)
49+
{
50+
code_blockt gen_nondet_init_code;
51+
const bool skip_classid = true;
52+
gen_nondet_init(
53+
aux_symbol_expr,
54+
gen_nondet_init_code,
55+
symbol_table,
56+
source_loc,
57+
skip_classid,
58+
allocation_typet::DYNAMIC,
59+
object_factory_parameters,
60+
update_in_placet::NO_UPDATE_IN_PLACE);
61+
62+
goto_programt instructions;
63+
goto_convert(
64+
gen_nondet_init_code, symbol_table, instructions, message_handler, mode);
65+
return instructions;
66+
}
67+
2568
/// Checks an instruction to see whether it contains an assignment from
2669
/// side_effect_expr_nondet. If so, replaces the instruction with a range of
2770
/// instructions to properly nondet-initialize the lhs.
2871
/// \param goto_program: The goto program to modify.
2972
/// \param target: One of the steps in that goto program.
3073
/// \param symbol_table: The global symbol table.
3174
/// \param message_handler: Handles logging.
32-
/// \param max_nondet_array_length: Maximum size of new nondet arrays.
33-
/// \return The next instruction to process with this function.
34-
static goto_programt::targett insert_nondet_init_code(
75+
/// \param object_factory_parameters: Parameters for the generation of nondet
76+
/// objects.
77+
/// \param mode: Language mode
78+
/// \return The next instruction to process with this function and a boolean
79+
/// indicating whether any changes were made to the goto program.
80+
static std::pair<goto_programt::targett, bool> insert_nondet_init_code(
3581
goto_programt &goto_program,
3682
const goto_programt::targett &target,
3783
symbol_table_baset &symbol_table,
3884
message_handlert &message_handler,
3985
object_factory_parameterst object_factory_parameters,
4086
const irep_idt &mode)
4187
{
42-
// Return if the instruction isn't an assignment
43-
const auto next_instr=std::next(target);
44-
if(!target->is_assign())
45-
{
46-
return next_instr;
47-
}
88+
const auto next_instr = std::next(target);
4889

49-
// Return if the rhs of the assignment isn't a side effect expression
50-
const auto &assign=to_code_assign(target->code);
51-
if(assign.rhs().id()!=ID_side_effect)
90+
// We only expect to find nondets in the rhs of an assignments, and in return
91+
// statements if remove_returns has not been run, but we do a more general
92+
// check on all operands in case this changes
93+
for(exprt &op : target->code.operands())
5294
{
53-
return next_instr;
54-
}
95+
if(!is_nondet_pointer(op))
96+
{
97+
continue;
98+
}
5599

56-
// Return if the rhs isn't nondet
57-
const auto &side_effect=to_side_effect_expr(assign.rhs());
58-
if(side_effect.get_statement()!=ID_nondet)
59-
{
60-
return next_instr;
61-
}
100+
const auto &nondet_expr = to_side_effect_expr_nondet(op);
62101

63-
const auto lhs=assign.lhs();
64-
// If the lhs type doesn't have a subtype then I guess it's primitive and
65-
// we want to bail out now
66-
if(!lhs.type().has_subtype())
67-
{
68-
return next_instr;
69-
}
102+
if(!nondet_expr.get_nullable())
103+
object_factory_parameters.max_nonnull_tree_depth = 1;
70104

71-
// Although, if the type is a ptr-to-void then we also want to bail
72-
if(lhs.type().subtype().id()==ID_empty ||
73-
lhs.type().subtype().id()==ID_code)
74-
{
75-
return next_instr;
76-
}
105+
const source_locationt &source_loc = target->source_location;
77106

78-
// Check whether the nondet object may be null
79-
if(!to_side_effect_expr_nondet(side_effect).get_nullable())
80-
object_factory_parameters.max_nonnull_tree_depth = 1;
81-
// Get the symbol to nondet-init
82-
const auto source_loc=target->source_location;
107+
// Currently the code looks like this
108+
// target: instruction containing op
109+
// When we are done it will look like this
110+
// target : declare aux_symbol
111+
// . <gen_nondet_init_instructions - many lines>
112+
// . <gen_nondet_init_instructions - many lines>
113+
// . <gen_nondet_init_instructions - many lines>
114+
// target2: instruction containing op, with op replaced by aux_symbol
115+
// dead aux_symbol
83116

84-
// Erase the nondet assignment
85-
target->make_skip();
117+
symbolt &aux_symbol = get_fresh_aux_symbol(
118+
op.type(),
119+
id2string(goto_programt::get_function_id(goto_program)),
120+
"nondet_tmp",
121+
source_loc,
122+
ID_java,
123+
symbol_table);
86124

87-
// Generate nondet init code
88-
code_blockt init_code;
89-
gen_nondet_init(
90-
lhs,
91-
init_code,
92-
symbol_table,
93-
source_loc,
94-
true,
95-
allocation_typet::DYNAMIC,
96-
object_factory_parameters,
97-
update_in_placet::NO_UPDATE_IN_PLACE);
125+
const symbol_exprt aux_symbol_expr = aux_symbol.symbol_expr();
126+
op = aux_symbol_expr;
98127

99-
// Convert this code into goto instructions
100-
goto_programt new_instructions;
101-
goto_convert(
102-
init_code, symbol_table, new_instructions, message_handler, mode);
128+
// Insert an instruction declaring `aux_symbol` before `target`, being
129+
// careful to preserve jumps to `target`
130+
goto_programt::instructiont decl_aux_symbol;
131+
decl_aux_symbol.make_decl(code_declt(aux_symbol_expr));
132+
decl_aux_symbol.source_location = source_loc;
133+
goto_program.insert_before_swap(target, decl_aux_symbol);
103134

104-
// Insert the new instructions into the instruction list
105-
goto_program.destructive_insert(next_instr, new_instructions);
106-
goto_program.update();
135+
// Keep track of the new location of the instruction containing op.
136+
const goto_programt::targett target2 = std::next(target);
107137

108-
return next_instr;
138+
goto_programt gen_nondet_init_instructions =
139+
get_gen_nondet_init_instructions(
140+
aux_symbol_expr,
141+
source_loc,
142+
symbol_table,
143+
message_handler,
144+
object_factory_parameters,
145+
mode);
146+
goto_program.destructive_insert(target2, gen_nondet_init_instructions);
147+
148+
goto_programt::targett dead_aux_symbol = goto_program.insert_after(target2);
149+
dead_aux_symbol->make_dead();
150+
dead_aux_symbol->code = code_deadt(aux_symbol_expr);
151+
dead_aux_symbol->source_location = source_loc;
152+
153+
// In theory target could have more than one operand which should be
154+
// replaced by convert_nondet, so we return target2 so that it
155+
// will be checked again
156+
return std::make_pair(target2, true);
157+
}
158+
159+
return std::make_pair(next_instr, false);
109160
}
110161

111162
/// For each instruction in the goto program, checks if it is an assignment from
@@ -114,25 +165,35 @@ static goto_programt::targett insert_nondet_init_code(
114165
/// \param goto_program: The goto program to modify.
115166
/// \param symbol_table: The global symbol table.
116167
/// \param message_handler: Handles logging.
117-
/// \param max_nondet_array_length: Maximum size of new nondet arrays.
168+
/// \param object_factory_parameters: Parameters for the generation of nondet
169+
/// objects.
170+
/// \param mode: Language mode
118171
void convert_nondet(
119172
goto_programt &goto_program,
120173
symbol_table_baset &symbol_table,
121174
message_handlert &message_handler,
122175
const object_factory_parameterst &object_factory_parameters,
123176
const irep_idt &mode)
124177
{
125-
for(auto instruction_iterator=goto_program.instructions.begin(),
126-
end=goto_program.instructions.end();
127-
instruction_iterator!=end;)
178+
bool changed = false;
179+
auto instruction_iterator = goto_program.instructions.begin();
180+
181+
while(instruction_iterator != goto_program.instructions.end())
128182
{
129-
instruction_iterator = insert_nondet_init_code(
183+
auto ret = insert_nondet_init_code(
130184
goto_program,
131185
instruction_iterator,
132186
symbol_table,
133187
message_handler,
134188
object_factory_parameters,
135189
mode);
190+
instruction_iterator = ret.first;
191+
changed |= ret.second;
192+
}
193+
194+
if(changed)
195+
{
196+
goto_program.update();
136197
}
137198
}
138199

jbmc/unit/java_bytecode/java_replace_nondet/replace_nondet.cpp

+67-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <goto-programs/remove_virtual_functions.h>
1616
#include <goto-programs/remove_returns.h>
1717

18+
#include <java_bytecode/convert_java_nondet.h>
19+
#include <java_bytecode/object_factory_parameters.h>
1820
#include <java_bytecode/remove_instanceof.h>
1921
#include <java_bytecode/replace_java_nondet.h>
2022

@@ -26,7 +28,7 @@
2628
#include <iostream>
2729
#include <java-testing-utils/load_java_class.h>
2830

29-
void validate_method_removal(
31+
void validate_nondet_method_removed(
3032
std::list<goto_programt::instructiont> instructions)
3133
{
3234
bool method_removed = true, replacement_nondet_exists = false;
@@ -90,6 +92,40 @@ void validate_method_removal(
9092
REQUIRE(replacement_nondet_exists);
9193
}
9294

95+
void validate_nondets_converted(
96+
std::list<goto_programt::instructiont> instructions)
97+
{
98+
bool nondet_exists = false;
99+
bool allocate_exists = false;
100+
for(const auto &inst : instructions)
101+
{
102+
// Check that our NONDET(<type>) exists on a rhs somewhere.
103+
exprt target_expression =
104+
(inst.is_assign()
105+
? to_code_assign(inst.code).rhs()
106+
: inst.is_return() ? to_code_return(inst.code).return_value()
107+
: inst.code);
108+
109+
if(
110+
const auto side_effect =
111+
expr_try_dynamic_cast<side_effect_exprt>(target_expression))
112+
{
113+
if(side_effect->get_statement() == ID_nondet)
114+
{
115+
nondet_exists = true;
116+
}
117+
118+
if(side_effect->get_statement() == ID_allocate)
119+
{
120+
allocate_exists = true;
121+
}
122+
}
123+
}
124+
125+
REQUIRE_FALSE(nondet_exists);
126+
REQUIRE(allocate_exists);
127+
}
128+
93129
void load_and_test_method(
94130
const std::string &method_signature,
95131
goto_functionst &functions,
@@ -110,26 +146,50 @@ void load_and_test_method(
110146
remove_virtual_functions(model_function);
111147

112148
// Then test both situations.
149+
THEN("Replace nondet should work after remove returns has been called.")
150+
{
151+
remove_returns(model_function, [](const irep_idt &) { return false; });
152+
153+
replace_java_nondet(model_function);
154+
155+
validate_nondet_method_removed(goto_function.body.instructions);
156+
}
157+
158+
THEN("Replace nondet should work before remove returns has been called.")
159+
{
160+
replace_java_nondet(model_function);
161+
162+
remove_returns(model_function, [](const irep_idt &) { return false; });
163+
164+
validate_nondet_method_removed(goto_function.body.instructions);
165+
}
166+
167+
object_factory_parameterst params{};
168+
113169
THEN(
114-
"Code should work when remove returns is called before "
115-
"replace_java_nondet.")
170+
"Replace and convert nondet should work after remove returns has been "
171+
"called.")
116172
{
117173
remove_returns(model_function, [](const irep_idt &) { return false; });
118174

119175
replace_java_nondet(model_function);
120176

121-
validate_method_removal(goto_function.body.instructions);
177+
convert_nondet(model_function, null_message_handler, params, ID_java);
178+
179+
validate_nondets_converted(goto_function.body.instructions);
122180
}
123181

124182
THEN(
125-
"Code should work when remove returns is called after "
126-
"replace_java_nondet.")
183+
"Replace and convert nondet should work before remove returns has been "
184+
"called.")
127185
{
128186
replace_java_nondet(model_function);
129187

188+
convert_nondet(model_function, null_message_handler, params, ID_java);
189+
130190
remove_returns(model_function, [](const irep_idt &) { return false; });
131191

132-
validate_method_removal(goto_function.body.instructions);
192+
validate_nondets_converted(goto_function.body.instructions);
133193
}
134194
}
135195

0 commit comments

Comments
 (0)