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; |
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 |
| 104 /// Collect @MirrorsUsed annotations in all libraries. Called by the |
| 105 /// compiler after all libraries are loaded, but before resolution. |
88 void analyzeUsage(LibraryElement mainApp) { | 106 void analyzeUsage(LibraryElement mainApp) { |
89 if (compiler.mirrorsLibrary == null) return; | 107 if (compiler.mirrorsLibrary == null) return; |
90 MirrorUsageAnalyzer analyzer = new MirrorUsageAnalyzer(compiler, this); | |
91 measure(analyzer.run); | 108 measure(analyzer.run); |
92 List<String> symbols = analyzer.mergedMirrorUsage.symbols; | 109 List<String> symbols = analyzer.mergedMirrorUsage.symbols; |
93 List<Element> targets = analyzer.mergedMirrorUsage.targets; | 110 List<Element> targets = analyzer.mergedMirrorUsage.targets; |
94 List<Element> metaTargets = analyzer.mergedMirrorUsage.metaTargets; | 111 List<Element> metaTargets = analyzer.mergedMirrorUsage.metaTargets; |
95 compiler.backend.registerMirrorUsage( | 112 compiler.backend.registerMirrorUsage( |
96 symbols == null ? null : new Set<String>.from(symbols), | 113 symbols == null ? null : new Set<String>.from(symbols), |
97 targets == null ? null : new Set<Element>.from(targets), | 114 targets == null ? null : new Set<Element>.from(targets), |
98 metaTargets == null ? null : new Set<Element>.from(metaTargets)); | 115 metaTargets == null ? null : new Set<Element>.from(metaTargets)); |
99 librariesWithUsage = analyzer.librariesWithUsage; | 116 librariesWithUsage = analyzer.librariesWithUsage; |
100 } | 117 } |
101 | 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. |
102 bool hasMirrorUsage(Element element) { | 122 bool hasMirrorUsage(Element element) { |
103 return librariesWithUsage != null | 123 return librariesWithUsage != null |
104 && librariesWithUsage.contains(element.getLibrary()); | 124 && librariesWithUsage.contains(element.getLibrary()); |
105 } | 125 } |
| 126 |
| 127 /// Call-back from the resolver to analyze MirorsUsed annotations. The result |
| 128 /// is stored in [analyzer] and later used to compute |
| 129 /// [:analyzer.mergedMirrorUsage:]. |
| 130 void validate(NewExpression node, TreeElements mapping) { |
| 131 for (Node argument in node.send.arguments) { |
| 132 NamedArgument named = argument.asNamedArgument(); |
| 133 if (named == null) continue; |
| 134 Constant value = compiler.metadataHandler.compileNodeWithDefinitions( |
| 135 named.expression, mapping, isConst: true); |
| 136 |
| 137 ConstantMapper mapper = |
| 138 new ConstantMapper(compiler.metadataHandler, mapping, compiler); |
| 139 named.expression.accept(mapper); |
| 140 |
| 141 MirrorUsageBuilder builder = |
| 142 new MirrorUsageBuilder( |
| 143 analyzer, mapping.currentElement.getLibrary(), named.expression, |
| 144 value, mapper.constantToNodeMap); |
| 145 |
| 146 if (named.name.source == const SourceString('symbols')) { |
| 147 analyzer.cachedValues[value] = |
| 148 builder.convertToListOfStrings( |
| 149 builder.convertConstantToUsageList(value, onlyStrings: true)); |
| 150 } else if (named.name.source == const SourceString('targets')) { |
| 151 analyzer.cachedValues[value] = |
| 152 builder.resolveUsageList(builder.convertConstantToUsageList(value)); |
| 153 } else if (named.name.source == const SourceString('metaTargets')) { |
| 154 analyzer.cachedValues[value] = |
| 155 builder.resolveUsageList(builder.convertConstantToUsageList(value)); |
| 156 } else if (named.name.source == const SourceString('override')) { |
| 157 analyzer.cachedValues[value] = |
| 158 builder.resolveUsageList(builder.convertConstantToUsageList(value)); |
| 159 } |
| 160 } |
| 161 } |
106 } | 162 } |
107 | 163 |
108 class MirrorUsageAnalyzer { | 164 class MirrorUsageAnalyzer { |
109 final Compiler compiler; | 165 final Compiler compiler; |
110 final MirrorUsageAnalyzerTask task; | 166 final MirrorUsageAnalyzerTask task; |
111 final List<LibraryElement> wildcard; | 167 List<LibraryElement> wildcard; |
112 final Set<LibraryElement> librariesWithUsage; | 168 final Set<LibraryElement> librariesWithUsage; |
113 final Set<LibraryElement> librariesWithoutUsage; | 169 final Map<Constant, List> cachedValues; |
114 MirrorUsage mergedMirrorUsage; | 170 MirrorUsage mergedMirrorUsage; |
115 | 171 |
116 MirrorUsageAnalyzer(Compiler compiler, this.task) | 172 MirrorUsageAnalyzer(Compiler compiler, this.task) |
117 : compiler = compiler, | 173 : compiler = compiler, |
118 wildcard = compiler.libraries.values.toList(), | |
119 librariesWithUsage = new Set<LibraryElement>(), | 174 librariesWithUsage = new Set<LibraryElement>(), |
120 librariesWithoutUsage = new Set<LibraryElement>(); | 175 cachedValues = new Map<Constant, List>(); |
121 | 176 |
| 177 /// Collect and merge all @MirrorsUsed annotations. As a side-effect, also |
| 178 /// compute which libraries have the annotation (which is used by |
| 179 /// [MirrorUsageAnalyzerTask.hasMirrorUsage]). |
122 void run() { | 180 void run() { |
| 181 wildcard = compiler.libraries.values.toList(); |
123 Map<LibraryElement, List<MirrorUsage>> usageMap = | 182 Map<LibraryElement, List<MirrorUsage>> usageMap = |
124 collectMirrorsUsedAnnotation(); | 183 collectMirrorsUsedAnnotation(); |
125 propagateOverrides(usageMap); | 184 propagateOverrides(usageMap); |
126 librariesWithoutUsage.removeAll(usageMap.keys); | 185 Set<LibraryElement> librariesWithoutUsage = new Set<LibraryElement>(); |
| 186 usageMap.forEach((LibraryElement library, List<MirrorUsage> usage) { |
| 187 if (usage.isEmpty) librariesWithoutUsage.add(library); |
| 188 }); |
127 if (librariesWithoutUsage.isEmpty) { | 189 if (librariesWithoutUsage.isEmpty) { |
128 mergedMirrorUsage = mergeUsages(usageMap); | 190 mergedMirrorUsage = mergeUsages(usageMap); |
129 } else { | 191 } else { |
130 mergedMirrorUsage = new MirrorUsage(null, wildcard, null, null); | 192 mergedMirrorUsage = new MirrorUsage(null, wildcard, null, null); |
131 } | 193 } |
132 } | 194 } |
133 | 195 |
| 196 /// Collect all @MirrorsUsed from all libraries and represent them as |
| 197 /// [MirrorUsage]. |
134 Map<LibraryElement, List<MirrorUsage>> collectMirrorsUsedAnnotation() { | 198 Map<LibraryElement, List<MirrorUsage>> collectMirrorsUsedAnnotation() { |
135 Map<LibraryElement, List<MirrorUsage>> result = | 199 Map<LibraryElement, List<MirrorUsage>> result = |
136 new Map<LibraryElement, List<MirrorUsage>>(); | 200 new Map<LibraryElement, List<MirrorUsage>>(); |
137 for (LibraryElement library in compiler.libraries.values) { | 201 for (LibraryElement library in compiler.libraries.values) { |
138 if (library.isInternalLibrary) continue; | 202 if (library.isInternalLibrary) continue; |
139 librariesWithoutUsage.add(library); | |
140 for (LibraryTag tag in library.tags) { | 203 for (LibraryTag tag in library.tags) { |
141 Import importTag = tag.asImport(); | 204 Import importTag = tag.asImport(); |
142 if (importTag == null) continue; | 205 if (importTag == null) continue; |
143 compiler.withCurrentElement(library, () { | 206 compiler.withCurrentElement(library, () { |
144 List<MirrorUsage> usages = | 207 List<MirrorUsage> usages = |
145 mirrorsUsedOnLibraryTag(library, importTag); | 208 mirrorsUsedOnLibraryTag(library, importTag); |
146 if (usages != null) { | 209 if (usages != null) { |
147 List<MirrorUsage> existing = result[library]; | 210 List<MirrorUsage> existing = result[library]; |
148 if (existing != null) { | 211 if (existing != null) { |
149 existing.addAll(usages); | 212 existing.addAll(usages); |
150 } else { | 213 } else { |
151 result[library] = usages; | 214 result[library] = usages; |
152 } | 215 } |
153 } | 216 } |
154 }); | 217 }); |
155 } | 218 } |
156 } | 219 } |
157 return result; | 220 return result; |
158 } | 221 } |
159 | 222 |
| 223 /// Apply [MirrorUsage] with 'override' to libraries they override. |
160 void propagateOverrides(Map<LibraryElement, List<MirrorUsage>> usageMap) { | 224 void propagateOverrides(Map<LibraryElement, List<MirrorUsage>> usageMap) { |
161 Map<LibraryElement, List<MirrorUsage>> propagatedOverrides = | 225 Map<LibraryElement, List<MirrorUsage>> propagatedOverrides = |
162 new Map<LibraryElement, List<MirrorUsage>>(); | 226 new Map<LibraryElement, List<MirrorUsage>>(); |
163 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) { | 227 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) { |
164 for (MirrorUsage usage in usages) { | 228 for (MirrorUsage usage in usages) { |
165 List<Element> override = usage.override; | 229 List<Element> override = usage.override; |
166 if (override == null) continue; | 230 if (override == null) continue; |
167 if (override == wildcard) { | 231 if (override == wildcard) { |
168 for (LibraryElement overridden in wildcard) { | 232 for (LibraryElement overridden in wildcard) { |
169 if (overridden != library) { | 233 if (overridden != library) { |
(...skipping 12 matching lines...) Expand all Loading... |
182 } | 246 } |
183 }); | 247 }); |
184 propagatedOverrides.forEach((LibraryElement overridden, | 248 propagatedOverrides.forEach((LibraryElement overridden, |
185 List<MirrorUsage> overriddenUsages) { | 249 List<MirrorUsage> overriddenUsages) { |
186 List<MirrorUsage> usages = | 250 List<MirrorUsage> usages = |
187 usageMap.putIfAbsent(overridden, () => <MirrorUsage>[]); | 251 usageMap.putIfAbsent(overridden, () => <MirrorUsage>[]); |
188 usages.addAll(overriddenUsages); | 252 usages.addAll(overriddenUsages); |
189 }); | 253 }); |
190 } | 254 } |
191 | 255 |
| 256 /// Find @MirrorsUsed annotations on the given import [tag] in [library]. The |
| 257 /// annotations are represented as [MirrorUsage]. |
192 List<MirrorUsage> mirrorsUsedOnLibraryTag(LibraryElement library, | 258 List<MirrorUsage> mirrorsUsedOnLibraryTag(LibraryElement library, |
193 Import tag) { | 259 Import tag) { |
194 LibraryElement importedLibrary = library.getLibraryFromTag(tag); | 260 LibraryElement importedLibrary = library.getLibraryFromTag(tag); |
195 if (importedLibrary != compiler.mirrorsLibrary) { | 261 if (importedLibrary != compiler.mirrorsLibrary) { |
196 return null; | 262 return null; |
197 } | 263 } |
198 List<MirrorUsage> result = <MirrorUsage>[]; | 264 List<MirrorUsage> result = <MirrorUsage>[]; |
199 for (MetadataAnnotation metadata in tag.metadata) { | 265 for (MetadataAnnotation metadata in tag.metadata) { |
200 metadata.ensureResolved(compiler); | 266 metadata.ensureResolved(compiler); |
201 Element element = metadata.value.computeType(compiler).element; | 267 Element element = metadata.value.computeType(compiler).element; |
202 if (element == compiler.mirrorsUsedClass) { | 268 if (element == compiler.mirrorsUsedClass) { |
203 try { | 269 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 } | 270 } |
212 } | 271 } |
213 return result; | 272 return result; |
214 } | 273 } |
215 | 274 |
| 275 /// Merge all [MirrorUsage] instances accross all libraries. |
216 MirrorUsage mergeUsages(Map<LibraryElement, List<MirrorUsage>> usageMap) { | 276 MirrorUsage mergeUsages(Map<LibraryElement, List<MirrorUsage>> usageMap) { |
217 Set<MirrorUsage> usagesToMerge = new Set<MirrorUsage>(); | 277 Set<MirrorUsage> usagesToMerge = new Set<MirrorUsage>(); |
218 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) { | 278 usageMap.forEach((LibraryElement library, List<MirrorUsage> usages) { |
219 librariesWithUsage.add(library); | 279 librariesWithUsage.add(library); |
220 usagesToMerge.addAll(usages); | 280 usagesToMerge.addAll(usages); |
221 }); | 281 }); |
222 if (usagesToMerge.isEmpty) { | 282 if (usagesToMerge.isEmpty) { |
223 return new MirrorUsage(null, wildcard, null, null); | 283 return new MirrorUsage(null, wildcard, null, null); |
224 } else { | 284 } else { |
225 MirrorUsage result = new MirrorUsage(null, null, null, null); | 285 MirrorUsage result = new MirrorUsage(null, null, null, null); |
226 for (MirrorUsage usage in usagesToMerge) { | 286 for (MirrorUsage usage in usagesToMerge) { |
227 result = merge(result, usage); | 287 result = merge(result, usage); |
228 } | 288 } |
229 return result; | 289 return result; |
230 } | 290 } |
231 } | 291 } |
232 | 292 |
| 293 /// Merge [a] with [b]. The resulting [MirrorUsage] simply has the symbols, |
| 294 /// targets, and metaTargets of [a] and [b] concatenated. 'override' is |
| 295 /// ignored. |
233 MirrorUsage merge(MirrorUsage a, MirrorUsage b) { | 296 MirrorUsage merge(MirrorUsage a, MirrorUsage b) { |
| 297 // TOOO(ahe): Should be an instance method on MirrorUsage. |
234 if (a.symbols == null && a.targets == null && a.metaTargets == null) { | 298 if (a.symbols == null && a.targets == null && a.metaTargets == null) { |
235 return b; | 299 return b; |
236 } else if ( | 300 } else if ( |
237 b.symbols == null && b.targets == null && b.metaTargets == null) { | 301 b.symbols == null && b.targets == null && b.metaTargets == null) { |
238 return a; | 302 return a; |
239 } | 303 } |
240 // TODO(ahe): Test the following cases. | 304 // TODO(ahe): Test the following cases. |
241 List<String> symbols = a.symbols; | 305 List<String> symbols = a.symbols; |
242 if (symbols == null) { | 306 if (symbols == null) { |
243 symbols = b.symbols; | 307 symbols = b.symbols; |
244 } else if (b.symbols != null) { | 308 } else if (b.symbols != null) { |
245 symbols.addAll(b.symbols); | 309 symbols.addAll(b.symbols); |
246 } | 310 } |
247 List<Element> targets = a.targets; | 311 List<Element> targets = a.targets; |
248 if (targets == null) { | 312 if (targets == null) { |
249 targets = b.targets; | 313 targets = b.targets; |
250 } else if (targets != wildcard && b.targets != null) { | 314 } else if (targets != wildcard && b.targets != null) { |
251 targets.addAll(b.targets); | 315 targets.addAll(b.targets); |
252 } | 316 } |
253 List<Element> metaTargets = a.metaTargets; | 317 List<Element> metaTargets = a.metaTargets; |
254 if (metaTargets == null) { | 318 if (metaTargets == null) { |
255 metaTargets = b.metaTargets; | 319 metaTargets = b.metaTargets; |
256 } else if (metaTargets != wildcard && b.metaTargets != null) { | 320 } else if (metaTargets != wildcard && b.metaTargets != null) { |
257 metaTargets.addAll(b.metaTargets); | 321 metaTargets.addAll(b.metaTargets); |
258 } | 322 } |
259 return new MirrorUsage(symbols, targets, metaTargets, null); | 323 return new MirrorUsage(symbols, targets, metaTargets, null); |
260 } | 324 } |
| 325 |
| 326 /// Convert a [constant] to an instance of [MirrorUsage] using information |
| 327 /// that was resolved during [MirrorUsageAnalyzerTask.validate]. |
| 328 MirrorUsage buildUsage(ConstructedConstant constant) { |
| 329 Map<Element, Constant> fields = constant.fieldElements; |
| 330 VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember( |
| 331 const SourceString('symbols')); |
| 332 VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember( |
| 333 const SourceString('targets')); |
| 334 VariableElement metaTargetsField = |
| 335 compiler.mirrorsUsedClass.lookupLocalMember( |
| 336 const SourceString('metaTargets')); |
| 337 VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember( |
| 338 const SourceString('override')); |
| 339 |
| 340 return new MirrorUsage( |
| 341 cachedValues[fields[symbolsField]], |
| 342 cachedValues[fields[targetsField]], |
| 343 cachedValues[fields[metaTargetsField]], |
| 344 cachedValues[fields[overrideField]]); |
| 345 } |
261 } | 346 } |
262 | 347 |
| 348 /// Used to represent a resolved MirrorsUsed constant. |
263 class MirrorUsage { | 349 class MirrorUsage { |
264 final List<String> symbols; | 350 final List<String> symbols; |
265 final List<Element> targets; | 351 final List<Element> targets; |
266 final List<Element> metaTargets; | 352 final List<Element> metaTargets; |
267 final List<Element> override; | 353 final List<Element> override; |
268 | 354 |
269 MirrorUsage(this.symbols, this.targets, this.metaTargets, this.override); | 355 MirrorUsage(this.symbols, this.targets, this.metaTargets, this.override); |
270 | 356 |
271 String toString() { | 357 String toString() { |
272 return | 358 return |
273 'MirrorUsage(' | 359 'MirrorUsage(' |
274 'symbols = $symbols, ' | 360 'symbols = $symbols, ' |
275 'targets = $targets, ' | 361 'targets = $targets, ' |
276 'metaTargets = $metaTargets, ' | 362 'metaTargets = $metaTargets, ' |
277 'override = $override' | 363 'override = $override' |
278 ')'; | 364 ')'; |
279 | 365 |
280 } | 366 } |
281 } | 367 } |
282 | 368 |
283 class MirrorUsageBuilder { | 369 class MirrorUsageBuilder { |
284 MirrorUsageAnalyzer analyzer; | 370 final MirrorUsageAnalyzer analyzer; |
285 LibraryElement enclosingLibrary; | 371 final LibraryElement enclosingLibrary; |
| 372 final Spannable spannable; |
| 373 final Constant constant; |
| 374 final Map<Constant, Node> constantToNodeMap; |
286 | 375 |
287 MirrorUsageBuilder(this.analyzer, this.enclosingLibrary); | 376 MirrorUsageBuilder( |
| 377 this.analyzer, |
| 378 this.enclosingLibrary, |
| 379 this.spannable, |
| 380 this.constant, |
| 381 this.constantToNodeMap); |
288 | 382 |
289 Compiler get compiler => analyzer.compiler; | 383 Compiler get compiler => analyzer.compiler; |
290 | 384 |
291 MirrorUsage build(ConstructedConstant constant) { | 385 /// Convert a constant to a list of [String] and [Type] values. If the |
292 Map<Element, Constant> fields = constant.fieldElements; | 386 /// constant is a single [String], it is assumed to be a comma-separated list |
293 VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember( | 387 /// of qualified names. If the constant is a [Type] t, the result is [:[t]:]. |
294 const SourceString('symbols')); | 388 /// Otherwise, the constant is assumed to represent a list of strings (each a |
295 VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember( | 389 /// qualified name) and types, and such a list is constructed. |
296 const SourceString('targets')); | 390 List convertConstantToUsageList( |
297 VariableElement metaTargetsField = | 391 Constant constant, { bool onlyStrings: false }) { |
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()) { | 392 if (constant.isNull()) { |
317 return null; | 393 return null; |
318 } else if (constant.isList()) { | 394 } else if (constant.isList()) { |
319 ListConstant list = constant; | 395 ListConstant list = constant; |
320 List result = []; | 396 List result = []; |
321 for (Constant entry in list.entries) { | 397 for (Constant entry in list.entries) { |
322 if (entry.isString()) { | 398 if (entry.isString()) { |
323 StringConstant string = entry; | 399 StringConstant string = entry; |
324 result.add(string.value.slowToString()); | 400 result.add(string.value.slowToString()); |
325 } else if (entry.isType()) { | 401 } else if (!onlyStrings && entry.isType()) { |
326 TypeConstant type = entry; | 402 TypeConstant type = entry; |
327 result.add(type.representedType); | 403 result.add(type.representedType); |
328 } else { | 404 } else { |
329 throw new BadMirrorsUsedAnnotation( | 405 Spannable node = positionOf(entry); |
330 'Expected a string or type, but got "$entry".'); | 406 MessageKind kind = onlyStrings |
| 407 ? MessageKind.MIRRORS_EXPECTED_STRING |
| 408 : MessageKind.MIRRORS_EXPECTED_STRING_OR_TYPE; |
| 409 compiler.reportHint( |
| 410 node, |
| 411 kind, {'name': node, 'type': apiTypeOf(entry)}); |
331 } | 412 } |
332 } | 413 } |
333 return result; | 414 return result; |
334 } else if (constant.isType()) { | 415 } else if (!onlyStrings && constant.isType()) { |
335 TypeConstant type = constant; | 416 TypeConstant type = constant; |
336 return [type.representedType]; | 417 return [type.representedType]; |
337 } else if (constant.isString()) { | 418 } else if (constant.isString()) { |
338 StringConstant string = constant; | 419 StringConstant string = constant; |
339 return | 420 return |
340 string.value.slowToString().split(',').map((e) => e.trim()).toList(); | 421 string.value.slowToString().split(',').map((e) => e.trim()).toList(); |
341 } else { | 422 } else { |
342 throw new BadMirrorsUsedAnnotation( | 423 Spannable node = positionOf(constant); |
343 'Expected a string or a list of string, but got "$constant".'); | 424 MessageKind kind = onlyStrings |
| 425 ? MessageKind.MIRRORS_EXPECTED_STRING_OR_LIST |
| 426 : MessageKind.MIRRORS_EXPECTED_STRING_TYPE_OR_LIST; |
| 427 compiler.reportHint( |
| 428 node, |
| 429 kind, {'name': node, 'type': apiTypeOf(constant)}); |
| 430 return null; |
344 } | 431 } |
345 } | 432 } |
346 | 433 |
| 434 /// Find the first non-implementation interface of constant. |
| 435 DartType apiTypeOf(Constant constant) { |
| 436 DartType type = constant.computeType(compiler); |
| 437 LibraryElement library = type.element.getLibrary(); |
| 438 if (type.kind == TypeKind.INTERFACE && library.isInternalLibrary) { |
| 439 InterfaceType interface = type; |
| 440 ClassElement cls = type.element; |
| 441 for (DartType supertype in cls.ensureResolved(compiler).allSupertypes) { |
| 442 if (supertype.kind == TypeKind.INTERFACE |
| 443 && !supertype.element.getLibrary().isInternalLibrary) { |
| 444 return interface.asInstanceOf(supertype.element); |
| 445 } |
| 446 } |
| 447 } |
| 448 return type; |
| 449 } |
| 450 |
| 451 /// Ensure a list contains only strings. |
347 List<String> convertToListOfStrings(List list) { | 452 List<String> convertToListOfStrings(List list) { |
348 if (list == null) return null; | 453 if (list == null) return null; |
349 List<String> result = new List<String>(list.length); | 454 List<String> result = new List<String>(list.length); |
350 int count = 0; | 455 int count = 0; |
351 for (var entry in list) { | 456 for (var entry in list) { |
352 if (entry is! String) { | 457 assert(invariant(spannable, entry is String)); |
353 throw new BadMirrorsUsedAnnotation( | |
354 'Expected a string, but got "$entry"'); | |
355 } | |
356 result[count++] = entry; | 458 result[count++] = entry; |
357 } | 459 } |
358 return result; | 460 return result; |
359 } | 461 } |
360 | 462 |
| 463 /// Convert a list of strings and types to a list of elements. Types are |
| 464 /// converted to their corresponding element, and strings are resolved as |
| 465 /// follows: |
| 466 /// |
| 467 /// First find the longest library name that is a prefix of the string, if |
| 468 /// there are none, resolve using [resolveExpression]. Otherwise, resolve the |
| 469 /// rest of the string using [resolveLocalExpression]. |
361 List<Element> resolveUsageList(List list) { | 470 List<Element> resolveUsageList(List list) { |
362 if (list == null) return null; | 471 if (list == null) return null; |
363 if (list.length == 1 && list[0] == '*') { | 472 if (list.length == 1 && list[0] == '*') { |
364 return analyzer.wildcard; | 473 return analyzer.wildcard; |
365 } | 474 } |
366 List<Element> result = <Element>[]; | 475 List<Element> result = <Element>[]; |
367 for (var entry in list) { | 476 for (var entry in list) { |
368 if (entry is DartType) { | 477 if (entry is DartType) { |
369 DartType type = entry; | 478 DartType type = entry; |
370 result.add(type.element); | 479 result.add(type.element); |
371 } else { | 480 } else { |
372 String string = entry; | 481 String string = entry; |
| 482 LibraryElement libraryCandiate; |
| 483 String libraryNameCandiate; |
373 for (LibraryElement l in compiler.libraries.values) { | 484 for (LibraryElement l in compiler.libraries.values) { |
374 if (l.hasLibraryName()) { | 485 if (l.hasLibraryName()) { |
375 String libraryName = l.getLibraryOrScriptName(); | 486 String libraryName = l.getLibraryOrScriptName(); |
376 if (string == libraryName || string.startsWith('$libraryName.')) { | 487 if (string == libraryName) { |
377 result.add(l); | 488 // Found an exact match. |
| 489 libraryCandiate = l; |
| 490 libraryNameCandiate = libraryName; |
378 break; | 491 break; |
| 492 } else if (string.startsWith('$libraryName.')) { |
| 493 if (libraryNameCandiate == null |
| 494 || libraryNameCandiate.length < libraryName.length) { |
| 495 // Found a better candiate |
| 496 libraryCandiate = l; |
| 497 libraryNameCandiate = libraryName; |
| 498 } |
379 } | 499 } |
380 } | 500 } |
381 } | 501 } |
| 502 Element e; |
| 503 if (libraryNameCandiate == string) { |
| 504 e = libraryCandiate; |
| 505 } else if (libraryNameCandiate != null) { |
| 506 e = resolveLocalExpression( |
| 507 libraryCandiate, |
| 508 string.substring(libraryNameCandiate.length + 1).split('.')); |
| 509 } else { |
| 510 e = resolveExpression(string); |
| 511 } |
| 512 if (e != null) result.add(e); |
382 } | 513 } |
383 } | 514 } |
384 return result; | 515 return result; |
385 } | 516 } |
| 517 |
| 518 /// Resolve [expression] in [enclosingLibrary]'s import scope. |
| 519 Element resolveExpression(String expression) { |
| 520 List<String> identifiers = expression.split('.'); |
| 521 Element element = enclosingLibrary.find(new SourceString(identifiers[0])); |
| 522 if (element == null) { |
| 523 compiler.reportHint( |
| 524 spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_CURRENT_LIBRARY, |
| 525 {'name': expression}); |
| 526 return null; |
| 527 } else { |
| 528 if (identifiers.length == 1) return element; |
| 529 return resolveLocalExpression(element, identifiers.sublist(1)); |
| 530 } |
| 531 } |
| 532 |
| 533 /// Resolve [identifiers] in [element]'s local members. |
| 534 Element resolveLocalExpression(Element element, List<String> identifiers) { |
| 535 Element current = element; |
| 536 for (String identifier in identifiers) { |
| 537 Element e = findLocalMemberIn(current, new SourceString(identifier)); |
| 538 if (e == null) { |
| 539 if (current.isLibrary()) { |
| 540 LibraryElement library = current; |
| 541 compiler.reportHint( |
| 542 spannable, MessageKind.MIRRORS_CANNOT_RESOLVE_IN_LIBRARY, |
| 543 {'name': identifiers[0], |
| 544 'library': library.getLibraryOrScriptName()}); |
| 545 } else { |
| 546 compiler.reportHint( |
| 547 spannable, MessageKind.MIRRORS_CANNOT_FIND_IN_ELEMENT, |
| 548 {'name': identifier, 'element': current.name}); |
| 549 } |
| 550 return current; |
| 551 } |
| 552 current = e; |
| 553 } |
| 554 return current; |
| 555 } |
| 556 |
| 557 /// Helper method to lookup members in a [ScopeContainerElement]. If |
| 558 /// [element] is not a ScopeContainerElement, return null. |
| 559 Element findLocalMemberIn(Element element, SourceString name) { |
| 560 if (element is ScopeContainerElement) { |
| 561 ScopeContainerElement scope = element; |
| 562 return scope.localLookup(name); |
| 563 } |
| 564 return null; |
| 565 } |
| 566 |
| 567 /// Attempt to find a [Spannable] corresponding to constant. |
| 568 Spannable positionOf(Constant constant) { |
| 569 Node node = constantToNodeMap[constant]; |
| 570 if (node == null) { |
| 571 // TODO(ahe): Returning [spannable] here leads to confusing error |
| 572 // messages. For example, consider: |
| 573 // @MirrorsUsed(targets: fisk) |
| 574 // import 'dart:mirrors'; |
| 575 // |
| 576 // const fisk = const [main]; |
| 577 // |
| 578 // main() {} |
| 579 // |
| 580 // The message is: |
| 581 // example.dart:1:23: Hint: Can't use 'fisk' here because ... |
| 582 // Did you forget to add quotes? |
| 583 // @MirrorsUsed(targets: fisk) |
| 584 // ^^^^ |
| 585 // |
| 586 // Instead of saying 'fisk' should pretty print the problematic constant |
| 587 // value. |
| 588 return spannable; |
| 589 } |
| 590 return node; |
| 591 } |
386 } | 592 } |
387 | |
388 class BadMirrorsUsedAnnotation { | |
389 final String message; | |
390 BadMirrorsUsedAnnotation(this.message); | |
391 } | |
OLD | NEW |