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 
  70     OptimisticTypesCalculator(final Compiler compiler) {
  71         this.compiler = compiler;
  72     }
  73 
  74     @Override
  75     public boolean enterAccessNode(final AccessNode accessNode) {
  76         tagNeverOptimistic(accessNode.getBase());
  77         return true;
  78     }
  79 
  80     @Override
  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
  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
 110     public boolean enterCallNode(final CallNode callNode) {
 111         tagNeverOptimistic(callNode.getFunction());
 112         return true;
 113     }
 114 
 115     @Override
 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
 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
 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
 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
 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
 162     public boolean enterIndexNode(final IndexNode indexNode) {
 163         tagNeverOptimistic(indexNode.getBase());
 164         return true;
 165     }
 166 
 167     @Override
 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
 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
 185     public boolean enterVarNode(final VarNode varNode) {
 186         tagNeverOptimistic(varNode.getName());
 187         return true;
 188     }
 189 
 190     @Override
 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
 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
 206     public Node leaveFunctionNode(final FunctionNode functionNode) {
 207         neverOptimistic.pop();
 208         return functionNode;
 209     }
 210 
 211     @Override
 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 
 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 
 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 
 248     private void tagNeverOptimisticLoopTest(final LoopNode loopNode) {
 249         final JoinPredecessorExpression test = loopNode.getTest();
 250         if(test != null) {
 251             tagNeverOptimistic(test.getExpression());
 252         }
 253     }
 254 }