Skip to content

Commit de1915a

Browse files
author
Thomas Kiley
authored
Merge pull request diffblue#2074 from owen-jones-diffblue/owen-jones-diffblue/lazy-methods-no-candidate-callees
Deal with virtual function calls with no candidate targets
2 parents ad62682 + 1c34d22 commit de1915a

File tree

9 files changed

+123
-84
lines changed

9 files changed

+123
-84
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
public class Test {
2+
3+
interface factory_intf {
4+
public intf getintf();
5+
}
6+
7+
interface intf {
8+
public void f();
9+
}
10+
11+
public static void main(factory_intf i) { i.getintf().f(); }
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Test.class
3+
--lazy-methods --show-goto-functions --verbosity 10 --function Test.main
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^CI lazy methods: elaborate java::Test\$intf\.f:\(\)V$
7+
Test\$intf\.f:\(\)V\(\);$
8+
--
9+
--
10+
This doesn't work under symex-driven lazy loading because it is incompatible with --lazy-methods

src/java_bytecode/ci_lazy_methods.cpp

Lines changed: 86 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@ bool ci_lazy_methodst::operator()(
7272
method_bytecodet &method_bytecode,
7373
const method_convertert &method_converter)
7474
{
75-
std::vector<irep_idt> method_worklist1;
76-
std::vector<irep_idt> method_worklist2;
75+
std::unordered_set<irep_idt> methods_to_convert_later;
7776

7877
main_function_resultt main_function =
7978
get_main_symbol(symbol_table, main_class, get_message_handler());
@@ -95,94 +94,121 @@ bool ci_lazy_methodst::operator()(
9594
const irep_idt methodid =
9695
"java::" + id2string(class_name) + "." + id2string(method.name)
9796
+ ":" + id2string(method.descriptor);
98-
method_worklist2.push_back(methodid);
97+
methods_to_convert_later.insert(methodid);
9998
}
10099
}
101100
}
102101
else
103-
method_worklist2.push_back(main_function.main_function.name);
102+
methods_to_convert_later.insert(main_function.main_function.name);
104103

105104
// Add any extra entry points specified; we should elaborate these in the
106105
// same way as the main function.
107106
std::vector<irep_idt> extra_entry_points=lazy_methods_extra_entry_points;
108107
resolve_method_names(extra_entry_points, symbol_table);
109-
method_worklist2.insert(
110-
method_worklist2.begin(),
111-
extra_entry_points.begin(),
112-
extra_entry_points.end());
108+
methods_to_convert_later.insert(
109+
extra_entry_points.begin(), extra_entry_points.end());
113110

114-
std::set<irep_idt> instantiated_classes;
111+
std::unordered_set<irep_idt> instantiated_classes;
115112

116113
{
117-
std::vector<irep_idt> initial_callable_methods;
114+
std::unordered_set<irep_idt> initial_callable_methods;
118115
ci_lazy_methods_neededt initial_lazy_methods(
119-
initial_callable_methods,
120-
instantiated_classes,
121-
symbol_table);
116+
initial_callable_methods, instantiated_classes, symbol_table);
122117
initialize_instantiated_classes(
123-
method_worklist2,
124-
namespacet(symbol_table),
125-
initial_lazy_methods);
126-
method_worklist2.insert(
127-
method_worklist2.end(),
128-
initial_callable_methods.begin(),
129-
initial_callable_methods.end());
118+
methods_to_convert_later, namespacet(symbol_table), initial_lazy_methods);
119+
methods_to_convert_later.insert(
120+
initial_callable_methods.begin(), initial_callable_methods.end());
130121
}
131122

132-
std::set<irep_idt> methods_already_populated;
133-
std::vector<const code_function_callt *> virtual_callsites;
123+
std::unordered_set<irep_idt> methods_already_populated;
124+
std::unordered_set<exprt, irep_hash> virtual_function_calls;
134125

