OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'package:analyzer/analyzer.dart' as analyzer; | 5 import 'package:analyzer/analyzer.dart' as analyzer; |
6 import 'package:analyzer/dart/ast/ast.dart'; | 6 import 'package:analyzer/dart/ast/ast.dart'; |
7 import 'package:analyzer/dart/element/type.dart' show DartType; | 7 import 'package:analyzer/dart/element/type.dart' show DartType; |
8 import 'package:analyzer/src/dart/ast/ast.dart' show FunctionBodyImpl; | 8 import 'package:analyzer/src/dart/ast/ast.dart' show FunctionBodyImpl; |
9 import 'package:analyzer/src/dart/ast/utilities.dart' show NodeReplacer; | 9 import 'package:analyzer/src/dart/ast/utilities.dart' show NodeReplacer; |
10 import 'package:analyzer/src/dart/element/type.dart' show DynamicTypeImpl; | 10 import 'package:analyzer/src/dart/element/type.dart' show DynamicTypeImpl; |
11 import 'package:analyzer/src/generated/parser.dart' show ResolutionCopier; | 11 import 'package:analyzer/src/generated/parser.dart' show ResolutionCopier; |
12 import 'package:analyzer/src/task/strong/info.dart' | 12 import 'package:analyzer/src/task/strong/ast_properties.dart' as ast_properties; |
13 show CoercionInfo, DownCast, DynamicInvoke; | |
14 import 'package:logging/logging.dart' as logger; | 13 import 'package:logging/logging.dart' as logger; |
15 | 14 |
16 import 'ast_builder.dart' show AstBuilder, RawAstBuilder; | 15 import 'ast_builder.dart' show AstBuilder, RawAstBuilder; |
17 import 'element_helpers.dart' show isInlineJS; | 16 import 'element_helpers.dart' show isInlineJS; |
18 | 17 |
19 final _log = new logger.Logger('dev_compiler.reify_coercions'); | 18 final _log = new logger.Logger('dev_compiler.reify_coercions'); |
20 | 19 |
21 // This class implements a pass which modifies (in place) the ast replacing | 20 // This class implements a pass which modifies (in place) the ast replacing |
22 // abstract coercion nodes with their dart implementations. | 21 // abstract coercion nodes with their dart implementations. |
23 class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> { | 22 class CoercionReifier extends analyzer.GeneralizingAstVisitor<Object> { |
24 final cloner = new _TreeCloner(); | 23 final cloner = new _TreeCloner(); |
25 | 24 |
26 CoercionReifier._(); | 25 CoercionReifier._(); |
27 | 26 |
28 /// Transforms the given compilation units, and returns a new AST with | 27 /// Transforms the given compilation units, and returns a new AST with |
29 /// explicit coercion nodes in appropriate places. | 28 /// explicit coercion nodes in appropriate places. |
30 static List<CompilationUnit> reify(List<CompilationUnit> units) { | 29 static List<CompilationUnit> reify(List<CompilationUnit> units) { |
31 var cr = new CoercionReifier._(); | 30 var cr = new CoercionReifier._(); |
32 // Clone compilation units, so we don't modify the originals. | 31 return units.map(cr.visitCompilationUnit).toList(growable: false); |
33 units = units.map(cr._clone).toList(growable: false); | 32 } |
34 units.forEach(cr.visitCompilationUnit); | 33 |
35 return units; | 34 @override |
| 35 CompilationUnit visitCompilationUnit(CompilationUnit node) { |
| 36 if (ast_properties.hasImplicitCasts(node)) { |
| 37 // Clone compilation unit, so we don't modify the originals. |
| 38 node = _clone(node); |
| 39 super.visitCompilationUnit(node); |
| 40 } |
| 41 return node; |
36 } | 42 } |
37 | 43 |
38 @override | 44 @override |
39 visitExpression(Expression node) { | 45 visitExpression(Expression node) { |
40 var coercion = CoercionInfo.get(node); | 46 node.visitChildren(this); |
41 if (coercion is DownCast) { | 47 |
42 return _visitDownCast(coercion, node); | 48 var castType = ast_properties.getImplicitCast(node); |
| 49 if (castType != null) { |
| 50 _replaceNode(node.parent, node, _castExpression(node, castType)); |
43 } | 51 } |
44 return super.visitExpression(node); | |
45 } | 52 } |
46 | 53 |
47 @override | 54 @override |
| 55 visitMethodInvocation(MethodInvocation node) { |
| 56 if (isInlineJS(node.methodName.staticElement)) { |
| 57 // Don't cast our inline-JS code in SDK. |
| 58 ast_properties.setImplicitCast(node, null); |
| 59 } |
| 60 visitExpression(node); |
| 61 } |
| 62 |
| 63 @override |
48 visitParenthesizedExpression(ParenthesizedExpression node) { | 64 visitParenthesizedExpression(ParenthesizedExpression node) { |
49 super.visitParenthesizedExpression(node); | 65 super.visitParenthesizedExpression(node); |
50 node.staticType = node.expression.staticType; | 66 node.staticType = node.expression.staticType; |
51 } | 67 } |
52 | 68 |
53 @override | 69 @override |
54 visitForEachStatement(ForEachStatement node) { | 70 visitForEachStatement(ForEachStatement node) { |
55 // Visit other children. | 71 // Visit other children. |
56 node.iterable.accept(this); | 72 node.iterable.accept(this); |
57 node.body.accept(this); | 73 node.body.accept(this); |
58 | 74 |
59 // If needed, assert a cast inside the body before the variable is read. | 75 // If needed, assert a cast inside the body before the variable is read. |
60 var variable = node.identifier ?? node.loopVariable.identifier; | 76 var variable = node.identifier ?? node.loopVariable.identifier; |
61 var coercion = CoercionInfo.get(variable); | 77 var castType = ast_properties.getImplicitCast(variable); |
62 if (coercion is DownCast) { | 78 if (castType != null) { |
63 // Build the cast. We will place this cast in the body, so need to clone | 79 // Build the cast. We will place this cast in the body, so need to clone |
64 // the variable's AST node and clear out its static type (otherwise we | 80 // the variable's AST node and clear out its static type (otherwise we |
65 // will optimize away the cast). | 81 // will optimize away the cast). |
66 var cast = _castExpression( | 82 var cast = _castExpression( |
67 _clone(variable)..staticType = DynamicTypeImpl.instance, | 83 _clone(variable)..staticType = DynamicTypeImpl.instance, castType); |
68 coercion.convertedType); | |
69 | 84 |
70 var body = node.body; | 85 var body = node.body; |
71 var blockBody = <Statement>[RawAstBuilder.expressionStatement(cast)]; | 86 var blockBody = <Statement>[RawAstBuilder.expressionStatement(cast)]; |
72 if (body is Block) { | 87 if (body is Block) { |
73 blockBody.addAll(body.statements); | 88 blockBody.addAll(body.statements); |
74 } else { | 89 } else { |
75 blockBody.add(body); | 90 blockBody.add(body); |
76 } | 91 } |
77 _replaceNode(node, body, RawAstBuilder.block(blockBody)); | 92 _replaceNode(node, body, RawAstBuilder.block(blockBody)); |
78 } | 93 } |
79 } | 94 } |
80 | 95 |
81 void _visitDownCast(DownCast node, Expression expr) { | |
82 expr.visitChildren(this); | |
83 _replaceNode(expr.parent, expr, coerceExpression(expr, node)); | |
84 } | |
85 | |
86 void _replaceNode(AstNode parent, AstNode oldNode, AstNode newNode) { | 96 void _replaceNode(AstNode parent, AstNode oldNode, AstNode newNode) { |
87 if (!identical(oldNode, newNode)) { | 97 if (!identical(oldNode, newNode)) { |
88 var replaced = parent.accept(new NodeReplacer(oldNode, newNode)); | 98 var replaced = parent.accept(new NodeReplacer(oldNode, newNode)); |
89 // It looks like NodeReplacer will always return true. | 99 // It looks like NodeReplacer will always return true. |
90 // It does throw IllegalArgumentException though, if child is not found. | 100 // It does throw IllegalArgumentException though, if child is not found. |
91 assert(replaced); | 101 assert(replaced); |
92 } | 102 } |
93 } | 103 } |
94 | 104 |
95 /// Coerce [e] using [c], returning a new expression. | |
96 Expression coerceExpression(Expression e, DownCast node) { | |
97 if (e is NamedExpression) { | |
98 Expression inner = coerceExpression(e.expression, node); | |
99 return new NamedExpression(e.name, inner); | |
100 } | |
101 if (e is MethodInvocation && isInlineJS(e.methodName.staticElement)) { | |
102 // Inline JS code should not need casts. | |
103 return e; | |
104 } | |
105 return _castExpression(e, node.convertedType); | |
106 } | |
107 | |
108 Expression _castExpression(Expression e, DartType toType) { | 105 Expression _castExpression(Expression e, DartType toType) { |
109 // We use an empty name in the AST, because the JS code generator only cares | 106 // We use an empty name in the AST, because the JS code generator only cares |
110 // about the target type. It does not look at the AST name. | 107 // about the target type. It does not look at the AST name. |
111 var typeName = new TypeName(AstBuilder.identifierFromString(''), null); | 108 var typeName = new TypeName(AstBuilder.identifierFromString(''), null); |
112 typeName.type = toType; | 109 typeName.type = toType; |
113 var cast = AstBuilder.asExpression(e, typeName); | 110 var cast = AstBuilder.asExpression(e, typeName); |
114 cast.staticType = toType; | 111 cast.staticType = toType; |
115 return cast; | 112 return cast; |
116 } | 113 } |
117 | 114 |
118 /*=T*/ _clone/*<T extends AstNode>*/(/*=T*/ node) { | 115 /*=T*/ _clone/*<T extends AstNode>*/(/*=T*/ node) { |
119 var copy = node.accept(cloner) as dynamic/*=T*/; | 116 var copy = node.accept(cloner) as dynamic/*=T*/; |
120 ResolutionCopier.copyResolutionData(node, copy); | 117 ResolutionCopier.copyResolutionData(node, copy); |
121 return copy; | 118 return copy; |
122 } | 119 } |
123 } | 120 } |
124 | 121 |
125 class _TreeCloner extends analyzer.AstCloner { | 122 class _TreeCloner extends analyzer.AstCloner { |
126 void _cloneProperties(AstNode clone, AstNode node) { | 123 void _cloneProperties(AstNode clone, AstNode node) { |
127 if (clone != null) { | 124 if (clone is Expression) { |
128 CoercionInfo.set(clone, CoercionInfo.get(node)); | 125 ast_properties.setImplicitCast( |
129 DynamicInvoke.set(clone, DynamicInvoke.get(node)); | 126 clone, ast_properties.getImplicitCast(node)); |
| 127 ast_properties.setIsDynamicInvoke( |
| 128 clone, ast_properties.isDynamicInvoke(node)); |
130 } | 129 } |
131 } | 130 } |
132 | 131 |
133 @override | 132 @override |
134 /*=E*/ cloneNode/*<E extends AstNode>*/(/*=E*/ node) { | 133 /*=E*/ cloneNode/*<E extends AstNode>*/(/*=E*/ node) { |
135 var clone = super.cloneNode(node); | 134 var clone = super.cloneNode(node); |
136 _cloneProperties(clone, node); | 135 _cloneProperties(clone, node); |
137 return clone; | 136 return clone; |
138 } | 137 } |
139 | 138 |
(...skipping 26 matching lines...) Expand all Loading... |
166 | 165 |
167 // TODO(jmesserly): workaround for | 166 // TODO(jmesserly): workaround for |
168 // https://github.com/dart-lang/sdk/issues/26368 | 167 // https://github.com/dart-lang/sdk/issues/26368 |
169 @override | 168 @override |
170 TypeName visitTypeName(TypeName node) { | 169 TypeName visitTypeName(TypeName node) { |
171 var clone = super.visitTypeName(node); | 170 var clone = super.visitTypeName(node); |
172 clone.type = node.type; | 171 clone.type = node.type; |
173 return clone; | 172 return clone; |
174 } | 173 } |
175 } | 174 } |
OLD | NEW |