Skip to content

Commit d437833

Browse files
Added an uncaught exceptions analysis
This analysis computes an overapproximation of the set of exceptions thrown by each method and it is used to prune some of the exception handling instrumentation introduced by remove_exceptions.cpp
1 parent a54d806 commit d437833

File tree

3 files changed

+470
-5
lines changed

3 files changed

+470
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
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_subtypes
17+
18+
Inputs:
19+
20+
Outputs:
21+
22+
Purpose: computes the set of subtypes of "type" by iterating over
23+
the symbol table
24+
\*******************************************************************/
25+
26+
static void get_subtypes(
27+
const irep_idt &type,
28+
std::set<irep_idt> &subtypes,
29+
const namespacet &ns)
30+
{
31+
irep_idt candidate;
32+
bool new_subtype=true;
33+
const symbol_tablet &symbol_table=ns.get_symbol_table();
34+
35+
// as we don't know the order types have been recorded,
36+
// we need to iterate over the symbol table until there are no more
37+
// new subtypes found
38+
while(new_subtype)
39+
{
40+
new_subtype=false;
41+
// iterate over the symbol_table in order to find subtypes of type
42+
forall_symbols(it, symbol_table.symbols)
43+
{
44+
// we only look at type entries
45+
if(it->second.is_type)
46+
{
47+
// every type is a potential candidate
48+
candidate=it->second.name;
49+
// current candidate is already in subtypes
50+
if(find(subtypes.begin(),
51+
subtypes.end(),
52+
candidate)!=subtypes.end())
53+
continue;
54+
// get its base class
55+
const irept::subt &bases=to_class_type((it->second).type).bases();
56+
if(bases.size()>0)
57+
{
58+
const irept &base = bases[0];
59+
const irept &base_type=base.find(ID_type);
60+
assert(base_type.id()==ID_symbol);
61+
62+
// is it derived from type?
63+
// i.e. does it have type or one of its subtypes as a base?
64+
if(base_type.get(ID_identifier)==type ||
65+
find(subtypes.begin(), subtypes.end(),
66+
base_type.get(ID_identifier))!=subtypes.end())
67+
{
68+
subtypes.insert(candidate);
69+
new_subtype=true;
70+
}
71+
}
72+
}
73+
}
74+
}
75+
}
76+
77+
/*******************************************************************\
78+
79+
Function: get_static_type
80+
81+
Inputs:
82+
83+
Outputs:
84+
85+
Purpose:
86+
87+
\*******************************************************************/
88+
89+
static irep_idt get_static_type(const typet &type)
90+
{
91+
if(type.id()==ID_pointer)
92+
{
93+
return get_static_type(type.subtype());
94+
}
95+
else if(type.id()==ID_symbol)
96+
{
97+
return to_symbol_type(type).get_identifier();
98+
}
99+
return ID_empty;
100+
}
101+
102+
/*******************************************************************\
103+
104+
Function: uncaught_exceptions_domaint::join
105+
106+
Inputs:
107+
108+
Outputs:
109+
110+
Purpose:
111+
112+
\*******************************************************************/
113+
114+
void uncaught_exceptions_domaint::join(
115+
const irep_idt &element)
116+
{
117+
thrown.insert(element);
118+
}
119+
120+
void uncaught_exceptions_domaint::join(
121+
const std::set<irep_idt> &elements)
122+
{
123+
thrown.insert(elements.begin(), elements.end());
124+
}
125+
126+
127+
/*******************************************************************\
128+
129+
Function: uncaught_exceptions_domaint::transform
130+
131+
Inputs:
132+
133+
Outputs:
134+
135+
Purpose:
136+
137+
\*******************************************************************/
138+
139+
void uncaught_exceptions_domaint::transform(
140+
const goto_programt::const_targett from,
141+
uncaught_exceptions_analysist &uea,
142+
const namespacet &ns)
143+
{
144+
const goto_programt::instructiont &instruction=*from;
145+
146+
switch(instruction.type)
147+
{
148+
case THROW:
149+
{
150+
const exprt &exc_symbol=instruction.code;
151+
152+
// retrieve the static type of the thrown exception
153+
const irep_idt &type_id=get_static_type(exc_symbol.type());
154+
join(type_id);
155+
// we must consider all the subtypes given that
156+
// the runtime type is a subtype of the static type
157+
std::set<irep_idt> subtypes;
158+
get_subtypes(type_id, subtypes, ns);
159+
join(subtypes);
160+
break;
161+
}
162+
case CATCH:
163+
{
164+
if(!instruction.code.has_operands())
165+
{
166+
if(!instruction.targets.empty()) // push
167+
{
168+
std::set<irep_idt> caught;
169+
stack_caught.push_back(caught);
170+
std::set<irep_idt> &last_caught=stack_caught.back();
171+
const irept::subt &exception_list=
172+
instruction.code.find(ID_exception_list).get_sub();
173+
174+
for(const auto &exc : exception_list)
175+
{
176+
last_caught.insert(exc.id());
177+
std::set<irep_idt> subtypes;
178+
get_subtypes(exc.id(), subtypes, ns);
179+
last_caught.insert(subtypes.begin(), subtypes.end());
180+
}
181+
}
182+
else // pop
183+
{
184+
if(!stack_caught.empty())
185+
{
186+
const std::set<irep_idt> &caught=stack_caught.back();
187+
join(caught);
188+
// remove the caught exceptions
189+
for(const auto &exc_id : caught)
190+
{
191+
const std::set<irep_idt>::iterator it=
192+
std::find(thrown.begin(), thrown.end(), exc_id);
193+
if(it!=thrown.end())
194+
thrown.erase(it);
195+
}
196+
stack_caught.pop_back();
197+
}
198+
}
199+
}
200+
break;
201+
}
202+
case FUNCTION_CALL:
203+
{
204+
const exprt &function_expr=
205+
to_code_function_call(instruction.code).function();
206+
assert(function_expr.id()==ID_symbol);
207+
const irep_idt &function_name=
208+
to_symbol_expr(function_expr).get_identifier();
209+
// use the current information about the callee
210+
join(uea.exceptions_map[function_name]);
211+
break;
212+
}
213+
default:
214+
{}
215+
}
216+
}
217+
218+
/*******************************************************************\
219+
220+
Function: uncaught_exceptions_domaint::get_elements
221+
222+
Inputs:
223+
224+
Outputs:
225+
226+
Purpose:
227+
228+
\*******************************************************************/
229+
230+
void uncaught_exceptions_domaint::get_elements(
231+
std::set<irep_idt> &elements)
232+
{
233+
elements=thrown;
234+
}
235+
236+
/*******************************************************************\
237+
238+
Function: uncaught_exceptions_analysist::collect_uncaught_exceptions
239+
240+
Inputs:
241+
242+
Outputs:
243+
244+
Purpose:
245+
246+
\*******************************************************************/
247+
248+
void uncaught_exceptions_analysist::collect_uncaught_exceptions(
249+
const goto_functionst &goto_functions,
250+
const namespacet &ns)
251+
{
252+
bool change=true;
253+
254+
while(change)
255+
{
256+
change=false;
257+
// add all the functions to the worklist
258+
forall_goto_functions(current_function, goto_functions)
259+
{
260+
domain.make_top();
261+
const goto_programt &goto_program=current_function->second.body;
262+
263+
if(goto_program.empty())
264+
continue;
265+
266+
forall_goto_program_instructions(instr_it, goto_program)
267+
{
268+
domain.transform(instr_it, *this, ns);
269+
}
270+
// did our estimation for the current function improve?
271+
std::set<irep_idt> elements;
272+
domain.get_elements(elements);
273+
change=change||
274+
exceptions_map[current_function->first].size()!=elements.size();
275+
exceptions_map[current_function->first]=elements;
276+
}
277+
}
278+
}
279+
280+
/*******************************************************************\
281+
282+
Function: uncaught_exceptions_analysist::output
283+
284+
Inputs:
285+
286+
Outputs:
287+
288+
Purpose:
289+
290+
\*******************************************************************/
291+
292+
void uncaught_exceptions_analysist::output(
293+
const goto_functionst &goto_functions)
294+
{
295+
#ifdef DEBUG
296+
forall_goto_functions(it, goto_functions)
297+
{
298+
if(exceptions_map[it->first].size()>0)
299+
{
300+
std::cout << "Uncaught exceptions in function " <<
301+
it->first << ": " << std::endl;
302+
assert(exceptions_map.find(it->first)!=exceptions_map.end());
303+
for(auto exc_id : exceptions_map[it->first])
304+
std::cout << id2string(exc_id) << " ";
305+
std::cout << std::endl;
306+
}
307+
}
308+
#endif
309+
}
310+
311+
/*******************************************************************\
312+
313+
Function: uncaught_exceptions_analysist::operator()
314+
315+
Inputs:
316+
317+
Outputs:
318+
319+
Purpose:
320+
321+
\*******************************************************************/
322+
323+
void uncaught_exceptions_analysist::operator()(
324+
const goto_functionst &goto_functions,
325+
const namespacet &ns,
326+
exceptions_mapt &exceptions)
327+
{
328+
collect_uncaught_exceptions(goto_functions, ns);
329+
exceptions=exceptions_map;
330+
output(goto_functions);
331+
}
332+
333+
/*******************************************************************\
334+
335+
Function: uncaught_exceptions
336+
337+
Inputs:
338+
339+
Outputs:
340+
341+
Purpose:
342+
343+
\*******************************************************************/
344+
345+
void uncaught_exceptions(
346+
const goto_functionst &goto_functions,
347+
const namespacet &ns,
348+
std::map<irep_idt, std::set<irep_idt>> &exceptions_map)
349+
{
350+
uncaught_exceptions_analysist exceptions;
351+
exceptions(goto_functions, ns, exceptions_map);
352+
}

0 commit comments

Comments
 (0)