Chromium Code Reviews| 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 |