| // 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 '../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 = new EnumSet<T>(); |
| |
| AbstractUsage() { |
| _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); |
| |
| 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; |
| |
| /// `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 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; |
| } |
| |
| @override |
| String toString() => '$entity:${appliedUse.iterable(MemberUse.values)}'; |
| } |
| |
| class FieldUsage extends MemberUsage { |
| @override |
| bool hasInit = false; |
| @override |
| bool hasRead = false; |
| @override |
| bool hasWrite = false; |
| |
| FieldUsage(FieldEntity field, {bool isNative: false}) |
| : super.internal(field) { |
| // TODO(johnniwinther): Track native fields through member usage. |
| if (!isNative) { |
| init(); |
| } |
| } |
| |
| @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> 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 |
| String toString() => 'FieldUsage($entity,hasInit=$hasInit,hasRead=$hasRead,' |
| 'hasWrite=$hasWrite,pendingUse=${_pendingUse.iterable(MemberUse.values)}'; |
| } |
| |
| class FinalFieldUsage extends MemberUsage { |
| @override |
| bool hasInit = false; |
| @override |
| bool hasRead = false; |
| |
| FinalFieldUsage(FieldEntity field, {bool isNative: false}) |
| : super.internal(field) { |
| if (!isNative) { |
| init(); |
| } |
| } |
| |
| @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> 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 |
| String toString() => 'FinalFieldUsage($entity,hasInit=$hasInit,' |
| 'hasRead=$hasRead,pendingUse=${_pendingUse.iterable(MemberUse.values)}'; |
| } |
| |
| class FunctionUsage extends MemberUsage { |
| @override |
| bool hasInvoke = false; |
| @override |
| bool hasRead = false; |
| |
| FunctionUsage(FunctionEntity function) : 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; |
| } |
| |
| class ParameterTrackingFunctionUsage extends MemberUsage { |
| @override |
| bool hasRead = false; |
| |
| final ParameterUsage _parameterUsage; |
| |
| ParameterTrackingFunctionUsage(FunctionEntity function) |
| : _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; |
| } |
| |
| class GetterUsage extends MemberUsage { |
| @override |
| bool hasRead = false; |
| |
| GetterUsage(FunctionEntity getter) : 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(); |
| } |
| |
| class SetterUsage extends MemberUsage { |
| @override |
| bool hasWrite = false; |
| |
| SetterUsage(FunctionEntity setter) : 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(); |
| } |
| |
| class ConstructorUsage extends MemberUsage { |
| @override |
| bool hasInvoke = false; |
| |
| ConstructorUsage(ConstructorEntity constructor) : 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; |
| } |
| |
| class ParameterTrackingConstructorUsage extends MemberUsage { |
| final ParameterUsage _parameterUsage; |
| |
| 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; |
| } |
| |
| /// 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); |
| |
| 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 = false; |
| bool get hasClosurization => false; |
| |
| StaticMemberUsage.internal(this.entity); |
| |
| 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> read() => tearOff(); |
| |
| @override |
| EnumSet<MemberUse> write() => normalUse(); |
| |
| @override |
| EnumSet<MemberUse> invoke(CallStructure callStructure) => normalUse(); |
| |
| @override |
| EnumSet<MemberUse> fullyUse() => normalUse(); |
| |
| @override |
| bool get hasPendingNormalUse => _pendingUse.contains(MemberUse.NORMAL); |
| |
| @override |
| bool get isFullyInvoked => hasInvoke; |
| |
| @override |
| EnumSet<MemberUse> get _originalUse => MemberUses.NORMAL_ONLY; |
| |
| @override |
| String toString() => '$entity:${appliedUse.iterable(MemberUse.values)}'; |
| } |
| |
| class GeneralStaticMemberUsage extends StaticMemberUsage { |
| GeneralStaticMemberUsage(MemberEntity entity) : super.internal(entity); |
| |
| @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; |
| } |
| |
| class StaticFunctionUsage extends StaticMemberUsage { |
| @override |
| bool hasClosurization = false; |
| |
| StaticFunctionUsage(FunctionEntity entity) : super.internal(entity); |
| |
| @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; |
| } |
| |
| /// 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); |
| } |
| } |
| |
| 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); |
| } |
| } |