Skip to content

Commit 73eaeb6

Browse files
author
Joel Allred
committed
Extract string_dependencies into its own files
Because we can.
1 parent ce8bc12 commit 73eaeb6

File tree

8 files changed

+567
-523
lines changed

8 files changed

+567
-523
lines changed

jbmc/unit/solvers/strings/string_refinement/dependency_graph.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Author: Diffblue Ltd.
1111

1212
#include <java_bytecode/java_types.h>
1313
#include <solvers/strings/string_refinement_util.h>
14+
#include <solvers/strings/string_dependencies.h>
1415
#include <util/arith_tools.h>
1516
#include <util/std_expr.h>
1617
#include <util/std_types.h>

src/solvers/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ SRC = $(BOOLEFORCE_SRC) \
162162
strings/array_pool.cpp \
163163
strings/equation_symbol_mapping.cpp \
164164
strings/string_builtin_function.cpp \
165+
strings/string_dependencies.cpp \
165166
strings/string_refinement.cpp \
166167
strings/string_refinement_util.cpp \
167168
strings/string_constraint.cpp \
Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
/*******************************************************************\
2+
3+
Module: String solver
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
#include "string_dependencies.h"
10+
#include <algorithm>
11+
#include <functional>
12+
#include <iostream>
13+
#include <numeric>
14+
#include <unordered_set>
15+
#include <util/arith_tools.h>
16+
#include <util/expr_iterator.h>
17+
#include <util/expr_util.h>
18+
#include <util/graph.h>
19+
#include <util/magic.h>
20+
#include <util/make_unique.h>
21+
#include <util/ssa_expr.h>
22+
#include <util/std_expr.h>
23+
24+
/// Applies `f` on all strings contained in `e` that are not if-expressions.
25+
/// For instance on input `cond1?s1:cond2?s2:s3` we apply `f` on s1, s2 and s3.
26+
static void for_each_atomic_string(
27+
const array_string_exprt &e,
28+
const std::function<void(const array_string_exprt &)> f);
29+
30+
/// Construct a string_builtin_functiont object from a function application
31+
/// \return a unique pointer to the created object
32+
static std::unique_ptr<string_builtin_functiont> to_string_builtin_function(
33+
const function_application_exprt &fun_app,
34+
const exprt &return_code,
35+
array_poolt &array_pool)
36+
{
37+
const auto name = expr_checked_cast<symbol_exprt>(fun_app.function());
38+
PRECONDITION(!is_ssa_expr(name));
39+
40+
const irep_idt &id = name.get_identifier();
41+
42+
if(id == ID_cprover_string_insert_func)
43+
return util_make_unique<string_insertion_builtin_functiont>(
44+
return_code, fun_app.arguments(), array_pool);
45+
46+
if(id == ID_cprover_string_concat_func)
47+
return util_make_unique<string_concatenation_builtin_functiont>(
48+
return_code, fun_app.arguments(), array_pool);
49+
50+
if(id == ID_cprover_string_concat_char_func)
51+
return util_make_unique<string_concat_char_builtin_functiont>(
52+
return_code, fun_app.arguments(), array_pool);
53+
54+
if(id == ID_cprover_string_of_int_func)
55+
return util_make_unique<string_of_int_builtin_functiont>(
56+
return_code, fun_app.arguments(), array_pool);
57+
58+
if(id == ID_cprover_string_char_set_func)
59+
return util_make_unique<string_set_char_builtin_functiont>(
60+
return_code, fun_app.arguments(), array_pool);
61+
62+
if(id == ID_cprover_string_to_lower_case_func)
63+
return util_make_unique<string_to_lower_case_builtin_functiont>(
64+
return_code, fun_app.arguments(), array_pool);
65+
66+
if(id == ID_cprover_string_to_upper_case_func)
67+
return util_make_unique<string_to_upper_case_builtin_functiont>(
68+
return_code, fun_app.arguments(), array_pool);
69+
70+
return util_make_unique<string_builtin_function_with_no_evalt>(
71+
return_code, fun_app, array_pool);
72+
}
73+
74+
string_dependenciest::string_nodet &
75+
string_dependenciest::get_node(const array_string_exprt &e)
76+
{
77+
auto entry_inserted = node_index_pool.emplace(e, string_nodes.size());
78+
if(!entry_inserted.second)
79+
return string_nodes[entry_inserted.first->second];
80+
81+
string_nodes.emplace_back(e, entry_inserted.first->second);
82+
return string_nodes.back();
83+
}
84+
85+
std::unique_ptr<const string_dependenciest::string_nodet>
86+
string_dependenciest::node_at(const array_string_exprt &e) const
87+
{
88+
const auto &it = node_index_pool.find(e);
89+
if(it != node_index_pool.end())
90+
return util_make_unique<const string_nodet>(string_nodes.at(it->second));
91+
return {};
92+
}
93+
94+
string_dependenciest::builtin_function_nodet &string_dependenciest::make_node(
95+
std::unique_ptr<string_builtin_functiont> &builtin_function)
96+
{
97+
builtin_function_nodes.emplace_back(
98+
std::move(builtin_function), builtin_function_nodes.size());
99+
return builtin_function_nodes.back();
100+
}
101+
102+
const string_builtin_functiont &string_dependenciest::get_builtin_function(
103+
const builtin_function_nodet &node) const
104+
{
105+
return *node.data;
106+
}
107+
108+
static void for_each_atomic_string(
109+
const array_string_exprt &e,
110+
const std::function<void(const array_string_exprt &)> f)
111+
{
112+
if(e.id() != ID_if)
113+
return f(e);
114+
115+
const auto if_expr = to_if_expr(e);
116+
for_each_atomic_string(to_array_string_expr(if_expr.true_case()), f);
117+
for_each_atomic_string(to_array_string_expr(if_expr.false_case()), f);
118+
}
119+
120+
void string_dependenciest::add_dependency(
121+
const array_string_exprt &e,
122+
const builtin_function_nodet &builtin_function_node)
123+
{
124+
for_each_atomic_string(e, [&](const array_string_exprt &s) { //NOLINT
125+
string_nodet &string_node = get_node(s);
126+
string_node.dependencies.push_back(builtin_function_node.index);
127+
});
128+
}
129+
130+
void string_dependenciest::clear()
131+
{
132+
builtin_function_nodes.clear();
133+
string_nodes.clear();
134+
node_index_pool.clear();
135+
clean_cache();
136+
}
137+
138+
static void add_dependency_to_string_subexprs(
139+
string_dependenciest &dependencies,
140+
const function_application_exprt &fun_app,
141+
const string_dependenciest::builtin_function_nodet &builtin_function_node,
142+
array_poolt &array_pool)
143+
{
144+
PRECONDITION(fun_app.arguments()[0].type().id() != ID_pointer);
145+
if(
146+
fun_app.arguments().size() > 1 &&
147+
fun_app.arguments()[1].type().id() == ID_pointer)
148+
{
149+
// Case where the result is given as a pointer argument
150+
const array_string_exprt string =
151+
array_pool.find(fun_app.arguments()[1], fun_app.arguments()[0]);
152+
dependencies.add_dependency(string, builtin_function_node);
153+
}
154+
155+
for(const auto &expr : fun_app.arguments())
156+
{
157+
std::for_each(
158+
expr.depth_begin(),
159+
expr.depth_end(),
160+
[&](const exprt &e) { // NOLINT
161+
if(is_refined_string_type(e.type()))
162+
{
163+
const auto string_struct = expr_checked_cast<struct_exprt>(e);
164+
const auto string = of_argument(array_pool, string_struct);
165+
dependencies.add_dependency(string, builtin_function_node);
166+
}
167+
});
168+
}
169+
}
170+
171+
optionalt<exprt> string_dependenciest::eval(
172+
const array_string_exprt &s,
173+
const std::function<exprt(const exprt &)> &get_value) const
174+
{
175+
const auto &it = node_index_pool.find(s);
176+
if(it == node_index_pool.end())
177+
return {};
178+
179+
if(eval_string_cache[it->second])
180+
return eval_string_cache[it->second];
181+
182+
const auto node = string_nodes[it->second];
183+
const auto &f = node.result_from;
184+
if(f && node.dependencies.size() == 1)
185+
{
186+
const auto value_opt = builtin_function_nodes[*f].data->eval(get_value);
187+
eval_string_cache[it->second] = value_opt;
188+
return value_opt;
189+
}
190+
return {};
191+
}
192+
193+
void string_dependenciest::clean_cache()
194+
{
195+
eval_string_cache.resize(string_nodes.size());
196+
for(auto &e : eval_string_cache)
197+
e.reset();
198+
}
199+
200+
bool add_node(
201+
string_dependenciest &dependencies,
202+
const equal_exprt &equation,
203+
array_poolt &array_pool)
204+
{
205+
const auto fun_app =
206+
expr_try_dynamic_cast<function_application_exprt>(equation.rhs());
207+
if(!fun_app)
208+
return false;
209+
210+
auto builtin_function =
211+
to_string_builtin_function(*fun_app, equation.lhs(), array_pool);
212+
213+
CHECK_RETURN(builtin_function != nullptr);
214+
const auto &builtin_function_node = dependencies.make_node(builtin_function);
215+
// Warning: `builtin_function` has been emptied and should not be used anymore
216+
217+
if(
218+
const auto &string_result =
219+
dependencies.get_builtin_function(builtin_function_node).string_result())
220+
{
221+
dependencies.add_dependency(*string_result, builtin_function_node);
222+
auto &node = dependencies.get_node(*string_result);
223+
node.result_from = builtin_function_node.index;
224+
225+
// Ensure all atomic strings in the argument have an associated node
226+
for(const auto arg : builtin_function_node.data->string_arguments())
227+
{
228+
for_each_atomic_string(
229+
arg, [&](const array_string_exprt &atomic) { // NOLINT
230+
(void)dependencies.get_node(atomic);
231+
});
232+
}
233+
}
234+
else
235+
add_dependency_to_string_subexprs(
236+
dependencies, *fun_app, builtin_function_node, array_pool);
237+
238+
return true;
239+
}
240+
241+
void string_dependenciest::for_each_dependency(
242+
const builtin_function_nodet &node,
243+
const std::function<void(const string_nodet &)> &f) const
244+
{
245+
for(const auto &s : node.data->string_arguments())
246+
{
247+
std::vector<std::reference_wrapper<const exprt>> stack({s});
248+
while(!stack.empty())
249+
{
250+
const auto current = stack.back();
251+
stack.pop_back();
252+
if(const auto if_expr = expr_try_dynamic_cast<if_exprt>(current.get()))
253+
{
254+
stack.emplace_back(if_expr->true_case());
255+
stack.emplace_back(if_expr->false_case());
256+
}
257+
else
258+
{
259+
const auto string_node = node_at(to_array_string_expr(current));
260+
INVARIANT(
261+
string_node,
262+
"dependencies of the node should have been added to the graph at "
263+
"node creation " +
264+
current.get().pretty());
265+
f(*string_node);
266+
}
267+
}
268+
}
269+
}
270+
271+
void string_dependenciest::for_each_dependency(
272+
const string_nodet &node,
273+
const std::function<void(const builtin_function_nodet &)> &f) const
274+
{
275+
for(const std::size_t &index : node.dependencies)
276+
f(builtin_function_nodes[index]);
277+
}
278+
279+
void string_dependenciest::for_each_successor(
280+
const nodet &node,
281+
const std::function<void(const nodet &)> &f) const
282+
{
283+
switch(node.kind)
284+
{
285+
case nodet::BUILTIN:
286+
for_each_dependency(
287+
builtin_function_nodes[node.index],
288+
[&](const string_nodet &n) { return f(nodet(n)); });
289+
break;
290+
291+
case nodet::STRING:
292+
for_each_dependency(
293+
string_nodes[node.index],
294+
[&](const builtin_function_nodet &n) { return f(nodet(n)); });
295+
break;
296+
}
297+
}
298+
299+
void string_dependenciest::for_each_node(
300+
const std::function<void(const nodet &)> &f) const
301+
{
302+
for(const auto string_node : string_nodes)
303+
f(nodet(string_node));
304+
for(std::size_t i = 0; i < builtin_function_nodes.size(); ++i)
305+
f(nodet(builtin_function_nodes[i]));
306+
}
307+
308+
void string_dependenciest::output_dot(std::ostream &stream) const
309+
{
310+
const auto for_each =
311+
[&](const std::function<void(const nodet &)> &f) { // NOLINT
312+
for_each_node(f);
313+
};
314+
const auto for_each_succ =
315+
[&](const nodet &n, const std::function<void(const nodet &)> &f) { // NOLINT
316+
for_each_successor(n, f);
317+
};
318+
const auto node_to_string = [&](const nodet &n) { // NOLINT
319+
std::stringstream ostream;
320+
if(n.kind == nodet::BUILTIN)
321+
ostream << '"' << builtin_function_nodes[n.index].data->name() << '_'
322+
<< n.index << '"';
323+
else
324+
ostream << '"' << format(string_nodes[n.index].expr) << '"';
325+
return ostream.str();
326+
};
327+
stream << "digraph dependencies {\n";
328+
output_dot_generic<nodet>(
329+
stream, for_each, for_each_succ, node_to_string, node_to_string);
330+
stream << '}' << std::endl;
331+
}
332+
333+
void string_dependenciest::add_constraints(
334+
string_constraint_generatort &generator)
335+
{
336+
std::unordered_set<nodet, node_hash> test_dependencies;
337+
for(const auto &builtin : builtin_function_nodes)
338+
{
339+
if(builtin.data->maybe_testing_function())
340+
test_dependencies.insert(nodet(builtin));
341+
}
342+
343+
get_reachable(
344+
test_dependencies,
345+
[&](
346+
const nodet &n,
347+
const std::function<void(const nodet &)> &f) { // NOLINT
348+
for_each_successor(n, f);
349+
});
350+
351+
for(const auto &node : builtin_function_nodes)
352+
{
353+
if(test_dependencies.count(nodet(node)))
354+
{
355+
const auto &builtin = builtin_function_nodes[node.index];
356+
string_constraintst constraints = builtin.data->constraints(generator);
357+
merge(generator.constraints, std::move(constraints));
358+
}
359+
else
360+
generator.constraints.existential.push_back(
361+
node.data->length_constraint());
362+
}
363+
}

0 commit comments

Comments
 (0)