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 
  72     OptimisticTypesCalculator(final Compiler compiler) {
  73         this.compiler = compiler;
  74     }
  75 
  76     @Override
  77     public boolean enterAccessNode(final AccessNode accessNode) {
  78         tagNeverOptimistic(accessNode.getBase());
  79         return true;
  80     }
  81 
  82     @Override
  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
  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
 114     public boolean enterCallNode(final CallNode callNode) {
 115         tagNeverOptimistic(callNode.getFunction());
 116         return true;
 117     }
 118 
 119     @Override
 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
 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
 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
 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
 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
 166     public boolean enterIndexNode(final IndexNode indexNode) {
 167         tagNeverOptimistic(indexNode.getBase());
 168         return true;
 169     }
 170 
 171     @Override
 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
 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
 189     public boolean enterVarNode(final VarNode varNode) {
 190         tagNeverOptimistic(varNode.getName());
 191         return true;
 192     }
 193 
 194     @Override
 195     public boolean enterObjectNode(ObjectNode objectNode) {
 196         if (objectNode.getSplitRanges() != null) {
 197             return false;
 198         }
 199         return super.enterObjectNode(objectNode);
 200     }
 201 
 202     @Override
 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
 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
 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
 227     public Node leaveFunctionNode(final FunctionNode functionNode) {
 228         neverOptimistic.pop();
 229         return functionNode;
 230     }
 231 
 232     @Override
 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 
 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 
 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 
 269     private void tagNeverOptimisticLoopTest(final LoopNode loopNode) {
 270         final JoinPredecessorExpression test = loopNode.getTest();
 271         if(test != null) {
 272             tagNeverOptimistic(test.getExpression());
 273         }
 274     }
 275 }