| Index: lib/src/codegen/reify_coercions.dart
|
| diff --git a/lib/src/codegen/reify_coercions.dart b/lib/src/codegen/reify_coercions.dart
|
| index 4ed5003fdbf6ab9e0cc53a63cbc53a8a342f273d..f38e039aba799c26b67fdb1bfadab7873455aaed 100644
|
| --- a/lib/src/codegen/reify_coercions.dart
|
| +++ b/lib/src/codegen/reify_coercions.dart
|
| @@ -6,7 +6,10 @@ import 'package:analyzer/analyzer.dart' as analyzer;
|
| import 'package:analyzer/dart/ast/ast.dart';
|
| import 'package:analyzer/dart/element/element.dart';
|
| import 'package:analyzer/dart/element/type.dart';
|
| +import 'package:analyzer/src/dart/ast/ast.dart' show FunctionBodyImpl;
|
| import 'package:analyzer/src/dart/ast/utilities.dart' show NodeReplacer;
|
| +import 'package:analyzer/src/dart/element/type.dart' show DynamicTypeImpl;
|
| +import 'package:analyzer/src/generated/parser.dart' show ResolutionCopier;
|
| import 'package:analyzer/src/task/strong/info.dart';
|
| import 'package:logging/logging.dart' as logger;
|
|
|
| @@ -30,41 +33,71 @@ class NewTypeIdDesc {
|
| // This class implements a pass which modifies (in place) the ast replacing
|
| // abstract coercion nodes with their dart implementations.
|
| class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> {
|
| + final cloner = new _TreeCloner();
|
|
|
| - CoercionReifier();
|
| -
|
| - /// This should be the entry point for this class.
|
| + /// Makes coercions explicit in the resolved AST, and returns the new AST.
|
| ///
|
| - /// Entering via the visit functions directly may not do the right
|
| - /// thing with respect to discharging the collected definitions.
|
| + /// This should be the entry point for this class.
|
| + /// Entering via the visit functions directly will incorrectly mutate the AST.
|
| ///
|
| - /// Returns the set of new type identifiers added by the reifier
|
| - void reify(List<CompilationUnit> units) {
|
| + /// Returns the new compilation units.
|
| + List<CompilationUnit> reify(List<CompilationUnit> units) {
|
| + // Copy the AST before modifying it.
|
| + units = units.map(_clone).toList();
|
| + // Visit the AST and make coercions explicit.
|
| units.forEach(visitCompilationUnit);
|
| + return units;
|
| }
|
|
|
| @override
|
| - Object visitExpression(Expression node) {
|
| - var info = CoercionInfo.get(node);
|
| - if (info is DownCast) {
|
| - return _visitDownCast(info, node);
|
| + visitExpression(Expression node) {
|
| + var coercion = CoercionInfo.get(node);
|
| + if (coercion is DownCast) {
|
| + return _visitDownCast(coercion, node);
|
| }
|
| return super.visitExpression(node);
|
| }
|
|
|
| - ///////////////// Private //////////////////////////////////
|
| + @override
|
| + visitForEachStatement(ForEachStatement node) {
|
| + // Visit other children.
|
| + node.iterable.accept(this);
|
| + node.body.accept(this);
|
| +
|
| + // If needed, assert a cast inside the body before the variable is read.
|
| + var variable = node.identifier ?? node.loopVariable.identifier;
|
| + var coercion = CoercionInfo.get(variable);
|
| + if (coercion is DownCast) {
|
| + // Build the cast. We will place this cast in the body, so need to clone
|
| + // the variable's AST node and clear out its static type (otherwise we
|
| + // will optimize away the cast).
|
| + var cast = _castExpression(
|
| + _clone(variable)..staticType = DynamicTypeImpl.instance,
|
| + coercion.convertedType);
|
| +
|
| + var body = node.body;
|
| + var blockBody = <Statement>[RawAstBuilder.expressionStatement(cast)];
|
| + if (body is Block) {
|
| + blockBody.addAll(body.statements);
|
| + } else {
|
| + blockBody.add(body);
|
| + }
|
| + _replaceNode(node, body, RawAstBuilder.block(blockBody));
|
| + }
|
| + }
|
|
|
| - Object _visitDownCast(DownCast node, Expression expr) {
|
| - var parent = expr.parent;
|
| + void _visitDownCast(DownCast node, Expression expr) {
|
| expr.visitChildren(this);
|
| - Expression newE = coerceExpression(expr, node);
|
| - if (!identical(expr, newE)) {
|
| - var replaced = parent.accept(new NodeReplacer(expr, newE));
|
| + _replaceNode(expr.parent, expr, coerceExpression(expr, node));
|
| + }
|
| +
|
| + void _replaceNode(AstNode parent, AstNode oldNode, AstNode newNode) {
|
| + if (!identical(oldNode, newNode)) {
|
| + var replaced = parent.accept(new NodeReplacer(oldNode, newNode));
|
| // It looks like NodeReplacer will always return true.
|
| // It does throw IllegalArgumentException though, if child is not found.
|
| assert(replaced);
|
| }
|
| - return null;
|
| }
|
|
|
| /// Coerce [e] using [c], returning a new expression.
|
| @@ -76,8 +109,6 @@ class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> {
|
| return _castExpression(e, node.convertedType);
|
| }
|
|
|
| - ///////////////// Private //////////////////////////////////
|
| -
|
| Expression _castExpression(Expression e, DartType toType) {
|
| // We use an empty name in the AST, because the JS code generator only cares
|
| // about the target type. It does not look at the AST name.
|
| @@ -87,4 +118,53 @@ class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> {
|
| cast.staticType = toType;
|
| return cast;
|
| }
|
| +
|
| + /*=T*/ _clone/*<T extends AstNode>*/(/*=T*/ node) {
|
| + var copy = node.accept(cloner);
|
| + ResolutionCopier.copyResolutionData(node, copy);
|
| + return copy;
|
| + }
|
| +}
|
| +
|
| +class _TreeCloner extends analyzer.AstCloner {
|
| + void _cloneProperties(AstNode clone, AstNode node) {
|
| + if (clone != null) {
|
| + CoercionInfo.set(clone, CoercionInfo.get(node));
|
| + DynamicInvoke.set(clone, DynamicInvoke.get(node));
|
| + }
|
| + }
|
| +
|
| + @override
|
| + AstNode cloneNode(AstNode node) {
|
| + var clone = super.cloneNode(node);
|
| + _cloneProperties(clone, node);
|
| + return clone;
|
| + }
|
| +
|
| + @override
|
| + List cloneNodeList(List list) {
|
| + var clone = super.cloneNodeList(list);
|
| + for (int i = 0, len = list.length; i < len; i++) {
|
| + _cloneProperties(clone[i], list[i]);
|
| + }
|
| + return clone;
|
| + }
|
| +
|
| + // TODO(jmesserly): ResolutionCopier is not copying this yet.
|
| + @override
|
| + BlockFunctionBody visitBlockFunctionBody(BlockFunctionBody node) {
|
| + var clone = super.visitBlockFunctionBody(node);
|
| + (clone as FunctionBodyImpl).localVariableInfo =
|
| + (node as FunctionBodyImpl).localVariableInfo;
|
| + return clone;
|
| + }
|
| +
|
| + @override
|
| + ExpressionFunctionBody visitExpressionFunctionBody(
|
| + ExpressionFunctionBody node) {
|
| + var clone = super.visitExpressionFunctionBody(node);
|
| + (clone as FunctionBodyImpl).localVariableInfo =
|
| + (node as FunctionBodyImpl).localVariableInfo;
|
| + return clone;
|
| + }
|
| }
|
|
|