Skip to content

Uncaught exceptions analysis #510

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions regression/cbmc-java/virtual7/test.desc
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ test.class
--show-goto-functions
^EXIT=0$
^SIGNAL=0$
IF "java::E".*THEN GOTO [67]
IF "java::B".*THEN GOTO [67]
IF "java::D".*THEN GOTO [67]
IF "java::C".*THEN GOTO [67]
IF "java::E".*THEN GOTO [12]
IF "java::B".*THEN GOTO [12]
IF "java::D".*THEN GOTO [12]
IF "java::C".*THEN GOTO [12]
--
IF "java::A".*THEN GOTO
1 change: 1 addition & 0 deletions src/analyses/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ SRC = natural_loops.cpp is_threaded.cpp dirty.cpp interval_analysis.cpp \
goto_rw.cpp reaching_definitions.cpp ai.cpp local_cfg.cpp \
local_bitvector_analysis.cpp dependence_graph.cpp \
constant_propagator.cpp replace_symbol_ext.cpp \
uncaught_exceptions_analysis.cpp \
flow_insensitive_analysis.cpp \
custom_bitvector_analysis.cpp escape_analysis.cpp global_may_alias.cpp

Expand Down
352 changes: 352 additions & 0 deletions src/analyses/uncaught_exceptions_analysis.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
/*******************************************************************\

Module: Over-approximating uncaught exceptions analysis

Author: Cristina David

\*******************************************************************/

#ifdef DEBUG
#include <iostream>
#endif
#include "uncaught_exceptions_analysis.h"

/*******************************************************************\

Function: get_subtypes

Inputs:

Outputs:

Purpose: computes the set of subtypes of "type" by iterating over
the symbol table
\*******************************************************************/

static void get_subtypes(
const irep_idt &type,
std::set<irep_idt> &subtypes,
const namespacet &ns)
{
irep_idt candidate;
bool new_subtype=true;
const symbol_tablet &symbol_table=ns.get_symbol_table();

// as we don't know the order types have been recorded,
// we need to iterate over the symbol table until there are no more
// new subtypes found
while(new_subtype)
{
new_subtype=false;
// iterate over the symbol_table in order to find subtypes of type
forall_symbols(it, symbol_table.symbols)
{
// we only look at type entries
if(it->second.is_type)
{
// every type is a potential candidate
candidate=it->second.name;
// current candidate is already in subtypes
if(find(subtypes.begin(),
subtypes.end(),
candidate)!=subtypes.end())
continue;
// get its base class
const irept::subt &bases=to_class_type((it->second).type).bases();
if(bases.size()>0)
{
const irept &base = bases[0];
const irept &base_type=base.find(ID_type);
assert(base_type.id()==ID_symbol);

// is it derived from type?
// i.e. does it have type or one of its subtypes as a base?
if(base_type.get(ID_identifier)==type ||
find(subtypes.begin(), subtypes.end(),
base_type.get(ID_identifier))!=subtypes.end())
{
subtypes.insert(candidate);
new_subtype=true;
}
}
}
}
}
}

/*******************************************************************\

Function: get_static_type

Inputs:

Outputs:

Purpose:

\*******************************************************************/

static irep_idt get_static_type(const typet &type)
{
if(type.id()==ID_pointer)
{
return get_static_type(type.subtype());
}
else if(type.id()==ID_symbol)
{
return to_symbol_type(type).get_identifier();
}
return ID_empty;
}

/*******************************************************************\

Function: uncaught_exceptions_domaint::join

Inputs:

Outputs:

Purpose:

\*******************************************************************/

void uncaught_exceptions_domaint::join(
const irep_idt &element)
{
thrown.insert(element);
}

void uncaught_exceptions_domaint::join(
const std::set<irep_idt> &elements)
{
thrown.insert(elements.begin(), elements.end());
}


/*******************************************************************\

Function: uncaught_exceptions_domaint::transform

Inputs:

Outputs:

Purpose:

\*******************************************************************/

