@@ -72,8 +72,7 @@ bool ci_lazy_methodst::operator()(
72
72
method_bytecodet &method_bytecode,
73
73
const method_convertert &method_converter)
74
74
{
75
- std::vector<irep_idt> method_worklist1;
76
- std::vector<irep_idt> method_worklist2;
75
+ std::unordered_set<irep_idt> methods_to_convert_later;
77
76
78
77
main_function_resultt main_function =
79
78
get_main_symbol (symbol_table, main_class, get_message_handler ());
@@ -95,94 +94,121 @@ bool ci_lazy_methodst::operator()(
95
94
const irep_idt methodid =
96
95
" java::" + id2string (class_name) + " ." + id2string (method.name )
97
96
+ " :" + id2string (method.descriptor );
98
- method_worklist2. push_back (methodid);
97
+ methods_to_convert_later. insert (methodid);
99
98
}
100
99
}
101
100
}
102
101
else
103
- method_worklist2. push_back (main_function.main_function .name );
102
+ methods_to_convert_later. insert (main_function.main_function .name );
104
103
105
104
// Add any extra entry points specified; we should elaborate these in the
106
105
// same way as the main function.
107
106
std::vector<irep_idt> extra_entry_points=lazy_methods_extra_entry_points;
108
107
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 ());
113
110
114
- std::set <irep_idt> instantiated_classes;
111
+ std::unordered_set <irep_idt> instantiated_classes;
115
112
116
113
{
117
- std::vector <irep_idt> initial_callable_methods;
114
+ std::unordered_set <irep_idt> initial_callable_methods;
118
115
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);
122
117
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 ());
130
121
}
131
122
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 ;
134
125
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)
137
130
{
138
131
any_new_methods=false ;
139
- while (!method_worklist2 .empty ())
132
+ while (!methods_to_convert_later .empty ())
140
133
{
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)
143
137
{
144
138
if (!methods_already_populated.insert (mname).second )
145
139
continue ;
146
140
debug () << " CI lazy methods: elaborate " << mname << eom;
147
141
if (
148
142
method_converter (
149
143
mname,
150
- // Note this wraps *references* to method_worklist2 &
144
+ // Note this wraps *references* to methods_to_convert_later &
151
145
// instantiated_classes
152
146
ci_lazy_methods_neededt (
153
- method_worklist2 , instantiated_classes, symbol_table)))
147
+ methods_to_convert_later , instantiated_classes, symbol_table)))
154
148
{
155
149
// Couldn't convert this function
156
150
continue ;
157
151
}
158
152
gather_virtual_callsites (
159
- symbol_table.lookup_ref (mname).value ,
160
- virtual_callsites);
153
+ symbol_table.lookup_ref (mname).value , virtual_function_calls);
161
154
any_new_methods=true ;
162
155
}
163
- method_worklist1.clear ();
164
156
}
165
157
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:
168
160
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;
173
163
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 ;
177
175
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)
179
183
{
180
- // This will also create a stub if a virtual callsite has no targets.
184
+ std::unordered_set<irep_idt> candidate_target_methods;
181
185
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);
183
210
}
184
211
}
185
- while (any_new_methods);
186
212
187
213
// Remove symbols for methods that were declared but never used:
188
214
symbol_tablet keep_symbols;
@@ -279,7 +305,7 @@ void ci_lazy_methodst::resolve_method_names(
279
305
// / whose references may be passed, directly or indirectly, to any of the
280
306
// / functions in `entry_points`.
281
307
void ci_lazy_methodst::initialize_instantiated_classes (
282
- const std::vector <irep_idt> &entry_points,
308
+ const std::unordered_set <irep_idt> &entry_points,
283
309
const namespacet &ns,
284
310
ci_lazy_methods_neededt &needed_lazy_methods)
285
311
{
@@ -386,15 +412,15 @@ void ci_lazy_methodst::initialize_instantiated_classes_from_pointer(
386
412
// / e that calls a virtual function.
387
413
void ci_lazy_methodst::gather_virtual_callsites (
388
414
const exprt &e,
389
- std::vector< const code_function_callt * > &result)
415
+ std::unordered_set<exprt, irep_hash > &result)
390
416
{
391
417
if (e.id ()!=ID_code)
392
418
return ;
393
419
const codet &c=to_code (e);
394
420
if (c.get_statement ()==ID_function_call &&
395
421
to_code_function_call (c).function ().id ()==ID_virtual_function)
396
422
{
397
- result.push_back (& to_code_function_call (c));
423
+ result.insert ( to_code_function_call (c). function ( ));
398
424
}
399
425
else
400
426
{
@@ -409,14 +435,14 @@ void ci_lazy_methodst::gather_virtual_callsites(
409
435
// / should be determined.
410
436
// / \param instantiated_classes: set of classes that can be instantiated. Any
411
437
// / potential callee not in this set will be ignored.
412
- // / \param symbol_table: global symbol table
413
438
// / \param [out] callable_methods: Populated with all possible `c` callees,
414
439
// / taking `instantiated_classes` into account (virtual function overrides
415
440
// / defined on classes that are not 'needed' are ignored)
441
+ // / \param symbol_table: global symbol table
416
442
void ci_lazy_methodst::get_virtual_method_targets (
417
443
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,
420
446
symbol_tablet &symbol_table)
421
447
{
422
448
PRECONDITION (called_function.id ()==ID_virtual_function);
@@ -429,26 +455,16 @@ void ci_lazy_methodst::get_virtual_method_targets(
429
455
!call_basename.empty (),
430
456
" Virtual function must have a reasonable name after removing class" );
431
457
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);
440
461
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)
443
463
{
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);
452
468
}
453
469
}
454
470
@@ -554,7 +570,7 @@ void ci_lazy_methodst::gather_field_types(
554
570
// / `call_basename` if found and `classname` is present in
555
571
// / `instantiated_classes`, or irep_idt() otherwise.
556
572
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,
558
574
const irep_idt &call_basename,
559
575
const irep_idt &classname,
560
576
const symbol_tablet &symbol_table)
0 commit comments