| 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 91a36dfc5e17e4933a479a8f6bb807851dedf391..325d9e54abdc5fb050024fc2b00409feaa271f8d 100644
|
| --- a/pkg/kernel/lib/transformations/closure/converter.dart
|
| +++ b/pkg/kernel/lib/transformations/closure/converter.dart
|
| @@ -27,6 +27,7 @@ import '../../ast.dart'
|
| Initializer,
|
| InvalidExpression,
|
| InvocationExpression,
|
| + Let,
|
| Library,
|
| LocalInitializer,
|
| Member,
|
| @@ -67,6 +68,8 @@ import 'context.dart' show Context, NoContext;
|
|
|
| import 'info.dart' show ClosureInfo;
|
|
|
| +import 'rewriter.dart' show AstRewriter, BlockRewriter, InitializerRewriter;
|
| +
|
| class ClosureConverter extends Transformer {
|
| final CoreTypes coreTypes;
|
| final Class contextClass;
|
| @@ -103,12 +106,10 @@ class ClosureConverter extends Transformer {
|
|
|
| FunctionNode currentFunction;
|
|
|
| - Block _currentBlock;
|
| -
|
| - int _insertionIndex = 0;
|
| -
|
| Context context;
|
|
|
| + AstRewriter rewriter;
|
| +
|
| /// Maps original type variable (aka type parameter) to a hoisted type
|
| /// variable type.
|
| ///
|
| @@ -150,20 +151,13 @@ class ClosureConverter extends Transformer {
|
| throw "No file uri for ${currentMember.runtimeType}";
|
| }
|
|
|
| - void insert(Statement statement) {
|
| - _currentBlock.statements.insert(_insertionIndex++, statement);
|
| - statement.parent = _currentBlock;
|
| - }
|
| -
|
| TreeNode saveContext(TreeNode f()) {
|
| - Block savedBlock = _currentBlock;
|
| - int savedIndex = _insertionIndex;
|
| + AstRewriter old = rewriter;
|
| Context savedContext = context;
|
| try {
|
| return f();
|
| } finally {
|
| - _currentBlock = savedBlock;
|
| - _insertionIndex = savedIndex;
|
| + rewriter = old;
|
| context = savedContext;
|
| }
|
| }
|
| @@ -196,26 +190,68 @@ class ClosureConverter extends Transformer {
|
| return node;
|
| }
|
|
|
| + void extendContextWith(VariableDeclaration parameter) {
|
| + context.extend(parameter, new VariableGet(parameter));
|
| + }
|
| +
|
| TreeNode visitConstructor(Constructor node) {
|
| assert(isEmptyContext);
|
| -
|
| currentMember = node;
|
| -
|
| + // Transform initializers.
|
| + for (Initializer initializer in node.initializers) {
|
| + if (initializer is FieldInitializer) {
|
| + // Create a rewriter and a context for the initializer expression.
|
| + rewriter = new InitializerRewriter(initializer.value);
|
| + context = new NoContext(this);
|
| + // Save the expression to visit it in the extended context, since the
|
| + // rewriter will modify `initializer.value`.
|
| + Expression initializerExpression = initializer.value;
|
| + // Extend the context with all captured parameters of the constructor.
|
| + // TODO(karlklose): add a fine-grained analysis of captured parameters.
|
| + node.function.positionalParameters
|
| + .where(capturedVariables.contains)
|
| + .forEach(extendContextWith);
|
| + node.function.namedParameters
|
| + .where(capturedVariables.contains)
|
| + .forEach(extendContextWith);
|
| + // Transform the initializer expression.
|
| + var parent = initializerExpression.parent;
|
| + initializerExpression = initializerExpression.accept(this);
|
| + initializerExpression.parent = parent;
|
| + if (parent is Let) {
|
| + parent.body = initializerExpression;
|
| + } else if (parent is FieldInitializer) {
|
| + parent.value = initializerExpression;
|
| + } else {
|
| + throw "Found unexpected node '${node.runtimeType}, expected a 'Let' "
|
| + "or a 'FieldInitializer'.";
|
| + }
|
| + }
|
| + }
|
| + rewriter = null;
|
| + // Transform constructor body.
|
| FunctionNode function = node.function;
|
| if (function.body != null && function.body is! EmptyStatement) {
|
| setupContextForFunctionBody(function);
|
| VariableDeclaration self = thisAccess[currentMemberFunction];
|
| - // TODO(karlklose): transform initializers
|
| if (self != null) {
|
| context.extend(self, new ThisExpression());
|
| }
|
| node.function.accept(this);
|
| resetContext();
|
| }
|
| -
|
| return node;
|
| }
|
|
|
| + AstRewriter makeRewriterForBody(FunctionNode function) {
|
| + Statement body = function.body;
|
| + if (body is! Block) {
|
| + body = new Block(<Statement>[body]);
|
| + function.body = function.body.parent = body;
|
| + }
|
| + return new BlockRewriter(body);
|
| + }
|
| +
|
| Expression handleLocalFunction(FunctionNode function) {
|
| FunctionNode enclosingFunction = currentFunction;
|
| Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution;
|
| @@ -223,13 +259,7 @@ class ClosureConverter extends Transformer {
|
| Statement body = function.body;
|
| assert(body != null);
|
|
|
| - if (body is Block) {
|
| - _currentBlock = body;
|
| - } else {
|
| - _currentBlock = new Block(<Statement>[body]);
|
| - function.body = body.parent = _currentBlock;
|
| - }
|
| - _insertionIndex = 0;
|
| + rewriter = makeRewriterForBody(function);
|
|
|
| VariableDeclaration contextVariable = new VariableDeclaration(
|
| "#contextParameter",
|
| @@ -275,9 +305,11 @@ class ClosureConverter extends Transformer {
|
| });
|
| }
|
|
|
| - TreeNode visitFunctionExpression(FunctionExpression node) => saveContext(() {
|
| - return handleLocalFunction(node.function);
|
| - });
|
| + TreeNode visitFunctionExpression(FunctionExpression node) {
|
| + return saveContext(() {
|
| + return handleLocalFunction(node.function);
|
| + });
|
| + }
|
|
|
| /// Add a new class to the current library that looks like this:
|
| ///
|
| @@ -394,28 +426,21 @@ class ClosureConverter extends Transformer {
|
| assert(body != null);
|
| currentMemberFunction = function;
|
| // Ensure that the body is a block which becomes the current block.
|
| - if (body is Block) {
|
| - _currentBlock = body;
|
| - } else {
|
| - _currentBlock = new Block(<Statement>[body]);
|
| - function.body = body.parent = _currentBlock;
|
| - }
|
| - _insertionIndex = 0;
|
| + 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);
|
| }
|
|
|
| void resetContext() {
|
| - _currentBlock = null;
|
| - _insertionIndex = 0;
|
| + rewriter = null;
|
| context = null;
|
| currentMemberFunction = null;
|
| currentMember = null;
|
| }
|
|
|
| bool get isEmptyContext {
|
| - return _currentBlock == null && _insertionIndex == 0 && context == null;
|
| + return rewriter == null && context == null;
|
| }
|
|
|
| TreeNode visitLocalInitializer(LocalInitializer node) {
|
| @@ -426,16 +451,14 @@ class ClosureConverter extends Transformer {
|
|
|
| TreeNode visitFunctionNode(FunctionNode node) {
|
| transformList(node.typeParameters, this, node);
|
| -
|
| - void extend(VariableDeclaration parameter) {
|
| - context.extend(parameter, new VariableGet(parameter));
|
| - }
|
| -
|
| // TODO: Can parameters contain initializers (e.g., for optional ones) that
|
| // need to be closure converted?
|
| - node.positionalParameters.where(capturedVariables.contains).forEach(extend);
|
| - node.namedParameters.where(capturedVariables.contains).forEach(extend);
|
| -
|
| + 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;
|
| @@ -444,25 +467,8 @@ class ClosureConverter extends Transformer {
|
|
|
| TreeNode visitBlock(Block node) {
|
| return saveContext(() {
|
| - if (_currentBlock != node) {
|
| - _currentBlock = node;
|
| - _insertionIndex = 0;
|
| - }
|
| -
|
| - while (_insertionIndex < _currentBlock.statements.length) {
|
| - assert(_currentBlock == node);
|
| -
|
| - var original = _currentBlock.statements[_insertionIndex];
|
| - var transformed = original.accept(this);
|
| - assert(_currentBlock.statements[_insertionIndex] == original);
|
| - if (transformed == null) {
|
| - _currentBlock.statements.removeAt(_insertionIndex);
|
| - } else {
|
| - _currentBlock.statements[_insertionIndex++] = transformed;
|
| - transformed.parent = _currentBlock;
|
| - }
|
| - }
|
| -
|
| + BlockRewriter blockRewriter = rewriter = rewriter.forNestedBlock(node);
|
| + blockRewriter.transformStatements(node, this);
|
| return node;
|
| });
|
| }
|
| @@ -556,9 +562,9 @@ class ClosureConverter extends Transformer {
|
| statements.add(node);
|
| node.variables.clear();
|
| node.updates.insert(0, cloneContext());
|
| - _currentBlock = new Block(statements);
|
| - _insertionIndex = 0;
|
| - return _currentBlock.accept(this);
|
| + Block block = new Block(statements);
|
| + rewriter = new BlockRewriter(block);
|
| + return block.accept(this);
|
| });
|
| }
|
| return super.visitForStatement(node);
|
|
|