package janala.interpreters; import janala.config.Config; import janala.solvers.History; public final class StringValue extends ObjectValue { private final String string; private SymbolicStringExpression symbolicExp; public StringValue(String string, int address) { super(100, address); this.string = string; } public StringValue(String string, SymbolicStringExpression symbolicExp) { super(100, -1); this.string = string; this.symbolicExp = symbolicExp; } @Override public boolean equals(Object o) { if (o == null) { return false; } else if (o == this) { return true; } else if (o instanceof StringValue) { StringValue other = (StringValue)o; return (this.string == other.string || this.string.equals(other.string)) && (this.symbolicExp == other.symbolicExp || this.symbolicExp.equals(other.symbolicExp)); } else { return false; } } @Override public String getConcrete() { return string; } public SymbolicStringExpression getSymbolicExp() { return symbolicExp; } private String escapeRE(String str) { return str.replaceAll("([^a-zA-z0-9])", "\\\\$1"); } private Value invokeEquals(Value arg) { if (arg instanceof StringValue) { StringValue other = (StringValue) arg; boolean result = string.equals(other.string); if (symbolicExp != null && other.symbolicExp != null) { return new IntValue( result ? 1 : 0, new SymbolicStringPredicate( SymbolicStringPredicate.STRING_COMPARISON_OPS.EQ, symbolicExp, other.symbolicExp)); } else if (symbolicExp != null) { return new IntValue( result ? 1 : 0, new SymbolicStringPredicate( SymbolicStringPredicate.STRING_COMPARISON_OPS.EQ, symbolicExp, other.string)); } else if (other.symbolicExp != null) { return new IntValue( result ? 1 : 0, new SymbolicStringPredicate( SymbolicStringPredicate.STRING_COMPARISON_OPS.EQ, string, other.symbolicExp)); } else { return new IntValue(result ? 1 : 0); } } else { // arg is not StringValue type. return new IntValue(0); } } private Value invokeStartsWith(Value[] args) { if (args.length == 1) { if (args[0] instanceof StringValue) { StringValue other = (StringValue) args[0]; boolean result = string.startsWith(other.string); if (symbolicExp != null) { return new IntValue( result ? 1 : 0, new SymbolicStringPredicate( SymbolicStringPredicate.STRING_COMPARISON_OPS.IN, symbolicExp, escapeRE(other.string) + ".*")); } else { return new IntValue(result ? 1 : 0); } } } else if (args.length == 2) { if (args[0] instanceof StringValue) { StringValue other = (StringValue) args[0]; IntValue offset = (IntValue) args[1]; boolean result = string.startsWith(other.string, offset.concrete); if (symbolicExp != null) { return new IntValue( result ? 1 : 0, new SymbolicStringPredicate( SymbolicStringPredicate.STRING_COMPARISON_OPS.IN, symbolicExp, ".{" + offset.concrete + "}" + escapeRE(other.string) + ".*")); } else { return new IntValue(result ? 1 : 0); } } } return new IntValue(0); } private Value invokeEndsWith(Value arg) { StringValue other = (StringValue) arg; boolean result = string.endsWith(other.string); if (symbolicExp != null) { return new IntValue( result ? 1 : 0, new SymbolicStringPredicate( SymbolicStringPredicate.STRING_COMPARISON_OPS.IN, symbolicExp, ".*" + escapeRE(other.string))); } else { return new IntValue(result ? 1 : 0); } } private Value invokeContains(Value arg) { StringValue other = (StringValue) arg; boolean result = string.contains(other.string); if (symbolicExp != null) { return new IntValue( result ? 1 : 0, new SymbolicStringPredicate( SymbolicStringPredicate.STRING_COMPARISON_OPS.IN, symbolicExp, ".*" + escapeRE(other.string) + ".*")); } else { return new IntValue(result ? 1 : 0); } } private Value invokeConcat(Value arg) { StringValue other = (StringValue) arg; String result = string.concat(other.string); if (symbolicExp != null && other.symbolicExp != null) { return new StringValue(result, symbolicExp.concat(other.symbolicExp)); } else if (symbolicExp != null) { return new StringValue(result, symbolicExp.concatStr(other.string)); } else if (other.symbolicExp != null) { return new StringValue(result, other.symbolicExp.concatToStr(string)); } else { return new StringValue(result, null); } } private Value invokeMatches(Value arg) { StringValue other = (StringValue) arg; boolean result = string.matches(other.string); if (symbolicExp != null) { return new IntValue( result ? 1 : 0, new SymbolicStringPredicate( SymbolicStringPredicate.STRING_COMPARISON_OPS.IN, symbolicExp, other.string)); } else { return new IntValue(result ? 1 : 0); } } private Value invokeReplace(Value from, Value to) { if ((from instanceof IntValue) && (to instanceof IntValue)) { // e.g., replace('a', 'b') char fromChar = (char) ((IntValue) from).concrete; char toChar = (char) ((IntValue) to).concrete; // TODO(zhihan): Consider how to handle expressions. // As of now just use the concrete value. return new StringValue(string.replace(fromChar, toChar), null); } return null; } @Override public Value invokeMethod(String name, Value[] args, History history) { if (name.equals("equals") && args.length == 1) { return invokeEquals(args[0]); } else if (name.equals("startsWith")) { return invokeStartsWith(args); } else if (name.equals("endsWith") && args.length == 1) { return invokeEndsWith(args[0]); } else if (name.equals("contains") && args.length == 1) { return invokeContains(args[0]); } else if (name.equals("concat") && args.length == 1) { return invokeConcat(args[0]); } else if (name.equals("length") && (args == null || args.length == 0)) { int result = string.length(); if (symbolicExp != null) { return symbolicExp.getField("length"); } else { return new IntValue(result); } } else if (name.equals("matches") && args.length == 1) { return invokeMatches(args[0]); } else if (name.equals("replace") && args.length == 2) { return invokeReplace(args[0], args[1]); } return super.invokeMethod(name, args, history); } public int MAKE_SYMBOLIC(History history) { IntValue length = new IntValue(string.length()); int ret = symbol; symbol = symbol + inc; symbolicExp = new SymbolicStringExpression(ret, length); length.MAKE_SYMBOLIC(history); Constraint results = length.symbolic.setop(COMPARISON_OPS.GE); boolean resultc = length.concrete >= 0; history.checkAndSetBranch(resultc, results, 0); if (resultc) { history.setLastBranchDone(); } results = length .ISUB(new IntValue(Config.instance.maxStringLength)) .symbolic .setop(COMPARISON_OPS.LE); resultc = length.concrete <= Config.instance.maxStringLength; history.checkAndSetBranch(resultc, results, 0); if (resultc) { history.setLastBranchDone(); } return ret; } }