mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 20:51:50 +00:00
Handle constant field init registration correctly.
+ avoid side-effects of checking enqueuer invariants Change-Id: Id327371bc70e4b1b5fd4d326b8c8cf8caf50ea0e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/98142 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
parent
da0de0a867
commit
7ef28f0b03
|
@ -308,7 +308,7 @@ class ResolutionEnqueuer extends EnqueuerImpl {
|
|||
_reporter.internalError(member,
|
||||
'Unenqueued use of $member: ${useSet.iterable(MemberUse.values)}');
|
||||
}
|
||||
});
|
||||
}, dryRun: true);
|
||||
}
|
||||
|
||||
/// Callback for applying the use of a [member].
|
||||
|
|
|
@ -121,12 +121,13 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
|||
|
||||
@override
|
||||
void checkClass(ClassEntity cls) {
|
||||
_worldBuilder.processClassMembers(cls, (MemberEntity member, useSet) {
|
||||
_worldBuilder.processClassMembers(cls,
|
||||
(MemberEntity member, EnumSet<MemberUse> useSet) {
|
||||
if (useSet.isNotEmpty) {
|
||||
failedAt(member,
|
||||
'Unenqueued use of $member: ${useSet.iterable(MemberUse.values)}');
|
||||
}
|
||||
});
|
||||
}, dryRun: true);
|
||||
}
|
||||
|
||||
/// Callback for applying the use of a [cls].
|
||||
|
|
|
@ -478,29 +478,33 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
closurizedMembers.add(element);
|
||||
}
|
||||
|
||||
void processClassMembers(ClassEntity cls, MemberUsedCallback memberUsed) {
|
||||
void processClassMembers(ClassEntity cls, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false}) {
|
||||
_elementEnvironment.forEachClassMember(cls,
|
||||
(ClassEntity cls, MemberEntity member) {
|
||||
_processInstantiatedClassMember(cls, member, memberUsed);
|
||||
_processInstantiatedClassMember(cls, member, memberUsed, dryRun: dryRun);
|
||||
});
|
||||
}
|
||||
|
||||
void _processInstantiatedClassMember(ClassEntity cls,
|
||||
covariant MemberEntity member, MemberUsedCallback memberUsed) {
|
||||
covariant MemberEntity member, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false}) {
|
||||
if (!member.isInstanceMember) return;
|
||||
_getMemberUsage(member, memberUsed);
|
||||
}
|
||||
|
||||
MemberUsage _getMemberUsage(
|
||||
covariant MemberEntity member, MemberUsedCallback memberUsed) {
|
||||
covariant MemberEntity member, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false}) {
|
||||
// TODO(johnniwinther): Change [TypeMask] to not apply to a superclass
|
||||
// member unless the class has been instantiated. Similar to
|
||||
// [StrongModeConstraint].
|
||||
return _instanceMemberUsage.putIfAbsent(member, () {
|
||||
MemberUsage usage = _instanceMemberUsage[member];
|
||||
if (usage == null) {
|
||||
String memberName = member.name;
|
||||
ClassEntity cls = member.enclosingClass;
|
||||
bool isNative = _nativeBasicData.isNativeClass(cls);
|
||||
MemberUsage usage = new MemberUsage(member, isNative: isNative);
|
||||
usage = new MemberUsage(member, isNative: isNative);
|
||||
EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
|
||||
useSet.addAll(usage.appliedUse);
|
||||
if (!usage.hasRead && hasInvokedGetter(member)) {
|
||||
|
@ -515,6 +519,7 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
useSet.addAll(usage.invoke(null));
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
if (usage.hasPendingClosurizationUse) {
|
||||
// Store the member in [instanceFunctionsByName] to catch
|
||||
// getters on the function.
|
||||
|
@ -529,9 +534,15 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
.putIfAbsent(memberName, () => new Set<MemberUsage>())
|
||||
.add(usage);
|
||||
}
|
||||
_instanceMemberUsage[member] = usage;
|
||||
}
|
||||
memberUsed(member, useSet);
|
||||
} else {
|
||||
if (dryRun) {
|
||||
usage = usage.clone();
|
||||
}
|
||||
}
|
||||
return usage;
|
||||
});
|
||||
}
|
||||
|
||||
void _processSet(Map<String, Set<MemberUsage>> map, String memberName,
|
||||
|
|
|
@ -11,9 +11,11 @@ import '../util/enumset.dart';
|
|||
import 'call_structure.dart';
|
||||
|
||||
abstract class AbstractUsage<T> {
|
||||
final EnumSet<T> _pendingUse = new EnumSet<T>();
|
||||
final EnumSet<T> _pendingUse;
|
||||
|
||||
AbstractUsage() {
|
||||
AbstractUsage.cloned(this._pendingUse);
|
||||
|
||||
AbstractUsage() : this._pendingUse = new EnumSet<T>() {
|
||||
_pendingUse.addAll(_originalUse);
|
||||
}
|
||||
|
||||
|
@ -37,7 +39,10 @@ abstract class AbstractUsage<T> {
|
|||
abstract class MemberUsage extends AbstractUsage<MemberUse> {
|
||||
final MemberEntity entity;
|
||||
|
||||
MemberUsage.internal(this.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}) {
|
||||
|
@ -150,20 +155,43 @@ abstract class MemberUsage extends AbstractUsage<MemberUse> {
|
|||
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 = false;
|
||||
bool hasInit;
|
||||
@override
|
||||
bool hasRead = false;
|
||||
bool hasRead;
|
||||
@override
|
||||
bool hasWrite = false;
|
||||
bool hasWrite;
|
||||
|
||||
FieldUsage.cloned(FieldEntity field, EnumSet<MemberUse> pendingUse,
|
||||
{this.hasInit, this.hasRead, this.hasWrite})
|
||||
: super.cloned(field, pendingUse);
|
||||
|
||||
FieldUsage(FieldEntity field, {bool isNative: false})
|
||||
: super.internal(field) {
|
||||
: hasInit = false,
|
||||
hasRead = false,
|
||||
hasWrite = false,
|
||||
super.internal(field) {
|
||||
// TODO(johnniwinther): Track native fields through member usage.
|
||||
if (!isNative) {
|
||||
init();
|
||||
|
@ -227,6 +255,12 @@ class FieldUsage extends MemberUsage {
|
|||
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)}';
|
||||
|
@ -234,12 +268,18 @@ class FieldUsage extends MemberUsage {
|
|||
|
||||
class FinalFieldUsage extends MemberUsage {
|
||||
@override
|
||||
bool hasInit = false;
|
||||
bool hasInit;
|
||||
@override
|
||||
bool hasRead = false;
|
||||
bool hasRead;
|
||||
|
||||
FinalFieldUsage.cloned(FieldEntity field, EnumSet<MemberUse> pendingUse,
|
||||
{this.hasInit, this.hasRead})
|
||||
: super.cloned(field, pendingUse);
|
||||
|
||||
FinalFieldUsage(FieldEntity field, {bool isNative: false})
|
||||
: super.internal(field) {
|
||||
: this.hasInit = false,
|
||||
this.hasRead = false,
|
||||
super.internal(field) {
|
||||
if (!isNative) {
|
||||
init();
|
||||
}
|
||||
|
@ -289,6 +329,12 @@ class FinalFieldUsage extends MemberUsage {
|
|||
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)}';
|
||||
|
@ -296,11 +342,18 @@ class FinalFieldUsage extends MemberUsage {
|
|||
|
||||
class FunctionUsage extends MemberUsage {
|
||||
@override
|
||||
bool hasInvoke = false;
|
||||
bool hasInvoke;
|
||||
@override
|
||||
bool hasRead = false;
|
||||
bool hasRead;
|
||||
|
||||
FunctionUsage(FunctionEntity function) : super.internal(function) {
|
||||
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.
|
||||
|
@ -361,16 +414,28 @@ class FunctionUsage extends MemberUsage {
|
|||
@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 = false;
|
||||
bool hasRead;
|
||||
|
||||
final ParameterUsage _parameterUsage;
|
||||
|
||||
ParameterTrackingFunctionUsage.cloned(FunctionEntity function,
|
||||
this._parameterUsage, EnumSet<MemberUse> pendingUse,
|
||||
{this.hasRead})
|
||||
: super.cloned(function, pendingUse);
|
||||
|
||||
ParameterTrackingFunctionUsage(FunctionEntity function)
|
||||
: _parameterUsage = new ParameterUsage(function.parameterStructure),
|
||||
: 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
|
||||
|
@ -447,13 +512,26 @@ class ParameterTrackingFunctionUsage extends MemberUsage {
|
|||
|
||||
@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 = false;
|
||||
bool hasRead;
|
||||
|
||||
GetterUsage(FunctionEntity getter) : super.internal(getter);
|
||||
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;
|
||||
|
@ -472,13 +550,25 @@ class GetterUsage extends MemberUsage {
|
|||
|
||||
@override
|
||||
EnumSet<MemberUse> fullyUse() => read();
|
||||
|
||||
@override
|
||||
MemberUsage clone() {
|
||||
return new GetterUsage.cloned(entity, _pendingUse.clone(),
|
||||
hasRead: hasRead);
|
||||
}
|
||||
}
|
||||
|
||||
class SetterUsage extends MemberUsage {
|
||||
@override
|
||||
bool hasWrite = false;
|
||||
bool hasWrite;
|
||||
|
||||
SetterUsage(FunctionEntity setter) : super.internal(setter);
|
||||
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;
|
||||
|
@ -494,13 +584,26 @@ class SetterUsage extends MemberUsage {
|
|||
|
||||
@override
|
||||
EnumSet<MemberUse> fullyUse() => write();
|
||||
|
||||
@override
|
||||
MemberUsage clone() {
|
||||
return new SetterUsage.cloned(entity, _pendingUse.clone(),
|
||||
hasWrite: hasWrite);
|
||||
}
|
||||
}
|
||||
|
||||
class ConstructorUsage extends MemberUsage {
|
||||
@override
|
||||
bool hasInvoke = false;
|
||||
bool hasInvoke;
|
||||
|
||||
ConstructorUsage(ConstructorEntity constructor) : super.internal(constructor);
|
||||
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;
|
||||
|
@ -528,11 +631,21 @@ class ConstructorUsage extends MemberUsage {
|
|||
@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);
|
||||
|
@ -580,6 +693,12 @@ class ParameterTrackingConstructorUsage extends MemberUsage {
|
|||
|
||||
@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.
|
||||
|
@ -627,7 +746,7 @@ class ClassUsage extends AbstractUsage<ClassUse> {
|
|||
|
||||
final ClassEntity cls;
|
||||
|
||||
ClassUsage(this.cls);
|
||||
ClassUsage(this.cls) : super();
|
||||
|
||||
EnumSet<ClassUse> instantiate() {
|
||||
if (isInstantiated) {
|
||||
|
@ -673,10 +792,16 @@ abstract class StaticMemberUsage extends AbstractUsage<MemberUse>
|
|||
@override
|
||||
final MemberEntity entity;
|
||||
|
||||
bool hasNormalUse = false;
|
||||
bool hasNormalUse;
|
||||
bool get hasClosurization => false;
|
||||
|
||||
StaticMemberUsage.internal(this.entity);
|
||||
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) {
|
||||
|
@ -712,6 +837,21 @@ abstract class StaticMemberUsage extends AbstractUsage<MemberUse>
|
|||
@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)}';
|
||||
}
|
||||
|
@ -719,6 +859,11 @@ abstract class StaticMemberUsage extends AbstractUsage<MemberUse>
|
|||
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();
|
||||
|
||||
|
@ -742,13 +887,26 @@ class GeneralStaticMemberUsage extends StaticMemberUsage {
|
|||
|
||||
@override
|
||||
ParameterStructure get invokedParameters => null;
|
||||
|
||||
@override
|
||||
MemberUsage clone() {
|
||||
return new GeneralStaticMemberUsage.cloned(entity, _pendingUse.clone(),
|
||||
hasNormalUse: hasNormalUse);
|
||||
}
|
||||
}
|
||||
|
||||
class StaticFunctionUsage extends StaticMemberUsage {
|
||||
@override
|
||||
bool hasClosurization = false;
|
||||
bool hasClosurization;
|
||||
|
||||
StaticFunctionUsage(FunctionEntity entity) : super.internal(entity);
|
||||
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;
|
||||
|
@ -787,6 +945,12 @@ class StaticFunctionUsage extends StaticMemberUsage {
|
|||
@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
|
||||
|
@ -827,6 +991,16 @@ class ParameterUsage {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -888,4 +1062,12 @@ class ParameterUsage {
|
|||
.toList(),
|
||||
_areAllTypeParametersProvided ? _parameterStructure.typeParameters : 0);
|
||||
}
|
||||
|
||||
ParameterUsage clone() {
|
||||
return new ParameterUsage.cloned(_parameterStructure,
|
||||
hasInvoke: _hasInvoke,
|
||||
providedPositionalParameters: _providedPositionalParameters,
|
||||
areAllTypeParametersProvided: _areAllTypeParametersProvided,
|
||||
unprovidedNamedParameters: _unprovidedNamedParameters?.toSet());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,7 +108,8 @@ abstract class ResolutionEnqueuerWorldBuilder extends ResolutionWorldBuilder {
|
|||
|
||||
/// Computes usage for all members declared by [cls]. Calls [membersUsed] with
|
||||
/// the usage changes for each member.
|
||||
void processClassMembers(ClassEntity cls, MemberUsedCallback memberUsed);
|
||||
void processClassMembers(ClassEntity cls, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false});
|
||||
|
||||
/// Applies the [dynamicUse] to applicable instance members. Calls
|
||||
/// [membersUsed] with the usage changes for each member.
|
||||
|
@ -738,11 +739,7 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
|
||||
MemberEntity element = staticUse.element;
|
||||
EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
|
||||
MemberUsage usage = _memberUsage.putIfAbsent(element, () {
|
||||
MemberUsage usage = new MemberUsage(element, trackParameters: true);
|
||||
useSet.addAll(usage.appliedUse);
|
||||
return usage;
|
||||
});
|
||||
MemberUsage usage = _getMemberUsage(element, useSet);
|
||||
|
||||
if ((element.isStatic || element.isTopLevel) && element.isField) {
|
||||
allReferencedStaticFields.add(staticUse.element);
|
||||
|
@ -837,10 +834,11 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
}
|
||||
|
||||
@override
|
||||
void processClassMembers(ClassEntity cls, MemberUsedCallback memberUsed) {
|
||||
void processClassMembers(ClassEntity cls, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false}) {
|
||||
_elementEnvironment.forEachClassMember(cls,
|
||||
(ClassEntity cls, MemberEntity member) {
|
||||
_processInstantiatedClassMember(cls, member, memberUsed);
|
||||
_processInstantiatedClassMember(cls, member, memberUsed, dryRun: dryRun);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -864,10 +862,15 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
map[memberName].addAll(remaining);
|
||||
}
|
||||
|
||||
void _processInstantiatedClassMember(ClassEntity cls,
|
||||
covariant MemberEntity member, MemberUsedCallback memberUsed) {
|
||||
if (!member.isInstanceMember) return;
|
||||
MemberUsage _getMemberUsage(MemberEntity member, EnumSet<MemberUse> useSet,
|
||||
{bool dryRun: false}) {
|
||||
MemberUsage usage = _memberUsage[member];
|
||||
if (usage == null) {
|
||||
if (member.isInstanceMember) {
|
||||
String memberName = member.name;
|
||||
ClassEntity cls = member.enclosingClass;
|
||||
// TODO(johnniwinther): Change this to use isNativeMember when we use
|
||||
// CFE constants.
|
||||
// The obvious thing to test here would be "member.isNative",
|
||||
// however, that only works after metadata has been parsed/analyzed,
|
||||
// and that may not have happened yet.
|
||||
|
@ -875,14 +878,11 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
// its metadata parsed and analyzed.
|
||||
// Note: this assumes that there are no non-native fields on native
|
||||
// classes, which may not be the case when a native class is subclassed.
|
||||
bool newUsage = false;
|
||||
MemberUsage usage = _memberUsage.putIfAbsent(member, () {
|
||||
newUsage = true;
|
||||
bool isNative = _nativeBasicData.isNativeClass(cls);
|
||||
EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
|
||||
MemberUsage usage =
|
||||
usage =
|
||||
new MemberUsage(member, isNative: isNative, trackParameters: true);
|
||||
useSet.addAll(usage.appliedUse);
|
||||
if (!dryRun) {
|
||||
if (member.isField && isNative) {
|
||||
registerUsedElement(member);
|
||||
}
|
||||
|
@ -891,6 +891,7 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
_elementEnvironment.isGenericClass(cls)) {
|
||||
closurizedMembersWithFreeTypeVariables.add(member);
|
||||
}
|
||||
}
|
||||
|
||||
if (!usage.hasRead && _hasInvokedGetter(member)) {
|
||||
useSet.addAll(usage.read());
|
||||
|
@ -909,6 +910,7 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
useSet.addAll(usage.write());
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
if (usage.hasPendingNormalUse) {
|
||||
// The element is not yet used. Add it to the list of instance
|
||||
// members to still be processed.
|
||||
|
@ -923,10 +925,35 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
.putIfAbsent(memberName, () => new Set<MemberUsage>())
|
||||
.add(usage);
|
||||
}
|
||||
memberUsed(usage.entity, useSet);
|
||||
}
|
||||
} else {
|
||||
usage = new MemberUsage(member, trackParameters: true);
|
||||
useSet.addAll(usage.appliedUse);
|
||||
}
|
||||
}
|
||||
if (!dryRun) {
|
||||
_memberUsage[member] = usage;
|
||||
}
|
||||
return usage;
|
||||
});
|
||||
if (!usage.fullyUsed && !newUsage) {
|
||||
}
|
||||
|
||||
void _processInstantiatedClassMember(ClassEntity cls,
|
||||
covariant MemberEntity member, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false}) {
|
||||
if (!member.isInstanceMember) return;
|
||||
String memberName = member.name;
|
||||
|
||||
MemberUsage usage = _memberUsage[member];
|
||||
if (usage == null) {
|
||||
EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
|
||||
usage = _getMemberUsage(member, useSet, dryRun: dryRun);
|
||||
memberUsed(usage.entity, useSet);
|
||||
} else {
|
||||
MemberUsage original = usage;
|
||||
if (dryRun) {
|
||||
usage = usage.clone();
|
||||
}
|
||||
if (!usage.fullyUsed) {
|
||||
EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
|
||||
if (!usage.hasRead && _hasInvokedGetter(member)) {
|
||||
useSet.addAll(usage.read());
|
||||
|
@ -944,14 +971,21 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
if (!usage.hasWrite && hasInvokedSetter(member)) {
|
||||
useSet.addAll(usage.write());
|
||||
}
|
||||
if (!dryRun) {
|
||||
if (!usage.hasPendingNormalUse) {
|
||||
_instanceMembersByName[memberName]?.remove(usage);
|
||||
}
|
||||
if (!usage.hasPendingClosurizationUse) {
|
||||
_instanceFunctionsByName[memberName]?.remove(usage);
|
||||
}
|
||||
}
|
||||
memberUsed(usage.entity, useSet);
|
||||
}
|
||||
if (dryRun && !original.dataEquals(usage)) {
|
||||
_elementMap.reporter.internalError(member,
|
||||
'Unenqueued usage of $member: before: $original, after: $usage');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -98,6 +98,9 @@ abstract class EnumSet<E> {
|
|||
/// Returns `true` if this set is not empty.
|
||||
bool get isNotEmpty => value != 0;
|
||||
|
||||
/// Returns a new mutable enum set that contains the values of this set.
|
||||
EnumSet<E> clone() => new EnumSet<E>.fromValue(value);
|
||||
|
||||
@override
|
||||
int get hashCode => value.hashCode * 19;
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
// Derived from dart2js_extra/constant_folding_test
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
/*element: main:calls=[checkAll$1(1),checkAll$1(1),checkAll$1(1),checkAll$1(1),checkAll$1(1),checkAll$1(1),checkAll$1(1)],params=0*/
|
||||
void main() {
|
||||
const BitNot(42, 4294967253).check();
|
||||
const BitNot(4294967253, 42).check();
|
||||
const BitNot(-42, 41).check();
|
||||
const BitNot(-1, 0).check();
|
||||
const BitNot(0, 0xFFFFFFFF).check();
|
||||
const BitNot(4294967295, 0).check();
|
||||
const BitNot(0x12121212121212, 0xEDEDEDED).check();
|
||||
}
|
||||
|
||||
/*element: jsEquals:calls=[Expect_equals(3),Expect_equals(3),get$isNegative(1),get$isNegative(1),toString$0(1),toString$0(1)],params=3*/
|
||||
void jsEquals(expected, actual, [String reason = null]) {
|
||||
if (expected is num && actual is num) {
|
||||
if (expected.isNaN && actual.isNaN) return;
|
||||
}
|
||||
|
||||
Expect.equals(expected, actual, reason);
|
||||
|
||||
if (expected == 0 && actual == 0) {
|
||||
Expect.equals(
|
||||
expected.isNegative,
|
||||
actual.isNegative,
|
||||
(reason == null ? "" : "$reason ") +
|
||||
"${expected.toString()} and "
|
||||
"${actual.toString()} have different signs.");
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TestOp {
|
||||
final expected;
|
||||
|
||||
final result;
|
||||
|
||||
const TestOp(this.expected, this.result);
|
||||
|
||||
/*element: TestOp.checkAll:access=[arg,expected,result],calls=[jsEquals(3),jsEquals(3),jsEquals(3)],params=1*/
|
||||
@pragma('dart2js:noInline')
|
||||
checkAll(evalResult) {
|
||||
jsEquals(expected, result,
|
||||
"Frontend constant evaluation does not yield expected value.");
|
||||
jsEquals(expected, evalResult,
|
||||
"Backend constant evaluation does not yield expected value.");
|
||||
jsEquals(expected, eval(), "eval() does not yield expected value.");
|
||||
}
|
||||
|
||||
eval();
|
||||
}
|
||||
|
||||
class BitNot extends TestOp {
|
||||
/*element: BitNot.arg:emitted*/
|
||||
final arg;
|
||||
|
||||
const BitNot(this.arg, expected) : super(expected, ~arg);
|
||||
|
||||
@pragma('dart2js:tryInline')
|
||||
check() => checkAll(eval());
|
||||
|
||||
@override
|
||||
@pragma('dart2js:tryInline')
|
||||
eval() => ~arg;
|
||||
}
|
|
@ -26,11 +26,15 @@ method1(dynamic c) {
|
|||
class Class2a<T> {
|
||||
/*strong.element: Class2a.field2:checked,elided*/
|
||||
/*omit.element: Class2a.field2:elided*/
|
||||
/*strongConst.element: Class2a.field2:checked,elided*/
|
||||
/*omitConst.element: Class2a.field2:elided*/
|
||||
T field2;
|
||||
}
|
||||
|
||||
/*strong.element: method2:calls=[set$field2(1)],params=1*/
|
||||
/*omit.element: method2:params=1*/
|
||||
/*strongConst.element: method2:calls=[set$field2(1)],params=1*/
|
||||
/*omitConst.element: method2:params=1*/
|
||||
@pragma('dart2js:noInline')
|
||||
method2(dynamic c) {
|
||||
c.field2 = 42;
|
||||
|
@ -39,12 +43,16 @@ method2(dynamic c) {
|
|||
class Class3a {
|
||||
/*strong.element: Class3a.field3:checked,elided*/
|
||||
/*omit.element: Class3a.field3:elided,set=simple*/
|
||||
/*strongConst.element: Class3a.field3:checked,elided*/
|
||||
/*omitConst.element: Class3a.field3:elided,set=simple*/
|
||||
int field3;
|
||||
}
|
||||
|
||||
class Class3b {
|
||||
/*strong.element: Class3b.field3:checked,elided*/
|
||||
/*omit.element: Class3b.field3:elided,set=simple*/
|
||||
/*strongConst.element: Class3b.field3:checked,elided*/
|
||||
/*omitConst.element: Class3b.field3:elided,set=simple*/
|
||||
int field3;
|
||||
}
|
||||
|
||||
|
@ -57,12 +65,17 @@ method3(dynamic c) {
|
|||
class Class4a {
|
||||
/*strong.element: Class4a.field4:checked,elided*/
|
||||
/*omit.element: Class4a.field4:elided,set=simple*/
|
||||
/*strongConst.element: Class4a.field4:checked,elided*/
|
||||
/*omitConst.element: Class4a.field4:elided,set=simple*/
|
||||
int field4;
|
||||
}
|
||||
|
||||
class Class4b implements Class4a {
|
||||
/*strong.element: Class4b.field4:checked,elided*/
|
||||
/*omit.element: Class4b.field4:elided,set=simple*/
|
||||
/*strongConst.element: Class4b.field4:checked,elided*/
|
||||
/*omitConst.element: Class4b.field4:elided,set=simple*/
|
||||
@override
|
||||
int field4;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ main(List<String> args) {
|
|||
asyncTest(() async {
|
||||
Directory dataDir =
|
||||
new Directory.fromUri(Platform.script.resolve('model_data'));
|
||||
await checkTests(dataDir, const ModelDataComputer(), args: args);
|
||||
await checkTests(dataDir, const ModelDataComputer(),
|
||||
args: args, testCFEConstants: true);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
// Derived from dart2js_extra/constant_folding_test
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
/*element: main:invoke*/
|
||||
void main() {
|
||||
const BitNot(42, 4294967253).check();
|
||||
const BitNot(4294967253, 42).check();
|
||||
const BitNot(-42, 41).check();
|
||||
const BitNot(-1, 0).check();
|
||||
const BitNot(0, 0xFFFFFFFF).check();
|
||||
const BitNot(4294967295, 0).check();
|
||||
const BitNot(0x12121212121212, 0xEDEDEDED).check();
|
||||
}
|
||||
|
||||
/*element: jsEquals:invoke*/
|
||||
void jsEquals(expected, actual, [String reason = null]) {
|
||||
if (expected is num && actual is num) {
|
||||
if (expected.isNaN && actual.isNaN) return;
|
||||
}
|
||||
|
||||
Expect.equals(expected, actual, reason);
|
||||
|
||||
if (expected == 0 && actual == 0) {
|
||||
Expect.equals(
|
||||
expected.isNegative,
|
||||
actual.isNegative,
|
||||
(reason == null ? "" : "$reason ") +
|
||||
"${expected.toString()} and "
|
||||
"${actual.toString()} have different signs.");
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TestOp {
|
||||
/*element: TestOp.expected:init,read*/
|
||||
final expected;
|
||||
|
||||
/*element: TestOp.result:init,read*/
|
||||
final result;
|
||||
|
||||
/*strong.element: TestOp.:invoke*/
|
||||
const TestOp(this.expected, this.result);
|
||||
|
||||
/*element: TestOp.checkAll:invoke*/
|
||||
@pragma('dart2js:noInline')
|
||||
checkAll(evalResult) {
|
||||
jsEquals(expected, result,
|
||||
"Frontend constant evaluation does not yield expected value.");
|
||||
jsEquals(expected, evalResult,
|
||||
"Backend constant evaluation does not yield expected value.");
|
||||
jsEquals(expected, eval(), "eval() does not yield expected value.");
|
||||
}
|
||||
|
||||
eval();
|
||||
}
|
||||
|
||||
class BitNot extends TestOp {
|
||||
/*element: BitNot.arg:init,read*/
|
||||
final arg;
|
||||
|
||||
/*strong.element: BitNot.:invoke*/
|
||||
const BitNot(this.arg, expected) : super(expected, ~arg);
|
||||
|
||||
/*element: BitNot.check:invoke*/
|
||||
@pragma('dart2js:tryInline')
|
||||
check() => checkAll(eval());
|
||||
|
||||
/*element: BitNot.eval:invoke*/
|
||||
@override
|
||||
@pragma('dart2js:tryInline')
|
||||
eval() => ~arg;
|
||||
}
|
|
@ -23,12 +23,18 @@ main(List<String> args) {
|
|||
print(' Test with enqueuer checks');
|
||||
print('------------------------------------------------------------------');
|
||||
await checkTests(dataDir, const ClosedWorldDataComputer(false),
|
||||
args: args, testOmit: false, testFrontend: true);
|
||||
args: args,
|
||||
testOmit: false,
|
||||
testFrontend: true,
|
||||
testCFEConstants: true);
|
||||
print('------------------------------------------------------------------');
|
||||
print(' Test without enqueuer checks');
|
||||
print('------------------------------------------------------------------');
|
||||
await checkTests(dataDir, const ClosedWorldDataComputer(true),
|
||||
args: args, testOmit: false, testFrontend: true);
|
||||
args: args,
|
||||
testOmit: false,
|
||||
testFrontend: true,
|
||||
testCFEConstants: true);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue