Skip to content

Commit 48ee475

Browse files
authored
Merge pull request diffblue#1616 from svorenova/inner_classes_tg1190_part1
[TG-1190] Parse generic information of outer classes
2 parents 263fef4 + 0163362 commit 48ee475

16 files changed

+570
-3
lines changed

src/java_bytecode/java_bytecode_convert_class.cpp

+160
Original file line numberDiff line numberDiff line change
@@ -517,3 +517,163 @@ bool java_bytecode_convert_class(
517517

518518
return true;
519519
}
520+
521+
/// For a given generic type parameter, check if there is a parameter in the
522+
/// given vector of replacement parameters with a matching name. If yes,
523+
/// replace the identifier of the parameter at hand by the identifier of
524+
/// the matching parameter.
525+
/// Example: if \p parameter has identifier `java::Outer$Inner::T` and
526+
/// there is a replacement parameter with identifier `java::Outer::T`, the
527+
/// identifier of \p parameter gets set to `java::Outer::T`.
528+
/// \param parameter
529+
/// \param replacement_parameters vector of generic parameters (only viable
530+
/// ones, i.e., only those that can actually appear here such as generic
531+
/// parameters of outer classes of the class specified by the prefix of \p
532+
/// parameter identifier)
533+
static void find_and_replace_parameter(
534+
java_generic_parametert &parameter,
535+
const std::vector<java_generic_parametert> &replacement_parameters)
536+
{
537+
// get the name of the parameter, e.g., `T` from `java::Class::T`
538+
const std::string &parameter_full_name =
539+
as_string(parameter.type_variable_ref().get_identifier());
540+
const std::string &parameter_name =
541+
parameter_full_name.substr(parameter_full_name.rfind("::") + 2);
542+
543+
// check if there is a replacement parameter with the same name
544+
const auto replacement_parameter_p = std::find_if(
545+
replacement_parameters.begin(),
546+
replacement_parameters.end(),
547+
[&parameter_name](const java_generic_parametert &replacement_param)
548+
{
549+
const std::string &replacement_parameter_full_name =
550+
as_string(replacement_param.type_variable().get_identifier());
551+
return parameter_name.compare(
552+
replacement_parameter_full_name.substr(
553+
replacement_parameter_full_name.rfind("::") + 2)) == 0;
554+
});
555+
556+
// if a replacement parameter was found, update the identifier
557+
if(replacement_parameter_p != replacement_parameters.end())
558+
{
559+
const std::string &replacement_parameter_full_name =
560+
as_string(replacement_parameter_p->type_variable().get_identifier());
561+
562+
// the replacement parameter is a viable one, i.e., it comes from an outer
563+
// class
564+
PRECONDITION(
565+
parameter_full_name.substr(0, parameter_full_name.rfind("::"))
566+
.compare(
567+
replacement_parameter_full_name.substr(
568+
0, replacement_parameter_full_name.rfind("::"))) > 0);
569+
570+
parameter.type_variable_ref().set_identifier(
571+
replacement_parameter_full_name);
572+
}
573+
}
574+
575+
/// Recursively find all generic type parameters of a given type and replace
576+
/// their identifiers if matching replacement parameter exist.
577+
/// Example: class `Outer<T>` has an inner class `Inner` that has a field
578+
/// `t` of type `Generic<T>`. This function ensures that the parameter points to
579+
/// `java::Outer::T` instead of `java::Outer$Inner::T`.
580+
/// \param type
581+
/// \param replacement_parameters
582+
static void find_and_replace_parameters(
583+
typet &type,
584+
const std::vector<java_generic_parametert> &replacement_parameters)
585+
{
586+
if(is_java_generic_parameter(type))
587+
{
588+
find_and_replace_parameter(
589+
to_java_generic_parameter(type), replacement_parameters);
590+
}
591+
else if(is_java_generic_type(type))
592+
{
593+
java_generic_typet &generic_type = to_java_generic_type(type);
594+
std::vector<reference_typet> &arguments =
595+
generic_type.generic_type_arguments();
596+
for(auto &argument : arguments)
597+
{
598+
find_and_replace_parameters(argument, replacement_parameters);
599+
}
600+
}
601+
}
602+
603+
/// Checks if the class is implicitly generic, i.e., it is an inner class of
604+
/// any generic class. All uses of the implicit generic type parameters within
605+
/// the inner class are updated to point to the type parameters of the
606+
/// corresponding outer classes.
607+
/// \param class_name
608+
/// \param symbol_table
609+
void mark_java_implicitly_generic_class_type(
610+
const irep_idt &class_name,
611+
symbol_tablet &symbol_table)
612+
{
613+
const std::string qualified_class_name = "java::" + id2string(class_name);
614+
PRECONDITION(symbol_table.has_symbol(qualified_class_name));
615+
symbolt &class_symbol = symbol_table.get_writeable_ref(qualified_class_name);
616+
java_class_typet &class_type = to_java_class_type(class_symbol.type);
617+
618+
// the class must be an inner non-static class, i.e., have a field this$*
619+
// TODO this should be simplified once static inner classes are marked
620+
// during parsing
621+
bool no_this_field = std::none_of(
622+
class_type.components().begin(),
623+
class_type.components().end(),
624+
[](const struct_union_typet::componentt &component)
625+
{
626+
return id2string(component.get_name()).substr(0, 5) == "this$";
627+
});
628+
if(no_this_field)
629+
{
630+
return;
631+
}
632+
633+
// create a vector of all generic type parameters of all outer classes, in
634+
// the order from the outer-most inwards
635+
std::vector<java_generic_parametert> implicit_generic_type_parameters;
636+
std::string::size_type outer_class_delimiter =
637+
qualified_class_name.rfind("$");
638+
while(outer_class_delimiter != std::string::npos)
639+
{
640+
std::string outer_class_name =
641+
qualified_class_name.substr(0, outer_class_delimiter);
642+
if(symbol_table.has_symbol(outer_class_name))
643+
{
644+
const symbolt &outer_class_symbol =
645+
symbol_table.lookup_ref(outer_class_name);
646+
const java_class_typet &outer_class_type =
647+
to_java_class_type(outer_class_symbol.type);
648+
if(is_java_generic_class_type(outer_class_type))
649+
{
650+
const auto &outer_generic_type_parameters =
651+
to_java_generic_class_type(outer_class_type).generic_types();
652+
implicit_generic_type_parameters.insert(
653+
implicit_generic_type_parameters.begin(),
654+
outer_generic_type_parameters.begin(),
655+
outer_generic_type_parameters.end());
656+
}
657+
outer_class_delimiter = outer_class_name.rfind("$");
658+
}
659+
else
660+
{
661+
throw missing_outer_class_symbol_exceptiont(
662+
outer_class_name, qualified_class_name);
663+
}
664+
}
665+
666+
// if there are any implicit generic type parameters, mark the class
667+
// implicitly generic and update identifiers of type parameters used in fields
668+
if(!implicit_generic_type_parameters.empty())
669+
{
670+
class_symbol.type = java_implicitly_generic_class_typet(
671+
class_type, implicit_generic_type_parameters);
672+
673+
for(auto &field : class_type.components())
674+
{
675+
find_and_replace_parameters(
676+
field.type(), implicit_generic_type_parameters);
677+
}
678+
}
679+
}

