Skip to content

Commit df6a0b3

Browse files
author
Thomas Kiley
authored
Merge pull request #1406 from NlightNFotis/fotis/generics_support
[TG-375] Fotis/generics support
2 parents 123162b + d735cc5 commit df6a0b3

29 files changed

+540
-8
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
public class generics {
2+
3+
class element<E> {
4+
E elem;
5+
}
6+
7+
class bound_element<NUM extends java.lang.Number> {
8+
NUM elem;
9+
NUM f() {
10+
return elem;
11+
}
12+
void g(NUM e) {
13+
elem=e;
14+
}
15+
}
16+
17+
bound_element<Integer> belem;
18+
19+
Integer f(int n) {
20+
21+
element<Integer> e=new element<>();
22+
e.elem=n;
23+
bound_element<Integer> be=new bound_element<>();
24+
belem=new bound_element<>();
25+
be.elem=new Integer(n+1);
26+
if(n>0)
27+
return e.elem;
28+
else
29+
return be.elem;
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
generics.class
3+
--show-symbol-table
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^Type........: struct generics\$bound_element\<java::java.lang.Integer\> \{
7+
__CPROVER_string \@class_identifier; boolean \@lock; struct java.lang.Object \@java.lang.Object; struct java.lang.Integer \*elem; struct generics \*this\$0; \}$
8+
--

src/java_bytecode/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ SRC = bytecode_info.cpp \
2525
java_string_library_preprocess.cpp \
2626
java_types.cpp \
2727
java_utils.cpp \
28+
generate_java_generic_type.cpp \
2829
mz_zip_archive.cpp \
2930
select_pointer_type.cpp \
3031
# Empty last line
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*******************************************************************\
2+
3+
Module: Generate Java Generic Type - Instantiate a generic class with
4+
concrete type information.
5+
6+
Author: DiffBlue Limited. All rights reserved.
7+
8+
\*******************************************************************/
9+
#include "generate_java_generic_type.h"
10+
#include <util/namespace.h>
11+
#include <java_bytecode/java_types.h>
12+
#include <java_bytecode/java_utils.h>
13+
14+
15+
generate_java_generic_typet::generate_java_generic_typet(
16+
message_handlert &message_handler):
17+
message_handler(message_handler)
18+
{}
19+
20+
/// Generate a concrete instantiation of a generic type.
21+
/// \param existing_generic_type The type to be concretised
22+
/// \param symbol_table The symbol table that the concrete type will be
23+
/// inserted into.
24+
/// \return The symbol as it was retrieved from the symbol table after
25+
/// it has been inserted into.
26+
symbolt generate_java_generic_typet::operator()(
27+
const java_generic_typet &existing_generic_type,
28+
symbol_tablet &symbol_table) const
29+
{
30+
namespacet ns(symbol_table);
31+
32+
const typet &pointer_subtype=ns.follow(existing_generic_type.subtype());
33+
34+
INVARIANT(
35+
pointer_subtype.id()==ID_struct, "Only pointers to classes in java");
36+
37+
const java_class_typet &replacement_type=
38+
to_java_class_type(pointer_subtype);
39+
const irep_idt new_tag=build_generic_tag(
40+
existing_generic_type, replacement_type);
41+
struct_union_typet::componentst replacement_components=
42+
replacement_type.components();
43+
44+
// Small auxiliary function, to perform the inplace
45+
// 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_generics_class_type(replacement_type);
53+
54+
auto component_identifier=
55+
to_java_generic_parameter(component.type()).type_variable()
56+
.get_identifier();
57+
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+
}
71+
return component;
72+
};
73+
74+
std::size_t pre_modification_size=to_java_class_type(
75+
ns.follow(existing_generic_type.subtype())).components().size();
76+
77+
std::for_each(
78+
replacement_components.begin(),
79+
replacement_components.end(),
80+
replace_type_for_generic_field);
81+
82+
std::size_t after_modification_size=
83+
replacement_type.components().size();
84+
85+
INVARIANT(
86+
pre_modification_size==after_modification_size,
87+
"All components in the original class should be in the new class");
88+
89+
const auto expected_symbol="java::"+id2string(new_tag);
90+
91+
generate_class_stub(
92+
new_tag,
93+
symbol_table,
94+
message_handler,
95+
replacement_components);
96+
auto symbol=symbol_table.lookup(expected_symbol);
97+
INVARIANT(symbol, "New class not created");
98+
return *symbol;
99+
}
100+
101+
/// Build a unique tag for the generic to be instantiated.
102+
/// \param existing_generic_type The type we want to concretise
103+
/// \param original_class
104+
/// \return A tag for the new generic we want a unique tag for.
105+
irep_idt generate_java_generic_typet::build_generic_tag(
106+
const java_generic_typet &existing_generic_type,
107+
const java_class_typet &original_class) const
108+
{
109+
std::ostringstream new_tag_buffer;
110+
new_tag_buffer << original_class.get_tag();
111+
new_tag_buffer << "<";
112+
bool first=true;
113+
for(const typet &param : existing_generic_type.generic_type_variables())
114+
{
115+
if(!first)
116+
new_tag_buffer << ",";
117+
first=false;
118+
119+
INVARIANT(
120+
is_java_generic_inst_parameter(param),
121+
"Only create full concretized generic types");
122+
new_tag_buffer << param.subtype().get(ID_identifier);
123+
}
124+
125+
new_tag_buffer << ">";
126+
127+
return new_tag_buffer.str();
128+
}
129+
130+
131+
/// Activate the generic instantiation code.
132+
/// \param message_handler
133+
/// \param symbol_table The symbol table so far.
134+
void
135+
instantiate_generics(
136+
message_handlert &message_handler,
137+
symbol_tablet &symbol_table)
138+
{
139+
generate_java_generic_typet instantiate_generic_type(message_handler);
140+
// check out the symbols in the symbol table at this point to see if we
141+
// have a a generic type in.
142+
for(const auto &symbol : symbol_table.symbols)
143+
{
144+
if(symbol.second.type.id()==ID_struct)
145+
{
146+
auto symbol_struct=to_struct_type(symbol.second.type);
147+
auto &components=symbol_struct.components();
148+
149+
for(const auto &component : components)
150+
{
151+
if(is_java_generic_type(component.type()))
152+
{
153+
const auto &type_vars=to_java_generic_type(component.type()).
154+
generic_type_variables();
155+
156+
// Before we can instantiate a generic component, we need
157+
// its type variables to be instantiated parameters
158+
if(all_of(type_vars.cbegin(), type_vars.cend(),
159+
[](const typet &type)
160+
{
161+
return is_java_generic_inst_parameter(type);
162+
}))
163+
{
164+
instantiate_generic_type(
165+
to_java_generic_type(component.type()), symbol_table);
166+
}
167+
}
168+
}
169+
}
170+
}
171+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*******************************************************************\
2+
3+
Module: Generate Java Generic Type - Instantiate a generic class with
4+
concrete type information.
5+
6+
Author: DiffBlue Limited. All rights reserved.
7+
8+
\*******************************************************************/
9+
#ifndef CPROVER_JAVA_BYTECODE_GENERATE_JAVA_GENERIC_TYPE_H
10+
#define CPROVER_JAVA_BYTECODE_GENERATE_JAVA_GENERIC_TYPE_H
11+
12+
#include <util/message.h>
13+
#include <util/symbol_table.h>
14+
#include <util/std_types.h>
15+
#include <java_bytecode/java_types.h>
16+
17+
class generate_java_generic_typet
18+
{
19+
public:
20+
generate_java_generic_typet(
21+
message_handlert &message_handler);
22+
23+
symbolt operator()(
24+
const java_generic_typet &existing_generic_type,
25+
symbol_tablet &symbol_table) const;
26+
private:
27+
irep_idt build_generic_tag(
28+
const java_generic_typet &existing_generic_type,
29+
const java_class_typet &original_class) const;
30+
31+
message_handlert &message_handler;
32+
};
33+
34+
void instantiate_generics(
35+
message_handlert &message_handler,
36+
symbol_tablet &symbol_table);
37+
38+
#endif // CPROVER_JAVA_BYTECODE_GENERATE_JAVA_GENERIC_TYPE_H

src/java_bytecode/java_bytecode_convert_class.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ class java_bytecode_convert_classt:public messaget
6363
generate_class_stub(
6464
parse_tree.parsed_class.name,
6565
symbol_table,
66-
get_message_handler());
66+
get_message_handler(),
67+
struct_union_typet::componentst{});
6768
}
6869