void uncaught_exceptions_domaint::transform(
const goto_programt::const_targett from,
uncaught_exceptions_analysist &uea,
const namespacet &ns)
{
const goto_programt::instructiont &instruction=*from;

switch(instruction.type)
{
case THROW:
{
const exprt &exc_symbol=instruction.code;

// retrieve the static type of the thrown exception
const irep_idt &type_id=get_static_type(exc_symbol.type());
join(type_id);
// we must consider all the subtypes given that
// the runtime type is a subtype of the static type
std::set<irep_idt> subtypes;
get_subtypes(type_id, subtypes, ns);
join(subtypes);
break;
}
case CATCH:
{
if(!instruction.code.has_operands())
{
if(!instruction.targets.empty()) // push
{
std::set<irep_idt> caught;
stack_caught.push_back(caught);
std::set<irep_idt> &last_caught=stack_caught.back();
const irept::subt &exception_list=
instruction.code.find(ID_exception_list).get_sub();

for(const auto &exc : exception_list)
{
last_caught.insert(exc.id());
std::set<irep_idt> subtypes;
get_subtypes(exc.id(), subtypes, ns);
last_caught.insert(subtypes.begin(), subtypes.end());
}
}
else // pop
{
if(!stack_caught.empty())
{
const std::set<irep_idt> &caught=stack_caught.back();
join(caught);
// remove the caught exceptions
for(const auto &exc_id : caught)
{
const std::set<irep_idt>::iterator it=
std::find(thrown.begin(), thrown.end(), exc_id);
if(it!=thrown.end())
thrown.erase(it);
}
stack_caught.pop_back();
}
}
}
break;
}
case FUNCTION_CALL:
{
const exprt &function_expr=
to_code_function_call(instruction.code).function();
assert(function_expr.id()==ID_symbol);
const irep_idt &function_name=
to_symbol_expr(function_expr).get_identifier();
// use the current information about the callee
join(uea.exceptions_map[function_name]);
break;
}
default:
{}
}
}

/*******************************************************************\

Function: uncaught_exceptions_domaint::get_elements

Inputs:

Outputs:

Purpose:

\*******************************************************************/

void uncaught_exceptions_domaint::get_elements(
std::set<irep_idt> &elements)
{
elements=thrown;
}

/*******************************************************************\

Function: uncaught_exceptions_analysist::collect_uncaught_exceptions

Inputs:

Outputs:

Purpose:

\*******************************************************************/

void uncaught_exceptions_analysist::collect_uncaught_exceptions(
const goto_functionst &goto_functions,
const namespacet &ns)
{
bool change=true;

while(change)
{
change=false;
// add all the functions to the worklist
forall_goto_functions(current_function, goto_functions)
{
domain.make_top();
const goto_programt &goto_program=current_function->second.body;

if(goto_program.empty())
continue;

forall_goto_program_instructions(instr_it, goto_program)
{
domain.transform(instr_it, *this, ns);
}
// did our estimation for the current function improve?
std::set<irep_idt> elements;
domain.get_elements(elements);
change=change||
exceptions_map[current_function->first].size()!=elements.size();
exceptions_map[current_function->first]=elements;
}
}
}

/*******************************************************************\

Function: uncaught_exceptions_analysist::output

Inputs:

Outputs:

Purpose:

\*******************************************************************/

void uncaught_exceptions_analysist::output(
const goto_functionst &goto_functions)
{
#ifdef DEBUG
forall_goto_functions(it, goto_functions)
{
if(exceptions_map[it->first].size()>0)
{
std::cout << "Uncaught exceptions in function " <<
it->first << ": " << std::endl;
assert(exceptions_map.find(it->first)!=exceptions_map.end());
for(auto exc_id : exceptions_map[it->first])
std::cout << id2string(exc_id) << " ";
std::cout << std::endl;
}
}
#endif
}

/*******************************************************************\

Function: uncaught_exceptions_analysist::operator()

Inputs:

Outputs:

Purpose:

\*******************************************************************/

void uncaught_exceptions_analysist::operator()(
const goto_functionst &goto_functions,
const namespacet &ns,
exceptions_mapt &exceptions)
{
collect_uncaught_exceptions(goto_functions, ns);
exceptions=exceptions_map;
output(goto_functions);
}

/*******************************************************************\

Function: uncaught_exceptions

Inputs:

Outputs:

Purpose:

\*******************************************************************/

void uncaught_exceptions(
const goto_functionst &goto_functions,
const namespacet &ns,
std::map<irep_idt, std::set<irep_idt>> &exceptions_map)
{
uncaught_exceptions_analysist exceptions;
exceptions(goto_functions, ns, exceptions_map);
}
Loading