Skip to content

Commit 16f63e8

Browse files
authored
Merge pull request #4781 from danpoe/feature/simplify-expression-index-of
[TG-8284] Expression simplification for indexOf()
2 parents 1e5fd65 + 7b7d526 commit 16f63e8

File tree

25 files changed

+290
-1
lines changed

25 files changed

+290
-1
lines changed
846 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
public class Main {
2+
public void constantIndexOf() {
3+
String s1 = "abcabc";
4+
String s2 = "bc";
5+
assert s1.indexOf(s2) == 1;
6+
assert s1.indexOf(s2, -10) == 1;
7+
assert s1.indexOf("") == 0;
8+
assert s1.indexOf(s2, 3) == 4;
9+
assert s1.indexOf("cd") == -1;
10+
assert s1.indexOf(s2, 10) == -1;
11+
assert s1.indexOf("", 10) == -1;
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf
4+
^Generated [0-9]+ VCC\(s\), 0 remaining after simplification$
5+
^VERIFICATION SUCCESSFUL$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
--
725 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
public class Main {
2+
public void constantIndexOf() {
3+
String s1 = "abcabc";
4+
assert s1.indexOf('b') == 1;
5+
assert s1.indexOf('b', -10) == 1;
6+
assert s1.indexOf('b', 3) == 4;
7+
assert s1.indexOf('d') == -1;
8+
assert s1.indexOf('b', 10) == -1;
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf
4+
^Generated [0-9]+ VCC\(s\), 0 remaining after simplification$
5+
^VERIFICATION SUCCESSFUL$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
--
629 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
public class Main {
2+
public void constantIndexOf(String arg) {
3+
String s1 = "abcabc";
4+
assert s1.indexOf(arg, 10) == -1;
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;)V.assertion.1"
4+
^Generated [0-9]+ VCC\(s\), 0 remaining after simplification$
5+
^VERIFICATION SUCCESSFUL$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
--
9+
--
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
public class Main {
2+
public void constantIndexOf(String s, char c, int i) {
3+
4+
String s1 = "abcabc";
5+
String s2 = "bc";
6+
7+
assert s1.indexOf(c) == 1;
8+
assert s1.indexOf(s) == 1;
9+
assert s1.indexOf(c, 1) == 1;
10+
assert s1.indexOf(s, 1) == 1;
11+
12+
assert s.indexOf('a') == 1;
13+
assert s.indexOf(s1) == 1;
14+
assert s.indexOf('a', 1) == 1;
15+
assert s.indexOf(s1, 1) == 1;
16+
17+
assert s1.indexOf(s, -10) == 1;
18+
assert s1.indexOf(c, -10) == 1;
19+
20+
assert s1.indexOf(s2, i) == 1;
21+
assert s1.indexOf('a', i) == 1;
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.1" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.10" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.11" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.12" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.2" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.3" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.4" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.5" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.6" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.7" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.8" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE symex-driven-lazy-loading-expected-failure
2+
Main.class
3+
--function Main.constantIndexOf --property "java::Main.constantIndexOf:(Ljava/lang/String;CI)V.assertion.9" --show-vcc
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--

Diff for: src/util/simplify_expr.cpp

+113
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,115 @@ static simplify_exprt::resultt<> simplify_string_compare_to(
262262
first_shorter ? char1 - char2 : char2 - char1, expr.type());
263263
}
264264

265+
/// Simplify String.indexOf function when arguments are constant
266+
///
267+
/// \param expr: the expression to simplify
268+
/// \param ns: namespace
269+
/// \return: the modified expression or an unchanged expression
270+
static simplify_exprt::resultt<> simplify_string_index_of(
271+
const function_application_exprt &expr,
272+
const namespacet &ns)
273+
{
274+
std::size_t starting_index = 0;
275+
276+
// Determine starting index for the comparison (if given)
277+
if(expr.arguments().size() == 3)
278+
{
279+
auto &starting_index_expr = expr.arguments().at(2);
280+
281+
if(starting_index_expr.id() != ID_constant)
282+
{
283+
return simplify_exprt::unchanged(expr);
284+
}
285+
286+
const mp_integer idx =
287+
numeric_cast_v<mp_integer>(to_constant_expr(starting_index_expr));
288+
289+
// Negative indices are treated like 0
290+
if(idx > 0)
291+
{
292+
starting_index = numeric_cast_v<std::size_t>(idx);
293+
}
294+
}
295+
296+
const refined_string_exprt &s1 = to_string_expr(expr.arguments().at(0));
297+
298+
const auto s1_data_opt = try_get_string_data_array(s1, ns);
299+
300+
if(!s1_data_opt)
301+
{
302+
return simplify_exprt::unchanged(expr);
303+
}
304+
305+
const array_exprt &s1_data = s1_data_opt->get();
306+
307+
if(starting_index >= s1_data.operands().size())
308+
{
309+
return from_integer(-1, expr.type());
310+
}
311+
312+
// Iterator pointing to the character in the first string at which the second
313+
// string or character was found
314+
exprt::operandst::const_iterator it;
315+
316+
if(can_cast_expr<refined_string_exprt>(expr.arguments().at(1)))
317+
{
318+
// Second argument is a string
319+
320+
const refined_string_exprt &s2 =
321+
to_string_expr(expr.arguments().at(1));
322+
323+
const auto s2_data_opt = try_get_string_data_array(s2, ns);
324+
325+
if(!s2_data_opt)
326+
{
327+
return simplify_exprt::unchanged(expr);
328+
}
329+
330+
const array_exprt &s2_data = s2_data_opt->get();
331+
332+
it = std::search(
333+
std::next(s1_data.operands().begin(), starting_index),
334+
s1_data.operands().end(),
335+
s2_data.operands().begin(),
336+
s2_data.operands().end());
337+
}
338+
else if(expr.arguments().at(1).id() == ID_constant)
339+
{
340+
// Second argument is a constant character
341+
342+
const constant_exprt &c1 = to_constant_expr(expr.arguments().at(1));
343+
const auto c1_val = numeric_cast_v<mp_integer>(c1);
344+
345+
auto pred = [&](const exprt &c2)
346+
{
347+
const auto c2_val = numeric_cast_v<mp_integer>(to_constant_expr(c2));
348+
349+
return c1_val == c2_val;
350+
};
351+
352+
it = std::find_if(
353+
std::next(s1_data.operands().begin(), starting_index),
354+
s1_data.operands().end(),
355+
pred);
356+
}
357+
else
358+
{
359+
return simplify_exprt::unchanged(expr);
360+
}
361+
362+
if(it == s1_data.operands().end())
363+
{
364+
return from_integer(-1, expr.type());
365+
}
366+
else
367+
{
368+
const std::size_t idx = std::distance(s1_data.operands().begin(), it);
369+
370+
return from_integer(idx, expr.type());
371+
}
372+
}
373+
265374
simplify_exprt::resultt<> simplify_exprt::simplify_function_application(
266375
const function_application_exprt &expr)
267376
{
@@ -345,6 +454,10 @@ simplify_exprt::resultt<> simplify_exprt::simplify_function_application(
345454
{
346455
return simplify_string_compare_to(expr, ns);
347456
}
457+
else if(func_id == ID_cprover_string_index_of_func)
458+
{
459+
return simplify_string_index_of(expr, ns);
460+
}
348461
else if(func_id == ID_cprover_string_char_at_func)
349462
{
350463
if(expr.arguments().at(1).id() != ID_constant)

Diff for: src/util/string_expr.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ inline const refined_string_exprt &to_string_expr(const exprt &expr)
173173
template <>
174174
inline bool can_cast_expr<refined_string_exprt>(const exprt &base)
175175
{
176-
return base.id() == ID_struct && base.operands().size() == 2;
176+
return base.id() == ID_struct && base.operands().size() == 2 &&
177+
is_refined_string_type(base.type());
177178
}
178179

179180
inline void validate_expr(const refined_string_exprt &x)

0 commit comments

Comments
 (0)