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