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 |