mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 14:32:49 +00:00
Reimplement member usage tracking
We now track member usage in terms of static, dynamic and super access for reads, writes and invocations. The information collected during closed world computation is now the basis for the potential member usage in codegen, thus ensuring that we cannot conclude in codegen that for instance a field is read dynamically when the closed world knows that it is never read dynamically. Closes #36516 Change-Id: I3a1cb87c71268c34bcd67e14a035d9d1be324ab0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100840 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
parent
b36734f4da
commit
8d4816b7ff
|
@ -296,7 +296,7 @@ abstract class DeferredLoadTask extends CompilerTask {
|
|||
_collectTypeArgumentDependencies(
|
||||
staticUse.type.typeArguments, dependencies);
|
||||
break;
|
||||
case StaticUseKind.INVOKE:
|
||||
case StaticUseKind.STATIC_INVOKE:
|
||||
case StaticUseKind.CLOSURE_CALL:
|
||||
case StaticUseKind.DIRECT_INVOKE:
|
||||
// TODO(johnniwinther): Use rti need data to skip unneeded type
|
||||
|
|
|
@ -207,7 +207,7 @@ abstract class EnqueuerImpl extends Enqueuer {
|
|||
|
||||
/// Check enqueuer consistency after the queue has been closed.
|
||||
bool checkEnqueuerConsistency(ElementEnvironment elementEnvironment) {
|
||||
task.measure(() {
|
||||
task.measureSubtask('resolution.check', () {
|
||||
// Run through the classes and see if we need to enqueue more methods.
|
||||
for (ClassEntity classElement
|
||||
in worldBuilder.directlyInstantiatedClasses) {
|
||||
|
@ -284,7 +284,7 @@ class ResolutionEnqueuer extends EnqueuerImpl {
|
|||
{ConstructorEntity constructor,
|
||||
bool nativeUsage: false,
|
||||
bool globalDependency: false}) {
|
||||
task.measure(() {
|
||||
task.measureSubtask('resolution.typeUse', () {
|
||||
_worldBuilder.registerTypeInstantiation(type, _applyClassUse,
|
||||
constructor: constructor);
|
||||
listener.registerInstantiatedType(type,
|
||||
|
@ -307,7 +307,7 @@ class ResolutionEnqueuer extends EnqueuerImpl {
|
|||
_reporter.internalError(member,
|
||||
'Unenqueued use of $member: ${useSet.iterable(MemberUse.values)}');
|
||||
}
|
||||
}, dryRun: true);
|
||||
}, checkEnqueuerConsistency: true);
|
||||
}
|
||||
|
||||
/// Callback for applying the use of a [member].
|
||||
|
@ -343,14 +343,14 @@ class ResolutionEnqueuer extends EnqueuerImpl {
|
|||
|
||||
@override
|
||||
void processDynamicUse(DynamicUse dynamicUse) {
|
||||
task.measure(() {
|
||||
task.measureSubtask('resolution.dynamicUse', () {
|
||||
_worldBuilder.registerDynamicUse(dynamicUse, _applyMemberUse);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void processConstantUse(ConstantUse constantUse) {
|
||||
task.measure(() {
|
||||
task.measureSubtask('resolution.constantUse', () {
|
||||
if (_worldBuilder.registerConstantUse(constantUse)) {
|
||||
applyImpact(listener.registerUsedConstant(constantUse.value),
|
||||
impactSource: 'constant use');
|
||||
|
@ -361,18 +361,20 @@ class ResolutionEnqueuer extends EnqueuerImpl {
|
|||
|
||||
@override
|
||||
void processStaticUse(StaticUse staticUse) {
|
||||
_worldBuilder.registerStaticUse(staticUse, _applyMemberUse);
|
||||
// TODO(johnniwinther): Add `ResolutionWorldBuilder.registerConstructorUse`
|
||||
// for these:
|
||||
switch (staticUse.kind) {
|
||||
case StaticUseKind.CONSTRUCTOR_INVOKE:
|
||||
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
|
||||
_registerInstantiatedType(staticUse.type,
|
||||
constructor: staticUse.element, globalDependency: false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
task.measureSubtask('resolution.staticUse', () {
|
||||
_worldBuilder.registerStaticUse(staticUse, _applyMemberUse);
|
||||
// TODO(johnniwinther): Add `ResolutionWorldBuilder.registerConstructorUse`
|
||||
// for these:
|
||||
switch (staticUse.kind) {
|
||||
case StaticUseKind.CONSTRUCTOR_INVOKE:
|
||||
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
|
||||
_registerInstantiatedType(staticUse.type,
|
||||
constructor: staticUse.element, globalDependency: false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -107,7 +107,7 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
|||
|
||||
void _registerInstantiatedType(InterfaceType type,
|
||||
{bool nativeUsage: false}) {
|
||||
task.measure(() {
|
||||
task.measureSubtask('codegen.typeUse', () {
|
||||
_worldBuilder.registerTypeInstantiation(type, _applyClassUse);
|
||||
listener.registerInstantiatedType(type, nativeUsage: nativeUsage);
|
||||
});
|
||||
|
@ -127,7 +127,7 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
|||
failedAt(member,
|
||||
'Unenqueued use of $member: ${useSet.iterable(MemberUse.values)}');
|
||||
}
|
||||
}, dryRun: true);
|
||||
}, checkEnqueuerConsistency: true);
|
||||
}
|
||||
|
||||
/// Callback for applying the use of a [cls].
|
||||
|
@ -160,26 +160,28 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
|||
|
||||
@override
|
||||
void processDynamicUse(DynamicUse dynamicUse) {
|
||||
task.measure(() {
|
||||
task.measureSubtask('codegen.dynamicUse', () {
|
||||
_worldBuilder.registerDynamicUse(dynamicUse, _applyMemberUse);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void processStaticUse(StaticUse staticUse) {
|
||||
_worldBuilder.registerStaticUse(staticUse, _applyMemberUse);
|
||||
switch (staticUse.kind) {
|
||||
case StaticUseKind.CONSTRUCTOR_INVOKE:
|
||||
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
|
||||
processTypeUse(new TypeUse.instantiation(staticUse.type));
|
||||
break;
|
||||
case StaticUseKind.INLINING:
|
||||
// TODO(johnniwinther): Should this be tracked with _MemberUsage ?
|
||||
listener.registerUsedElement(staticUse.element);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
task.measureSubtask('codegen.staticUse', () {
|
||||
_worldBuilder.registerStaticUse(staticUse, _applyMemberUse);
|
||||
switch (staticUse.kind) {
|
||||
case StaticUseKind.CONSTRUCTOR_INVOKE:
|
||||
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
|
||||
processTypeUse(new TypeUse.instantiation(staticUse.type));
|
||||
break;
|
||||
case StaticUseKind.INLINING:
|
||||
// TODO(johnniwinther): Should this be tracked with _MemberUsage ?
|
||||
listener.registerUsedElement(staticUse.element);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -229,7 +231,7 @@ class CodegenEnqueuer extends EnqueuerImpl {
|
|||
|
||||
@override
|
||||
void processConstantUse(ConstantUse constantUse) {
|
||||
task.measure(() {
|
||||
task.measureSubtask('codegen.constantUse', () {
|
||||
if (_worldBuilder.registerConstantUse(constantUse)) {
|
||||
applyImpact(listener.registerUsedConstant(constantUse.value));
|
||||
_recentConstants = true;
|
||||
|
|
|
@ -53,7 +53,7 @@ class ParameterStubGenerator {
|
|||
_closedWorld.elementEnvironment;
|
||||
|
||||
bool needsSuperGetter(FunctionEntity element) =>
|
||||
_codegenWorld.methodsNeedingSuperGetter.contains(element);
|
||||
_codegenWorld.methodsNeedsSuperGetter(element);
|
||||
|
||||
/// Generates stubs to fill in missing optional named or positional arguments
|
||||
/// and missing type arguments. Returns `null` if no stub is needed.
|
||||
|
|
|
@ -477,7 +477,7 @@ class ProgramBuilder {
|
|||
|
||||
void _addJsInteropStubs(LibrariesMap librariesMap) {
|
||||
if (_classes.containsKey(_commonElements.objectClass)) {
|
||||
var toStringInvocation = _namer.invocationName(Selectors.toString_);
|
||||
js.Name toStringInvocation = _namer.invocationName(Selectors.toString_);
|
||||
// TODO(jacobr): register toString as used so that it is always accessible
|
||||
// from JavaScript.
|
||||
_classes[_commonElements.objectClass].callStubs.add(_buildStubMethod(
|
||||
|
@ -492,22 +492,23 @@ class ProgramBuilder {
|
|||
// a regular getter that returns a JavaScript function and tearing off
|
||||
// a method in the case where there exist multiple JavaScript classes
|
||||
// that conflict on whether the member is a getter or a method.
|
||||
var interceptorClass = _classes[_commonElements.jsJavaScriptObjectClass];
|
||||
var stubNames = new Set<String>();
|
||||
Class interceptorClass = _classes[_commonElements.jsJavaScriptObjectClass];
|
||||
Set<String> stubNames = {};
|
||||
librariesMap
|
||||
.forEach((LibraryEntity library, List<ClassEntity> classElements, _) {
|
||||
for (ClassEntity cls in classElements) {
|
||||
if (_nativeData.isJsInteropClass(cls)) {
|
||||
_elementEnvironment.forEachLocalClassMember(cls,
|
||||
(MemberEntity member) {
|
||||
var jsName = _nativeData.computeUnescapedJSInteropName(member.name);
|
||||
String jsName =
|
||||
_nativeData.computeUnescapedJSInteropName(member.name);
|
||||
if (!member.isInstanceMember) return;
|
||||
if (member.isGetter || member.isField || member.isFunction) {
|
||||
var selectors =
|
||||
Iterable<Selector> selectors =
|
||||
_codegenWorld.getterInvocationsByName(member.name);
|
||||
if (selectors != null && !selectors.isEmpty) {
|
||||
for (var selector in selectors.keys) {
|
||||
var stubName = _namer.invocationName(selector);
|
||||
for (Selector selector in selectors) {
|
||||
js.Name stubName = _namer.invocationName(selector);
|
||||
if (stubNames.add(stubName.key)) {
|
||||
interceptorClass.callStubs.add(_buildStubMethod(stubName,
|
||||
js.js('function(obj) { return obj.# }', [jsName]),
|
||||
|
@ -518,7 +519,7 @@ class ProgramBuilder {
|
|||
}
|
||||
|
||||
if (member.isSetter || (member.isField && !member.isConst)) {
|
||||
var selectors =
|
||||
Iterable<Selector> selectors =
|
||||
_codegenWorld.setterInvocationsByName(member.name);
|
||||
if (selectors != null && !selectors.isEmpty) {
|
||||
var stubName = _namer.setterForMember(member);
|
||||
|
@ -765,11 +766,11 @@ class ProgramBuilder {
|
|||
assert(!field.needsUncheckedSetter);
|
||||
FieldEntity element = field.element;
|
||||
js.Expression code = _generatedCode[element];
|
||||
assert(code != null, "No setter code for field: $field");
|
||||
if (code == null) {
|
||||
// TODO(johnniwinther): Static types are not honoured in the dynamic
|
||||
// uses created in codegen, leading to dead code, as known by the
|
||||
// closed world computation, being triggered by the codegen
|
||||
// enqueuer. We cautiously generate an empty function for this case.
|
||||
// This should never occur because codegen member usage is now
|
||||
// limited by closed world member usage. In the case we've missed a
|
||||
// spot we cautiously generate an empty function.
|
||||
code = js.js("function() {}");
|
||||
}
|
||||
js.Name name = _namer.deriveSetterName(field.accessorName);
|
||||
|
@ -901,9 +902,8 @@ class ProgramBuilder {
|
|||
isClosureCallMethod = true;
|
||||
} else {
|
||||
// Careful with operators.
|
||||
canTearOff = _codegenWorld.hasInvokedGetter(element);
|
||||
assert(canTearOff ||
|
||||
!_codegenWorld.methodsNeedingSuperGetter.contains(element));
|
||||
canTearOff = _codegenWorld.hasInvokedGetter(element) ||
|
||||
_codegenWorld.methodsNeedsSuperGetter(element);
|
||||
tearOffName = _namer.getterForElement(element);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1084,11 +1084,12 @@ class FragmentEmitter {
|
|||
js.Expression code;
|
||||
if (field.isElided) {
|
||||
ConstantValue constantValue = field.constantValue;
|
||||
assert(
|
||||
constantValue != null, "No constant value for elided field: $field");
|
||||
if (constantValue == null) {
|
||||
// TODO(johnniwinther): Static types are not honoured in the dynamic
|
||||
// uses created in codegen, leading to dead code, as known by the closed
|
||||
// world computation, being triggered by the codegen enqueuer. We
|
||||
// cautiously generate a null constant for this case.
|
||||
// This should never occur because codegen member usage is now limited
|
||||
// by closed world member usage. In the case we've missed a spot we
|
||||
// cautiously generate a null constant.
|
||||
constantValue = new NullConstantValue();
|
||||
}
|
||||
code = js.js(
|
||||
|
|
|
@ -32,6 +32,7 @@ import '../serialization/serialization.dart';
|
|||
import '../universe/class_hierarchy.dart';
|
||||
import '../universe/class_set.dart';
|
||||
import '../universe/function_set.dart' show FunctionSet;
|
||||
import '../universe/member_usage.dart';
|
||||
import '../universe/selector.dart';
|
||||
import '../world.dart';
|
||||
import 'element_map.dart';
|
||||
|
@ -93,6 +94,8 @@ class JsClosedWorld implements JClosedWorld {
|
|||
final OutputUnitData outputUnitData;
|
||||
Sorter _sorter;
|
||||
|
||||
final Map<MemberEntity, MemberAccess> memberAccess;
|
||||
|
||||
JsClosedWorld(
|
||||
this.elementMap,
|
||||
this.nativeData,
|
||||
|
@ -113,7 +116,8 @@ class JsClosedWorld implements JClosedWorld {
|
|||
this.annotationsData,
|
||||
this.globalLocalsMap,
|
||||
this.closureDataLookup,
|
||||
this.outputUnitData) {
|
||||
this.outputUnitData,
|
||||
this.memberAccess) {
|
||||
_abstractValueDomain = abstractValueStrategy.createDomain(this);
|
||||
}
|
||||
|
||||
|
@ -167,6 +171,9 @@ class JsClosedWorld implements JClosedWorld {
|
|||
OutputUnitData outputUnitData =
|
||||
new OutputUnitData.readFromDataSource(source);
|
||||
|
||||
Map<MemberEntity, MemberAccess> memberAccess =
|
||||
source.readMemberMap(() => new MemberAccess.readFromDataSource(source));
|
||||
|
||||
source.end(tag);
|
||||
|
||||
return new JsClosedWorld(
|
||||
|
@ -189,7 +196,8 @@ class JsClosedWorld implements JClosedWorld {
|
|||
annotationsData,
|
||||
globalLocalsMap,
|
||||
closureData,
|
||||
outputUnitData);
|
||||
outputUnitData,
|
||||
memberAccess);
|
||||
}
|
||||
|
||||
/// Serializes this [JsClosedWorld] to [sink].
|
||||
|
@ -217,6 +225,8 @@ class JsClosedWorld implements JClosedWorld {
|
|||
annotationsData.writeToDataSink(sink);
|
||||
closureDataLookup.writeToDataSink(sink);
|
||||
outputUnitData.writeToDataSink(sink);
|
||||
sink.writeMemberMap(
|
||||
memberAccess, (MemberAccess access) => access.writeToDataSink(sink));
|
||||
sink.end(tag);
|
||||
}
|
||||
|
||||
|
@ -539,6 +549,11 @@ class JsClosedWorld implements JClosedWorld {
|
|||
return elementEnvironment.isMixinApplication(cls) &&
|
||||
!elementEnvironment.isUnnamedMixinApplication(cls);
|
||||
}
|
||||
|
||||
@override
|
||||
MemberAccess getMemberAccess(MemberEntity member) {
|
||||
return memberAccess[member];
|
||||
}
|
||||
}
|
||||
|
||||
class KernelSorter implements Sorter {
|
||||
|
|
|
@ -29,6 +29,7 @@ import '../options.dart';
|
|||
import '../universe/class_hierarchy.dart';
|
||||
import '../universe/class_set.dart';
|
||||
import '../universe/feature.dart';
|
||||
import '../universe/member_usage.dart';
|
||||
import '../universe/selector.dart';
|
||||
import '../world.dart';
|
||||
import 'closure.dart';
|
||||
|
@ -202,6 +203,11 @@ class JsClosedWorldBuilder {
|
|||
OutputUnitData outputUnitData =
|
||||
_convertOutputUnitData(map, kOutputUnitData, closureData);
|
||||
|
||||
Map<MemberEntity, MemberAccess> memberAccess = map.toBackendMemberMap(
|
||||
closedWorld.liveMemberUsage,
|
||||
(MemberUsage usage) =>
|
||||
new MemberAccess(usage.reads, usage.writes, usage.invokes));
|
||||
|
||||
return new JsClosedWorld(
|
||||
_elementMap,
|
||||
nativeData,
|
||||
|
@ -226,7 +232,8 @@ class JsClosedWorldBuilder {
|
|||
annotationsData,
|
||||
_globalLocalsMap,
|
||||
closureData,
|
||||
outputUnitData);
|
||||
outputUnitData,
|
||||
memberAccess);
|
||||
}
|
||||
|
||||
BackendUsage _convertBackendUsage(
|
||||
|
@ -487,7 +494,7 @@ class JsClosedWorldBuilder {
|
|||
map.toBackendLibrary,
|
||||
convertClassMap,
|
||||
convertMemberMap,
|
||||
(m) => convertMap<ConstantValue, OutputUnit>(
|
||||
(m) => convertMap<ConstantValue, OutputUnit, OutputUnit>(
|
||||
m, map.toBackendConstant, (v) => v));
|
||||
}
|
||||
}
|
||||
|
@ -633,20 +640,20 @@ abstract class JsToFrontendMap {
|
|||
return convertMap(map, toBackendClass, convert);
|
||||
}
|
||||
|
||||
Map<MemberEntity, V> toBackendMemberMap<V>(
|
||||
Map<MemberEntity, V> map, V convert(V value)) {
|
||||
Map<MemberEntity, V2> toBackendMemberMap<V1, V2>(
|
||||
Map<MemberEntity, V1> map, V2 convert(V1 value)) {
|
||||
return convertMap(map, toBackendMember, convert);
|
||||
}
|
||||
}
|
||||
|
||||
E identity<E>(E element) => element;
|
||||
|
||||
Map<K, V> convertMap<K, V>(
|
||||
Map<K, V> map, K convertKey(K key), V convertValue(V value)) {
|
||||
Map<K, V> newMap = <K, V>{};
|
||||
map.forEach((K key, V value) {
|
||||
Map<K, V2> convertMap<K, V1, V2>(
|
||||
Map<K, V1> map, K convertKey(K key), V2 convertValue(V1 value)) {
|
||||
Map<K, V2> newMap = <K, V2>{};
|
||||
map.forEach((K key, V1 value) {
|
||||
K newKey = convertKey(key);
|
||||
V newValue = convertValue(value);
|
||||
V2 newValue = convertValue(value);
|
||||
if (newKey != null && newValue != null) {
|
||||
// Entities that are not used don't have a corresponding backend entity.
|
||||
newMap[newKey] = newValue;
|
||||
|
|
|
@ -1979,6 +1979,8 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
|
|||
// for this to work.
|
||||
assert(selector.applies(target),
|
||||
failedAt(node, '$selector does not apply to $target'));
|
||||
assert(!selector.isGetter && !selector.isSetter,
|
||||
"Unexpected direct invocation selector: $selector.");
|
||||
_registry.registerStaticUse(new StaticUse.directInvoke(
|
||||
target, selector.callStructure, node.typeArguments));
|
||||
} else {
|
||||
|
|
|
@ -46,9 +46,9 @@ abstract class CodegenWorld extends BuiltWorld {
|
|||
|
||||
Map<Selector, SelectorConstraints> invocationsByName(String name);
|
||||
|
||||
Map<Selector, SelectorConstraints> getterInvocationsByName(String name);
|
||||
Iterable<Selector> getterInvocationsByName(String name);
|
||||
|
||||
Map<Selector, SelectorConstraints> setterInvocationsByName(String name);
|
||||
Iterable<Selector> setterInvocationsByName(String name);
|
||||
|
||||
void forEachInvokedName(
|
||||
f(String name, Map<Selector, SelectorConstraints> selectors));
|
||||
|
@ -68,7 +68,7 @@ abstract class CodegenWorld extends BuiltWorld {
|
|||
/// All directly or indirectly instantiated classes.
|
||||
Iterable<ClassEntity> get instantiatedClasses;
|
||||
|
||||
Iterable<FunctionEntity> get methodsNeedingSuperGetter;
|
||||
bool methodsNeedsSuperGetter(FunctionEntity function);
|
||||
|
||||
/// The calls [f] for all static fields.
|
||||
void forEachStaticField(void Function(FieldEntity) f);
|
||||
|
@ -111,8 +111,6 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
/// Classes implemented by directly instantiated classes.
|
||||
final Set<ClassEntity> _implementedClasses = new Set<ClassEntity>();
|
||||
|
||||
final Set<FunctionEntity> _methodsNeedingSuperGetter =
|
||||
new Set<FunctionEntity>();
|
||||
final Map<String, Map<Selector, SelectorConstraints>> _invokedNames =
|
||||
<String, Map<Selector, SelectorConstraints>>{};
|
||||
final Map<String, Map<Selector, SelectorConstraints>> _invokedGetters =
|
||||
|
@ -129,14 +127,22 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
final Map<MemberEntity, MemberUsage> _memberUsage =
|
||||
<MemberEntity, MemberUsage>{};
|
||||
|
||||
/// Map containing instance members of live classes that are not yet live
|
||||
/// themselves.
|
||||
final Map<String, Set<MemberUsage>> _instanceMembersByName =
|
||||
/// Map containing instance members of live classes that have not yet been
|
||||
/// fully invoked dynamically.
|
||||
///
|
||||
/// A method is fully invoked if all is optional parameter have been passed
|
||||
/// in some invocation.
|
||||
final Map<String, Set<MemberUsage>> _invokableInstanceMembersByName =
|
||||
<String, Set<MemberUsage>>{};
|
||||
|
||||
/// Map containing instance methods of live classes that are not yet
|
||||
/// closurized.
|
||||
final Map<String, Set<MemberUsage>> _instanceFunctionsByName =
|
||||
/// Map containing instance members of live classes that have not yet been
|
||||
/// read from dynamically.
|
||||
final Map<String, Set<MemberUsage>> _readableInstanceMembersByName =
|
||||
<String, Set<MemberUsage>>{};
|
||||
|
||||
/// Map containing instance members of live classes that have not yet been
|
||||
/// written to dynamically.
|
||||
final Map<String, Set<MemberUsage>> _writableInstanceMembersByName =
|
||||
<String, Set<MemberUsage>>{};
|
||||
|
||||
final Set<DartType> _isChecks = new Set<DartType>();
|
||||
|
@ -208,35 +214,51 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
return false;
|
||||
}
|
||||
|
||||
bool hasInvocation(MemberEntity member) {
|
||||
return _hasMatchingSelector(
|
||||
_invokedNames[member.name], member, _closedWorld);
|
||||
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, _closedWorld)) {
|
||||
callStructures ??= new Set<CallStructure>();
|
||||
callStructures.add(selector.callStructure);
|
||||
}
|
||||
}
|
||||
}
|
||||
return callStructures ?? const <CallStructure>[];
|
||||
}
|
||||
|
||||
bool hasInvokedGetter(MemberEntity member) {
|
||||
return _hasMatchingSelector(
|
||||
_invokedGetters[member.name], member, _closedWorld) ||
|
||||
member.isFunction && _methodsNeedingSuperGetter.contains(member);
|
||||
Iterable<CallStructure> _getInvocationCallStructures(MemberEntity member) {
|
||||
return _getMatchingCallStructures(_invokedNames[member.name], member);
|
||||
}
|
||||
|
||||
bool hasInvokedSetter(MemberEntity member) {
|
||||
bool _hasInvokedGetter(MemberEntity member) {
|
||||
return _hasMatchingSelector(
|
||||
_invokedGetters[member.name], member, _closedWorld);
|
||||
}
|
||||
|
||||
bool _hasInvokedSetter(MemberEntity member) {
|
||||
return _hasMatchingSelector(
|
||||
_invokedSetters[member.name], member, _closedWorld);
|
||||
}
|
||||
|
||||
bool registerDynamicUse(
|
||||
void registerDynamicUse(
|
||||
DynamicUse dynamicUse, MemberUsedCallback memberUsed) {
|
||||
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 shouldBeRemoved(MemberUsage usage)) {
|
||||
_processSet(memberMap, methodName, (MemberUsage usage) {
|
||||
if (selector.appliesUnnamed(usage.entity) &&
|
||||
_selectorConstraintsStrategy.appliedUnnamed(
|
||||
dynamicUse, usage.entity, _closedWorld)) {
|
||||
memberUsed(usage.entity, action(usage));
|
||||
return true;
|
||||
return shouldBeRemoved(usage);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
@ -247,27 +269,31 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
registerDynamicInvocation(
|
||||
dynamicUse.selector, dynamicUse.typeArguments);
|
||||
if (_registerNewSelector(dynamicUse, _invokedNames)) {
|
||||
// We don't track parameters in the codegen world builder, so we
|
||||
// pass `null` instead of the concrete call structure.
|
||||
_process(_instanceMembersByName, (m) => m.invoke(null));
|
||||
return true;
|
||||
_process(
|
||||
_invokableInstanceMembersByName,
|
||||
(m) => m.invoke(Accesses.dynamicAccess, selector.callStructure),
|
||||
// If not all optional parameters have been passed in invocations
|
||||
// we must keep the member in [_invokableInstanceMembersByName].
|
||||
(u) => !u.hasPendingDynamicInvoke);
|
||||
}
|
||||
break;
|
||||
case DynamicUseKind.GET:
|
||||
if (_registerNewSelector(dynamicUse, _invokedGetters)) {
|
||||
_process(_instanceMembersByName, (m) => m.read());
|
||||
_process(_instanceFunctionsByName, (m) => m.read());
|
||||
return true;
|
||||
_process(
|
||||
_readableInstanceMembersByName,
|
||||
(m) => m.read(Accesses.dynamicAccess),
|
||||
(u) => !u.hasPendingDynamicRead);
|
||||
}
|
||||
break;
|
||||
case DynamicUseKind.SET:
|
||||
if (_registerNewSelector(dynamicUse, _invokedSetters)) {
|
||||
_process(_instanceMembersByName, (m) => m.write());
|
||||
return true;
|
||||
_process(
|
||||
_writableInstanceMembersByName,
|
||||
(m) => m.write(Accesses.dynamicAccess),
|
||||
(u) => !u.hasPendingDynamicWrite);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _registerNewSelector(DynamicUse dynamicUse,
|
||||
|
@ -296,32 +322,43 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
MemberUsage usage = _getMemberUsage(element, useSet);
|
||||
switch (staticUse.kind) {
|
||||
case StaticUseKind.STATIC_TEAR_OFF:
|
||||
useSet.addAll(usage.read());
|
||||
useSet.addAll(usage.read(Accesses.staticAccess));
|
||||
break;
|
||||
case StaticUseKind.FIELD_GET:
|
||||
case StaticUseKind.FIELD_SET:
|
||||
case StaticUseKind.INSTANCE_FIELD_GET:
|
||||
case StaticUseKind.INSTANCE_FIELD_SET:
|
||||
case StaticUseKind.CALL_METHOD:
|
||||
// TODO(johnniwinther): Avoid this. Currently [FIELD_GET] and
|
||||
// [FIELD_SET] contains [BoxFieldElement]s which we cannot enqueue.
|
||||
// Also [CLOSURE] contains [LocalFunctionElement] which we cannot
|
||||
// enqueue.
|
||||
break;
|
||||
case StaticUseKind.INVOKE:
|
||||
case StaticUseKind.SUPER_INVOKE:
|
||||
registerStaticInvocation(staticUse);
|
||||
// We don't track parameters in the codegen world builder, so we
|
||||
// pass `null` instead of the concrete call structure.
|
||||
useSet.addAll(usage.invoke(null));
|
||||
useSet.addAll(
|
||||
usage.invoke(Accesses.superAccess, staticUse.callStructure));
|
||||
break;
|
||||
case StaticUseKind.STATIC_INVOKE:
|
||||
registerStaticInvocation(staticUse);
|
||||
useSet.addAll(
|
||||
usage.invoke(Accesses.staticAccess, staticUse.callStructure));
|
||||
break;
|
||||
case StaticUseKind.SUPER_FIELD_SET:
|
||||
case StaticUseKind.SET:
|
||||
useSet.addAll(usage.write());
|
||||
useSet.addAll(usage.write(Accesses.superAccess));
|
||||
break;
|
||||
case StaticUseKind.SUPER_SETTER_SET:
|
||||
useSet.addAll(usage.write(Accesses.superAccess));
|
||||
break;
|
||||
case StaticUseKind.STATIC_SET:
|
||||
useSet.addAll(usage.write(Accesses.staticAccess));
|
||||
break;
|
||||
case StaticUseKind.SUPER_TEAR_OFF:
|
||||
_methodsNeedingSuperGetter.add(element);
|
||||
useSet.addAll(usage.read());
|
||||
useSet.addAll(usage.read(Accesses.superAccess));
|
||||
break;
|
||||
case StaticUseKind.GET:
|
||||
useSet.addAll(usage.read());
|
||||
case StaticUseKind.SUPER_GET:
|
||||
useSet.addAll(usage.read(Accesses.superAccess));
|
||||
break;
|
||||
case StaticUseKind.STATIC_GET:
|
||||
useSet.addAll(usage.read(Accesses.staticAccess));
|
||||
break;
|
||||
case StaticUseKind.FIELD_INIT:
|
||||
useSet.addAll(usage.init());
|
||||
|
@ -333,14 +370,15 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
|
||||
// We don't track parameters in the codegen world builder, so we
|
||||
// pass `null` instead of the concrete call structure.
|
||||
useSet.addAll(usage.invoke(null));
|
||||
useSet.addAll(
|
||||
usage.invoke(Accesses.staticAccess, staticUse.callStructure));
|
||||
break;
|
||||
case StaticUseKind.DIRECT_INVOKE:
|
||||
MemberEntity member = staticUse.element;
|
||||
// We don't track parameters in the codegen world builder, so we
|
||||
// pass `null` instead of the concrete call structure.
|
||||
useSet.addAll(usage.invoke(null));
|
||||
_instanceMembersByName[usage.entity.name]?.remove(usage);
|
||||
useSet.addAll(
|
||||
usage.invoke(Accesses.staticAccess, staticUse.callStructure));
|
||||
if (staticUse.typeArguments?.isNotEmpty ?? false) {
|
||||
registerDynamicInvocation(
|
||||
new Selector.call(member.memberName, staticUse.callStructure),
|
||||
|
@ -361,85 +399,99 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
}
|
||||
|
||||
void processClassMembers(ClassEntity cls, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false}) {
|
||||
{bool checkEnqueuerConsistency: false}) {
|
||||
_elementEnvironment.forEachClassMember(cls,
|
||||
(ClassEntity cls, MemberEntity member) {
|
||||
_processInstantiatedClassMember(cls, member, memberUsed, dryRun: dryRun);
|
||||
_processInstantiatedClassMember(cls, member, memberUsed,
|
||||
checkEnqueuerConsistency: checkEnqueuerConsistency);
|
||||
});
|
||||
}
|
||||
|
||||
void _processInstantiatedClassMember(
|
||||
ClassEntity cls, MemberEntity member, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false}) {
|
||||
{bool checkEnqueuerConsistency: false}) {
|
||||
if (!member.isInstanceMember) return;
|
||||
EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
|
||||
_getMemberUsage(member, useSet);
|
||||
MemberUsage usage = _getMemberUsage(member, useSet);
|
||||
if (useSet.isNotEmpty) {
|
||||
memberUsed(member, useSet);
|
||||
if (checkEnqueuerConsistency) {
|
||||
throw new SpannableAssertionFailure(member,
|
||||
'Unenqueued usage of $member: \nbefore: <none>\nafter : $usage');
|
||||
} else {
|
||||
memberUsed(member, useSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemberUsage _getMemberUsage(MemberEntity member, EnumSet<MemberUse> useSet,
|
||||
{bool dryRun: false}) {
|
||||
{bool checkEnqueuerConsistency: false}) {
|
||||
// TODO(johnniwinther): Change [TypeMask] to not apply to a superclass
|
||||
// member unless the class has been instantiated. Similar to
|
||||
// [StrongModeConstraint].
|
||||
MemberUsage usage = _memberUsage[member];
|
||||
if (usage == null) {
|
||||
MemberAccess potentialAccess = _closedWorld.getMemberAccess(member);
|
||||
if (member.isInstanceMember) {
|
||||
String memberName = member.name;
|
||||
ClassEntity cls = member.enclosingClass;
|
||||
bool isNative = _nativeBasicData.isNativeClass(cls);
|
||||
usage = new MemberUsage(member);
|
||||
usage = new MemberUsage(member, potentialAccess: potentialAccess);
|
||||
if (member.isField && !isNative) {
|
||||
useSet.addAll(usage.init());
|
||||
}
|
||||
if (member is JSignatureMethod) {
|
||||
// We mark signature methods as "always used" to prevent them from being
|
||||
// optimized away.
|
||||
// We mark signature methods as "always used" to prevent them from
|
||||
// being optimized away.
|
||||
// TODO(johnniwinther): Make this a part of the regular enqueueing.
|
||||
useSet.addAll(usage.invoke(CallStructure.NO_ARGS));
|
||||
useSet.addAll(
|
||||
usage.invoke(Accesses.dynamicAccess, CallStructure.NO_ARGS));
|
||||
}
|
||||
|
||||
if (!usage.hasRead && hasInvokedGetter(member)) {
|
||||
useSet.addAll(usage.read());
|
||||
if (usage.hasPendingDynamicRead && _hasInvokedGetter(member)) {
|
||||
useSet.addAll(usage.read(Accesses.dynamicAccess));
|
||||
}
|
||||
if (!usage.hasWrite && hasInvokedSetter(member)) {
|
||||
useSet.addAll(usage.write());
|
||||
if (usage.hasPendingDynamicWrite && _hasInvokedSetter(member)) {
|
||||
useSet.addAll(usage.write(Accesses.dynamicAccess));
|
||||
}
|
||||
if (!usage.hasInvoke && hasInvocation(member)) {
|
||||
// We don't track parameters in the codegen world builder, so we
|
||||
// pass `null` instead of the concrete call structures.
|
||||
useSet.addAll(usage.invoke(null));
|
||||
if (usage.hasPendingDynamicInvoke) {
|
||||
Iterable<CallStructure> callStructures =
|
||||
_getInvocationCallStructures(member);
|
||||
for (CallStructure callStructure in callStructures) {
|
||||
useSet.addAll(usage.invoke(Accesses.dynamicAccess, callStructure));
|
||||
if (!usage.hasPendingDynamicInvoke) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
if (usage.hasPendingClosurizationUse) {
|
||||
// Store the member in [instanceFunctionsByName] to catch
|
||||
// getters on the function.
|
||||
_instanceFunctionsByName
|
||||
.putIfAbsent(usage.entity.name, () => new Set<MemberUsage>())
|
||||
if (!checkEnqueuerConsistency) {
|
||||
if (usage.hasPendingDynamicInvoke) {
|
||||
_invokableInstanceMembersByName
|
||||
.putIfAbsent(memberName, () => {})
|
||||
.add(usage);
|
||||
}
|
||||
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>())
|
||||
if (usage.hasPendingDynamicRead) {
|
||||
_readableInstanceMembersByName
|
||||
.putIfAbsent(memberName, () => {})
|
||||
.add(usage);
|
||||
}
|
||||
if (usage.hasPendingDynamicWrite) {
|
||||
_writableInstanceMembersByName
|
||||
.putIfAbsent(memberName, () => {})
|
||||
.add(usage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
usage = new MemberUsage(member);
|
||||
usage = new MemberUsage(member, potentialAccess: potentialAccess);
|
||||
if (member.isField) {
|
||||
useSet.addAll(usage.init());
|
||||
}
|
||||
}
|
||||
if (!dryRun) {
|
||||
if (!checkEnqueuerConsistency) {
|
||||
_memberUsage[member] = usage;
|
||||
}
|
||||
} else {
|
||||
if (dryRun) {
|
||||
if (checkEnqueuerConsistency) {
|
||||
usage = usage.clone();
|
||||
}
|
||||
}
|
||||
|
@ -521,7 +573,6 @@ class CodegenWorldBuilderImpl extends WorldBuilderBase
|
|||
constTypeLiterals: _constTypeLiterals,
|
||||
directlyInstantiatedClasses: directlyInstantiatedClasses,
|
||||
typeVariableTypeLiterals: typeVariableTypeLiterals,
|
||||
methodsNeedingSuperGetter: _methodsNeedingSuperGetter,
|
||||
instantiatedClasses: instantiatedClasses,
|
||||
isChecks: _isChecks,
|
||||
instantiatedTypes: _instantiatedTypes,
|
||||
|
@ -549,9 +600,6 @@ class CodegenWorldImpl implements CodegenWorld {
|
|||
@override
|
||||
final Iterable<TypeVariableType> typeVariableTypeLiterals;
|
||||
|
||||
@override
|
||||
final Iterable<FunctionEntity> methodsNeedingSuperGetter;
|
||||
|
||||
@override
|
||||
final Iterable<ClassEntity> instantiatedClasses;
|
||||
|
||||
|
@ -580,7 +628,6 @@ class CodegenWorldImpl implements CodegenWorld {
|
|||
{this.constTypeLiterals,
|
||||
this.directlyInstantiatedClasses,
|
||||
this.typeVariableTypeLiterals,
|
||||
this.methodsNeedingSuperGetter,
|
||||
this.instantiatedClasses,
|
||||
this.isChecks,
|
||||
this.instantiatedTypes,
|
||||
|
@ -754,15 +801,23 @@ class CodegenWorldImpl implements CodegenWorld {
|
|||
|
||||
@override
|
||||
bool hasInvokedGetter(MemberEntity member) {
|
||||
return _hasMatchingSelector(
|
||||
_invokedGetters[member.name], member, _closedWorld) ||
|
||||
member.isFunction && methodsNeedingSuperGetter.contains(member);
|
||||
MemberUsage memberUsage = _liveMemberUsage[member];
|
||||
if (memberUsage == null) return false;
|
||||
return memberUsage.reads.contains(Access.dynamicAccess);
|
||||
}
|
||||
|
||||
@override
|
||||
bool methodsNeedsSuperGetter(FunctionEntity function) {
|
||||
MemberUsage memberUsage = _liveMemberUsage[function];
|
||||
if (memberUsage == null) return false;
|
||||
return memberUsage.reads.contains(Access.superAccess);
|
||||
}
|
||||
|
||||
@override
|
||||
bool hasInvokedSetter(MemberEntity member) {
|
||||
return _hasMatchingSelector(
|
||||
_invokedSetters[member.name], member, _closedWorld);
|
||||
MemberUsage memberUsage = _liveMemberUsage[member];
|
||||
if (memberUsage == null) return false;
|
||||
return memberUsage.writes.contains(Access.dynamicAccess);
|
||||
}
|
||||
|
||||
Map<Selector, SelectorConstraints> _asUnmodifiable(
|
||||
|
@ -777,13 +832,13 @@ class CodegenWorldImpl implements CodegenWorld {
|
|||
}
|
||||
|
||||
@override
|
||||
Map<Selector, SelectorConstraints> getterInvocationsByName(String name) {
|
||||
return _asUnmodifiable(_invokedGetters[name]);
|
||||
Iterable<Selector> getterInvocationsByName(String name) {
|
||||
return _invokedGetters[name]?.keys;
|
||||
}
|
||||
|
||||
@override
|
||||
Map<Selector, SelectorConstraints> setterInvocationsByName(String name) {
|
||||
return _asUnmodifiable(_invokedSetters[name]);
|
||||
Iterable<Selector> setterInvocationsByName(String name) {
|
||||
return _invokedSetters[name]?.keys;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -66,8 +66,12 @@ abstract class ResolutionEnqueuerWorldBuilder extends ResolutionWorldBuilder {
|
|||
|
||||
/// Computes usage for all members declared by [cls]. Calls [membersUsed] with
|
||||
/// the usage changes for each member.
|
||||
///
|
||||
/// If [checkEnqueuerConsistency] is `true` we check that no new member
|
||||
/// usage can be found. This check is performed without changing the already
|
||||
/// collected member usage.
|
||||
void processClassMembers(ClassEntity cls, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false});
|
||||
{bool checkEnqueuerConsistency: false});
|
||||
|
||||
/// Applies the [dynamicUse] to applicable instance members. Calls
|
||||
/// [membersUsed] with the usage changes for each member.
|
||||
|
@ -288,14 +292,22 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
|
||||
Map<MemberEntity, MemberUsage> get memberUsageForTesting => _memberUsage;
|
||||
|
||||
/// Map containing instance members of live classes that are not yet fully
|
||||
/// live themselves.
|
||||
final Map<String, Set<MemberUsage>> _instanceMembersByName =
|
||||
/// Map containing instance members of live classes that have not yet been
|
||||
/// fully invoked dynamically.
|
||||
///
|
||||
/// A method is fully invoked if all is optional parameter have been passed
|
||||
/// in some invocation.
|
||||
final Map<String, Set<MemberUsage>> _invokableInstanceMembersByName =
|
||||
<String, Set<MemberUsage>>{};
|
||||
|
||||
/// Map containing instance methods of live classes that are not yet
|
||||
/// closurized.
|
||||
final Map<String, Set<MemberUsage>> _instanceFunctionsByName =
|
||||
/// Map containing instance members of live classes that have not yet been
|
||||
/// read from dynamically.
|
||||
final Map<String, Set<MemberUsage>> _readableInstanceMembersByName =
|
||||
<String, Set<MemberUsage>>{};
|
||||
|
||||
/// Map containing instance members of live classes that have not yet been
|
||||
/// written to dynamically.
|
||||
final Map<String, Set<MemberUsage>> _writableInstanceMembersByName =
|
||||
<String, Set<MemberUsage>>{};
|
||||
|
||||
final Set<FieldEntity> _fieldSetters = new Set<FieldEntity>();
|
||||
|
@ -490,8 +502,7 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
}
|
||||
|
||||
bool _hasInvokedGetter(MemberEntity member) {
|
||||
return _hasMatchingSelector(_invokedGetters[member.name], member) ||
|
||||
member.isFunction && _methodsNeedingSuperGetter.contains(member);
|
||||
return _hasMatchingSelector(_invokedGetters[member.name], member);
|
||||
}
|
||||
|
||||
bool _hasInvokedSetter(MemberEntity member) {
|
||||
|
@ -525,23 +536,36 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
dynamicUse.selector, dynamicUse.typeArguments);
|
||||
if (_registerNewSelector(dynamicUse, _invokedNames)) {
|
||||
_process(
|
||||
_instanceMembersByName,
|
||||
(m) => m.invoke(dynamicUse.selector.callStructure),
|
||||
(u) => !u.hasPendingNormalUse);
|
||||
_invokableInstanceMembersByName,
|
||||
(m) => m.invoke(
|
||||
Accesses.dynamicAccess, dynamicUse.selector.callStructure),
|
||||
// If not all optional parameters have been passed in invocations
|
||||
// we must keep the member in [_invokableInstanceMembersByName].
|
||||
// TODO(johnniwinther): Also remove from
|
||||
// [_readableInstanceMembersByName] in case of getters/setters.
|
||||
(u) => !u.hasPendingDynamicInvoke);
|
||||
}
|
||||
break;
|
||||
case DynamicUseKind.GET:
|
||||
if (_registerNewSelector(dynamicUse, _invokedGetters)) {
|
||||
_process(_instanceMembersByName, (m) => m.read(),
|
||||
(u) => !u.hasPendingNormalUse);
|
||||
_process(_instanceFunctionsByName, (m) => m.read(),
|
||||
(u) => !u.hasPendingClosurizationUse);
|
||||
_process(
|
||||
_readableInstanceMembersByName,
|
||||
(m) => m.read(Accesses.dynamicAccess),
|
||||
// TODO(johnniwinther): Members cannot be partially read so
|
||||
// we should always remove them.
|
||||
// TODO(johnniwinther): Also remove from
|
||||
// [_invokableInstanceMembersByName] in case of methods.
|
||||
(u) => !u.hasPendingDynamicRead);
|
||||
}
|
||||
break;
|
||||
case DynamicUseKind.SET:
|
||||
if (_registerNewSelector(dynamicUse, _invokedSetters)) {
|
||||
_process(_instanceMembersByName, (m) => m.write(),
|
||||
(u) => !u.hasPendingNormalUse);
|
||||
_process(
|
||||
_writableInstanceMembersByName,
|
||||
(m) => m.write(Accesses.dynamicAccess),
|
||||
// TODO(johnniwinther): Members cannot be partially written so
|
||||
// we should always remove them.
|
||||
(u) => !u.hasPendingDynamicWrite);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -604,10 +628,11 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
// [FIELD_SET] contains [BoxFieldElement]s which we cannot enqueue.
|
||||
// Also [CLOSURE] contains [LocalFunctionElement] which we cannot
|
||||
// enqueue.
|
||||
|
||||
switch (staticUse.kind) {
|
||||
case StaticUseKind.FIELD_GET:
|
||||
case StaticUseKind.INSTANCE_FIELD_GET:
|
||||
break;
|
||||
case StaticUseKind.FIELD_SET:
|
||||
case StaticUseKind.INSTANCE_FIELD_SET:
|
||||
_fieldSetters.add(staticUse.element);
|
||||
break;
|
||||
case StaticUseKind.CLOSURE:
|
||||
|
@ -615,21 +640,27 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
// Already handled above.
|
||||
break;
|
||||
case StaticUseKind.SUPER_TEAR_OFF:
|
||||
useSet.addAll(usage.read());
|
||||
useSet.addAll(usage.read(Accesses.superAccess));
|
||||
_methodsNeedingSuperGetter.add(staticUse.element);
|
||||
break;
|
||||
case StaticUseKind.SUPER_FIELD_SET:
|
||||
_fieldSetters.add(staticUse.element);
|
||||
useSet.addAll(usage.write());
|
||||
useSet.addAll(usage.write(Accesses.superAccess));
|
||||
break;
|
||||
case StaticUseKind.GET:
|
||||
useSet.addAll(usage.read());
|
||||
case StaticUseKind.SUPER_GET:
|
||||
useSet.addAll(usage.read(Accesses.superAccess));
|
||||
break;
|
||||
case StaticUseKind.STATIC_GET:
|
||||
useSet.addAll(usage.read(Accesses.staticAccess));
|
||||
break;
|
||||
case StaticUseKind.STATIC_TEAR_OFF:
|
||||
useSet.addAll(usage.read());
|
||||
useSet.addAll(usage.read(Accesses.staticAccess));
|
||||
break;
|
||||
case StaticUseKind.SET:
|
||||
useSet.addAll(usage.write());
|
||||
case StaticUseKind.SUPER_SETTER_SET:
|
||||
useSet.addAll(usage.write(Accesses.superAccess));
|
||||
break;
|
||||
case StaticUseKind.STATIC_SET:
|
||||
useSet.addAll(usage.write(Accesses.staticAccess));
|
||||
break;
|
||||
case StaticUseKind.FIELD_INIT:
|
||||
useSet.addAll(usage.init());
|
||||
|
@ -637,13 +668,20 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
case StaticUseKind.FIELD_CONSTANT_INIT:
|
||||
useSet.addAll(usage.constantInit(staticUse.constant));
|
||||
break;
|
||||
case StaticUseKind.INVOKE:
|
||||
case StaticUseKind.SUPER_INVOKE:
|
||||
registerStaticInvocation(staticUse);
|
||||
useSet.addAll(usage.invoke(staticUse.callStructure));
|
||||
useSet.addAll(
|
||||
usage.invoke(Accesses.superAccess, staticUse.callStructure));
|
||||
break;
|
||||
case StaticUseKind.STATIC_INVOKE:
|
||||
registerStaticInvocation(staticUse);
|
||||
useSet.addAll(
|
||||
usage.invoke(Accesses.staticAccess, staticUse.callStructure));
|
||||
break;
|
||||
case StaticUseKind.CONSTRUCTOR_INVOKE:
|
||||
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
|
||||
useSet.addAll(usage.invoke(staticUse.callStructure));
|
||||
useSet.addAll(
|
||||
usage.invoke(Accesses.staticAccess, staticUse.callStructure));
|
||||
break;
|
||||
case StaticUseKind.DIRECT_INVOKE:
|
||||
failedAt(element, 'Direct static use is not supported for resolution.');
|
||||
|
@ -692,10 +730,11 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
|
||||
@override
|
||||
void processClassMembers(ClassEntity cls, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false}) {
|
||||
{bool checkEnqueuerConsistency: false}) {
|
||||
_elementEnvironment.forEachClassMember(cls,
|
||||
(ClassEntity cls, MemberEntity member) {
|
||||
_processInstantiatedClassMember(cls, member, memberUsed, dryRun: dryRun);
|
||||
_processInstantiatedClassMember(cls, member, memberUsed,
|
||||
checkEnqueuerConsistency: checkEnqueuerConsistency);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -720,7 +759,7 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
}
|
||||
|
||||
MemberUsage _getMemberUsage(MemberEntity member, EnumSet<MemberUse> useSet,
|
||||
{bool dryRun: false}) {
|
||||
{bool checkEnqueuerConsistency: false}) {
|
||||
MemberUsage usage = _memberUsage[member];
|
||||
if (usage == null) {
|
||||
if (member.isInstanceMember) {
|
||||
|
@ -736,11 +775,11 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
// 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 isNative = _nativeBasicData.isNativeClass(cls);
|
||||
usage = new MemberUsage(member, trackParameters: true);
|
||||
usage = new MemberUsage(member);
|
||||
if (member.isField && !isNative) {
|
||||
useSet.addAll(usage.init());
|
||||
}
|
||||
if (!dryRun) {
|
||||
if (!checkEnqueuerConsistency) {
|
||||
if (member.isField && isNative) {
|
||||
registerUsedElement(member);
|
||||
}
|
||||
|
@ -751,100 +790,114 @@ class ResolutionWorldBuilderImpl extends WorldBuilderBase
|
|||
}
|
||||
}
|
||||
|
||||
if (!usage.hasRead && _hasInvokedGetter(member)) {
|
||||
useSet.addAll(usage.read());
|
||||
if (usage.hasPendingDynamicRead && _hasInvokedGetter(member)) {
|
||||
useSet.addAll(usage.read(Accesses.dynamicAccess));
|
||||
}
|
||||
if (!usage.isFullyInvoked) {
|
||||
if (usage.hasPendingDynamicInvoke) {
|
||||
Iterable<CallStructure> callStructures =
|
||||
_getInvocationCallStructures(member);
|
||||
for (CallStructure callStructure in callStructures) {
|
||||
useSet.addAll(usage.invoke(callStructure));
|
||||
if (usage.isFullyInvoked) {
|
||||
useSet.addAll(usage.invoke(Accesses.dynamicAccess, callStructure));
|
||||
if (!usage.hasPendingDynamicInvoke) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!usage.hasWrite && _hasInvokedSetter(member)) {
|
||||
useSet.addAll(usage.write());
|
||||
if (usage.hasPendingDynamicWrite && _hasInvokedSetter(member)) {
|
||||
useSet.addAll(usage.write(Accesses.dynamicAccess));
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
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>())
|
||||
if (!checkEnqueuerConsistency) {
|
||||
if (usage.hasPendingDynamicInvoke) {
|
||||
_invokableInstanceMembersByName
|
||||
.putIfAbsent(memberName, () => {})
|
||||
.add(usage);
|
||||
}
|
||||
if (usage.hasPendingClosurizationUse) {
|
||||
// Store the member in [instanceFunctionsByName] to catch
|
||||
// getters on the function.
|
||||
_instanceFunctionsByName
|
||||
.putIfAbsent(memberName, () => new Set<MemberUsage>())
|
||||
if (usage.hasPendingDynamicRead) {
|
||||
_readableInstanceMembersByName
|
||||
.putIfAbsent(memberName, () => {})
|
||||
.add(usage);
|
||||
}
|
||||
if (usage.hasPendingDynamicWrite) {
|
||||
_writableInstanceMembersByName
|
||||
.putIfAbsent(memberName, () => {})
|
||||
.add(usage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
usage = new MemberUsage(member, trackParameters: true);
|
||||
usage = new MemberUsage(member);
|
||||
if (member.isField) {
|
||||
useSet.addAll(usage.init());
|
||||
}
|
||||
}
|
||||
if (!dryRun) {
|
||||
if (!checkEnqueuerConsistency) {
|
||||
_memberUsage[member] = usage;
|
||||
}
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
void _processInstantiatedClassMember(ClassEntity cls,
|
||||
covariant MemberEntity member, MemberUsedCallback memberUsed,
|
||||
{bool dryRun: false}) {
|
||||
void _processInstantiatedClassMember(
|
||||
ClassEntity cls, MemberEntity member, MemberUsedCallback memberUsed,
|
||||
{bool checkEnqueuerConsistency: 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);
|
||||
usage = _getMemberUsage(member, useSet,
|
||||
checkEnqueuerConsistency: checkEnqueuerConsistency);
|
||||
if (useSet.isNotEmpty) {
|
||||
if (checkEnqueuerConsistency) {
|
||||
throw new SpannableAssertionFailure(member,
|
||||
'Unenqueued usage of $member: \nbefore: <none>\nafter : $usage');
|
||||
} else {
|
||||
memberUsed(usage.entity, useSet);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MemberUsage original = usage;
|
||||
if (dryRun) {
|
||||
if (checkEnqueuerConsistency) {
|
||||
usage = usage.clone();
|
||||
}
|
||||
if (!usage.fullyUsed) {
|
||||
if (usage.hasPendingDynamicUse) {
|
||||
EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
|
||||
if (!usage.hasRead && _hasInvokedGetter(member)) {
|
||||
useSet.addAll(usage.read());
|
||||
if (usage.hasPendingDynamicRead && _hasInvokedGetter(member)) {
|
||||
useSet.addAll(usage.read(Accesses.dynamicAccess));
|
||||
}
|
||||
if (!usage.isFullyInvoked) {
|
||||
if (usage.hasPendingDynamicInvoke) {
|
||||
Iterable<CallStructure> callStructures =
|
||||
_getInvocationCallStructures(member);
|
||||
for (CallStructure callStructure in callStructures) {
|
||||
useSet.addAll(usage.invoke(callStructure));
|
||||
if (usage.isFullyInvoked) {
|
||||
useSet.addAll(usage.invoke(Accesses.dynamicAccess, callStructure));
|
||||
if (!usage.hasPendingDynamicInvoke) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!usage.hasWrite && _hasInvokedSetter(member)) {
|
||||
useSet.addAll(usage.write());
|
||||
if (usage.hasPendingDynamicWrite && _hasInvokedSetter(member)) {
|
||||
useSet.addAll(usage.write(Accesses.dynamicAccess));
|
||||
}
|
||||
if (!dryRun) {
|
||||
if (!usage.hasPendingNormalUse) {
|
||||
_instanceMembersByName[memberName]?.remove(usage);
|
||||
if (!checkEnqueuerConsistency) {
|
||||
if (!usage.hasPendingDynamicRead) {
|
||||
_readableInstanceMembersByName[memberName]?.remove(usage);
|
||||
}
|
||||
if (!usage.hasPendingClosurizationUse) {
|
||||
_instanceFunctionsByName[memberName]?.remove(usage);
|
||||
if (!usage.hasPendingDynamicInvoke) {
|
||||
_invokableInstanceMembersByName[memberName]?.remove(usage);
|
||||
}
|
||||
if (!usage.hasPendingDynamicWrite) {
|
||||
_writableInstanceMembersByName[memberName]?.remove(usage);
|
||||
}
|
||||
}
|
||||
if (checkEnqueuerConsistency && !original.dataEquals(usage)) {
|
||||
_elementMap.reporter.internalError(
|
||||
member,
|
||||
'Unenqueued usage of $member: \n'
|
||||
'before: $original\nafter : $usage');
|
||||
}
|
||||
memberUsed(usage.entity, useSet);
|
||||
}
|
||||
if (dryRun && !original.dataEquals(usage)) {
|
||||
_elementMap.reporter.internalError(member,
|
||||
'Unenqueued usage of $member: before: $original, after: $usage');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -147,8 +147,11 @@ enum StaticUseKind {
|
|||
STATIC_TEAR_OFF,
|
||||
SUPER_TEAR_OFF,
|
||||
SUPER_FIELD_SET,
|
||||
FIELD_GET,
|
||||
FIELD_SET,
|
||||
SUPER_GET,
|
||||
SUPER_SETTER_SET,
|
||||
SUPER_INVOKE,
|
||||
INSTANCE_FIELD_GET,
|
||||
INSTANCE_FIELD_SET,
|
||||
CLOSURE,
|
||||
CLOSURE_CALL,
|
||||
CALL_METHOD,
|
||||
|
@ -156,9 +159,9 @@ enum StaticUseKind {
|
|||
CONST_CONSTRUCTOR_INVOKE,
|
||||
DIRECT_INVOKE,
|
||||
INLINING,
|
||||
INVOKE,
|
||||
GET,
|
||||
SET,
|
||||
STATIC_INVOKE,
|
||||
STATIC_GET,
|
||||
STATIC_SET,
|
||||
FIELD_INIT,
|
||||
FIELD_CONSTANT_INIT,
|
||||
}
|
||||
|
@ -197,9 +200,10 @@ class StaticUse {
|
|||
String get shortText {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
switch (kind) {
|
||||
case StaticUseKind.FIELD_SET:
|
||||
case StaticUseKind.INSTANCE_FIELD_SET:
|
||||
case StaticUseKind.SUPER_FIELD_SET:
|
||||
case StaticUseKind.SET:
|
||||
case StaticUseKind.SUPER_SETTER_SET:
|
||||
case StaticUseKind.STATIC_SET:
|
||||
sb.write('set:');
|
||||
break;
|
||||
case StaticUseKind.FIELD_INIT:
|
||||
|
@ -268,8 +272,8 @@ class StaticUse {
|
|||
failedAt(element,
|
||||
"Not CallStructure for static invocation of element $element."));
|
||||
|
||||
return new GenericStaticUse(element, StaticUseKind.INVOKE, callStructure,
|
||||
typeArguments, deferredImport);
|
||||
return new GenericStaticUse(element, StaticUseKind.STATIC_INVOKE,
|
||||
callStructure, typeArguments, deferredImport);
|
||||
}
|
||||
|
||||
/// Closurization of a static or top-level function [element].
|
||||
|
@ -300,7 +304,7 @@ class StaticUse {
|
|||
element.isField || element.isGetter,
|
||||
failedAt(element,
|
||||
"Static get element $element must be a field or a getter."));
|
||||
return new StaticUse.internal(element, StaticUseKind.GET,
|
||||
return new StaticUse.internal(element, StaticUseKind.STATIC_GET,
|
||||
deferredImport: deferredImport);
|
||||
}
|
||||
|
||||
|
@ -317,7 +321,7 @@ class StaticUse {
|
|||
(element.isField && element.isAssignable) || element.isSetter,
|
||||
failedAt(element,
|
||||
"Static set element $element must be a field or a setter."));
|
||||
return new StaticUse.internal(element, StaticUseKind.SET,
|
||||
return new StaticUse.internal(element, StaticUseKind.STATIC_SET,
|
||||
deferredImport: deferredImport);
|
||||
}
|
||||
|
||||
|
@ -348,7 +352,7 @@ class StaticUse {
|
|||
failedAt(element,
|
||||
"Not CallStructure for super invocation of element $element."));
|
||||
return new GenericStaticUse(
|
||||
element, StaticUseKind.INVOKE, callStructure, typeArguments);
|
||||
element, StaticUseKind.SUPER_INVOKE, callStructure, typeArguments);
|
||||
}
|
||||
|
||||
/// Read access of a super field or getter [element].
|
||||
|
@ -361,7 +365,7 @@ class StaticUse {
|
|||
element.isField || element.isGetter,
|
||||
failedAt(element,
|
||||
"Super get element $element must be a field or a getter."));
|
||||
return new StaticUse.internal(element, StaticUseKind.GET);
|
||||
return new StaticUse.internal(element, StaticUseKind.SUPER_GET);
|
||||
}
|
||||
|
||||
/// Write access of a super field [element].
|
||||
|
@ -383,7 +387,7 @@ class StaticUse {
|
|||
element, "Super set element $element must be an instance method."));
|
||||
assert(element.isSetter,
|
||||
failedAt(element, "Super set element $element must be a setter."));
|
||||
return new StaticUse.internal(element, StaticUseKind.SET);
|
||||
return new StaticUse.internal(element, StaticUseKind.SUPER_SETTER_SET);
|
||||
}
|
||||
|
||||
/// Closurization of a super method [element].
|
||||
|
@ -411,7 +415,7 @@ class StaticUse {
|
|||
element,
|
||||
"Not CallStructure for super constructor invocation of element "
|
||||
"$element."));
|
||||
return new StaticUse.internal(element, StaticUseKind.INVOKE,
|
||||
return new StaticUse.internal(element, StaticUseKind.STATIC_INVOKE,
|
||||
callStructure: callStructure);
|
||||
}
|
||||
|
||||
|
@ -425,14 +429,14 @@ class StaticUse {
|
|||
element,
|
||||
"Not CallStructure for constructor body invocation of element "
|
||||
"$element."));
|
||||
return new StaticUse.internal(element, StaticUseKind.INVOKE,
|
||||
return new StaticUse.internal(element, StaticUseKind.STATIC_INVOKE,
|
||||
callStructure: callStructure);
|
||||
}
|
||||
|
||||
/// Direct invocation of a generator (body) [element], as a static call or
|
||||
/// through a this or super constructor call.
|
||||
factory StaticUse.generatorBodyInvoke(FunctionEntity element) {
|
||||
return new StaticUse.internal(element, StaticUseKind.INVOKE,
|
||||
return new StaticUse.internal(element, StaticUseKind.STATIC_INVOKE,
|
||||
callStructure: CallStructure.NO_ARGS);
|
||||
}
|
||||
|
||||
|
@ -459,7 +463,7 @@ class StaticUse {
|
|||
element.isField || element.isGetter,
|
||||
failedAt(element,
|
||||
"Direct get element $element must be a field or a getter."));
|
||||
return new StaticUse.internal(element, StaticUseKind.GET);
|
||||
return new StaticUse.internal(element, StaticUseKind.STATIC_GET);
|
||||
}
|
||||
|
||||
/// Direct write access of a field [element].
|
||||
|
@ -470,7 +474,7 @@ class StaticUse {
|
|||
"Direct set element $element must be an instance member."));
|
||||
assert(element.isField,
|
||||
failedAt(element, "Direct set element $element must be a field."));
|
||||
return new StaticUse.internal(element, StaticUseKind.SET);
|
||||
return new StaticUse.internal(element, StaticUseKind.STATIC_SET);
|
||||
}
|
||||
|
||||
/// Constructor invocation of [element] with the given [callStructure].
|
||||
|
@ -486,7 +490,7 @@ class StaticUse {
|
|||
element,
|
||||
"Not CallStructure for constructor invocation of element "
|
||||
"$element."));
|
||||
return new StaticUse.internal(element, StaticUseKind.INVOKE,
|
||||
return new StaticUse.internal(element, StaticUseKind.STATIC_INVOKE,
|
||||
callStructure: callStructure);
|
||||
}
|
||||
|
||||
|
@ -559,7 +563,7 @@ class StaticUse {
|
|||
element.isInstanceMember || element is JRecordField,
|
||||
failedAt(element,
|
||||
"Field init element $element must be an instance or boxed field."));
|
||||
return new StaticUse.internal(element, StaticUseKind.FIELD_GET);
|
||||
return new StaticUse.internal(element, StaticUseKind.INSTANCE_FIELD_GET);
|
||||
}
|
||||
|
||||
/// Write access of an instance field or boxed field [element].
|
||||
|
@ -568,7 +572,7 @@ class StaticUse {
|
|||
element.isInstanceMember || element is JRecordField,
|
||||
failedAt(element,
|
||||
"Field init element $element must be an instance or boxed field."));
|
||||
return new StaticUse.internal(element, StaticUseKind.FIELD_SET);
|
||||
return new StaticUse.internal(element, StaticUseKind.INSTANCE_FIELD_SET);
|
||||
}
|
||||
|
||||
/// Read of a local function [element].
|
||||
|
@ -592,7 +596,7 @@ class StaticUse {
|
|||
/// Implicit method/constructor invocation of [element] created by the
|
||||
/// backend.
|
||||
factory StaticUse.implicitInvoke(FunctionEntity element) {
|
||||
return new StaticUse.internal(element, StaticUseKind.INVOKE,
|
||||
return new StaticUse.internal(element, StaticUseKind.STATIC_INVOKE,
|
||||
callStructure: element.parameterStructure.callStructure);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,14 +36,16 @@ abstract class EnumSet<E> {
|
|||
/// value indices.
|
||||
void set value(int mask);
|
||||
|
||||
/// Adds [enumValue] to this set.
|
||||
void add(E enumValue);
|
||||
/// Adds [enumValue] to this set. Returns `true` if the set was changed by
|
||||
/// this action.
|
||||
bool add(E enumValue);
|
||||
|
||||
/// Adds all enum values in [set] to this set.
|
||||
void addAll(EnumSet<E> set);
|
||||
|
||||
/// Removes [enumValue] from this set.
|
||||
void remove(E enumValue);
|
||||
/// Removes [enumValue] from this set. Returns `true` if the set was changed
|
||||
/// by this action.
|
||||
bool remove(E enumValue);
|
||||
|
||||
/// Removes all enum values in [set] from this set. The set of removed values
|
||||
/// is returned.
|
||||
|
@ -149,8 +151,10 @@ class _EnumSet<E> extends EnumSet<E> {
|
|||
}
|
||||
|
||||
@override
|
||||
void add(E enumValue) {
|
||||
bool add(E enumValue) {
|
||||
int before = _value;
|
||||
_value |= 1 << (enumValue as dynamic).index;
|
||||
return _value != before;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -159,8 +163,10 @@ class _EnumSet<E> extends EnumSet<E> {
|
|||
}
|
||||
|
||||
@override
|
||||
void remove(E enumValue) {
|
||||
bool remove(E enumValue) {
|
||||
int before = _value;
|
||||
_value &= ~(1 << (enumValue as dynamic).index);
|
||||
return _value != before;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -201,7 +207,7 @@ class _ConstEnumSet<E> extends EnumSet<E> {
|
|||
}
|
||||
|
||||
@override
|
||||
void add(E enumValue) {
|
||||
bool add(E enumValue) {
|
||||
throw new UnsupportedError('EnumSet.add');
|
||||
}
|
||||
|
||||
|
@ -212,16 +218,36 @@ class _ConstEnumSet<E> extends EnumSet<E> {
|
|||
|
||||
@override
|
||||
void clear() {
|
||||
throw new UnsupportedError('EnumSet.clear');
|
||||
if (isEmpty) {
|
||||
// We allow this no-op operation on an immutable set to support using a
|
||||
// constant empty set together with mutable sets where applicable.
|
||||
} else {
|
||||
throw new UnsupportedError('EnumSet.clear');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void remove(E enumValue) {
|
||||
bool remove(E enumValue) {
|
||||
if (isEmpty) {
|
||||
// We allow this no-op operation on an immutable set to support using a
|
||||
// constant empty set together with mutable sets where applicable.
|
||||
return false;
|
||||
}
|
||||
throw new UnsupportedError('EnumSet.remove');
|
||||
}
|
||||
|
||||
@override
|
||||
EnumSet<E> removeAll(EnumSet<E> set) {
|
||||
if (isEmpty) {
|
||||
// We allow this no-op operation on an immutable set to support using a
|
||||
// constant empty set together with mutable sets where applicable.
|
||||
return this;
|
||||
}
|
||||
if (set.isEmpty) {
|
||||
// We allow this no-op operation on an immutable set to support using a
|
||||
// constant empty set together with mutable sets where applicable.
|
||||
return set.clone();
|
||||
}
|
||||
throw new UnsupportedError('EnumSet.removeAll');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -198,6 +198,10 @@ abstract class JClosedWorld implements World {
|
|||
/// Returns the single [MemberEntity] that matches a call to [selector] on the
|
||||
/// [receiver]. If multiple targets exist, `null` is returned.
|
||||
MemberEntity locateSingleMember(Selector selector, AbstractValue receiver);
|
||||
|
||||
/// Returns the set of read, write, and invocation accesses found on [member]
|
||||
/// during the closed world computation.
|
||||
MemberAccess getMemberAccess(MemberEntity member);
|
||||
}
|
||||
|
||||
abstract class OpenWorld implements World {
|
||||
|
|
|
@ -11,6 +11,9 @@ var field1b;
|
|||
/*element: field1c:init,read,write*/
|
||||
var field1c;
|
||||
|
||||
/*element: field1d:init,read*/
|
||||
var field1d;
|
||||
|
||||
/*element: field2a:read*/
|
||||
get field2a => 42;
|
||||
|
||||
|
@ -27,6 +30,11 @@ get field2c => 42;
|
|||
/*element: field2c=:write*/
|
||||
set field2c(_) {}
|
||||
|
||||
/*element: field2d:read*/
|
||||
get field2d => 42;
|
||||
|
||||
set field2d(_) {}
|
||||
|
||||
class Class {
|
||||
/*element: Class.field1a:init,read*/
|
||||
var field1a;
|
||||
|
@ -37,6 +45,9 @@ class Class {
|
|||
/*element: Class.field1c:init,read,write*/
|
||||
var field1c;
|
||||
|
||||
/*element: Class.field1d:init,invoke,read=static*/
|
||||
var field1d;
|
||||
|
||||
/*element: Class.field2a:read*/
|
||||
get field2a => 42;
|
||||
|
||||
|
@ -53,6 +64,11 @@ class Class {
|
|||
/*element: Class.field2c=:write*/
|
||||
set field2c(_) {}
|
||||
|
||||
/*element: Class.field2d:invoke,read=static*/
|
||||
get field2d => null;
|
||||
|
||||
set field2d(_) {}
|
||||
|
||||
/*element: Class.field3a:init*/
|
||||
var field3a = 0;
|
||||
|
||||
|
@ -67,10 +83,12 @@ class Class {
|
|||
field1a;
|
||||
field1b = 42;
|
||||
field1c = field1c;
|
||||
field1d();
|
||||
|
||||
field2a;
|
||||
field2b = 42;
|
||||
field2c = field2c;
|
||||
field2d();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,10 +97,12 @@ main() {
|
|||
field1a;
|
||||
field1b = 42;
|
||||
field1c = field1c;
|
||||
field1d();
|
||||
|
||||
field2a;
|
||||
field2b = 42;
|
||||
field2c = field2c;
|
||||
field2d();
|
||||
|
||||
new Class().test();
|
||||
}
|
||||
|
|
|
@ -165,7 +165,7 @@ class Class1b {
|
|||
|
||||
/*element: Class2.:invoke*/
|
||||
class Class2 {
|
||||
/*element: Class2.c:init,read*/
|
||||
/*element: Class2.c:init,invoke,read=static*/
|
||||
Class1a c;
|
||||
}
|
||||
|
||||
|
|
122
tests/compiler/dart2js/member_usage/data/super.dart
Normal file
122
tests/compiler/dart2js/member_usage/data/super.dart
Normal file
|
@ -0,0 +1,122 @@
|
|||
// 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 Super {
|
||||
/*element: Super.field1:init,read=super*/
|
||||
var field1;
|
||||
|
||||
/*element: Super.field2:init,write=super*/
|
||||
var field2;
|
||||
|
||||
/*element: Super.field3:init,read=super*/
|
||||
var field3;
|
||||
|
||||
/*element: Super.field4:init,read=super*/
|
||||
final field4;
|
||||
|
||||
/*element: Super.field5:init,read=super*/
|
||||
final field5;
|
||||
|
||||
/*element: Super.constructor1:invoke*/
|
||||
Super.constructor1(this.field4, this.field5);
|
||||
|
||||
/*element: Super.constructor2:invoke=(0)*/
|
||||
Super.constructor2([this.field4, this.field5]);
|
||||
|
||||
/*element: Super.constructor3:invoke=(1)*/
|
||||
Super.constructor3([this.field4, this.field5]);
|
||||
|
||||
/*element: Super.method1:invoke=(1):super*/
|
||||
method1([a, b]) {}
|
||||
|
||||
/*element: Super.method2:invoke,read=super*/
|
||||
method2([a, b]) {}
|
||||
|
||||
/*element: Super.getter1:read=super*/
|
||||
get getter1 => null;
|
||||
|
||||
/*element: Super.getter2:read=super*/
|
||||
get getter2 => null;
|
||||
|
||||
/*element: Super.setter1=:write=super*/
|
||||
set setter1(_) {}
|
||||
|
||||
/*element: Super.call:invoke=(0,a,b,c)*/
|
||||
void call({a, b, c, d}) {}
|
||||
}
|
||||
|
||||
class Sub extends Super {
|
||||
/*element: Sub.constructor1:invoke=(1)*/
|
||||
Sub.constructor1([field4, field5]) : super.constructor1(field4, field5);
|
||||
|
||||
/*element: Sub.constructor2:invoke*/
|
||||
Sub.constructor2() : super.constructor2();
|
||||
|
||||
/*element: Sub.readSuperField:invoke*/
|
||||
readSuperField() {
|
||||
return super.field1;
|
||||
}
|
||||
|
||||
/*element: Sub.writeSuperField:invoke*/
|
||||
writeSuperField() {
|
||||
super.field2 = null;
|
||||
}
|
||||
|
||||
/*element: Sub.invokeSuperField:invoke*/
|
||||
invokeSuperField() {
|
||||
super.field3(a: 0);
|
||||
}
|
||||
|
||||
/*element: Sub.readSuperFinalField:invoke*/
|
||||
readSuperFinalField() {
|
||||
return super.field4;
|
||||
}
|
||||
|
||||
/*element: Sub.invokeSuperFinalField:invoke*/
|
||||
invokeSuperFinalField() {
|
||||
super.field5(b: 0);
|
||||
}
|
||||
|
||||
/*element: Sub.invokeSuperMethod:invoke*/
|
||||
invokeSuperMethod() {
|
||||
super.method1(0);
|
||||
}
|
||||
|
||||
/*element: Sub.readSuperMethod:invoke*/
|
||||
readSuperMethod() {
|
||||
return super.method2;
|
||||
}
|
||||
|
||||
/*element: Sub.readSuperGetter:invoke*/
|
||||
readSuperGetter() {
|
||||
return super.getter1;
|
||||
}
|
||||
|
||||
/*element: Sub.invokeSuperGetter:invoke*/
|
||||
invokeSuperGetter() {
|
||||
return super.getter2(c: 0);
|
||||
}
|
||||
|
||||
/*element: Sub.writeSuperSetter:invoke*/
|
||||
writeSuperSetter() {
|
||||
super.setter1 = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*element: main:invoke*/
|
||||
void main() {
|
||||
new Super.constructor3(null);
|
||||
new Sub.constructor1(null);
|
||||
new Sub.constructor2()
|
||||
..readSuperField()
|
||||
..writeSuperField()
|
||||
..invokeSuperField()
|
||||
..readSuperFinalField()
|
||||
..invokeSuperFinalField()
|
||||
..invokeSuperMethod()
|
||||
..readSuperMethod()
|
||||
..readSuperGetter()
|
||||
..invokeSuperGetter()
|
||||
..writeSuperSetter();
|
||||
}
|
|
@ -11,6 +11,7 @@ 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/enumset.dart';
|
||||
import 'package:compiler/src/util/features.dart';
|
||||
import 'package:kernel/ast.dart' as ir;
|
||||
import '../equivalence/id_equivalence.dart';
|
||||
|
@ -55,6 +56,31 @@ class ClosedWorldDataComputer extends DataComputer<Features> {
|
|||
Enqueuer.skipEnqueuerCheckForTesting = skipEnqueuerCheck;
|
||||
}
|
||||
|
||||
/// Compute a short textual representation of [access] on member.
|
||||
///
|
||||
/// Dynamic access on instance members and static access on non-instance
|
||||
/// members is implicit, so we only annotate super access and static access
|
||||
/// not implied by dynamic or super access.
|
||||
String computeAccessText(MemberEntity member, EnumSet<Access> access,
|
||||
[String prefix]) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
String delimiter = '';
|
||||
if (prefix != null) {
|
||||
sb.write(prefix);
|
||||
delimiter = ':';
|
||||
}
|
||||
if (access.contains(Access.superAccess)) {
|
||||
sb.write(delimiter);
|
||||
sb.write('super');
|
||||
} else if (member.isInstanceMember &&
|
||||
access.contains(Access.staticAccess) &&
|
||||
!access.contains(Access.dynamicAccess)) {
|
||||
sb.write(delimiter);
|
||||
sb.write('static');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
void computeMemberData(Compiler compiler, MemberEntity member,
|
||||
Map<Id, ActualData<Features>> actualMap,
|
||||
|
@ -71,15 +97,20 @@ class ClosedWorldDataComputer extends DataComputer<Features> {
|
|||
features.add(Tags.init);
|
||||
}
|
||||
if (memberUsage.hasRead) {
|
||||
features.add(Tags.read);
|
||||
features[Tags.read] = computeAccessText(member, memberUsage.reads);
|
||||
}
|
||||
if (memberUsage.hasWrite) {
|
||||
features.add(Tags.write);
|
||||
features[Tags.write] = computeAccessText(member, memberUsage.writes);
|
||||
}
|
||||
if (memberUsage.isFullyInvoked) {
|
||||
features.add(Tags.invoke);
|
||||
} else if (memberUsage.hasInvoke) {
|
||||
features[Tags.invoke] = memberUsage.invokedParameters.shortText;
|
||||
if (memberUsage.hasInvoke) {
|
||||
if (memberUsage is MethodUsage &&
|
||||
!memberUsage.parameterUsage.isFullyUsed) {
|
||||
features[Tags.invoke] = computeAccessText(member, memberUsage.invokes,
|
||||
memberUsage.invokedParameters.shortText);
|
||||
} else {
|
||||
features[Tags.invoke] =
|
||||
computeAccessText(member, memberUsage.invokes);
|
||||
}
|
||||
}
|
||||
}
|
||||
Id id = computeEntityId(node);
|
||||
|
|
Loading…
Reference in a new issue