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