| 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 'constants/expressions.dart'; | 7 import 'constants/expressions.dart'; |
| 8 import 'constants/values.dart' show | 8 import 'constants/values.dart' show |
| 9 Constant, | 9 ConstantValue, |
| 10 ConstructedConstant, | 10 ConstructedConstantValue, |
| 11 ListConstant, | 11 ListConstantValue, |
| 12 StringConstant, | 12 StringConstantValue, |
| 13 TypeConstant; | 13 TypeConstantValue; |
| 14 | 14 |
| 15 import 'dart_types.dart' show | 15 import 'dart_types.dart' show |
| 16 DartType, | 16 DartType, |
| 17 InterfaceType, | 17 InterfaceType, |
| 18 TypeKind; | 18 TypeKind; |
| 19 | 19 |
| 20 import 'dart2jslib.dart' show | 20 import 'dart2jslib.dart' show |
| 21 Compiler, | 21 Compiler, |
| 22 CompilerTask, | 22 CompilerTask, |
| 23 ConstantCompiler, | 23 ConstantCompiler, |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 128 } | 128 } |
| 129 | 129 |
| 130 /// Call-back from the resolver to analyze MirorsUsed annotations. The result | 130 /// Call-back from the resolver to analyze MirorsUsed annotations. The result |
| 131 /// is stored in [analyzer] and later used to compute | 131 /// is stored in [analyzer] and later used to compute |
| 132 /// [:analyzer.mergedMirrorUsage:]. | 132 /// [:analyzer.mergedMirrorUsage:]. |
| 133 void validate(NewExpression node, TreeElements mapping) { | 133 void validate(NewExpression node, TreeElements mapping) { |
| 134 for (Node argument in node.send.arguments) { | 134 for (Node argument in node.send.arguments) { |
| 135 NamedArgument named = argument.asNamedArgument(); | 135 NamedArgument named = argument.asNamedArgument(); |
| 136 if (named == null) continue; | 136 if (named == null) continue; |
| 137 ConstantCompiler constantCompiler = compiler.resolver.constantCompiler; | 137 ConstantCompiler constantCompiler = compiler.resolver.constantCompiler; |
| 138 Constant value = | 138 ConstantValue value = |
| 139 constantCompiler.compileNode(named.expression, mapping).value; | 139 constantCompiler.compileNode(named.expression, mapping).value; |
| 140 | 140 |
| 141 MirrorUsageBuilder builder = | 141 MirrorUsageBuilder builder = |
| 142 new MirrorUsageBuilder( | 142 new MirrorUsageBuilder( |
| 143 analyzer, mapping.analyzedElement.library, named.expression, | 143 analyzer, mapping.analyzedElement.library, named.expression, |
| 144 value, mapping); | 144 value, mapping); |
| 145 | 145 |
| 146 if (named.name.source == 'symbols') { | 146 if (named.name.source == 'symbols') { |
| 147 analyzer.cachedStrings[value] = | 147 analyzer.cachedStrings[value] = |
| 148 builder.convertConstantToUsageList(value, onlyStrings: true); | 148 builder.convertConstantToUsageList(value, onlyStrings: true); |
| 149 } else if (named.name.source == 'targets') { | 149 } else if (named.name.source == 'targets') { |
| 150 analyzer.cachedElements[value] = | 150 analyzer.cachedElements[value] = |
| 151 builder.resolveUsageList(builder.convertConstantToUsageList(value)); | 151 builder.resolveUsageList(builder.convertConstantToUsageList(value)); |
| 152 } else if (named.name.source == 'metaTargets') { | 152 } else if (named.name.source == 'metaTargets') { |
| 153 analyzer.cachedElements[value] = | 153 analyzer.cachedElements[value] = |
| 154 builder.resolveUsageList(builder.convertConstantToUsageList(value)); | 154 builder.resolveUsageList(builder.convertConstantToUsageList(value)); |
| 155 } else if (named.name.source == 'override') { | 155 } else if (named.name.source == 'override') { |
| 156 analyzer.cachedElements[value] = | 156 analyzer.cachedElements[value] = |
| 157 builder.resolveUsageList(builder.convertConstantToUsageList(value)); | 157 builder.resolveUsageList(builder.convertConstantToUsageList(value)); |
| 158 } | 158 } |
| 159 } | 159 } |
| 160 } | 160 } |
| 161 } | 161 } |
| 162 | 162 |
| 163 class MirrorUsageAnalyzer { | 163 class MirrorUsageAnalyzer { |
| 164 final Compiler compiler; | 164 final Compiler compiler; |
| 165 final MirrorUsageAnalyzerTask task; | 165 final MirrorUsageAnalyzerTask task; |
| 166 List<LibraryElement> wildcard; | 166 List<LibraryElement> wildcard; |
| 167 final Set<LibraryElement> librariesWithUsage; | 167 final Set<LibraryElement> librariesWithUsage; |
| 168 final Map<Constant, List<String>> cachedStrings; | 168 final Map<ConstantValue, List<String>> cachedStrings; |
| 169 final Map<Constant, List<Element>> cachedElements; | 169 final Map<ConstantValue, List<Element>> cachedElements; |
| 170 MirrorUsage mergedMirrorUsage; | 170 MirrorUsage mergedMirrorUsage; |
| 171 | 171 |
| 172 MirrorUsageAnalyzer(Compiler compiler, this.task) | 172 MirrorUsageAnalyzer(Compiler compiler, this.task) |
| 173 : compiler = compiler, | 173 : compiler = compiler, |
| 174 librariesWithUsage = new Set<LibraryElement>(), | 174 librariesWithUsage = new Set<LibraryElement>(), |
| 175 cachedStrings = new Map<Constant, List<String>>(), | 175 cachedStrings = new Map<ConstantValue, List<String>>(), |
| 176 cachedElements = new Map<Constant, List<Element>>(); | 176 cachedElements = new Map<ConstantValue, List<Element>>(); |
| 177 | 177 |
| 178 /// Collect and merge all @MirrorsUsed annotations. As a side-effect, also | 178 /// Collect and merge all @MirrorsUsed annotations. As a side-effect, also |
| 179 /// compute which libraries have the annotation (which is used by | 179 /// compute which libraries have the annotation (which is used by |
| 180 /// [MirrorUsageAnalyzerTask.hasMirrorUsage]). | 180 /// [MirrorUsageAnalyzerTask.hasMirrorUsage]). |
| 181 void run() { | 181 void run() { |
| 182 wildcard = compiler.libraryLoader.libraries.toList(); | 182 wildcard = compiler.libraryLoader.libraries.toList(); |
| 183 Map<LibraryElement, List<MirrorUsage>> usageMap = | 183 Map<LibraryElement, List<MirrorUsage>> usageMap = |
| 184 collectMirrorsUsedAnnotation(); | 184 collectMirrorsUsedAnnotation(); |
| 185 propagateOverrides(usageMap); | 185 propagateOverrides(usageMap); |
| 186 Set<LibraryElement> librariesWithoutUsage = new Set<LibraryElement>(); | 186 Set<LibraryElement> librariesWithoutUsage = new Set<LibraryElement>(); |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 319 if (metaTargets == null) { | 319 if (metaTargets == null) { |
| 320 metaTargets = b.metaTargets; | 320 metaTargets = b.metaTargets; |
| 321 } else if (metaTargets != wildcard && b.metaTargets != null) { | 321 } else if (metaTargets != wildcard && b.metaTargets != null) { |
| 322 metaTargets.addAll(b.metaTargets); | 322 metaTargets.addAll(b.metaTargets); |
| 323 } | 323 } |
| 324 return new MirrorUsage(symbols, targets, metaTargets, null); | 324 return new MirrorUsage(symbols, targets, metaTargets, null); |
| 325 } | 325 } |
| 326 | 326 |
| 327 /// Convert a [constant] to an instance of [MirrorUsage] using information | 327 /// Convert a [constant] to an instance of [MirrorUsage] using information |
| 328 /// that was resolved during [MirrorUsageAnalyzerTask.validate]. | 328 /// that was resolved during [MirrorUsageAnalyzerTask.validate]. |
| 329 MirrorUsage buildUsage(ConstructedConstant constant) { | 329 MirrorUsage buildUsage(ConstructedConstantValue constant) { |
| 330 Map<Element, Constant> fields = constant.fieldElements; | 330 Map<Element, ConstantValue> fields = constant.fieldElements; |
| 331 VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember( | 331 VariableElement symbolsField = compiler.mirrorsUsedClass.lookupLocalMember( |
| 332 'symbols'); | 332 'symbols'); |
| 333 VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember( | 333 VariableElement targetsField = compiler.mirrorsUsedClass.lookupLocalMember( |
| 334 'targets'); | 334 'targets'); |
| 335 VariableElement metaTargetsField = | 335 VariableElement metaTargetsField = |
| 336 compiler.mirrorsUsedClass.lookupLocalMember( | 336 compiler.mirrorsUsedClass.lookupLocalMember( |
| 337 'metaTargets'); | 337 'metaTargets'); |
| 338 VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember( | 338 VariableElement overrideField = compiler.mirrorsUsedClass.lookupLocalMember( |
| 339 'override'); | 339 'override'); |
| 340 | 340 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 364 'override = $override' | 364 'override = $override' |
| 365 ')'; | 365 ')'; |
| 366 | 366 |
| 367 } | 367 } |
| 368 } | 368 } |
| 369 | 369 |
| 370 class MirrorUsageBuilder { | 370 class MirrorUsageBuilder { |
| 371 final MirrorUsageAnalyzer analyzer; | 371 final MirrorUsageAnalyzer analyzer; |
| 372 final LibraryElement enclosingLibrary; | 372 final LibraryElement enclosingLibrary; |
| 373 final Spannable spannable; | 373 final Spannable spannable; |
| 374 final Constant constant; | 374 final ConstantValue constant; |
| 375 final TreeElements elements; | 375 final TreeElements elements; |
| 376 | 376 |
| 377 MirrorUsageBuilder( | 377 MirrorUsageBuilder( |
| 378 this.analyzer, | 378 this.analyzer, |
| 379 this.enclosingLibrary, | 379 this.enclosingLibrary, |
| 380 this.spannable, | 380 this.spannable, |
| 381 this.constant, | 381 this.constant, |
| 382 this.elements); | 382 this.elements); |
| 383 | 383 |
| 384 Compiler get compiler => analyzer.compiler; | 384 Compiler get compiler => analyzer.compiler; |
| 385 | 385 |
| 386 /// Convert a constant to a list of [String] and [Type] values. If the | 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 | 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]:]. | 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 | 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 | 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 | 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 | 392 /// [Type] values are treated as an error (meaning that the value is ignored |
| 393 /// and a hint is emitted). | 393 /// and a hint is emitted). |
| 394 List convertConstantToUsageList( | 394 List convertConstantToUsageList( |
| 395 Constant constant, { bool onlyStrings: false }) { | 395 ConstantValue constant, { bool onlyStrings: false }) { |
| 396 if (constant.isNull) { | 396 if (constant.isNull) { |
| 397 return null; | 397 return null; |
| 398 } else if (constant.isList) { | 398 } else if (constant.isList) { |
| 399 ListConstant list = constant; | 399 ListConstantValue list = constant; |
| 400 List result = onlyStrings ? <String> [] : []; | 400 List result = onlyStrings ? <String> [] : []; |
| 401 for (Constant entry in list.entries) { | 401 for (ConstantValue entry in list.entries) { |
| 402 if (entry.isString) { | 402 if (entry.isString) { |
| 403 StringConstant string = entry; | 403 StringConstantValue string = entry; |
| 404 result.add(string.value.slowToString()); | 404 result.add(string.primitiveValue.slowToString()); |
| 405 } else if (!onlyStrings && entry.isType) { | 405 } else if (!onlyStrings && entry.isType) { |
| 406 TypeConstant type = entry; | 406 TypeConstantValue type = entry; |
| 407 result.add(type.representedType); | 407 result.add(type.representedType); |
| 408 } else { | 408 } else { |
| 409 Spannable node = positionOf(entry); | 409 Spannable node = positionOf(entry); |
| 410 MessageKind kind = onlyStrings | 410 MessageKind kind = onlyStrings |
| 411 ? MessageKind.MIRRORS_EXPECTED_STRING | 411 ? MessageKind.MIRRORS_EXPECTED_STRING |
| 412 : MessageKind.MIRRORS_EXPECTED_STRING_OR_TYPE; | 412 : MessageKind.MIRRORS_EXPECTED_STRING_OR_TYPE; |
| 413 compiler.reportHint( | 413 compiler.reportHint( |
| 414 node, | 414 node, |
| 415 kind, {'name': node, 'type': apiTypeOf(entry)}); | 415 kind, {'name': node, 'type': apiTypeOf(entry)}); |
| 416 } | 416 } |
| 417 } | 417 } |
| 418 return result; | 418 return result; |
| 419 } else if (!onlyStrings && constant.isType) { | 419 } else if (!onlyStrings && constant.isType) { |
| 420 TypeConstant type = constant; | 420 TypeConstantValue type = constant; |
| 421 return [type.representedType]; | 421 return [type.representedType]; |
| 422 } else if (constant.isString) { | 422 } else if (constant.isString) { |
| 423 StringConstant string = constant; | 423 StringConstantValue string = constant; |
| 424 var iterable = | 424 var iterable = |
| 425 string.value.slowToString().split(',').map((e) => e.trim()); | 425 string.primitiveValue.slowToString().split(',').map((e) => e.trim()); |
| 426 return onlyStrings ? new List<String>.from(iterable) : iterable.toList(); | 426 return onlyStrings ? new List<String>.from(iterable) : iterable.toList(); |
| 427 } else { | 427 } else { |
| 428 Spannable node = positionOf(constant); | 428 Spannable node = positionOf(constant); |
| 429 MessageKind kind = onlyStrings | 429 MessageKind kind = onlyStrings |
| 430 ? MessageKind.MIRRORS_EXPECTED_STRING_OR_LIST | 430 ? MessageKind.MIRRORS_EXPECTED_STRING_OR_LIST |
| 431 : MessageKind.MIRRORS_EXPECTED_STRING_TYPE_OR_LIST; | 431 : MessageKind.MIRRORS_EXPECTED_STRING_TYPE_OR_LIST; |
| 432 compiler.reportHint( | 432 compiler.reportHint( |
| 433 node, | 433 node, |
| 434 kind, {'name': node, 'type': apiTypeOf(constant)}); | 434 kind, {'name': node, 'type': apiTypeOf(constant)}); |
| 435 return null; | 435 return null; |
| 436 } | 436 } |
| 437 } | 437 } |
| 438 | 438 |
| 439 /// Find the first non-implementation interface of constant. | 439 /// Find the first non-implementation interface of constant. |
| 440 DartType apiTypeOf(Constant constant) { | 440 DartType apiTypeOf(ConstantValue constant) { |
| 441 DartType type = constant.computeType(compiler); | 441 DartType type = constant.computeType(compiler); |
| 442 LibraryElement library = type.element.library; | 442 LibraryElement library = type.element.library; |
| 443 if (type.isInterfaceType && library.isInternalLibrary) { | 443 if (type.isInterfaceType && library.isInternalLibrary) { |
| 444 InterfaceType interface = type; | 444 InterfaceType interface = type; |
| 445 ClassElement cls = type.element; | 445 ClassElement cls = type.element; |
| 446 cls.ensureResolved(compiler); | 446 cls.ensureResolved(compiler); |
| 447 for (DartType supertype in cls.allSupertypes) { | 447 for (DartType supertype in cls.allSupertypes) { |
| 448 if (supertype.isInterfaceType | 448 if (supertype.isInterfaceType |
| 449 && !supertype.element.library.isInternalLibrary) { | 449 && !supertype.element.library.isInternalLibrary) { |
| 450 return interface.asInstanceOf(supertype.element); | 450 return interface.asInstanceOf(supertype.element); |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 556 if (element.isClass) { | 556 if (element.isClass) { |
| 557 ClassElement cls = element; | 557 ClassElement cls = element; |
| 558 cls.ensureResolved(compiler); | 558 cls.ensureResolved(compiler); |
| 559 } | 559 } |
| 560 return scope.localLookup(name); | 560 return scope.localLookup(name); |
| 561 } | 561 } |
| 562 return null; | 562 return null; |
| 563 } | 563 } |
| 564 | 564 |
| 565 /// Attempt to find a [Spannable] corresponding to constant. | 565 /// Attempt to find a [Spannable] corresponding to constant. |
| 566 Spannable positionOf(Constant constant) { | 566 Spannable positionOf(ConstantValue constant) { |
| 567 Node node; | 567 Node node; |
| 568 elements.forEachConstantNode((Node n, ConstExp c) { | 568 elements.forEachConstantNode((Node n, ConstantExpression c) { |
| 569 if (node == null && c.value == constant) { | 569 if (node == null && c.value == constant) { |
| 570 node = n; | 570 node = n; |
| 571 } | 571 } |
| 572 }); | 572 }); |
| 573 if (node == null) { | 573 if (node == null) { |
| 574 // TODO(ahe): Returning [spannable] here leads to confusing error | 574 // TODO(ahe): Returning [spannable] here leads to confusing error |
| 575 // messages. For example, consider: | 575 // messages. For example, consider: |
| 576 // @MirrorsUsed(targets: fisk) | 576 // @MirrorsUsed(targets: fisk) |
| 577 // import 'dart:mirrors'; | 577 // import 'dart:mirrors'; |
| 578 // | 578 // |
| 579 // const fisk = const [main]; | 579 // const fisk = const [main]; |
| 580 // | 580 // |
| 581 // main() {} | 581 // main() {} |
| 582 // | 582 // |
| 583 // The message is: | 583 // The message is: |
| 584 // example.dart:1:23: Hint: Can't use 'fisk' here because ... | 584 // example.dart:1:23: Hint: Can't use 'fisk' here because ... |
| 585 // Did you forget to add quotes? | 585 // Did you forget to add quotes? |
| 586 // @MirrorsUsed(targets: fisk) | 586 // @MirrorsUsed(targets: fisk) |
| 587 // ^^^^ | 587 // ^^^^ |
| 588 // | 588 // |
| 589 // Instead of saying 'fisk' should pretty print the problematic constant | 589 // Instead of saying 'fisk' should pretty print the problematic constant |
| 590 // value. | 590 // value. |
| 591 return spannable; | 591 return spannable; |
| 592 } | 592 } |
| 593 return node; | 593 return node; |
| 594 } | 594 } |
| 595 } | 595 } |
| OLD | NEW |