Skip to content

Commit 32a4186

Browse files
Merge pull request #2302 from romainbrenguier/refactor/ci-lazy-methods
Extract some methods from ci lazy methods
2 parents 360fabe + c4aadab commit 32a4186

File tree

2 files changed

+187
-99
lines changed

2 files changed

+187
-99
lines changed

jbmc/src/java_bytecode/ci_lazy_methods.cpp

+162-99
Original file line numberDiff line numberDiff line change
@@ -98,34 +98,8 @@ bool ci_lazy_methodst::operator()(
9898
method_bytecodet &method_bytecode,
9999
const method_convertert &method_converter)
100100
{
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);
129103

130104
// Add any extra entry points specified; we should elaborate these in the
131105
// same way as the main function.
@@ -152,46 +126,30 @@ bool ci_lazy_methodst::operator()(
152126

153127
bool any_new_classes = true;
154128
while(any_new_classes)
155-
{bool any_new_methods = true;
156-
while(any_new_methods)
157129
{
158-
any_new_methods=false;
159-
while(!methods_to_convert_later.empty())
130+
bool any_new_methods = true;
131+
while(any_new_methods)
160132
{
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())
164135
{
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)
184139
{
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;
190151
}
191-
192-
any_new_methods=true;
193152
}
194-
}
195153

196154
// Given the object types we now know may be created, populate more
197155
// possible virtual function call targets:
@@ -209,43 +167,11 @@ bool ci_lazy_methodst::operator()(
209167
}
210168
}
211169

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);
249175
}
250176

251177
// cproverNondetInitialize has to be force-loaded for mocks to return valid
@@ -297,6 +223,143 @@ bool ci_lazy_methodst::operator()(
297223
return false;
298224
}
299225

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+
300363
/// Translates the given list of method names from human-readable to
301364
/// internal syntax.
302365
/// Expands any wildcards (entries ending in '.*') in the given method

jbmc/src/java_bytecode/ci_lazy_methods.h

+25
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,31 @@ class ci_lazy_methodst:public messaget
169169
const std::vector<irep_idt> &extra_instantiated_classes;
170170
const select_pointer_typet &pointer_type_selector;
171171
const synthetic_methods_mapt &synthetic_methods;
172+
173+
std::unordered_set<irep_idt>
174+
entry_point_methods(const symbol_tablet &symbol_table);
175+
176+
struct convert_method_resultt
177+
{
178+
bool class_initializer_seen = false;
179+
bool new_method_seen = false;
180+
};
181+
182+
convert_method_resultt convert_and_analyze_method(
183+
const method_convertert &method_converter,
184+
std::unordered_set<irep_idt> &methods_already_populated,
185+
const bool class_initializer_already_seen,
186+
const irep_idt &method_name,
187+
symbol_tablet &symbol_table,
188+
std::unordered_set<irep_idt> &methods_to_convert_later,
189+
std::unordered_set<irep_idt> &instantiated_classes,
190+
std::unordered_set<exprt, irep_hash> &virtual_function_calls);
191+
192+
bool handle_virtual_methods_with_no_callees(
193+
std::unordered_set<irep_idt> &methods_to_convert_later,
194+
std::unordered_set<irep_idt> &instantiated_classes,
195+
const std::unordered_set<exprt, irep_hash> &virtual_function_calls,
196+
symbol_tablet &symbol_table);
172197
};
173198

174199
#endif // CPROVER_JAVA_BYTECODE_GATHER_METHODS_LAZILY_H

0 commit comments

Comments
 (0)