| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, 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 'mock_sdk.dart'; | |
| 6 import 'package:analyzer/file_system/memory_file_system.dart'; | |
| 7 import 'package:analyzer/src/generated/ast.dart'; | |
| 8 import 'package:analyzer/src/generated/element.dart'; | |
| 9 import 'package:analyzer/src/generated/sdk.dart'; | |
| 10 import 'package:analyzer/src/generated/source.dart'; | |
| 11 import 'package:compiler/src/dart2jslib.dart' show NullSink; | |
| 12 import 'package:unittest/unittest.dart'; | |
| 13 | |
| 14 import '../lib/src/closed_world.dart'; | |
| 15 import '../lib/src/driver.dart'; | |
| 16 | |
| 17 main() { | |
| 18 test('Toplevel function', () { | |
| 19 var helper = new TreeShakerTestHelper(''' | |
| 20 main() { | |
| 21 foo(); | |
| 22 } | |
| 23 foo() { | |
| 24 } | |
| 25 '''); | |
| 26 helper.assertHasFunction('main'); | |
| 27 helper.assertHasFunction('foo'); | |
| 28 }); | |
| 29 | |
| 30 test('Toplevel field read', () { | |
| 31 var helper = new TreeShakerTestHelper(''' | |
| 32 main() { | |
| 33 return foo; | |
| 34 } | |
| 35 var foo; | |
| 36 var bar; | |
| 37 '''); | |
| 38 helper.assertHasFunction('main'); | |
| 39 helper.assertHasVariable('foo'); | |
| 40 helper.assertNoVariable('bar'); | |
| 41 }); | |
| 42 | |
| 43 test('Toplevel field write', () { | |
| 44 var helper = new TreeShakerTestHelper(''' | |
| 45 main() { | |
| 46 foo = 1; | |
| 47 } | |
| 48 var foo; | |
| 49 var bar; | |
| 50 '''); | |
| 51 helper.assertHasFunction('main'); | |
| 52 helper.assertHasVariable('foo'); | |
| 53 helper.assertNoVariable('bar'); | |
| 54 }); | |
| 55 | |
| 56 test('Toplevel field invocation', () { | |
| 57 var helper = new TreeShakerTestHelper(''' | |
| 58 main() { | |
| 59 return foo(); | |
| 60 } | |
| 61 var foo; | |
| 62 var bar; | |
| 63 '''); | |
| 64 helper.assertHasFunction('main'); | |
| 65 helper.assertHasVariable('foo'); | |
| 66 helper.assertNoVariable('bar'); | |
| 67 }); | |
| 68 | |
| 69 test('Member field invocation', () { | |
| 70 var helper = new TreeShakerTestHelper(''' | |
| 71 class A { | |
| 72 void call() {} | |
| 73 void baz() {} | |
| 74 } | |
| 75 main() { | |
| 76 new A(); | |
| 77 foo(); | |
| 78 } | |
| 79 var foo; | |
| 80 var bar; | |
| 81 '''); | |
| 82 helper.assertHasFunction('main'); | |
| 83 helper.assertHasVariable('foo'); | |
| 84 helper.assertNoVariable('bar'); | |
| 85 helper.assertHasInstantiatedClass('A'); | |
| 86 helper.assertHasMethod('A.call'); | |
| 87 helper.assertNoMethod('A.baz'); | |
| 88 }); | |
| 89 | |
| 90 test('Class instantiation', () { | |
| 91 var helper = new TreeShakerTestHelper(''' | |
| 92 main() { | |
| 93 var x = new A(); | |
| 94 } | |
| 95 class A {} | |
| 96 class B {} | |
| 97 '''); | |
| 98 helper.assertHasInstantiatedClass('A'); | |
| 99 helper.assertNoInstantiatedClass('B'); | |
| 100 }); | |
| 101 | |
| 102 test('Super class instantiation', () { | |
| 103 var helper = new TreeShakerTestHelper(''' | |
| 104 main() { | |
| 105 var x = new B(); | |
| 106 } | |
| 107 class A {} | |
| 108 class B extends A {} | |
| 109 '''); | |
| 110 helper.assertHasInstantiatedClass('A'); | |
| 111 helper.assertHasInstantiatedClass('B'); | |
| 112 }); | |
| 113 | |
| 114 test('Method invocation', () { | |
| 115 var helper = new TreeShakerTestHelper(''' | |
| 116 main() { | |
| 117 var x = new A().foo(); | |
| 118 } | |
| 119 class A { | |
| 120 foo() {} | |
| 121 bar() {} | |
| 122 } | |
| 123 class B { | |
| 124 foo() {} | |
| 125 bar() {} | |
| 126 } | |
| 127 '''); | |
| 128 helper.assertHasMethod('A.foo'); | |
| 129 helper.assertNoMethod('A.bar'); | |
| 130 helper.assertNoMethod('B.foo'); | |
| 131 helper.assertNoMethod('B.bar'); | |
| 132 }); | |
| 133 | |
| 134 test('Method invocation on dynamic', () { | |
| 135 var helper = new TreeShakerTestHelper(''' | |
| 136 class A { | |
| 137 m1() {} | |
| 138 m2() {} | |
| 139 } | |
| 140 foo(dynamic x) { | |
| 141 x.m1(); | |
| 142 } | |
| 143 main() { | |
| 144 foo(new A()); | |
| 145 } | |
| 146 '''); | |
| 147 helper.assertHasMethod('A.m1'); | |
| 148 helper.assertNoMethod('A.m2'); | |
| 149 }); | |
| 150 | |
| 151 test('Method invocation on dynamic via cascade', () { | |
| 152 var helper = new TreeShakerTestHelper(''' | |
| 153 class A { | |
| 154 m1() {} | |
| 155 m2() {} | |
| 156 } | |
| 157 foo(dynamic x) { | |
| 158 x..m1()..m2(); | |
| 159 } | |
| 160 main() { | |
| 161 foo(new A()); | |
| 162 } | |
| 163 '''); | |
| 164 helper.assertHasMethod('A.m1'); | |
| 165 helper.assertHasMethod('A.m2'); | |
| 166 }); | |
| 167 | |
| 168 test('Getter usage', () { | |
| 169 var helper = new TreeShakerTestHelper(''' | |
| 170 class A { | |
| 171 get g1 => null; | |
| 172 get g2 => null; | |
| 173 set g1(x) {} | |
| 174 set g2(x) {} | |
| 175 } | |
| 176 class B { | |
| 177 get g1 => null; | |
| 178 get g2 => null; | |
| 179 set g1(x) {} | |
| 180 set g2(x) {} | |
| 181 } | |
| 182 main() { | |
| 183 new A().g1; | |
| 184 } | |
| 185 '''); | |
| 186 helper.assertHasGetter('A.g1'); | |
| 187 helper.assertNoGetter('A.g2'); | |
| 188 helper.assertNoGetter('B.g1'); | |
| 189 helper.assertNoGetter('B.g2'); | |
| 190 helper.assertNoSetter('A.g1'); | |
| 191 helper.assertNoSetter('A.g2'); | |
| 192 helper.assertNoSetter('B.g1'); | |
| 193 helper.assertNoSetter('B.g2'); | |
| 194 }); | |
| 195 | |
| 196 test('Setter usage', () { | |
| 197 var helper = new TreeShakerTestHelper(''' | |
| 198 class A { | |
| 199 get g1 => null; | |
| 200 get g2 => null; | |
| 201 set g1(x) {} | |
| 202 set g2(x) {} | |
| 203 } | |
| 204 class B { | |
| 205 get g1 => null; | |
| 206 get g2 => null; | |
| 207 set g1(x) {} | |
| 208 set g2(x) {} | |
| 209 } | |
| 210 main() { | |
| 211 new A().g1 = 1; | |
| 212 } | |
| 213 '''); | |
| 214 helper.assertHasSetter('A.g1'); | |
| 215 helper.assertNoSetter('A.g2'); | |
| 216 helper.assertNoSetter('B.g1'); | |
| 217 helper.assertNoSetter('B.g2'); | |
| 218 helper.assertNoGetter('A.g1'); | |
| 219 helper.assertNoGetter('A.g2'); | |
| 220 helper.assertNoGetter('B.g1'); | |
| 221 helper.assertNoGetter('B.g2'); | |
| 222 }); | |
| 223 | |
| 224 test('Field read', () { | |
| 225 var helper = new TreeShakerTestHelper(''' | |
| 226 class A { | |
| 227 var f1; | |
| 228 var f2; | |
| 229 } | |
| 230 class B { | |
| 231 var f1; | |
| 232 var f2; | |
| 233 } | |
| 234 main() { | |
| 235 new A().f1; | |
| 236 } | |
| 237 '''); | |
| 238 helper.assertHasField('A.f1'); | |
| 239 helper.assertNoField('A.f2'); | |
| 240 helper.assertNoField('B.f1'); | |
| 241 helper.assertNoField('B.f2'); | |
| 242 }); | |
| 243 | |
| 244 test('Field write', () { | |
| 245 var helper = new TreeShakerTestHelper(''' | |
| 246 class A { | |
| 247 var f1; | |
| 248 var f2; | |
| 249 } | |
| 250 class B { | |
| 251 var f1; | |
| 252 var f2; | |
| 253 } | |
| 254 main() { | |
| 255 new A().f1 = 1; | |
| 256 } | |
| 257 '''); | |
| 258 helper.assertHasField('A.f1'); | |
| 259 helper.assertNoField('A.f2'); | |
| 260 helper.assertNoField('B.f1'); | |
| 261 helper.assertNoField('B.f2'); | |
| 262 }); | |
| 263 | |
| 264 test('Ordinary constructor with initializer list', () { | |
| 265 var helper = new TreeShakerTestHelper(''' | |
| 266 class A { | |
| 267 A() : x = f(); | |
| 268 var x; | |
| 269 foo() {} | |
| 270 } | |
| 271 f() {} | |
| 272 main() { | |
| 273 new A().foo(); | |
| 274 } | |
| 275 '''); | |
| 276 helper.assertHasMethod('A.foo'); | |
| 277 helper.assertHasFunction('f'); | |
| 278 }); | |
| 279 | |
| 280 test('Redirecting constructor', () { | |
| 281 var helper = new TreeShakerTestHelper(''' | |
| 282 class A { | |
| 283 A.a1() : this.a2(); | |
| 284 A.a2(); | |
| 285 foo() {} | |
| 286 } | |
| 287 main() { | |
| 288 new A.a1().foo(); | |
| 289 } | |
| 290 '''); | |
| 291 helper.assertHasMethod('A.foo'); | |
| 292 }); | |
| 293 | |
| 294 test('Factory constructor', () { | |
| 295 var helper = new TreeShakerTestHelper(''' | |
| 296 class A { | |
| 297 factory A() { | |
| 298 return new B(); | |
| 299 } | |
| 300 foo() {} | |
| 301 } | |
| 302 class B { | |
| 303 B(); | |
| 304 foo() {} | |
| 305 } | |
| 306 main() { | |
| 307 new A().foo(); | |
| 308 } | |
| 309 '''); | |
| 310 helper.assertHasMethod('B.foo'); | |
| 311 helper.assertNoMethod('A.foo'); | |
| 312 }); | |
| 313 | |
| 314 test('Redirecting factory constructor', () { | |
| 315 var helper = new TreeShakerTestHelper(''' | |
| 316 class A { | |
| 317 factory A() = B; | |
| 318 foo() {} | |
| 319 } | |
| 320 class B { | |
| 321 B(); | |
| 322 foo() {} | |
| 323 } | |
| 324 main() { | |
| 325 new A().foo(); | |
| 326 } | |
| 327 '''); | |
| 328 helper.assertHasMethod('B.foo'); | |
| 329 helper.assertNoMethod('A.foo'); | |
| 330 }); | |
| 331 } | |
| 332 | |
| 333 class TreeShakerTestHelper { | |
| 334 /** | |
| 335 * The name of the root file. | |
| 336 */ | |
| 337 String rootFile = '/root.dart'; | |
| 338 | |
| 339 /** | |
| 340 * ClosedWorld that resulted from tree shaking. | |
| 341 */ | |
| 342 ClosedWorld world; | |
| 343 | |
| 344 /** | |
| 345 * Functions contained in [world], indexed by name. | |
| 346 */ | |
| 347 Map<String, FunctionDeclaration> functions = <String, FunctionDeclaration>{}; | |
| 348 | |
| 349 /** | |
| 350 * Methods contained in [world], indexed by className.methodName. | |
| 351 */ | |
| 352 Map<String, MethodDeclaration> methods = <String, MethodDeclaration>{}; | |
| 353 | |
| 354 /** | |
| 355 * Getters contained in [world], indexed by className.propertyName. | |
| 356 */ | |
| 357 Map<String, MethodDeclaration> getters = <String, MethodDeclaration>{}; | |
| 358 | |
| 359 /** | |
| 360 * Setters contained in [world], indexed by className.propertyName. | |
| 361 */ | |
| 362 Map<String, MethodDeclaration> setters = <String, MethodDeclaration>{}; | |
| 363 | |
| 364 /** | |
| 365 * Fields contained in [world], indexed by className.fieldName. | |
| 366 */ | |
| 367 Map<String, VariableDeclaration> fields = <String, VariableDeclaration>{}; | |
| 368 | |
| 369 /** | |
| 370 * Top level variables contained in [world], indexed by name. | |
| 371 */ | |
| 372 Map<String, VariableDeclaration> variables = <String, VariableDeclaration>{}; | |
| 373 | |
| 374 /** | |
| 375 * Classes instantiated in [world], indexed by name. | |
| 376 */ | |
| 377 Map<String, ClassDeclaration> instantiatedClasses = <String, | |
| 378 ClassDeclaration>{}; | |
| 379 | |
| 380 /** | |
| 381 * Create a TreeShakerTestHelper based on the given file contents. | |
| 382 */ | |
| 383 TreeShakerTestHelper(String contents) { | |
| 384 MemoryResourceProvider provider = new MemoryResourceProvider(); | |
| 385 DartSdk sdk = new MockSdk(); | |
| 386 Driver driver = new Driver(provider, sdk, NullSink.outputProvider); | |
| 387 provider.newFile(rootFile, contents); | |
| 388 Source rootSource = driver.setRoot(rootFile); | |
| 389 FunctionElement entryPoint = driver.resolveEntryPoint(rootSource); | |
| 390 world = driver.computeWorld(entryPoint); | |
| 391 world.executableElements.forEach( | |
| 392 (ExecutableElement element, Declaration node) { | |
| 393 if (element is FunctionElement) { | |
| 394 FunctionDeclaration declaration = node as FunctionDeclaration; | |
| 395 expect(declaration, isNotNull); | |
| 396 expect(declaration.element, equals(element)); | |
| 397 functions[element.name] = declaration; | |
| 398 } else if (element is MethodElement) { | |
| 399 MethodDeclaration declaration = node as MethodDeclaration; | |
| 400 expect(declaration, isNotNull); | |
| 401 expect(declaration.element, equals(element)); | |
| 402 methods['${element.enclosingElement.name}.${element.name}'] = | |
| 403 declaration; | |
| 404 } else if (element is PropertyAccessorElement) { | |
| 405 MethodDeclaration declaration = node as MethodDeclaration; | |
| 406 expect(declaration, isNotNull); | |
| 407 expect(declaration.element, equals(element)); | |
| 408 if (declaration.isGetter) { | |
| 409 getters['${element.enclosingElement.name}.${element.name}'] = | |
| 410 declaration; | |
| 411 } else if (declaration.isSetter) { | |
| 412 setters['${element.enclosingElement.name}.${element.displayName}'] = | |
| 413 declaration; | |
| 414 } else { | |
| 415 fail('Unexpected property accessor (neither getter nor setter)'); | |
| 416 } | |
| 417 } | |
| 418 }); | |
| 419 world.instantiatedClasses.forEach( | |
| 420 (ClassElement element, ClassDeclaration declaration) { | |
| 421 expect(declaration, isNotNull); | |
| 422 expect(declaration.element, equals(element)); | |
| 423 instantiatedClasses[element.name] = declaration; | |
| 424 }); | |
| 425 world.fields.forEach( | |
| 426 (FieldElement element, VariableDeclaration declaration) { | |
| 427 expect(declaration, isNotNull); | |
| 428 expect(declaration.element, equals(element)); | |
| 429 fields['${element.enclosingElement.name}.${element.name}'] = declaration; | |
| 430 }); | |
| 431 world.variables.forEach( | |
| 432 (TopLevelVariableElement element, VariableDeclaration declaration) { | |
| 433 expect(declaration, isNotNull); | |
| 434 expect(declaration.element, equals(element)); | |
| 435 variables['${element.name}'] = declaration; | |
| 436 }); | |
| 437 } | |
| 438 | |
| 439 /** | |
| 440 * Asserts that [world] contains a field with the given qualified name. | |
| 441 */ | |
| 442 void assertHasField(String qualifiedName) { | |
| 443 expect(fields, contains(qualifiedName)); | |
| 444 } | |
| 445 | |
| 446 /** | |
| 447 * Asserts that [world] contains a top level variable with the given name. | |
| 448 */ | |
| 449 void assertHasVariable(String name) { | |
| 450 expect(variables, contains(name)); | |
| 451 } | |
| 452 | |
| 453 /** | |
| 454 * Asserts that [world] contains a top-level function with the given name. | |
| 455 */ | |
| 456 void assertHasFunction(String name) { | |
| 457 expect(functions, contains(name)); | |
| 458 } | |
| 459 | |
| 460 /** | |
| 461 * Asserts that [world] contains a getter with the given qualified name. | |
| 462 */ | |
| 463 void assertHasGetter(String qualifiedName) { | |
| 464 expect(getters, contains(qualifiedName)); | |
| 465 } | |
| 466 | |
| 467 /** | |
| 468 * Asserts that [world] contains a setter with the given qualified name. | |
| 469 */ | |
| 470 void assertHasSetter(String qualifiedName) { | |
| 471 expect(setters, contains(qualifiedName)); | |
| 472 } | |
| 473 | |
| 474 /** | |
| 475 * Asserts that [world] instantiates a class with the given name. | |
| 476 */ | |
| 477 void assertHasInstantiatedClass(String name) { | |
| 478 expect(instantiatedClasses, contains(name)); | |
| 479 } | |
| 480 | |
| 481 /** | |
| 482 * Asserts that [world] contains a method with the given qualified name. | |
| 483 * | |
| 484 * [qualifiedName] - the qualified name in form 'className.methodName'. | |
| 485 */ | |
| 486 void assertHasMethod(String qualifiedName) { | |
| 487 expect(methods, contains(qualifiedName)); | |
| 488 } | |
| 489 | |
| 490 /** | |
| 491 * Asserts that [world] doesn't contain a field with the given qualified | |
| 492 * name. | |
| 493 */ | |
| 494 void assertNoField(String qualifiedName) { | |
| 495 expect(fields, isNot(contains(qualifiedName))); | |
| 496 } | |
| 497 | |
| 498 /** | |
| 499 * Asserts that [world] doesn't contain a top level variable with the given | |
| 500 * name. | |
| 501 */ | |
| 502 void assertNoVariable(String name) { | |
| 503 expect(variables, isNot(contains(name))); | |
| 504 } | |
| 505 | |
| 506 /** | |
| 507 * Asserts that [world] doesn't contain a top-level function with the given | |
| 508 * name. | |
| 509 */ | |
| 510 void assertNoFunction(String name) { | |
| 511 expect(functions, isNot(contains(name))); | |
| 512 } | |
| 513 | |
| 514 /** | |
| 515 * Asserts that [world] doesn't contain a getter with the given qualified | |
| 516 * name. | |
| 517 */ | |
| 518 void assertNoGetter(String qualifiedName) { | |
| 519 expect(getters, isNot(contains(qualifiedName))); | |
| 520 } | |
| 521 | |
| 522 /** | |
| 523 * Asserts that [world] doesn't contain a setter with the given qualified | |
| 524 * name. | |
| 525 */ | |
| 526 void assertNoSetter(String qualifiedName) { | |
| 527 expect(setters, isNot(contains(qualifiedName))); | |
| 528 } | |
| 529 | |
| 530 /** | |
| 531 * Asserts that [world] doesn't instantiate a class with the given name. | |
| 532 */ | |
| 533 void assertNoInstantiatedClass(String name) { | |
| 534 expect(instantiatedClasses, isNot(contains(name))); | |
| 535 } | |
| 536 | |
| 537 /** | |
| 538 * Asserts that [world] doesn't contain a method with the given qualified | |
| 539 * name. | |
| 540 * | |
| 541 * [qualifiedName] - the qualified name in form 'className.methodName'. | |
| 542 */ | |
| 543 void assertNoMethod(String qualifiedName) { | |
| 544 expect(methods, isNot(contains(qualifiedName))); | |
| 545 } | |
| 546 } | |
| OLD | NEW |