Code with Finding: |
class CollapseVariableDeclarations.ExploitAssigns { /** * Collapse the given assign expression into the expression directly * following it, if possible. * * @param expr The expression that may be moved. * @param exprParent The parent of {@code expr}. * @param value The value of this expression, expressed as a node. Each * expression may have multiple values, so this function may be called * multiple times for the same expression. For example, * <code> * a = true; * </code> * is equal to the name "a" and the boolean "true". * @return Whether the expression was collapsed succesfully. */ private boolean collapseAssignEqualTo(Node expr, Node exprParent, Node value) { Node assign = expr.getFirstChild(); Node parent = exprParent; Node next = expr.getNext(); while (next != null) { switch (next.getType()) { case Token.AND: case Token.OR: case Token.HOOK: case Token.IF: case Token.RETURN: case Token.EXPR_RESULT: // Dive down the left side parent = next; next = next.getFirstChild(); break;
case Token.VAR: if (next.getFirstChild().hasChildren()) { parent = next.getFirstChild(); next = parent.getFirstChild(); break; } return false;
case Token.GETPROP: case Token.NAME: if (next.isQualifiedName()) { String nextName = next.getQualifiedName(); if (value.isQualifiedName() && nextName.equals(value.getQualifiedName())) { // If the previous expression evaluates to value of a // qualified name, and that qualified name is used again // shortly, then we can exploit the assign here.
// Verify the assignment doesn't change its own value. if (!isSafeReplacement(next, assign)) { return false; }
exprParent.removeChild(expr); expr.removeChild(assign); parent.replaceChild(next, assign); return true; } } return false;
case Token.NUMBER: case Token.TRUE: case Token.FALSE: case Token.NULL: case Token.STRING: if (value.getType() == next.getType()) { if ((next.getType() == Token.STRING || next.getType() == Token.NUMBER) && !next.isEquivalentTo(value)) { return false; }
// If the r-value of the expr assign is an immutable value, // and the value is used again shortly, then we can exploit // the assign here. exprParent.removeChild(expr); expr.removeChild(assign); parent.replaceChild(next, assign); return true; } return false;
case Token.ASSIGN: // Assigns are really tricky. In lots of cases, we want to inline // into the right side of the assign. But the left side of the // assign is evaluated first, and it may have convoluted logic: // a = null; // (a = b).c = null; // We don't want to exploit the first assign. Similarly: // a.b = null; // a.b.c = null; // We don't want to exploit the first assign either. // // To protect against this, we simply only inline when the left side // is guaranteed to evaluate to the same L-value no matter what. Node leftSide = next.getFirstChild(); if (leftSide.getType() == Token.NAME || leftSide.getType() == Token.GETPROP && leftSide.getFirstChild().getType() == Token.THIS) { // Dive down the right side of the assign. parent = next; next = leftSide.getNext(); break; } else { return false; }
default: // Return without inlining a thing return false; } }
return false; }
}
|