Skip to content

Commit ceafd85

Browse files
committed
Java string solver: ensure base types are loaded
This adds a mechanism to the Java frontend class loader for its caller to advise it of extra class dependencies to load, and uses it from the String preprocessor to indicate that since it will add base classes to String, StringBuilder etc, then the class loader should create symbols for those bases. This restores the invariant that holds when --string-refine is not passed, that if a class references another as a base, then that other class has an entry in the symbol table (although that entry may be incomplete).
1 parent 1e17db6 commit ceafd85

5 files changed

+78
-14
lines changed

src/java_bytecode/java_bytecode_language.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,16 @@ bool java_bytecode_languaget::parse(
147147
java_class_loader.set_message_handler(get_message_handler());
148148
java_class_loader.set_java_cp_include_files(java_cp_include_files);
149149
java_class_loader.add_load_classes(java_load_classes);
150+
if(string_refinement_enabled)
151+
{
152+
string_preprocess.initialize_known_type_table();
153+
154+
auto get_string_base_classes = [this](const irep_idt &id) { // NOLINT (*)
155+
return string_preprocess.get_string_type_base_classes(id);
156+
};
157+
158+
java_class_loader.set_extra_class_refs_function(get_string_base_classes);
159+
}
150160

151161
// look at extension
152162
if(has_suffix(path, ".class"))

src/java_bytecode/java_class_loader.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ java_bytecode_parse_treet &java_class_loadert::operator()(
7272
it!=parse_tree.class_refs.end();
7373
it++)
7474
queue.push(*it);
75+
76+
// Add any extra dependencies provided by our caller:
77+
if(get_extra_class_refs)
78+
{
79+
std::vector<irep_idt> extra_class_refs =
80+
get_extra_class_refs(c);
81+
82+
for(const irep_idt &id : extra_class_refs)
83+
queue.push(id);
84+
}
7585
}
7686

7787
return class_map[class_name];
@@ -83,6 +93,16 @@ void java_class_loadert::set_java_cp_include_files(
8393
java_cp_include_files=_java_cp_include_files;
8494
}
8595

96+
/// Sets a function that provides extra dependencies for a particular class.
97+
/// Currently used by the string preprocessor to note that even if we don't
98+
/// have a definition of core string types, it will nontheless give them
99+
/// certain known superclasses and interfaces, such as Serializable.
100+
void java_class_loadert::set_extra_class_refs_function(
101+
java_class_loadert::get_extra_class_refs_functiont func)
102+
{
103+
get_extra_class_refs = func;
104+
}
105+
86106
/// Retrieves a class file from a jar and loads it into the tree
87107
bool java_class_loadert::get_class_file(
88108
java_class_loader_limitt &class_loader_limit,

src/java_bytecode/java_class_loader.h

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ class java_class_loadert:public messaget
3434
void set_java_cp_include_files(std::string &);
3535
void add_load_classes(const std::vector<irep_idt> &);
3636

37+
/// A function that yields a list of extra dependencies based on a class name.
38+
typedef std::function<std::vector<irep_idt>(const irep_idt &)>
39+
get_extra_class_refs_functiont;
40+
41+
void set_extra_class_refs_function(get_extra_class_refs_functiont func);
42+
3743
static std::string file_to_class_name(const std::string &);
3844
static std::string class_name_to_file(const irep_idt &);
3945

@@ -133,6 +139,7 @@ class java_class_loadert:public messaget
133139
private:
134140
std::map<std::string, jar_filet> m_archives;
135141
std::vector<irep_idt> java_load_classes;
142+
get_extra_class_refs_functiont get_extra_class_refs;
136143
};
137144

138145
#endif // CPROVER_JAVA_BYTECODE_JAVA_CLASS_LOADER_H

src/java_bytecode/java_string_library_preprocess.cpp

+38-14
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,34 @@ typet string_length_type()
186186
return java_int_type();
187187
}
188188

