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