Skip to content

Commit 9c03585

Browse files
committed
Implement constant propagation for substring operations
This implements constant propagation of ID_cprover_string_substring_func function application expressions. These are in turn used by the various substring operations of String and StringBuilder.
1 parent 7357f8e commit 9c03585

File tree

18 files changed

+244
-0
lines changed

18 files changed

+244
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
public class Main {
2+
public void test() {
3+
StringBuilder sb = new StringBuilder("abc");
4+
5+
String s1 = sb.substring(0);
6+
assert s1.length() == 3;
7+
assert s1.startsWith("abc");
8+
9+
String s2 = sb.substring(1);
10+
assert s2.length() == 2;
11+
assert s2.startsWith("bc");
12+
13+
String s3 = sb.substring(0, 3);
14+
assert s3.length() == 3;
15+
assert s3.startsWith("abc");
16+
17+
String s4 = sb.substring(0, 0);
18+
assert s4.length() == 0;
19+
assert s4.startsWith("");
20+
21+
String s5 = sb.substring(0, 1);
22+
assert s5.length() == 1;
23+
assert s5.startsWith("a");
24+
}
25+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
Main.class
3+
--function Main.test
4+
^Generated [0-9]+ VCC\(s\), 0 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
--
Binary file not shown.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
public class Main {
2+
public void test(StringBuilder sb1, int i) {
3+
StringBuilder sb2 = new StringBuilder("abc");
4+
5+
String s1 = sb1.substring(0);
6+
assert s1.startsWith("xyz");
7+
8+
String s2 = sb2.substring(i);
9+
assert s2.startsWith("xyz");
10+
11+
String s3 = sb1.substring(i);
12+
assert s3.startsWith("xyz");
13+
}
14+
}
Lines changed: 8 additions & 0 deletions
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.test --property "java::Main.test:(Ljava/lang/StringBuilder;I)V.assertion.1" --show-vcc
4+
^Generated [0-9]+ VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Lines changed: 8 additions & 0 deletions
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.test --property "java::Main.test:(Ljava/lang/StringBuilder;I)V.assertion.2" --show-vcc
4+
^Generated [0-9]+ VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Lines changed: 8 additions & 0 deletions
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.test --property "java::Main.test:(Ljava/lang/StringBuilder;I)V.assertion.3" --show-vcc
4+
^Generated [0-9]+ VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Binary file not shown.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
public class Main {
2+
public void test() {
3+
String s = "abc";
4+
5+
String s1 = s.substring(0);
6+
assert s1.equals("abc");
7+
8+
String s2 = s.substring(1);
9+
assert s2.equals("bc");
10+
11+
String s3 = s.substring(0, 3);
12+
assert s3.equals("abc");
13+
14+
String s4 = s.substring(0, 0);
15+
assert s4.equals("");
16+
17+
String s5 = s.substring(0, 1);
18+
assert s5.equals("a");
19+
}
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
Main.class
3+
--function Main.test --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar`
4+
^Generated [0-9]+ VCC\(s\), 0 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
--
Binary file not shown.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
public class Main {
2+
public void test(String s1, int i) {
3+
String s2 = "abc";
4+
5+
String s3 = s1.substring(0);
6+
assert s3.startsWith("xyz");
7+
8+
String s4 = s2.substring(i);
9+
assert s4.startsWith("xyz");
10+
11+
String s5 = s1.substring(i);
12+
assert s5.startsWith("xyz");
13+
}
14+
}
Lines changed: 8 additions & 0 deletions
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.test --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property "java::Main.test:(Ljava/lang/String;I)V.assertion.1" --show-vcc
4+
^Generated [0-9]+ VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Lines changed: 8 additions & 0 deletions
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.test --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property "java::Main.test:(Ljava/lang/String;I)V.assertion.2" --show-vcc
4+
^Generated [0-9]+ VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Lines changed: 8 additions & 0 deletions
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.test --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property "java::Main.test:(Ljava/lang/String;I)V.assertion.3" --show-vcc
4+
^Generated [0-9]+ VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--

src/goto-symex/goto_symex.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ bool goto_symext::constant_propagate_assignment_with_side_effects(
140140
constant_propagate_empty_string(state, symex_assign, f_l1);
141141
return true;
142142
}
143+
else if(func_id == ID_cprover_string_substring_func)
144+
{
145+
return constant_propagate_string_substring(
146+
state, symex_assign, f_l1, f_l2);
147+
}
143148
}
144149
}
145150

@@ -287,3 +292,90 @@ bool goto_symext::constant_propagate_string_concat(
287292

288293
return true;
289294
}
295+
296+
bool goto_symext::constant_propagate_string_substring(
297+
statet &state,
298+
symex_assignt &symex_assign,
299+
const function_application_exprt &f_l1,
300+
const function_application_exprt &f_l2)
301+
{
302+
const std::size_t num_operands = f_l1.arguments().size();
303+
304+
PRECONDITION(num_operands >= 4);
305+
PRECONDITION(num_operands <= 5);
306+
307+
const auto &f_type = to_mathematical_function_type(f_l1.function().type());
308+
const auto &length_type = f_type.domain().at(0);
309+
const auto &char_type = to_pointer_type(f_type.domain().at(1)).subtype();
310+
311+
const refined_string_exprt &s = to_string_expr(f_l2.arguments().at(2));
312+
const auto s_data_opt = try_get_string_data_array(s, ns);
313+
314+
if(!s_data_opt)
315+
return false;
316+
317+
const array_exprt &s_data = s_data_opt->get();
318+
319+
mp_integer end_index;
320+
321+
if(num_operands == 5)
322+
{
323+
const auto &end_index_expr = f_l2.arguments().at(4);
324+
325+
if(end_index_expr.id() != ID_constant)
326+
{
327+
return false;
328+
}
329+
330+
end_index = numeric_cast_v<mp_integer>(to_constant_expr(end_index_expr));
331+
332+
if(end_index < 0 || end_index > s_data.operands().size())
333+
{
334+
return false;
335+
}
336+
}
337+
else
338+
{
339+
end_index = s_data.operands().size();
340+
}
341+
342+
const auto &start_index_expr = f_l2.arguments().at(3);
343+
344+
if(start_index_expr.id() != ID_constant)
345+
{
346+
return false;
347+
}
348+
349+
mp_integer start_index =
350+
numeric_cast_v<mp_integer>(to_constant_expr(start_index_expr));
351+
352+
if(start_index < 0 || start_index > end_index)
353+
{
354+
return false;
355+
}
356+
357+
const constant_exprt new_char_array_length =
358+
from_integer(end_index - start_index, length_type);
359+
360+
array_typet new_char_array_type(char_type, new_char_array_length);
361+
362+
exprt::operandst operands(
363+
std::next(
364+
s_data.operands().begin(),
365+
numeric_cast_v<std::size_t>(start_index)),
366+
std::next(
367+
s_data.operands().begin(),
368+
numeric_cast_v<std::size_t>(end_index)));
369+
370+
array_exprt new_char_array(std::move(operands), new_char_array_type);
371+
372+
assign_string_constant(
373+
state,
374+
symex_assign,
375+
to_ssa_expr(f_l1.arguments().at(0)),
376+
new_char_array_length,
377+
to_ssa_expr(f_l1.arguments().at(1)),
378+
new_char_array);
379+
380+
return true;
381+
}

src/goto-symex/goto_symex.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,19 @@ class goto_symext
566566
const function_application_exprt &f_l1,
567567
const function_application_exprt &f_l2);
568568

569+
/// Attempt to constant propagate getting a substring of a string
570+
///
571+
/// \param state: goto symex state
572+
/// \param symex_assign: object handling symbol assignments
573+
/// \param f_l1: application of function ID_cprover_string_substring_func with
574+
/// l1 renaming applied
575+
/// \param f_l2: same as above but with l2 renaming applied
576+
bool constant_propagate_string_substring(
577+
statet &state,
578+
symex_assignt &symex_assign,
579+
const function_application_exprt &f_l1,
580+
const function_application_exprt &f_l2);
581+
569582
/// Assign constant string length and string data given by a char array to
570583
/// given ssa variables
571584
///

0 commit comments

Comments
 (0)