Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library analyzer.src.dart.sdk.patch; | |
| 6 | |
| 7 import 'package:analyzer/dart/ast/ast.dart'; | |
| 8 import 'package:analyzer/dart/ast/token.dart'; | |
| 9 import 'package:analyzer/error/listener.dart'; | |
| 10 import 'package:analyzer/file_system/file_system.dart'; | |
| 11 import 'package:analyzer/src/dart/scanner/reader.dart'; | |
| 12 import 'package:analyzer/src/dart/scanner/scanner.dart'; | |
| 13 import 'package:analyzer/src/dart/sdk/sdk.dart'; | |
| 14 import 'package:analyzer/src/generated/parser.dart'; | |
| 15 import 'package:analyzer/src/generated/sdk.dart'; | |
| 16 import 'package:analyzer/src/generated/source.dart'; | |
| 17 import 'package:meta/meta.dart'; | |
| 18 import 'package:path/src/context.dart'; | |
| 19 | |
| 20 /** | |
| 21 * [SdkPatcher] applies patches to SDK [CompilationUnit]. | |
| 22 */ | |
| 23 class SdkPatcher { | |
| 24 /** | |
| 25 * Patch the given [unit] of a SDK [source] with the patches defined in | |
| 26 * the [sdk] for the given [platform]. Throw [ArgumentError] if a patch | |
| 27 * file cannot be read, or the contents violates rules for patch files. | |
| 28 * | |
| 29 * If [addNewTopLevelDeclarations] is `true`, then the [unit] is the | |
| 30 * defining unit of a library, so new top-level declarations should be | |
| 31 * added to this unit. For parts new declarations may be added only to the | |
| 32 * patched classes. | |
| 33 * | |
| 34 * TODO(scheglov) auto-detect [addNewTopLevelDeclarations] | |
| 35 */ | |
| 36 void patch(DartSdk sdk, int platform, AnalysisErrorListener errorListener, | |
| 37 Source source, CompilationUnit unit, | |
| 38 {bool addNewTopLevelDeclarations: true}) { | |
| 39 if (sdk is FolderBasedDartSdk) { | |
|
Paul Berry
2016/10/11 20:24:45
Will sdk ever not be FolderBasedDartSdk? What sho
scheglov
2016/10/11 20:34:08
Done.
| |
| 40 // Prepare the patch files to apply. | |
| 41 List<String> patchPaths; | |
| 42 { | |
| 43 String uriStr = source.uri.toString(); | |
| 44 SdkLibrary sdkLibrary = sdk.getSdkLibrary(uriStr); | |
|
Paul Berry
2016/10/11 20:24:45
When patching a file that is a "part" of an SDK li
scheglov
2016/10/11 20:34:08
I will add TODO for now.
| |
| 45 if (sdkLibrary == null) { | |
| 46 throw new ArgumentError( | |
| 47 'The library $uriStr is not defined in the SDK.'); | |
| 48 } | |
| 49 patchPaths = sdkLibrary.getPatches(platform); | |
| 50 } | |
| 51 | |
| 52 bool strongMode = sdk.analysisOptions.strongMode; | |
| 53 Context pathContext = sdk.resourceProvider.pathContext; | |
| 54 for (String path in patchPaths) { | |
| 55 String pathInLib = pathContext.joinAll(path.split('/')); | |
| 56 File patchFile = sdk.libraryDirectory.getChildAssumingFile(pathInLib); | |
| 57 if (!patchFile.exists) { | |
| 58 throw new ArgumentError( | |
| 59 'The patch file ${patchFile.path} does not exist.'); | |
| 60 } | |
| 61 Source patchSource = patchFile.createSource(); | |
| 62 CompilationUnit patchUnit = | |
| 63 parse(patchSource, strongMode, errorListener); | |
| 64 _patchTopLevelDeclarations( | |
| 65 source, unit, patchSource, patchUnit, addNewTopLevelDeclarations); | |
| 66 } | |
| 67 } | |
| 68 } | |
| 69 | |
| 70 void _failExternalKeyword(Source source, String name, int offset) { | |
| 71 throw new ArgumentError( | |
| 72 'The keyword "external" was expected for "$name" in $source @ $offset.') ; | |
| 73 } | |
| 74 | |
| 75 void _patchTopLevelDeclarations( | |
| 76 Source baseSource, | |
| 77 CompilationUnit baseUnit, | |
| 78 Source patchSource, | |
| 79 CompilationUnit patchUnit, | |
| 80 bool addNewTopLevelDeclarations) { | |
| 81 List<CompilationUnitMember> declarationsToAppend = []; | |
| 82 for (CompilationUnitMember patchDeclaration in patchUnit.declarations) { | |
| 83 if (patchDeclaration is FunctionDeclaration) { | |
| 84 String name = patchDeclaration.name.name; | |
| 85 if (_hasPatchAnnotation(patchDeclaration.metadata)) { | |
| 86 for (CompilationUnitMember baseDeclaration in baseUnit.declarations) { | |
| 87 if (patchDeclaration is FunctionDeclaration && | |
| 88 baseDeclaration is FunctionDeclaration && | |
| 89 baseDeclaration.name.name == name) { | |
| 90 if (_hasPatchAnnotation(patchDeclaration.metadata)) { | |
| 91 // Remove the "external" keyword. | |
| 92 if (baseDeclaration.externalKeyword != null) { | |
| 93 baseDeclaration.externalKeyword = null; | |
| 94 } else { | |
| 95 _failExternalKeyword( | |
| 96 baseSource, name, baseDeclaration.offset); | |
| 97 } | |
| 98 // Replace the body. | |
| 99 baseDeclaration.functionExpression.body = | |
| 100 patchDeclaration.functionExpression.body; | |
| 101 } | |
| 102 } | |
| 103 } | |
| 104 } else if (addNewTopLevelDeclarations) { | |
| 105 // No @patch, must be private. | |
| 106 if (!Identifier.isPrivateName(name)) { | |
| 107 throw new ArgumentError( | |
| 108 'The patch file $patchSource attempts to append ' | |
| 109 'a non-private declaration "$name".'); | |
| 110 } | |
| 111 declarationsToAppend.add(patchDeclaration); | |
| 112 } | |
| 113 } | |
| 114 } | |
| 115 // Append new top-level declarations. | |
| 116 baseUnit.declarations.addAll(declarationsToAppend); | |
| 117 } | |
| 118 | |
| 119 /** | |
| 120 * Parse the given [source] into AST. | |
| 121 */ | |
| 122 @visibleForTesting | |
| 123 static CompilationUnit parse( | |
| 124 Source source, bool strong, AnalysisErrorListener errorListener) { | |
| 125 String code = source.contents.data; | |
| 126 | |
| 127 CharSequenceReader reader = new CharSequenceReader(code); | |
| 128 Scanner scanner = new Scanner(source, reader, errorListener); | |
| 129 scanner.scanGenericMethodComments = strong; | |
| 130 Token token = scanner.tokenize(); | |
| 131 LineInfo lineInfo = new LineInfo(scanner.lineStarts); | |
| 132 | |
| 133 Parser parser = new Parser(source, errorListener); | |
| 134 parser.parseGenericMethodComments = strong; | |
| 135 CompilationUnit unit = parser.parseCompilationUnit(token); | |
| 136 unit.lineInfo = lineInfo; | |
| 137 return unit; | |
| 138 } | |
| 139 | |
| 140 /** | |
| 141 * Return `true` if [metadata] has the `@patch` annotation. | |
| 142 */ | |
| 143 static bool _hasPatchAnnotation(List<Annotation> metadata) { | |
| 144 return metadata.any((annotation) { | |
| 145 Identifier name = annotation.name; | |
| 146 return annotation.constructorName == null && | |
| 147 name is SimpleIdentifier && | |
| 148 name.name == 'patch'; | |
| 149 }); | |
| 150 } | |
| 151 } | |
| OLD | NEW |