author | attila |
Wed, 20 Dec 2017 17:36:50 +0100 | |
changeset 48354 | c96d4c720995 |
parent 47276 | bfa048898f11 |
permissions | -rw-r--r-- |
24751 | 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 |
||
25244
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
28 |
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN; |
24751 | 29 |
import static jdk.nashorn.internal.ir.Expression.isAlwaysFalse; |
30 |
import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue; |
|
27969 | 31 |
|
24751 | 32 |
import java.util.ArrayDeque; |
25244
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
33 |
import java.util.ArrayList; |
24751 | 34 |
import java.util.Collections; |
35 |
import java.util.Deque; |
|
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
36 |
import java.util.HashMap; |
24751 | 37 |
import java.util.HashSet; |
38 |
import java.util.IdentityHashMap; |
|
39 |
import java.util.Iterator; |
|
40 |
import java.util.LinkedList; |
|
41 |
import java.util.List; |
|
42 |
import java.util.Map; |
|
43 |
import java.util.Set; |
|
44 |
import jdk.nashorn.internal.codegen.types.Type; |
|
45 |
import jdk.nashorn.internal.ir.AccessNode; |
|
48354 | 46 |
import jdk.nashorn.internal.ir.BaseNode; |
24751 | 47 |
import jdk.nashorn.internal.ir.BinaryNode; |
48 |
import jdk.nashorn.internal.ir.Block; |
|
49 |
import jdk.nashorn.internal.ir.BreakNode; |
|
50 |
import jdk.nashorn.internal.ir.BreakableNode; |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
51 |
import jdk.nashorn.internal.ir.CallNode; |
24751 | 52 |
import jdk.nashorn.internal.ir.CaseNode; |
53 |
import jdk.nashorn.internal.ir.CatchNode; |
|
54 |
import jdk.nashorn.internal.ir.ContinueNode; |
|
55 |
import jdk.nashorn.internal.ir.Expression; |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
56 |
import jdk.nashorn.internal.ir.ExpressionStatement; |
24751 | 57 |
import jdk.nashorn.internal.ir.ForNode; |
58 |
import jdk.nashorn.internal.ir.FunctionNode; |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
59 |
import jdk.nashorn.internal.ir.GetSplitState; |
24751 | 60 |
import jdk.nashorn.internal.ir.IdentNode; |
61 |
import jdk.nashorn.internal.ir.IfNode; |
|
62 |
import jdk.nashorn.internal.ir.IndexNode; |
|
63 |
import jdk.nashorn.internal.ir.JoinPredecessor; |
|
64 |
import jdk.nashorn.internal.ir.JoinPredecessorExpression; |
|
65 |
import jdk.nashorn.internal.ir.JumpStatement; |
|
28690 | 66 |
import jdk.nashorn.internal.ir.JumpToInlinedFinally; |
24751 | 67 |
import jdk.nashorn.internal.ir.LabelNode; |
68 |
import jdk.nashorn.internal.ir.LexicalContext; |
|
69 |
import jdk.nashorn.internal.ir.LexicalContextNode; |
|
70 |
import jdk.nashorn.internal.ir.LiteralNode; |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
71 |
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; |
24751 | 72 |
import jdk.nashorn.internal.ir.LocalVariableConversion; |
73 |
import jdk.nashorn.internal.ir.LoopNode; |
|
74 |
import jdk.nashorn.internal.ir.Node; |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
75 |
import jdk.nashorn.internal.ir.ObjectNode; |
24751 | 76 |
import jdk.nashorn.internal.ir.PropertyNode; |
77 |
import jdk.nashorn.internal.ir.ReturnNode; |
|
78 |
import jdk.nashorn.internal.ir.RuntimeNode; |
|
79 |
import jdk.nashorn.internal.ir.RuntimeNode.Request; |
|
27206 | 80 |
import jdk.nashorn.internal.ir.SplitReturn; |
25244
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
81 |
import jdk.nashorn.internal.ir.Statement; |
24751 | 82 |
import jdk.nashorn.internal.ir.SwitchNode; |
83 |
import jdk.nashorn.internal.ir.Symbol; |
|
84 |
import jdk.nashorn.internal.ir.TernaryNode; |
|
85 |
import jdk.nashorn.internal.ir.ThrowNode; |
|
86 |
import jdk.nashorn.internal.ir.TryNode; |
|
87 |
import jdk.nashorn.internal.ir.UnaryNode; |
|
88 |
import jdk.nashorn.internal.ir.VarNode; |
|
89 |
import jdk.nashorn.internal.ir.WhileNode; |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
90 |
import jdk.nashorn.internal.ir.WithNode; |
24751 | 91 |
import jdk.nashorn.internal.ir.visitor.NodeVisitor; |
32888
24f99be3d5ab
8134502: introduce abstraction for basic NodeVisitor usage
attila
parents:
32692
diff
changeset
|
92 |
import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor; |
24751 | 93 |
import jdk.nashorn.internal.parser.TokenType; |
94 |
||
95 |
/** |
|
96 |
* Calculates types for local variables. For purposes of local variable type calculation, the only types used are |
|
97 |
* Undefined, boolean, int, long, double, and Object. The calculation eagerly widens types of local variable to their |
|
98 |
* widest at control flow join points. |
|
99 |
* TODO: investigate a more sophisticated solution that uses use/def information to only widens the type of a local |
|
100 |
* variable to its widest used type after the join point. That would eliminate some widenings of undefined variables to |
|
101 |
* object, most notably those used only in loops. We need a full liveness analysis for that. Currently, we can establish |
|
102 |
* per-type liveness, which eliminates most of unwanted dead widenings. |
|
27972
8ec664fdf8da
8066236: RuntimeNode forces copy creation on visitation
attila
parents:
27970
diff
changeset
|
103 |
* NOTE: the way this class is implemented, it actually processes the AST in two passes. The first pass is top-down and |
8ec664fdf8da
8066236: RuntimeNode forces copy creation on visitation
attila
parents:
27970
diff
changeset
|
104 |
* implemented in {@code enterXxx} methods. This pass does not mutate the AST (except for one occurrence, noted below), |
8ec664fdf8da
8066236: RuntimeNode forces copy creation on visitation
attila
parents:
27970
diff
changeset
|
105 |
* as being able to find relevant labels for control flow joins is sensitive to their reference identity, and mutated |
8ec664fdf8da
8066236: RuntimeNode forces copy creation on visitation
attila
parents:
27970
diff
changeset
|
106 |
* label-carrying nodes will create copies of their labels. A second bottom-up pass applying the changes is implemented |
8ec664fdf8da
8066236: RuntimeNode forces copy creation on visitation
attila
parents:
27970
diff
changeset
|
107 |
* in the separate visitor sitting in {@link #leaveFunctionNode(FunctionNode)}. This visitor will also instantiate new |
8ec664fdf8da
8066236: RuntimeNode forces copy creation on visitation
attila
parents:
27970
diff
changeset
|
108 |
* instances of the calculator to be run on nested functions (when not lazy compiling). |
8ec664fdf8da
8066236: RuntimeNode forces copy creation on visitation
attila
parents:
27970
diff
changeset
|
109 |
* |
24751 | 110 |
*/ |
32888
24f99be3d5ab
8134502: introduce abstraction for basic NodeVisitor usage
attila
parents:
32692
diff
changeset
|
111 |
final class LocalVariableTypesCalculator extends SimpleNodeVisitor { |
24751 | 112 |
|
113 |
private static class JumpOrigin { |
|
114 |
final JoinPredecessor node; |
|
115 |
final Map<Symbol, LvarType> types; |
|
116 |
||
117 |
JumpOrigin(final JoinPredecessor node, final Map<Symbol, LvarType> types) { |
|
118 |
this.node = node; |
|
119 |
this.types = types; |
|
120 |
} |
|
121 |
} |
|
122 |
||
123 |
private static class JumpTarget { |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
124 |
private final List<JumpOrigin> origins = new LinkedList<>(); |
24751 | 125 |
private Map<Symbol, LvarType> types = Collections.emptyMap(); |
126 |
||
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
127 |
void addOrigin(final JoinPredecessor originNode, final Map<Symbol, LvarType> originTypes, final LocalVariableTypesCalculator calc) { |
24751 | 128 |
origins.add(new JumpOrigin(originNode, originTypes)); |
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
129 |
this.types = calc.getUnionTypes(this.types, originTypes); |
24751 | 130 |
} |
131 |
} |
|
132 |
private enum LvarType { |
|
133 |
UNDEFINED(Type.UNDEFINED), |
|
134 |
BOOLEAN(Type.BOOLEAN), |
|
135 |
INT(Type.INT), |
|
136 |
DOUBLE(Type.NUMBER), |
|
137 |
OBJECT(Type.OBJECT); |
|
138 |
||
139 |
private final Type type; |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
140 |
private final TypeHolderExpression typeExpression; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
141 |
|
24751 | 142 |
private LvarType(final Type type) { |
143 |
this.type = type; |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
144 |
this.typeExpression = new TypeHolderExpression(type); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
145 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
146 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
147 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
148 |
/** |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
149 |
* A bogus Expression subclass that only reports its type. Used to interrogate BinaryNode and UnaryNode about their |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
150 |
* types by creating temporary copies of them and replacing their operands with instances of these. An alternative |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
151 |
* solution would be to add BinaryNode.getType(Type lhsType, Type rhsType) and UnaryNode.getType(Type exprType) |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
152 |
* methods. For the time being though, this is easier to implement and is in fact fairly clean. It does result in |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
153 |
* generation of higher number of temporary short lived nodes, though. |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
154 |
*/ |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
155 |
private static class TypeHolderExpression extends Expression { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
156 |
private static final long serialVersionUID = 1L; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
157 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
158 |
private final Type type; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
159 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
160 |
TypeHolderExpression(final Type type) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
161 |
super(0L, 0, 0); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
162 |
this.type = type; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
163 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
164 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
165 |
@Override |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
166 |
public Node accept(final NodeVisitor<? extends LexicalContext> visitor) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
167 |
throw new AssertionError(); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
168 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
169 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
170 |
@Override |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
171 |
public Type getType() { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
172 |
return type; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
173 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
174 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
175 |
@Override |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
176 |
public void toString(final StringBuilder sb, final boolean printType) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
177 |
throw new AssertionError(); |
24751 | 178 |
} |
179 |
} |
|
180 |
||
181 |
private static final Map<Type, LvarType> TO_LVAR_TYPE = new IdentityHashMap<>(); |
|
182 |
||
183 |
static { |
|
184 |
for(final LvarType lvarType: LvarType.values()) { |
|
185 |
TO_LVAR_TYPE.put(lvarType.type, lvarType); |
|
186 |
} |
|
187 |
} |
|
188 |
||
189 |
@SuppressWarnings("unchecked") |
|
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
190 |
private static HashMap<Symbol, LvarType> cloneMap(final Map<Symbol, LvarType> map) { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
191 |
return (HashMap<Symbol, LvarType>)((HashMap<?,?>)map).clone(); |
24751 | 192 |
} |
193 |
||
194 |
private LocalVariableConversion createConversion(final Symbol symbol, final LvarType branchLvarType, |
|
195 |
final Map<Symbol, LvarType> joinLvarTypes, final LocalVariableConversion next) { |
|
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
196 |
if (invalidatedSymbols.contains(symbol)) { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
197 |
return next; |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
198 |
} |
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
199 |
final LvarType targetType = joinLvarTypes.get(symbol); |
24751 | 200 |
assert targetType != null; |
201 |
if(targetType == branchLvarType) { |
|
202 |
return next; |
|
203 |
} |
|
204 |
// NOTE: we could naively just use symbolIsUsed(symbol, branchLvarType) here, but that'd be wrong. While |
|
205 |
// technically a conversion will read the value of the symbol with that type, but it will also write it to a new |
|
206 |
// type, and that type might be dead (we can't know yet). For this reason, we don't treat conversion reads as |
|
207 |
// real uses until we know their target type is live. If we didn't do this, and just did a symbolIsUsed here, |
|
208 |
// we'd introduce false live variables which could nevertheless turn into dead ones in a subsequent |
|
209 |
// deoptimization, causing a shift in the list of live locals that'd cause erroneous restoration of |
|
210 |
// continuations (since RewriteException's byteCodeSlots carries an array and not a name-value map). |
|
211 |
||
212 |
symbolIsConverted(symbol, branchLvarType, targetType); |
|
213 |
return new LocalVariableConversion(symbol, branchLvarType.type, targetType.type, next); |
|
214 |
} |
|
215 |
||
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
216 |
private Map<Symbol, LvarType> getUnionTypes(final Map<Symbol, LvarType> types1, final Map<Symbol, LvarType> types2) { |
24751 | 217 |
if(types1 == types2 || types1.isEmpty()) { |
218 |
return types2; |
|
219 |
} else if(types2.isEmpty()) { |
|
220 |
return types1; |
|
221 |
} |
|
222 |
final Set<Symbol> commonSymbols = new HashSet<>(types1.keySet()); |
|
223 |
commonSymbols.retainAll(types2.keySet()); |
|
224 |
// We have a chance of returning an unmodified set if both sets have the same keys and one is strictly wider |
|
225 |
// than the other. |
|
226 |
final int commonSize = commonSymbols.size(); |
|
227 |
final int types1Size = types1.size(); |
|
228 |
final int types2Size = types2.size(); |
|
229 |
if(commonSize == types1Size && commonSize == types2Size) { |
|
230 |
boolean matches1 = true, matches2 = true; |
|
231 |
Map<Symbol, LvarType> union = null; |
|
232 |
for(final Symbol symbol: commonSymbols) { |
|
233 |
final LvarType type1 = types1.get(symbol); |
|
234 |
final LvarType type2 = types2.get(symbol); |
|
29404
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
235 |
final LvarType widest = widestLvarType(type1, type2); |
24751 | 236 |
if(widest != type1 && matches1) { |
237 |
matches1 = false; |
|
238 |
if(!matches2) { |
|
239 |
union = cloneMap(types1); |
|
240 |
} |
|
241 |
} |
|
242 |
if (widest != type2 && matches2) { |
|
243 |
matches2 = false; |
|
244 |
if(!matches1) { |
|
245 |
union = cloneMap(types2); |
|
246 |
} |
|
247 |
} |
|
29404
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
248 |
if(!(matches1 || matches2)) { |
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
249 |
assert union != null; |
24751 | 250 |
union.put(symbol, widest); |
251 |
} |
|
252 |
} |
|
253 |
return matches1 ? types1 : matches2 ? types2 : union; |
|
254 |
} |
|
255 |
// General case |
|
256 |
final Map<Symbol, LvarType> union; |
|
257 |
if(types1Size > types2Size) { |
|
258 |
union = cloneMap(types1); |
|
259 |
union.putAll(types2); |
|
260 |
} else { |
|
261 |
union = cloneMap(types2); |
|
262 |
union.putAll(types1); |
|
263 |
} |
|
264 |
for(final Symbol symbol: commonSymbols) { |
|
265 |
final LvarType type1 = types1.get(symbol); |
|
266 |
final LvarType type2 = types2.get(symbol); |
|
267 |
union.put(symbol, widestLvarType(type1, type2)); |
|
268 |
} |
|
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
269 |
// If the two sets of symbols differ, there's a good chance that some of |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
270 |
// symbols only appearing in one of the sets are lexically invalidated, |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
271 |
// so we remove them from further consideration. |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
272 |
// This is not strictly necessary, just a working set size optimization. |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
273 |
union.keySet().removeAll(invalidatedSymbols); |
24751 | 274 |
return union; |
275 |
} |
|
276 |
||
277 |
private static void symbolIsUsed(final Symbol symbol, final LvarType type) { |
|
278 |
if(type != LvarType.UNDEFINED) { |
|
279 |
symbol.setHasSlotFor(type.type); |
|
280 |
} |
|
281 |
} |
|
282 |
||
283 |
private static class SymbolConversions { |
|
34732 | 284 |
private static final byte I2D = 1 << 0; |
285 |
private static final byte I2O = 1 << 1; |
|
286 |
private static final byte D2O = 1 << 2; |
|
24751 | 287 |
|
288 |
private byte conversions; |
|
289 |
||
290 |
void recordConversion(final LvarType from, final LvarType to) { |
|
27361 | 291 |
switch (from) { |
24751 | 292 |
case UNDEFINED: |
293 |
return; |
|
294 |
case INT: |
|
295 |
case BOOLEAN: |
|
27361 | 296 |
switch (to) { |
24751 | 297 |
case DOUBLE: |
298 |
recordConversion(I2D); |
|
299 |
return; |
|
300 |
case OBJECT: |
|
301 |
recordConversion(I2O); |
|
302 |
return; |
|
303 |
default: |
|
304 |
illegalConversion(from, to); |
|
305 |
return; |
|
306 |
} |
|
307 |
case DOUBLE: |
|
308 |
if(to == LvarType.OBJECT) { |
|
309 |
recordConversion(D2O); |
|
310 |
} |
|
311 |
return; |
|
312 |
default: |
|
313 |
illegalConversion(from, to); |
|
314 |
} |
|
315 |
} |
|
316 |
||
317 |
private static void illegalConversion(final LvarType from, final LvarType to) { |
|
318 |
throw new AssertionError("Invalid conversion from " + from + " to " + to); |
|
319 |
} |
|
320 |
||
321 |
void recordConversion(final byte convFlag) { |
|
322 |
conversions = (byte)(conversions | convFlag); |
|
323 |
} |
|
324 |
||
325 |
boolean hasConversion(final byte convFlag) { |
|
326 |
return (conversions & convFlag) != 0; |
|
327 |
} |
|
328 |
||
329 |
void calculateTypeLiveness(final Symbol symbol) { |
|
330 |
if(symbol.hasSlotFor(Type.OBJECT)) { |
|
331 |
if(hasConversion(D2O)) { |
|
332 |
symbol.setHasSlotFor(Type.NUMBER); |
|
333 |
} |
|
334 |
if(hasConversion(I2O)) { |
|
335 |
symbol.setHasSlotFor(Type.INT); |
|
336 |
} |
|
337 |
} |
|
338 |
if(symbol.hasSlotFor(Type.NUMBER)) { |
|
339 |
if(hasConversion(I2D)) { |
|
340 |
symbol.setHasSlotFor(Type.INT); |
|
341 |
} |
|
342 |
} |
|
343 |
} |
|
344 |
} |
|
345 |
||
346 |
private void symbolIsConverted(final Symbol symbol, final LvarType from, final LvarType to) { |
|
347 |
SymbolConversions conversions = symbolConversions.get(symbol); |
|
348 |
if(conversions == null) { |
|
349 |
conversions = new SymbolConversions(); |
|
350 |
symbolConversions.put(symbol, conversions); |
|
351 |
} |
|
352 |
conversions.recordConversion(from, to); |
|
353 |
} |
|
354 |
||
355 |
private static LvarType toLvarType(final Type type) { |
|
356 |
assert type != null; |
|
357 |
final LvarType lvarType = TO_LVAR_TYPE.get(type); |
|
358 |
if(lvarType != null) { |
|
359 |
return lvarType; |
|
360 |
} |
|
34732 | 361 |
assert type.isObject() : "Unsupported primitive type: " + type; |
24751 | 362 |
return LvarType.OBJECT; |
363 |
} |
|
364 |
private static LvarType widestLvarType(final LvarType t1, final LvarType t2) { |
|
365 |
if(t1 == t2) { |
|
366 |
return t1; |
|
367 |
} |
|
368 |
// Undefined or boolean to anything always widens to object. |
|
369 |
if(t1.ordinal() < LvarType.INT.ordinal() || t2.ordinal() < LvarType.INT.ordinal()) { |
|
370 |
return LvarType.OBJECT; |
|
371 |
} |
|
372 |
return LvarType.values()[Math.max(t1.ordinal(), t2.ordinal())]; |
|
373 |
} |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
374 |
private final Compiler compiler; |
24751 | 375 |
private final Map<Label, JumpTarget> jumpTargets = new IdentityHashMap<>(); |
376 |
// Local variable type mapping at the currently evaluated point. No map instance is ever modified; setLvarType() always |
|
377 |
// allocates a new map. Immutability of maps allows for cheap snapshots by just keeping the reference to the current |
|
378 |
// value. |
|
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
379 |
private Map<Symbol, LvarType> localVariableTypes = Collections.emptyMap(); |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
380 |
// Set of symbols whose lexical scope has already ended. |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
381 |
private final Set<Symbol> invalidatedSymbols = new HashSet<>(); |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
382 |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
383 |
// Stack for evaluated expression types. |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
384 |
private final Deque<LvarType> typeStack = new ArrayDeque<>(); |
24751 | 385 |
|
386 |
// Whether the current point in the AST is reachable code |
|
387 |
private boolean reachable = true; |
|
388 |
// Return type of the function |
|
389 |
private Type returnType = Type.UNKNOWN; |
|
25244
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
390 |
// Synthetic return node that we must insert at the end of the function if it's end is reachable. |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
391 |
private ReturnNode syntheticReturn; |
24751 | 392 |
|
393 |
private boolean alreadyEnteredTopLevelFunction; |
|
394 |
||
395 |
// LvarType and conversion information gathered during the top-down pass; applied to nodes in the bottom-up pass. |
|
396 |
private final Map<JoinPredecessor, LocalVariableConversion> localVariableConversions = new IdentityHashMap<>(); |
|
397 |
||
398 |
private final Map<IdentNode, LvarType> identifierLvarTypes = new IdentityHashMap<>(); |
|
399 |
private final Map<Symbol, SymbolConversions> symbolConversions = new IdentityHashMap<>(); |
|
400 |
||
401 |
// Stack of open labels for starts of catch blocks, one for every currently traversed try block; for inserting |
|
402 |
// control flow edges to them. Note that we currently don't insert actual control flow edges, but instead edges that |
|
403 |
// help us with type calculations. This means that some operations that can result in an exception being thrown |
|
404 |
// aren't considered (function calls, side effecting property getters and setters etc.), while some operations that |
|
405 |
// don't result in control flow transfers do originate an edge to the catch blocks (namely, assignments to local |
|
406 |
// variables). |
|
407 |
private final Deque<Label> catchLabels = new ArrayDeque<>(); |
|
408 |
||
47276
bfa048898f11
8187962: Optimistic types ignore JavaAdapter return types
hannesw
parents:
47216
diff
changeset
|
409 |
private LocalVariableTypesCalculator(final Compiler compiler) { |
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
410 |
this.compiler = compiler; |
24751 | 411 |
} |
412 |
||
47276
bfa048898f11
8187962: Optimistic types ignore JavaAdapter return types
hannesw
parents:
47216
diff
changeset
|
413 |
LocalVariableTypesCalculator(final Compiler compiler, final Type returnType) { |
bfa048898f11
8187962: Optimistic types ignore JavaAdapter return types
hannesw
parents:
47216
diff
changeset
|
414 |
this(compiler); |
bfa048898f11
8187962: Optimistic types ignore JavaAdapter return types
hannesw
parents:
47216
diff
changeset
|
415 |
this.returnType = returnType; |
bfa048898f11
8187962: Optimistic types ignore JavaAdapter return types
hannesw
parents:
47216
diff
changeset
|
416 |
} |
bfa048898f11
8187962: Optimistic types ignore JavaAdapter return types
hannesw
parents:
47216
diff
changeset
|
417 |
|
24751 | 418 |
private JumpTarget createJumpTarget(final Label label) { |
419 |
assert !jumpTargets.containsKey(label); |
|
420 |
final JumpTarget jumpTarget = new JumpTarget(); |
|
421 |
jumpTargets.put(label, jumpTarget); |
|
422 |
return jumpTarget; |
|
423 |
} |
|
424 |
||
425 |
private void doesNotContinueSequentially() { |
|
426 |
reachable = false; |
|
427 |
localVariableTypes = Collections.emptyMap(); |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
428 |
assertTypeStackIsEmpty(); |
24751 | 429 |
} |
430 |
||
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
431 |
private boolean pushExpressionType(final Expression expr) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
432 |
typeStack.push(toLvarType(expr.getType())); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
433 |
return false; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
434 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
435 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
436 |
@Override |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
437 |
public boolean enterAccessNode(final AccessNode accessNode) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
438 |
visitExpression(accessNode.getBase()); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
439 |
return pushExpressionType(accessNode); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
440 |
} |
24751 | 441 |
|
442 |
@Override |
|
443 |
public boolean enterBinaryNode(final BinaryNode binaryNode) { |
|
27969 | 444 |
// NOTE: regardless of operator's lexical associativity, lhs is always evaluated first. |
24751 | 445 |
final Expression lhs = binaryNode.lhs(); |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
446 |
final LvarType lhsType; |
30392
dc4a419b2982
8079362: Enforce best practices for Node token API usage
attila
parents:
29404
diff
changeset
|
447 |
if (!(lhs instanceof IdentNode && binaryNode.isTokenType(TokenType.ASSIGN))) { |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
448 |
lhsType = visitExpression(lhs); |
27969 | 449 |
} else { |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
450 |
// Can't visit IdentNode on LHS of a simple assignment, as visits imply use, and this is def. |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
451 |
// The type is irrelevant, as only RHS is used to determine the type anyway. |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
452 |
lhsType = LvarType.UNDEFINED; |
24751 | 453 |
} |
454 |
||
27969 | 455 |
final boolean isLogical = binaryNode.isLogical(); |
456 |
final Label joinLabel = isLogical ? new Label("") : null; |
|
457 |
if(isLogical) { |
|
458 |
jumpToLabel((JoinPredecessor)lhs, joinLabel); |
|
459 |
} |
|
460 |
||
461 |
final Expression rhs = binaryNode.rhs(); |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
462 |
final LvarType rhsType = visitExpression(rhs); |
27969 | 463 |
if(isLogical) { |
464 |
jumpToLabel((JoinPredecessor)rhs, joinLabel); |
|
465 |
} |
|
466 |
joinOnLabel(joinLabel); |
|
467 |
||
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
468 |
final LvarType type = toLvarType(binaryNode.setOperands(lhsType.typeExpression, rhsType.typeExpression).getType()); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
469 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
470 |
if(binaryNode.isAssignment() && lhs instanceof IdentNode) { |
24751 | 471 |
if(binaryNode.isSelfModifying()) { |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
472 |
onSelfAssignment((IdentNode)lhs, type); |
24751 | 473 |
} else { |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
474 |
onAssignment((IdentNode)lhs, type); |
24751 | 475 |
} |
476 |
} |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
477 |
typeStack.push(type); |
24751 | 478 |
return false; |
479 |
} |
|
480 |
||
481 |
@Override |
|
482 |
public boolean enterBlock(final Block block) { |
|
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
483 |
boolean cloned = false; |
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
484 |
for(final Symbol symbol: block.getSymbols()) { |
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
485 |
if(symbol.isBytecodeLocal()) { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
486 |
if (getLocalVariableTypeOrNull(symbol) == null) { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
487 |
if (!cloned) { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
488 |
cloneOrNewLocalVariableTypes(); |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
489 |
cloned = true; |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
490 |
} |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
491 |
localVariableTypes.put(symbol, LvarType.UNDEFINED); |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
492 |
} |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
493 |
// In case we're repeating analysis of a lexical scope (e.g. it's in a loop), |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
494 |
// make sure all symbols lexically scoped by the block become valid again. |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
495 |
invalidatedSymbols.remove(symbol); |
24751 | 496 |
} |
497 |
} |
|
498 |
return true; |
|
499 |
} |
|
500 |
||
501 |
@Override |
|
502 |
public boolean enterBreakNode(final BreakNode breakNode) { |
|
26889
dba314d7a634
8059371: Code duplication in handling of break and continue
attila
parents:
26766
diff
changeset
|
503 |
return enterJumpStatement(breakNode); |
24751 | 504 |
} |
505 |
||
506 |
@Override |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
507 |
public boolean enterCallNode(final CallNode callNode) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
508 |
visitExpression(callNode.getFunction()); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
509 |
visitExpressions(callNode.getArgs()); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
510 |
final CallNode.EvalArgs evalArgs = callNode.getEvalArgs(); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
511 |
if (evalArgs != null) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
512 |
visitExpressions(evalArgs.getArgs()); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
513 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
514 |
return pushExpressionType(callNode); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
515 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
516 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
517 |
@Override |
24751 | 518 |
public boolean enterContinueNode(final ContinueNode continueNode) { |
26889
dba314d7a634
8059371: Code duplication in handling of break and continue
attila
parents:
26766
diff
changeset
|
519 |
return enterJumpStatement(continueNode); |
dba314d7a634
8059371: Code duplication in handling of break and continue
attila
parents:
26766
diff
changeset
|
520 |
} |
dba314d7a634
8059371: Code duplication in handling of break and continue
attila
parents:
26766
diff
changeset
|
521 |
|
dba314d7a634
8059371: Code duplication in handling of break and continue
attila
parents:
26766
diff
changeset
|
522 |
private boolean enterJumpStatement(final JumpStatement jump) { |
24751 | 523 |
if(!reachable) { |
524 |
return false; |
|
525 |
} |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
526 |
assertTypeStackIsEmpty(); |
28690 | 527 |
jumpToLabel(jump, jump.getTargetLabel(lc), getBreakTargetTypes(jump.getPopScopeLimit(lc))); |
24751 | 528 |
doesNotContinueSequentially(); |
529 |
return false; |
|
530 |
} |
|
531 |
||
532 |
@Override |
|
533 |
protected boolean enterDefault(final Node node) { |
|
534 |
return reachable; |
|
535 |
} |
|
536 |
||
537 |
private void enterDoWhileLoop(final WhileNode loopNode) { |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
538 |
assertTypeStackIsEmpty(); |
24751 | 539 |
final JoinPredecessorExpression test = loopNode.getTest(); |
540 |
final Block body = loopNode.getBody(); |
|
541 |
final Label continueLabel = loopNode.getContinueLabel(); |
|
542 |
final Label breakLabel = loopNode.getBreakLabel(); |
|
543 |
final Map<Symbol, LvarType> beforeLoopTypes = localVariableTypes; |
|
544 |
final Label repeatLabel = new Label(""); |
|
545 |
for(;;) { |
|
546 |
jumpToLabel(loopNode, repeatLabel, beforeLoopTypes); |
|
547 |
final Map<Symbol, LvarType> beforeRepeatTypes = localVariableTypes; |
|
548 |
body.accept(this); |
|
549 |
if(reachable) { |
|
550 |
jumpToLabel(body, continueLabel); |
|
551 |
} |
|
552 |
joinOnLabel(continueLabel); |
|
553 |
if(!reachable) { |
|
554 |
break; |
|
555 |
} |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
556 |
visitExpressionOnEmptyStack(test); |
24751 | 557 |
jumpToLabel(test, breakLabel); |
558 |
if(isAlwaysFalse(test)) { |
|
559 |
break; |
|
560 |
} |
|
561 |
jumpToLabel(test, repeatLabel); |
|
562 |
joinOnLabel(repeatLabel); |
|
563 |
if(localVariableTypes.equals(beforeRepeatTypes)) { |
|
564 |
break; |
|
565 |
} |
|
566 |
resetJoinPoint(continueLabel); |
|
567 |
resetJoinPoint(breakLabel); |
|
568 |
resetJoinPoint(repeatLabel); |
|
569 |
} |
|
570 |
||
571 |
if(isAlwaysTrue(test)) { |
|
572 |
doesNotContinueSequentially(); |
|
573 |
} |
|
574 |
||
575 |
leaveBreakable(loopNode); |
|
576 |
} |
|
577 |
||
578 |
@Override |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
579 |
public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
580 |
if (reachable) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
581 |
visitExpressionOnEmptyStack(expressionStatement.getExpression()); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
582 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
583 |
return false; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
584 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
585 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
586 |
private void assertTypeStackIsEmpty() { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
587 |
assert typeStack.isEmpty(); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
588 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
589 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
590 |
@Override |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
591 |
protected Node leaveDefault(final Node node) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
592 |
assert !(node instanceof Expression); // All expressions were handled |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
593 |
assert !(node instanceof Statement) || typeStack.isEmpty(); // No statements leave with a non-empty stack |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
594 |
return node; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
595 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
596 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
597 |
private LvarType visitExpressionOnEmptyStack(final Expression expr) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
598 |
assertTypeStackIsEmpty(); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
599 |
return visitExpression(expr); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
600 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
601 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
602 |
private LvarType visitExpression(final Expression expr) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
603 |
final int stackSize = typeStack.size(); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
604 |
expr.accept(this); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
605 |
assert typeStack.size() == stackSize + 1; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
606 |
return typeStack.pop(); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
607 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
608 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
609 |
private void visitExpressions(final List<Expression> exprs) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
610 |
for(final Expression expr: exprs) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
611 |
if (expr != null) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
612 |
visitExpression(expr); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
613 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
614 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
615 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
616 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
617 |
@Override |
24751 | 618 |
public boolean enterForNode(final ForNode forNode) { |
619 |
if(!reachable) { |
|
620 |
return false; |
|
621 |
} |
|
622 |
||
623 |
final Expression init = forNode.getInit(); |
|
36696 | 624 |
if(forNode.isForInOrOf()) { |
26507
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
625 |
final JoinPredecessorExpression iterable = forNode.getModify(); |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
626 |
visitExpression(iterable); |
26507
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
627 |
enterTestFirstLoop(forNode, null, init, |
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
628 |
// If we're iterating over property names, and we can discern from the runtime environment |
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
629 |
// of the compilation that the object being iterated over must use strings for property |
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
630 |
// names (e.g., it is a native JS object or array), then we'll not bother trying to treat |
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
631 |
// the property names optimistically. |
26766 | 632 |
!compiler.useOptimisticTypes() || (!forNode.isForEach() && compiler.hasStringPropertyIterator(iterable.getExpression()))); |
24751 | 633 |
} else { |
634 |
if(init != null) { |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
635 |
visitExpressionOnEmptyStack(init); |
24751 | 636 |
} |
26507
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
637 |
enterTestFirstLoop(forNode, forNode.getModify(), null, false); |
24751 | 638 |
} |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
639 |
assertTypeStackIsEmpty(); |
24751 | 640 |
return false; |
641 |
} |
|
642 |
||
643 |
@Override |
|
644 |
public boolean enterFunctionNode(final FunctionNode functionNode) { |
|
645 |
if(alreadyEnteredTopLevelFunction) { |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
646 |
typeStack.push(LvarType.OBJECT); |
24751 | 647 |
return false; |
648 |
} |
|
649 |
int pos = 0; |
|
650 |
if(!functionNode.isVarArg()) { |
|
651 |
for (final IdentNode param : functionNode.getParameters()) { |
|
652 |
final Symbol symbol = param.getSymbol(); |
|
653 |
// Parameter is not necessarily bytecode local as it can be scoped due to nested context use, but it |
|
654 |
// must have a slot if we aren't in a function with vararg signature. |
|
655 |
assert symbol.hasSlot(); |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
656 |
final Type callSiteParamType = compiler.getParamType(functionNode, pos); |
24751 | 657 |
final LvarType paramType = callSiteParamType == null ? LvarType.OBJECT : toLvarType(callSiteParamType); |
658 |
setType(symbol, paramType); |
|
659 |
// Make sure parameter slot for its incoming value is not marked dead. NOTE: this is a heuristic. Right |
|
660 |
// now, CodeGenerator.expandParameters() relies on the fact that every parameter's final slot width will |
|
661 |
// be at least the same as incoming width, therefore even if a parameter is never read, we'll still keep |
|
662 |
// its slot. |
|
663 |
symbolIsUsed(symbol); |
|
664 |
setIdentifierLvarType(param, paramType); |
|
665 |
pos++; |
|
666 |
} |
|
667 |
} |
|
668 |
setCompilerConstantAsObject(functionNode, CompilerConstants.THIS); |
|
25238
28476bdc25ce
8047728: (function(x){var o={x:0}; with(o){delete x} return o.x})() evaluates to 0 instead of undefined
sundar
parents:
25234
diff
changeset
|
669 |
|
25240
f92c14b1ca11
8047959: bindings created for declarations in eval code are not mutable
sundar
parents:
25238
diff
changeset
|
670 |
// TODO: coarse-grained. If we wanted to solve it completely precisely, |
25238
28476bdc25ce
8047728: (function(x){var o={x:0}; with(o){delete x} return o.x})() evaluates to 0 instead of undefined
sundar
parents:
25234
diff
changeset
|
671 |
// we'd also need to push/pop its type when handling WithNode (so that |
28476bdc25ce
8047728: (function(x){var o={x:0}; with(o){delete x} return o.x})() evaluates to 0 instead of undefined
sundar
parents:
25234
diff
changeset
|
672 |
// it can go back to undefined after a 'with' block. |
28476bdc25ce
8047728: (function(x){var o={x:0}; with(o){delete x} return o.x})() evaluates to 0 instead of undefined
sundar
parents:
25234
diff
changeset
|
673 |
if(functionNode.hasScopeBlock() || functionNode.needsParentScope()) { |
24751 | 674 |
setCompilerConstantAsObject(functionNode, CompilerConstants.SCOPE); |
675 |
} |
|
676 |
if(functionNode.needsCallee()) { |
|
677 |
setCompilerConstantAsObject(functionNode, CompilerConstants.CALLEE); |
|
678 |
} |
|
679 |
if(functionNode.needsArguments()) { |
|
680 |
setCompilerConstantAsObject(functionNode, CompilerConstants.ARGUMENTS); |
|
681 |
} |
|
682 |
||
683 |
alreadyEnteredTopLevelFunction = true; |
|
684 |
return true; |
|
685 |
} |
|
686 |
||
687 |
@Override |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
688 |
public boolean enterGetSplitState(final GetSplitState getSplitState) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
689 |
return pushExpressionType(getSplitState); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
690 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
691 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
692 |
@Override |
24751 | 693 |
public boolean enterIdentNode(final IdentNode identNode) { |
694 |
final Symbol symbol = identNode.getSymbol(); |
|
695 |
if(symbol.isBytecodeLocal()) { |
|
696 |
symbolIsUsed(symbol); |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
697 |
final LvarType type = getLocalVariableType(symbol); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
698 |
setIdentifierLvarType(identNode, type); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
699 |
typeStack.push(type); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
700 |
} else { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
701 |
pushExpressionType(identNode); |
24751 | 702 |
} |
703 |
return false; |
|
704 |
} |
|
705 |
||
706 |
@Override |
|
707 |
public boolean enterIfNode(final IfNode ifNode) { |
|
29404
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
708 |
processIfNode(ifNode); |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
709 |
return false; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
710 |
} |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
711 |
|
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
712 |
private void processIfNode(final IfNode ifNode) { |
24751 | 713 |
if(!reachable) { |
29404
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
714 |
return; |
24751 | 715 |
} |
716 |
||
717 |
final Expression test = ifNode.getTest(); |
|
718 |
final Block pass = ifNode.getPass(); |
|
719 |
final Block fail = ifNode.getFail(); |
|
720 |
||
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
721 |
visitExpressionOnEmptyStack(test); |
24751 | 722 |
|
29404
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
723 |
final Map<Symbol, LvarType> passLvarTypes; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
724 |
final boolean reachableFromPass; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
725 |
final boolean isTestAlwaysTrue = isAlwaysTrue(test); |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
726 |
if(isAlwaysFalse(test)) { |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
727 |
passLvarTypes = null; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
728 |
reachableFromPass = false; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
729 |
} else { |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
730 |
final Map<Symbol, LvarType> afterTestLvarTypes = localVariableTypes; |
24751 | 731 |
pass.accept(this); |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
732 |
assertTypeStackIsEmpty(); |
29404
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
733 |
if (isTestAlwaysTrue) { |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
734 |
return; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
735 |
} |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
736 |
passLvarTypes = localVariableTypes; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
737 |
reachableFromPass = reachable; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
738 |
localVariableTypes = afterTestLvarTypes; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
739 |
reachable = true; |
24751 | 740 |
} |
741 |
||
29404
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
742 |
// If we get here, then we need to consider the case where pass block is not executed |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
743 |
assert !isTestAlwaysTrue; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
744 |
|
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
745 |
if (fail != null) { |
24751 | 746 |
fail.accept(this); |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
747 |
assertTypeStackIsEmpty(); |
24751 | 748 |
} |
749 |
||
29404
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
750 |
if(reachable) { |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
751 |
if(reachableFromPass) { |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
752 |
final Map<Symbol, LvarType> failLvarTypes = localVariableTypes; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
753 |
localVariableTypes = getUnionTypes(passLvarTypes, failLvarTypes); |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
754 |
setConversion(pass, passLvarTypes, localVariableTypes); |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
755 |
// IfNode itself is associated with conversions that might need to be performed after the test if |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
756 |
// there's no else branch. E.g. |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
757 |
// if(x = 1, cond) { x = 1.0 } must widen "x = 1" to a double. |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
758 |
setConversion(fail != null ? fail : ifNode, failLvarTypes, localVariableTypes); |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
759 |
} |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
760 |
} else if (reachableFromPass) { |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
761 |
assert passLvarTypes != null; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
762 |
localVariableTypes = passLvarTypes; |
d9023e6faff1
8074487: Static analysis of IfNode should consider terminating branches
attila
parents:
29283
diff
changeset
|
763 |
reachable = true; |
24751 | 764 |
} |
765 |
} |
|
766 |
||
767 |
@Override |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
768 |
public boolean enterIndexNode(final IndexNode indexNode) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
769 |
visitExpression(indexNode.getBase()); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
770 |
visitExpression(indexNode.getIndex()); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
771 |
return pushExpressionType(indexNode); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
772 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
773 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
774 |
@Override |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
775 |
public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinExpr) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
776 |
final Expression expr = joinExpr.getExpression(); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
777 |
if (expr != null) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
778 |
expr.accept(this); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
779 |
} else { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
780 |
typeStack.push(LvarType.UNDEFINED); |
24751 | 781 |
} |
782 |
return false; |
|
783 |
} |
|
784 |
||
785 |
@Override |
|
28690 | 786 |
public boolean enterJumpToInlinedFinally(final JumpToInlinedFinally jumpToInlinedFinally) { |
787 |
return enterJumpStatement(jumpToInlinedFinally); |
|
788 |
} |
|
789 |
||
790 |
@Override |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
791 |
public boolean enterLiteralNode(final LiteralNode<?> literalNode) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
792 |
if (literalNode instanceof ArrayLiteralNode) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
793 |
final List<Expression> expressions = ((ArrayLiteralNode)literalNode).getElementExpressions(); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
794 |
if (expressions != null) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
795 |
visitExpressions(expressions); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
796 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
797 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
798 |
pushExpressionType(literalNode); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
799 |
return false; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
800 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
801 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
802 |
@Override |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
803 |
public boolean enterObjectNode(final ObjectNode objectNode) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
804 |
for(final PropertyNode propertyNode: objectNode.getElements()) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
805 |
// Avoid falsely adding property keys to the control flow graph |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
806 |
final Expression value = propertyNode.getValue(); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
807 |
if (value != null) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
808 |
visitExpression(value); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
809 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
810 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
811 |
return pushExpressionType(objectNode); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
812 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
813 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
814 |
@Override |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
815 |
public boolean enterPropertyNode(final PropertyNode propertyNode) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
816 |
// Property nodes are only accessible through object literals, and we handled that case above |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
817 |
throw new AssertionError(); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
818 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
819 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
820 |
@Override |
24751 | 821 |
public boolean enterReturnNode(final ReturnNode returnNode) { |
26766 | 822 |
if(!reachable) { |
823 |
return false; |
|
824 |
} |
|
825 |
||
24751 | 826 |
final Expression returnExpr = returnNode.getExpression(); |
827 |
final Type returnExprType; |
|
828 |
if(returnExpr != null) { |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
829 |
returnExprType = visitExpressionOnEmptyStack(returnExpr).type; |
24751 | 830 |
} else { |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
831 |
assertTypeStackIsEmpty(); |
24751 | 832 |
returnExprType = Type.UNDEFINED; |
833 |
} |
|
834 |
returnType = Type.widestReturnType(returnType, returnExprType); |
|
835 |
doesNotContinueSequentially(); |
|
836 |
return false; |
|
837 |
} |
|
838 |
||
839 |
@Override |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
840 |
public boolean enterRuntimeNode(final RuntimeNode runtimeNode) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
841 |
visitExpressions(runtimeNode.getArgs()); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
842 |
return pushExpressionType(runtimeNode); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
843 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
844 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
845 |
@Override |
27206 | 846 |
public boolean enterSplitReturn(final SplitReturn splitReturn) { |
847 |
doesNotContinueSequentially(); |
|
848 |
return false; |
|
24751 | 849 |
} |
850 |
||
851 |
@Override |
|
852 |
public boolean enterSwitchNode(final SwitchNode switchNode) { |
|
853 |
if(!reachable) { |
|
854 |
return false; |
|
855 |
} |
|
856 |
||
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
857 |
visitExpressionOnEmptyStack(switchNode.getExpression()); |
24751 | 858 |
|
859 |
final List<CaseNode> cases = switchNode.getCases(); |
|
860 |
if(cases.isEmpty()) { |
|
861 |
return false; |
|
862 |
} |
|
863 |
||
864 |
// Control flow is different for all-integer cases where we dispatch by switch table, and for all other cases |
|
865 |
// where we do sequential comparison. Note that CaseNode objects act as join points. |
|
27970
7b0048b90967
8066225: NPE in MethodEmitter with duplicate integer switch cases
attila
parents:
27969
diff
changeset
|
866 |
final boolean isInteger = switchNode.isUniqueInteger(); |
24751 | 867 |
final Label breakLabel = switchNode.getBreakLabel(); |
868 |
final boolean hasDefault = switchNode.getDefaultCase() != null; |
|
869 |
||
870 |
boolean tagUsed = false; |
|
871 |
for(final CaseNode caseNode: cases) { |
|
872 |
final Expression test = caseNode.getTest(); |
|
873 |
if(!isInteger && test != null) { |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
874 |
visitExpressionOnEmptyStack(test); |
24751 | 875 |
if(!tagUsed) { |
876 |
symbolIsUsed(switchNode.getTag(), LvarType.OBJECT); |
|
877 |
tagUsed = true; |
|
878 |
} |
|
879 |
} |
|
880 |
// CaseNode carries the conversions that need to be performed on its entry from the test. |
|
881 |
// CodeGenerator ensures these are only emitted when arriving on the branch and not through a |
|
882 |
// fallthrough. |
|
883 |
jumpToLabel(caseNode, caseNode.getBody().getEntryLabel()); |
|
884 |
} |
|
885 |
if(!hasDefault) { |
|
886 |
// No default case means we can arrive at the break label without entering any cases. In that case |
|
887 |
// SwitchNode will carry the conversions that need to be performed before it does that jump. |
|
888 |
jumpToLabel(switchNode, breakLabel); |
|
889 |
} |
|
890 |
||
891 |
// All cases are arrived at through jumps |
|
892 |
doesNotContinueSequentially(); |
|
893 |
||
894 |
Block previousBlock = null; |
|
895 |
for(final CaseNode caseNode: cases) { |
|
896 |
final Block body = caseNode.getBody(); |
|
897 |
final Label entryLabel = body.getEntryLabel(); |
|
898 |
if(previousBlock != null && reachable) { |
|
899 |
jumpToLabel(previousBlock, entryLabel); |
|
900 |
} |
|
901 |
joinOnLabel(entryLabel); |
|
902 |
assert reachable == true; |
|
903 |
body.accept(this); |
|
904 |
previousBlock = body; |
|
905 |
} |
|
906 |
if(previousBlock != null && reachable) { |
|
907 |
jumpToLabel(previousBlock, breakLabel); |
|
908 |
} |
|
909 |
leaveBreakable(switchNode); |
|
910 |
return false; |
|
911 |
} |
|
912 |
||
913 |
@Override |
|
914 |
public boolean enterTernaryNode(final TernaryNode ternaryNode) { |
|
915 |
final Expression test = ternaryNode.getTest(); |
|
916 |
final Expression trueExpr = ternaryNode.getTrueExpression(); |
|
917 |
final Expression falseExpr = ternaryNode.getFalseExpression(); |
|
918 |
||
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
919 |
visitExpression(test); |
24751 | 920 |
|
921 |
final Map<Symbol, LvarType> testExitLvarTypes = localVariableTypes; |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
922 |
final LvarType trueType; |
24751 | 923 |
if(!isAlwaysFalse(test)) { |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
924 |
trueType = visitExpression(trueExpr); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
925 |
} else { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
926 |
trueType = null; |
24751 | 927 |
} |
928 |
final Map<Symbol, LvarType> trueExitLvarTypes = localVariableTypes; |
|
929 |
localVariableTypes = testExitLvarTypes; |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
930 |
final LvarType falseType; |
24751 | 931 |
if(!isAlwaysTrue(test)) { |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
932 |
falseType = visitExpression(falseExpr); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
933 |
} else { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
934 |
falseType = null; |
24751 | 935 |
} |
936 |
final Map<Symbol, LvarType> falseExitLvarTypes = localVariableTypes; |
|
937 |
localVariableTypes = getUnionTypes(trueExitLvarTypes, falseExitLvarTypes); |
|
938 |
setConversion((JoinPredecessor)trueExpr, trueExitLvarTypes, localVariableTypes); |
|
939 |
setConversion((JoinPredecessor)falseExpr, falseExitLvarTypes, localVariableTypes); |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
940 |
|
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
941 |
typeStack.push(trueType != null ? falseType != null ? widestLvarType(trueType, falseType) : trueType : assertNotNull(falseType)); |
24751 | 942 |
return false; |
943 |
} |
|
944 |
||
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
945 |
private static <T> T assertNotNull(final T t) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
946 |
assert t != null; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
947 |
return t; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
948 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
949 |
|
26507
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
950 |
private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify, |
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
951 |
final Expression iteratorValues, final boolean iteratorValuesAreObject) { |
24751 | 952 |
final JoinPredecessorExpression test = loopNode.getTest(); |
953 |
if(isAlwaysFalse(test)) { |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
954 |
visitExpressionOnEmptyStack(test); |
24751 | 955 |
return; |
956 |
} |
|
957 |
||
958 |
final Label continueLabel = loopNode.getContinueLabel(); |
|
959 |
final Label breakLabel = loopNode.getBreakLabel(); |
|
960 |
||
961 |
final Label repeatLabel = modify == null ? continueLabel : new Label(""); |
|
962 |
final Map<Symbol, LvarType> beforeLoopTypes = localVariableTypes; |
|
963 |
for(;;) { |
|
964 |
jumpToLabel(loopNode, repeatLabel, beforeLoopTypes); |
|
965 |
final Map<Symbol, LvarType> beforeRepeatTypes = localVariableTypes; |
|
966 |
if(test != null) { |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
967 |
visitExpressionOnEmptyStack(test); |
24751 | 968 |
} |
969 |
if(!isAlwaysTrue(test)) { |
|
970 |
jumpToLabel(test, breakLabel); |
|
971 |
} |
|
972 |
if(iteratorValues instanceof IdentNode) { |
|
26507
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
973 |
final IdentNode ident = (IdentNode)iteratorValues; |
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
974 |
// Receives iterator values; the optimistic type of the iterator values is tracked on the |
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
975 |
// identifier, but we override optimism if it's known that the object being iterated over will |
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
976 |
// never have primitive property names. |
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
977 |
onAssignment(ident, iteratorValuesAreObject ? LvarType.OBJECT : |
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
978 |
toLvarType(compiler.getOptimisticType(ident))); |
24751 | 979 |
} |
980 |
final Block body = loopNode.getBody(); |
|
981 |
body.accept(this); |
|
982 |
if(reachable) { |
|
983 |
jumpToLabel(body, continueLabel); |
|
984 |
} |
|
985 |
joinOnLabel(continueLabel); |
|
986 |
if(!reachable) { |
|
987 |
break; |
|
988 |
} |
|
989 |
if(modify != null) { |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
990 |
visitExpressionOnEmptyStack(modify); |
24751 | 991 |
jumpToLabel(modify, repeatLabel); |
992 |
joinOnLabel(repeatLabel); |
|
993 |
} |
|
994 |
if(localVariableTypes.equals(beforeRepeatTypes)) { |
|
995 |
break; |
|
996 |
} |
|
997 |
// Reset the join points and repeat the analysis |
|
998 |
resetJoinPoint(continueLabel); |
|
999 |
resetJoinPoint(breakLabel); |
|
1000 |
resetJoinPoint(repeatLabel); |
|
1001 |
} |
|
1002 |
||
1003 |
if(isAlwaysTrue(test) && iteratorValues == null) { |
|
1004 |
doesNotContinueSequentially(); |
|
1005 |
} |
|
1006 |
||
1007 |
leaveBreakable(loopNode); |
|
1008 |
} |
|
1009 |
||
1010 |
@Override |
|
1011 |
public boolean enterThrowNode(final ThrowNode throwNode) { |
|
25244
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1012 |
if(!reachable) { |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1013 |
return false; |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1014 |
} |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1015 |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1016 |
visitExpressionOnEmptyStack(throwNode.getExpression()); |
24751 | 1017 |
jumpToCatchBlock(throwNode); |
1018 |
doesNotContinueSequentially(); |
|
1019 |
return false; |
|
1020 |
} |
|
1021 |
||
1022 |
@Override |
|
1023 |
public boolean enterTryNode(final TryNode tryNode) { |
|
1024 |
if(!reachable) { |
|
1025 |
return false; |
|
1026 |
} |
|
1027 |
||
1028 |
// This is the label for the join point at the entry of the catch blocks. |
|
1029 |
final Label catchLabel = new Label(""); |
|
1030 |
catchLabels.push(catchLabel); |
|
1031 |
||
1032 |
// Presume that even the start of the try block can immediately go to the catch |
|
1033 |
jumpToLabel(tryNode, catchLabel); |
|
1034 |
||
1035 |
final Block body = tryNode.getBody(); |
|
1036 |
body.accept(this); |
|
1037 |
catchLabels.pop(); |
|
1038 |
||
1039 |
// Final exit label for the whole try/catch construct (after the try block and after all catches). |
|
1040 |
final Label endLabel = new Label(""); |
|
1041 |
||
1042 |
boolean canExit = false; |
|
1043 |
if(reachable) { |
|
1044 |
jumpToLabel(body, endLabel); |
|
1045 |
canExit = true; |
|
1046 |
} |
|
1047 |
doesNotContinueSequentially(); |
|
1048 |
||
28690 | 1049 |
for (final Block inlinedFinally : tryNode.getInlinedFinallies()) { |
1050 |
final Block finallyBody = TryNode.getLabelledInlinedFinallyBlock(inlinedFinally); |
|
1051 |
joinOnLabel(finallyBody.getEntryLabel()); |
|
1052 |
// NOTE: the jump to inlined finally can end up in dead code, so it is not necessarily reachable. |
|
1053 |
if (reachable) { |
|
1054 |
finallyBody.accept(this); |
|
1055 |
// All inlined finallies end with a jump or a return |
|
1056 |
assert !reachable; |
|
1057 |
} |
|
1058 |
} |
|
1059 |
||
24751 | 1060 |
joinOnLabel(catchLabel); |
1061 |
for(final CatchNode catchNode: tryNode.getCatches()) { |
|
41983
eb674141ab03
8156615: Catch parameter can be a BindingPattern in ES6 mode
sdama
parents:
41981
diff
changeset
|
1062 |
final IdentNode exception = catchNode.getExceptionIdentifier(); |
24751 | 1063 |
onAssignment(exception, LvarType.OBJECT); |
1064 |
final Expression condition = catchNode.getExceptionCondition(); |
|
1065 |
if(condition != null) { |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1066 |
visitExpression(condition); |
24751 | 1067 |
} |
1068 |
final Map<Symbol, LvarType> afterConditionTypes = localVariableTypes; |
|
1069 |
final Block catchBody = catchNode.getBody(); |
|
1070 |
// TODO: currently, we consider that the catch blocks are always reachable from the try block as currently |
|
1071 |
// we lack enough analysis to prove that no statement before a break/continue/return in the try block can |
|
1072 |
// throw an exception. |
|
1073 |
reachable = true; |
|
1074 |
catchBody.accept(this); |
|
1075 |
if(reachable) { |
|
1076 |
jumpToLabel(catchBody, endLabel); |
|
1077 |
canExit = true; |
|
1078 |
} |
|
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1079 |
localVariableTypes = afterConditionTypes; |
24751 | 1080 |
} |
1081 |
// NOTE: if we had one or more conditional catch blocks with no unconditional catch block following them, then |
|
1082 |
// there will be an unconditional rethrow, so the join point can never be reached from the last |
|
1083 |
// conditionExpression. |
|
1084 |
doesNotContinueSequentially(); |
|
1085 |
||
1086 |
if(canExit) { |
|
1087 |
joinOnLabel(endLabel); |
|
1088 |
} |
|
1089 |
||
1090 |
return false; |
|
1091 |
} |
|
1092 |
||
1093 |
||
1094 |
@Override |
|
1095 |
public boolean enterUnaryNode(final UnaryNode unaryNode) { |
|
1096 |
final Expression expr = unaryNode.getExpression(); |
|
48354 | 1097 |
final LvarType unaryType; |
1098 |
if (unaryNode.tokenType() == TokenType.DELETE && expr instanceof IdentNode) { |
|
1099 |
// not visiting deleted identifiers; they don't count as use |
|
1100 |
unaryType = toLvarType(unaryNode.getType()); |
|
1101 |
} else { |
|
1102 |
unaryType = toLvarType(unaryNode.setExpression(visitExpression(expr).typeExpression).getType()); |
|
1103 |
if (unaryNode.isSelfModifying() && expr instanceof IdentNode) { |
|
1104 |
onSelfAssignment((IdentNode) expr, unaryType); |
|
1105 |
} |
|
24751 | 1106 |
} |
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1107 |
typeStack.push(unaryType); |
24751 | 1108 |
return false; |
1109 |
} |
|
1110 |
||
1111 |
@Override |
|
1112 |
public boolean enterVarNode(final VarNode varNode) { |
|
26766 | 1113 |
if (!reachable) { |
1114 |
return false; |
|
1115 |
} |
|
24751 | 1116 |
final Expression init = varNode.getInit(); |
1117 |
if(init != null) { |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1118 |
onAssignment(varNode.getName(), visitExpression(init)); |
24751 | 1119 |
} |
1120 |
return false; |
|
1121 |
} |
|
1122 |
||
1123 |
@Override |
|
1124 |
public boolean enterWhileNode(final WhileNode whileNode) { |
|
1125 |
if(!reachable) { |
|
1126 |
return false; |
|
1127 |
} |
|
1128 |
if(whileNode.isDoWhile()) { |
|
1129 |
enterDoWhileLoop(whileNode); |
|
1130 |
} else { |
|
26507
9d6e3ec59878
8034954: Optimistic iteration in for-in and for-each
attila
parents:
25865
diff
changeset
|
1131 |
enterTestFirstLoop(whileNode, null, null, false); |
24751 | 1132 |
} |
1133 |
return false; |
|
1134 |
} |
|
1135 |
||
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1136 |
@Override |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1137 |
public boolean enterWithNode(final WithNode withNode) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1138 |
if (reachable) { |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1139 |
visitExpression(withNode.getExpression()); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1140 |
withNode.getBody().accept(this); |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1141 |
} |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1142 |
return false; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1143 |
}; |
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1144 |
|
28690 | 1145 |
private Map<Symbol, LvarType> getBreakTargetTypes(final LexicalContextNode target) { |
24751 | 1146 |
// Remove symbols defined in the the blocks that are being broken out of. |
1147 |
Map<Symbol, LvarType> types = localVariableTypes; |
|
1148 |
for(final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) { |
|
1149 |
final LexicalContextNode node = it.next(); |
|
1150 |
if(node instanceof Block) { |
|
1151 |
for(final Symbol symbol: ((Block)node).getSymbols()) { |
|
1152 |
if(localVariableTypes.containsKey(symbol)) { |
|
1153 |
if(types == localVariableTypes) { |
|
1154 |
types = cloneMap(localVariableTypes); |
|
1155 |
} |
|
1156 |
types.remove(symbol); |
|
1157 |
} |
|
1158 |
} |
|
1159 |
} |
|
1160 |
if(node == target) { |
|
1161 |
break; |
|
1162 |
} |
|
1163 |
} |
|
1164 |
return types; |
|
1165 |
} |
|
1166 |
||
27969 | 1167 |
/** |
1168 |
* Returns the current type of the local variable represented by the symbol. This is the most strict of all |
|
1169 |
* {@code getLocalVariableType*} methods, as it will throw an assertion if the type is null. Therefore, it is only |
|
1170 |
* safe to be invoked on symbols known to be bytecode locals, and only after they have been initialized. |
|
1171 |
* Regardless, it is recommended to use this method in majority of cases, as because of its strictness it is the |
|
1172 |
* best suited for catching missing type calculation bugs early. |
|
1173 |
* @param symbol a symbol representing a bytecode local variable. |
|
1174 |
* @return the current type of the local variable represented by the symbol |
|
1175 |
*/ |
|
24751 | 1176 |
private LvarType getLocalVariableType(final Symbol symbol) { |
1177 |
final LvarType type = getLocalVariableTypeOrNull(symbol); |
|
1178 |
assert type != null; |
|
1179 |
return type; |
|
1180 |
} |
|
1181 |
||
27969 | 1182 |
/** |
1183 |
* Gets the type for a variable represented by a symbol, or null if the type is not know. This is the least strict |
|
1184 |
* of all local variable type getters, and as such its use is discouraged except in initialization scenarios (where |
|
1185 |
* a just-defined symbol might still be null). |
|
1186 |
* @param symbol the symbol |
|
1187 |
* @return the current type for the symbol, or null if the type is not known either because the symbol has not been |
|
1188 |
* initialized, or because the symbol does not represent a bytecode local variable. |
|
1189 |
*/ |
|
24751 | 1190 |
private LvarType getLocalVariableTypeOrNull(final Symbol symbol) { |
1191 |
return localVariableTypes.get(symbol); |
|
1192 |
} |
|
1193 |
||
1194 |
private JumpTarget getOrCreateJumpTarget(final Label label) { |
|
1195 |
JumpTarget jumpTarget = jumpTargets.get(label); |
|
1196 |
if(jumpTarget == null) { |
|
1197 |
jumpTarget = createJumpTarget(label); |
|
1198 |
} |
|
1199 |
return jumpTarget; |
|
1200 |
} |
|
1201 |
||
1202 |
||
1203 |
/** |
|
1204 |
* If there's a join point associated with a label, insert the join point into the flow. |
|
1205 |
* @param label the label to insert a join point for. |
|
1206 |
*/ |
|
1207 |
private void joinOnLabel(final Label label) { |
|
1208 |
final JumpTarget jumpTarget = jumpTargets.remove(label); |
|
1209 |
if(jumpTarget == null) { |
|
1210 |
return; |
|
1211 |
} |
|
1212 |
assert !jumpTarget.origins.isEmpty(); |
|
1213 |
reachable = true; |
|
1214 |
localVariableTypes = getUnionTypes(jumpTarget.types, localVariableTypes); |
|
1215 |
for(final JumpOrigin jumpOrigin: jumpTarget.origins) { |
|
1216 |
setConversion(jumpOrigin.node, jumpOrigin.types, localVariableTypes); |
|
1217 |
} |
|
1218 |
} |
|
1219 |
||
1220 |
/** |
|
1221 |
* If we're in a try/catch block, add an edge from the specified node to the try node's pre-catch label. |
|
1222 |
*/ |
|
1223 |
private void jumpToCatchBlock(final JoinPredecessor jumpOrigin) { |
|
1224 |
final Label currentCatchLabel = catchLabels.peek(); |
|
1225 |
if(currentCatchLabel != null) { |
|
1226 |
jumpToLabel(jumpOrigin, currentCatchLabel); |
|
1227 |
} |
|
1228 |
} |
|
1229 |
||
1230 |
private void jumpToLabel(final JoinPredecessor jumpOrigin, final Label label) { |
|
1231 |
jumpToLabel(jumpOrigin, label, localVariableTypes); |
|
1232 |
} |
|
1233 |
||
1234 |
private void jumpToLabel(final JoinPredecessor jumpOrigin, final Label label, final Map<Symbol, LvarType> types) { |
|
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1235 |
getOrCreateJumpTarget(label).addOrigin(jumpOrigin, types, this); |
24751 | 1236 |
} |
1237 |
||
1238 |
@Override |
|
1239 |
public Node leaveBlock(final Block block) { |
|
1240 |
if(lc.isFunctionBody()) { |
|
25244
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1241 |
if(reachable) { |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1242 |
// reachable==true means we can reach the end of the function without an explicit return statement. We |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1243 |
// need to insert a synthetic one then. This logic used to be in Lower.leaveBlock(), but Lower's |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1244 |
// reachability analysis (through Terminal.isTerminal() flags) is not precise enough so |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1245 |
// Lower$BlockLexicalContext.afterSetStatements will sometimes think the control flow terminates even |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1246 |
// when it didn't. Example: function() { switch((z)) { default: {break; } throw x; } }. |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1247 |
createSyntheticReturn(block); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1248 |
assert !reachable; |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1249 |
} |
24751 | 1250 |
// We must calculate the return type here (and not in leaveFunctionNode) as it can affect the liveness of |
1251 |
// the :return symbol and thus affect conversion type liveness calculations for it. |
|
1252 |
calculateReturnType(); |
|
1253 |
} |
|
1254 |
||
1255 |
boolean cloned = false; |
|
1256 |
for(final Symbol symbol: block.getSymbols()) { |
|
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1257 |
if(symbol.hasSlot()) { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1258 |
// Invalidate the symbol when its defining block ends |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1259 |
if (symbol.isBytecodeLocal()) { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1260 |
if(localVariableTypes.containsKey(symbol)) { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1261 |
if(!cloned) { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1262 |
localVariableTypes = cloneMap(localVariableTypes); |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1263 |
cloned = true; |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1264 |
} |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1265 |
} |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1266 |
invalidateSymbol(symbol); |
24751 | 1267 |
} |
1268 |
||
1269 |
final SymbolConversions conversions = symbolConversions.get(symbol); |
|
1270 |
if(conversions != null) { |
|
1271 |
// Potentially make some currently dead types live if they're needed as a source of a type |
|
1272 |
// conversion at a join. |
|
1273 |
conversions.calculateTypeLiveness(symbol); |
|
1274 |
} |
|
1275 |
if(symbol.slotCount() == 0) { |
|
1276 |
// This is a local variable that is never read. It won't need a slot. |
|
1277 |
symbol.setNeedsSlot(false); |
|
1278 |
} |
|
1279 |
} |
|
1280 |
} |
|
1281 |
||
1282 |
if(reachable) { |
|
1283 |
// TODO: this is totally backwards. Block should not be breakable, LabelNode should be breakable. |
|
1284 |
final LabelNode labelNode = lc.getCurrentBlockLabelNode(); |
|
1285 |
if(labelNode != null) { |
|
1286 |
jumpToLabel(labelNode, block.getBreakLabel()); |
|
1287 |
} |
|
1288 |
} |
|
1289 |
leaveBreakable(block); |
|
1290 |
return block; |
|
1291 |
} |
|
1292 |
||
1293 |
private void calculateReturnType() { |
|
1294 |
// NOTE: if return type is unknown, then the function does not explicitly return a value. Such a function under |
|
1295 |
// ECMAScript rules returns Undefined, which has Type.OBJECT. We might consider an optimization in the future |
|
1296 |
// where we can return void functions. |
|
1297 |
if(returnType.isUnknown()) { |
|
1298 |
returnType = Type.OBJECT; |
|
1299 |
} |
|
1300 |
} |
|
25244
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1301 |
|
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1302 |
private void createSyntheticReturn(final Block body) { |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1303 |
final FunctionNode functionNode = lc.getCurrentFunction(); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1304 |
final long token = functionNode.getToken(); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1305 |
final int finish = functionNode.getFinish(); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1306 |
final List<Statement> statements = body.getStatements(); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1307 |
final int lineNumber = statements.isEmpty() ? functionNode.getLineNumber() : statements.get(statements.size() - 1).getLineNumber(); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1308 |
final IdentNode returnExpr; |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1309 |
if(functionNode.isProgram()) { |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1310 |
returnExpr = new IdentNode(token, finish, RETURN.symbolName()).setSymbol(getCompilerConstantSymbol(functionNode, RETURN)); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1311 |
} else { |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1312 |
returnExpr = null; |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1313 |
} |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1314 |
syntheticReturn = new ReturnNode(lineNumber, token, finish, returnExpr); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1315 |
syntheticReturn.accept(this); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1316 |
} |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1317 |
|
24751 | 1318 |
/** |
1319 |
* Leave a breakable node. If there's a join point associated with its break label (meaning there was at least one |
|
1320 |
* break statement to the end of the node), insert the join point into the flow. |
|
1321 |
* @param breakable the breakable node being left. |
|
1322 |
*/ |
|
1323 |
private void leaveBreakable(final BreakableNode breakable) { |
|
1324 |
joinOnLabel(breakable.getBreakLabel()); |
|
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1325 |
assertTypeStackIsEmpty(); |
24751 | 1326 |
} |
1327 |
||
1328 |
@Override |
|
1329 |
public Node leaveFunctionNode(final FunctionNode functionNode) { |
|
1330 |
// Sets the return type of the function and also performs the bottom-up pass of applying type and conversion |
|
1331 |
// information to nodes as well as doing the calculation on nested functions as required. |
|
1332 |
FunctionNode newFunction = functionNode; |
|
32888
24f99be3d5ab
8134502: introduce abstraction for basic NodeVisitor usage
attila
parents:
32692
diff
changeset
|
1333 |
final SimpleNodeVisitor applyChangesVisitor = new SimpleNodeVisitor() { |
24751 | 1334 |
private boolean inOuterFunction = true; |
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
1335 |
private final Deque<JoinPredecessor> joinPredecessors = new ArrayDeque<>(); |
24751 | 1336 |
|
1337 |
@Override |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
1338 |
protected boolean enterDefault(final Node node) { |
24751 | 1339 |
if(!inOuterFunction) { |
1340 |
return false; |
|
1341 |
} |
|
1342 |
if(node instanceof JoinPredecessor) { |
|
1343 |
joinPredecessors.push((JoinPredecessor)node); |
|
1344 |
} |
|
1345 |
return inOuterFunction; |
|
1346 |
} |
|
1347 |
||
1348 |
@Override |
|
1349 |
public boolean enterFunctionNode(final FunctionNode fn) { |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
1350 |
if(compiler.isOnDemandCompilation()) { |
24751 | 1351 |
// Only calculate nested function local variable types if we're doing eager compilation |
1352 |
return false; |
|
1353 |
} |
|
1354 |
inOuterFunction = false; |
|
1355 |
return true; |
|
1356 |
} |
|
1357 |
||
48354 | 1358 |
@Override |
1359 |
public boolean enterUnaryNode(final UnaryNode unaryNode) { |
|
1360 |
// not visiting deleted identifiers; they don't count as use |
|
1361 |
return !(unaryNode.tokenType() == TokenType.DELETE && unaryNode.getExpression() instanceof IdentNode); |
|
1362 |
} |
|
1363 |
||
24751 | 1364 |
@SuppressWarnings("fallthrough") |
1365 |
@Override |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
1366 |
public Node leaveBinaryNode(final BinaryNode binaryNode) { |
24751 | 1367 |
if(binaryNode.isComparison()) { |
1368 |
final Expression lhs = binaryNode.lhs(); |
|
1369 |
final Expression rhs = binaryNode.rhs(); |
|
1370 |
||
1371 |
final TokenType tt = binaryNode.tokenType(); |
|
1372 |
switch (tt) { |
|
1373 |
case EQ_STRICT: |
|
1374 |
case NE_STRICT: |
|
1375 |
// Specialize comparison with undefined |
|
1376 |
final Expression undefinedNode = createIsUndefined(binaryNode, lhs, rhs, |
|
1377 |
tt == TokenType.EQ_STRICT ? Request.IS_UNDEFINED : Request.IS_NOT_UNDEFINED); |
|
1378 |
if(undefinedNode != binaryNode) { |
|
1379 |
return undefinedNode; |
|
1380 |
} |
|
1381 |
// Specialize comparison of boolean with non-boolean |
|
1382 |
if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) { |
|
29283
fb47e4d25a9f
8035712: Restore some of the RuntimeCallSite specializations
attila
parents:
28690
diff
changeset
|
1383 |
return new RuntimeNode(binaryNode); |
24751 | 1384 |
} |
1385 |
// fallthrough |
|
1386 |
default: |
|
29283
fb47e4d25a9f
8035712: Restore some of the RuntimeCallSite specializations
attila
parents:
28690
diff
changeset
|
1387 |
if (lhs.getType().isObject() && rhs.getType().isObject()) { |
fb47e4d25a9f
8035712: Restore some of the RuntimeCallSite specializations
attila
parents:
28690
diff
changeset
|
1388 |
return new RuntimeNode(binaryNode); |
24751 | 1389 |
} |
1390 |
} |
|
1391 |
} else if(binaryNode.isOptimisticUndecidedType()) { |
|
1392 |
// At this point, we can assign a static type to the optimistic binary ADD operator as now we know |
|
1393 |
// the types of its operands. |
|
25829
1a5e1de71e57
8051439: Wrong type calculated for ADD operator with undefined operand
attila
parents:
25249
diff
changeset
|
1394 |
return binaryNode.decideType(); |
24751 | 1395 |
} |
1396 |
return binaryNode; |
|
1397 |
} |
|
1398 |
||
1399 |
@Override |
|
1400 |
protected Node leaveDefault(final Node node) { |
|
1401 |
if(node instanceof JoinPredecessor) { |
|
1402 |
final JoinPredecessor original = joinPredecessors.pop(); |
|
1403 |
assert original.getClass() == node.getClass() : original.getClass().getName() + "!=" + node.getClass().getName(); |
|
28690 | 1404 |
final JoinPredecessor newNode = setLocalVariableConversion(original, (JoinPredecessor)node); |
1405 |
if (newNode instanceof LexicalContextNode) { |
|
1406 |
lc.replace((LexicalContextNode)node, (LexicalContextNode)newNode); |
|
1407 |
} |
|
1408 |
return (Node)newNode; |
|
24751 | 1409 |
} |
1410 |
return node; |
|
1411 |
} |
|
1412 |
||
1413 |
@Override |
|
25244
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1414 |
public Node leaveBlock(final Block block) { |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1415 |
if(inOuterFunction && syntheticReturn != null && lc.isFunctionBody()) { |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1416 |
final ArrayList<Statement> stmts = new ArrayList<>(block.getStatements()); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1417 |
stmts.add((ReturnNode)syntheticReturn.accept(this)); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1418 |
return block.setStatements(lc, stmts); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1419 |
} |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1420 |
return super.leaveBlock(block); |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1421 |
} |
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1422 |
|
627d7e86f3b5
8047357: More precise synthetic return + unreachable throw
attila
parents:
25240
diff
changeset
|
1423 |
@Override |
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
1424 |
public Node leaveFunctionNode(final FunctionNode nestedFunctionNode) { |
24751 | 1425 |
inOuterFunction = true; |
1426 |
final FunctionNode newNestedFunction = (FunctionNode)nestedFunctionNode.accept( |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
1427 |
new LocalVariableTypesCalculator(compiler)); |
24751 | 1428 |
lc.replace(nestedFunctionNode, newNestedFunction); |
1429 |
return newNestedFunction; |
|
1430 |
} |
|
1431 |
||
1432 |
@Override |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
1433 |
public Node leaveIdentNode(final IdentNode identNode) { |
24751 | 1434 |
final IdentNode original = (IdentNode)joinPredecessors.pop(); |
1435 |
final Symbol symbol = identNode.getSymbol(); |
|
1436 |
if(symbol == null) { |
|
1437 |
assert identNode.isPropertyName(); |
|
1438 |
return identNode; |
|
1439 |
} else if(symbol.hasSlot()) { |
|
1440 |
assert !symbol.isScope() || symbol.isParam(); // Only params can be slotted and scoped. |
|
1441 |
assert original.getName().equals(identNode.getName()); |
|
1442 |
final LvarType lvarType = identifierLvarTypes.remove(original); |
|
1443 |
if(lvarType != null) { |
|
1444 |
return setLocalVariableConversion(original, identNode.setType(lvarType.type)); |
|
1445 |
} |
|
1446 |
// If there's no type, then the identifier must've been in unreachable code. In that case, it can't |
|
1447 |
// have assigned conversions either. |
|
1448 |
assert localVariableConversions.get(original) == null; |
|
1449 |
} else { |
|
1450 |
assert identIsDeadAndHasNoLiveConversions(original); |
|
1451 |
} |
|
1452 |
return identNode; |
|
1453 |
} |
|
1454 |
||
1455 |
@Override |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
1456 |
public Node leaveLiteralNode(final LiteralNode<?> literalNode) { |
25234
e2f9df6b8797
8047078: Fuzzing bug discovered when ArrayLiteralNodes weren't immutable
lagergren
parents:
24759
diff
changeset
|
1457 |
//for e.g. ArrayLiteralNodes the initial types may have been narrowed due to the |
e2f9df6b8797
8047078: Fuzzing bug discovered when ArrayLiteralNodes weren't immutable
lagergren
parents:
24759
diff
changeset
|
1458 |
//introduction of optimistic behavior - hence ensure that all literal nodes are |
e2f9df6b8797
8047078: Fuzzing bug discovered when ArrayLiteralNodes weren't immutable
lagergren
parents:
24759
diff
changeset
|
1459 |
//reinitialized |
e2f9df6b8797
8047078: Fuzzing bug discovered when ArrayLiteralNodes weren't immutable
lagergren
parents:
24759
diff
changeset
|
1460 |
return literalNode.initialize(lc); |
24751 | 1461 |
} |
1462 |
||
1463 |
@Override |
|
1464 |
public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { |
|
1465 |
final Request request = runtimeNode.getRequest(); |
|
1466 |
final boolean isEqStrict = request == Request.EQ_STRICT; |
|
1467 |
if(isEqStrict || request == Request.NE_STRICT) { |
|
1468 |
return createIsUndefined(runtimeNode, runtimeNode.getArgs().get(0), runtimeNode.getArgs().get(1), |
|
1469 |
isEqStrict ? Request.IS_UNDEFINED : Request.IS_NOT_UNDEFINED); |
|
1470 |
} |
|
1471 |
return runtimeNode; |
|
1472 |
} |
|
1473 |
||
1474 |
@SuppressWarnings("unchecked") |
|
1475 |
private <T extends JoinPredecessor> T setLocalVariableConversion(final JoinPredecessor original, final T jp) { |
|
1476 |
// NOTE: can't use Map.remove() as our copy-on-write AST semantics means some nodes appear twice (in |
|
1477 |
// finally blocks), so we need to be able to access conversions for them multiple times. |
|
1478 |
return (T)jp.setLocalVariableConversion(lc, localVariableConversions.get(original)); |
|
1479 |
} |
|
1480 |
}; |
|
1481 |
||
1482 |
newFunction = newFunction.setBody(lc, (Block)newFunction.getBody().accept(applyChangesVisitor)); |
|
1483 |
newFunction = newFunction.setReturnType(lc, returnType); |
|
1484 |
||
1485 |
||
1486 |
newFunction = newFunction.setParameters(lc, newFunction.visitParameters(applyChangesVisitor)); |
|
1487 |
return newFunction; |
|
1488 |
} |
|
1489 |
||
1490 |
private static Expression createIsUndefined(final Expression parent, final Expression lhs, final Expression rhs, final Request request) { |
|
1491 |
if (isUndefinedIdent(lhs) || isUndefinedIdent(rhs)) { |
|
1492 |
return new RuntimeNode(parent, request, lhs, rhs); |
|
1493 |
} |
|
1494 |
return parent; |
|
1495 |
} |
|
1496 |
||
1497 |
private static boolean isUndefinedIdent(final Expression expr) { |
|
1498 |
return expr instanceof IdentNode && "undefined".equals(((IdentNode)expr).getName()); |
|
1499 |
} |
|
1500 |
||
1501 |
private boolean identIsDeadAndHasNoLiveConversions(final IdentNode identNode) { |
|
1502 |
final LocalVariableConversion conv = localVariableConversions.get(identNode); |
|
1503 |
return conv == null || !conv.isLive(); |
|
1504 |
} |
|
1505 |
||
1506 |
private void onAssignment(final IdentNode identNode, final LvarType type) { |
|
1507 |
final Symbol symbol = identNode.getSymbol(); |
|
1508 |
assert symbol != null : identNode.getName(); |
|
1509 |
if(!symbol.isBytecodeLocal()) { |
|
1510 |
return; |
|
1511 |
} |
|
1512 |
assert type != null; |
|
1513 |
final LvarType finalType; |
|
1514 |
if(type == LvarType.UNDEFINED && getLocalVariableType(symbol) != LvarType.UNDEFINED) { |
|
1515 |
// Explicit assignment of a known undefined local variable to a local variable that is not undefined will |
|
1516 |
// materialize that undefined in the assignment target. Note that assigning known undefined to known |
|
1517 |
// undefined will *not* initialize the variable, e.g. "var x; var y = x;" compiles to no-op. |
|
1518 |
finalType = LvarType.OBJECT; |
|
1519 |
symbol.setFlag(Symbol.HAS_OBJECT_VALUE); |
|
1520 |
} else { |
|
1521 |
finalType = type; |
|
1522 |
} |
|
1523 |
setType(symbol, finalType); |
|
1524 |
// Explicit assignment of an undefined value. Make sure the variable can store an object |
|
1525 |
// TODO: if we communicated the fact to codegen with a flag on the IdentNode that the value was already |
|
1526 |
// undefined before the assignment, we could just ignore it. In general, we could ignore an assignment if we |
|
1527 |
// know that the value assigned is the same as the current value of the variable, but we'd need constant |
|
1528 |
// propagation for that. |
|
1529 |
setIdentifierLvarType(identNode, finalType); |
|
1530 |
// For purposes of type calculation, we consider an assignment to a local variable to be followed by |
|
1531 |
// the catch nodes of the current (if any) try block. This will effectively enforce that narrower |
|
1532 |
// assignments to a local variable in a try block will also have to store a widened value as well. Code |
|
1533 |
// within the try block will be able to keep loading the narrower value, but after the try block only |
|
1534 |
// the widest value will remain live. |
|
1535 |
// Rationale for this is that if there's an use for that variable in any of the catch blocks, or |
|
1536 |
// following the catch blocks, they must use the widest type. |
|
1537 |
// Example: |
|
1538 |
/* |
|
1539 |
Originally: |
|
1540 |
=========== |
|
1541 |
var x; |
|
1542 |
try { |
|
1543 |
x = 1; <-- stores into int slot for x |
|
1544 |
f(x); <-- loads the int slot for x |
|
1545 |
x = 3.14 <-- stores into the double slot for x |
|
1546 |
f(x); <-- loads the double slot for x |
|
1547 |
x = 1; <-- stores into int slot for x |
|
1548 |
f(x); <-- loads the int slot for x |
|
1549 |
} finally { |
|
1550 |
f(x); <-- loads the double slot for x, but can be reached by a path where x is int, so we need |
|
1551 |
to go back and ensure that double values are also always stored along with int |
|
1552 |
values. |
|
1553 |
} |
|
1554 |
||
1555 |
After correction: |
|
1556 |
================= |
|
1557 |
||
1558 |
var x; |
|
1559 |
try { |
|
1560 |
x = 1; <-- stores into both int and double slots for x |
|
1561 |
f(x); <-- loads the int slot for x |
|
1562 |
x = 3.14 <-- stores into the double slot for x |
|
1563 |
f(x); <-- loads the double slot for x |
|
1564 |
x = 1; <-- stores into both int and double slots for x |
|
1565 |
f(x); <-- loads the int slot for x |
|
1566 |
} finally { |
|
1567 |
f(x); <-- loads the double slot for x |
|
1568 |
} |
|
1569 |
*/ |
|
1570 |
jumpToCatchBlock(identNode); |
|
1571 |
} |
|
1572 |
||
28130
433d6755c5f8
8067774: Use a stack of types when calculating local variable types
attila
parents:
27972
diff
changeset
|
1573 |
private void onSelfAssignment(final IdentNode identNode, final LvarType type) { |
24751 | 1574 |
final Symbol symbol = identNode.getSymbol(); |
1575 |
assert symbol != null : identNode.getName(); |
|
1576 |
if(!symbol.isBytecodeLocal()) { |
|
1577 |
return; |
|
1578 |
} |
|
1579 |
// Self-assignment never produce either a boolean or undefined |
|
1580 |
assert type != null && type != LvarType.UNDEFINED && type != LvarType.BOOLEAN; |
|
1581 |
setType(symbol, type); |
|
1582 |
jumpToCatchBlock(identNode); |
|
1583 |
} |
|
1584 |
||
1585 |
private void resetJoinPoint(final Label label) { |
|
1586 |
jumpTargets.remove(label); |
|
1587 |
} |
|
1588 |
||
1589 |
private void setCompilerConstantAsObject(final FunctionNode functionNode, final CompilerConstants cc) { |
|
1590 |
final Symbol symbol = getCompilerConstantSymbol(functionNode, cc); |
|
1591 |
setType(symbol, LvarType.OBJECT); |
|
1592 |
// never mark compiler constants as dead |
|
1593 |
symbolIsUsed(symbol); |
|
1594 |
} |
|
1595 |
||
1596 |
private static Symbol getCompilerConstantSymbol(final FunctionNode functionNode, final CompilerConstants cc) { |
|
1597 |
return functionNode.getBody().getExistingSymbol(cc.symbolName()); |
|
1598 |
} |
|
1599 |
||
1600 |
private void setConversion(final JoinPredecessor node, final Map<Symbol, LvarType> branchLvarTypes, final Map<Symbol, LvarType> joinLvarTypes) { |
|
1601 |
if(node == null) { |
|
1602 |
return; |
|
1603 |
} |
|
1604 |
if(branchLvarTypes.isEmpty() || joinLvarTypes.isEmpty()) { |
|
1605 |
localVariableConversions.remove(node); |
|
1606 |
} |
|
1607 |
||
1608 |
LocalVariableConversion conversion = null; |
|
1609 |
if(node instanceof IdentNode) { |
|
1610 |
// conversions on variable assignment in try block are special cases, as they only apply to the variable |
|
1611 |
// being assigned and all other conversions should be ignored. |
|
1612 |
final Symbol symbol = ((IdentNode)node).getSymbol(); |
|
1613 |
conversion = createConversion(symbol, branchLvarTypes.get(symbol), joinLvarTypes, null); |
|
1614 |
} else { |
|
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
1615 |
for(final Map.Entry<Symbol, LvarType> entry: branchLvarTypes.entrySet()) { |
24751 | 1616 |
final Symbol symbol = entry.getKey(); |
24759
31aed7d9c02a
8034206: Make parts of code pipeline reusable in order to facilitate faster warmup and faster lazy compilation.
lagergren
parents:
24751
diff
changeset
|
1617 |
final LvarType branchLvarType = entry.getValue(); |
24751 | 1618 |
conversion = createConversion(symbol, branchLvarType, joinLvarTypes, conversion); |
1619 |
} |
|
1620 |
} |
|
1621 |
if(conversion != null) { |
|
1622 |
localVariableConversions.put(node, conversion); |
|
1623 |
} else { |
|
1624 |
localVariableConversions.remove(node); |
|
1625 |
} |
|
1626 |
} |
|
1627 |
||
1628 |
private void setIdentifierLvarType(final IdentNode identNode, final LvarType type) { |
|
1629 |
assert type != null; |
|
1630 |
identifierLvarTypes.put(identNode, type); |
|
1631 |
} |
|
1632 |
||
1633 |
/** |
|
1634 |
* Marks a local variable as having a specific type from this point onward. Invoked by stores to local variables. |
|
1635 |
* @param symbol the symbol representing the variable |
|
1636 |
* @param type the type |
|
1637 |
*/ |
|
1638 |
private void setType(final Symbol symbol, final LvarType type) { |
|
1639 |
if(getLocalVariableTypeOrNull(symbol) == type) { |
|
1640 |
return; |
|
1641 |
} |
|
1642 |
assert symbol.hasSlot(); |
|
1643 |
assert !symbol.isGlobal(); |
|
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1644 |
cloneOrNewLocalVariableTypes(); |
24751 | 1645 |
localVariableTypes.put(symbol, type); |
1646 |
} |
|
1647 |
||
41981
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1648 |
private void cloneOrNewLocalVariableTypes() { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1649 |
localVariableTypes = localVariableTypes.isEmpty() ? new HashMap<Symbol, LvarType>() : cloneMap(localVariableTypes); |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1650 |
} |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1651 |
|
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1652 |
private void invalidateSymbol(final Symbol symbol) { |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1653 |
localVariableTypes.remove(symbol); |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1654 |
invalidatedSymbols.add(symbol); |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1655 |
} |
10375379aeac
8168373: don't emit conversions for symbols outside their lexical scope
attila
parents:
36696
diff
changeset
|
1656 |
|
24751 | 1657 |
/** |
1658 |
* Set a flag in the symbol marking it as needing to be able to store a value of a particular type. Every symbol for |
|
1659 |
* a local variable will be assigned between 1 and 6 local variable slots for storing all types it is known to need |
|
1660 |
* to store. |
|
1661 |
* @param symbol the symbol |
|
1662 |
*/ |
|
1663 |
private void symbolIsUsed(final Symbol symbol) { |
|
1664 |
symbolIsUsed(symbol, getLocalVariableType(symbol)); |
|
1665 |
} |
|
1666 |
} |