src/java_bytecode/java_bytecode_convert_class.h

+18
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,22 @@ bool java_bytecode_convert_class(
2828
lazy_methods_modet,
2929
java_string_library_preprocesst &string_preprocess);
3030

31+
void mark_java_implicitly_generic_class_type(
32+
const irep_idt &class_name,
33+
symbol_tablet &symbol_table);
34+
35+
/// An exception that is raised checking whether a class is implicitly
36+
/// generic if a symbol for an outer class is missing
37+
class missing_outer_class_symbol_exceptiont : public std::logic_error
38+
{
39+
public:
40+
explicit missing_outer_class_symbol_exceptiont(
41+
const std::string &outer,
42+
const std::string &inner)
43+
: std::logic_error(
44+
"Missing outer class symbol: " + outer + ", for class " + inner)
45+
{
46+
}
47+
};
48+
3149
#endif // CPROVER_JAVA_BYTECODE_JAVA_BYTECODE_CONVERT_CLASS_H

src/java_bytecode/java_bytecode_language.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,26 @@ bool java_bytecode_languaget::typecheck(
209209
return true;
210210
}
211211

212+
// find and mark all implicitly generic class types
213+
// this can only be done once all the class symbols have been created
214+
for(const auto &c : java_class_loader.class_map)
215+
{
216+
if(c.second.parsed_class.name.empty())
217+
continue;
218+
try
219+
{
220+
mark_java_implicitly_generic_class_type(
221+
c.second.parsed_class.name, symbol_table);
222+
}
223+
catch(missing_outer_class_symbol_exceptiont &)
224+
{
225+
messaget::warning()
226+
<< "Not marking class " << c.first
227+
<< " implicitly generic due to missing outer class symbols"
228+
<< messaget::eom;
229+
}
230+
}
231+
212232
// Now incrementally elaborate methods
213233
// that are reachable from this entry point.
214234
if(lazy_methods_mode==LAZY_METHODS_MODE_CONTEXT_INSENSITIVE)

