Skip to content

Commit 229d1ee

Browse files
Merge pull request diffblue#1977 from romainbrenguier/bugfix/string-equals-TG-2179
Fix bug in preprocessing of String.equals
2 parents 97a6713 + a997ffb commit 229d1ee

File tree

6 files changed

+108
-40
lines changed

6 files changed

+108
-40
lines changed
572 Bytes
Binary file not shown.

regression/jbmc-strings/StringEquals/Test.java

+48
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Uses CProverString, must be compiled with ../cprover/CProverString.java
2+
import org.cprover.*;
3+
14
public class Test {
25
public static void check(int i, String s) {
36
String t = "Hello World";
@@ -15,4 +18,49 @@ else if (i==4)
1518
else if (i==5)
1619
assert(s.equals(x));
1720
}
21+
22+
public static boolean referenceImplementation(String s, Object o) {
23+
if (! (o instanceof String))
24+
return false;
25+
26+
String s2 = (String) o;
27+
if (s.length() != s2.length())
28+
return false;
29+
30+
for (int i = 0; i < s.length(); i++) {
31+
if (CProverString.charAt(s, i) != CProverString.charAt(s2, i))
32+
return false;
33+
}
34+
35+
return true;
36+
}
37+
38+
public static boolean verifyNonNull(String s, Object o) {
39+
// Filter
40+
if (s == null || o == null)
41+
return false;
42+
43+
// Act
44+
boolean result = s.equals(o);
45+
boolean referenceResult = referenceImplementation(s, o);
46+
47+
// Assert
48+
assert result == referenceResult;
49+
50+
// Return
51+
return result;
52+
}
53+
54+
public static boolean verify(String s, Object o) {
55+
// Act
56+
boolean result = s.equals(o);
57+
boolean referenceResult = referenceImplementation(s, o);
58+
59+
// Assert
60+
assert result == referenceResult;
61+
62+
// Return
63+
return result;
64+
}
65+
1866
}

regression/jbmc-strings/StringEquals/test.desc

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ Test.class
33
--refine-strings --string-max-length 40 --function Test.check
44
^EXIT=10$
55
^SIGNAL=0$
6-
assertion at file Test.java line 6 .* SUCCESS
7-
assertion at file Test.java line 8 .* FAILURE
8-
assertion at file Test.java line 10 .* SUCCESS
9-
assertion at file Test.java line 12 .* FAILURE
10-
assertion at file Test.java line 14 .* SUCCESS
11-
assertion at file Test.java line 16 .* FAILURE
6+
assertion at file Test.java line 9 .* SUCCESS
7+
assertion at file Test.java line 11 .* FAILURE
8+
assertion at file Test.java line 13 .* SUCCESS
9+
assertion at file Test.java line 15 .* FAILURE
10+
assertion at file Test.java line 17 .* SUCCESS
11+
assertion at file Test.java line 19 .* FAILURE
1212
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
KNOWNBUG
2+
Test.class
3+
--refine-strings --string-max-input-length 5 --string-max-length 100 --unwind 10 --function Test.verify --java-throw-runtime-exceptions
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
assertion at file Test.java line 60 .* SUCCESS
7+
--
8+
--
9+
null case not handled currently
10+
TG-2179
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CORE
2+
Test.class
3+
--refine-strings --string-max-input-length 20 --string-max-length 100 --unwind 30 --function Test.verifyNonNull
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
assertion at file Test.java line 48 .* SUCCESS

src/java_bytecode/java_string_library_preprocess.cpp

+37-34
Original file line numberDiff line numberDiff line change
@@ -930,53 +930,56 @@ java_string_library_preprocesst::string_literal_to_string_expr(
930930
/// \param symbol_table: symbol table
931931
/// \return Code corresponding to:
932932
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
933-
/// string_expr1 = {this->length; *this->data}
934-
/// string_expr2 = {arg->length; *arg->data}
935-
/// return cprover_string_equal(string_expr1, string_expr2)
933+
/// IF arg->@class_identifier == "java.lang.String"
934+
/// THEN
935+
/// string_expr1 = {this->length; *this->data}
936+
/// string_expr2 = {arg->length; *arg->data}
937+
/// bool string_equals_tmp = cprover_string_equal(string_expr1, string_expr2)
938+
/// return string_equals_tmp
939+
/// ELSE
940+
/// return false
936941
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
937942
codet java_string_library_preprocesst::make_equals_function_code(
938943
const code_typet &type,
939944
const source_locationt &loc,
940945
symbol_table_baset &symbol_table)
941946
{
942-
code_blockt code;
947+
const typet &return_type = type.return_type();
943948
exprt::operandst ops;
944949
for(const auto &p : type.parameters())
945950
{
946951
symbol_exprt sym(p.get_identifier(), p.type());
947952
ops.push_back(sym);
948953
}
949-
exprt::operandst args=process_equals_function_operands(
950-
ops, loc, symbol_table, code);
951-
952-
member_exprt class_identifier(
953-
checked_dereference(ops[1], ops[1].type().subtype()),
954-
"@class_identifier",
955-
string_typet());
956-
957-
// Check the object argument is a String.
958-
equal_exprt arg_is_string(
959-
class_identifier, constant_exprt("java::java.lang.String", string_typet()));
960954

961-
// Check content equality
962-
const symbolt string_equals_sym = get_fresh_aux_symbol(
963-
java_boolean_type(), "string_equals", "str_eq", loc, ID_java, symbol_table);
964-
const symbol_exprt string_equals = string_equals_sym.symbol_expr();
965-
code.add(code_declt(string_equals), loc);
966-
code.add(
967-
code_assignt(
968-
string_equals,
969-
make_function_application(
970-
ID_cprover_string_equal_func, args, type.return_type(), symbol_table)),
971-
loc);
972-
973-
code.add(
974-
code_returnt(
975-
if_exprt(
976-
arg_is_string,
977-
string_equals,
978-
from_integer(false, java_boolean_type()))),
979-
loc);
955+
code_ifthenelset code;
956+
code.cond() = [&] {
957+
const member_exprt class_identifier(
958+
checked_dereference(ops[1], ops[1].type().subtype()),
959+
"@class_identifier",
960+
string_typet());
961+
return equal_exprt(
962+
class_identifier,
963+
constant_exprt("java::java.lang.String", string_typet()));
964+
}();
965+
966+
code.then_case() = [&] {
967+
code_blockt instance_case;
968+
// Check content equality
969+
const symbolt string_equals_sym = get_fresh_aux_symbol(
970+
return_type, "string_equals", "str_eq", loc, ID_java, symbol_table);
971+
const symbol_exprt string_equals = string_equals_sym.symbol_expr();
972+
instance_case.add(code_declt(string_equals), loc);
973+
const exprt::operandst args =
974+
process_equals_function_operands(ops, loc, symbol_table, instance_case);
975+
const auto fun_app = make_function_application(
976+
ID_cprover_string_equal_func, args, return_type, symbol_table);
977+
instance_case.add(code_assignt(string_equals, fun_app), loc);
978+
instance_case.add(code_returnt(string_equals), loc);
979+
return instance_case;
980+
}();
981+
982+
code.else_case() = code_returnt(from_integer(false, return_type));
980983
return code;
981984
}
982985

0 commit comments

Comments
 (0)