CBMC
Loading...
Searching...
No Matches
remove_exceptions.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Remove exception handling
4
5Author: Cristina David
6
7Date: December 2016
8
9\*******************************************************************/
10
13
14#include "remove_exceptions.h"
15#include "remove_instanceof.h"
16
17#ifdef DEBUG
18#include <iostream>
19#endif
20
21#include <util/c_types.h>
22#include <util/pointer_expr.h>
23#include <util/std_code.h>
24
27
29
31
32#include "java_expr.h"
33#include "java_types.h"
34
82{
83 typedef std::vector<std::pair<
85 typedef std::vector<catch_handlerst> stack_catcht;
86
87public:
88 typedef std::function<bool(const irep_idt &)> function_may_throwt;
89
101 {
103 {
104 INVARIANT(
105 class_hierarchy != nullptr,
106 "remove_exceptions needs a class hierarchy to remove instanceof "
107 "statements (either supply one, or don't use REMOVE_ADDED_INSTANCEOF)");
108 }
109 }
110
111 void operator()(goto_functionst &goto_functions);
112 void
113 operator()(const irep_idt &function_identifier, goto_programt &goto_program);
114
115protected:
121
128
130
132
134 goto_programt &goto_program,
136 bool may_catch);
137
140 goto_programt &goto_program,
141 std::size_t &universal_try,
142 std::size_t &universal_catch);
143
145 const irep_idt &function_identifier,
146 goto_programt &goto_program,
149 const std::vector<symbol_exprt> &locals);
150
151 bool instrument_throw(
152 const irep_idt &function_identifier,
153 goto_programt &goto_program,
155 const stack_catcht &,
156 const std::vector<symbol_exprt> &);
157
159 const irep_idt &function_identifier,
160 goto_programt &goto_program,
162 const stack_catcht &,
163 const std::vector<symbol_exprt> &);
164
166 const irep_idt &function_identifier,
167 goto_programt &goto_program);
168};
169
173{
174 const symbolt *existing_symbol =
176 INVARIANT(
177 existing_symbol != nullptr,
178 "Java frontend should have created @inflight_exception variable");
179 return existing_symbol->symbol_expr();
180}
181
188 const goto_programt &goto_program) const
189{
190 for(const auto &instruction : goto_program.instructions)
191 {
192 if(instruction.is_throw())
193 {
194 return true;
195 }
196
197 if(instruction.is_function_call())
198 {
199 const exprt &function_expr = instruction.call_function();
202 "identifier expected to be a symbol");
203 const irep_idt &function_name=
204 to_symbol_expr(function_expr).get_identifier();
205 if(function_may_throw(function_name))
206 return true;
207 }
208 }
209
210 return false;
211}
212
224 goto_programt &goto_program,
226 bool may_catch)
227{
228 PRECONDITION(instr_it->type() == CATCH);
229
230 if(may_catch)
231 {
232 // retrieve the exception variable
234 to_code_landingpad(instr_it->code()).catch_expr();
235
238 // next we reset the exceptional return to NULL
240
241 // add the assignment @inflight_exception = NULL
242 goto_program.insert_after(
243 instr_it,
246 instr_it->source_location()));
247
248 // add the assignment exc = @inflight_exception (before the null assignment)
249 goto_program.insert_after(
250 instr_it,
255 instr_it->source_location()));
256 }
257
258 instr_it->turn_into_skip();
259}
260
284 goto_programt &goto_program,
285 std::size_t &universal_try,
286 std::size_t &universal_catch)
287{
288 for(std::size_t i=stack_catch.size(); i>0;)
289 {
290 i--;
291 for(std::size_t j=0; j<stack_catch[i].size(); ++j)
292 {
293 if(stack_catch[i][j].first.empty())
294 {
295 // Record the position of the default behaviour as any further catches
296 // will not capture the throw
299
300 // Universal handler. Highest on the stack takes
301 // precedence, so overwrite any we've already seen:
302 return stack_catch[i][j].second;
303 }
304 }
305 }
306 // Unless we have a universal exception handler, jump to end of function
307 return goto_program.get_end_function();
308}
309
321 const irep_idt &function_identifier,
322 goto_programt &goto_program,
325 const std::vector<symbol_exprt> &locals)
326{
327 // Jump to the universal handler or function end, as appropriate.
328 // This will appear after the GOTO-based dynamic dispatch below
330
331 // find the symbol corresponding to the caught exceptions
334
335 std::size_t default_try=0;
336 std::size_t default_catch=(!stack_catch.empty()) ? stack_catch[0].size() : 0;
337
338 goto_programt::targett default_target=
341
342 // add GOTOs implementing the dynamic dispatch of the
343 // exception handlers.
344 // The first loop is in forward order because the insertion reverses the order
345 // we note that try1{ try2 {} catch2c {} catch2d {}} catch1a() {} catch1b{}
346 // must catch in the following order: 2c 2d 1a 1b hence the numerical index
347 // is reversed whereas the letter ordering remains the same.
348 for(std::size_t i=default_try; i<stack_catch.size(); i++)
349 {
350 for(std::size_t j=(i==default_try) ? default_catch : stack_catch[i].size();
351 j>0;)
352 {
353 j--;
355 stack_catch[i][j].second;
356 if(!stack_catch[i][j].first.empty())
357 {
358 // use instanceof to check that this is the correct handler
359 struct_tag_typet type(stack_catch[i][j].first);
360
362
363 // Normal exception handler, make an instanceof check.
365 instr_it,
367 new_state_pc, check, instr_it->source_location()));
368
370 {
372 function_identifier,
373 t_exc,
374 goto_program,
378 }
379 }
380 }
381 }
382
384 default_target, true_exprt(), instr_it->source_location());
385
386 // add dead instructions
387 for(const auto &local : locals)
388 {
389 goto_program.insert_after(
390 instr_it, goto_programt::make_dead(local, instr_it->source_location()));
391 }
392}
393
397 const irep_idt &function_identifier,
398 goto_programt &goto_program,
401 const std::vector<symbol_exprt> &locals)
402{
403 PRECONDITION(instr_it->type() == THROW);
404
405 const exprt &exc_expr =
407
409 function_identifier, goto_program, instr_it, stack_catch, locals);
410
411 // find the symbol where the thrown exception should be stored:
414
415 // now turn the `throw' into an assignment with the appropriate cast
419 instr_it->source_location());
420
421 return true;
422}
423
428 const irep_idt &function_identifier,
429 goto_programt &goto_program,
432 const std::vector<symbol_exprt> &locals)
433{
435
436 // save the address of the next instruction
438 next_it++;
439
440 const auto &function = instr_it->call_function();
441
443 function.id() == ID_symbol, "function call expected to be a symbol");
444 const irep_idt &callee_id = to_symbol_expr(function).get_identifier();
445
447 {
451
453 {
454 // Function is annotated must-not-throw, but we can't prove that here.
455 // Insert an ASSUME(@inflight_exception == null):
456 goto_program.insert_after(
457 instr_it,
459
461 }
462 else
463 {
465 function_identifier, goto_program, instr_it, stack_catch, locals);
466
467 // add a null check (so that instanceof can be applied)
468 goto_program.insert_after(
469 instr_it,
471 next_it,
473 instr_it->source_location()));
474
476 }
477 }
478
480}
481
486 const irep_idt &function_identifier,
487 goto_programt &goto_program)
488{
489 stack_catcht stack_catch; // stack of try-catch blocks
490 std::vector<std::vector<symbol_exprt>> stack_locals; // stack of local vars
491 std::vector<symbol_exprt> locals;
492
493 if(goto_program.empty())
494 return;
495
497 function_or_callees_may_throw(goto_program);
498
499 bool did_something = false;
500 bool added_goto_instruction = false;
501
503 {
504 if(instr_it->is_decl())
505 {
506 locals.push_back(instr_it->decl_symbol());
507 }
508 // Is it a handler push/pop or catch landing-pad?
509 else if(instr_it->type() == CATCH)
510 {
511 const irep_idt &statement = instr_it->code().get_statement();
512 // Is it an exception landing pad (start of a catch block)?
513 if(statement==ID_exception_landingpad)
514 {
517 }
518 // Is it a catch handler pop?
519 else if(statement==ID_pop_catch)
520 {
521 // pop the local vars stack
522 if(!stack_locals.empty())
523 {
524 locals=stack_locals.back();
525 stack_locals.pop_back();
526 }
527 // pop from the stack if possible
528 if(!stack_catch.empty())
529 {
530 stack_catch.pop_back();
531 }
532 else
533 {
534#ifdef DEBUG
535 std::cout << "Remove exceptions: empty stack\n";
536#endif
537 }
538 }
539 // Is it a catch handler push?
540 else if(statement==ID_push_catch)
541 {
542 stack_locals.push_back(locals);
543 locals.clear();
544
546 stack_catch.push_back(exception);
548 stack_catch.back();
549
550 // copy targets
551 const code_push_catcht::exception_listt &exception_list =
552 to_code_push_catch(instr_it->code()).exception_list();
553
554 // The target list can be empty if `finish_catch_push_targets` found that
555 // the targets were unreachable (in which case no exception can truly
556 // be thrown at runtime)
557 INVARIANT(
558 instr_it->targets.empty() ||
559 exception_list.size()==instr_it->targets.size(),
560 "`exception_list` should contain current instruction's targets");
561
562 // Fill the map with the catch type and the target
563 unsigned i=0;
564 for(auto target : instr_it->targets)
565 {
566 last_exception.push_back(
567 std::make_pair(exception_list[i].get_tag(), target));
568 i++;
569 }
570 }
571 else
572 {
573 INVARIANT(
574 false,
575 "CATCH opcode should be one of push-catch, pop-catch, landingpad");
576 }
577
578 instr_it->turn_into_skip();
579 did_something = true;
580 }
581 else if(instr_it->type() == THROW)
582 {
584 function_identifier, goto_program, instr_it, stack_catch, locals);
585 }
586 else if(instr_it->type() == FUNCTION_CALL)
587 {
590 function_identifier, goto_program, instr_it, stack_catch, locals);
594 }
595 }
596
597 // INITIALIZE_FUNCTION should not contain any exception handling branches for
598 // two reasons: (1) with symex-driven lazy loading it means that code that
599 // references @inflight_exception might be generated before
600 // @inflight_exception is initialized; (2) symex can analyze
601 // INITIALIZE_FUNCTION much faster if it doesn't contain any branching.
602 INVARIANT(
603 function_identifier != INITIALIZE_FUNCTION || !added_goto_instruction,
604 INITIALIZE_FUNCTION " should not contain any exception handling branches");
605
606 if(did_something)
607 remove_skip(goto_program);
608}
609
611{
612 for(auto &gf_entry : goto_functions.function_map)
613 instrument_exceptions(gf_entry.first, gf_entry.second.body);
614}
615
617operator()(const irep_idt &function_identifier, goto_programt &goto_program)
618{
619 instrument_exceptions(function_identifier, goto_program);
620}
621
624 symbol_table_baset &symbol_table,
625 goto_functionst &goto_functions,
626 message_handlert &message_handler)
627{
628 const namespacet ns(symbol_table);
629 std::map<irep_idt, std::set<irep_idt>> exceptions_map;
630
631 uncaught_exceptions(goto_functions, ns, exceptions_map);
632
633 remove_exceptionst::function_may_throwt function_may_throw =
634 [&exceptions_map](const irep_idt &id) {
635 return !exceptions_map[id].empty();
636 };
637
639 symbol_table, nullptr, function_may_throw, false, message_handler);
640
641 remove_exceptions(goto_functions);
642}
643
657 const irep_idt &function_identifier,
658 goto_programt &goto_program,
659 symbol_table_baset &symbol_table,
660 message_handlert &message_handler)
661{
663 [](const irep_idt &) { return true; };
664
666 symbol_table, nullptr, any_function_may_throw, false, message_handler);
667
668 remove_exceptions(function_identifier, goto_program);
669}
670
679 goto_modelt &goto_model,
680 message_handlert &message_handler)
681{
683 goto_model.symbol_table, goto_model.goto_functions, message_handler);
684}
685
688 symbol_table_baset &symbol_table,
689 goto_functionst &goto_functions,
690 const class_hierarchyt &class_hierarchy,
691 message_handlert &message_handler)
692{
693 const namespacet ns(symbol_table);
694 std::map<irep_idt, std::set<irep_idt>> exceptions_map;
695
696 uncaught_exceptions(goto_functions, ns, exceptions_map);
697
698 remove_exceptionst::function_may_throwt function_may_throw =
699 [&exceptions_map](const irep_idt &id) {
700 return !exceptions_map[id].empty();
701 };
702
704 symbol_table, &class_hierarchy, function_may_throw, true, message_handler);
705
706 remove_exceptions(goto_functions);
707}
708
724 const irep_idt &function_identifier,
725 goto_programt &goto_program,
726 symbol_table_baset &symbol_table,
727 const class_hierarchyt &class_hierarchy,
728 message_handlert &message_handler)
729{
731 [](const irep_idt &) { return true; };
732
734 symbol_table,
735 &class_hierarchy,
737 true,
738 message_handler);
739
740 remove_exceptions(function_identifier, goto_program);
741}
742
753 goto_modelt &goto_model,
754 const class_hierarchyt &class_hierarchy,
755 message_handlert &message_handler)
756{
758 goto_model.symbol_table,
759 goto_model.goto_functions,
760 class_hierarchy,
761 message_handler);
762}
pointer_typet pointer_type(const typet &subtype)
Definition c_types.cpp:235
virtual void clear()
Reset the abstract state.
Definition ai.h:265
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Non-graph-based representation of the class hierarchy.
A goto_instruction_codet representing an assignment in the program.
std::vector< exception_list_entryt > exception_listt
Definition std_code.h:1849
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:38
Equality.
Definition std_expr.h:1366
Base class for all expressions.
Definition expr.h:56
A collection of goto functions.
function_mapt function_map
symbol_tablet symbol_table
Symbol table.
Definition goto_model.h:31
goto_functionst goto_functions
GOTO functions.
Definition goto_model.h:34
A generic container class for the GOTO intermediate representation of one function.
static instructiont make_assumption(const exprt &g, const source_locationt &l=source_locationt::nil())
instructionst instructions
The list of instructions in the goto program.
static instructiont make_dead(const symbol_exprt &symbol, const source_locationt &l=source_locationt::nil())
instructionst::iterator targett
static instructiont make_assignment(const code_assignt &_code, const source_locationt &l=source_locationt::nil())
Create an assignment instruction.
targett get_end_function()
Get an instruction iterator pointing to the END_FUNCTION instruction of the goto program.
targett insert_after(const_targett target)
Insertion after the instruction pointed-to by the given instruction iterator target.
static instructiont make_goto(targett _target, const source_locationt &l=source_locationt::nil())
bool empty() const
Is the program empty?
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
The null pointer constant.
Lowers high-level exception descriptions into low-level operations suitable for symex and other analy...
const class_hierarchyt * class_hierarchy
std::vector< catch_handlerst > stack_catcht
function_may_throwt function_may_throw
symbol_exprt get_inflight_exception_global()
Create a global named java::@inflight_exception that holds any exception that has been thrown but not...
void add_exception_dispatch_sequence(const irep_idt &function_identifier, goto_programt &goto_program, const goto_programt::targett &instr_it, const stack_catcht &stack_catch, const std::vector< symbol_exprt > &locals)
Emit the code: if (exception instanceof ExnA) then goto handlerA else if (exception instanceof ExnB) ...
message_handlert & message_handler
void instrument_exception_handler(goto_programt &goto_program, const goto_programt::targett &, bool may_catch)
Translates an exception landing-pad into instructions that copy the in-flight exception pointer to a ...
std::vector< std::pair< irep_idt, goto_programt::targett > > catch_handlerst
bool function_or_callees_may_throw(const goto_programt &) const
Checks whether a function may ever experience an exception (whether or not it catches),...
goto_programt::targett find_universal_exception(const remove_exceptionst::stack_catcht &stack_catch, goto_programt &goto_program, std::size_t &universal_try, std::size_t &universal_catch)
Find the innermost universal exception handler for the current program location which may throw (i....
remove_exceptionst(symbol_table_baset &_symbol_table, const class_hierarchyt *_class_hierarchy, function_may_throwt _function_may_throw, bool _remove_added_instanceof, message_handlert &_message_handler)
std::function< bool(const irep_idt &)> function_may_throwt
instrumentation_resultt instrument_function_call(const irep_idt &function_identifier, goto_programt &goto_program, const goto_programt::targett &, const stack_catcht &, const std::vector< symbol_exprt > &)
instruments each function call that may escape exceptions with conditional GOTOS to the corresponding...
bool instrument_throw(const irep_idt &function_identifier, goto_programt &goto_program, const goto_programt::targett &, const stack_catcht &, const std::vector< symbol_exprt > &)
instruments each throw with conditional GOTOS to the corresponding exception handlers
void instrument_exceptions(const irep_idt &function_identifier, goto_programt &goto_program)
instruments throws, function calls that may escape exceptions and exception handlers.
symbol_table_baset & symbol_table
void operator()(goto_functionst &goto_functions)
A struct tag type, i.e., struct_typet with an identifier.
Definition std_types.h:493
Expression to hold a symbol (variable)
Definition std_expr.h:131
The symbol table base class interface.
const symbolt * lookup(const irep_idt &name) const
Find a symbol in the symbol table for read-only access.
const symbolt & lookup_ref(const irep_idt &name) const
Find a symbol in the symbol table for read-only access.
Symbol table entry.
Definition symbol.h:28
The Boolean constant true.
Definition std_expr.h:3190
Semantic type conversion.
Definition std_expr.h:2073
static exprt get_exception_symbol(const exprt &exor)
Returns the symbol corresponding to an exception.
Symbol Table + CFG.
#define Forall_goto_program_instructions(it, program)
@ FUNCTION_CALL
@ CATCH
@ THROW
Java-specific exprt subclasses.
static irep_idt get_tag(const typet &type)
empty_typet java_void_type()
STL namespace.
API to expression classes for Pointers.
void remove_exceptions_using_instanceof(symbol_table_baset &symbol_table, goto_functionst &goto_functions, message_handlert &message_handler)
removes throws/CATCH-POP/CATCH-PUSH
void remove_exceptions(symbol_table_baset &symbol_table, goto_functionst &goto_functions, const class_hierarchyt &class_hierarchy, message_handlert &message_handler)
removes throws/CATCH-POP/CATCH-PUSH
Remove function exceptional returns.
#define INFLIGHT_EXCEPTION_VARIABLE_NAME
void remove_instanceof(const irep_idt &function_identifier, goto_programt::targett target, goto_programt &goto_program, symbol_table_baset &symbol_table, const class_hierarchyt &class_hierarchy, message_handlert &message_handler)
Replace an instanceof in the expression or guard of the passed instruction of the given function body...
Remove Instance-of Operators.
void remove_skip(goto_programt &goto_program, goto_programt::targett begin, goto_programt::targett end)
remove unnecessary skip statements
Program Transformation.
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition invariant.h:534
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
#define INITIALIZE_FUNCTION
static code_push_catcht & to_code_push_catch(codet &code)
Definition std_code.h:1883
static code_landingpadt & to_code_landingpad(codet &code)
Definition std_code.h:1972
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:272
void uncaught_exceptions(const goto_functionst &goto_functions, const namespacet &ns, std::map< irep_idt, std::set< irep_idt > > &exceptions_map)
Applies the uncaught exceptions analysis and outputs the result.
Over-approximative uncaught exceptions analysis.
dstringt irep_idt