Skip to content

Commit 3f9d063

Browse files
author
Joel Allred
authored
Merge pull request #4695 from allredj/clean-up/extract-string-dependencies
Extract string dependencies into its own files
2 parents f117f25 + b2bdda5 commit 3f9d063

File tree

8 files changed

+560
-524
lines changed

8 files changed

+560
-524
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Author: Diffblue Ltd.
1010
#include <testing-utils/use_catch.h>
1111

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

0 commit comments

Comments
 (0)