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

Unified Diff: dart/sdk/lib/_internal/compiler/implementation/mirrors_used.dart

Issue 21242002: Retain elements a finer granularity than library. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Address review comments 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 side-by-side diff with in-line comments
Download patch
Index: dart/sdk/lib/_internal/compiler/implementation/mirrors_used.dart
diff --git a/dart/sdk/lib/_internal/compiler/implementation/mirrors_used.dart b/dart/sdk/lib/_internal/compiler/implementation/mirrors_used.dart
index 7b795a2137857b62b99a644d5ea76acee99561b0..e868ce0841e5e3cd885c30b2948ae10e672bf8e3 100644
--- a/dart/sdk/lib/_internal/compiler/implementation/mirrors_used.dart
+++ b/dart/sdk/lib/_internal/compiler/implementation/mirrors_used.dart
@@ -13,23 +13,36 @@ import 'dart2jslib.dart' show
MessageKind,
SourceString,
StringConstant,
- TypeConstant;
+ TreeElements,
+ TypeConstant,
+ invariant;
import 'elements/elements.dart' show
+ ClassElement,
Element,
LibraryElement,
MetadataAnnotation,
+ ScopeContainerElement,
VariableElement;
import 'util/util.dart' show
- Link;
+ Link,
+ Spannable;
import 'dart_types.dart' show
- DartType;
+ DartType,
+ InterfaceType,
+ TypeKind;
import 'tree/tree.dart' show
Import,
- LibraryTag;
+ LibraryTag,
+ NamedArgument,
+ NewExpression,
+ Node;
+
+import 'resolution/resolution.dart' show
+ ConstantMapper;
/**
* Compiler task that analyzes MirrorsUsed annotations.
@@ -81,13 +94,17 @@ import 'tree/tree.dart' show
*/
class MirrorUsageAnalyzerTask extends CompilerTask {
Set<LibraryElement> librariesWithUsage;
+ MirrorUsageAnalyzer analyzer;
MirrorUsageAnalyzerTask(Compiler compiler)
- : super(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 (compiler.mirrorsLibrary == null) return;
- MirrorUsageAnalyzer analyzer = new MirrorUsageAnalyzer(compiler, this);
measure(analyzer.run);
List<String> symbols = analyzer.mergedMirrorUsage.symbols;
List<Element> targets = analyzer.mergedMirrorUsage.targets;
@@ -99,31 +116,76 @@ class MirrorUsageAnalyzerTask extends CompilerTask {
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) {
return librariesWithUsage != null
&& librariesWithUsage.contains(element.getLibrary());
}
+
+ /// 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;
+ Constant value = compiler.metadataHandler.compileNodeWithDefinitions(
+ named.expression, mapping, isConst: true);
+
+ ConstantMapper mapper =
+ new ConstantMapper(compiler.metadataHandler, mapping, compiler);
+ named.expression.accept(mapper);
+
+ MirrorUsageBuilder builder =
+ new MirrorUsageBuilder(
+ analyzer, mapping.currentElement.getLibrary(), named.expression,
+ value, mapper.constantToNodeMap);
+
+ if (named.name.source == const SourceString('symbols')) {
+ analyzer.cachedValues[value] =
+ builder.convertToListOfStrings(
+ builder.convertConstantToUsageList(value, onlyStrings: true));
+ } else if (named.name.source == const SourceString('targets')) {
+ analyzer.cachedValues[value] =
+ builder.resolveUsageList(builder.convertConstantToUsageList(value));
+ } else if (named.name.source == const SourceString('metaTargets')) {
+ analyzer.cachedValues[value] =
+ builder.resolveUsageList(builder.convertConstantToUsageList(value));
+ } else if (named.name.source == const SourceString('override')) {
+ analyzer.cachedValues[value] =
+ builder.resolveUsageList(builder.convertConstantToUsageList(value));
+ }
+ }
+ }
}
class MirrorUsageAnalyzer {
final Compiler compiler;
final MirrorUsageAnalyzerTask task;
- final List<LibraryElement> wildcard;
+ List<LibraryElement> wildcard;
final Set<LibraryElement> librariesWithUsage;
- final Set<LibraryElement> librariesWithoutUsage;
+ final Map<Constant, List> cachedValues;
MirrorUsage mergedMirrorUsage;
MirrorUsageAnalyzer(Compiler compiler, this.task)
: compiler = compiler,
- wildcard = compiler.libraries.values.toList(),
librariesWithUsage = new Set<LibraryElement>(),
- librariesWithoutUsage = new Set<LibraryElement>();
+ cachedValues = new Map<Constant, List>();
+ /// 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.libraries.values.toList();
Map<LibraryElement, List<MirrorUsage>> usageMap =
collectMirrorsUsedAnnotation();
propagateOverrides(usageMap);
- librariesWithoutUsage.removeAll(usageMap.keys);
+ 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 {
@@ -131,12 +193,13 @@ class MirrorUsageAnalyzer {
}
}
+ /// 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.libraries.values) {
if (library.isInternalLibrary) continue;
- librariesWithoutUsage.add(library);
for (LibraryTag tag in library.tags) {
Import importTag = tag.asImport();
if (importTag == null) continue;
@@ -157,6 +220,7 @@ class MirrorUsageAnalyzer {
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>>();
@@ -189,6 +253,8 @@ class MirrorUsageAnalyzer {
});
}
+ /// 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);
@@ -200,19 +266,13 @@ class MirrorUsageAnalyzer {
metadata.ensureResolved(compiler);
Element element = metadata.value.computeType(compiler).element;
if (element == compiler.mirrorsUsedClass) {
- try {
- MirrorUsage usage =
- new MirrorUsageBuilder(this, library).build(metadata.value);
- result.add(usage);
- } on BadMirrorsUsedAnnotation catch (e) {
- compiler.reportError(
- metadata, MessageKind.GENERIC, {'text': e.message});
- }
+ result.add(buildUsage(metadata.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) {
@@ -230,7 +290,11 @@ class MirrorUsageAnalyzer {
}
}
+ /// 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 (
@@ -258,8 +322,30 @@ class MirrorUsageAnalyzer {
}
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(ConstructedConstant constant) {
+ Map<Element, Constant> fields = constant.fieldElements;
+ VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember(
+ const SourceString('symbols'));
+ VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember(
+ const SourceString('targets'));
+ VariableElement metaTargetsField =
+ compiler.mirrorsUsedClass.lookupLocalMember(
+ const SourceString('metaTargets'));
+ VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember(
+ const SourceString('override'));
+
+ return new MirrorUsage(
+ cachedValues[fields[symbolsField]],
+ cachedValues[fields[targetsField]],
+ cachedValues[fields[metaTargetsField]],
+ cachedValues[fields[overrideField]]);
+ }
}
+/// Used to represent a resolved MirrorsUsed constant.
class MirrorUsage {
final List<String> symbols;
final List<Element> targets;
@@ -281,38 +367,28 @@ class MirrorUsage {
}
class MirrorUsageBuilder {
- MirrorUsageAnalyzer analyzer;
- LibraryElement enclosingLibrary;
+ final MirrorUsageAnalyzer analyzer;
+ final LibraryElement enclosingLibrary;
+ final Spannable spannable;
+ final Constant constant;
+ final Map<Constant, Node> constantToNodeMap;
- MirrorUsageBuilder(this.analyzer, this.enclosingLibrary);
+ MirrorUsageBuilder(
+ this.analyzer,
+ this.enclosingLibrary,
+ this.spannable,
+ this.constant,
+ this.constantToNodeMap);
Compiler get compiler => analyzer.compiler;
- MirrorUsage build(ConstructedConstant constant) {
- Map<Element, Constant> fields = constant.fieldElements;
- VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember(
- const SourceString('symbols'));
- VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember(
- const SourceString('targets'));
- VariableElement metaTargetsField =
- compiler.mirrorsUsedClass.lookupLocalMember(
- const SourceString('metaTargets'));
- VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember(
- const SourceString('override'));
- List<String> symbols =
- convertToListOfStrings(
- convertConstantToUsageList(fields[symbolsField]));
- List<Element> targets =
- resolveUsageList(convertConstantToUsageList(fields[targetsField]));
-
- List<Element> metaTargets =
- resolveUsageList(convertConstantToUsageList(fields[metaTargetsField]));
- List<Element> override =
- resolveUsageList(convertConstantToUsageList(fields[overrideField]));
- return new MirrorUsage(symbols, targets, metaTargets, override);
- }
-
- List convertConstantToUsageList(Constant constant) {
+ /// 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.
+ List convertConstantToUsageList(
+ Constant constant, { bool onlyStrings: false }) {
if (constant.isNull()) {
return null;
} else if (constant.isList()) {
@@ -322,16 +398,21 @@ class MirrorUsageBuilder {
if (entry.isString()) {
StringConstant string = entry;
result.add(string.value.slowToString());
- } else if (entry.isType()) {
+ } else if (!onlyStrings && entry.isType()) {
TypeConstant type = entry;
result.add(type.representedType);
} else {
- throw new BadMirrorsUsedAnnotation(
- 'Expected a string or type, but got "$entry".');
+ 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 (constant.isType()) {
+ } else if (!onlyStrings && constant.isType()) {
TypeConstant type = constant;
return [type.representedType];
} else if (constant.isString()) {
@@ -339,25 +420,53 @@ class MirrorUsageBuilder {
return
string.value.slowToString().split(',').map((e) => e.trim()).toList();
} else {
- throw new BadMirrorsUsedAnnotation(
- 'Expected a string or a list of string, but got "$constant".');
+ 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(Constant constant) {
+ DartType type = constant.computeType(compiler);
+ LibraryElement library = type.element.getLibrary();
+ if (type.kind == TypeKind.INTERFACE && library.isInternalLibrary) {
+ InterfaceType interface = type;
+ ClassElement cls = type.element;
+ for (DartType supertype in cls.ensureResolved(compiler).allSupertypes) {
+ if (supertype.kind == TypeKind.INTERFACE
+ && !supertype.element.getLibrary().isInternalLibrary) {
+ return interface.asInstanceOf(supertype.element);
+ }
+ }
}
+ return type;
}
+ /// Ensure a list contains only strings.
List<String> convertToListOfStrings(List list) {
if (list == null) return null;
List<String> result = new List<String>(list.length);
int count = 0;
for (var entry in list) {
- if (entry is! String) {
- throw new BadMirrorsUsedAnnotation(
- 'Expected a string, but got "$entry"');
- }
+ assert(invariant(spannable, entry is String));
result[count++] = entry;
}
return result;
}
+ /// 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] == '*') {
@@ -370,22 +479,114 @@ class MirrorUsageBuilder {
result.add(type.element);
} else {
String string = entry;
+ LibraryElement libraryCandiate;
+ String libraryNameCandiate;
for (LibraryElement l in compiler.libraries.values) {
if (l.hasLibraryName()) {
String libraryName = l.getLibraryOrScriptName();
- if (string == libraryName || string.startsWith('$libraryName.')) {
- result.add(l);
+ 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;
}
-}
-class BadMirrorsUsedAnnotation {
- final String message;
- BadMirrorsUsedAnnotation(this.message);
+ /// Resolve [expression] in [enclosingLibrary]'s import scope.
+ Element resolveExpression(String expression) {
+ List<String> identifiers = expression.split('.');
+ Element element = enclosingLibrary.find(new SourceString(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, new SourceString(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, SourceString name) {
+ if (element is ScopeContainerElement) {
+ ScopeContainerElement scope = element;
+ return scope.localLookup(name);
+ }
+ return null;
+ }
+
+ /// Attempt to find a [Spannable] corresponding to constant.
+ Spannable positionOf(Constant constant) {
+ Node node = constantToNodeMap[constant];
+ 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;
+ }
}

Powered by Google App Engine
This is Rietveld 408576698