6970
typedef java_bytecode_parse_treet::classt classt;

src/java_bytecode/java_bytecode_instrument.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ codet java_bytecode_instrumentt::throw_exception(
100100
generate_class_stub(
101101
exc_name,
102102
symbol_table,
103-
get_message_handler());
103+
get_message_handler(),
104+
struct_union_typet::componentst{});
104105
}
105106

106107
pointer_typet exc_ptr_type=

src/java_bytecode/java_bytecode_language.cpp

+15-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Author: Daniel Kroening, [email protected]
3030
#include "java_class_loader.h"
3131
#include "java_utils.h"
3232
#include <java_bytecode/ci_lazy_methods.h>
33+
#include <java_bytecode/generate_java_generic_type.h>
3334

3435
#include "expr2java.h"
3536

@@ -238,11 +239,21 @@ bool java_bytecode_languaget::typecheck(
238239
get_message_handler());
239240

240241
// now typecheck all
241-
if(java_bytecode_typecheck(
242-
symbol_table, get_message_handler(), string_refinement_enabled))
243-
return true;
242+
bool res=java_bytecode_typecheck(
243+
symbol_table, get_message_handler(), string_refinement_enabled);
244+
// NOTE (FOTIS): There is some unintuitive logic here, where
245+
// java_bytecode_check will return TRUE if typechecking failed, and FALSE
246+
// if everything went well...
247+
if(res)
248+
{
249+
// there is no point in continuing to concretise
250+
// the generic types if typechecking failed.
251+
return res;
252+
}
244253

