Skip to content

Commit a2e2f74

Browse files
author
Matthias Güdemann
authored
Merge pull request diffblue#1636 from svorenova/inner_classes_tg1190_part2
[TG-1190] Specialisation for implicitly generic classes
2 parents 98017ce + 2fd9300 commit a2e2f74

22 files changed

+930
-96
lines changed

src/java_bytecode/generate_java_generic_type.cpp

+188-46
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,25 @@ symbolt generate_java_generic_typet::operator()(
3636
INVARIANT(
3737
pointer_subtype.id()==ID_struct, "Only pointers to classes in java");
3838
INVARIANT(
39-
is_java_generic_class_type(pointer_subtype),
39+
is_java_generic_class_type(pointer_subtype) ||
40+
is_java_implicitly_generic_class_type(pointer_subtype),
4041
"Generic references type must be a generic class");
4142

42-
const java_generic_class_typet &generic_class_definition =
43-
to_java_generic_class_type(to_java_class_type(pointer_subtype));
43+
const java_class_typet &class_definition =
44+
to_java_class_type(pointer_subtype);
4445

4546
const irep_idt generic_name =
46-
build_generic_name(existing_generic_type, generic_class_definition);
47+
build_generic_name(existing_generic_type, class_definition);
4748
struct_union_typet::componentst replacement_components =
48-
generic_class_definition.components();
49+
class_definition.components();
4950

5051
// Small auxiliary function, to perform the inplace
5152
// modification of the generic fields.
5253
auto replace_type_for_generic_field =
5354
[&](struct_union_typet::componentt &component) {
5455

5556
component.type() = substitute_type(
56-
component.type(), generic_class_definition, existing_generic_type);
57+
component.type(), class_definition, existing_generic_type);
5758

5859
return component;
5960
};
@@ -66,16 +67,15 @@ symbolt generate_java_generic_typet::operator()(
6667
replacement_components.end(),
6768
replace_type_for_generic_field);
6869

69-
std::size_t after_modification_size =
70-
generic_class_definition.components().size();
70+
std::size_t after_modification_size = class_definition.components().size();
7171

7272
INVARIANT(
7373
pre_modification_size==after_modification_size,
7474
"All components in the original class should be in the new class");
7575

7676
const java_specialized_generic_class_typet new_java_class{
7777
generic_name,
78-
generic_class_definition.get_tag(),
78+
class_definition.get_tag(),
7979
replacement_components,
8080
existing_generic_type.generic_type_arguments()};
8181

