diff --git a/liquidjava-verifier/pom.xml b/liquidjava-verifier/pom.xml index 5b42f4cb..bcfa7079 100644 --- a/liquidjava-verifier/pom.xml +++ b/liquidjava-verifier/pom.xml @@ -95,7 +95,50 @@ + +org.jacoco +jacoco-maven-plugin +0.8.14 + + +default-prepare-agent + +prepare-agent + + + +default-report + +report + + + +default-check + +check + + + + +BUNDLE + + +COMPLEXITY +COVEREDRATIO +0.60 + + + + + + + + + + + + @@ -207,3 +250,4 @@ + diff --git a/liquidjava-verifier/src/main/java/liquidjava/rj_language/opt/BuiltinFunctionPredicate.java b/liquidjava-verifier/src/main/java/liquidjava/rj_language/opt/BuiltinFunctionPredicate.java new file mode 100644 index 00000000..62284b25 --- /dev/null +++ b/liquidjava-verifier/src/main/java/liquidjava/rj_language/opt/BuiltinFunctionPredicate.java @@ -0,0 +1,33 @@ +package liquidjava.rj_language; + +import liquidjava.errors.ErrorEmitter; +import liquidjava.rj_language.parsing.ParsingException; +import spoon.reflect.declaration.CtElement; + +public class BuiltinFunctionPredicate extends Predicate { + + public BuiltinFunctionPredicate(ErrorEmitter ee, CtElement elem, String functionName, String... params) + throws ParsingException { + super(functionName + "(" + getFormattedParams(params) + ")", elem, ee); + } + + public static BuiltinFunctionPredicate builtin_length(String param, CtElement elem, ErrorEmitter ee) + throws ParsingException { + return new BuiltinFunctionPredicate(ee, elem, "length", param); + } + + public static BuiltinFunctionPredicate builtin_addToIndex(String array, String index, String value, CtElement elem, + ErrorEmitter ee) throws ParsingException { + return new BuiltinFunctionPredicate(ee, elem, "addToIndex", index, value); + } + + private static String getFormattedParams(String... params) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < params.length; i++) { + sb.append(params[i]); + if (i < params.length - 1) + sb.append(", "); + } + return sb.toString(); + } +} diff --git a/liquidjava-verifier/src/main/java/liquidjava/rj_language/opt/Predicate.java b/liquidjava-verifier/src/main/java/liquidjava/rj_language/opt/Predicate.java new file mode 100644 index 00000000..7fb7fa94 --- /dev/null +++ b/liquidjava-verifier/src/main/java/liquidjava/rj_language/opt/Predicate.java @@ -0,0 +1,269 @@ +package liquidjava.rj_language; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import liquidjava.errors.ErrorEmitter; +import liquidjava.errors.ErrorHandler; +import liquidjava.processor.context.AliasWrapper; +import liquidjava.processor.context.Context; +import liquidjava.processor.context.GhostState; +import liquidjava.processor.facade.AliasDTO; +import liquidjava.rj_language.ast.BinaryExpression; +import liquidjava.rj_language.ast.Expression; +import liquidjava.rj_language.ast.FunctionInvocation; +import liquidjava.rj_language.ast.GroupExpression; +import liquidjava.rj_language.ast.Ite; +import liquidjava.rj_language.ast.LiteralBoolean; +import liquidjava.rj_language.ast.LiteralInt; +import liquidjava.rj_language.ast.LiteralReal; +import liquidjava.rj_language.ast.UnaryExpression; +import liquidjava.rj_language.ast.Var; +import liquidjava.rj_language.opt.derivation_node.DerivationNode; +import liquidjava.rj_language.opt.derivation_node.ValDerivationNode; +import liquidjava.rj_language.opt.ExpressionSimplifier; +import liquidjava.rj_language.parsing.ParsingException; +import liquidjava.rj_language.parsing.RefinementsParser; +import liquidjava.utils.Utils; +import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.CtType; +import spoon.reflect.factory.Factory; + +/** + * Acts as a wrapper for Expression AST + * + * @author cgamboa + */ +public class Predicate { + + protected Expression exp; + protected String prefix; + + /** Create a predicate with the expression true */ + public Predicate() { + exp = new LiteralBoolean(true); + } + + /** + * Create a new predicate with a refinement + * + * @param ref + * @param element + * @param e + * + * @throws ParsingException + */ + public Predicate(String ref, CtElement element, ErrorEmitter e) throws ParsingException { + this(ref, element, e, element.getParent(CtType.class).getQualifiedName()); + } + + /** + * Create a new predicate with a refinement and a given prefix for the class + * + * @param ref + * @param element + * @param e + * @param prefix + * + * @throws ParsingException + */ + public Predicate(String ref, CtElement element, ErrorEmitter e, String prefix) throws ParsingException { + this.prefix = prefix; + exp = parse(ref, element, e); + if (e.foundError()) { + return; + } + if (!(exp instanceof GroupExpression)) { + exp = new GroupExpression(exp); + } + } + + /** Create a predicate with the expression true */ + public Predicate(Expression e) { + exp = e; + } + + protected Expression parse(String ref, CtElement element, ErrorEmitter e) throws ParsingException { + try { + return RefinementsParser.createAST(ref, prefix); + } catch (ParsingException e1) { + ErrorHandler.printSyntaxError(e1.getMessage(), ref, element, e); + throw e1; + } + } + + protected Expression innerParse(String ref, ErrorEmitter e, String prefix) { + try { + return RefinementsParser.createAST(ref, prefix); + } catch (ParsingException e1) { + ErrorHandler.printSyntaxError(e1.getMessage(), ref, e); + } + return null; + } + + public Predicate changeAliasToRefinement(Context context, Factory f) throws Exception { + Expression ref = getExpression(); + + Map alias = new HashMap<>(); + for (AliasWrapper aw : context.getAlias()) { + alias.put(aw.getName(), aw.createAliasDTO()); + } + + ref = ref.changeAlias(alias, context, f); + return new Predicate(ref); + } + + public Predicate negate() { + return new Predicate(new UnaryExpression("!", exp)); + } + + public Predicate substituteVariable(String from, String to) { + Expression ec = exp.clone(); + ec = ec.substitute(new Var(from), new Var(to)); + return new Predicate(ec); + } + + public List getVariableNames() { + List l = new ArrayList<>(); + exp.getVariableNames(l); + return l; + } + + public List getStateInvocations(List lgs) { + if (lgs == null) + return new ArrayList<>(); + List all = lgs.stream().map(p -> p.getQualifiedName()).collect(Collectors.toList()); + List toAdd = new ArrayList<>(); + exp.getStateInvocations(toAdd, all); + + List gh = new ArrayList<>(); + for (String n : toAdd) { + for (GhostState g : lgs) + if (g.matches(n)) + gh.add(g); + } + + return gh; + } + + /** Change old mentions of previous name to the new name e.g., old(previousName) -> newName */ + public Predicate changeOldMentions(String previousName, String newName, ErrorEmitter ee) { + Expression e = exp.clone(); + Expression prev = createVar(previousName).getExpression(); + List le = new ArrayList<>(); + le.add(createVar(newName).getExpression()); + e.substituteFunction(Utils.OLD, le, prev); + return new Predicate(e); + } + + public List getOldVariableNames() { + List ls = new ArrayList<>(); + expressionGetOldVariableNames(this.exp, ls); + return ls; + } + + private void expressionGetOldVariableNames(Expression exp, List ls) { + if (exp instanceof FunctionInvocation) { + FunctionInvocation fi = (FunctionInvocation) exp; + if (fi.getName().equals(Utils.OLD)) { + List le = fi.getArgs(); + for (Expression e : le) { + if (e instanceof Var) + ls.add(((Var) e).getName()); + } + } + } + if (exp.hasChildren()) { + for (var ch : exp.getChildren()) + expressionGetOldVariableNames(ch, ls); + } + } + + public Predicate changeStatesToRefinements(List ghostState, String[] toChange, ErrorEmitter ee) { + Map nameRefinementMap = new HashMap<>(); + for (GhostState gs : ghostState) { + if (gs.getRefinement() != null) { // is a state and not a ghost state + String name = gs.getQualifiedName(); + Expression exp = innerParse(gs.getRefinement().toString(), ee, gs.getPrefix()); + nameRefinementMap.put(name, exp); + // Also allow simple name lookup to enable hierarchy matching + String simple = Utils.getSimpleName(name); + nameRefinementMap.putIfAbsent(simple, exp); + } + } + Expression e = exp.substituteState(nameRefinementMap, toChange); + return new Predicate(e); + } + + public boolean isBooleanTrue() { + return exp.isBooleanTrue(); + } + + @Override + public String toString() { + return exp.toString(); + } + + @Override + public Predicate clone() { + return new Predicate(exp.clone()); + } + + public Expression getExpression() { + return exp; + } + + public ValDerivationNode simplify() { + return ExpressionSimplifier.simplify(exp.clone()); + } + + public static Predicate createConjunction(Predicate c1, Predicate c2) { + return new Predicate(new BinaryExpression(c1.getExpression(), Utils.AND, c2.getExpression())); + } + + public static Predicate createDisjunction(Predicate c1, Predicate c2) { + return new Predicate(new BinaryExpression(c1.getExpression(), Utils.OR, c2.getExpression())); + } + + public static Predicate createEquals(Predicate c1, Predicate c2) { + return new Predicate(new BinaryExpression(c1.getExpression(), Utils.EQ, c2.getExpression())); + } + + public static Predicate createITE(Predicate c1, Predicate c2, Predicate c3) { + return new Predicate(new Ite(c1.getExpression(), c2.getExpression(), c3.getExpression())); + } + + public static Predicate createLit(String value, String type) { + Expression ex; + if (type.equals(Utils.BOOLEAN)) + ex = new LiteralBoolean(value); + else if (type.equals(Utils.INT)) + ex = new LiteralInt(value); + else if (type.equals(Utils.DOUBLE)) + ex = new LiteralReal(value); + else if (type.equals(Utils.SHORT)) + ex = new LiteralInt(value); + else if (type.equals(Utils.LONG)) + ex = new LiteralReal(value); + else // if(type.equals(Utils.DOUBLE)) + ex = new LiteralReal(value); + return new Predicate(ex); + } + + public static Predicate createOperation(Predicate c1, String op, Predicate c2) { + return new Predicate(new BinaryExpression(c1.getExpression(), op, c2.getExpression())); + } + + public static Predicate createVar(String name) { + return new Predicate(new Var(name)); + } + + public static Predicate createInvocation(String name, Predicate... Predicates) { + List le = new ArrayList<>(); + for (Predicate c : Predicates) + le.add(c.getExpression()); + return new Predicate(new FunctionInvocation(name, le)); + } +} diff --git a/liquidjava-verifier/src/test/java/liquidjava/rj_language/TestLiteralString.java b/liquidjava-verifier/src/test/java/liquidjava/rj_language/TestLiteralString.java new file mode 100644 index 00000000..7e82772f --- /dev/null +++ b/liquidjava-verifier/src/test/java/liquidjava/rj_language/TestLiteralString.java @@ -0,0 +1,14 @@ +package liquidjava.rj_language; + +import static org.junit.Assert.assertNotEquals; +import org.junit.Test; +import liquidjava.rj_language.ast.LiteralString; + +public class TestLiteralString { + @Test + public void testLiteralString() { + LiteralString s1 = new LiteralString("hello"); + LiteralString s2 = new LiteralString("world"); + assertNotEquals(s1.hashCode(), s2.hashCode()); + } +} diff --git a/liquidjava-verifier/src/test/java/liquidjava/rj_language/opt/TestOptimization.java b/liquidjava-verifier/src/test/java/liquidjava/rj_language/opt/TestOptimization.java new file mode 100644 index 00000000..26f02e37 --- /dev/null +++ b/liquidjava-verifier/src/test/java/liquidjava/rj_language/opt/TestOptimization.java @@ -0,0 +1,16 @@ +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +import liquidjava.rj_language.ast.BinaryExpression; +import liquidjava.rj_language.ast.LiteralInt; +import liquidjava.rj_language.opt.ConstantFolding; +import liquidjava.rj_language.opt.derivation_node.ValDerivationNode; + +public class TestOptimization { + @Test + public void testBinaryFold() { + BinaryExpression b = new BinaryExpression(new LiteralInt(1), "+", new LiteralInt(2)); + ValDerivationNode r = ConstantFolding.fold(new ValDerivationNode(b, null)); + assertEquals(r.getValue(), new LiteralInt(3)); + } +}