Files
sdk/pkg/kernel/lib/transformations/closure/rewriter.dart
T

128 lines
4.5 KiB
Dart

// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library kernel.transformations.closure.rewriter;
import '../../ast.dart';
import 'converter.dart' show ClosureConverter;
/// Used by the [Context] to initialize and update the context variable
/// used to capture the variables closed over by functions.
abstract class AstRewriter {
/// The declared variable that holds the context.
VariableDeclaration contextDeclaration;
/// The expression used to initialize the size of the context stored in
/// [contextDeclaration]. This expression is modified when the context is
/// extended.
IntLiteral contextSize;
/// Creates a new [AstRewriter] for a (nested) [Block].
BlockRewriter forNestedBlock(Block block);
/// Inserts an allocation of a context and initializes [contextDeclaration]
/// and [contextSize].
void insertContextDeclaration(Class contextClass, Expression accessParent);
/// Inserts an expression or statement that extends the context, where
/// [arguments] holds a pair of the new index and the initial value.
void insertExtendContext(Expression accessContext, Arguments arguments);
void _createDeclaration(Class contextClass) {
assert(contextDeclaration == null && contextSize == null);
contextSize = new IntLiteral(0);
contextDeclaration = new VariableDeclaration.forValue(
new ConstructorInvocation(contextClass.constructors.first,
new Arguments(<Expression>[contextSize])),
type: new InterfaceType(contextClass));
contextDeclaration.name = "#context";
}
}
/// Adds a local variable for the context and adds update [Statement]s to the
/// current block.
class BlockRewriter extends AstRewriter {
Block _currentBlock;
int _insertionIndex;
BlockRewriter(this._currentBlock) : _insertionIndex = 0;
BlockRewriter forNestedBlock(Block block) {
return _currentBlock != block ? new BlockRewriter(block) : this;
}
void transformStatements(Block block, ClosureConverter converter) {
while (_insertionIndex < _currentBlock.statements.length) {
var original = _currentBlock.statements[_insertionIndex];
var transformed = original.accept(converter);
assert(_currentBlock.statements[_insertionIndex] == original);
if (transformed == null) {
_currentBlock.statements.removeAt(_insertionIndex);
} else {
_currentBlock.statements[_insertionIndex++] = transformed;
transformed.parent = _currentBlock;
}
}
}
void _insertStatement(Statement statement) {
_currentBlock.statements.insert(_insertionIndex++, statement);
statement.parent = _currentBlock;
}
void insertContextDeclaration(Class contextClass, Expression accessParent) {
_createDeclaration(contextClass);
_insertStatement(contextDeclaration);
if (accessParent is! NullLiteral) {
_insertStatement(new ExpressionStatement(new PropertySet(
new VariableGet(contextDeclaration),
new Name('parent'),
accessParent)));
}
}
void insertExtendContext(Expression accessContext, Arguments arguments) {
_insertStatement(new ExpressionStatement(
new MethodInvocation(accessContext, new Name('[]='), arguments)));
}
}
/// Creates and updates the context as [Let] bindings around the initializer
/// expression.
class InitializerRewriter extends AstRewriter {
final Expression initializingExpression;
InitializerRewriter(this.initializingExpression) {
assert(initializingExpression.parent is FieldInitializer);
}
@override
BlockRewriter forNestedBlock(Block block) {
return new BlockRewriter(block);
}
@override
void insertContextDeclaration(Class contextClass, Expression accessParent) {
_createDeclaration(contextClass);
FieldInitializer parent = initializingExpression.parent;
Let binding = new Let(contextDeclaration, initializingExpression);
initializingExpression.parent = binding;
parent.value = binding;
binding.parent = parent;
}
@override
void insertExtendContext(Expression accessContext, Arguments arguments) {
Expression extendContext =
new MethodInvocation(accessContext, new Name('[]='), arguments);
Let parent = initializingExpression.parent;
Let binding = new Let(
new VariableDeclaration(null, initializer: extendContext),
initializingExpression);
parent.body = binding;
binding.parent = parent;
}
}