Chromium Code Reviews| 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; |