Skip to content

Commit 8f9a9c7

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 115c5f8 commit 8f9a9c7

File tree

18 files changed

+284
-0
lines changed

18 files changed

+284
-0
lines changed
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+
}
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
public class Main {
2+
public void test1(StringBuilder sb) {
3+
String s = sb.substring(0);
4+
assert s.startsWith("xyz");
5+
}
6+
7+
public void test2(int i) {
8+
StringBuilder sb = new StringBuilder("abc");
9+
10+
String s = sb.substring(i);
11+
assert s.startsWith("xyz");
12+
}
13+
14+
public void test3(StringBuilder sb, int i) {
15+
String s = sb.substring(i);
16+
assert s.startsWith("xyz");
17+
}
18+
}
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.test1 --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property "java::Main.test1:(Ljava/lang/StringBuilder;)V.assertion.1"
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
^VERIFICATION FAILED$
8+
--
9+
--
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.test2 --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property "java::Main.test2:(I)V.assertion.1"
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
^VERIFICATION FAILED$
8+
--
9+
--
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.test3 --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property "java::Main.test3:(Ljava/lang/StringBuilder;I)V.assertion.1"
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
^VERIFICATION FAILED$
8+
--
9+
--
Binary file not shown.
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+
}
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
public class Main {
2+
public void test1(String s1) {
3+
String s2 = s1.substring(0);
4+
assert s2.startsWith("xyz");
5+
}
6+
7+
public void test2(int i) {
8+
String s1 = "abc";
9+
10+
String s2 = s1.substring(i);
11+
assert s2.startsWith("xyz");
12+
}
13+
14+
public void test3(String s1, int i) {
15+
String s2 = s1.substring(i);
16+
assert s2.startsWith("xyz");
17+
}
18+
}
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.test1 --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property "java::Main.test1:(Ljava/lang/String;)V.assertion.1"
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
^VERIFICATION FAILED$
8+
--
9+
--
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.test2 --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property "java::Main.test2:(I)V.assertion.1"
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
^VERIFICATION FAILED$
8+
--
9+
--
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.test3 --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property "java::Main.test3:(Ljava/lang/String;I)V.assertion.1"
4+
^Generated 1 VCC\(s\), 1 remaining after simplification$
5+
^EXIT=10$
6+
^SIGNAL=0$
7+
^VERIFICATION FAILED$
8+
--
9+
--

src/goto-symex/goto_symex.cpp

+111
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,26 @@ 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(const statet &state, const exprt &expr)
319+
{
320+
if(expr.id() != ID_symbol)
321+
{
322+
return {};
323+
}
324+
325+
const auto constant_expr_opt =
326+
state.propagation.find(to_symbol_expr(expr).get_identifier());
327+
328+
if(!constant_expr_opt || constant_expr_opt->get().id() != ID_constant)
329+
{
330+
return {};
331+
}
332+
333+
return optionalt<std::reference_wrapper<const constant_exprt>>(
334+
to_constant_expr(constant_expr_opt->get()));
335+
}
336+
313337
void goto_symext::constant_propagate_empty_string(
314338
statet &state,
315339
symex_assignt &symex_assign,
@@ -387,3 +411,90 @@ bool goto_symext::constant_propagate_string_concat(
387411

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

src/goto-symex/goto_symex.h

+20
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,13 @@ class goto_symext
627640
optionalt<std::reference_wrapper<const array_exprt>>
628641
try_evaluate_constant_string(const statet &state, const exprt &content);
629642

643+
// clang-format off
644+
static optionalt<std::reference_wrapper<const constant_exprt>>
645+
try_evaluate_constant(
646+
const statet &state,
647+
const exprt &expr);
648+
// clang-format on
649+
630650
// havocs the given object
631651
void havoc_rec(statet &state, const guardt &guard, const exprt &dest);
632652

0 commit comments

Comments
 (0)