1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.codegen; 27 28 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 29 30 import java.util.ArrayDeque; 31 import java.util.BitSet; 32 import java.util.Deque; 33 import jdk.nashorn.internal.ir.AccessNode; 34 import jdk.nashorn.internal.ir.BinaryNode; 35 import jdk.nashorn.internal.ir.CallNode; 36 import jdk.nashorn.internal.ir.CatchNode; 37 import jdk.nashorn.internal.ir.Expression; 38 import jdk.nashorn.internal.ir.ExpressionStatement; 39 import jdk.nashorn.internal.ir.ForNode; 40 import jdk.nashorn.internal.ir.FunctionNode; 41 import jdk.nashorn.internal.ir.IdentNode; 42 import jdk.nashorn.internal.ir.IfNode; 43 import jdk.nashorn.internal.ir.IndexNode; 44 import jdk.nashorn.internal.ir.JoinPredecessorExpression; 45 import jdk.nashorn.internal.ir.LoopNode; 46 import jdk.nashorn.internal.ir.Node; 47 import jdk.nashorn.internal.ir.Optimistic; 48 import jdk.nashorn.internal.ir.PropertyNode; 49 import jdk.nashorn.internal.ir.Symbol; 50 import jdk.nashorn.internal.ir.TernaryNode; 51 import jdk.nashorn.internal.ir.UnaryNode; 52 import jdk.nashorn.internal.ir.VarNode; 53 import jdk.nashorn.internal.ir.WhileNode; 54 import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; 55 import jdk.nashorn.internal.parser.TokenType; 56 import jdk.nashorn.internal.runtime.ScriptObject; 57 58 /** 59 * Assigns optimistic types to expressions that can have them. This class mainly contains logic for which expressions 60 * must not ever be marked as optimistic, assigning narrowest non-invalidated types to program points from the 61 * compilation environment, as well as initializing optimistic types of global properties for scripts. 62 */ 63 final class OptimisticTypesCalculator extends SimpleNodeVisitor { 64 65 final Compiler compiler; 66 67 // Per-function bit set of program points that must never be optimistic. 68 final Deque<BitSet> neverOptimistic = new ArrayDeque<>(); 69 OptimisticTypesCalculator(final Compiler compiler)70 OptimisticTypesCalculator(final Compiler compiler) { 71 this.compiler = compiler; 72 } 73 74 @Override enterAccessNode(final AccessNode accessNode)75 public boolean enterAccessNode(final AccessNode accessNode) { 76 tagNeverOptimistic(accessNode.getBase()); 77 return true; 78 } 79 80 @Override enterPropertyNode(final PropertyNode propertyNode)81 public boolean enterPropertyNode(final PropertyNode propertyNode) { 82 if(propertyNode.getKeyName().equals(ScriptObject.PROTO_PROPERTY_NAME)) { 83 tagNeverOptimistic(propertyNode.getValue()); 84 } 85 return super.enterPropertyNode(propertyNode); 86 } 87 88 @Override enterBinaryNode(final BinaryNode binaryNode)89 public boolean enterBinaryNode(final BinaryNode binaryNode) { 90 if(binaryNode.isAssignment()) { 91 final Expression lhs = binaryNode.lhs(); 92 if(!binaryNode.isSelfModifying()) { 93 tagNeverOptimistic(lhs); 94 } 95 if(lhs instanceof IdentNode) { 96 final Symbol symbol = ((IdentNode)lhs).getSymbol(); 97 // Assignment to internal symbols is never optimistic, except for self-assignment expressions 98 if(symbol.isInternal() && !binaryNode.rhs().isSelfModifying()) { 99 tagNeverOptimistic(binaryNode.rhs()); 100 } 101 } 102 } else if(binaryNode.isTokenType(TokenType.INSTANCEOF)) { 103 tagNeverOptimistic(binaryNode.lhs()); 104 tagNeverOptimistic(binaryNode.rhs()); 105 } 106 return true; 107 } 108 109 @Override enterCallNode(final CallNode callNode)110 public boolean enterCallNode(final CallNode callNode) { 111 tagNeverOptimistic(callNode.getFunction()); 112 return true; 113 } 114 115 @Override enterCatchNode(final CatchNode catchNode)116 public boolean enterCatchNode(final CatchNode catchNode) { 117 // Condition is never optimistic (always coerced to boolean). 118 tagNeverOptimistic(catchNode.getExceptionCondition()); 119 return true; 120 } 121 122 @Override enterExpressionStatement(final ExpressionStatement expressionStatement)123 public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) { 124 final Expression expr = expressionStatement.getExpression(); 125 if(!expr.isSelfModifying()) { 126 tagNeverOptimistic(expr); 127 } 128 return true; 129 } 130 131 @Override enterForNode(final ForNode forNode)132 public boolean enterForNode(final ForNode forNode) { 133 if(forNode.isForIn()) { 134 // for..in has the iterable in its "modify" 135 tagNeverOptimistic(forNode.getModify()); 136 } else { 137 // Test is never optimistic (always coerced to boolean). 138 tagNeverOptimisticLoopTest(forNode); 139 } 140 return true; 141 } 142 143 @Override enterFunctionNode(final FunctionNode functionNode)144 public boolean enterFunctionNode(final FunctionNode functionNode) { 145 if (!neverOptimistic.isEmpty() && compiler.isOnDemandCompilation()) { 146 // This is a nested function, and we're doing on-demand compilation. In these compilations, we never descend 147 // into nested functions. 148 return false; 149 } 150 neverOptimistic.push(new BitSet()); 151 return true; 152 } 153 154 @Override enterIfNode(final IfNode ifNode)155 public boolean enterIfNode(final IfNode ifNode) { 156 // Test is never optimistic (always coerced to boolean). 157 tagNeverOptimistic(ifNode.getTest()); 158 return true; 159 } 160 161 @Override enterIndexNode(final IndexNode indexNode)162 public boolean enterIndexNode(final IndexNode indexNode) { 163 tagNeverOptimistic(indexNode.getBase()); 164 return true; 165 } 166 167 @Override enterTernaryNode(final TernaryNode ternaryNode)168 public boolean enterTernaryNode(final TernaryNode ternaryNode) { 169 // Test is never optimistic (always coerced to boolean). 170 tagNeverOptimistic(ternaryNode.getTest()); 171 return true; 172 } 173 174 @Override enterUnaryNode(final UnaryNode unaryNode)175 public boolean enterUnaryNode(final UnaryNode unaryNode) { 176 if(unaryNode.isTokenType(TokenType.NOT) || unaryNode.isTokenType(TokenType.NEW)) { 177 // Operand of boolean negation is never optimistic (always coerced to boolean). 178 // Operand of "new" is never optimistic (always coerced to Object). 179 tagNeverOptimistic(unaryNode.getExpression()); 180 } 181 return true; 182 } 183 184 @Override enterVarNode(final VarNode varNode)185 public boolean enterVarNode(final VarNode varNode) { 186 tagNeverOptimistic(varNode.getName()); 187 return true; 188 } 189 190 @Override enterWhileNode(final WhileNode whileNode)191 public boolean enterWhileNode(final WhileNode whileNode) { 192 // Test is never optimistic (always coerced to boolean). 193 tagNeverOptimisticLoopTest(whileNode); 194 return true; 195 } 196 197 @Override leaveDefault(final Node node)198 protected Node leaveDefault(final Node node) { 199 if(node instanceof Optimistic) { 200 return leaveOptimistic((Optimistic)node); 201 } 202 return node; 203 } 204 205 @Override leaveFunctionNode(final FunctionNode functionNode)206 public Node leaveFunctionNode(final FunctionNode functionNode) { 207 neverOptimistic.pop(); 208 return functionNode; 209 } 210 211 @Override leaveIdentNode(final IdentNode identNode)212 public Node leaveIdentNode(final IdentNode identNode) { 213 final Symbol symbol = identNode.getSymbol(); 214 if(symbol == null) { 215 assert identNode.isPropertyName(); 216 return identNode; 217 } else if(symbol.isBytecodeLocal()) { 218 // Identifiers accessing bytecode local variables will never be optimistic, as type calculation phase over 219 // them will always assign them statically provable types. Note that access to function parameters can still 220 // be optimistic if the parameter needs to be in scope as it's used by a nested function. 221 return identNode; 222 } else if(symbol.isParam() && lc.getCurrentFunction().isVarArg()) { 223 // Parameters in vararg methods are not optimistic; we always access them using Object getters. 224 return identNode.setType(identNode.getMostPessimisticType()); 225 } else { 226 assert symbol.isScope(); 227 return leaveOptimistic(identNode); 228 } 229 } 230 leaveOptimistic(final Optimistic opt)231 private Expression leaveOptimistic(final Optimistic opt) { 232 final int pp = opt.getProgramPoint(); 233 if(isValid(pp) && !neverOptimistic.peek().get(pp)) { 234 return (Expression)opt.setType(compiler.getOptimisticType(opt)); 235 } 236 return (Expression)opt; 237 } 238 tagNeverOptimistic(final Expression expr)239 private void tagNeverOptimistic(final Expression expr) { 240 if(expr instanceof Optimistic) { 241 final int pp = ((Optimistic)expr).getProgramPoint(); 242 if(isValid(pp)) { 243 neverOptimistic.peek().set(pp); 244 } 245 } 246 } 247 tagNeverOptimisticLoopTest(final LoopNode loopNode)248 private void tagNeverOptimisticLoopTest(final LoopNode loopNode) { 249 final JoinPredecessorExpression test = loopNode.getTest(); 250 if(test != null) { 251 tagNeverOptimistic(test.getExpression()); 252 } 253 } 254 } 255