135-
bool any_new_methods=false;
136-
do
126+
bool any_new_classes = true;
127+
while(any_new_classes)
128+
{bool any_new_methods = true;
129+
while(any_new_methods)
137130
{
138131
any_new_methods=false;
139-
while(!method_worklist2.empty())
132+
while(!methods_to_convert_later.empty())
140133
{
141-
std::swap(method_worklist1, method_worklist2);
142-
for(const auto &mname : method_worklist1)
134+
std::unordered_set<irep_idt> methods_to_convert;
135+
std::swap(methods_to_convert, methods_to_convert_later);
136+
for(const auto &mname : methods_to_convert)
143137
{
144138
if(!methods_already_populated.insert(mname).second)
145139
continue;
146140
debug() << "CI lazy methods: elaborate " << mname << eom;
147141
if(
148142
method_converter(
149143
mname,
150-
// Note this wraps *references* to method_worklist2 &
144+
// Note this wraps *references* to methods_to_convert_later &
151145
// instantiated_classes
152146
ci_lazy_methods_neededt(
153-
method_worklist2, instantiated_classes, symbol_table)))
147+
methods_to_convert_later, instantiated_classes, symbol_table)))
154148
{
155149
// Couldn't convert this function
156150
continue;
157151
}
158152
gather_virtual_callsites(
159-
symbol_table.lookup_ref(mname).value,
160-
virtual_callsites);
153+
symbol_table.lookup_ref(mname).value, virtual_function_calls);
161154
any_new_methods=true;
162155
}
163-
method_worklist1.clear();
164156
}
165157

166-
// Given the object types we now know may be created, populate more
167-
// possible virtual function call targets:
158+
// Given the object types we now know may be created, populate more
159+
// possible virtual function call targets:
168160

169-
debug() << "CI lazy methods: add virtual method targets ("
170-
<< virtual_callsites.size()
171-
<< " callsites)"
172-
<< eom;
161+
debug() << "CI lazy methods: add virtual method targets ("
162+
<< virtual_function_calls.size() << " callsites)" << eom;
173163

174-
std::unordered_set<exprt, irep_hash> unique_functions;
175-
for(const code_function_callt *virtual_callsite : virtual_callsites)
176-
unique_functions.insert(virtual_callsite->function());
164+
for(const exprt &function : virtual_function_calls)
165+
{
166+
get_virtual_method_targets(
167+
function,
168+
instantiated_classes,
169+
methods_to_convert_later,
170+
symbol_table);
171+
}
172+
}
173+
174+
any_new_classes = false;
177175

178-
for(const exprt &function : unique_functions)
176+
// Look for virtual callsites with no candidate targets. If we have
177+
// invokevirtual A.f and we don't believe either A or any of its children
178+
// may exist, we assume specifically A is somehow instantiated. Note this
179+
// may result in an abstract class being classified as instantiated, which
180+
// stands in for some unknown concrete subclass: in this case the called
181+
// method will be a stub.
182+
for(const exprt &virtual_function_call : virtual_function_calls)
179183
{
180-
// This will also create a stub if a virtual callsite has no targets.
184+
std::unordered_set<irep_idt> candidate_target_methods;
181185
get_virtual_method_targets(
182-
function, instantiated_classes, method_worklist2, symbol_table);
186+
virtual_function_call,
187+
instantiated_classes,
188+
candidate_target_methods,
189+
symbol_table);
190+
191+
if(!candidate_target_methods.empty())
192+
continue;
193+
194+
// Add the call class to instantiated_classes and assert that it
195+
// didn't already exist
196+
const irep_idt &call_class = virtual_function_call.get(ID_C_class);
197+
auto ret_class = instantiated_classes.insert(call_class);
198+
CHECK_RETURN(ret_class.second);
199+
any_new_classes = true;
200+
201+
// Check that `get_virtual_method_target` returns a method now
202+
const irep_idt &call_basename =
203+
virtual_function_call.get(ID_component_name);
204+
const irep_idt method_name = get_virtual_method_target(
205+
instantiated_classes, call_basename, call_class, symbol_table);
206+
CHECK_RETURN(!method_name.empty());
207+
208+
// Add what it returns to methods_to_convert_later
209+
methods_to_convert_later.insert(method_name);
183210
}
184211
}
185-
while(any_new_methods);
186212

