| 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 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |