Skip to content

Commit d263967

Browse files
committed
Add Java simple method stubbing pass
This generates simple stub bodies using the same method as convert_nondet, but creates the stubs out-of-line rather than inline. That adds support for nondet constructors, which jbmc previously ignored.
1 parent 5906e8d commit d263967

File tree

3 files changed

+333
-0
lines changed

3 files changed

+333
-0
lines changed

src/java_bytecode/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ SRC = bytecode_info.cpp \
3636
remove_java_new.cpp \
3737
replace_java_nondet.cpp \
3838
select_pointer_type.cpp \
39+
simple_method_stubbing.cpp \
3940
# Empty last line
4041

4142
INCLUDES= -I ..
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
/*******************************************************************\
2+
3+
Module: Simple Java method stubbing
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Java simple opaque stub generation
11+
12+
#include "simple_method_stubbing.h"
13+
14+
#include <util/std_expr.h>
15+
#include <util/std_code.h>
16+
#include <util/pointer_offset_size.h>
17+
#include <util/namespace.h>
18+
#include <util/prefix.h>
19+
#include <util/fresh_symbol.h>
20+
#include <util/invariant_utils.h>
21+
22+
#include <java_bytecode/java_object_factory.h>
23+
#include <java_bytecode/java_pointer_casts.h>
24+
25+
class java_simple_method_stubst
26+
{
27+
public:
28+
java_simple_method_stubst(
29+
symbol_table_baset &_symbol_table,
30+
bool _assume_non_null,
31+
const object_factory_parameterst &_object_factory_parameters,
32+
message_handlert &_message_handler)
33+
: symbol_table(_symbol_table),
34+
assume_non_null(_assume_non_null),
35+
object_factory_parameters(_object_factory_parameters),
36+
message_handler(_message_handler)
37+
{
38+
}
39+
40+
void create_method_stub_at(
41+
const typet &expected_type,
42+
const exprt &ptr,
43+
const source_locationt &loc,
44+
code_blockt &parent_block,
45+
unsigned insert_before_index,
46+
bool is_constructor,
47+
bool update_in_place);
48+
49+
void create_method_stub(symbolt &symbol);
50+
51+
void check_method_stub(
52+
const irep_idt &);
53+
54+
protected:
55+
symbol_table_baset &symbol_table;
56+
bool assume_non_null;
57+
const object_factory_parameterst &object_factory_parameters;
58+
message_handlert &message_handler;
59+
};
60+
61+
/// Nondet-initialize an object, including allocating referees for reference
62+
/// fields and nondet-initialising those recursively. Reference fields are
63+
/// nondeterministically assigned either a fresh object or null; arrays are
64+
/// additionally nondeterministically assigned between 0 and
65+
/// `max_nondet_array_length` members.
66+
/// \param expected_type: the expected actual type of `ptr`. We will cast
67+
/// `ptr` to this type and initialize assuming the actual referee has this
68+
/// type.
69+
/// \param ptr: pointer to the memory to initialize
70+
/// \param loc: source location to set for the opaque method stub
71+
/// \param [out] parent_block: The parent block in which the new instructions
72+
/// will be added.
73+
/// \param insert_before_index: The position in which the new instructions
74+
/// will be added.
75+
/// \param is_constructor: true if we're initialising the `this` pointer of a
76+
/// constructor. In this case the target's class identifier has been set and
77+
/// should not be overridden.
78+
/// \param update_in_place: Whether to generate the nondet in place or not.
79+
void java_simple_method_stubst::create_method_stub_at(
80+
const typet &expected_type,
81+
const exprt &ptr,
82+
const source_locationt &loc,
83+
code_blockt &parent_block,
84+
const unsigned insert_before_index,
85+
const bool is_constructor,
86+
const bool update_in_place)
87+
{
88+
// At this point we know 'ptr' points to an opaque-typed object.
89+
// We should nondet-initialize it and insert the instructions *after*
90+
// the offending call at (*parent_block)[insert_before_index].
91+
92+
INVARIANT_WITH_IREP(
93+
expected_type.id() == ID_pointer,
94+
"Nondet initializer result type: expected a pointer",
95+
expected_type);
96+
97+
namespacet ns(symbol_table);
98+
99+
const auto &expected_base = ns.follow(expected_type.subtype());
100+
if(expected_base.id() != ID_struct)
101+
return;
102+
103+
const exprt cast_ptr =
104+
make_clean_pointer_cast(ptr, to_pointer_type(expected_type), ns);
105+
106+
exprt to_init = cast_ptr;
107+
// If it's a constructor the thing we're constructing has already
108+
// been allocated by this point.
109+
if(is_constructor)
110+
to_init = dereference_exprt(to_init, expected_base);
111+
112+
// Generate new instructions.
113+
code_blockt new_instructions;
114+
gen_nondet_init(
115+
to_init,
116+
new_instructions,
117+
symbol_table,
118+
loc,
119+
is_constructor,
120+
allocation_typet::DYNAMIC,
121+
!assume_non_null,
122+
object_factory_parameters,
123+
update_in_place ? update_in_placet::MUST_UPDATE_IN_PLACE
124+
: update_in_placet::NO_UPDATE_IN_PLACE);
125+
126+
// Insert new_instructions into parent block.
127+
if(!new_instructions.operands().empty())
128+
{
129+
auto insert_position = parent_block.operands().begin();
130+
std::advance(insert_position, insert_before_index);
131+
parent_block.operands().insert(
132+
insert_position,
133+
new_instructions.operands().begin(),
134+
new_instructions.operands().end());
135+
}
136+
}
137+
138+
/// Replaces sym's value with an opaque method stub. If sym is a
139+
/// constructor function this nondet-initializes `*this` using the function
140+
/// above; otherwise it generates a return value using a similar method.
141+
/// \param symbol: Function symbol to stub
142+
void java_simple_method_stubst::create_method_stub(symbolt &symbol)
143+
{
144+
code_blockt new_instructions;
145+
const code_typet &required_type = to_code_type(symbol.type);
146+
namespacet ns(symbol_table);
147+
148+
// synthetic source location that contains the opaque function name only
149+
source_locationt synthesized_source_location;
150+
synthesized_source_location.set_function(symbol.name);
151+
152+
// Initialize the return value or `this` parameter under construction.
153+
// Note symbol.type is required_type, but with write access
154+
// Probably required_type should not be const
155+
const bool is_constructor = required_type.get_is_constructor();
156+
if(is_constructor)
157+
{
158+
const auto &this_argument = required_type.parameters()[0];
159+
const typet &this_type = this_argument.type();
160+
symbolt &init_symbol = get_fresh_aux_symbol(
161+
this_type,
162+
"to_construct",
163+
"to_construct",
164+
synthesized_source_location,
165+
ID_java,
166+
symbol_table);
167+
init_symbol.type = this_type;
168+
const symbol_exprt &init_symbol_expression = init_symbol.symbol_expr();
169+
code_assignt get_argument(
170+
init_symbol_expression, symbol_exprt(this_argument.get_identifier()));
171+
get_argument.add_source_location() = synthesized_source_location;
172+
new_instructions.copy_to_operands(get_argument);
173+
create_method_stub_at(
174+
this_type,
175+
init_symbol_expression,
176+
synthesized_source_location,
177+
new_instructions,
178+
1,
179+
true,
180+
false);
181+
}
182+
else
183+
{
184+
const typet &required_return_type = required_type.return_type();
185+
if(required_return_type != empty_typet())
186+
{
187+
symbolt &to_return_symbol = get_fresh_aux_symbol(
188+
required_return_type,
189+
"to_return",
190+
"to_return",
191+
synthesized_source_location,
192+
ID_java,
193+
symbol_table);
194+
to_return_symbol.type = required_return_type;
195+
const exprt &to_return = to_return_symbol.symbol_expr();
196+
if(to_return_symbol.type.id()!=ID_pointer)
197+
{
198+
gen_nondet_init(
199+
to_return,
200+
new_instructions,
201+
symbol_table,
202+
source_locationt(),
203+
false,
204+
allocation_typet::LOCAL, // Irrelevant as type is primitive
205+
!assume_non_null,
206+
object_factory_parameters,
207+
update_in_placet::NO_UPDATE_IN_PLACE);
208+
}
209+
else
210+
create_method_stub_at(
211+
required_return_type,
212+
to_return,
213+
synthesized_source_location,
214+
new_instructions,
215+
0,
216+
false,
217+
false);
218+
new_instructions.copy_to_operands(code_returnt(to_return));
219+
}
220+
}
221+
222+
symbol.value = new_instructions;
223+
}
224+
225+
/// Replaces `sym` with a function stub per the function above if it is
226+
/// of suitable type.
227+
/// \param symname: Symbol name to consider stubbing
228+
void java_simple_method_stubst::check_method_stub(
229+
const irep_idt &symname)
230+
{
231+
const symbolt &sym=*symbol_table.lookup(symname);
232+
if(sym.is_type)
233+
return;
234+
if(sym.value.id()!=ID_nil)
235+
return;
236+
if(sym.type.id()!=ID_code)
237+
return;
238+
// Don't stub internal locking primitives:
239+
if(sym.name=="java::monitorenter" || sym.name=="java::monitorexit")
240+
return;
241+
242+
create_method_stub(*symbol_table.get_writeable(symname));
243+
}
244+
245+
void java_generate_simple_method_stub(
246+
const irep_idt &function_name,
247+
symbol_table_baset &symbol_table,
248+
bool assume_non_null,
249+
const object_factory_parameterst &object_factory_parameters,
250+
message_handlert &message_handler)
251+
{
252+
java_simple_method_stubst stub_generator(
253+
symbol_table,
254+
assume_non_null,
255+
object_factory_parameters,
256+
message_handler);
257+
258+
stub_generator.check_method_stub(function_name);
259+
}
260+
261+
262+
/// Generates function stubs for most undefined functions in the symbol
263+
/// table, except as forbidden in
264+
/// `java_simple_method_stubst::check_method_stub`.
265+
/// \param symbol_table: Global symbol table
266+
/// \param assume_non_null: If true, generated function stubs will never
267+
/// initialize reference members with null.
268+
/// \param object_factory_parameters: specifies exactly how nondet composite
269+
/// objects should be created-- for example, object graph depth.
270+
/// \param message_handler: Logging
271+
void java_generate_simple_method_stubs(
272+
symbol_table_baset &symbol_table,
273+
bool assume_non_null,
274+
const object_factory_parameterst &object_factory_parameters,
275+
message_handlert &message_handler)
276+
{
277+
// The intent here is to apply stub_generator.check_method_stub() to
278+
// all the identifiers in the symbol table. However this method may alter the
279+
// symbol table and iterating over a container which is being modified has
280+
// undefined behaviour. Therefore in order for this to work reliably, we must
281+
// take a copy of the identifiers in the symbol table and iterate over that,
282+
// instead of iterating over the symbol table itself.
283+
std::vector<irep_idt> identifiers;
284+
identifiers.reserve(symbol_table.symbols.size());
285+
for(const auto &symbol : symbol_table)
286+
identifiers.push_back(symbol.first);
287+
288+
java_simple_method_stubst stub_generator(
289+
symbol_table,
290+
assume_non_null,
291+
object_factory_parameters,
292+
message_handler);
293+
294+
for(const auto &identifier : identifiers)
295+
{
296+
stub_generator.check_method_stub(identifier);
297+
}
298+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*******************************************************************\
2+
3+
Module: Simple Java method stubbing
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Java simple opaque stub generation
11+
12+
#ifndef CPROVER_JAVA_BYTECODE_SIMPLE_METHOD_STUBBING_H
13+
#define CPROVER_JAVA_BYTECODE_SIMPLE_METHOD_STUBBING_H
14+
15+
#include <util/symbol_table_base.h>
16+
#include <java_bytecode/java_bytecode_language.h>
17+
18+
class message_handlert;
19+
class code_typet;
20+
21+
void java_generate_simple_method_stub(
22+
const irep_idt &function_name,
23+
symbol_table_baset &symbol_table,
24+
bool assume_non_null,
25+
const object_factory_parameterst &object_factory_parameters,
26+
message_handlert &message_handler);
27+
28+
void java_generate_simple_method_stubs(
29+
symbol_table_baset &symbol_table,
30+
bool assume_non_null,
31+
const object_factory_parameterst &object_factory_parameters,
32+
message_handlert &message_handler);
33+
34+
#endif

0 commit comments

Comments
 (0)