OLD | NEW |
---|---|
(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 } | |
OLD | NEW |