Skip to content

Commit 69b370c

Browse files
authored
Merge pull request #3982 from JohnDumbell/jd/enhancement/symex_constant_string_compare
Try to evaluate constant string comparisons in symex
2 parents b0806f2 + 17a985a commit 69b370c

File tree

5 files changed

+100
-0
lines changed

5 files changed

+100
-0
lines changed
Binary file not shown.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package string_refinement;
2+
3+
public class RefineStrings {
4+
public void constantComparison() {
5+
String helloWorld = "Hello world";
6+
assert helloWorld.equals(helloWorld());
7+
}
8+
9+
public String helloWorld() {
10+
return "Hello world";
11+
}
12+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
RefineStrings.class
3+
--function string_refinement.RefineStrings.constantComparison --show-vcc --cp `../../../../scripts/format_classpath.sh . ../../../lib/java-models-library/target/core-models.jar`
4+
^Generated [0-9]+ VCC\(s\), 0 remaining after simplification$
5+
--
6+
.*cprover_string_startswith_func*.
7+
--
8+
All instances of cprover_string_startswith_func can be calculated as false or true, so there should be no instances of this call.

src/util/simplify_expr.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@ Author: Daniel Kroening, [email protected]
1919
#include "expr_util.h"
2020
#include "fixedbv.h"
2121
#include "invariant.h"
22+
#include "mathematical_expr.h"
2223
#include "namespace.h"
2324
#include "pointer_offset_size.h"
2425
#include "rational.h"
2526
#include "rational_tools.h"
2627
#include "simplify_utils.h"
2728
#include "std_expr.h"
29+
#include "string_expr.h"
30+
#include "symbol.h"
2831
#include "type_eq.h"
2932

3033
// #define DEBUGX
@@ -158,6 +161,77 @@ bool simplify_exprt::simplify_popcount(popcount_exprt &expr)
158161
return true;
159162
}
160163

164+
bool simplify_exprt::simplify_function_application(exprt &expr)
165+
{
166+
const function_application_exprt &function_app =
167+
to_function_application_expr(expr);
168+
irep_idt func_id =
169+
function_app.function().find(ID_expression).get(ID_identifier);
170+
171+
// Starts-with is used for .equals.
172+
if(func_id == ID_cprover_string_startswith_func)
173+
{
174+
// We want to get both arguments of any starts-with comparison, and
175+
// trace them back to the actual string instance. All variables on the
176+
// way must be constant for us to be sure this will work.
177+
auto &first_argument = to_string_expr(function_app.arguments().at(0));
178+
auto &second_argument = to_string_expr(function_app.arguments().at(1));
179+
180+
if(
181+
first_argument.content().id() != ID_address_of ||
182+
second_argument.content().id() != ID_address_of)
183+
{
184+
return true;
185+
}
186+
187+
const address_of_exprt &first_address_of =
188+
to_address_of_expr(first_argument.content());
189+
const address_of_exprt &second_address_of =
190+
to_address_of_expr(second_argument.content());
191+
192+
// Constants are got via address_of expressions, so this helps filter out
193+
// things that are non-constant.
194+
if(
195+
first_address_of.object().id() != ID_index ||
196+
first_address_of.object().id() != ID_index)
197+
{
198+
return true;
199+
}
200+
201+
const index_exprt &first_index_expression =
202+
to_index_expr(first_address_of.object());
203+
const index_exprt &second_index_expression =
204+
to_index_expr(second_address_of.object());
205+
206+
const exprt &first_string_array = first_index_expression.array();
207+
const exprt &second_string_array = second_index_expression.array();
208+
209+
// Check if our symbols type is an array.
210+
if(
211+
first_string_array.type().id() != ID_array ||
212+
second_string_array.type().id() != ID_array)
213+
{
214+
return true;
215+
}
216+
217+
// If so, it'll be a string array that we can use.
218+
const array_string_exprt &first_string = to_array_string_expr(
219+
ns.lookup(first_string_array.get(ID_identifier)).value);
220+
221+
const array_string_exprt &second_string = to_array_string_expr(
222+
ns.lookup(second_string_array.get(ID_identifier)).value);
223+
224+
// If the lengths are the same we can do the .equals substitute.
225+
if(first_string.length() == second_string.length())
226+
{
227+
expr = from_integer(first_string == second_string ? 1 : 0, expr.type());
228+
return false;
229+
}
230+
}
231+
232+
return true;
233+
}
234+
161235
bool simplify_exprt::simplify_typecast(exprt &expr)
162236
{
163237
if(expr.operands().size()!=1)
@@ -2265,6 +2339,8 @@ bool simplify_exprt::simplify_node(exprt &expr)
22652339
result=simplify_sign(expr) && result;
22662340
else if(expr.id() == ID_popcount)
22672341
result = simplify_popcount(to_popcount_expr(expr)) && result;
2342+
else if(expr.id() == ID_function_application)
2343+
result = simplify_function_application(expr) && result;
22682344

22692345
#ifdef DEBUGX
22702346
if(!result

src/util/simplify_expr_class.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ class simplify_exprt
114114
bool simplify_sign(exprt &expr);
115115
bool simplify_popcount(popcount_exprt &expr);
116116

117+
/// Attempt to simplify mathematical function applications if we have
118+
/// enough information to do so. Currently focused on constant comparisons.
119+
bool simplify_function_application(exprt &expr);
120+
117121
// auxiliary
118122
bool simplify_if_implies(
119123
exprt &expr, const exprt &cond, bool truth, bool &new_truth);

0 commit comments

Comments
 (0)