Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: frog/leg/ssa/closure.dart

Issue 9873021: Move frog/leg to lib/compiler/implementation. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « frog/leg/ssa/builder.dart ('k') | frog/leg/ssa/codegen.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 class ClosureFieldElement extends Element {
6 ClosureFieldElement(SourceString name, ClassElement enclosing)
7 : super(name, ElementKind.FIELD, enclosing);
8
9 bool isInstanceMember() => true;
10 bool isAssignable() => false;
11
12 String toString() => "ClosureFieldElement($name)";
13 }
14
15 class ClosureClassElement extends ClassElement {
16 ClosureClassElement(Compiler compiler, Element enclosingElement)
17 : super(compiler.closureClass.name, enclosingElement) {
18 isResolved = true;
19 compiler.closureClass.ensureResolved(compiler);
20 supertype = compiler.closureClass.computeType(compiler);
21 }
22 }
23
24 // The box-element for a scope, and the captured variables that need to be
25 // stored in the box.
26 class ClosureScope {
27 Element boxElement;
28 Map<Element, Element> capturedVariableMapping;
29 // If the scope is attached to a [For] contains the variables that are
30 // declared in the initializer of the [For] and that need to be boxed.
31 // Otherwise contains the empty List.
32 List<Element> boxedLoopVariables;
33
34 ClosureScope(this.boxElement, this.capturedVariableMapping)
35 : boxedLoopVariables = const <Element>[];
36
37 bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty();
38 }
39
40 class ClosureData {
41 // The closure's element before any translation. Will be null for methods.
42 final FunctionElement closureElement;
43 // The closureClassElement will be null for methods that are not local
44 // closures.
45 final ClassElement closureClassElement;
46 // The callElement will be null for methods that are not local closures.
47 final FunctionElement callElement;
48 // The [thisElement] makes handling 'this' easier by treating it like any
49 // other argument. It is only set for instance-members.
50 final Element thisElement;
51
52 // Maps free locals, arguments and function elements to their captured
53 // copies.
54 final Map<Element, Element> freeVariableMapping;
55 // Maps closure-fields to their captured elements. This is somehow the inverse
56 // mapping of [freeVariableMapping], but whereas [freeVariableMapping] does
57 // not deal with boxes, here we map instance-fields (which might represent
58 // boxes) to their boxElement.
59 final Map<Element, Element> capturedFieldMapping;
60
61 // Maps scopes ([Loop] and [FunctionExpression] nodes) to their
62 // [ClosureScope] which contains their box and the
63 // captured variables that are stored in the box.
64 // This map will be empty if the method/closure of this [ClosureData] does not
65 // contain any nested closure.
66 final Map<Node, ClosureScope> capturingScopes;
67
68 final Set<Element> usedVariablesInTry;
69
70 ClosureData(this.closureElement,
71 this.closureClassElement,
72 this.callElement,
73 this.thisElement)
74 : this.freeVariableMapping = new Map<Element, Element>(),
75 this.capturedFieldMapping = new Map<Element, Element>(),
76 this.capturingScopes = new Map<Node, ClosureScope>(),
77 this.usedVariablesInTry = new Set<Element>();
78
79 bool isClosure() => closureElement !== null;
80 }
81
82 class ClosureTranslator extends AbstractVisitor {
83 final Compiler compiler;
84 final TreeElements elements;
85 int boxCounter = 0;
86 bool inTryCatchOrFinally = false;
87 final Map<Node, ClosureData> closureDataCache;
88
89 // Map of captured variables. Initially they will map to themselves. If
90 // a variable needs to be boxed then the scope declaring the variable
91 // will update this mapping.
92 Map<Element, Element> capturedVariableMapping;
93 // List of encountered closures.
94 List<FunctionExpression> closures;
95
96 // The variables that have been declared in the current scope.
97 List<Element> scopeVariables;
98
99 FunctionElement currentFunctionElement;
100 // The closureData of the currentFunctionElement.
101 ClosureData closureData;
102
103 bool insideClosure = false;
104
105 ClosureTranslator(Compiler compiler, this.elements)
106 : capturedVariableMapping = new Map<Element, Element>(),
107 closures = <FunctionExpression>[],
108 this.compiler = compiler,
109 this.closureDataCache = compiler.builder.closureDataCache;
110
111 ClosureData translate(Node node) {
112 // Closures have already been analyzed when visiting the surrounding
113 // method/function. This also shortcuts for bailout functions.
114 ClosureData cached = closureDataCache[node];
115 if (cached !== null) return cached;
116
117 visit(node);
118 // When variables need to be boxed their [capturedVariableMapping] is
119 // updated, but we delay updating the similar freeVariableMapping in the
120 // closure datas that capture these variables.
121 // The closures don't have their fields (in the closure class) set, either.
122 updateClosures();
123
124 return closureDataCache[node];
125 }
126
127 // This function runs through all of the existing closures and updates their
128 // free variables to the boxed value. It also adds the field-elements to the
129 // class representing the closure. At the same time it fills the
130 // [capturedFieldMapping].
131 void updateClosures() {
132 for (FunctionExpression closure in closures) {
133 // The captured variables that need to be stored in a field of the closure
134 // class.
135 Set<Element> fieldCaptures = new Set<Element>();
136 ClosureData data = closureDataCache[closure];
137 Map<Element, Element> freeVariableMapping = data.freeVariableMapping;
138 // We get a copy of the keys and iterate over it, to avoid modifications
139 // to the map while iterating over it.
140 freeVariableMapping.getKeys().forEach((Element fromElement) {
141 assert(fromElement == freeVariableMapping[fromElement]);
142 Element updatedElement = capturedVariableMapping[fromElement];
143 assert(updatedElement !== null);
144 if (fromElement == updatedElement) {
145 assert(freeVariableMapping[fromElement] == updatedElement);
146 assert(Elements.isLocal(updatedElement));
147 // The variable has not been boxed.
148 fieldCaptures.add(updatedElement);
149 } else {
150 // A boxed element.
151 freeVariableMapping[fromElement] = updatedElement;
152 Element boxElement = updatedElement.enclosingElement;
153 assert(boxElement.kind == ElementKind.VARIABLE);
154 fieldCaptures.add(boxElement);
155 }
156 });
157 ClassElement closureElement = data.closureClassElement;
158 assert(closureElement != null || fieldCaptures.isEmpty());
159 for (Element boxElement in fieldCaptures) {
160 Element fieldElement =
161 new ClosureFieldElement(boxElement.name, closureElement);
162 closureElement.backendMembers =
163 closureElement.backendMembers.prepend(fieldElement);
164 data.capturedFieldMapping[fieldElement] = boxElement;
165 freeVariableMapping[boxElement] = fieldElement;
166 }
167 }
168 }
169
170 void useLocal(Element element) {
171 // TODO(floitsch): replace this with a general solution.
172 Element functionElement = currentFunctionElement;
173 if (functionElement.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
174 ConstructorBodyElement body = functionElement;
175 functionElement = body.constructor;
176 }
177 // If the element is not declared in the current function and the element
178 // is not the closure itself we need to mark the element as free variable.
179 if (element.enclosingElement != functionElement &&
180 element != functionElement) {
181 assert(closureData.freeVariableMapping[element] == null ||
182 closureData.freeVariableMapping[element] == element);
183 closureData.freeVariableMapping[element] = element;
184 } else if (inTryCatchOrFinally) {
185 // Don't mark the this-element. This would complicate things in the
186 // builder.
187 if (element != closureData.thisElement) {
188 // TODO(ngeoffray): only do this if the variable is mutated.
189 closureData.usedVariablesInTry.add(element);
190 }
191 }
192 }
193
194 void declareLocal(Element element) {
195 scopeVariables.add(element);
196 }
197
198 visit(Node node) => node.accept(this);
199
200 visitNode(Node node) => node.visitChildren(this);
201
202 visitVariableDefinitions(VariableDefinitions node) {
203 for (Link<Node> link = node.definitions.nodes;
204 !link.isEmpty();
205 link = link.tail) {
206 Node definition = link.head;
207 Element element = elements[definition];
208 assert(element !== null);
209 declareLocal(element);
210 }
211 // We still need to visit the right-hand sides of the init-assignments.
212 // Simply visit all children. We will visit the locals again and make them
213 // used, but that should not be a problem.
214 node.visitChildren(this);
215 }
216
217 visitIdentifier(Identifier node) {
218 if (node.isThis()) {
219 useLocal(closureData.thisElement);
220 }
221 node.visitChildren(this);
222 }
223
224 visitSend(Send node) {
225 Element element = elements[node];
226 if (Elements.isLocal(element)) {
227 useLocal(element);
228 } else if (node.receiver === null &&
229 Elements.isInstanceSend(node, elements)) {
230 useLocal(closureData.thisElement);
231 }
232 node.visitChildren(this);
233 }
234
235 // If variables that are declared in the [node] scope are captured and need
236 // to be boxed create a box-element and update the [capturingScopes] in the
237 // current [closureData].
238 // The boxed variables are updated in the [capturedVariableMapping].
239 void attachCapturedScopeVariables(Node node) {
240 Element box = null;
241 Map<Element, Element> scopeMapping = new Map<Element, Element>();
242 for (Element element in scopeVariables) {
243 if (capturedVariableMapping.containsKey(element)) {
244 if (box == null) {
245 // TODO(floitsch): construct better box names.
246 SourceString boxName = new SourceString("box${boxCounter++}");
247 box = new Element(boxName,
248 ElementKind.VARIABLE,
249 currentFunctionElement);
250 }
251 // TODO(floitsch): construct better boxed names.
252 String elementName = element.name.slowToString();
253 // We are currently using the name in an HForeign which could replace
254 // "$X" with something else.
255 String escaped = elementName.replaceAll("\$", "_");
256 SourceString boxedName = new SourceString("${escaped}_${boxCounter++}");
257 Element boxed = new Element(boxedName, ElementKind.FIELD, box);
258 scopeMapping[element] = boxed;
259 capturedVariableMapping[element] = boxed;
260 }
261 }
262 if (!scopeMapping.isEmpty()) {
263 ClosureScope scope = new ClosureScope(box, scopeMapping);
264 closureData.capturingScopes[node] = scope;
265 }
266 }
267
268 visitLoop(Loop node) {
269 List<Element> oldScopeVariables = scopeVariables;
270 scopeVariables = new List<Element>();
271 node.visitChildren(this);
272 attachCapturedScopeVariables(node);
273 scopeVariables = oldScopeVariables;
274 }
275
276 visitFor(For node) {
277 visitLoop(node);
278 // See if we have declared loop variables that need to be boxed.
279 if (node.initializer === null) return;
280 VariableDefinitions definitions = node.initializer.asVariableDefinitions();
281 if (definitions == null) return;
282 ClosureScope scopeData = closureData.capturingScopes[node];
283 if (scopeData === null) return;
284 List<Element> result = <Element>[];
285 for (Link<Node> link = definitions.definitions.nodes;
286 !link.isEmpty();
287 link = link.tail) {
288 Node definition = link.head;
289 Element element = elements[definition];
290 if (capturedVariableMapping.containsKey(element)) {
291 result.add(element);
292 };
293 }
294 scopeData.boxedLoopVariables = result;
295 }
296
297 ClosureData globalizeClosure(FunctionExpression node) {
298 FunctionElement element = elements[node];
299 ClassElement globalizedElement =
300 new ClosureClassElement(compiler, element.getCompilationUnit());
301 FunctionElement callElement =
302 new FunctionElement.from(Namer.CLOSURE_INVOCATION_NAME,
303 element,
304 globalizedElement);
305 globalizedElement.backendMembers =
306 const EmptyLink<Element>().prepend(callElement);
307 // The nested function's 'this' is the same as the one for the outer
308 // function. It could be [null] if we are inside a static method.
309 Element thisElement = closureData.thisElement;
310 return new ClosureData(element, globalizedElement,
311 callElement, thisElement);
312 }
313
314 visitFunctionExpression(FunctionExpression node) {
315 Element element = elements[node];
316 if (element.kind === ElementKind.PARAMETER) {
317 // TODO(ahe): This is a hack. This method should *not* call
318 // visitChildren.
319 return node.name.accept(this);
320 }
321 bool isClosure = (closureData !== null);
322
323 if (isClosure) closures.add(node);
324
325 bool oldInsideClosure = insideClosure;
326 FunctionElement oldFunctionElement = currentFunctionElement;
327 ClosureData oldClosureData = closureData;
328 List<Element> oldScopeVariables = scopeVariables;
329
330
331 insideClosure = isClosure;
332 currentFunctionElement = elements[node];
333 if (insideClosure) {
334 closureData = globalizeClosure(node);
335 } else {
336 Element thisElement = null;
337 // TODO(floitsch): we should not need to look for generative constructors.
338 // At the moment we store only one ClosureData for both the factory and
339 // the body.
340 if (element.isInstanceMember() ||
341 element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
342 // TODO(floitsch): currently all variables are considered to be
343 // declared in the GENERATIVE_CONSTRUCTOR. Including the 'this'.
344 Element thisEnclosingElement = element;
345 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY) {
346 ConstructorBodyElement body = element;
347 thisEnclosingElement = body.constructor;
348 }
349 thisElement = new Element(const SourceString("this"),
350 ElementKind.PARAMETER,
351 thisEnclosingElement);
352 }
353 closureData = new ClosureData(null, null, null, thisElement);
354 }
355 scopeVariables = new List<Element>();
356
357 // TODO(floitsch): a named function is visible from inside itself. Add
358 // the element to the block.
359
360 // We have to declare the implicit 'this' parameter.
361 if (!insideClosure && closureData.thisElement !== null) {
362 declareLocal(closureData.thisElement);
363 }
364 // If we are inside a named closure we have to declare ourselve. For
365 // simplicity we declare the local even if the closure does not have a name
366 // It will simply not be used.
367 if (insideClosure) {
368 declareLocal(element);
369 }
370
371 // TODO(ahe): This is problematic. The backend should not repeat
372 // the work of the resolver. It is the resolver's job to create
373 // parameters, etc. Other phases should only visit statements.
374 // TODO(floitsch): we avoid visiting the initializers on purpose so that we
375 // get an error-message later in the builder.
376 if (node.parameters !== null) node.parameters.accept(this);
377 if (node.body !== null) node.body.accept(this);
378
379 attachCapturedScopeVariables(node);
380
381 closureDataCache[node] = closureData;
382
383 ClosureData savedClosureData = closureData;
384 bool savedInsideClosure = insideClosure;
385
386 // Restore old values.
387 scopeVariables = oldScopeVariables;
388 insideClosure = oldInsideClosure;
389 closureData = oldClosureData;
390 currentFunctionElement = oldFunctionElement;
391
392 // Mark all free variables as captured and use them in the outer function.
393 List<Element> freeVariables =
394 savedClosureData.freeVariableMapping.getKeys();
395 assert(freeVariables.isEmpty() || savedInsideClosure);
396 for (Element freeElement in freeVariables) {
397 if (capturedVariableMapping[freeElement] != null &&
398 capturedVariableMapping[freeElement] != freeElement) {
399 compiler.internalError('In closure analyzer', node: node);
400 }
401 capturedVariableMapping[freeElement] = freeElement;
402 useLocal(freeElement);
403 }
404 }
405
406 visitFunctionDeclaration(FunctionDeclaration node) {
407 node.visitChildren(this);
408 declareLocal(elements[node]);
409 }
410
411 visitTryStatement(TryStatement node) {
412 // TODO(ngeoffray): implement finer grain state.
413 inTryCatchOrFinally = true;
414 node.visitChildren(this);
415 inTryCatchOrFinally = false;
416 }
417 }
OLDNEW
« no previous file with comments | « frog/leg/ssa/builder.dart ('k') | frog/leg/ssa/codegen.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698