| OLD | NEW |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, 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 import 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:convert'; | 6 import 'dart:convert'; |
| 7 import 'dart:io'; | 7 import 'dart:io'; |
| 8 | 8 |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/ast/visitor.dart'; | 10 import 'package:analyzer/dart/ast/visitor.dart'; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 group('front_end_inference_test', () { | 29 group('front_end_inference_test', () { |
| 30 defineReflectiveSuite(() { | 30 defineReflectiveSuite(() { |
| 31 defineReflectiveTests(RunFrontEndInferenceTest); | 31 defineReflectiveTests(RunFrontEndInferenceTest); |
| 32 }); | 32 }); |
| 33 }, timeout: new Timeout(const Duration(seconds: 120))); | 33 }, timeout: new Timeout(const Duration(seconds: 120))); |
| 34 } | 34 } |
| 35 | 35 |
| 36 /// Set this to `true` to cause expectation comments to be updated. | 36 /// Set this to `true` to cause expectation comments to be updated. |
| 37 const bool fixProblems = false; | 37 const bool fixProblems = false; |
| 38 | 38 |
| 39 void _appendElementName(StringBuffer buffer, Element element) { | |
| 40 // Synthetic FunctionElement(s) don't have a name or enclosing library. | |
| 41 if (element.isSynthetic && element is FunctionElement) { | |
| 42 return; | |
| 43 } | |
| 44 | |
| 45 LibraryElement library = element.library; | |
| 46 if (library == null) { | |
| 47 throw new StateError('Unexpected element without library: $element'); | |
| 48 } | |
| 49 String libraryName = library.name; | |
| 50 | |
| 51 String name = element.name ?? ''; | |
| 52 if (libraryName != 'dart.core' && | |
| 53 libraryName != 'dart.async' && | |
| 54 libraryName != 'test') { | |
| 55 buffer.write('$libraryName::'); | |
| 56 } | |
| 57 var enclosing = element.enclosingElement; | |
| 58 if (enclosing is ClassElement) { | |
| 59 buffer.write('${enclosing.name}::'); | |
| 60 } | |
| 61 buffer.write('$name'); | |
| 62 } | |
| 63 | |
| 64 @reflectiveTest | 39 @reflectiveTest |
| 65 class RunFrontEndInferenceTest { | 40 class RunFrontEndInferenceTest { |
| 66 test_run() async { | 41 test_run() async { |
| 67 String pkgPath = _findPkgRoot(); | 42 String pkgPath = _findPkgRoot(); |
| 68 String fePath = pathos.join(pkgPath, 'front_end', 'testcases', 'inference'); | 43 String fePath = pathos.join(pkgPath, 'front_end', 'testcases', 'inference'); |
| 69 List<File> dartFiles = new Directory(fePath) | 44 List<File> dartFiles = new Directory(fePath) |
| 70 .listSync() | 45 .listSync() |
| 71 .where((entry) => entry is File && entry.path.endsWith('.dart')) | 46 .where((entry) => entry is File && entry.path.endsWith('.dart')) |
| 72 .map((entry) => entry as File) | 47 .map((entry) => entry as File) |
| 73 .toList(); | 48 .toList(); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 102 if (parts[i] == 'pkg' && | 77 if (parts[i] == 'pkg' && |
| 103 parts[i + 1] == 'analyzer' && | 78 parts[i + 1] == 'analyzer' && |
| 104 parts[i + 2] == 'test') { | 79 parts[i + 2] == 'test') { |
| 105 return pathos.joinAll(parts.sublist(0, i + 1)); | 80 return pathos.joinAll(parts.sublist(0, i + 1)); |
| 106 } | 81 } |
| 107 } | 82 } |
| 108 throw new StateError('Unable to find sdk/pkg/ in $scriptPath'); | 83 throw new StateError('Unable to find sdk/pkg/ in $scriptPath'); |
| 109 } | 84 } |
| 110 } | 85 } |
| 111 | 86 |
| 87 class _ElementNamer { |
| 88 final ConstructorElement currentFactoryConstructor; |
| 89 |
| 90 _ElementNamer(this.currentFactoryConstructor); |
| 91 |
| 92 void appendElementName(StringBuffer buffer, Element element) { |
| 93 // Synthetic FunctionElement(s) don't have a name or enclosing library. |
| 94 if (element.isSynthetic && element is FunctionElement) { |
| 95 return; |
| 96 } |
| 97 |
| 98 LibraryElement library = element.library; |
| 99 if (library == null) { |
| 100 throw new StateError('Unexpected element without library: $element'); |
| 101 } |
| 102 String libraryName = library.name; |
| 103 |
| 104 String name = element.name ?? ''; |
| 105 if (libraryName != 'dart.core' && |
| 106 libraryName != 'dart.async' && |
| 107 libraryName != 'test') { |
| 108 buffer.write('$libraryName::'); |
| 109 } |
| 110 var enclosing = element.enclosingElement; |
| 111 if (enclosing is ClassElement) { |
| 112 buffer.write('${enclosing.name}::'); |
| 113 if (currentFactoryConstructor != null && |
| 114 identical(enclosing, currentFactoryConstructor.enclosingElement)) { |
| 115 String factoryConstructorName = currentFactoryConstructor.name; |
| 116 if (factoryConstructorName == '') { |
| 117 factoryConstructorName = '•'; |
| 118 } |
| 119 buffer.write('$factoryConstructorName::'); |
| 120 } |
| 121 } |
| 122 buffer.write('$name'); |
| 123 } |
| 124 } |
| 125 |
| 112 class _FrontEndInferenceTest extends BaseAnalysisDriverTest { | 126 class _FrontEndInferenceTest extends BaseAnalysisDriverTest { |
| 113 Future<String> runTest(String path, String code) async { | 127 Future<String> runTest(String path, String code) async { |
| 114 Uri uri = provider.pathContext.toUri(path); | 128 Uri uri = provider.pathContext.toUri(path); |
| 115 | 129 |
| 116 List<int> lineStarts = new LineInfo.fromContent(code).lineStarts; | 130 List<int> lineStarts = new LineInfo.fromContent(code).lineStarts; |
| 117 fasta.CompilerContext.current.uriToSource[relativizeUri(uri).toString()] = | 131 fasta.CompilerContext.current.uriToSource[relativizeUri(uri).toString()] = |
| 118 new fasta.Source(lineStarts, UTF8.encode(code)); | 132 new fasta.Source(lineStarts, UTF8.encode(code)); |
| 119 | 133 |
| 120 var validation = new fasta.ValidatingInstrumentation(); | 134 var validation = new fasta.ValidatingInstrumentation(); |
| 121 await validation.loadExpectations(uri); | 135 await validation.loadExpectations(uri); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 136 } | 150 } |
| 137 } else { | 151 } else { |
| 138 return null; | 152 return null; |
| 139 } | 153 } |
| 140 } | 154 } |
| 141 } | 155 } |
| 142 | 156 |
| 143 /// Instance of [InstrumentationValue] describing a [MethodElement]. | 157 /// Instance of [InstrumentationValue] describing a [MethodElement]. |
| 144 class _InstrumentationValueForMethodElement extends fasta.InstrumentationValue { | 158 class _InstrumentationValueForMethodElement extends fasta.InstrumentationValue { |
| 145 final MethodElement element; | 159 final MethodElement element; |
| 160 final _ElementNamer elementNamer; |
| 146 | 161 |
| 147 _InstrumentationValueForMethodElement(this.element); | 162 _InstrumentationValueForMethodElement(this.element, this.elementNamer); |
| 148 | 163 |
| 149 @override | 164 @override |
| 150 String toString() { | 165 String toString() { |
| 151 StringBuffer buffer = new StringBuffer(); | 166 StringBuffer buffer = new StringBuffer(); |
| 152 _appendElementName(buffer, element); | 167 elementNamer.appendElementName(buffer, element); |
| 153 return buffer.toString(); | 168 return buffer.toString(); |
| 154 } | 169 } |
| 155 } | 170 } |
| 156 | 171 |
| 157 /** | 172 /** |
| 158 * Instance of [InstrumentationValue] describing a [DartType]. | 173 * Instance of [InstrumentationValue] describing a [DartType]. |
| 159 */ | 174 */ |
| 160 class _InstrumentationValueForType extends fasta.InstrumentationValue { | 175 class _InstrumentationValueForType extends fasta.InstrumentationValue { |
| 161 final DartType type; | 176 final DartType type; |
| 177 final _ElementNamer elementNamer; |
| 162 | 178 |
| 163 _InstrumentationValueForType(this.type); | 179 _InstrumentationValueForType(this.type, this.elementNamer); |
| 164 | 180 |
| 165 @override | 181 @override |
| 166 String toString() { | 182 String toString() { |
| 167 StringBuffer buffer = new StringBuffer(); | 183 StringBuffer buffer = new StringBuffer(); |
| 168 _appendType(buffer, type); | 184 _appendType(buffer, type); |
| 169 return buffer.toString(); | 185 return buffer.toString(); |
| 170 } | 186 } |
| 171 | 187 |
| 172 void _appendList<T>(StringBuffer buffer, String open, String close, | 188 void _appendList<T>(StringBuffer buffer, String open, String close, |
| 173 List<T> items, String separator, writeItem(T item), | 189 List<T> items, String separator, writeItem(T item), |
| (...skipping 22 matching lines...) Expand all Loading... |
| 196 } | 212 } |
| 197 | 213 |
| 198 void _appendType(StringBuffer buffer, DartType type) { | 214 void _appendType(StringBuffer buffer, DartType type) { |
| 199 if (type is FunctionType) { | 215 if (type is FunctionType) { |
| 200 _appendTypeArguments(buffer, type.typeArguments); | 216 _appendTypeArguments(buffer, type.typeArguments); |
| 201 _appendParameters(buffer, type.parameters); | 217 _appendParameters(buffer, type.parameters); |
| 202 buffer.write(' -> '); | 218 buffer.write(' -> '); |
| 203 _appendType(buffer, type.returnType); | 219 _appendType(buffer, type.returnType); |
| 204 } else if (type is InterfaceType) { | 220 } else if (type is InterfaceType) { |
| 205 ClassElement element = type.element; | 221 ClassElement element = type.element; |
| 206 _appendElementName(buffer, element); | 222 elementNamer.appendElementName(buffer, element); |
| 207 _appendTypeArguments(buffer, type.typeArguments); | 223 _appendTypeArguments(buffer, type.typeArguments); |
| 208 } else if (type.isBottom) { | 224 } else if (type.isBottom) { |
| 209 buffer.write('<BottomType>'); | 225 buffer.write('<BottomType>'); |
| 226 } else if (type is TypeParameterType) { |
| 227 elementNamer.appendElementName(buffer, type.element); |
| 210 } else { | 228 } else { |
| 211 buffer.write(type.toString()); | 229 buffer.write(type.toString()); |
| 212 } | 230 } |
| 213 } | 231 } |
| 214 | 232 |
| 215 void _appendTypeArguments(StringBuffer buffer, List<DartType> typeArguments) { | 233 void _appendTypeArguments(StringBuffer buffer, List<DartType> typeArguments) { |
| 216 _appendList<DartType>(buffer, '<', '>', typeArguments, ', ', | 234 _appendList<DartType>(buffer, '<', '>', typeArguments, ', ', |
| 217 (type) => _appendType(buffer, type)); | 235 (type) => _appendType(buffer, type)); |
| 218 } | 236 } |
| 219 } | 237 } |
| 220 | 238 |
| 221 /** | 239 /** |
| 222 * Instance of [InstrumentationValue] describing a list of [DartType]s. | 240 * Instance of [InstrumentationValue] describing a list of [DartType]s. |
| 223 */ | 241 */ |
| 224 class _InstrumentationValueForTypeArgs extends fasta.InstrumentationValue { | 242 class _InstrumentationValueForTypeArgs extends fasta.InstrumentationValue { |
| 225 final List<DartType> types; | 243 final List<DartType> types; |
| 244 final _ElementNamer elementNamer; |
| 226 | 245 |
| 227 const _InstrumentationValueForTypeArgs(this.types); | 246 const _InstrumentationValueForTypeArgs(this.types, this.elementNamer); |
| 228 | 247 |
| 229 @override | 248 @override |
| 230 String toString() => types | 249 String toString() => types |
| 231 .map((type) => new _InstrumentationValueForType(type).toString()) | 250 .map((type) => |
| 251 new _InstrumentationValueForType(type, elementNamer).toString()) |
| 232 .join(', '); | 252 .join(', '); |
| 233 } | 253 } |
| 234 | 254 |
| 235 /** | 255 /** |
| 236 * Visitor for ASTs that reports instrumentation for types. | 256 * Visitor for ASTs that reports instrumentation for types. |
| 237 */ | 257 */ |
| 238 class _InstrumentationVisitor extends RecursiveAstVisitor<Null> { | 258 class _InstrumentationVisitor extends RecursiveAstVisitor<Null> { |
| 239 final fasta.Instrumentation _instrumentation; | 259 final fasta.Instrumentation _instrumentation; |
| 240 final Uri uri; | 260 final Uri uri; |
| 261 _ElementNamer elementNamer = new _ElementNamer(null); |
| 241 | 262 |
| 242 _InstrumentationVisitor(this._instrumentation, this.uri); | 263 _InstrumentationVisitor(this._instrumentation, this.uri); |
| 243 | 264 |
| 244 visitBinaryExpression(BinaryExpression node) { | 265 visitBinaryExpression(BinaryExpression node) { |
| 245 super.visitBinaryExpression(node); | 266 super.visitBinaryExpression(node); |
| 246 _recordMethodTarget(node.operator.charOffset, node.staticElement); | 267 _recordMethodTarget(node.operator.charOffset, node.staticElement); |
| 247 } | 268 } |
| 248 | 269 |
| 270 @override |
| 271 visitConstructorDeclaration(ConstructorDeclaration node) { |
| 272 _ElementNamer oldElementNamer = elementNamer; |
| 273 if (node.factoryKeyword != null) { |
| 274 // Factory constructors are represented in kernel as static methods, so |
| 275 // their type parameters get replicated, e.g.: |
| 276 // class C<T> { |
| 277 // factory C.ctor() { |
| 278 // T t; // Refers to C::T |
| 279 // ... |
| 280 // } |
| 281 // } |
| 282 // gets converted to: |
| 283 // class C<T> { |
| 284 // static C<T> C.ctor<T>() { |
| 285 // T t; // Refers to C::ctor::T |
| 286 // ... |
| 287 // } |
| 288 // } |
| 289 // So to match kernel behavior, we have to arrange for this renaming to |
| 290 // happen during output. |
| 291 elementNamer = new _ElementNamer(node.element); |
| 292 } |
| 293 super.visitConstructorDeclaration(node); |
| 294 elementNamer = oldElementNamer; |
| 295 } |
| 296 |
| 249 visitFunctionExpression(FunctionExpression node) { | 297 visitFunctionExpression(FunctionExpression node) { |
| 250 super.visitFunctionExpression(node); | 298 super.visitFunctionExpression(node); |
| 251 if (node.parent is! FunctionDeclaration) { | 299 if (node.parent is! FunctionDeclaration) { |
| 252 DartType type = node.staticType; | 300 DartType type = node.staticType; |
| 253 if (type is FunctionType) { | 301 if (type is FunctionType) { |
| 254 _instrumentation.record(uri, node.offset, 'returnType', | 302 _instrumentation.record(uri, node.offset, 'returnType', |
| 255 new _InstrumentationValueForType(type.returnType)); | 303 new _InstrumentationValueForType(type.returnType, elementNamer)); |
| 256 List<FormalParameter> parameters = node.parameters.parameters; | 304 List<FormalParameter> parameters = node.parameters.parameters; |
| 257 for (int i = 0; i < parameters.length; i++) { | 305 for (int i = 0; i < parameters.length; i++) { |
| 258 FormalParameter parameter = parameters[i]; | 306 FormalParameter parameter = parameters[i]; |
| 259 if (parameter is SimpleFormalParameter && parameter.type == null) { | 307 if (parameter is SimpleFormalParameter && parameter.type == null) { |
| 260 _recordType(parameter.offset, type.parameters[i].type); | 308 _recordType(parameter.offset, type.parameters[i].type); |
| 261 } | 309 } |
| 262 } | 310 } |
| 263 } | 311 } |
| 264 } | 312 } |
| 265 } | 313 } |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 | 370 |
| 323 visitSimpleIdentifier(SimpleIdentifier node) { | 371 visitSimpleIdentifier(SimpleIdentifier node) { |
| 324 super.visitSimpleIdentifier(node); | 372 super.visitSimpleIdentifier(node); |
| 325 Element element = node.staticElement; | 373 Element element = node.staticElement; |
| 326 void recordPromotions(DartType elementType) { | 374 void recordPromotions(DartType elementType) { |
| 327 if (node.inGetterContext() && !node.inDeclarationContext()) { | 375 if (node.inGetterContext() && !node.inDeclarationContext()) { |
| 328 int offset = node.offset; | 376 int offset = node.offset; |
| 329 DartType type = node.staticType; | 377 DartType type = node.staticType; |
| 330 if (!identical(type, elementType)) { | 378 if (!identical(type, elementType)) { |
| 331 _instrumentation.record(uri, offset, 'promotedType', | 379 _instrumentation.record(uri, offset, 'promotedType', |
| 332 new _InstrumentationValueForType(type)); | 380 new _InstrumentationValueForType(type, elementNamer)); |
| 333 } | 381 } |
| 334 } | 382 } |
| 335 } | 383 } |
| 336 | 384 |
| 337 if (element is LocalVariableElement) { | 385 if (element is LocalVariableElement) { |
| 338 recordPromotions(element.type); | 386 recordPromotions(element.type); |
| 339 } else if (element is ParameterElement) { | 387 } else if (element is ParameterElement) { |
| 340 recordPromotions(element.type); | 388 recordPromotions(element.type); |
| 341 } | 389 } |
| 342 } | 390 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 364 f.typeFormals.isEmpty) { | 412 f.typeFormals.isEmpty) { |
| 365 return _recoverTypeArguments(g, f); | 413 return _recoverTypeArguments(g, f); |
| 366 } else { | 414 } else { |
| 367 return const []; | 415 return const []; |
| 368 } | 416 } |
| 369 } | 417 } |
| 370 | 418 |
| 371 void _recordMethodTarget(int offset, Element element) { | 419 void _recordMethodTarget(int offset, Element element) { |
| 372 if (element is MethodElement) { | 420 if (element is MethodElement) { |
| 373 _instrumentation.record(uri, offset, 'target', | 421 _instrumentation.record(uri, offset, 'target', |
| 374 new _InstrumentationValueForMethodElement(element)); | 422 new _InstrumentationValueForMethodElement(element, elementNamer)); |
| 375 } | 423 } |
| 376 } | 424 } |
| 377 | 425 |
| 378 void _recordTopType(int offset, DartType type) { | 426 void _recordTopType(int offset, DartType type) { |
| 379 _instrumentation.record( | 427 _instrumentation.record(uri, offset, 'topType', |
| 380 uri, offset, 'topType', new _InstrumentationValueForType(type)); | 428 new _InstrumentationValueForType(type, elementNamer)); |
| 381 } | 429 } |
| 382 | 430 |
| 383 void _recordType(int offset, DartType type) { | 431 void _recordType(int offset, DartType type) { |
| 384 _instrumentation.record( | 432 _instrumentation.record(uri, offset, 'type', |
| 385 uri, offset, 'type', new _InstrumentationValueForType(type)); | 433 new _InstrumentationValueForType(type, elementNamer)); |
| 386 } | 434 } |
| 387 | 435 |
| 388 void _recordTypeArguments(int offset, List<DartType> typeArguments) { | 436 void _recordTypeArguments(int offset, List<DartType> typeArguments) { |
| 389 _instrumentation.record(uri, offset, 'typeArgs', | 437 _instrumentation.record(uri, offset, 'typeArgs', |
| 390 new _InstrumentationValueForTypeArgs(typeArguments)); | 438 new _InstrumentationValueForTypeArgs(typeArguments, elementNamer)); |
| 391 } | 439 } |
| 392 | 440 |
| 393 /// Based on DDC code generator's `_recoverTypeArguments` | 441 /// Based on DDC code generator's `_recoverTypeArguments` |
| 394 Iterable<DartType> _recoverTypeArguments(FunctionType g, FunctionType f) { | 442 Iterable<DartType> _recoverTypeArguments(FunctionType g, FunctionType f) { |
| 395 assert(identical(g.element, f.element)); | 443 assert(identical(g.element, f.element)); |
| 396 assert(g.typeFormals.isNotEmpty && f.typeFormals.isEmpty); | 444 assert(g.typeFormals.isNotEmpty && f.typeFormals.isEmpty); |
| 397 assert(g.typeFormals.length + g.typeArguments.length == | 445 assert(g.typeFormals.length + g.typeArguments.length == |
| 398 f.typeArguments.length); | 446 f.typeArguments.length); |
| 399 return f.typeArguments.skip(g.typeArguments.length); | 447 return f.typeArguments.skip(g.typeArguments.length); |
| 400 } | 448 } |
| 401 } | 449 } |
| OLD | NEW |