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

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

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

Powered by Google App Engine
This is Rietveld 408576698