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.LiteralNode; 46 import jdk.nashorn.internal.ir.LoopNode; 47 import jdk.nashorn.internal.ir.Node; 48 import jdk.nashorn.internal.ir.ObjectNode; 49 import jdk.nashorn.internal.ir.Optimistic; 50 import jdk.nashorn.internal.ir.PropertyNode; 51 import jdk.nashorn.internal.ir.Symbol; 52 import jdk.nashorn.internal.ir.TernaryNode; 53 import jdk.nashorn.internal.ir.UnaryNode; 54 import jdk.nashorn.internal.ir.VarNode; 55 import jdk.nashorn.internal.ir.WhileNode; 56 import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; 57 import jdk.nashorn.internal.parser.TokenType; 58 import jdk.nashorn.internal.runtime.ScriptObject; 59 60 /** 61 * Assigns optimistic types to expressions that can have them. This class mainly contains logic for which expressions 62 * must not ever be marked as optimistic, assigning narrowest non-invalidated types to program points from the 63 * compilation environment, as well as initializing optimistic types of global properties for scripts. 64 */ 65 final class OptimisticTypesCalculator extends SimpleNodeVisitor { 66 67 final Compiler compiler; 68 69 // Per-function bit set of program points that must never be optimistic. 70 final Deque<BitSet> neverOptimistic = new ArrayDeque<>(); 71 OptimisticTypesCalculator(final Compiler compiler)72 OptimisticTypesCalculator(final Compiler compiler) { 73 this.compiler = compiler; 74 } 75 76 @Override enterAccessNode(final AccessNode accessNode)77 public boolean enterAccessNode(final AccessNode accessNode) { 78 tagNeverOptimistic(accessNode.getBase()); 79 return true; 80 } 81 82 @Override enterPropertyNode(final PropertyNode propertyNode)83 public boolean enterPropertyNode(final PropertyNode propertyNode) { 84 if(ScriptObject.PROTO_PROPERTY_NAME.equals(propertyNode.getKeyName())) { 85 tagNeverOptimistic(propertyNode.getValue()); 86 } 87 return super.enterPropertyNode(propertyNode); 88 } 89 90 @Override enterBinaryNode(final BinaryNode binaryNode)91 public boolean enterBinaryNode(final BinaryNode binaryNode) { 92 if(binaryNode.isAssignment()) { 93 final Expression lhs = binaryNode.lhs(); 94 if(!binaryNode.isSelfModifying()) { 95 tagNeverOptimistic(lhs); 96 } 97 if(lhs instanceof IdentNode) { 98 final Symbol symbol = ((IdentNode)lhs).getSymbol(); 99 // Assignment to internal symbols is never optimistic, except for self-assignment expressions 100 if(symbol.isInternal() && !binaryNode.rhs().isSelfModifying()) { 101 tagNeverOptimistic(binaryNode.rhs()); 102 } 103 } 104 } else if(binaryNode.isTokenType(TokenType.INSTANCEOF) 105 || binaryNode.isTokenType(TokenType.EQ_STRICT) 106 || binaryNode.isTokenType(TokenType.NE_STRICT)) { 107 tagNeverOptimistic(binaryNode.lhs()); 108 tagNeverOptimistic(binaryNode.rhs()); 109 } 110 return true; 111 } 112 113 @Override enterCallNode(final CallNode callNode)114 public boolean enterCallNode(final CallNode callNode) { 115 tagNeverOptimistic(callNode.getFunction()); 116 return true; 117 } 118 119 @Override enterCatchNode(final CatchNode catchNode)120 public boolean enterCatchNode(final CatchNode catchNode) { 121 // Condition is never optimistic (always coerced to boolean). 122 tagNeverOptimistic(catchNode.getExceptionCondition()); 123 return true; 124 } 125 126 @Override enterExpressionStatement(final ExpressionStatement expressionStatement)127 public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) { 128 final Expression expr = expressionStatement.getExpression(); 129 if(!expr.isSelfModifying()) { 130 tagNeverOptimistic(expr); 131 } 132 return true; 133 } 134 135 @Override enterForNode(final ForNode forNode)136 public boolean enterForNode(final ForNode forNode) { 137 if(forNode.isForInOrOf()) { 138 // for..in has the iterable in its "modify" 139 tagNeverOptimistic(forNode.getModify()); 140 } else { 141 // Test is never optimistic (always coerced to boolean). 142 tagNeverOptimisticLoopTest(forNode); 143 } 144 return true; 145 } 146 147 @Override enterFunctionNode(final FunctionNode functionNode)148 public boolean enterFunctionNode(final FunctionNode functionNode) { 149 if (!neverOptimistic.isEmpty() && compiler.isOnDemandCompilation()) { 150 // This is a nested function, and we're doing on-demand compilation. In these compilations, we never descend 151 // into nested functions. 152 return false; 153 } 154 neverOptimistic.push(new BitSet()); 155 return true; 156 } 157 158 @Override enterIfNode(final IfNode ifNode)159 public boolean enterIfNode(final IfNode ifNode) { 160 // Test is never optimistic (always coerced to boolean). 161 tagNeverOptimistic(ifNode.getTest()); 162 return true; 163 } 164 165 @Override enterIndexNode(final IndexNode indexNode)166 public boolean enterIndexNode(final IndexNode indexNode) { 167 tagNeverOptimistic(indexNode.getBase()); 168 return true; 169 } 170 171 @Override enterTernaryNode(final TernaryNode ternaryNode)172 public boolean enterTernaryNode(final TernaryNode ternaryNode) { 173 // Test is never optimistic (always coerced to boolean). 174 tagNeverOptimistic(ternaryNode.getTest()); 175 return true; 176 } 177 178 @Override enterUnaryNode(final UnaryNode unaryNode)179 public boolean enterUnaryNode(final UnaryNode unaryNode) { 180 if(unaryNode.isTokenType(TokenType.NOT) || unaryNode.isTokenType(TokenType.NEW)) { 181 // Operand of boolean negation is never optimistic (always coerced to boolean). 182 // Operand of "new" is never optimistic (always coerced to Object). 183 tagNeverOptimistic(unaryNode.getExpression()); 184 } 185 return true; 186 } 187 188 @Override enterVarNode(final VarNode varNode)189 public boolean enterVarNode(final VarNode varNode) { 190 tagNeverOptimistic(varNode.getName()); 191 return true; 192 } 193 194 @Override enterObjectNode(ObjectNode objectNode)195 public boolean enterObjectNode(ObjectNode objectNode) { 196 if (objectNode.getSplitRanges() != null) { 197 return false; 198 } 199 return super.enterObjectNode(objectNode); 200 } 201 202 @Override enterLiteralNode(LiteralNode<?> literalNode)203 public boolean enterLiteralNode(LiteralNode<?> literalNode) { 204 if (literalNode.isArray() && ((LiteralNode.ArrayLiteralNode) literalNode).getSplitRanges() != null) { 205 return false; 206 } 207 208 return super.enterLiteralNode(literalNode); 209 } 210 211 @Override enterWhileNode(final WhileNode whileNode)212 public boolean enterWhileNode(final WhileNode whileNode) { 213 // Test is never optimistic (always coerced to boolean). 214 tagNeverOptimisticLoopTest(whileNode); 215 return true; 216 } 217 218 @Override leaveDefault(final Node node)219 protected Node leaveDefault(final Node node) { 220 if(node instanceof Optimistic) { 221 return leaveOptimistic((Optimistic)node); 222 } 223 return node; 224 } 225 226 @Override leaveFunctionNode(final FunctionNode functionNode)227 public Node leaveFunctionNode(final FunctionNode functionNode) { 228 neverOptimistic.pop(); 229 return functionNode; 230 } 231 232 @Override leaveIdentNode(final IdentNode identNode)233 public Node leaveIdentNode(final IdentNode identNode) { 234 final Symbol symbol = identNode.getSymbol(); 235 if(symbol == null) { 236 assert identNode.isPropertyName(); 237 return identNode; 238 } else if(symbol.isBytecodeLocal()) { 239 // Identifiers accessing bytecode local variables will never be optimistic, as type calculation phase over 240 // them will always assign them statically provable types. Note that access to function parameters can still 241 // be optimistic if the parameter needs to be in scope as it's used by a nested function. 242 return identNode; 243 } else if(symbol.isParam() && lc.getCurrentFunction().isVarArg()) { 244 // Parameters in vararg methods are not optimistic; we always access them using Object getters. 245 return identNode.setType(identNode.getMostPessimisticType()); 246 } else { 247 assert symbol.isScope(); 248 return leaveOptimistic(identNode); 249 } 250 } 251 leaveOptimistic(final Optimistic opt)252 private Expression leaveOptimistic(final Optimistic opt) { 253 final int pp = opt.getProgramPoint(); 254 if(isValid(pp) && !neverOptimistic.peek().get(pp)) { 255 return (Expression)opt.setType(compiler.getOptimisticType(opt)); 256 } 257 return (Expression)opt; 258 } 259 tagNeverOptimistic(final Expression expr)260 private void tagNeverOptimistic(final Expression expr) { 261 if(expr instanceof Optimistic) { 262 final int pp = ((Optimistic)expr).getProgramPoint(); 263 if(isValid(pp)) { 264 neverOptimistic.peek().set(pp); 265 } 266 } 267 } 268 tagNeverOptimisticLoopTest(final LoopNode loopNode)269 private void tagNeverOptimisticLoopTest(final LoopNode loopNode) { 270 final JoinPredecessorExpression test = loopNode.getTest(); 271 if(test != null) { 272 tagNeverOptimistic(test.getExpression()); 273 } 274 } 275 } 276