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 11 matching lines...) Expand all Loading... | |
22 */ | 22 */ |
23 class SdkPatcher { | 23 class SdkPatcher { |
24 String _baseDesc; | 24 String _baseDesc; |
25 String _patchDesc; | 25 String _patchDesc; |
26 CompilationUnit _patchUnit; | 26 CompilationUnit _patchUnit; |
27 | 27 |
28 /** | 28 /** |
29 * Patch the given [unit] of a SDK [source] with the patches defined in | 29 * Patch the given [unit] of a SDK [source] with the patches defined in |
30 * the [sdk] for the given [platform]. Throw [ArgumentError] if a patch | 30 * the [sdk] for the given [platform]. Throw [ArgumentError] if a patch |
31 * file cannot be read, or the contents violates rules for patch files. | 31 * file cannot be read, or the contents violates rules for patch files. |
32 * | |
33 * If [addNewTopLevelDeclarations] is `true`, then the [unit] is the | |
34 * defining unit of a library, so new top-level declarations should be | |
35 * added to this unit. For parts new declarations may be added only to the | |
36 * patched classes. | |
37 * | |
38 * TODO(scheglov) auto-detect [addNewTopLevelDeclarations] | |
39 */ | 32 */ |
40 void patch(FolderBasedDartSdk sdk, int platform, | 33 void patch( |
41 AnalysisErrorListener errorListener, Source source, CompilationUnit unit, | 34 FolderBasedDartSdk sdk, |
42 {bool addNewTopLevelDeclarations: true}) { | 35 int platform, |
36 AnalysisErrorListener errorListener, | |
37 Source source, | |
38 CompilationUnit unit) { | |
39 // Process URI. | |
40 String libraryUriStr; | |
41 bool isLibraryDefiningUnit; | |
42 { | |
43 Uri uri = source.uri; | |
44 if (uri.scheme != 'dart') { | |
45 throw new ArgumentError( | |
46 'The URI of the unit to patch must has the "dart" scheme: $uri'); | |
Brian Wilkerson
2016/10/13 21:05:31
"has" --> "have"
| |
47 } | |
48 List<String> uriSegments = uri.pathSegments; | |
49 libraryUriStr = 'dart:${uriSegments.first}'; | |
50 isLibraryDefiningUnit = uriSegments.length == 1; | |
51 } | |
43 // Prepare the patch files to apply. | 52 // Prepare the patch files to apply. |
44 List<String> patchPaths; | 53 List<String> patchPaths; |
45 { | 54 { |
46 // TODO(scheglov) add support for patching parts | 55 SdkLibrary sdkLibrary = sdk.getSdkLibrary(libraryUriStr); |
47 String uriStr = source.uri.toString(); | |
48 SdkLibrary sdkLibrary = sdk.getSdkLibrary(uriStr); | |
49 if (sdkLibrary == null) { | 56 if (sdkLibrary == null) { |
50 throw new ArgumentError( | 57 throw new ArgumentError( |
51 'The library $uriStr is not defined in the SDK.'); | 58 'The library $libraryUriStr is not defined in the SDK.'); |
52 } | 59 } |
53 patchPaths = sdkLibrary.getPatches(platform); | 60 patchPaths = sdkLibrary.getPatches(platform); |
54 } | 61 } |
55 | 62 |
56 bool strongMode = sdk.analysisOptions.strongMode; | 63 bool strongMode = sdk.analysisOptions.strongMode; |
57 Context pathContext = sdk.resourceProvider.pathContext; | 64 Context pathContext = sdk.resourceProvider.pathContext; |
58 for (String path in patchPaths) { | 65 for (String path in patchPaths) { |
59 String pathInLib = pathContext.joinAll(path.split('/')); | 66 String pathInLib = pathContext.joinAll(path.split('/')); |
60 File patchFile = sdk.libraryDirectory.getChildAssumingFile(pathInLib); | 67 File patchFile = sdk.libraryDirectory.getChildAssumingFile(pathInLib); |
61 if (!patchFile.exists) { | 68 if (!patchFile.exists) { |
62 throw new ArgumentError( | 69 throw new ArgumentError( |
63 'The patch file ${patchFile.path} for $source does not exist.'); | 70 'The patch file ${patchFile.path} for $source does not exist.'); |
64 } | 71 } |
65 Source patchSource = patchFile.createSource(); | 72 Source patchSource = patchFile.createSource(); |
66 CompilationUnit patchUnit = parse(patchSource, strongMode, errorListener); | 73 CompilationUnit patchUnit = parse(patchSource, strongMode, errorListener); |
67 | 74 |
68 // Prepare for reporting errors. | 75 // Prepare for reporting errors. |
69 _baseDesc = source.toString(); | 76 _baseDesc = source.toString(); |
70 _patchDesc = patchFile.path; | 77 _patchDesc = patchFile.path; |
71 _patchUnit = patchUnit; | 78 _patchUnit = patchUnit; |
72 | 79 |
73 _patchDirectives( | 80 if (isLibraryDefiningUnit) { |
74 source, unit, patchSource, patchUnit, addNewTopLevelDeclarations); | 81 _patchDirectives(source, unit, patchSource, patchUnit); |
75 _patchTopLevelDeclarations(unit, patchUnit, addNewTopLevelDeclarations); | 82 } |
83 _patchTopLevelDeclarations(unit, patchUnit, isLibraryDefiningUnit); | |
76 } | 84 } |
77 } | 85 } |
78 | 86 |
79 void _failExternalKeyword(String name, int offset) { | 87 void _failExternalKeyword(String name, int offset) { |
80 throw new ArgumentError( | 88 throw new ArgumentError( |
81 'The keyword "external" was expected for "$name" in $_baseDesc @ $offset .'); | 89 'The keyword "external" was expected for "$name" in $_baseDesc @ $offset .'); |
82 } | 90 } |
83 | 91 |
84 void _failIfPublicName(AstNode node, String name) { | 92 void _failIfPublicName(AstNode node, String name) { |
85 if (!Identifier.isPrivateName(name)) { | 93 if (!Identifier.isPrivateName(name)) { |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
195 String className = patchClass.name.name; | 203 String className = patchClass.name.name; |
196 _failInPatch('contains an unsupported class member in $className', | 204 _failInPatch('contains an unsupported class member in $className', |
197 patchMember.offset); | 205 patchMember.offset); |
198 } | 206 } |
199 } | 207 } |
200 // Append new class members. | 208 // Append new class members. |
201 _appendToNodeList( | 209 _appendToNodeList( |
202 baseClass.members, membersToAppend, baseClass.leftBracket); | 210 baseClass.members, membersToAppend, baseClass.leftBracket); |
203 } | 211 } |
204 | 212 |
205 void _patchDirectives( | 213 void _patchDirectives(Source baseSource, CompilationUnit baseUnit, |
206 Source baseSource, | 214 Source patchSource, CompilationUnit patchUnit) { |
207 CompilationUnit baseUnit, | |
208 Source patchSource, | |
209 CompilationUnit patchUnit, | |
210 bool addNewTopLevelDeclarations) { | |
211 for (Directive patchDirective in patchUnit.directives) { | 215 for (Directive patchDirective in patchUnit.directives) { |
212 if (patchDirective is ImportDirective) { | 216 if (patchDirective is ImportDirective) { |
213 baseUnit.directives.add(patchDirective); | 217 baseUnit.directives.add(patchDirective); |
214 } else { | 218 } else { |
215 _failInPatch('contains an unsupported "$patchDirective" directive', | 219 _failInPatch('contains an unsupported "$patchDirective" directive', |
216 patchDirective.offset); | 220 patchDirective.offset); |
217 } | 221 } |
218 } | 222 } |
219 } | 223 } |
220 | 224 |
221 void _patchTopLevelDeclarations(CompilationUnit baseUnit, | 225 void _patchTopLevelDeclarations(CompilationUnit baseUnit, |
222 CompilationUnit patchUnit, bool addNewTopLevelDeclarations) { | 226 CompilationUnit patchUnit, bool appendNewTopLevelDeclarations) { |
223 List<CompilationUnitMember> declarationsToAppend = []; | 227 List<CompilationUnitMember> declarationsToAppend = []; |
224 for (CompilationUnitMember patchDeclaration in patchUnit.declarations) { | 228 for (CompilationUnitMember patchDeclaration in patchUnit.declarations) { |
225 if (patchDeclaration is FunctionDeclaration) { | 229 if (patchDeclaration is FunctionDeclaration) { |
226 String name = patchDeclaration.name.name; | 230 String name = patchDeclaration.name.name; |
227 if (_hasPatchAnnotation(patchDeclaration.metadata)) { | 231 if (_hasPatchAnnotation(patchDeclaration.metadata)) { |
228 for (CompilationUnitMember baseDeclaration in baseUnit.declarations) { | 232 for (CompilationUnitMember baseDeclaration in baseUnit.declarations) { |
229 if (patchDeclaration is FunctionDeclaration && | 233 if (patchDeclaration is FunctionDeclaration && |
230 baseDeclaration is FunctionDeclaration && | 234 baseDeclaration is FunctionDeclaration && |
231 baseDeclaration.name.name == name) { | 235 baseDeclaration.name.name == name) { |
232 // Remove the "external" keyword. | 236 // Remove the "external" keyword. |
233 Token externalKeyword = baseDeclaration.externalKeyword; | 237 Token externalKeyword = baseDeclaration.externalKeyword; |
234 if (externalKeyword != null) { | 238 if (externalKeyword != null) { |
235 baseDeclaration.externalKeyword = null; | 239 baseDeclaration.externalKeyword = null; |
236 _removeToken(externalKeyword); | 240 _removeToken(externalKeyword); |
237 } else { | 241 } else { |
238 _failExternalKeyword(name, baseDeclaration.offset); | 242 _failExternalKeyword(name, baseDeclaration.offset); |
239 } | 243 } |
240 // Replace the body. | 244 // Replace the body. |
241 FunctionExpression oldExpr = baseDeclaration.functionExpression; | 245 FunctionExpression oldExpr = baseDeclaration.functionExpression; |
242 FunctionBody newBody = patchDeclaration.functionExpression.body; | 246 FunctionBody newBody = patchDeclaration.functionExpression.body; |
243 _replaceNodeTokens(oldExpr.body, newBody); | 247 _replaceNodeTokens(oldExpr.body, newBody); |
244 oldExpr.body = newBody; | 248 oldExpr.body = newBody; |
245 } | 249 } |
246 } | 250 } |
247 } else if (addNewTopLevelDeclarations) { | 251 } else if (appendNewTopLevelDeclarations) { |
248 _failIfPublicName(patchDeclaration, name); | 252 _failIfPublicName(patchDeclaration, name); |
249 declarationsToAppend.add(patchDeclaration); | 253 declarationsToAppend.add(patchDeclaration); |
250 } | 254 } |
251 } else if (patchDeclaration is FunctionTypeAlias) { | 255 } else if (patchDeclaration is FunctionTypeAlias) { |
252 if (patchDeclaration.metadata.isNotEmpty) { | 256 if (patchDeclaration.metadata.isNotEmpty) { |
253 _failInPatch('contains a function type alias with an annotation', | 257 _failInPatch('contains a function type alias with an annotation', |
254 patchDeclaration.offset); | 258 patchDeclaration.offset); |
255 } | 259 } |
256 _failIfPublicName(patchDeclaration, patchDeclaration.name.name); | 260 _failIfPublicName(patchDeclaration, patchDeclaration.name.name); |
257 declarationsToAppend.add(patchDeclaration); | 261 declarationsToAppend.add(patchDeclaration); |
258 } else if (patchDeclaration is ClassDeclaration) { | 262 } else if (patchDeclaration is ClassDeclaration) { |
259 if (_hasPatchAnnotation(patchDeclaration.metadata)) { | 263 if (_hasPatchAnnotation(patchDeclaration.metadata)) { |
260 String name = patchDeclaration.name.name; | 264 String name = patchDeclaration.name.name; |
261 for (CompilationUnitMember baseDeclaration in baseUnit.declarations) { | 265 for (CompilationUnitMember baseDeclaration in baseUnit.declarations) { |
262 if (baseDeclaration is ClassDeclaration && | 266 if (baseDeclaration is ClassDeclaration && |
263 baseDeclaration.name.name == name) { | 267 baseDeclaration.name.name == name) { |
264 _patchClassMembers(baseDeclaration, patchDeclaration); | 268 _patchClassMembers(baseDeclaration, patchDeclaration); |
265 } | 269 } |
266 } | 270 } |
267 } else { | 271 } else { |
268 _failIfPublicName(patchDeclaration, patchDeclaration.name.name); | 272 _failIfPublicName(patchDeclaration, patchDeclaration.name.name); |
269 declarationsToAppend.add(patchDeclaration); | 273 declarationsToAppend.add(patchDeclaration); |
270 } | 274 } |
271 } else { | 275 } else { |
272 _failInPatch('contains an unsupported top-level declaration', | 276 _failInPatch('contains an unsupported top-level declaration', |
273 patchDeclaration.offset); | 277 patchDeclaration.offset); |
274 } | 278 } |
275 } | 279 } |
276 // Append new top-level declarations. | 280 // Append new top-level declarations. |
277 _appendToNodeList(baseUnit.declarations, declarationsToAppend, | 281 if (appendNewTopLevelDeclarations) { |
278 baseUnit.endToken.previous); | 282 _appendToNodeList(baseUnit.declarations, declarationsToAppend, |
283 baseUnit.endToken.previous); | |
284 } | |
279 } | 285 } |
280 | 286 |
281 /** | 287 /** |
282 * Parse the given [source] into AST. | 288 * Parse the given [source] into AST. |
283 */ | 289 */ |
284 @visibleForTesting | 290 @visibleForTesting |
285 static CompilationUnit parse( | 291 static CompilationUnit parse( |
286 Source source, bool strong, AnalysisErrorListener errorListener) { | 292 Source source, bool strong, AnalysisErrorListener errorListener) { |
287 String code = source.contents.data; | 293 String code = source.contents.data; |
288 | 294 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
334 } | 340 } |
335 | 341 |
336 /** | 342 /** |
337 * Replace tokens of the [oldNode] with tokens of the [newNode]. | 343 * Replace tokens of the [oldNode] with tokens of the [newNode]. |
338 */ | 344 */ |
339 static void _replaceNodeTokens(AstNode oldNode, AstNode newNode) { | 345 static void _replaceNodeTokens(AstNode oldNode, AstNode newNode) { |
340 oldNode.beginToken.previous.setNext(newNode.beginToken); | 346 oldNode.beginToken.previous.setNext(newNode.beginToken); |
341 newNode.endToken.setNext(oldNode.endToken.next); | 347 newNode.endToken.setNext(oldNode.endToken.next); |
342 } | 348 } |
343 } | 349 } |
OLD | NEW |