Skip to content

Commit c07cdf9

Browse files
authored
Merge pull request #940 from cristina-david/feature/uncaught-exceptions
Uncaught exceptions analysis
2 parents d0ec242 + f42cc33 commit c07cdf9

File tree

5 files changed

+524
-51
lines changed

5 files changed

+524
-51
lines changed

regression/cbmc-java/virtual7/test.desc

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ test.class
33
--show-goto-functions
44
^EXIT=0$
55
^SIGNAL=0$
6-
IF "java::E".*THEN GOTO [67]
7-
IF "java::B".*THEN GOTO [67]
8-
IF "java::D".*THEN GOTO [67]
9-
IF "java::C".*THEN GOTO [67]
6+
IF "java::E".*THEN GOTO [12]
7+
IF "java::B".*THEN GOTO [12]
8+
IF "java::D".*THEN GOTO [12]
9+
IF "java::C".*THEN GOTO [12]
1010
--
1111
IF "java::A".*THEN GOTO

src/analyses/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ SRC = ai.cpp \
2424
reaching_definitions.cpp \
2525
replace_symbol_ext.cpp \
2626
static_analysis.cpp \
27+
uncaught_exceptions_analysis.cpp \
2728
uninitialized_domain.cpp \
2829
# Empty last line
2930

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
/*******************************************************************\
2+
3+
Module: Over-approximating uncaught exceptions analysis
4+
5+
Author: Cristina David
6+
7+
\*******************************************************************/
8+
9+
#ifdef DEBUG
10+
#include <iostream>
11+
#endif
12+
#include "uncaught_exceptions_analysis.h"
13+
14+
/*******************************************************************\
15+
16+
Function: get_exception_type
17+
18+
Inputs:
19+
20+
Outputs:
21+
22+
Purpose: Returns the compile type of an exception
23+
24+
\*******************************************************************/
25+
26+
irep_idt uncaught_exceptions_domaint::get_exception_type(const typet &type)
27+
{
28+
assert(type.id()==ID_pointer);
29+
30+
if(type.subtype().id()==ID_symbol)
31+
{
32+
return to_symbol_type(type.subtype()).get_identifier();
33+
}
34+
return ID_empty;
35+
}
36+
37+
/*******************************************************************\
38+
39+
Function: get_exception_symbol
40+
41+
Inputs:
42+
43+
Outputs:
44+
45+
Purpose: Returns the symbol corresponding to an exception
46+
47+
\*******************************************************************/
48+
49+
exprt uncaught_exceptions_domaint::get_exception_symbol(const exprt &expr)
50+
{
51+
if(expr.id()!=ID_symbol && expr.has_operands())
52+
return get_exception_symbol(expr.op0());
53+
54+
return expr;
55+
}
56+
57+
/*******************************************************************\
58+
59+
Function: uncaught_exceptions_domaint::join
60+
61+
Inputs:
62+
63+
Outputs:
64+
65+
Purpose: The join operator for the uncaught exceptions domain
66+
67+
\*******************************************************************/
68+
69+
void uncaught_exceptions_domaint::join(
70+
const irep_idt &element)
71+
{
72+
thrown.insert(element);
73+
}
74+
75+
void uncaught_exceptions_domaint::join(
76+
const std::set<irep_idt> &elements)
77+
{
78+
thrown.insert(elements.begin(), elements.end());
79+
}
80+
81+
void uncaught_exceptions_domaint::join(
82+
const std::vector<irep_idt> &elements)
83+
{
84+
thrown.insert(elements.begin(), elements.end());
85+
}
86+
87+
88+
/*******************************************************************\
89+
90+
Function: uncaught_exceptions_domaint::transform
91+
92+
Inputs:
93+
94+
Outputs:
95+
96+
Purpose: The transformer for the uncaught exceptions domain
97+
98+
\*******************************************************************/
99+
100+
void uncaught_exceptions_domaint::transform(
101+
const goto_programt::const_targett from,
102+
uncaught_exceptions_analysist &uea,
103+
const namespacet &ns)
104+
{
105+
const goto_programt::instructiont &instruction=*from;
106+
107+
switch(instruction.type)
108+
{
109+
case THROW:
110+
{
111+
const exprt &exc_symbol=get_exception_symbol(instruction.code);
112+
// retrieve the static type of the thrown exception
113+
const irep_idt &type_id=get_exception_type(exc_symbol.type());
114+
bool assertion_error=
115+
id2string(type_id).find("java.lang.AssertionError")!=std::string::npos;
116+
if(!assertion_error)
117+
{
118+
join(type_id);
119+
// we must consider all the subtypes given that
120+
// the runtime type is a subtype of the static type
121+
std::vector<irep_idt> subtypes=
122+
class_hierarchy.get_children_trans(type_id);
123+
join(subtypes);
124+
}
125+
break;
126+
}
127+
case CATCH:
128+
{
129+
if(!instruction.code.has_operands())
130+
{
131+
if(!instruction.targets.empty()) // push
132+
{
133+
std::set<irep_idt> caught;
134+
stack_caught.push_back(caught);
135+
std::set<irep_idt> &last_caught=stack_caught.back();
136+
const irept::subt &exception_list=
137+
instruction.code.find(ID_exception_list).get_sub();
138+
139+
for(const auto &exc : exception_list)
140+
{
141+
last_caught.insert(exc.id());
142+
std::vector<irep_idt> subtypes=
143+
class_hierarchy.get_children_trans(exc.id());
144+
last_caught.insert(subtypes.begin(), subtypes.end());
145+
}
146+
}
147+
else // pop
148+
{
149+
if(!stack_caught.empty())
150+
{
151+
const std::set<irep_idt> &caught=stack_caught.back();
152+
join(caught);
153+
// remove the caught exceptions
154+
for(const auto &exc_id : caught)
155+
thrown.erase(exc_id);
156+
stack_caught.pop_back();
157+
}
158+
}
159+
}
160+
break;
161+
}
162+
case FUNCTION_CALL:
163+
{
164+
const exprt &function_expr=
165+
to_code_function_call(instruction.code).function();
166+
assert(function_expr.id()==ID_symbol);
167+
const irep_idt &function_name=
168+
to_symbol_expr(function_expr).get_identifier();
169+
// use the current information about the callee
170+
join(uea.exceptions_map[function_name]);
171+
break;
172+
}
173+
default:
174+
{}
175+
}
176+
}
177+
178+
/*******************************************************************\
179+
180+
Function: uncaught_exceptions_domaint::get_elements
181+
182+
Inputs:
183+
184+
Outputs:
185+
186+
Purpose: Returns the value of the private member thrown
187+
188+
\*******************************************************************/
189+
190+
const std::set<irep_idt> &uncaught_exceptions_domaint::get_elements() const
191+
{
192+
return thrown;
193+
}
194+
195+
/*******************************************************************\
196+
197+
Function: uncaught_exceptions_domaint::operator()
198+
199+
Inputs:
200+
201+
Outputs:
202+
203+
Purpose: Constructs the class hierarchy
204+
205+
\*******************************************************************/
206+
207+
void uncaught_exceptions_domaint::operator()(
208+
const namespacet &ns)
209+
{
210+
class_hierarchy(ns.get_symbol_table());
211+
}
212+
213+
/*******************************************************************\
214+
215+
Function: uncaught_exceptions_analysist::collect_uncaught_exceptions
216+
217+
Inputs:
218+
219+
Outputs:
220+
221+
Purpose: Runs the uncaught exceptions analysis, which
222+
populates the exceptions map
223+
224+
\*******************************************************************/
225+
226+
void uncaught_exceptions_analysist::collect_uncaught_exceptions(
227+
const goto_functionst &goto_functions,
228+
const namespacet &ns)
229+
{
230+
bool change=true;
231+
232+
while(change)
233+
{
234+
change=false;
235+
// add all the functions to the worklist
236+
forall_goto_functions(current_function, goto_functions)
237+
{
238+
domain.make_top();
239+
const goto_programt &goto_program=current_function->second.body;
240+
241+
if(goto_program.empty())
242+
continue;
243+
244+
forall_goto_program_instructions(instr_it, goto_program)
245+
{
246+
domain.transform(instr_it, *this, ns);
247+
}
248+
// did our estimation for the current function improve?
249+
const std::set<irep_idt> &elements=domain.get_elements();
250+
if(exceptions_map[current_function->first].size()<elements.size())
251+
{
252+
change=true;
253+
exceptions_map[current_function->first]=elements;
254+
}
255+
}
256+
}
257+
}
258+
259+
/*******************************************************************\
260+
261+
Function: uncaught_exceptions_analysist::output
262+
263+
Inputs:
264+
265+
Outputs:
266+
267+
Purpose: Prints the exceptions map that maps each method to the
268+
set of exceptions that may escape it
269+
270+
\*******************************************************************/
271+
272+
void uncaught_exceptions_analysist::output(
273+
const goto_functionst &goto_functions)
274+
{
275+
#ifdef DEBUG
276+
forall_goto_functions(it, goto_functions)
277+
{
278+
if(exceptions_map[it->first].size()>0)
279+
{
280+
std::cout << "Uncaught exceptions in function " <<
281+
it->first << ": " << std::endl;
282+
assert(exceptions_map.find(it->first)!=exceptions_map.end());
283+
for(auto exc_id : exceptions_map[it->first])
284+
std::cout << id2string(exc_id) << " ";
285+
std::cout << std::endl;
286+
}
287+
}
288+
#endif
289+
}
290+
291+
/*******************************************************************\
292+
293+
Function: uncaught_exceptions_analysist::operator()
294+
295+
Inputs:
296+
297+
Outputs:
298+
299+
Purpose: Applies the uncaught exceptions analysis and
300+
outputs the result
301+
302+
\*******************************************************************/
303+
304+
void uncaught_exceptions_analysist::operator()(
305+
const goto_functionst &goto_functions,
306+
const namespacet &ns,
307+
exceptions_mapt &exceptions)
308+
{
309+
domain(ns);
310+
collect_uncaught_exceptions(goto_functions, ns);
311+
exceptions=exceptions_map;
312+
output(goto_functions);
313+
}
314+
315+
/*******************************************************************\
316+
317+
Function: uncaught_exceptions
318+
319+
Inputs:
320+
321+
Outputs:
322+
323+
Purpose: Applies the uncaught exceptions analysis and
324+
outputs the result
325+
326+
\*******************************************************************/
327+
328+
void uncaught_exceptions(
329+
const goto_functionst &goto_functions,
330+
const namespacet &ns,
331+
std::map<irep_idt, std::set<irep_idt>> &exceptions_map)
332+
{
333+
uncaught_exceptions_analysist exceptions;
334+
exceptions(goto_functions, ns, exceptions_map);
335+
}

0 commit comments

Comments
 (0)