Index: pkg/compiler/lib/src/mirrors_used.dart |
diff --git a/pkg/compiler/lib/src/mirrors_used.dart b/pkg/compiler/lib/src/mirrors_used.dart |
deleted file mode 100644 |
index 208904edeb8b8e7ddec86332d5eae1a27ef3f694..0000000000000000000000000000000000000000 |
--- a/pkg/compiler/lib/src/mirrors_used.dart |
+++ /dev/null |
@@ -1,595 +0,0 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-library dart2js.mirrors_used; |
- |
-import 'constants/expressions.dart'; |
-import 'constants/values.dart' show |
- ConstantValue, |
- ConstructedConstantValue, |
- ListConstantValue, |
- StringConstantValue, |
- TypeConstantValue; |
- |
-import 'dart_types.dart' show |
- DartType, |
- InterfaceType, |
- TypeKind; |
- |
-import 'dart2jslib.dart' show |
- Compiler, |
- CompilerTask, |
- ConstantCompiler, |
- MessageKind, |
- TreeElements, |
- invariant; |
- |
-import 'elements/elements.dart' show |
- ClassElement, |
- Element, |
- LibraryElement, |
- MetadataAnnotation, |
- ScopeContainerElement, |
- VariableElement; |
- |
-import 'tree/tree.dart' show |
- Import, |
- LibraryTag, |
- NamedArgument, |
- NewExpression, |
- Node; |
- |
-import 'util/util.dart' show |
- Link, |
- Spannable; |
- |
-/** |
- * Compiler task that analyzes MirrorsUsed annotations. |
- * |
- * When importing 'dart:mirrors', it is possible to annotate the import with |
- * MirrorsUsed annotation. This is a way to declare what elements will be |
- * reflected on at runtime. Such elements, even they would normally be |
- * discarded by the implicit tree-shaking algorithm must be preserved in the |
- * final output. |
- * |
- * Since some libraries cannot tell exactly what they will be reflecting on, it |
- * is possible for one library to specify a MirrorsUsed annotation that applies |
- * to another library. For example: |
- * |
- * Mirror utility library that cannot tell what it is reflecting on: |
- * library mirror_utils; |
- * import 'dart:mirrors'; |
- * ... |
- * |
- * The main app which knows how it use the mirror utility library: |
- * library main_app; |
- * @MirrorsUsed(override='mirror_utils') |
- * import 'dart:mirrors'; |
- * import 'mirror_utils.dart'; |
- * ... |
- * |
- * In this case, we say that @MirrorsUsed in main_app overrides @MirrorsUsed in |
- * mirror_utils. |
- * |
- * It is possible to override all libraries using override='*'. If multiple |
- * catch-all overrides like this, they are merged together. |
- * |
- * It is possible for library "a" to declare that it overrides library "b", and |
- * vice versa. In this case, both annotations will be discarded and the |
- * compiler will emit a hint (that is, a warning that is not specified by the |
- * language specification). |
- * |
- * After applying all the overrides, we can iterate over libraries that import |
- * 'dart:mirrors'. If a library does not have an associated MirrorsUsed |
- * annotation, then we have to discard all MirrorsUsed annotations and assume |
- * everything can be reflected on. |
- * |
- * On the other hand, if all libraries importing dart:mirrors have a |
- * MirrorsUsed annotation, these annotations are merged. |
- * |
- * MERGING MIRRORSUSED |
- * |
- * TBD. |
- */ |
-class MirrorUsageAnalyzerTask extends CompilerTask { |
- Set<LibraryElement> librariesWithUsage; |
- MirrorUsageAnalyzer analyzer; |
- |
- MirrorUsageAnalyzerTask(Compiler compiler) |
- : super(compiler) { |
- analyzer = new MirrorUsageAnalyzer(compiler, this); |
- } |
- |
- /// Collect @MirrorsUsed annotations in all libraries. Called by the |
- /// compiler after all libraries are loaded, but before resolution. |
- void analyzeUsage(LibraryElement mainApp) { |
- if (mainApp == null || compiler.mirrorsLibrary == null) return; |
- measure(analyzer.run); |
- List<String> symbols = analyzer.mergedMirrorUsage.symbols; |
- List<Element> targets = analyzer.mergedMirrorUsage.targets; |
- List<Element> metaTargets = analyzer.mergedMirrorUsage.metaTargets; |
- compiler.backend.registerMirrorUsage( |
- symbols == null ? null : new Set<String>.from(symbols), |
- targets == null ? null : new Set<Element>.from(targets), |
- metaTargets == null ? null : new Set<Element>.from(metaTargets)); |
- librariesWithUsage = analyzer.librariesWithUsage; |
- } |
- |
- /// Is there a @MirrorsUsed annotation in the library of [element]? Used by |
- /// the resolver to suppress hints about using new Symbol or |
- /// MirrorSystem.getName. |
- bool hasMirrorUsage(Element element) { |
- LibraryElement library = element.library; |
- // Internal libraries always have implicit mirror usage. |
- return library.isInternalLibrary |
- || (librariesWithUsage != null |
- && librariesWithUsage.contains(library)); |
- } |
- |
- /// Call-back from the resolver to analyze MirorsUsed annotations. The result |
- /// is stored in [analyzer] and later used to compute |
- /// [:analyzer.mergedMirrorUsage:]. |
- void validate(NewExpression node, TreeElements mapping) { |
- for (Node argument in node.send.arguments) { |
- NamedArgument named = argument.asNamedArgument(); |
- if (named == null) continue; |
- ConstantCompiler constantCompiler = compiler.resolver.constantCompiler; |
- ConstantValue value = |
- constantCompiler.compileNode(named.expression, mapping).value; |
- |
- MirrorUsageBuilder builder = |
- new MirrorUsageBuilder( |
- analyzer, mapping.analyzedElement.library, named.expression, |
- value, mapping); |
- |
- if (named.name.source == 'symbols') { |
- analyzer.cachedStrings[value] = |
- builder.convertConstantToUsageList(value, onlyStrings: true); |
- } else if (named.name.source == 'targets') { |
- analyzer.cachedElements[value] = |
- builder.resolveUsageList(builder.convertConstantToUsageList(value)); |
- } else if (named.name.source == 'metaTargets') { |
- analyzer.cachedElements[value] = |
- builder.resolveUsageList(builder.convertConstantToUsageList(value)); |
- } else if (named.name.source == 'override') { |
- analyzer.cachedElements[value] = |
- builder.resolveUsageList(builder.convertConstantToUsageList(value)); |
- } |
- } |
- } |
-} |
- |
-class MirrorUsageAnalyzer { |
- final Compiler compiler; |
- final MirrorUsageAnalyzerTask task; |
- List<LibraryElement> wildcard; |
- final Set<LibraryElement> librariesWithUsage; |
- final Map<ConstantValue, List<String>> cachedStrings; |
- final Map<ConstantValue, List<Element>> cachedElements; |
- MirrorUsage mergedMirrorUsage; |
- |
- MirrorUsageAnalyzer(Compiler compiler, this.task) |
- : compiler = compiler, |
- librariesWithUsage = new Set<LibraryElement>(), |
- cachedStrings = new Map<ConstantValue, List<String>>(), |
- cachedElements = new Map<ConstantValue, List<Element>>(); |
- |
- /// Collect and merge all @MirrorsUsed annotations. As a side-effect, also |
- /// compute which libraries have the annotation (which is used by |
- /// [MirrorUsageAnalyzerTask.hasMirrorUsage]). |
- void run() { |
- wildcard = compiler.libraryLoader.libraries.toList(); |
- Map<LibraryElement, List<MirrorUsage>> usageMap = |
- collectMirrorsUsedAnnotation(); |
- propagateOverrides(usageMap); |
- Set<LibraryElement> librariesWithoutUsage = new Set<LibraryElement>(); |
- usageMap.forEach((LibraryElement library, List<MirrorUsage> usage) { |
- if (usage.isEmpty) librariesWithoutUsage.add(library); |
- }); |
- if (librariesWithoutUsage.isEmpty) { |
- mergedMirrorUsage = mergeUsages(usageMap); |
- } else { |
- mergedMirrorUsage = new MirrorUsage(null, null, null, null); |
- } |
- } |
- |
- /// Collect all @MirrorsUsed from all libraries and represent them as |
- /// [MirrorUsage]. |
- Map<LibraryElement, List<MirrorUsage>> collectMirrorsUsedAnnotation() { |
- Map<LibraryElement, List<MirrorUsage>> result = |
- new Map<LibraryElement, List<MirrorUsage>>(); |
- for (LibraryElement library in compiler.libraryLoader.libraries) { |
- if (library.isInternalLibrary) continue; |
- for (LibraryTag tag in library.tags) { |
- Import importTag = tag.asImport(); |
- if (importTag == null) continue; |
- compiler.withCurrentElement(library, () { |
- List<MirrorUsage> usages = |
- mirrorsUsedOnLibraryTag(library, importTag); |
- if (usages != null) { |
- List<MirrorUsage> existing = result[library]; |
- if (existing != null) { |
- existing.addAll(usages); |
- } else { |
- result[library] = usages; |
- } |
- } |
- }); |
- } |
- } |
- return result; |
- } |
- |
- /// Apply [MirrorUsage] with 'override' to libraries they override. |
- void propagateOverrides(Map<LibraryElement, List<MirrorUsage>> usageMap) { |
- Map<LibraryElement, List<MirrorUsage>> propagatedOverrides = |
- new Map<LibraryElement, List<MirrorUsage>>(); |
- usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) { |
- for (MirrorUsage usage in usages) { |
- List<Element> override = usage.override; |
- if (override == null) continue; |
- if (override == wildcard) { |
- for (LibraryElement overridden in wildcard) { |
- if (overridden != library) { |
- List<MirrorUsage> overriddenUsages = propagatedOverrides |
- .putIfAbsent(overridden, () => <MirrorUsage>[]); |
- overriddenUsages.add(usage); |
- } |
- } |
- } else { |
- for (Element overridden in override) { |
- List<MirrorUsage> overriddenUsages = propagatedOverrides |
- .putIfAbsent(overridden, () => <MirrorUsage>[]); |
- overriddenUsages.add(usage); |
- } |
- } |
- } |
- }); |
- propagatedOverrides.forEach((LibraryElement overridden, |
- List<MirrorUsage> overriddenUsages) { |
- List<MirrorUsage> usages = |
- usageMap.putIfAbsent(overridden, () => <MirrorUsage>[]); |
- usages.addAll(overriddenUsages); |
- }); |
- } |
- |
- /// Find @MirrorsUsed annotations on the given import [tag] in [library]. The |
- /// annotations are represented as [MirrorUsage]. |
- List<MirrorUsage> mirrorsUsedOnLibraryTag(LibraryElement library, |
- Import tag) { |
- LibraryElement importedLibrary = library.getLibraryFromTag(tag); |
- if (importedLibrary != compiler.mirrorsLibrary) { |
- return null; |
- } |
- List<MirrorUsage> result = <MirrorUsage>[]; |
- for (MetadataAnnotation metadata in tag.metadata) { |
- metadata.ensureResolved(compiler); |
- Element element = metadata.constant.value.computeType(compiler).element; |
- if (element == compiler.mirrorsUsedClass) { |
- result.add(buildUsage(metadata.constant.value)); |
- } |
- } |
- return result; |
- } |
- |
- /// Merge all [MirrorUsage] instances accross all libraries. |
- MirrorUsage mergeUsages(Map<LibraryElement, List<MirrorUsage>> usageMap) { |
- Set<MirrorUsage> usagesToMerge = new Set<MirrorUsage>(); |
- usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) { |
- librariesWithUsage.add(library); |
- usagesToMerge.addAll(usages); |
- }); |
- if (usagesToMerge.isEmpty) { |
- return new MirrorUsage(null, wildcard, null, null); |
- } else { |
- MirrorUsage result = new MirrorUsage(null, null, null, null); |
- for (MirrorUsage usage in usagesToMerge) { |
- result = merge(result, usage); |
- } |
- return result; |
- } |
- } |
- |
- /// Merge [a] with [b]. The resulting [MirrorUsage] simply has the symbols, |
- /// targets, and metaTargets of [a] and [b] concatenated. 'override' is |
- /// ignored. |
- MirrorUsage merge(MirrorUsage a, MirrorUsage b) { |
- // TOOO(ahe): Should be an instance method on MirrorUsage. |
- if (a.symbols == null && a.targets == null && a.metaTargets == null) { |
- return b; |
- } else if ( |
- b.symbols == null && b.targets == null && b.metaTargets == null) { |
- return a; |
- } |
- // TODO(ahe): Test the following cases. |
- List<String> symbols = a.symbols; |
- if (symbols == null) { |
- symbols = b.symbols; |
- } else if (b.symbols != null) { |
- symbols.addAll(b.symbols); |
- } |
- List<Element> targets = a.targets; |
- if (targets == null) { |
- targets = b.targets; |
- } else if (targets != wildcard && b.targets != null) { |
- targets.addAll(b.targets); |
- } |
- List<Element> metaTargets = a.metaTargets; |
- if (metaTargets == null) { |
- metaTargets = b.metaTargets; |
- } else if (metaTargets != wildcard && b.metaTargets != null) { |
- metaTargets.addAll(b.metaTargets); |
- } |
- return new MirrorUsage(symbols, targets, metaTargets, null); |
- } |
- |
- /// Convert a [constant] to an instance of [MirrorUsage] using information |
- /// that was resolved during [MirrorUsageAnalyzerTask.validate]. |
- MirrorUsage buildUsage(ConstructedConstantValue constant) { |
- Map<Element, ConstantValue> fields = constant.fieldElements; |
- VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember( |
- 'symbols'); |
- VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember( |
- 'targets'); |
- VariableElement metaTargetsField = |
- compiler.mirrorsUsedClass.lookupLocalMember( |
- 'metaTargets'); |
- VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember( |
- 'override'); |
- |
- return new MirrorUsage( |
- cachedStrings[fields[symbolsField]], |
- cachedElements[fields[targetsField]], |
- cachedElements[fields[metaTargetsField]], |
- cachedElements[fields[overrideField]]); |
- } |
-} |
- |
-/// Used to represent a resolved MirrorsUsed constant. |
-class MirrorUsage { |
- final List<String> symbols; |
- final List<Element> targets; |
- final List<Element> metaTargets; |
- final List<Element> override; |
- |
- MirrorUsage(this.symbols, this.targets, this.metaTargets, this.override); |
- |
- String toString() { |
- return |
- 'MirrorUsage(' |
- 'symbols = $symbols, ' |
- 'targets = $targets, ' |
- 'metaTargets = $metaTargets, ' |
- 'override = $override' |
- ')'; |
- |
- } |
-} |
- |
-class MirrorUsageBuilder { |
- final MirrorUsageAnalyzer analyzer; |
- final LibraryElement enclosingLibrary; |
- final Spannable spannable; |
- final ConstantValue constant; |
- final TreeElements elements; |
- |
- MirrorUsageBuilder( |
- this.analyzer, |
- this.enclosingLibrary, |
- this.spannable, |
- this.constant, |
- this.elements); |
- |
- Compiler get compiler => analyzer.compiler; |
- |
- /// Convert a constant to a list of [String] and [Type] values. If the |
- /// constant is a single [String], it is assumed to be a comma-separated list |
- /// of qualified names. If the constant is a [Type] t, the result is [:[t]:]. |
- /// Otherwise, the constant is assumed to represent a list of strings (each a |
- /// qualified name) and types, and such a list is constructed. If |
- /// [onlyStrings] is true, the returned list is a [:List<String>:] and any |
- /// [Type] values are treated as an error (meaning that the value is ignored |
- /// and a hint is emitted). |
- List convertConstantToUsageList( |
- ConstantValue constant, { bool onlyStrings: false }) { |
- if (constant.isNull) { |
- return null; |
- } else if (constant.isList) { |
- ListConstantValue list = constant; |
- List result = onlyStrings ? <String> [] : []; |
- for (ConstantValue entry in list.entries) { |
- if (entry.isString) { |
- StringConstantValue string = entry; |
- result.add(string.primitiveValue.slowToString()); |
- } else if (!onlyStrings && entry.isType) { |
- TypeConstantValue type = entry; |
- result.add(type.representedType); |
- } else { |
- Spannable node = positionOf(entry); |
- MessageKind kind = onlyStrings |
- ? MessageKind.MIRRORS_EXPECTED_STRING |
- : MessageKind.MIRRORS_EXPECTED_STRING_OR_TYPE; |
- compiler.reportHint( |
- node, |
- kind, {'name': node, 'type': apiTypeOf(entry)}); |
- } |
- } |
- return result; |
- } else if (!onlyStrings && constant.isType) { |
- TypeConstantValue type = constant; |
- return [type.representedType]; |
- } else if (constant.isString) { |
- StringConstantValue string = constant; |
- var iterable = |
- string.primitiveValue.slowToString().split(',').map((e) => e.trim()); |
- return onlyStrings ? new List<String>.from(iterable) : iterable.toList(); |
- } else { |
- Spannable node = positionOf(constant); |
- MessageKind kind = onlyStrings |
- ? MessageKind.MIRRORS_EXPECTED_STRING_OR_LIST |
- : MessageKind.MIRRORS_EXPECTED_STRING_TYPE_OR_LIST; |
- compiler.reportHint( |
- node, |
- kind, {'name': node, 'type': apiTypeOf(constant)}); |
- return null; |
- } |
- } |
- |
- /// Find the first non-implementation interface of constant. |
- DartType apiTypeOf(ConstantValue constant) { |
- DartType type = constant.computeType(compiler); |
- LibraryElement library = type.element.library; |
- if (type.isInterfaceType && library.isInternalLibrary) { |
- InterfaceType interface = type; |
- ClassElement cls = type.element; |
- cls.ensureResolved(compiler); |
- for (DartType supertype in cls.allSupertypes) { |
- if (supertype.isInterfaceType |
- && !supertype.element.library.isInternalLibrary) { |
- return interface.asInstanceOf(supertype.element); |
- } |
- } |
- } |
- return type; |
- } |
- |
- /// Convert a list of strings and types to a list of elements. Types are |
- /// converted to their corresponding element, and strings are resolved as |
- /// follows: |
- /// |
- /// First find the longest library name that is a prefix of the string, if |
- /// there are none, resolve using [resolveExpression]. Otherwise, resolve the |
- /// rest of the string using [resolveLocalExpression]. |
- List<Element> resolveUsageList(List list) { |
- if (list == null) return null; |
- if (list.length == 1 && list[0] == '*') { |
- return analyzer.wildcard; |
- } |
- List<Element> result = <Element>[]; |
- for (var entry in list) { |
- if (entry is DartType) { |
- DartType type = entry; |
- result.add(type.element); |
- } else { |
- String string = entry; |
- LibraryElement libraryCandiate; |
- String libraryNameCandiate; |
- for (LibraryElement l in compiler.libraryLoader.libraries) { |
- if (l.hasLibraryName()) { |
- String libraryName = l.getLibraryOrScriptName(); |
- if (string == libraryName) { |
- // Found an exact match. |
- libraryCandiate = l; |
- libraryNameCandiate = libraryName; |
- break; |
- } else if (string.startsWith('$libraryName.')) { |
- if (libraryNameCandiate == null |
- || libraryNameCandiate.length < libraryName.length) { |
- // Found a better candiate |
- libraryCandiate = l; |
- libraryNameCandiate = libraryName; |
- } |
- } |
- } |
- } |
- Element e; |
- if (libraryNameCandiate == string) { |
- e = libraryCandiate; |
- } else if (libraryNameCandiate != null) { |
- e = resolveLocalExpression( |
- libraryCandiate, |
- string.substring(libraryNameCandiate.length + 1).split('.')); |
- } else { |
- e = resolveExpression(string); |
- } |
- if (e != null) result.add(e); |
- } |
- } |
- return result; |
- } |
- |
- /// Resolve [expression] in [enclosingLibrary]'s import scope. |
- Element resolveExpression(String expression) { |
- List<String> identifiers = expression.split('.'); |
- Element element = enclosingLibrary.find(identifiers[0]); |
- if (element == null) { |
- compiler.reportHint( |
- spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_CURRENT_LIBRARY, |
- {'name': expression}); |
- return null; |
- } else { |
- if (identifiers.length == 1) return element; |
- return resolveLocalExpression(element, identifiers.sublist(1)); |
- } |
- } |
- |
- /// Resolve [identifiers] in [element]'s local members. |
- Element resolveLocalExpression(Element element, List<String> identifiers) { |
- Element current = element; |
- for (String identifier in identifiers) { |
- Element e = findLocalMemberIn(current, identifier); |
- if (e == null) { |
- if (current.isLibrary) { |
- LibraryElement library = current; |
- compiler.reportHint( |
- spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_LIBRARY, |
- {'name': identifiers[0], |
- 'library': library.getLibraryOrScriptName()}); |
- } else { |
- compiler.reportHint( |
- spannable, MessageKind.MIRRORS_CANNOT_FIND_IN_ELEMENT, |
- {'name': identifier, 'element': current.name}); |
- } |
- return current; |
- } |
- current = e; |
- } |
- return current; |
- } |
- |
- /// Helper method to lookup members in a [ScopeContainerElement]. If |
- /// [element] is not a ScopeContainerElement, return null. |
- Element findLocalMemberIn(Element element, String name) { |
- if (element is ScopeContainerElement) { |
- ScopeContainerElement scope = element; |
- if (element.isClass) { |
- ClassElement cls = element; |
- cls.ensureResolved(compiler); |
- } |
- return scope.localLookup(name); |
- } |
- return null; |
- } |
- |
- /// Attempt to find a [Spannable] corresponding to constant. |
- Spannable positionOf(ConstantValue constant) { |
- Node node; |
- elements.forEachConstantNode((Node n, ConstantExpression c) { |
- if (node == null && c.value == constant) { |
- node = n; |
- } |
- }); |
- if (node == null) { |
- // TODO(ahe): Returning [spannable] here leads to confusing error |
- // messages. For example, consider: |
- // @MirrorsUsed(targets: fisk) |
- // import 'dart:mirrors'; |
- // |
- // const fisk = const [main]; |
- // |
- // main() {} |
- // |
- // The message is: |
- // example.dart:1:23: Hint: Can't use 'fisk' here because ... |
- // Did you forget to add quotes? |
- // @MirrorsUsed(targets: fisk) |
- // ^^^^ |
- // |
- // Instead of saying 'fisk' should pretty print the problematic constant |
- // value. |
- return spannable; |
- } |
- return node; |
- } |
-} |