|
8 | 8 |
|
9 | 9 | #include <algorithm>
|
10 | 10 | #include <numeric>
|
| 11 | +#include <functional> |
| 12 | +#include <iostream> |
11 | 13 | #include <util/arith_tools.h>
|
| 14 | +#include <util/ssa_expr.h> |
12 | 15 | #include <util/std_expr.h>
|
13 | 16 | #include <util/expr_iterator.h>
|
| 17 | +#include <util/graph.h> |
14 | 18 | #include <util/magic.h>
|
15 | 19 | #include "string_refinement_util.h"
|
16 | 20 |
|
@@ -157,3 +161,270 @@ equation_symbol_mappingt::find_equations(const exprt &expr)
|
157 | 161 | {
|
158 | 162 | return equations_containing[expr];
|
159 | 163 | }
|
| 164 | + |
| 165 | +string_insertion_builtin_functiont::string_insertion_builtin_functiont( |
| 166 | + const std::vector<exprt> &fun_args, |
| 167 | + array_poolt &array_pool) |
| 168 | +{ |
| 169 | + PRECONDITION(fun_args.size() > 3); |
| 170 | + const auto arg1 = expr_checked_cast<struct_exprt>(fun_args[2]); |
| 171 | + input1 = array_pool.find(arg1.op1(), arg1.op0()); |
| 172 | + const auto arg2 = expr_checked_cast<struct_exprt>(fun_args[3]); |
| 173 | + input2 = array_pool.find(arg2.op1(), arg2.op0()); |
| 174 | + result = array_pool.find(fun_args[1], fun_args[0]); |
| 175 | + args.insert(args.end(), fun_args.begin() + 4, fun_args.end()); |
| 176 | +} |
| 177 | + |
| 178 | +/// Construct a string_builtin_functiont object from a function application |
| 179 | +/// \return a unique pointer to the created object, this unique pointer is empty |
| 180 | +/// if the function does not correspond to one of the supported |
| 181 | +/// builtin_functions. |
| 182 | +static std::unique_ptr<string_builtin_functiont> to_string_builtin_function( |
| 183 | + const function_application_exprt &fun_app, |
| 184 | + array_poolt &array_pool) |
| 185 | +{ |
| 186 | + const auto name = expr_checked_cast<symbol_exprt>(fun_app.function()); |
| 187 | + const irep_idt &id = is_ssa_expr(name) ? to_ssa_expr(name).get_object_name() |
| 188 | + : name.get_identifier(); |
| 189 | + |
| 190 | + if(id == ID_cprover_string_insert_func) |
| 191 | + return std::unique_ptr<string_builtin_functiont>( |
| 192 | + new string_insertion_builtin_functiont(fun_app.arguments(), array_pool)); |
| 193 | + |
| 194 | + if(id == ID_cprover_string_concat_func) |
| 195 | + return std::unique_ptr<string_builtin_functiont>( |
| 196 | + new string_insertion_builtin_functiont(fun_app.arguments(), array_pool)); |
| 197 | + |
| 198 | + return {}; |
| 199 | +} |
| 200 | + |
| 201 | +string_dependencest::string_nodet & |
| 202 | +string_dependencest::get_node(const array_string_exprt &e) |
| 203 | +{ |
| 204 | + auto entry_inserted = node_index_pool.emplace(e, string_nodes.size()); |
| 205 | + if(!entry_inserted.second) |
| 206 | + return string_nodes[entry_inserted.first->second]; |
| 207 | + |
| 208 | + string_nodes.emplace_back(); |
| 209 | + return string_nodes.back(); |
| 210 | +} |
| 211 | + |
| 212 | +string_dependencest::builtin_function_nodet string_dependencest::make_node( |
| 213 | + std::unique_ptr<string_builtin_functiont> &builtin_function) |
| 214 | +{ |
| 215 | + const builtin_function_nodet builtin_function_node( |
| 216 | + builtin_function_nodes.size()); |
| 217 | + builtin_function_nodes.emplace_back(); |
| 218 | + builtin_function_nodes.back().swap(builtin_function); |
| 219 | + return builtin_function_node; |
| 220 | +} |
| 221 | + |
| 222 | +const string_builtin_functiont &string_dependencest::get_builtin_function( |
| 223 | + const builtin_function_nodet &node) const |
| 224 | +{ |
| 225 | + PRECONDITION(node.index < builtin_function_nodes.size()); |
| 226 | + return *(builtin_function_nodes[node.index]); |
| 227 | +} |
| 228 | + |
| 229 | +const std::vector<string_dependencest::builtin_function_nodet> & |
| 230 | +string_dependencest::dependencies( |
| 231 | + const string_dependencest::string_nodet &node) const |
| 232 | +{ |
| 233 | + return node.dependencies; |
| 234 | +} |
| 235 | + |
| 236 | +void string_dependencest::add_dependency( |
| 237 | + const array_string_exprt &e, |
| 238 | + const builtin_function_nodet &builtin_function_node) |
| 239 | +{ |
| 240 | + string_nodet &string_node = get_node(e); |
| 241 | + string_node.dependencies.push_back(builtin_function_node); |
| 242 | +} |
| 243 | + |
| 244 | +void string_dependencest::add_unknown_dependency(const array_string_exprt &e) |
| 245 | +{ |
| 246 | + string_nodet &string_node = get_node(e); |
| 247 | + string_node.depends_on_unknown_builtin_function = true; |
| 248 | +} |
| 249 | + |
| 250 | +static void add_unknown_dependency_to_string_subexprs( |
| 251 | + string_dependencest &dependencies, |
| 252 | + const function_application_exprt &fun_app, |
| 253 | + array_poolt &array_pool) |
| 254 | +{ |
| 255 | + for(const auto &expr : fun_app.arguments()) |
| 256 | + { |
| 257 | + std::for_each( |
| 258 | + expr.depth_begin(), expr.depth_end(), [&](const exprt &e) { // NOLINT |
| 259 | + const auto &string_struct = expr_try_dynamic_cast<struct_exprt>(e); |
| 260 | + if(string_struct && string_struct->operands().size() == 2) |
| 261 | + { |
| 262 | + const array_string_exprt string = |
| 263 | + array_pool.find(string_struct->op1(), string_struct->op0()); |
| 264 | + dependencies.add_unknown_dependency(string); |
| 265 | + } |
| 266 | + }); |
| 267 | + } |
| 268 | +} |
| 269 | + |
| 270 | +static void add_dependency_to_string_subexprs( |
| 271 | + string_dependencest &dependencies, |
| 272 | + const function_application_exprt &fun_app, |
| 273 | + const string_dependencest::builtin_function_nodet &builtin_function_node, |
| 274 | + array_poolt &array_pool) |
| 275 | +{ |
| 276 | + PRECONDITION(fun_app.arguments()[0].type().id() != ID_pointer); |
| 277 | + if( |
| 278 | + fun_app.arguments().size() > 1 && |
| 279 | + fun_app.arguments()[1].type().id() == ID_pointer) |
| 280 | + { |
| 281 | + // Case where the result is given as a pointer argument |
| 282 | + const array_string_exprt string = |
| 283 | + array_pool.find(fun_app.arguments()[1], fun_app.arguments()[0]); |
| 284 | + dependencies.add_dependency(string, builtin_function_node); |
| 285 | + } |
| 286 | + |
| 287 | + for(const auto &expr : fun_app.arguments()) |
| 288 | + { |
| 289 | + std::for_each( |
| 290 | + expr.depth_begin(), |
| 291 | + expr.depth_end(), |
| 292 | + [&](const exprt &e) { // NOLINT |
| 293 | + if(const auto structure = expr_try_dynamic_cast<struct_exprt>(e)) |
| 294 | + { |
| 295 | + const array_string_exprt string = array_pool.of_argument(*structure); |
| 296 | + dependencies.add_dependency(string, builtin_function_node); |
| 297 | + } |
| 298 | + }); |
| 299 | + } |
| 300 | +} |
| 301 | + |
| 302 | +string_dependencest::node_indext string_dependencest::size() const |
| 303 | +{ |
| 304 | + return builtin_function_nodes.size() + string_nodes.size(); |
| 305 | +} |
| 306 | + |
| 307 | +/// Convert an index of a string node in `string_nodes` to the node_indext for |
| 308 | +/// the same node |
| 309 | +static std::size_t string_index_to_node_index(const std::size_t string_index) |
| 310 | +{ |
| 311 | + return 2 * string_index + 1; |
| 312 | +} |
| 313 | + |
| 314 | +/// Convert an index of a builtin function node to the node_indext for |
| 315 | +/// the same node |
| 316 | +static std::size_t |
| 317 | +builtin_function_index_to_node_index(const std::size_t builtin_index) |
| 318 | +{ |
| 319 | + return 2 * builtin_index; |
| 320 | +} |
| 321 | + |
| 322 | +string_dependencest::node_indext |
| 323 | +string_dependencest::node_index(const builtin_function_nodet &n) const |
| 324 | +{ |
| 325 | + return builtin_function_index_to_node_index(n.index); |
| 326 | +} |
| 327 | + |
| 328 | +string_dependencest::node_indext |
| 329 | +string_dependencest::node_index(const array_string_exprt &s) const |
| 330 | +{ |
| 331 | + return string_index_to_node_index(node_index_pool.at(s)); |
| 332 | +} |
| 333 | + |
| 334 | +optionalt<string_dependencest::builtin_function_nodet> |
| 335 | +string_dependencest::get_builtin_function_node(node_indext i) const |
| 336 | +{ |
| 337 | + if(i % 2 == 0) |
| 338 | + return builtin_function_nodet(i / 2); |
| 339 | + return {}; |
| 340 | +} |
| 341 | + |
| 342 | +optionalt<string_dependencest::string_nodet> |
| 343 | +string_dependencest::get_string_node(node_indext i) const |
| 344 | +{ |
| 345 | + if(i % 2 == 1 && i / 2 < string_nodes.size()) |
| 346 | + return string_nodes[i / 2]; |
| 347 | + return {}; |
| 348 | +} |
| 349 | + |
| 350 | +bool add_node( |
| 351 | + string_dependencest &dependencies, |
| 352 | + const equal_exprt &equation, |
| 353 | + array_poolt &array_pool) |
| 354 | +{ |
| 355 | + const auto fun_app = |
| 356 | + expr_try_dynamic_cast<function_application_exprt>(equation.rhs()); |
| 357 | + if(!fun_app) |
| 358 | + return false; |
| 359 | + |
| 360 | + auto builtin_function = to_string_builtin_function(*fun_app, array_pool); |
| 361 | + |
| 362 | + if(!builtin_function) |
| 363 | + { |
| 364 | + add_unknown_dependency_to_string_subexprs( |
| 365 | + dependencies, *fun_app, array_pool); |
| 366 | + return true; |
| 367 | + } |
| 368 | + |
| 369 | + const auto &builtin_function_node = dependencies.make_node(builtin_function); |
| 370 | + // Warning: `builtin_function` has been emptied and should not be used anymore |
| 371 | + |
| 372 | + if( |
| 373 | + const auto &string_result = |
| 374 | + dependencies.get_builtin_function(builtin_function_node).string_result()) |
| 375 | + dependencies.add_dependency(*string_result, builtin_function_node); |
| 376 | + else |
| 377 | + add_dependency_to_string_subexprs( |
| 378 | + dependencies, *fun_app, builtin_function_node, array_pool); |
| 379 | + |
| 380 | + return true; |
| 381 | +} |
| 382 | + |
| 383 | +void string_dependencest::for_each_successor( |
| 384 | + const std::size_t &i, |
| 385 | + const std::function<void(const std::size_t &)> &f) const |
| 386 | +{ |
| 387 | + if(const auto &builtin_function_node = get_builtin_function_node(i)) |
| 388 | + { |
| 389 | + const string_builtin_functiont &p = |
| 390 | + get_builtin_function(*builtin_function_node); |
| 391 | + std::for_each( |
| 392 | + p.string_arguments().begin(), |
| 393 | + p.string_arguments().end(), |
| 394 | + [&](const array_string_exprt &s) { f(node_index(s)); }); |
| 395 | + } |
| 396 | + else if(const auto &s = get_string_node(i)) |
| 397 | + { |
| 398 | + std::for_each( |
| 399 | + s->dependencies.begin(), |
| 400 | + s->dependencies.end(), |
| 401 | + [&](const builtin_function_nodet &p) { f(node_index(p)); }); |
| 402 | + } |
| 403 | + else |
| 404 | + UNREACHABLE; |
| 405 | +} |
| 406 | + |
| 407 | +void string_dependencest::output_dot(std::ostream &stream) const |
| 408 | +{ |
| 409 | + const auto for_each_node = |
| 410 | + [&](const std::function<void(const std::size_t &)> &f) { // NOLINT |
| 411 | + for(std::size_t i = 0; i < string_nodes.size(); ++i) |
| 412 | + f(string_index_to_node_index(i)); |
| 413 | + for(std::size_t i = 0; i < builtin_function_nodes.size(); ++i) |
| 414 | + f(builtin_function_index_to_node_index(i)); |
| 415 | + }; |
| 416 | + |
| 417 | + const auto for_each_succ = [&]( |
| 418 | + const std::size_t &i, |
| 419 | + const std::function<void(const std::size_t &)> &f) { // NOLINT |
| 420 | + for_each_successor(i, f); |
| 421 | + }; |
| 422 | + |
| 423 | + const auto node_to_string = [&](const std::size_t &i) { // NOLINT |
| 424 | + return std::to_string(i); |
| 425 | + }; |
| 426 | + stream << "digraph dependencies {\n"; |
| 427 | + output_dot_generic<std::size_t>( |
| 428 | + stream, for_each_node, for_each_succ, node_to_string); |
| 429 | + stream << '}' << std::endl; |
| 430 | +} |
0 commit comments