|
| 1 | +/*******************************************************************\ |
| 2 | +
|
| 3 | +Module: Remove Java Nondet expressions |
| 4 | +
|
| 5 | +Author: Reuben Thomas, [email protected] |
| 6 | +
|
| 7 | +\*******************************************************************/ |
| 8 | + |
| 9 | +#include "goto-programs/remove_java_nondet.h" |
| 10 | +#include "goto-programs/goto_convert.h" |
| 11 | +#include "goto-programs/goto_model.h" |
| 12 | + |
| 13 | +#include "java_bytecode/java_object_factory.h" |
| 14 | + |
| 15 | +#include "util/irep_ids.h" |
| 16 | + |
| 17 | +#include <algorithm> |
| 18 | +#include <regex> |
| 19 | + |
| 20 | +/*******************************************************************\ |
| 21 | +
|
| 22 | + Struct: nondet_instruction_infot |
| 23 | +
|
| 24 | + Purpose: Hold information about a nondet instruction invocation. |
| 25 | +
|
| 26 | +\*******************************************************************/ |
| 27 | + |
| 28 | +struct nondet_instruction_infot final |
| 29 | +{ |
| 30 | + bool is_nondet_instruction; |
| 31 | + bool is_never_null; |
| 32 | +}; |
| 33 | + |
| 34 | +/*******************************************************************\ |
| 35 | +
|
| 36 | +Function: is_nondet_returning_object |
| 37 | +
|
| 38 | + Inputs: |
| 39 | + function_call: A function call expression. |
| 40 | +
|
| 41 | + Outputs: Whether the function has a nondet signature, and whether the nondet |
| 42 | + item may be null. |
| 43 | +
|
| 44 | +
|
| 45 | + Purpose: Find whether the supplied function call has a recognised 'nondet' |
| 46 | + library signature. If so, specify whether the function call is |
| 47 | + expected to return non-null. |
| 48 | +
|
| 49 | +\*******************************************************************/ |
| 50 | + |
| 51 | +static nondet_instruction_infot is_nondet_returning_object( |
| 52 | + const code_function_callt &function_call) |
| 53 | +{ |
| 54 | + const auto &function_symbol=to_symbol_expr(function_call.function()); |
| 55 | + const auto function_name=id2string(function_symbol.get_identifier()); |
| 56 | + std::smatch match_results; |
| 57 | + if(!std::regex_match( |
| 58 | + function_name, |
| 59 | + match_results, |
| 60 | + std::regex(".*org\\.cprover\\.CProver\\.nondetWith(out)?Null.*"))) |
| 61 | + { |
| 62 | + return {false, false}; |
| 63 | + } |
| 64 | + |
| 65 | + return {true, match_results[1].matched}; |
| 66 | +} |
| 67 | + |
| 68 | +/*******************************************************************\ |
| 69 | +
|
| 70 | +Function: get_nondet_instruction_info |
| 71 | +
|
| 72 | + Inputs: |
| 73 | + instr: A goto program instruction. |
| 74 | +
|
| 75 | + Outputs: Whether the instruction is a function with a nondet signature, and |
| 76 | + whether the nondet item may be null. |
| 77 | +
|
| 78 | + Purpose: Return whether the instruction has a recognised 'nondet' library |
| 79 | + signature. |
| 80 | +
|
| 81 | +\*******************************************************************/ |
| 82 | + |
| 83 | +static nondet_instruction_infot get_nondet_instruction_info( |
| 84 | + const goto_programt::targett &instr) |
| 85 | +{ |
| 86 | + if(!(instr->is_function_call() && instr->code.id()==ID_code)) |
| 87 | + { |
| 88 | + return {false, false}; |
| 89 | + } |
| 90 | + const auto &code=to_code(instr->code); |
| 91 | + if(code.get_statement()!=ID_function_call) |
| 92 | + { |
| 93 | + return {false, false}; |
| 94 | + } |
| 95 | + const auto &function_call=to_code_function_call(code); |
| 96 | + return is_nondet_returning_object(function_call); |
| 97 | +} |
| 98 | + |
| 99 | +/*******************************************************************\ |
| 100 | +
|
| 101 | +Function: is_symbol_with_id |
| 102 | +
|
| 103 | + Inputs: |
| 104 | + expr: The expression which may be a symbol. |
| 105 | + identifier: Some identifier. |
| 106 | +
|
| 107 | + Outputs: True if the expression is a symbol with the specified identifier. |
| 108 | +
|
| 109 | + Purpose: Return whether the expression is a symbol with the specified |
| 110 | + identifier. |
| 111 | +
|
| 112 | +\*******************************************************************/ |
| 113 | + |
| 114 | +static bool is_symbol_with_id(const exprt& expr, const dstringt& identifier) |
| 115 | +{ |
| 116 | + return expr.id()==ID_symbol && |
| 117 | + to_symbol_expr(expr).get_identifier()==identifier; |
| 118 | +} |
| 119 | + |
| 120 | +/*******************************************************************\ |
| 121 | +
|
| 122 | +Function: is_typecast_with_id |
| 123 | +
|
| 124 | + Inputs: |
| 125 | + expr: The expression which may be a typecast. |
| 126 | + identifier: Some identifier. |
| 127 | +
|
| 128 | + Outputs: True if the expression is a typecast with one operand, and the |
| 129 | + typecast's identifier matches the specified identifier. |
| 130 | +
|
| 131 | + Purpose: Return whether the expression is a typecast with the specified |
| 132 | + identifier. |
| 133 | +
|
| 134 | +\*******************************************************************/ |
| 135 | + |
| 136 | +static bool is_typecast_with_id(const exprt& expr, const dstringt& identifier) |
| 137 | +{ |
| 138 | + if(!(expr.id()==ID_typecast && expr.operands().size()==1)) |
| 139 | + { |
| 140 | + return false; |
| 141 | + } |
| 142 | + const auto &typecast=to_typecast_expr(expr); |
| 143 | + if(!(typecast.op().id()==ID_symbol && !typecast.op().has_operands())) |
| 144 | + { |
| 145 | + return false; |
| 146 | + } |
| 147 | + const auto &op_symbol=to_symbol_expr(typecast.op()); |
| 148 | + // Return whether the typecast has the expected operand |
| 149 | + return op_symbol.get_identifier()==identifier; |
| 150 | +} |
| 151 | + |
| 152 | +/*******************************************************************\ |
| 153 | +
|
| 154 | +Function: is_assignment_from |
| 155 | +
|
| 156 | + Inputs: |
| 157 | + instr: A goto program instruction. |
| 158 | + identifier: Some identifier. |
| 159 | +
|
| 160 | + Outputs: True if the expression is a typecast with one operand, and the |
| 161 | + typecast's identifier matches the specified identifier. |
| 162 | +
|
| 163 | + Purpose: Return whether the instruction is an assignment, and the rhs is a |
| 164 | + symbol or typecast expression with the specified identifier. |
| 165 | +
|
| 166 | +\*******************************************************************/ |
| 167 | + |
| 168 | +static bool is_assignment_from( |
| 169 | + const goto_programt::instructiont &instr, |
| 170 | + const dstringt &identifier) |
| 171 | +{ |
| 172 | + // If not an assignment, return false |
| 173 | + if(!instr.is_assign()) |
| 174 | + { |
| 175 | + return false; |
| 176 | + } |
| 177 | + const auto &rhs=to_code_assign(instr.code).rhs(); |
| 178 | + return is_symbol_with_id(rhs, identifier) || |
| 179 | + is_typecast_with_id(rhs, identifier); |
| 180 | +} |
| 181 | + |
| 182 | +/*******************************************************************\ |
| 183 | +
|
| 184 | +Function: process_target |
| 185 | +
|
| 186 | + Inputs: |
| 187 | + message_handler: Handles logging. |
| 188 | + symbol_table: The table of known symbols. |
| 189 | + max_nondet_array_length: Max length of arrays in new nondet objects. |
| 190 | + instructions: The list of all instructions. |
| 191 | + instr: The instruction to check. |
| 192 | +
|
| 193 | + Outputs: The next instruction to process, probably with this function. |
| 194 | +
|
| 195 | + Purpose: Given an iterator into a list of instructions, modify the list to |
| 196 | + replace 'nondet' library functions with CBMC-native nondet |
| 197 | + expressions, and return an iterator to the next instruction to check. |
| 198 | +
|
| 199 | +\*******************************************************************/ |
| 200 | + |
| 201 | +static goto_programt::targett process_target( |
| 202 | + message_handlert &message_handler, |
| 203 | + symbol_tablet &symbol_table, |
| 204 | + const size_t max_nondet_array_length, |
| 205 | + goto_programt &prog, |
| 206 | + const goto_programt::targett &instr) |
| 207 | +{ |
| 208 | + // Check whether this is our nondet library method, and return if not |
| 209 | + const auto instr_info=get_nondet_instruction_info(instr); |
| 210 | + const auto next_instr=std::next(instr); |
| 211 | + if(!instr_info.is_nondet_instruction) |
| 212 | + { |
| 213 | + return next_instr; |
| 214 | + } |
| 215 | + |
| 216 | + // Look at the next instruction, ensure that it is an assignment |
| 217 | + assert(next_instr->is_assign()); |
| 218 | + // Get the name of the LHS of the assignment |
| 219 | + const auto &next_instr_assign_lhs=to_code_assign(next_instr->code).lhs(); |
| 220 | + if(!(next_instr_assign_lhs.id()==ID_symbol && |
| 221 | + !next_instr_assign_lhs.has_operands())) |
| 222 | + { |
| 223 | + return next_instr; |
| 224 | + } |
| 225 | + const auto return_identifier= |
| 226 | + to_symbol_expr(next_instr_assign_lhs).get_identifier(); |
| 227 | + |
| 228 | + auto &instructions=prog.instructions; |
| 229 | + const auto end=std::end(instructions); |
| 230 | + |
| 231 | + // Look for an instruction where this name is on the RHS of an assignment |
| 232 | + const auto matching_assignment=std::find_if( |
| 233 | + next_instr, |
| 234 | + end, |
| 235 | + [&return_identifier](const goto_programt::instructiont &instr) |
| 236 | + { |
| 237 | + return is_assignment_from(instr, return_identifier); |
| 238 | + }); |
| 239 | + |
| 240 | + assert(matching_assignment!=end); |
| 241 | + |
| 242 | + // Assume that the LHS of *this* assignment is the actual nondet variable |
| 243 | + const auto &code_assign=to_code_assign(matching_assignment->code); |
| 244 | + const auto nondet_var=code_assign.lhs(); |
| 245 | + const auto source_loc=instr->source_location; |
| 246 | + |
| 247 | + // Erase from the nondet function call to the assignment |
| 248 | + const auto after_matching_assignment=std::next(matching_assignment); |
| 249 | + assert(after_matching_assignment!=end); |
| 250 | + const auto after_erased= |
| 251 | + instructions.erase(instr, after_matching_assignment); |
| 252 | + |
| 253 | + // Generate nondet init code |
| 254 | + code_blockt init_code; |
| 255 | + gen_nondet_init( |
| 256 | + nondet_var, |
| 257 | + init_code, |
| 258 | + symbol_table, |
| 259 | + source_loc, |
| 260 | + false, |
| 261 | + true, |
| 262 | + instr_info.is_never_null, |
| 263 | + message_handler, |
| 264 | + max_nondet_array_length); |
| 265 | + |
| 266 | + // Convert this code into goto instructions |
| 267 | + goto_programt new_instructions; |
| 268 | + goto_convert(init_code, symbol_table, new_instructions, message_handler); |
| 269 | + |
| 270 | + // Insert the new instructions into the instruction list |
| 271 | + prog.destructive_insert(after_erased, new_instructions); |
| 272 | + prog.update(); |
| 273 | + |
| 274 | + return after_erased; |
| 275 | +} |
| 276 | + |
| 277 | +/*******************************************************************\ |
| 278 | +
|
| 279 | +Function: remove_java_nondet |
| 280 | +
|
| 281 | + Inputs: |
| 282 | + message_handler: Object which prints messages. |
| 283 | + symbol_table: The list of known symbols. |
| 284 | + max_nondet_array_length: The maximum length of new nondet arrays. |
| 285 | + goto_program: The program to modify. |
| 286 | +
|
| 287 | + Purpose: Modify a goto_programt to replace 'nondet' library functions with |
| 288 | + CBMC-native nondet expressions. |
| 289 | +
|
| 290 | +\*******************************************************************/ |
| 291 | + |
| 292 | +static void remove_java_nondet( |
| 293 | + message_handlert &message_handler, |
| 294 | + symbol_tablet &symbol_table, |
| 295 | + const size_t max_nondet_array_length, |
| 296 | + goto_programt &goto_program) |
| 297 | +{ |
| 298 | + // Check each instruction. |
| 299 | + // `process_target` may modify the list in place, and returns the next |
| 300 | + // iterator to look at. |
| 301 | + for(auto instruction_iterator=std::begin(goto_program.instructions), |
| 302 | + end=std::end(goto_program.instructions); |
| 303 | + instruction_iterator!=end; |
| 304 | + instruction_iterator=process_target( |
| 305 | + message_handler, |
| 306 | + symbol_table, |
| 307 | + max_nondet_array_length, |
| 308 | + goto_program, |
| 309 | + instruction_iterator)) |
| 310 | + { |
| 311 | + // Loop body deliberately empty. |
| 312 | + } |
| 313 | +} |
| 314 | + |
| 315 | +void remove_java_nondet( |
| 316 | + message_handlert &message_handler, |
| 317 | + symbol_tablet &symbol_table, |
| 318 | + const size_t max_nondet_array_length, |
| 319 | + goto_functionst &goto_functions) |
| 320 | +{ |
| 321 | + for(auto &f : goto_functions.function_map) |
| 322 | + { |
| 323 | + remove_java_nondet( |
| 324 | + message_handler, |
| 325 | + symbol_table, |
| 326 | + max_nondet_array_length, |
| 327 | + f.second.body); |
| 328 | + } |
| 329 | + goto_functions.compute_location_numbers(); |
| 330 | +} |
0 commit comments