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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/closure.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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
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 library closureToClassMapper;
6
7 import "elements/elements.dart";
8 import "dart2jslib.dart";
9 import "dart_types.dart";
10 import "js_backend/js_backend.dart" show JavaScriptBackend;
11 import "scanner/scannerlib.dart" show Token;
12 import "tree/tree.dart";
13 import "util/util.dart";
14 import "elements/modelx.dart"
15 show BaseFunctionElementX,
16 ClassElementX,
17 ElementX,
18 LocalFunctionElementX;
19 import "elements/visitor.dart" show ElementVisitor;
20
21 import 'universe/universe.dart' show
22 Universe;
23
24 class ClosureNamer {
25 String getClosureVariableName(String name, int id) {
26 return "${name}_$id";
27 }
28
29 void forgetElement(Element element) {}
30 }
31
32 class ClosureTask extends CompilerTask {
33 Map<Node, ClosureClassMap> closureMappingCache;
34 ClosureNamer namer;
35 ClosureTask(Compiler compiler, this.namer)
36 : closureMappingCache = new Map<Node, ClosureClassMap>(),
37 super(compiler);
38
39 String get name => "Closure Simplifier";
40
41 ClosureClassMap computeClosureToClassMapping(Element element,
42 Node node,
43 TreeElements elements) {
44 return measure(() {
45 ClosureClassMap cached = closureMappingCache[node];
46 if (cached != null) return cached;
47
48 ClosureTranslator translator =
49 new ClosureTranslator(compiler, elements, closureMappingCache, namer);
50
51 // The translator will store the computed closure-mappings inside the
52 // cache. One for given node and one for each nested closure.
53 if (node is FunctionExpression) {
54 translator.translateFunction(element, node);
55 } else if (element.isSynthesized) {
56 return new ClosureClassMap(null, null, null, new ThisLocal(element));
57 } else {
58 assert(element.isField);
59 VariableElement field = element;
60 if (field.initializer != null) {
61 // The lazy initializer of a static.
62 translator.translateLazyInitializer(element, node, field.initializer);
63 } else {
64 assert(element.isInstanceMember);
65 closureMappingCache[node] =
66 new ClosureClassMap(null, null, null, new ThisLocal(element));
67 }
68 }
69 assert(closureMappingCache[node] != null);
70 return closureMappingCache[node];
71 });
72 }
73
74 ClosureClassMap getMappingForNestedFunction(FunctionExpression node) {
75 return measure(() {
76 ClosureClassMap nestedClosureData = closureMappingCache[node];
77 if (nestedClosureData == null) {
78 compiler.internalError(node, "No closure cache.");
79 }
80 return nestedClosureData;
81 });
82 }
83
84 void forgetElement(var closure) {
85 ClosureClassElement cls;
86 if (closure is ClosureFieldElement) {
87 cls = closure.closureClass;
88 } else if (closure is SynthesizedCallMethodElementX) {
89 cls = closure.closureClass;
90 } else {
91 throw new SpannableAssertionFailure(
92 closure, 'Not a closure: $closure (${closure.runtimeType}).');
93 }
94 namer.forgetElement(cls);
95 compiler.enqueuer.codegen.forgetElement(cls);
96 }
97 }
98
99 /// Common interface for [BoxFieldElement] and [ClosureFieldElement] as
100 /// non-elements.
101 abstract class CapturedVariable {}
102
103 // TODO(ahe): These classes continuously cause problems. We need to
104 // find a more general solution.
105 class ClosureFieldElement extends ElementX
106 implements VariableElement, CapturedVariable {
107 /// The source variable this element refers to.
108 final Local local;
109
110 ClosureFieldElement(String name,
111 this.local,
112 ClosureClassElement enclosing)
113 : super(name, ElementKind.FIELD, enclosing);
114
115 /// Use [closureClass] instead.
116 @deprecated
117 get enclosingElement => super.enclosingElement;
118
119 ClosureClassElement get closureClass => super.enclosingElement;
120
121 MemberElement get memberContext => closureClass.methodElement.memberContext;
122
123 bool get hasNode => false;
124
125 Node get node {
126 throw new SpannableAssertionFailure(local,
127 'Should not access node of ClosureFieldElement.');
128 }
129
130 bool get hasResolvedAst => hasTreeElements;
131
132 ResolvedAst get resolvedAst {
133 return new ResolvedAst(this, null, treeElements);
134 }
135
136 Expression get initializer {
137 throw new SpannableAssertionFailure(local,
138 'Should not access initializer of ClosureFieldElement.');
139 }
140
141 bool get isInstanceMember => true;
142 bool get isAssignable => false;
143
144 DartType computeType(Compiler compiler) => type;
145
146 DartType get type {
147 if (local is LocalElement) {
148 LocalElement element = local;
149 return element.type;
150 }
151 return const DynamicType();
152 }
153
154 String toString() => "ClosureFieldElement($name)";
155
156 accept(ElementVisitor visitor) => visitor.visitClosureFieldElement(this);
157
158 Element get analyzableElement => closureClass.methodElement.analyzableElement;
159 }
160
161 // TODO(ahe): These classes continuously cause problems. We need to find
162 // a more general solution.
163 class ClosureClassElement extends ClassElementX {
164 DartType rawType;
165 DartType thisType;
166 FunctionType callType;
167 /// Node that corresponds to this closure, used for source position.
168 final FunctionExpression node;
169
170 /**
171 * The element for the declaration of the function expression.
172 */
173 final LocalFunctionElement methodElement;
174
175 final List<ClosureFieldElement> _closureFields = <ClosureFieldElement>[];
176
177 ClosureClassElement(this.node,
178 String name,
179 Compiler compiler,
180 LocalFunctionElement closure)
181 : this.methodElement = closure,
182 super(name,
183 closure.compilationUnit,
184 // By assigning a fresh class-id we make sure that the hashcode
185 // is unique, but also emit closure classes after all other
186 // classes (since the emitter sorts classes by their id).
187 compiler.getNextFreeClassId(),
188 STATE_DONE) {
189 JavaScriptBackend backend = compiler.backend;
190 ClassElement superclass = methodElement.isInstanceMember
191 ? backend.boundClosureClass
192 : backend.closureClass;
193 superclass.ensureResolved(compiler);
194 supertype = superclass.thisType;
195 interfaces = const Link<DartType>();
196 thisType = rawType = new InterfaceType(this);
197 allSupertypesAndSelf =
198 superclass.allSupertypesAndSelf.extendClass(thisType);
199 callType = methodElement.type;
200 }
201
202 Iterable<ClosureFieldElement> get closureFields => _closureFields;
203
204 void addField(ClosureFieldElement field, DiagnosticListener listener) {
205 _closureFields.add(field);
206 addMember(field, listener);
207 }
208
209 bool get hasNode => true;
210
211 bool get isClosure => true;
212
213 Token get position => node.getBeginToken();
214
215 Node parseNode(DiagnosticListener listener) => node;
216
217 // A [ClosureClassElement] is nested inside a function or initializer in terms
218 // of [enclosingElement], but still has to be treated as a top-level
219 // element.
220 bool get isTopLevel => true;
221
222 get enclosingElement => methodElement;
223
224 accept(ElementVisitor visitor) => visitor.visitClosureClassElement(this);
225 }
226
227 /// A local variable that contains the box object holding the [BoxFieldElement]
228 /// fields.
229 class BoxLocal extends Local {
230 final String name;
231 final ExecutableElement executableContext;
232
233 BoxLocal(this.name, this.executableContext);
234 }
235
236 // TODO(ngeoffray, ahe): These classes continuously cause problems. We need to
237 // find a more general solution.
238 class BoxFieldElement extends ElementX
239 implements TypedElement, CapturedVariable {
240 final BoxLocal box;
241
242 BoxFieldElement(String name, this.variableElement, BoxLocal box)
243 : this.box = box,
244 super(name, ElementKind.FIELD, box.executableContext);
245
246 DartType computeType(Compiler compiler) => type;
247
248 DartType get type => variableElement.type;
249
250 final VariableElement variableElement;
251
252 accept(ElementVisitor visitor) => visitor.visitBoxFieldElement(this);
253 }
254
255 /// A local variable used encode the direct (uncaptured) references to [this].
256 class ThisLocal extends Local {
257 final ExecutableElement executableContext;
258
259 ThisLocal(this.executableContext);
260
261 String get name => 'this';
262
263 ClassElement get enclosingClass => executableContext.enclosingClass;
264 }
265
266 /// Call method of a closure class.
267 class SynthesizedCallMethodElementX extends BaseFunctionElementX {
268 final LocalFunctionElement expression;
269
270 SynthesizedCallMethodElementX(String name,
271 LocalFunctionElementX other,
272 ClosureClassElement enclosing)
273 : expression = other,
274 super(name, other.kind, other.modifiers, enclosing, false) {
275 functionSignatureCache = other.functionSignature;
276 }
277
278 /// Use [closureClass] instead.
279 @deprecated
280 get enclosingElement => super.enclosingElement;
281
282 ClosureClassElement get closureClass => super.enclosingElement;
283
284 MemberElement get memberContext {
285 return closureClass.methodElement.memberContext;
286 }
287
288 bool get hasNode => expression.hasNode;
289
290 FunctionExpression get node => expression.node;
291
292 FunctionExpression parseNode(DiagnosticListener listener) => node;
293
294 ResolvedAst get resolvedAst {
295 return new ResolvedAst(this, node, treeElements);
296 }
297
298 Element get analyzableElement => closureClass.methodElement.analyzableElement;
299 }
300
301 // The box-element for a scope, and the captured variables that need to be
302 // stored in the box.
303 class ClosureScope {
304 BoxLocal boxElement;
305 Map<VariableElement, BoxFieldElement> _capturedVariableMapping;
306
307 // If the scope is attached to a [For] contains the variables that are
308 // declared in the initializer of the [For] and that need to be boxed.
309 // Otherwise contains the empty List.
310 List<VariableElement> boxedLoopVariables = const <VariableElement>[];
311
312 ClosureScope(this.boxElement, this._capturedVariableMapping);
313
314 bool hasBoxedLoopVariables() => !boxedLoopVariables.isEmpty;
315
316 bool isCapturedVariable(VariableElement variable) {
317 return _capturedVariableMapping.containsKey(variable);
318 }
319
320 void forEachCapturedVariable(f(LocalVariableElement variable,
321 BoxFieldElement boxField)) {
322 _capturedVariableMapping.forEach(f);
323 }
324 }
325
326 class ClosureClassMap {
327 // The closure's element before any translation. Will be null for methods.
328 final LocalFunctionElement closureElement;
329 // The closureClassElement will be null for methods that are not local
330 // closures.
331 final ClosureClassElement closureClassElement;
332 // The callElement will be null for methods that are not local closures.
333 final FunctionElement callElement;
334 // The [thisElement] makes handling 'this' easier by treating it like any
335 // other argument. It is only set for instance-members.
336 final ThisLocal thisLocal;
337
338 // Maps free locals, arguments and function elements to their captured
339 // copies.
340 final Map<Local, CapturedVariable> _freeVariableMapping =
341 new Map<Local, CapturedVariable>();
342
343 // Maps closure-fields to their captured elements. This is somehow the inverse
344 // mapping of [freeVariableMapping], but whereas [freeVariableMapping] does
345 // not deal with boxes, here we map instance-fields (which might represent
346 // boxes) to their boxElement.
347 final Map<ClosureFieldElement, Local> _closureFieldMapping =
348 new Map<ClosureFieldElement, Local>();
349
350 // Maps scopes ([Loop] and [FunctionExpression] nodes) to their
351 // [ClosureScope] which contains their box and the
352 // captured variables that are stored in the box.
353 // This map will be empty if the method/closure of this [ClosureData] does not
354 // contain any nested closure.
355 final Map<Node, ClosureScope> capturingScopes = new Map<Node, ClosureScope>();
356
357 final Set<Local> usedVariablesInTry = new Set<Local>();
358
359 ClosureClassMap(this.closureElement,
360 this.closureClassElement,
361 this.callElement,
362 this.thisLocal);
363
364 void addFreeVariable(Local element) {
365 assert(_freeVariableMapping[element] == null);
366 _freeVariableMapping[element] = null;
367 }
368
369 Iterable<Local> get freeVariables => _freeVariableMapping.keys;
370
371 bool isFreeVariable(Local element) {
372 return _freeVariableMapping.containsKey(element);
373 }
374
375 CapturedVariable getFreeVariableElement(Local element) {
376 return _freeVariableMapping[element];
377 }
378
379 /// Sets the free [variable] to be captured by the [boxField].
380 void setFreeVariableBoxField(Local variable,
381 BoxFieldElement boxField) {
382 _freeVariableMapping[variable] = boxField;
383 }
384
385 /// Sets the free [variable] to be captured by the [closureField].
386 void setFreeVariableClosureField(Local variable,
387 ClosureFieldElement closureField) {
388 _freeVariableMapping[variable] = closureField;
389 }
390
391
392 void forEachFreeVariable(f(Local variable,
393 CapturedVariable field)) {
394 _freeVariableMapping.forEach(f);
395 }
396
397 Local getLocalVariableForClosureField(ClosureFieldElement field) {
398 return _closureFieldMapping[field];
399 }
400
401 void setLocalVariableForClosureField(ClosureFieldElement field,
402 Local variable) {
403 _closureFieldMapping[field] = variable;
404 }
405
406 bool get isClosure => closureElement != null;
407
408 bool capturingScopesBox(Local variable) {
409 return capturingScopes.values.any((scope) {
410 return scope.boxedLoopVariables.contains(variable);
411 });
412 }
413
414 bool isVariableBoxed(Local variable) {
415 CapturedVariable copy = _freeVariableMapping[variable];
416 if (copy is BoxFieldElement) {
417 return true;
418 }
419 return capturingScopesBox(variable);
420 }
421
422 void forEachCapturedVariable(void f(Local variable,
423 CapturedVariable field)) {
424 _freeVariableMapping.forEach((variable, copy) {
425 if (variable is BoxLocal) return;
426 f(variable, copy);
427 });
428 capturingScopes.values.forEach((ClosureScope scope) {
429 scope.forEachCapturedVariable(f);
430 });
431 }
432
433 void forEachBoxedVariable(void f(LocalVariableElement local,
434 BoxFieldElement field)) {
435 _freeVariableMapping.forEach((variable, copy) {
436 if (!isVariableBoxed(variable)) return;
437 f(variable, copy);
438 });
439 capturingScopes.values.forEach((ClosureScope scope) {
440 scope.forEachCapturedVariable(f);
441 });
442 }
443
444 void removeMyselfFrom(Universe universe) {
445 _freeVariableMapping.values.forEach((e) {
446 universe.closurizedMembers.remove(e);
447 universe.fieldSetters.remove(e);
448 universe.fieldGetters.remove(e);
449 });
450 }
451 }
452
453 class ClosureTranslator extends Visitor {
454 final Compiler compiler;
455 final TreeElements elements;
456 int closureFieldCounter = 0;
457 int boxedFieldCounter = 0;
458 bool inTryStatement = false;
459 final Map<Node, ClosureClassMap> closureMappingCache;
460
461 // Map of captured variables. Initially they will map to `null`. If
462 // a variable needs to be boxed then the scope declaring the variable
463 // will update this to mapping to the capturing [BoxFieldElement].
464 Map<Local, BoxFieldElement> _capturedVariableMapping =
465 new Map<Local, BoxFieldElement>();
466
467 // List of encountered closures.
468 List<Expression> closures = <Expression>[];
469
470 // The local variables that have been declared in the current scope.
471 List<LocalVariableElement> scopeVariables;
472
473 // Keep track of the mutated local variables so that we don't need to box
474 // non-mutated variables.
475 Set<LocalVariableElement> mutatedVariables = new Set<LocalVariableElement>();
476
477 MemberElement outermostElement;
478 ExecutableElement executableContext;
479
480 // The closureData of the currentFunctionElement.
481 ClosureClassMap closureData;
482
483 ClosureNamer namer;
484
485 bool insideClosure = false;
486
487 ClosureTranslator(this.compiler,
488 this.elements,
489 this.closureMappingCache,
490 this.namer);
491
492 bool isCapturedVariable(Local element) {
493 return _capturedVariableMapping.containsKey(element);
494 }
495
496 void addCapturedVariable(Node node, Local variable) {
497 if (_capturedVariableMapping[variable] != null) {
498 compiler.internalError(node, 'In closure analyzer.');
499 }
500 _capturedVariableMapping[variable] = null;
501 }
502
503 void setCapturedVariableBoxField(Local variable,
504 BoxFieldElement boxField) {
505 assert(isCapturedVariable(variable));
506 _capturedVariableMapping[variable] = boxField;
507 }
508
509 BoxFieldElement getCapturedVariableBoxField(Local variable) {
510 return _capturedVariableMapping[variable];
511 }
512
513 void translateFunction(Element element, FunctionExpression node) {
514 // For constructors the [element] and the [:elements[node]:] may differ.
515 // The [:elements[node]:] always points to the generative-constructor
516 // element, whereas the [element] might be the constructor-body element.
517 visit(node); // [visitFunctionExpression] will call [visitInvokable].
518 // When variables need to be boxed their [_capturedVariableMapping] is
519 // updated, but we delay updating the similar freeVariableMapping in the
520 // closure datas that capture these variables.
521 // The closures don't have their fields (in the closure class) set, either.
522 updateClosures();
523 }
524
525 void translateLazyInitializer(VariableElement element,
526 VariableDefinitions node,
527 Expression initializer) {
528 visitInvokable(element, node, () { visit(initializer); });
529 updateClosures();
530 }
531
532 // This function runs through all of the existing closures and updates their
533 // free variables to the boxed value. It also adds the field-elements to the
534 // class representing the closure.
535 void updateClosures() {
536 for (Expression closure in closures) {
537 // The captured variables that need to be stored in a field of the closure
538 // class.
539 Set<Local> fieldCaptures = new Set<Local>();
540 Set<BoxLocal> boxes = new Set<BoxLocal>();
541 ClosureClassMap data = closureMappingCache[closure];
542 // We get a copy of the keys and iterate over it, to avoid modifications
543 // to the map while iterating over it.
544 Iterable<Local> freeVariables = data.freeVariables.toList();
545 freeVariables.forEach((Local fromElement) {
546 assert(data.isFreeVariable(fromElement));
547 assert(data.getFreeVariableElement(fromElement) == null);
548 assert(isCapturedVariable(fromElement));
549 BoxFieldElement boxFieldElement =
550 getCapturedVariableBoxField(fromElement);
551 if (boxFieldElement == null) {
552 assert(fromElement is! BoxLocal);
553 // The variable has not been boxed.
554 fieldCaptures.add(fromElement);
555 } else {
556 // A boxed element.
557 data.setFreeVariableBoxField(fromElement, boxFieldElement);
558 boxes.add(boxFieldElement.box);
559 }
560 });
561 ClosureClassElement closureClass = data.closureClassElement;
562 assert(closureClass != null ||
563 (fieldCaptures.isEmpty && boxes.isEmpty));
564
565 void addClosureField(Local local, String name) {
566 ClosureFieldElement closureField =
567 new ClosureFieldElement(name, local, closureClass);
568 closureClass.addField(closureField, compiler);
569 data.setLocalVariableForClosureField(closureField, local);
570 data.setFreeVariableClosureField(local, closureField);
571 }
572
573 // Add the box elements first so we get the same ordering.
574 // TODO(sra): What is the canonical order of multiple boxes?
575 for (BoxLocal capturedElement in boxes) {
576 addClosureField(capturedElement, capturedElement.name);
577 }
578
579 /// Comparator for locals. Position boxes before elements.
580 int compareLocals(a, b) {
581 if (a is Element && b is Element) {
582 return Elements.compareByPosition(a, b);
583 } else if (a is Element) {
584 return 1;
585 } else if (b is Element) {
586 return -1;
587 } else {
588 return a.name.compareTo(b.name);
589 }
590 }
591
592 for (Local capturedLocal in fieldCaptures.toList()..sort(compareLocals)) {
593 int id = closureFieldCounter++;
594 String name = namer.getClosureVariableName(capturedLocal.name, id);
595 addClosureField(capturedLocal, name);
596 }
597 closureClass.reverseBackendMembers();
598 }
599 }
600
601 void useLocal(Local variable) {
602 // If the element is not declared in the current function and the element
603 // is not the closure itself we need to mark the element as free variable.
604 // Note that the check on [insideClosure] is not just an
605 // optimization: factories have type parameters as function
606 // parameters, and type parameters are declared in the class, not
607 // the factory.
608 bool inCurrentContext(Local variable) {
609 return variable == executableContext ||
610 variable.executableContext == executableContext;
611 }
612
613 if (insideClosure && !inCurrentContext(variable)) {
614 closureData.addFreeVariable(variable);
615 } else if (inTryStatement) {
616 // Don't mark the this-element or a self-reference. This would complicate
617 // things in the builder.
618 // Note that nested (named) functions are immutable.
619 if (variable != closureData.thisLocal &&
620 variable != closureData.closureElement) {
621 // TODO(ngeoffray): only do this if the variable is mutated.
622 closureData.usedVariablesInTry.add(variable);
623 }
624 }
625 }
626
627 void useTypeVariableAsLocal(TypeVariableType typeVariable) {
628 useLocal(new TypeVariableLocal(typeVariable, outermostElement));
629 }
630
631 void declareLocal(LocalVariableElement element) {
632 scopeVariables.add(element);
633 }
634
635 void registerNeedsThis() {
636 if (closureData.thisLocal != null) {
637 useLocal(closureData.thisLocal);
638 }
639 }
640
641 visit(Node node) => node.accept(this);
642
643 visitNode(Node node) => node.visitChildren(this);
644
645 visitVariableDefinitions(VariableDefinitions node) {
646 if (node.type != null) {
647 visit(node.type);
648 }
649 for (Link<Node> link = node.definitions.nodes;
650 !link.isEmpty;
651 link = link.tail) {
652 Node definition = link.head;
653 LocalElement element = elements[definition];
654 assert(element != null);
655 if (!element.isInitializingFormal) {
656 declareLocal(element);
657 }
658 // We still need to visit the right-hand sides of the init-assignments.
659 // For SendSets don't visit the left again. Otherwise it would be marked
660 // as mutated.
661 if (definition is Send) {
662 Send assignment = definition;
663 Node arguments = assignment.argumentsNode;
664 if (arguments != null) {
665 visit(arguments);
666 }
667 } else {
668 visit(definition);
669 }
670 }
671 }
672
673 visitTypeAnnotation(TypeAnnotation node) {
674 MemberElement member = executableContext.memberContext;
675 DartType type = elements.getType(node);
676 // TODO(karlklose,johnniwinther): if the type is null, the annotation is
677 // from a parameter which has been analyzed before the method has been
678 // resolved and the result has been thrown away.
679 if (compiler.enableTypeAssertions && type != null &&
680 type.containsTypeVariables) {
681 if (insideClosure && member.isFactoryConstructor) {
682 // This is a closure in a factory constructor. Since there is no
683 // [:this:], we have to mark the type arguments as free variables to
684 // capture them in the closure.
685 type.forEachTypeVariable((TypeVariableType variable) {
686 useTypeVariableAsLocal(variable);
687 });
688 }
689 if (member.isInstanceMember && !member.isField) {
690 // In checked mode, using a type variable in a type annotation may lead
691 // to a runtime type check that needs to access the type argument and
692 // therefore the closure needs a this-element, if it is not in a field
693 // initializer; field initatializers are evaluated in a context where
694 // the type arguments are available in locals.
695 registerNeedsThis();
696 }
697 }
698 }
699
700 visitIdentifier(Identifier node) {
701 if (node.isThis()) {
702 registerNeedsThis();
703 } else {
704 Element element = elements[node];
705 if (element != null && element.isTypeVariable) {
706 if (outermostElement.isConstructor) {
707 TypeVariableElement typeVariable = element;
708 useTypeVariableAsLocal(typeVariable.type);
709 } else {
710 registerNeedsThis();
711 }
712 }
713 }
714 node.visitChildren(this);
715 }
716
717 visitSend(Send node) {
718 Element element = elements[node];
719 if (Elements.isLocal(element)) {
720 LocalElement localElement = element;
721 useLocal(localElement);
722 } else if (element != null && element.isTypeVariable) {
723 TypeVariableElement variable = element;
724 analyzeType(variable.type);
725 } else if (node.receiver == null &&
726 Elements.isInstanceSend(node, elements)) {
727 registerNeedsThis();
728 } else if (node.isSuperCall) {
729 registerNeedsThis();
730 } else if (node.isTypeTest || node.isTypeCast) {
731 TypeAnnotation annotation = node.typeAnnotationFromIsCheckOrCast;
732 DartType type = elements.getType(annotation);
733 analyzeType(type);
734 } else if (node.isTypeTest) {
735 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast);
736 analyzeType(type);
737 } else if (node.isTypeCast) {
738 DartType type = elements.getType(node.arguments.head);
739 analyzeType(type);
740 } else if (elements.isAssert(node) && !compiler.enableUserAssertions) {
741 return;
742 }
743 node.visitChildren(this);
744 }
745
746 visitSendSet(SendSet node) {
747 Element element = elements[node];
748 if (Elements.isLocal(element)) {
749 mutatedVariables.add(element);
750 if (compiler.enableTypeAssertions) {
751 TypedElement typedElement = element;
752 analyzeTypeVariables(typedElement.type);
753 }
754 }
755 super.visitSendSet(node);
756 }
757
758 visitNewExpression(NewExpression node) {
759 DartType type = elements.getType(node);
760 analyzeType(type);
761 node.visitChildren(this);
762 }
763
764 void analyzeTypeVariables(DartType type) {
765 type.forEachTypeVariable((TypeVariableType typeVariable) {
766 // Field initializers are inlined and access the type variable as
767 // normal parameters.
768 if (!outermostElement.isField &&
769 !outermostElement.isConstructor) {
770 registerNeedsThis();
771 } else {
772 useTypeVariableAsLocal(typeVariable);
773 }
774 });
775 }
776
777 void analyzeType(DartType type) {
778 // TODO(johnniwinther): Find out why this can be null.
779 if (type == null) return;
780 if (outermostElement.isClassMember &&
781 compiler.backend.classNeedsRti(outermostElement.enclosingClass)) {
782 if (outermostElement.isConstructor ||
783 outermostElement.isField) {
784 analyzeTypeVariables(type);
785 } else if (outermostElement.isInstanceMember) {
786 if (type.containsTypeVariables) {
787 registerNeedsThis();
788 }
789 }
790 }
791 }
792
793 // If variables that are declared in the [node] scope are captured and need
794 // to be boxed create a box-element and update the [capturingScopes] in the
795 // current [closureData].
796 // The boxed variables are updated in the [capturedVariableMapping].
797 void attachCapturedScopeVariables(Node node) {
798 BoxLocal box = null;
799 Map<LocalVariableElement, BoxFieldElement> scopeMapping =
800 new Map<LocalVariableElement, BoxFieldElement>();
801
802 void boxCapturedVariable(LocalVariableElement variable) {
803 if (isCapturedVariable(variable)) {
804 if (box == null) {
805 // TODO(floitsch): construct better box names.
806 String boxName =
807 namer.getClosureVariableName('box', closureFieldCounter++);
808 box = new BoxLocal(boxName, executableContext);
809 }
810 String elementName = variable.name;
811 String boxedName =
812 namer.getClosureVariableName(elementName, boxedFieldCounter++);
813 // TODO(kasperl): Should this be a FieldElement instead?
814 BoxFieldElement boxed = new BoxFieldElement(boxedName, variable, box);
815 // No need to rename the fields of a box, so we give them a native name
816 // right now.
817 boxed.setFixedBackendName(boxedName);
818 scopeMapping[variable] = boxed;
819 setCapturedVariableBoxField(variable, boxed);
820 }
821 }
822
823 for (LocalVariableElement variable in scopeVariables) {
824 // No need to box non-assignable elements.
825 if (!variable.isAssignable) continue;
826 if (!mutatedVariables.contains(variable)) continue;
827 boxCapturedVariable(variable);
828 }
829 if (!scopeMapping.isEmpty) {
830 ClosureScope scope = new ClosureScope(box, scopeMapping);
831 closureData.capturingScopes[node] = scope;
832 }
833 }
834
835 void inNewScope(Node node, Function action) {
836 List<LocalVariableElement> oldScopeVariables = scopeVariables;
837 scopeVariables = <LocalVariableElement>[];
838 action();
839 attachCapturedScopeVariables(node);
840 mutatedVariables.removeAll(scopeVariables);
841 scopeVariables = oldScopeVariables;
842 }
843
844 visitLoop(Loop node) {
845 inNewScope(node, () {
846 node.visitChildren(this);
847 });
848 }
849
850 visitFor(For node) {
851 visitLoop(node);
852 // See if we have declared loop variables that need to be boxed.
853 if (node.initializer == null) return;
854 VariableDefinitions definitions = node.initializer.asVariableDefinitions();
855 if (definitions == null) return;
856 ClosureScope scopeData = closureData.capturingScopes[node];
857 if (scopeData == null) return;
858 List<LocalVariableElement> result = <LocalVariableElement>[];
859 for (Link<Node> link = definitions.definitions.nodes;
860 !link.isEmpty;
861 link = link.tail) {
862 Node definition = link.head;
863 LocalVariableElement element = elements[definition];
864 if (isCapturedVariable(element)) {
865 result.add(element);
866 }
867 }
868 scopeData.boxedLoopVariables = result;
869 }
870
871 /** Returns a non-unique name for the given closure element. */
872 String computeClosureName(Element element) {
873 Link<String> parts = const Link<String>();
874 String ownName = element.name;
875 if (ownName == null || ownName == "") {
876 parts = parts.prepend("closure");
877 } else {
878 parts = parts.prepend(ownName);
879 }
880 for (Element enclosingElement = element.enclosingElement;
881 enclosingElement != null &&
882 (enclosingElement.kind == ElementKind.GENERATIVE_CONSTRUCTOR_BODY
883 || enclosingElement.kind == ElementKind.GENERATIVE_CONSTRUCTOR
884 || enclosingElement.kind == ElementKind.CLASS
885 || enclosingElement.kind == ElementKind.FUNCTION
886 || enclosingElement.kind == ElementKind.GETTER
887 || enclosingElement.kind == ElementKind.SETTER);
888 enclosingElement = enclosingElement.enclosingElement) {
889 // TODO(johnniwinther): Simplify computed names.
890 if (enclosingElement.isGenerativeConstructor ||
891 enclosingElement.isGenerativeConstructorBody ||
892 enclosingElement.isFactoryConstructor) {
893 parts = parts.prepend(
894 Elements.reconstructConstructorName(enclosingElement));
895 } else {
896 String surroundingName =
897 Elements.operatorNameToIdentifier(enclosingElement.name);
898 parts = parts.prepend(surroundingName);
899 }
900 // A generative constructors's parent is the class; the class name is
901 // already part of the generative constructor's name.
902 if (enclosingElement.kind == ElementKind.GENERATIVE_CONSTRUCTOR) break;
903 }
904 StringBuffer sb = new StringBuffer();
905 parts.printOn(sb, '_');
906 return sb.toString();
907 }
908
909 JavaScriptBackend get backend => compiler.backend;
910
911 ClosureClassMap globalizeClosure(FunctionExpression node,
912 LocalFunctionElement element) {
913 String closureName = computeClosureName(element);
914 ClosureClassElement globalizedElement = new ClosureClassElement(
915 node, closureName, compiler, element);
916 FunctionElement callElement =
917 new SynthesizedCallMethodElementX(Compiler.CALL_OPERATOR_NAME,
918 element,
919 globalizedElement);
920 backend.maybeMarkClosureAsNeededForReflection(globalizedElement, callElement , element);
921 MemberElement enclosing = element.memberContext;
922 enclosing.nestedClosures.add(callElement);
923 globalizedElement.addMember(callElement, compiler);
924 globalizedElement.computeAllClassMembers(compiler);
925 // The nested function's 'this' is the same as the one for the outer
926 // function. It could be [null] if we are inside a static method.
927 ThisLocal thisElement = closureData.thisLocal;
928
929 return new ClosureClassMap(element, globalizedElement,
930 callElement, thisElement);
931 }
932
933 void visitInvokable(ExecutableElement element,
934 Node node,
935 void visitChildren()) {
936 bool oldInsideClosure = insideClosure;
937 Element oldFunctionElement = executableContext;
938 ClosureClassMap oldClosureData = closureData;
939
940 insideClosure = outermostElement != null;
941 LocalFunctionElement closure;
942 executableContext = element;
943 if (insideClosure) {
944 closure = element;
945 closures.add(node);
946 closureData = globalizeClosure(node, closure);
947 } else {
948 outermostElement = element;
949 ThisLocal thisElement = null;
950 if (element.isInstanceMember || element.isGenerativeConstructor) {
951 thisElement = new ThisLocal(element);
952 }
953 closureData = new ClosureClassMap(null, null, null, thisElement);
954 }
955 closureMappingCache[node] = closureData;
956
957 inNewScope(node, () {
958 DartType type = element.type;
959 // If the method needs RTI, or checked mode is set, we need to
960 // escape the potential type variables used in that closure.
961 if (element is FunctionElement &&
962 (compiler.backend.methodNeedsRti(element) ||
963 compiler.enableTypeAssertions)) {
964 analyzeTypeVariables(type);
965 }
966
967 visitChildren();
968 });
969
970
971 ClosureClassMap savedClosureData = closureData;
972 bool savedInsideClosure = insideClosure;
973
974 // Restore old values.
975 insideClosure = oldInsideClosure;
976 closureData = oldClosureData;
977 executableContext = oldFunctionElement;
978
979 // Mark all free variables as captured and use them in the outer function.
980 Iterable<Local> freeVariables = savedClosureData.freeVariables;
981 assert(freeVariables.isEmpty || savedInsideClosure);
982 for (Local freeVariable in freeVariables) {
983 addCapturedVariable(node, freeVariable);
984 useLocal(freeVariable);
985 }
986 }
987
988 visitFunctionExpression(FunctionExpression node) {
989 Element element = elements[node];
990
991 if (element.isParameter) {
992 // TODO(ahe): This is a hack. This method should *not* call
993 // visitChildren.
994 return node.name.accept(this);
995 }
996
997 visitInvokable(element, node, () {
998 // TODO(ahe): This is problematic. The backend should not repeat
999 // the work of the resolver. It is the resolver's job to create
1000 // parameters, etc. Other phases should only visit statements.
1001 if (node.parameters != null) node.parameters.accept(this);
1002 if (node.initializers != null) node.initializers.accept(this);
1003 if (node.body != null) node.body.accept(this);
1004 });
1005 }
1006
1007 visitTryStatement(TryStatement node) {
1008 // TODO(ngeoffray): implement finer grain state.
1009 bool oldInTryStatement = inTryStatement;
1010 inTryStatement = true;
1011 node.visitChildren(this);
1012 inTryStatement = oldInTryStatement;
1013 }
1014 }
1015
1016 /// A type variable as a local variable.
1017 class TypeVariableLocal implements Local {
1018 final TypeVariableType typeVariable;
1019 final ExecutableElement executableContext;
1020
1021 TypeVariableLocal(this.typeVariable, this.executableContext);
1022
1023 String get name => typeVariable.name;
1024
1025 int get hashCode => typeVariable.hashCode;
1026
1027 bool operator ==(other) {
1028 if (other is! TypeVariableLocal) return false;
1029 return typeVariable == other.typeVariable;
1030 }
1031 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698