OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 class ClosureFieldElement extends Element { | 5 class ClosureFieldElement extends Element { |
6 ClosureFieldElement(SourceString name, ClassElement enclosing) | 6 ClosureFieldElement(SourceString name, ClassElement enclosing) |
7 : super(name, ElementKind.FIELD, enclosing); | 7 : super(name, ElementKind.FIELD, enclosing); |
8 | 8 |
9 bool isInstanceMember() => true; | 9 bool isInstanceMember() => true; |
10 bool isAssignable() => false; | 10 bool isAssignable() => false; |
11 | 11 |
12 String toString() => "ClosureFieldElement($name)"; | 12 String toString() => "ClosureFieldElement($name)"; |
13 } | 13 } |
14 | 14 |
15 // The box-element for a scope, and the captured variables that need to be | 15 // The box-element for a scope, and the captured variables that need to be |
16 // stored in the box. | 16 // stored in the box. |
17 class ClosureScope { | 17 class ClosureScope { |
18 Element boxElement; | 18 Element boxElement; |
19 Map<Element, Element> capturedVariableMapping; | 19 Map<Element, Element> capturedVariableMapping; |
20 | 20 |
21 ClosureScope(this.boxElement, this.capturedVariableMapping); | 21 ClosureScope(this.boxElement, this.capturedVariableMapping); |
22 } | 22 } |
23 | 23 |
24 class ClosureData { | 24 class ClosureData { |
25 // The globalizedClosureElement will be null for methods that are not local | 25 // The globalizedClosureElement will be null for methods that are not local |
26 // closures. | 26 // closures. |
27 final ClassElement globalizedClosureElement; | 27 final ClassElement globalizedClosureElement; |
28 // The callElement will be null for methods that are not local closures. | 28 // The callElement will be null for methods that are not local closures. |
29 final FunctionElement callElement; | 29 final FunctionElement callElement; |
30 // The [thisElement] makes handling 'this' easier by treating it like any | |
31 // other argument. It is only set for instance-members. | |
32 final Element thisElement; | |
30 | 33 |
31 // Maps free locals, arguments and function elements to their captured | 34 // Maps free locals, arguments and function elements to their captured |
32 // copies. | 35 // copies. |
33 final Map<Element, Element> freeVariableMapping; | 36 final Map<Element, Element> freeVariableMapping; |
34 // Maps closure-fields to their captured elements. This is somehow the inverse | 37 // Maps closure-fields to their captured elements. This is somehow the inverse |
35 // mapping of [freeVariableMapping], but whereas [freeVariableMapping] does | 38 // mapping of [freeVariableMapping], but whereas [freeVariableMapping] does |
36 // not deal with boxes, here we map instance-fields (which might represent | 39 // not deal with boxes, here we map instance-fields (which might represent |
37 // boxes) to their boxElement. | 40 // boxes) to their boxElement. |
38 final Map<Element, Element> capturedFieldMapping; | 41 final Map<Element, Element> capturedFieldMapping; |
39 | 42 |
40 // Maps scopes ([Loop] and [FunctionExpression] nodes) to their | 43 // Maps scopes ([Loop] and [FunctionExpression] nodes) to their |
41 // [ClosureScope] which contains their box and the | 44 // [ClosureScope] which contains their box and the |
42 // captured variables that are stored in the box. | 45 // captured variables that are stored in the box. |
43 // This map will be empty if the method/closure of this [ClosureData] does not | 46 // This map will be empty if the method/closure of this [ClosureData] does not |
44 // contain any nested closure. | 47 // contain any nested closure. |
45 final Map<Node, ClosureScope> capturingScopes; | 48 final Map<Node, ClosureScope> capturingScopes; |
46 | 49 |
47 final Set<Element> usedVariablesInTry; | 50 final Set<Element> usedVariablesInTry; |
48 | 51 |
49 ClosureData(this.globalizedClosureElement, this.callElement) | 52 ClosureData(this.globalizedClosureElement, this.callElement, this.thisElement) |
50 : this.freeVariableMapping = new Map<Element, Element>(), | 53 : this.freeVariableMapping = new Map<Element, Element>(), |
51 this.capturedFieldMapping = new Map<Element, Element>(), | 54 this.capturedFieldMapping = new Map<Element, Element>(), |
52 this.capturingScopes = new Map<Node, ClosureScope>(), | 55 this.capturingScopes = new Map<Node, ClosureScope>(), |
53 this.usedVariablesInTry = new Set<Element>(); | 56 this.usedVariablesInTry = new Set<Element>(); |
54 } | 57 } |
55 | 58 |
56 Map<Node, ClosureData> _closureDataCache; | 59 Map<Node, ClosureData> _closureDataCache; |
57 Map<Node, ClosureData> get closureDataCache() { | 60 Map<Node, ClosureData> get closureDataCache() { |
58 if (_closureDataCache === null) { | 61 if (_closureDataCache === null) { |
59 _closureDataCache = new HashMap<Node, ClosureData>(); | 62 _closureDataCache = new HashMap<Node, ClosureData>(); |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
174 assert(element !== null); | 177 assert(element !== null); |
175 declareLocal(element); | 178 declareLocal(element); |
176 } | 179 } |
177 // We still need to visit the right-hand sides of the init-assignments. | 180 // We still need to visit the right-hand sides of the init-assignments. |
178 // Simply visit all children. We will visit the locals again and make them | 181 // Simply visit all children. We will visit the locals again and make them |
179 // used, but that should not be a problem. | 182 // used, but that should not be a problem. |
180 node.visitChildren(this); | 183 node.visitChildren(this); |
181 } | 184 } |
182 | 185 |
183 visitIdentifier(Identifier node) { | 186 visitIdentifier(Identifier node) { |
184 // TODO(floitsch): handle 'this'. | 187 if (node.isThis()) { |
185 if (node.isThis() && insideClosure) { | 188 useLocal(closureData.thisElement); |
186 compiler.unimplemented("ClosureAnalyzer.visitIdentifier this-capture", | |
187 node: node); | |
188 } | 189 } |
189 node.visitChildren(this); | 190 node.visitChildren(this); |
190 } | 191 } |
191 | 192 |
192 visitSend(Send node) { | 193 visitSend(Send node) { |
193 Element element = elements[node]; | 194 Element element = elements[node]; |
194 if (Elements.isLocal(element)) { | 195 if (Elements.isLocal(element)) { |
195 useLocal(element); | 196 useLocal(element); |
196 } else if (element === null && node.receiver === null) { | 197 } else if (node.receiver === null && |
197 if (insideClosure) { | 198 (element === null || |
ngeoffray
2012/02/08 12:37:58
Please add a:
Elements.isUnbound(Send node, TreeE
floitsch
2012/02/08 13:17:07
Now uses isInstanceSend.
| |
198 compiler.unimplemented("ClosureTranslator.visitSend this-capture"); | 199 Elements.isInstanceField(element) || |
199 } | 200 Elements.isInstanceMethod(element))) { |
201 useLocal(closureData.thisElement); | |
200 } | 202 } |
201 node.visitChildren(this); | 203 node.visitChildren(this); |
202 } | 204 } |
203 | 205 |
204 // If variables that are declared in the [node] scope are captured and need | 206 // If variables that are declared in the [node] scope are captured and need |
205 // to be boxed create a box-element and update the [capturingScopes] in the | 207 // to be boxed create a box-element and update the [capturingScopes] in the |
206 // current [closureData]. | 208 // current [closureData]. |
207 // The boxed variables are updated in the [capturedVariableMapping]. | 209 // The boxed variables are updated in the [capturedVariableMapping]. |
208 void attachCapturedScopeVariables(Node node) { | 210 void attachCapturedScopeVariables(Node node) { |
209 Element box = null; | 211 Element box = null; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
250 FunctionElement callElement = | 252 FunctionElement callElement = |
251 new FunctionElement.from(Namer.CLOSURE_INVOCATION_NAME, | 253 new FunctionElement.from(Namer.CLOSURE_INVOCATION_NAME, |
252 element, | 254 element, |
253 globalizedElement); | 255 globalizedElement); |
254 globalizedElement.backendMembers = | 256 globalizedElement.backendMembers = |
255 const EmptyLink<Element>().prepend(callElement); | 257 const EmptyLink<Element>().prepend(callElement); |
256 globalizedElement.isResolved = true; | 258 globalizedElement.isResolved = true; |
257 ClassElement objectClass = | 259 ClassElement objectClass = |
258 compiler.coreLibrary.find(const SourceString('Object')); | 260 compiler.coreLibrary.find(const SourceString('Object')); |
259 globalizedElement.supertype = new SimpleType(Types.OBJECT, objectClass); | 261 globalizedElement.supertype = new SimpleType(Types.OBJECT, objectClass); |
260 return new ClosureData(globalizedElement, callElement); | 262 // The nested function's 'this' is the same as the one for the outer |
263 // function. It could be [null] if we are inside a static method. | |
264 Element thisElement = closureData.thisElement; | |
265 return new ClosureData(globalizedElement, callElement, thisElement); | |
261 } | 266 } |
262 | 267 |
263 visitFunctionExpression(FunctionExpression node) { | 268 visitFunctionExpression(FunctionExpression node) { |
269 FunctionElement element = elements[node]; | |
264 bool isClosure = (closureData !== null); | 270 bool isClosure = (closureData !== null); |
265 | 271 |
266 if (isClosure) closures.add(node); | 272 if (isClosure) closures.add(node); |
267 | 273 |
268 bool oldInsideClosure = insideClosure; | 274 bool oldInsideClosure = insideClosure; |
269 FunctionElement oldFunctionElement = currentFunctionElement; | 275 FunctionElement oldFunctionElement = currentFunctionElement; |
270 ClosureData oldClosureData = closureData; | 276 ClosureData oldClosureData = closureData; |
271 List<Element> oldScopeVariables = scopeVariables; | 277 List<Element> oldScopeVariables = scopeVariables; |
272 | 278 |
273 | 279 |
274 insideClosure = isClosure; | 280 insideClosure = isClosure; |
275 currentFunctionElement = elements[node]; | 281 currentFunctionElement = elements[node]; |
276 closureData = insideClosure ? | 282 if (insideClosure) { |
277 globalizeClosure(node) : | 283 closureData = globalizeClosure(node); |
278 new ClosureData(null, null); | 284 } else { |
285 Element thisElement = null; | |
286 // TODO(floitsch): we should not need to look for generative constructors. | |
ngeoffray
2012/02/08 12:37:58
So why are we doing it?
floitsch
2012/02/08 13:17:07
updated comment.
| |
287 if (element.isInstanceMember() || | |
288 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { | |
289 thisElement = new VariableElement(const SourceString("this"), | |
ngeoffray
2012/02/08 12:37:58
new Element
floitsch
2012/02/08 13:17:07
Done.
| |
290 null, | |
291 ElementKind.PARAMETER, | |
292 element); | |
293 } | |
294 closureData = new ClosureData(null, null, thisElement); | |
295 } | |
279 scopeVariables = new List<Element>(); | 296 scopeVariables = new List<Element>(); |
280 | 297 |
281 // TODO(floitsch): a named function is visible from inside itself. Add | 298 // TODO(floitsch): a named function is visible from inside itself. Add |
282 // the element to the block. | 299 // the element to the block. |
283 | 300 |
301 // We have to declare the implicit 'this' parameter. | |
302 if (!insideClosure && closureData.thisElement !== null) { | |
303 declareLocal(closureData.thisElement); | |
304 } | |
305 | |
284 node.visitChildren(this); | 306 node.visitChildren(this); |
285 | 307 |
286 attachCapturedScopeVariables(node); | 308 attachCapturedScopeVariables(node); |
287 | 309 |
288 closureDataCache[node] = closureData; | 310 closureDataCache[node] = closureData; |
289 | 311 |
290 ClosureData savedClosureData = closureData; | 312 ClosureData savedClosureData = closureData; |
291 bool savedInsideClosure = insideClosure; | 313 bool savedInsideClosure = insideClosure; |
292 | 314 |
293 // Restore old values. | 315 // Restore old values. |
(...skipping 21 matching lines...) Expand all Loading... | |
315 } | 337 } |
316 } | 338 } |
317 | 339 |
318 visitTryStatement(TryStatement node) { | 340 visitTryStatement(TryStatement node) { |
319 // TODO(ngeoffray): implement finer grain state. | 341 // TODO(ngeoffray): implement finer grain state. |
320 inTryCatchOrFinally = true; | 342 inTryCatchOrFinally = true; |
321 node.visitChildren(this); | 343 node.visitChildren(this); |
322 inTryCatchOrFinally = false; | 344 inTryCatchOrFinally = false; |
323 } | 345 } |
324 } | 346 } |
OLD | NEW |