|
| 1 | +/*******************************************************************\ |
| 2 | +
|
| 3 | +Module: Simple Java method stubbing |
| 4 | +
|
| 5 | +Author: Diffblue Ltd. |
| 6 | +
|
| 7 | +\*******************************************************************/ |
| 8 | + |
| 9 | +/// \file |
| 10 | +/// Java simple opaque stub generation |
| 11 | + |
| 12 | +#include "simple_method_stubbing.h" |
| 13 | + |
| 14 | +#include <java_bytecode/java_object_factory.h> |
| 15 | +#include <java_bytecode/java_pointer_casts.h> |
| 16 | + |
| 17 | +#include <util/fresh_symbol.h> |
| 18 | +#include <util/invariant_utils.h> |
| 19 | +#include <util/namespace.h> |
| 20 | +#include <util/std_code.h> |
| 21 | +#include <util/std_expr.h> |
| 22 | + |
| 23 | +class java_simple_method_stubst |
| 24 | +{ |
| 25 | +public: |
| 26 | + java_simple_method_stubst( |
| 27 | + symbol_table_baset &_symbol_table, |
| 28 | + bool _assume_non_null, |
| 29 | + const object_factory_parameterst &_object_factory_parameters, |
| 30 | + message_handlert &_message_handler) |
| 31 | + : symbol_table(_symbol_table), |
| 32 | + assume_non_null(_assume_non_null), |
| 33 | + object_factory_parameters(_object_factory_parameters), |
| 34 | + message_handler(_message_handler) |
| 35 | + { |
| 36 | + } |
| 37 | + |
| 38 | + void create_method_stub_at( |
| 39 | + const typet &expected_type, |
| 40 | + const exprt &ptr, |
| 41 | + const source_locationt &loc, |
| 42 | + code_blockt &parent_block, |
| 43 | + unsigned insert_before_index, |
| 44 | + bool is_constructor, |
| 45 | + bool update_in_place); |
| 46 | + |
| 47 | + void create_method_stub(symbolt &symbol); |
| 48 | + |
| 49 | + void check_method_stub(const irep_idt &); |
| 50 | + |
| 51 | +protected: |
| 52 | + symbol_table_baset &symbol_table; |
| 53 | + bool assume_non_null; |
| 54 | + const object_factory_parameterst &object_factory_parameters; |
| 55 | + message_handlert &message_handler; |
| 56 | +}; |
| 57 | + |
| 58 | +/// Nondet-initialize an object, including allocating referees for reference |
| 59 | +/// fields and nondet-initialising those recursively. Reference fields are |
| 60 | +/// nondeterministically assigned either a fresh object or null; arrays are |
| 61 | +/// additionally nondeterministically assigned between 0 and |
| 62 | +/// `max_nondet_array_length` members. |
| 63 | +/// \param expected_type: the expected actual type of `ptr`. We will cast |
| 64 | +/// `ptr` to this type and initialize assuming the actual referee has this |
| 65 | +/// type. |
| 66 | +/// \param ptr: pointer to the memory to initialize |
| 67 | +/// \param loc: source location to set for the opaque method stub |
| 68 | +/// \param [out] parent_block: The parent block in which the new instructions |
| 69 | +/// will be added. |
| 70 | +/// \param insert_before_index: The position in which the new instructions |
| 71 | +/// will be added. |
| 72 | +/// \param is_constructor: true if we're initialising the `this` pointer of a |
| 73 | +/// constructor. In this case the target's class identifier has been set and |
| 74 | +/// should not be overridden. |
| 75 | +/// \param update_in_place: Whether to generate the nondet in place or not. |
| 76 | +void java_simple_method_stubst::create_method_stub_at( |
| 77 | + const typet &expected_type, |
| 78 | + const exprt &ptr, |
| 79 | + const source_locationt &loc, |
| 80 | + code_blockt &parent_block, |
| 81 | + const unsigned insert_before_index, |
| 82 | + const bool is_constructor, |
| 83 | + const bool update_in_place) |
| 84 | +{ |
| 85 | + // At this point we know 'ptr' points to an opaque-typed object. |
| 86 | + // We should nondet-initialize it and insert the instructions *after* |
| 87 | + // the offending call at (*parent_block)[insert_before_index]. |
| 88 | + |
| 89 | + INVARIANT_WITH_IREP( |
| 90 | + expected_type.id() == ID_pointer, |
| 91 | + "Nondet initializer result type: expected a pointer", |
| 92 | + expected_type); |
| 93 | + |
| 94 | + namespacet ns(symbol_table); |
| 95 | + |
| 96 | + const auto &expected_base = ns.follow(expected_type.subtype()); |
| 97 | + if(expected_base.id() != ID_struct) |
| 98 | + return; |
| 99 | + |
| 100 | + const exprt cast_ptr = |
| 101 | + make_clean_pointer_cast(ptr, to_pointer_type(expected_type), ns); |
| 102 | + |
| 103 | + exprt to_init = cast_ptr; |
| 104 | + // If it's a constructor the thing we're constructing has already |
| 105 | + // been allocated by this point. |
| 106 | + if(is_constructor) |
| 107 | + to_init = dereference_exprt(to_init, expected_base); |
| 108 | + |
| 109 | + // Generate new instructions. |
| 110 | + code_blockt new_instructions; |
| 111 | + gen_nondet_init( |
| 112 | + to_init, |
| 113 | + new_instructions, |
| 114 | + symbol_table, |
| 115 | + loc, |
| 116 | + is_constructor, |
| 117 | + allocation_typet::DYNAMIC, |
| 118 | + !assume_non_null, |
| 119 | + object_factory_parameters, |
| 120 | + update_in_place ? update_in_placet::MUST_UPDATE_IN_PLACE |
| 121 | + : update_in_placet::NO_UPDATE_IN_PLACE); |
| 122 | + |
| 123 | + // Insert new_instructions into parent block. |
| 124 | + if(!new_instructions.operands().empty()) |
| 125 | + { |
| 126 | + auto insert_position = parent_block.operands().begin(); |
| 127 | + std::advance(insert_position, insert_before_index); |
| 128 | + parent_block.operands().insert( |
| 129 | + insert_position, |
| 130 | + new_instructions.operands().begin(), |
| 131 | + new_instructions.operands().end()); |
| 132 | + } |
| 133 | +} |
| 134 | + |
| 135 | +/// Replaces sym's value with an opaque method stub. If sym is a |
| 136 | +/// constructor function this nondet-initializes `*this` using the function |
| 137 | +/// above; otherwise it generates a return value using a similar method. |
| 138 | +/// \param symbol: Function symbol to stub |
| 139 | +void java_simple_method_stubst::create_method_stub(symbolt &symbol) |
| 140 | +{ |
| 141 | + code_blockt new_instructions; |
| 142 | + const code_typet &required_type = to_code_type(symbol.type); |
| 143 | + |
| 144 | + // synthetic source location that contains the opaque function name only |
| 145 | + source_locationt synthesized_source_location; |
| 146 | + synthesized_source_location.set_function(symbol.name); |
| 147 | + |
| 148 | + // Initialize the return value or `this` parameter under construction. |
| 149 | + // Note symbol.type is required_type, but with write access |
| 150 | + // Probably required_type should not be const |
| 151 | + const bool is_constructor = required_type.get_is_constructor(); |
| 152 | + if(is_constructor) |
| 153 | + { |
| 154 | + const auto &this_argument = required_type.parameters()[0]; |
| 155 | + const typet &this_type = this_argument.type(); |
| 156 | + symbolt &init_symbol = get_fresh_aux_symbol( |
| 157 | + this_type, |
| 158 | + "to_construct", |
| 159 | + "to_construct", |
| 160 | + synthesized_source_location, |
| 161 | + ID_java, |
| 162 | + symbol_table); |
| 163 | + const symbol_exprt &init_symbol_expression = init_symbol.symbol_expr(); |
| 164 | + code_assignt get_argument( |
| 165 | + init_symbol_expression, symbol_exprt(this_argument.get_identifier())); |
| 166 | + get_argument.add_source_location() = synthesized_source_location; |
| 167 | + new_instructions.copy_to_operands(get_argument); |
| 168 | + create_method_stub_at( |
| 169 | + this_type, |
| 170 | + init_symbol_expression, |
| 171 | + synthesized_source_location, |
| 172 | + new_instructions, |
| 173 | + 1, |
| 174 | + true, |
| 175 | + false); |
| 176 | + } |
| 177 | + else |
| 178 | + { |
| 179 | + const typet &required_return_type = required_type.return_type(); |
| 180 | + if(required_return_type != empty_typet()) |
| 181 | + { |
| 182 | + symbolt &to_return_symbol = get_fresh_aux_symbol( |
| 183 | + required_return_type, |
| 184 | + "to_return", |
| 185 | + "to_return", |
| 186 | + synthesized_source_location, |
| 187 | + ID_java, |
| 188 | + symbol_table); |
| 189 | + const exprt &to_return = to_return_symbol.symbol_expr(); |
| 190 | + if(to_return_symbol.type.id() != ID_pointer) |
| 191 | + { |
| 192 | + gen_nondet_init( |
| 193 | + to_return, |
| 194 | + new_instructions, |
| 195 | + symbol_table, |
| 196 | + source_locationt(), |
| 197 | + false, |
| 198 | + allocation_typet::LOCAL, // Irrelevant as type is primitive |
| 199 | + !assume_non_null, |
| 200 | + object_factory_parameters, |
| 201 | + update_in_placet::NO_UPDATE_IN_PLACE); |
| 202 | + } |
| 203 | + else |
| 204 | + create_method_stub_at( |
| 205 | + required_return_type, |
| 206 | + to_return, |
| 207 | + synthesized_source_location, |
| 208 | + new_instructions, |
| 209 | + 0, |
| 210 | + false, |
| 211 | + false); |
| 212 | + new_instructions.copy_to_operands(code_returnt(to_return)); |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + symbol.value = new_instructions; |
| 217 | +} |
| 218 | + |
| 219 | +/// Replaces `sym` with a function stub per the function above if it is |
| 220 | +/// of suitable type. |
| 221 | +/// \param symname: Symbol name to consider stubbing |
| 222 | +void java_simple_method_stubst::check_method_stub(const irep_idt &symname) |
| 223 | +{ |
| 224 | + const symbolt &sym = *symbol_table.lookup(symname); |
| 225 | + if( |
| 226 | + !sym.is_type && sym.value.id() == ID_nil && sym.type.id() == ID_code && |
| 227 | + // Don't stub internal locking primitives: |
| 228 | + sym.name != "java::monitorenter" && sym.name != "java::monitorexit") |
| 229 | + { |
| 230 | + create_method_stub(*symbol_table.get_writeable(symname)); |
| 231 | + } |
| 232 | +} |
| 233 | + |
| 234 | +void java_generate_simple_method_stub( |
| 235 | + const irep_idt &function_name, |
| 236 | + symbol_table_baset &symbol_table, |
| 237 | + bool assume_non_null, |
| 238 | + const object_factory_parameterst &object_factory_parameters, |
| 239 | + message_handlert &message_handler) |
| 240 | +{ |
| 241 | + java_simple_method_stubst stub_generator( |
| 242 | + symbol_table, assume_non_null, object_factory_parameters, message_handler); |
| 243 | + |
| 244 | + stub_generator.check_method_stub(function_name); |
| 245 | +} |
| 246 | + |
| 247 | +/// Generates function stubs for most undefined functions in the symbol |
| 248 | +/// table, except as forbidden in |
| 249 | +/// `java_simple_method_stubst::check_method_stub`. |
| 250 | +/// \param symbol_table: Global symbol table |
| 251 | +/// \param assume_non_null: If true, generated function stubs will never |
| 252 | +/// initialize reference members with null. |
| 253 | +/// \param object_factory_parameters: specifies exactly how nondet composite |
| 254 | +/// objects should be created-- for example, object graph depth. |
| 255 | +/// \param message_handler: Logging |
| 256 | +void java_generate_simple_method_stubs( |
| 257 | + symbol_table_baset &symbol_table, |
| 258 | + bool assume_non_null, |
| 259 | + const object_factory_parameterst &object_factory_parameters, |
| 260 | + message_handlert &message_handler) |
| 261 | +{ |
| 262 | + // The intent here is to apply stub_generator.check_method_stub() to |
| 263 | + // all the identifiers in the symbol table. However this method may alter the |
| 264 | + // symbol table and iterating over a container which is being modified has |
| 265 | + // undefined behaviour. Therefore in order for this to work reliably, we must |
| 266 | + // take a copy of the identifiers in the symbol table and iterate over that, |
| 267 | + // instead of iterating over the symbol table itself. |
| 268 | + std::vector<irep_idt> identifiers; |
| 269 | + identifiers.reserve(symbol_table.symbols.size()); |
| 270 | + for(const auto &symbol : symbol_table) |
| 271 | + identifiers.push_back(symbol.first); |
| 272 | + |
| 273 | + java_simple_method_stubst stub_generator( |
| 274 | + symbol_table, assume_non_null, object_factory_parameters, message_handler); |
| 275 | + |
| 276 | + for(const auto &identifier : identifiers) |
| 277 | + { |
| 278 | + stub_generator.check_method_stub(identifier); |
| 279 | + } |
| 280 | +} |
0 commit comments