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 |