@@ -110,7 +110,7 @@ symbolt generate_java_generic_typet::operator()(
110110
/// there are none to replace, the original type.
111111
typet generate_java_generic_typet::substitute_type(
112112
const typet &parameter_type,
113-
const java_generic_class_typet &generic_class,
113+
const java_class_typet &class_definition,
114114
const java_generic_typet &generic_reference) const
115115
{
116116
if(is_java_generic_parameter(parameter_type))
@@ -119,8 +119,28 @@ typet generate_java_generic_typet::substitute_type(
119119
.type_variable()
120120
.get_identifier();
121121

122-
optionalt<size_t> results =
123-
java_generics_get_index_for_subtype(generic_class, component_identifier);
122+
// see if it is a generic parameter introduced by this class
123+
optionalt<size_t> results;
124+
if(is_java_generic_class_type(class_definition))
125+
{
126+
const java_generic_class_typet &generic_class =
127+
to_java_generic_class_type(class_definition);
128+
129+
results = java_generics_get_index_for_subtype(
130+
generic_class.generic_types(), component_identifier);
131+
}
132+
// see if it is an implicit generic parameter introduced by an outer class
133+
if(!results.has_value())
134+
{
135+
INVARIANT(
136+
is_java_implicitly_generic_class_type(class_definition),
137+
"The parameter must either be a generic type or implicit generic type");
138+
const java_implicitly_generic_class_typet &implicitly_generic_class =
139+
to_java_implicitly_generic_class_type(class_definition);
140+
results = java_generics_get_index_for_subtype(
141+
implicitly_generic_class.implicit_generic_types(),
142+
component_identifier);
143+
}
124144

125145
INVARIANT(results.has_value(), "generic component type not found");
126146
return generic_reference.generic_type_arguments()[*results];
@@ -142,7 +162,7 @@ typet generate_java_generic_typet::substitute_type(
142162
[&](const reference_typet &generic_param) -> reference_typet
143163
{
144164
const typet &replacement_type =
145-
substitute_type(generic_param, generic_class, generic_reference);
165+
substitute_type(generic_param, class_definition, generic_reference);
146166

147167
// This code will be simplified when references aren't considered to
148168
// be generic parameters
@@ -172,8 +192,8 @@ typet generate_java_generic_typet::substitute_type(
172192
const typet &array_element_type =
173193
java_array_element_type(array_subtype);
174194

175-
const typet &new_array_type =
176-
substitute_type(array_element_type, generic_class, generic_reference);
195+
const typet &new_array_type = substitute_type(
196+
array_element_type, class_definition, generic_reference);
177197

178198
typet replacement_array_type = java_array_type('a');
179199
replacement_array_type.subtype().set(ID_C_element_type, new_array_type);
@@ -184,51 +204,173 @@ typet generate_java_generic_typet::substitute_type(
184204
return parameter_type;
185205
}
186206

187-
/// Build a unique tag for the generic to be instantiated.
207+
/// Creates a name for an argument that is an array, e.g., for an array of
208+
/// Integers it returns a string `array[reference]of_java::java.lang.Integer`
209+
/// \param id argument of type array
210+
/// \param generic_argument_p array reference type
211+
/// \return name as a string
212+
static irep_idt build_name_for_array_argument(
213+
const irep_idt &id,
214+
const reference_typet &generic_argument_p)
215+
{
216+
PRECONDITION(is_java_array_tag(id));
217+
std::ostringstream name_buffer;
218+
name_buffer << pretty_print_java_type(id2string(id));
219+
const typet &element_type =
220+
java_array_element_type(to_symbol_type(generic_argument_p.subtype()));
221+
222+
// If this is an array of references then we will specialize its
223+
// identifier using the type of the objects in the array. Else, there
224+
// can be a problem with the same symbols for different instantiations
225+
// using arrays with different types.
226+
if(element_type.id() == ID_pointer)
227+
{
228+
const symbol_typet element_symbol = to_symbol_type(element_type.subtype());
229+
name_buffer << "of_" + id2string(element_symbol.get_identifier());
230+
}
231+
return name_buffer.str();
232+
}
233+
234+
/// Build a generic name for a generic class, from given generic arguments.
235+
/// For example, given a class `Class` with two generic type parameters
236+
/// `java::Class::T` and `java::Class::U`, and two arguments
237+
/// `java::java.lang.Integer` and `java::Outer$Inner`, the returned string is
238+
/// `<java::java.lang.Integer, java::Outer$Inner>`.
239+
/// \param generic_argument_p iterator over generic arguments
240+
/// \param generic_type_p iterator over generic types, starts with types for
241+
/// the given class, may continue with generic types of its inner classes
242+
/// \param generic_types_end end of the vector of generic types
243+
/// \param class_name name of the class for which the tag is being built
244+
/// \return name as a string of the form `<*, *, ..., *>`
245+
static irep_idt build_generic_name_for_class_arguments(
246+
std::vector<reference_typet>::const_iterator &generic_argument_p,
247+
std::vector<java_generic_parametert>::const_iterator &generic_type_p,
248+
const std::vector<java_generic_parametert>::const_iterator &generic_types_end,
249+
const std::string &class_name)
250+
{
251+
std::ostringstream name_buffer;
252+
bool first = true;
253+
std::string parameter_class_name =
254+
(*generic_type_p).get_parameter_class_name();
255+
PRECONDITION(parameter_class_name == class_name);
256+
257+
while(parameter_class_name == class_name)
258+
{
259+
if(first)
260+
{
261+
name_buffer << "<";
262+
first = false;
263+
}
264+
else
265+
{
266+
name_buffer << ", ";
267+
}
268+
269+
const irep_idt &id(
270+
id2string((*generic_argument_p).subtype().get(ID_identifier)));
271+
if(is_java_array_tag(id))
272+
{
273+
name_buffer << build_name_for_array_argument(id, *generic_argument_p);
274+
}
275+
else
276+
{
277+
name_buffer << id2string(id);
278+
}
279+
280+
++generic_argument_p;
281+
++generic_type_p;
282+
if(generic_type_p != generic_types_end)
283+
{
284+
parameter_class_name = (*generic_type_p).get_parameter_class_name();
285+
}
286+
else
287+
{
288+
break;
289+
}
290+
}
291+
name_buffer << ">";
292+
return name_buffer.str();
293+
}
294+
295+
/// Build a unique name for the generic to be instantiated.
188296
/// \param existing_generic_type The type we want to concretise
189297
/// \param original_class
190-
/// \return A tag for the new generic we want a unique tag for.
298+
/// \return A name for the new generic we want a unique name for.
191299
irep_idt generate_java_generic_typet::build_generic_name(
192300
const java_generic_typet &existing_generic_type,
193301
const java_class_typet &original_class) const
194302
{
195-
std::ostringstream new_tag_buffer;
196-
new_tag_buffer << original_class.get_tag();
197-
new_tag_buffer << "<";
198-
bool first=true;
199-
for(const typet &type_argument : existing_generic_type
200-
.generic_type_arguments())
303+
std::ostringstream generic_name_buffer;
304+
const std::string &original_class_name = original_class.get_tag().c_str();
305+
auto generic_argument_p =
306+
existing_generic_type.generic_type_arguments().begin();
307+
308+
// if the original class is implicitly generic, add tags for all generic
309+
// outer classes
310+
// NOTE here we assume that the implicit generic types are ordered from the
311+
// outermost outer class inwards, this is currently guaranteed by the way
312+
// this vector is constructed in
313+
// java_bytecode_convert_class:mark_java_implicitly_generic_class_type
314+
if(is_java_implicitly_generic_class_type(original_class))
201315
{
202-
if(!first)
203-
new_tag_buffer << ", ";
204-
first=false;
316+
const java_implicitly_generic_class_typet
317+
&implicitly_generic_original_class =
318+
to_java_implicitly_generic_class_type(original_class);
205319

206320
INVARIANT(
207-
!is_java_generic_parameter(type_argument),
208-
"Only create full concretized generic types");
209-
const irep_idt &id(id2string(type_argument.subtype().get(ID_identifier)));
210-
new_tag_buffer << pretty_print_java_type(id2string(id));
211-
if(is_java_array_tag(id))
321+
existing_generic_type.generic_type_arguments().size() >=
322+
implicitly_generic_original_class.implicit_generic_types().size(),
323+
"All implicit generic types must be concretised");
324+
auto implicit_generic_type_p =
325+
implicitly_generic_original_class.implicit_generic_types().begin();
326+
const auto &implicit_generic_types_end =
327+
implicitly_generic_original_class.implicit_generic_types().end();
328+
std::string current_outer_class_name;
329+
330+
while(implicit_generic_type_p != implicit_generic_types_end)
212331
{
213-
const typet &element_type =
214-
java_array_element_type(to_symbol_type(type_argument.subtype()));
215-
216-
// If this is an array of references then we will specialize its
217-
// identifier using the type of the objects in the array. Else, there can
218-
// be a problem with the same symbols for different instantiations using
219-
// arrays with different types.
220-
if(element_type.id() == ID_pointer)
221-
{
222-
const symbol_typet element_symbol =
223-
to_symbol_type(element_type.subtype());
224-
new_tag_buffer << "of_" << id2string(element_symbol.get_identifier());
225-
}
332+
current_outer_class_name =
333+
(*implicit_generic_type_p).get_parameter_class_name();
334+
generic_name_buffer << current_outer_class_name;
335+
generic_name_buffer << build_generic_name_for_class_arguments(
336+
generic_argument_p,
337+
implicit_generic_type_p,
338+
implicit_generic_types_end,
339+
current_outer_class_name);
226340
}
341+
generic_name_buffer << original_class_name.substr(
342+
current_outer_class_name.length(), std::string::npos);
343+
}
344+
else
345+
{
346+
generic_name_buffer << original_class_name;
227347
}
228348

229-
new_tag_buffer << ">";
349+
// if the original class is generic, add tag for the class itself
350+
if(is_java_generic_class_type(original_class))
351+
{
352+
const java_generic_class_typet &generic_original_class =
353+
to_java_generic_class_type(original_class);
230354

231-
return new_tag_buffer.str();
355+
INVARIANT(
356+
std::distance(
357+
generic_argument_p,
358+
existing_generic_type.generic_type_arguments().end()) ==
359+
static_cast<int>(generic_original_class.generic_types().size()),
360+
"All generic types must be concretised");
361+
auto generic_type_p = generic_original_class.generic_types().begin();
362+
363+
generic_name_buffer << build_generic_name_for_class_arguments(
364+
generic_argument_p,
365+
generic_type_p,
366+
generic_original_class.generic_types().end(),
367+
original_class_name);
368+
}
369+
370+
INVARIANT(
371+
generic_argument_p == existing_generic_type.generic_type_arguments().end(),
372+
"All type arguments must have been added to the name");
373+
return generic_name_buffer.str();
232374
}
233375

234376
/// Construct the symbol to be moved into the symbol table

src/java_bytecode/generate_java_generic_type.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class generate_java_generic_typet
2929

3030
typet substitute_type(
3131
const typet &parameter_type,
32-
const java_generic_class_typet &replacement_type,
32+
const java_class_typet &replacement_type,
3333
const java_generic_typet &generic_reference) const;
3434

3535
type_symbolt build_symbol_from_specialised_class(

src/java_bytecode/java_types.h

+14-6
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ class java_generic_parametert:public reference_typet
116116
return const_cast<type_variablet &>(type_variables().front());
117117
}
118118

119+
const std::string get_parameter_class_name() const
120+
{
121+
const std::string &parameter_name =
122+
type_variable().get_identifier().c_str();
123+
PRECONDITION(has_prefix(parameter_name, "java::"));
124+
int prefix_length = std::string("java::").length();
125+
const std::string name = parameter_name.substr(
126+
prefix_length, parameter_name.rfind("::") - prefix_length);
127+
return name;
128+
}
129+
119130
private:
120131
typedef std::vector<type_variablet> type_variablest;
121132
const type_variablest &type_variables() const
@@ -410,21 +421,18 @@ inline typet java_type_from_string_with_exception(
410421
/// \param identifier The string identifier of the type of the component.
411422
/// \return Optional with the size if the identifier was found.
412423
inline const optionalt<size_t> java_generics_get_index_for_subtype(
413-
const java_generic_class_typet &t,
424+
const std::vector<java_generic_parametert> &gen_types,
414425
const irep_idt &identifier)
415426
{
416-
const std::vector<java_generic_parametert> &gen_types=
417-
t.generic_types();
418-
419427
const auto iter = std::find_if(
420428
gen_types.cbegin(),
421429
gen_types.cend(),
422430
[&identifier](const java_generic_parametert &ref)
423431
{
424-
return ref.type_variable().get_identifier()==identifier;
432+
return ref.type_variable().get_identifier() == identifier;
425433
});
426434

427-
if(iter==gen_types.cend())
435+
if(iter == gen_types.cend())
428436
{
429437
return {};
430438
}

0 commit comments

Comments
 (0)