Skip to content

Commit 696c139

Browse files
author
thk123
committed
Pulled out logic for finding the correct resolution for a given virtual call
When calling method f on a class A, if class A implements it, A::f is called. If it doesn't then we should work up the class hierachy till we find a class that fills in the gap. This was separately implemented in different locations with varying levels of accuracy. This pulls all three into a common class responsible for this.
1 parent 2520c51 commit 696c139

7 files changed

+245
-85
lines changed

src/goto-programs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ SRC = basic_blocks.cpp \
4949
remove_vector.cpp \
5050
remove_virtual_functions.cpp \
5151
replace_java_nondet.cpp \
52+
resolve_concrete_function_call.cpp \
5253
safety_checker.cpp \
5354
set_properties.cpp \
5455
show_goto_functions.cpp \

src/goto-programs/remove_virtual_functions.cpp

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Author: Daniel Kroening, [email protected]
1313
#include "class_hierarchy.h"
1414
#include "class_identifier.h"
1515

16+
#include <goto-programs/resolve_concrete_function_call.h>
17+
1618
#include <util/c_types.h>
1719
#include <util/prefix.h>
1820
#include <util/type_eq.h>
@@ -258,32 +260,26 @@ void remove_virtual_functionst::get_functions(
258260
{
259261
const irep_idt class_id=function.get(ID_C_class);
260262
const irep_idt component_name=function.get(ID_component_name);
261-
assert(!class_id.empty());
263+
INVARIANT(!class_id.empty(), "All virtual functions must have a class");
264+
265+
resolve_concrete_function_callt get_virtual_call_target(
266+
symbol_table, class_hierarchy);
267+
const resolve_concrete_function_callt::concrete_function_callt &
268+
resolved_call=get_virtual_call_target(class_id, component_name);
262269
functiont root_function;
263270

264-
// Start from current class, go to parents until something
265-
// is found.
266-
irep_idt c=class_id;
267-
while(!c.empty())
271+
if(resolved_call.is_valid())
268272
{
269-
exprt method=get_method(c, component_name);
270-
if(method.is_not_nil())
271-
{
272-
root_function.class_id=c;
273-
root_function.symbol_expr=to_symbol_expr(method);
274-
root_function.symbol_expr.set(ID_C_class, c);
275-
break; // abort
276-
}
273+
root_function.class_id=resolved_call.get_class_identifier();
277274

278-
const class_hierarchyt::idst &parents=
279-
class_hierarchy.class_map[c].parents;
275+
const symbolt &called_symbol=
276+
symbol_table.lookup(resolved_call.get_virtual_method_name());
280277

281-
if(parents.empty())
282-
break;
283-
c=parents.front();
278+
root_function.symbol_expr=called_symbol.symbol_expr();
279+
root_function.symbol_expr.set(
280+
ID_C_class, resolved_call.get_class_identifier());
284281
}
285-
286-
if(root_function.class_id.empty())
282+
else
287283
{
288284
// No definition here; this is an abstract function.
289285
root_function.class_id=class_id;
@@ -306,8 +302,9 @@ exprt remove_virtual_functionst::get_method(
306302
const irep_idt &class_id,
307303
const irep_idt &component_name) const
308304
{
309-
irep_idt id=id2string(class_id)+"."+
310-
id2string(component_name);
305+
const irep_idt &id=
306+
resolve_concrete_function_callt::build_virtual_method_name(
307+
class_id, component_name);
311308

312309
const symbolt *symbol;
313310
if(ns.lookup(id, symbol))
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*******************************************************************\
2+
3+
Module: GOTO Program Utilities
4+
5+
Author: DiffBlue Limited. All rights reserved.
6+
7+
\*******************************************************************/
8+
9+
#include <algorithm>
10+
11+
#include "resolve_concrete_function_call.h"
12+
13+
/// See the operator() method comment.
14+
/// \param symbol_table: The symbol table to resolve the function call in
15+
resolve_concrete_function_callt::resolve_concrete_function_callt(
16+
const symbol_tablet &symbol_table):
17+
symbol_table(symbol_table)
18+
{
19+
class_hierarchy(symbol_table);
20+
}
21+
22+
/// See the operator() method comment
23+
/// \param symbol_table: The symbol table to resolve the function call in
24+
/// \param class_hierarchy: A prebuilt class_hierachy based on the symbol_table
25+
///
26+
resolve_concrete_function_callt::resolve_concrete_function_callt(
27+
const symbol_tablet &symbol_table, const class_hierarchyt &class_hierarchy):
28+
class_hierarchy(class_hierarchy),
29+
symbol_table(symbol_table)
30+
{
31+
// We require the class_hierarchy to be already populated if we are being
32+
// supplied it.
33+
PRECONDITION(!class_hierarchy.class_map.empty());
34+
}
35+
36+
/// Given a virtual function call, identify what concrete call this would be
37+
/// resolved to. For example, calling a method on a class will call its
38+
/// implementions if it exists, else will call the first parent that provides an
39+
/// implementation. If none are found, an empty string will be returned.
40+
/// \param class_id: The name of the class the function is being called on
41+
/// \param component_name: The base name of the function (e.g. without the
42+
/// class specifier
43+
/// \return The concrete call that has been resolved
44+
resolve_concrete_function_callt::concrete_function_callt
45+
resolve_concrete_function_callt::operator()(
46+
const irep_idt &class_id, const irep_idt &component_name)
47+
{
48+
PRECONDITION(!class_id.empty());
49+
PRECONDITION(!component_name.empty());
50+
51+
irep_idt current_class=class_id;
52+
while(!current_class.empty())
53+
{
54+
const irep_idt &virtual_method_name=
55+
build_virtual_method_name(current_class, component_name);
56+
57+
if(symbol_table.has_symbol(virtual_method_name))
58+
{
59+
return concrete_function_callt(current_class, component_name);
60+
}
61+
62+
const class_hierarchyt::idst &parents=
63+
class_hierarchy.class_map[current_class].parents;
64+
65+
if(parents.empty())
66+
break;
67+
current_class=parents.front();
68+
}
69+
70+
return concrete_function_callt();
71+
}
72+
73+
/// Build a method name as found in a GOTO symbol table equivalent to the name
74+
/// of a concrete call of method component_method_name on class class_name
75+
/// \param component_method_name: The name of the function
76+
/// \param class_name: The class the implementation would be found on.
77+
/// \return A name for looking up in the symbol table for classes `class_name`'s
78+
/// implementation of `component_name`
79+
irep_idt resolve_concrete_function_callt::build_virtual_method_name(
80+
const irep_idt &class_name, const irep_idt &component_method_name)
81+
{
82+
// Verify the parameters are called in the correct order.
83+
PRECONDITION(id2string(class_name).find("::")!=std::string::npos);
84+
PRECONDITION(id2string(component_method_name).find("(")!=std::string::npos);
85+
return id2string(class_name)+'.'+id2string(component_method_name);
86+
}
87+
88+
/// Get the full name of this function
89+
/// \return The symbol name for this function call
90+
irep_idt resolve_concrete_function_callt::concrete_function_callt::
91+
get_virtual_method_name() const
92+
{
93+
return resolve_concrete_function_callt::build_virtual_method_name(
94+
class_identifier, function_identifier);
95+
}
96+
97+
/// Use to check if this concrete_function_callt has been fully constructed.
98+
/// \return True if this represents a real concrete function call
99+
bool resolve_concrete_function_callt::concrete_function_callt::is_valid() const
100+
{
101+
return !class_identifier.empty();
102+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*******************************************************************\
2+
3+
Module: GOTO Program Utilities
4+
5+
Author: DiffBlue Limited. All rights reserved.
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Given a virtual function call on a specific class, find which implementation
11+
/// needs to be used.
12+
13+
#ifndef CPROVER_GOTO_PROGRAMS_RESOLVE_CONCRETE_FUNCTION_CALL_H
14+
#define CPROVER_GOTO_PROGRAMS_RESOLVE_CONCRETE_FUNCTION_CALL_H
15+
16+
#include <util/symbol_table.h>
17+
#include <util/std_expr.h>
18+
#include <util/std_code.h>
19+
#include <goto-programs/class_hierarchy.h>
20+
21+
class resolve_concrete_function_callt
22+
{
23+
public:
24+
explicit resolve_concrete_function_callt(const symbol_tablet &symbol_table);
25+
resolve_concrete_function_callt(
26+
const symbol_tablet &symbol_table, const class_hierarchyt &class_hierarchy);
27+
28+
class concrete_function_callt
29+
{
30+
public:
31+
concrete_function_callt()
32+
{}
33+
34+
concrete_function_callt(
35+
const irep_idt &class_id, const irep_idt &method_id):
36+
class_identifier(class_id),
37+
function_identifier(method_id)
38+
{}
39+
40+
irep_idt get_virtual_method_name() const;
41+
42+
irep_idt get_class_identifier() const
43+
{
44+
return class_identifier;
45+
}
46+
47+
irep_idt get_function_identifier() const
48+
{
49+
return function_identifier;
50+
}
51+
52+
bool is_valid() const;
53+
54+
private:
55+
irep_idt class_identifier;
56+
irep_idt function_identifier;
57+
};
58+
59+
concrete_function_callt operator()(
60+
const irep_idt &class_id, const irep_idt &component_name);
61+
62+
static irep_idt build_virtual_method_name(
63+
const irep_idt &class_name, const irep_idt &component_method_name);
64+
65+
private:
66+
bool does_implementation_exist(
67+
const irep_idt &class_name,
68+
const irep_idt &component_method_name,
69+
const irep_idt &calling_class_name);
70+
71+
class_hierarchyt class_hierarchy;
72+
const symbol_tablet &symbol_table;
73+
};
74+
75+
#endif // CPROVER_GOTO_PROGRAMS_RESOLVE_CONCRETE_FUNCTION_CALL_H

src/java_bytecode/gather_methods_lazily.cpp

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <util/suffix.h>
1616
#include <java_bytecode/java_string_library_preprocess.h>
1717

18+
#include <goto-programs/resolve_concrete_function_call.h>
19+
1820

1921
gather_methods_lazilyt::gather_methods_lazilyt(
2022
const symbol_tablet &symbol_table,
@@ -468,41 +470,14 @@ irep_idt gather_methods_lazilyt::get_virtual_method_target(
468470
// Program-wide, is this class ever instantiated?
469471
if(!needed_classes.count(classname))
470472
return irep_idt();
471-
auto methodid=build_virtual_method_name(classname, call_basename);
472-
if(symbol_table.has_symbol(methodid))
473-
return methodid;
473+
474+
resolve_concrete_function_callt call_resolver(symbol_table, class_hierarchy);
475+
const resolve_concrete_function_callt ::concrete_function_callt &
476+
resolved_call=call_resolver(classname, call_basename);
477+
478+
if(resolved_call.is_valid())
479+
return resolved_call.get_virtual_method_name();
474480
else
475-
{
476-
// no method found for this specific classs, but a call to this class
477-
// will be resolved in one of its base classes so we should work up the
478-
// heirarchy to see if one resovles
479-
class_hierarchyt::idst parent_classes=
480-
class_hierarchy.get_parents_trans(classname);
481-
for(const irep_idt &parent_class_id : parent_classes)
482-
{
483-
auto parent_method_id=
484-
build_virtual_method_name(parent_class_id, call_basename);
485-
if(symbol_table.has_symbol(parent_method_id))
486-
{
487-
return parent_method_id;
488-
}
489-
}
490481
return irep_idt();
491-
}
492482
}
493483

494-
495-
/// Build a method name as found in a GOTO symbol table equivalent to the name
496-
/// of a concrete call of method component_method_name on class class_name
497-
/// \param component_method_name: The name of the function
498-
/// \param class_name: The class the implementation would be found on.
499-
/// \return A name for looking up in the symbol table for classes `class_name`'s
500-
/// implementation of `component_name`
501-
irep_idt gather_methods_lazilyt::build_virtual_method_name(
502-
const irep_idt &class_name, const irep_idt &component_method_name)
503-
{
504-
// Verify the parameters are called in the correct order.
505-
PRECONDITION(id2string(class_name).find("::")!=std::string::npos);
506-
PRECONDITION(id2string(component_method_name).find("(")!=std::string::npos);
507-
return id2string(class_name)+'.'+id2string(component_method_name);
508-
}

0 commit comments

Comments
 (0)