Track parameter use in invocations in MemberUsage
Change-Id: I53b4ad1ff533d913bdff7e48558fdc56ce016e3f
Reviewed-on: https://dart-review.googlesource.com/c/89525
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/elements/entities.dart b/pkg/compiler/lib/src/elements/entities.dart
index 0adff08..a3a5c4d 100644
--- a/pkg/compiler/lib/src/elements/entities.dart
+++ b/pkg/compiler/lib/src/elements/entities.dart
@@ -350,6 +350,24 @@
return true;
}
+ /// Short textual representation use for testing.
+ String get shortText {
+ StringBuffer sb = new StringBuffer();
+ if (typeParameters != 0) {
+ sb.write('<');
+ sb.write(typeParameters);
+ sb.write('>');
+ }
+ sb.write('(');
+ sb.write(positionalParameters);
+ if (namedParameters.length > 0) {
+ sb.write(',');
+ sb.write(namedParameters.join(','));
+ }
+ sb.write(')');
+ return sb.toString();
+ }
+
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('ParameterStructure(');
@@ -359,4 +377,7 @@
sb.write('typeParameters=$typeParameters)');
return sb.toString();
}
+
+ int get size =>
+ positionalParameters + typeParameters + namedParameters.length;
}
diff --git a/pkg/compiler/lib/src/universe/call_structure.dart b/pkg/compiler/lib/src/universe/call_structure.dart
index c574ac8..d40a26e 100644
--- a/pkg/compiler/lib/src/universe/call_structure.dart
+++ b/pkg/compiler/lib/src/universe/call_structure.dart
@@ -86,6 +86,19 @@
? this
: new CallStructure(argumentCount, namedArguments);
+ /// Short textual representation use for testing.
+ String get shortText {
+ StringBuffer sb = new StringBuffer();
+ sb.write('(');
+ sb.write(positionalArgumentCount);
+ if (namedArgumentCount > 0) {
+ sb.write(',');
+ sb.write(getOrderedNamedArguments().join(','));
+ }
+ sb.write(')');
+ return sb.toString();
+ }
+
/// A description of the argument structure.
String structureToString() {
StringBuffer sb = new StringBuffer();
diff --git a/pkg/compiler/lib/src/universe/codegen_world_builder.dart b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
index 5f44099..4d4bab4 100644
--- a/pkg/compiler/lib/src/universe/codegen_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
@@ -521,14 +521,14 @@
useSet.addAll(usage.invoke(null));
}
- if (usage.pendingUse.contains(MemberUse.CLOSURIZE_INSTANCE)) {
+ if (usage.hasPendingClosurizationUse) {
// Store the member in [instanceFunctionsByName] to catch
// getters on the function.
_instanceFunctionsByName
.putIfAbsent(usage.entity.name, () => new Set<MemberUsage>())
.add(usage);
}
- if (usage.pendingUse.contains(MemberUse.NORMAL)) {
+ if (usage.hasPendingNormalUse) {
// The element is not yet used. Add it to the list of instance
// members to still be processed.
_instanceMembersByName
diff --git a/pkg/compiler/lib/src/universe/member_usage.dart b/pkg/compiler/lib/src/universe/member_usage.dart
index 91dbc6f..b7d31e9 100644
--- a/pkg/compiler/lib/src/universe/member_usage.dart
+++ b/pkg/compiler/lib/src/universe/member_usage.dart
@@ -2,6 +2,8 @@
// 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;
@@ -15,9 +17,6 @@
_pendingUse.addAll(_originalUse);
}
- /// Returns the possible uses of [entity] that have not yet been registered.
- EnumSet<T> get pendingUse => _pendingUse;
-
/// Returns the uses of [entity] that have been registered.
EnumSet<T> get appliedUse => _originalUse.minus(_pendingUse);
@@ -40,7 +39,8 @@
MemberUsage.internal(this.entity);
- factory MemberUsage(MemberEntity member, {bool isNative: false}) {
+ factory MemberUsage(MemberEntity member,
+ {bool isNative: false, bool trackParameters: false}) {
if (member.isField) {
if (member.isAssignable) {
return new FieldUsage(member, isNative: isNative);
@@ -52,10 +52,18 @@
} else if (member.isSetter) {
return new SetterUsage(member);
} else if (member.isConstructor) {
- return new ConstructorUsage(member);
+ if (trackParameters) {
+ return new ParameterTrackingConstructorUsage(member);
+ } else {
+ return new ConstructorUsage(member);
+ }
} else {
assert(member.isFunction, failedAt(member, "Unexpected member: $member"));
- return new FunctionUsage(member);
+ if (trackParameters) {
+ return new ParameterTrackingFunctionUsage(member);
+ } else {
+ return new FunctionUsage(member);
+ }
}
}
@@ -71,6 +79,29 @@
/// 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;
@@ -216,9 +247,15 @@
}
}
+ FunctionEntity get entity => super.entity;
+
EnumSet<MemberUse> get _originalUse =>
entity.isInstanceMember ? MemberUses.ALL_INSTANCE : MemberUses.ALL_STATIC;
+ bool get hasPendingClosurizationUse => entity.isInstanceMember
+ ? _pendingUse.contains(MemberUse.CLOSURIZE_INSTANCE)
+ : _pendingUse.contains(MemberUse.CLOSURIZE_STATIC);
+
@override
EnumSet<MemberUse> read() => fullyUse();
@@ -255,6 +292,86 @@
@override
bool get fullyUsed => hasInvoke && hasRead;
+
+ @override
+ ParameterStructure get invokedParameters =>
+ hasInvoke ? entity.parameterStructure : null;
+}
+
+class ParameterTrackingFunctionUsage extends MemberUsage {
+ 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);
+ }
+ }
+
+ bool get hasInvoke => _parameterUsage.hasInvoke;
+
+ bool get hasPendingClosurizationUse => entity.isInstanceMember
+ ? _pendingUse.contains(MemberUse.CLOSURIZE_INSTANCE)
+ : _pendingUse.contains(MemberUse.CLOSURIZE_STATIC);
+
+ 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;
+ _parameterUsage.invoke(callStructure);
+ if (alreadyHasInvoke) {
+ return MemberUses.NONE;
+ }
+ return _pendingUse
+ .removeAll(hasRead ? MemberUses.NONE : MemberUses.NORMAL_ONLY);
+ }
+
+ @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;
+
+ bool get isFullyInvoked => _parameterUsage.isFullyUsed;
+
+ @override
+ bool get fullyUsed => isFullyInvoked && hasRead;
+
+ @override
+ ParameterStructure get invokedParameters => _parameterUsage.invokedParameters;
}
class GetterUsage extends MemberUsage {
@@ -327,6 +444,54 @@
@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);
+
+ ConstructorEntity get entity => super.entity;
+
+ EnumSet<MemberUse> get _originalUse => MemberUses.NORMAL_ONLY;
+
+ @override
+ EnumSet<MemberUse> invoke(CallStructure callStructure) {
+ if (isFullyInvoked) {
+ return MemberUses.NONE;
+ }
+ bool alreadyHasInvoke = hasInvoke;
+ _parameterUsage.invoke(callStructure);
+ if (alreadyHasInvoke) {
+ return MemberUses.NONE;
+ }
+ return _pendingUse
+ .removeAll(hasRead ? MemberUses.NONE : MemberUses.NORMAL_ONLY);
+ }
+
+ @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;
+
+ bool get isFullyInvoked => _parameterUsage.isFullyUsed;
+
+ @override
+ ParameterStructure get invokedParameters => _parameterUsage.invokedParameters;
}
/// Enum class for the possible kind of use of [MemberEntity] objects.
@@ -427,6 +592,12 @@
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;
String toString() => '$entity:${appliedUse.iterable(MemberUse.values)}';
@@ -448,12 +619,20 @@
@override
bool get hasRead => hasNormalUse;
+
+ @override
+ bool get hasPendingClosurizationUse => false;
+
+ @override
+ ParameterStructure get invokedParameters => null;
}
class StaticFunctionUsage extends StaticMemberUsage {
bool hasClosurization = false;
- StaticFunctionUsage(MemberEntity entity) : super.internal(entity);
+ StaticFunctionUsage(FunctionEntity entity) : super.internal(entity);
+
+ FunctionEntity get entity => super.entity;
EnumSet<MemberUse> tearOff() {
if (hasClosurization) {
@@ -467,6 +646,10 @@
EnumSet<MemberUse> get _originalUse => MemberUses.ALL_STATIC;
@override
+ bool get hasPendingClosurizationUse =>
+ _pendingUse.contains(MemberUse.CLOSURIZE_STATIC);
+
+ @override
bool get fullyUsed => hasNormalUse && hasClosurization;
@override
@@ -477,4 +660,100 @@
@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 been provided in at least one
+ /// invocation of the method or constructor.
+ ///
+ /// If all named parameters have been provided this is set to `null`.
+ Set<String> _providedNamedParameters;
+
+ ParameterUsage(this._parameterStructure) {
+ _hasInvoke = false;
+ _areAllTypeParametersProvided = _parameterStructure.typeParameters == 0;
+ _providedPositionalParameters = _parameterStructure.positionalParameters ==
+ _parameterStructure.requiredParameters
+ ? null
+ : 0;
+ if (!_parameterStructure.namedParameters.isEmpty) {
+ _providedNamedParameters =
+ new Set<String>.from(_parameterStructure.namedParameters);
+ }
+ }
+
+ void invoke(CallStructure callStructure) {
+ if (isFullyUsed) return;
+ _hasInvoke = true;
+ if (_providedPositionalParameters != null) {
+ _providedPositionalParameters = Math.max(
+ _providedPositionalParameters, callStructure.positionalArgumentCount);
+ if (_providedPositionalParameters >=
+ _parameterStructure.positionalParameters) {
+ _providedPositionalParameters = null;
+ }
+ }
+ if (_providedNamedParameters != null &&
+ callStructure.namedArguments.isNotEmpty) {
+ _providedNamedParameters.removeAll(callStructure.namedArguments);
+ if (_providedNamedParameters.isEmpty) {
+ _providedNamedParameters = null;
+ }
+ }
+ if (!_areAllTypeParametersProvided && callStructure.typeArgumentCount > 0) {
+ _areAllTypeParametersProvided = true;
+ }
+ }
+
+ bool get hasInvoke => _hasInvoke;
+
+ bool get isFullyUsed =>
+ _hasInvoke &&
+ _providedPositionalParameters == null &&
+ _providedNamedParameters == null &&
+ _areAllTypeParametersProvided;
+
+ void fullyUse() {
+ _hasInvoke = true;
+ _providedPositionalParameters = null;
+ _providedNamedParameters = null;
+ _areAllTypeParametersProvided = true;
+ }
+
+ ParameterStructure get invokedParameters {
+ if (!_hasInvoke) return null;
+ if (isFullyUsed) return _parameterStructure;
+ return new ParameterStructure(
+ _parameterStructure.requiredParameters,
+ _providedPositionalParameters ??
+ _parameterStructure.positionalParameters,
+ _providedNamedParameters == null
+ ? _parameterStructure.namedParameters
+ : _parameterStructure.namedParameters
+ .where((n) => !_providedNamedParameters.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 77a8db9..c3e3aa3 100644
--- a/pkg/compiler/lib/src/universe/resolution_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
@@ -22,11 +22,12 @@
import '../kernel/kernel_world.dart';
import '../native/enqueue.dart' show NativeResolutionEnqueuer;
import '../options.dart';
-import '../universe/class_set.dart';
import '../util/enumset.dart';
import '../util/util.dart';
import '../world.dart' show KClosedWorld, OpenWorld;
+import 'call_structure.dart';
import 'class_hierarchy.dart' show ClassHierarchyBuilder, ClassQueries;
+import 'class_set.dart';
import 'member_usage.dart';
import 'selector.dart' show Selector;
import 'use.dart'
@@ -328,10 +329,12 @@
Map<ClassEntity, ClassUsage> get classUsageForTesting => _processedClasses;
- /// Map of registered usage of static members of live classes.
+ /// Map of registered usage of members of live classes.
final Map<MemberEntity, MemberUsage> _memberUsage =
<MemberEntity, MemberUsage>{};
+ Map<MemberEntity, MemberUsage> get memberUsageForTesting => _memberUsage;
+
Map<MemberEntity, MemberUsage> get staticMemberUsageForTesting {
Map<MemberEntity, MemberUsage> map = <MemberEntity, MemberUsage>{};
_memberUsage.forEach((MemberEntity member, MemberUsage usage) {
@@ -352,8 +355,8 @@
return map;
}
- /// Map containing instance members of live classes that are not yet live
- /// themselves.
+ /// Map containing instance members of live classes that are not yet fully
+ /// live themselves.
final Map<String, Set<MemberUsage>> _instanceMembersByName =
<String, Set<MemberUsage>>{};
@@ -581,6 +584,22 @@
getInstantiationMap().forEach(f);
}
+ Iterable<CallStructure> _getMatchingCallStructures(
+ Map<Selector, SelectorConstraints> selectors, MemberEntity member) {
+ if (selectors == null) return const <CallStructure>[];
+ Set<CallStructure> callStructures;
+ for (Selector selector in selectors.keys) {
+ if (selector.appliesUnnamed(member)) {
+ SelectorConstraints masks = selectors[selector];
+ if (masks.canHit(member, selector.memberName, this)) {
+ callStructures ??= new Set<CallStructure>();
+ callStructures.add(selector.callStructure);
+ }
+ }
+ }
+ return callStructures ?? const <CallStructure>[];
+ }
+
bool _hasMatchingSelector(
Map<Selector, SelectorConstraints> selectors, MemberEntity member) {
if (selectors == null) return false;
@@ -600,8 +619,8 @@
return _instantiationInfo;
}
- bool _hasInvocation(MemberEntity member) {
- return _hasMatchingSelector(_invokedNames[member.name], member);
+ Iterable<CallStructure> _getInvocationCallStructures(MemberEntity member) {
+ return _getMatchingCallStructures(_invokedNames[member.name], member);
}
bool _hasInvokedGetter(MemberEntity member) {
@@ -618,8 +637,10 @@
Selector selector = dynamicUse.selector;
String methodName = selector.name;
- void _process(Map<String, Set<MemberUsage>> memberMap,
- EnumSet<MemberUse> action(MemberUsage usage)) {
+ void _process(
+ Map<String, Set<MemberUsage>> memberMap,
+ EnumSet<MemberUse> action(MemberUsage usage),
+ bool shouldBeRemove(MemberUsage usage)) {
_processSet(memberMap, methodName, (MemberUsage usage) {
if (selector.appliesUnnamed(usage.entity) &&
_selectorConstraintsStrategy.appliedUnnamed(
@@ -636,19 +657,24 @@
registerDynamicInvocation(
dynamicUse.selector, dynamicUse.typeArguments);
if (_registerNewSelector(dynamicUse, _invokedNames)) {
- _process(_instanceMembersByName,
- (m) => m.invoke(dynamicUse.selector.callStructure));
+ _process(
+ _instanceMembersByName,
+ (m) => m.invoke(dynamicUse.selector.callStructure),
+ (u) => !u.hasPendingNormalUse);
}
break;
case DynamicUseKind.GET:
if (_registerNewSelector(dynamicUse, _invokedGetters)) {
- _process(_instanceMembersByName, (m) => m.read());
- _process(_instanceFunctionsByName, (m) => m.read());
+ _process(_instanceMembersByName, (m) => m.read(),
+ (u) => !u.hasPendingNormalUse);
+ _process(_instanceFunctionsByName, (m) => m.read(),
+ (u) => !u.hasPendingClosurizationUse);
}
break;
case DynamicUseKind.SET:
if (_registerNewSelector(dynamicUse, _invokedSetters)) {
- _process(_instanceMembersByName, (m) => m.write());
+ _process(_instanceMembersByName, (m) => m.write(),
+ (u) => !u.hasPendingNormalUse);
}
break;
}
@@ -703,7 +729,7 @@
MemberEntity element = staticUse.element;
EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
MemberUsage usage = _memberUsage.putIfAbsent(element, () {
- MemberUsage usage = new MemberUsage(element);
+ MemberUsage usage = new MemberUsage(element, trackParameters: true);
useSet.addAll(usage.appliedUse);
return usage;
});
@@ -843,7 +869,8 @@
newUsage = true;
bool isNative = _nativeBasicData.isNativeClass(cls);
EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
- MemberUsage usage = new MemberUsage(member, isNative: isNative);
+ MemberUsage usage =
+ new MemberUsage(member, isNative: isNative, trackParameters: true);
useSet.addAll(usage.appliedUse);
if (member.isField && isNative) {
registerUsedElement(member);
@@ -857,21 +884,28 @@
if (!usage.hasRead && _hasInvokedGetter(member)) {
useSet.addAll(usage.read());
}
- if (!usage.hasInvoke && _hasInvocation(member)) {
- useSet.addAll(usage.invoke(null));
+ if (!usage.isFullyInvoked) {
+ Iterable<CallStructure> callStructures =
+ _getInvocationCallStructures(member);
+ for (CallStructure callStructure in callStructures) {
+ useSet.addAll(usage.invoke(callStructure));
+ if (usage.isFullyInvoked) {
+ break;
+ }
+ }
}
if (!usage.hasWrite && hasInvokedSetter(member)) {
useSet.addAll(usage.write());
}
- if (usage.pendingUse.contains(MemberUse.NORMAL)) {
+ if (usage.hasPendingNormalUse) {
// The element is not yet used. Add it to the list of instance
// members to still be processed.
_instanceMembersByName
.putIfAbsent(memberName, () => new Set<MemberUsage>())
.add(usage);
}
- if (usage.pendingUse.contains(MemberUse.CLOSURIZE_INSTANCE)) {
+ if (usage.hasPendingClosurizationUse) {
// Store the member in [instanceFunctionsByName] to catch
// getters on the function.
_instanceFunctionsByName
@@ -886,16 +920,23 @@
if (!usage.hasRead && _hasInvokedGetter(member)) {
useSet.addAll(usage.read());
}
- if (!usage.hasInvoke && _hasInvocation(member)) {
- useSet.addAll(usage.invoke(null));
+ if (!usage.isFullyInvoked) {
+ Iterable<CallStructure> callStructures =
+ _getInvocationCallStructures(member);
+ for (CallStructure callStructure in callStructures) {
+ useSet.addAll(usage.invoke(callStructure));
+ if (usage.isFullyInvoked) {
+ break;
+ }
+ }
}
if (!usage.hasWrite && hasInvokedSetter(member)) {
useSet.addAll(usage.write());
}
- if (!usage.pendingUse.contains(MemberUse.NORMAL)) {
+ if (!usage.hasPendingNormalUse) {
_instanceMembersByName[memberName]?.remove(usage);
}
- if (!usage.pendingUse.contains(MemberUse.CLOSURIZE_INSTANCE)) {
+ if (!usage.hasPendingClosurizationUse) {
_instanceFunctionsByName[memberName]?.remove(usage);
}
memberUsed(usage.entity, useSet);
diff --git a/pkg/compiler/lib/src/universe/use.dart b/pkg/compiler/lib/src/universe/use.dart
index ab8e69c..ab3ffbb 100644
--- a/pkg/compiler/lib/src/universe/use.dart
+++ b/pkg/compiler/lib/src/universe/use.dart
@@ -64,13 +64,7 @@
sb.write('>');
}
if (selector.isCall) {
- sb.write('(');
- sb.write(selector.callStructure.positionalArgumentCount);
- if (selector.callStructure.namedArgumentCount > 0) {
- sb.write(',');
- sb.write(selector.callStructure.getOrderedNamedArguments().join(','));
- }
- sb.write(')');
+ sb.write(selector.callStructure.shortText);
} else if (selector.isSetter) {
sb.write('=');
}
diff --git a/pkg/compiler/lib/src/util/features.dart b/pkg/compiler/lib/src/util/features.dart
index 5263b40..652f8ab 100644
--- a/pkg/compiler/lib/src/util/features.dart
+++ b/pkg/compiler/lib/src/util/features.dart
@@ -68,6 +68,7 @@
/// encoded in `[...]` will be parsed as lists of strings.
static Features fromText(String text) {
Features features = new Features();
+ if (text == null) return features;
int index = 0;
while (index < text.length) {
int eqPos = text.indexOf('=', index);
diff --git a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
index 84bad06..cfd8f5c 100644
--- a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
+++ b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
@@ -750,7 +750,7 @@
}
} else if (expectedValue != actualValue) {
errorsFound.add(
- "Mismatch for $key: expected '$expectedValue', found '${actualValue}");
+ "Mismatch for $key: expected '$expectedValue', found '${actualValue}'");
}
});
return errorsFound.isNotEmpty ? errorsFound.join('\n ') : null;
diff --git a/tests/compiler/dart2js/member_usage/data/constructors.dart b/tests/compiler/dart2js/member_usage/data/constructors.dart
new file mode 100644
index 0000000..fe9889d
--- /dev/null
+++ b/tests/compiler/dart2js/member_usage/data/constructors.dart
@@ -0,0 +1,95 @@
+// 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.
+
+class Class {
+ /*element: Class.constructor1:invoke*/
+ Class.constructor1() {}
+
+ /*element: Class.constructor2a:invoke=(0)*/
+ Class.constructor2a([a]) {}
+
+ /*element: Class.constructor2b:invoke*/
+ Class.constructor2b([a]) {}
+
+ /*element: Class.constructor2c:invoke*/
+ Class.constructor2c([a]) {}
+
+ /*element: Class.constructor3a:invoke=(0)*/
+ Class.constructor3a([a, b]) {}
+
+ /*element: Class.constructor3b:invoke=(1)*/
+ Class.constructor3b([a, b]) {}
+
+ /*element: Class.constructor3c:invoke*/
+ Class.constructor3c([a, b]) {}
+
+ /*element: Class.constructor4a:invoke=(0)*/
+ Class.constructor4a({a}) {}
+
+ /*element: Class.constructor4b:invoke*/
+ Class.constructor4b({a}) {}
+
+ /*element: Class.constructor4c:invoke*/
+ Class.constructor4c({a}) {}
+
+ /*element: Class.constructor5a:invoke=(0)*/
+ Class.constructor5a({a, b}) {}
+
+ /*element: Class.constructor5b:invoke=(0,a)*/
+ Class.constructor5b({a, b}) {}
+
+ /*element: Class.constructor5c:invoke=(0,b)*/
+ Class.constructor5c({a, b}) {}
+
+ /*element: Class.constructor6a:invoke=(1)*/
+ Class.constructor6a(a, [b, c]) {}
+
+ /*element: Class.constructor6b:invoke=(2)*/
+ Class.constructor6b(a, [b, c]) {}
+
+ /*element: Class.constructor6c:invoke*/
+ Class.constructor6c(a, [b, c]) {}
+
+ /*element: Class.constructor7a:invoke=(1)*/
+ Class.constructor7a(a, {b, c}) {}
+
+ /*element: Class.constructor7b:invoke=(1,b)*/
+ Class.constructor7b(a, {b, c}) {}
+
+ /*element: Class.constructor7c:invoke=(1,c)*/
+ Class.constructor7c(a, {b, c}) {}
+}
+
+/*element: main:invoke*/
+main() {
+ new Class.constructor1();
+
+ new Class.constructor2a();
+ new Class.constructor2b(null);
+ new Class.constructor2c();
+ new Class.constructor2c(null);
+
+ new Class.constructor3a();
+ new Class.constructor3b();
+ new Class.constructor3b(null);
+ new Class.constructor3c(null, null);
+
+ new Class.constructor4a();
+ new Class.constructor4b(a: null);
+ new Class.constructor4c();
+ new Class.constructor4c(a: null);
+
+ new Class.constructor5a();
+ new Class.constructor5b(a: null);
+ new Class.constructor5c(b: null);
+
+ new Class.constructor6a(null);
+ new Class.constructor6b(null);
+ new Class.constructor6b(null, null);
+ new Class.constructor6c(null, null, null);
+
+ new Class.constructor7a(null);
+ new Class.constructor7b(null, b: null);
+ new Class.constructor7c(null, c: null);
+}
diff --git a/tests/compiler/dart2js/member_usage/data/fields.dart b/tests/compiler/dart2js/member_usage/data/fields.dart
new file mode 100644
index 0000000..22a24a9
--- /dev/null
+++ b/tests/compiler/dart2js/member_usage/data/fields.dart
@@ -0,0 +1,80 @@
+// 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.
+
+/*element: field1a:read*/
+var field1a;
+
+/*element: field1b:write*/
+var field1b;
+
+/*element: field1c:read,write*/
+var field1c;
+
+/*element: field2a:read*/
+get field2a => 42;
+
+set field2a(_) {}
+
+get field2b => 42;
+
+/*element: field2b=:write*/
+set field2b(_) {}
+
+/*element: field2c:read*/
+get field2c => 42;
+
+/*element: field2c=:write*/
+set field2c(_) {}
+
+/*element: Class.:invoke*/
+class Class {
+ /*element: Class.field1a:read*/
+ var field1a;
+
+ /*element: Class.field1b:write*/
+ var field1b;
+
+ /*element: Class.field1c:read,write*/
+ var field1c;
+
+ /*element: Class.field2a:read*/
+ get field2a => 42;
+
+ set field2a(_) {}
+
+ get field2b => 42;
+
+ /*element: Class.field2b=:write*/
+ set field2b(_) {}
+
+ /*element: Class.field2c:read*/
+ get field2c => 42;
+
+ /*element: Class.field2c=:write*/
+ set field2c(_) {}
+
+ /*element: Class.test:invoke*/
+ test() {
+ field1a;
+ field1b = 42;
+ field1c = field1c;
+
+ field2a;
+ field2b = 42;
+ field2c = field2c;
+ }
+}
+
+/*element: main:invoke*/
+main() {
+ field1a;
+ field1b = 42;
+ field1c = field1c;
+
+ field2a;
+ field2b = 42;
+ field2c = field2c;
+
+ new Class().test();
+}
diff --git a/tests/compiler/dart2js/member_usage/data/general.dart b/tests/compiler/dart2js/member_usage/data/general.dart
new file mode 100644
index 0000000..0cb1266
--- /dev/null
+++ b/tests/compiler/dart2js/member_usage/data/general.dart
@@ -0,0 +1,212 @@
+// 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.
+
+/*element: A.:invoke*/
+class A {
+ /*element: A.method1:invoke*/
+ method1() {}
+
+ method2() {}
+
+ /*element: A.method4:invoke*/
+ method4() {}
+
+ /*element: A.getter:read*/
+ get getter => 42;
+
+ set setter(_) {}
+}
+
+/*element: B.:invoke*/
+class B {
+ method1() {}
+ method2() {}
+
+ /*element: B.method5:invoke*/
+ method5() {}
+ get getter => 42;
+
+ /*element: B.setter=:write*/
+ set setter(_) {}
+}
+
+/*element: C.:invoke*/
+class C extends A {
+ /*element: C.method1:invoke*/
+ method1() {}
+
+ /*element: B.method2:invoke*/
+ method2() {}
+ method4() {}
+
+ /*element: C.getter:read*/
+ get getter => 42;
+ set setter(_) {}
+}
+
+/*element: D.:invoke*/
+class D implements B {
+ method1() {}
+
+ /*element: D.method2:invoke*/
+ method2() {}
+ method5() {}
+ get getter => 42;
+
+ /*element: D.setter=:write*/
+ set setter(_) {}
+}
+
+class E implements A {
+ method1() {}
+ method2() {}
+ method4() {}
+ get getter => 42;
+ set setter(_) {}
+}
+
+class F extends B {
+ method1() {}
+ method2() {}
+ method5() {}
+ get getter => 42;
+ set setter(_) {}
+}
+
+class G {
+ /*element: G.method1:invoke*/
+ method1() {}
+ method2() {}
+ method4() {}
+
+ /*element: G.getter:read*/
+ get getter => 42;
+ set setter(_) {}
+}
+
+/*element: H.:invoke*/
+class H extends Object with G implements A {}
+
+/*element: I.:invoke*/
+class I {
+ /*element: I.method1:invoke*/
+ method1() {}
+ method2() {}
+ method4() {}
+
+ /*element: I.getter:read*/
+ get getter => 42;
+ set setter(_) {}
+}
+
+/*element: J.:invoke*/
+class J extends I implements A {}
+
+class K {
+ /*element: K.method1:invoke*/
+ method1() {}
+ method2() {}
+
+ /*element: K.getter:read*/
+ get getter => 42;
+ set setter(_) {}
+}
+
+class L = Object with K;
+class L2 = Object with L;
+
+/*element: M.:invoke*/
+class M extends L {}
+
+/*element: M2.:invoke*/
+class M2 extends L2 {}
+
+/*element: N.:invoke*/
+class N {
+ method1() {}
+ get getter => 42;
+ set setter(_) {}
+}
+
+abstract class O extends N {}
+
+/*element: P.:invoke*/
+class P implements O {
+ /*element: P.method1:invoke*/
+ method1() {}
+
+ /*element: P.getter:read*/
+ get getter => 42;
+
+ /*element: P.setter=:write*/
+ set setter(_) {}
+}
+
+/*element: Q.:invoke*/
+class Q {
+ /*element: Q.method3:invoke*/
+ method3() {}
+}
+
+/*element: R.:invoke*/
+class R extends Q {}
+
+/*element: Class1a.:invoke*/
+class Class1a {
+ /*element: Class1a.call:invoke*/
+ call(a, b, c) {} // Call structure only used in Class1a and Class2b.
+}
+
+/*element: Class1b.:invoke*/
+class Class1b {
+ call(a, b, c) {}
+}
+
+/*element: Class2.:invoke*/
+class Class2 {
+ /*element: Class2.c:read,write*/
+ Class1a c;
+}
+
+/*element: main:invoke*/
+main() {
+ method1();
+ method2();
+}
+
+/*element: method1:invoke*/
+@pragma('dart2js:disableFinal')
+method1() {
+ A a = new A();
+ B b = new B();
+ a.method1();
+ a.getter;
+ b.method2();
+ b.setter = 42;
+ new C();
+ new D();
+ new H();
+ new J();
+ new M().method1();
+ new M2().getter;
+ new N();
+ O o = new P();
+ o.method1();
+ o.getter;
+ o.setter = 42;
+ R r;
+ r.method3();
+ r = new R(); // Create R after call.
+ new Class1a();
+ new Class1b();
+ new Class2().c(0, 1, 2);
+}
+
+/*element: method2:invoke*/
+method2() {
+ A a = new A();
+ B b = new B();
+ a.method4();
+ b.method5();
+}
diff --git a/tests/compiler/dart2js/member_usage/data/instance_method_parameters.dart b/tests/compiler/dart2js/member_usage/data/instance_method_parameters.dart
new file mode 100644
index 0000000..28754c5
--- /dev/null
+++ b/tests/compiler/dart2js/member_usage/data/instance_method_parameters.dart
@@ -0,0 +1,134 @@
+// 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.
+
+/*element: Class.:invoke*/
+class Class {
+ /*element: Class.method1:invoke*/
+ method1() {}
+
+ /*element: Class.method2a:invoke=(0)*/
+ method2a([a]) {}
+
+ /*element: Class.method2b:invoke*/
+ method2b([a]) {}
+
+ /*element: Class.method2c:invoke*/
+ method2c([a]) {}
+
+ /*element: Class.method2d:invoke,read*/
+ method2d([a]) {}
+
+ /*element: Class.method3a:invoke=(0)*/
+ method3a([a, b]) {}
+
+ /*element: Class.method3b:invoke=(1)*/
+ method3b([a, b]) {}
+
+ /*element: Class.method3c:invoke*/
+ method3c([a, b]) {}
+
+ /*element: Class.method3d:invoke,read*/
+ method3d([a, b]) {}
+
+ /*element: Class.method4a:invoke=(0)*/
+ method4a({a}) {}
+
+ /*element: Class.method4b:invoke*/
+ method4b({a}) {}
+
+ /*element: Class.method4c:invoke*/
+ method4c({a}) {}
+
+ /*element: Class.method4d:invoke,read*/
+ method4d({a}) {}
+
+ /*element: Class.method5a:invoke=(0)*/
+ method5a({a, b}) {}
+
+ /*element: Class.method5b:invoke=(0,a)*/
+ method5b({a, b}) {}
+
+ /*element: Class.method5c:invoke=(0,b)*/
+ method5c({a, b}) {}
+
+ /*element: Class.method5d:invoke,read*/
+ method5d({a, b}) {}
+
+ /*element: Class.method6a:invoke*/
+ method6a<T>() {}
+
+ /*element: Class.method6b:invoke,read*/
+ method6b<T>() {}
+
+ /*element: Class.method7a:invoke=(1)*/
+ method7a(a, [b, c]) {}
+
+ /*element: Class.method7b:invoke=(2)*/
+ method7b(a, [b, c]) {}
+
+ /*element: Class.method7c:invoke*/
+ method7c(a, [b, c]) {}
+
+ /*element: Class.method7d:invoke,read*/
+ method7d(a, [b, c]) {}
+
+ /*element: Class.method8a:invoke=(1)*/
+ method8a(a, {b, c}) {}
+
+ /*element: Class.method8b:invoke=(1,b)*/
+ method8b(a, {b, c}) {}
+
+ /*element: Class.method8c:invoke=(1,c)*/
+ method8c(a, {b, c}) {}
+
+ /*element: Class.method8d:invoke,read*/
+ method8d(a, {b, c}) {}
+
+ /*element: Class.test:invoke*/
+ test() {
+ method1();
+
+ method2a();
+ method2b(null);
+ method2c();
+ method2c(null);
+ method2d;
+
+ method3a();
+ method3b();
+ method3b(null);
+ method3c(null, null);
+ method3d;
+
+ method4a();
+ method4b(a: null);
+ method4c();
+ method4c(a: null);
+ method4d;
+
+ method5a();
+ method5b(a: null);
+ method5c(b: null);
+ method5d;
+
+ method6a();
+ method6b;
+
+ method7a(null);
+ method7b(null);
+ method7b(null, null);
+ method7c(null, null, null);
+ method7d;
+
+ method8a(null);
+ method8b(null, b: null);
+ method8c(null, c: null);
+ method8d;
+ }
+}
+
+/*element: main:invoke*/
+main() {
+ new Class().test();
+}
diff --git a/tests/compiler/dart2js/member_usage/data/static_method_parameters.dart b/tests/compiler/dart2js/member_usage/data/static_method_parameters.dart
new file mode 100644
index 0000000..bb133a2
--- /dev/null
+++ b/tests/compiler/dart2js/member_usage/data/static_method_parameters.dart
@@ -0,0 +1,126 @@
+// 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.
+
+/*element: method1:invoke*/
+method1() {}
+
+/*element: method2a:invoke=(0)*/
+method2a([a]) {}
+
+/*element: method2b:invoke*/
+method2b([a]) {}
+
+/*element: method2c:invoke*/
+method2c([a]) {}
+
+/*element: method2d:invoke,read*/
+method2d([a]) {}
+
+/*element: method3a:invoke=(0)*/
+method3a([a, b]) {}
+
+/*element: method3b:invoke=(1)*/
+method3b([a, b]) {}
+
+/*element: method3c:invoke*/
+method3c([a, b]) {}
+
+/*element: method3d:invoke,read*/
+method3d([a, b]) {}
+
+/*element: method4a:invoke=(0)*/
+method4a({a}) {}
+
+/*element: method4b:invoke*/
+method4b({a}) {}
+
+/*element: method4c:invoke*/
+method4c({a}) {}
+
+/*element: method4d:invoke,read*/
+method4d({a}) {}
+
+/*element: method5a:invoke=(0)*/
+method5a({a, b}) {}
+
+/*element: method5b:invoke=(0,a)*/
+method5b({a, b}) {}
+
+/*element: method5c:invoke=(0,b)*/
+method5c({a, b}) {}
+
+/*element: method5d:invoke,read*/
+method5d({a, b}) {}
+
+/*element: method6a:invoke*/
+method6a<T>() {}
+
+/*element: method6b:invoke,read*/
+method6b<T>() {}
+
+/*element: method7a:invoke=(1)*/
+method7a(a, [b, c]) {}
+
+/*element: method7b:invoke=(2)*/
+method7b(a, [b, c]) {}
+
+/*element: method7c:invoke*/
+method7c(a, [b, c]) {}
+
+/*element: method7d:invoke,read*/
+method7d(a, [b, c]) {}
+
+/*element: method8a:invoke=(1)*/
+method8a(a, {b, c}) {}
+
+/*element: method8b:invoke=(1,b)*/
+method8b(a, {b, c}) {}
+
+/*element: method8c:invoke=(1,c)*/
+method8c(a, {b, c}) {}
+
+/*element: method8d:invoke,read*/
+method8d(a, {b, c}) {}
+
+/*element: main:invoke*/
+main() {
+ method1();
+
+ method2a();
+ method2b(null);
+ method2c();
+ method2c(null);
+ method2d;
+
+ method3a();
+ method3b();
+ method3b(null);
+ method3c(null, null);
+ method3d;
+
+ method4a();
+ method4b(a: null);
+ method4c();
+ method4c(a: null);
+ method4d;
+
+ method5a();
+ method5b(a: null);
+ method5c(b: null);
+ method5d;
+
+ method6a();
+ method6b;
+
+ method7a(null);
+ method7b(null);
+ method7b(null, null);
+ method7c(null, null, null);
+ method7d;
+
+ method8a(null);
+ method8b(null, b: null);
+ method8c(null, c: null);
+ method8d;
+}
diff --git a/tests/compiler/dart2js/member_usage/member_usage_test.dart b/tests/compiler/dart2js/member_usage/member_usage_test.dart
new file mode 100644
index 0000000..0590221
--- /dev/null
+++ b/tests/compiler/dart2js/member_usage/member_usage_test.dart
@@ -0,0 +1,67 @@
+// 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.
+
+import 'dart:io';
+import 'package:async_helper/async_helper.dart';
+import 'package:compiler/src/compiler.dart';
+import 'package:compiler/src/elements/entities.dart';
+import 'package:compiler/src/ir/util.dart';
+import 'package:compiler/src/kernel/kernel_strategy.dart';
+import 'package:compiler/src/universe/member_usage.dart';
+import 'package:compiler/src/universe/resolution_world_builder.dart';
+import 'package:compiler/src/util/features.dart';
+import 'package:kernel/ast.dart' as ir;
+import '../equivalence/id_equivalence.dart';
+import '../equivalence/id_equivalence_helper.dart';
+
+main(List<String> args) {
+ asyncTest(() async {
+ Directory dataDir = new Directory.fromUri(Platform.script.resolve('data'));
+ await checkTests(dataDir, const ClosedWorldDataComputer(),
+ args: args, testOmit: false, testFrontend: true);
+ });
+}
+
+class Tags {
+ static const String read = 'read';
+ static const String write = 'write';
+ static const String invoke = 'invoke';
+}
+
+class ClosedWorldDataComputer extends DataComputer<Features> {
+ const ClosedWorldDataComputer();
+
+ @override
+ void computeMemberData(Compiler compiler, MemberEntity member,
+ Map<Id, ActualData<Features>> actualMap,
+ {bool verbose: false}) {
+ KernelFrontEndStrategy frontendStrategy = compiler.frontendStrategy;
+ ResolutionWorldBuilderImpl resolutionWorldBuilder =
+ compiler.resolutionWorldBuilder;
+ ir.Member node = frontendStrategy.elementMap.getMemberNode(member);
+ Features features = new Features();
+ MemberUsage memberUsage =
+ resolutionWorldBuilder.memberUsageForTesting[member];
+ if (memberUsage != null) {
+ if (memberUsage.hasRead) {
+ features.add(Tags.read);
+ }
+ if (memberUsage.hasWrite) {
+ features.add(Tags.write);
+ }
+ if (memberUsage.isFullyInvoked) {
+ features.add(Tags.invoke);
+ } else if (memberUsage.hasInvoke) {
+ features[Tags.invoke] = memberUsage.invokedParameters.shortText;
+ }
+ }
+ Id id = computeEntityId(node);
+ actualMap[id] = new ActualData<Features>(
+ id, features, computeSourceSpanFromTreeNode(node), member);
+ }
+
+ @override
+ DataInterpreter<Features> get dataValidator =>
+ const FeaturesDataInterpreter();
+}