Skip to content

Commit a6b2cda

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 ec1127c commit a6b2cda

File tree

3 files changed

+315
-0
lines changed

3 files changed

+315
-0
lines changed

src/java_bytecode/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ SRC = bytecode_info.cpp \
3737
remove_java_new.cpp \
3838
replace_java_nondet.cpp \
3939
select_pointer_type.cpp \
40+
simple_method_stubbing.cpp \
4041
# Empty last line
4142

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