Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(178)

Side by Side Diff: dart/sdk/lib/_internal/compiler/implementation/mirrors_used.dart

Issue 21110003: Implement MirrorUsed.targets for libraries (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Merged with r25609 and added test. Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, 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 library dart2js.mirrors_used;
6
7 import 'dart2jslib.dart' show
8 Compiler,
9 CompilerTask,
10 Constant,
11 ConstructedConstant,
12 ListConstant,
13 MessageKind,
14 SourceString,
15 StringConstant,
16 TypeConstant;
17
18 import 'elements/elements.dart' show
19 Element,
20 LibraryElement,
21 MetadataAnnotation,
22 VariableElement;
23
24 import 'util/util.dart' show
25 Link;
26
27 import 'dart_types.dart' show
28 DartType;
29
30 import 'tree/tree.dart' show
31 Import,
32 LibraryTag;
33
34 /**
35 * Compiler task that analyzes MirrorsUsed annotations.
36 *
37 * When importing 'dart:mirrors', it is possible to annotate the import with
38 * MirrorsUsed annotation. This is a way to declare what elements will be
39 * reflected on at runtime. Such elements, even they would normally be
40 * discarded by the implicit tree-shaking algorithm must be preserved in the
41 * final output.
42 *
43 * Since some libraries cannot tell exactly what they will be reflecting on, it
44 * is possible for one library to specify a MirrorsUsed annotation that applies
45 * to another library. For example:
46 *
47 * Mirror utility library that cannot tell what it is reflecting on:
48 * library mirror_utils;
49 * import 'dart:mirrors';
50 * ...
51 *
52 * The main app which knows how it use the mirror utility library:
53 * library main_app;
54 * @MirrorsUsed(override='mirror_utils')
55 * import 'dart:mirrors';
56 * import 'mirror_utils.dart';
57 * ...
58 *
59 * In this case, we say that @MirrorsUsed in main_app overrides @MirrorsUsed in
60 * mirror_utils.
61 *
62 * It is possible to override all libraries using override='*'. If multiple
63 * catch-all overrides like this, they are merged together.
64 *
65 * It is possible for library "a" to declare that it overrides library "b", and
66 * vice versa. In this case, both annotations will be discarded and the
67 * compiler will emit a hint (that is, a warning that is not really a warning).
Johnni Winther 2013/07/30 11:15:09 'not really a warning' -> 'not defined by the lang
ahe 2013/07/30 12:05:45 Done.
68 *
69 * After applying all the overrides, we can iterate over libraries that import
70 * 'dart:mirrors'. If a library does not have an associated MirrorsUsed
71 * annotation, then we have to discard all MirrorsUsed annotations and assume
72 * everything can be reflected on.
73 *
74 * On the other hand, if all libraries importing dart:mirrors have a
75 * MirrorsUsed annotation, these annotations are merged.
76 *
77 * MERGING MIRRORSUSED
78 *
79 * TBD.
80 */
81 class MirrorUsageAnalyzerTask extends CompilerTask {
82 Set<LibraryElement> librariesWithUsage;
83
84 MirrorUsageAnalyzerTask(Compiler compiler)
85 : super(compiler);
86
87 void analyzeUsage(LibraryElement mainApp) {
88 if (compiler.mirrorsLibrary == null) return;
89 MirrorUsageAnalyzer analyzer = new MirrorUsageAnalyzer(compiler, this);
90 measure(analyzer.run);
91 List<String> symbols = analyzer.mergedMirrorUsage.symbols;
92 List<Element> targets = analyzer.mergedMirrorUsage.targets;
93 List<Element> metaTargets = analyzer.mergedMirrorUsage.metaTargets;
94 compiler.backend.registerMirrorUsage(
95 symbols == null ? null : new Set<String>.from(symbols),
96 targets == null ? null : new Set<Element>.from(targets),
97 metaTargets == null ? null : new Set<Element>.from(metaTargets));
98 librariesWithUsage = analyzer.librariesWithUsage;
99 }
100
101 bool hasMirrorUsage(Element element) {
102 return librariesWithUsage != null
103 && librariesWithUsage.contains(element.getLibrary());
104 }
105 }
106
107 class MirrorUsageAnalyzer {
108 final Compiler compiler;
109 final MirrorUsageAnalyzerTask task;
110 final List<LibraryElement> wildcard;
111 final Set<LibraryElement> librariesWithUsage;
112 final Set<LibraryElement> librariesWithoutUsage;
113 MirrorUsage mergedMirrorUsage;
114
115 MirrorUsageAnalyzer(Compiler compiler, this.task)
116 : compiler = compiler,
117 wildcard = compiler.libraries.values.toList(),
118 librariesWithUsage = new Set<LibraryElement>(),
119 librariesWithoutUsage = new Set<LibraryElement>();
120
121 void run() {
122 Map<LibraryElement, List<MirrorUsage>> usageMap =
123 collectMirrorsUsedAnnotation();
124 // TODO(ahe): Consider if the cycle check is necessary, perhaps override
125 // doesn't replace but merge with existing annotations.
126 checkCyclicOverrides(usageMap);
127 propagateOverrides(usageMap);
128 librariesWithoutUsage.removeAll(usageMap.keys);
129 if (librariesWithoutUsage.isEmpty) {
130 mergedMirrorUsage = mergeUsages(usageMap);
131 } else {
132 mergedMirrorUsage = new MirrorUsage(null, wildcard, null, null);
133 }
134 }
135
136 Map<LibraryElement, List<MirrorUsage>> collectMirrorsUsedAnnotation() {
137 Map<LibraryElement, List<MirrorUsage>> result =
138 new Map<LibraryElement, List<MirrorUsage>>();
139 for (LibraryElement library in compiler.libraries.values) {
140 if (library.isInternalLibrary) continue;
141 librariesWithoutUsage.add(library);
142 for (LibraryTag tag in library.tags) {
143 Import importTag = tag.asImport();
144 if (importTag == null) continue;
145 compiler.withCurrentElement(library, () {
146 List<MirrorUsage> usages =
147 mirrorsUsedOnLibraryTag(library, importTag);
148 if (usages != null) {
149 List<MirrorUsage> existing = result[library];
150 if (existing != null) {
151 existing.addAll(usages);
152 } else {
153 result[library] = usages;
154 }
155 }
156 });
157 }
158 }
159 return result;
160 }
161
162 void checkCyclicOverrides(Map<LibraryElement, List<MirrorUsage>> usageMap) {
163 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) {
164 for (MirrorUsage usage in usages) {
165 List<Element> override = usage.override;
166 if (override == null || override == wildcard) continue;
167 for (Element overridden in override) {
168 List<MirrorUsage> overriddenUsages = usageMap[overridden];
169 if (overriddenUsages == null) continue;
170 for (MirrorUsage overriddenUsage in overriddenUsages) {
171 if (overriddenUsage.override.contains(library)) {
Johnni Winther 2013/07/30 11:15:09 I don't think this will find cycles of length > 2.
ahe 2013/07/30 12:05:45 It seems like we need something else. I have remov
172 // TODO(ahe): Test this.
173 // TODO(ahe): Discard the annotations.
174 compiler.reportHint(
175 library,
176 MessageKind.GENERIC, {'text': 'Cyclic override.'});
177 compiler.reportHint(
178 overridden,
179 MessageKind.GENERIC, {'text': 'Cyclic override.'});
180 }
181 }
182 }
183 }
184 });
185 }
186
187 void propagateOverrides(Map<LibraryElement, List<MirrorUsage>> usageMap) {
188 Map<LibraryElement, List<MirrorUsage>> propagatedOverrides =
189 new Map<LibraryElement, List<MirrorUsage>>();
190 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) {
Johnni Winther 2013/07/30 11:15:09 What if we have library a; @MirrorsUsed(override=
ahe 2013/07/30 12:05:45 I think we're settling on a simpler solution.
191 for (MirrorUsage usage in usages) {
192 List<Element> override = usage.override;
193 if (override == null) continue;
194 if (override == wildcard) {
195 for (LibraryElement overridden in wildcard) {
196 if (overridden != library) {
197 List<MirrorUsage> overriddenUsages = propagatedOverrides
198 .putIfAbsent(overridden, () => <MirrorUsage>[]);
199 overriddenUsages.add(usage);
200 }
201 }
202 } else {
203 for (Element overridden in override) {
204 List<MirrorUsage> overriddenUsages = propagatedOverrides
205 .putIfAbsent(overridden, () => <MirrorUsage>[]);
206 overriddenUsages.add(usage);
207 }
208 }
209 }
210 });
211 propagatedOverrides.forEach((LibraryElement overridden,
212 List<MirrorUsage> overriddenUsages) {
213 List<MirrorUsage> usages =
214 usageMap.putIfAbsent(overridden, () => <MirrorUsage>[]);
215 usages.addAll(overriddenUsages);
216 });
217 }
218
219 List<MirrorUsage> mirrorsUsedOnLibraryTag(LibraryElement library,
220 Import tag) {
221 LibraryElement importedLibrary = library.getLibraryFromTag(tag);
222 if (importedLibrary != compiler.mirrorsLibrary) {
223 return null;
224 }
225 List<MirrorUsage> result = <MirrorUsage>[];
226 for (MetadataAnnotation metadata in tag.metadata) {
227 metadata.ensureResolved(compiler);
228 Element element = metadata.value.computeType(compiler).element;
229 if (element == compiler.mirrorsUsedClass) {
230 try {
231 MirrorUsage usage =
232 new MirrorUsageBuilder(this, library).build(metadata.value);
233 result.add(usage);
234 } on BadMirrorsUsedAnnotation catch (e) {
235 compiler.reportError(
236 metadata, MessageKind.GENERIC, {'text': e.message});
237 }
238 }
239 }
240 return result;
241 }
242
243 MirrorUsage mergeUsages(Map<LibraryElement, List<MirrorUsage>> usageMap) {
244 Set<MirrorUsage> usagesToMerge = new Set<MirrorUsage>();
245 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) {
246 librariesWithUsage.add(library);
247 usagesToMerge.addAll(usages);
248 });
249 if (usagesToMerge.isEmpty) {
250 return new MirrorUsage(null, wildcard, null, null);
251 } else {
252 MirrorUsage result = new MirrorUsage(null, null, null, null);
253 for (MirrorUsage usage in usagesToMerge) {
254 result = merge(result, usage);
255 }
256 return result;
257 }
258 }
259
260 MirrorUsage merge(MirrorUsage a, MirrorUsage b) {
261 if (a.symbols == null && a.targets == null && a.metaTargets == null) {
262 return b;
263 } else if (b.symbols == null && b.targets == null && b.metaTargets == null) {
Johnni Winther 2013/07/30 11:15:09 Long line.
ahe 2013/07/30 12:05:45 Done.
264 return a;
265 }
266 // TODO(ahe): Test the following cases.
267 List<String> symbols = a.symbols;
268 if (symbols == null) {
269 symbols = b.symbols;
270 } else if (b.symbols != null) {
271 symbols.addAll(b.symbols);
272 }
273 List<Element> targets = a.targets;
274 if (targets == null) {
275 targets = b.targets;
276 } else if (targets != wildcard && b.targets != null) {
277 targets.addAll(b.targets);
278 }
279 List<Element> metaTargets = a.metaTargets;
280 if (metaTargets == null) {
281 metaTargets = b.metaTargets;
282 } else if (metaTargets != wildcard && b.metaTargets != null) {
283 metaTargets.addAll(b.metaTargets);
284 }
285 return new MirrorUsage(symbols, targets, metaTargets, null);
286 }
287 }
288
289 class MirrorUsage {
290 final List<String> symbols;
291 final List<Element> targets;
292 final List<Element> metaTargets;
293 final List<Element> override;
294
295 MirrorUsage(this.symbols, this.targets, this.metaTargets, this.override);
296
297 String toString() {
298 return
299 'MirrorUsage('
300 'symbols = $symbols, '
301 'targets = $targets, '
302 'metaTargets = $metaTargets, '
303 'override = $override'
304 ')';
305
306 }
307 }
308
309 class MirrorUsageBuilder {
310 MirrorUsageAnalyzer analyzer;
311 LibraryElement enclosingLibrary;
312
313 MirrorUsageBuilder(this.analyzer, this.enclosingLibrary);
314
315 Compiler get compiler => analyzer.compiler;
316
317 MirrorUsage build(ConstructedConstant constant) {
318 Map<Element, Constant> fields = constant.fieldElements;
319 VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember(
320 const SourceString('symbols'));
321 VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember(
322 const SourceString('targets'));
323 VariableElement metaTargetsField =
324 compiler.mirrorsUsedClass.lookupLocalMember(
325 const SourceString('metaTargets'));
326 VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember(
327 const SourceString('override'));
328 List<String> symbols =
329 convertToListOfStrings(
330 convertConstantToUsageList(fields[symbolsField]));
331 List<Element> targets =
332 resolveUsageList(convertConstantToUsageList(fields[targetsField]));
333
334 List<Element> metaTargets =
335 resolveUsageList(convertConstantToUsageList(fields[metaTargetsField]));
336 List<Element> override =
337 resolveUsageList(convertConstantToUsageList(fields[overrideField]));
338 return new MirrorUsage(symbols, targets, metaTargets, override);
339 }
340
341 List convertConstantToUsageList(Constant constant) {
342 if (constant.isNull()) {
343 return null;
344 } else if (constant.isList()) {
345 ListConstant list = constant;
346 List result = [];
347 for (Constant entry in list.entries) {
348 if (entry.isString()) {
349 StringConstant string = entry;
350 result.add(string.value.slowToString());
351 } else if (entry.isType()) {
352 TypeConstant type = entry;
353 result.add(type.representedType);
354 } else {
355 throw new BadMirrorsUsedAnnotation(
356 'Expected a string or type, but got "$entry".');
357 }
358 }
359 return result;
360 } else if (constant.isType()) {
361 TypeConstant type = constant;
362 return [type.representedType];
363 } else if (constant.isString()) {
364 StringConstant string = constant;
365 return
366 string.value.slowToString().split(',').map((e) => e.trim()).toList();
367 } else {
368 throw new BadMirrorsUsedAnnotation(
369 'Expected a string or a list of string, but got "$constant".');
370 }
371 }
372
373 List<String> convertToListOfStrings(List list) {
374 if (list == null) return null;
375 List<String> result = new List<String>(list.length);
376 int count = 0;
377 for (var entry in list) {
378 if (entry is! String) {
379 throw new BadMirrorsUsedAnnotation(
380 'Expected a string, but got "$entry"');
381 }
382 result[count++] = entry;
383 }
384 return result;
385 }
386
387 List<Element> resolveUsageList(List list) {
388 if (list == null) return null;
389 if (list.length == 1 && list[0] == '*') {
390 return analyzer.wildcard;
391 }
392 List<Element> result = <Element>[];
393 for (var entry in list) {
394 if (entry is DartType) {
395 DartType type = entry;
396 result.add(type.element);
397 } else {
398 String string = entry;
399 for (LibraryElement l in compiler.libraries.values) {
400 if (l.hasLibraryName()) {
401 String libraryName = l.getLibraryOrScriptName();
402 if (string == libraryName || string.startsWith('$libraryName.')) {
Johnni Winther 2013/07/30 11:15:09 Add a TODO to ensure resolution to the longest mat
ahe 2013/07/30 12:05:45 Actually, I'm already rewriting this in another CL
403 result.add(l);
404 break;
405 }
406 }
407 }
408 }
409 }
410 return result;
411 }
412 }
413
414 class BadMirrorsUsedAnnotation {
415 final String message;
416 BadMirrorsUsedAnnotation(this.message);
417 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698