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