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

Side by Side Diff: pkg/kernel/lib/transformations/closure/converter.dart

Issue 2561723003: Merge kernel closure conversion into the Dart SDK (Closed)
Patch Set: Remove path constraint Created 4 years 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
OLDNEW
(Empty)
1 // Copyright (c) 2016, 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 kernel.transformations.closure.converter;
6
7 import '../../ast.dart'
8 show
9 Arguments,
10 Block,
11 Catch,
12 Class,
13 Constructor,
14 ConstructorInvocation,
15 DartType,
16 EmptyStatement,
17 Expression,
18 ExpressionStatement,
19 Field,
20 FieldInitializer,
21 ForInStatement,
22 ForStatement,
23 FunctionDeclaration,
24 FunctionExpression,
25 FunctionNode,
26 InferredValue,
27 Initializer,
28 InvalidExpression,
29 InvocationExpression,
30 Library,
31 LocalInitializer,
32 Member,
33 MethodInvocation,
34 Name,
35 NamedExpression,
36 NullLiteral,
37 Procedure,
38 ProcedureKind,
39 PropertyGet,
40 ReturnStatement,
41 Statement,
42 StaticGet,
43 StaticInvocation,
44 StringLiteral,
45 Supertype,
46 ThisExpression,
47 Transformer,
48 TreeNode,
49 TypeParameter,
50 TypeParameterType,
51 VariableDeclaration,
52 VariableGet,
53 VariableSet,
54 transformList;
55
56 import '../../frontend/accessors.dart' show VariableAccessor;
57
58 import '../../clone.dart' show CloneVisitor;
59
60 import '../../core_types.dart' show CoreTypes;
61
62 import '../../type_algebra.dart' show substitute;
63
64 import 'clone_without_body.dart' show CloneWithoutBody;
65
66 import 'context.dart' show Context, NoContext;
67
68 import 'info.dart' show ClosureInfo;
69
70 class ClosureConverter extends Transformer {
71 final CoreTypes coreTypes;
72 final Class contextClass;
73 final Set<VariableDeclaration> capturedVariables;
74 final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables;
75 final Map<FunctionNode, VariableDeclaration> thisAccess;
76 final Map<FunctionNode, String> localNames;
77
78 /// Records place-holders for cloning contexts. See [visitForStatement].
79 final Set<InvalidExpression> contextClonePlaceHolders =
80 new Set<InvalidExpression>();
81
82 /// Maps the names of all instance methods that may be torn off (aka
83 /// implicitly closurized) to `${name.name}#get`.
84 final Map<Name, Name> tearOffGetterNames;
85
86 final CloneVisitor cloner = new CloneWithoutBody();
87
88 /// New members to add to [currentLibrary] after it has been
89 /// transformed. These members will not be transformed themselves.
90 final List<TreeNode> newLibraryMembers = <TreeNode>[];
91
92 /// New members to add to [currentClass] after it has been transformed. These
93 /// members will not be transformed themselves.
94 final List<Member> newClassMembers = <Member>[];
95
96 Library currentLibrary;
97
98 Class currentClass;
99
100 Member currentMember;
101
102 FunctionNode currentMemberFunction;
103
104 FunctionNode currentFunction;
105
106 Block _currentBlock;
107
108 int _insertionIndex = 0;
109
110 Context context;
111
112 /// Maps original type variable (aka type parameter) to a hoisted type
113 /// variable type.
114 ///
115 /// For example, consider:
116 ///
117 /// class C<T> {
118 /// f() => (x) => x is T;
119 /// }
120 ///
121 /// This is currently converted to:
122 ///
123 /// class C<T> {
124 /// f() => new Closure#0<T>();
125 /// }
126 /// class Closure#0<T_> implements Function {
127 /// call(x) => x is T_;
128 /// }
129 ///
130 /// In this example, `typeSubstitution[T].parameter == T_` when transforming
131 /// the closure in `f`.
132 Map<TypeParameter, DartType> typeSubstitution =
133 const <TypeParameter, DartType>{};
134
135 ClosureConverter(this.coreTypes, ClosureInfo info, this.contextClass)
136 : this.capturedVariables = info.variables,
137 this.capturedTypeVariables = info.typeVariables,
138 this.thisAccess = info.thisAccess,
139 this.localNames = info.localNames,
140 this.tearOffGetterNames = info.tearOffGetterNames;
141
142 bool get isOuterMostContext {
143 return currentFunction == null || currentMemberFunction == currentFunction;
144 }
145
146 String get currentFileUri {
147 if (currentMember is Constructor) return currentClass.fileUri;
148 if (currentMember is Field) return (currentMember as Field).fileUri;
149 if (currentMember is Procedure) return (currentMember as Procedure).fileUri;
150 throw "No file uri";
151 }
152
153 void insert(Statement statement) {
154 _currentBlock.statements.insert(_insertionIndex++, statement);
155 statement.parent = _currentBlock;
156 }
157
158 TreeNode saveContext(TreeNode f()) {
159 Block savedBlock = _currentBlock;
160 int savedIndex = _insertionIndex;
161 Context savedContext = context;
162 try {
163 return f();
164 } finally {
165 _currentBlock = savedBlock;
166 _insertionIndex = savedIndex;
167 context = savedContext;
168 }
169 }
170
171 TreeNode visitLibrary(Library node) {
172 assert(newLibraryMembers.isEmpty);
173 if (node == contextClass.enclosingLibrary) return node;
174 currentLibrary = node;
175 node = super.visitLibrary(node);
176 for (TreeNode member in newLibraryMembers) {
177 if (member is Class) {
178 node.addClass(member);
179 } else {
180 node.addMember(member);
181 }
182 }
183 newLibraryMembers.clear();
184 currentLibrary = null;
185 return node;
186 }
187
188 TreeNode visitClass(Class node) {
189 assert(newClassMembers.isEmpty);
190 currentClass = node;
191 node = super.visitClass(node);
192 newClassMembers.forEach(node.addMember);
193 newClassMembers.clear();
194 currentClass = null;
195 return node;
196 }
197
198 TreeNode visitConstructor(Constructor node) {
199 // TODO(ahe): Convert closures in constructors as well.
200 return node;
201 }
202
203 Expression handleLocalFunction(FunctionNode function) {
204 FunctionNode enclosingFunction = currentFunction;
205 Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution;
206 currentFunction = function;
207 Statement body = function.body;
208 assert(body != null);
209
210 if (body is Block) {
211 _currentBlock = body;
212 } else {
213 _currentBlock = new Block(<Statement>[body]);
214 function.body = body.parent = _currentBlock;
215 }
216 _insertionIndex = 0;
217
218 VariableDeclaration contextVariable = new VariableDeclaration(
219 "#contextParameter",
220 type: contextClass.rawType,
221 isFinal: true);
222 Context parent = context;
223 context = context.toNestedContext(new VariableAccessor(contextVariable));
224
225 Set<TypeParameter> captured = capturedTypeVariables[currentFunction];
226 if (captured != null) {
227 typeSubstitution = copyTypeVariables(captured);
228 } else {
229 typeSubstitution = const <TypeParameter, DartType>{};
230 }
231
232 function.transformChildren(this);
233
234 Expression result = addClosure(function, contextVariable, parent.expression,
235 typeSubstitution, enclosingTypeSubstitution);
236 currentFunction = enclosingFunction;
237 typeSubstitution = enclosingTypeSubstitution;
238 return result;
239 }
240
241 TreeNode visitFunctionDeclaration(FunctionDeclaration node) {
242 /// Is this closure itself captured by a closure?
243 bool isCaptured = capturedVariables.contains(node.variable);
244 if (isCaptured) {
245 context.extend(node.variable, new InvalidExpression());
246 }
247 Context parent = context;
248 return saveContext(() {
249 Expression expression = handleLocalFunction(node.function);
250
251 if (isCaptured) {
252 parent.update(node.variable, expression);
253 return null;
254 } else {
255 node.variable.initializer = expression;
256 expression.parent = node.variable;
257 return node.variable;
258 }
259 });
260 }
261
262 TreeNode visitFunctionExpression(FunctionExpression node) => saveContext(() {
263 return handleLocalFunction(node.function);
264 });
265
266 /// Add a new class to the current library that looks like this:
267 ///
268 /// class Closure#0 extends core::Object implements core::Function {
269 /// field _in::Context context;
270 /// constructor •(final _in::Context #t1) → dynamic
271 /// : self::Closure 0::context = #t1
272 /// ;
273 /// method call(/* The parameters of [function] */) → dynamic {
274 /// /// #t2 is [contextVariable].
275 /// final _in::Context #t2 = this.{self::Closure#0::context};
276 /// /* The body of [function]. */
277 /// }
278 /// }
279 ///
280 /// Returns a constructor call to invoke the above constructor.
281 ///
282 /// TODO(ahe): We shouldn't create a class for each closure. Instead we turn
283 /// [function] into a top-level function and use the Dart VM's mechnism for
284 /// closures.
285 Expression addClosure(
286 FunctionNode function,
287 VariableDeclaration contextVariable,
288 Expression accessContext,
289 Map<TypeParameter, DartType> substitution,
290 Map<TypeParameter, DartType> enclosingTypeSubstitution) {
291 Field contextField = new Field(
292 // TODO(ahe): Rename to #context.
293 new Name("context"),
294 type: contextClass.rawType,
295 fileUri: currentFileUri);
296 Class closureClass = createClosureClass(function,
297 fields: [contextField], substitution: substitution);
298 closureClass.addMember(new Procedure(
299 new Name("call"), ProcedureKind.Method, function,
300 fileUri: currentFileUri));
301 newLibraryMembers.add(closureClass);
302 Statement note = new ExpressionStatement(
303 new StringLiteral("This is a temporary solution. "
304 "In the VM, this will become an additional parameter."));
305 List<Statement> statements = <Statement>[note, contextVariable];
306 Statement body = function.body;
307 if (body is Block) {
308 statements.addAll(body.statements);
309 } else {
310 statements.add(body);
311 }
312 function.body = new Block(statements);
313 function.body.parent = function;
314 contextVariable.initializer =
315 new PropertyGet(new ThisExpression(), contextField.name, contextField);
316 contextVariable.initializer.parent = contextVariable;
317 return new ConstructorInvocation(
318 closureClass.constructors.single,
319 new Arguments(<Expression>[accessContext], types:
320 new List<DartType>.from(substitution.keys.map((TypeParameter t) {
321 return substitute(
322 new TypeParameterType(t), enclosingTypeSubstitution);
323 }))));
324 }
325
326 TreeNode visitField(Field node) {
327 currentMember = node;
328 context = new NoContext(this);
329 if (node.isInstanceMember) {
330 Name tearOffName = tearOffGetterNames[node.name];
331 if (tearOffName != null) {
332 // TODO(ahe): If we rewrite setters, we can rename the field to avoid
333 // an indirection in most cases.
334 addFieldForwarder(tearOffName, node);
335 }
336 }
337 node = super.visitField(node);
338 context = null;
339 currentMember = null;
340 return node;
341 }
342
343 TreeNode visitProcedure(Procedure node) {
344 currentMember = node;
345 assert(_currentBlock == null);
346 assert(_insertionIndex == 0);
347 assert(context == null);
348
349 Statement body = node.function.body;
350
351 if (node.isInstanceMember) {
352 Name tearOffName = tearOffGetterNames[node.name];
353 if (tearOffName != null) {
354 if (node.isGetter) {
355 // We rename the getter to avoid an indirection in most cases.
356 Name oldName = node.name;
357 node.name = tearOffName;
358 addGetterForwarder(oldName, node);
359 } else if (node.kind == ProcedureKind.Method) {
360 addTearOffGetter(tearOffName, node);
361 }
362 }
363 }
364
365 if (body == null) return node;
366
367 currentMemberFunction = node.function;
368
369 // Ensure that the body is a block which becomes the current block.
370 if (body is Block) {
371 _currentBlock = body;
372 } else {
373 _currentBlock = new Block(<Statement>[body]);
374 node.function.body = body.parent = _currentBlock;
375 }
376 _insertionIndex = 0;
377
378 // Start with no context. This happens after setting up _currentBlock
379 // so statements can be emitted into _currentBlock if necessary.
380 context = new NoContext(this);
381
382 VariableDeclaration self = thisAccess[currentMemberFunction];
383 if (self != null) {
384 context.extend(self, new ThisExpression());
385 }
386
387 node.transformChildren(this);
388
389 _currentBlock = null;
390 _insertionIndex = 0;
391 context = null;
392 currentMemberFunction = null;
393 currentMember = null;
394 return node;
395 }
396
397 TreeNode visitLocalInitializer(LocalInitializer node) {
398 assert(!capturedVariables.contains(node.variable));
399 node.transformChildren(this);
400 return node;
401 }
402
403 TreeNode visitFunctionNode(FunctionNode node) {
404 transformList(node.typeParameters, this, node);
405
406 void extend(VariableDeclaration parameter) {
407 context.extend(parameter, new VariableGet(parameter));
408 }
409
410 // TODO: Can parameters contain initializers (e.g., for optional ones) that
411 // need to be closure converted?
412 node.positionalParameters.where(capturedVariables.contains).forEach(extend);
413 node.namedParameters.where(capturedVariables.contains).forEach(extend);
414
415 assert(node.body != null);
416 node.body = node.body.accept(this);
417 node.body.parent = node;
418 return node;
419 }
420
421 TreeNode visitBlock(Block node) => saveContext(() {
422 if (_currentBlock != node) {
423 _currentBlock = node;
424 _insertionIndex = 0;
425 }
426
427 while (_insertionIndex < _currentBlock.statements.length) {
428 assert(_currentBlock == node);
429
430 var original = _currentBlock.statements[_insertionIndex];
431 var transformed = original.accept(this);
432 assert(_currentBlock.statements[_insertionIndex] == original);
433 if (transformed == null) {
434 _currentBlock.statements.removeAt(_insertionIndex);
435 } else {
436 _currentBlock.statements[_insertionIndex++] = transformed;
437 transformed.parent = _currentBlock;
438 }
439 }
440
441 return node;
442 });
443
444 TreeNode visitVariableDeclaration(VariableDeclaration node) {
445 node.transformChildren(this);
446
447 if (!capturedVariables.contains(node)) return node;
448 context.extend(node, node.initializer ?? new NullLiteral());
449
450 if (node.parent == currentFunction) return node;
451 if (node.parent is Block) {
452 // When returning null, the parent block will remove this node from its
453 // list of statements.
454 // TODO(ahe): I'd like to avoid testing on the parent pointer.
455 return null;
456 }
457 throw "Unexpected parent for $node: ${node.parent.parent}";
458 }
459
460 TreeNode visitVariableGet(VariableGet node) {
461 return capturedVariables.contains(node.variable)
462 ? context.lookup(node.variable)
463 : node;
464 }
465
466 TreeNode visitVariableSet(VariableSet node) {
467 node.transformChildren(this);
468
469 return capturedVariables.contains(node.variable)
470 ? context.assign(node.variable, node.value,
471 voidContext: isInVoidContext(node))
472 : node;
473 }
474
475 bool isInVoidContext(Expression node) {
476 TreeNode parent = node.parent;
477 return parent is ExpressionStatement ||
478 parent is ForStatement && parent.condition != node;
479 }
480
481 DartType visitDartType(DartType node) {
482 return substitute(node, typeSubstitution);
483 }
484
485 VariableDeclaration getReplacementLoopVariable(VariableDeclaration variable) {
486 VariableDeclaration newVariable = new VariableDeclaration(variable.name,
487 initializer: variable.initializer,
488 type: variable.type)..flags = variable.flags;
489 variable.initializer = new VariableGet(newVariable);
490 variable.initializer.parent = variable;
491 return newVariable;
492 }
493
494 Expression cloneContext() {
495 InvalidExpression placeHolder = new InvalidExpression();
496 contextClonePlaceHolders.add(placeHolder);
497 return placeHolder;
498 }
499
500 TreeNode visitInvalidExpression(InvalidExpression node) {
501 return contextClonePlaceHolders.remove(node) ? context.clone() : node;
502 }
503
504 TreeNode visitForStatement(ForStatement node) {
505 if (node.variables.any(capturedVariables.contains)) {
506 // In Dart, loop variables are new variables on each iteration of the
507 // loop. This is only observable when a loop variable is captured by a
508 // closure, which is the situation we're in here. So we transform the
509 // loop.
510 //
511 // Consider the following example, where `x` is `node.variables.first`,
512 // and `#t1` is a temporary variable:
513 //
514 // for (var x = 0; x < 10; x++) body;
515 //
516 // This is transformed to:
517 //
518 // {
519 // var x = 0;
520 // for (; x < 10; clone-context, x++) body;
521 // }
522 //
523 // `clone-context` is a place-holder that will later be replaced by an
524 // expression that clones the current closure context (see
525 // [visitInvalidExpression]).
526 return saveContext(() {
527 context = context.toNestedContext();
528 List<Statement> statements = <Statement>[];
529 statements.addAll(node.variables);
530 statements.add(node);
531 node.variables.clear();
532 node.updates.insert(0, cloneContext());
533 _currentBlock = new Block(statements);
534 _insertionIndex = 0;
535 return _currentBlock.accept(this);
536 });
537 }
538 return super.visitForStatement(node);
539 }
540
541 TreeNode visitForInStatement(ForInStatement node) {
542 if (capturedVariables.contains(node.variable)) {
543 // In Dart, loop variables are new variables on each iteration of the
544 // loop. This is only observable when the loop variable is captured by a
545 // closure, so we need to transform the for-in loop when `node.variable`
546 // is captured.
547 //
548 // Consider the following example, where `x` is `node.variable`, and
549 // `#t1` is a temporary variable:
550 //
551 // for (var x in expr) body;
552 //
553 // Notice that we can assume that `x` doesn't have an initializer based
554 // on invariants in the Kernel AST. This is transformed to:
555 //
556 // for (var #t1 in expr) { var x = #t1; body; }
557 //
558 // After this, we call super to apply the normal closure conversion to
559 // the transformed for-in loop.
560 VariableDeclaration variable = node.variable;
561 VariableDeclaration newVariable = getReplacementLoopVariable(variable);
562 node.variable = newVariable;
563 newVariable.parent = node;
564 node.body = new Block(<Statement>[variable, node.body]);
565 node.body.parent = node;
566 }
567 return super.visitForInStatement(node);
568 }
569
570 TreeNode visitThisExpression(ThisExpression node) {
571 return isOuterMostContext
572 ? node
573 : context.lookup(thisAccess[currentMemberFunction]);
574 }
575
576 TreeNode visitStaticGet(StaticGet node) {
577 Member target = node.target;
578 if (target is Procedure && target.kind == ProcedureKind.Method) {
579 Expression expression = getTearOffExpression(node.target);
580 expression.transformChildren(this);
581 return expression;
582 }
583 return super.visitStaticGet(node);
584 }
585
586 TreeNode visitPropertyGet(PropertyGet node) {
587 Name tearOffName = tearOffGetterNames[node.name];
588 if (tearOffName != null) {
589 node.name = tearOffName;
590 }
591 return super.visitPropertyGet(node);
592 }
593
594 TreeNode visitCatch(Catch node) {
595 VariableDeclaration exception = node.exception;
596 VariableDeclaration stackTrace = node.stackTrace;
597 if (stackTrace != null && capturedVariables.contains(stackTrace)) {
598 Block block = node.body = ensureBlock(node.body);
599 block.parent = node;
600 node.stackTrace = new VariableDeclaration(null);
601 node.stackTrace.parent = node;
602 stackTrace.initializer = new VariableGet(node.stackTrace);
603 block.statements.insert(0, stackTrace);
604 stackTrace.parent = block;
605 }
606 if (exception != null && capturedVariables.contains(exception)) {
607 Block block = node.body = ensureBlock(node.body);
608 block.parent = node;
609 node.exception = new VariableDeclaration(null);
610 node.exception.parent = node;
611 exception.initializer = new VariableGet(node.exception);
612 block.statements.insert(0, exception);
613 exception.parent = block;
614 }
615 return super.visitCatch(node);
616 }
617
618 Block ensureBlock(Statement statement) {
619 return statement is Block ? statement : new Block(<Statement>[statement]);
620 }
621
622 /// Creates a closure that will invoke [procedure] and return an expression
623 /// that instantiates that closure.
624 Expression getTearOffExpression(Procedure procedure) {
625 Map<TypeParameter, DartType> substitution = procedure.isInstanceMember
626 // Note: we do not attempt to avoid copying type variables that aren't
627 // used in the signature of [procedure]. It might be more economical to
628 // only copy type variables that are used. However, we assume that
629 // passing type arguments that match the enclosing class' type
630 // variables will be handled most efficiently.
631 ? copyTypeVariables(procedure.enclosingClass.typeParameters)
632 : const <TypeParameter, DartType>{};
633 Expression receiver = null;
634 List<Field> fields = null;
635 if (procedure.isInstanceMember) {
636 // TODO(ahe): Rename to #self.
637 Field self = new Field(new Name("self"), fileUri: currentFileUri);
638 self.type = substitute(procedure.enclosingClass.thisType, substitution);
639 fields = <Field>[self];
640 receiver = new PropertyGet(new ThisExpression(), self.name, self);
641 }
642 Class closureClass = createClosureClass(procedure.function,
643 fields: fields, substitution: substitution);
644 closureClass.addMember(new Procedure(new Name("call"), ProcedureKind.Method,
645 forwardFunction(procedure, receiver, substitution),
646 fileUri: currentFileUri));
647 newLibraryMembers.add(closureClass);
648 Arguments constructorArguments = procedure.isInstanceMember
649 ? new Arguments(<Expression>[new ThisExpression()])
650 : new Arguments.empty();
651 if (substitution.isNotEmpty) {
652 constructorArguments.types
653 .addAll(procedure.enclosingClass.thisType.typeArguments);
654 }
655 return new ConstructorInvocation(
656 closureClass.constructors.single, constructorArguments);
657 }
658
659 /// Creates a function that has the same signature as `procedure.function`
660 /// and which forwards all arguments to `procedure`.
661 FunctionNode forwardFunction(Procedure procedure, Expression receiver,
662 Map<TypeParameter, DartType> substitution) {
663 CloneVisitor cloner = substitution.isEmpty
664 ? this.cloner
665 : new CloneWithoutBody(typeSubstitution: substitution);
666 FunctionNode function = procedure.function;
667 List<TypeParameter> typeParameters =
668 function.typeParameters.map(cloner.clone).toList();
669 List<VariableDeclaration> positionalParameters =
670 function.positionalParameters.map(cloner.clone).toList();
671 List<VariableDeclaration> namedParameters =
672 function.namedParameters.map(cloner.clone).toList();
673 // TODO(ahe): Clone or copy inferredReturnValue?
674 InferredValue inferredReturnValue = null;
675
676 List<DartType> types = typeParameters
677 .map((TypeParameter parameter) => new TypeParameterType(parameter))
678 .toList();
679 List<Expression> positional = positionalParameters
680 .map((VariableDeclaration parameter) => new VariableGet(parameter))
681 .toList();
682 List<NamedExpression> named =
683 namedParameters.map((VariableDeclaration parameter) {
684 return new NamedExpression(parameter.name, new VariableGet(parameter));
685 }).toList();
686
687 Arguments arguments = new Arguments(positional, types: types, named: named);
688 InvocationExpression invocation = procedure.isInstanceMember
689 ? new MethodInvocation(receiver, procedure.name, arguments, procedure)
690 : new StaticInvocation(procedure, arguments);
691 return new FunctionNode(new ReturnStatement(invocation),
692 typeParameters: typeParameters,
693 positionalParameters: positionalParameters,
694 namedParameters: namedParameters,
695 requiredParameterCount: function.requiredParameterCount,
696 returnType: substitute(function.returnType, substitution),
697 inferredReturnValue: inferredReturnValue);
698 }
699
700 /// Creates copies of the type variables in [original] and returns a
701 /// substitution that can be passed to [substitute] to substitute all uses of
702 /// [original] with their copies.
703 Map<TypeParameter, DartType> copyTypeVariables(
704 Iterable<TypeParameter> original) {
705 if (original.isEmpty) return const <TypeParameter, DartType>{};
706 Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{};
707 for (TypeParameter t in original) {
708 substitution[t] = new TypeParameterType(new TypeParameter(t.name));
709 }
710 substitution.forEach((TypeParameter t, DartType copy) {
711 if (copy is TypeParameterType) {
712 copy.parameter.bound = substitute(t.bound, substitution);
713 }
714 });
715 return substitution;
716 }
717
718 Class createClosureClass(FunctionNode function,
719 {List<Field> fields, Map<TypeParameter, DartType> substitution}) {
720 List<TypeParameter> typeParameters = new List<TypeParameter>.from(
721 substitution.values
722 .map((DartType t) => (t as TypeParameterType).parameter));
723 Class closureClass = new Class(
724 name: 'Closure#${localNames[function]}',
725 supertype: new Supertype(coreTypes.objectClass, const <DartType>[]),
726 typeParameters: typeParameters,
727 implementedTypes: <Supertype>[
728 new Supertype(coreTypes.functionClass, const <DartType>[])
729 ],
730 fileUri: currentFileUri);
731 addClosureClassNote(closureClass);
732
733 List<VariableDeclaration> parameters = <VariableDeclaration>[];
734 List<Initializer> initializers = <Initializer>[];
735 for (Field field in fields ?? const <Field>[]) {
736 closureClass.addMember(field);
737 VariableDeclaration parameter = new VariableDeclaration(field.name.name,
738 type: field.type, isFinal: true);
739 parameters.add(parameter);
740 initializers.add(new FieldInitializer(field, new VariableGet(parameter)));
741 }
742
743 closureClass.addMember(new Constructor(
744 new FunctionNode(new EmptyStatement(),
745 positionalParameters: parameters),
746 name: new Name(""),
747 initializers: initializers));
748
749 return closureClass;
750 }
751
752 Statement forwardToThisProperty(Member node) {
753 assert(node is Field || (node is Procedure && node.isGetter));
754 return new ReturnStatement(
755 new PropertyGet(new ThisExpression(), node.name, node));
756 }
757
758 void addFieldForwarder(Name name, Field field) {
759 newClassMembers.add(new Procedure(name, ProcedureKind.Getter,
760 new FunctionNode(forwardToThisProperty(field)),
761 fileUri: currentFileUri));
762 }
763
764 Procedure copyWithBody(Procedure procedure, Statement body) {
765 Procedure copy = cloner.clone(procedure);
766 copy.function.body = body;
767 copy.function.body.parent = copy.function;
768 return copy;
769 }
770
771 void addGetterForwarder(Name name, Procedure getter) {
772 assert(getter.isGetter);
773 newClassMembers
774 .add(copyWithBody(getter, forwardToThisProperty(getter))..name = name);
775 }
776
777 void addTearOffGetter(Name name, Procedure procedure) {
778 newClassMembers.add(new Procedure(name, ProcedureKind.Getter,
779 new FunctionNode(new ReturnStatement(getTearOffExpression(procedure))),
780 fileUri: currentFileUri));
781 }
782
783 // TODO(ahe): Remove this method when we don't generate closure classes
784 // anymore.
785 void addClosureClassNote(Class closureClass) {
786 closureClass.addMember(new Field(new Name("note"),
787 type: coreTypes.stringClass.rawType,
788 initializer: new StringLiteral(
789 "This is temporary. The VM doesn't need closure classes."),
790 fileUri: currentFileUri));
791 }
792 }
OLDNEW
« no previous file with comments | « pkg/kernel/lib/transformations/closure/context.dart ('k') | pkg/kernel/lib/transformations/closure/info.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698