Chromium Code Reviews| 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..db30f5a5fb0763ba5dc8818432f8c03c91357cd7 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,15 @@ 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); |
| + } |
| 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; |
| @@ -103,27 +118,63 @@ class MirrorUsageAnalyzerTask extends CompilerTask { |
| return librariesWithUsage != null |
| && librariesWithUsage.contains(element.getLibrary()); |
| } |
| + |
| + 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>(); |
| 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 { |
| @@ -136,7 +187,6 @@ class MirrorUsageAnalyzer { |
| 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; |
| @@ -200,14 +250,7 @@ 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; |
| @@ -258,6 +301,25 @@ class MirrorUsageAnalyzer { |
| } |
| return new MirrorUsage(symbols, targets, metaTargets, null); |
| } |
| + |
| + MirrorUsage buildUsage(ConstructedConstant constructedConstant) { |
| + Map<Element, Constant> fields = constructedConstant.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]]); |
| + } |
| } |
| class MirrorUsage { |
| @@ -281,38 +343,23 @@ 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) { |
| + List convertConstantToUsageList( |
| + Constant constant, { bool onlyStrings: false }) { |
| if (constant.isNull()) { |
| return null; |
| } else if (constant.isList()) { |
| @@ -322,16 +369,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,9 +391,31 @@ 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; |
| + } |
| + } |
| + |
| + DartType apiTypeOf(Constant constant) { |
| + DartType type = constant.computeType(compiler); |
|
Johnni Winther
2013/08/07 10:39:17
This doesn't work for double constants with zero f
ahe
2013/08/07 13:40:20
That sounds like a bug in our constant system.
|
| + 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; |
| } |
| List<String> convertToListOfStrings(List list) { |
| @@ -349,10 +423,7 @@ class MirrorUsageBuilder { |
| 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; |
| @@ -370,22 +441,111 @@ 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': identifiers[0]}); |
| + 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; |
| + } |
| + |
| + Element findLocalMemberIn(Element element, SourceString name) { |
| + if (element is ScopeContainerElement) { |
| + ScopeContainerElement scope = element as dynamic; |
| + return scope.localLookup(name); |
| + } |
| + return null; |
| + } |
| + |
| + Spannable positionOf(Constant constant) { |
| + Node node = constantToNodeMap[constant]; |
| + if (node != null) { |
| + // TODO(ahe): Returning [node] 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 node; |
| + } |
| + return spannable; |
| + } |
| } |