blob: 2e916d01ae05d6de75cb36f110ae20f97b03800b [file] [log] [blame]
// Copyright (c) 2019, 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.
import 'package:kernel/ast.dart';
/// Wraps the initializers of late local variables in closures.
void transformLibraries(List<Library> libraries) {
const transformer = _LateVarInitTransformer();
libraries.forEach(transformer.visitLibrary);
}
class _LateVarInitTransformer extends Transformer {
const _LateVarInitTransformer();
bool _shouldApplyTransform(VariableDeclaration v) {
// This transform only applies to late variables.
if (!v.isLate) return false;
// Const variables are ignored.
if (v.isConst) return false;
// Variables with no initializer or a trivial initializer are ignored.
if (v.initializer == null) return false;
final Expression init = v.initializer;
if (init is StringLiteral) return false;
if (init is BoolLiteral) return false;
if (init is IntLiteral) return false;
if (init is DoubleLiteral) return false;
if (init is NullLiteral) return false;
if (init is ConstantExpression && init.constant is PrimitiveConstant) {
return false;
}
return true;
}
List<Statement> _transformVariableDeclaration(
TreeNode parent, VariableDeclaration node) {
super.visitVariableDeclaration(node);
final fnNode =
FunctionNode(ReturnStatement(node.initializer), returnType: node.type);
final fn = FunctionDeclaration(
VariableDeclaration("#${node.name}#initializer",
type: fnNode.thisFunctionType),
fnNode)
..parent = parent;
node.initializer =
MethodInvocation(VariableGet(fn.variable), Name("call"), Arguments([]))
..parent = node;
return [fn, node];
}
void _transformStatements(TreeNode parent, List<Statement> statements) {
List<Statement> oldStatements = statements;
for (var i = 0; i < oldStatements.length; ++i) {
Statement s = oldStatements[i];
if (s is VariableDeclaration && _shouldApplyTransform(s)) {
if (oldStatements == statements) {
oldStatements = List<Statement>.of(statements);
statements.clear();
}
statements.addAll(_transformVariableDeclaration(parent, s));
} else if (oldStatements != statements) {
statements.add(s.accept<TreeNode>(this)..parent = parent);
} else {
statements[i] = s.accept<TreeNode>(this)..parent = parent;
}
}
}
@override
visitBlock(Block node) {
_transformStatements(node, node.statements);
return node;
}
@override
visitAssertBlock(AssertBlock node) {
_transformStatements(node, node.statements);
return node;
}
}