| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 import 'dart:async'; | |
| 6 import "package:expect/expect.dart"; | |
| 7 import "package:async_helper/async_helper.dart"; | |
| 8 import 'package:compiler/src/types/types.dart'; | |
| 9 import 'package:compiler/src/inferrer/concrete_types_inferrer.dart'; | |
| 10 import 'package:compiler/src/universe/call_structure.dart' show | |
| 11 CallStructure; | |
| 12 import 'package:compiler/src/universe/selector.dart' show | |
| 13 Selector; | |
| 14 | |
| 15 import "compiler_helper.dart"; | |
| 16 import "type_mask_test_helper.dart"; | |
| 17 | |
| 18 /** | |
| 19 * Finds the node corresponding to the last occurence of the substring | |
| 20 * [: identifier; :] in the program represented by the visited AST. | |
| 21 */ | |
| 22 class VariableFinderVisitor extends Visitor { | |
| 23 final String identifier; | |
| 24 Node result; | |
| 25 | |
| 26 VariableFinderVisitor(this.identifier); | |
| 27 | |
| 28 visitSend(Send node) { | |
| 29 if (node.isPropertyAccess | |
| 30 && node.selector.asIdentifier().source == identifier) { | |
| 31 result = node; | |
| 32 } else { | |
| 33 node.visitChildren(this); | |
| 34 } | |
| 35 } | |
| 36 | |
| 37 visitNode(Node node) { | |
| 38 node.visitChildren(this); | |
| 39 } | |
| 40 } | |
| 41 | |
| 42 class AnalysisResult { | |
| 43 MockCompiler compiler; | |
| 44 ConcreteTypesInferrer inferrer; | |
| 45 Node ast; | |
| 46 | |
| 47 BaseType int; | |
| 48 BaseType double; | |
| 49 BaseType num; | |
| 50 BaseType bool; | |
| 51 BaseType string; | |
| 52 BaseType list; | |
| 53 BaseType growableList; | |
| 54 BaseType map; | |
| 55 BaseType nullType; | |
| 56 BaseType functionType; | |
| 57 | |
| 58 AnalysisResult(MockCompiler compiler) : this.compiler = compiler { | |
| 59 inferrer = compiler.typesTask.concreteTypesInferrer; | |
| 60 int = inferrer.baseTypes.intBaseType; | |
| 61 double = inferrer.baseTypes.doubleBaseType; | |
| 62 num = inferrer.baseTypes.numBaseType; | |
| 63 bool = inferrer.baseTypes.boolBaseType; | |
| 64 string = inferrer.baseTypes.stringBaseType; | |
| 65 list = inferrer.baseTypes.listBaseType; | |
| 66 growableList = inferrer.baseTypes.growableListBaseType; | |
| 67 map = inferrer.baseTypes.mapBaseType; | |
| 68 nullType = const NullBaseType(); | |
| 69 functionType = inferrer.baseTypes.functionBaseType; | |
| 70 FunctionElement mainElement = compiler.mainApp.find('main'); | |
| 71 ast = mainElement.node; | |
| 72 } | |
| 73 | |
| 74 BaseType base(String className) { | |
| 75 final source = className; | |
| 76 return new ClassBaseType(compiler.mainApp.find(source)); | |
| 77 } | |
| 78 | |
| 79 /** | |
| 80 * Finds the [Node] corresponding to the last occurence of the substring | |
| 81 * [: identifier; :] in the program represented by the visited AST. For | |
| 82 * instance, returns the AST node representing [: foo; :] in | |
| 83 * [: main() { foo = 1; foo; } :]. | |
| 84 */ | |
| 85 Node findNode(String identifier) { | |
| 86 VariableFinderVisitor finder = new VariableFinderVisitor(identifier); | |
| 87 ast.accept(finder); | |
| 88 return finder.result; | |
| 89 } | |
| 90 | |
| 91 /** | |
| 92 * Finds the [Element] corresponding to [: className#fieldName :]. | |
| 93 */ | |
| 94 Element findField(String className, String fieldName) { | |
| 95 ClassElement element = compiler.mainApp.find(className); | |
| 96 return element.lookupLocalMember(fieldName); | |
| 97 } | |
| 98 | |
| 99 ConcreteType concreteFrom(List<BaseType> baseTypes) { | |
| 100 ConcreteType result = inferrer.emptyConcreteType; | |
| 101 for (final baseType in baseTypes) { | |
| 102 result = result.union(inferrer.singletonConcreteType(baseType)); | |
| 103 } | |
| 104 // We make sure the concrete types expected by the tests don't default to | |
| 105 // dynamic because of widening. | |
| 106 assert(!result.isUnknown()); | |
| 107 return result; | |
| 108 } | |
| 109 | |
| 110 /** | |
| 111 * Checks that the inferred type of the node corresponding to the last | |
| 112 * occurence of [: variable; :] in the program is the concrete type | |
| 113 * made of [baseTypes]. | |
| 114 */ | |
| 115 void checkNodeHasType(String variable, List<BaseType> baseTypes) { | |
| 116 Expect.equals( | |
| 117 concreteFrom(baseTypes), | |
| 118 inferrer.inferredTypes[findNode(variable)]); | |
| 119 } | |
| 120 | |
| 121 /** | |
| 122 * Checks that the inferred type of the node corresponding to the last | |
| 123 * occurence of [: variable; :] in the program is the unknown concrete type. | |
| 124 */ | |
| 125 void checkNodeHasUnknownType(String variable) { | |
| 126 Expect.isTrue(inferrer.inferredTypes[findNode(variable)].isUnknown()); | |
| 127 } | |
| 128 | |
| 129 /** | |
| 130 * Checks that [: className#fieldName :]'s inferred type is the concrete type | |
| 131 * made of [baseTypes]. | |
| 132 */ | |
| 133 void checkFieldHasType(String className, String fieldName, | |
| 134 List<BaseType> baseTypes) { | |
| 135 Expect.equals( | |
| 136 concreteFrom(baseTypes), | |
| 137 inferrer.inferredFieldTypes[findField(className, fieldName)]); | |
| 138 } | |
| 139 | |
| 140 /** | |
| 141 * Checks that [: className#fieldName :]'s inferred type is the unknown | |
| 142 * concrete type. | |
| 143 */ | |
| 144 void checkFieldHasUknownType(String className, String fieldName) { | |
| 145 Expect.isTrue( | |
| 146 inferrer.inferredFieldTypes[findField(className, fieldName)] | |
| 147 .isUnknown()); | |
| 148 } | |
| 149 | |
| 150 /** Checks that the inferred type for [selector] is [mask]. */ | |
| 151 void checkSelectorHasType(Selector selector, | |
| 152 TypeMask mask, | |
| 153 TypeMask expectedMask) { | |
| 154 Expect.equals(expectedMask, inferrer.getTypeOfSelector(selector, mask)); | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 const String DYNAMIC = '"__dynamic_for_test"'; | |
| 159 | |
| 160 Future<AnalysisResult> analyze(String code, {int maxConcreteTypeSize: 1000}) { | |
| 161 Uri uri = new Uri(scheme: 'dart', path: 'test'); | |
| 162 MockCompiler compiler = new MockCompiler.internal( | |
| 163 enableConcreteTypeInference: true, | |
| 164 maxConcreteTypeSize: maxConcreteTypeSize); | |
| 165 compiler.diagnosticHandler = createHandler(compiler, code); | |
| 166 compiler.registerSource(uri, code); | |
| 167 compiler.typesTask.concreteTypesInferrer.testMode = true; | |
| 168 return compiler.runCompiler(uri).then((_) { | |
| 169 return new AnalysisResult(compiler); | |
| 170 }); | |
| 171 } | |
| 172 | |
| 173 testDynamicBackDoor() { | |
| 174 final String source = """ | |
| 175 main () { | |
| 176 var x = $DYNAMIC; | |
| 177 x; | |
| 178 } | |
| 179 """; | |
| 180 return analyze(source).then((result) { | |
| 181 result.checkNodeHasUnknownType('x'); | |
| 182 }); | |
| 183 } | |
| 184 | |
| 185 testVariableDeclaration() { | |
| 186 final String source = r""" | |
| 187 main() { | |
| 188 var v1; | |
| 189 var v2; | |
| 190 v2 = 1; | |
| 191 v1; v2; | |
| 192 } | |
| 193 """; | |
| 194 return analyze(source).then((result) { | |
| 195 result.checkNodeHasType('v1', [result.nullType]); | |
| 196 result.checkNodeHasType('v2', [result.int]); | |
| 197 }); | |
| 198 } | |
| 199 | |
| 200 testLiterals() { | |
| 201 final String source = r""" | |
| 202 main() { | |
| 203 var v1 = 42; | |
| 204 var v2 = 42.1; | |
| 205 var v3 = 'abc'; | |
| 206 var v4 = true; | |
| 207 var v5 = null; | |
| 208 v1; v2; v3; v4; v5; | |
| 209 } | |
| 210 """; | |
| 211 return analyze(source).then((result) { | |
| 212 result.checkNodeHasType('v1', [result.int]); | |
| 213 result.checkNodeHasType('v2', [result.double]); | |
| 214 result.checkNodeHasType('v3', [result.string]); | |
| 215 result.checkNodeHasType('v4', [result.bool]); | |
| 216 result.checkNodeHasType('v5', [result.nullType]); | |
| 217 }); | |
| 218 } | |
| 219 | |
| 220 testRedefinition() { | |
| 221 final String source = r""" | |
| 222 main() { | |
| 223 var foo = 42; | |
| 224 foo = 'abc'; | |
| 225 foo; | |
| 226 } | |
| 227 """; | |
| 228 return analyze(source).then((result) { | |
| 229 result.checkNodeHasType('foo', [result.string]); | |
| 230 }); | |
| 231 } | |
| 232 | |
| 233 testIfThenElse() { | |
| 234 final String source = r""" | |
| 235 main() { | |
| 236 var foo = 42; | |
| 237 if (true) { | |
| 238 foo = 'abc'; | |
| 239 } else { | |
| 240 foo = false; | |
| 241 } | |
| 242 foo; | |
| 243 } | |
| 244 """; | |
| 245 return analyze(source).then((result) { | |
| 246 result.checkNodeHasType('foo', [result.string, result.bool]); | |
| 247 }); | |
| 248 } | |
| 249 | |
| 250 testTernaryIf() { | |
| 251 final String source = r""" | |
| 252 main() { | |
| 253 var foo = 42; | |
| 254 foo = true ? 'abc' : false; | |
| 255 foo; | |
| 256 } | |
| 257 """; | |
| 258 return analyze(source).then((result) { | |
| 259 result.checkNodeHasType('foo', [result.string, result.bool]); | |
| 260 }); | |
| 261 } | |
| 262 | |
| 263 testWhile() { | |
| 264 final String source = r""" | |
| 265 class A { f() => new B(); } | |
| 266 class B { f() => new C(); } | |
| 267 class C { f() => new A(); } | |
| 268 main() { | |
| 269 var bar = null; | |
| 270 var foo = new A(); | |
| 271 while(bar = 42) { | |
| 272 foo = foo.f(); | |
| 273 } | |
| 274 foo; bar; | |
| 275 } | |
| 276 """; | |
| 277 return analyze(source).then((result) { | |
| 278 result.checkNodeHasType( | |
| 279 'foo', | |
| 280 [result.base('A'), result.base('B'), result.base('C')]); | |
| 281 // Check that the condition is evaluated. | |
| 282 // TODO(polux): bar's type could be inferred to be {int} here. | |
| 283 result.checkNodeHasType('bar', [result.int, result.nullType]); | |
| 284 }); | |
| 285 } | |
| 286 | |
| 287 testDoWhile() { | |
| 288 final String source = r""" | |
| 289 class A { f() => new B(); } | |
| 290 class B { f() => new C(); } | |
| 291 class C { f() => new A(); } | |
| 292 main() { | |
| 293 var bar = null; | |
| 294 var foo = new A(); | |
| 295 do { | |
| 296 foo = foo.f(); | |
| 297 } while (bar = 42); | |
| 298 foo; bar; | |
| 299 } | |
| 300 """; | |
| 301 return analyze(source).then((AnalysisResult result) { | |
| 302 result.checkNodeHasType( | |
| 303 'foo', | |
| 304 [result.base('A'), result.base('B'), result.base('C')]); | |
| 305 // Check that the condition is evaluated. | |
| 306 result.checkNodeHasType('bar', [result.int]); | |
| 307 }); | |
| 308 } | |
| 309 | |
| 310 testFor1() { | |
| 311 final String source = r""" | |
| 312 class A { f() => new B(); } | |
| 313 class B { f() => new C(); } | |
| 314 class C { f() => new A(); } | |
| 315 main() { | |
| 316 var foo = new A(); | |
| 317 for(;;) { | |
| 318 foo = foo.f(); | |
| 319 } | |
| 320 foo; | |
| 321 } | |
| 322 """; | |
| 323 return analyze(source).then((result) { | |
| 324 result.checkNodeHasType( | |
| 325 'foo', | |
| 326 [result.base('A'), result.base('B'), result.base('C')]); | |
| 327 }); | |
| 328 } | |
| 329 | |
| 330 testFor2() { | |
| 331 final String source = r""" | |
| 332 class A { f() => new B(); test() => true; } | |
| 333 class B { f() => new A(); test() => true; } | |
| 334 main() { | |
| 335 var bar = null; | |
| 336 var foo = new A(); | |
| 337 for(var i = new A(); bar = 42; i = i.f()) { | |
| 338 foo = i; | |
| 339 } | |
| 340 foo; bar; | |
| 341 } | |
| 342 """; | |
| 343 return analyze(source).then((result) { | |
| 344 result.checkNodeHasType('foo', [result.base('A'), result.base('B')]); | |
| 345 // Check that the condition is evaluated. | |
| 346 // TODO(polux): bar's type could be inferred to be {int} here. | |
| 347 result.checkNodeHasType('bar', [result.int, result.nullType]); | |
| 348 }); | |
| 349 } | |
| 350 | |
| 351 testFor3() { | |
| 352 final String source = r""" | |
| 353 main() { | |
| 354 var i = 1; | |
| 355 for(;;) { | |
| 356 var x = 2; | |
| 357 i = x; | |
| 358 } | |
| 359 i; | |
| 360 } | |
| 361 """; | |
| 362 return analyze(source).then((result) { | |
| 363 result.checkNodeHasType('i', [result.int]); | |
| 364 }); | |
| 365 } | |
| 366 | |
| 367 testForIn() { | |
| 368 final String source = r""" | |
| 369 class MyIterator { | |
| 370 var counter = 0; | |
| 371 | |
| 372 moveNext() { | |
| 373 if (counter == 0) { | |
| 374 counter = 1; | |
| 375 return true; | |
| 376 } else if (counter == 1) { | |
| 377 counter = 2; | |
| 378 return true; | |
| 379 } else { | |
| 380 return false; | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 get current => (counter == 1) ? "foo" : 42; | |
| 385 } | |
| 386 | |
| 387 class MyIterable { | |
| 388 get iterator => new MyIterator(); | |
| 389 } | |
| 390 | |
| 391 main() { | |
| 392 var res; | |
| 393 for (var i in new MyIterable()) { | |
| 394 res = i; | |
| 395 } | |
| 396 res; | |
| 397 } | |
| 398 """; | |
| 399 return analyze(source).then((AnalysisResult result) { | |
| 400 result.checkNodeHasType('res', | |
| 401 [result.int, result.string, result.nullType]); | |
| 402 }); | |
| 403 } | |
| 404 | |
| 405 testToplevelVariable() { | |
| 406 final String source = r""" | |
| 407 final top = 'abc'; | |
| 408 class A { | |
| 409 f() => top; | |
| 410 } | |
| 411 main() { | |
| 412 var foo = top; | |
| 413 var bar = new A().f(); | |
| 414 foo; bar; | |
| 415 } | |
| 416 """; | |
| 417 return analyze(source).then((result) { | |
| 418 result.checkNodeHasType('foo', [result.string]); | |
| 419 result.checkNodeHasType('bar', [result.string]); | |
| 420 }); | |
| 421 } | |
| 422 | |
| 423 testToplevelVariable2() { | |
| 424 final String source = r""" | |
| 425 class A { | |
| 426 var x; | |
| 427 } | |
| 428 final top = new A().x; | |
| 429 | |
| 430 main() { | |
| 431 var a = new A(); | |
| 432 a.x = 42; | |
| 433 a.x = "abc"; | |
| 434 var foo = top; | |
| 435 foo; | |
| 436 } | |
| 437 """; | |
| 438 return analyze(source).then((result) { | |
| 439 result.checkNodeHasType('foo', [result.nullType, result.int, | |
| 440 result.string]); | |
| 441 }); | |
| 442 } | |
| 443 | |
| 444 testToplevelVariable3() { | |
| 445 final String source = r""" | |
| 446 var top = "a"; | |
| 447 | |
| 448 f() => top; | |
| 449 | |
| 450 main() { | |
| 451 var foo = f(); | |
| 452 var bar = top; | |
| 453 top = 42; | |
| 454 var baz = top; | |
| 455 foo; bar; baz; | |
| 456 } | |
| 457 """; | |
| 458 return analyze(source).then((result) { | |
| 459 result.checkNodeHasType('foo', [result.int, result.string]); | |
| 460 result.checkNodeHasType('bar', [result.int, result.string]); | |
| 461 result.checkNodeHasType('baz', [result.int, result.string]); | |
| 462 }); | |
| 463 } | |
| 464 | |
| 465 testNonRecusiveFunction() { | |
| 466 final String source = r""" | |
| 467 f(x, y) => true ? x : y; | |
| 468 main() { var foo = f(42, "abc"); foo; } | |
| 469 """; | |
| 470 return analyze(source).then((result) { | |
| 471 result.checkNodeHasType('foo', [result.int, result.string]); | |
| 472 }); | |
| 473 } | |
| 474 | |
| 475 testMultipleReturns() { | |
| 476 final String source = r""" | |
| 477 f(x, y) { | |
| 478 if (true) return x; | |
| 479 else return y; | |
| 480 } | |
| 481 main() { var foo = f(42, "abc"); foo; } | |
| 482 """; | |
| 483 return analyze(source).then((result) { | |
| 484 result.checkNodeHasType('foo', [result.int, result.string]); | |
| 485 }); | |
| 486 } | |
| 487 | |
| 488 testRecusiveFunction() { | |
| 489 final String source = r""" | |
| 490 f(x) { | |
| 491 if (true) return x; | |
| 492 else return f(true ? x : "abc"); | |
| 493 } | |
| 494 main() { var foo = f(42); foo; } | |
| 495 """; | |
| 496 return analyze(source).then((result) { | |
| 497 result.checkNodeHasType('foo', [result.int, result.string]); | |
| 498 }); | |
| 499 } | |
| 500 | |
| 501 testMutuallyRecusiveFunction() { | |
| 502 final String source = r""" | |
| 503 f() => true ? 42 : g(); | |
| 504 g() => true ? "abc" : f(); | |
| 505 main() { var foo = f(); foo; } | |
| 506 """; | |
| 507 return analyze(source).then((result) { | |
| 508 result.checkNodeHasType('foo', [result.int, result.string]); | |
| 509 }); | |
| 510 } | |
| 511 | |
| 512 testSimpleSend() { | |
| 513 final String source = """ | |
| 514 class A { | |
| 515 f(x) => x; | |
| 516 } | |
| 517 class B { | |
| 518 f(x) => 'abc'; | |
| 519 } | |
| 520 class C { | |
| 521 f(x) => 3.14; | |
| 522 } | |
| 523 class D { | |
| 524 var f; // we check that this field is ignored in calls to dynamic.f() | |
| 525 D(this.f); | |
| 526 } | |
| 527 main() { | |
| 528 new B(); new D(42); // we instantiate B and D but not C | |
| 529 var foo = new A().f(42); | |
| 530 var bar = $DYNAMIC.f(42); | |
| 531 foo; bar; | |
| 532 } | |
| 533 """; | |
| 534 return analyze(source).then((result) { | |
| 535 result.checkNodeHasType('foo', [result.int]); | |
| 536 result.checkNodeHasType('bar', [result.int, result.string]); | |
| 537 }); | |
| 538 } | |
| 539 | |
| 540 testSendToThis1() { | |
| 541 final String source = r""" | |
| 542 class A { | |
| 543 A(); | |
| 544 f() => g(); | |
| 545 g() => 42; | |
| 546 } | |
| 547 main() { | |
| 548 var foo = new A().f(); | |
| 549 foo; | |
| 550 } | |
| 551 """; | |
| 552 return analyze(source).then((result) { | |
| 553 result.checkNodeHasType('foo', [result.int]); | |
| 554 }); | |
| 555 } | |
| 556 | |
| 557 testSendToThis2() { | |
| 558 final String source = r""" | |
| 559 class A { | |
| 560 foo() => this; | |
| 561 } | |
| 562 class B extends A { | |
| 563 bar() => foo(); | |
| 564 } | |
| 565 main() { | |
| 566 var x = new B().bar(); | |
| 567 x; | |
| 568 } | |
| 569 """; | |
| 570 return analyze(source).then((result) { | |
| 571 result.checkNodeHasType('x', [result.base('B')]); | |
| 572 }); | |
| 573 } | |
| 574 | |
| 575 testSendToThis3() { | |
| 576 final String source = r""" | |
| 577 class A { | |
| 578 bar() => 42; | |
| 579 foo() => bar(); | |
| 580 } | |
| 581 class B extends A { | |
| 582 bar() => "abc"; | |
| 583 } | |
| 584 main() { | |
| 585 var x = new B().foo(); | |
| 586 x; | |
| 587 } | |
| 588 """; | |
| 589 return analyze(source).then((AnalysisResult result) { | |
| 590 result.checkNodeHasType('x', [result.string]); | |
| 591 }); | |
| 592 } | |
| 593 | |
| 594 testSendToThis4() { | |
| 595 final String source = """ | |
| 596 class A { | |
| 597 bar() => 42; | |
| 598 foo() => bar(); | |
| 599 } | |
| 600 class B extends A { | |
| 601 bar() => "abc"; | |
| 602 } | |
| 603 main() { | |
| 604 new A(); new B(); // make A and B seen | |
| 605 var x = $DYNAMIC.foo(); | |
| 606 x; | |
| 607 } | |
| 608 """; | |
| 609 return analyze(source).then((AnalysisResult result) { | |
| 610 result.checkNodeHasType('x', [result.int, result.string]); | |
| 611 }); | |
| 612 } | |
| 613 | |
| 614 testConstructor() { | |
| 615 final String source = r""" | |
| 616 class A { | |
| 617 var x, y, z; | |
| 618 A(this.x, a) : y = a { z = 'abc'; } | |
| 619 } | |
| 620 main() { | |
| 621 new A(42, 'abc'); | |
| 622 new A(true, null); | |
| 623 } | |
| 624 """; | |
| 625 return analyze(source).then((result) { | |
| 626 result.checkFieldHasType('A', 'x', [result.int, result.bool]); | |
| 627 result.checkFieldHasType('A', 'y', [result.string, result.nullType]); | |
| 628 result.checkFieldHasType('A', 'z', [result.string]); | |
| 629 }); | |
| 630 } | |
| 631 | |
| 632 testGetters() { | |
| 633 final String source = """ | |
| 634 class A { | |
| 635 var x; | |
| 636 A(this.x); | |
| 637 get y => x; | |
| 638 get z => y; | |
| 639 } | |
| 640 class B { | |
| 641 var x; | |
| 642 B(this.x); | |
| 643 } | |
| 644 main() { | |
| 645 var a = new A(42); | |
| 646 var b = new B('abc'); | |
| 647 var foo = a.x; | |
| 648 var bar = a.y; | |
| 649 var baz = a.z; | |
| 650 var qux = null.x; | |
| 651 var quux = $DYNAMIC.x; | |
| 652 foo; bar; baz; qux; quux; | |
| 653 } | |
| 654 """; | |
| 655 return analyze(source).then((result) { | |
| 656 result.checkNodeHasType('foo', [result.int]); | |
| 657 result.checkNodeHasType('bar', [result.int]); | |
| 658 result.checkNodeHasType('baz', [result.int]); | |
| 659 result.checkNodeHasType('qux', []); | |
| 660 result.checkNodeHasType('quux', [result.int, result.string]); | |
| 661 }); | |
| 662 } | |
| 663 | |
| 664 testDynamicGetters() { | |
| 665 final String source = """ | |
| 666 class A { | |
| 667 get x => f(); | |
| 668 f() => 42; | |
| 669 } | |
| 670 class B extends A { | |
| 671 f() => "abc"; | |
| 672 } | |
| 673 main() { | |
| 674 new A(); new B(); // make A and B seen | |
| 675 var x = $DYNAMIC.x; | |
| 676 x; | |
| 677 } | |
| 678 """; | |
| 679 return analyze(source).then((result) { | |
| 680 result.checkNodeHasType('x', [result.int, result.string]); | |
| 681 }); | |
| 682 } | |
| 683 | |
| 684 testToplevelGetters() { | |
| 685 final String source = """ | |
| 686 int _x = 42; | |
| 687 get x => _x; | |
| 688 | |
| 689 f() => x; | |
| 690 | |
| 691 main() { | |
| 692 var foo = f(); | |
| 693 var bar = x; | |
| 694 _x = "a"; | |
| 695 var baz = x; | |
| 696 foo; bar; baz; | |
| 697 } | |
| 698 """; | |
| 699 return analyze(source).then((result) { | |
| 700 result.checkNodeHasType('foo', [result.int, result.string]); | |
| 701 result.checkNodeHasType('bar', [result.int, result.string]); | |
| 702 result.checkNodeHasType('baz', [result.int, result.string]); | |
| 703 }); | |
| 704 } | |
| 705 | |
| 706 testSetters() { | |
| 707 final String source = """ | |
| 708 class A { | |
| 709 var x; | |
| 710 var w; | |
| 711 A(this.x, this.w); | |
| 712 set y(a) { x = a; z = a; } | |
| 713 set z(a) { w = a; } | |
| 714 } | |
| 715 class B { | |
| 716 var x; | |
| 717 B(this.x); | |
| 718 } | |
| 719 main() { | |
| 720 var a = new A(42, 42); | |
| 721 var b = new B(42); | |
| 722 a.x = 'abc'; | |
| 723 a.y = true; | |
| 724 null.x = 42; // should be ignored | |
| 725 $DYNAMIC.x = null; | |
| 726 $DYNAMIC.y = 3.14; | |
| 727 } | |
| 728 """; | |
| 729 return analyze(source).then((result) { | |
| 730 result.checkFieldHasType('B', 'x', | |
| 731 [result.int, // new B(42) | |
| 732 result.nullType]); // dynamic.x = null | |
| 733 result.checkFieldHasType('A', 'x', | |
| 734 [result.int, // new A(42, ...) | |
| 735 result.string, // a.x = 'abc' | |
| 736 result.bool, // a.y = true | |
| 737 result.nullType, // dynamic.x = null | |
| 738 result.double]); // dynamic.y = 3.14 | |
| 739 result.checkFieldHasType('A', 'w', | |
| 740 [result.int, // new A(..., 42) | |
| 741 result.bool, // a.y = true | |
| 742 result.double]); // dynamic.y = 3.14 | |
| 743 }); | |
| 744 } | |
| 745 | |
| 746 testToplevelSetters() { | |
| 747 final String source = """ | |
| 748 int _x = 42; | |
| 749 set x(y) => _x = y; | |
| 750 | |
| 751 f(y) { x = y; } | |
| 752 | |
| 753 main() { | |
| 754 var foo = _x; | |
| 755 x = "a"; | |
| 756 var bar = _x; | |
| 757 f(true); | |
| 758 var baz = _x; | |
| 759 foo; bar; baz; | |
| 760 } | |
| 761 """; | |
| 762 return analyze(source).then((result) { | |
| 763 result.checkNodeHasType('foo', [result.int, result.string, result.bool]); | |
| 764 result.checkNodeHasType('bar', [result.int, result.string, result.bool]); | |
| 765 result.checkNodeHasType('baz', [result.int, result.string, result.bool]); | |
| 766 }); | |
| 767 } | |
| 768 | |
| 769 | |
| 770 testOptionalNamedParameters() { | |
| 771 final String source = r""" | |
| 772 class A { | |
| 773 var x, y, z, w; | |
| 774 A(this.x, {this.y, this.z, this.w}); | |
| 775 } | |
| 776 class B { | |
| 777 var x, y; | |
| 778 B(this.x, {this.y}); | |
| 779 } | |
| 780 class C { | |
| 781 var x, y; | |
| 782 C(this.x, {this.y}); | |
| 783 } | |
| 784 class Test { | |
| 785 var a, b, c, d; | |
| 786 var e, f; | |
| 787 var g, h; | |
| 788 | |
| 789 Test(this.a, this.b, this.c, this.d, | |
| 790 this.e, this.f, | |
| 791 this.g, this.h); | |
| 792 | |
| 793 f1(x, {y, z, w}) { | |
| 794 a = x; | |
| 795 b = y; | |
| 796 c = z; | |
| 797 d = w; | |
| 798 } | |
| 799 f2(x, {y}) { | |
| 800 e = x; | |
| 801 f = y; | |
| 802 } | |
| 803 f3(x, {y}) { | |
| 804 g = x; | |
| 805 h = y; | |
| 806 } | |
| 807 } | |
| 808 class Foo { | |
| 809 } | |
| 810 main() { | |
| 811 // We want to test expiclitely for null later so we initialize all the | |
| 812 // fields of Test with a placeholder type: Foo. | |
| 813 var foo = new Foo(); | |
| 814 var test = new Test(foo, foo, foo, foo, foo, foo, foo, foo); | |
| 815 | |
| 816 new A(42); | |
| 817 new A('abc', w: true, z: 42.1); | |
| 818 test.f1(42); | |
| 819 test.f1('abc', w: true, z: 42.1); | |
| 820 | |
| 821 new B('abc', y: true); | |
| 822 new B(1, 2); // too many positional arguments | |
| 823 test.f2('abc', y: true); | |
| 824 test.f2(1, 2); // too many positional arguments | |
| 825 | |
| 826 new C('abc', y: true); | |
| 827 new C(1, z: 2); // non-existing named parameter | |
| 828 test.f3('abc', y: true); | |
| 829 test.f3(1, z: 2); // non-existing named parameter | |
| 830 } | |
| 831 """; | |
| 832 return analyze(source).then((result) { | |
| 833 | |
| 834 final foo = result.base('Foo'); | |
| 835 final nil = result.nullType; | |
| 836 | |
| 837 result.checkFieldHasType('A', 'x', [result.int, result.string]); | |
| 838 result.checkFieldHasType('A', 'y', [nil]); | |
| 839 result.checkFieldHasType('A', 'z', [nil, result.double]); | |
| 840 result.checkFieldHasType('A', 'w', [nil, result.bool]); | |
| 841 result.checkFieldHasType('Test', 'a', [foo, result.int, result.string]); | |
| 842 result.checkFieldHasType('Test', 'b', [foo, nil]); | |
| 843 result.checkFieldHasType('Test', 'c', [foo, nil, result.double]); | |
| 844 result.checkFieldHasType('Test', 'd', [foo, nil, result.bool]); | |
| 845 | |
| 846 result.checkFieldHasType('B', 'x', [result.string]); | |
| 847 result.checkFieldHasType('B', 'y', [result.bool]); | |
| 848 result.checkFieldHasType('Test', 'e', [foo, result.string]); | |
| 849 result.checkFieldHasType('Test', 'f', [foo, result.bool]); | |
| 850 | |
| 851 result.checkFieldHasType('C', 'x', [result.string]); | |
| 852 result.checkFieldHasType('C', 'y', [result.bool]); | |
| 853 result.checkFieldHasType('Test', 'g', [foo, result.string]); | |
| 854 result.checkFieldHasType('Test', 'h', [foo, result.bool]); | |
| 855 }); | |
| 856 } | |
| 857 | |
| 858 testOptionalPositionalParameters() { | |
| 859 final String source = r""" | |
| 860 class A { | |
| 861 var x, y, z, w; | |
| 862 A(this.x, [this.y, this.z, this.w]); | |
| 863 } | |
| 864 class B { | |
| 865 var x, y; | |
| 866 B(this.x, [this.y]); | |
| 867 } | |
| 868 class Test { | |
| 869 var a, b, c, d; | |
| 870 var e, f; | |
| 871 | |
| 872 Test(this.a, this.b, this.c, this.d, | |
| 873 this.e, this.f); | |
| 874 | |
| 875 f1(x, [y, z, w]) { | |
| 876 a = x; | |
| 877 b = y; | |
| 878 c = z; | |
| 879 d = w; | |
| 880 } | |
| 881 f2(x, [y]) { | |
| 882 e = x; | |
| 883 f = y; | |
| 884 } | |
| 885 } | |
| 886 class Foo { | |
| 887 } | |
| 888 main() { | |
| 889 // We want to test expiclitely for null later so we initialize all the | |
| 890 // fields of Test with a placeholder type: Foo. | |
| 891 var foo = new Foo(); | |
| 892 var test = new Test(foo, foo, foo, foo, foo, foo); | |
| 893 | |
| 894 new A(42); | |
| 895 new A('abc', true, 42.1); | |
| 896 test.f1(42); | |
| 897 test.f1('abc', true, 42.1); | |
| 898 | |
| 899 new B('a', true); | |
| 900 new B(1, 2, 3); // too many arguments | |
| 901 test.f2('a', true); | |
| 902 test.f2(1, 2, 3); // too many arguments | |
| 903 } | |
| 904 """; | |
| 905 return analyze(source).then((result) { | |
| 906 | |
| 907 final foo = result.base('Foo'); | |
| 908 final nil = result.nullType; | |
| 909 | |
| 910 result.checkFieldHasType('A', 'x', [result.int, result.string]); | |
| 911 result.checkFieldHasType('A', 'y', [nil, result.bool]); | |
| 912 result.checkFieldHasType('A', 'z', [nil, result.double]); | |
| 913 result.checkFieldHasType('A', 'w', [nil]); | |
| 914 result.checkFieldHasType('Test', 'a', [foo, result.int, result.string]); | |
| 915 result.checkFieldHasType('Test', 'b', [foo, nil, result.bool]); | |
| 916 result.checkFieldHasType('Test', 'c', [foo, nil, result.double]); | |
| 917 result.checkFieldHasType('Test', 'd', [foo, nil]); | |
| 918 | |
| 919 result.checkFieldHasType('B', 'x', [result.string]); | |
| 920 result.checkFieldHasType('B', 'y', [result.bool]); | |
| 921 result.checkFieldHasType('Test', 'e', [foo, result.string]); | |
| 922 result.checkFieldHasType('Test', 'f', [foo, result.bool]); | |
| 923 }); | |
| 924 } | |
| 925 | |
| 926 testListLiterals() { | |
| 927 final String source = r""" | |
| 928 class A { | |
| 929 var x; | |
| 930 A(this.x); | |
| 931 } | |
| 932 main() { | |
| 933 var x = []; | |
| 934 var y = [1, "a", null, new A(42)]; | |
| 935 x; y; | |
| 936 } | |
| 937 """; | |
| 938 return analyze(source).then((result) { | |
| 939 result.checkNodeHasType('x', [result.growableList]); | |
| 940 result.checkNodeHasType('y', [result.growableList]); | |
| 941 result.checkFieldHasType('A', 'x', [result.int]); | |
| 942 }); | |
| 943 } | |
| 944 | |
| 945 testMapLiterals() { | |
| 946 final String source = r""" | |
| 947 class A { | |
| 948 var x; | |
| 949 A(this.x); | |
| 950 } | |
| 951 main() { | |
| 952 var x = {}; | |
| 953 var y = {'a': "foo", 'b': new A(42) }; | |
| 954 x; y; | |
| 955 } | |
| 956 """; | |
| 957 return analyze(source).then((result) { | |
| 958 result.checkNodeHasType('x', [result.map]); | |
| 959 result.checkNodeHasType('y', [result.map]); | |
| 960 result.checkFieldHasType('A', 'x', [result.int]); | |
| 961 }); | |
| 962 } | |
| 963 | |
| 964 testReturn() { | |
| 965 final String source = r""" | |
| 966 f() { if (true) { return 1; }; return "a"; } | |
| 967 g() { f(); return; } | |
| 968 main() { | |
| 969 var x = f(); | |
| 970 var y = g(); | |
| 971 x; y; | |
| 972 } | |
| 973 """; | |
| 974 return analyze(source).then((result) { | |
| 975 result.checkNodeHasType('x', [result.int, result.string]); | |
| 976 result.checkNodeHasType('y', [result.nullType]); | |
| 977 }); | |
| 978 } | |
| 979 | |
| 980 testNoReturn() { | |
| 981 final String source = r""" | |
| 982 f() { if (true) { return 1; }; } | |
| 983 g() { f(); } | |
| 984 main() { | |
| 985 var x = f(); | |
| 986 var y = g(); | |
| 987 x; y; | |
| 988 } | |
| 989 """; | |
| 990 return analyze(source).then((result) { | |
| 991 result.checkNodeHasType('x', [result.int, result.nullType]); | |
| 992 result.checkNodeHasType('y', [result.nullType]); | |
| 993 }); | |
| 994 } | |
| 995 | |
| 996 testArithmeticOperators() { | |
| 997 String source(op) { | |
| 998 return """ | |
| 999 main() { | |
| 1000 var a = 1 $op 2; | |
| 1001 var b = 1 $op 2.1; | |
| 1002 var c = 1.1 $op 2; | |
| 1003 var d = 1.1 $op 2.1; | |
| 1004 var e = (1 $op 2.1) $op 1; | |
| 1005 var f = 1 $op (1 $op 2.1); | |
| 1006 var g = (1 $op 2.1) $op 1.1; | |
| 1007 var h = 1.1 $op (1 $op 2); | |
| 1008 var i = (1 $op 2) $op 1; | |
| 1009 var j = 1 $op (1 $op 2); | |
| 1010 var k = (1.1 $op 2.1) $op 1.1; | |
| 1011 var l = 1.1 $op (1.1 $op 2.1); | |
| 1012 a; b; c; d; e; f; g; h; i; j; k; l; | |
| 1013 }"""; | |
| 1014 } | |
| 1015 return Future.forEach(['+', '*', '-'], (String op) { | |
| 1016 return analyze(source(op)).then((result) { | |
| 1017 result.checkNodeHasType('a', [result.int]); | |
| 1018 result.checkNodeHasType('b', [result.num]); | |
| 1019 result.checkNodeHasType('c', [result.num]); | |
| 1020 result.checkNodeHasType('d', [result.double]); | |
| 1021 result.checkNodeHasType('e', [result.num]); | |
| 1022 result.checkNodeHasType('f', [result.num]); | |
| 1023 result.checkNodeHasType('g', [result.num]); | |
| 1024 result.checkNodeHasType('h', [result.num]); | |
| 1025 result.checkNodeHasType('i', [result.int]); | |
| 1026 result.checkNodeHasType('j', [result.int]); | |
| 1027 result.checkNodeHasType('k', [result.double]); | |
| 1028 result.checkNodeHasType('l', [result.double]); | |
| 1029 }); | |
| 1030 }); | |
| 1031 } | |
| 1032 | |
| 1033 testBooleanOperators() { | |
| 1034 String source(op) { | |
| 1035 return """ | |
| 1036 main() { | |
| 1037 var a = true $op null; | |
| 1038 var b = null $op true; | |
| 1039 var c = 1 $op true; | |
| 1040 var d = true $op "a"; | |
| 1041 a; b; c; d; | |
| 1042 }"""; | |
| 1043 } | |
| 1044 return Future.forEach(['&&', '||'], (String op) { | |
| 1045 return analyze(source(op)).then((result) { | |
| 1046 result.checkNodeHasType('a', [result.bool]); | |
| 1047 result.checkNodeHasType('b', [result.bool]); | |
| 1048 result.checkNodeHasType('c', [result.bool]); | |
| 1049 result.checkNodeHasType('d', [result.bool]); | |
| 1050 }); | |
| 1051 }); | |
| 1052 } | |
| 1053 | |
| 1054 testBooleanOperatorsShortCirtcuit() { | |
| 1055 String source(op) { | |
| 1056 return """ | |
| 1057 main() { | |
| 1058 var x = null; | |
| 1059 "foo" $op (x = 42); | |
| 1060 x; | |
| 1061 }"""; | |
| 1062 } | |
| 1063 return Future.forEach(['&&', '||'], (String op) { | |
| 1064 return analyze(source(op)).then((AnalysisResult result) { | |
| 1065 result.checkNodeHasType('x', [result.nullType, result.int]); | |
| 1066 }); | |
| 1067 }); | |
| 1068 } | |
| 1069 | |
| 1070 testOperators() { | |
| 1071 final String source = r""" | |
| 1072 class A { | |
| 1073 operator <(x) => 42; | |
| 1074 operator <<(x) => "a"; | |
| 1075 } | |
| 1076 main() { | |
| 1077 var x = new A() < "foo"; | |
| 1078 var y = new A() << "foo"; | |
| 1079 x; y; | |
| 1080 } | |
| 1081 """; | |
| 1082 return analyze(source).then((result) { | |
| 1083 result.checkNodeHasType('x', [result.int]); | |
| 1084 result.checkNodeHasType('y', [result.string]); | |
| 1085 }); | |
| 1086 } | |
| 1087 | |
| 1088 testSetIndexOperator() { | |
| 1089 final String source = r""" | |
| 1090 class A { | |
| 1091 var witness1; | |
| 1092 var witness2; | |
| 1093 operator []=(i, x) { witness1 = i; witness2 = x; } | |
| 1094 } | |
| 1095 main() { | |
| 1096 var x = new A()[42] = "abc"; | |
| 1097 x; | |
| 1098 } | |
| 1099 """; | |
| 1100 return analyze(source).then((result) { | |
| 1101 result.checkNodeHasType('x', [result.string]); | |
| 1102 result.checkFieldHasType('A', 'witness1', [result.int, result.nullType]); | |
| 1103 result.checkFieldHasType('A', 'witness2', [result.string, result.nullType]); | |
| 1104 }); | |
| 1105 } | |
| 1106 | |
| 1107 testCompoundOperators1() { | |
| 1108 final String source = r""" | |
| 1109 class A { | |
| 1110 operator +(x) => "foo"; | |
| 1111 } | |
| 1112 main() { | |
| 1113 var x1 = 1; | |
| 1114 x1++; | |
| 1115 var x2 = 1; | |
| 1116 ++x2; | |
| 1117 var x3 = 1; | |
| 1118 x3 += 42; | |
| 1119 var x4 = new A(); | |
| 1120 x4++; | |
| 1121 var x5 = new A(); | |
| 1122 ++x5; | |
| 1123 var x6 = new A(); | |
| 1124 x6 += true; | |
| 1125 | |
| 1126 x1; x2; x3; x4; x5; x6; | |
| 1127 } | |
| 1128 """; | |
| 1129 return analyze(source).then((result) { | |
| 1130 result.checkNodeHasType('x1', [result.int]); | |
| 1131 result.checkNodeHasType('x2', [result.int]); | |
| 1132 result.checkNodeHasType('x3', [result.int]); | |
| 1133 result.checkNodeHasType('x4', [result.string]); | |
| 1134 result.checkNodeHasType('x5', [result.string]); | |
| 1135 result.checkNodeHasType('x6', [result.string]); | |
| 1136 }); | |
| 1137 } | |
| 1138 | |
| 1139 | |
| 1140 testCompoundOperators2() { | |
| 1141 final String source = r""" | |
| 1142 class A { | |
| 1143 var xx; | |
| 1144 var yy; | |
| 1145 var witness1; | |
| 1146 var witness2; | |
| 1147 var witness3; | |
| 1148 var witness4; | |
| 1149 | |
| 1150 A(this.xx, this.yy); | |
| 1151 get x { witness1 = "foo"; return xx; } | |
| 1152 set x(a) { witness2 = "foo"; xx = a; } | |
| 1153 get y { witness3 = "foo"; return yy; } | |
| 1154 set y(a) { witness4 = "foo"; yy = a; } | |
| 1155 } | |
| 1156 main () { | |
| 1157 var a = new A(1, 1); | |
| 1158 a.x++; | |
| 1159 a.y++; | |
| 1160 } | |
| 1161 """; | |
| 1162 return analyze(source).then((result) { | |
| 1163 result.checkFieldHasType('A', 'xx', [result.int]); | |
| 1164 result.checkFieldHasType('A', 'yy', [result.int]); | |
| 1165 result.checkFieldHasType('A', 'witness1', [result.string, result.nullType]); | |
| 1166 result.checkFieldHasType('A', 'witness2', [result.string, result.nullType]); | |
| 1167 result.checkFieldHasType('A', 'witness3', [result.string, result.nullType]); | |
| 1168 result.checkFieldHasType('A', 'witness4', [result.string, result.nullType]); | |
| 1169 }); | |
| 1170 } | |
| 1171 | |
| 1172 testInequality() { | |
| 1173 final String source = r""" | |
| 1174 class A { | |
| 1175 var witness; | |
| 1176 operator ==(x) { witness = "foo"; return "abc"; } | |
| 1177 } | |
| 1178 class B { | |
| 1179 operator ==(x) { throw "error"; } | |
| 1180 } | |
| 1181 main() { | |
| 1182 var foo = 1 != 2; | |
| 1183 var bar = (new A() != 2); | |
| 1184 var baz = (new B() != 2); | |
| 1185 foo; bar; baz; | |
| 1186 } | |
| 1187 """; | |
| 1188 return analyze(source).then((result) { | |
| 1189 result.checkNodeHasType('foo', [result.bool]); | |
| 1190 result.checkNodeHasType('bar', [result.bool]); | |
| 1191 // TODO(polux): could be even better: empty | |
| 1192 result.checkNodeHasType('baz', [result.bool]); | |
| 1193 result.checkFieldHasType('A', 'witness', [result.string, result.nullType]); | |
| 1194 }); | |
| 1195 } | |
| 1196 | |
| 1197 testFieldInitialization1() { | |
| 1198 final String source = r""" | |
| 1199 class A { | |
| 1200 var x; | |
| 1201 var y = 1; | |
| 1202 } | |
| 1203 class B extends A { | |
| 1204 var z = "foo"; | |
| 1205 } | |
| 1206 main () { | |
| 1207 // we need to access y and z once to trigger their analysis | |
| 1208 new B().y; | |
| 1209 new B().z; | |
| 1210 } | |
| 1211 """; | |
| 1212 return analyze(source).then((result) { | |
| 1213 result.checkFieldHasType('A', 'x', [result.nullType]); | |
| 1214 result.checkFieldHasType('A', 'y', [result.int]); | |
| 1215 result.checkFieldHasType('B', 'z', [result.string]); | |
| 1216 }); | |
| 1217 } | |
| 1218 | |
| 1219 testFieldInitialization2() { | |
| 1220 final String source = r""" | |
| 1221 var top = 42; | |
| 1222 class A { | |
| 1223 var x = top; | |
| 1224 } | |
| 1225 main () { | |
| 1226 // we need to access X once to trigger its analysis | |
| 1227 new A().x; | |
| 1228 } | |
| 1229 """; | |
| 1230 return analyze(source).then((result) { | |
| 1231 result.checkFieldHasType('A', 'x', [result.int]); | |
| 1232 }); | |
| 1233 } | |
| 1234 | |
| 1235 testFieldInitialization3() { | |
| 1236 final String source = r""" | |
| 1237 class A { | |
| 1238 var x; | |
| 1239 } | |
| 1240 f() => new A().x; | |
| 1241 class B { | |
| 1242 var x = new A().x; | |
| 1243 var y = f(); | |
| 1244 } | |
| 1245 main () { | |
| 1246 var foo = new B().x; | |
| 1247 var bar = new B().y; | |
| 1248 new A().x = "a"; | |
| 1249 foo; bar; | |
| 1250 } | |
| 1251 """; | |
| 1252 return analyze(source).then((result) { | |
| 1253 // checks that B.B is set as a reader of A.x | |
| 1254 result.checkFieldHasType('B', 'x', [result.nullType, result.string]); | |
| 1255 // checks that B.B is set as a caller of f | |
| 1256 result.checkFieldHasType('B', 'y', [result.nullType, result.string]); | |
| 1257 // checks that readers of x are notified by changes in x's type | |
| 1258 result.checkNodeHasType('foo', [result.nullType, result.string]); | |
| 1259 // checks that readers of y are notified by changes in y's type | |
| 1260 result.checkNodeHasType('bar', [result.nullType, result.string]); | |
| 1261 }); | |
| 1262 } | |
| 1263 | |
| 1264 testLists() { | |
| 1265 final String source = """ | |
| 1266 class A {} | |
| 1267 class B {} | |
| 1268 class C {} | |
| 1269 class D {} | |
| 1270 class E {} | |
| 1271 class F {} | |
| 1272 class G {} | |
| 1273 | |
| 1274 main() { | |
| 1275 var l1 = [new A()]; | |
| 1276 var l2 = []; | |
| 1277 l1['a'] = new B(); // raises an error, so B should not be recorded | |
| 1278 l1[1] = new C(); | |
| 1279 l1.add(new D()); | |
| 1280 l1.insert('a', new E()); // raises an error, so E should not be recorded | |
| 1281 l1.insert(1, new F()); | |
| 1282 $DYNAMIC[1] = new G(); | |
| 1283 var x1 = l1[1]; | |
| 1284 var x2 = l2[1]; | |
| 1285 var x3 = l1['foo']; // raises an error, should return empty | |
| 1286 var x4 = l1.removeAt(1); | |
| 1287 var x5 = l2.removeAt(1); | |
| 1288 var x6 = l1.removeAt('a'); // raises an error, should return empty | |
| 1289 var x7 = l1.removeLast(); | |
| 1290 var x8 = l2.removeLast(); | |
| 1291 x1; x2; x3; x4; x5; x6; x7; x8; | |
| 1292 }"""; | |
| 1293 return analyze(source).then((result) { | |
| 1294 final expectedTypes = ['A', 'C', 'D', 'F', 'G'].map(result.base).toList(); | |
| 1295 result.checkNodeHasType('x1', expectedTypes); | |
| 1296 result.checkNodeHasType('x2', expectedTypes); | |
| 1297 result.checkNodeHasType('x3', []); | |
| 1298 result.checkNodeHasType('x4', expectedTypes); | |
| 1299 result.checkNodeHasType('x5', expectedTypes); | |
| 1300 result.checkNodeHasType('x6', []); | |
| 1301 result.checkNodeHasType('x7', expectedTypes); | |
| 1302 result.checkNodeHasType('x8', expectedTypes); | |
| 1303 }); | |
| 1304 } | |
| 1305 | |
| 1306 testListWithCapacity() { | |
| 1307 final String source = r""" | |
| 1308 main() { | |
| 1309 var l = new List(10); | |
| 1310 var x = [][0]; | |
| 1311 x; | |
| 1312 }"""; | |
| 1313 return analyze(source).then((result) { | |
| 1314 result.checkNodeHasType('x', [result.nullType]); | |
| 1315 }); | |
| 1316 } | |
| 1317 | |
| 1318 testEmptyList() { | |
| 1319 final String source = r""" | |
| 1320 main() { | |
| 1321 var l = new List(); | |
| 1322 var x = l[0]; | |
| 1323 x; | |
| 1324 }"""; | |
| 1325 return analyze(source).then((result) { | |
| 1326 result.checkNodeHasType('x', []); | |
| 1327 }); | |
| 1328 } | |
| 1329 | |
| 1330 testSendWithWrongArity() { | |
| 1331 final String source = r""" | |
| 1332 f(x) { } | |
| 1333 class A { g(x) { } } | |
| 1334 main () { | |
| 1335 var x = f(); | |
| 1336 var y = f(1, 2); | |
| 1337 var z = new A().g(); | |
| 1338 var w = new A().g(1, 2); | |
| 1339 x; y; z; w; | |
| 1340 } | |
| 1341 """; | |
| 1342 return analyze(source).then((result) { | |
| 1343 // TODO(polux): It would be better if x and y also had the empty type. This | |
| 1344 // requires a change in SimpleTypeInferrerVisitor.visitStaticSend which | |
| 1345 // would impact the default type inference and possibly break dart2js. | |
| 1346 // Keeping this change for a later CL. | |
| 1347 result.checkNodeHasUnknownType('x'); | |
| 1348 result.checkNodeHasUnknownType('y'); | |
| 1349 result.checkNodeHasType('z', []); | |
| 1350 result.checkNodeHasType('w', []); | |
| 1351 }); | |
| 1352 } | |
| 1353 | |
| 1354 testBigTypesWidening1() { | |
| 1355 final String source = r""" | |
| 1356 small() => true ? 1 : 'abc'; | |
| 1357 big() => true ? 1 : (true ? 'abc' : false); | |
| 1358 main () { | |
| 1359 var x = small(); | |
| 1360 var y = big(); | |
| 1361 x; y; | |
| 1362 } | |
| 1363 """; | |
| 1364 return analyze(source, maxConcreteTypeSize: 2).then((result) { | |
| 1365 result.checkNodeHasType('x', [result.int, result.string]); | |
| 1366 result.checkNodeHasUnknownType('y'); | |
| 1367 }); | |
| 1368 } | |
| 1369 | |
| 1370 testBigTypesWidening2() { | |
| 1371 final String source = r""" | |
| 1372 class A { | |
| 1373 var x, y; | |
| 1374 A(this.x, this.y); | |
| 1375 } | |
| 1376 main () { | |
| 1377 var a = new A(1, 1); | |
| 1378 a.x = 'abc'; | |
| 1379 a.y = 'abc'; | |
| 1380 a.y = true; | |
| 1381 } | |
| 1382 """; | |
| 1383 return analyze(source, maxConcreteTypeSize: 2).then((result) { | |
| 1384 result.checkFieldHasType('A', 'x', [result.int, result.string]); | |
| 1385 result.checkFieldHasUknownType('A', 'y'); | |
| 1386 }); | |
| 1387 } | |
| 1388 | |
| 1389 testDynamicIsAbsorbing() { | |
| 1390 final String source = """ | |
| 1391 main () { | |
| 1392 var x = 1; | |
| 1393 if (true) { | |
| 1394 x = $DYNAMIC; | |
| 1395 } else { | |
| 1396 x = 42; | |
| 1397 } | |
| 1398 x; | |
| 1399 } | |
| 1400 """; | |
| 1401 return analyze(source).then((result) { | |
| 1402 result.checkNodeHasUnknownType('x'); | |
| 1403 }); | |
| 1404 } | |
| 1405 | |
| 1406 testJsCall() { | |
| 1407 final String source = r""" | |
| 1408 import 'dart:_foreign_helper'; | |
| 1409 import 'dart:_interceptors'; | |
| 1410 | |
| 1411 abstract class AbstractA {} | |
| 1412 class A extends AbstractA {} | |
| 1413 class B extends A {} | |
| 1414 class BB extends B {} | |
| 1415 class C extends A {} | |
| 1416 class D implements A {} | |
| 1417 class E extends A {} | |
| 1418 | |
| 1419 class X {} | |
| 1420 | |
| 1421 main () { | |
| 1422 // we don't create any E on purpose | |
| 1423 new B(); new BB(); new C(); new D(); | |
| 1424 | |
| 1425 var a = JS('', '1'); | |
| 1426 var b = JS('Object', '1'); | |
| 1427 var c = JS('JSExtendableArray', '1'); | |
| 1428 var cNull = JS('JSExtendableArray|Null', '1'); | |
| 1429 var d = JS('String', '1'); | |
| 1430 var dNull = JS('String|Null', '1'); | |
| 1431 var e = JS('int', '1'); | |
| 1432 var eNull = JS('int|Null', '1'); | |
| 1433 var f = JS('double', '1'); | |
| 1434 var fNull = JS('double|Null', '1'); | |
| 1435 var g = JS('num', '1'); | |
| 1436 var gNull = JS('num|Null', '1'); | |
| 1437 var h = JS('bool', '1'); | |
| 1438 var hNull = JS('bool|Null', '1'); | |
| 1439 var i = JS('AbstractA', '1'); | |
| 1440 var iNull = JS('AbstractA|Null', '1'); | |
| 1441 | |
| 1442 a; b; c; cNull; d; dNull; e; eNull; f; fNull; g; gNull; h; hNull; i; | |
| 1443 iNull; | |
| 1444 } | |
| 1445 """; | |
| 1446 return analyze(source, maxConcreteTypeSize: 6).then((result) { | |
| 1447 List maybe(List types) => new List.from(types)..add(result.nullType); | |
| 1448 // a and b have all the types seen by the resolver, which are more than 6 | |
| 1449 result.checkNodeHasUnknownType('a'); | |
| 1450 result.checkNodeHasUnknownType('b'); | |
| 1451 final expectedCType = [result.growableList]; | |
| 1452 result.checkNodeHasType('c', expectedCType); | |
| 1453 result.checkNodeHasType('cNull', maybe(expectedCType)); | |
| 1454 final expectedDType = [result.string]; | |
| 1455 result.checkNodeHasType('d', expectedDType); | |
| 1456 result.checkNodeHasType('dNull', maybe(expectedDType)); | |
| 1457 final expectedEType = [result.int]; | |
| 1458 result.checkNodeHasType('e', expectedEType); | |
| 1459 result.checkNodeHasType('eNull', maybe(expectedEType)); | |
| 1460 final expectedFType = [result.num]; | |
| 1461 result.checkNodeHasType('f', expectedFType); | |
| 1462 result.checkNodeHasType('fNull', maybe(expectedFType)); | |
| 1463 final expectedGType = [result.num]; | |
| 1464 result.checkNodeHasType('g', expectedGType); | |
| 1465 result.checkNodeHasType('gNull', maybe(expectedGType)); | |
| 1466 final expectedType = [result.bool]; | |
| 1467 result.checkNodeHasType('h', expectedType); | |
| 1468 result.checkNodeHasType('hNull', maybe(expectedType)); | |
| 1469 final expectedIType = [result.base('B'), | |
| 1470 result.base('BB'), | |
| 1471 result.base('C'), | |
| 1472 result.base('D')]; | |
| 1473 result.checkNodeHasType('i', expectedIType); | |
| 1474 result.checkNodeHasType('iNull', maybe(expectedIType)); | |
| 1475 }); | |
| 1476 } | |
| 1477 | |
| 1478 testJsCallAugmentsSeenClasses() { | |
| 1479 final String source1 = """ | |
| 1480 main () { | |
| 1481 var x = $DYNAMIC.truncate(); | |
| 1482 x; | |
| 1483 } | |
| 1484 """; | |
| 1485 return analyze(source1).then((AnalysisResult result) { | |
| 1486 result.checkNodeHasType('x', []); | |
| 1487 }).whenComplete(() { | |
| 1488 | |
| 1489 final String source2 = """ | |
| 1490 import 'dart:_foreign_helper'; | |
| 1491 | |
| 1492 main () { | |
| 1493 var x = $DYNAMIC.truncate(); | |
| 1494 JS('double', 'foo'); | |
| 1495 x; | |
| 1496 } | |
| 1497 """; | |
| 1498 return analyze(source2).then((AnalysisResult result) { | |
| 1499 result.checkNodeHasType('x', [result.int]); | |
| 1500 }); | |
| 1501 }); | |
| 1502 } | |
| 1503 | |
| 1504 testIsCheck() { | |
| 1505 final String source = r""" | |
| 1506 main () { | |
| 1507 var x = (1 is String); | |
| 1508 x; | |
| 1509 } | |
| 1510 """; | |
| 1511 return analyze(source).then((result) { | |
| 1512 result.checkNodeHasType('x', [result.bool]); | |
| 1513 }); | |
| 1514 } | |
| 1515 | |
| 1516 testSeenClasses() { | |
| 1517 final String source = """ | |
| 1518 class A { | |
| 1519 witness() => 42; | |
| 1520 } | |
| 1521 class B { | |
| 1522 witness() => "string"; | |
| 1523 } | |
| 1524 class AFactory { | |
| 1525 onlyCalledInAFactory() => new A(); | |
| 1526 } | |
| 1527 class BFactory { | |
| 1528 onlyCalledInAFactory() => new B(); | |
| 1529 } | |
| 1530 | |
| 1531 main() { | |
| 1532 new AFactory().onlyCalledInAFactory(); | |
| 1533 new BFactory(); | |
| 1534 // should be of type {int} and not {int, String} since B is unreachable | |
| 1535 var foo = $DYNAMIC.witness(); | |
| 1536 foo; | |
| 1537 } | |
| 1538 """; | |
| 1539 return analyze(source).then((result) { | |
| 1540 result.checkNodeHasType('foo', [result.int]); | |
| 1541 }); | |
| 1542 } | |
| 1543 | |
| 1544 testIntDoubleNum() { | |
| 1545 final String source = r""" | |
| 1546 main() { | |
| 1547 var a = 1; | |
| 1548 var b = 1.1; | |
| 1549 var c = true ? 1 : 1.1; | |
| 1550 a; b; c; | |
| 1551 } | |
| 1552 """; | |
| 1553 return analyze(source).then((result) { | |
| 1554 result.checkNodeHasType('a', [result.int]); | |
| 1555 result.checkNodeHasType('b', [result.double]); | |
| 1556 result.checkNodeHasType('c', [result.num]); | |
| 1557 }); | |
| 1558 } | |
| 1559 | |
| 1560 testConcreteTypeToTypeMask() { | |
| 1561 final String source = r""" | |
| 1562 class A {} | |
| 1563 class B extends A {} | |
| 1564 class C extends A {} | |
| 1565 class D implements A {} | |
| 1566 main() { | |
| 1567 new A(); | |
| 1568 new B(); | |
| 1569 new C(); | |
| 1570 new D(); | |
| 1571 } | |
| 1572 """; | |
| 1573 return analyze(source).then((result) { | |
| 1574 | |
| 1575 convert(ConcreteType type) { | |
| 1576 return result.compiler.typesTask.concreteTypesInferrer | |
| 1577 .types.concreteTypeToTypeMask(type); | |
| 1578 } | |
| 1579 | |
| 1580 final nullSingleton = | |
| 1581 result.compiler.typesTask.concreteTypesInferrer.singletonConcreteType( | |
| 1582 new NullBaseType()); | |
| 1583 | |
| 1584 singleton(ClassElement element) { | |
| 1585 return result.compiler.typesTask.concreteTypesInferrer | |
| 1586 .singletonConcreteType(new ClassBaseType(element)); | |
| 1587 } | |
| 1588 | |
| 1589 var world = result.compiler.world; | |
| 1590 | |
| 1591 ClassElement a = findElement(result.compiler, 'A'); | |
| 1592 ClassElement b = findElement(result.compiler, 'B'); | |
| 1593 ClassElement c = findElement(result.compiler, 'C'); | |
| 1594 ClassElement d = findElement(result.compiler, 'D'); | |
| 1595 | |
| 1596 for (ClassElement cls in [a, b, c, d]) { | |
| 1597 Expect.equals(convert(singleton(cls)), | |
| 1598 new TypeMask.nonNullExact(cls, world)); | |
| 1599 } | |
| 1600 | |
| 1601 for (ClassElement cls in [a, b, c, d]) { | |
| 1602 Expect.equals(convert(singleton(cls).union(nullSingleton)), | |
| 1603 new TypeMask.exact(cls, world)); | |
| 1604 } | |
| 1605 | |
| 1606 Expect.equals(convert(singleton(a).union(singleton(b))), | |
| 1607 new TypeMask.nonNullSubclass(a, world)); | |
| 1608 | |
| 1609 Expect.equals( | |
| 1610 convert(singleton(a).union(singleton(b)).union(nullSingleton)), | |
| 1611 new TypeMask.subclass(a, world)); | |
| 1612 | |
| 1613 Expect.equals( | |
| 1614 simplify(convert(singleton(b).union(singleton(d))), result.compiler), | |
| 1615 new TypeMask.nonNullSubtype(a, world)); | |
| 1616 }); | |
| 1617 } | |
| 1618 | |
| 1619 testSelectors() { | |
| 1620 final String source = r""" | |
| 1621 // ABC <--- A | |
| 1622 // `- BC <--- B | |
| 1623 // `- C | |
| 1624 | |
| 1625 class ABC {} | |
| 1626 class A extends ABC {} | |
| 1627 class BC extends ABC {} | |
| 1628 class B extends BC {} | |
| 1629 class C extends BC {} | |
| 1630 | |
| 1631 class XY {} | |
| 1632 class X extends XY { foo() => new B(); } | |
| 1633 class Y extends XY { foo() => new C(); } | |
| 1634 class Z { foo() => new A(); } | |
| 1635 | |
| 1636 main() { | |
| 1637 new X().foo(); | |
| 1638 new Y().foo(); | |
| 1639 new Z().foo(); | |
| 1640 } | |
| 1641 """; | |
| 1642 return analyze(source).then((result) { | |
| 1643 | |
| 1644 var world = result.compiler.world; | |
| 1645 | |
| 1646 ClassElement a = findElement(result.compiler, 'A'); | |
| 1647 ClassElement b = findElement(result.compiler, 'B'); | |
| 1648 ClassElement c = findElement(result.compiler, 'C'); | |
| 1649 ClassElement xy = findElement(result.compiler, 'XY'); | |
| 1650 ClassElement x = findElement(result.compiler, 'X'); | |
| 1651 ClassElement y = findElement(result.compiler, 'Y'); | |
| 1652 ClassElement z = findElement(result.compiler, 'Z'); | |
| 1653 | |
| 1654 Selector foo = | |
| 1655 new Selector.call(const PublicName("foo"), CallStructure.NO_ARGS); | |
| 1656 | |
| 1657 result.checkSelectorHasType( | |
| 1658 foo, | |
| 1659 null, | |
| 1660 new TypeMask.unionOf([a, b, c] | |
| 1661 .map((cls) => new TypeMask.nonNullExact(cls, world)), | |
| 1662 result.compiler.world)); | |
| 1663 result.checkSelectorHasType( | |
| 1664 foo, | |
| 1665 new TypeMask.subclass(x, world), | |
| 1666 new TypeMask.nonNullExact(b, world)); | |
| 1667 result.checkSelectorHasType( | |
| 1668 foo, | |
| 1669 new TypeMask.subclass(y, world), | |
| 1670 new TypeMask.nonNullExact(c, world)); | |
| 1671 result.checkSelectorHasType( | |
| 1672 foo, | |
| 1673 new TypeMask.subclass(z, world), | |
| 1674 new TypeMask.nonNullExact(a, world)); | |
| 1675 result.checkSelectorHasType( | |
| 1676 foo, | |
| 1677 new TypeMask.subclass(xy, world), | |
| 1678 new TypeMask.unionOf([b, c].map((cls) => | |
| 1679 new TypeMask.nonNullExact(cls, world)), world)); | |
| 1680 | |
| 1681 result.checkSelectorHasType( | |
| 1682 new Selector.call(const PublicName("bar"), CallStructure.NO_ARGS), | |
| 1683 null, null); | |
| 1684 }); | |
| 1685 } | |
| 1686 | |
| 1687 testEqualsNullSelector() { | |
| 1688 final String source = r""" | |
| 1689 main() { | |
| 1690 1 == null; | |
| 1691 } | |
| 1692 """; | |
| 1693 return analyze(source).then((result) { | |
| 1694 ClassElement bool = result.compiler.backend.boolImplementation; | |
| 1695 result.checkSelectorHasType(new Selector.binaryOperator('=='), | |
| 1696 null, | |
| 1697 new TypeMask.nonNullExact(bool, | |
| 1698 result.compiler.world)); | |
| 1699 }); | |
| 1700 } | |
| 1701 | |
| 1702 testMixins() { | |
| 1703 final String source = r""" | |
| 1704 class A { | |
| 1705 foo() => "abc"; | |
| 1706 get x => 42; | |
| 1707 } | |
| 1708 class B extends Object with A { | |
| 1709 bar() => foo(); | |
| 1710 baz() => x; | |
| 1711 } | |
| 1712 main() { | |
| 1713 var b = new B(); | |
| 1714 var x = b.foo(); | |
| 1715 var y = b.bar(); | |
| 1716 var z = b.x; | |
| 1717 var w = b.baz(); | |
| 1718 x; y; z; w; | |
| 1719 } | |
| 1720 """; | |
| 1721 return analyze(source).then((result) { | |
| 1722 result.checkNodeHasType('x', [result.string]); | |
| 1723 result.checkNodeHasType('y', [result.string]); | |
| 1724 result.checkNodeHasType('z', [result.int]); | |
| 1725 result.checkNodeHasType('w', [result.int]); | |
| 1726 }); | |
| 1727 } | |
| 1728 | |
| 1729 testClosures1() { | |
| 1730 final String source = r""" | |
| 1731 class A { | |
| 1732 final foo = 42; | |
| 1733 } | |
| 1734 class B { | |
| 1735 final foo = "abc"; | |
| 1736 } | |
| 1737 class C { | |
| 1738 final foo = true; | |
| 1739 } | |
| 1740 main() { | |
| 1741 var a; | |
| 1742 var f = (x) { | |
| 1743 a = x.foo; | |
| 1744 }; | |
| 1745 // We make sure that x doesn't have type dynamic by adding C to the | |
| 1746 // set of seen classes and by checking that a's type doesn't contain | |
| 1747 // bool. | |
| 1748 new C(); | |
| 1749 f(new A()); | |
| 1750 f(new B()); | |
| 1751 a; | |
| 1752 } | |
| 1753 """; | |
| 1754 return analyze(source).then((AnalysisResult result) { | |
| 1755 result.checkNodeHasType('a', [result.nullType, result.int, result.string]); | |
| 1756 }); | |
| 1757 } | |
| 1758 | |
| 1759 testClosures2() { | |
| 1760 final String source = r""" | |
| 1761 class A { | |
| 1762 final foo = 42; | |
| 1763 } | |
| 1764 class B { | |
| 1765 final foo = "abc"; | |
| 1766 } | |
| 1767 class C { | |
| 1768 final foo = true; | |
| 1769 } | |
| 1770 main() { | |
| 1771 // We make sure that x doesn't have type dynamic by adding C to the | |
| 1772 // set of seen classes and by checking that a's type doesn't contain | |
| 1773 // bool. | |
| 1774 new C(); | |
| 1775 | |
| 1776 var a; | |
| 1777 f(x) { | |
| 1778 a = x.foo; | |
| 1779 } | |
| 1780 f(new A()); | |
| 1781 f(new B()); | |
| 1782 a; f; | |
| 1783 } | |
| 1784 """; | |
| 1785 return analyze(source).then((AnalysisResult result) { | |
| 1786 result.checkNodeHasType('a', [result.nullType, result.int, result.string]); | |
| 1787 result.checkNodeHasType('f', [result.functionType]); | |
| 1788 }); | |
| 1789 } | |
| 1790 | |
| 1791 testClosures3() { | |
| 1792 final String source = r""" | |
| 1793 class A { | |
| 1794 var g; | |
| 1795 A(this.g); | |
| 1796 } | |
| 1797 main() { | |
| 1798 var foo = new A((x) => x).g(42); | |
| 1799 foo; | |
| 1800 } | |
| 1801 """; | |
| 1802 return analyze(source).then((result) { | |
| 1803 result.checkNodeHasType('foo', [result.int]); | |
| 1804 }); | |
| 1805 } | |
| 1806 | |
| 1807 testClosures4() { | |
| 1808 final String source = """ | |
| 1809 class A { | |
| 1810 var f = $DYNAMIC; | |
| 1811 } | |
| 1812 main() { | |
| 1813 var f = (x) => x; | |
| 1814 var g = (x) => "a"; | |
| 1815 var h = (x, y) => true; | |
| 1816 | |
| 1817 var foo = $DYNAMIC(42); | |
| 1818 var bar = new A().f(1.2); | |
| 1819 var baz = $DYNAMIC.f(null); | |
| 1820 | |
| 1821 foo; bar; baz; | |
| 1822 } | |
| 1823 """; | |
| 1824 return analyze(source).then((result) { | |
| 1825 result.checkNodeHasType('foo', [result.int, result.string]); | |
| 1826 result.checkNodeHasType('bar', [result.double, result.string]); | |
| 1827 result.checkNodeHasType('baz', [result.nullType, result.string]); | |
| 1828 }); | |
| 1829 } | |
| 1830 | |
| 1831 testClosures5() { | |
| 1832 final String source = r""" | |
| 1833 f(x) => x; | |
| 1834 class A { | |
| 1835 var g; | |
| 1836 A(this.g); | |
| 1837 } | |
| 1838 main() { | |
| 1839 var foo = new A(f).g(42); | |
| 1840 foo; | |
| 1841 } | |
| 1842 """; | |
| 1843 return analyze(source).then((result) { | |
| 1844 result.checkNodeHasType('foo', [result.int]); | |
| 1845 }); | |
| 1846 } | |
| 1847 | |
| 1848 testClosures6() { | |
| 1849 final String source = r""" | |
| 1850 class A { | |
| 1851 var g; | |
| 1852 A(this.g); | |
| 1853 } | |
| 1854 class B { | |
| 1855 f(x) => x; | |
| 1856 } | |
| 1857 main() { | |
| 1858 var foo = new A(new B().f).g(42); | |
| 1859 foo; | |
| 1860 } | |
| 1861 """; | |
| 1862 return analyze(source).then((result) { | |
| 1863 result.checkNodeHasType('foo', [result.int]); | |
| 1864 }); | |
| 1865 } | |
| 1866 | |
| 1867 testClosures7() { | |
| 1868 final String source = r""" | |
| 1869 class A { | |
| 1870 final x = 42; | |
| 1871 f() => () => x; | |
| 1872 } | |
| 1873 main() { | |
| 1874 var foo = new A().f()(); | |
| 1875 foo; | |
| 1876 } | |
| 1877 """; | |
| 1878 return analyze(source).then((result) { | |
| 1879 result.checkNodeHasType('foo', [result.int]); | |
| 1880 }); | |
| 1881 } | |
| 1882 | |
| 1883 testClosures8() { | |
| 1884 final String source = r""" | |
| 1885 class A { | |
| 1886 final x = 42; | |
| 1887 f() => () => x; | |
| 1888 } | |
| 1889 class B extends A { | |
| 1890 get x => "a"; | |
| 1891 } | |
| 1892 main() { | |
| 1893 var foo = new B().f()(); | |
| 1894 foo; | |
| 1895 } | |
| 1896 """; | |
| 1897 return analyze(source).then((result) { | |
| 1898 result.checkNodeHasType('foo', [result.string]); | |
| 1899 }); | |
| 1900 } | |
| 1901 | |
| 1902 testClosures9() { | |
| 1903 final String source = r""" | |
| 1904 class A { | |
| 1905 g() => 42; | |
| 1906 f() => () => g(); | |
| 1907 } | |
| 1908 class B extends A { | |
| 1909 g() => "a"; | |
| 1910 } | |
| 1911 main() { | |
| 1912 var foo = new B().f()(); | |
| 1913 foo; | |
| 1914 } | |
| 1915 """; | |
| 1916 return analyze(source).then((result) { | |
| 1917 result.checkNodeHasType('foo', [result.string]); | |
| 1918 }); | |
| 1919 } | |
| 1920 | |
| 1921 testClosures10() { | |
| 1922 final String source = r""" | |
| 1923 class A { | |
| 1924 f() => 42; | |
| 1925 } | |
| 1926 main() { | |
| 1927 var a = new A(); | |
| 1928 g() => a.f(); | |
| 1929 var foo = g(); | |
| 1930 foo; a; | |
| 1931 } | |
| 1932 """; | |
| 1933 return analyze(source).then((result) { | |
| 1934 result.checkNodeHasType('foo', [result.int]); | |
| 1935 }); | |
| 1936 } | |
| 1937 | |
| 1938 testClosures11() { | |
| 1939 final String source = r""" | |
| 1940 class A { | |
| 1941 var x; | |
| 1942 f() => x; | |
| 1943 } | |
| 1944 main() { | |
| 1945 var a = new A(); | |
| 1946 f() => a.f(); | |
| 1947 a.x = 42; | |
| 1948 var foo = f(); | |
| 1949 foo; | |
| 1950 } | |
| 1951 """; | |
| 1952 return analyze(source).then((result) { | |
| 1953 result.checkNodeHasType('foo', [result.nullType, result.int]); | |
| 1954 }); | |
| 1955 } | |
| 1956 | |
| 1957 testClosures12() { | |
| 1958 final String source = r""" | |
| 1959 var f = (x) => x; | |
| 1960 main() { | |
| 1961 var foo = f(1); | |
| 1962 foo; | |
| 1963 } | |
| 1964 """; | |
| 1965 return analyze(source).then((result) { | |
| 1966 result.checkNodeHasType('foo', [result.int]); | |
| 1967 }); | |
| 1968 } | |
| 1969 | |
| 1970 testRefinement() { | |
| 1971 final String source = """ | |
| 1972 class A { | |
| 1973 f() => null; | |
| 1974 g() => 42; | |
| 1975 } | |
| 1976 class B { | |
| 1977 g() => "aa"; | |
| 1978 } | |
| 1979 main() { | |
| 1980 var x = $DYNAMIC ? new A() : new B(); | |
| 1981 x.f(); | |
| 1982 var foo = x.g(); | |
| 1983 foo; | |
| 1984 } | |
| 1985 """; | |
| 1986 return analyze(source).then((result) { | |
| 1987 result.checkNodeHasType('foo', [result.int]); | |
| 1988 }); | |
| 1989 } | |
| 1990 | |
| 1991 testDefaultArguments() { | |
| 1992 final String source = r""" | |
| 1993 f1([x = 42]) => x; | |
| 1994 g1([x]) => x; | |
| 1995 | |
| 1996 f2({x: 42}) => x; | |
| 1997 g2({x}) => x; | |
| 1998 | |
| 1999 main() { | |
| 2000 var xf1 = f1(); | |
| 2001 var xg1 = g1(); | |
| 2002 var xf2 = f2(); | |
| 2003 var xg2 = g2(); | |
| 2004 xf1; xg1; xf2; xg2; | |
| 2005 } | |
| 2006 """; | |
| 2007 return analyze(source).then((result) { | |
| 2008 result.checkNodeHasType('xf1', [result.int]); | |
| 2009 result.checkNodeHasType('xg1', [result.nullType]); | |
| 2010 result.checkNodeHasType('xf2', [result.int]); | |
| 2011 result.checkNodeHasType('xg2', [result.nullType]); | |
| 2012 }); | |
| 2013 } | |
| 2014 | |
| 2015 testSuperConstructorCall() { | |
| 2016 final String source = r""" | |
| 2017 class A { | |
| 2018 final x; | |
| 2019 A(this.x); | |
| 2020 } | |
| 2021 | |
| 2022 class B extends A { | |
| 2023 B(x) : super(x); | |
| 2024 } | |
| 2025 main() { | |
| 2026 var b = new B(42); | |
| 2027 var foo = b.x; | |
| 2028 foo; | |
| 2029 } | |
| 2030 """; | |
| 2031 return analyze(source).then((result) { | |
| 2032 result.checkFieldHasType('A', 'x', [result.int]); | |
| 2033 result.checkNodeHasType('foo', [result.int]); | |
| 2034 }); | |
| 2035 } | |
| 2036 | |
| 2037 testSuperConstructorCall2() { | |
| 2038 final String source = r""" | |
| 2039 class A { | |
| 2040 var x; | |
| 2041 A() { | |
| 2042 x = 42; | |
| 2043 } | |
| 2044 } | |
| 2045 class B extends A { | |
| 2046 } | |
| 2047 main() { | |
| 2048 new B(); | |
| 2049 } | |
| 2050 """; | |
| 2051 return analyze(source).then((result) { | |
| 2052 result.checkFieldHasType('A', 'x', [result.int]); | |
| 2053 }); | |
| 2054 } | |
| 2055 | |
| 2056 testSuperConstructorCall3() { | |
| 2057 final String source = r""" | |
| 2058 class A { | |
| 2059 var x; | |
| 2060 A() { | |
| 2061 x = 42; | |
| 2062 } | |
| 2063 } | |
| 2064 class B extends A { | |
| 2065 B(unused) {} | |
| 2066 } | |
| 2067 main() { | |
| 2068 new B("abc"); | |
| 2069 } | |
| 2070 """; | |
| 2071 return analyze(source).then((result) { | |
| 2072 result.checkFieldHasType('A', 'x', [result.int]); | |
| 2073 }); | |
| 2074 } | |
| 2075 | |
| 2076 void main() { | |
| 2077 asyncTest(() => Future.forEach([ | |
| 2078 testDynamicBackDoor, | |
| 2079 testVariableDeclaration, | |
| 2080 testLiterals, | |
| 2081 testRedefinition, | |
| 2082 testIfThenElse, | |
| 2083 testTernaryIf, | |
| 2084 testWhile, | |
| 2085 testDoWhile, | |
| 2086 testFor1, | |
| 2087 testFor2, | |
| 2088 testFor3, | |
| 2089 testForIn, | |
| 2090 testToplevelVariable, | |
| 2091 testToplevelVariable2, | |
| 2092 testToplevelVariable3, | |
| 2093 testNonRecusiveFunction, | |
| 2094 testMultipleReturns, | |
| 2095 testRecusiveFunction, | |
| 2096 testMutuallyRecusiveFunction, | |
| 2097 testSimpleSend, | |
| 2098 testSendToThis1, | |
| 2099 testSendToThis2, | |
| 2100 testSendToThis3, | |
| 2101 testSendToThis4, | |
| 2102 testConstructor, | |
| 2103 testGetters, | |
| 2104 testToplevelGetters, | |
| 2105 testDynamicGetters, | |
| 2106 testSetters, | |
| 2107 testToplevelSetters, | |
| 2108 testOptionalNamedParameters, | |
| 2109 testOptionalPositionalParameters, | |
| 2110 testListLiterals, | |
| 2111 testMapLiterals, | |
| 2112 testReturn, | |
| 2113 testNoReturn, | |
| 2114 testArithmeticOperators, | |
| 2115 testBooleanOperators, | |
| 2116 testBooleanOperatorsShortCirtcuit, | |
| 2117 testOperators, | |
| 2118 testCompoundOperators1, | |
| 2119 testCompoundOperators2, | |
| 2120 testSetIndexOperator, | |
| 2121 testInequality, | |
| 2122 testFieldInitialization1, | |
| 2123 testFieldInitialization2, | |
| 2124 testFieldInitialization3, | |
| 2125 testSendWithWrongArity, | |
| 2126 testBigTypesWidening1, | |
| 2127 testBigTypesWidening2, | |
| 2128 testDynamicIsAbsorbing, | |
| 2129 testLists, | |
| 2130 testListWithCapacity, | |
| 2131 testEmptyList, | |
| 2132 testJsCall, | |
| 2133 testJsCallAugmentsSeenClasses, | |
| 2134 testIsCheck, | |
| 2135 testSeenClasses, | |
| 2136 testIntDoubleNum, | |
| 2137 testConcreteTypeToTypeMask, | |
| 2138 testSelectors, | |
| 2139 // TODO(polux): this test is failing, see http://dartbug.com/16825. | |
| 2140 //testEqualsNullSelector, | |
| 2141 testMixins, | |
| 2142 testClosures1, | |
| 2143 testClosures2, | |
| 2144 testClosures3, | |
| 2145 testClosures4, | |
| 2146 testClosures5, | |
| 2147 testClosures6, | |
| 2148 testClosures7, | |
| 2149 testClosures8, | |
| 2150 testClosures9, | |
| 2151 testClosures10, | |
| 2152 testClosures11, | |
| 2153 testClosures12, | |
| 2154 testRefinement, | |
| 2155 testDefaultArguments, | |
| 2156 testSuperConstructorCall, | |
| 2157 testSuperConstructorCall2, | |
| 2158 testSuperConstructorCall3, | |
| 2159 ], (f) => f())); | |
| 2160 } | |
| OLD | NEW |