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 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
180 assert(element !== null); | 183 assert(element !== null); |
181 declareLocal(element); | 184 declareLocal(element); |
182 } | 185 } |
183 // We still need to visit the right-hand sides of the init-assignments. | 186 // We still need to visit the right-hand sides of the init-assignments. |
184 // Simply visit all children. We will visit the locals again and make them | 187 // Simply visit all children. We will visit the locals again and make them |
185 // used, but that should not be a problem. | 188 // used, but that should not be a problem. |
186 node.visitChildren(this); | 189 node.visitChildren(this); |
187 } | 190 } |
188 | 191 |
189 visitIdentifier(Identifier node) { | 192 visitIdentifier(Identifier node) { |
190 // TODO(floitsch): handle 'this'. | 193 if (node.isThis()) { |
191 if (node.isThis() && insideClosure) { | 194 useLocal(closureData.thisElement); |
192 compiler.unimplemented("ClosureAnalyzer.visitIdentifier this-capture", | |
193 node: node); | |
194 } | 195 } |
195 node.visitChildren(this); | 196 node.visitChildren(this); |
196 } | 197 } |
197 | 198 |
198 visitSend(Send node) { | 199 visitSend(Send node) { |
199 Element element = elements[node]; | 200 Element element = elements[node]; |
200 if (Elements.isLocal(element)) { | 201 if (Elements.isLocal(element)) { |
201 useLocal(element); | 202 useLocal(element); |
202 } else if (element === null && node.receiver === null) { | 203 } else if (node.receiver === null && |
203 if (insideClosure) { | 204 Elements.isInstanceSend(node, elements)) { |
204 compiler.unimplemented("ClosureTranslator.visitSend this-capture"); | 205 useLocal(closureData.thisElement); |
205 } | |
206 } | 206 } |
207 node.visitChildren(this); | 207 node.visitChildren(this); |
208 } | 208 } |
209 | 209 |
210 // If variables that are declared in the [node] scope are captured and need | 210 // If variables that are declared in the [node] scope are captured and need |
211 // to be boxed create a box-element and update the [capturingScopes] in the | 211 // to be boxed create a box-element and update the [capturingScopes] in the |
212 // current [closureData]. | 212 // current [closureData]. |
213 // The boxed variables are updated in the [capturedVariableMapping]. | 213 // The boxed variables are updated in the [capturedVariableMapping]. |
214 void attachCapturedScopeVariables(Node node) { | 214 void attachCapturedScopeVariables(Node node) { |
215 Element box = null; | 215 Element box = null; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
256 FunctionElement callElement = | 256 FunctionElement callElement = |
257 new FunctionElement.from(Namer.CLOSURE_INVOCATION_NAME, | 257 new FunctionElement.from(Namer.CLOSURE_INVOCATION_NAME, |
258 element, | 258 element, |
259 globalizedElement); | 259 globalizedElement); |
260 globalizedElement.backendMembers = | 260 globalizedElement.backendMembers = |
261 const EmptyLink<Element>().prepend(callElement); | 261 const EmptyLink<Element>().prepend(callElement); |
262 globalizedElement.isResolved = true; | 262 globalizedElement.isResolved = true; |
263 ClassElement objectClass = | 263 ClassElement objectClass = |
264 compiler.coreLibrary.find(const SourceString('Object')); | 264 compiler.coreLibrary.find(const SourceString('Object')); |
265 globalizedElement.supertype = new SimpleType(Types.OBJECT, objectClass); | 265 globalizedElement.supertype = new SimpleType(Types.OBJECT, objectClass); |
266 return new ClosureData(globalizedElement, callElement); | 266 // The nested function's 'this' is the same as the one for the outer |
| 267 // function. It could be [null] if we are inside a static method. |
| 268 Element thisElement = closureData.thisElement; |
| 269 return new ClosureData(globalizedElement, callElement, thisElement); |
267 } | 270 } |
268 | 271 |
269 visitFunctionExpression(FunctionExpression node) { | 272 visitFunctionExpression(FunctionExpression node) { |
| 273 FunctionElement element = elements[node]; |
270 bool isClosure = (closureData !== null); | 274 bool isClosure = (closureData !== null); |
271 | 275 |
272 if (isClosure) closures.add(node); | 276 if (isClosure) closures.add(node); |
273 | 277 |
274 bool oldInsideClosure = insideClosure; | 278 bool oldInsideClosure = insideClosure; |
275 FunctionElement oldFunctionElement = currentFunctionElement; | 279 FunctionElement oldFunctionElement = currentFunctionElement; |
276 ClosureData oldClosureData = closureData; | 280 ClosureData oldClosureData = closureData; |
277 List<Element> oldScopeVariables = scopeVariables; | 281 List<Element> oldScopeVariables = scopeVariables; |
278 | 282 |
279 | 283 |
280 insideClosure = isClosure; | 284 insideClosure = isClosure; |
281 currentFunctionElement = elements[node]; | 285 currentFunctionElement = elements[node]; |
282 closureData = insideClosure ? | 286 if (insideClosure) { |
283 globalizeClosure(node) : | 287 closureData = globalizeClosure(node); |
284 new ClosureData(null, null); | 288 } else { |
| 289 Element thisElement = null; |
| 290 // TODO(floitsch): we should not need to look for generative constructors. |
| 291 // At the moment we store only one ClosureData for both the factory and |
| 292 // the body. |
| 293 if (element.isInstanceMember() || |
| 294 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) { |
| 295 // TODO(floitsch): currently all variables are considered to be |
| 296 // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'. |
| 297 Element thisEnclosingElement = element; |
| 298 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) { |
| 299 ConstructorBodyElement body = element; |
| 300 thisEnclosingElement = body.constructor; |
| 301 } |
| 302 thisElement = new Element(const SourceString("this"), |
| 303 ElementKind.PARAMETER, |
| 304 thisEnclosingElement); |
| 305 } |
| 306 closureData = new ClosureData(null, null, thisElement); |
| 307 } |
285 scopeVariables = new List<Element>(); | 308 scopeVariables = new List<Element>(); |
286 | 309 |
287 // TODO(floitsch): a named function is visible from inside itself. Add | 310 // TODO(floitsch): a named function is visible from inside itself. Add |
288 // the element to the block. | 311 // the element to the block. |
289 | 312 |
| 313 // We have to declare the implicit 'this' parameter. |
| 314 if (!insideClosure && closureData.thisElement !== null) { |
| 315 declareLocal(closureData.thisElement); |
| 316 } |
| 317 |
290 node.visitChildren(this); | 318 node.visitChildren(this); |
291 | 319 |
292 attachCapturedScopeVariables(node); | 320 attachCapturedScopeVariables(node); |
293 | 321 |
294 closureDataCache[node] = closureData; | 322 closureDataCache[node] = closureData; |
295 | 323 |
296 ClosureData savedClosureData = closureData; | 324 ClosureData savedClosureData = closureData; |
297 bool savedInsideClosure = insideClosure; | 325 bool savedInsideClosure = insideClosure; |
298 | 326 |
299 // Restore old values. | 327 // Restore old values. |
(...skipping 21 matching lines...) Expand all Loading... |
321 } | 349 } |
322 } | 350 } |
323 | 351 |
324 visitTryStatement(TryStatement node) { | 352 visitTryStatement(TryStatement node) { |
325 // TODO(ngeoffray): implement finer grain state. | 353 // TODO(ngeoffray): implement finer grain state. |
326 inTryCatchOrFinally = true; | 354 inTryCatchOrFinally = true; |
327 node.visitChildren(this); | 355 node.visitChildren(this); |
328 inTryCatchOrFinally = false; | 356 inTryCatchOrFinally = false; |
329 } | 357 } |
330 } | 358 } |
OLD | NEW |