// This transforms actions that are assignments into one or two parts: if the action depends on a // previous property value, // it generates a variable declaration that is initialized to the value before the said action. // This is the "pre" statement. // The second "post" statement is the action converted to an assert that checks if the results are // correct. // Post and pre increments and decrements and compound assign operators are expanded to simple // assigns private static List<ActionCheck> generateActionChecks( String action, Map<String, Integer> usedVarNames) { final List<ActionCheck> checks = new ArrayList<ActionCheck>(); final List<Statement> statements = actionAsStatements(action); for (Statement statement : statements) { // We're looking for an action that is an assign expression statement if (!(statement instanceof ExpressionStmt)) { continue; } Expression expression = ((ExpressionStmt) statement).getExpression(); if (!(expression instanceof AssignExpr)) { // Check if this is actually a decrement or increment operator if (expression instanceof UnaryExpr) { final UnaryExpr unary = (UnaryExpr) expression; final Operator operator; switch (unary.getOperator()) { case preDecrement: case posDecrement: operator = Operator.minus; break; case preIncrement: case posIncrement: operator = Operator.plus; break; default: continue; } // If this is the case, expand it and keep going final Expression target = unary.getExpr(); expression = new AssignExpr( target, new BinaryExpr(target, new IntegerLiteralExpr("1"), operator), AssignExpr.Operator.assign); } else { continue; } } AssignExpr assign = (AssignExpr) expression; if (assign.getOperator() != AssignExpr.Operator.assign) { // This is a compound assign, so expand it to a simple assign final Expression target = assign.getTarget(); assign = new AssignExpr( target, new BinaryExpr( target, new EnclosedExpr(assign.getValue()), getExpandedOperator(assign.getOperator())), AssignExpr.Operator.assign); } // Generate the check code for the assignment checks.add(new ActionCheck(assign, usedVarNames)); } return checks; }
// This logically inverts a condition expression, simplifying the AST if possible private static Expression invertCondition(Expression expression) { if (expression instanceof UnaryExpr) { final UnaryExpr unary = (UnaryExpr) expression; // Unary not expression, just remove the not if (unary.getOperator() == UnaryExpr.Operator.not) { return unary.getExpr(); } else { // Else wrap it in a not return new UnaryExpr(expression, UnaryExpr.Operator.not); } } else if (expression instanceof BinaryExpr) { final BinaryExpr binary = (BinaryExpr) expression; final Operator inverted; // For binary compare operators, just use the opposite operator // Boolean operators use De Morgan's law // Other operators use a not operator, but are also enclosed in () switch (binary.getOperator()) { case equals: inverted = Operator.notEquals; break; case notEquals: inverted = Operator.equals; break; case less: inverted = Operator.greaterEquals; break; case greater: inverted = Operator.lessEquals; break; case lessEquals: inverted = Operator.greater; break; case greaterEquals: inverted = Operator.less; break; case or: return new BinaryExpr( invertCondition(binary.getLeft()), invertCondition(binary.getRight()), Operator.and); case and: return new BinaryExpr( invertCondition(binary.getLeft()), invertCondition(binary.getRight()), Operator.or); default: return new UnaryExpr(new EnclosedExpr(binary), UnaryExpr.Operator.not); } return new BinaryExpr(binary.getLeft(), binary.getRight(), inverted); } else if (expression instanceof AssignExpr || expression instanceof ConditionalExpr || expression instanceof InstanceOfExpr) { // This expressions have lower precedence so they need to be enclosed in () return new UnaryExpr(new EnclosedExpr(expression), UnaryExpr.Operator.not); } else { // These are higher precedence, no need for () return new UnaryExpr(expression, UnaryExpr.Operator.not); } }
@Override public Boolean visit(final UnaryExpr n1, final Node arg) { final UnaryExpr n2 = (UnaryExpr) arg; if (n1.getOperator() != n2.getOperator()) { return Boolean.FALSE; } if (!nodeEquals(n1.getExpr(), n2.getExpr())) { return Boolean.FALSE; } return Boolean.TRUE; }
@Override public void visit(final UnaryExpr n, final A arg) { visitComment(n.getComment(), arg); n.getExpr().accept(this, arg); }