Skip to content

Commit f488acd

Browse files
author
Daniel Kroening
authored
Merge pull request #393 from smowton/improve_remove_virtual_functions_master
Improve remove virtual functions
2 parents be555a2 + 065bfb9 commit f488acd

File tree

6 files changed

+175
-37
lines changed

6 files changed

+175
-37
lines changed

regression/cbmc-java/virtual6/A.class

485 Bytes
Binary file not shown.

regression/cbmc-java/virtual6/A.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
public class A {
3+
4+
int f() {
5+
return 1;
6+
}
7+
8+
public void main(int unknown) {
9+
10+
A a = new A();
11+
B b = new B();
12+
C c = new C();
13+
A callee;
14+
switch(unknown) {
15+
case 1:
16+
callee = a;
17+
break;
18+
case 2:
19+
callee = b;
20+
break;
21+
default:
22+
callee = c;
23+
break;
24+
}
25+
26+
callee.f();
27+
28+
}
29+
30+
}
31+
32+
class B extends A {
33+
34+
int f() {
35+
return 2;
36+
}
37+
38+
}
39+
40+
class C extends B {}

regression/cbmc-java/virtual6/B.class

211 Bytes
Binary file not shown.

regression/cbmc-java/virtual6/C.class

161 Bytes
Binary file not shown.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
A.class
3+
--function A.main --show-goto-functions
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
IF "java::C".*THEN GOTO
7+
IF "java::B".*THEN GOTO
8+
IF "java::A".*THEN GOTO

src/goto-programs/remove_virtual_functions.cpp

Lines changed: 127 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Author: Daniel Kroening, [email protected]
77
\*******************************************************************/
88

99
#include <util/prefix.h>
10+
#include <util/type_eq.h>
1011

1112
#include "class_hierarchy.h"
1213
#include "remove_virtual_functions.h"
@@ -43,13 +44,23 @@ class remove_virtual_functionst
4344
class functiont
4445
{
4546
public:
47+
functiont() {}
48+
explicit functiont(const irep_idt& _class_id) :
49+
class_id(_class_id)
50+
{}
51+
4652
symbol_exprt symbol_expr;
4753
irep_idt class_id;
4854
};
4955

5056
typedef std::vector<functiont> functionst;
5157
void get_functions(const exprt &, functionst &);
52-
exprt get_method(const irep_idt &class_id, const irep_idt &component_name);
58+
void get_child_functions_rec(
59+
const irep_idt &, const symbol_exprt &,
60+
const irep_idt &, functionst &) const;
61+
exprt get_method(
62+
const irep_idt &class_id,
63+
const irep_idt &component_name) const;
5364

