| OLD | NEW |
| (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 } | |
| OLD | NEW |