forked from diffblue/cbmc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request diffblue#2297 from romainbrenguier/bugfix/starts-with
Fix bugs with String.startsWith in string refinement
- Loading branch information
Showing
8 changed files
with
174 additions
and
76 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Must be compiled with CProverString: | ||
// javac Test.java ../cprover/CProverString.java | ||
public class Test { | ||
|
||
// Reference implementation | ||
public static boolean referenceStartsWith(String s, String prefix, int offset) { | ||
if (offset < 0 || offset > s.length() - prefix.length()) { | ||
return false; | ||
} | ||
|
||
for (int i = 0; i < prefix.length(); i++) { | ||
if (org.cprover.CProverString.charAt(s, offset + i) | ||
!= org.cprover.CProverString.charAt(prefix, i)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
public static boolean check(String s, String t, int offset) { | ||
// Filter out null strings | ||
if(s == null || t == null) { | ||
return false; | ||
} | ||
|
||
// Act | ||
final boolean result = s.startsWith(t, offset); | ||
|
||
// Assert | ||
final boolean referenceResult = referenceStartsWith(s, t, offset); | ||
assert(result == referenceResult); | ||
|
||
// Check reachability | ||
assert(result == false); | ||
return result; | ||
} | ||
|
||
public static boolean checkDet() { | ||
boolean result = false; | ||
result = "foo".startsWith("foo", 0); | ||
assert(result); | ||
result = "foo".startsWith("f", -1); | ||
assert(!result); | ||
result = "foo".startsWith("oo", 1); | ||
assert(result); | ||
result = "foo".startsWith("f", 1); | ||
assert(!result); | ||
result = "foo".startsWith("bar", 0); | ||
assert(!result); | ||
result = "foo".startsWith("oo", 2); | ||
assert(!result); | ||
assert(false); | ||
return result; | ||
} | ||
|
||
public static boolean checkNonDet(String s) { | ||
// Filter | ||
if (s == null) { | ||
return false; | ||
} | ||
|
||
// Act | ||
final boolean result = s.startsWith(s, 1); | ||
|
||
// Assert | ||
assert(!result); | ||
|
||
// Check reachability | ||
assert(false); | ||
return result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
CORE | ||
Test.class | ||
--refine-strings --string-max-input-length 10 --unwind 10 --function Test.check | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
assertion at file Test.java line 31 .*: SUCCESS | ||
assertion at file Test.java line 34 .*: FAILURE | ||
-- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
CORE | ||
Test.class | ||
--refine-strings --string-max-input-length 100 --unwind 10 --function Test.checkDet | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
assertion at file Test.java line 41 .*: SUCCESS | ||
assertion at file Test.java line 43 .*: SUCCESS | ||
assertion at file Test.java line 45 .*: SUCCESS | ||
assertion at file Test.java line 47 .*: SUCCESS | ||
assertion at file Test.java line 49 .*: SUCCESS | ||
assertion at file Test.java line 51 .*: SUCCESS | ||
assertion at file Test.java line 52 .*: FAILURE | ||
-- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
CORE | ||
Test.class | ||
--refine-strings --string-max-input-length 100 --unwind 10 --function Test.checkNonDet | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
assertion at file Test.java line 66 .*: SUCCESS | ||
assertion at file Test.java line 69 .*: FAILURE | ||
-- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,15 +15,19 @@ Author: Romain Brenguier, [email protected] | |
#include <util/deprecate.h> | ||
|
||
/// Add axioms stating that the returned expression is true exactly when the | ||
/// first string is a prefix of the second one, starting at position offset. | ||
/// offset is greater or equal to 0 and the first string is a prefix of the | ||
/// second one, starting at position offset. | ||
/// | ||
/// These axioms are: | ||
/// 1. \f$ {\tt isprefix} \Rightarrow |str| \ge |{\tt prefix}|+offset \f$ | ||
/// 2. \f$ \forall 0 \le qvar<|{\tt prefix}|.\ {\tt isprefix} | ||
/// \Rightarrow s0[witness+{\tt offset}]=s2[witness] \f$ | ||
/// 3. \f$ !{\tt isprefix} \Rightarrow |{\tt str}|<|{\tt prefix}|+{\tt offset} | ||
/// \lor (0 \le witness<|{\tt prefix}| | ||
/// \land {\tt str}[witness+{\tt offset}] \ne {\tt prefix}[witness])\f$ | ||
/// 1. isprefix => offset_within_bounds | ||
/// 2. forall qvar in [0, |prefix|). | ||
/// isprefix => str[qvar + offset] = prefix[qvar] | ||
/// 3. !isprefix => !offset_within_bounds | ||
/// || 0 <= witness < |prefix| | ||
/// && str[witness+offset] != prefix[witness] | ||
/// | ||
/// where offset_within_bounds is: | ||
/// offset >= 0 && offset <= |str| && |str| - offset >= |prefix| | ||
/// | ||
/// \param prefix: an array of characters | ||
/// \param str: an array of characters | ||
|
@@ -34,34 +38,39 @@ exprt string_constraint_generatort::add_axioms_for_is_prefix( | |
const array_string_exprt &str, | ||
const exprt &offset) | ||
{ | ||
symbol_exprt isprefix=fresh_boolean("isprefix"); | ||
const typet &index_type=str.length().type(); | ||
const symbol_exprt isprefix = fresh_boolean("isprefix"); | ||
const typet &index_type = str.length().type(); | ||
const exprt offset_within_bounds = and_exprt( | ||
binary_relation_exprt(offset, ID_ge, from_integer(0, offset.type())), | ||
binary_relation_exprt(offset, ID_le, str.length()), | ||
binary_relation_exprt( | ||
minus_exprt(str.length(), offset), ID_ge, prefix.length())); | ||
|
||
// Axiom 1. | ||
lemmas.push_back( | ||
implies_exprt( | ||
isprefix, str.axiom_for_length_ge(plus_exprt(prefix.length(), offset)))); | ||
|
||
symbol_exprt qvar=fresh_univ_index("QA_isprefix", index_type); | ||
string_constraintt a2( | ||
qvar, | ||
prefix.length(), | ||
implies_exprt( | ||
isprefix, equal_exprt(str[plus_exprt(qvar, offset)], prefix[qvar]))); | ||
constraints.push_back(a2); | ||
|
||
symbol_exprt witness=fresh_exist_index("witness_not_isprefix", index_type); | ||
and_exprt witness_diff( | ||
axiom_for_is_positive_index(witness), | ||
and_exprt( | ||
lemmas.push_back(implies_exprt(isprefix, offset_within_bounds)); | ||
|
||
// Axiom 2. | ||
constraints.push_back([&] { | ||
const symbol_exprt qvar = fresh_univ_index("QA_isprefix", index_type); | ||
const exprt body = implies_exprt( | ||
isprefix, equal_exprt(str[plus_exprt(qvar, offset)], prefix[qvar])); | ||
return string_constraintt(qvar, prefix.length(), body); | ||
}()); | ||
|
||
// Axiom 3. | ||
lemmas.push_back([&] { | ||
const exprt witness = fresh_exist_index("witness_not_isprefix", index_type); | ||
const exprt strings_differ_at_witness = and_exprt( | ||
axiom_for_is_positive_index(witness), | ||
prefix.axiom_for_length_gt(witness), | ||
notequal_exprt(str[plus_exprt(witness, offset)], prefix[witness]))); | ||
or_exprt s0_notpref_s1( | ||
not_exprt(str.axiom_for_length_ge(plus_exprt(prefix.length(), offset))), | ||
witness_diff); | ||
notequal_exprt(str[plus_exprt(witness, offset)], prefix[witness])); | ||
const exprt s1_does_not_start_with_s0 = or_exprt( | ||
not_exprt(offset_within_bounds), | ||
not_exprt(str.axiom_for_length_ge(plus_exprt(prefix.length(), offset))), | ||
strings_differ_at_witness); | ||
return implies_exprt(not_exprt(isprefix), s1_does_not_start_with_s0); | ||
}()); | ||
|
||
implies_exprt a3(not_exprt(isprefix), s0_notpref_s1); | ||
lemmas.push_back(a3); | ||
return isprefix; | ||
} | ||
|
||
|
@@ -135,6 +144,7 @@ exprt string_constraint_generatort::add_axioms_for_is_empty( | |
/// \param swap_arguments: boolean flag telling whether the suffix is the second | ||
/// argument or the first argument | ||
/// \return Boolean expression `issuffix` | ||
DEPRECATED("should use `strings_startwith(s0, s1, s1.length - s0.length)`") | ||
exprt string_constraint_generatort::add_axioms_for_is_suffix( | ||
const function_application_exprt &f, bool swap_arguments) | ||
{ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters