Skip to content

Commit 1ccbf83

Browse files
author
thk123
committed
Correctly handle generic classes that have a array field
Previously we only converted types that were precisely a generic parameter. Now we deal with the case where the array is T type'd.
1 parent f60d8c8 commit 1ccbf83

7 files changed

+146
-51
lines changed

src/java_bytecode/generate_java_generic_type.cpp

+64-24
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include <java_bytecode/java_types.h>
1212
#include <java_bytecode/java_utils.h>
1313

14-
1514
generate_java_generic_typet::generate_java_generic_typet(
1615
message_handlert &message_handler):
1716
message_handler(message_handler)
@@ -43,31 +42,14 @@ symbolt generate_java_generic_typet::operator()(
4342

4443
// Small auxiliary function, to perform the inplace
4544
// modification of the generic fields.
46-
auto replace_type_for_generic_field=
47-
[&](struct_union_typet::componentt &component)
48-
{
49-
if(is_java_generic_parameter(component.type()))
50-
{
51-
auto replacement_type_param =
52-
to_java_generic_class_type(replacement_type);
45+
auto replace_type_for_generic_field =
46+
[&](struct_union_typet::componentt &component) {
5347

54-
auto component_identifier=
55-
to_java_generic_parameter(component.type()).type_variable()
56-
.get_identifier();
48+
component.type() = substitute_type(
49+
component.type(),
50+
to_java_generic_class_type(replacement_type),
51+
existing_generic_type);
5752

58-
optionalt<size_t> results=java_generics_get_index_for_subtype(
59-
replacement_type_param, component_identifier);
60-
61-
INVARIANT(
62-
results.has_value(),
63-
"generic component type not found");
64-
65-
if(results)
66-
{
67-
component.type()=
68-
existing_generic_type.generic_type_variables()[*results];
69-
}
70-
}
7153
return component;
7254
};
7355

@@ -98,6 +80,64 @@ symbolt generate_java_generic_typet::operator()(
9880
return *symbol;
9981
}
10082

83+
/// For a given type, if the type contains a Java generic parameter, we look
84+
/// that parameter up and return the relevant type. This works recursively on
85+
/// arrays so that T [] is converted to RelevantType [].
86+
/// \param parameter_type: The type under consideration
87+
/// \param generic_class: The generic class that the \p parameter_type
88+
/// belongs to (e.g. the type of a component of the class). This is used to
89+
/// look up the mapping from name of generic parameter to its index.
90+
/// \param generic_reference: The instantiated version of the generic class
91+
/// used to look up the instantiated type. This is expected to be fully
92+
/// instantiated.
93+
/// \return A newly constructed type with generic parameters replaced, or if
94+
/// there are none to replace, the original type.
95+
typet generate_java_generic_typet::substitute_type(
96+
const typet &parameter_type,
97+
const java_generic_class_typet &generic_class,
98+
const java_generic_typet &generic_reference) const
99+
{
100+
if(is_java_generic_parameter(parameter_type))
101+
{
102+
auto component_identifier = to_java_generic_parameter(parameter_type)
103+
.type_variable()
104+
.get_identifier();
105+
106+
optionalt<size_t> results =
107+
java_generics_get_index_for_subtype(generic_class, component_identifier);
108+
109+
INVARIANT(results.has_value(), "generic component type not found");
110+
111+
if(results)
112+
{
113+
return generic_reference.generic_type_variables()[*results];
114+
}
115+
else
116+
{
117+
return parameter_type;
118+
}
119+
}
120+
else if(
121+
parameter_type.id() == ID_pointer &&
122+
parameter_type.subtype().id() == ID_symbol)
123+
{
124+
const symbol_typet &array_subtype =
125+
to_symbol_type(parameter_type.subtype());
126+
if(is_java_array_tag(array_subtype.get_identifier()))
127+
{
128+
const typet &array_element_type = java_array_element_type(array_subtype);
129+
130+
const typet &new_array_type =
131+
substitute_type(array_element_type, generic_class, generic_reference);
132+
133+
typet replacement_array_type = java_array_type('a');
134+
replacement_array_type.subtype().set(ID_C_element_type, new_array_type);
135+
return replacement_array_type;
136+
}
137+
}
138+
return parameter_type;
139+
}
140+
101141
/// Build a unique tag for the generic to be instantiated.
102142
/// \param existing_generic_type The type we want to concretise
103143
/// \param original_class

src/java_bytecode/generate_java_generic_type.h

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ class generate_java_generic_typet
2828
const java_generic_typet &existing_generic_type,
2929
const java_class_typet &original_class) const;
3030

31+
typet substitute_type(
32+
const typet &parameter_type,
33+
const java_generic_class_typet &replacement_type,
34+
const java_generic_typet &generic_reference) const;
35+
3136
message_handlert &message_handler;
3237
};
3338

unit/java_bytecode/generate_concrete_generic_type/generate_java_generic_type.cpp

