Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart2js.mirrors_used; | 5 library dart2js.mirrors_used; |
|
Johnni Winther
2013/08/07 10:39:17
Overall for this file: Document methods.
ahe
2013/08/07 13:40:20
Done.
| |
| 6 | 6 |
| 7 import 'dart2jslib.dart' show | 7 import 'dart2jslib.dart' show |
| 8 Compiler, | 8 Compiler, |
| 9 CompilerTask, | 9 CompilerTask, |
| 10 Constant, | 10 Constant, |
| 11 ConstructedConstant, | 11 ConstructedConstant, |
| 12 ListConstant, | 12 ListConstant, |
| 13 MessageKind, | 13 MessageKind, |
| 14 SourceString, | 14 SourceString, |
| 15 StringConstant, | 15 StringConstant, |
| 16 TypeConstant; | 16 TreeElements, |
| 17 TypeConstant, | |
| 18 invariant; | |
| 17 | 19 |
| 18 import 'elements/elements.dart' show | 20 import 'elements/elements.dart' show |
| 21 ClassElement, | |
| 19 Element, | 22 Element, |
| 20 LibraryElement, | 23 LibraryElement, |
| 21 MetadataAnnotation, | 24 MetadataAnnotation, |
| 25 ScopeContainerElement, | |
| 22 VariableElement; | 26 VariableElement; |
| 23 | 27 |
| 24 import 'util/util.dart' show | 28 import 'util/util.dart' show |
| 25 Link; | 29 Link, |
| 30 Spannable; | |
| 26 | 31 |
| 27 import 'dart_types.dart' show | 32 import 'dart_types.dart' show |
| 28 DartType; | 33 DartType, |
| 34 InterfaceType, | |
| 35 TypeKind; | |
| 29 | 36 |
| 30 import 'tree/tree.dart' show | 37 import 'tree/tree.dart' show |
| 31 Import, | 38 Import, |
| 32 LibraryTag; | 39 LibraryTag, |
| 40 NamedArgument, | |
| 41 NewExpression, | |
| 42 Node; | |
| 43 | |
| 44 import 'resolution/resolution.dart' show | |
| 45 ConstantMapper; | |
| 33 | 46 |
| 34 /** | 47 /** |
| 35 * Compiler task that analyzes MirrorsUsed annotations. | 48 * Compiler task that analyzes MirrorsUsed annotations. |
| 36 * | 49 * |
| 37 * When importing 'dart:mirrors', it is possible to annotate the import with | 50 * 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 | 51 * MirrorsUsed annotation. This is a way to declare what elements will be |
| 39 * reflected on at runtime. Such elements, even they would normally be | 52 * reflected on at runtime. Such elements, even they would normally be |
| 40 * discarded by the implicit tree-shaking algorithm must be preserved in the | 53 * discarded by the implicit tree-shaking algorithm must be preserved in the |
| 41 * final output. | 54 * final output. |
| 42 * | 55 * |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 74 * | 87 * |
| 75 * On the other hand, if all libraries importing dart:mirrors have a | 88 * On the other hand, if all libraries importing dart:mirrors have a |
| 76 * MirrorsUsed annotation, these annotations are merged. | 89 * MirrorsUsed annotation, these annotations are merged. |
| 77 * | 90 * |
| 78 * MERGING MIRRORSUSED | 91 * MERGING MIRRORSUSED |
| 79 * | 92 * |
| 80 * TBD. | 93 * TBD. |
| 81 */ | 94 */ |
| 82 class MirrorUsageAnalyzerTask extends CompilerTask { | 95 class MirrorUsageAnalyzerTask extends CompilerTask { |
| 83 Set<LibraryElement> librariesWithUsage; | 96 Set<LibraryElement> librariesWithUsage; |
| 97 MirrorUsageAnalyzer analyzer; | |
| 84 | 98 |
| 85 MirrorUsageAnalyzerTask(Compiler compiler) | 99 MirrorUsageAnalyzerTask(Compiler compiler) |
| 86 : super(compiler); | 100 : super(compiler) { |
| 101 analyzer = new MirrorUsageAnalyzer(compiler, this); | |
| 102 } | |
| 87 | 103 |
| 88 void analyzeUsage(LibraryElement mainApp) { | 104 void analyzeUsage(LibraryElement mainApp) { |
| 89 if (compiler.mirrorsLibrary == null) return; | 105 if (compiler.mirrorsLibrary == null) return; |
| 90 MirrorUsageAnalyzer analyzer = new MirrorUsageAnalyzer(compiler, this); | |
| 91 measure(analyzer.run); | 106 measure(analyzer.run); |
| 92 List<String> symbols = analyzer.mergedMirrorUsage.symbols; | 107 List<String> symbols = analyzer.mergedMirrorUsage.symbols; |
| 93 List<Element> targets = analyzer.mergedMirrorUsage.targets; | 108 List<Element> targets = analyzer.mergedMirrorUsage.targets; |
| 94 List<Element> metaTargets = analyzer.mergedMirrorUsage.metaTargets; | 109 List<Element> metaTargets = analyzer.mergedMirrorUsage.metaTargets; |
| 95 compiler.backend.registerMirrorUsage( | 110 compiler.backend.registerMirrorUsage( |
| 96 symbols == null ? null : new Set<String>.from(symbols), | 111 symbols == null ? null : new Set<String>.from(symbols), |
| 97 targets == null ? null : new Set<Element>.from(targets), | 112 targets == null ? null : new Set<Element>.from(targets), |
| 98 metaTargets == null ? null : new Set<Element>.from(metaTargets)); | 113 metaTargets == null ? null : new Set<Element>.from(metaTargets)); |
| 99 librariesWithUsage = analyzer.librariesWithUsage; | 114 librariesWithUsage = analyzer.librariesWithUsage; |
| 100 } | 115 } |
| 101 | 116 |
| 102 bool hasMirrorUsage(Element element) { | 117 bool hasMirrorUsage(Element element) { |
| 103 return librariesWithUsage != null | 118 return librariesWithUsage != null |
| 104 && librariesWithUsage.contains(element.getLibrary()); | 119 && librariesWithUsage.contains(element.getLibrary()); |
| 105 } | 120 } |
| 121 | |
| 122 void validate(NewExpression node, TreeElements mapping) { | |
| 123 for (Node argument in node.send.arguments) { | |
| 124 NamedArgument named = argument.asNamedArgument(); | |
| 125 if (named == null) continue; | |
| 126 Constant value = compiler.metadataHandler.compileNodeWithDefinitions( | |
| 127 named.expression, mapping, isConst: true); | |
| 128 | |
| 129 ConstantMapper mapper = | |
| 130 new ConstantMapper(compiler.metadataHandler, mapping, compiler); | |
| 131 named.expression.accept(mapper); | |
| 132 | |
| 133 MirrorUsageBuilder builder = | |
| 134 new MirrorUsageBuilder( | |
| 135 analyzer, mapping.currentElement.getLibrary(), named.expression, | |
| 136 value, mapper.constantToNodeMap); | |
| 137 | |
| 138 if (named.name.source == const SourceString('symbols')) { | |
| 139 analyzer.cachedValues[value] = | |
| 140 builder.convertToListOfStrings( | |
| 141 builder.convertConstantToUsageList(value, onlyStrings: true)); | |
| 142 } else if (named.name.source == const SourceString('targets')) { | |
| 143 analyzer.cachedValues[value] = | |
| 144 builder.resolveUsageList(builder.convertConstantToUsageList(value)); | |
| 145 } else if (named.name.source == const SourceString('metaTargets')) { | |
| 146 analyzer.cachedValues[value] = | |
| 147 builder.resolveUsageList(builder.convertConstantToUsageList(value)); | |
| 148 } else if (named.name.source == const SourceString('override')) { | |
| 149 analyzer.cachedValues[value] = | |
| 150 builder.resolveUsageList(builder.convertConstantToUsageList(value)); | |
| 151 } | |
| 152 } | |
| 153 } | |
| 106 } | 154 } |
| 107 | 155 |
| 108 class MirrorUsageAnalyzer { | 156 class MirrorUsageAnalyzer { |
| 109 final Compiler compiler; | 157 final Compiler compiler; |
| 110 final MirrorUsageAnalyzerTask task; | 158 final MirrorUsageAnalyzerTask task; |
| 111 final List<LibraryElement> wildcard; | 159 List<LibraryElement> wildcard; |
| 112 final Set<LibraryElement> librariesWithUsage; | 160 final Set<LibraryElement> librariesWithUsage; |
| 113 final Set<LibraryElement> librariesWithoutUsage; | 161 final Map<Constant, List> cachedValues; |
| 114 MirrorUsage mergedMirrorUsage; | 162 MirrorUsage mergedMirrorUsage; |
| 115 | 163 |
| 116 MirrorUsageAnalyzer(Compiler compiler, this.task) | 164 MirrorUsageAnalyzer(Compiler compiler, this.task) |
| 117 : compiler = compiler, | 165 : compiler = compiler, |
| 118 wildcard = compiler.libraries.values.toList(), | |
| 119 librariesWithUsage = new Set<LibraryElement>(), | 166 librariesWithUsage = new Set<LibraryElement>(), |
| 120 librariesWithoutUsage = new Set<LibraryElement>(); | 167 cachedValues = new Map<Constant, List>(); |
| 121 | 168 |
| 122 void run() { | 169 void run() { |
| 170 wildcard = compiler.libraries.values.toList(); | |
| 123 Map<LibraryElement, List<MirrorUsage>> usageMap = | 171 Map<LibraryElement, List<MirrorUsage>> usageMap = |
| 124 collectMirrorsUsedAnnotation(); | 172 collectMirrorsUsedAnnotation(); |
| 125 propagateOverrides(usageMap); | 173 propagateOverrides(usageMap); |
| 126 librariesWithoutUsage.removeAll(usageMap.keys); | 174 Set<LibraryElement> librariesWithoutUsage = new Set<LibraryElement>(); |
| 175 usageMap.forEach((LibraryElement library, List<MirrorUsage> usage) { | |
| 176 if (usage.isEmpty) librariesWithoutUsage.add(library); | |
| 177 }); | |
| 127 if (librariesWithoutUsage.isEmpty) { | 178 if (librariesWithoutUsage.isEmpty) { |
| 128 mergedMirrorUsage = mergeUsages(usageMap); | 179 mergedMirrorUsage = mergeUsages(usageMap); |
| 129 } else { | 180 } else { |
| 130 mergedMirrorUsage = new MirrorUsage(null, wildcard, null, null); | 181 mergedMirrorUsage = new MirrorUsage(null, wildcard, null, null); |
| 131 } | 182 } |
| 132 } | 183 } |
| 133 | 184 |
| 134 Map<LibraryElement, List<MirrorUsage>> collectMirrorsUsedAnnotation() { | 185 Map<LibraryElement, List<MirrorUsage>> collectMirrorsUsedAnnotation() { |
| 135 Map<LibraryElement, List<MirrorUsage>> result = | 186 Map<LibraryElement, List<MirrorUsage>> result = |
| 136 new Map<LibraryElement, List<MirrorUsage>>(); | 187 new Map<LibraryElement, List<MirrorUsage>>(); |
| 137 for (LibraryElement library in compiler.libraries.values) { | 188 for (LibraryElement library in compiler.libraries.values) { |
| 138 if (library.isInternalLibrary) continue; | 189 if (library.isInternalLibrary) continue; |
| 139 librariesWithoutUsage.add(library); | |
| 140 for (LibraryTag tag in library.tags) { | 190 for (LibraryTag tag in library.tags) { |
| 141 Import importTag = tag.asImport(); | 191 Import importTag = tag.asImport(); |
| 142 if (importTag == null) continue; | 192 if (importTag == null) continue; |
| 143 compiler.withCurrentElement(library, () { | 193 compiler.withCurrentElement(library, () { |
| 144 List<MirrorUsage> usages = | 194 List<MirrorUsage> usages = |
| 145 mirrorsUsedOnLibraryTag(library, importTag); | 195 mirrorsUsedOnLibraryTag(library, importTag); |
| 146 if (usages != null) { | 196 if (usages != null) { |
| 147 List<MirrorUsage> existing = result[library]; | 197 List<MirrorUsage> existing = result[library]; |
| 148 if (existing != null) { | 198 if (existing != null) { |
| 149 existing.addAll(usages); | 199 existing.addAll(usages); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 193 Import tag) { | 243 Import tag) { |
| 194 LibraryElement importedLibrary = library.getLibraryFromTag(tag); | 244 LibraryElement importedLibrary = library.getLibraryFromTag(tag); |
| 195 if (importedLibrary != compiler.mirrorsLibrary) { | 245 if (importedLibrary != compiler.mirrorsLibrary) { |
| 196 return null; | 246 return null; |
| 197 } | 247 } |
| 198 List<MirrorUsage> result = <MirrorUsage>[]; | 248 List<MirrorUsage> result = <MirrorUsage>[]; |
| 199 for (MetadataAnnotation metadata in tag.metadata) { | 249 for (MetadataAnnotation metadata in tag.metadata) { |
| 200 metadata.ensureResolved(compiler); | 250 metadata.ensureResolved(compiler); |
| 201 Element element = metadata.value.computeType(compiler).element; | 251 Element element = metadata.value.computeType(compiler).element; |
| 202 if (element == compiler.mirrorsUsedClass) { | 252 if (element == compiler.mirrorsUsedClass) { |
| 203 try { | 253 result.add(buildUsage(metadata.value)); |
| 204 MirrorUsage usage = | |
| 205 new MirrorUsageBuilder(this, library).build(metadata.value); | |
| 206 result.add(usage); | |
| 207 } on BadMirrorsUsedAnnotation catch (e) { | |
| 208 compiler.reportError( | |
| 209 metadata, MessageKind.GENERIC, {'text': e.message}); | |
| 210 } | |
| 211 } | 254 } |
| 212 } | 255 } |
| 213 return result; | 256 return result; |
| 214 } | 257 } |
| 215 | 258 |
| 216 MirrorUsage mergeUsages(Map<LibraryElement, List<MirrorUsage>> usageMap) { | 259 MirrorUsage mergeUsages(Map<LibraryElement, List<MirrorUsage>> usageMap) { |
| 217 Set<MirrorUsage> usagesToMerge = new Set<MirrorUsage>(); | 260 Set<MirrorUsage> usagesToMerge = new Set<MirrorUsage>(); |
| 218 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) { | 261 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) { |
| 219 librariesWithUsage.add(library); | 262 librariesWithUsage.add(library); |
| 220 usagesToMerge.addAll(usages); | 263 usagesToMerge.addAll(usages); |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 251 targets.addAll(b.targets); | 294 targets.addAll(b.targets); |
| 252 } | 295 } |
| 253 List<Element> metaTargets = a.metaTargets; | 296 List<Element> metaTargets = a.metaTargets; |
| 254 if (metaTargets == null) { | 297 if (metaTargets == null) { |
| 255 metaTargets = b.metaTargets; | 298 metaTargets = b.metaTargets; |
| 256 } else if (metaTargets != wildcard && b.metaTargets != null) { | 299 } else if (metaTargets != wildcard && b.metaTargets != null) { |
| 257 metaTargets.addAll(b.metaTargets); | 300 metaTargets.addAll(b.metaTargets); |
| 258 } | 301 } |
| 259 return new MirrorUsage(symbols, targets, metaTargets, null); | 302 return new MirrorUsage(symbols, targets, metaTargets, null); |
| 260 } | 303 } |
| 304 | |
| 305 MirrorUsage buildUsage(ConstructedConstant constructedConstant) { | |
| 306 Map<Element, Constant> fields = constructedConstant.fieldElements; | |
| 307 VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember( | |
| 308 const SourceString('symbols')); | |
| 309 VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember( | |
| 310 const SourceString('targets')); | |
| 311 VariableElement metaTargetsField = | |
| 312 compiler.mirrorsUsedClass.lookupLocalMember( | |
| 313 const SourceString('metaTargets')); | |
| 314 VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember( | |
| 315 const SourceString('override')); | |
| 316 | |
| 317 return new MirrorUsage( | |
| 318 cachedValues[fields[symbolsField]], | |
| 319 cachedValues[fields[targetsField]], | |
| 320 cachedValues[fields[metaTargetsField]], | |
| 321 cachedValues[fields[overrideField]]); | |
| 322 } | |
| 261 } | 323 } |
| 262 | 324 |
| 263 class MirrorUsage { | 325 class MirrorUsage { |
| 264 final List<String> symbols; | 326 final List<String> symbols; |
| 265 final List<Element> targets; | 327 final List<Element> targets; |
| 266 final List<Element> metaTargets; | 328 final List<Element> metaTargets; |
| 267 final List<Element> override; | 329 final List<Element> override; |
| 268 | 330 |
| 269 MirrorUsage(this.symbols, this.targets, this.metaTargets, this.override); | 331 MirrorUsage(this.symbols, this.targets, this.metaTargets, this.override); |
| 270 | 332 |
| 271 String toString() { | 333 String toString() { |
| 272 return | 334 return |
| 273 'MirrorUsage(' | 335 'MirrorUsage(' |
| 274 'symbols = $symbols, ' | 336 'symbols = $symbols, ' |
| 275 'targets = $targets, ' | 337 'targets = $targets, ' |
| 276 'metaTargets = $metaTargets, ' | 338 'metaTargets = $metaTargets, ' |
| 277 'override = $override' | 339 'override = $override' |
| 278 ')'; | 340 ')'; |
| 279 | 341 |
| 280 } | 342 } |
| 281 } | 343 } |
| 282 | 344 |
| 283 class MirrorUsageBuilder { | 345 class MirrorUsageBuilder { |
| 284 MirrorUsageAnalyzer analyzer; | 346 final MirrorUsageAnalyzer analyzer; |
| 285 LibraryElement enclosingLibrary; | 347 final LibraryElement enclosingLibrary; |
| 348 final Spannable spannable; | |
| 349 final Constant constant; | |
| 350 final Map<Constant, Node> constantToNodeMap; | |
| 286 | 351 |
| 287 MirrorUsageBuilder(this.analyzer, this.enclosingLibrary); | 352 MirrorUsageBuilder( |
| 353 this.analyzer, | |
| 354 this.enclosingLibrary, | |
| 355 this.spannable, | |
| 356 this.constant, | |
| 357 this.constantToNodeMap); | |
| 288 | 358 |
| 289 Compiler get compiler => analyzer.compiler; | 359 Compiler get compiler => analyzer.compiler; |
| 290 | 360 |
| 291 MirrorUsage build(ConstructedConstant constant) { | 361 List convertConstantToUsageList( |
| 292 Map<Element, Constant> fields = constant.fieldElements; | 362 Constant constant, { bool onlyStrings: false }) { |
| 293 VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember( | |
| 294 const SourceString('symbols')); | |
| 295 VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember( | |
| 296 const SourceString('targets')); | |
| 297 VariableElement metaTargetsField = | |
| 298 compiler.mirrorsUsedClass.lookupLocalMember( | |
| 299 const SourceString('metaTargets')); | |
| 300 VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember( | |
| 301 const SourceString('override')); | |
| 302 List<String> symbols = | |
| 303 convertToListOfStrings( | |
| 304 convertConstantToUsageList(fields[symbolsField])); | |
| 305 List<Element> targets = | |
| 306 resolveUsageList(convertConstantToUsageList(fields[targetsField])); | |
| 307 | |
| 308 List<Element> metaTargets = | |
| 309 resolveUsageList(convertConstantToUsageList(fields[metaTargetsField])); | |
| 310 List<Element> override = | |
| 311 resolveUsageList(convertConstantToUsageList(fields[overrideField])); | |
| 312 return new MirrorUsage(symbols, targets, metaTargets, override); | |
| 313 } | |
| 314 | |
| 315 List convertConstantToUsageList(Constant constant) { | |
| 316 if (constant.isNull()) { | 363 if (constant.isNull()) { |
| 317 return null; | 364 return null; |
| 318 } else if (constant.isList()) { | 365 } else if (constant.isList()) { |
| 319 ListConstant list = constant; | 366 ListConstant list = constant; |
| 320 List result = []; | 367 List result = []; |
| 321 for (Constant entry in list.entries) { | 368 for (Constant entry in list.entries) { |
| 322 if (entry.isString()) { | 369 if (entry.isString()) { |
| 323 StringConstant string = entry; | 370 StringConstant string = entry; |
| 324 result.add(string.value.slowToString()); | 371 result.add(string.value.slowToString()); |
| 325 } else if (entry.isType()) { | 372 } else if (!onlyStrings && entry.isType()) { |
| 326 TypeConstant type = entry; | 373 TypeConstant type = entry; |
| 327 result.add(type.representedType); | 374 result.add(type.representedType); |
| 328 } else { | 375 } else { |
| 329 throw new BadMirrorsUsedAnnotation( | 376 Spannable node = positionOf(entry); |
| 330 'Expected a string or type, but got "$entry".'); | 377 MessageKind kind = onlyStrings |
| 378 ? MessageKind.MIRRORS_EXPECTED_STRING | |
| 379 : MessageKind.MIRRORS_EXPECTED_STRING_OR_TYPE; | |
| 380 compiler.reportHint( | |
| 381 node, | |
| 382 kind, {'name': node, 'type': apiTypeOf(entry)}); | |
| 331 } | 383 } |
| 332 } | 384 } |
| 333 return result; | 385 return result; |
| 334 } else if (constant.isType()) { | 386 } else if (!onlyStrings && constant.isType()) { |
| 335 TypeConstant type = constant; | 387 TypeConstant type = constant; |
| 336 return [type.representedType]; | 388 return [type.representedType]; |
| 337 } else if (constant.isString()) { | 389 } else if (constant.isString()) { |
| 338 StringConstant string = constant; | 390 StringConstant string = constant; |
| 339 return | 391 return |
| 340 string.value.slowToString().split(',').map((e) => e.trim()).toList(); | 392 string.value.slowToString().split(',').map((e) => e.trim()).toList(); |
| 341 } else { | 393 } else { |
| 342 throw new BadMirrorsUsedAnnotation( | 394 Spannable node = positionOf(constant); |
| 343 'Expected a string or a list of string, but got "$constant".'); | 395 MessageKind kind = onlyStrings |
| 396 ? MessageKind.MIRRORS_EXPECTED_STRING_OR_LIST | |
| 397 : MessageKind.MIRRORS_EXPECTED_STRING_TYPE_OR_LIST; | |
| 398 compiler.reportHint( | |
| 399 node, | |
| 400 kind, {'name': node, 'type': apiTypeOf(constant)}); | |
| 401 return null; | |
| 344 } | 402 } |
| 345 } | 403 } |
| 346 | 404 |
| 405 DartType apiTypeOf(Constant constant) { | |
| 406 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.
| |
| 407 LibraryElement library = type.element.getLibrary(); | |
| 408 if (type.kind == TypeKind.INTERFACE && library.isInternalLibrary) { | |
| 409 InterfaceType interface = type; | |
| 410 ClassElement cls = type.element; | |
| 411 for (DartType supertype in cls.ensureResolved(compiler).allSupertypes) { | |
| 412 if (supertype.kind == TypeKind.INTERFACE | |
| 413 && !supertype.element.getLibrary().isInternalLibrary) { | |
| 414 return interface.asInstanceOf(supertype.element); | |
| 415 } | |
| 416 } | |
| 417 } | |
| 418 return type; | |
| 419 } | |
| 420 | |
| 347 List<String> convertToListOfStrings(List list) { | 421 List<String> convertToListOfStrings(List list) { |
| 348 if (list == null) return null; | 422 if (list == null) return null; |
| 349 List<String> result = new List<String>(list.length); | 423 List<String> result = new List<String>(list.length); |
| 350 int count = 0; | 424 int count = 0; |
| 351 for (var entry in list) { | 425 for (var entry in list) { |
| 352 if (entry is! String) { | 426 assert(invariant(spannable, entry is String)); |
| 353 throw new BadMirrorsUsedAnnotation( | |
| 354 'Expected a string, but got "$entry"'); | |
| 355 } | |
| 356 result[count++] = entry; | 427 result[count++] = entry; |
| 357 } | 428 } |
| 358 return result; | 429 return result; |
| 359 } | 430 } |
| 360 | 431 |
| 361 List<Element> resolveUsageList(List list) { | 432 List<Element> resolveUsageList(List list) { |
| 362 if (list == null) return null; | 433 if (list == null) return null; |
| 363 if (list.length == 1 && list[0] == '*') { | 434 if (list.length == 1 && list[0] == '*') { |
| 364 return analyzer.wildcard; | 435 return analyzer.wildcard; |
| 365 } | 436 } |
| 366 List<Element> result = <Element>[]; | 437 List<Element> result = <Element>[]; |
| 367 for (var entry in list) { | 438 for (var entry in list) { |
| 368 if (entry is DartType) { | 439 if (entry is DartType) { |
| 369 DartType type = entry; | 440 DartType type = entry; |
| 370 result.add(type.element); | 441 result.add(type.element); |
| 371 } else { | 442 } else { |
| 372 String string = entry; | 443 String string = entry; |
| 444 LibraryElement libraryCandiate; | |
| 445 String libraryNameCandiate; | |
| 373 for (LibraryElement l in compiler.libraries.values) { | 446 for (LibraryElement l in compiler.libraries.values) { |
| 374 if (l.hasLibraryName()) { | 447 if (l.hasLibraryName()) { |
| 375 String libraryName = l.getLibraryOrScriptName(); | 448 String libraryName = l.getLibraryOrScriptName(); |
| 376 if (string == libraryName || string.startsWith('$libraryName.')) { | 449 if (string == libraryName) { |
| 377 result.add(l); | 450 // Found an exact match. |
| 451 libraryCandiate = l; | |
| 452 libraryNameCandiate = libraryName; | |
| 378 break; | 453 break; |
| 454 } else if (string.startsWith('$libraryName.')) { | |
| 455 if (libraryNameCandiate == null | |
| 456 || libraryNameCandiate.length < libraryName.length) { | |
| 457 // Found a better candiate | |
| 458 libraryCandiate = l; | |
| 459 libraryNameCandiate = libraryName; | |
| 460 } | |
| 379 } | 461 } |
| 380 } | 462 } |
| 381 } | 463 } |
| 464 Element e; | |
| 465 if (libraryNameCandiate == string) { | |
| 466 e = libraryCandiate; | |
| 467 } else if (libraryNameCandiate != null) { | |
| 468 e = resolveLocalExpression( | |
| 469 libraryCandiate, | |
| 470 string.substring(libraryNameCandiate.length + 1).split('.')); | |
| 471 } else { | |
| 472 e = resolveExpression(string); | |
| 473 } | |
| 474 if (e != null) result.add(e); | |
| 382 } | 475 } |
| 383 } | 476 } |
| 384 return result; | 477 return result; |
| 385 } | 478 } |
| 479 | |
| 480 /// Resolve [expression] in [enclosingLibrary]'s import scope. | |
| 481 Element resolveExpression(String expression) { | |
| 482 List<String> identifiers = expression.split('.'); | |
| 483 Element element = enclosingLibrary.find(new SourceString(identifiers[0])); | |
| 484 if (element == null) { | |
| 485 compiler.reportHint( | |
| 486 spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_CURRENT_LIBRARY, | |
| 487 {'name': identifiers[0]}); | |
| 488 return null; | |
| 489 } else { | |
| 490 if (identifiers.length == 1) return element; | |
| 491 return resolveLocalExpression(element, identifiers.sublist(1)); | |
| 492 } | |
| 493 } | |
| 494 | |
| 495 /// Resolve [identifiers] in [element]'s local members. | |
| 496 Element resolveLocalExpression(Element element, List<String> identifiers) { | |
| 497 Element current = element; | |
| 498 for (String identifier in identifiers) { | |
| 499 Element e = findLocalMemberIn(current, new SourceString(identifier)); | |
| 500 if (e == null) { | |
| 501 if (current.isLibrary()) { | |
| 502 LibraryElement library = current; | |
| 503 compiler.reportHint( | |
| 504 spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_LIBRARY, | |
| 505 {'name': identifiers[0], | |
| 506 'library': library.getLibraryOrScriptName()}); | |
| 507 } else { | |
| 508 compiler.reportHint( | |
| 509 spannable, MessageKind.MIRRORS_CANNOT_FIND_IN_ELEMENT, | |
| 510 {'name': identifier, 'element': current.name}); | |
| 511 } | |
| 512 return current; | |
| 513 } | |
| 514 current = e; | |
| 515 } | |
| 516 return current; | |
| 517 } | |
| 518 | |
| 519 Element findLocalMemberIn(Element element, SourceString name) { | |
| 520 if (element is ScopeContainerElement) { | |
| 521 ScopeContainerElement scope = element as dynamic; | |
| 522 return scope.localLookup(name); | |
| 523 } | |
| 524 return null; | |
| 525 } | |
| 526 | |
| 527 Spannable positionOf(Constant constant) { | |
| 528 Node node = constantToNodeMap[constant]; | |
| 529 if (node != null) { | |
| 530 // TODO(ahe): Returning [node] here leads to confusing error messages. | |
| 531 // For example, consider: | |
| 532 // @MirrorsUsed(targets: fisk) | |
| 533 // import 'dart:mirrors'; | |
| 534 // | |
| 535 // const fisk = const [main]; | |
| 536 // | |
| 537 // main() {} | |
| 538 // | |
| 539 // The message is: | |
| 540 // example.dart:1:23: Hint: Can't use 'fisk' here because ... | |
| 541 // Did you forget to add quotes? | |
| 542 // @MirrorsUsed(targets: fisk) | |
| 543 // ^^^^ | |
| 544 // | |
| 545 // Instead of saying 'fisk' should pretty print the problematic constant | |
| 546 // value. | |
| 547 return node; | |
| 548 } | |
| 549 return spannable; | |
| 550 } | |
| 386 } | 551 } |
| 387 | |
| 388 class BadMirrorsUsedAnnotation { | |
| 389 final String message; | |
| 390 BadMirrorsUsedAnnotation(this.message); | |
| 391 } | |
| OLD | NEW |