OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 library analyzer.src.dart.sdk.patch; | 5 library analyzer.src.dart.sdk.patch; |
6 | 6 |
7 import 'package:analyzer/dart/ast/ast.dart'; | 7 import 'package:analyzer/dart/ast/ast.dart'; |
8 import 'package:analyzer/dart/ast/token.dart'; | 8 import 'package:analyzer/dart/ast/token.dart'; |
9 import 'package:analyzer/error/listener.dart'; | 9 import 'package:analyzer/error/listener.dart'; |
10 import 'package:analyzer/file_system/file_system.dart'; | 10 import 'package:analyzer/file_system/file_system.dart'; |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
125 for (CompilationUnitMember patchDeclaration in patchUnit.declarations) { | 125 for (CompilationUnitMember patchDeclaration in patchUnit.declarations) { |
126 if (patchDeclaration is FunctionDeclaration) { | 126 if (patchDeclaration is FunctionDeclaration) { |
127 String name = patchDeclaration.name.name; | 127 String name = patchDeclaration.name.name; |
128 if (_hasPatchAnnotation(patchDeclaration.metadata)) { | 128 if (_hasPatchAnnotation(patchDeclaration.metadata)) { |
129 for (CompilationUnitMember baseDeclaration in baseUnit.declarations) { | 129 for (CompilationUnitMember baseDeclaration in baseUnit.declarations) { |
130 if (patchDeclaration is FunctionDeclaration && | 130 if (patchDeclaration is FunctionDeclaration && |
131 baseDeclaration is FunctionDeclaration && | 131 baseDeclaration is FunctionDeclaration && |
132 baseDeclaration.name.name == name) { | 132 baseDeclaration.name.name == name) { |
133 if (_hasPatchAnnotation(patchDeclaration.metadata)) { | 133 if (_hasPatchAnnotation(patchDeclaration.metadata)) { |
134 // Remove the "external" keyword. | 134 // Remove the "external" keyword. |
135 if (baseDeclaration.externalKeyword != null) { | 135 Token externalKeyword = baseDeclaration.externalKeyword; |
| 136 if (externalKeyword != null) { |
136 baseDeclaration.externalKeyword = null; | 137 baseDeclaration.externalKeyword = null; |
| 138 _removeToken(externalKeyword); |
137 } else { | 139 } else { |
138 _failExternalKeyword( | 140 _failExternalKeyword( |
139 baseSource, name, baseDeclaration.offset); | 141 baseSource, name, baseDeclaration.offset); |
140 } | 142 } |
141 // Replace the body. | 143 // Replace the body. |
142 baseDeclaration.functionExpression.body = | 144 FunctionExpression oldExpr = baseDeclaration.functionExpression; |
143 patchDeclaration.functionExpression.body; | 145 FunctionBody newBody = patchDeclaration.functionExpression.body; |
| 146 _replaceNodeTokens(oldExpr.body, newBody); |
| 147 oldExpr.body = newBody; |
144 } | 148 } |
145 } | 149 } |
146 } | 150 } |
147 } else if (addNewTopLevelDeclarations) { | 151 } else if (addNewTopLevelDeclarations) { |
148 _failIfPublicName(patchDeclaration, name); | 152 _failIfPublicName(patchDeclaration, name); |
149 declarationsToAppend.add(patchDeclaration); | 153 declarationsToAppend.add(patchDeclaration); |
150 } | 154 } |
151 } else if (patchDeclaration is FunctionTypeAlias) { | 155 } else if (patchDeclaration is FunctionTypeAlias) { |
152 if (patchDeclaration.metadata.isNotEmpty) { | 156 if (patchDeclaration.metadata.isNotEmpty) { |
153 _failInPatch('contains a function type alias with an annotation', | 157 _failInPatch('contains a function type alias with an annotation', |
154 patchDeclaration.offset); | 158 patchDeclaration.offset); |
155 } | 159 } |
156 _failIfPublicName(patchDeclaration, patchDeclaration.name.name); | 160 _failIfPublicName(patchDeclaration, patchDeclaration.name.name); |
157 declarationsToAppend.add(patchDeclaration); | 161 declarationsToAppend.add(patchDeclaration); |
158 } else { | 162 } else { |
159 _failInPatch('contains an unsupported top-level declaration', | 163 _failInPatch('contains an unsupported top-level declaration', |
160 patchDeclaration.offset); | 164 patchDeclaration.offset); |
161 } | 165 } |
162 } | 166 } |
163 // Append new top-level declarations. | 167 // Append new top-level declarations. |
164 baseUnit.declarations.addAll(declarationsToAppend); | 168 Token lastToken = baseUnit.endToken.previous; |
| 169 for (CompilationUnitMember newDeclaration in declarationsToAppend) { |
| 170 newDeclaration.endToken.setNext(lastToken.next); |
| 171 lastToken.setNext(newDeclaration.beginToken); |
| 172 baseUnit.declarations.add(newDeclaration); |
| 173 lastToken = newDeclaration.endToken; |
| 174 } |
165 } | 175 } |
166 | 176 |
167 /** | 177 /** |
168 * Parse the given [source] into AST. | 178 * Parse the given [source] into AST. |
169 */ | 179 */ |
170 @visibleForTesting | 180 @visibleForTesting |
171 static CompilationUnit parse( | 181 static CompilationUnit parse( |
172 Source source, bool strong, AnalysisErrorListener errorListener) { | 182 Source source, bool strong, AnalysisErrorListener errorListener) { |
173 String code = source.contents.data; | 183 String code = source.contents.data; |
174 | 184 |
(...skipping 14 matching lines...) Expand all Loading... |
189 * Return `true` if [metadata] has the `@patch` annotation. | 199 * Return `true` if [metadata] has the `@patch` annotation. |
190 */ | 200 */ |
191 static bool _hasPatchAnnotation(List<Annotation> metadata) { | 201 static bool _hasPatchAnnotation(List<Annotation> metadata) { |
192 return metadata.any((annotation) { | 202 return metadata.any((annotation) { |
193 Identifier name = annotation.name; | 203 Identifier name = annotation.name; |
194 return annotation.constructorName == null && | 204 return annotation.constructorName == null && |
195 name is SimpleIdentifier && | 205 name is SimpleIdentifier && |
196 name.name == 'patch'; | 206 name.name == 'patch'; |
197 }); | 207 }); |
198 } | 208 } |
| 209 |
| 210 /** |
| 211 * Remove the [token] from the stream. |
| 212 */ |
| 213 static void _removeToken(Token token) { |
| 214 token.previous.setNext(token.next); |
| 215 } |
| 216 |
| 217 /** |
| 218 * Replace tokens of the [oldNode] with tokens of the [newNode]. |
| 219 */ |
| 220 static void _replaceNodeTokens(AstNode oldNode, AstNode newNode) { |
| 221 oldNode.beginToken.previous.setNext(newNode.beginToken); |
| 222 newNode.endToken.setNext(oldNode.endToken.next); |
| 223 } |
199 } | 224 } |
OLD | NEW |