+71-27
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,7 @@ SCENARIO(
179179
// We have a 'harness' class who's only purpose is to contain a reference
180180
// to the generic class so that we can test the specialization of that generic
181181
// class
182-
const irep_idt harness_class=
183-
"java::generic_field_array_instantiation";
182+
const irep_idt harness_class = "java::generic_field_array_instantiation";
184183

185184
// We want to test that the specialized/instantiated class has it's field
186185
// type updated, so find the specialized class, not the generic class.
@@ -190,20 +189,16 @@ SCENARIO(
190189

191190
GIVEN("A generic type instantiated with an array type")
192191
{
193-
symbol_tablet new_symbol_table=
194-
load_java_class(
195-
"generic_field_array_instantiation",
196-
"./java_bytecode/generate_concrete_generic_type");
192+
symbol_tablet new_symbol_table = load_java_class(
193+
"generic_field_array_instantiation",
194+
"./java_bytecode/generate_concrete_generic_type");
197195

198196
// Ensure the class has been specialized
199197
REQUIRE(new_symbol_table.has_symbol(harness_class));
200-
const symbolt &harness_symbol=
201-
new_symbol_table.lookup_ref(harness_class);
198+
const symbolt &harness_symbol = new_symbol_table.lookup_ref(harness_class);
202199

203-
const struct_typet::componentt &harness_component=
204-
require_type::require_component(
205-
to_struct_type(harness_symbol.type),
206-
"f");
200+
const struct_typet::componentt &harness_component =
201+
require_type::require_component(to_struct_type(harness_symbol.type), "f");
207202

208203
ui_message_handlert message_handler;
209204
generate_java_generic_typet instantiate_generic_type(message_handler);
@@ -212,25 +207,22 @@ SCENARIO(
212207

213208
// Test the specialized class
214209
REQUIRE(new_symbol_table.has_symbol(test_class));
215-
const symbolt test_class_symbol=
216-
new_symbol_table.lookup_ref(test_class);
210+
const symbolt test_class_symbol = new_symbol_table.lookup_ref(test_class);
217211

218-
REQUIRE(test_class_symbol.type.id()==ID_struct);
219-
const struct_typet::componentt &field_component=
212+
REQUIRE(test_class_symbol.type.id() == ID_struct);
213+
const struct_typet::componentt &field_component =
220214
require_type::require_component(
221-
to_struct_type(test_class_symbol.type),
222-
"gf");
223-
const typet &test_field_type=field_component.type();
215+
to_struct_type(test_class_symbol.type), "gf");
216+
const typet &test_field_type = field_component.type();
224217

225-
REQUIRE(test_field_type.id()==ID_pointer);
226-
REQUIRE(test_field_type.subtype().id()==ID_symbol);
227-
const symbol_typet test_field_array=
218+
REQUIRE(test_field_type.id() == ID_pointer);
219+
REQUIRE(test_field_type.subtype().id() == ID_symbol);
220+
const symbol_typet test_field_array =
228221
to_symbol_type(test_field_type.subtype());
229-
REQUIRE(test_field_array.get_identifier()=="java::array[reference]");
230-
const pointer_typet &element_type=
231-
require_type::require_pointer(
232-
java_array_element_type(test_field_array),
233-
symbol_typet("java::java.lang.Float"));
222+
REQUIRE(test_field_array.get_identifier() == "java::array[reference]");
223+
const pointer_typet &element_type = require_type::require_pointer(
224+
java_array_element_type(test_field_array),
225+
symbol_typet("java::java.lang.Float"));
234226

235227
// check for other specialized classes, in particular different symbol ids
236228
// for arrays with different element types
@@ -270,3 +262,55 @@ SCENARIO(
270262
}
271263
}
272264
}
265+
266+
SCENARIO(
267+
"generate_java_generic_type with a generic array field",
268+
"[core][java_bytecode][generate_java_generic_type]")
269+
{
270+
const irep_idt harness_class = "java::generic_field_array_instantiation";
271+
GIVEN("A generic class with a field of type T []")
272+
{
273+
symbol_tablet new_symbol_table = load_java_class(
274+
"generic_field_array_instantiation",
275+
"./java_bytecode/generate_concrete_generic_type");
276+
277+
const irep_idt inner_class = "genericArray";
278+
279+
WHEN("We specialise that class from a reference to it")
280+
{
281+
specialise_generic_from_component(
282+
harness_class, "genericArrayField", new_symbol_table);
283+
THEN(
284+
"There should be a specialised version of the class in the symbol "
285+
"table")
286+
{
287+
const irep_idt specialised_class_name = id2string(harness_class) + "$" +
288+
id2string(inner_class) +
289+
"<java::java.lang.Float>";
290+
REQUIRE(new_symbol_table.has_symbol(specialised_class_name));
291+
292+
const symbolt test_class_symbol =
293+
new_symbol_table.lookup_ref(specialised_class_name);
294+
295+
THEN("The array field should be specialised to be an array of floats")
296+
{
297+
REQUIRE(test_class_symbol.type.id() == ID_struct);
298+
const struct_typet::componentt &field_component =
299+
require_type::require_component(
300+
to_struct_type(test_class_symbol.type), "arrayField");
301+
302+
const pointer_typet &component_pointer_type =
303+
require_type::require_pointer(field_component.type(), {});
304+
305+
const symbol_typet &pointer_subtype = require_type::require_symbol(
306+
component_pointer_type.subtype(), "java::array[reference]");
307+
308+
const typet &array_type = java_array_element_type(pointer_subtype);
309+
310+
require_type::require_pointer(
311+
array_type, symbol_typet("java::java.lang.Float"));
312+
}
313+
}
314+
}
315+
}
316+
}

unit/java_bytecode/generate_concrete_generic_type/generic_field_array_instantiation.java

+6
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ class generic<T> {
44
T gf;
55
}
66

7+
class genericArray<T> {
8+
T [] arrayField;
9+
}
10+
711
generic<Float []> f;
812
generic<Integer []> g;
913
generic<int []> h;
1014
generic<float []> i;
1115
Float [] af;
16+
17+
genericArray<Float> genericArrayField;
1218
}

0 commit comments

Comments
 (0)