@@ -98,34 +98,8 @@ bool ci_lazy_methodst::operator()(
98
98
method_bytecodet &method_bytecode,
99
99
const method_convertert &method_converter)
100
100
{
101
- std::unordered_set<irep_idt> methods_to_convert_later;
102
-
103
- main_function_resultt main_function =
104
- get_main_symbol (symbol_table, main_class, get_message_handler ());
105
- if (!main_function.is_success ())
106
- {
107
- // Failed, mark all functions in the given main class(es)
108
- // reachable.
109
- std::vector<irep_idt> reachable_classes;
110
- if (!main_class.empty ())
111
- reachable_classes.push_back (main_class);
112
- else
113
- reachable_classes = main_jar_classes;
114
- for (const irep_idt &class_name : reachable_classes)
115
- {
116
- const auto &methods =
117
- java_class_loader.get_original_class (class_name).parsed_class .methods ;
118
- for (const auto &method : methods)
119
- {
120
- const irep_idt methodid =
121
- " java::" + id2string (class_name) + " ." + id2string (method.name )
122
- + " :" + id2string (method.descriptor );
123
- methods_to_convert_later.insert (methodid);
124
- }
125
- }
126
- }
127
- else
128
- methods_to_convert_later.insert (main_function.main_function .name );
101
+ std::unordered_set<irep_idt> methods_to_convert_later =
102
+ entry_point_methods (symbol_table);
129
103
130
104
// Add any extra entry points specified; we should elaborate these in the
131
105
// same way as the main function.
@@ -152,46 +126,30 @@ bool ci_lazy_methodst::operator()(
152
126
153
127
bool any_new_classes = true ;
154
128
while (any_new_classes)
155
- {bool any_new_methods = true ;
156
- while (any_new_methods)
157
129
{
158
- any_new_methods= false ;
159
- while (!methods_to_convert_later. empty () )
130
+ bool any_new_methods = true ;
131
+ while (any_new_methods )
160
132
{
161
- std::unordered_set<irep_idt> methods_to_convert;
162
- std::swap (methods_to_convert, methods_to_convert_later);
163
- for (const auto &mname : methods_to_convert)
133
+ any_new_methods = false ;
134
+ while (!methods_to_convert_later.empty ())
164
135
{
165
- if (!methods_already_populated.insert (mname).second )
166
- continue ;
167
- debug () << " CI lazy methods: elaborate " << mname << eom;
168
- if (
169
- method_converter (
170
- mname,
171
- // Note this wraps *references* to methods_to_convert_later &
172
- // instantiated_classes
173
- ci_lazy_methods_neededt (
174
- methods_to_convert_later, instantiated_classes, symbol_table)))
175
- {
176
- // Couldn't convert this function
177
- continue ;
178
- }
179
- const exprt &method_body = symbol_table.lookup_ref (mname).value ;
180
-
181
- gather_virtual_callsites (method_body, virtual_function_calls);
182
-
183
- if (!class_initializer_seen && references_class_model (method_body))
136
+ std::unordered_set<irep_idt> methods_to_convert;
137
+ std::swap (methods_to_convert, methods_to_convert_later);
138
+ for (const auto &mname : methods_to_convert)
184
139
{
185
- class_initializer_seen = true ;
186
- irep_idt initializer_signature =
187
- get_java_class_literal_initializer_signature ();
188
- if (symbol_table.has_symbol (initializer_signature))
189
- methods_to_convert_later.insert (initializer_signature);
140
+ const auto conversion_result = convert_and_analyze_method (
141
+ method_converter,
142
+ methods_already_populated,
143
+ class_initializer_seen,
144
+ mname,
145
+ symbol_table,
146
+ methods_to_convert_later,
147
+ instantiated_classes,
148
+ virtual_function_calls);
149
+ any_new_methods |= conversion_result.new_method_seen ;
150
+ class_initializer_seen |= conversion_result.class_initializer_seen ;
190
151
}
191
-
192
- any_new_methods=true ;
193
152
}
194
- }
195
153
196
154
// Given the object types we now know may be created, populate more
197
155
// possible virtual function call targets:
@@ -209,43 +167,11 @@ bool ci_lazy_methodst::operator()(
209
167
}
210
168
}
211
169
212
- any_new_classes = false ;
213
-
214
- // Look for virtual callsites with no candidate targets. If we have
215
- // invokevirtual A.f and we don't believe either A or any of its children
216
- // may exist, we assume specifically A is somehow instantiated. Note this
217
- // may result in an abstract class being classified as instantiated, which
218
- // stands in for some unknown concrete subclass: in this case the called
219
- // method will be a stub.
220
- for (const exprt &virtual_function_call : virtual_function_calls)
221
- {
222
- std::unordered_set<irep_idt> candidate_target_methods;
223
- get_virtual_method_targets (
224
- virtual_function_call,
225
- instantiated_classes,
226
- candidate_target_methods,
227
- symbol_table);
228
-
229
- if (!candidate_target_methods.empty ())
230
- continue ;
231
-
232
- // Add the call class to instantiated_classes and assert that it
233
- // didn't already exist
234
- const irep_idt &call_class = virtual_function_call.get (ID_C_class);
235
- auto ret_class = instantiated_classes.insert (call_class);
236
- CHECK_RETURN (ret_class.second );
237
- any_new_classes = true ;
238
-
239
- // Check that `get_virtual_method_target` returns a method now
240
- const irep_idt &call_basename =
241
- virtual_function_call.get (ID_component_name);
242
- const irep_idt method_name = get_virtual_method_target (
243
- instantiated_classes, call_basename, call_class, symbol_table);
244
- CHECK_RETURN (!method_name.empty ());
245
-
246
- // Add what it returns to methods_to_convert_later
247
- methods_to_convert_later.insert (method_name);
248
- }
170
+ any_new_classes = handle_virtual_methods_with_no_callees (
171
+ methods_to_convert_later,
172
+ instantiated_classes,
173
+ virtual_function_calls,
174
+ symbol_table);
249
175
}
250
176
251
177
// cproverNondetInitialize has to be force-loaded for mocks to return valid
@@ -297,6 +223,143 @@ bool ci_lazy_methodst::operator()(
297
223
return false ;
298
224
}
299
225
226
+ // / Look for virtual callsites with no candidate targets. If we have
227
+ // / invokevirtual A.f and we don't believe either A or any of its children
228
+ // / may exist, we assume specifically A is somehow instantiated. Note this
229
+ // / may result in an abstract class being classified as instantiated, which
230
+ // / stands in for some unknown concrete subclass: in this case the called
231
+ // / method will be a stub.
232
+ // / \return whether a new class was encountered
233
+ bool ci_lazy_methodst::handle_virtual_methods_with_no_callees (
234
+ std::unordered_set<irep_idt> &methods_to_convert_later,
235
+ std::unordered_set<irep_idt> &instantiated_classes,
236
+ const std::unordered_set<exprt, irep_hash> &virtual_function_calls,
237
+ symbol_tablet &symbol_table)
238
+ {
239
+ bool any_new_classes = false ;
240
+ for (const exprt &virtual_function_call : virtual_function_calls)
241
+ {
242
+ std::unordered_set<irep_idt> candidate_target_methods;
243
+ get_virtual_method_targets (
244
+ virtual_function_call,
245
+ instantiated_classes,
246
+ candidate_target_methods,
247
+ symbol_table);
248
+
249
+ if (!candidate_target_methods.empty ())
250
+ continue ;
251
+
252
+ // Add the call class to instantiated_classes and assert that it
253
+ // didn't already exist
254
+ const irep_idt &call_class = virtual_function_call.get (ID_C_class);
255
+ auto ret_class = instantiated_classes.insert (call_class);
256
+ CHECK_RETURN (ret_class.second );
257
+ any_new_classes = true ;
258
+
259
+ // Check that `get_virtual_method_target` returns a method now
260
+ const irep_idt &call_basename =
261
+ virtual_function_call.get (ID_component_name);
262
+ const irep_idt method_name = get_virtual_method_target (
263
+ instantiated_classes, call_basename, call_class, symbol_table);
264
+ CHECK_RETURN (!method_name.empty ());
265
+
266
+ // Add what it returns to methods_to_convert_later
267
+ methods_to_convert_later.insert (method_name);
268
+ }
269
+ return any_new_classes;
270
+ }
271
+
272
+ // / Convert a method, add it to the populated set, add needed methods to
273
+ // / methods_to_convert_later and add virtual calls from the method to
274
+ // / virtual_function_calls
275
+ // / \return structure containing two Booleans:
276
+ // / * class_initializer_seen which is true if the class_initializer_seen
277
+ // / argument was false and the class_model is referenced in
278
+ // / the body of the method
279
+ // / * new_method_seen if the method was not converted before and was
280
+ // / converted successfully here
281
+ ci_lazy_methodst::convert_method_resultt
282
+ ci_lazy_methodst::convert_and_analyze_method (
283
+ const method_convertert &method_converter,
284
+ std::unordered_set<irep_idt> &methods_already_populated,
285
+ const bool class_initializer_already_seen,
286
+ const irep_idt &method_name,
287
+ symbol_tablet &symbol_table,
288
+ std::unordered_set<irep_idt> &methods_to_convert_later,
289
+ std::unordered_set<irep_idt> &instantiated_classes,
290
+ std::unordered_set<exprt, irep_hash> &virtual_function_calls)
291
+ {
292
+ convert_method_resultt result;
293
+ if (!methods_already_populated.insert (method_name).second )
294
+ return result;
295
+
296
+ debug () << " CI lazy methods: elaborate " << method_name << eom;
297
+
298
+ // Note this wraps *references* to methods_to_convert_later &
299
+ // instantiated_classes
300
+ ci_lazy_methods_neededt needed_methods (
301
+ methods_to_convert_later, instantiated_classes, symbol_table);
302
+
303
+ const bool could_not_convert_function =
304
+ method_converter (method_name, needed_methods);
305
+ if (could_not_convert_function)
306
+ return result;
307
+
308
+ const exprt &method_body = symbol_table.lookup_ref (method_name).value ;
309
+ gather_virtual_callsites (method_body, virtual_function_calls);
310
+
311
+ if (!class_initializer_already_seen && references_class_model (method_body))
312
+ {
313
+ result.class_initializer_seen = true ;
314
+ const irep_idt initializer_signature =
315
+ get_java_class_literal_initializer_signature ();
316
+ if (symbol_table.has_symbol (initializer_signature))
317
+ methods_to_convert_later.insert (initializer_signature);
318
+ }
319
+ result.new_method_seen = true ;
320
+ return result;
321
+ }
322
+
323
+ // / Entry point methods are either:
324
+ // / * the "main" function of the `main_class` if it exists
325
+ // / * all the methods of the main class if it is not empty
326
+ // / * all the methods of the main jar file
327
+ // / \return set of identifiers of entry point methods
328
+ std::unordered_set<irep_idt>
329
+ ci_lazy_methodst::entry_point_methods (const symbol_tablet &symbol_table)
330
+ {
331
+ std::unordered_set<irep_idt> methods_to_convert_later;
332
+
333
+ const main_function_resultt main_function = get_main_symbol (
334
+ symbol_table, this ->main_class , this ->get_message_handler ());
335
+ if (!main_function.is_success ())
336
+ {
337
+ // Failed, mark all functions in the given main class(es)
338
+ // reachable.
339
+ std::vector<irep_idt> reachable_classes;
340
+ if (!this ->main_class .empty ())
341
+ reachable_classes.push_back (this ->main_class );
342
+ else
343
+ reachable_classes = this ->main_jar_classes ;
344
+ for (const irep_idt &class_name : reachable_classes)
345
+ {
346
+ const auto &methods =
347
+ this ->java_class_loader .get_original_class (class_name)
348
+ .parsed_class .methods ;
349
+ for (const auto &method : methods)
350
+ {
351
+ const irep_idt methodid = " java::" + id2string (class_name) + " ." +
352
+ id2string (method.name ) + " :" +
353
+ id2string (method.descriptor );
354
+ methods_to_convert_later.insert (methodid);
355
+ }
356
+ }
357
+ }
358
+ else
359
+ methods_to_convert_later.insert (main_function.main_function .name );
360
+ return methods_to_convert_later;
361
+ }
362
+
300
363
// / Translates the given list of method names from human-readable to
301
364
// / internal syntax.
302
365
// / Expands any wildcards (entries ending in '.*') in the given method
0 commit comments