blob: f3c8e2179768b60ad33d624f37d2a815072071c3 [file] [log] [blame]
// 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 '../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();
Iterable<DynamicUse> get dynamicUses => const <DynamicUse>[];
Iterable<StaticUse> get staticUses => const <StaticUse>[];
// 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 <TypeUse>[];
Iterable<ConstantUse> get constantUses => const <ConstantUse>[];
bool get isEmpty => true;
void apply(WorldImpactVisitor visitor) {
staticUses.forEach(visitor.visitStaticUse);
dynamicUses.forEach(visitor.visitDynamicUse);
typeUses.forEach(visitor.visitTypeUse);
constantUses.forEach(visitor.visitConstantUse);
}
@override
String toString() => dump(this);
static String dump(WorldImpact worldImpact) {
StringBuffer sb = new StringBuffer();
printOn(sb, worldImpact);
return sb.toString();
}
static void printOn(StringBuffer sb, WorldImpact worldImpact) {
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 ??= new Setlet<DynamicUse>();
_dynamicUses.add(dynamicUse);
}
@override
Iterable<DynamicUse> get dynamicUses {
return _dynamicUses != null ? _dynamicUses : const <DynamicUse>[];
}
@override
void registerTypeUse(TypeUse typeUse) {
assert(typeUse != null);
_typeUses ??= new Setlet<TypeUse>();
_typeUses.add(typeUse);
}
@override
Iterable<TypeUse> get typeUses {
return _typeUses != null ? _typeUses : const <TypeUse>[];
}
@override
void registerStaticUse(StaticUse staticUse) {
assert(staticUse != null);
_staticUses ??= new Setlet<StaticUse>();
_staticUses.add(staticUse);
}
@override
Iterable<StaticUse> get staticUses {
return _staticUses != null ? _staticUses : const <StaticUse>[];
}
@override
void registerConstantUse(ConstantUse constantUse) {
assert(constantUse != null);
_constantUses ??= new Setlet<ConstantUse>();
_constantUses.add(constantUse);
}
@override
Iterable<ConstantUse> get constantUses {
return _constantUses != null ? _constantUses : const <ConstantUse>[];
}
}
/// [WorldImpactBuilder] that can create and collect a sequence of
/// [WorldImpact]s.
class StagedWorldImpactBuilder implements WorldImpactBuilder {
final bool collectImpacts;
WorldImpactBuilderImpl _currentBuilder;
List<WorldImpactBuilderImpl> _builders = <WorldImpactBuilderImpl>[];
StagedWorldImpactBuilder({this.collectImpacts: false});
void _ensureBuilder() {
if (_currentBuilder == null) {
_currentBuilder = new 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
bool get isEmpty {
return worldImpact.isEmpty &&
_staticUses == null &&
_typeUses == null &&
_dynamicUses == null &&
_constantUses == null;
}
@override
Iterable<DynamicUse> get dynamicUses {
return _dynamicUses != null ? _dynamicUses : worldImpact.dynamicUses;
}
@override
void registerDynamicUse(DynamicUse dynamicUse) {
if (_dynamicUses == null) {
_dynamicUses = new Setlet<DynamicUse>();
_dynamicUses.addAll(worldImpact.dynamicUses);
}
_dynamicUses.add(dynamicUse);
}
@override
void registerTypeUse(TypeUse typeUse) {
if (_typeUses == null) {
_typeUses = new Setlet<TypeUse>();
_typeUses.addAll(worldImpact.typeUses);
}
_typeUses.add(typeUse);
}
@override
Iterable<TypeUse> get typeUses {
return _typeUses != null ? _typeUses : worldImpact.typeUses;
}
@override
void registerStaticUse(StaticUse staticUse) {
if (_staticUses == null) {
_staticUses = new Setlet<StaticUse>();
_staticUses.addAll(worldImpact.staticUses);
}
_staticUses.add(staticUse);
}
@override
Iterable<StaticUse> get staticUses {
return _staticUses != null ? _staticUses : worldImpact.staticUses;
}
@override
Iterable<ConstantUse> get constantUses {
return _constantUses != null ? _constantUses : worldImpact.constantUses;
}
@override
void registerConstantUse(ConstantUse constantUse) {
if (_constantUses == null) {
_constantUses = new Setlet<ConstantUse>();
_constantUses.addAll(worldImpact.constantUses);
}
_constantUses.add(constantUse);
}
@override
void apply(WorldImpactVisitor visitor) {
staticUses.forEach(visitor.visitStaticUse);
dynamicUses.forEach(visitor.visitDynamicUse);
typeUses.forEach(visitor.visitTypeUse);
constantUses.forEach(visitor.visitConstantUse);
}
@override
String toString() {
StringBuffer sb = new 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(StaticUse staticUse);
void visitDynamicUse(DynamicUse dynamicUse);
void visitTypeUse(TypeUse typeUse);
void visitConstantUse(ConstantUse typeUse);
}
// TODO(johnniwinther): Remove these when we get anonymous local classes.
typedef void VisitUse<U>(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(StaticUse use) {
if (_visitStaticUse != null) {
_visitStaticUse(use);
}
}
@override
void visitDynamicUse(DynamicUse use) {
if (_visitDynamicUse != null) {
_visitDynamicUse(use);
}
}
@override
void visitTypeUse(TypeUse use) {
if (_visitTypeUse != null) {
_visitTypeUse(use);
}
}
@override
void visitConstantUse(ConstantUse use) {
if (_visitConstantUse != null) {
_visitConstantUse(use);
}
}
}