Skip to content

Commit f85494b

Browse files
committed
Add remove_java_nondet
Use specialised member functions instead of `get` Fix indentation Remove insert_before
1 parent 7ed66a1 commit f85494b

File tree

4 files changed

+386
-0
lines changed

4 files changed

+386
-0
lines changed

src/cbmc/cbmc_parse_options.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Author: Daniel Kroening, [email protected]
4141
#include <goto-programs/link_to_library.h>
4242
#include <goto-programs/remove_skip.h>
4343
#include <goto-programs/show_goto_functions.h>
44+
#include <goto-programs/remove_java_nondet.h>
4445

4546
#include <goto-symex/rewrite_union.h>
4647
#include <goto-symex/adjust_float_expressions.h>
@@ -53,6 +54,8 @@ Author: Daniel Kroening, [email protected]
5354

5455
#include <langapi/mode.h>
5556

57+
#include "java_bytecode/java_bytecode_language.h"
58+
5659
#include "cbmc_solvers.h"
5760
#include "cbmc_parse_options.h"
5861
#include "bmc.h"
@@ -921,6 +924,19 @@ bool cbmc_parse_optionst::process_goto_program(
921924
remove_complex(symbol_table, goto_functions);
922925
rewrite_union(goto_functions, ns);
923926

927+
// Similar removal of java nondet statements:
928+
// TODO Should really get this from java_bytecode_language somehow, but we
929+
// don't have an instance of that here.
930+
const auto max_nondet_array_length=
931+
cmdline.isset("java-max-input-array-length")
932+
? std::stoi(cmdline.get_value("java-max-input-array-length"))
933+
: MAX_NONDET_ARRAY_LENGTH_DEFAULT;
934+
remove_java_nondet(
935+
ui_message_handler,
936+
symbol_table,
937+
max_nondet_array_length,
938+
goto_functions);
939+
924940
// add generic checks
925941
status() << "Generic Property Instrumentation" << eom;
926942
goto_check(ns, options, goto_functions);

src/goto-programs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ SRC = goto_convert.cpp goto_convert_function_call.cpp \
2525
remove_const_function_pointers.cpp \
2626
system_library_symbols.cpp \
2727
string_refine_preprocess.cpp \
28+
remove_java_nondet.cpp \
2829
# Empty last line
2930

3031
INCLUDES= -I ..
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
/*******************************************************************\
2+
3+
Module: Remove Java Nondet expressions
4+
5+
Author: Reuben Thomas, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#include "goto-programs/remove_java_nondet.h"
10+
#include "goto-programs/goto_convert.h"
11+
#include "goto-programs/goto_model.h"
12+
13+
#include "java_bytecode/java_object_factory.h"
14+
15+
#include "util/irep_ids.h"
16+
17+
#include <algorithm>
18+
#include <regex>
19+
20+
/*******************************************************************\
21+
22+
Struct: nondet_instruction_infot
23+
24+
Purpose: Hold information about a nondet instruction invocation.
25+
26+
\*******************************************************************/
27+
28+
struct nondet_instruction_infot final
29+
{
30+
bool is_nondet_instruction;
31+
bool is_never_null;
32+
};
33+
34+
/*******************************************************************\
35+
36+
Function: is_nondet_returning_object
37+
38+
Inputs:
39+
function_call: A function call expression.
40+
41+
Outputs: Whether the function has a nondet signature, and whether the nondet
42+
item may be null.
43+
44+
45+
Purpose: Find whether the supplied function call has a recognised 'nondet'
46+
library signature. If so, specify whether the function call is
47+
expected to return non-null.
48+
49+
\*******************************************************************/
50+
51+
static nondet_instruction_infot is_nondet_returning_object(
52+
const code_function_callt &function_call)
53+
{
54+
const auto &function_symbol=to_symbol_expr(function_call.function());
55+
const auto function_name=id2string(function_symbol.get_identifier());
56+
std::smatch match_results;
57+
if(!std::regex_match(
58+
function_name,
59+
match_results,
60+
std::regex(".*org\\.cprover\\.CProver\\.nondetWith(out)?Null.*")))
61+
{
62+
return {false, false};
63+
}
64+
65+
return {true, match_results[1].matched};
66+
}
67+
68+
/*******************************************************************\
69+
70+
Function: get_nondet_instruction_info
71+
72+
Inputs:
73+
instr: A goto program instruction.
74+
75+
Outputs: Whether the instruction is a function with a nondet signature, and
76+
whether the nondet item may be null.
77+
78+
Purpose: Return whether the instruction has a recognised 'nondet' library
79+
signature.
80+
81+
\*******************************************************************/
82+
83+
static nondet_instruction_infot get_nondet_instruction_info(
84+
const goto_programt::targett &instr)
85+
{
86+
if(!(instr->is_function_call() && instr->code.id()==ID_code))
87+
{
88+
return {false, false};
89+
}
90+
const auto &code=to_code(instr->code);
91+
if(code.get_statement()!=ID_function_call)
92+
{
93+
return {false, false};
94+
}
95+
const auto &function_call=to_code_function_call(code);
96+
return is_nondet_returning_object(function_call);
97+
}
98+
99+
/*******************************************************************\
100+
101+
Function: is_symbol_with_id
102+
103+
Inputs:
104+
expr: The expression which may be a symbol.
105+
identifier: Some identifier.
106+
107+
Outputs: True if the expression is a symbol with the specified identifier.
108+
109+
Purpose: Return whether the expression is a symbol with the specified
110+
identifier.
111+
112+
\*******************************************************************/
113+
114+
static bool is_symbol_with_id(const exprt& expr, const dstringt& identifier)
115+
{
116+
return expr.id()==ID_symbol &&
117+
to_symbol_expr(expr).get_identifier()==identifier;
118+
}
119+
120+
/*******************************************************************\
121+
122+
Function: is_typecast_with_id
123+
124+
Inputs:
125+
expr: The expression which may be a typecast.
126+
identifier: Some identifier.
127+
128+
Outputs: True if the expression is a typecast with one operand, and the
129+
typecast's identifier matches the specified identifier.
130+
131+
Purpose: Return whether the expression is a typecast with the specified
132+
identifier.
133+
134+
\*******************************************************************/
135+
136+
static bool is_typecast_with_id(const exprt& expr, const dstringt& identifier)
137+
{
138+
if(!(expr.id()==ID_typecast && expr.operands().size()==1))
139+
{
140+
return false;
141+
}
142+
const auto &typecast=to_typecast_expr(expr);
143+
if(!(typecast.op().id()==ID_symbol && !typecast.op().has_operands()))
144+
{
145+
return false;
146+
}
147+
const auto &op_symbol=to_symbol_expr(typecast.op());
148+
// Return whether the typecast has the expected operand
149+
return op_symbol.get_identifier()==identifier;
150+
}
151+
152+
/*******************************************************************\
153+
154+
Function: is_assignment_from
155+
156+
Inputs:
157+
instr: A goto program instruction.
158+
identifier: Some identifier.
159+
160+
Outputs: True if the expression is a typecast with one operand, and the
161+
typecast's identifier matches the specified identifier.
162+
163+
Purpose: Return whether the instruction is an assignment, and the rhs is a
164+
symbol or typecast expression with the specified identifier.
165+
166+
\*******************************************************************/
167+
168+
static bool is_assignment_from(
169+
const goto_programt::instructiont &instr,
170+
const dstringt &identifier)
171+
{
172+
// If not an assignment, return false
173+
if(!instr.is_assign())
174+
{
175+
return false;
176+
}
177+
const auto &rhs=to_code_assign(instr.code).rhs();
178+
return is_symbol_with_id(rhs, identifier) ||
179+
is_typecast_with_id(rhs, identifier);
180+
}
181+
182+
/*******************************************************************\
183+
184+
Function: process_target
185+
186+
Inputs:
187+
message_handler: Handles logging.
188+
symbol_table: The table of known symbols.
189+
max_nondet_array_length: Max length of arrays in new nondet objects.
190+
instructions: The list of all instructions.
191+
instr: The instruction to check.
192+
193+
Outputs: The next instruction to process, probably with this function.
194+
195+
Purpose: Given an iterator into a list of instructions, modify the list to
196+
replace 'nondet' library functions with CBMC-native nondet
197+
expressions, and return an iterator to the next instruction to check.
198+
199+
\*******************************************************************/
200+
201+
static goto_programt::targett process_target(
202+
message_handlert &message_handler,
203+
symbol_tablet &symbol_table,
204+
const size_t max_nondet_array_length,
205+
goto_programt &prog,
206+
const goto_programt::targett &instr)
207+
{
208+
// Check whether this is our nondet library method, and return if not
209+
const auto instr_info=get_nondet_instruction_info(instr);
210+
const auto next_instr=std::next(instr);
211+
if(!instr_info.is_nondet_instruction)
212+
{
213+
return next_instr;
214+
}
215+
216+
// Look at the next instruction, ensure that it is an assignment
217+
assert(next_instr->is_assign());
218+
// Get the name of the LHS of the assignment
219+
const auto &next_instr_assign_lhs=to_code_assign(next_instr->code).lhs();
220+
if(!(next_instr_assign_lhs.id()==ID_symbol &&
221+
!next_instr_assign_lhs.has_operands()))
222+
{
223+
return next_instr;
224+
}
225+
const auto return_identifier=
226+
to_symbol_expr(next_instr_assign_lhs).get_identifier();
227+
228+
auto &instructions=prog.instructions;
229+
const auto end=std::end(instructions);
230+
231+
// Look for an instruction where this name is on the RHS of an assignment
232+
const auto matching_assignment=std::find_if(
233+
next_instr,
234+
end,
235+
[&return_identifier](const goto_programt::instructiont &instr)
236+
{
237+
return is_assignment_from(instr, return_identifier);
238+
});
239+
240+
assert(matching_assignment!=end);
241+
242+
// Assume that the LHS of *this* assignment is the actual nondet variable
243+
const auto &code_assign=to_code_assign(matching_assignment->code);
244+
const auto nondet_var=code_assign.lhs();
245+
const auto source_loc=instr->source_location;
246+
247+
// Erase from the nondet function call to the assignment
248+
const auto after_matching_assignment=std::next(matching_assignment);
249+
assert(after_matching_assignment!=end);
250+
const auto after_erased=
251+
instructions.erase(instr, after_matching_assignment);
252+
253+
// Generate nondet init code
254+
code_blockt init_code;
255+
gen_nondet_init(
256+
nondet_var,
257+
init_code,
258+
symbol_table,
259+
source_loc,
260+
false,
261+
true,
262+
instr_info.is_never_null,
263+
message_handler,
264+
max_nondet_array_length);
265+
266+
// Convert this code into goto instructions
267+
goto_programt new_instructions;
268+
goto_convert(init_code, symbol_table, new_instructions, message_handler);
269+
270+
// Insert the new instructions into the instruction list
271+
prog.destructive_insert(after_erased, new_instructions);
272+
prog.update();
273+
274+
return after_erased;
275+
}
276+
277+
/*******************************************************************\
278+
279+
Function: remove_java_nondet
280+
281+
Inputs:
282+
message_handler: Object which prints messages.
283+
symbol_table: The list of known symbols.
284+
max_nondet_array_length: The maximum length of new nondet arrays.
285+
goto_program: The program to modify.
286+
287+
Purpose: Modify a goto_programt to replace 'nondet' library functions with
288+
CBMC-native nondet expressions.
289+
290+
\*******************************************************************/
291+
292+
static void remove_java_nondet(
293+
message_handlert &message_handler,
294+
symbol_tablet &symbol_table,
295+
const size_t max_nondet_array_length,
296+
goto_programt &goto_program)
297+
{
298+
// Check each instruction.
299+
// `process_target` may modify the list in place, and returns the next
300+
// iterator to look at.
301+
for(auto instruction_iterator=std::begin(goto_program.instructions),
302+
end=std::end(goto_program.instructions);
303+
instruction_iterator!=end;
304+
instruction_iterator=process_target(
305+
message_handler,
306+
symbol_table,
307+
max_nondet_array_length,
308+
goto_program,
309+
instruction_iterator))
310+
{
311+
// Loop body deliberately empty.
312+
}
313+
}
314+
315+
void remove_java_nondet(
316+
message_handlert &message_handler,
317+
symbol_tablet &symbol_table,
318+
const size_t max_nondet_array_length,
319+
goto_functionst &goto_functions)
320+
{
321+
for(auto &f : goto_functions.function_map)
322+
{
323+
remove_java_nondet(
324+
message_handler,
325+
symbol_table,
326+
max_nondet_array_length,
327+
f.second.body);
328+
}
329+
goto_functions.compute_location_numbers();
330+
}

0 commit comments

Comments
 (0)