blob: e368cbb0b49d21252e6532b909105690a91b5d23 [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'dart:math' as Math;
import '../common.dart';
import '../constants/values.dart';
import '../elements/entities.dart';
import '../js_model/elements.dart' show JSignatureMethod;
import '../util/enumset.dart';
import 'call_structure.dart';
abstract class AbstractUsage<T> {
final EnumSet<T> _pendingUse;
AbstractUsage.cloned(this._pendingUse);
AbstractUsage() : this._pendingUse = new EnumSet<T>() {
_pendingUse.addAll(_originalUse);
}
/// Returns the uses of [entity] that have been registered.
EnumSet<T> get appliedUse => _originalUse.minus(_pendingUse);
EnumSet<T> get _originalUse;
/// `true` if the [appliedUse] is non-empty.
bool get hasUse => appliedUse.isNotEmpty;
/// Returns `true` if [other] has the same original and pending usage as this.
bool hasSameUsage(AbstractUsage<T> other) {
if (identical(this, other)) return true;
return _originalUse.value == other._originalUse.value &&
_pendingUse.value == other._pendingUse.value;
}
}
/// Registry for the observed use of a member [entity] in the open world.
abstract class MemberUsage extends AbstractUsage<MemberUse> {
final MemberEntity entity;
MemberUsage.internal(this.entity) : super();
MemberUsage.cloned(this.entity, EnumSet<MemberUse> pendingUse)
: super.cloned(pendingUse);
factory MemberUsage(MemberEntity member,
{bool isNative: false, bool trackParameters: false}) {
if (member.isField) {
if (member.isAssignable) {
return new FieldUsage(member, isNative: isNative);
} else {
return new FinalFieldUsage(member, isNative: isNative);
}
} else if (member.isGetter) {
return new GetterUsage(member);
} else if (member.isSetter) {
return new SetterUsage(member);
} else if (member.isConstructor) {
if (trackParameters) {
return new ParameterTrackingConstructorUsage(member);
} else {
return new ConstructorUsage(member);
}
} else {
assert(member.isFunction, failedAt(member, "Unexpected member: $member"));
if (trackParameters) {
return new ParameterTrackingFunctionUsage(member);
} else {
return new FunctionUsage(member);
}
}
}
/// `true` if [entity] has been initialized.
bool get hasInit => true;
Iterable<ConstantValue> get initialConstants => null;
/// `true` if [entity] has been read as a value. For a field this is a normal
/// read access, for a function this is a closurization.
bool get hasRead => false;
/// `true` if a value has been written to [entity].
bool get hasWrite => false;
/// `true` if an invocation has been performed on the value [entity]. For a
/// function this is a normal invocation, for a field this is a read access
/// followed by an invocation of the function-like value.
bool get hasInvoke => false;
/// `true` if all parameters are provided in invocations of [entity].
///
/// For method or constructors with no optional arguments this is the same
/// as [hasInvoke] but for method or constructors with optional arguments some
/// parameters may have been provided in any invocation in which case
/// [isFullyInvoked] is `false`.
bool get isFullyInvoked => hasInvoke;
/// Returns the [ParameterStructure] corresponding to the parameters that are
/// used in invocations of [entity]. For a field, getter or setter this is
/// always `null`.
ParameterStructure get invokedParameters => null;
/// `true` if [entity] has further normal use. For a field this means that
/// it hasn't been read from or written to. For a function this means that it
/// hasn't been invoked or, when parameter usage is tracked, that some
/// parameters haven't been provided in any invocation.
bool get hasPendingNormalUse => _pendingUse.contains(MemberUse.NORMAL);
/// `true` if [entity] hasn't been closurized. This is only used for
/// functions.
bool get hasPendingClosurizationUse => false;
/// `true` if [entity] has been used in all the ways possible.
bool get fullyUsed;
/// Registers the [entity] has been initialized and returns the new
/// [MemberUse]s that it caused.
///
/// For a field this is the initial write access, for a function this is a
/// no-op.
EnumSet<MemberUse> init() => MemberUses.NONE;
/// Registers the [entity] has been initialized with [constant] and returns
/// the new [MemberUse]s that it caused.
///
/// For a field this is the initial write access, for a function this is a
/// no-op.
EnumSet<MemberUse> constantInit(ConstantValue constant) => MemberUses.NONE;
/// Registers a read of the value of [entity] and returns the new [MemberUse]s
/// that it caused.
///
/// For a field this is a normal read access, for a function this is a
/// closurization.
EnumSet<MemberUse> read() => MemberUses.NONE;
/// Registers a write of a value to [entity] and returns the new [MemberUse]s
/// that it caused.
EnumSet<MemberUse> write() => MemberUses.NONE;
/// Registers an invocation on the value of [entity] and returns the new
/// [MemberUse]s that it caused.
///
/// For a function this is a normal invocation, for a field this is a read
/// access followed by an invocation of the function-like value.
EnumSet<MemberUse> invoke(CallStructure callStructure) => MemberUses.NONE;
/// Registers all possible uses of [entity] and returns the new [MemberUse]s
/// that it caused.
EnumSet<MemberUse> fullyUse() => MemberUses.NONE;
@override
EnumSet<MemberUse> get _originalUse => MemberUses.NORMAL_ONLY;
@override
int get hashCode => entity.hashCode;
@override
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! MemberUsage) return false;
return entity == other.entity;
}
MemberUsage clone();
bool dataEquals(MemberUsage other) {
assert(entity == other.entity);
return hasInit == other.hasInit &&
hasRead == other.hasRead &&
hasInvoke == other.hasInvoke &&
hasWrite == other.hasWrite &&
hasPendingClosurizationUse == other.hasPendingClosurizationUse &&
hasPendingNormalUse == other.hasPendingNormalUse &&
fullyUsed == other.fullyUsed &&
isFullyInvoked == other.isFullyInvoked &&
_pendingUse == other._pendingUse &&
appliedUse == other.appliedUse;
}
@override
String toString() => '$entity:${appliedUse.iterable(MemberUse.values)}';
}
class FieldUsage extends MemberUsage {
@override
bool hasInit;
@override
bool hasRead;
@override
bool hasWrite;
List<ConstantValue> _initialConstants;
FieldUsage.cloned(FieldEntity field, EnumSet<MemberUse> pendingUse,
{this.hasInit, this.hasRead, this.hasWrite})
: super.cloned(field, pendingUse);
FieldUsage(FieldEntity field, {bool isNative: false})
: hasInit = false,
hasRead = false,
hasWrite = false,
super.internal(field) {
// TODO(johnniwinther): Track native fields through member usage.
if (!isNative) {
init();
}
}
@override
Iterable<ConstantValue> get initialConstants => _initialConstants ?? const [];
@override
bool get hasPendingNormalUse => !fullyUsed;
@override
bool get fullyUsed => hasInit && hasRead && hasWrite;
@override
EnumSet<MemberUse> init() {
if (hasInit) {
return MemberUses.NONE;
}
hasInit = true;
EnumSet<MemberUse> result = _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
if (!fullyUsed) {
result = result.union(MemberUses.PARTIAL_USE_ONLY);
}
return result;
}
@override
EnumSet<MemberUse> constantInit(ConstantValue constant) {
_initialConstants ??= [];
_initialConstants.add(constant);
return init();
}
@override
EnumSet<MemberUse> read() {
if (hasRead) {
return MemberUses.NONE;
}
hasRead = true;
EnumSet<MemberUse> result = _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
if (!fullyUsed) {
result = result.union(MemberUses.PARTIAL_USE_ONLY);
}
return result;
}
@override
EnumSet<MemberUse> write() {
if (hasWrite) {
return MemberUses.NONE;
}
hasWrite = true;
EnumSet<MemberUse> result = _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
if (!fullyUsed) {
result = result.union(MemberUses.PARTIAL_USE_ONLY);
}
return result;
}
@override
EnumSet<MemberUse> invoke(CallStructure callStructure) => read();
@override
EnumSet<MemberUse> fullyUse() {
if (fullyUsed) {
return MemberUses.NONE;
}
hasInit = hasRead = hasWrite = true;
return _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
}
@override
MemberUsage clone() {
return new FieldUsage.cloned(entity, _pendingUse.clone(),
hasInit: hasInit, hasRead: hasRead, hasWrite: hasWrite);
}
@override
String toString() => 'FieldUsage($entity,hasInit=$hasInit,hasRead=$hasRead,'
'hasWrite=$hasWrite,pendingUse=${_pendingUse.iterable(MemberUse.values)},'
'initialConstants=${initialConstants.map((c) => c.toStructuredText())})';
}
class FinalFieldUsage extends MemberUsage {
@override
bool hasInit;
@override
bool hasRead;
List<ConstantValue> _initialConstants;
FinalFieldUsage.cloned(FieldEntity field, EnumSet<MemberUse> pendingUse,
{this.hasInit, this.hasRead})
: super.cloned(field, pendingUse);
FinalFieldUsage(FieldEntity field, {bool isNative: false})
: this.hasInit = false,
this.hasRead = false,
super.internal(field) {
if (!isNative) {
init();
}
}
@override
Iterable<ConstantValue> get initialConstants => _initialConstants ?? const [];
@override
bool get hasPendingNormalUse => !fullyUsed;
@override
bool get fullyUsed => hasInit && hasRead;
@override
EnumSet<MemberUse> init() {
if (hasInit) {
return MemberUses.NONE;
}
hasInit = true;
EnumSet<MemberUse> result = _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
if (!fullyUsed) {
result = result.union(MemberUses.PARTIAL_USE_ONLY);
}
return result;
}
@override
EnumSet<MemberUse> constantInit(ConstantValue constant) {
_initialConstants ??= [];
_initialConstants.add(constant);
return init();
}
@override
EnumSet<MemberUse> read() {
if (hasRead) {
return MemberUses.NONE;
}
hasRead = true;
EnumSet<MemberUse> result = _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
if (!fullyUsed) {
result = result.union(MemberUses.PARTIAL_USE_ONLY);
}
return result;
}
@override
EnumSet<MemberUse> invoke(CallStructure callStructure) => read();
@override
EnumSet<MemberUse> fullyUse() {
if (fullyUsed) {
return MemberUses.NONE;
}
hasInit = hasRead = true;
return _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
}
@override
MemberUsage clone() {
return new FinalFieldUsage.cloned(entity, _pendingUse.clone(),
hasInit: hasInit, hasRead: hasRead);
}
@override
String toString() => 'FinalFieldUsage($entity,hasInit=$hasInit,'
'hasRead=$hasRead,pendingUse=${_pendingUse.iterable(MemberUse.values)},'
'initialConstants=${initialConstants.map((c) => c.toStructuredText())})';
}
class FunctionUsage extends MemberUsage {
@override
bool hasInvoke;
@override
bool hasRead;
FunctionUsage.cloned(FunctionEntity function, EnumSet<MemberUse> pendingUse,
{this.hasInvoke, this.hasRead})
: super.cloned(function, pendingUse);
FunctionUsage(FunctionEntity function)
: this.hasInvoke = false,
this.hasRead = false,
super.internal(function) {
if (function is JSignatureMethod) {
// We mark signature methods as "always used" to prevent them from being
// optimized away.
// TODO(johnniwinther): Make this a part of the regular enqueueing.
invoke(function.parameterStructure.callStructure);
}
}
@override
FunctionEntity get entity => super.entity;
@override
EnumSet<MemberUse> get _originalUse =>
entity.isInstanceMember ? MemberUses.ALL_INSTANCE : MemberUses.ALL_STATIC;
@override
bool get hasPendingClosurizationUse => entity.isInstanceMember
? _pendingUse.contains(MemberUse.CLOSURIZE_INSTANCE)
: _pendingUse.contains(MemberUse.CLOSURIZE_STATIC);
@override
EnumSet<MemberUse> read() => fullyUse();
@override
EnumSet<MemberUse> invoke(CallStructure callStructure) {
if (hasInvoke) {
return MemberUses.NONE;
}
hasInvoke = true;
return _pendingUse
.removeAll(hasRead ? MemberUses.NONE : MemberUses.NORMAL_ONLY);
}
@override
EnumSet<MemberUse> fullyUse() {
if (hasInvoke) {
if (hasRead) {
return MemberUses.NONE;
}
hasRead = true;
return _pendingUse.removeAll(entity.isInstanceMember
? MemberUses.CLOSURIZE_INSTANCE_ONLY
: MemberUses.CLOSURIZE_STATIC_ONLY);
} else if (hasRead) {
hasInvoke = true;
return _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
} else {
hasRead = hasInvoke = true;
return _pendingUse.removeAll(entity.isInstanceMember
? MemberUses.ALL_INSTANCE
: MemberUses.ALL_STATIC);
}
}
@override
bool get fullyUsed => hasInvoke && hasRead;
@override
ParameterStructure get invokedParameters =>
hasInvoke ? entity.parameterStructure : null;
@override
MemberUsage clone() {
return new FunctionUsage.cloned(entity, _pendingUse.clone(),
hasInvoke: hasInvoke, hasRead: hasRead);
}
}
class ParameterTrackingFunctionUsage extends MemberUsage {
@override
bool hasRead;
final ParameterUsage _parameterUsage;
ParameterTrackingFunctionUsage.cloned(FunctionEntity function,
this._parameterUsage, EnumSet<MemberUse> pendingUse,
{this.hasRead})
: super.cloned(function, pendingUse);
ParameterTrackingFunctionUsage(FunctionEntity function)
: hasRead = false,
_parameterUsage = new ParameterUsage(function.parameterStructure),
super.internal(function) {
if (function is JSignatureMethod) {
// We mark signature methods as "always used" to prevent them from being
// optimized away.
// TODO(johnniwinther): Make this a part of the regular enqueueing.
invoke(CallStructure.NO_ARGS);
}
}
@override
bool get hasInvoke => _parameterUsage.hasInvoke;
@override
bool get hasPendingClosurizationUse => entity.isInstanceMember
? _pendingUse.contains(MemberUse.CLOSURIZE_INSTANCE)
: _pendingUse.contains(MemberUse.CLOSURIZE_STATIC);
@override
EnumSet<MemberUse> get _originalUse =>
entity.isInstanceMember ? MemberUses.ALL_INSTANCE : MemberUses.ALL_STATIC;
@override
EnumSet<MemberUse> read() => fullyUse();
@override
EnumSet<MemberUse> invoke(CallStructure callStructure) {
if (_parameterUsage.isFullyUsed) {
return MemberUses.NONE;
}
bool alreadyHasInvoke = hasInvoke;
bool hasPartialChange = _parameterUsage.invoke(callStructure);
EnumSet<MemberUse> result;
if (alreadyHasInvoke) {
result = MemberUses.NONE;
} else {
result = _pendingUse
.removeAll(hasRead ? MemberUses.NONE : MemberUses.NORMAL_ONLY);
}
return hasPartialChange
? result.union(MemberUses.PARTIAL_USE_ONLY)
: result;
}
@override
EnumSet<MemberUse> fullyUse() {
bool alreadyHasInvoke = hasInvoke;
_parameterUsage.fullyUse();
if (alreadyHasInvoke) {
if (hasRead) {
return MemberUses.NONE;
}
hasRead = true;
return _pendingUse.removeAll(entity.isInstanceMember
? MemberUses.CLOSURIZE_INSTANCE_ONLY
: MemberUses.CLOSURIZE_STATIC_ONLY);
} else if (hasRead) {
return _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
} else {
hasRead = true;
return _pendingUse.removeAll(entity.isInstanceMember
? MemberUses.ALL_INSTANCE
: MemberUses.ALL_STATIC);
}
}
@override
bool get hasPendingNormalUse => !isFullyInvoked;
@override
bool get isFullyInvoked => _parameterUsage.isFullyUsed;
@override
bool get fullyUsed => isFullyInvoked && hasRead;
@override
ParameterStructure get invokedParameters => _parameterUsage.invokedParameters;
@override
MemberUsage clone() {
return new ParameterTrackingFunctionUsage.cloned(
entity, _parameterUsage.clone(), _pendingUse.clone(),
hasRead: hasRead);
}
}
class GetterUsage extends MemberUsage {
@override
bool hasRead;
GetterUsage.cloned(FunctionEntity getter, EnumSet<MemberUse> pendingUse,
{this.hasRead})
: super.cloned(getter, pendingUse);
GetterUsage(FunctionEntity getter)
: hasRead = false,
super.internal(getter);
@override
bool get fullyUsed => hasRead;
@override
EnumSet<MemberUse> read() {
if (hasRead) {
return MemberUses.NONE;
}
hasRead = true;
return _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
}
@override
EnumSet<MemberUse> invoke(CallStructure callStructure) => read();
@override
EnumSet<MemberUse> fullyUse() => read();
@override
MemberUsage clone() {
return new GetterUsage.cloned(entity, _pendingUse.clone(),
hasRead: hasRead);
}
}
class SetterUsage extends MemberUsage {
@override
bool hasWrite;
SetterUsage.cloned(FunctionEntity setter, EnumSet<MemberUse> pendingUse,
{this.hasWrite})
: super.cloned(setter, pendingUse);
SetterUsage(FunctionEntity setter)
: hasWrite = false,
super.internal(setter);
@override
bool get fullyUsed => hasWrite;
@override
EnumSet<MemberUse> write() {
if (hasWrite) {
return MemberUses.NONE;
}
hasWrite = true;
return _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
}
@override
EnumSet<MemberUse> fullyUse() => write();
@override
MemberUsage clone() {
return new SetterUsage.cloned(entity, _pendingUse.clone(),
hasWrite: hasWrite);
}
}
class ConstructorUsage extends MemberUsage {
@override
bool hasInvoke;
ConstructorUsage.cloned(
ConstructorEntity constructor, EnumSet<MemberUse> pendingUse,
{this.hasInvoke})
: super.cloned(constructor, pendingUse);
ConstructorUsage(ConstructorEntity constructor)
: hasInvoke = false,
super.internal(constructor);
@override
ConstructorEntity get entity => super.entity;
@override
EnumSet<MemberUse> get _originalUse => MemberUses.NORMAL_ONLY;
@override
EnumSet<MemberUse> invoke(CallStructure callStructure) {
if (hasInvoke) {
return MemberUses.NONE;
}
hasInvoke = true;
return _pendingUse
.removeAll(hasRead ? MemberUses.NONE : MemberUses.NORMAL_ONLY);
}
@override
EnumSet<MemberUse> fullyUse() =>
invoke(entity.parameterStructure.callStructure);
@override
bool get fullyUsed => hasInvoke;
@override
ParameterStructure get invokedParameters =>
hasInvoke ? entity.parameterStructure : null;
@override
MemberUsage clone() {
return new ConstructorUsage.cloned(entity, _pendingUse.clone(),
hasInvoke: hasInvoke);
}
}
class ParameterTrackingConstructorUsage extends MemberUsage {
final ParameterUsage _parameterUsage;
ParameterTrackingConstructorUsage.cloned(ConstructorEntity constructor,
this._parameterUsage, EnumSet<MemberUse> pendingUse)
: super.cloned(constructor, pendingUse);
ParameterTrackingConstructorUsage(ConstructorEntity constructor)
: _parameterUsage = new ParameterUsage(constructor.parameterStructure),
super.internal(constructor);
@override
ConstructorEntity get entity => super.entity;
@override
EnumSet<MemberUse> get _originalUse => MemberUses.NORMAL_ONLY;
@override
EnumSet<MemberUse> invoke(CallStructure callStructure) {
if (isFullyInvoked) {
return MemberUses.NONE;
}
bool alreadyHasInvoke = hasInvoke;
bool hasPartialChange = _parameterUsage.invoke(callStructure);
EnumSet<MemberUse> result;
if (alreadyHasInvoke) {
result = MemberUses.NONE;
} else {
result = _pendingUse
.removeAll(hasRead ? MemberUses.NONE : MemberUses.NORMAL_ONLY);
}
return hasPartialChange
? result.union(MemberUses.PARTIAL_USE_ONLY)
: result;
}
@override
EnumSet<MemberUse> fullyUse() =>
invoke(entity.parameterStructure.callStructure);
@override
bool get hasInvoke => _parameterUsage.hasInvoke;
@override
bool get fullyUsed => _parameterUsage.isFullyUsed;
@override
bool get hasPendingNormalUse => !isFullyInvoked;
@override
bool get isFullyInvoked => _parameterUsage.isFullyUsed;
@override
ParameterStructure get invokedParameters => _parameterUsage.invokedParameters;
@override
MemberUsage clone() {
return new ParameterTrackingConstructorUsage.cloned(
entity, _parameterUsage.clone(), _pendingUse.clone());
}
}
/// Enum class for the possible kind of use of [MemberEntity] objects.
enum MemberUse {
/// Read or write of a field, or invocation of a method.
NORMAL,
/// Tear-off of an instance method.
CLOSURIZE_INSTANCE,
/// Tear-off of a static method.
CLOSURIZE_STATIC,
/// Invocation that provides previously unprovided optional parameters.
///
/// This is used to check that no partial use is missed by the enqueuer, as
/// asserted through the `Enqueuery.checkEnqueuerConsistency` method.
PARTIAL_USE,
}
/// Common [EnumSet]s used for [MemberUse].
class MemberUses {
static const EnumSet<MemberUse> NONE = const EnumSet<MemberUse>.fixed(0);
static const EnumSet<MemberUse> NORMAL_ONLY =
const EnumSet<MemberUse>.fixed(1);
static const EnumSet<MemberUse> CLOSURIZE_INSTANCE_ONLY =
const EnumSet<MemberUse>.fixed(2);
static const EnumSet<MemberUse> CLOSURIZE_STATIC_ONLY =
const EnumSet<MemberUse>.fixed(4);
static const EnumSet<MemberUse> ALL_INSTANCE =
const EnumSet<MemberUse>.fixed(3);
static const EnumSet<MemberUse> ALL_STATIC =
const EnumSet<MemberUse>.fixed(5);
static const EnumSet<MemberUse> PARTIAL_USE_ONLY =
const EnumSet<MemberUse>.fixed(8);
}
typedef void MemberUsedCallback(MemberEntity member, EnumSet<MemberUse> useSet);
/// Registry for the observed use of a class [entity] in the open world.
// TODO(johnniwinther): Merge this with [InstantiationInfo].
class ClassUsage extends AbstractUsage<ClassUse> {
bool isInstantiated = false;
bool isImplemented = false;
final ClassEntity cls;
ClassUsage(this.cls) : super();
EnumSet<ClassUse> instantiate() {
if (isInstantiated) {
return ClassUses.NONE;
}
isInstantiated = true;
return _pendingUse.removeAll(ClassUses.INSTANTIATED_ONLY);
}
EnumSet<ClassUse> implement() {
if (isImplemented) {
return ClassUses.NONE;
}
isImplemented = true;
return _pendingUse.removeAll(ClassUses.IMPLEMENTED_ONLY);
}
@override
EnumSet<ClassUse> get _originalUse => ClassUses.ALL;
@override
String toString() => '$cls:${appliedUse.iterable(ClassUse.values)}';
}
/// Enum class for the possible kind of use of [ClassEntity] objects.
enum ClassUse { INSTANTIATED, IMPLEMENTED }
/// Common [EnumSet]s used for [ClassUse].
class ClassUses {
static const EnumSet<ClassUse> NONE = const EnumSet<ClassUse>.fixed(0);
static const EnumSet<ClassUse> INSTANTIATED_ONLY =
const EnumSet<ClassUse>.fixed(1);
static const EnumSet<ClassUse> IMPLEMENTED_ONLY =
const EnumSet<ClassUse>.fixed(2);
static const EnumSet<ClassUse> ALL = const EnumSet<ClassUse>.fixed(3);
}
typedef void ClassUsedCallback(ClassEntity cls, EnumSet<ClassUse> useSet);
// TODO(johnniwinther): Merge this with [MemberUsage].
abstract class StaticMemberUsage extends AbstractUsage<MemberUse>
implements MemberUsage {
@override
final MemberEntity entity;
bool hasNormalUse;
bool get hasClosurization => false;
StaticMemberUsage.cloned(this.entity, EnumSet<MemberUse> pendingUse,
{this.hasNormalUse: false})
: super.cloned(pendingUse);
StaticMemberUsage.internal(this.entity)
: this.hasNormalUse = false,
super();
EnumSet<MemberUse> normalUse() {
if (hasNormalUse) {
return MemberUses.NONE;
}
hasNormalUse = true;
return _pendingUse.removeAll(MemberUses.NORMAL_ONLY);
}
EnumSet<MemberUse> tearOff();
@override
EnumSet<MemberUse> init() => normalUse();
@override
EnumSet<MemberUse> constantInit(ConstantValue constant) => normalUse();
@override
EnumSet<MemberUse> read() => tearOff();
@override
EnumSet<MemberUse> write() => normalUse();
@override
EnumSet<MemberUse> invoke(CallStructure callStructure) => normalUse();
@override
EnumSet<MemberUse> fullyUse() => normalUse();
@override
Iterable<ConstantValue> get initialConstants => null;
@override
bool get hasPendingNormalUse => _pendingUse.contains(MemberUse.NORMAL);
@override
bool get isFullyInvoked => hasInvoke;
@override
EnumSet<MemberUse> get _originalUse => MemberUses.NORMAL_ONLY;
@override
bool dataEquals(MemberUsage other) {
assert(entity == other.entity);
return hasInit == other.hasInit &&
hasRead == other.hasRead &&
hasInvoke == other.hasInvoke &&
hasWrite == other.hasWrite &&
hasPendingClosurizationUse == other.hasPendingClosurizationUse &&
hasPendingNormalUse == other.hasPendingNormalUse &&
fullyUsed == other.fullyUsed &&
isFullyInvoked == other.isFullyInvoked &&
_pendingUse == other._pendingUse &&
appliedUse == other.appliedUse;
}
@override
String toString() => '$entity:${appliedUse.iterable(MemberUse.values)}';
}
class GeneralStaticMemberUsage extends StaticMemberUsage {
GeneralStaticMemberUsage(MemberEntity entity) : super.internal(entity);
GeneralStaticMemberUsage.cloned(
MemberEntity entity, EnumSet<MemberUse> pendingUse,
{bool hasNormalUse})
: super.cloned(entity, pendingUse, hasNormalUse: hasNormalUse);
@override
EnumSet<MemberUse> tearOff() => normalUse();
@override
bool get hasInit => true;
@override
bool get fullyUsed => hasNormalUse;
@override
bool get hasInvoke => hasNormalUse;
@override
bool get hasWrite => hasNormalUse;
@override
bool get hasRead => hasNormalUse;
@override
bool get hasPendingClosurizationUse => false;
@override
ParameterStructure get invokedParameters => null;
@override
MemberUsage clone() {
return new GeneralStaticMemberUsage.cloned(entity, _pendingUse.clone(),
hasNormalUse: hasNormalUse);
}
}
class StaticFunctionUsage extends StaticMemberUsage {
@override
bool hasClosurization;
StaticFunctionUsage(FunctionEntity entity)
: hasClosurization = false,
super.internal(entity);
StaticFunctionUsage.cloned(
FunctionEntity entity, EnumSet<MemberUse> pendingUse,
{bool hasNormalUse, this.hasClosurization})
: super.cloned(entity, pendingUse, hasNormalUse: hasNormalUse);
@override
FunctionEntity get entity => super.entity;
@override
bool get hasInit => true;
@override
EnumSet<MemberUse> tearOff() {
if (hasClosurization) {
return MemberUses.NONE;
}
hasNormalUse = hasClosurization = true;
return _pendingUse.removeAll(MemberUses.ALL_STATIC);
}
@override
EnumSet<MemberUse> get _originalUse => MemberUses.ALL_STATIC;
@override
bool get hasPendingClosurizationUse =>
_pendingUse.contains(MemberUse.CLOSURIZE_STATIC);
@override
bool get fullyUsed => hasNormalUse && hasClosurization;
@override
bool get hasInvoke => hasNormalUse;
@override
bool get hasWrite => hasNormalUse;
@override
bool get hasRead => hasClosurization;
@override
ParameterStructure get invokedParameters =>
hasInvoke ? entity.parameterStructure : null;
@override
MemberUsage clone() {
return new StaticFunctionUsage.cloned(entity, _pendingUse.clone(),
hasNormalUse: hasNormalUse, hasClosurization: hasClosurization);
}
}
/// Object used for tracking parameter use in constructor and method
/// invocations.
class ParameterUsage {
/// The original parameter structure of the method or constructor.
final ParameterStructure _parameterStructure;
/// `true` if the method or constructor has at least one invocation.
bool _hasInvoke;
/// The maximum number of (optional) positional parameters provided in
/// invocations of the method or constructor.
///
/// If all positional parameters having been provided this is set to `null`.
int _providedPositionalParameters;
/// `true` if all type parameters have been provided in at least one
/// invocation of the method or constructor.
bool _areAllTypeParametersProvided;
/// The set of named parameters that have not yet been provided in any
/// invocation of the method or constructor.
///
/// If all named parameters have been provided this is set to `null`.
Set<String> _unprovidedNamedParameters;
ParameterUsage(this._parameterStructure) {
_hasInvoke = false;
_areAllTypeParametersProvided = _parameterStructure.typeParameters == 0;
_providedPositionalParameters = _parameterStructure.positionalParameters ==
_parameterStructure.requiredParameters
? null
: 0;
if (!_parameterStructure.namedParameters.isEmpty) {
_unprovidedNamedParameters =
new Set<String>.from(_parameterStructure.namedParameters);
}
}
ParameterUsage.cloned(this._parameterStructure,
{bool hasInvoke,
int providedPositionalParameters,
bool areAllTypeParametersProvided,
Set<String> unprovidedNamedParameters})
: _hasInvoke = hasInvoke,
_providedPositionalParameters = providedPositionalParameters,
_areAllTypeParametersProvided = areAllTypeParametersProvided,
_unprovidedNamedParameters = unprovidedNamedParameters;
bool invoke(CallStructure callStructure) {
if (isFullyUsed) return false;
_hasInvoke = true;
bool changed = false;
if (_providedPositionalParameters != null) {
int newProvidedPositionalParameters = Math.max(
_providedPositionalParameters, callStructure.positionalArgumentCount);
changed |=
newProvidedPositionalParameters != _providedPositionalParameters;
_providedPositionalParameters = newProvidedPositionalParameters;
if (_providedPositionalParameters >=
_parameterStructure.positionalParameters) {
_providedPositionalParameters = null;
}
}
if (_unprovidedNamedParameters != null &&
callStructure.namedArguments.isNotEmpty) {
int _providedNamedParametersCount = _unprovidedNamedParameters.length;
_unprovidedNamedParameters.removeAll(callStructure.namedArguments);
changed |=
_providedNamedParametersCount != _unprovidedNamedParameters.length;
if (_unprovidedNamedParameters.isEmpty) {
_unprovidedNamedParameters = null;
}
}
if (!_areAllTypeParametersProvided && callStructure.typeArgumentCount > 0) {
_areAllTypeParametersProvided = true;
changed = true;
}
return changed;
}
bool get hasInvoke => _hasInvoke;
bool get isFullyUsed =>
_hasInvoke &&
_providedPositionalParameters == null &&
_unprovidedNamedParameters == null &&
_areAllTypeParametersProvided;
void fullyUse() {
_hasInvoke = true;
_providedPositionalParameters = null;
_unprovidedNamedParameters = null;
_areAllTypeParametersProvided = true;
}
ParameterStructure get invokedParameters {
if (!_hasInvoke) return null;
if (isFullyUsed) return _parameterStructure;
return new ParameterStructure(
_parameterStructure.requiredParameters,
_providedPositionalParameters ??
_parameterStructure.positionalParameters,
_unprovidedNamedParameters == null
? _parameterStructure.namedParameters
: _parameterStructure.namedParameters
.where((n) => !_unprovidedNamedParameters.contains(n))
.toList(),
_areAllTypeParametersProvided ? _parameterStructure.typeParameters : 0);
}
ParameterUsage clone() {
return new ParameterUsage.cloned(_parameterStructure,
hasInvoke: _hasInvoke,
providedPositionalParameters: _providedPositionalParameters,
areAllTypeParametersProvided: _areAllTypeParametersProvided,
unprovidedNamedParameters: _unprovidedNamedParameters?.toSet());
}
}