187213
// Remove symbols for methods that were declared but never used:
188214
symbol_tablet keep_symbols;
@@ -279,7 +305,7 @@ void ci_lazy_methodst::resolve_method_names(
279305
/// whose references may be passed, directly or indirectly, to any of the
280306
/// functions in `entry_points`.
281307
void ci_lazy_methodst::initialize_instantiated_classes(
282-
const std::vector<irep_idt> &entry_points,
308+
const std::unordered_set<irep_idt> &entry_points,
283309
const namespacet &ns,
284310
ci_lazy_methods_neededt &needed_lazy_methods)
285311
{
@@ -386,15 +412,15 @@ void ci_lazy_methodst::initialize_instantiated_classes_from_pointer(
386412
/// e that calls a virtual function.
387413
void ci_lazy_methodst::gather_virtual_callsites(
388414
const exprt &e,
389-
std::vector<const code_function_callt *> &result)
415+
std::unordered_set<exprt, irep_hash> &result)
390416
{
391417
if(e.id()!=ID_code)
392418
return;
393419
const codet &c=to_code(e);
394420
if(c.get_statement()==ID_function_call &&
395421
to_code_function_call(c).function().id()==ID_virtual_function)
396422
{
397-
result.push_back(&to_code_function_call(c));
423+
result.insert(to_code_function_call(c).function());
398424
}
399425
else
400426
{
@@ -409,14 +435,14 @@ void ci_lazy_methodst::gather_virtual_callsites(
409435
/// should be determined.
410436
/// \param instantiated_classes: set of classes that can be instantiated. Any
411437
/// potential callee not in this set will be ignored.
412-
/// \param symbol_table: global symbol table
413438
/// \param [out] callable_methods: Populated with all possible `c` callees,
414439
/// taking `instantiated_classes` into account (virtual function overrides
415440
/// defined on classes that are not 'needed' are ignored)
441+
/// \param symbol_table: global symbol table
416442
void ci_lazy_methodst::get_virtual_method_targets(
417443
const exprt &called_function,
418-
const std::set<irep_idt> &instantiated_classes,
419-
std::vector<irep_idt> &callable_methods,
444+
const std::unordered_set<irep_idt> &instantiated_classes,
445+
std::unordered_set<irep_idt> &callable_methods,
420446
symbol_tablet &symbol_table)
421447
{
422448
PRECONDITION(called_function.id()==ID_virtual_function);
@@ -429,26 +455,16 @@ void ci_lazy_methodst::get_virtual_method_targets(
429455
!call_basename.empty(),
430456
"Virtual function must have a reasonable name after removing class");
431457

432-
const irep_idt &self_method=
433-
get_virtual_method_target(
434-
instantiated_classes, call_basename, call_class, symbol_table);
435-
436-
if(!self_method.empty())
437-
{
438-
callable_methods.push_back(self_method);
439-
}
458+
class_hierarchyt::idst self_and_child_classes =
459+
class_hierarchy.get_children_trans(call_class);
460+
self_and_child_classes.push_back(call_class);
440461

441-
const auto child_classes=class_hierarchy.get_children_trans(call_class);
442-
for(const auto &child_class : child_classes)
462+
for(const irep_idt &class_name : self_and_child_classes)
443463
{
444-
const auto child_method=
445-
get_virtual_method_target(
446-
instantiated_classes,
447-
call_basename,
448-
child_class,
449-
symbol_table);
450-
if(!child_method.empty())
451-
callable_methods.push_back(child_method);
464+
const irep_idt method_name = get_virtual_method_target(
465+
instantiated_classes, call_basename, class_name, symbol_table);
466+
if(!method_name.empty())
467+
callable_methods.insert(method_name);
452468
}
453469
}
454470

@@ -554,7 +570,7 @@ void ci_lazy_methodst::gather_field_types(
554570
/// `call_basename` if found and `classname` is present in
555571
/// `instantiated_classes`, or irep_idt() otherwise.
556572
irep_idt ci_lazy_methodst::get_virtual_method_target(
557-
const std::set<irep_idt> &instantiated_classes,
573+
const std::unordered_set<irep_idt> &instantiated_classes,
558574
const irep_idt &call_basename,
559575
const irep_idt &classname,
560576
const symbol_tablet &symbol_table)

src/java_bytecode/ci_lazy_methods.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class ci_lazy_methodst:public messaget
115115
const symbol_tablet &symbol_table);
116116

117117
void initialize_instantiated_classes(
118-
const std::vector<irep_idt> &entry_points,
118+
const std::unordered_set<irep_idt> &entry_points,
119119
const namespacet &ns,
120120
ci_lazy_methods_neededt &needed_lazy_methods);
121121

@@ -131,12 +131,12 @@ class ci_lazy_methodst:public messaget
131131

132132
void gather_virtual_callsites(
133133
const exprt &e,
134-
std::vector<const code_function_callt *> &result);
134+
std::unordered_set<exprt, irep_hash> &result);
135135

136136
void get_virtual_method_targets(
137137
const exprt &called_function,
138-
const std::set<irep_idt> &instantiated_classes,
139-
std::vector<irep_idt> &callable_methods,
138+
const std::unordered_set<irep_idt> &instantiated_classes,
139+
std::unordered_set<irep_idt> &callable_methods,
140140
symbol_tablet &symbol_table);
141141

142142
void gather_needed_globals(
@@ -150,7 +150,7 @@ class ci_lazy_methodst:public messaget
150150
ci_lazy_methods_neededt &needed_lazy_methods);
151151

152152
irep_idt get_virtual_method_target(
153-
const std::set<irep_idt> &instantiated_classes,
153+
const std::unordered_set<irep_idt> &instantiated_classes,
154154
const irep_idt &call_basename,
155155
const irep_idt &classname,
156156
const symbol_tablet &symbol_table);

src/java_bytecode/ci_lazy_methods_needed.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Author: Chris Smowton, [email protected]
2020
void ci_lazy_methods_neededt::add_needed_method(
2121
const irep_idt &method_symbol_name)
2222
{
23-
callable_methods.push_back(method_symbol_name);
23+
callable_methods.insert(method_symbol_name);
2424
}
2525

2626
/// Notes class `class_symbol_name` will be instantiated, or a static field

src/java_bytecode/ci_lazy_methods_needed.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@ Author: Chris Smowton, [email protected]
1414

1515
#include <vector>
1616
#include <set>
17+
#include <unordered_set>
1718
#include <util/symbol_table.h>
1819

1920
class ci_lazy_methods_neededt
2021
{
2122
public:
2223
ci_lazy_methods_neededt(
23-
std::vector<irep_idt> &_callable_methods,
24-
std::set<irep_idt> &_instantiated_classes,
25-
symbol_tablet &_symbol_table):
26-
callable_methods(_callable_methods),
27-
instantiated_classes(_instantiated_classes),
28-
symbol_table(_symbol_table)
24+
std::unordered_set<irep_idt> &_callable_methods,
25+
std::unordered_set<irep_idt> &_instantiated_classes,
26+
symbol_tablet &_symbol_table)
27+
: callable_methods(_callable_methods),
28+
instantiated_classes(_instantiated_classes),
29+
symbol_table(_symbol_table)
2930
{}
3031

3132
void add_needed_method(const irep_idt &);
@@ -38,11 +39,11 @@ class ci_lazy_methods_neededt
3839
// contain all methods that have previously been elaborated.
3940
// It should be changed to a set if we develop the need to use
4041
// it that way.
41-
std::vector<irep_idt> &callable_methods;
42+
std::unordered_set<irep_idt> &callable_methods;
4243
// instantiated_classes on the other hand is a true set of every class
4344
// found so far, so we can use a membership test to avoid
4445
// repeatedly exploring a class hierarchy.
45-
std::set<irep_idt> &instantiated_classes;
46+
std::unordered_set<irep_idt> &instantiated_classes;
4647
symbol_tablet &symbol_table;
4748
};
4849

0 commit comments

Comments
 (0)