Fix registration of (un)provided parameters.
Change-Id: I5cf1b7543561a728a219a62c8fa6633d4d25846a
Reviewed-on: https://dart-review.googlesource.com/c/91223
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/enqueue.dart b/pkg/compiler/lib/src/enqueue.dart
index 499f749..4defca5 100644
--- a/pkg/compiler/lib/src/enqueue.dart
+++ b/pkg/compiler/lib/src/enqueue.dart
@@ -76,6 +76,14 @@
}
abstract class Enqueuer {
+ /// If `true` the checking for unenqueued members is skipped. The current
+ /// implementation registers parameter usages as a side-effect so unit
+ /// testing of member usage we need to test both with and without the
+ /// enqueuer check.
+ // TODO(johnniwinther): [checkEnqueuerConsistency] should not have
+ // side-effects.
+ static bool skipEnqueuerCheckForTesting = false;
+
WorldBuilder get worldBuilder;
void open(ImpactStrategy impactStrategy, FunctionEntity mainMethod,
@@ -277,6 +285,7 @@
bool checkNoEnqueuedInvokedInstanceMethods(
ElementEnvironment elementEnvironment) {
+ if (Enqueuer.skipEnqueuerCheckForTesting) return true;
return checkEnqueuerConsistency(elementEnvironment);
}
diff --git a/pkg/compiler/lib/src/universe/member_usage.dart b/pkg/compiler/lib/src/universe/member_usage.dart
index b7d31e9..57481fb 100644
--- a/pkg/compiler/lib/src/universe/member_usage.dart
+++ b/pkg/compiler/lib/src/universe/member_usage.dart
@@ -332,12 +332,17 @@
return MemberUses.NONE;
}
bool alreadyHasInvoke = hasInvoke;
- _parameterUsage.invoke(callStructure);
+ bool hasPartialChange = _parameterUsage.invoke(callStructure);
+ EnumSet<MemberUse> result;
if (alreadyHasInvoke) {
- return MemberUses.NONE;
+ result = MemberUses.NONE;
+ } else {
+ result = _pendingUse
+ .removeAll(hasRead ? MemberUses.NONE : MemberUses.NORMAL_ONLY);
}
- return _pendingUse
- .removeAll(hasRead ? MemberUses.NONE : MemberUses.NORMAL_ONLY);
+ return hasPartialChange
+ ? result.union(MemberUses.PARTIAL_USE_ONLY)
+ : result;
}
@override
@@ -467,12 +472,17 @@
return MemberUses.NONE;
}
bool alreadyHasInvoke = hasInvoke;
- _parameterUsage.invoke(callStructure);
+ bool hasPartialChange = _parameterUsage.invoke(callStructure);
+ EnumSet<MemberUse> result;
if (alreadyHasInvoke) {
- return MemberUses.NONE;
+ result = MemberUses.NONE;
+ } else {
+ result = _pendingUse
+ .removeAll(hasRead ? MemberUses.NONE : MemberUses.NORMAL_ONLY);
}
- return _pendingUse
- .removeAll(hasRead ? MemberUses.NONE : MemberUses.NORMAL_ONLY);
+ return hasPartialChange
+ ? result.union(MemberUses.PARTIAL_USE_ONLY)
+ : result;
}
@override
@@ -495,7 +505,22 @@
}
/// Enum class for the possible kind of use of [MemberEntity] objects.
-enum MemberUse { NORMAL, CLOSURIZE_INSTANCE, CLOSURIZE_STATIC }
+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 {
@@ -510,6 +535,8 @@
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);
@@ -685,11 +712,11 @@
/// invocation of the method or constructor.
bool _areAllTypeParametersProvided;
- /// The set of named parameters that have been provided in at least one
+ /// 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> _providedNamedParameters;
+ Set<String> _unprovidedNamedParameters;
ParameterUsage(this._parameterStructure) {
_hasInvoke = false;
@@ -699,32 +726,41 @@
? null
: 0;
if (!_parameterStructure.namedParameters.isEmpty) {
- _providedNamedParameters =
+ _unprovidedNamedParameters =
new Set<String>.from(_parameterStructure.namedParameters);
}
}
- void invoke(CallStructure callStructure) {
- if (isFullyUsed) return;
+ bool invoke(CallStructure callStructure) {
+ if (isFullyUsed) return false;
_hasInvoke = true;
+ bool changed = false;
if (_providedPositionalParameters != null) {
- _providedPositionalParameters = Math.max(
+ int newProvidedPositionalParameters = Math.max(
_providedPositionalParameters, callStructure.positionalArgumentCount);
+ changed |=
+ newProvidedPositionalParameters != _providedPositionalParameters;
+ _providedPositionalParameters = newProvidedPositionalParameters;
if (_providedPositionalParameters >=
_parameterStructure.positionalParameters) {
_providedPositionalParameters = null;
}
}
- if (_providedNamedParameters != null &&
+ if (_unprovidedNamedParameters != null &&
callStructure.namedArguments.isNotEmpty) {
- _providedNamedParameters.removeAll(callStructure.namedArguments);
- if (_providedNamedParameters.isEmpty) {
- _providedNamedParameters = null;
+ 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;
@@ -732,13 +768,13 @@
bool get isFullyUsed =>
_hasInvoke &&
_providedPositionalParameters == null &&
- _providedNamedParameters == null &&
+ _unprovidedNamedParameters == null &&
_areAllTypeParametersProvided;
void fullyUse() {
_hasInvoke = true;
_providedPositionalParameters = null;
- _providedNamedParameters = null;
+ _unprovidedNamedParameters = null;
_areAllTypeParametersProvided = true;
}
@@ -749,10 +785,10 @@
_parameterStructure.requiredParameters,
_providedPositionalParameters ??
_parameterStructure.positionalParameters,
- _providedNamedParameters == null
+ _unprovidedNamedParameters == null
? _parameterStructure.namedParameters
: _parameterStructure.namedParameters
- .where((n) => !_providedNamedParameters.contains(n))
+ .where((n) => !_unprovidedNamedParameters.contains(n))
.toList(),
_areAllTypeParametersProvided ? _parameterStructure.typeParameters : 0);
}
diff --git a/pkg/compiler/lib/src/universe/resolution_world_builder.dart b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
index c3e3aa3..62bce9b 100644
--- a/pkg/compiler/lib/src/universe/resolution_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
@@ -640,13 +640,13 @@
void _process(
Map<String, Set<MemberUsage>> memberMap,
EnumSet<MemberUse> action(MemberUsage usage),
- bool shouldBeRemove(MemberUsage usage)) {
+ bool shouldBeRemoved(MemberUsage usage)) {
_processSet(memberMap, methodName, (MemberUsage usage) {
if (selector.appliesUnnamed(usage.entity) &&
_selectorConstraintsStrategy.appliedUnnamed(
dynamicUse, usage.entity, this)) {
memberUsed(usage.entity, action(usage));
- return true;
+ return shouldBeRemoved(usage);
}
return false;
});
@@ -848,7 +848,9 @@
map[memberName] = new Set<MemberUsage>();
Set<MemberUsage> remaining = new Set<MemberUsage>();
for (MemberUsage usage in members) {
- if (!updateUsage(usage)) remaining.add(usage);
+ if (!updateUsage(usage)) {
+ remaining.add(usage);
+ }
}
map[memberName].addAll(remaining);
}
diff --git a/pkg/compiler/lib/src/util/enumset.dart b/pkg/compiler/lib/src/util/enumset.dart
index 97df1a4..e292aa6 100644
--- a/pkg/compiler/lib/src/util/enumset.dart
+++ b/pkg/compiler/lib/src/util/enumset.dart
@@ -54,6 +54,12 @@
return new EnumSet.fromValue(value & other.value);
}
+ /// Returns a new set containing all values either in this set or in the
+ /// [other] set.
+ EnumSet<E> union(EnumSet<E> other) {
+ return new EnumSet.fromValue(value | other.value);
+ }
+
/// Returns a new set containing all values in this set that are not in the
/// [other] set.
EnumSet<E> minus(EnumSet<E> other) {
diff --git a/tests/compiler/dart2js/member_usage/member_usage_test.dart b/tests/compiler/dart2js/member_usage/member_usage_test.dart
index 0590221..32853b9 100644
--- a/tests/compiler/dart2js/member_usage/member_usage_test.dart
+++ b/tests/compiler/dart2js/member_usage/member_usage_test.dart
@@ -6,6 +6,7 @@
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/elements/entities.dart';
+import 'package:compiler/src/enqueue.dart';
import 'package:compiler/src/ir/util.dart';
import 'package:compiler/src/kernel/kernel_strategy.dart';
import 'package:compiler/src/universe/member_usage.dart';
@@ -18,7 +19,15 @@
main(List<String> args) {
asyncTest(() async {
Directory dataDir = new Directory.fromUri(Platform.script.resolve('data'));
- await checkTests(dataDir, const ClosedWorldDataComputer(),
+ print('------------------------------------------------------------------');
+ print(' Test with enqueuer checks');
+ print('------------------------------------------------------------------');
+ await checkTests(dataDir, const ClosedWorldDataComputer(false),
+ args: args, testOmit: false, testFrontend: true);
+ print('------------------------------------------------------------------');
+ print(' Test without enqueuer checks');
+ print('------------------------------------------------------------------');
+ await checkTests(dataDir, const ClosedWorldDataComputer(true),
args: args, testOmit: false, testFrontend: true);
});
}
@@ -30,7 +39,14 @@
}
class ClosedWorldDataComputer extends DataComputer<Features> {
- const ClosedWorldDataComputer();
+ final bool skipEnqueuerCheck;
+
+ const ClosedWorldDataComputer(this.skipEnqueuerCheck);
+
+ @override
+ void setup() {
+ Enqueuer.skipEnqueuerCheckForTesting = skipEnqueuerCheck;
+ }
@override
void computeMemberData(Compiler compiler, MemberEntity member,