5465
exprt build_class_identifier(const exprt &);
5566
};
@@ -170,33 +181,60 @@ void remove_virtual_functionst::remove_virtual_function(
170181
goto_programt new_code_calls;
171182
goto_programt new_code_gotos;
172183

184+
// Get a pointer from which we can extract a clsid.
185+
// If it's already a pointer to an object of some sort, just use it;
186+
// if it's void* then use the parent of all possible candidates,
187+
// which by the nature of get_functions happens to be the last candidate.
188+
189+
exprt this_expr=code.arguments()[0];
190+
assert(this_expr.type().id()==ID_pointer &&
191+
"Non-pointer this-arg in remove-virtuals?");
192+
const auto &points_to=this_expr.type().subtype();
193+
if(points_to==empty_typet())
194+
{
195+
symbol_typet symbol_type(functions.back().class_id);
196+
this_expr=typecast_exprt(this_expr, pointer_typet(symbol_type));
197+
}
198+
exprt deref=dereference_exprt(this_expr, this_expr.type().subtype());
199+
exprt c_id2=build_class_identifier(deref);
200+
201+
goto_programt::targett last_function;
173202
for(const auto &fun : functions)
174203
{
175-
// call function
176204
goto_programt::targett t1=new_code_calls.add_instruction();
177-
t1->make_function_call(code);
178-
to_code_function_call(t1->code).function()=fun.symbol_expr;
205+
if(!fun.symbol_expr.get_identifier().empty())
206+
{
207+
// call function
208+
t1->make_function_call(code);
209+
auto &newcall=to_code_function_call(t1->code);
210+
newcall.function()=fun.symbol_expr;
211+
pointer_typet need_type(symbol_typet(fun.symbol_expr.get(ID_C_class)));
212+
if(!type_eq(newcall.arguments()[0].type(), need_type, ns))
213+
newcall.arguments()[0].make_typecast(need_type);
214+
}
215+
else
216+
{
217+
// No definition for this type; shouldn't be possible...
218+
t1->make_assertion(false_exprt());
219+
}
220+
221+
last_function=t1;
179222

180223
// goto final
181224
goto_programt::targett t3=new_code_calls.add_instruction();
182225
t3->make_goto(t_final, true_exprt());
183226

184-
exprt this_expr=code.arguments()[0];
185-
if(this_expr.type().id()!=ID_pointer ||
186-
this_expr.type().id()!=ID_struct)
187-
{
188-
symbol_typet symbol_type(fun.class_id);
189-
this_expr=typecast_exprt(this_expr, pointer_typet(symbol_type));
190-
}
191-
192-
exprt deref=dereference_exprt(this_expr, this_expr.type().subtype());
193227
exprt c_id1=constant_exprt(fun.class_id, string_typet());
194-
exprt c_id2=build_class_identifier(deref);
195228

196229
goto_programt::targett t4=new_code_gotos.add_instruction();
197230
t4->make_goto(t1, equal_exprt(c_id1, c_id2));
198231
}
199232

233+
// In any other case (most likely a stub class) call the most basic
234+
// version of the method we know to exist:
235+
goto_programt::targett fallthrough=new_code_gotos.add_instruction();
236+
fallthrough->make_goto(last_function);
237+
200238
goto_programt new_code;
201239

202240
// patch them all together
@@ -226,6 +264,61 @@ void remove_virtual_functionst::remove_virtual_function(
226264

227265
/*******************************************************************\
228266
267+
Function: remove_virtual_functionst::get_child_functions_rec
268+
269+
Inputs: `this_id`: class name
270+
`last_method_defn`: the most-derived parent of `this_id`
271+
to define the requested function
272+
`component_name`: name of the function searched for
273+
274+
Outputs: `functions` is assigned a list of {class name, function symbol}
275+
pairs indicating that if `this` is of the given class, then the
276+
call will target the given function. Thus if A <: B <: C and A
277+
and C provide overrides of `f` (but B does not),
278+
get_child_functions_rec("C", C.f, "f") -> [{"C", C.f},
279+
{"B", C.f},
280+
{"A", A.f}]
281+
282+
Purpose: Used by get_functions to track the most-derived parent that
283+
provides an override of a given function.
284+
285+
\*******************************************************************/
286+
287+
void remove_virtual_functionst::get_child_functions_rec(
288+
const irep_idt &this_id,
289+
const symbol_exprt &last_method_defn,
290+
const irep_idt &component_name,
291+
functionst &functions) const
292+
{
293+
auto findit=class_hierarchy.class_map.find(this_id);
294+
if(findit==class_hierarchy.class_map.end())
295+
return;
296+
297+
for(const auto & child : findit->second.children)
298+
{
299+
exprt method=get_method(child, component_name);
300+
functiont function(child);
301+
if(method.is_not_nil())
302+
{
303+
function.symbol_expr=to_symbol_expr(method);
304+
function.symbol_expr.set(ID_C_class, child);
305+
}
306+
else
307+
{
308+
function.symbol_expr=last_method_defn;
309+
}
310+
functions.push_back(function);
311+
312+
get_child_functions_rec(
313+
child,
314+
function.symbol_expr,
315+
component_name,
316+
functions);
317+
}
318+
}
319+
320+
/*******************************************************************\
321+
229322
Function: remove_virtual_functionst::get_functions
230323
231324
Inputs:
@@ -243,23 +336,7 @@ void remove_virtual_functionst::get_functions(
243336
const irep_idt class_id=function.get(ID_C_class);
244337
const irep_idt component_name=function.get(ID_component_name);
245338
assert(!class_id.empty());
246-
247-
// iterate over all children, transitively
248-
std::vector<irep_idt> children=
249-
class_hierarchy.get_children_trans(class_id);
250-
251-
for(const auto &child : children)
252-
{
253-
exprt method=get_method(child, component_name);
254-
if(method.is_not_nil())
255-
{
256-
functiont function;
257-
function.class_id=child;
258-
function.symbol_expr=to_symbol_expr(method);
259-
function.symbol_expr.set(ID_C_class, child);
260-
functions.push_back(function);
261-
}
262-
}
339+
functiont root_function;
263340

264341
// Start from current class, go to parents until something
265342
// is found.
@@ -269,11 +346,9 @@ void remove_virtual_functionst::get_functions(
269346
exprt method=get_method(c, component_name);
270347
if(method.is_not_nil())
271348
{
272-
functiont function;
273-
function.class_id=c;
274-
function.symbol_expr=to_symbol_expr(method);
275-
function.symbol_expr.set(ID_C_class, c);
276-
functions.push_back(function);
349+
root_function.class_id=c;
350+
root_function.symbol_expr=to_symbol_expr(method);
351+
root_function.symbol_expr.set(ID_C_class, c);
277352
break; // abort
278353
}
279354

@@ -283,6 +358,21 @@ void remove_virtual_functionst::get_functions(
283358
if(parents.empty()) break;
284359
c=parents.front();
285360
}
361+
362+
if(root_function.class_id.empty())
363+
{
364+
// No definition here; this is an abstract function.
365+
root_function.class_id=class_id;
366+
}
367+
368+
// iterate over all children, transitively
369+
get_child_functions_rec(
370+
class_id,
371+
root_function.symbol_expr,
372+
component_name,
373+
functions);
374+
375+
functions.push_back(root_function);
286376
}
287377

288378
/*******************************************************************\
@@ -299,7 +389,7 @@ Function: remove_virtual_functionst::get_method
299389

300390
exprt remove_virtual_functionst::get_method(
301391
const irep_idt &class_id,
302-
const irep_idt &component_name)
392+
const irep_idt &component_name) const
303393
{
304394
irep_idt id=id2string(class_id)+"."+
305395
id2string(component_name);

0 commit comments

Comments
 (0)