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:
Johnni Winther 2019-03-29 16:44:15 +00:00 committed by commit-bot@chromium.org
parent da0de0a867
commit 7ef28f0b03
11 changed files with 534 additions and 137 deletions

View file

@ -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].

View file

@ -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].

View file

@ -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,

View file

@ -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());
}
}

View file

@ -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

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
});
}

View file

@ -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;
}

View file

@ -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);
});
}