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

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

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 'constants/expressions.dart';
8 import 'constants/values.dart' show
9 ConstantValue,
10 ConstructedConstantValue,
11 ListConstantValue,
12 StringConstantValue,
13 TypeConstantValue;
14
15 import 'dart_types.dart' show
16 DartType,
17 InterfaceType,
18 TypeKind;
19
20 import 'dart2jslib.dart' show
21 Compiler,
22 CompilerTask,
23 ConstantCompiler,
24 MessageKind,
25 TreeElements,
26 invariant;
27
28 import 'elements/elements.dart' show
29 ClassElement,
30 Element,
31 LibraryElement,
32 MetadataAnnotation,
33 ScopeContainerElement,
34 VariableElement;
35
36 import 'tree/tree.dart' show
37 Import,
38 LibraryTag,
39 NamedArgument,
40 NewExpression,
41 Node;
42
43 import 'util/util.dart' show
44 Link,
45 Spannable;
46
47 /**
48 * Compiler task that analyzes MirrorsUsed annotations.
49 *
50 * When importing 'dart:mirrors', it is possible to annotate the import with
51 * MirrorsUsed annotation. This is a way to declare what elements will be
52 * reflected on at runtime. Such elements, even they would normally be
53 * discarded by the implicit tree-shaking algorithm must be preserved in the
54 * final output.
55 *
56 * Since some libraries cannot tell exactly what they will be reflecting on, it
57 * is possible for one library to specify a MirrorsUsed annotation that applies
58 * to another library. For example:
59 *
60 * Mirror utility library that cannot tell what it is reflecting on:
61 * library mirror_utils;
62 * import 'dart:mirrors';
63 * ...
64 *
65 * The main app which knows how it use the mirror utility library:
66 * library main_app;
67 * @MirrorsUsed(override='mirror_utils')
68 * import 'dart:mirrors';
69 * import 'mirror_utils.dart';
70 * ...
71 *
72 * In this case, we say that @MirrorsUsed in main_app overrides @MirrorsUsed in
73 * mirror_utils.
74 *
75 * It is possible to override all libraries using override='*'. If multiple
76 * catch-all overrides like this, they are merged together.
77 *
78 * It is possible for library "a" to declare that it overrides library "b", and
79 * vice versa. In this case, both annotations will be discarded and the
80 * compiler will emit a hint (that is, a warning that is not specified by the
81 * language specification).
82 *
83 * After applying all the overrides, we can iterate over libraries that import
84 * 'dart:mirrors'. If a library does not have an associated MirrorsUsed
85 * annotation, then we have to discard all MirrorsUsed annotations and assume
86 * everything can be reflected on.
87 *
88 * On the other hand, if all libraries importing dart:mirrors have a
89 * MirrorsUsed annotation, these annotations are merged.
90 *
91 * MERGING MIRRORSUSED
92 *
93 * TBD.
94 */
95 class MirrorUsageAnalyzerTask extends CompilerTask {
96 Set<LibraryElement> librariesWithUsage;
97 MirrorUsageAnalyzer analyzer;
98
99 MirrorUsageAnalyzerTask(Compiler compiler)
100 : super(compiler) {
101 analyzer = new MirrorUsageAnalyzer(compiler, this);
102 }
103
104 /// Collect @MirrorsUsed annotations in all libraries. Called by the
105 /// compiler after all libraries are loaded, but before resolution.
106 void analyzeUsage(LibraryElement mainApp) {
107 if (mainApp == null || compiler.mirrorsLibrary == null) return;
108 measure(analyzer.run);
109 List<String> symbols = analyzer.mergedMirrorUsage.symbols;
110 List<Element> targets = analyzer.mergedMirrorUsage.targets;
111 List<Element> metaTargets = analyzer.mergedMirrorUsage.metaTargets;
112 compiler.backend.registerMirrorUsage(
113 symbols == null ? null : new Set<String>.from(symbols),
114 targets == null ? null : new Set<Element>.from(targets),
115 metaTargets == null ? null : new Set<Element>.from(metaTargets));
116 librariesWithUsage = analyzer.librariesWithUsage;
117 }
118
119 /// Is there a @MirrorsUsed annotation in the library of [element]? Used by
120 /// the resolver to suppress hints about using new Symbol or
121 /// MirrorSystem.getName.
122 bool hasMirrorUsage(Element element) {
123 LibraryElement library = element.library;
124 // Internal libraries always have implicit mirror usage.
125 return library.isInternalLibrary
126 || (librariesWithUsage != null
127 && librariesWithUsage.contains(library));
128 }
129
130 /// Call-back from the resolver to analyze MirorsUsed annotations. The result
131 /// is stored in [analyzer] and later used to compute
132 /// [:analyzer.mergedMirrorUsage:].
133 void validate(NewExpression node, TreeElements mapping) {
134 for (Node argument in node.send.arguments) {
135 NamedArgument named = argument.asNamedArgument();
136 if (named == null) continue;
137 ConstantCompiler constantCompiler = compiler.resolver.constantCompiler;
138 ConstantValue value =
139 constantCompiler.compileNode(named.expression, mapping).value;
140
141 MirrorUsageBuilder builder =
142 new MirrorUsageBuilder(
143 analyzer, mapping.analyzedElement.library, named.expression,
144 value, mapping);
145
146 if (named.name.source == 'symbols') {
147 analyzer.cachedStrings[value] =
148 builder.convertConstantToUsageList(value, onlyStrings: true);
149 } else if (named.name.source == 'targets') {
150 analyzer.cachedElements[value] =
151 builder.resolveUsageList(builder.convertConstantToUsageList(value));
152 } else if (named.name.source == 'metaTargets') {
153 analyzer.cachedElements[value] =
154 builder.resolveUsageList(builder.convertConstantToUsageList(value));
155 } else if (named.name.source == 'override') {
156 analyzer.cachedElements[value] =
157 builder.resolveUsageList(builder.convertConstantToUsageList(value));
158 }
159 }
160 }
161 }
162
163 class MirrorUsageAnalyzer {
164 final Compiler compiler;
165 final MirrorUsageAnalyzerTask task;
166 List<LibraryElement> wildcard;
167 final Set<LibraryElement> librariesWithUsage;
168 final Map<ConstantValue, List<String>> cachedStrings;
169 final Map<ConstantValue, List<Element>> cachedElements;
170 MirrorUsage mergedMirrorUsage;
171
172 MirrorUsageAnalyzer(Compiler compiler, this.task)
173 : compiler = compiler,
174 librariesWithUsage = new Set<LibraryElement>(),
175 cachedStrings = new Map<ConstantValue, List<String>>(),
176 cachedElements = new Map<ConstantValue, List<Element>>();
177
178 /// Collect and merge all @MirrorsUsed annotations. As a side-effect, also
179 /// compute which libraries have the annotation (which is used by
180 /// [MirrorUsageAnalyzerTask.hasMirrorUsage]).
181 void run() {
182 wildcard = compiler.libraryLoader.libraries.toList();
183 Map<LibraryElement, List<MirrorUsage>> usageMap =
184 collectMirrorsUsedAnnotation();
185 propagateOverrides(usageMap);
186 Set<LibraryElement> librariesWithoutUsage = new Set<LibraryElement>();
187 usageMap.forEach((LibraryElement library, List<MirrorUsage> usage) {
188 if (usage.isEmpty) librariesWithoutUsage.add(library);
189 });
190 if (librariesWithoutUsage.isEmpty) {
191 mergedMirrorUsage = mergeUsages(usageMap);
192 } else {
193 mergedMirrorUsage = new MirrorUsage(null, null, null, null);
194 }
195 }
196
197 /// Collect all @MirrorsUsed from all libraries and represent them as
198 /// [MirrorUsage].
199 Map<LibraryElement, List<MirrorUsage>> collectMirrorsUsedAnnotation() {
200 Map<LibraryElement, List<MirrorUsage>> result =
201 new Map<LibraryElement, List<MirrorUsage>>();
202 for (LibraryElement library in compiler.libraryLoader.libraries) {
203 if (library.isInternalLibrary) continue;
204 for (LibraryTag tag in library.tags) {
205 Import importTag = tag.asImport();
206 if (importTag == null) continue;
207 compiler.withCurrentElement(library, () {
208 List<MirrorUsage> usages =
209 mirrorsUsedOnLibraryTag(library, importTag);
210 if (usages != null) {
211 List<MirrorUsage> existing = result[library];
212 if (existing != null) {
213 existing.addAll(usages);
214 } else {
215 result[library] = usages;
216 }
217 }
218 });
219 }
220 }
221 return result;
222 }
223
224 /// Apply [MirrorUsage] with 'override' to libraries they override.
225 void propagateOverrides(Map<LibraryElement, List<MirrorUsage>> usageMap) {
226 Map<LibraryElement, List<MirrorUsage>> propagatedOverrides =
227 new Map<LibraryElement, List<MirrorUsage>>();
228 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) {
229 for (MirrorUsage usage in usages) {
230 List<Element> override = usage.override;
231 if (override == null) continue;
232 if (override == wildcard) {
233 for (LibraryElement overridden in wildcard) {
234 if (overridden != library) {
235 List<MirrorUsage> overriddenUsages = propagatedOverrides
236 .putIfAbsent(overridden, () => <MirrorUsage>[]);
237 overriddenUsages.add(usage);
238 }
239 }
240 } else {
241 for (Element overridden in override) {
242 List<MirrorUsage> overriddenUsages = propagatedOverrides
243 .putIfAbsent(overridden, () => <MirrorUsage>[]);
244 overriddenUsages.add(usage);
245 }
246 }
247 }
248 });
249 propagatedOverrides.forEach((LibraryElement overridden,
250 List<MirrorUsage> overriddenUsages) {
251 List<MirrorUsage> usages =
252 usageMap.putIfAbsent(overridden, () => <MirrorUsage>[]);
253 usages.addAll(overriddenUsages);
254 });
255 }
256
257 /// Find @MirrorsUsed annotations on the given import [tag] in [library]. The
258 /// annotations are represented as [MirrorUsage].
259 List<MirrorUsage> mirrorsUsedOnLibraryTag(LibraryElement library,
260 Import tag) {
261 LibraryElement importedLibrary = library.getLibraryFromTag(tag);
262 if (importedLibrary != compiler.mirrorsLibrary) {
263 return null;
264 }
265 List<MirrorUsage> result = <MirrorUsage>[];
266 for (MetadataAnnotation metadata in tag.metadata) {
267 metadata.ensureResolved(compiler);
268 Element element = metadata.constant.value.computeType(compiler).element;
269 if (element == compiler.mirrorsUsedClass) {
270 result.add(buildUsage(metadata.constant.value));
271 }
272 }
273 return result;
274 }
275
276 /// Merge all [MirrorUsage] instances accross all libraries.
277 MirrorUsage mergeUsages(Map<LibraryElement, List<MirrorUsage>> usageMap) {
278 Set<MirrorUsage> usagesToMerge = new Set<MirrorUsage>();
279 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) {
280 librariesWithUsage.add(library);
281 usagesToMerge.addAll(usages);
282 });
283 if (usagesToMerge.isEmpty) {
284 return new MirrorUsage(null, wildcard, null, null);
285 } else {
286 MirrorUsage result = new MirrorUsage(null, null, null, null);
287 for (MirrorUsage usage in usagesToMerge) {
288 result = merge(result, usage);
289 }
290 return result;
291 }
292 }
293
294 /// Merge [a] with [b]. The resulting [MirrorUsage] simply has the symbols,
295 /// targets, and metaTargets of [a] and [b] concatenated. 'override' is
296 /// ignored.
297 MirrorUsage merge(MirrorUsage a, MirrorUsage b) {
298 // TOOO(ahe): Should be an instance method on MirrorUsage.
299 if (a.symbols == null && a.targets == null && a.metaTargets == null) {
300 return b;
301 } else if (
302 b.symbols == null && b.targets == null && b.metaTargets == null) {
303 return a;
304 }
305 // TODO(ahe): Test the following cases.
306 List<String> symbols = a.symbols;
307 if (symbols == null) {
308 symbols = b.symbols;
309 } else if (b.symbols != null) {
310 symbols.addAll(b.symbols);
311 }
312 List<Element> targets = a.targets;
313 if (targets == null) {
314 targets = b.targets;
315 } else if (targets != wildcard && b.targets != null) {
316 targets.addAll(b.targets);
317 }
318 List<Element> metaTargets = a.metaTargets;
319 if (metaTargets == null) {
320 metaTargets = b.metaTargets;
321 } else if (metaTargets != wildcard && b.metaTargets != null) {
322 metaTargets.addAll(b.metaTargets);
323 }
324 return new MirrorUsage(symbols, targets, metaTargets, null);
325 }
326
327 /// Convert a [constant] to an instance of [MirrorUsage] using information
328 /// that was resolved during [MirrorUsageAnalyzerTask.validate].
329 MirrorUsage buildUsage(ConstructedConstantValue constant) {
330 Map<Element, ConstantValue> fields = constant.fieldElements;
331 VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember(
332 'symbols');
333 VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember(
334 'targets');
335 VariableElement metaTargetsField =
336 compiler.mirrorsUsedClass.lookupLocalMember(
337 'metaTargets');
338 VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember(
339 'override');
340
341 return new MirrorUsage(
342 cachedStrings[fields[symbolsField]],
343 cachedElements[fields[targetsField]],
344 cachedElements[fields[metaTargetsField]],
345 cachedElements[fields[overrideField]]);
346 }
347 }
348
349 /// Used to represent a resolved MirrorsUsed constant.
350 class MirrorUsage {
351 final List<String> symbols;
352 final List<Element> targets;
353 final List<Element> metaTargets;
354 final List<Element> override;
355
356 MirrorUsage(this.symbols, this.targets, this.metaTargets, this.override);
357
358 String toString() {
359 return
360 'MirrorUsage('
361 'symbols = $symbols, '
362 'targets = $targets, '
363 'metaTargets = $metaTargets, '
364 'override = $override'
365 ')';
366
367 }
368 }
369
370 class MirrorUsageBuilder {
371 final MirrorUsageAnalyzer analyzer;
372 final LibraryElement enclosingLibrary;
373 final Spannable spannable;
374 final ConstantValue constant;
375 final TreeElements elements;
376
377 MirrorUsageBuilder(
378 this.analyzer,
379 this.enclosingLibrary,
380 this.spannable,
381 this.constant,
382 this.elements);
383
384 Compiler get compiler => analyzer.compiler;
385
386 /// Convert a constant to a list of [String] and [Type] values. If the
387 /// constant is a single [String], it is assumed to be a comma-separated list
388 /// of qualified names. If the constant is a [Type] t, the result is [:[t]:].
389 /// Otherwise, the constant is assumed to represent a list of strings (each a
390 /// qualified name) and types, and such a list is constructed. If
391 /// [onlyStrings] is true, the returned list is a [:List<String>:] and any
392 /// [Type] values are treated as an error (meaning that the value is ignored
393 /// and a hint is emitted).
394 List convertConstantToUsageList(
395 ConstantValue constant, { bool onlyStrings: false }) {
396 if (constant.isNull) {
397 return null;
398 } else if (constant.isList) {
399 ListConstantValue list = constant;
400 List result = onlyStrings ? <String> [] : [];
401 for (ConstantValue entry in list.entries) {
402 if (entry.isString) {
403 StringConstantValue string = entry;
404 result.add(string.primitiveValue.slowToString());
405 } else if (!onlyStrings && entry.isType) {
406 TypeConstantValue type = entry;
407 result.add(type.representedType);
408 } else {
409 Spannable node = positionOf(entry);
410 MessageKind kind = onlyStrings
411 ? MessageKind.MIRRORS_EXPECTED_STRING
412 : MessageKind.MIRRORS_EXPECTED_STRING_OR_TYPE;
413 compiler.reportHint(
414 node,
415 kind, {'name': node, 'type': apiTypeOf(entry)});
416 }
417 }
418 return result;
419 } else if (!onlyStrings && constant.isType) {
420 TypeConstantValue type = constant;
421 return [type.representedType];
422 } else if (constant.isString) {
423 StringConstantValue string = constant;
424 var iterable =
425 string.primitiveValue.slowToString().split(',').map((e) => e.trim());
426 return onlyStrings ? new List<String>.from(iterable) : iterable.toList();
427 } else {
428 Spannable node = positionOf(constant);
429 MessageKind kind = onlyStrings
430 ? MessageKind.MIRRORS_EXPECTED_STRING_OR_LIST
431 : MessageKind.MIRRORS_EXPECTED_STRING_TYPE_OR_LIST;
432 compiler.reportHint(
433 node,
434 kind, {'name': node, 'type': apiTypeOf(constant)});
435 return null;
436 }
437 }
438
439 /// Find the first non-implementation interface of constant.
440 DartType apiTypeOf(ConstantValue constant) {
441 DartType type = constant.computeType(compiler);
442 LibraryElement library = type.element.library;
443 if (type.isInterfaceType && library.isInternalLibrary) {
444 InterfaceType interface = type;
445 ClassElement cls = type.element;
446 cls.ensureResolved(compiler);
447 for (DartType supertype in cls.allSupertypes) {
448 if (supertype.isInterfaceType
449 && !supertype.element.library.isInternalLibrary) {
450 return interface.asInstanceOf(supertype.element);
451 }
452 }
453 }
454 return type;
455 }
456
457 /// Convert a list of strings and types to a list of elements. Types are
458 /// converted to their corresponding element, and strings are resolved as
459 /// follows:
460 ///
461 /// First find the longest library name that is a prefix of the string, if
462 /// there are none, resolve using [resolveExpression]. Otherwise, resolve the
463 /// rest of the string using [resolveLocalExpression].
464 List<Element> resolveUsageList(List list) {
465 if (list == null) return null;
466 if (list.length == 1 && list[0] == '*') {
467 return analyzer.wildcard;
468 }
469 List<Element> result = <Element>[];
470 for (var entry in list) {
471 if (entry is DartType) {
472 DartType type = entry;
473 result.add(type.element);
474 } else {
475 String string = entry;
476 LibraryElement libraryCandiate;
477 String libraryNameCandiate;
478 for (LibraryElement l in compiler.libraryLoader.libraries) {
479 if (l.hasLibraryName()) {
480 String libraryName = l.getLibraryOrScriptName();
481 if (string == libraryName) {
482 // Found an exact match.
483 libraryCandiate = l;
484 libraryNameCandiate = libraryName;
485 break;
486 } else if (string.startsWith('$libraryName.')) {
487 if (libraryNameCandiate == null
488 || libraryNameCandiate.length < libraryName.length) {
489 // Found a better candiate
490 libraryCandiate = l;
491 libraryNameCandiate = libraryName;
492 }
493 }
494 }
495 }
496 Element e;
497 if (libraryNameCandiate == string) {
498 e = libraryCandiate;
499 } else if (libraryNameCandiate != null) {
500 e = resolveLocalExpression(
501 libraryCandiate,
502 string.substring(libraryNameCandiate.length + 1).split('.'));
503 } else {
504 e = resolveExpression(string);
505 }
506 if (e != null) result.add(e);
507 }
508 }
509 return result;
510 }
511
512 /// Resolve [expression] in [enclosingLibrary]'s import scope.
513 Element resolveExpression(String expression) {
514 List<String> identifiers = expression.split('.');
515 Element element = enclosingLibrary.find(identifiers[0]);
516 if (element == null) {
517 compiler.reportHint(
518 spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_CURRENT_LIBRARY,
519 {'name': expression});
520 return null;
521 } else {
522 if (identifiers.length == 1) return element;
523 return resolveLocalExpression(element, identifiers.sublist(1));
524 }
525 }
526
527 /// Resolve [identifiers] in [element]'s local members.
528 Element resolveLocalExpression(Element element, List<String> identifiers) {
529 Element current = element;
530 for (String identifier in identifiers) {
531 Element e = findLocalMemberIn(current, identifier);
532 if (e == null) {
533 if (current.isLibrary) {
534 LibraryElement library = current;
535 compiler.reportHint(
536 spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_LIBRARY,
537 {'name': identifiers[0],
538 'library': library.getLibraryOrScriptName()});
539 } else {
540 compiler.reportHint(
541 spannable, MessageKind.MIRRORS_CANNOT_FIND_IN_ELEMENT,
542 {'name': identifier, 'element': current.name});
543 }
544 return current;
545 }
546 current = e;
547 }
548 return current;
549 }
550
551 /// Helper method to lookup members in a [ScopeContainerElement]. If
552 /// [element] is not a ScopeContainerElement, return null.
553 Element findLocalMemberIn(Element element, String name) {
554 if (element is ScopeContainerElement) {
555 ScopeContainerElement scope = element;
556 if (element.isClass) {
557 ClassElement cls = element;
558 cls.ensureResolved(compiler);
559 }
560 return scope.localLookup(name);
561 }
562 return null;
563 }
564
565 /// Attempt to find a [Spannable] corresponding to constant.
566 Spannable positionOf(ConstantValue constant) {
567 Node node;
568 elements.forEachConstantNode((Node n, ConstantExpression c) {
569 if (node == null && c.value == constant) {
570 node = n;
571 }
572 });
573 if (node == null) {
574 // TODO(ahe): Returning [spannable] here leads to confusing error
575 // messages. For example, consider:
576 // @MirrorsUsed(targets: fisk)
577 // import 'dart:mirrors';
578 //
579 // const fisk = const [main];
580 //
581 // main() {}
582 //
583 // The message is:
584 // example.dart:1:23: Hint: Can't use 'fisk' here because ...
585 // Did you forget to add quotes?
586 // @MirrorsUsed(targets: fisk)
587 // ^^^^
588 //
589 // Instead of saying 'fisk' should pretty print the problematic constant
590 // value.
591 return spannable;
592 }
593 return node;
594 }
595 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698