Skip to content

Commit 6749aaf

Browse files
author
Matthias Güdemann
authored
Merge pull request #2670 from mgudemann/feature/multi_object_input_parameter
[TG-4331] Support multiple types for input parameters for entry point function
2 parents 82a1bd3 + 4ed36c6 commit 6749aaf

File tree

6 files changed

+267
-11
lines changed

6 files changed

+267
-11
lines changed

jbmc/src/java_bytecode/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ SRC = bytecode_info.cpp \
3535
java_utils.cpp \
3636
load_method_by_regex.cpp \
3737
mz_zip_archive.cpp \
38+
nondet.cpp \
3839
remove_exceptions.cpp \
3940
remove_instanceof.cpp \
4041
remove_java_new.cpp \

jbmc/src/java_bytecode/java_entry_point.cpp

+88-11
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Author: Daniel Kroening, [email protected]
2222
#include "java_object_factory.h"
2323
#include "java_string_literals.h"
2424
#include "java_utils.h"
25+
#include "nondet.h"
26+
#include <util/fresh_symbol.h>
2527

2628
#define JAVA_MAIN_METHOD "main:([Ljava/lang/String;)V"
2729

@@ -317,17 +319,92 @@ exprt::operandst java_build_arguments(
317319

318320
parameters.function_id = goto_functionst::entry_point();
319321

320-
// generate code to allocate and non-deterministicaly initialize the
321-
// argument
322-
main_arguments[param_number] = object_factory(
323-
p.type(),
324-
base_name,
325-
init_code,
326-
symbol_table,
327-
parameters,
328-
allocation_typet::LOCAL,
329-
function.location,
330-
pointer_type_selector);
322+
namespacet ns(symbol_table);
323+
324+
// Generate code to allocate and non-deterministicaly initialize the
325+
// argument, if the argument has different possible object types (e.g., from
326+
// casts in the function body), then choose one in a non-deterministic way.
327+
const auto alternatives =
328+
pointer_type_selector.get_parameter_alternative_types(
329+
function.name, p.get_identifier(), ns);
330+
if(alternatives.empty())
331+
{
332+
main_arguments[param_number] = object_factory(
333+
p.type(),
334+
base_name,
335+
init_code,
336+
symbol_table,
337+
parameters,
338+
allocation_typet::LOCAL,
339+
function.location,
340+
pointer_type_selector);
341+
}
342+
else
343+
{
344+
INVARIANT(!is_this, "We cannot have different types for `this` here");
345+
// create a non-deterministic switch between all possible values for the
346+
// type of the parameter.
347+
code_switcht code_switch;
348+
349+
// the idea is to get a new symbol for the parameter value `tmp`
350+
351+
// then add a non-deterministic switch over all possible input types,
352+
// construct the object type at hand and assign to `tmp`
353+
354+
// switch(...)
355+
// {
356+
// case obj1:
357+
// tmp_expr = object_factory(...)
358+
// param = tmp_expr
359+
// break
360+
// ...
361+
// }
362+
// method(..., param, ...)
363+
//
364+
365+
const symbolt result_symbol = get_fresh_aux_symbol(
366+
p.type(),
367+
id2string(function.name),
368+
"nondet_parameter_" + std::to_string(param_number),
369+
function.location,
370+
ID_java,
371+
symbol_table);
372+
main_arguments[param_number] = result_symbol.symbol_expr();
373+
374+
std::vector<codet> cases(alternatives.size());
375+
const auto initialize_parameter = [&](const symbol_typet &type) {
376+
code_blockt init_code_for_type;
377+
exprt init_expr_for_parameter = object_factory(
378+
java_reference_type(type),
379+
id2string(base_name) + "_alternative_" +
380+
id2string(type.get_identifier()),
381+
init_code_for_type,
382+
symbol_table,
383+
parameters,
384+
allocation_typet::DYNAMIC,
385+
function.location,
386+
pointer_type_selector);
387+
init_code_for_type.add(
388+
code_assignt(
389+
result_symbol.symbol_expr(),
390+
typecast_exprt(init_expr_for_parameter, p.type())));
391+
return init_code_for_type;
392+
};
393+
394+
std::transform(
395+
alternatives.begin(),
396+
alternatives.end(),
397+
cases.begin(),
398+
initialize_parameter);
399+
400+
init_code.add(
401+
generate_nondet_switch(
402+
id2string(function.name) + "_" + std::to_string(param_number),
403+
cases,
404+
java_int_type(),
405+
function.location,
406+
symbol_table));
407+
}
331408

332409
// record as an input
333410
codet input(ID_input);

jbmc/src/java_bytecode/nondet.cpp

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*******************************************************************\
2+
3+
Module: Non-deterministic object init and choice for JBMC
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
#include "nondet.h"
10+
11+
#include <java_bytecode/java_types.h>
12+
#include <util/arith_tools.h>
13+
#include <util/c_types.h>
14+
#include <util/fresh_symbol.h>
15+
#include <util/symbol.h>
16+
17+
/// Gets a fresh nondet choice in range (min_value, max_value). GOTO generated
18+
/// resembles:
19+
/// ```
20+
/// int_type name_prefix::nondet_int = NONDET(int_type)
21+
/// ASSUME(name_prefix::nondet_int >= min_value)
22+
/// ASSUME(name_prefix::nondet_int <= max_value)
23+
/// ```
24+
/// \param min_value: Minimum value (inclusive) of returned int.
25+
/// \param max_value: Maximum value (inclusive) of returned int.
26+
/// \param name_prefix: Prefix for the fresh symbol name generated.
27+
/// \param int_type: The type of the int used to non-deterministically choose
28+
/// one of the switch cases.
29+
/// \param source_location: The location to mark the generated int with.
30+
/// \param symbol_table: The global symbol table.
31+
/// \param instructions [out]: Output instructions are written to
32+
/// 'instructions'. These declare, nondet-initialise and range-constrain (with
33+
/// assume statements) a fresh integer.
34+
/// \return Returns a symbol expression for the resulting integer.
35+
symbol_exprt generate_nondet_int(
36+
const int64_t min_value,
37+
const int64_t max_value,
38+
const std::string &name_prefix,
39+
const typet &int_type,
40+
const source_locationt &source_location,
41+
symbol_table_baset &symbol_table,
42+
code_blockt &instructions)
43+
{
44+
PRECONDITION(min_value < max_value);
45+
46+
// Declare a symbol for the non deterministic integer.
47+
const symbol_exprt &nondet_symbol = get_fresh_aux_symbol(
48+
int_type,
49+
name_prefix,
50+
"nondet_int",
51+
source_location,
52+
ID_java,
53+
symbol_table)
54+
.symbol_expr();
55+
instructions.add(code_declt(nondet_symbol));
56+
57+
// Assign the symbol any non deterministic integer value.
58+
// int_type name_prefix::nondet_int = NONDET(int_type)
59+
instructions.add(
60+
code_assignt(nondet_symbol, side_effect_expr_nondett(int_type)));
61+
62+
// Constrain the non deterministic integer with a lower bound of `min_value`.
63+
// ASSUME(name_prefix::nondet_int >= min_value)
64+
instructions.add(
65+
code_assumet(
66+
binary_predicate_exprt(
67+
nondet_symbol, ID_ge, from_integer(min_value, int_type))));
68+
69+
// Constrain the non deterministic integer with an upper bound of `max_value`.
70+
// ASSUME(name_prefix::nondet_int <= max_value)
71+
instructions.add(
72+
code_assumet(
73+
binary_predicate_exprt(
74+
nondet_symbol, ID_le, from_integer(max_value, int_type))));
75+
76+
return nondet_symbol;
77+
}
78+
79+
/// Pick nondeterministically between imperative actions 'switch_cases'.
80+
/// \param name_prefix: Name prefix for fresh symbols
81+
/// \param switch_cases: List of codet objects to execute in each switch case.
82+
/// \param int_type: The type of the int used to non-deterministically choose
83+
/// one of the switch cases.
84+
/// \param source_location: The location to mark the generated int with.
85+
/// \param symbol_table: The global symbol table.
86+
/// \return Returns a nondet-switch choosing between switch_cases. The resulting
87+
/// switch block has no default case.
88+
code_blockt generate_nondet_switch(
89+
const irep_idt &name_prefix,
90+
const alternate_casest &switch_cases,
91+
const typet &int_type,
92+
const source_locationt &source_location,
93+
symbol_table_baset &symbol_table)
94+
{
95+
PRECONDITION(!switch_cases.empty());
96+
97+
if(switch_cases.size() == 1)
98+
return code_blockt({switch_cases[0]});
99+
100+
code_switcht result_switch;
101+
code_blockt result_block;
102+
103+
const symbol_exprt &switch_value = generate_nondet_int(
104+
0,
105+
switch_cases.size() - 1,
106+
id2string(name_prefix),
107+
int_type,
108+
source_location,
109+
symbol_table,
110+
result_block);
111+
112+
result_switch.value() = switch_value;
113+
114+
code_blockt switch_block;
115+
int64_t case_number = 0;
116+
for(const auto &switch_case : switch_cases)
117+
{
118+
code_blockt this_block;
119+
this_block.add(switch_case);
120+
this_block.add(code_breakt());
121+
code_switch_caset this_case;
122+
this_case.case_op() = from_integer(case_number, switch_value.type());
123+
this_case.code() = this_block;
124+
switch_block.move(this_case);
125+
++case_number;
126+
}
127+
128+
result_switch.body() = switch_block;
129+
result_block.move(result_switch);
130+
return result_block;
131+
}

jbmc/src/java_bytecode/nondet.h

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*******************************************************************\
2+
3+
Module: Non-deterministic object init and choice for JBMC
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
#ifndef CPROVER_JAVA_BYTECODE_NONDET_H
10+
#define CPROVER_JAVA_BYTECODE_NONDET_H
11+
12+
#include <util/std_code.h>
13+
#include <util/std_expr.h>
14+
#include <util/symbol_table.h>
15+
16+
symbol_exprt generate_nondet_int(
17+
int64_t min_value,
18+
int64_t max_value,
19+
const std::string &name_prefix,
20+
const typet &int_type,
21+
const source_locationt &source_location,
22+
symbol_table_baset &symbol_table,
23+
code_blockt &instructions);
24+
25+
typedef std::vector<codet> alternate_casest;
26+
27+
code_blockt generate_nondet_switch(
28+
const irep_idt &name_prefix,
29+
const alternate_casest &switch_cases,
30+
const typet &int_type,
31+
const source_locationt &source_location,
32+
symbol_table_baset &symbol_table);
33+
34+
#endif // CPROVER_JAVA_BYTECODE_NONDET_H

jbmc/src/java_bytecode/select_pointer_type.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,11 @@ select_pointer_typet::get_recursively_instantiated_type(
223223
visited.erase(parameter_name);
224224
return inst_val;
225225
}
226+
227+
std::set<symbol_typet> select_pointer_typet::get_parameter_alternative_types(
228+
const irep_idt &function_name,
229+
const irep_idt &parameter_name,
230+
const namespacet &ns) const
231+
{
232+
return {};
233+
}

jbmc/src/java_bytecode/select_pointer_type.h

+5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class select_pointer_typet
4242
&generic_parameter_specialization_map,
4343
const namespacet &ns) const;
4444

45+
virtual std::set<symbol_typet> get_parameter_alternative_types(
46+
const irep_idt &function_name,
47+
const irep_idt &parameter_name,
48+
const namespacet &ns) const;
49+
4550
pointer_typet specialize_generics(
4651
const pointer_typet &pointer_type,
4752
const generic_parameter_specialization_mapt

0 commit comments

Comments
 (0)