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 // Test of Compiler.forgetElement. | |
6 library trydart.forget_element_test; | |
7 | |
8 import 'package:compiler/src/elements/elements.dart' show | |
9 AstElement, | |
10 ClassElement, | |
11 Element, | |
12 FunctionElement, | |
13 LocalFunctionElement, | |
14 MetadataAnnotation, | |
15 ScopeContainerElement, | |
16 VariableElement; | |
17 | |
18 import 'package:compiler/src/js_backend/js_backend.dart' show | |
19 JavaScriptBackend; | |
20 | |
21 import 'package:compiler/src/tree/tree.dart' as tree; | |
22 | |
23 import 'package:compiler/src/parser/partial_elements.dart' show | |
24 PartialMetadataAnnotation; | |
25 | |
26 import 'package:compiler/src/elements/visitor.dart' show | |
27 ElementVisitor; | |
28 | |
29 import 'package:compiler/src/compile_time_constants.dart' show | |
30 DartConstantCompiler; | |
31 | |
32 import 'package:compiler/src/universe/universe.dart' show | |
33 Universe; | |
34 | |
35 import 'package:compiler/src/dart_types.dart' show | |
36 DartType; | |
37 | |
38 import 'compiler_test_case.dart'; | |
39 | |
40 import 'forget_element_assertion.dart' show | |
41 assertUnimplementedLocalMetadata; | |
42 | |
43 class ForgetElementTestCase extends CompilerTestCase { | |
44 final int expectedClosureCount; | |
45 | |
46 final int expectedMetadataCount; | |
47 | |
48 final int expectedConstantCount; | |
49 | |
50 final int expectedInitialValueCount; | |
51 | |
52 final int expectedInitialDartValueCount; | |
53 | |
54 final int additionalClosureClassMaps; | |
55 | |
56 JavaScriptBackend get backend => compiler.backend; | |
57 | |
58 DartConstantCompiler get dartConstants => | |
59 backend.constantCompilerTask.dartConstantCompiler; | |
60 | |
61 Universe get codegenUniverse => compiler.enqueuer.codegen.universe; | |
62 | |
63 Universe get resolutionUniverse => compiler.enqueuer.resolution.universe; | |
64 | |
65 ForgetElementTestCase( | |
66 String source, | |
67 {int closureCount: 0, | |
68 int metadataCount: 0, | |
69 int constantCount: 0, | |
70 int initialValueCount: 0, | |
71 int initialDartValueCount: null, | |
72 this.additionalClosureClassMaps: 0}) | |
73 : this.expectedClosureCount = closureCount, | |
74 this.expectedMetadataCount = metadataCount, | |
75 this.expectedConstantCount = constantCount, | |
76 this.expectedInitialValueCount = initialValueCount, | |
77 // Sometimes these numbers aren't the same. Appears to happen with | |
78 // non-const fields, because those aren't compile-time constants in the | |
79 // strict language specification sense. | |
80 this.expectedInitialDartValueCount = (initialDartValueCount == null) | |
81 ? initialValueCount : initialDartValueCount, | |
82 super(source); | |
83 | |
84 Future run() => compile().then((LibraryElement library) { | |
85 | |
86 // Check that the compiler has recorded the expected number of closures. | |
87 Expect.equals( | |
88 expectedClosureCount, closuresInLibrary(library).length, | |
89 'closure count'); | |
90 | |
91 // Check that the compiler has recorded the expected number of metadata | |
92 // annotations. | |
93 Expect.equals( | |
94 expectedMetadataCount, metadataInLibrary(library).length, | |
95 'metadata count'); | |
96 | |
97 // Check that the compiler has recorded the expected number of | |
98 // constants. Since metadata is also constants, those must also be counted. | |
99 Expect.equals( | |
100 expectedConstantCount + expectedMetadataCount, | |
101 constantsIn(library).length, | |
102 'constant count'); | |
103 | |
104 // Check that the compiler has recorded the expected number of initial | |
105 // values. | |
106 Expect.equals( | |
107 expectedInitialValueCount, | |
108 elementsWithJsInitialValuesIn(library).length, | |
109 'number of fields with initial values (JS)'); | |
110 Expect.equals( | |
111 expectedInitialDartValueCount, | |
112 elementsWithDartInitialValuesIn(library).length, | |
113 'number of fields with initial values (Dart)'); | |
114 | |
115 // Check that the compiler has recorded the expected number of closure | |
116 // class maps. There's always at least one, from main. Each top-level | |
117 // element also seems to induce one. | |
118 Expect.equals( | |
119 expectedClosureCount + additionalClosureClassMaps, | |
120 closureClassMapsIn(library).length - 1, | |
121 'closure class map count ${closureClassMapsIn(library)}'); | |
122 | |
123 | |
124 // Forget about all elements. | |
125 library.forEachLocalMember(compiler.forgetElement); | |
126 | |
127 // Check that all the closures were forgotten. | |
128 Expect.isTrue(closuresInLibrary(library).isEmpty, 'closures'); | |
129 | |
130 // Check that the metadata annotations were forgotten. | |
131 Expect.isTrue(metadataInLibrary(library).isEmpty, 'metadata'); | |
132 | |
133 // Check that the constants were forgotten. | |
134 Expect.isTrue(constantsIn(library).isEmpty, 'constants'); | |
135 | |
136 // Check that initial values were forgotten. | |
137 Expect.isTrue( | |
138 elementsWithJsInitialValuesIn(library).isEmpty, | |
139 'fields with initial values (JS)'); | |
140 Expect.isTrue( | |
141 elementsWithDartInitialValuesIn(library).isEmpty, | |
142 'fields with initial values (Dart)'); | |
143 | |
144 // Check that closure class maps were forgotten. | |
145 Expect.isTrue(closureClassMapsIn(library).isEmpty, 'closure class maps'); | |
146 | |
147 // Check that istantiated types and classes were forgotten. | |
148 Expect.isTrue( | |
149 resolutionTypesIn(library).isEmpty, 'resolution instantiatedTypes'); | |
150 Expect.isTrue( | |
151 resolutionClassesIn(library).isEmpty, 'resolution instantiatedClasses'); | |
152 Expect.isTrue( | |
153 codegenTypesIn(library).isEmpty, 'codegen instantiatedTypes'); | |
154 Expect.isTrue( | |
155 codegenClassesIn(library).isEmpty, 'codegen instantiatedClasses'); | |
156 | |
157 // Check that other members remembered by [Universe] were forgotten. | |
158 Expect.isTrue( | |
159 resolutionMembersIn(library).isEmpty, 'resolution misc members'); | |
160 Expect.isTrue( | |
161 codegenMembersIn(library).isEmpty, 'codegen misc members'); | |
162 | |
163 // Check that classes remembered by the enqueuer have been forgotten. | |
164 Expect.isTrue( | |
165 codegenSeenClassesIn(library).isEmpty, 'codegen seen classes'); | |
166 Expect.isTrue( | |
167 resolutionSeenClassesIn(library).isEmpty, 'resolution seen classes'); | |
168 }); | |
169 | |
170 Iterable closuresInLibrary(LibraryElement library) { | |
171 return compiler.enqueuer.resolution.universe.allClosures.where( | |
172 (LocalFunctionElement closure) => closure.library == library); | |
173 } | |
174 | |
175 Iterable metadataInLibrary(LibraryElement library) { | |
176 return backend.constants.metadataConstantMap.keys.where( | |
177 (MetadataAnnotation metadata) { | |
178 return metadata.annotatedElement.library == library; | |
179 }); | |
180 } | |
181 | |
182 Iterable<tree.Node> nodesIn(LibraryElement library) { | |
183 NodeCollector collector = new NodeCollector(); | |
184 library.forEachLocalMember((e) { | |
185 if (e is AstElement && e.hasNode) { | |
186 e.node.accept(collector); | |
187 } | |
188 | |
189 // Due to quirks of history, only parameter metadata is recorded in AST | |
190 // nodes, so they must be extracted from the elements. | |
191 for (MetadataAnnotation metadata in e.metadata) { | |
192 if (metadata is PartialMetadataAnnotation) { | |
193 if (metadata.cachedNode != null) { | |
194 metadata.cachedNode.accept(collector); | |
195 } | |
196 } | |
197 } | |
198 }); | |
199 | |
200 List<MetadataAnnotation> metadata = | |
201 (new MetadataCollector()..visit(library, null)).metadata; | |
202 return collector.nodes; | |
203 } | |
204 | |
205 Iterable constantsIn(LibraryElement library) { | |
206 return nodesIn(library) | |
207 .map((node) => backend.constants.nodeConstantMap[node]) | |
208 .where((constant) => constant != null); | |
209 } | |
210 | |
211 Iterable elementsWithJsInitialValuesIn(LibraryElement library) { | |
212 return backend.constants.initialVariableValues.keys.where( | |
213 (VariableElement element) => element.library == library); | |
214 } | |
215 | |
216 Iterable elementsWithDartInitialValuesIn(LibraryElement library) { | |
217 return dartConstants.initialVariableValues.keys.where( | |
218 (VariableElement element) => element.library == library); | |
219 } | |
220 | |
221 Iterable closureClassMapsIn(LibraryElement library) { | |
222 Map cache = compiler.closureToClassMapper.closureMappingCache; | |
223 return nodesIn(library).where((node) => cache[node] != null); | |
224 } | |
225 | |
226 Iterable codegenTypesIn(LibraryElement library) { | |
227 return codegenUniverse.instantiatedTypes.where( | |
228 (DartType type) => type.element.library == library); | |
229 } | |
230 | |
231 Iterable codegenClassesIn(LibraryElement library) { | |
232 return codegenUniverse.directlyInstantiatedClasses.where( | |
233 (ClassElement cls) => cls.library == library); | |
234 } | |
235 | |
236 Iterable codegenMembersIn(LibraryElement library) { | |
237 sameLibrary(e) => e.library == library; | |
238 return new Set() | |
239 ..addAll(codegenUniverse.closurizedMembers.where(sameLibrary)) | |
240 ..addAll(codegenUniverse.fieldSetters.where(sameLibrary)) | |
241 ..addAll(codegenUniverse.fieldGetters.where(sameLibrary)); | |
242 } | |
243 | |
244 Iterable resolutionTypesIn(LibraryElement library) { | |
245 return resolutionUniverse.instantiatedTypes.where( | |
246 (DartType type) => type.element.library == library); | |
247 } | |
248 | |
249 Iterable resolutionClassesIn(LibraryElement library) { | |
250 return resolutionUniverse.directlyInstantiatedClasses.where( | |
251 (ClassElement cls) => cls.library == library); | |
252 } | |
253 | |
254 Iterable resolutionMembersIn(LibraryElement library) { | |
255 sameLibrary(e) => e.library == library; | |
256 return new Set() | |
257 ..addAll(resolutionUniverse.closurizedMembers.where(sameLibrary)) | |
258 ..addAll(resolutionUniverse.fieldSetters.where(sameLibrary)) | |
259 ..addAll(resolutionUniverse.fieldGetters.where(sameLibrary)); | |
260 } | |
261 | |
262 Iterable codegenSeenClassesIn(LibraryElement library) { | |
263 return compiler.enqueuer.codegen.processedClasses.where( | |
264 (e) => e.library == library); | |
265 } | |
266 | |
267 Iterable resolutionSeenClassesIn(LibraryElement library) { | |
268 return compiler.enqueuer.resolution.processedClasses.where( | |
269 (e) => e.library == library); | |
270 } | |
271 } | |
272 | |
273 class NodeCollector extends tree.Visitor { | |
274 final List<tree.Node> nodes = <tree.Node>[]; | |
275 | |
276 void visitNode(tree.Node node) { | |
277 nodes.add(node); | |
278 node.visitChildren(this); | |
279 } | |
280 } | |
281 | |
282 class MetadataCollector extends ElementVisitor { | |
283 final List<MetadataAnnotation> metadata = <MetadataAnnotation>[]; | |
284 | |
285 void visitElement(Element e, _) { | |
286 metadata.addAll(e.metadata.toList()); | |
287 } | |
288 | |
289 void visitScopeContainerElement(ScopeContainerElement e, _) { | |
290 super.visitScopeContainerElement(e); | |
291 e.forEachLocalMember(this.visit); | |
292 } | |
293 | |
294 void visitFunctionElement(FunctionElement e, _) { | |
295 super.visitFunctionElement(e); | |
296 if (e.hasFunctionSignature) { | |
297 e.functionSignature.forEachParameter(this.visit); | |
298 } | |
299 } | |
300 } | |
301 | |
302 void main() { | |
303 runTests(tests); | |
304 } | |
305 | |
306 List<CompilerTestCase> get tests => <CompilerTestCase>[ | |
307 | |
308 // Edge case: empty body. | |
309 new ForgetElementTestCase( | |
310 'main() {}'), | |
311 | |
312 // Edge case: simple arrow function. | |
313 new ForgetElementTestCase( | |
314 'main() => null;'), | |
315 | |
316 // Test that a local closure is discarded correctly. | |
317 new ForgetElementTestCase( | |
318 'main() => (() => null)();', | |
319 closureCount: 1), | |
320 | |
321 // Test that nested closures are discarded correctly. | |
322 new ForgetElementTestCase( | |
323 'main() => (() => (() => null)())();', | |
324 closureCount: 2), | |
325 | |
326 // Test that nested closures are discarded correctly. | |
327 new ForgetElementTestCase( | |
328 'main() => (() => (() => (() => null)())())();', | |
329 closureCount: 3), | |
330 | |
331 // Test that metadata on top-level function is discarded correctly. | |
332 new ForgetElementTestCase( | |
333 '@Constant() main() => null; $CONSTANT_CLASS', | |
334 metadataCount: 1), | |
335 | |
336 // Test that metadata on top-level variable is discarded correctly. | |
337 new ForgetElementTestCase( | |
338 '@Constant() var x; main() => x; $CONSTANT_CLASS', | |
339 metadataCount: 1, | |
340 initialValueCount: 1, | |
341 initialDartValueCount: 0), | |
342 | |
343 // Test that metadata on parameter on a local function is discarded | |
344 // correctly. | |
345 new ForgetElementTestCase( | |
346 'main() => ((@Constant() x) => x)(null); $CONSTANT_CLASS', | |
347 closureCount: 1, | |
348 metadataCount: 1), | |
349 | |
350 // Test that a constant in a top-level method body is discarded | |
351 // correctly. | |
352 new ForgetElementTestCase( | |
353 'main() => const Constant(); $CONSTANT_CLASS', | |
354 constantCount: 1), | |
355 | |
356 // Test that a constant in a nested function body is discarded | |
357 // correctly. | |
358 new ForgetElementTestCase( | |
359 'main() => (() => const Constant())(); $CONSTANT_CLASS', | |
360 constantCount: 1, | |
361 closureCount: 1), | |
362 | |
363 // Test that a constant in a nested function body is discarded | |
364 // correctly. | |
365 new ForgetElementTestCase( | |
366 'main() => (() => (() => const Constant())())(); $CONSTANT_CLASS', | |
367 constantCount: 1, | |
368 closureCount: 2), | |
369 | |
370 // Test that a constant in a top-level variable initializer is | |
371 // discarded correctly. | |
372 new ForgetElementTestCase( | |
373 'main() => x; var x = const Constant(); $CONSTANT_CLASS', | |
374 constantCount: 1, | |
375 initialValueCount: 1, | |
376 initialDartValueCount: 0, | |
377 additionalClosureClassMaps: 1), | |
378 | |
379 // Test that a constant in a parameter initializer is discarded | |
380 // correctly. | |
381 new ForgetElementTestCase( | |
382 'main([x = const Constant()]) => x; $CONSTANT_CLASS', | |
383 constantCount: 1, | |
384 initialValueCount: 1), | |
385 | |
386 // Test that a constant in a parameter initializer is discarded | |
387 // correctly (nested function). | |
388 new ForgetElementTestCase( | |
389 'main() => (([x = const Constant()]) => x)(); $CONSTANT_CLASS', | |
390 closureCount: 1, | |
391 constantCount: 1, | |
392 initialValueCount: 1), | |
393 | |
394 // Test that a constant in a parameter initializer is discarded | |
395 // correctly (deeply nested function). | |
396 new ForgetElementTestCase( | |
397 'main() => (() => (([x = const Constant()]) => x)())();' | |
398 ' $CONSTANT_CLASS', | |
399 closureCount: 2, | |
400 constantCount: 1, | |
401 initialValueCount: 1), | |
402 | |
403 // TODO(ahe): Add test for super sends [backend.aliasedSuperMembers]. | |
404 ]..addAll(assertUnimplementedLocalMetadata()); | |
OLD | NEW |