Skip to content

Commit 8279e91

Browse files
committed
Constant propagation of CProverString.setLength()
Only minimal tests are added at this point. The error conditions (like index out of bounds) will be handled by the models. Once we have models that use CProverString.setLength(), more comprehensive tests will be added.
1 parent fd4a8ec commit 8279e91

File tree

14 files changed

+250
-1
lines changed

14 files changed

+250
-1
lines changed
Binary file not shown.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import org.cprover.CProverString;
2+
3+
public class Test
4+
{
5+
public void testSuccess1()
6+
{
7+
StringBuffer sb = new StringBuffer("abc");
8+
CProverString.setLength(sb, 0);
9+
assert sb.toString().equals("");
10+
}
11+
12+
public void testSuccess2()
13+
{
14+
StringBuffer sb = new StringBuffer("abc");
15+
CProverString.setLength(sb, 2);
16+
assert sb.toString().equals("ab");
17+
}
18+
19+
public void testSuccess3()
20+
{
21+
StringBuffer sb = new StringBuffer("abc");
22+
CProverString.setLength(sb, 3);
23+
assert sb.toString().equals("abc");
24+
}
25+
26+
public void testSuccess4()
27+
{
28+
StringBuffer sb = new StringBuffer("abc");
29+
CProverString.setLength(sb, 4);
30+
assert sb.toString().equals("abc\u0000");
31+
}
32+
33+
public void testSuccess5()
34+
{
35+
StringBuffer sb = new StringBuffer("abc");
36+
CProverString.setLength(sb, 5);
37+
assert sb.toString().equals("abc\u0000\u0000");
38+
}
39+
40+
public void testSuccess6(StringBuffer sb)
41+
{
42+
CProverString.setLength(sb, 0);
43+
assert sb.toString().equals("");
44+
}
45+
46+
public void testNoPropagation1()
47+
{
48+
StringBuffer sb = new StringBuffer("abc");
49+
CProverString.setLength(sb, -1);
50+
assert sb.toString().equals("abc");
51+
}
52+
53+
public void testNoPropagation2(StringBuffer sb)
54+
{
55+
CProverString.setLength(sb, 3);
56+
assert sb.toString().equals("abc");
57+
}
58+
59+
public void testNoPropagation3(int newLength)
60+
{
61+
StringBuffer sb = new StringBuffer("abc");
62+
CProverString.setLength(sb, newLength);
63+
assert sb.toString().equals("abc");
64+
}
65+
}
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+
Test.class
3+
--function Test.testNoPropagation1 --show-vcc --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property 'java::Test.testNoPropagation1:()V.assertion.1'
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+
Test.class
3+
--function Test.testNoPropagation2 --show-vcc --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property 'java::Test.testNoPropagation2:(Ljava/lang/StringBuffer;)V.assertion.1'
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+
Test.class
3+
--function Test.testNoPropagation3 --show-vcc --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar` --property 'java::Test.testNoPropagation3:(I)V.assertion.1'
4+
^Generated [0-9]+ VCC\(s\), 1 remaining after simplification$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
Test.class
3+
--function Test.testSuccess1 --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+
--
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
Test.class
3+
--function Test.testSuccess2 --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+
--
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
Test.class
3+
--function Test.testSuccess3 --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+
--
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
Test.class
3+
--function Test.testSuccess4 --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+
--
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
Test.class
3+
--function Test.testSuccess5 --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+
--
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
Test.class
3+
--function Test.testSuccess5 --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+
--

jbmc/src/java_bytecode/java_string_library_preprocess.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1531,8 +1531,11 @@ void java_string_library_preprocesst::initialize_conversion_table()
15311531
["java::org.cprover.CProverString.setCharAt:(Ljava/lang/String;IC)V"] =
15321532
ID_cprover_string_char_set_func;
15331533
cprover_equivalent_to_java_assign_function
1534-
["java::org.cprover.CProverString.setLength:(Ljava/lang/String;I)V"] =
1534+
["java::org.cprover.CProverString.setLength:(Ljava/lang/StringBuffer;I)V"] =
15351535
ID_cprover_string_set_length_func;
1536+
cprover_equivalent_to_java_assign_function
1537+
["java::org.cprover.CProverString.setLength:(Ljava/lang/"
1538+
"StringBuilder;I)V"] = ID_cprover_string_set_length_func;
15361539
cprover_equivalent_to_java_string_returning_function
15371540
["java::org.cprover.CProverString.subSequence:(Ljava/lang/String;II)Ljava/"
15381541
"lang/CharSequence;"] = ID_cprover_string_substring_func;

src/goto-symex/goto_symex.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ bool goto_symext::constant_propagate_assignment_with_side_effects(
199199
{
200200
return constant_propagate_delete(state, symex_assign, f_l1);
201201
}
202+
else if(func_id == ID_cprover_string_set_length_func)
203+
{
204+
return constant_propagate_set_length(state, symex_assign, f_l1);
205+
}
202206
}
203207
}
204208

@@ -763,3 +767,84 @@ bool goto_symext::constant_propagate_delete(
763767

764768
return true;
765769
}
770+
771+
bool goto_symext::constant_propagate_set_length(
772+
statet &state,
773+
symex_assignt &symex_assign,
774+
const function_application_exprt &f_l1)
775+
{
776+
// The function application expression f_l1 takes the following arguments:
777+
// - result string length (output parameter)
778+
// - result string data array (output parameter)
779+
// - current string
780+
// - new length of the string
781+
PRECONDITION(f_l1.arguments().size() == 4);
782+
783+
const auto &f_type = to_mathematical_function_type(f_l1.function().type());
784+
const auto &length_type = f_type.domain().at(0);
785+
const auto &char_type = to_pointer_type(f_type.domain().at(1)).subtype();
786+
787+
const auto &new_length_opt =
788+
try_evaluate_constant(state, f_l1.arguments().at(3));
789+
790+
if(!new_length_opt)
791+
{
792+
return false;
793+
}
794+
795+
const mp_integer new_length =
796+
numeric_cast_v<mp_integer>(new_length_opt->get());
797+
798+
if(new_length < 0)
799+
{
800+
return false;
801+
}
802+
803+
const std::size_t new_size = numeric_cast_v<std::size_t>(new_length);
804+
805+
const constant_exprt new_char_array_length =
806+
from_integer(new_size, length_type);
807+
808+
const array_typet new_char_array_type(char_type, new_char_array_length);
809+
810+
exprt::operandst operands;
811+
812+
if(new_size != 0)
813+
{
814+
operands.reserve(new_size);
815+
816+
const refined_string_exprt &s = to_string_expr(f_l1.arguments().at(2));
817+
const auto s_data_opt = try_evaluate_constant_string(state, s.content());
818+
819+
if(!s_data_opt)
820+
{
821+
return false;
822+
}
823+
824+
const array_exprt &s_data = s_data_opt->get();
825+
826+
operands.insert(
827+
operands.end(),
828+
s_data.operands().begin(),
829+
std::next(
830+
s_data.operands().begin(),
831+
std::min(new_size, s_data.operands().size())));
832+
833+
operands.insert(
834+
operands.end(),
835+
new_size - std::min(new_size, s_data.operands().size()),
836+
from_integer(0, char_type));
837+
}
838+
839+
const array_exprt new_char_array(std::move(operands), new_char_array_type);
840+
841+
assign_string_constant(
842+
state,
843+
symex_assign,
844+
to_ssa_expr(f_l1.arguments().at(0)),
845+
new_char_array_length,
846+
to_ssa_expr(f_l1.arguments().at(1)),
847+
new_char_array);
848+
849+
return true;
850+
}

src/goto-symex/goto_symex.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,24 @@ class goto_symext
623623
symex_assignt &symex_assign,
624624
const function_application_exprt &f_l1);
625625

626+
/// Attempt to constant propagate setting the length of a string
627+
///
628+
/// If the new length is less than the current length, characters are stripped
629+
/// from the end to match the new length. If the new length is greater than
630+
/// the current length, null characters '\\u0000' are appended to match the
631+
/// new length.
632+
///
633+
/// \param state: goto symex state
634+
/// \param symex_assign: object handling symbol assignments
635+
/// \param f_l1: application of function ID_cprover_string_set_length_func
636+
/// with l1 renaming applied
637+
/// \return true if the operation could be evaluated to a constant string,
638+
/// false otherwise
639+
bool constant_propagate_set_length(
640+
statet &state,
641+
symex_assignt &symex_assign,
642+
const function_application_exprt &f_l1);
643+
626644
/// Assign constant string length and string data given by a char array to
627645
/// given ssa variables
628646
///

0 commit comments

Comments
 (0)