Skip to content

Commit 3111255

Browse files
author
svorenova
committed
Introduce a map of parameter-type pairs
The map of generic parameters and a stack of their concrete types in the various depths of the current scope.
1 parent a15b75f commit 3111255

6 files changed

+269
-2
lines changed

src/java_bytecode/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ SRC = bytecode_info.cpp \
44
ci_lazy_methods_needed.cpp \
55
expr2java.cpp \
66
generic_arguments_name_builder.cpp \
7+
generic_parameter_specialization_map_keys.cpp \
78
jar_file.cpp \
89
java_bytecode_convert_class.cpp \
910
java_bytecode_convert_method.cpp \
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/// Author: DiffBlue Limited. All Rights Reserved.
2+
3+
#include "generic_parameter_specialization_map_keys.h"
4+
5+
/// \param type Source type
6+
/// \return The vector of implicitly generic and (explicitly) generic type
7+
/// parameters of the given type.
8+
const std::vector<java_generic_parametert>
9+
get_all_generic_parameters(const typet &type)
10+
{
11+
PRECONDITION(
12+
is_java_generic_class_type(type) ||
13+
is_java_implicitly_generic_class_type(type));
14+
std::vector<java_generic_parametert> generic_parameters;
15+
if(is_java_implicitly_generic_class_type(type))
16+
{
17+
const java_implicitly_generic_class_typet &implicitly_generic_class =
18+
to_java_implicitly_generic_class_type(to_java_class_type(type));
19+
generic_parameters.insert(
20+
generic_parameters.end(),
21+
implicitly_generic_class.implicit_generic_types().begin(),
22+
implicitly_generic_class.implicit_generic_types().end());
23+
}
24+
if(is_java_generic_class_type(type))
25+
{
26+
const java_generic_class_typet &generic_class =
27+
to_java_generic_class_type(to_java_class_type(type));
28+
generic_parameters.insert(
29+
generic_parameters.end(),
30+
generic_class.generic_types().begin(),
31+
generic_class.generic_types().end());
32+
}
33+
return generic_parameters;
34+
}
35+
36+
/// Add pairs to the controlled map. Own the keys and pop from their stack
37+
/// on destruction; otherwise do nothing.
38+
/// \param parameters generic parameters that are the keys of the pairs to add
39+
/// \param types a type to add for each parameter
40+
const void generic_parameter_specialization_map_keyst::insert_pairs(
41+
const std::vector<java_generic_parametert> &parameters,
42+
const std::vector<reference_typet> &types)
43+
{
44+
INVARIANT(erase_keys.empty(), "insert_pairs should only be called once");
45+
PRECONDITION(parameters.size() == types.size());
46+
47+
// Pair up the parameters and types for easier manipulation later
48+
std::vector<std::pair<java_generic_parametert, reference_typet>> pairs;
49+
pairs.reserve(parameters.size());
50+
std::transform(
51+
parameters.begin(),
52+
parameters.end(),
53+
types.begin(),
54+
std::back_inserter(pairs),
55+
[&](java_generic_parametert param, reference_typet type)
56+
{
57+
return std::make_pair(param, type);
58+
});
59+
60+
for(const auto &pair : pairs)
61+
{
62+
// Only add the pair if the type is not the parameter itself, e.g.,
63+
// pair.first = pair.second = java::A::T. This can happen for example
64+
// when initiating a pointer to an implicitly java generic class type
65+
// in gen_nondet_init and would result in a loop when the map is used
66+
// to look up the type of the parameter.
67+
if(
68+
!(is_java_generic_parameter(pair.second) &&
69+
to_java_generic_parameter(pair.second).get_name() ==
70+
pair.first.get_name()))
71+
{
72+
const irep_idt &key = pair.first.get_name();
73+
if(generic_parameter_specialization_map.count(key) == 0)
74+
generic_parameter_specialization_map.emplace(
75+
key, std::stack<reference_typet>());
76+
(*generic_parameter_specialization_map.find(key))
77+
.second.push(pair.second);
78+
79+
// We added something, so pop it when this is destroyed:
80+
erase_keys.push_back(key);
81+
}
82+
}
83+
}
84+
85+
/// Add a pair of a parameter and its types for each generic parameter of the
86+
/// given generic pointer type to the controlled map. Own the keys and pop
87+
/// from their stack on destruction; otherwise do nothing.
88+
/// \param pointer_type pointer type to get the specialized generic types from
89+
/// \param pointer_subtype_struct struct type to which the generic pointer
90+
/// points, must be generic if the pointer is generic
91+
const void generic_parameter_specialization_map_keyst::insert_pairs_for_pointer(
92+
const pointer_typet &pointer_type,
93+
const typet &pointer_subtype_struct)
94+
{
95+
if(is_java_generic_type(pointer_type))
96+
{
97+
// The supplied type must be the full type of the pointer's subtype
98+
PRECONDITION(
99+
pointer_type.subtype().get(ID_identifier) ==
100+
pointer_subtype_struct.get(ID_name));
101+
102+
const java_generic_typet &generic_pointer =
103+
to_java_generic_type(pointer_type);
104+
105+
// If the pointer points to an incomplete class, don't treat the generics
106+
if(!pointer_subtype_struct.get_bool(ID_incomplete_class))
107+
{
108+
PRECONDITION(
109+
is_java_generic_class_type(pointer_subtype_struct) ||
110+
is_java_implicitly_generic_class_type(pointer_subtype_struct));
111+
const std::vector<java_generic_parametert> &generic_parameters =
112+
get_all_generic_parameters(pointer_subtype_struct);
113+
114+
INVARIANT(
115+
generic_pointer.generic_type_arguments().size() ==
116+
generic_parameters.size(),
117+
"All generic parameters of the pointer type need to be specified");
118+
119+
insert_pairs(
120+
generic_parameters, generic_pointer.generic_type_arguments());
121+
}
122+
}
123+
}
124+
125+
/// Add a pair of a parameter and its types for each generic parameter of the
126+
/// given generic symbol type to the controlled map. This function is used
127+
/// for generic bases (superclass or interfaces) where the reference to it is
128+
/// in the form of a symbol rather than a pointer (as opposed to the function
129+
/// insert_pairs_for_pointer). Own the keys and pop from their stack
130+
/// on destruction; otherwise do nothing.
131+
/// \param symbol_type symbol type to get the specialized generic types from
132+
/// \param symbol_struct struct type of the symbol type, must be generic if
133+
/// the symbol is generic
134+
const void generic_parameter_specialization_map_keyst::insert_pairs_for_symbol(
135+
const symbol_typet &symbol_type,
136+
const typet &symbol_struct)
137+
{
138+
if(is_java_generic_symbol_type(symbol_type))
139+
{
140+
java_generic_symbol_typet generic_symbol =
141+
to_java_generic_symbol_type(symbol_type);
142+
143+
PRECONDITION(
144+
is_java_generic_class_type(symbol_struct) ||
145+
is_java_implicitly_generic_class_type(symbol_struct));
146+
const std::vector<java_generic_parametert> &generic_parameters =
147+
get_all_generic_parameters(symbol_struct);
148+
149+
INVARIANT(
150+
generic_symbol.generic_types().size() == generic_parameters.size(),
151+
"All generic parameters of the superclass need to be concretized");
152+
153+
insert_pairs(generic_parameters, generic_symbol.generic_types());
154+
}
155+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/// Author: DiffBlue Limited. All Rights Reserved.
2+
3+
#ifndef CPROVER_JAVA_BYTECODE_GENERIC_PARAMETER_SPECIALIZATION_MAP_KEYS_H
4+
#define CPROVER_JAVA_BYTECODE_GENERIC_PARAMETER_SPECIALIZATION_MAP_KEYS_H
5+
6+
#include "select_pointer_type.h"
7+
#include "java_types.h"
8+
9+
/// \file
10+
/// Generic-parameter-specialization-map entries owner class.
11+
/// Generic-parameter-specialization-map maps generic parameters to a stack
12+
/// of their types in (every depth of) the current scope. This class adds
13+
/// entries to the map for a particular scope, and ensures that they are erased
14+
/// on leaving that scope.
15+
class generic_parameter_specialization_map_keyst
16+
{
17+
public:
18+
/// Initialize a generic-parameter-specialization-map entry owner operating
19+
/// on a given map. Initially it does not own any map entry.
20+
/// \param _generic_parameter_specialization_map: map to operate on.
21+
explicit generic_parameter_specialization_map_keyst(
22+
generic_parameter_specialization_mapt
23+
&_generic_parameter_specialization_map)
24+
: generic_parameter_specialization_map(
25+
_generic_parameter_specialization_map)
26+
{
27+
}
28+
29+
/// Removes the top of the stack for each key in erase_keys from the
30+
/// controlled map.
31+
~generic_parameter_specialization_map_keyst()
32+
{
33+
for(const auto key : erase_keys)
34+
{
35+
PRECONDITION(generic_parameter_specialization_map.count(key) != 0);
36+
(*generic_parameter_specialization_map.find(key)).second.pop();
37+
if((*generic_parameter_specialization_map.find(key)).second.empty())
38+
{
39+
generic_parameter_specialization_map.erase(key);
40+
}
41+
}
42+
}
43+
44+
// Objects of these class cannot be copied in any way - delete the copy
45+
// constructor and copy assignment operator
46+
generic_parameter_specialization_map_keyst(
47+
const generic_parameter_specialization_map_keyst &) = delete;
48+
generic_parameter_specialization_map_keyst &
49+
operator=(const generic_parameter_specialization_map_keyst &) = delete;
50+
51+
const void insert_pairs_for_pointer(
52+
const pointer_typet &pointer_type,
53+
const typet &pointer_subtype_struct);
54+
const void insert_pairs_for_symbol(
55+
const symbol_typet &symbol_type,
56+
const typet &symbol_struct);
57+
58+
private:
59+
/// Generic parameter specialization map to modify
60+
generic_parameter_specialization_mapt &generic_parameter_specialization_map;
61+
/// Keys of the entries to pop on destruction
62+
std::vector<irep_idt> erase_keys;
63+
64+
const void insert_pairs(
65+
const std::vector<java_generic_parametert> &parameters,
66+
const std::vector<reference_typet> &types);
67+
};
68+
69+
#endif // CPROVER_JAVA_BYTECODE_GENERIC_PARAMETER_SPECIALIZATION_MAP_KEYS_H

src/java_bytecode/java_object_factory.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Author: Daniel Kroening, [email protected]
3333
#include "java_utils.h"
3434
#include "java_string_library_preprocess.h"
3535
#include "java_root_class.h"
36+
#include "generic_parameter_specialization_map_keys.h"
3637

3738
static symbolt &new_tmp_symbol(
3839
symbol_table_baset &symbol_table,
@@ -68,6 +69,16 @@ class java_object_factoryt
6869
/// this set AND the tree depth becomes >= than the maximum value above.
6970
std::unordered_set<irep_idt, irep_id_hash> recursion_set;
7071

72+
/// Every time the non-det generator visits a type and the type is generic
73+
/// (either a struct or a pointer), the following map is used to store and
74+
/// look up the concrete types of the generic paramaters in the current
75+
/// scope. Note that not all generic parameters need to have a concrete
76+
/// type, e.g., the method under test is generic. The types are removed
77+
/// from the map when the scope changes. Note that in different depths
78+
/// of the scope the parameters can be specialized with different types
79+
/// so we keep a stack of types for each parameter.
80+
generic_parameter_specialization_mapt generic_parameter_specialization_map;
81+
7182
/// The symbol table.
7283
symbol_table_baset &symbol_table;
7384

@@ -1151,6 +1162,15 @@ void java_object_factoryt::gen_nondet_init(
11511162
{
11521163
// dereferenced type
11531164
const pointer_typet &pointer_type=to_pointer_type(type);
1165+
1166+
// If we are about to initialize a generic pointer type, add its concrete
1167+
// types to the map and delete them on leaving this function scope.
1168+
generic_parameter_specialization_map_keyst
1169+
generic_parameter_specialization_map_keys(
1170+
generic_parameter_specialization_map);
1171+
generic_parameter_specialization_map_keys.insert_pairs_for_pointer(
1172+
pointer_type, ns.follow(pointer_type.subtype()));
1173+
11541174
gen_nondet_pointer_init(
11551175
assignments,
11561176
expr,
@@ -1164,6 +1184,21 @@ void java_object_factoryt::gen_nondet_init(
11641184
else if(type.id()==ID_struct)
11651185
{
11661186
const struct_typet struct_type=to_struct_type(type);
1187+
1188+
// If we are about to initialize a generic class (as a superclass object
1189+
// for a different object), add its concrete types to the map and delete
1190+
// them on leaving this function scope.
1191+
generic_parameter_specialization_map_keyst
1192+
generic_parameter_specialization_map_keys(
1193+
generic_parameter_specialization_map);
1194+
if(is_sub)
1195+
{
1196+
const typet &symbol = override_ ? override_type : expr.type();
1197+
PRECONDITION(symbol.id() == ID_symbol);
1198+
generic_parameter_specialization_map_keys.insert_pairs_for_symbol(
1199+
to_symbol_type(symbol), struct_type);
1200+
}
1201+
11671202
gen_nondet_struct_init(
11681203
assignments,
11691204
expr,

src/java_bytecode/java_object_factory.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ exprt object_factory(
107107
allocation_typet alloc_type,
108108
const source_locationt &location);
109109

110+
typedef std::unordered_map<irep_idt, std::stack<reference_typet>,
111+
irep_id_hash> generic_parameter_specialization_mapt;
112+
110113
enum class update_in_placet
111114
{
112115
NO_UPDATE_IN_PLACE,

src/java_bytecode/java_types.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,14 @@ class java_generic_parametert:public reference_typet
120120
return const_cast<type_variablet &>(type_variables().front());
121121
}
122122

123+
const irep_idt get_name() const
124+
{
125+
return type_variable().get_identifier();
126+
}
127+
123128
const std::string get_parameter_class_name() const
124129
{
125-
const std::string &parameter_name =
126-
type_variable().get_identifier().c_str();
130+
const std::string &parameter_name = get_name().c_str();
127131
PRECONDITION(has_prefix(parameter_name, "java::"));
128132
int prefix_length = std::string("java::").length();
129133
const std::string name = parameter_name.substr(

0 commit comments

Comments
 (0)