Skip to content

Commit a006aae

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 aa5fcda commit a006aae

File tree

18 files changed

+268
-0
lines changed

18 files changed

+268
-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: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ bool goto_symext::constant_propagate_assignment_with_side_effects(
181181
constant_propagate_empty_string(state, symex_assign, f_l1);
182182
return true;
183183
}
184+
else if(func_id == ID_cprover_string_substring_func)
185+
{
186+
return constant_propagate_string_substring(state, symex_assign, f_l1);
187+
}
184188
}
185189
}
186190

@@ -310,6 +314,28 @@ goto_symext::try_evaluate_constant_string(
310314
return try_get_string_data_array(s_pointer_opt->get(), ns);
311315
}
312316

317+
optionalt<std::reference_wrapper<const constant_exprt>>
318+
goto_symext::try_evaluate_constant(
319+
const statet &state,
320+
const exprt &expr)
321+
{
322+
if(expr.id() != ID_symbol)
323+
{
324+
return {};
325+
}
326+
327+
const auto constant_expr_opt =
328+
state.propagation.find(to_symbol_expr(expr).get_identifier());
329+
330+
if(!constant_expr_opt || constant_expr_opt->get().id() != ID_constant)
331+
{
332+
return {};
333+
}
334+
335+
return optionalt<std::reference_wrapper<const constant_exprt>>(
336+
to_constant_expr(constant_expr_opt->get()));
337+
}
338+
313339
void goto_symext::constant_propagate_empty_string(
314340
statet &state,
315341
symex_assignt &symex_assign,
@@ -387,3 +413,90 @@ bool goto_symext::constant_propagate_string_concat(
387413

388414
return true;
389415
}
416+
417+
bool goto_symext::constant_propagate_string_substring(
418+
statet &state,
419+
symex_assignt &symex_assign,
420+
const function_application_exprt &f_l1)
421+
{
422+
const std::size_t num_operands = f_l1.arguments().size();
423+
424+
PRECONDITION(num_operands >= 4);
425+
PRECONDITION(num_operands <= 5);
426+
427+
const auto &f_type = to_mathematical_function_type(f_l1.function().type());
428+
const auto &length_type = f_type.domain().at(0);
429+
const auto &char_type = to_pointer_type(f_type.domain().at(1)).subtype();
430+
431+
const refined_string_exprt &s = to_string_expr(f_l1.arguments().at(2));
432+
const auto s_data_opt = try_evaluate_constant_string(state, s.content());
433+
434+
if(!s_data_opt)
435+
return false;
436+
437+
const array_exprt &s_data = s_data_opt->get();
438+
439+
mp_integer end_index;
440+
441+
if(num_operands == 5)
442+
{
443+
const auto end_index_expr_opt =
444+
try_evaluate_constant(state, f_l1.arguments().at(4));
445+
446+
if(!end_index_expr_opt)
447+
{
448+
return false;
449+
}
450+
451+
end_index = numeric_cast_v<mp_integer>(
452+
to_constant_expr(*end_index_expr_opt));
453+
454+
if(end_index < 0 || end_index > s_data.operands().size())
455+
{
456+
return false;
457+
}
458+
}
459+
else
460+
{
461+
end_index = s_data.operands().size();
462+
}
463+
464+
const auto start_index_expr_opt =
465+
try_evaluate_constant(state, f_l1.arguments().at(3));
466+
467+
if(!start_index_expr_opt)
468+
{
469+
return false;
470+
}
471+
472+
mp_integer start_index =
473+
numeric_cast_v<mp_integer>(to_constant_expr(*start_index_expr_opt));
474+
475+
if(start_index < 0 || start_index > end_index)
476+
{
477+
return false;
478+
}
479+
480+
const constant_exprt new_char_array_length =
481+
from_integer(end_index - start_index, length_type);
482+
483+
array_typet new_char_array_type(char_type, new_char_array_length);
484+
485+
exprt::operandst operands(
486+
std::next(
487+
s_data.operands().begin(), numeric_cast_v<std::size_t>(start_index)),
488+
std::next(
489+
s_data.operands().begin(), numeric_cast_v<std::size_t>(end_index)));
490+
491+
array_exprt new_char_array(std::move(operands), new_char_array_type);
492+
493+
assign_string_constant(
494+
state,
495+
symex_assign,
496+
to_ssa_expr(f_l1.arguments().at(0)),
497+
new_char_array_length,
498+
to_ssa_expr(f_l1.arguments().at(1)),
499+
new_char_array);
500+
501+
return true;
502+
}

src/goto-symex/goto_symex.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,19 @@ class goto_symext
568568
symex_assignt &symex_assign,
569569
const function_application_exprt &f_l1);
570570

571+
/// Attempt to constant propagate getting a substring of a string
572+
///
573+
/// \param state: goto symex state
574+
/// \param symex_assign: object handling symbol assignments
575+
/// \param f_l1: application of function ID_cprover_string_substring_func with
576+
/// l1 renaming applied
577+
/// \return true if the operation could be evaluated to a constant string,
578+
/// false otherwise
579+
bool constant_propagate_string_substring(
580+
statet &state,
581+
symex_assignt &symex_assign,
582+
const function_application_exprt &f_l1);
583+
571584
/// Assign constant string length and string data given by a char array to
572585
/// given ssa variables
573586
///
@@ -627,6 +640,9 @@ class goto_symext
627640
optionalt<std::reference_wrapper<const array_exprt>>
628641
try_evaluate_constant_string(const statet &state, const exprt &content);
629642

643+
optionalt<std::reference_wrapper<const constant_exprt>>
644+
try_evaluate_constant(const statet &state, const exprt &expr);
645+
630646
// havocs the given object
631647
void havoc_rec(statet &state, const guardt &guard, const exprt &dest);
632648

0 commit comments

Comments
 (0)