189+
/// Gets the base classes for known String and String-related types, or returns
190+
/// an empty list for other types.
191+
/// \param class_name: class identifier, without "java::" prefix.
192+
/// \return a list of base classes, again without "java::" prefix.
193+
std::vector<irep_idt>
194+
java_string_library_preprocesst::get_string_type_base_classes(
195+
const irep_idt &class_name)
196+
{
197+
if(!is_known_string_type(class_name))
198+
return {};
199+
200+
std::vector<irep_idt> bases;
201+
bases.reserve(3);
202+
if(class_name != "java.lang.CharSequence")
203+
{
204+
bases.push_back("java.io.Serializable");
205+
bases.push_back("java.lang.CharSequence");
206+
}
207+
if(class_name == "java.lang.String")
208+
bases.push_back("java.lang.Comparable");
209+
210+
if(class_name == "java.lang.StringBuilder" ||
211+
class_name == "java.lang.StringBuffer")
212+
bases.push_back("java.lang.AbstractStringBuilder");
213+
214+
return bases;
215+
}
216+
189217
/// Add to the symbol table type declaration for a String-like Java class.
190218
/// \param class_name: a name for the class such as "java.lang.String"
191219
/// \param symbol_table: symbol table to which the class will be added
@@ -206,17 +234,10 @@ void java_string_library_preprocesst::add_string_type(
206234
string_type.components()[2].type() = pointer_type(java_char_type());
207235
string_type.set_access(ID_public);
208236
string_type.add_base(symbol_typet("java::java.lang.Object"));
209-
if(class_name!="java.lang.CharSequence")
210-
{
211-
string_type.add_base(symbol_typet("java::java.io.Serializable"));
212-
string_type.add_base(symbol_typet("java::java.lang.CharSequence"));
213-
}
214-
if(class_name=="java.lang.String")
215-
string_type.add_base(symbol_typet("java::java.lang.Comparable"));
216237

217-
if(class_name=="java.lang.StringBuilder" ||
218-
class_name=="java.lang.StringBuffer")
219-
string_type.add_base(symbol_typet("java::java.lang.AbstractStringBuilder"));
238+
std::vector<irep_idt> bases = get_string_type_base_classes(class_name);
239+
for(const irep_idt &base_name : bases)
240+
string_type.add_base(symbol_typet("java::" + id2string(base_name)));
220241

221242
symbolt tmp_string_symbol;
222243
tmp_string_symbol.name="java::"+id2string(class_name);
@@ -1848,16 +1869,19 @@ bool java_string_library_preprocesst::is_known_string_type(
18481869
return string_types.find(class_name)!=string_types.end();
18491870
}
18501871

1851-
/// fill maps with correspondence from java method names to conversion functions
1852-
void java_string_library_preprocesst::initialize_conversion_table()
1872+
void java_string_library_preprocesst::initialize_known_type_table()
18531873
{
1854-
character_preprocess.initialize_conversion_table();
1855-
18561874
string_types=
18571875
std::unordered_set<irep_idt, irep_id_hash>{"java.lang.String",
18581876
"java.lang.StringBuilder",
18591877
"java.lang.CharSequence",
18601878
"java.lang.StringBuffer"};
1879+
}
1880+
1881+
/// fill maps with correspondence from java method names to conversion functions
1882+
void java_string_library_preprocesst::initialize_conversion_table()
1883+
{
1884+
character_preprocess.initialize_conversion_table();
18611885

18621886
// The following list of function is organized by libraries, with
18631887
// constructors first and then methods in alphabetic order.

src/java_bytecode/java_string_library_preprocess.h

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class java_string_library_preprocesst:public messaget
4343
{
4444
}
4545

46+
void initialize_known_type_table();
4647
void initialize_conversion_table();
4748
void initialize_refined_string_type();
4849

@@ -56,6 +57,8 @@ class java_string_library_preprocesst:public messaget
5657
{
5758
return character_preprocess.replace_character_call(call);
5859
}
60+
std::vector<irep_idt> get_string_type_base_classes(
61+
const irep_idt &class_name);
5962
void add_string_type(const irep_idt &class_name, symbol_tablet &symbol_table);
6063
bool is_known_string_type(irep_idt class_name);
6164

0 commit comments

Comments
 (0)