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