Code with Finding: |
class PeepholeFoldConstants {
/**
* Try to fold an array join: ['a', 'b', 'c'].join('') -> 'abc';
*/
private Node tryFoldArrayJoin(Node n) {
Node callTarget = n.getFirstChild();
if (callTarget == null) {
return n;
}
Node right = callTarget.getNext();
if (right == null) {
return n;
}
if (!NodeUtil.isGetProp(callTarget) || !NodeUtil.isImmutableValue(right)) {
return n;
}
Node arrayNode = callTarget.getFirstChild();
Node functionName = arrayNode.getNext();
if ((arrayNode.getType() != Token.ARRAYLIT) ||
!functionName.getString().equals("join")) {
return n;
}
String joinString = NodeUtil.getStringValue(right);
List<Node> arrayFoldedChildren = Lists.newLinkedList();
StringBuilder sb = null;
int foldedSize = 0;
Node prev = null;
Node elem = arrayNode.getFirstChild();
// Merges adjacent String nodes.
while (elem != null) {
if (NodeUtil.isImmutableValue(elem)) {
if (sb == null) {
sb = new StringBuilder();
} else {
sb.append(joinString);
}
sb.append(NodeUtil.getStringValue(elem));
} else {
if (sb != null) {
Preconditions.checkNotNull(prev);
// + 2 for the quotes.
foldedSize += sb.length() + 2;
arrayFoldedChildren.add(
Node.newString(sb.toString()).copyInformationFrom(prev));
sb = null;
}
foldedSize += InlineCostEstimator.getCost(elem);
arrayFoldedChildren.add(elem);
}
prev = elem;
elem = elem.getNext();
}
if (sb != null) {
Preconditions.checkNotNull(prev);
// + 2 for the quotes.
foldedSize += sb.length() + 2;
arrayFoldedChildren.add(
Node.newString(sb.toString()).copyInformationFrom(prev));
}
// one for each comma.
foldedSize += arrayFoldedChildren.size() - 1;
int originalSize = InlineCostEstimator.getCost(n);
switch (arrayFoldedChildren.size()) {
case 0:
Node emptyStringNode = Node.newString("");
n.getParent().replaceChild(n, emptyStringNode);
reportCodeChange();
return emptyStringNode;
case 1:
Node foldedStringNode = arrayFoldedChildren.remove(0);
if (foldedSize > originalSize) {
return n;
}
arrayNode.detachChildren();
if (foldedStringNode.getType() != Token.STRING) {
// If the Node is not a string literal, ensure that
// it is coerced to a string.
Node replacement = new Node(Token.ADD,
Node.newString("").copyInformationFrom(right),
foldedStringNode);
foldedStringNode = replacement;
}
n.getParent().replaceChild(n, foldedStringNode);
reportCodeChange();
return foldedStringNode;
default:
// No folding could actually be performed.
if (arrayFoldedChildren.size() == arrayNode.getChildCount()) {
return n;
}
int kJoinOverhead = "[].join()".length();
foldedSize += kJoinOverhead;
foldedSize += InlineCostEstimator.getCost(right);
if (foldedSize > originalSize) {
return n;
}
arrayNode.detachChildren();
for (Node node : arrayFoldedChildren) {
arrayNode.addChildToBack(node);
}
reportCodeChange();
break;
}
return n;
}
}
|