aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java')
-rw-r--r--src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java501
1 files changed, 295 insertions, 206 deletions
diff --git a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
index ac3c2934..c7f0800f 100644
--- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
+++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
@@ -40,34 +40,37 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Function;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
-import jdk.nashorn.internal.ir.BaseNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.BreakNode;
import jdk.nashorn.internal.ir.BreakableNode;
+import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
import jdk.nashorn.internal.ir.Expression;
+import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
-import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
+import jdk.nashorn.internal.ir.GetSplitState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.JoinPredecessor;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
import jdk.nashorn.internal.ir.JumpStatement;
+import jdk.nashorn.internal.ir.JumpToInlinedFinally;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LexicalContextNode;
import jdk.nashorn.internal.ir.LiteralNode;
+import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LocalVariableConversion;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.ObjectNode;
import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
@@ -82,7 +85,9 @@ import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
+import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
/**
@@ -101,7 +106,7 @@ import jdk.nashorn.internal.parser.TokenType;
* instances of the calculator to be run on nested functions (when not lazy compiling).
*
*/
-final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
+final class LocalVariableTypesCalculator extends SimpleNodeVisitor {
private static class JumpOrigin {
final JoinPredecessor node;
@@ -131,8 +136,44 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
OBJECT(Type.OBJECT);
private final Type type;
+ private final TypeHolderExpression typeExpression;
+
private LvarType(final Type type) {
this.type = type;
+ this.typeExpression = new TypeHolderExpression(type);
+ }
+ }
+
+ /**
+ * A bogus Expression subclass that only reports its type. Used to interrogate BinaryNode and UnaryNode about their
+ * types by creating temporary copies of them and replacing their operands with instances of these. An alternative
+ * solution would be to add BinaryNode.getType(Type lhsType, Type rhsType) and UnaryNode.getType(Type exprType)
+ * methods. For the time being though, this is easier to implement and is in fact fairly clean. It does result in
+ * generation of higher number of temporary short lived nodes, though.
+ */
+ private static class TypeHolderExpression extends Expression {
+ private static final long serialVersionUID = 1L;
+
+ private final Type type;
+
+ TypeHolderExpression(final Type type) {
+ super(0L, 0, 0);
+ this.type = type;
+ }
+
+ @Override
+ public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+ @Override
+ public void toString(final StringBuilder sb, final boolean printType) {
+ throw new AssertionError();
}
}
@@ -165,7 +206,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
// continuations (since RewriteException's byteCodeSlots carries an array and not a name-value map).
symbolIsConverted(symbol, branchLvarType, targetType);
- //symbolIsUsed(symbol, branchLvarType);
return new LocalVariableConversion(symbol, branchLvarType.type, targetType.type, next);
}
@@ -188,7 +228,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
for(final Symbol symbol: commonSymbols) {
final LvarType type1 = types1.get(symbol);
final LvarType type2 = types2.get(symbol);
- final LvarType widest = widestLvarType(type1, type2);
+ final LvarType widest = widestLvarType(type1, type2);
if(widest != type1 && matches1) {
matches1 = false;
if(!matches2) {
@@ -201,7 +241,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
union = cloneMap(types2);
}
}
- if(!(matches1 || matches2) && union != null) { //remove overly enthusiastic "union can be null" warning
+ if(!(matches1 || matches2)) {
assert union != null;
union.put(symbol, widest);
}
@@ -359,6 +399,8 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
// allocates a new map. Immutability of maps allows for cheap snapshots by just keeping the reference to the current
// value.
private Map<Symbol, LvarType> localVariableTypes = new IdentityHashMap<>();
+ // Stack for evaluated expression types.
+ private final Deque<LvarType> typeStack = new ArrayDeque<>();
// Whether the current point in the AST is reachable code
private boolean reachable = true;
@@ -375,8 +417,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
private final Map<IdentNode, LvarType> identifierLvarTypes = new IdentityHashMap<>();
private final Map<Symbol, SymbolConversions> symbolConversions = new IdentityHashMap<>();
- private SymbolToType symbolToType = new SymbolToType();
-
// Stack of open labels for starts of catch blocks, one for every currently traversed try block; for inserting
// control flow edges to them. Note that we currently don't insert actual control flow edges, but instead edges that
// help us with type calculations. This means that some operations that can result in an exception being thrown
@@ -386,7 +426,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
private final Deque<Label> catchLabels = new ArrayDeque<>();
LocalVariableTypesCalculator(final Compiler compiler) {
- super(new LexicalContext());
this.compiler = compiler;
}
@@ -400,62 +439,56 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
private void doesNotContinueSequentially() {
reachable = false;
localVariableTypes = Collections.emptyMap();
+ assertTypeStackIsEmpty();
}
+ private boolean pushExpressionType(final Expression expr) {
+ typeStack.push(toLvarType(expr.getType()));
+ return false;
+ }
+
+ @Override
+ public boolean enterAccessNode(final AccessNode accessNode) {
+ visitExpression(accessNode.getBase());
+ return pushExpressionType(accessNode);
+ }
@Override
public boolean enterBinaryNode(final BinaryNode binaryNode) {
// NOTE: regardless of operator's lexical associativity, lhs is always evaluated first.
final Expression lhs = binaryNode.lhs();
- final boolean isAssignment = binaryNode.isAssignment();
- LvarType lhsTypeOnLoad = null;
- if(isAssignment) {
- if(lhs instanceof BaseNode) {
- ((BaseNode)lhs).getBase().accept(this);
- if(lhs instanceof IndexNode) {
- ((IndexNode)lhs).getIndex().accept(this);
- } else {
- assert lhs instanceof AccessNode;
- }
- } else {
- assert lhs instanceof IdentNode;
- if(binaryNode.isSelfModifying()) {
- final IdentNode ident = ((IdentNode)lhs);
- ident.accept(this);
- // Self-assignment can cause a change in the type of the variable. For purposes of evaluating
- // the type of the operation, we must use its type as it was when it was loaded. If we didn't
- // do this, some awkward expressions would end up being calculated incorrectly, e.g.
- // "var x; x += x = 0;". In this case we have undefined+int so the result type is double (NaN).
- // However, if we used the type of "x" on LHS after we evaluated RHS, we'd see int+int, so the
- // result type would be either optimistic int or pessimistic long, which would be wrong.
- lhsTypeOnLoad = getLocalVariableTypeIfBytecode(ident.getSymbol());
- }
- }
+ final LvarType lhsType;
+ if (!(lhs instanceof IdentNode && binaryNode.isTokenType(TokenType.ASSIGN))) {
+ lhsType = visitExpression(lhs);
} else {
- lhs.accept(this);
+ // Can't visit IdentNode on LHS of a simple assignment, as visits imply use, and this is def.
+ // The type is irrelevant, as only RHS is used to determine the type anyway.
+ lhsType = LvarType.UNDEFINED;
}
final boolean isLogical = binaryNode.isLogical();
- assert !(isAssignment && isLogical); // there are no logical assignment operators in JS
final Label joinLabel = isLogical ? new Label("") : null;
if(isLogical) {
jumpToLabel((JoinPredecessor)lhs, joinLabel);
}
final Expression rhs = binaryNode.rhs();
- rhs.accept(this);
+ final LvarType rhsType = visitExpression(rhs);
if(isLogical) {
jumpToLabel((JoinPredecessor)rhs, joinLabel);
}
joinOnLabel(joinLabel);
- if(isAssignment && lhs instanceof IdentNode) {
+ final LvarType type = toLvarType(binaryNode.setOperands(lhsType.typeExpression, rhsType.typeExpression).getType());
+
+ if(binaryNode.isAssignment() && lhs instanceof IdentNode) {
if(binaryNode.isSelfModifying()) {
- onSelfAssignment((IdentNode)lhs, binaryNode, lhsTypeOnLoad);
+ onSelfAssignment((IdentNode)lhs, type);
} else {
- onAssignment((IdentNode)lhs, rhs);
+ onAssignment((IdentNode)lhs, type);
}
}
+ typeStack.push(type);
return false;
}
@@ -475,6 +508,17 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
@Override
+ public boolean enterCallNode(final CallNode callNode) {
+ visitExpression(callNode.getFunction());
+ visitExpressions(callNode.getArgs());
+ final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
+ if (evalArgs != null) {
+ visitExpressions(evalArgs.getArgs());
+ }
+ return pushExpressionType(callNode);
+ }
+
+ @Override
public boolean enterContinueNode(final ContinueNode continueNode) {
return enterJumpStatement(continueNode);
}
@@ -483,8 +527,8 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
if(!reachable) {
return false;
}
- final BreakableNode target = jump.getTarget(lc);
- jumpToLabel(jump, jump.getTargetLabel(target), getBreakTargetTypes(target));
+ assertTypeStackIsEmpty();
+ jumpToLabel(jump, jump.getTargetLabel(lc), getBreakTargetTypes(jump.getPopScopeLimit(lc)));
doesNotContinueSequentially();
return false;
}
@@ -495,6 +539,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
private void enterDoWhileLoop(final WhileNode loopNode) {
+ assertTypeStackIsEmpty();
final JoinPredecessorExpression test = loopNode.getTest();
final Block body = loopNode.getBody();
final Label continueLabel = loopNode.getContinueLabel();
@@ -512,7 +557,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
if(!reachable) {
break;
}
- test.accept(this);
+ visitExpressionOnEmptyStack(test);
jumpToLabel(test, breakLabel);
if(isAlwaysFalse(test)) {
break;
@@ -535,6 +580,45 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
@Override
+ public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
+ if (reachable) {
+ visitExpressionOnEmptyStack(expressionStatement.getExpression());
+ }
+ return false;
+ }
+
+ private void assertTypeStackIsEmpty() {
+ assert typeStack.isEmpty();
+ }
+
+ @Override
+ protected Node leaveDefault(final Node node) {
+ assert !(node instanceof Expression); // All expressions were handled
+ assert !(node instanceof Statement) || typeStack.isEmpty(); // No statements leave with a non-empty stack
+ return node;
+ }
+
+ private LvarType visitExpressionOnEmptyStack(final Expression expr) {
+ assertTypeStackIsEmpty();
+ return visitExpression(expr);
+ }
+
+ private LvarType visitExpression(final Expression expr) {
+ final int stackSize = typeStack.size();
+ expr.accept(this);
+ assert typeStack.size() == stackSize + 1;
+ return typeStack.pop();
+ }
+
+ private void visitExpressions(final List<Expression> exprs) {
+ for(final Expression expr: exprs) {
+ if (expr != null) {
+ visitExpression(expr);
+ }
+ }
+ }
+
+ @Override
public boolean enterForNode(final ForNode forNode) {
if(!reachable) {
return false;
@@ -543,7 +627,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
final Expression init = forNode.getInit();
if(forNode.isForIn()) {
final JoinPredecessorExpression iterable = forNode.getModify();
- iterable.accept(this);
+ visitExpression(iterable);
enterTestFirstLoop(forNode, null, init,
// If we're iterating over property names, and we can discern from the runtime environment
// of the compilation that the object being iterated over must use strings for property
@@ -552,16 +636,18 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
!compiler.useOptimisticTypes() || (!forNode.isForEach() && compiler.hasStringPropertyIterator(iterable.getExpression())));
} else {
if(init != null) {
- init.accept(this);
+ visitExpressionOnEmptyStack(init);
}
enterTestFirstLoop(forNode, forNode.getModify(), null, false);
}
+ assertTypeStackIsEmpty();
return false;
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
if(alreadyEnteredTopLevelFunction) {
+ typeStack.push(LvarType.OBJECT);
return false;
}
int pos = 0;
@@ -603,79 +689,139 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
@Override
+ public boolean enterGetSplitState(final GetSplitState getSplitState) {
+ return pushExpressionType(getSplitState);
+ }
+
+ @Override
public boolean enterIdentNode(final IdentNode identNode) {
final Symbol symbol = identNode.getSymbol();
if(symbol.isBytecodeLocal()) {
symbolIsUsed(symbol);
- setIdentifierLvarType(identNode, getLocalVariableType(symbol));
+ final LvarType type = getLocalVariableType(symbol);
+ setIdentifierLvarType(identNode, type);
+ typeStack.push(type);
+ } else {
+ pushExpressionType(identNode);
}
return false;
}
@Override
public boolean enterIfNode(final IfNode ifNode) {
+ processIfNode(ifNode);
+ return false;
+ }
+
+ private void processIfNode(final IfNode ifNode) {
if(!reachable) {
- return false;
+ return;
}
final Expression test = ifNode.getTest();
final Block pass = ifNode.getPass();
final Block fail = ifNode.getFail();
- test.accept(this);
+ visitExpressionOnEmptyStack(test);
- final Map<Symbol, LvarType> afterTestLvarTypes = localVariableTypes;
- if(!isAlwaysFalse(test)) {
+ final Map<Symbol, LvarType> passLvarTypes;
+ final boolean reachableFromPass;
+ final boolean isTestAlwaysTrue = isAlwaysTrue(test);
+ if(isAlwaysFalse(test)) {
+ passLvarTypes = null;
+ reachableFromPass = false;
+ } else {
+ final Map<Symbol, LvarType> afterTestLvarTypes = localVariableTypes;
pass.accept(this);
+ assertTypeStackIsEmpty();
+ if (isTestAlwaysTrue) {
+ return;
+ }
+ passLvarTypes = localVariableTypes;
+ reachableFromPass = reachable;
+ localVariableTypes = afterTestLvarTypes;
+ reachable = true;
}
- final Map<Symbol, LvarType> passLvarTypes = localVariableTypes;
- final boolean reachableFromPass = reachable;
- reachable = true;
- localVariableTypes = afterTestLvarTypes;
- if(!isAlwaysTrue(test) && fail != null) {
+ // If we get here, then we need to consider the case where pass block is not executed
+ assert !isTestAlwaysTrue;
+
+ if (fail != null) {
fail.accept(this);
- final boolean reachableFromFail = reachable;
- reachable |= reachableFromPass;
- if(!reachable) {
- return false;
- }
+ assertTypeStackIsEmpty();
+ }
- if(reachableFromFail) {
- if(reachableFromPass) {
- final Map<Symbol, LvarType> failLvarTypes = localVariableTypes;
- localVariableTypes = getUnionTypes(passLvarTypes, failLvarTypes);
- setConversion(pass, passLvarTypes, localVariableTypes);
- setConversion(fail, failLvarTypes, localVariableTypes);
- }
- return false;
+ if(reachable) {
+ if(reachableFromPass) {
+ final Map<Symbol, LvarType> failLvarTypes = localVariableTypes;
+ localVariableTypes = getUnionTypes(passLvarTypes, failLvarTypes);
+ setConversion(pass, passLvarTypes, localVariableTypes);
+ // IfNode itself is associated with conversions that might need to be performed after the test if
+ // there's no else branch. E.g.
+ // if(x = 1, cond) { x = 1.0 } must widen "x = 1" to a double.
+ setConversion(fail != null ? fail : ifNode, failLvarTypes, localVariableTypes);
}
+ } else if (reachableFromPass) {
+ assert passLvarTypes != null;
+ localVariableTypes = passLvarTypes;
+ reachable = true;
}
+ }
+
+ @Override
+ public boolean enterIndexNode(final IndexNode indexNode) {
+ visitExpression(indexNode.getBase());
+ visitExpression(indexNode.getIndex());
+ return pushExpressionType(indexNode);
+ }
- if(reachableFromPass) {
- localVariableTypes = getUnionTypes(afterTestLvarTypes, passLvarTypes);
- // IfNode itself is associated with conversions that might need to be performed after the test if there's no
- // else branch. E.g.
- // if(x = 1, cond) { x = 1.0 } must widen "x = 1" to a double.
- setConversion(pass, passLvarTypes, localVariableTypes);
- setConversion(ifNode, afterTestLvarTypes, localVariableTypes);
+ @Override
+ public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinExpr) {
+ final Expression expr = joinExpr.getExpression();
+ if (expr != null) {
+ expr.accept(this);
} else {
- localVariableTypes = afterTestLvarTypes;
+ typeStack.push(LvarType.UNDEFINED);
}
-
return false;
}
@Override
- public boolean enterPropertyNode(final PropertyNode propertyNode) {
- // Avoid falsely adding property keys to the control flow graph
- if(propertyNode.getValue() != null) {
- propertyNode.getValue().accept(this);
+ public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) {
+ return enterJumpStatement(jumpToInlinedFinally);
+ }
+
+ @Override
+ public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
+ if (literalNode instanceof ArrayLiteralNode) {
+ final List<Expression> expressions = ((ArrayLiteralNode)literalNode).getElementExpressions();
+ if (expressions != null) {
+ visitExpressions(expressions);
+ }
}
+ pushExpressionType(literalNode);
return false;
}
@Override
+ public boolean enterObjectNode(final ObjectNode objectNode) {
+ for(final PropertyNode propertyNode: objectNode.getElements()) {
+ // Avoid falsely adding property keys to the control flow graph
+ final Expression value = propertyNode.getValue();
+ if (value != null) {
+ visitExpression(value);
+ }
+ }
+ return pushExpressionType(objectNode);
+ }
+
+ @Override
+ public boolean enterPropertyNode(final PropertyNode propertyNode) {
+ // Property nodes are only accessible through object literals, and we handled that case above
+ throw new AssertionError();
+ }
+
+ @Override
public boolean enterReturnNode(final ReturnNode returnNode) {
if(!reachable) {
return false;
@@ -684,9 +830,9 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
final Expression returnExpr = returnNode.getExpression();
final Type returnExprType;
if(returnExpr != null) {
- returnExpr.accept(this);
- returnExprType = getType(returnExpr);
+ returnExprType = visitExpressionOnEmptyStack(returnExpr).type;
} else {
+ assertTypeStackIsEmpty();
returnExprType = Type.UNDEFINED;
}
returnType = Type.widestReturnType(returnType, returnExprType);
@@ -695,6 +841,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
@Override
+ public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
+ visitExpressions(runtimeNode.getArgs());
+ return pushExpressionType(runtimeNode);
+ }
+
+ @Override
public boolean enterSplitReturn(final SplitReturn splitReturn) {
doesNotContinueSequentially();
return false;
@@ -706,8 +858,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
return false;
}
- final Expression expr = switchNode.getExpression();
- expr.accept(this);
+ visitExpressionOnEmptyStack(switchNode.getExpression());
final List<CaseNode> cases = switchNode.getCases();
if(cases.isEmpty()) {
@@ -724,7 +875,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
for(final CaseNode caseNode: cases) {
final Expression test = caseNode.getTest();
if(!isInteger && test != null) {
- test.accept(this);
+ visitExpressionOnEmptyStack(test);
if(!tagUsed) {
symbolIsUsed(switchNode.getTag(), LvarType.OBJECT);
tagUsed = true;
@@ -769,29 +920,42 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
final Expression trueExpr = ternaryNode.getTrueExpression();
final Expression falseExpr = ternaryNode.getFalseExpression();
- test.accept(this);
+ visitExpression(test);
final Map<Symbol, LvarType> testExitLvarTypes = localVariableTypes;
+ final LvarType trueType;
if(!isAlwaysFalse(test)) {
- trueExpr.accept(this);
+ trueType = visitExpression(trueExpr);
+ } else {
+ trueType = null;
}
final Map<Symbol, LvarType> trueExitLvarTypes = localVariableTypes;
localVariableTypes = testExitLvarTypes;
+ final LvarType falseType;
if(!isAlwaysTrue(test)) {
- falseExpr.accept(this);
+ falseType = visitExpression(falseExpr);
+ } else {
+ falseType = null;
}
final Map<Symbol, LvarType> falseExitLvarTypes = localVariableTypes;
localVariableTypes = getUnionTypes(trueExitLvarTypes, falseExitLvarTypes);
setConversion((JoinPredecessor)trueExpr, trueExitLvarTypes, localVariableTypes);
setConversion((JoinPredecessor)falseExpr, falseExitLvarTypes, localVariableTypes);
+
+ typeStack.push(trueType != null ? falseType != null ? widestLvarType(trueType, falseType) : trueType : assertNotNull(falseType));
return false;
}
+ private static <T> T assertNotNull(final T t) {
+ assert t != null;
+ return t;
+ }
+
private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify,
final Expression iteratorValues, final boolean iteratorValuesAreObject) {
final JoinPredecessorExpression test = loopNode.getTest();
if(isAlwaysFalse(test)) {
- test.accept(this);
+ visitExpressionOnEmptyStack(test);
return;
}
@@ -804,7 +968,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
jumpToLabel(loopNode, repeatLabel, beforeLoopTypes);
final Map<Symbol, LvarType> beforeRepeatTypes = localVariableTypes;
if(test != null) {
- test.accept(this);
+ visitExpressionOnEmptyStack(test);
}
if(!isAlwaysTrue(test)) {
jumpToLabel(test, breakLabel);
@@ -827,7 +991,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
break;
}
if(modify != null) {
- modify.accept(this);
+ visitExpressionOnEmptyStack(modify);
jumpToLabel(modify, repeatLabel);
joinOnLabel(repeatLabel);
}
@@ -853,7 +1017,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
return false;
}
- throwNode.getExpression().accept(this);
+ visitExpressionOnEmptyStack(throwNode.getExpression());
jumpToCatchBlock(throwNode);
doesNotContinueSequentially();
return false;
@@ -886,13 +1050,24 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
doesNotContinueSequentially();
+ for (final Block inlinedFinally : tryNode.getInlinedFinallies()) {
+ final Block finallyBody = TryNode.getLabelledInlinedFinallyBlock(inlinedFinally);
+ joinOnLabel(finallyBody.getEntryLabel());
+ // NOTE: the jump to inlined finally can end up in dead code, so it is not necessarily reachable.
+ if (reachable) {
+ finallyBody.accept(this);
+ // All inlined finallies end with a jump or a return
+ assert !reachable;
+ }
+ }
+
joinOnLabel(catchLabel);
for(final CatchNode catchNode: tryNode.getCatches()) {
final IdentNode exception = catchNode.getException();
onAssignment(exception, LvarType.OBJECT);
final Expression condition = catchNode.getExceptionCondition();
if(condition != null) {
- condition.accept(this);
+ visitExpression(condition);
}
final Map<Symbol, LvarType> afterConditionTypes = localVariableTypes;
final Block catchBody = catchNode.getBody();
@@ -927,14 +1102,11 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
@Override
public boolean enterUnaryNode(final UnaryNode unaryNode) {
final Expression expr = unaryNode.getExpression();
- expr.accept(this);
-
- if(unaryNode.isSelfModifying()) {
- if(expr instanceof IdentNode) {
- final IdentNode ident = (IdentNode)expr;
- onSelfAssignment(ident, unaryNode, getLocalVariableTypeIfBytecode(ident.getSymbol()));
- }
+ final LvarType unaryType = toLvarType(unaryNode.setExpression(visitExpression(expr).typeExpression).getType());
+ if(unaryNode.isSelfModifying() && expr instanceof IdentNode) {
+ onSelfAssignment((IdentNode)expr, unaryType);
}
+ typeStack.push(unaryType);
return false;
}
@@ -945,8 +1117,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
final Expression init = varNode.getInit();
if(init != null) {
- init.accept(this);
- onAssignment(varNode.getName(), init);
+ onAssignment(varNode.getName(), visitExpression(init));
}
return false;
}
@@ -964,7 +1135,16 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
return false;
}
- private Map<Symbol, LvarType> getBreakTargetTypes(final BreakableNode target) {
+ @Override
+ public boolean enterWithNode(final WithNode withNode) {
+ if (reachable) {
+ visitExpression(withNode.getExpression());
+ withNode.getBody().accept(this);
+ }
+ return false;
+ };
+
+ private Map<Symbol, LvarType> getBreakTargetTypes(final LexicalContextNode target) {
// Remove symbols defined in the the blocks that are being broken out of.
Map<Symbol, LvarType> types = localVariableTypes;
for(final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
@@ -1002,18 +1182,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
/**
- * Gets the type for a local variable if it is a bytecode local, otherwise null. Can be used in circumstances where
- * the type is irrelevant if the symbol is not a bytecode local. Note that for bytecode locals, it delegates to
- * {@link #getLocalVariableType(Symbol)}, so it will still assert that the type for such variable is already
- * defined (that is, not null).
- * @param symbol the symbol representing the variable.
- * @return the current variable type, if it is a bytecode local, otherwise null.
- */
- private LvarType getLocalVariableTypeIfBytecode(final Symbol symbol) {
- return symbol.isBytecodeLocal() ? getLocalVariableType(symbol) : null;
- }
-
- /**
* Gets the type for a variable represented by a symbol, or null if the type is not know. This is the least strict
* of all local variable type getters, and as such its use is discouraged except in initialization scenarios (where
* a just-defined symbol might still be null).
@@ -1154,6 +1322,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
*/
private void leaveBreakable(final BreakableNode breakable) {
joinOnLabel(breakable.getBreakLabel());
+ assertTypeStackIsEmpty();
}
@Override
@@ -1161,7 +1330,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
// Sets the return type of the function and also performs the bottom-up pass of applying type and conversion
// information to nodes as well as doing the calculation on nested functions as required.
FunctionNode newFunction = functionNode;
- final NodeVisitor<LexicalContext> applyChangesVisitor = new NodeVisitor<LexicalContext>(new LexicalContext()) {
+ final SimpleNodeVisitor applyChangesVisitor = new SimpleNodeVisitor() {
private boolean inOuterFunction = true;
private final Deque<JoinPredecessor> joinPredecessors = new ArrayDeque<>();
@@ -1193,8 +1362,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
final Expression lhs = binaryNode.lhs();
final Expression rhs = binaryNode.rhs();
- Type cmpWidest = Type.widest(lhs.getType(), rhs.getType());
- boolean newRuntimeNode = false, finalized = false;
final TokenType tt = binaryNode.tokenType();
switch (tt) {
case EQ_STRICT:
@@ -1207,14 +1374,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
}
// Specialize comparison of boolean with non-boolean
if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
- newRuntimeNode = true;
- cmpWidest = Type.OBJECT;
- finalized = true;
+ return new RuntimeNode(binaryNode);
}
// fallthrough
default:
- if (newRuntimeNode || cmpWidest.isObject()) {
- return new RuntimeNode(binaryNode).setIsFinal(finalized);
+ if (lhs.getType().isObject() && rhs.getType().isObject()) {
+ return new RuntimeNode(binaryNode);
}
}
} else if(binaryNode.isOptimisticUndecidedType()) {
@@ -1230,7 +1395,11 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
if(node instanceof JoinPredecessor) {
final JoinPredecessor original = joinPredecessors.pop();
assert original.getClass() == node.getClass() : original.getClass().getName() + "!=" + node.getClass().getName();
- return (Node)setLocalVariableConversion(original, (JoinPredecessor)node);
+ final JoinPredecessor newNode = setLocalVariableConversion(original, (JoinPredecessor)node);
+ if (newNode instanceof LexicalContextNode) {
+ lc.replace((LexicalContextNode)node, (LexicalContextNode)newNode);
+ }
+ return (Node)newNode;
}
return node;
}
@@ -1308,7 +1477,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
newFunction = newFunction.setReturnType(lc, returnType);
- newFunction = newFunction.setState(lc, CompilationState.LOCAL_VARIABLE_TYPES_CALCULATED);
newFunction = newFunction.setParameters(lc, newFunction.visitParameters(applyChangesVisitor));
return newFunction;
}
@@ -1329,10 +1497,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
return conv == null || !conv.isLive();
}
- private void onAssignment(final IdentNode identNode, final Expression rhs) {
- onAssignment(identNode, toLvarType(getType(rhs)));
- }
-
private void onAssignment(final IdentNode identNode, final LvarType type) {
final Symbol symbol = identNode.getSymbol();
assert symbol != null : identNode.getName();
@@ -1400,13 +1564,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
jumpToCatchBlock(identNode);
}
- private void onSelfAssignment(final IdentNode identNode, final Expression assignment, final LvarType typeOnLoad) {
+ private void onSelfAssignment(final IdentNode identNode, final LvarType type) {
final Symbol symbol = identNode.getSymbol();
assert symbol != null : identNode.getName();
if(!symbol.isBytecodeLocal()) {
return;
}
- final LvarType type = toLvarType(getType(assignment, symbol, typeOnLoad.type));
// Self-assignment never produce either a boolean or undefined
assert type != null && type != LvarType.UNDEFINED && type != LvarType.BOOLEAN;
setType(symbol, type);
@@ -1466,7 +1629,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
* @param symbol the symbol representing the variable
* @param type the type
*/
- @SuppressWarnings("unused")
private void setType(final Symbol symbol, final LvarType type) {
if(getLocalVariableTypeOrNull(symbol) == type) {
return;
@@ -1486,77 +1648,4 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
private void symbolIsUsed(final Symbol symbol) {
symbolIsUsed(symbol, getLocalVariableType(symbol));
}
-
- /**
- * Gets the type of the expression, dependent on the current types of the local variables.
- *
- * @param expr the expression
- * @return the current type of the expression dependent on the current types of the local variables.
- */
- private Type getType(final Expression expr) {
- return expr.getType(getSymbolToType());
- }
-
- /**
- * Returns a function object from symbols to their types, used by the expressions to evaluate their type.
- * {@link BinaryNode} specifically uses identity of the function to cache type calculations. This method makes
- * sure to return the same function object while the local variable types don't change, and create a new function
- * object if the local variable types have been changed.
- * @return a function object representing a mapping from symbols to their types.
- */
- private Function<Symbol, Type> getSymbolToType() {
- if(symbolToType.isStale()) {
- symbolToType = new SymbolToType();
- }
- return symbolToType;
- }
-
- private class SymbolToType implements Function<Symbol, Type> {
- private final Object boundTypes = localVariableTypes;
- @Override
- public Type apply(final Symbol t) {
- return getLocalVariableType(t).type;
- }
-
- boolean isStale() {
- return boundTypes != localVariableTypes;
- }
- }
-
- /**
- * Gets the type of the expression, dependent on the current types of the local variables and a single overridden
- * symbol type. Used by type calculation on compound operators to ensure the type of the LHS at the time it was
- * loaded (which can potentially be different after RHS evaluation, e.g. "var x; x += x = 0;") is preserved for
- * the calculation.
- *
- * @param expr the expression
- * @param overriddenSymbol the overridden symbol
- * @param overriddenType the overridden type
- * @return the current type of the expression dependent on the current types of the local variables and the single
- * potentially overridden type.
- */
- private Type getType(final Expression expr, final Symbol overriddenSymbol, final Type overriddenType) {
- return expr.getType(getSymbolToType(overriddenSymbol, overriddenType));
- }
-
- private Function<Symbol, Type> getSymbolToType(final Symbol overriddenSymbol, final Type overriddenType) {
- return getLocalVariableType(overriddenSymbol).type == overriddenType ? getSymbolToType() :
- new SymbolToTypeOverride(overriddenSymbol, overriddenType);
- }
-
- private class SymbolToTypeOverride implements Function<Symbol, Type> {
- private final Function<Symbol, Type> originalSymbolToType = getSymbolToType();
- private final Symbol overriddenSymbol;
- private final Type overriddenType;
-
- SymbolToTypeOverride(final Symbol overriddenSymbol, final Type overriddenType) {
- this.overriddenSymbol = overriddenSymbol;
- this.overriddenType = overriddenType;
- }
-
- @Override
- public Type apply(final Symbol symbol) {
- return symbol == overriddenSymbol ? overriddenType : originalSymbolToType.apply(symbol);
- }
- }
}