| 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'; |
| 11 import 'package:analyzer/src/dart/scanner/reader.dart'; | 11 import 'package:analyzer/src/dart/scanner/reader.dart'; |
| 12 import 'package:analyzer/src/dart/scanner/scanner.dart'; | 12 import 'package:analyzer/src/dart/scanner/scanner.dart'; |
| 13 import 'package:analyzer/src/dart/sdk/sdk.dart'; | 13 import 'package:analyzer/src/dart/sdk/sdk.dart'; |
| 14 import 'package:analyzer/src/generated/parser.dart'; | 14 import 'package:analyzer/src/generated/parser.dart'; |
| 15 import 'package:analyzer/src/generated/sdk.dart'; | 15 import 'package:analyzer/src/generated/sdk.dart'; |
| 16 import 'package:analyzer/src/generated/source.dart'; | 16 import 'package:analyzer/src/generated/source.dart'; |
| 17 import 'package:meta/meta.dart'; | 17 import 'package:meta/meta.dart'; |
| 18 import 'package:path/src/context.dart'; | 18 import 'package:path/src/context.dart'; |
| 19 | 19 |
| 20 /** | 20 /** |
| 21 * [SdkPatcher] applies patches to SDK [CompilationUnit]. | 21 * [SdkPatcher] applies patches to SDK [CompilationUnit]. |
| 22 */ | 22 */ |
| 23 class SdkPatcher { | 23 class SdkPatcher { |
| 24 String _baseDesc; |
| 25 String _patchDesc; |
| 26 CompilationUnit _patchUnit; |
| 27 |
| 24 /** | 28 /** |
| 25 * 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 |
| 26 * the [sdk] for the given [platform]. Throw [ArgumentError] if a patch | 30 * the [sdk] for the given [platform]. Throw [ArgumentError] if a patch |
| 27 * 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. |
| 28 * | 32 * |
| 29 * If [addNewTopLevelDeclarations] is `true`, then the [unit] is the | 33 * If [addNewTopLevelDeclarations] is `true`, then the [unit] is the |
| 30 * defining unit of a library, so new top-level declarations should be | 34 * 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 | 35 * added to this unit. For parts new declarations may be added only to the |
| 32 * patched classes. | 36 * patched classes. |
| 33 * | 37 * |
| (...skipping 15 matching lines...) Expand all Loading... |
| 49 patchPaths = sdkLibrary.getPatches(platform); | 53 patchPaths = sdkLibrary.getPatches(platform); |
| 50 } | 54 } |
| 51 | 55 |
| 52 bool strongMode = sdk.analysisOptions.strongMode; | 56 bool strongMode = sdk.analysisOptions.strongMode; |
| 53 Context pathContext = sdk.resourceProvider.pathContext; | 57 Context pathContext = sdk.resourceProvider.pathContext; |
| 54 for (String path in patchPaths) { | 58 for (String path in patchPaths) { |
| 55 String pathInLib = pathContext.joinAll(path.split('/')); | 59 String pathInLib = pathContext.joinAll(path.split('/')); |
| 56 File patchFile = sdk.libraryDirectory.getChildAssumingFile(pathInLib); | 60 File patchFile = sdk.libraryDirectory.getChildAssumingFile(pathInLib); |
| 57 if (!patchFile.exists) { | 61 if (!patchFile.exists) { |
| 58 throw new ArgumentError( | 62 throw new ArgumentError( |
| 59 'The patch file ${patchFile.path} does not exist.'); | 63 'The patch file ${patchFile.path} for $source does not exist.'); |
| 60 } | 64 } |
| 61 Source patchSource = patchFile.createSource(); | 65 Source patchSource = patchFile.createSource(); |
| 62 CompilationUnit patchUnit = parse(patchSource, strongMode, errorListener); | 66 CompilationUnit patchUnit = parse(patchSource, strongMode, errorListener); |
| 67 |
| 68 // Prepare for reporting errors. |
| 69 _baseDesc = source.toString(); |
| 70 _patchDesc = patchFile.path; |
| 71 _patchUnit = patchUnit; |
| 72 |
| 73 _patchDirectives( |
| 74 source, unit, patchSource, patchUnit, addNewTopLevelDeclarations); |
| 63 _patchTopLevelDeclarations( | 75 _patchTopLevelDeclarations( |
| 64 source, unit, patchSource, patchUnit, addNewTopLevelDeclarations); | 76 source, unit, patchSource, patchUnit, addNewTopLevelDeclarations); |
| 65 } | 77 } |
| 66 } | 78 } |
| 67 | 79 |
| 68 void _failExternalKeyword(Source source, String name, int offset) { | 80 void _failExternalKeyword(Source source, String name, int offset) { |
| 69 throw new ArgumentError( | 81 throw new ArgumentError( |
| 70 'The keyword "external" was expected for "$name" in $source @ $offset.')
; | 82 'The keyword "external" was expected for "$name" in $source @ $offset.')
; |
| 71 } | 83 } |
| 72 | 84 |
| 85 void _failIfPublicName(AstNode node, String name) { |
| 86 if (!Identifier.isPrivateName(name)) { |
| 87 _failInPatch('contains a public declaration "$name"', node.offset); |
| 88 } |
| 89 } |
| 90 |
| 91 void _failInPatch(String message, int offset) { |
| 92 String loc = _getLocationDesc3(_patchUnit, offset); |
| 93 throw new ArgumentError( |
| 94 'The patch file $_patchDesc for $_baseDesc $message at $loc.'); |
| 95 } |
| 96 |
| 97 String _getLocationDesc3(CompilationUnit unit, int offset) { |
| 98 LineInfo_Location location = unit.lineInfo.getLocation(offset); |
| 99 return 'the line ${location.lineNumber}'; |
| 100 } |
| 101 |
| 102 void _patchDirectives( |
| 103 Source baseSource, |
| 104 CompilationUnit baseUnit, |
| 105 Source patchSource, |
| 106 CompilationUnit patchUnit, |
| 107 bool addNewTopLevelDeclarations) { |
| 108 for (Directive patchDirective in patchUnit.directives) { |
| 109 if (patchDirective is ImportDirective) { |
| 110 baseUnit.directives.add(patchDirective); |
| 111 } else { |
| 112 _failInPatch('contains an unsupported "$patchDirective" directive', |
| 113 patchDirective.offset); |
| 114 } |
| 115 } |
| 116 } |
| 117 |
| 73 void _patchTopLevelDeclarations( | 118 void _patchTopLevelDeclarations( |
| 74 Source baseSource, | 119 Source baseSource, |
| 75 CompilationUnit baseUnit, | 120 CompilationUnit baseUnit, |
| 76 Source patchSource, | 121 Source patchSource, |
| 77 CompilationUnit patchUnit, | 122 CompilationUnit patchUnit, |
| 78 bool addNewTopLevelDeclarations) { | 123 bool addNewTopLevelDeclarations) { |
| 79 List<CompilationUnitMember> declarationsToAppend = []; | 124 List<CompilationUnitMember> declarationsToAppend = []; |
| 80 for (CompilationUnitMember patchDeclaration in patchUnit.declarations) { | 125 for (CompilationUnitMember patchDeclaration in patchUnit.declarations) { |
| 81 if (patchDeclaration is FunctionDeclaration) { | 126 if (patchDeclaration is FunctionDeclaration) { |
| 82 String name = patchDeclaration.name.name; | 127 String name = patchDeclaration.name.name; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 93 _failExternalKeyword( | 138 _failExternalKeyword( |
| 94 baseSource, name, baseDeclaration.offset); | 139 baseSource, name, baseDeclaration.offset); |
| 95 } | 140 } |
| 96 // Replace the body. | 141 // Replace the body. |
| 97 baseDeclaration.functionExpression.body = | 142 baseDeclaration.functionExpression.body = |
| 98 patchDeclaration.functionExpression.body; | 143 patchDeclaration.functionExpression.body; |
| 99 } | 144 } |
| 100 } | 145 } |
| 101 } | 146 } |
| 102 } else if (addNewTopLevelDeclarations) { | 147 } else if (addNewTopLevelDeclarations) { |
| 103 // No @patch, must be private. | 148 _failIfPublicName(patchDeclaration, name); |
| 104 if (!Identifier.isPrivateName(name)) { | |
| 105 throw new ArgumentError( | |
| 106 'The patch file $patchSource attempts to append ' | |
| 107 'a non-private declaration "$name".'); | |
| 108 } | |
| 109 declarationsToAppend.add(patchDeclaration); | 149 declarationsToAppend.add(patchDeclaration); |
| 110 } | 150 } |
| 151 } else if (patchDeclaration is FunctionTypeAlias) { |
| 152 if (patchDeclaration.metadata.isNotEmpty) { |
| 153 _failInPatch('contains a function type alias with an annotation', |
| 154 patchDeclaration.offset); |
| 155 } |
| 156 _failIfPublicName(patchDeclaration, patchDeclaration.name.name); |
| 157 declarationsToAppend.add(patchDeclaration); |
| 158 } else { |
| 159 _failInPatch('contains an unsupported top-level declaration', |
| 160 patchDeclaration.offset); |
| 111 } | 161 } |
| 112 } | 162 } |
| 113 // Append new top-level declarations. | 163 // Append new top-level declarations. |
| 114 baseUnit.declarations.addAll(declarationsToAppend); | 164 baseUnit.declarations.addAll(declarationsToAppend); |
| 115 } | 165 } |
| 116 | 166 |
| 117 /** | 167 /** |
| 118 * Parse the given [source] into AST. | 168 * Parse the given [source] into AST. |
| 119 */ | 169 */ |
| 120 @visibleForTesting | 170 @visibleForTesting |
| (...skipping 19 matching lines...) Expand all Loading... |
| 140 */ | 190 */ |
| 141 static bool _hasPatchAnnotation(List<Annotation> metadata) { | 191 static bool _hasPatchAnnotation(List<Annotation> metadata) { |
| 142 return metadata.any((annotation) { | 192 return metadata.any((annotation) { |
| 143 Identifier name = annotation.name; | 193 Identifier name = annotation.name; |
| 144 return annotation.constructorName == null && | 194 return annotation.constructorName == null && |
| 145 name is SimpleIdentifier && | 195 name is SimpleIdentifier && |
| 146 name.name == 'patch'; | 196 name.name == 'patch'; |
| 147 }); | 197 }); |
| 148 } | 198 } |
| 149 } | 199 } |
| OLD | NEW |