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; |
+ } |
} |