|
| 1 | +/*******************************************************************\ |
| 2 | +
|
| 3 | +Module: Dynamic frame condition checking for function contracts |
| 4 | +
|
| 5 | +Author: Remi Delmas, [email protected] |
| 6 | +Date: August 2022 |
| 7 | +
|
| 8 | +\*******************************************************************/ |
| 9 | + |
| 10 | +#include "dfcc_dsl_contract_handler.h" |
| 11 | + |
| 12 | +#include <util/arith_tools.h> |
| 13 | +#include <util/c_types.h> |
| 14 | +#include <util/expr_util.h> |
| 15 | +#include <util/format_expr.h> |
| 16 | +#include <util/format_type.h> |
| 17 | +#include <util/fresh_symbol.h> |
| 18 | +#include <util/invariant.h> |
| 19 | +#include <util/mathematical_expr.h> |
| 20 | +#include <util/namespace.h> |
| 21 | +#include <util/pointer_offset_size.h> |
| 22 | +#include <util/std_expr.h> |
| 23 | + |
| 24 | +#include <goto-programs/goto_model.h> |
| 25 | +#include <goto-programs/remove_function_pointers.h> |
| 26 | + |
| 27 | +#include <ansi-c/c_expr.h> |
| 28 | +#include <goto-instrument/contracts/contracts.h> |
| 29 | +#include <goto-instrument/contracts/utils.h> |
| 30 | +#include <langapi/language_util.h> |
| 31 | + |
| 32 | +#include "dfcc_dsl_wrapper_program.h" |
| 33 | +#include "dfcc_instrument.h" |
| 34 | +#include "dfcc_library.h" |
| 35 | +#include "dfcc_spec_functions.h" |
| 36 | +#include "dfcc_utils.h" |
| 37 | + |
| 38 | +std::map<irep_idt, dfcc_dsl_contract_functionst> |
| 39 | + dfcc_dsl_contract_handlert::contract_cache; |
| 40 | + |
| 41 | +dfcc_dsl_contract_handlert::dfcc_dsl_contract_handlert( |
| 42 | + goto_modelt &goto_model, |
| 43 | + messaget &log, |
| 44 | + dfcc_utilst &utils, |
| 45 | + dfcc_libraryt &library, |
| 46 | + dfcc_instrumentt &instrument, |
| 47 | + dfcc_spec_functionst &spec_functions) |
| 48 | + : goto_model(goto_model), |
| 49 | + log(log), |
| 50 | + message_handler(log.get_message_handler()), |
| 51 | + utils(utils), |
| 52 | + library(library), |
| 53 | + instrument(instrument), |
| 54 | + spec_functions(spec_functions), |
| 55 | + ns(goto_model.symbol_table) |
| 56 | +{ |
| 57 | +} |
| 58 | + |
| 59 | +const dfcc_dsl_contract_functionst & |
| 60 | +dfcc_dsl_contract_handlert::get_contract_functions(const irep_idt &contract_id) |
| 61 | +{ |
| 62 | + auto found = dfcc_dsl_contract_handlert::contract_cache.find(contract_id); |
| 63 | + if(found == dfcc_dsl_contract_handlert::contract_cache.end()) |
| 64 | + { |
| 65 | + dfcc_dsl_contract_handlert::contract_cache.insert( |
| 66 | + {contract_id, |
| 67 | + dfcc_dsl_contract_functionst( |
| 68 | + get_pure_contract_symbol(contract_id), |
| 69 | + goto_model, |
| 70 | + log, |
| 71 | + utils, |
| 72 | + library, |
| 73 | + spec_functions)}); |
| 74 | + } |
| 75 | + return dfcc_dsl_contract_handlert::contract_cache.at(contract_id); |
| 76 | +} |
| 77 | + |
| 78 | +const int |
| 79 | +dfcc_dsl_contract_handlert::get_assigns_clause_size(const irep_idt &contract_id) |
| 80 | +{ |
| 81 | + return get_contract_functions(contract_id).get_nof_assigns_targets(); |
| 82 | +} |
| 83 | + |
| 84 | +void dfcc_dsl_contract_handlert::add_contract_instructions( |
| 85 | + const dfcc_contract_modet contract_mode, |
| 86 | + const irep_idt &wrapper_id, |
| 87 | + const irep_idt &wrapped_id, |
| 88 | + const irep_idt &contract_id, |
| 89 | + const symbolt &wrapper_write_set_symbol, |
| 90 | + goto_programt &dest, |
| 91 | + std::set<irep_idt> &function_pointer_contracts) |
| 92 | +{ |
| 93 | + dfcc_dsl_wrapper_programt( |
| 94 | + contract_mode, |
| 95 | + utils.get_function_symbol(wrapper_id), |
| 96 | + utils.get_function_symbol(wrapped_id), |
| 97 | + get_contract_functions(contract_id), |
| 98 | + wrapper_write_set_symbol, |
| 99 | + goto_model, |
| 100 | + log, |
| 101 | + utils, |
| 102 | + library, |
| 103 | + instrument) |
| 104 | + .add_to_dest(dest, function_pointer_contracts); |
| 105 | +} |
| 106 | + |
| 107 | +const symbolt *dfcc_dsl_contract_handlert::get_pure_contract_symbol_ptr( |
| 108 | + const irep_idt &contract_id) |
| 109 | +{ |
| 110 | + const auto &contract_symbol = utils.get_function_symbol(contract_id); |
| 111 | + const symbolt *pure_contract_symbol = nullptr; |
| 112 | + auto pure_contract_id = "contract::" + id2string(contract_id); |
| 113 | + if(!ns.lookup(pure_contract_id, pure_contract_symbol)) |
| 114 | + { |
| 115 | + log.debug() << "contract_symbol: " << contract_symbol.name << messaget::eom; |
| 116 | + log.debug() << contract_symbol.type.pretty() << messaget::eom; |
| 117 | + log.debug() << "pure_contract_symbol: " << pure_contract_id |
| 118 | + << messaget::eom; |
| 119 | + log.debug() << pure_contract_symbol->type.pretty() << messaget::eom; |
| 120 | + |
| 121 | + check_signature_compat( |
| 122 | + contract_id, |
| 123 | + to_code_type(contract_symbol.type), |
| 124 | + pure_contract_id, |
| 125 | + to_code_type(pure_contract_symbol->type)); |
| 126 | + } |
| 127 | + else |
| 128 | + { |
| 129 | + // The contract symbol might not have been created if the function had |
| 130 | + // no contract or a contract with all empty clauses (which is equivalent). |
| 131 | + // in that case we create a fresh symbol again with empty clauses |
| 132 | + symbolt new_symbol; |
| 133 | + new_symbol.name = pure_contract_id; |
| 134 | + new_symbol.base_name = pure_contract_id; |
| 135 | + new_symbol.pretty_name = pure_contract_id; |
| 136 | + new_symbol.is_property = true; |
| 137 | + new_symbol.type = contract_symbol.type; |
| 138 | + new_symbol.mode = contract_symbol.mode; |
| 139 | + new_symbol.module = contract_symbol.module; |
| 140 | + new_symbol.location = contract_symbol.location; |
| 141 | + auto entry = goto_model.symbol_table.insert(std::move(new_symbol)); |
| 142 | + if(!entry.second) |
| 143 | + { |
| 144 | + log.error().source_location = contract_symbol.location; |
| 145 | + log.error() << "contract '" << contract_symbol.display_name() |
| 146 | + << "' already set at " << entry.first.location.as_string() |
| 147 | + << messaget::eom; |
| 148 | + throw 0; |
| 149 | + } |
| 150 | + // this lookup must work, and no need to check for signature compatibility |
| 151 | + ns.lookup(pure_contract_id, pure_contract_symbol); |
| 152 | + } |
| 153 | + return pure_contract_symbol; |
| 154 | +} |
| 155 | + |
| 156 | +const symbolt &dfcc_dsl_contract_handlert::get_pure_contract_symbol( |
| 157 | + const irep_idt &contract_id) |
| 158 | +{ |
| 159 | + auto result = get_pure_contract_symbol_ptr(contract_id); |
| 160 | + if(result != nullptr) |
| 161 | + return *result; |
| 162 | + |
| 163 | + log.error() << "dfcc_dsl_contract_handlert: pure contract symbol for " |
| 164 | + << contract_id << " was not found, aborting" << messaget::eom; |
| 165 | + throw 0; |
| 166 | +} |
| 167 | + |
| 168 | +const bool dfcc_dsl_contract_handlert::pure_contract_symbol_exists( |
| 169 | + const irep_idt &contract_id) |
| 170 | +{ |
| 171 | + auto result = get_pure_contract_symbol_ptr(contract_id); |
| 172 | + return result != nullptr; |
| 173 | +} |
| 174 | + |
| 175 | +void dfcc_dsl_contract_handlert::check_signature_compat( |
| 176 | + const irep_idt &contract_id, |
| 177 | + const code_typet &contract_type, |
| 178 | + const irep_idt &pure_contract_id, |
| 179 | + const code_typet &pure_contract_type) |
| 180 | +{ |
| 181 | + // can we turn a call to `contract` into a call to `pure_contract` ? |
| 182 | + bool compatible = |
| 183 | + function_is_type_compatible(true, contract_type, pure_contract_type, ns); |
| 184 | + |
| 185 | + if(!compatible) |
| 186 | + { |
| 187 | + log.error() << "dfcc_dsl_contract_handlert: function '" << contract_id |
| 188 | + << "' and the corresponding pure contract symbol '" |
| 189 | + << pure_contract_id |
| 190 | + << "' have incompatible type signatures:" << messaget::eom; |
| 191 | + log.error() << "- contract return type " |
| 192 | + << format(contract_type.return_type()) << messaget::eom; |
| 193 | + for(const auto ¶m : contract_type.parameters()) |
| 194 | + { |
| 195 | + log.error() << "- contract param type " << format(param.type()) |
| 196 | + << messaget::eom; |
| 197 | + } |
| 198 | + |
| 199 | + log.error() << "- pure contract return type " |
| 200 | + << format(pure_contract_type.return_type()) << messaget::eom; |
| 201 | + for(const auto ¶m : pure_contract_type.parameters()) |
| 202 | + { |
| 203 | + log.error() << "- pure contract param type " << format(param.type()) |
| 204 | + << messaget::eom; |
| 205 | + } |
| 206 | + |
| 207 | + log.error() << "aborting." << messaget::eom; |
| 208 | + throw 0; |
| 209 | + } |
| 210 | +} |
0 commit comments