src/java_bytecode/java_types.h

+65
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ class java_generic_parametert:public reference_typet
110110
return type_variables().front();
111111
}
112112

113+
type_variablet &type_variable_ref()
114+
{
115+
PRECONDITION(!type_variables().empty());
116+
return const_cast<type_variablet &>(type_variables().front());
117+
}
118+
113119
private:
114120
typedef std::vector<type_variablet> type_variablest;
115121
const type_variablest &type_variables() const
@@ -313,6 +319,65 @@ inline const typet &java_generic_class_type_bound(size_t index, const typet &t)
313319
return gen_type.subtype();
314320
}
315321

322+
/// Type to hold a Java class that is implicitly generic, e.g., an inner
323+
/// class of a generic outer class or a derived class of a generic base
324+
/// class. Extends the java class type.
325+
class java_implicitly_generic_class_typet : public java_class_typet
326+
{
327+
public:
328+
typedef std::vector<java_generic_parametert> implicit_generic_typest;
329+
330+
explicit java_implicitly_generic_class_typet(
331+
const java_class_typet &class_type,
332+
const implicit_generic_typest &generic_types)
333+
: java_class_typet(class_type)
334+
{
335+
set(ID_C_java_implicitly_generic_class_type, true);
336+
for(const auto &type : generic_types)
337+
{
338+
implicit_generic_types().push_back(type);
339+
}
340+
}
341+
342+
const implicit_generic_typest &implicit_generic_types() const
343+
{
344+
return (
345+
const implicit_generic_typest
346+
&)(find(ID_implicit_generic_types).get_sub());
347+
}
348+
349+
implicit_generic_typest &implicit_generic_types()
350+
{
351+
return (
352+
implicit_generic_typest &)(add(ID_implicit_generic_types).get_sub());
353+
}
354+
};
355+
356+
/// \param type: the type to check
357+
/// \return true if type is a implicitly generic java class type
358+
inline bool is_java_implicitly_generic_class_type(const typet &type)
359+
{
360+
return type.get_bool(ID_C_java_implicitly_generic_class_type);
361+
}
362+
363+
/// \param type: the type to check
364+
/// \return cast of type to java_generics_class_typet
365+
inline const java_implicitly_generic_class_typet &
366+
to_java_implicitly_generic_class_type(const java_class_typet &type)
367+
{
368+
PRECONDITION(is_java_implicitly_generic_class_type(type));
369+
return static_cast<const java_implicitly_generic_class_typet &>(type);
370+
}
371+
372+
/// \param type: source type
373+
/// \return cast of type into a java class type with generics
374+
inline java_implicitly_generic_class_typet &
375+
to_java_implicitly_generic_class_type(java_class_typet &type)
376+
{
377+
PRECONDITION(is_java_implicitly_generic_class_type(type));
378+
return static_cast<java_implicitly_generic_class_typet &>(type);
379+
}
380+
316381
/// An exception that is raised for unsupported class signature.
317382
/// Currently we do not parse multiple bounds.
318383
class unsupported_java_class_signature_exceptiont:public std::logic_error

src/util/irep_ids.def

+2
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,9 @@ IREP_ID_TWO(C_java_generic_parameter, #java_generic_parameter)
831831
IREP_ID_TWO(C_java_generic_type, #java_generic_type)
832832
IREP_ID_TWO(C_java_generics_class_type, #java_generics_class_type)
833833
IREP_ID_TWO(C_specialized_generic_java_class, #specialized_generic_java_class)
834+
IREP_ID_TWO(C_java_implicitly_generic_class_type, #java_implicitly_generic_class_type)
834835
IREP_ID_TWO(generic_types, #generic_types)
836+
IREP_ID_TWO(implicit_generic_types, #implicit_generic_types)
835837
IREP_ID_TWO(type_variables, #type_variables)
836838
IREP_ID_ONE(havoc_object)
837839
IREP_ID_TWO(overflow_shl, overflow-shl)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
class GenericClassWithInnerClasses<T>
2+
{
3+
class Inner {
4+
T t1;
5+
Generic<T> t2;
6+
7+
class InnerInner {
8+
T tt1;
9+
Generic<Generic<T>> tt2;
10+
}
11+
}
12+
13+
class GenericInner<U> {
14+
T gt1;
15+
GenericTwoParam<T,U> gt2;
16+
17+
class GenericInnerInner<V>{
18+
19+
}
20+
}
21+
22+
static class StaticInner<U>{
23+
U st;
24+
}
25+
26+
Inner f1;
27+
Inner.InnerInner f2;
28+
GenericInner<Integer> f3;
29+
}

0 commit comments

Comments
 (0)