Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1896)

Side by Side Diff: pkg/analyzer/lib/src/dart/sdk/patch.dart

Issue 2616993002: Reject patches that change parameter names/types or return types. (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/analyzer/test/src/dart/sdk/patch_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 String loc = _getLocationDesc3(_patchUnit, offset); 92 String loc = _getLocationDesc3(_patchUnit, offset);
93 throw new ArgumentError( 93 throw new ArgumentError(
94 'The patch file $_patchDesc for $_baseDesc $message at $loc.'); 94 'The patch file $_patchDesc for $_baseDesc $message at $loc.');
95 } 95 }
96 96
97 String _getLocationDesc3(CompilationUnit unit, int offset) { 97 String _getLocationDesc3(CompilationUnit unit, int offset) {
98 LineInfo_Location location = unit.lineInfo.getLocation(offset); 98 LineInfo_Location location = unit.lineInfo.getLocation(offset);
99 return 'the line ${location.lineNumber}'; 99 return 'the line ${location.lineNumber}';
100 } 100 }
101 101
102 void _matchParameterLists(FormalParameterList baseParameters,
103 FormalParameterList patchParameters, String context()) {
104 if (baseParameters == null && patchParameters == null) return;
105 if (baseParameters == null || patchParameters == null) {
106 throw new ArgumentError("${context()}, parameter lists don't match");
107 }
108 if (baseParameters.parameters.length != patchParameters.parameters.length) {
109 throw new ArgumentError(
110 '${context()}, parameter lists have different lengths');
111 }
112 for (var i = 0; i < baseParameters.parameters.length; i++) {
113 _matchParameters(baseParameters.parameters[i],
114 patchParameters.parameters[i], () => '${context()}, parameter $i');
115 }
116 }
117
118 void _matchParameters(FormalParameter baseParameter,
119 FormalParameter patchParameter, String whichParameter()) {
120 if (baseParameter.identifier.name != patchParameter.identifier.name) {
121 throw new ArgumentError('${whichParameter()} has different name');
122 }
123 NormalFormalParameter baseParameterWithoutDefault =
124 _withoutDefault(baseParameter);
125 NormalFormalParameter patchParameterWithoutDefault =
126 _withoutDefault(patchParameter);
127 if (baseParameterWithoutDefault is SimpleFormalParameter &&
128 patchParameterWithoutDefault is SimpleFormalParameter) {
129 _matchTypes(baseParameterWithoutDefault.type,
130 patchParameterWithoutDefault.type, () => '${whichParameter()} type');
131 } else if (baseParameterWithoutDefault is FunctionTypedFormalParameter &&
132 patchParameterWithoutDefault is FunctionTypedFormalParameter) {
133 _matchTypes(
134 baseParameterWithoutDefault.returnType,
135 patchParameterWithoutDefault.returnType,
136 () => '${whichParameter()} return type');
137 _matchParameterLists(
138 baseParameterWithoutDefault.parameters,
139 patchParameterWithoutDefault.parameters,
140 () => '${whichParameter()} parameters');
141 } else if (baseParameterWithoutDefault is FieldFormalParameter &&
142 patchParameter is FieldFormalParameter) {
143 throw new ArgumentError(
144 '${whichParameter()} cannot be patched (field formal parameters are no t supported)');
145 } else {
146 throw new ArgumentError(
147 '${whichParameter()} mismatch (different parameter kinds)');
148 }
149 }
150
151 void _matchTypes(TypeName baseType, TypeName patchType, String whichType()) {
152 error() => new ArgumentError("${whichType()} doesn't match");
153 if (baseType == null && patchType == null) return;
154 if (baseType == null || patchType == null) throw error();
155 // Match up the types token by token; this is more restrictive than strictly
156 // necessary, but it's easy and sufficient for patching purposes.
157 Token baseToken = baseType.beginToken;
158 Token patchToken = patchType.beginToken;
159 while (true) {
160 if (baseToken.lexeme != patchToken.lexeme) throw error();
161 if (identical(baseToken, baseType.endToken) &&
162 identical(patchToken, patchType.endToken)) {
163 break;
164 }
165 if (identical(baseToken, baseType.endToken) ||
166 identical(patchToken, patchType.endToken)) {
167 throw error();
168 }
169 baseToken = baseToken.next;
170 patchToken = patchToken.next;
171 }
172 }
173
102 void _patchClassMembers( 174 void _patchClassMembers(
103 ClassDeclaration baseClass, ClassDeclaration patchClass) { 175 ClassDeclaration baseClass, ClassDeclaration patchClass) {
104 String className = baseClass.name.name; 176 String className = baseClass.name.name;
105 List<ClassMember> membersToAppend = []; 177 List<ClassMember> membersToAppend = [];
106 for (ClassMember patchMember in patchClass.members) { 178 for (ClassMember patchMember in patchClass.members) {
107 if (patchMember is FieldDeclaration) { 179 if (patchMember is FieldDeclaration) {
108 if (_hasPatchAnnotation(patchMember.metadata)) { 180 if (_hasPatchAnnotation(patchMember.metadata)) {
109 _failInPatch('attempts to patch a field', patchMember.offset); 181 _failInPatch('attempts to patch a field', patchMember.offset);
110 } 182 }
111 List<VariableDeclaration> fields = patchMember.fields.variables; 183 List<VariableDeclaration> fields = patchMember.fields.variables;
(...skipping 15 matching lines...) Expand all
127 if (baseMember is MethodDeclaration && 199 if (baseMember is MethodDeclaration &&
128 baseMember.name.name == name) { 200 baseMember.name.name == name) {
129 // Remove the "external" keyword. 201 // Remove the "external" keyword.
130 Token externalKeyword = baseMember.externalKeyword; 202 Token externalKeyword = baseMember.externalKeyword;
131 if (externalKeyword != null) { 203 if (externalKeyword != null) {
132 baseMember.externalKeyword = null; 204 baseMember.externalKeyword = null;
133 _removeToken(externalKeyword); 205 _removeToken(externalKeyword);
134 } else { 206 } else {
135 _failExternalKeyword(name, baseMember.offset); 207 _failExternalKeyword(name, baseMember.offset);
136 } 208 }
209 _matchParameterLists(
210 baseMember.parameters,
211 patchMember.parameters,
212 () => 'While patching $className.$name');
213 _matchTypes(baseMember.returnType, patchMember.returnType,
214 () => 'While patching $className.$name, return type');
137 // Replace the body. 215 // Replace the body.
138 FunctionBody oldBody = baseMember.body; 216 FunctionBody oldBody = baseMember.body;
139 FunctionBody newBody = patchMember.body; 217 FunctionBody newBody = patchMember.body;
140 _replaceNodeTokens(oldBody, newBody); 218 _replaceNodeTokens(oldBody, newBody);
141 baseMember.body = newBody; 219 baseMember.body = newBody;
142 } 220 }
143 } 221 }
144 } else { 222 } else {
145 _failIfPublicName(patchMember, name); 223 _failIfPublicName(patchMember, name);
146 membersToAppend.add(patchMember); 224 membersToAppend.add(patchMember);
(...skipping 23 matching lines...) Expand all
170 _failInPatch( 248 _failInPatch(
171 'attempts to replace factory constructor with a generative o ne', 249 'attempts to replace factory constructor with a generative o ne',
172 patchMember.offset); 250 patchMember.offset);
173 } 251 }
174 // The base constructor should not have initializers. 252 // The base constructor should not have initializers.
175 if (baseMember.initializers.isNotEmpty) { 253 if (baseMember.initializers.isNotEmpty) {
176 throw new ArgumentError( 254 throw new ArgumentError(
177 'Cannot patch external constructors with initializers ' 255 'Cannot patch external constructors with initializers '
178 'in $_baseDesc.'); 256 'in $_baseDesc.');
179 } 257 }
258 _matchParameterLists(
259 baseMember.parameters, patchMember.parameters, () {
260 String nameSuffix = name == null ? '' : '.$name';
261 return 'While patching $className$nameSuffix';
262 });
180 // Prepare nodes. 263 // Prepare nodes.
181 FunctionBody baseBody = baseMember.body; 264 FunctionBody baseBody = baseMember.body;
182 FunctionBody patchBody = patchMember.body; 265 FunctionBody patchBody = patchMember.body;
183 NodeList<ConstructorInitializer> baseInitializers = 266 NodeList<ConstructorInitializer> baseInitializers =
184 baseMember.initializers; 267 baseMember.initializers;
185 NodeList<ConstructorInitializer> patchInitializers = 268 NodeList<ConstructorInitializer> patchInitializers =
186 patchMember.initializers; 269 patchMember.initializers;
187 // Replace initializers and link tokens. 270 // Replace initializers and link tokens.
188 if (patchInitializers.isNotEmpty) { 271 if (patchInitializers.isNotEmpty) {
189 baseMember.parameters.endToken 272 baseMember.parameters.endToken
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 baseDeclaration is FunctionDeclaration && 326 baseDeclaration is FunctionDeclaration &&
244 baseDeclaration.name.name == name) { 327 baseDeclaration.name.name == name) {
245 // Remove the "external" keyword. 328 // Remove the "external" keyword.
246 Token externalKeyword = baseDeclaration.externalKeyword; 329 Token externalKeyword = baseDeclaration.externalKeyword;
247 if (externalKeyword != null) { 330 if (externalKeyword != null) {
248 baseDeclaration.externalKeyword = null; 331 baseDeclaration.externalKeyword = null;
249 _removeToken(externalKeyword); 332 _removeToken(externalKeyword);
250 } else { 333 } else {
251 _failExternalKeyword(name, baseDeclaration.offset); 334 _failExternalKeyword(name, baseDeclaration.offset);
252 } 335 }
336 _matchParameterLists(
337 baseDeclaration.functionExpression.parameters,
338 patchDeclaration.functionExpression.parameters,
339 () => 'While patching $name');
340 _matchTypes(
341 baseDeclaration.returnType,
342 patchDeclaration.returnType,
343 () => 'While patching $name, return type');
253 // Replace the body. 344 // Replace the body.
254 FunctionExpression oldExpr = baseDeclaration.functionExpression; 345 FunctionExpression oldExpr = baseDeclaration.functionExpression;
255 FunctionBody newBody = patchDeclaration.functionExpression.body; 346 FunctionBody newBody = patchDeclaration.functionExpression.body;
256 _replaceNodeTokens(oldExpr.body, newBody); 347 _replaceNodeTokens(oldExpr.body, newBody);
257 oldExpr.body = newBody; 348 oldExpr.body = newBody;
258 } 349 }
259 } 350 }
260 } else if (appendNewTopLevelDeclarations) { 351 } else if (appendNewTopLevelDeclarations) {
261 _failIfPublicName(patchDeclaration, name); 352 _failIfPublicName(patchDeclaration, name);
262 declarationsToAppend.add(patchDeclaration); 353 declarationsToAppend.add(patchDeclaration);
(...skipping 30 matching lines...) Expand all
293 patchDeclaration.offset); 384 patchDeclaration.offset);
294 } 385 }
295 } 386 }
296 // Append new top-level declarations. 387 // Append new top-level declarations.
297 if (appendNewTopLevelDeclarations) { 388 if (appendNewTopLevelDeclarations) {
298 _appendToNodeList(baseUnit.declarations, declarationsToAppend, 389 _appendToNodeList(baseUnit.declarations, declarationsToAppend,
299 baseUnit.endToken.previous); 390 baseUnit.endToken.previous);
300 } 391 }
301 } 392 }
302 393
394 NormalFormalParameter _withoutDefault(FormalParameter parameter) {
395 if (parameter is NormalFormalParameter) {
396 return parameter;
397 } else if (parameter is DefaultFormalParameter) {
398 return parameter.parameter;
399 } else {
400 // Should not happen.
401 assert(false);
402 return null;
403 }
404 }
405
303 /** 406 /**
304 * Parse the given [source] into AST. 407 * Parse the given [source] into AST.
305 */ 408 */
306 @visibleForTesting 409 @visibleForTesting
307 static CompilationUnit parse( 410 static CompilationUnit parse(
308 Source source, bool strong, AnalysisErrorListener errorListener) { 411 Source source, bool strong, AnalysisErrorListener errorListener) {
309 String code = source.contents.data; 412 String code = source.contents.data;
310 413
311 CharSequenceReader reader = new CharSequenceReader(code); 414 CharSequenceReader reader = new CharSequenceReader(code);
312 Scanner scanner = new Scanner(source, reader, errorListener); 415 Scanner scanner = new Scanner(source, reader, errorListener);
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 } 459 }
357 460
358 /** 461 /**
359 * Replace tokens of the [oldNode] with tokens of the [newNode]. 462 * Replace tokens of the [oldNode] with tokens of the [newNode].
360 */ 463 */
361 static void _replaceNodeTokens(AstNode oldNode, AstNode newNode) { 464 static void _replaceNodeTokens(AstNode oldNode, AstNode newNode) {
362 oldNode.beginToken.previous.setNext(newNode.beginToken); 465 oldNode.beginToken.previous.setNext(newNode.beginToken);
363 newNode.endToken.setNext(oldNode.endToken.next); 466 newNode.endToken.setNext(oldNode.endToken.next);
364 } 467 }
365 } 468 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer/test/src/dart/sdk/patch_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698