| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart2js.universe.world_impact; |
| |
| import '../elements/entities.dart'; |
| import '../util/util.dart' show Setlet; |
| import 'use.dart'; |
| |
| /// Describes how an element (e.g. a method) impacts the closed-world |
| /// semantics of a program. |
| /// |
| /// A [WorldImpact] contains information about how a program element affects our |
| /// understanding of what's live in a program. For example, it can indicate |
| /// that a method uses a certain feature, or allocates a specific type. |
| /// |
| /// The impact object can be computed locally by inspecting just the resolution |
| /// information of that element alone. The compiler uses [Universe] and |
| /// [World] to combine the information discovered in the impact objects of |
| /// all elements reachable in an application. |
| class WorldImpact { |
| const WorldImpact(); |
| |
| MemberEntity get member => null; |
| |
| Iterable<DynamicUse> get dynamicUses => const []; |
| |
| Iterable<StaticUse> get staticUses => const []; |
| |
| // TODO(johnniwinther): Replace this by called constructors with type |
| // arguments. |
| // TODO(johnniwinther): Collect all checked types for checked mode separately |
| // to support serialization. |
| |
| Iterable<TypeUse> get typeUses => const []; |
| |
| Iterable<ConstantUse> get constantUses => const []; |
| |
| bool get isEmpty => true; |
| |
| void apply(WorldImpactVisitor visitor) { |
| staticUses.forEach((StaticUse use) => visitor.visitStaticUse(member, use)); |
| dynamicUses |
| .forEach((DynamicUse use) => visitor.visitDynamicUse(member, use)); |
| typeUses.forEach((TypeUse use) => visitor.visitTypeUse(member, use)); |
| constantUses |
| .forEach((ConstantUse use) => visitor.visitConstantUse(member, use)); |
| } |
| |
| @override |
| String toString() => dump(this); |
| |
| static String dump(WorldImpact worldImpact) { |
| StringBuffer sb = StringBuffer(); |
| printOn(sb, worldImpact); |
| return sb.toString(); |
| } |
| |
| static void printOn(StringBuffer sb, WorldImpact worldImpact) { |
| sb.write('member: ${worldImpact.member}'); |
| |
| void add(String title, Iterable iterable) { |
| if (iterable.isNotEmpty) { |
| sb.write('\n $title:'); |
| iterable.forEach((e) => sb.write('\n $e')); |
| } |
| } |
| |
| add('dynamic uses', worldImpact.dynamicUses); |
| add('static uses', worldImpact.staticUses); |
| add('type uses', worldImpact.typeUses); |
| add('constant uses', worldImpact.constantUses); |
| } |
| } |
| |
| abstract class WorldImpactBuilder { |
| void registerDynamicUse(DynamicUse dynamicUse); |
| void registerTypeUse(TypeUse typeUse); |
| void registerStaticUse(StaticUse staticUse); |
| void registerConstantUse(ConstantUse constantUse); |
| } |
| |
| class WorldImpactBuilderImpl extends WorldImpact implements WorldImpactBuilder { |
| // TODO(johnniwinther): Do we benefit from lazy initialization of the |
| // [Setlet]s? |
| Set<DynamicUse> _dynamicUses; |
| Set<StaticUse> _staticUses; |
| Set<TypeUse> _typeUses; |
| Set<ConstantUse> _constantUses; |
| |
| WorldImpactBuilderImpl(); |
| |
| WorldImpactBuilderImpl.internal( |
| this._dynamicUses, this._staticUses, this._typeUses, this._constantUses); |
| |
| @override |
| bool get isEmpty => |
| _dynamicUses == null && |
| _staticUses == null && |
| _typeUses == null && |
| _constantUses == null; |
| |
| /// Copy uses in [impact] to this impact builder. |
| void addImpact(WorldImpact impact) { |
| if (impact.isEmpty) return; |
| impact.dynamicUses.forEach(registerDynamicUse); |
| impact.staticUses.forEach(registerStaticUse); |
| impact.typeUses.forEach(registerTypeUse); |
| impact.constantUses.forEach(registerConstantUse); |
| } |
| |
| @override |
| void registerDynamicUse(DynamicUse dynamicUse) { |
| assert(dynamicUse != null); |
| _dynamicUses ??= Setlet(); |
| _dynamicUses.add(dynamicUse); |
| } |
| |
| @override |
| Iterable<DynamicUse> get dynamicUses { |
| return _dynamicUses ?? const []; |
| } |
| |
| @override |
| void registerTypeUse(TypeUse typeUse) { |
| assert(typeUse != null); |
| _typeUses ??= Setlet(); |
| _typeUses.add(typeUse); |
| } |
| |
| @override |
| Iterable<TypeUse> get typeUses { |
| return _typeUses ?? const []; |
| } |
| |
| @override |
| void registerStaticUse(StaticUse staticUse) { |
| assert(staticUse != null); |
| _staticUses ??= Setlet(); |
| _staticUses.add(staticUse); |
| } |
| |
| @override |
| Iterable<StaticUse> get staticUses { |
| return _staticUses ?? const []; |
| } |
| |
| @override |
| void registerConstantUse(ConstantUse constantUse) { |
| assert(constantUse != null); |
| _constantUses ??= Setlet(); |
| _constantUses.add(constantUse); |
| } |
| |
| @override |
| Iterable<ConstantUse> get constantUses { |
| return _constantUses ?? const []; |
| } |
| } |
| |
| /// [WorldImpactBuilder] that can create and collect a sequence of |
| /// [WorldImpact]s. |
| class StagedWorldImpactBuilder implements WorldImpactBuilder { |
| final bool collectImpacts; |
| WorldImpactBuilderImpl _currentBuilder; |
| final List<WorldImpactBuilderImpl> _builders = []; |
| |
| StagedWorldImpactBuilder({this.collectImpacts = false}); |
| |
| void _ensureBuilder() { |
| if (_currentBuilder == null) { |
| _currentBuilder = WorldImpactBuilderImpl(); |
| if (collectImpacts) { |
| _builders.add(_currentBuilder); |
| } |
| } |
| } |
| |
| @override |
| void registerTypeUse(TypeUse typeUse) { |
| _ensureBuilder(); |
| _currentBuilder.registerTypeUse(typeUse); |
| } |
| |
| @override |
| void registerDynamicUse(DynamicUse dynamicUse) { |
| _ensureBuilder(); |
| _currentBuilder.registerDynamicUse(dynamicUse); |
| } |
| |
| @override |
| void registerStaticUse(StaticUse staticUse) { |
| _ensureBuilder(); |
| _currentBuilder.registerStaticUse(staticUse); |
| } |
| |
| @override |
| void registerConstantUse(ConstantUse constantUse) { |
| _ensureBuilder(); |
| _currentBuilder.registerConstantUse(constantUse); |
| } |
| |
| /// Returns the [WorldImpact] built so far with this builder. The builder |
| /// is reset, and if [collectImpacts] is `true` the impact is cached for |
| /// [worldImpacts]. |
| WorldImpact flush() { |
| if (_currentBuilder == null) return const WorldImpact(); |
| WorldImpact worldImpact = _currentBuilder; |
| _currentBuilder = null; |
| return worldImpact; |
| } |
| |
| /// If [collectImpacts] is `true` this returns all [WorldImpact]s built with |
| /// this builder. |
| Iterable<WorldImpact> get worldImpacts => _builders; |
| } |
| |
| /// Mutable implementation of [WorldImpact] used to transform |
| /// [ResolutionImpact] or [CodegenImpact] to [WorldImpact]. |
| class TransformedWorldImpact implements WorldImpact, WorldImpactBuilder { |
| final WorldImpact worldImpact; |
| |
| Setlet<StaticUse> _staticUses; |
| Setlet<TypeUse> _typeUses; |
| Setlet<DynamicUse> _dynamicUses; |
| Setlet<ConstantUse> _constantUses; |
| |
| TransformedWorldImpact(this.worldImpact); |
| |
| @override |
| MemberEntity get member => worldImpact.member; |
| |
| @override |
| bool get isEmpty { |
| return worldImpact.isEmpty && |
| _staticUses == null && |
| _typeUses == null && |
| _dynamicUses == null && |
| _constantUses == null; |
| } |
| |
| @override |
| Iterable<DynamicUse> get dynamicUses { |
| return _dynamicUses ?? worldImpact.dynamicUses; |
| } |
| |
| @override |
| void registerDynamicUse(DynamicUse dynamicUse) { |
| _dynamicUses ??= Setlet.of(worldImpact.dynamicUses); |
| _dynamicUses.add(dynamicUse); |
| } |
| |
| @override |
| void registerTypeUse(TypeUse typeUse) { |
| _typeUses ??= Setlet.of(worldImpact.typeUses); |
| _typeUses.add(typeUse); |
| } |
| |
| @override |
| Iterable<TypeUse> get typeUses { |
| return _typeUses ?? worldImpact.typeUses; |
| } |
| |
| @override |
| void registerStaticUse(StaticUse staticUse) { |
| _staticUses ??= Setlet.of(worldImpact.staticUses); |
| _staticUses.add(staticUse); |
| } |
| |
| @override |
| Iterable<StaticUse> get staticUses { |
| return _staticUses ?? worldImpact.staticUses; |
| } |
| |
| @override |
| Iterable<ConstantUse> get constantUses { |
| return _constantUses ?? worldImpact.constantUses; |
| } |
| |
| @override |
| void registerConstantUse(ConstantUse constantUse) { |
| _constantUses ??= Setlet.of(worldImpact.constantUses); |
| _constantUses.add(constantUse); |
| } |
| |
| @override |
| void apply(WorldImpactVisitor visitor) { |
| staticUses.forEach((StaticUse use) => visitor.visitStaticUse(member, use)); |
| dynamicUses |
| .forEach((DynamicUse use) => visitor.visitDynamicUse(member, use)); |
| typeUses.forEach((TypeUse use) => visitor.visitTypeUse(member, use)); |
| constantUses |
| .forEach((ConstantUse use) => visitor.visitConstantUse(member, use)); |
| } |
| |
| @override |
| String toString() { |
| StringBuffer sb = StringBuffer(); |
| sb.write('TransformedWorldImpact($worldImpact)'); |
| WorldImpact.printOn(sb, this); |
| return sb.toString(); |
| } |
| } |
| |
| /// Constant used to denote a specific use of a [WorldImpact]. |
| class ImpactUseCase { |
| final String name; |
| |
| const ImpactUseCase(this.name); |
| |
| @override |
| String toString() => 'ImpactUseCase($name)'; |
| } |
| |
| /// Strategy used for processing [WorldImpact] object in various use cases. |
| class ImpactStrategy { |
| const ImpactStrategy(); |
| |
| /// Applies [impact] to [visitor] for the [impactUseCase] of [impactSource]. |
| void visitImpact(var impactSource, WorldImpact impact, |
| WorldImpactVisitor visitor, ImpactUseCase impactUseCase) { |
| // Apply unconditionally. |
| impact.apply(visitor); |
| } |
| |
| /// Notifies the strategy that no more impacts of [impactUseCase] will be |
| /// applied. |
| void onImpactUsed(ImpactUseCase impactUseCase) { |
| // Do nothing. |
| } |
| } |
| |
| /// Visitor used to process the uses of a [WorldImpact]. |
| abstract class WorldImpactVisitor { |
| void visitStaticUse(MemberEntity member, StaticUse staticUse); |
| void visitDynamicUse(MemberEntity member, DynamicUse dynamicUse); |
| void visitTypeUse(MemberEntity member, TypeUse typeUse); |
| void visitConstantUse(MemberEntity member, ConstantUse typeUse); |
| } |
| |
| // TODO(johnniwinther): Remove these when we get anonymous local classes. |
| typedef VisitUse<U> = void Function(MemberEntity member, U use); |
| |
| class WorldImpactVisitorImpl implements WorldImpactVisitor { |
| final VisitUse<StaticUse> _visitStaticUse; |
| final VisitUse<DynamicUse> _visitDynamicUse; |
| final VisitUse<TypeUse> _visitTypeUse; |
| final VisitUse<ConstantUse> _visitConstantUse; |
| |
| WorldImpactVisitorImpl( |
| {VisitUse<StaticUse> visitStaticUse, |
| VisitUse<DynamicUse> visitDynamicUse, |
| VisitUse<TypeUse> visitTypeUse, |
| VisitUse<ConstantUse> visitConstantUse}) |
| : _visitStaticUse = visitStaticUse, |
| _visitDynamicUse = visitDynamicUse, |
| _visitTypeUse = visitTypeUse, |
| _visitConstantUse = visitConstantUse; |
| |
| @override |
| void visitStaticUse(MemberEntity member, StaticUse use) { |
| if (_visitStaticUse != null) { |
| _visitStaticUse(member, use); |
| } |
| } |
| |
| @override |
| void visitDynamicUse(MemberEntity member, DynamicUse use) { |
| if (_visitDynamicUse != null) { |
| _visitDynamicUse(member, use); |
| } |
| } |
| |
| @override |
| void visitTypeUse(MemberEntity member, TypeUse use) { |
| if (_visitTypeUse != null) { |
| _visitTypeUse(member, use); |
| } |
| } |
| |
| @override |
| void visitConstantUse(MemberEntity member, ConstantUse use) { |
| if (_visitConstantUse != null) { |
| _visitConstantUse(member, use); |
| } |
| } |
| } |