245-
return false;
254+
instantiate_generics(get_message_handler(), symbol_table);
255+
256+
return res;
246257
}
247258

248259
bool java_bytecode_languaget::generate_support_functions(

src/java_bytecode/java_types.h

+28
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Author: Daniel Kroening, [email protected]
1111
#define CPROVER_JAVA_BYTECODE_JAVA_TYPES_H
1212

1313
#include <util/invariant.h>
14+
#include <algorithm>
15+
1416
#include <util/type.h>
1517
#include <util/std_types.h>
1618
#include <util/c_types.h>
@@ -379,4 +381,30 @@ inline typet java_type_from_string_with_exception(
379381
}
380382
}
381383

384+
/// Get the index in the subtypes array for a given component.
385+
/// \param t The type we search for the subtypes in.
386+
/// \param identifier The string identifier of the type of the component.
387+
/// \return Optional with the size if the identifier was found.
388+
inline const optionalt<size_t> java_generics_get_index_for_subtype(
389+
const java_generics_class_typet &t,
390+
const irep_idt &identifier)
391+
{
392+
const std::vector<java_generic_parametert> &gen_types=t.generic_types();
393+
394+
const auto iter = std::find_if(
395+
gen_types.cbegin(),
396+
gen_types.cend(),
397+
[&identifier](const java_generic_parametert &ref)
398+
{
399+
return ref.type_variable().get_identifier()==identifier;
400+
});
401+
402+
if(iter==gen_types.cend())
403+
{
404+
return {};
405+
}
406+
407+
return std::distance(gen_types.cbegin(), iter);
408+
}
409+
382410
#endif // CPROVER_JAVA_BYTECODE_JAVA_TYPES_H

src/java_bytecode/java_utils.cpp

+22-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ const std::string java_class_to_package(const std::string &canonical_classname)
6767
void generate_class_stub(
6868
const irep_idt &class_name,
6969
symbol_tablet &symbol_table,
70-
message_handlert &message_handler)
70+
message_handlert &message_handler,
71+
const struct_union_typet::componentst &componentst)
7172
{
7273
class_typet class_type;
7374

@@ -100,6 +101,7 @@ void generate_class_stub(
100101
{
101102
// create the class identifier etc
102103
java_root_class(res.first);
104+
java_add_components_to_class(res.first, componentst);
103105
}
104106
}
105107

@@ -230,3 +232,22 @@ size_t find_closing_delimiter(
230232
// did not find corresponding closing '>'
231233
return std::string::npos;
232234
}
235+
236+
/// Add the components in components_to_add to the class denoted by
237+
/// class symbol.
238+
/// \param class_symbol The symbol representing the class we want to modify.
239+
/// \param components_to_add The vector with the components we want to add.
240+
void java_add_components_to_class(
241+
symbolt &class_symbol,
242+
const struct_union_typet::componentst &components_to_add)
243+
{
244+
PRECONDITION(class_symbol.is_type);
245+
PRECONDITION(class_symbol.type.id()==ID_struct);
246+
struct_typet &struct_type=to_struct_type(class_symbol.type);
247+
struct_typet::componentst &components=struct_type.components();
248+
249+
for(const struct_union_typet::componentt &component : components_to_add)
250+
{
251+
components.push_back(component);
252+
}
253+
}

0 commit comments

Comments
 (0)