package janala.interpreters;
import janala.solvers.CVC4Solver.CONSTRAINT_TYPE;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.Map;
public class SymbolicStringPredicate extends Constraint {
public static enum STRING_COMPARISON_OPS {
EQ,
NE,
IN,
NOTIN
};
private final STRING_COMPARISON_OPS op;
public STRING_COMPARISON_OPS getOp() { return op; }
private final Object left;
private final Object right;
public SymbolicStringPredicate(STRING_COMPARISON_OPS op, Object left, Object right) {
this.op = op;
this.left = left;
this.right = right;
}
public SymbolicStringPredicate(SymbolicStringPredicate other) {
this.op = other.op;
this.left = other.left;
this.right = other.right;
}
@Override
public void accept(ConstraintVisitor v) {
v.visitSymbolicStringPredicate(this);
}
@Override
public Constraint not() {
SymbolicStringPredicate ret = new SymbolicStringPredicate(this);
STRING_COMPARISON_OPS retOp = STRING_COMPARISON_OPS.NE;
switch (this.op) {
case EQ:
retOp = STRING_COMPARISON_OPS.NE;
break;
case NE:
retOp = STRING_COMPARISON_OPS.EQ;
break;
case IN:
retOp = STRING_COMPARISON_OPS.NOTIN;
break;
case NOTIN:
retOp = STRING_COMPARISON_OPS.IN;
break;
}
return new SymbolicStringPredicate(retOp, left, right);
}
@Override
public Constraint substitute(Map<String, Long> assignments) {
return this;
}
public Constraint substitute(ArrayList<Value> assignments) {
return this;
}
private String stringfy(Object s) {
if (s instanceof String) {
return "\"" + s + "\"";
} else if (s == null) {
return "null";
} else {
return s.toString();
}
}
public String toString() {
switch (this.op) {
case EQ:
return stringfy(this.left) + " == " + stringfy(this.right);
case NE:
return stringfy(this.left) + " != " + stringfy(this.right);
case IN:
return stringfy(this.left) + " regexin " + stringfy(this.right);
case NOTIN:
return stringfy(this.left) + " regexnotin " + stringfy(this.right);
}
throw new RuntimeException("Not implemented");
}
private static class ExprAt {
final boolean isSymbolic;
final String prefix;
final int symOrVal;
ExprAt(boolean symbolic, String prefix, int symOrVal) {
isSymbolic = symbolic;
this.prefix = prefix;
this.symOrVal = symOrVal;
}
}
private SymOrInt exprAt(Object sExpr, int i, Set<String> freeVars,
Map<String, Long> assignments) {
if (sExpr instanceof String) {
return new SymOrInt(((String) sExpr).charAt(i));
} else {
SymbolicStringExpression tmp = (SymbolicStringExpression) sExpr;
return tmp.getExprAt(i, freeVars, assignments);
}
}
private Constraint getStringEqualityFormula(
Object left,
Object right,
long length,
Set<String> freeVars,
Map<String, Long> assignments) {
SymbolicAndConstraint and = null;
if (length <= 0) {
return SymbolicTrueConstraint.instance;
}
for (int i = 0; i < length; i++) {
SymOrInt e1 = exprAt(left, i, freeVars, assignments);
SymOrInt e2 = exprAt(right, i, freeVars, assignments);
Constraint c;
c = new SymbolicIntCompareConstraint(e1, e2,
COMPARISON_OPS.EQ);
if (i != 0) {
and = and.AND(c);
} else {
and = new SymbolicAndConstraint(c);
}
}
return and;
}
private IntValue getLength(Object s) {
return (s instanceof String)
? new IntValue(((String) s).length())
: ((SymbolicStringExpression) s).getField("length");
}
public Constraint getFormula(
Set<String> freeVars,
CONSTRAINT_TYPE mode,
Map<String, Long> assignments) {
StringBuilder sb = new StringBuilder();
int j;
IntValue s1 = getLength(left);
IntValue s2 = getLength(right);
long length1 = s1.substituteInLinear(assignments);
long length2 = s2.substituteInLinear(assignments);
if (mode == CONSTRAINT_TYPE.INT) {
switch (this.op) {
case EQ:
IntValue val = s1.ISUB(s2);
if (val.symbolic != null) {
return val.symbolic.setop(COMPARISON_OPS.EQ);
} else {
if (val.getConcrete().equals(0)) {
return SymbolicTrueConstraint.instance;
} else {
return SymbolicFalseConstraint.instance;
}
}
case NE:
// This quick check is a little confusing.
// Essentially it checks that if left and right size are nonempty strings.
SymbolicInt int1 =
s1.symbolic != null ? s1.symbolic.setop(COMPARISON_OPS.GT) : null;
SymbolicInt int2 =
s2.symbolic != null ? s2.symbolic.setop(COMPARISON_OPS.GT) : null;
if (int1 != null && int2 != null) {
SymbolicAndConstraint ret = new SymbolicAndConstraint(int1);
return ret.AND(int2);
} else if (int1 != null) {
return int1;
} else if (int2 != null) {
return int2;
} else {
return SymbolicTrueConstraint.instance;
}
case IN:
// @todo regex_escape
return RegexpEncoder.getLengthFormulaString(
(String) this.right, "x", s1.getSymbol(), true);
case NOTIN:
// @todo regex_escape
return RegexpEncoder.getLengthFormulaString(
(String) this.right, "x", s1.getSymbol(), false);
}
} else if (mode == CONSTRAINT_TYPE.STR) {
switch (this.op) {
case EQ:
if (length1 != length2) {
return SymbolicFalseConstraint.instance;
} else {
return getStringEqualityFormula(this.left, this.right, length1, freeVars, assignments);
}
case NE:
if (length1 != length2) {
return SymbolicTrueConstraint.instance;
} else {
return new SymbolicNotConstraint(
getStringEqualityFormula(this.left, this.right, length1, freeVars, assignments));
}
// return (length1 !== length2)?"TRUE":"FALSE";
case IN:
for (j = 0; j < length1; j++) {
freeVars.add("x" + this.left + "__" + j);
}
// @todo regex_escape
return RegexpEncoder.getRegexpFormulaString(
(String) this.right, "x" + this.left + "__", (int) length1);
case NOTIN:
for (j = 0; j < length1; j++) {
freeVars.add("x" + this.left + "__" + j);
}
// @todo regex_escape
return RegexpEncoder.getRegexpFormulaString(
"~(" + (String) this.right + ")", "x" + this.left + "__", (int) length1);
}
}
throw new RuntimeException("Unsupported type");
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (o == this) {
return true;
}
if (!(o instanceof SymbolicStringPredicate)) {
return false;
}
SymbolicStringPredicate tmp = (SymbolicStringPredicate) o;
if (this.op != tmp.op) {
return false;
}
String s1 = stringfy(left);
String s2 = stringfy(right);
String s3 = stringfy(tmp.left);
String s4 = stringfy(tmp.right);
return (s1.equals(s3) && s2.equals(s4));
}
}