blob: 275bf5c93021a6869ca98ed3bfeadc8b7cda650a [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import java.util.ArrayDeque;
import java.util.BitSet;
import java.util.Deque;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CatchNode;
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.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
import jdk.nashorn.internal.ir.LoopNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Optimistic;
import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
* Assigns optimistic types to expressions that can have them. This class mainly contains logic for which expressions
* must not ever be marked as optimistic, assigning narrowest non-invalidated types to program points from the
* compilation environment, as well as initializing optimistic types of global properties for scripts.
*/
final class OptimisticTypesCalculator extends SimpleNodeVisitor {
final Compiler compiler;
// Per-function bit set of program points that must never be optimistic.
final Deque<BitSet> neverOptimistic = new ArrayDeque<>();
OptimisticTypesCalculator(final Compiler compiler) {
this.compiler = compiler;
}
@Override
public boolean enterAccessNode(final AccessNode accessNode) {
tagNeverOptimistic(accessNode.getBase());
return true;
}
@Override
public boolean enterPropertyNode(final PropertyNode propertyNode) {
if(propertyNode.getKeyName().equals(ScriptObject.PROTO_PROPERTY_NAME)) {
tagNeverOptimistic(propertyNode.getValue());
}
return super.enterPropertyNode(propertyNode);
}
@Override
public boolean enterBinaryNode(final BinaryNode binaryNode) {
if(binaryNode.isAssignment()) {
final Expression lhs = binaryNode.lhs();
if(!binaryNode.isSelfModifying()) {
tagNeverOptimistic(lhs);
}
if(lhs instanceof IdentNode) {
final Symbol symbol = ((IdentNode)lhs).getSymbol();
// Assignment to internal symbols is never optimistic, except for self-assignment expressions
if(symbol.isInternal() && !binaryNode.rhs().isSelfModifying()) {
tagNeverOptimistic(binaryNode.rhs());
}
}
} else if(binaryNode.isTokenType(TokenType.INSTANCEOF)) {
tagNeverOptimistic(binaryNode.lhs());
tagNeverOptimistic(binaryNode.rhs());
}
return true;
}
@Override
public boolean enterCallNode(final CallNode callNode) {
tagNeverOptimistic(callNode.getFunction());
return true;
}
@Override
public boolean enterCatchNode(final CatchNode catchNode) {
// Condition is never optimistic (always coerced to boolean).
tagNeverOptimistic(catchNode.getExceptionCondition());
return true;
}
@Override
public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
final Expression expr = expressionStatement.getExpression();
if(!expr.isSelfModifying()) {
tagNeverOptimistic(expr);
}
return true;
}
@Override
public boolean enterForNode(final ForNode forNode) {
if(forNode.isForIn()) {
// for..in has the iterable in its "modify"
tagNeverOptimistic(forNode.getModify());
} else {
// Test is never optimistic (always coerced to boolean).
tagNeverOptimisticLoopTest(forNode);
}
return true;
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
if (!neverOptimistic.isEmpty() && compiler.isOnDemandCompilation()) {
// This is a nested function, and we're doing on-demand compilation. In these compilations, we never descend
// into nested functions.
return false;
}
neverOptimistic.push(new BitSet());
return true;
}
@Override
public boolean enterIfNode(final IfNode ifNode) {
// Test is never optimistic (always coerced to boolean).
tagNeverOptimistic(ifNode.getTest());
return true;
}
@Override
public boolean enterIndexNode(final IndexNode indexNode) {
tagNeverOptimistic(indexNode.getBase());
return true;
}
@Override
public boolean enterTernaryNode(final TernaryNode ternaryNode) {
// Test is never optimistic (always coerced to boolean).
tagNeverOptimistic(ternaryNode.getTest());
return true;
}
@Override
public boolean enterUnaryNode(final UnaryNode unaryNode) {
if(unaryNode.isTokenType(TokenType.NOT) || unaryNode.isTokenType(TokenType.NEW)) {
// Operand of boolean negation is never optimistic (always coerced to boolean).
// Operand of "new" is never optimistic (always coerced to Object).
tagNeverOptimistic(unaryNode.getExpression());
}
return true;
}
@Override
public boolean enterVarNode(final VarNode varNode) {
tagNeverOptimistic(varNode.getName());
return true;
}
@Override
public boolean enterWhileNode(final WhileNode whileNode) {
// Test is never optimistic (always coerced to boolean).
tagNeverOptimisticLoopTest(whileNode);
return true;
}
@Override
protected Node leaveDefault(final Node node) {
if(node instanceof Optimistic) {
return leaveOptimistic((Optimistic)node);
}
return node;
}
@Override
public Node leaveFunctionNode(final FunctionNode functionNode) {
neverOptimistic.pop();
return functionNode;
}
@Override
public Node leaveIdentNode(final IdentNode identNode) {
final Symbol symbol = identNode.getSymbol();
if(symbol == null) {
assert identNode.isPropertyName();
return identNode;
} else if(symbol.isBytecodeLocal()) {
// Identifiers accessing bytecode local variables will never be optimistic, as type calculation phase over
// them will always assign them statically provable types. Note that access to function parameters can still
// be optimistic if the parameter needs to be in scope as it's used by a nested function.
return identNode;
} else if(symbol.isParam() && lc.getCurrentFunction().isVarArg()) {
// Parameters in vararg methods are not optimistic; we always access them using Object getters.
return identNode.setType(identNode.getMostPessimisticType());
} else {
assert symbol.isScope();
return leaveOptimistic(identNode);
}
}
private Expression leaveOptimistic(final Optimistic opt) {
final int pp = opt.getProgramPoint();
if(isValid(pp) && !neverOptimistic.peek().get(pp)) {
return (Expression)opt.setType(compiler.getOptimisticType(opt));
}
return (Expression)opt;
}
private void tagNeverOptimistic(final Expression expr) {
if(expr instanceof Optimistic) {
final int pp = ((Optimistic)expr).getProgramPoint();
if(isValid(pp)) {
neverOptimistic.peek().set(pp);
}
}
}
private void tagNeverOptimisticLoopTest(final LoopNode loopNode) {
final JoinPredecessorExpression test = loopNode.getTest();
if(test != null) {
tagNeverOptimistic(test.getExpression());
}
}
}