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

Unified Diff: pkg/kernel/lib/transformations/closure/converter.dart

Issue 2991853002: Fix duplicate context creation when closures appear in initializers. (Closed)
Patch Set: Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/kernel/lib/transformations/closure/converter.dart
diff --git a/pkg/kernel/lib/transformations/closure/converter.dart b/pkg/kernel/lib/transformations/closure/converter.dart
index 713ab38cdc6995e812fabb5dad3932f2fabd491d..d89cb843388febee2dc3dda254fb5c5983f3c182 100644
--- a/pkg/kernel/lib/transformations/closure/converter.dart
+++ b/pkg/kernel/lib/transformations/closure/converter.dart
@@ -28,6 +28,7 @@ import '../../ast.dart'
InvocationExpression,
Library,
LocalInitializer,
+ RedirectingInitializer,
Member,
MethodInvocation,
Name,
@@ -61,7 +62,7 @@ import '../../type_algebra.dart' show substitute;
import 'clone_without_body.dart' show CloneWithoutBody;
-import 'context.dart' show Context, NoContext;
+import 'context.dart' show Context, NoContext, LocalContext;
import 'info.dart' show ClosureInfo;
@@ -205,20 +206,80 @@ class ClosureConverter extends Transformer {
transformList(node.initializers, this, node);
node.initializers.insertAll(0, initRewriter.prefix);
- context = rewriter = null;
+ rewriter = null;
}
// Transform constructor body.
FunctionNode function = node.function;
if (function.body != null && function.body is! EmptyStatement) {
- setupContextForFunctionBody(function);
+ // If we created a context for the initializers, we need to re-use that
+ // context in the body of the function. Unfortunately, the context is
+ // declared in a local initializer and local initializers aren't visible
+ // in the body of the constructor. To work around this issue, we move the
+ // body into a new constructor and make this constructor redirect to that
+ // one, passing the context as an argument to the new constructor.
+ var movingCtor = context != null && context is! NoContext;
+
+ setupContextForFunctionBody(function, context);
VariableDeclaration self = thisAccess[currentMemberFunction];
if (self != null) {
context.extend(self, new ThisExpression());
}
node.function.accept(this);
- resetContext();
+
+ if (movingCtor) {
+ var newCtorName = new Name("${node.name.name}#redir");
+ var newCtor = new Constructor(node.function, name: newCtorName);
+ newClassMembers.add(newCtor);
+
+ for (var i = 1; i < node.initializers.length; ++i)
Dmitry Stefantsov 2017/07/28 12:36:16 I guess here we make some assumptions about the st
sjindel 2017/07/31 12:06:20 I rewrote it to look for the context's initializer
+ newCtor.initializers.add(node.initializers[i]);
+
+ node.initializers = node.initializers.take(1).toList();
+
+ var cv = new CloneVisitor();
+ var oldCtorParams = function.positionalParameters
+ .map(cv.visitVariableDeclaration)
+ .toList();
+ var oldCtorNamedParams =
+ function.namedParameters.map(cv.visitVariableDeclaration).toList();
+
+ function.positionalParameters.addAll(function.namedParameters);
+ function.namedParameters = [];
+
+ var args = <Expression>[];
+ assert(function.typeParameters.length == 0);
Dmitry Stefantsov 2017/07/28 12:36:15 I think we can't supply additional type arguments
sjindel 2017/07/31 12:06:20 The comment above the [Constructor] class confirms
+ args.addAll(oldCtorParams.map((decl) => new VariableGet(decl)));
+ args.addAll(oldCtorNamedParams.map((decl) => new VariableGet(decl)));
+
+ node.function = new FunctionNode(new EmptyStatement(),
+ typeParameters: [],
+ positionalParameters: oldCtorParams,
+ namedParameters: oldCtorNamedParams,
+ requiredParameterCount: function.requiredParameterCount,
+ returnType: function.returnType,
+ asyncMarker: function.asyncMarker,
+ dartAsyncMarker: function.dartAsyncMarker);
+ node.function.parent = node;
+
+ var contextDecl = (context as LocalContext).self;
+ var oldCtorDecl =
+ (new CloneVisitor()).visitVariableDeclaration(contextDecl);
Dmitry Stefantsov 2017/07/28 12:36:16 Can we use [cv] from above here?
sjindel 2017/07/31 12:06:20 Done.
+ contextDecl.initializer = null;
+ function.positionalParameters.add(contextDecl);
+ function.requiredParameterCount++;
+
+ var contextInit = node.initializers[0] as LocalInitializer;
Dmitry Stefantsov 2017/07/28 12:36:16 Same note about treating 0-th initializer as speci
sjindel 2017/07/31 12:06:20 Done.
+ contextInit.variable = oldCtorDecl;
+ oldCtorDecl.parent = contextInit;
+
+ args.add(new VariableGet(oldCtorDecl));
+ var redirInit =
+ new RedirectingInitializer(newCtor, new Arguments(args));
+ node.initializers.add(redirInit);
+ }
}
+ resetContext();
return node;
}
@@ -380,7 +441,8 @@ class ClosureConverter extends Transformer {
return node;
}
- void setupContextForFunctionBody(FunctionNode function) {
+ void setupContextForFunctionBody(FunctionNode function,
+ [Context reuse = null]) {
Dmitry Stefantsov 2017/07/28 12:36:16 Why do we need this optional parameter here?
sjindel 2017/07/31 12:06:20 So we don't reset the function body's context when
Dmitry Stefantsov 2017/07/31 15:05:35 I asked this question because [reuse] wasn't menti
sjindel 2017/07/31 15:32:16 I see, I suppose it wasn't actually needed then.
Statement body = function.body;
assert(body != null);
currentMemberFunction = function;
@@ -388,7 +450,7 @@ class ClosureConverter extends Transformer {
rewriter = makeRewriterForBody(function);
// Start with no context. This happens after setting up _currentBlock
// so statements can be emitted into _currentBlock if necessary.
- context = new NoContext(this);
+ if (context == null) context = new NoContext(this);
}
void resetContext() {
@@ -412,12 +474,17 @@ class ClosureConverter extends Transformer {
transformList(node.typeParameters, this, node);
// TODO: Can parameters contain initializers (e.g., for optional ones) that
// need to be closure converted?
- node.positionalParameters
- .where(capturedVariables.contains)
- .forEach(extendContextWith);
- node.namedParameters
- .where(capturedVariables.contains)
- .forEach(extendContextWith);
+ //
+ // Answer: no, initializers for optional parameters must be compile-time
+ // constants, which excludes closures.
Dmitry Stefantsov 2017/07/28 12:36:15 I would suggest rewriting this comment, so that it
sjindel 2017/07/31 12:06:20 Done.
+ if (context is NoContext) {
Dmitry Stefantsov 2017/07/28 12:36:15 Why do we need this check here?
sjindel 2017/07/31 12:06:20 We don't want to re-visit the function's parameter
Dmitry Stefantsov 2017/07/31 15:05:35 Yes, I meant that if this check makes distinction
sjindel 2017/07/31 15:32:16 The case is removed.
+ node.positionalParameters
+ .where(capturedVariables.contains)
+ .forEach(extendContextWith);
+ node.namedParameters
+ .where(capturedVariables.contains)
+ .forEach(extendContextWith);
+ }
assert(node.body != null);
node.body = node.body.accept(this);
node.body.parent = node;

Powered by Google App Engine
This is Rietveld 408576698