| 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 12 matching lines...) Expand all Loading... |
| 23 import 'package:test_reflective_loader/test_reflective_loader.dart'; | 23 import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| 24 | 24 |
| 25 import '../../dart/analysis/base.dart'; | 25 import '../../dart/analysis/base.dart'; |
| 26 | 26 |
| 27 main() { | 27 main() { |
| 28 // Use a group() wrapper to specify the timeout. | 28 // Use a group() wrapper to specify the timeout. |
| 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: 60))); | 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 |
| 39 @reflectiveTest | 64 @reflectiveTest |
| 40 class RunFrontEndInferenceTest { | 65 class RunFrontEndInferenceTest { |
| 41 test_run() async { | 66 test_run() async { |
| 42 String pkgPath = _findPkgRoot(); | 67 String pkgPath = _findPkgRoot(); |
| 43 String fePath = pathos.join(pkgPath, 'front_end', 'testcases', 'inference'); | 68 String fePath = pathos.join(pkgPath, 'front_end', 'testcases', 'inference'); |
| 44 List<File> dartFiles = new Directory(fePath) | 69 List<File> dartFiles = new Directory(fePath) |
| 45 .listSync() | 70 .listSync() |
| 46 .where((entry) => entry is File && entry.path.endsWith('.dart')) | 71 .where((entry) => entry is File && entry.path.endsWith('.dart')) |
| 47 .map((entry) => entry as File) | 72 .map((entry) => entry as File) |
| 48 .toList(); | 73 .toList(); |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 return null; | 133 return null; |
| 109 } else { | 134 } else { |
| 110 return validation.problemsAsString; | 135 return validation.problemsAsString; |
| 111 } | 136 } |
| 112 } else { | 137 } else { |
| 113 return null; | 138 return null; |
| 114 } | 139 } |
| 115 } | 140 } |
| 116 } | 141 } |
| 117 | 142 |
| 143 /// Instance of [InstrumentationValue] describing a [MethodElement]. |
| 144 class _InstrumentationValueForMethodElement extends fasta.InstrumentationValue { |
| 145 final MethodElement element; |
| 146 |
| 147 _InstrumentationValueForMethodElement(this.element); |
| 148 |
| 149 @override |
| 150 String toString() { |
| 151 StringBuffer buffer = new StringBuffer(); |
| 152 _appendElementName(buffer, element); |
| 153 return buffer.toString(); |
| 154 } |
| 155 } |
| 156 |
| 118 /** | 157 /** |
| 119 * Instance of [InstrumentationValue] describing a [DartType]. | 158 * Instance of [InstrumentationValue] describing a [DartType]. |
| 120 */ | 159 */ |
| 121 class _InstrumentationValueForType extends fasta.InstrumentationValue { | 160 class _InstrumentationValueForType extends fasta.InstrumentationValue { |
| 122 final DartType type; | 161 final DartType type; |
| 123 | 162 |
| 124 _InstrumentationValueForType(this.type); | 163 _InstrumentationValueForType(this.type); |
| 125 | 164 |
| 126 @override | 165 @override |
| 127 String toString() { | 166 String toString() { |
| 128 StringBuffer buffer = new StringBuffer(); | 167 StringBuffer buffer = new StringBuffer(); |
| 129 _appendType(buffer, type); | 168 _appendType(buffer, type); |
| 130 return buffer.toString(); | 169 return buffer.toString(); |
| 131 } | 170 } |
| 132 | 171 |
| 133 void _appendElementName(StringBuffer buffer, Element element) { | |
| 134 // Synthetic FunctionElement(s) don't have a name or enclosing library. | |
| 135 if (element.isSynthetic && element is FunctionElement) { | |
| 136 return; | |
| 137 } | |
| 138 | |
| 139 LibraryElement library = element.library; | |
| 140 if (library == null) { | |
| 141 throw new StateError('Unexpected element without library: $element'); | |
| 142 } | |
| 143 String libraryName = library.name; | |
| 144 | |
| 145 String name = element.name ?? ''; | |
| 146 if (libraryName != 'dart.core' && | |
| 147 libraryName != 'dart.async' && | |
| 148 libraryName != 'test') { | |
| 149 buffer.write('$libraryName::$name'); | |
| 150 } else { | |
| 151 buffer.write('$name'); | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 void _appendList<T>(StringBuffer buffer, String open, String close, | 172 void _appendList<T>(StringBuffer buffer, String open, String close, |
| 156 List<T> items, String separator, writeItem(T item), | 173 List<T> items, String separator, writeItem(T item), |
| 157 {bool includeEmpty: false}) { | 174 {bool includeEmpty: false}) { |
| 158 if (!includeEmpty && items.isEmpty) { | 175 if (!includeEmpty && items.isEmpty) { |
| 159 return; | 176 return; |
| 160 } | 177 } |
| 161 buffer.write(open); | 178 buffer.write(open); |
| 162 bool first = true; | 179 bool first = true; |
| 163 for (T item in items) { | 180 for (T item in items) { |
| 164 if (!first) { | 181 if (!first) { |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 217 | 234 |
| 218 /** | 235 /** |
| 219 * Visitor for ASTs that reports instrumentation for types. | 236 * Visitor for ASTs that reports instrumentation for types. |
| 220 */ | 237 */ |
| 221 class _InstrumentationVisitor extends RecursiveAstVisitor<Null> { | 238 class _InstrumentationVisitor extends RecursiveAstVisitor<Null> { |
| 222 final fasta.Instrumentation _instrumentation; | 239 final fasta.Instrumentation _instrumentation; |
| 223 final Uri uri; | 240 final Uri uri; |
| 224 | 241 |
| 225 _InstrumentationVisitor(this._instrumentation, this.uri); | 242 _InstrumentationVisitor(this._instrumentation, this.uri); |
| 226 | 243 |
| 244 visitBinaryExpression(BinaryExpression node) { |
| 245 super.visitBinaryExpression(node); |
| 246 _recordMethodTarget(node.operator.charOffset, node.staticElement); |
| 247 } |
| 248 |
| 227 visitFunctionExpression(FunctionExpression node) { | 249 visitFunctionExpression(FunctionExpression node) { |
| 228 super.visitFunctionExpression(node); | 250 super.visitFunctionExpression(node); |
| 229 if (node.parent is! FunctionDeclaration) { | 251 if (node.parent is! FunctionDeclaration) { |
| 230 DartType type = node.staticType; | 252 DartType type = node.staticType; |
| 231 if (type is FunctionType) { | 253 if (type is FunctionType) { |
| 232 _instrumentation.record(uri, node.offset, 'returnType', | 254 _instrumentation.record(uri, node.offset, 'returnType', |
| 233 new _InstrumentationValueForType(type.returnType)); | 255 new _InstrumentationValueForType(type.returnType)); |
| 234 List<FormalParameter> parameters = node.parameters.parameters; | 256 List<FormalParameter> parameters = node.parameters.parameters; |
| 235 for (int i = 0; i < parameters.length; i++) { | 257 for (int i = 0; i < parameters.length; i++) { |
| 236 FormalParameter parameter = parameters[i]; | 258 FormalParameter parameter = parameters[i]; |
| 237 if (parameter is SimpleFormalParameter && parameter.type == null) { | 259 if (parameter is SimpleFormalParameter && parameter.type == null) { |
| 238 _recordType(parameter.offset, type.parameters[i].type); | 260 _recordType(parameter.offset, type.parameters[i].type); |
| 239 } | 261 } |
| 240 } | 262 } |
| 241 } | 263 } |
| 242 } | 264 } |
| 243 } | 265 } |
| 244 | 266 |
| 267 visitIndexExpression(IndexExpression node) { |
| 268 super.visitIndexExpression(node); |
| 269 _recordMethodTarget(node.leftBracket.charOffset, node.staticElement); |
| 270 } |
| 271 |
| 245 visitInstanceCreationExpression(InstanceCreationExpression node) { | 272 visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 246 super.visitInstanceCreationExpression(node); | 273 super.visitInstanceCreationExpression(node); |
| 247 DartType type = node.staticType; | 274 DartType type = node.staticType; |
| 248 if (type is InterfaceType) { | 275 if (type is InterfaceType) { |
| 249 if (type.typeParameters.isNotEmpty && | 276 if (type.typeParameters.isNotEmpty && |
| 250 node.constructorName.type.typeArguments == null) { | 277 node.constructorName.type.typeArguments == null) { |
| 251 _recordTypeArguments(node.constructorName.offset, type.typeArguments); | 278 _recordTypeArguments(node.constructorName.offset, type.typeArguments); |
| 252 } | 279 } |
| 253 } | 280 } |
| 254 } | 281 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 268 if (node.typeArguments == null) { | 295 if (node.typeArguments == null) { |
| 269 DartType type = node.staticType; | 296 DartType type = node.staticType; |
| 270 if (type is InterfaceType) { | 297 if (type is InterfaceType) { |
| 271 _recordTypeArguments(node.offset, type.typeArguments); | 298 _recordTypeArguments(node.offset, type.typeArguments); |
| 272 } | 299 } |
| 273 } | 300 } |
| 274 } | 301 } |
| 275 | 302 |
| 276 visitMethodInvocation(MethodInvocation node) { | 303 visitMethodInvocation(MethodInvocation node) { |
| 277 super.visitMethodInvocation(node); | 304 super.visitMethodInvocation(node); |
| 305 _recordMethodTarget(node.methodName.offset, node.methodName.staticElement); |
| 278 if (node.typeArguments == null) { | 306 if (node.typeArguments == null) { |
| 279 var inferredTypeArguments = _getInferredFunctionTypeArguments( | 307 var inferredTypeArguments = _getInferredFunctionTypeArguments( |
| 280 node.function.staticType, | 308 node.function.staticType, |
| 281 node.staticInvokeType, | 309 node.staticInvokeType, |
| 282 node.typeArguments) | 310 node.typeArguments) |
| 283 .toList(); | 311 .toList(); |
| 284 if (inferredTypeArguments.isNotEmpty) { | 312 if (inferredTypeArguments.isNotEmpty) { |
| 285 _recordTypeArguments(node.methodName.offset, inferredTypeArguments); | 313 _recordTypeArguments(node.methodName.offset, inferredTypeArguments); |
| 286 } | 314 } |
| 287 } | 315 } |
| 288 } | 316 } |
| 289 | 317 |
| 318 visitPrefixExpression(PrefixExpression node) { |
| 319 super.visitPrefixExpression(node); |
| 320 _recordMethodTarget(node.operator.charOffset, node.staticElement); |
| 321 } |
| 322 |
| 290 visitSimpleIdentifier(SimpleIdentifier node) { | 323 visitSimpleIdentifier(SimpleIdentifier node) { |
| 291 super.visitSimpleIdentifier(node); | 324 super.visitSimpleIdentifier(node); |
| 292 Element element = node.staticElement; | 325 Element element = node.staticElement; |
| 293 void recordPromotions(DartType elementType) { | 326 void recordPromotions(DartType elementType) { |
| 294 if (node.inGetterContext() && !node.inDeclarationContext()) { | 327 if (node.inGetterContext() && !node.inDeclarationContext()) { |
| 295 int offset = node.offset; | 328 int offset = node.offset; |
| 296 DartType type = node.staticType; | 329 DartType type = node.staticType; |
| 297 if (identical(type, elementType)) { | 330 if (identical(type, elementType)) { |
| 298 _instrumentation.record(uri, offset, 'promotedType', | 331 _instrumentation.record(uri, offset, 'promotedType', |
| 299 const fasta.InstrumentationValueLiteral('none')); | 332 const fasta.InstrumentationValueLiteral('none')); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 331 if (g is FunctionType && | 364 if (g is FunctionType && |
| 332 g.typeFormals.isNotEmpty && | 365 g.typeFormals.isNotEmpty && |
| 333 f is FunctionType && | 366 f is FunctionType && |
| 334 f.typeFormals.isEmpty) { | 367 f.typeFormals.isEmpty) { |
| 335 return _recoverTypeArguments(g, f); | 368 return _recoverTypeArguments(g, f); |
| 336 } else { | 369 } else { |
| 337 return const []; | 370 return const []; |
| 338 } | 371 } |
| 339 } | 372 } |
| 340 | 373 |
| 374 void _recordMethodTarget(int offset, Element element) { |
| 375 if (element is MethodElement) { |
| 376 _instrumentation.record(uri, offset, 'target', |
| 377 new _InstrumentationValueForMethodElement(element)); |
| 378 } |
| 379 } |
| 380 |
| 341 void _recordTopType(int offset, DartType type) { | 381 void _recordTopType(int offset, DartType type) { |
| 342 _instrumentation.record( | 382 _instrumentation.record( |
| 343 uri, offset, 'topType', new _InstrumentationValueForType(type)); | 383 uri, offset, 'topType', new _InstrumentationValueForType(type)); |
| 344 } | 384 } |
| 345 | 385 |
| 346 void _recordType(int offset, DartType type) { | 386 void _recordType(int offset, DartType type) { |
| 347 _instrumentation.record( | 387 _instrumentation.record( |
| 348 uri, offset, 'type', new _InstrumentationValueForType(type)); | 388 uri, offset, 'type', new _InstrumentationValueForType(type)); |
| 349 } | 389 } |
| 350 | 390 |
| 351 void _recordTypeArguments(int offset, List<DartType> typeArguments) { | 391 void _recordTypeArguments(int offset, List<DartType> typeArguments) { |
| 352 _instrumentation.record(uri, offset, 'typeArgs', | 392 _instrumentation.record(uri, offset, 'typeArgs', |
| 353 new _InstrumentationValueForTypeArgs(typeArguments)); | 393 new _InstrumentationValueForTypeArgs(typeArguments)); |
| 354 } | 394 } |
| 355 | 395 |
| 356 /// Based on DDC code generator's `_recoverTypeArguments` | 396 /// Based on DDC code generator's `_recoverTypeArguments` |
| 357 Iterable<DartType> _recoverTypeArguments(FunctionType g, FunctionType f) { | 397 Iterable<DartType> _recoverTypeArguments(FunctionType g, FunctionType f) { |
| 358 assert(identical(g.element, f.element)); | 398 assert(identical(g.element, f.element)); |
| 359 assert(g.typeFormals.isNotEmpty && f.typeFormals.isEmpty); | 399 assert(g.typeFormals.isNotEmpty && f.typeFormals.isEmpty); |
| 360 assert(g.typeFormals.length + g.typeArguments.length == | 400 assert(g.typeFormals.length + g.typeArguments.length == |
| 361 f.typeArguments.length); | 401 f.typeArguments.length); |
| 362 return f.typeArguments.skip(g.typeArguments.length); | 402 return f.typeArguments.skip(g.typeArguments.length); |
| 363 } | 403 } |
| 364 } | 404 } |
| OLD | NEW |