CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
string_dependencies.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: String solver
4
5Author: Diffblue Ltd.
6
7\*******************************************************************/
8
12#include <unordered_set>
13#include <util/expr_iterator.h>
14#include <util/graph.h>
15#include <util/ssa_expr.h>
16
19static void for_each_atomic_string(
20 const array_string_exprt &e,
21 const std::function<void(const array_string_exprt &)> f);
22
25static std::unique_ptr<string_builtin_functiont> to_string_builtin_function(
27 const exprt &return_code,
28 array_poolt &array_pool)
29{
30 const auto name = expr_checked_cast<symbol_exprt>(fun_app.function());
32
33 const irep_idt &id = name.get_identifier();
34
36 return std::make_unique<string_insertion_builtin_functiont>(
37 return_code, fun_app.arguments(), array_pool);
38
40 return std::make_unique<string_concatenation_builtin_functiont>(
41 return_code, fun_app.arguments(), array_pool);
42
44 return std::make_unique<string_concat_char_builtin_functiont>(
45 return_code, fun_app.arguments(), array_pool);
46
48 return std::make_unique<string_format_builtin_functiont>(
49 return_code, fun_app.arguments(), array_pool);
50
52 return std::make_unique<string_of_int_builtin_functiont>(
53 return_code, fun_app.arguments(), array_pool);
54
56 return std::make_unique<string_set_char_builtin_functiont>(
57 return_code, fun_app.arguments(), array_pool);
58
60 return std::make_unique<string_to_lower_case_builtin_functiont>(
61 return_code, fun_app.arguments(), array_pool);
62
64 return std::make_unique<string_to_upper_case_builtin_functiont>(
65 return_code, fun_app.arguments(), array_pool);
66
67 return std::make_unique<string_builtin_function_with_no_evalt>(
68 return_code, fun_app, array_pool);
69}
70
73{
74 auto entry_inserted = node_index_pool.emplace(e, string_nodes.size());
75 if(!entry_inserted.second)
76 return string_nodes[entry_inserted.first->second];
77
78 string_nodes.emplace_back(e, entry_inserted.first->second);
79 return string_nodes.back();
80}
81
82std::unique_ptr<const string_dependenciest::string_nodet>
84{
85 const auto &it = node_index_pool.find(e);
86 if(it != node_index_pool.end())
87 return std::make_unique<const string_nodet>(string_nodes.at(it->second));
88 return {};
89}
90
92 std::unique_ptr<string_builtin_functiont> builtin_function)
93{
94 builtin_function_nodes.emplace_back(
95 std::move(builtin_function), builtin_function_nodes.size());
96 return builtin_function_nodes.back();
97}
98
104
106 const array_string_exprt &e,
107 const std::function<void(const array_string_exprt &)> f)
108{
109 if(e.id() != ID_if)
110 return f(e);
111
112 const auto if_expr = to_if_expr(e);
115}
116
118 const array_string_exprt &e,
120{
121 for_each_atomic_string(e, [&](const array_string_exprt &s) { //NOLINT
123 string_node.dependencies.push_back(builtin_function_node.index);
124 });
125}
126
134
136 string_dependenciest &dependencies,
139 array_poolt &array_pool)
140{
141 PRECONDITION(fun_app.arguments()[0].type().id() != ID_pointer);
142 if(
143 fun_app.arguments().size() > 1 &&
144 fun_app.arguments()[1].type().id() == ID_pointer)
145 {
146 // Case where the result is given as a pointer argument
147 const array_string_exprt string =
148 array_pool.find(fun_app.arguments()[1], fun_app.arguments()[0]);
149 dependencies.add_dependency(string, builtin_function_node);
150 }
151
152 for(const auto &expr : fun_app.arguments())
153 {
154 std::for_each(
155 expr.depth_begin(),
156 expr.depth_end(),
157 [&](const exprt &e) { // NOLINT
158 if(is_refined_string_type(e.type()))
159 {
160 const auto string_struct = expr_checked_cast<struct_exprt>(e);
161 const auto string = of_argument(array_pool, string_struct);
162 dependencies.add_dependency(string, builtin_function_node);
163 }
164 });
165 }
166}
167
168std::optional<exprt> string_dependenciest::eval(
169 const array_string_exprt &s,
170 const std::function<exprt(const exprt &)> &get_value) const
171{
172 const auto &it = node_index_pool.find(s);
173 if(it == node_index_pool.end())
174 return {};
175
176 if(eval_string_cache[it->second])
177 return eval_string_cache[it->second];
178
179 const auto node = string_nodes[it->second];
180 const auto &f = node.result_from;
181 if(f && node.dependencies.size() == 1)
182 {
183 const auto value_opt = builtin_function_nodes[*f].data->eval(get_value);
184 eval_string_cache[it->second] = value_opt;
185 return value_opt;
186 }
187 return {};
188}
189
191{
192 eval_string_cache.resize(string_nodes.size());
193 for(auto &e : eval_string_cache)
194 e.reset();
195}
196
197std::optional<exprt> add_node(
198 string_dependenciest &dependencies,
199 const exprt &expr,
200 array_poolt &array_pool,
201 symbol_generatort &fresh_symbol)
202{
204 if(!fun_app)
205 {
206 exprt copy = expr;
207 bool copy_differs_from_expr = false;
208 for(std::size_t i = 0; i < expr.operands().size(); ++i)
209 {
210 auto new_op =
211 add_node(dependencies, expr.operands()[i], array_pool, fresh_symbol);
212 if(new_op)
213 {
214 copy.operands()[i] = *new_op;
216 }
217 }
219 return copy;
220 return {};
221 }
222
223 const exprt return_code = fresh_symbol("string_builtin_return", expr.type());
224 auto builtin_function =
225 to_string_builtin_function(*fun_app, return_code, array_pool);
226
227 CHECK_RETURN(builtin_function != nullptr);
228 const auto &builtin_function_node =
229 dependencies.make_node(std::move(builtin_function));
230
231 if(
232 const auto &string_result =
233 dependencies.get_builtin_function(builtin_function_node).string_result())
234 {
235 dependencies.add_dependency(*string_result, builtin_function_node);
236 auto &node = dependencies.get_node(*string_result);
238
239 // Ensure all atomic strings in the argument have an associated node
240 for(const auto &arg : builtin_function_node.data->string_arguments())
241 {
243 arg, [&](const array_string_exprt &atomic) { // NOLINT
244 (void)dependencies.get_node(atomic);
245 });
246 }
247 }
248 else
250 dependencies, *fun_app, builtin_function_node, array_pool);
251
252 return return_code;
253}
254
256 const builtin_function_nodet &node,
257 const std::function<void(const string_nodet &)> &f) const
258{
259 for(const auto &s : node.data->string_arguments())
260 {
261 std::vector<std::reference_wrapper<const exprt>> stack({s});
262 while(!stack.empty())
263 {
264 const auto current = stack.back();
265 stack.pop_back();
266 if(const auto if_expr = expr_try_dynamic_cast<if_exprt>(current.get()))
267 {
268 stack.emplace_back(if_expr->true_case());
269 stack.emplace_back(if_expr->false_case());
270 }
271 else
272 {
273 const auto string_node = node_at(to_array_string_expr(current));
274 INVARIANT(
276 "dependencies of the node should have been added to the graph at "
277 "node creation " +
278 current.get().pretty());
279 f(*string_node);
280 }
281 }
282 }
283}
284
286 const string_nodet &node,
287 const std::function<void(const builtin_function_nodet &)> &f) const
288{
289 for(const std::size_t &index : node.dependencies)
290 f(builtin_function_nodes[index]);
291}
292
294 const nodet &node,
295 const std::function<void(const nodet &)> &f) const
296{
297 switch(node.kind)
298 {
299 case nodet::BUILTIN:
302 [&](const string_nodet &n) { return f(nodet(n)); });
303 break;
304
305 case nodet::STRING:
307 string_nodes[node.index],
308 [&](const builtin_function_nodet &n) { return f(nodet(n)); });
309 break;
310 }
311}
312
314 const std::function<void(const nodet &)> &f) const
315{
316 for(const auto &string_node : string_nodes)
317 f(nodet(string_node));
318 for(std::size_t i = 0; i < builtin_function_nodes.size(); ++i)
320}
321
323{
324 const auto for_each =
325 [&](const std::function<void(const nodet &)> &f) { // NOLINT
326 for_each_node(f);
327 };
328 const auto for_each_succ =
329 [&](const nodet &n, const std::function<void(const nodet &)> &f) { // NOLINT
331 };
332 const auto node_to_string = [&](const nodet &n) { // NOLINT
333 std::stringstream ostream;
334 if(n.kind == nodet::BUILTIN)
335 ostream << '"' << builtin_function_nodes[n.index].data->name() << '_'
336 << n.index << '"';
337 else
338 ostream << '"' << format(string_nodes[n.index].expr) << '"';
339 return ostream.str();
340 };
341 stream << "digraph dependencies {\n";
344 stream << '}' << std::endl;
345}
346
349 message_handlert &message_handler)
350{
351 std::unordered_set<nodet, node_hash> test_dependencies;
352 for(const auto &builtin : builtin_function_nodes)
353 {
354 if(builtin.data->maybe_testing_function())
356 }
357
360 [&](
361 const nodet &n,
362 const std::function<void(const nodet &)> &f) { // NOLINT
364 });
365
366 string_constraintst constraints;
367 for(const auto &node : builtin_function_nodes)
368 {
369 if(test_dependencies.count(nodet(node)))
370 {
371 const auto &builtin = builtin_function_nodes[node.index];
372 merge(constraints, builtin.data->constraints(generator, message_handler));
373 }
374 else
375 constraints.existential.push_back(node.data->length_constraint());
376 }
377 return constraints;
378}
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
Correspondance between arrays and pointers string representations.
Definition array_pool.h:42
array_string_exprt find(const exprt &pointer, const exprt &length)
Creates a new array if the pointer is not pointing to an array.
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:38
Base class for all expressions.
Definition expr.h:56
typet & type()
Return the type of the expression.
Definition expr.h:84
operandst & operands()
Definition expr.h:94
Application of (mathematical) function.
const irep_idt & id() const
Definition irep.h:388
Base class for string functions that are built in the solver.
A builtin function node contains a builtin function call.
std::unique_ptr< string_builtin_functiont > data
enum string_dependenciest::nodet::@6 kind
A string node points to builtin_function on which it depends.
std::optional< std::size_t > result_from
std::vector< std::size_t > dependencies
Keep track of dependencies between strings.
std::unique_ptr< const string_nodet > node_at(const array_string_exprt &e) const
std::vector< std::optional< exprt > > eval_string_cache
void for_each_node(const std::function< void(const nodet &)> &f) const
Applies f on all nodes.
builtin_function_nodet & make_node(std::unique_ptr< string_builtin_functiont > builtin_function)
void output_dot(std::ostream &stream) const
void clean_cache()
Clean the cache used by eval
std::vector< builtin_function_nodet > builtin_function_nodes
Set of nodes representing builtin_functions.
std::optional< exprt > eval(const array_string_exprt &s, const std::function< exprt(const exprt &)> &get_value) const
Attempt to evaluate the given string from the dependencies and valuation of strings on which it depen...
void clear()
Clear the content of the dependency graph.
const string_builtin_functiont & get_builtin_function(const builtin_function_nodet &node) const
std::unordered_map< array_string_exprt, std::size_t, irep_hash > node_index_pool
Nodes describing dependencies of a string: values of the map correspond to indexes in the vector stri...
std::vector< string_nodet > string_nodes
Set of nodes representing strings.
void for_each_dependency(const string_nodet &node, const std::function< void(const builtin_function_nodet &)> &f) const
Applies f to each node on which node depends.
string_constraintst add_constraints(string_constraint_generatort &generatort, message_handlert &message_handler)
For all builtin call on which a test (or an unsupported buitin) result depends, add the corresponding...
void for_each_successor(const nodet &i, const std::function< void(const nodet &)> &f) const
Applies f on all successors of the node n.
void add_dependency(const array_string_exprt &e, const builtin_function_nodet &builtin_function)
Add edge from node for e to node for builtin_function if e is a simple array expression.
string_nodet & get_node(const array_string_exprt &e)
Generation of fresh symbols of a given type.
Definition array_pool.h:22
Forward depth-first search iterators These iterators' copy operations are expensive,...
static format_containert< T > format(const T &o)
Definition format.h:37
A Template Class for Graphs.
void get_reachable(Container &set, const std::function< void(const typename Container::value_type &, const std::function< void(const typename Container::value_type &)> &)> &for_each_successor)
Add to set, nodes that are reachable from set.
Definition graph.h:573
#define CHECK_RETURN(CONDITION)
Definition invariant.h:495
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
bool is_ssa_expr(const exprt &expr)
Definition ssa_expr.h:125
const if_exprt & to_if_expr(const exprt &expr)
Cast an exprt to an if_exprt.
Definition std_expr.h:2577
Builtin functions for string concatenations.
void merge(string_constraintst &result, string_constraintst other)
Merge two sets of constraints by appending to the first one.
static void for_each_atomic_string(const array_string_exprt &e, const std::function< void(const array_string_exprt &)> f)
Applies f on all strings contained in e that are not if-expressions.
static std::unique_ptr< string_builtin_functiont > to_string_builtin_function(const function_application_exprt &fun_app, const exprt &return_code, array_poolt &array_pool)
Construct a string_builtin_functiont object from a function application.
static void add_dependency_to_string_subexprs(string_dependenciest &dependencies, const function_application_exprt &fun_app, const string_dependenciest::builtin_function_nodet &builtin_function_node, array_poolt &array_pool)
std::optional< exprt > add_node(string_dependenciest &dependencies, const exprt &expr, array_poolt &array_pool, symbol_generatort &fresh_symbol)
When a sub-expression of expr is a builtin_function, add a "string_builtin_function" node to the grap...
Keeps track of dependencies between strings.
array_string_exprt & to_array_string_expr(exprt &expr)
Definition string_expr.h:96
Built-in function for String.format.
Collection of constraints of different types: existential formulas, universal formulas,...