Migrate pkg/vm to null safety, part 3
TEST=ci Issue: https://github.com/dart-lang/sdk/issues/46620 Change-Id: I1ab793eec6ffd7e01cee1e6641138e96a4f4d9fc Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/207864 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
f4d3f801b3
commit
ccd063bda5
@@ -2,10 +2,7 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
/// Global type flow analysis.
|
||||
library kernel.transformations.analysis;
|
||||
|
||||
import 'dart:collection';
|
||||
import 'dart:core' hide Type;
|
||||
@@ -51,24 +48,28 @@ import '../pragma.dart';
|
||||
|
||||
/// Maintains set of dependent invocations.
|
||||
class _DependencyTracker {
|
||||
Set<_Invocation> _dependentInvocations;
|
||||
Set<_Invocation>? _dependentInvocations;
|
||||
|
||||
void addDependentInvocation(_Invocation invocation) {
|
||||
if (!identical(invocation, this)) {
|
||||
_dependentInvocations ??= new Set<_Invocation>();
|
||||
_dependentInvocations.add(invocation);
|
||||
var dependentInvocations = _dependentInvocations;
|
||||
if (dependentInvocations == null) {
|
||||
_dependentInvocations = dependentInvocations = Set<_Invocation>();
|
||||
}
|
||||
dependentInvocations.add(invocation);
|
||||
}
|
||||
}
|
||||
|
||||
void invalidateDependentInvocations(_WorkList workList) {
|
||||
if (_dependentInvocations != null) {
|
||||
final dependentInvocations = _dependentInvocations;
|
||||
if (dependentInvocations != null) {
|
||||
if (kPrintTrace) {
|
||||
tracePrint(' - CHANGED: $this');
|
||||
for (var di in _dependentInvocations) {
|
||||
for (var di in dependentInvocations) {
|
||||
tracePrint(' - invalidating $di');
|
||||
}
|
||||
}
|
||||
_dependentInvocations.forEach(workList.invalidateInvocation);
|
||||
dependentInvocations.forEach(workList.invalidateInvocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,12 +84,12 @@ abstract class _Invocation extends _DependencyTracker
|
||||
final Selector selector;
|
||||
final Args<Type> args;
|
||||
|
||||
Type result;
|
||||
Type? result;
|
||||
|
||||
/// Result of the invocation calculated before invocation was invalidated.
|
||||
/// Used to check if the re-analysis of the invocation yields the same
|
||||
/// result or not (to avoid invalidation of callers if result hasn't changed).
|
||||
Type invalidatedResult;
|
||||
Type? invalidatedResult;
|
||||
|
||||
/// Number of times result of this invocation was invalidated.
|
||||
int invalidationCounter = 0;
|
||||
@@ -108,7 +109,7 @@ abstract class _Invocation extends _DependencyTracker
|
||||
/// Returns result of this invocation if its available without
|
||||
/// further analysis, or `null` if it's not available.
|
||||
/// Used for recursive calls while this invocation is being processed.
|
||||
Type get resultForRecursiveInvocation => result;
|
||||
Type? get resultForRecursiveInvocation => result;
|
||||
|
||||
/// Use [type] as a current computed result of this invocation.
|
||||
/// If this invocation was invalidated, and the invalidated result is
|
||||
@@ -116,7 +117,6 @@ abstract class _Invocation extends _DependencyTracker
|
||||
/// Result type may be saturated if this invocation was invalidated
|
||||
/// too many times.
|
||||
void setResult(TypeFlowAnalysis typeFlowAnalysis, Type type) {
|
||||
assert(type != null);
|
||||
result = type;
|
||||
|
||||
if (invalidatedResult != null) {
|
||||
@@ -132,8 +132,8 @@ abstract class _Invocation extends _DependencyTracker
|
||||
// the analysis, result is saturated after invocation is invalidated
|
||||
// at least [_Invocation.invalidationLimit] times.
|
||||
if (invalidationCounter > _Invocation.invalidationLimit) {
|
||||
result =
|
||||
result.union(invalidatedResult, typeFlowAnalysis.hierarchyCache);
|
||||
result = result!
|
||||
.union(invalidatedResult!, typeFlowAnalysis.hierarchyCache);
|
||||
}
|
||||
}
|
||||
invalidatedResult = null;
|
||||
@@ -192,11 +192,12 @@ class _DirectInvocation extends _Invocation {
|
||||
// they could fail bounds checks.
|
||||
//
|
||||
// TODO(sjindel): Use [TypeCheck] to avoid bounds checks.
|
||||
if (selector.member.function != null) {
|
||||
typeChecksNeeded = selector.member.function.typeParameters
|
||||
.any((t) => t.isGenericCovariantImpl);
|
||||
final function = selector.member.function;
|
||||
if (function != null) {
|
||||
typeChecksNeeded =
|
||||
function.typeParameters.any((t) => t.isGenericCovariantImpl);
|
||||
} else {
|
||||
Field field = selector.member;
|
||||
Field field = selector.member as Field;
|
||||
if (selector.callKind == CallKind.PropertySet) {
|
||||
// TODO(dartbug.com/40615): Use TFA results to improve this criterion.
|
||||
typeChecksNeeded = field.isGenericCovariantImpl;
|
||||
@@ -277,13 +278,10 @@ class _DirectInvocation extends _Invocation {
|
||||
fieldValue.isInitialized = true;
|
||||
return const EmptyType();
|
||||
}
|
||||
|
||||
// Make dartanalyzer happy.
|
||||
throw 'Unexpected call kind ${selector.callKind}';
|
||||
}
|
||||
|
||||
Type _processFunction(TypeFlowAnalysis typeFlowAnalysis) {
|
||||
final Member member = selector.member;
|
||||
final Member member = selector.member!;
|
||||
if (selector.memberAgreesToCallKind(member)) {
|
||||
if (_argumentsValid()) {
|
||||
final summary = typeFlowAnalysis.getSummary(member);
|
||||
@@ -326,13 +324,12 @@ class _DirectInvocation extends _Invocation {
|
||||
}
|
||||
|
||||
bool _argumentsValid() {
|
||||
final function = selector.member.function;
|
||||
assert(function != null);
|
||||
|
||||
final member = selector.member!;
|
||||
final function = member.function!;
|
||||
final int positionalArguments = args.positionalCount;
|
||||
|
||||
final int firstParamIndex = numTypeParams(selector.member) +
|
||||
(hasReceiverArg(selector.member) ? 1 : 0);
|
||||
final int firstParamIndex =
|
||||
numTypeParams(member) + (hasReceiverArg(member) ? 1 : 0);
|
||||
final int requiredParameters =
|
||||
firstParamIndex + function.requiredParameterCount;
|
||||
if (positionalArguments < requiredParameters) {
|
||||
@@ -361,9 +358,9 @@ class _DirectInvocation extends _Invocation {
|
||||
|
||||
class _DispatchableInvocation extends _Invocation {
|
||||
bool _isPolymorphic = false;
|
||||
Set<Call> _callSites; // Populated only if not polymorphic.
|
||||
Member _monomorphicTarget;
|
||||
_DirectInvocation _monomorphicDirectInvocation;
|
||||
Set<Call>? _callSites; // Populated only if not polymorphic.
|
||||
Member? _monomorphicTarget;
|
||||
_DirectInvocation? _monomorphicDirectInvocation;
|
||||
|
||||
@override
|
||||
set typeChecksNeeded(bool value) {
|
||||
@@ -434,7 +431,8 @@ class _DispatchableInvocation extends _Invocation {
|
||||
|
||||
if (!_isPolymorphic) {
|
||||
assert(target == _monomorphicTarget);
|
||||
_monomorphicDirectInvocation = directInvocation;
|
||||
_monomorphicDirectInvocation =
|
||||
directInvocation as _DirectInvocation;
|
||||
}
|
||||
|
||||
type = typeFlowAnalysis.workList.processInvocation(directInvocation);
|
||||
@@ -486,13 +484,13 @@ class _DispatchableInvocation extends _Invocation {
|
||||
|
||||
final bool isNullableReceiver = receiver is NullableType;
|
||||
if (isNullableReceiver) {
|
||||
receiver = (receiver as NullableType).baseType;
|
||||
receiver = receiver.baseType;
|
||||
assert(receiver is! NullableType);
|
||||
}
|
||||
|
||||
if (selector is InterfaceSelector) {
|
||||
final staticReceiverType = new ConeType(typeFlowAnalysis.hierarchyCache
|
||||
.getTFClass(selector.member.enclosingClass));
|
||||
.getTFClass(selector.member!.enclosingClass!));
|
||||
receiver = receiver.intersection(
|
||||
staticReceiverType, typeFlowAnalysis.hierarchyCache);
|
||||
assert(receiver is! NullableType);
|
||||
@@ -507,7 +505,7 @@ class _DispatchableInvocation extends _Invocation {
|
||||
// invocation to the receiver class. A new allocated class discovered
|
||||
// in the receiver cone will invalidate this invocation.
|
||||
receiver = typeFlowAnalysis.hierarchyCache
|
||||
.specializeTypeCone((receiver as ConeType).cls, allowWideCone: false);
|
||||
.specializeTypeCone(receiver.cls, allowWideCone: false);
|
||||
}
|
||||
|
||||
assert(targets.isEmpty);
|
||||
@@ -535,7 +533,7 @@ class _DispatchableInvocation extends _Invocation {
|
||||
Class nullClass =
|
||||
typeFlowAnalysis.environment.coreTypes.deprecatedNullClass;
|
||||
|
||||
Member target = typeFlowAnalysis.hierarchyCache.hierarchy
|
||||
Member? target = typeFlowAnalysis.hierarchyCache.hierarchy
|
||||
.getDispatchTarget(nullClass, selector.name, setter: selector.isSetter);
|
||||
|
||||
if (target != null) {
|
||||
@@ -552,7 +550,7 @@ class _DispatchableInvocation extends _Invocation {
|
||||
TypeFlowAnalysis typeFlowAnalysis) {
|
||||
final TFClass cls = receiver.cls;
|
||||
|
||||
Member target =
|
||||
Member? target =
|
||||
(cls as _TFClassImpl).getDispatchTarget(selector, typeFlowAnalysis);
|
||||
|
||||
if (target != null) {
|
||||
@@ -647,8 +645,9 @@ class _DispatchableInvocation extends _Invocation {
|
||||
if (_isPolymorphic) {
|
||||
callSite.setPolymorphic();
|
||||
} else {
|
||||
if (_monomorphicTarget != null) {
|
||||
callSite.addTarget(_monomorphicTarget);
|
||||
final monomorphicTarget = _monomorphicTarget;
|
||||
if (monomorphicTarget != null) {
|
||||
callSite.addTarget(monomorphicTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,18 +659,20 @@ class _DispatchableInvocation extends _Invocation {
|
||||
/// Notify call sites monitoring this invocation about changes in
|
||||
/// polymorphism of this invocation.
|
||||
void _notifyCallSites() {
|
||||
if (_callSites != null) {
|
||||
_callSites.forEach(_notifyCallSite);
|
||||
final callSites = _callSites;
|
||||
if (callSites != null) {
|
||||
callSites.forEach(_notifyCallSite);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Type get resultForRecursiveInvocation {
|
||||
Type? get resultForRecursiveInvocation {
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
if (_monomorphicDirectInvocation != null) {
|
||||
return _monomorphicDirectInvocation.resultForRecursiveInvocation;
|
||||
final monomorphicDirectInvocation = _monomorphicDirectInvocation;
|
||||
if (monomorphicDirectInvocation != null) {
|
||||
return monomorphicDirectInvocation.resultForRecursiveInvocation;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -683,30 +684,30 @@ class _DispatchableInvocation extends _Invocation {
|
||||
/// 1) Add 1..N concrete types ordered by classId OR add 1 arbitrary type.
|
||||
/// 2) Make type nullable.
|
||||
class _ReceiverTypeBuilder {
|
||||
Type _type;
|
||||
List<ConcreteType> _list;
|
||||
Type? _type;
|
||||
List<ConcreteType>? _list;
|
||||
bool _nullable = false;
|
||||
|
||||
/// Appends a ConcreteType. May be called multiple times.
|
||||
/// Should not be used in conjunction with [addType].
|
||||
void addConcreteType(ConcreteType type) {
|
||||
if (_list == null) {
|
||||
if (_type == null) {
|
||||
final list = _list;
|
||||
if (list == null) {
|
||||
final Type? t = _type;
|
||||
if (t == null) {
|
||||
_type = type;
|
||||
return;
|
||||
}
|
||||
final ct = t as ConcreteType;
|
||||
|
||||
assert(_type is ConcreteType);
|
||||
assert(_type != type);
|
||||
|
||||
_list = <ConcreteType>[];
|
||||
_list.add(_type);
|
||||
|
||||
assert(ct != type);
|
||||
assert(ct.cls.id < type.cls.id);
|
||||
_list = <ConcreteType>[ct, type];
|
||||
_type = null;
|
||||
} else {
|
||||
assert(list.last.cls.id < type.cls.id);
|
||||
list.add(type);
|
||||
}
|
||||
|
||||
assert(_list.last.cls.id < type.cls.id);
|
||||
_list.add(type);
|
||||
}
|
||||
|
||||
/// Appends an arbitrary Type. May be called only once.
|
||||
@@ -723,12 +724,13 @@ class _ReceiverTypeBuilder {
|
||||
|
||||
/// Returns union of added types.
|
||||
Type toType() {
|
||||
Type t = _type;
|
||||
Type? t = _type;
|
||||
if (t == null) {
|
||||
if (_list == null) {
|
||||
final list = _list;
|
||||
if (list == null) {
|
||||
t = const EmptyType();
|
||||
} else {
|
||||
t = new SetType(_list);
|
||||
t = SetType(list);
|
||||
}
|
||||
} else {
|
||||
assert(_list == null);
|
||||
@@ -753,7 +755,7 @@ class _SelectorApproximation {
|
||||
static const int maxInvocationsPerSelector = 5000;
|
||||
|
||||
int count = 0;
|
||||
_Invocation approximation;
|
||||
_Invocation? approximation;
|
||||
}
|
||||
|
||||
/// Maintains ([Selector], [Args]) => [_Invocation] cache.
|
||||
@@ -771,7 +773,7 @@ class _InvocationsCache {
|
||||
_Invocation invocation = (selector is DirectSelector)
|
||||
? new _DirectInvocation(selector, args)
|
||||
: new _DispatchableInvocation(selector, args);
|
||||
_Invocation result = _invocations.lookup(invocation);
|
||||
_Invocation? result = _invocations.lookup(invocation);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
@@ -784,14 +786,16 @@ class _InvocationsCache {
|
||||
final sa = (_approximations[selector] ??= new _SelectorApproximation());
|
||||
|
||||
if (sa.count >= _SelectorApproximation.maxInvocationsPerSelector) {
|
||||
if (sa.approximation == null) {
|
||||
_Invocation? approximation = sa.approximation;
|
||||
if (approximation == null) {
|
||||
final rawArgs =
|
||||
_typeFlowAnalysis.summaryCollector.rawArguments(selector);
|
||||
sa.approximation = new _DispatchableInvocation(selector, rawArgs);
|
||||
sa.approximation =
|
||||
approximation = _DispatchableInvocation(selector, rawArgs);
|
||||
Statistics.approximateInvocationsCreated++;
|
||||
}
|
||||
Statistics.approximateInvocationsUsed++;
|
||||
return sa.approximation;
|
||||
return approximation;
|
||||
}
|
||||
|
||||
++sa.count;
|
||||
@@ -809,8 +813,8 @@ class _InvocationsCache {
|
||||
class _FieldValue extends _DependencyTracker {
|
||||
final Field field;
|
||||
final Type staticType;
|
||||
final Summary typeGuardSummary;
|
||||
Type value;
|
||||
final Summary? typeGuardSummary;
|
||||
Type value = const EmptyType();
|
||||
|
||||
/// Flag indicating if field initializer was executed.
|
||||
bool isInitialized = false;
|
||||
@@ -825,8 +829,6 @@ class _FieldValue extends _DependencyTracker {
|
||||
: staticType = typesBuilder.fromStaticType(field.type, true) {
|
||||
if (field.initializer == null && _isDefaultValueOfFieldObservable()) {
|
||||
value = new Type.nullable(const EmptyType());
|
||||
} else {
|
||||
value = const EmptyType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -839,8 +841,7 @@ class _FieldValue extends _DependencyTracker {
|
||||
return true;
|
||||
}
|
||||
|
||||
final enclosingClass = field.enclosingClass;
|
||||
assert(enclosingClass != null);
|
||||
final enclosingClass = field.enclosingClass!;
|
||||
|
||||
// Default value is not observable if every generative constructor
|
||||
// is redirecting or initializes the field.
|
||||
@@ -856,10 +857,11 @@ class _FieldValue extends _DependencyTracker {
|
||||
});
|
||||
}
|
||||
|
||||
void ensureInitialized(TypeFlowAnalysis typeFlowAnalysis, Type receiverType) {
|
||||
void ensureInitialized(
|
||||
TypeFlowAnalysis typeFlowAnalysis, Type? receiverType) {
|
||||
if (field.initializer != null) {
|
||||
assert(field.isStatic == (receiverType == null));
|
||||
final args = !field.isStatic ? <Type>[receiverType] : const <Type>[];
|
||||
final args = !field.isStatic ? <Type>[receiverType!] : const <Type>[];
|
||||
final initializerInvocation = typeFlowAnalysis._invocationsCache
|
||||
.getInvocation(
|
||||
new DirectSelector(field, callKind: CallKind.FieldInitializer),
|
||||
@@ -870,17 +872,18 @@ class _FieldValue extends _DependencyTracker {
|
||||
}
|
||||
}
|
||||
|
||||
Type getValue(TypeFlowAnalysis typeFlowAnalysis, Type receiverType) {
|
||||
Type getValue(TypeFlowAnalysis typeFlowAnalysis, Type? receiverType) {
|
||||
ensureInitialized(typeFlowAnalysis, receiverType);
|
||||
addDependentInvocation(typeFlowAnalysis.currentInvocation);
|
||||
final typeGuardSummary = this.typeGuardSummary;
|
||||
return (typeGuardSummary != null)
|
||||
? typeGuardSummary.apply(Args([receiverType, value]),
|
||||
? typeGuardSummary.apply(Args([receiverType!, value]),
|
||||
typeFlowAnalysis.hierarchyCache, typeFlowAnalysis)
|
||||
: value;
|
||||
}
|
||||
|
||||
void setValue(
|
||||
Type newValue, TypeFlowAnalysis typeFlowAnalysis, Type receiverType) {
|
||||
Type newValue, TypeFlowAnalysis typeFlowAnalysis, Type? receiverType) {
|
||||
// Make sure type cones are specialized before putting them into field
|
||||
// value, in order to ensure that dependency is established between
|
||||
// cone's base type and corresponding field setter.
|
||||
@@ -907,10 +910,11 @@ class _FieldValue extends _DependencyTracker {
|
||||
//
|
||||
final hierarchy = typeFlowAnalysis.hierarchyCache;
|
||||
// TODO(sjindel/tfa): Perform narrowing inside 'TypeCheck'.
|
||||
final typeGuardSummary = this.typeGuardSummary;
|
||||
final narrowedNewValue = typeGuardSummary != null
|
||||
? typeGuardSummary
|
||||
.apply(
|
||||
new Args([receiverType, newValue]), hierarchy, typeFlowAnalysis)
|
||||
.apply(new Args([receiverType!, newValue]), hierarchy,
|
||||
typeFlowAnalysis)
|
||||
.intersection(staticType, hierarchy)
|
||||
: newValue.specialize(hierarchy).intersection(staticType, hierarchy);
|
||||
Type newType =
|
||||
@@ -952,28 +956,26 @@ class _TFClassImpl extends TFClass {
|
||||
/// Flag indicating if this class has a noSuchMethod() method not inherited
|
||||
/// from Object.
|
||||
/// Lazy initialized by ClassHierarchyCache.hasNonTrivialNoSuchMethod().
|
||||
bool hasNonTrivialNoSuchMethod;
|
||||
bool? hasNonTrivialNoSuchMethod;
|
||||
|
||||
_TFClassImpl(int id, Class classNode, this.supertypes)
|
||||
: super(id, classNode) {
|
||||
supertypes.add(this);
|
||||
}
|
||||
|
||||
ConcreteType _concreteType;
|
||||
ConcreteType get concreteType =>
|
||||
_concreteType ??= new ConcreteType(this, null);
|
||||
late final ConcreteType concreteType = ConcreteType(this, null);
|
||||
|
||||
Type _specializedConeType;
|
||||
Type? _specializedConeType;
|
||||
Type get specializedConeType =>
|
||||
_specializedConeType ??= _calculateConeTypeSpecialization();
|
||||
|
||||
bool get hasWideCone =>
|
||||
_allocatedSubtypes.length > maxAllocatedTypesInSetSpecializations;
|
||||
|
||||
WideConeType _wideConeType;
|
||||
late final WideConeType _wideConeType = WideConeType(this);
|
||||
WideConeType get wideConeType {
|
||||
assert(hasWideCone);
|
||||
return _wideConeType ??= new WideConeType(this);
|
||||
return _wideConeType;
|
||||
}
|
||||
|
||||
Type _calculateConeTypeSpecialization() {
|
||||
@@ -999,14 +1001,16 @@ class _TFClassImpl extends TFClass {
|
||||
_specializedConeType = null; // Reset cached specialization.
|
||||
}
|
||||
|
||||
Member getDispatchTarget(
|
||||
Member? getDispatchTarget(
|
||||
Selector selector, TypeFlowAnalysis typeFlowAnalysis) {
|
||||
Member target = _dispatchTargets[selector];
|
||||
Member? target = _dispatchTargets[selector];
|
||||
if (target == null) {
|
||||
target = typeFlowAnalysis.hierarchyCache.hierarchy.getDispatchTarget(
|
||||
classNode, selector.name,
|
||||
setter: selector.isSetter);
|
||||
_dispatchTargets[selector] = target;
|
||||
if (target != null) {
|
||||
_dispatchTargets[selector] = target;
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
@@ -1021,10 +1025,11 @@ class GenericInterfacesInfoImpl implements GenericInterfacesInfo {
|
||||
final cachedFlattenedTypeArgs = <Class, List<DartType>>{};
|
||||
final cachedFlattenedTypeArgsForNonGeneric = <Class, List<Type>>{};
|
||||
|
||||
RuntimeTypeTranslatorImpl closedTypeTranslator;
|
||||
late final RuntimeTypeTranslatorImpl closedTypeTranslator;
|
||||
|
||||
GenericInterfacesInfoImpl(this.hierarchy) {
|
||||
closedTypeTranslator = RuntimeTypeTranslatorImpl.forClosedTypes(this);
|
||||
GenericInterfacesInfoImpl(CoreTypes coreTypes, this.hierarchy) {
|
||||
closedTypeTranslator =
|
||||
RuntimeTypeTranslatorImpl.forClosedTypes(coreTypes, this);
|
||||
}
|
||||
|
||||
List<DartType> flattenedTypeArgumentsFor(Class klass, {bool useCache: true}) {
|
||||
@@ -1049,7 +1054,7 @@ class GenericInterfacesInfoImpl implements GenericInterfacesInfo {
|
||||
if (klass == iface) return 0;
|
||||
|
||||
final pair = new SubtypePair(klass, iface);
|
||||
int offset = supertypeOffsetsCache[pair];
|
||||
int? offset = supertypeOffsetsCache[pair];
|
||||
|
||||
if (offset != null) return offset;
|
||||
|
||||
@@ -1064,16 +1069,16 @@ class GenericInterfacesInfoImpl implements GenericInterfacesInfo {
|
||||
}
|
||||
|
||||
List<Type> flattenedTypeArgumentsForNonGeneric(Class klass) {
|
||||
List<Type> result = cachedFlattenedTypeArgsForNonGeneric[klass];
|
||||
List<Type>? result = cachedFlattenedTypeArgsForNonGeneric[klass];
|
||||
if (result != null) return result;
|
||||
|
||||
List<DartType> flattenedTypeArgs =
|
||||
flattenedTypeArgumentsFor(klass, useCache: false);
|
||||
result = new List<Type>.filled(flattenedTypeArgs.length, null);
|
||||
for (int i = 0; i < flattenedTypeArgs.length; ++i) {
|
||||
final translated = closedTypeTranslator.translate(flattenedTypeArgs[i]);
|
||||
result = <Type>[];
|
||||
for (DartType arg in flattenedTypeArgs) {
|
||||
final translated = closedTypeTranslator.translate(arg);
|
||||
assert(translated is RuntimeType || translated is UnknownType);
|
||||
result[i] = translated;
|
||||
result.add(translated as Type);
|
||||
}
|
||||
cachedFlattenedTypeArgsForNonGeneric[klass] = result;
|
||||
return result;
|
||||
@@ -1108,10 +1113,8 @@ class _ClassHierarchyCache extends TypeHierarchy {
|
||||
_ClassHierarchyCache(this._typeFlowAnalysis, this.hierarchy,
|
||||
this.genericInterfacesInfo, this.environment, bool nullSafety)
|
||||
: objectNoSuchMethod = hierarchy.getDispatchTarget(
|
||||
environment.coreTypes.objectClass, noSuchMethodName),
|
||||
super(environment.coreTypes, nullSafety) {
|
||||
assert(objectNoSuchMethod != null);
|
||||
}
|
||||
environment.coreTypes.objectClass, noSuchMethodName)!,
|
||||
super(environment.coreTypes, nullSafety);
|
||||
|
||||
@override
|
||||
_TFClassImpl getTFClass(Class c) {
|
||||
@@ -1198,10 +1201,13 @@ class _ClassHierarchyCache extends TypeHierarchy {
|
||||
|
||||
bool hasNonTrivialNoSuchMethod(TFClass c) {
|
||||
final classImpl = c as _TFClassImpl;
|
||||
classImpl.hasNonTrivialNoSuchMethod ??=
|
||||
(hierarchy.getDispatchTarget(c.classNode, noSuchMethodName) !=
|
||||
objectNoSuchMethod);
|
||||
return classImpl.hasNonTrivialNoSuchMethod;
|
||||
bool? value = classImpl.hasNonTrivialNoSuchMethod;
|
||||
if (value == null) {
|
||||
classImpl.hasNonTrivialNoSuchMethod = value =
|
||||
(hierarchy.getDispatchTarget(c.classNode, noSuchMethodName) !=
|
||||
objectNoSuchMethod);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
_DynamicTargetSet getDynamicTargetSet(DynamicSelector selector) {
|
||||
@@ -1294,10 +1300,11 @@ class _WorkList {
|
||||
}
|
||||
|
||||
bool invalidateProtobufFields() {
|
||||
if (_typeFlowAnalysis.protobufHandler == null) {
|
||||
final protobufHandler = _typeFlowAnalysis.protobufHandler;
|
||||
if (protobufHandler == null) {
|
||||
return false;
|
||||
}
|
||||
final fields = _typeFlowAnalysis.protobufHandler.getInvalidatedFields();
|
||||
final fields = protobufHandler.getInvalidatedFields();
|
||||
if (fields.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
@@ -1305,7 +1312,7 @@ class _WorkList {
|
||||
for (var field in fields) {
|
||||
assert(field.isStatic);
|
||||
// Reset summary in order to rebuild it.
|
||||
_typeFlowAnalysis._summaries[field] = null;
|
||||
_typeFlowAnalysis._summaries.remove(field);
|
||||
// Invalidate (and enqueue) field initializer invocation.
|
||||
final initializerInvocation = _typeFlowAnalysis._invocationsCache
|
||||
.getInvocation(
|
||||
@@ -1328,10 +1335,11 @@ class _WorkList {
|
||||
}
|
||||
|
||||
Type processInvocation(_Invocation invocation) {
|
||||
if (invocation.result != null) {
|
||||
Type? result = invocation.result;
|
||||
if (result != null) {
|
||||
// Already processed.
|
||||
Statistics.usedCachedResultsOfInvocations++;
|
||||
return invocation.result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Test if tracing is enabled to avoid expensive message formatting.
|
||||
@@ -1374,18 +1382,18 @@ class _WorkList {
|
||||
-1);
|
||||
}
|
||||
processing.remove(invocation);
|
||||
return invocation.invalidatedResult;
|
||||
return invocation.invalidatedResult!;
|
||||
}
|
||||
|
||||
callStack.add(invocation);
|
||||
pending.remove(invocation);
|
||||
|
||||
Type result = invocation.process(_typeFlowAnalysis);
|
||||
result = invocation.process(_typeFlowAnalysis);
|
||||
|
||||
invocation.setResult(_typeFlowAnalysis, result);
|
||||
|
||||
// setResult may saturate result to ensure convergence.
|
||||
result = invocation.result;
|
||||
result = invocation.result!;
|
||||
|
||||
// Invocation is still pending - it was invalidated while being processed.
|
||||
// Move result to invalidatedResult.
|
||||
@@ -1434,12 +1442,12 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
||||
final TypeEnvironment environment;
|
||||
final LibraryIndex libraryIndex;
|
||||
final PragmaAnnotationParser annotationMatcher;
|
||||
final ProtobufHandler protobufHandler;
|
||||
NativeCodeOracle nativeCodeOracle;
|
||||
_ClassHierarchyCache hierarchyCache;
|
||||
SummaryCollector summaryCollector;
|
||||
_InvocationsCache _invocationsCache;
|
||||
_WorkList workList;
|
||||
final ProtobufHandler? protobufHandler;
|
||||
late NativeCodeOracle nativeCodeOracle;
|
||||
late _ClassHierarchyCache hierarchyCache;
|
||||
late SummaryCollector summaryCollector;
|
||||
late _InvocationsCache _invocationsCache;
|
||||
late _WorkList workList;
|
||||
GenericInterfacesInfo _genericInterfacesInfo;
|
||||
|
||||
final Map<Member, Summary> _summaries = <Member, Summary>{};
|
||||
@@ -1459,7 +1467,7 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
||||
this.environment,
|
||||
this.libraryIndex,
|
||||
this.protobufHandler,
|
||||
PragmaAnnotationParser matcher)
|
||||
PragmaAnnotationParser? matcher)
|
||||
: annotationMatcher =
|
||||
matcher ?? new ConstantPragmaAnnotationParser(coreTypes) {
|
||||
nativeCodeOracle = new NativeCodeOracle(libraryIndex, annotationMatcher);
|
||||
@@ -1488,9 +1496,9 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
||||
}
|
||||
|
||||
_FieldValue getFieldValue(Field field) {
|
||||
_FieldValue fieldValue = _fieldValues[field];
|
||||
_FieldValue? fieldValue = _fieldValues[field];
|
||||
if (fieldValue == null) {
|
||||
Summary typeGuardSummary = null;
|
||||
Summary? typeGuardSummary = null;
|
||||
if (field.isGenericCovariantImpl) {
|
||||
typeGuardSummary = summaryCollector.createSummary(field,
|
||||
fieldSummaryType: FieldSummaryType.kFieldGuard);
|
||||
@@ -1548,24 +1556,24 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
||||
|
||||
bool isClassAllocated(Class c) => hierarchyCache.allocatedClasses.contains(c);
|
||||
|
||||
Call callSite(TreeNode node) => summaryCollector.callSites[node];
|
||||
Call? callSite(TreeNode node) => summaryCollector.callSites[node];
|
||||
|
||||
TypeCheck explicitCast(AsExpression cast) =>
|
||||
TypeCheck? explicitCast(AsExpression cast) =>
|
||||
summaryCollector.explicitCasts[cast];
|
||||
|
||||
TypeCheck isTest(IsExpression node) => summaryCollector.isTests[node];
|
||||
TypeCheck? isTest(IsExpression node) => summaryCollector.isTests[node];
|
||||
|
||||
NarrowNotNull nullTest(TreeNode node) => summaryCollector.nullTests[node];
|
||||
NarrowNotNull? nullTest(TreeNode node) => summaryCollector.nullTests[node];
|
||||
|
||||
Type fieldType(Field field) => _fieldValues[field]?.value;
|
||||
Type? fieldType(Field field) => _fieldValues[field]?.value;
|
||||
|
||||
Args<Type> argumentTypes(Member member) => _summaries[member]?.argumentTypes;
|
||||
Args<Type>? argumentTypes(Member member) => _summaries[member]?.argumentTypes;
|
||||
|
||||
Type argumentType(Member member, VariableDeclaration memberParam) {
|
||||
Type? argumentType(Member member, VariableDeclaration memberParam) {
|
||||
return _summaries[member]?.argumentType(member, memberParam);
|
||||
}
|
||||
|
||||
List<VariableDeclaration> uncheckedParameters(Member member) =>
|
||||
List<VariableDeclaration>? uncheckedParameters(Member member) =>
|
||||
_summaries[member]?.uncheckedParameters;
|
||||
|
||||
bool isTearOffTaken(Member member) => _tearOffTaken.contains(member);
|
||||
@@ -1599,7 +1607,7 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
||||
/// ---- Implementation of [CallHandler] interface. ----
|
||||
|
||||
@override
|
||||
Type applyCall(Call callSite, Selector selector, Args<Type> args,
|
||||
Type applyCall(Call? callSite, Selector selector, Args<Type> args,
|
||||
{bool isResultUsed: true, bool processImmediately: true}) {
|
||||
_Invocation invocation = _invocationsCache.getInvocation(selector, args);
|
||||
|
||||
@@ -1629,7 +1637,7 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
||||
workList.enqueueInvocation(invocation);
|
||||
}
|
||||
|
||||
return null;
|
||||
return const EmptyType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1680,7 +1688,7 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
||||
}
|
||||
|
||||
@override
|
||||
void recordTearOff(Procedure target) {
|
||||
void recordTearOff(Member target) {
|
||||
_tearOffTaken.add(target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
/// Declares classes which describe a call: selectors and arguments.
|
||||
library vm.transformations.type_flow.calls;
|
||||
|
||||
import 'dart:core' hide Type;
|
||||
|
||||
@@ -30,10 +27,10 @@ abstract class Selector {
|
||||
Selector(this.callKind);
|
||||
|
||||
/// Interface or concrete target, may be null.
|
||||
Member get member;
|
||||
Member? get member;
|
||||
|
||||
/// Selector name.
|
||||
Name get name => member.name;
|
||||
Name get name => member!.name;
|
||||
|
||||
bool get isSetter => (callKind == CallKind.PropertySet);
|
||||
|
||||
@@ -46,6 +43,7 @@ abstract class Selector {
|
||||
|
||||
/// Static approximation of Dart return type.
|
||||
DartType get staticReturnType {
|
||||
final member = this.member;
|
||||
if (member == null) {
|
||||
return const DynamicType();
|
||||
}
|
||||
@@ -61,7 +59,6 @@ abstract class Selector {
|
||||
case CallKind.SetFieldInConstructor:
|
||||
return const NeverType.nonNullable();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool memberAgreesToCallKind(Member member) {
|
||||
@@ -79,7 +76,6 @@ abstract class Selector {
|
||||
case CallKind.SetFieldInConstructor:
|
||||
return member is Field;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String get _callKindPrefix {
|
||||
@@ -94,7 +90,6 @@ abstract class Selector {
|
||||
case CallKind.FieldInitializer:
|
||||
return 'init ';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +164,7 @@ class DynamicSelector extends Selector {
|
||||
DynamicSelector(CallKind callKind, this.name) : super(callKind);
|
||||
|
||||
@override
|
||||
Member get member => null;
|
||||
Member? get member => null;
|
||||
|
||||
@override
|
||||
int get hashCode => (super.hashCode ^ name.hashCode + 37) & kHashMask;
|
||||
@@ -189,7 +184,8 @@ class Args<T extends TypeExpr> {
|
||||
final List<T> values;
|
||||
final List<String> names;
|
||||
|
||||
int _hashCode;
|
||||
@override
|
||||
late final int hashCode = _computeHashCode();
|
||||
|
||||
Args(this.values, {this.names = const <String>[]}) {
|
||||
assert(isSorted(names));
|
||||
@@ -206,9 +202,6 @@ class Args<T extends TypeExpr> {
|
||||
|
||||
T get receiver => values[0];
|
||||
|
||||
@override
|
||||
int get hashCode => _hashCode ??= _computeHashCode();
|
||||
|
||||
int _computeHashCode() {
|
||||
int hash = 1231;
|
||||
for (var v in values) {
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
/// Handling of native code and entry points.
|
||||
library vm.transformations.type_flow.native_code;
|
||||
|
||||
import 'dart:core' hide Type;
|
||||
|
||||
@@ -35,24 +32,21 @@ abstract class EntryPointsListener {
|
||||
/// Record the fact that given member is called from this.
|
||||
void recordMemberCalledViaThis(Member target);
|
||||
|
||||
/// Record the fact that given method is torn off.
|
||||
void recordTearOff(Procedure target) {}
|
||||
/// Record the fact that given member is torn off.
|
||||
void recordTearOff(Member target) {}
|
||||
}
|
||||
|
||||
class PragmaEntryPointsVisitor extends RecursiveVisitor {
|
||||
final EntryPointsListener entryPoints;
|
||||
final NativeCodeOracle nativeCodeOracle;
|
||||
final PragmaAnnotationParser matcher;
|
||||
Class currentClass = null;
|
||||
|
||||
PragmaEntryPointsVisitor(
|
||||
this.entryPoints, this.nativeCodeOracle, this.matcher) {
|
||||
assert(matcher != null);
|
||||
}
|
||||
this.entryPoints, this.nativeCodeOracle, this.matcher);
|
||||
|
||||
PragmaEntryPointType _annotationsDefineRoot(List<Expression> annotations) {
|
||||
PragmaEntryPointType? _annotationsDefineRoot(List<Expression> annotations) {
|
||||
for (var annotation in annotations) {
|
||||
ParsedPragma pragma = matcher.parsePragma(annotation);
|
||||
ParsedPragma? pragma = matcher.parsePragma(annotation);
|
||||
if (pragma == null) continue;
|
||||
if (pragma is ParsedEntryPointPragma) return pragma.type;
|
||||
}
|
||||
@@ -72,7 +66,6 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
|
||||
}
|
||||
nativeCodeOracle.addClassReferencedFromNativeCode(klass);
|
||||
}
|
||||
currentClass = klass;
|
||||
klass.visitChildren(this);
|
||||
}
|
||||
|
||||
@@ -88,8 +81,7 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
|
||||
}
|
||||
Member target = proc;
|
||||
while (target is Procedure && target.isRedirectingFactory) {
|
||||
target = getRedirectingFactoryBody(target).target;
|
||||
assert(target != null);
|
||||
target = getRedirectingFactoryBody(target)!.target!;
|
||||
assert(
|
||||
(target is Procedure && target.isFactory) || target is Constructor);
|
||||
}
|
||||
@@ -152,7 +144,7 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
|
||||
}
|
||||
entryPoints
|
||||
.addRawCall(new DirectSelector(ctor, callKind: CallKind.Method));
|
||||
entryPoints.addAllocatedClass(currentClass);
|
||||
entryPoints.addAllocatedClass(ctor.enclosingClass);
|
||||
nativeCodeOracle.setMemberReferencedFromNativeCode(ctor);
|
||||
}
|
||||
}
|
||||
@@ -201,9 +193,7 @@ class NativeCodeOracle {
|
||||
final Set<Class> _classesReferencedFromNativeCode = new Set<Class>();
|
||||
final PragmaAnnotationParser _matcher;
|
||||
|
||||
NativeCodeOracle(this._libraryIndex, this._matcher) {
|
||||
assert(_matcher != null);
|
||||
}
|
||||
NativeCodeOracle(this._libraryIndex, this._matcher);
|
||||
|
||||
void addClassReferencedFromNativeCode(Class klass) {
|
||||
_classesReferencedFromNativeCode.add(klass);
|
||||
@@ -219,9 +209,9 @@ class NativeCodeOracle {
|
||||
bool isMemberReferencedFromNativeCode(Member member) =>
|
||||
_membersReferencedFromNativeCode.contains(member);
|
||||
|
||||
PragmaRecognizedType recognizedType(Member member) {
|
||||
PragmaRecognizedType? recognizedType(Member member) {
|
||||
for (var annotation in member.annotations) {
|
||||
ParsedPragma pragma = _matcher.parsePragma(annotation);
|
||||
ParsedPragma? pragma = _matcher.parsePragma(annotation);
|
||||
if (pragma is ParsedRecognized) {
|
||||
return pragma.type;
|
||||
}
|
||||
@@ -229,15 +219,16 @@ class NativeCodeOracle {
|
||||
return null;
|
||||
}
|
||||
|
||||
bool isRecognized(Member member, [List<PragmaRecognizedType> expectedTypes]) {
|
||||
PragmaRecognizedType type = recognizedType(member);
|
||||
bool isRecognized(Member member,
|
||||
[List<PragmaRecognizedType>? expectedTypes]) {
|
||||
PragmaRecognizedType? type = recognizedType(member);
|
||||
return type != null &&
|
||||
(expectedTypes == null || expectedTypes.contains(type));
|
||||
}
|
||||
|
||||
bool hasDisableUnboxedParameters(Member member) {
|
||||
for (var annotation in member.annotations) {
|
||||
ParsedPragma pragma = _matcher.parsePragma(annotation);
|
||||
ParsedPragma? pragma = _matcher.parsePragma(annotation);
|
||||
if (pragma is ParsedDisableUnboxedParameters) {
|
||||
if (member.enclosingLibrary.importUri.scheme != "dart") {
|
||||
throw "ERROR: Cannot use @pragma(vm:disable-unboxed-parameters) outside core libraries.";
|
||||
@@ -255,11 +246,11 @@ class NativeCodeOracle {
|
||||
EntryPointsListener entryPointsListener,
|
||||
TypesBuilder typesBuilder,
|
||||
RuntimeTypeTranslator translator) {
|
||||
TypeExpr returnType = null;
|
||||
bool nullable = null;
|
||||
TypeExpr? returnType = null;
|
||||
bool? nullable = null;
|
||||
|
||||
for (var annotation in member.annotations) {
|
||||
ParsedPragma pragma = _matcher.parsePragma(annotation);
|
||||
ParsedPragma? pragma = _matcher.parsePragma(annotation);
|
||||
if (pragma == null) continue;
|
||||
if (pragma is ParsedResultTypeByTypePragma ||
|
||||
pragma is ParsedResultTypeByPathPragma ||
|
||||
@@ -278,8 +269,8 @@ class NativeCodeOracle {
|
||||
returnType = entryPointsListener.addAllocatedClass(type.classNode);
|
||||
if (pragma.resultTypeUsesPassedTypeArguments) {
|
||||
returnType = translator.instantiateConcreteType(
|
||||
returnType,
|
||||
member.function.typeParameters
|
||||
returnType as ConcreteType,
|
||||
member.function!.typeParameters
|
||||
.map((t) => TypeParameterType(
|
||||
t, TypeParameterType.computeNullabilityFromBound(t)))
|
||||
.toList());
|
||||
@@ -314,7 +305,7 @@ class NativeCodeOracle {
|
||||
return returnType;
|
||||
} else {
|
||||
return typesBuilder.fromStaticType(
|
||||
member.function.returnType, nullable ?? true);
|
||||
member.function!.returnType, nullable ?? true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/clone.dart' show CloneVisitorNotMembers;
|
||||
import 'package:kernel/core_types.dart' show CoreTypes;
|
||||
@@ -47,14 +45,14 @@ class ProtobufHandler {
|
||||
final Procedure _builderInfoAddMethod;
|
||||
|
||||
// Type of BuilderInfo.add<Null>().
|
||||
FunctionType _typeOfBuilderInfoAddOfNull;
|
||||
late FunctionType _typeOfBuilderInfoAddOfNull;
|
||||
|
||||
final _messageClasses = <Class, _MessageClass>{};
|
||||
final _invalidatedClasses = <_MessageClass>{};
|
||||
|
||||
/// Creates [ProtobufHandler] instance for [component].
|
||||
/// Returns null if protobuf library is not used.
|
||||
static ProtobufHandler forComponent(
|
||||
static ProtobufHandler? forComponent(
|
||||
Component component, CoreTypes coreTypes) {
|
||||
final libraryIndex = LibraryIndex(component, [protobufLibraryUri]);
|
||||
if (!libraryIndex.containsLibrary(protobufLibraryUri)) {
|
||||
@@ -68,12 +66,12 @@ class ProtobufHandler {
|
||||
libraryIndex.getClass(protobufLibraryUri, 'GeneratedMessage'),
|
||||
_tagNumberClass =
|
||||
libraryIndex.getClass(protobufLibraryUri, 'TagNumber'),
|
||||
_tagNumberField = libraryIndex.getMember(
|
||||
protobufLibraryUri, 'TagNumber', 'tagNumber'),
|
||||
_tagNumberField =
|
||||
libraryIndex.getField(protobufLibraryUri, 'TagNumber', 'tagNumber'),
|
||||
_builderInfoClass =
|
||||
libraryIndex.getClass(protobufLibraryUri, 'BuilderInfo'),
|
||||
_builderInfoAddMethod =
|
||||
libraryIndex.getMember(protobufLibraryUri, 'BuilderInfo', 'add') {
|
||||
_builderInfoAddMethod = libraryIndex.getProcedure(
|
||||
protobufLibraryUri, 'BuilderInfo', 'add') {
|
||||
final functionType = _builderInfoAddMethod.getterType as FunctionType;
|
||||
_typeOfBuilderInfoAddOfNull = Substitution.fromPairs(
|
||||
functionType.typeParameters, const <DartType>[NullType()])
|
||||
@@ -130,8 +128,9 @@ class ProtobufHandler {
|
||||
List<Field> getInvalidatedFields() {
|
||||
final fields = <Field>[];
|
||||
for (var cls in _invalidatedClasses) {
|
||||
if (cls._metadataField != null) {
|
||||
fields.add(cls._metadataField);
|
||||
final field = cls._metadataField;
|
||||
if (field != null) {
|
||||
fields.add(field);
|
||||
}
|
||||
}
|
||||
_invalidatedClasses.clear();
|
||||
@@ -143,14 +142,15 @@ class ProtobufHandler {
|
||||
++Statistics.protobufMetadataInitializersUpdated;
|
||||
Statistics.protobufMetadataFieldsPruned -= cls.numberOfFieldsPruned;
|
||||
|
||||
final field = cls._metadataField;
|
||||
if (cls._originalInitializer == null) {
|
||||
cls._originalInitializer = field.initializer;
|
||||
final field = cls._metadataField!;
|
||||
Expression? originalInitializer = cls._originalInitializer;
|
||||
if (originalInitializer == null) {
|
||||
cls._originalInitializer = originalInitializer = field.initializer!;
|
||||
}
|
||||
final cloner = CloneVisitorNotMembers();
|
||||
field.initializer = cloner.clone(cls._originalInitializer)..parent = field;
|
||||
field.initializer = cloner.clone(originalInitializer)..parent = field;
|
||||
final transformer = _MetadataTransformer(this, cls);
|
||||
field.initializer.accept(transformer);
|
||||
field.initializer!.accept(transformer);
|
||||
_invalidatedClasses.remove(cls);
|
||||
|
||||
cls.numberOfFieldsPruned = transformer.numberOfFieldsPruned;
|
||||
@@ -168,8 +168,8 @@ class ProtobufHandler {
|
||||
}
|
||||
|
||||
class _MessageClass {
|
||||
Field _metadataField;
|
||||
Expression _originalInitializer;
|
||||
Field? _metadataField;
|
||||
Expression? _originalInitializer;
|
||||
final _usedTags = <int>{};
|
||||
int numberOfFieldsPruned = 0;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/external_name.dart';
|
||||
import 'package:kernel/type_environment.dart';
|
||||
@@ -57,7 +55,7 @@ class SignatureShaker {
|
||||
|
||||
SignatureShaker(this.typeFlowAnalysis, this.tableSelectorAssigner);
|
||||
|
||||
_ProcedureInfo _infoForMember(Member member) {
|
||||
_ProcedureInfo? _infoForMember(Member member) {
|
||||
if (!(member is Procedure &&
|
||||
(member.kind == ProcedureKind.Method ||
|
||||
member.kind == ProcedureKind.Factory) ||
|
||||
@@ -89,7 +87,7 @@ class SignatureShaker {
|
||||
}
|
||||
while (worklist.isNotEmpty) {
|
||||
_ParameterInfo param = worklist.removeLast();
|
||||
for (_ParameterInfo dependencyParam in param.useDependencies) {
|
||||
for (_ParameterInfo dependencyParam in param.useDependencies!) {
|
||||
if (!dependencyParam.isRead) {
|
||||
dependencyParam.isRead = true;
|
||||
if (dependencyParam.useDependencies != null) {
|
||||
@@ -141,7 +139,7 @@ class _ProcedureInfo {
|
||||
return positional.any((param) =>
|
||||
param.canBeEliminated ||
|
||||
(param.isAlwaysPassed &&
|
||||
param.index >= function.requiredParameterCount)) ||
|
||||
param.index! >= function.requiredParameterCount)) ||
|
||||
named.values
|
||||
.any((param) => param.canBeEliminated || param.isAlwaysPassed);
|
||||
}
|
||||
@@ -149,7 +147,7 @@ class _ProcedureInfo {
|
||||
|
||||
class _ParameterInfo {
|
||||
final _ProcedureInfo info;
|
||||
final int index;
|
||||
final int? index;
|
||||
|
||||
int passCount = 0;
|
||||
bool isRead = false;
|
||||
@@ -160,7 +158,7 @@ class _ParameterInfo {
|
||||
/// List of parameter variables which were passed as arguments via this
|
||||
/// parameter. When this parameter is considered used, all [useDependencies]
|
||||
/// parameters should be transitively marked as read.
|
||||
List<_ParameterInfo> useDependencies = null;
|
||||
List<_ParameterInfo>? useDependencies = null;
|
||||
|
||||
_ParameterInfo(this.info, this.index);
|
||||
|
||||
@@ -177,7 +175,7 @@ class _ParameterInfo {
|
||||
|
||||
void observeParameter(
|
||||
Member member, VariableDeclaration param, SignatureShaker shaker) {
|
||||
final Type type = shaker.typeFlowAnalysis.argumentType(member, param);
|
||||
final Type? type = shaker.typeFlowAnalysis.argumentType(member, param);
|
||||
|
||||
// A parameter is considered constant if the TFA has inferred it to have a
|
||||
// constant value in every implementation. The constant value inferred does
|
||||
@@ -214,19 +212,19 @@ class _Collect extends RecursiveVisitor {
|
||||
_Collect(this.shaker);
|
||||
|
||||
void enterFunction(Member member) {
|
||||
final _ProcedureInfo info = shaker._infoForMember(member);
|
||||
final _ProcedureInfo? info = shaker._infoForMember(member);
|
||||
if (info == null) return;
|
||||
|
||||
localParameters.clear();
|
||||
useDependencies.clear();
|
||||
final FunctionNode fun = member.function;
|
||||
final FunctionNode fun = member.function!;
|
||||
for (int i = 0; i < fun.positionalParameters.length; i++) {
|
||||
final VariableDeclaration param = fun.positionalParameters[i];
|
||||
localParameters[param] = info.ensurePositional(i)
|
||||
..observeParameter(member, param, shaker);
|
||||
}
|
||||
for (VariableDeclaration param in fun.namedParameters) {
|
||||
localParameters[param] = info.ensureNamed(param.name)
|
||||
localParameters[param] = info.ensureNamed(param.name!)
|
||||
..observeParameter(member, param, shaker);
|
||||
}
|
||||
|
||||
@@ -270,19 +268,22 @@ class _Collect extends RecursiveVisitor {
|
||||
|
||||
void addUseDependency(Expression arg, _ParameterInfo param) {
|
||||
if (arg is VariableGet) {
|
||||
_ParameterInfo localParam = localParameters[arg.variable];
|
||||
_ParameterInfo? localParam = localParameters[arg.variable];
|
||||
if (localParam != null && !localParam.isUsed) {
|
||||
// This is a parameter passed as an argument. Mark it as a use
|
||||
// dependency.
|
||||
param.useDependencies ??= [];
|
||||
param.useDependencies.add(localParam);
|
||||
var paramUseDependencies = param.useDependencies;
|
||||
if (paramUseDependencies == null) {
|
||||
param.useDependencies = paramUseDependencies = [];
|
||||
}
|
||||
paramUseDependencies.add(localParam);
|
||||
useDependencies.add(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collectCall(Member member, Arguments args) {
|
||||
final _ProcedureInfo info = shaker._infoForMember(member);
|
||||
final _ProcedureInfo? info = shaker._infoForMember(member);
|
||||
if (info == null) return;
|
||||
|
||||
for (int i = 0; i < args.positional.length; i++) {
|
||||
@@ -306,7 +307,10 @@ class _Collect extends RecursiveVisitor {
|
||||
|
||||
@override
|
||||
void visitSuperMethodInvocation(SuperMethodInvocation node) {
|
||||
collectCall(node.interfaceTarget, node.arguments);
|
||||
final interfaceTarget = node.interfaceTarget;
|
||||
if (interfaceTarget != null) {
|
||||
collectCall(interfaceTarget, node.arguments);
|
||||
}
|
||||
super.visitSuperMethodInvocation(node);
|
||||
}
|
||||
|
||||
@@ -338,7 +342,7 @@ class _Collect extends RecursiveVisitor {
|
||||
class _Transform extends RecursiveVisitor {
|
||||
final SignatureShaker shaker;
|
||||
|
||||
StaticTypeContext typeContext;
|
||||
late StaticTypeContext typeContext;
|
||||
final Map<VariableDeclaration, Constant> eliminatedParams = {};
|
||||
final Set<VariableDeclaration> unusedParams = {};
|
||||
final List<LocalInitializer> addedInitializers = [];
|
||||
@@ -349,16 +353,15 @@ class _Transform extends RecursiveVisitor {
|
||||
Member member, _ParameterInfo param, VariableDeclaration variable) {
|
||||
Constant value;
|
||||
if (param.isConstant) {
|
||||
Type type = shaker.typeFlowAnalysis.argumentType(member, variable);
|
||||
Type type = shaker.typeFlowAnalysis.argumentType(member, variable)!;
|
||||
if (type is ConcreteType) {
|
||||
assert(type.constant != null);
|
||||
value = type.constant;
|
||||
value = type.constant!;
|
||||
} else {
|
||||
assert(type is NullableType && type.baseType is EmptyType);
|
||||
value = NullConstant();
|
||||
}
|
||||
} else {
|
||||
value = (variable.initializer as ConstantExpression)?.constant ??
|
||||
value = (variable.initializer as ConstantExpression?)?.constant ??
|
||||
NullConstant();
|
||||
}
|
||||
eliminatedParams[variable] = value;
|
||||
@@ -370,10 +373,10 @@ class _Transform extends RecursiveVisitor {
|
||||
eliminatedParams.clear();
|
||||
unusedParams.clear();
|
||||
|
||||
final _ProcedureInfo info = shaker._infoForMember(member);
|
||||
final _ProcedureInfo? info = shaker._infoForMember(member);
|
||||
if (info == null || !info.eligible || info.callCount == 0) return;
|
||||
|
||||
final FunctionNode function = member.function;
|
||||
final FunctionNode function = member.function!;
|
||||
|
||||
if (!info.transformNeeded(function)) return;
|
||||
|
||||
@@ -404,9 +407,9 @@ class _Transform extends RecursiveVisitor {
|
||||
// as required positional parameters, alphabetically by name.
|
||||
final List<VariableDeclaration> sortedNamed = function.namedParameters
|
||||
.toList()
|
||||
..sort((var1, var2) => var1.name.compareTo(var2.name));
|
||||
..sort((var1, var2) => var1.name!.compareTo(var2.name!));
|
||||
for (VariableDeclaration variable in sortedNamed) {
|
||||
final _ParameterInfo param = info.named[variable.name];
|
||||
final _ParameterInfo param = info.named[variable.name!]!;
|
||||
if (param.isAlwaysPassed) {
|
||||
if (param.isUsed) {
|
||||
if (param.canBeEliminated) {
|
||||
@@ -451,7 +454,7 @@ class _Transform extends RecursiveVisitor {
|
||||
// 4. All named parameters that are not always passed and can't be
|
||||
// eliminated, as named parameters in alphabetical order.
|
||||
for (VariableDeclaration variable in sortedNamed) {
|
||||
final _ParameterInfo param = info.named[variable.name];
|
||||
final _ParameterInfo param = info.named[variable.name!]!;
|
||||
if (!param.isAlwaysPassed) {
|
||||
if (param.isUsed) {
|
||||
if (param.canBeEliminated) {
|
||||
@@ -475,7 +478,7 @@ class _Transform extends RecursiveVisitor {
|
||||
|
||||
@override
|
||||
void visitVariableGet(VariableGet node) {
|
||||
Constant constantValue = eliminatedParams[node.variable];
|
||||
Constant? constantValue = eliminatedParams[node.variable];
|
||||
if (constantValue != null) {
|
||||
node.replaceWith(ConstantExpression(constantValue));
|
||||
}
|
||||
@@ -511,7 +514,7 @@ class _Transform extends RecursiveVisitor {
|
||||
void Function(Expression, _ParameterInfo) fun) {
|
||||
for (int i = args.named.length - 1; i >= 0; i--) {
|
||||
final NamedExpression namedExp = args.named[i];
|
||||
fun(namedExp.value, info.named[namedExp.name]);
|
||||
fun(namedExp.value, info.named[namedExp.name]!);
|
||||
}
|
||||
for (int i = args.positional.length - 1; i >= 0; i--) {
|
||||
fun(args.positional[i], info.positional[i]);
|
||||
@@ -519,8 +522,8 @@ class _Transform extends RecursiveVisitor {
|
||||
}
|
||||
|
||||
void transformCall(
|
||||
Member target, TreeNode call, Expression receiver, Arguments args) {
|
||||
final _ProcedureInfo info = shaker._infoForMember(target);
|
||||
Member target, TreeNode call, Expression? receiver, Arguments args) {
|
||||
final _ProcedureInfo? info = shaker._infoForMember(target);
|
||||
if (info == null || !info.eligible) return;
|
||||
|
||||
bool transformNeeded = false;
|
||||
@@ -544,7 +547,7 @@ class _Transform extends RecursiveVisitor {
|
||||
Map<Expression, VariableDeclaration> hoisted = {};
|
||||
if (hoistingNeeded) {
|
||||
if (call is Initializer) {
|
||||
final Constructor constructor = call.parent;
|
||||
final Constructor constructor = call.parent as Constructor;
|
||||
forEachArgumentRev(args, info, (Expression arg, _ParameterInfo param) {
|
||||
if (mayHaveOrSeeSideEffects(arg) && !isUnusedParam(arg)) {
|
||||
VariableDeclaration argVar = VariableDeclaration(null,
|
||||
@@ -557,8 +560,8 @@ class _Transform extends RecursiveVisitor {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final TreeNode parent = call.parent;
|
||||
Expression current = call;
|
||||
final TreeNode parent = call.parent!;
|
||||
Expression current = call as Expression;
|
||||
forEachArgumentRev(args, info, (Expression arg, _ParameterInfo param) {
|
||||
if (mayHaveOrSeeSideEffects(arg) && !isUnusedParam(arg)) {
|
||||
VariableDeclaration argVar = VariableDeclaration(null,
|
||||
@@ -606,7 +609,7 @@ class _Transform extends RecursiveVisitor {
|
||||
final List<NamedExpression> sortedNamed = args.named.toList()
|
||||
..sort((var1, var2) => var1.name.compareTo(var2.name));
|
||||
for (NamedExpression arg in sortedNamed) {
|
||||
final _ParameterInfo param = info.named[arg.name];
|
||||
final _ParameterInfo param = info.named[arg.name]!;
|
||||
if (param.isAlwaysPassed && !param.canBeEliminated) {
|
||||
positional.add(getMaybeHoistedArg(arg.value));
|
||||
}
|
||||
@@ -625,7 +628,7 @@ class _Transform extends RecursiveVisitor {
|
||||
// eliminated, as named parameters in alphabetical order.
|
||||
// (Arguments are kept in original order.)
|
||||
for (NamedExpression arg in args.named) {
|
||||
final _ParameterInfo param = info.named[arg.name];
|
||||
final _ParameterInfo param = info.named[arg.name]!;
|
||||
if (!param.isAlwaysPassed && !param.canBeEliminated) {
|
||||
arg.value = getMaybeHoistedArg(arg.value)..parent = arg;
|
||||
named.add(arg);
|
||||
@@ -644,7 +647,10 @@ class _Transform extends RecursiveVisitor {
|
||||
@override
|
||||
void visitSuperMethodInvocation(SuperMethodInvocation node) {
|
||||
super.visitSuperMethodInvocation(node);
|
||||
transformCall(node.interfaceTarget, node, null, node.arguments);
|
||||
final interfaceTarget = node.interfaceTarget;
|
||||
if (interfaceTarget != null) {
|
||||
transformCall(interfaceTarget, node, null, node.arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
/// Type flow summary of a member, function or initializer.
|
||||
library vm.transformations.type_flow.summary;
|
||||
|
||||
import 'dart:core' hide Type;
|
||||
|
||||
@@ -18,7 +15,7 @@ import 'utils.dart';
|
||||
|
||||
abstract class CallHandler {
|
||||
Type applyCall(Call callSite, Selector selector, Args<Type> args,
|
||||
{bool isResultUsed});
|
||||
{required bool isResultUsed});
|
||||
void typeCheckTriggered();
|
||||
void addAllocatedClass(Class c);
|
||||
}
|
||||
@@ -27,14 +24,10 @@ abstract class CallHandler {
|
||||
abstract class Statement extends TypeExpr {
|
||||
/// Index of this statement in the [Summary].
|
||||
int index = -1;
|
||||
Summary summary;
|
||||
late Summary summary;
|
||||
|
||||
@override
|
||||
Type getComputedType(List<Type> types) {
|
||||
final type = types[index];
|
||||
assert(type != null);
|
||||
return type;
|
||||
}
|
||||
Type getComputedType(List<Type?> types) => types[index]!;
|
||||
|
||||
String get label => "t$index";
|
||||
|
||||
@@ -48,7 +41,7 @@ abstract class Statement extends TypeExpr {
|
||||
void accept(StatementVisitor visitor);
|
||||
|
||||
/// Execute this statement and compute its resulting type.
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler);
|
||||
}
|
||||
|
||||
@@ -74,9 +67,9 @@ class Parameter extends Statement {
|
||||
// [staticType] is null if no narrowing should be performed. This happens for
|
||||
// type parameters and for parameters whose type is narrowed by a [TypeCheck]
|
||||
// statement.
|
||||
final Type staticTypeForNarrowing;
|
||||
final Type? staticTypeForNarrowing;
|
||||
|
||||
Type defaultValue;
|
||||
Type? defaultValue;
|
||||
Type _argumentType = const EmptyType();
|
||||
|
||||
Parameter(this.name, this.staticTypeForNarrowing);
|
||||
@@ -97,7 +90,7 @@ class Parameter extends Statement {
|
||||
}
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) =>
|
||||
throw 'Unable to apply _Parameter';
|
||||
|
||||
@@ -110,7 +103,7 @@ class Parameter extends Statement {
|
||||
}
|
||||
|
||||
Type _observeNotPassed(TypeHierarchy typeHierarchy) {
|
||||
final Type argType = defaultValue.specialize(typeHierarchy);
|
||||
final Type argType = defaultValue!.specialize(typeHierarchy);
|
||||
_observeArgumentType(argType, typeHierarchy);
|
||||
return argType;
|
||||
}
|
||||
@@ -130,7 +123,7 @@ class Narrow extends Statement {
|
||||
String dump() => "$label = _Narrow ($arg to $type)";
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) =>
|
||||
arg.getComputedType(computedTypes).intersection(type, typeHierarchy);
|
||||
}
|
||||
@@ -147,11 +140,11 @@ class NarrowNotNull extends Narrow {
|
||||
|
||||
// Shared NarrowNotNull instances which are used when the outcome is
|
||||
// known at summary creation time.
|
||||
static final NarrowNotNull alwaysNotNull = NarrowNotNull(null)
|
||||
static final NarrowNotNull alwaysNotNull = NarrowNotNull(const EmptyType())
|
||||
.._flags = canBeNotNullFlag;
|
||||
static final NarrowNotNull alwaysNull = NarrowNotNull(null)
|
||||
static final NarrowNotNull alwaysNull = NarrowNotNull(const EmptyType())
|
||||
.._flags = canBeNullFlag;
|
||||
static final NarrowNotNull unknown = NarrowNotNull(null)
|
||||
static final NarrowNotNull unknown = NarrowNotNull(const EmptyType())
|
||||
.._flags = canBeNullFlag | canBeNotNullFlag;
|
||||
|
||||
bool get isAlwaysNull => (_flags & canBeNotNullFlag) == 0;
|
||||
@@ -173,14 +166,14 @@ class NarrowNotNull extends Narrow {
|
||||
}
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) =>
|
||||
handleArgument(arg.getComputedType(computedTypes));
|
||||
}
|
||||
|
||||
/// Joins values from multiple sources. Its type is a union of [values].
|
||||
class Join extends Statement {
|
||||
final String _name;
|
||||
final String? _name;
|
||||
final DartType staticType;
|
||||
final List<TypeExpr> values = <TypeExpr>[]; // TODO(alexmarkov): Set
|
||||
|
||||
@@ -197,15 +190,14 @@ class Join extends Statement {
|
||||
" (${values.join(", ")})";
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
Type type = null;
|
||||
assert(values.isNotEmpty);
|
||||
Type? type = null;
|
||||
for (var value in values) {
|
||||
final valueType = value.getComputedType(computedTypes);
|
||||
type = type != null ? type.union(valueType, typeHierarchy) : valueType;
|
||||
}
|
||||
return type;
|
||||
return type!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +214,7 @@ class Use extends Statement {
|
||||
String dump() => "$label = _Use ($arg)";
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) =>
|
||||
throw 'Use statements should be removed during summary normalization';
|
||||
}
|
||||
@@ -231,7 +223,7 @@ class Use extends Statement {
|
||||
class Call extends Statement {
|
||||
final Selector selector;
|
||||
final Args<TypeExpr> args;
|
||||
final Type staticResultType;
|
||||
final Type? staticResultType;
|
||||
|
||||
Call(this.selector, this.args, this.staticResultType,
|
||||
bool isInstanceCreation) {
|
||||
@@ -252,9 +244,10 @@ class Call extends Statement {
|
||||
String dump() => "$label${isResultUsed ? '*' : ''} = _Call $selector $args";
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
final List<Type> argTypes = new List<Type>.filled(args.values.length, null);
|
||||
final List<Type> argTypes =
|
||||
new List<Type>.filled(args.values.length, const EmptyType());
|
||||
for (int i = 0; i < args.values.length; i++) {
|
||||
final Type type = args.values[i].getComputedType(computedTypes);
|
||||
if (type == const EmptyType()) {
|
||||
@@ -271,14 +264,15 @@ class Call extends Statement {
|
||||
callHandler
|
||||
.addAllocatedClass((argTypes[0] as ConcreteType).cls.classNode);
|
||||
}
|
||||
final Stopwatch timer = kPrintTimings ? (new Stopwatch()..start()) : null;
|
||||
final Stopwatch? timer = kPrintTimings ? (new Stopwatch()..start()) : null;
|
||||
Type result = callHandler.applyCall(
|
||||
this, selector, new Args<Type>(argTypes, names: args.names),
|
||||
isResultUsed: isResultUsed);
|
||||
summary.calleeTime += kPrintTimings ? timer.elapsedMicroseconds : 0;
|
||||
summary.calleeTime += kPrintTimings ? timer!.elapsedMicroseconds : 0;
|
||||
if (isInstanceCreation) {
|
||||
result = argTypes[0];
|
||||
} else if (isResultUsed) {
|
||||
final staticResultType = this.staticResultType;
|
||||
if (staticResultType != null) {
|
||||
result = result.intersection(staticResultType, typeHierarchy);
|
||||
}
|
||||
@@ -302,9 +296,9 @@ class Call extends Statement {
|
||||
static const int kReceiverMayBeInt = (1 << 6);
|
||||
static const int kInstanceCreation = (1 << 7);
|
||||
|
||||
Member _monomorphicTarget;
|
||||
Member? _monomorphicTarget;
|
||||
|
||||
Member get monomorphicTarget => _monomorphicTarget;
|
||||
Member? get monomorphicTarget => _monomorphicTarget;
|
||||
|
||||
bool get isMonomorphic => (_flags & kMonomorphic) != 0;
|
||||
|
||||
@@ -396,18 +390,19 @@ class Extract extends Statement {
|
||||
"/$paramIndex]${nullability.suffix})";
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
Type argType = arg.getComputedType(computedTypes);
|
||||
Type extractedType;
|
||||
Type? extractedType;
|
||||
|
||||
void extractType(ConcreteType c) {
|
||||
if (c.typeArgs == null) {
|
||||
final typeArgs = c.typeArgs;
|
||||
if (typeArgs == null) {
|
||||
extractedType = const UnknownType();
|
||||
} else {
|
||||
final interfaceOffset = typeHierarchy.genericInterfaceOffsetFor(
|
||||
c.cls.classNode, referenceClass);
|
||||
final typeArg = c.typeArgs[interfaceOffset + paramIndex];
|
||||
final typeArg = typeArgs[interfaceOffset + paramIndex];
|
||||
Type extracted = typeArg;
|
||||
if (typeArg is RuntimeType) {
|
||||
final argNullability = typeArg.nullability;
|
||||
@@ -472,16 +467,15 @@ class CreateConcreteType extends Statement {
|
||||
}
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
bool hasRuntimeType = false;
|
||||
final types = new List<Type>.filled(flattenedTypeArgs.length, null);
|
||||
for (int i = 0; i < types.length; ++i) {
|
||||
final types = List<Type>.generate(flattenedTypeArgs.length, (int i) {
|
||||
final computed = flattenedTypeArgs[i].getComputedType(computedTypes);
|
||||
assert(computed is RuntimeType || computed is UnknownType);
|
||||
if (computed is RuntimeType) hasRuntimeType = true;
|
||||
types[i] = computed;
|
||||
}
|
||||
return computed;
|
||||
});
|
||||
return new ConcreteType(cls, hasRuntimeType ? types : null);
|
||||
}
|
||||
}
|
||||
@@ -505,14 +499,13 @@ class CreateRuntimeType extends Statement {
|
||||
"${nullability.suffix})";
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
final types = new List<RuntimeType>.filled(flattenedTypeArgs.length, null);
|
||||
for (int i = 0; i < types.length; ++i) {
|
||||
final computed = flattenedTypeArgs[i].getComputedType(computedTypes);
|
||||
assert(computed is RuntimeType || computed is UnknownType);
|
||||
final types = <RuntimeType>[];
|
||||
for (TypeExpr arg in flattenedTypeArgs) {
|
||||
final computed = arg.getComputedType(computedTypes);
|
||||
if (computed is UnknownType) return const UnknownType();
|
||||
types[i] = computed;
|
||||
types.add(computed as RuntimeType);
|
||||
}
|
||||
DartType dartType;
|
||||
if (klass == typeHierarchy.coreTypes.deprecatedFutureOrClass) {
|
||||
@@ -545,17 +538,15 @@ class TypeCheck extends Statement {
|
||||
// "unchecked" entrypoint.
|
||||
bool isTestedOnlyOnCheckedEntryPoint;
|
||||
|
||||
VariableDeclaration get parameter =>
|
||||
node is VariableDeclaration ? node : null;
|
||||
bool get isParameterCheck => node is VariableDeclaration;
|
||||
VariableDeclaration get parameterVariable => node as VariableDeclaration;
|
||||
|
||||
bool alwaysPass = true;
|
||||
bool alwaysFail = true;
|
||||
|
||||
TypeCheck(this.arg, this.type, this.node, this.staticType, this.kind) {
|
||||
assert(node != null);
|
||||
isTestedOnlyOnCheckedEntryPoint =
|
||||
parameter != null && !parameter.isCovariant;
|
||||
}
|
||||
TypeCheck(this.arg, this.type, this.node, this.staticType, this.kind)
|
||||
: isTestedOnlyOnCheckedEntryPoint =
|
||||
node is VariableDeclaration && !node.isCovariant;
|
||||
|
||||
@override
|
||||
void accept(StatementVisitor visitor) => visitor.visitTypeCheck(this);
|
||||
@@ -568,7 +559,7 @@ class TypeCheck extends Statement {
|
||||
}
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
Type apply(List<Type?> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
Type argType = arg.getComputedType(computedTypes);
|
||||
Type checkType = type.getComputedType(computedTypes);
|
||||
@@ -624,11 +615,11 @@ class Summary {
|
||||
int requiredParameterCount;
|
||||
|
||||
List<Statement> _statements = <Statement>[];
|
||||
TypeExpr result = null;
|
||||
Type resultType = EmptyType();
|
||||
TypeExpr result = const EmptyType();
|
||||
Type resultType = const EmptyType();
|
||||
|
||||
// Analysis time of callees. Populated only if kPrintTimings.
|
||||
int calleeTime;
|
||||
int calleeTime = 0;
|
||||
|
||||
Summary(this.name,
|
||||
{this.parameterCount: 0,
|
||||
@@ -658,7 +649,7 @@ class Summary {
|
||||
/// Apply this summary to the given arguments and return the resulting type.
|
||||
Type apply(Args<Type> arguments, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
final Stopwatch timer = kPrintTimings ? (new Stopwatch()..start()) : null;
|
||||
final Stopwatch? timer = kPrintTimings ? (new Stopwatch()..start()) : null;
|
||||
final int oldCalleeTime = calleeTime;
|
||||
calleeTime = 0;
|
||||
final args = arguments.values;
|
||||
@@ -677,7 +668,7 @@ class Summary {
|
||||
//
|
||||
// The first `parameterCount` statements are Parameters.
|
||||
|
||||
List<Type> types = new List<Type>.filled(_statements.length, null);
|
||||
List<Type?> types = new List<Type?>.filled(_statements.length, null);
|
||||
|
||||
for (int i = 0; i < positionalArgCount; i++) {
|
||||
final Parameter param = _statements[i] as Parameter;
|
||||
@@ -687,9 +678,9 @@ class Summary {
|
||||
}
|
||||
final argType = args[i].specialize(typeHierarchy);
|
||||
param._observeArgumentType(argType, typeHierarchy);
|
||||
if (param.staticTypeForNarrowing != null) {
|
||||
types[i] =
|
||||
argType.intersection(param.staticTypeForNarrowing, typeHierarchy);
|
||||
final staticTypeForNarrowing = param.staticTypeForNarrowing;
|
||||
if (staticTypeForNarrowing != null) {
|
||||
types[i] = argType.intersection(staticTypeForNarrowing, typeHierarchy);
|
||||
} else {
|
||||
// TODO(sjindel/tfa): Narrowing is performed inside a [TypeCheck] later.
|
||||
types[i] = args[i];
|
||||
@@ -710,9 +701,10 @@ class Summary {
|
||||
args[positionalArgCount + argIndex].specialize(typeHierarchy);
|
||||
argIndex++;
|
||||
param._observeArgumentType(argType, typeHierarchy);
|
||||
if (param.staticTypeForNarrowing != null) {
|
||||
final staticTypeForNarrowing = param.staticTypeForNarrowing;
|
||||
if (staticTypeForNarrowing != null) {
|
||||
types[i] =
|
||||
argType.intersection(param.staticTypeForNarrowing, typeHierarchy);
|
||||
argType.intersection(staticTypeForNarrowing, typeHierarchy);
|
||||
} else {
|
||||
types[i] = argType;
|
||||
}
|
||||
@@ -741,7 +733,7 @@ class Summary {
|
||||
resultType = resultType.union(computedType, typeHierarchy);
|
||||
|
||||
if (kPrintTimings) {
|
||||
final dirtyTime = timer.elapsedMicroseconds;
|
||||
final dirtyTime = timer!.elapsedMicroseconds;
|
||||
final pureTime = dirtyTime < calleeTime ? 0 : (dirtyTime - calleeTime);
|
||||
Statistics.numSummaryApplications.add(name);
|
||||
Statistics.dirtySummaryAnalysisTime.add(name, dirtyTime);
|
||||
@@ -753,9 +745,9 @@ class Summary {
|
||||
}
|
||||
|
||||
Args<Type> get argumentTypes {
|
||||
final argTypes = new List<Type>.filled(parameterCount, null);
|
||||
final argNames = new List<String>.filled(
|
||||
parameterCount - positionalParameterCount, null);
|
||||
final argTypes = new List<Type>.filled(parameterCount, const EmptyType());
|
||||
final argNames =
|
||||
new List<String>.filled(parameterCount - positionalParameterCount, '');
|
||||
for (int i = 0; i < parameterCount; i++) {
|
||||
Parameter param = _statements[i] as Parameter;
|
||||
argTypes[i] = param.argumentType;
|
||||
@@ -769,7 +761,7 @@ class Summary {
|
||||
Type argumentType(Member member, VariableDeclaration memberParam) {
|
||||
final int firstParamIndex =
|
||||
numTypeParams(member) + (hasReceiverArg(member) ? 1 : 0);
|
||||
final positional = member.function.positionalParameters;
|
||||
final positional = member.function!.positionalParameters;
|
||||
for (int i = 0; i < positional.length; i++) {
|
||||
if (positional[i] == memberParam) {
|
||||
final Parameter param = _statements[firstParamIndex + i] as Parameter;
|
||||
@@ -791,8 +783,8 @@ class Summary {
|
||||
for (Statement statement in _statements) {
|
||||
if (statement is TypeCheck &&
|
||||
statement.alwaysPass &&
|
||||
statement.parameter != null) {
|
||||
params.add(statement.parameter);
|
||||
statement.isParameterCheck) {
|
||||
params.add(statement.parameterVariable);
|
||||
}
|
||||
}
|
||||
return params;
|
||||
@@ -808,17 +800,17 @@ class Summary {
|
||||
(hasReceiverArg(member) ? 1 : 0) + numTypeParams(member);
|
||||
final Map<String, Parameter> paramsByName = {};
|
||||
for (int i = implicit; i < parameterCount; i++) {
|
||||
final Parameter param = statements[i];
|
||||
final Parameter param = statements[i] as Parameter;
|
||||
paramsByName[param.name] = param;
|
||||
}
|
||||
FunctionNode function = member.function;
|
||||
FunctionNode function = member.function!;
|
||||
statements.length = implicit;
|
||||
for (VariableDeclaration param in function.positionalParameters) {
|
||||
statements.add(paramsByName[param.name]);
|
||||
statements.add(paramsByName[param.name]!);
|
||||
}
|
||||
positionalParameterCount = statements.length;
|
||||
for (VariableDeclaration param in function.namedParameters) {
|
||||
statements.add(paramsByName[param.name]);
|
||||
statements.add(paramsByName[param.name]!);
|
||||
}
|
||||
parameterCount = statements.length;
|
||||
requiredParameterCount = implicit + function.requiredParameterCount;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,6 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
|
||||
import 'utils.dart' show UnionFind;
|
||||
@@ -18,7 +16,7 @@ class TableSelectorAssigner {
|
||||
final Map<Class, Map<Name, int>> _methodOrSetterMemberIds = {};
|
||||
|
||||
final UnionFind _unionFind = UnionFind();
|
||||
List<int> _selectorIdForMemberId;
|
||||
late List<int?> _selectorIdForMemberId;
|
||||
|
||||
TableSelectorAssigner(Component component) {
|
||||
for (Library library in component.libraries) {
|
||||
@@ -44,20 +42,21 @@ class TableSelectorAssigner {
|
||||
}
|
||||
}
|
||||
|
||||
Map<Name, int> _memberIdsForClass(Class cls, {bool getter}) {
|
||||
Map<Name, int> _memberIdsForClass(Class? cls, {required bool getter}) {
|
||||
if (cls == null) return {};
|
||||
|
||||
final cache = getter ? _getterMemberIds : _methodOrSetterMemberIds;
|
||||
|
||||
// Already computed for this class?
|
||||
Map<Name, int> memberIds = cache[cls];
|
||||
if (memberIds != null) return memberIds;
|
||||
final cachedMemberIds = cache[cls];
|
||||
if (cachedMemberIds != null) return cachedMemberIds;
|
||||
|
||||
// Merge maps from supertypes.
|
||||
memberIds = Map.from(_memberIdsForClass(cls.superclass, getter: getter));
|
||||
final memberIds =
|
||||
Map<Name, int>.from(_memberIdsForClass(cls.superclass, getter: getter));
|
||||
for (Supertype impl in cls.implementedTypes) {
|
||||
_memberIdsForClass(impl.classNode, getter: getter).forEach((name, id) {
|
||||
final int firstId = memberIds[name];
|
||||
final int? firstId = memberIds[name];
|
||||
if (firstId == null) {
|
||||
memberIds[name] = id;
|
||||
} else if (firstId != id) {
|
||||
@@ -99,9 +98,9 @@ class TableSelectorAssigner {
|
||||
return cache[cls] = memberIds;
|
||||
}
|
||||
|
||||
int _selectorIdForMember(Member member, {bool getter}) {
|
||||
int _selectorIdForMember(Member member, {required bool getter}) {
|
||||
final map = getter ? _getterMemberIds : _methodOrSetterMemberIds;
|
||||
int memberId = map[member.enclosingClass][member.name];
|
||||
int? memberId = map[member.enclosingClass!]![member.name];
|
||||
if (memberId == null) {
|
||||
assert(member is Procedure &&
|
||||
((identical(map, _getterMemberIds) &&
|
||||
@@ -115,7 +114,7 @@ class TableSelectorAssigner {
|
||||
return ProcedureAttributesMetadata.kInvalidSelectorId;
|
||||
}
|
||||
memberId = _unionFind.find(memberId);
|
||||
int selectorId = _selectorIdForMemberId[memberId];
|
||||
int? selectorId = _selectorIdForMemberId[memberId];
|
||||
if (selectorId == null) {
|
||||
_selectorIdForMemberId[memberId] = selectorId = metadata.addSelector();
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
/// Transformations based on type flow analysis.
|
||||
library vm.transformations.type_flow.transformer;
|
||||
|
||||
import 'dart:core' hide Type;
|
||||
|
||||
@@ -14,7 +11,8 @@ import 'package:kernel/ast.dart' hide Statement, StatementVisitor;
|
||||
import 'package:kernel/ast.dart' as ast show Statement;
|
||||
import 'package:kernel/clone.dart' show CloneVisitorNotMembers;
|
||||
import 'package:kernel/core_types.dart' show CoreTypes;
|
||||
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
||||
import 'package:kernel/class_hierarchy.dart'
|
||||
show ClassHierarchy, ClosedWorldClassHierarchy;
|
||||
import 'package:kernel/library_index.dart' show LibraryIndex;
|
||||
import 'package:kernel/type_environment.dart';
|
||||
|
||||
@@ -43,16 +41,18 @@ const bool kDumpClassHierarchy =
|
||||
/// Assumes strong mode and closed world.
|
||||
Component transformComponent(
|
||||
Target target, CoreTypes coreTypes, Component component,
|
||||
{PragmaAnnotationParser matcher,
|
||||
{PragmaAnnotationParser? matcher,
|
||||
bool treeShakeSignatures: true,
|
||||
bool treeShakeWriteOnlyFields: true,
|
||||
bool treeShakeProtobufs: false}) {
|
||||
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
|
||||
final hierarchy = new ClassHierarchy(component, coreTypes,
|
||||
onAmbiguousSupertypes: ignoreAmbiguousSupertypes);
|
||||
onAmbiguousSupertypes: ignoreAmbiguousSupertypes)
|
||||
as ClosedWorldClassHierarchy;
|
||||
final types = new TypeEnvironment(coreTypes, hierarchy);
|
||||
final libraryIndex = new LibraryIndex.all(component);
|
||||
final genericInterfacesInfo = new GenericInterfacesInfoImpl(hierarchy);
|
||||
final genericInterfacesInfo =
|
||||
new GenericInterfacesInfoImpl(coreTypes, hierarchy);
|
||||
final protobufHandler = treeShakeProtobufs
|
||||
? ProtobufHandler.forComponent(component, coreTypes)
|
||||
: null;
|
||||
@@ -73,7 +73,7 @@ Component transformComponent(
|
||||
protobufHandler,
|
||||
matcher);
|
||||
|
||||
Procedure main = component.mainMethod;
|
||||
Procedure? main = component.mainMethod;
|
||||
|
||||
// `main` can be null, roots can also come from @pragma("vm:entry-point").
|
||||
if (main != null) {
|
||||
@@ -149,7 +149,7 @@ class MoveFieldInitializers {
|
||||
if (!f.isStatic &&
|
||||
!f.isLate &&
|
||||
f.initializer != null &&
|
||||
mayHaveSideEffects(f.initializer))
|
||||
mayHaveSideEffects(f.initializer!))
|
||||
f
|
||||
];
|
||||
if (fields.isEmpty) return;
|
||||
@@ -173,7 +173,7 @@ class MoveFieldInitializers {
|
||||
};
|
||||
final List<Initializer> newInitializers = [];
|
||||
for (Field f in fields) {
|
||||
Expression initExpr = f.initializer;
|
||||
Expression initExpr = f.initializer!;
|
||||
if (!isFirst) {
|
||||
initExpr = CloneVisitorNotMembers().clone(initExpr);
|
||||
}
|
||||
@@ -204,7 +204,7 @@ class MoveFieldInitializers {
|
||||
class CleanupAnnotations extends RecursiveVisitor {
|
||||
final Class externalNameClass;
|
||||
final Class pragmaClass;
|
||||
final ProtobufHandler protobufHandler;
|
||||
final ProtobufHandler? protobufHandler;
|
||||
|
||||
CleanupAnnotations(
|
||||
CoreTypes coreTypes, LibraryIndex index, this.protobufHandler)
|
||||
@@ -238,7 +238,7 @@ class CleanupAnnotations extends RecursiveVisitor {
|
||||
return (cls == externalNameClass) ||
|
||||
(cls == pragmaClass) ||
|
||||
(protobufHandler != null &&
|
||||
protobufHandler.usesAnnotationClass(cls));
|
||||
protobufHandler!.usesAnnotationClass(cls));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -255,11 +255,11 @@ class TFADevirtualization extends Devirtualization {
|
||||
: super(_typeFlowAnalysis.environment.coreTypes, component, hierarchy);
|
||||
|
||||
@override
|
||||
DirectCallMetadata getDirectCall(TreeNode node, Member interfaceTarget,
|
||||
DirectCallMetadata? getDirectCall(TreeNode node, Member? interfaceTarget,
|
||||
{bool setter = false}) {
|
||||
final callSite = _typeFlowAnalysis.callSite(node);
|
||||
if (callSite != null) {
|
||||
final Member singleTarget = fieldMorpher
|
||||
final Member? singleTarget = fieldMorpher
|
||||
.getMorphedMember(callSite.monomorphicTarget, isSetter: setter);
|
||||
if (singleTarget != null) {
|
||||
return new DirectCallMetadata(
|
||||
@@ -283,12 +283,13 @@ class AnnotateKernel extends RecursiveVisitor {
|
||||
final UnboxingInfoMetadataRepository _unboxingInfoMetadata;
|
||||
final UnboxingInfoManager _unboxingInfo;
|
||||
final Class _intClass;
|
||||
Constant _nullConstant;
|
||||
late final Constant _nullConstant = NullConstant();
|
||||
|
||||
AnnotateKernel(Component component, this._typeFlowAnalysis, this.fieldMorpher,
|
||||
this._tableSelectorAssigner, this._unboxingInfo)
|
||||
: _directCallMetadataRepository =
|
||||
component.metadata[DirectCallMetadataRepository.repositoryTag],
|
||||
component.metadata[DirectCallMetadataRepository.repositoryTag]
|
||||
as DirectCallMetadataRepository,
|
||||
_inferredTypeMetadata = new InferredTypeMetadataRepository(),
|
||||
_unreachableNodeMetadata = new UnreachableNodeMetadataRepository(),
|
||||
_procedureAttributesMetadata =
|
||||
@@ -308,12 +309,10 @@ class AnnotateKernel extends RecursiveVisitor {
|
||||
return _directCallMetadataRepository.mapping.containsKey(node);
|
||||
}
|
||||
|
||||
InferredType _convertType(Type type,
|
||||
InferredType? _convertType(Type type,
|
||||
{bool skipCheck: false, bool receiverNotInt: false}) {
|
||||
assert(type != null);
|
||||
|
||||
Class concreteClass;
|
||||
Constant constantValue;
|
||||
Class? concreteClass;
|
||||
Constant? constantValue;
|
||||
bool isInt = false;
|
||||
|
||||
final nullable = type is NullableType;
|
||||
@@ -324,7 +323,7 @@ class AnnotateKernel extends RecursiveVisitor {
|
||||
if (nullable && type == const EmptyType()) {
|
||||
concreteClass =
|
||||
_typeFlowAnalysis.environment.coreTypes.deprecatedNullClass;
|
||||
constantValue = _nullConstant ??= new NullConstant();
|
||||
constantValue = _nullConstant;
|
||||
} else {
|
||||
concreteClass = type.getConcreteClass(_typeFlowAnalysis.hierarchyCache);
|
||||
|
||||
@@ -337,9 +336,9 @@ class AnnotateKernel extends RecursiveVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
List<DartType> typeArgs;
|
||||
List<DartType?>? typeArgs;
|
||||
if (type is ConcreteType && type.typeArgs != null) {
|
||||
typeArgs = type.typeArgs
|
||||
typeArgs = type.typeArgs!
|
||||
.take(type.numImmediateTypeArgs)
|
||||
.map((t) =>
|
||||
t is UnknownType ? null : (t as RuntimeType).representedType)
|
||||
@@ -374,7 +373,7 @@ class AnnotateKernel extends RecursiveVisitor {
|
||||
_unreachableNodeMetadata.mapping[node] = const UnreachableNode();
|
||||
}
|
||||
|
||||
void _annotateCallSite(TreeNode node, Member interfaceTarget) {
|
||||
void _annotateCallSite(TreeNode node, Member? interfaceTarget) {
|
||||
final callSite = _typeFlowAnalysis.callSite(node);
|
||||
if (callSite == null) return;
|
||||
if (!callSite.isReachable) {
|
||||
@@ -405,7 +404,7 @@ class AnnotateKernel extends RecursiveVisitor {
|
||||
if (interfaceTarget == null ||
|
||||
_typeFlowAnalysis.hierarchyCache.hierarchy.isSubtypeOf(
|
||||
_typeFlowAnalysis.hierarchyCache.coreTypes.intClass,
|
||||
interfaceTarget.enclosingClass)) {
|
||||
interfaceTarget.enclosingClass!)) {
|
||||
markReceiverNotInt = true;
|
||||
}
|
||||
}
|
||||
@@ -446,35 +445,33 @@ class AnnotateKernel extends RecursiveVisitor {
|
||||
void _annotateMember(Member member) {
|
||||
if (_typeFlowAnalysis.isMemberUsed(member)) {
|
||||
if (member is Field) {
|
||||
_setInferredType(member, _typeFlowAnalysis.fieldType(member));
|
||||
_setInferredType(member, _typeFlowAnalysis.fieldType(member)!);
|
||||
} else {
|
||||
Args<Type> argTypes = _typeFlowAnalysis.argumentTypes(member);
|
||||
Args<Type> argTypes = _typeFlowAnalysis.argumentTypes(member)!;
|
||||
final uncheckedParameters =
|
||||
_typeFlowAnalysis.uncheckedParameters(member);
|
||||
assert(argTypes != null);
|
||||
|
||||
final int firstParamIndex =
|
||||
numTypeParams(member) + (hasReceiverArg(member) ? 1 : 0);
|
||||
|
||||
final positionalParams = member.function.positionalParameters;
|
||||
final positionalParams = member.function!.positionalParameters;
|
||||
assert(argTypes.positionalCount ==
|
||||
firstParamIndex + positionalParams.length);
|
||||
|
||||
for (int i = 0; i < positionalParams.length; i++) {
|
||||
_setInferredType(
|
||||
positionalParams[i], argTypes.values[firstParamIndex + i],
|
||||
skipCheck: uncheckedParameters.contains(positionalParams[i]));
|
||||
skipCheck: uncheckedParameters!.contains(positionalParams[i]));
|
||||
}
|
||||
|
||||
// TODO(dartbug.com/32292): make sure parameters are sorted in kernel
|
||||
// AST and iterate parameters in parallel, without lookup.
|
||||
final names = argTypes.names;
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
final param = findNamedParameter(member.function, names[i]);
|
||||
assert(param != null);
|
||||
final param = findNamedParameter(member.function!, names[i])!;
|
||||
_setInferredType(param,
|
||||
argTypes.values[firstParamIndex + positionalParams.length + i],
|
||||
skipCheck: uncheckedParameters.contains(param));
|
||||
skipCheck: uncheckedParameters!.contains(param));
|
||||
}
|
||||
|
||||
// TODO(alexmarkov): figure out how to pass receiver type.
|
||||
@@ -521,7 +518,7 @@ class AnnotateKernel extends RecursiveVisitor {
|
||||
// interface target, and table dispatch calls need selector IDs for all
|
||||
// interface targets.
|
||||
if (member.isInstanceMember) {
|
||||
final original = fieldMorpher.getOriginalMember(member);
|
||||
final original = fieldMorpher.getOriginalMember(member)!;
|
||||
final attrs = new ProcedureAttributesMetadata(
|
||||
methodOrSetterCalledDynamically:
|
||||
_typeFlowAnalysis.isCalledDynamically(original),
|
||||
@@ -689,11 +686,11 @@ class TreeShaker {
|
||||
final Set<Member> _usedMembers = new Set<Member>();
|
||||
final Set<Extension> _usedExtensions = new Set<Extension>();
|
||||
final Set<Typedef> _usedTypedefs = new Set<Typedef>();
|
||||
FieldMorpher fieldMorpher;
|
||||
_TreeShakerTypeVisitor typeVisitor;
|
||||
_TreeShakerConstantVisitor constantVisitor;
|
||||
_TreeShakerPass1 _pass1;
|
||||
_TreeShakerPass2 _pass2;
|
||||
late final FieldMorpher fieldMorpher;
|
||||
late final _TreeShakerTypeVisitor typeVisitor;
|
||||
late final _TreeShakerConstantVisitor constantVisitor;
|
||||
late final _TreeShakerPass1 _pass1;
|
||||
late final _TreeShakerPass2 _pass2;
|
||||
|
||||
TreeShaker(Component component, this.typeFlowAnalysis,
|
||||
{this.treeShakeWriteOnlyFields: true}) {
|
||||
@@ -734,7 +731,7 @@ class TreeShaker {
|
||||
(!f.isStatic &&
|
||||
f.initializer != null &&
|
||||
isFieldInitializerReachable(f) &&
|
||||
mayHaveSideEffects(f.initializer)) ||
|
||||
mayHaveSideEffects(f.initializer!)) ||
|
||||
(f.isLate && f.isFinal)) ||
|
||||
isMemberReferencedFromNativeCode(f) ||
|
||||
_isInstanceFieldOfAllocatedEnum(f);
|
||||
@@ -745,8 +742,8 @@ class TreeShaker {
|
||||
bool _isInstanceFieldOfAllocatedEnum(Field node) =>
|
||||
!node.isStatic &&
|
||||
node.enclosingClass != null &&
|
||||
node.enclosingClass.isEnum &&
|
||||
isClassAllocated(node.enclosingClass);
|
||||
node.enclosingClass!.isEnum &&
|
||||
isClassAllocated(node.enclosingClass!);
|
||||
|
||||
void addClassUsedInType(Class c) {
|
||||
if (_classesUsedInType.add(c)) {
|
||||
@@ -777,7 +774,7 @@ class TreeShaker {
|
||||
_usedClasses.add(enclosingClass);
|
||||
}
|
||||
|
||||
FunctionNode func = null;
|
||||
FunctionNode? func = null;
|
||||
if (m is Field) {
|
||||
m.type.accept(typeVisitor);
|
||||
} else if (m is Procedure) {
|
||||
@@ -786,19 +783,19 @@ class TreeShaker {
|
||||
m.stubTarget = fieldMorpher.adjustInstanceCallTarget(
|
||||
m.concreteForwardingStubTarget,
|
||||
isSetter: m.isSetter);
|
||||
addUsedMember(m.concreteForwardingStubTarget);
|
||||
addUsedMember(m.concreteForwardingStubTarget!);
|
||||
}
|
||||
if (m.abstractForwardingStubTarget != null) {
|
||||
m.stubTarget = fieldMorpher.adjustInstanceCallTarget(
|
||||
m.abstractForwardingStubTarget,
|
||||
isSetter: m.isSetter);
|
||||
addUsedMember(m.abstractForwardingStubTarget);
|
||||
addUsedMember(m.abstractForwardingStubTarget!);
|
||||
}
|
||||
if (m.memberSignatureOrigin != null) {
|
||||
m.stubTarget = fieldMorpher.adjustInstanceCallTarget(
|
||||
m.memberSignatureOrigin,
|
||||
isSetter: m.isSetter);
|
||||
addUsedMember(m.memberSignatureOrigin);
|
||||
addUsedMember(m.memberSignatureOrigin!);
|
||||
}
|
||||
} else if (m is Constructor) {
|
||||
func = m.function;
|
||||
@@ -821,8 +818,7 @@ class TreeShaker {
|
||||
final extension = m.enclosingLibrary.extensions.firstWhere((extension) {
|
||||
return extension.members
|
||||
.any((descriptor) => descriptor.member.asMember == m);
|
||||
}, orElse: () => null);
|
||||
assert(extension != null);
|
||||
});
|
||||
|
||||
// Ensure we retain the [Extension] itself (though members might be
|
||||
// shaken)
|
||||
@@ -844,7 +840,7 @@ class TreeShaker {
|
||||
if (_usedExtensions.add(node)) {
|
||||
node.annotations = const <Expression>[];
|
||||
_pass1.transformTypeParameterList(node.typeParameters, node);
|
||||
node.onType?.accept(typeVisitor);
|
||||
node.onType.accept(typeVisitor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -898,7 +894,7 @@ class FieldMorpher {
|
||||
isAbstract: true, fileUri: field.fileUri);
|
||||
}
|
||||
accessor.fileOffset = field.fileOffset;
|
||||
field.enclosingClass.addProcedure(accessor);
|
||||
field.enclosingClass!.addProcedure(accessor);
|
||||
_removedFields[accessor] = field;
|
||||
shaker.addUsedMember(accessor);
|
||||
return accessor;
|
||||
@@ -908,7 +904,7 @@ class FieldMorpher {
|
||||
/// If necessary, creates a getter or setter as a replacement if target is a
|
||||
/// field which is going to be removed by the tree shaker.
|
||||
/// This method is used during tree shaker pass 1.
|
||||
Member adjustInstanceCallTarget(Member target, {bool isSetter = false}) {
|
||||
Member? adjustInstanceCallTarget(Member? target, {bool isSetter = false}) {
|
||||
if (target is Field && !shaker.retainField(target)) {
|
||||
final targets =
|
||||
isSetter ? _settersForRemovedFields : _gettersForRemovedFields;
|
||||
@@ -923,9 +919,9 @@ class FieldMorpher {
|
||||
|
||||
/// Return a member which replaced [target] in instance calls.
|
||||
/// This method can be used after tree shaking to discover replacement.
|
||||
Member getMorphedMember(Member target, {bool isSetter = false}) {
|
||||
if (target == null) {
|
||||
return null;
|
||||
Member? getMorphedMember(Member? target, {bool isSetter = false}) {
|
||||
if (target is! Field) {
|
||||
return target;
|
||||
}
|
||||
final targets =
|
||||
isSetter ? _settersForRemovedFields : _gettersForRemovedFields;
|
||||
@@ -934,7 +930,7 @@ class FieldMorpher {
|
||||
|
||||
/// Return original member which was replaced by [target] in instance calls.
|
||||
/// This method can be used after tree shaking.
|
||||
Member getOriginalMember(Member target) {
|
||||
Member? getOriginalMember(Member? target) {
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -994,16 +990,15 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
final TreeShaker shaker;
|
||||
final FieldMorpher fieldMorpher;
|
||||
final TypeEnvironment environment;
|
||||
Procedure _unsafeCast;
|
||||
|
||||
StaticTypeContext _staticTypeContext;
|
||||
Member _currentMember;
|
||||
StaticTypeContext? _staticTypeContext;
|
||||
Member? _currentMember;
|
||||
|
||||
StaticTypeContext get staticTypeContext =>
|
||||
_staticTypeContext ??= StaticTypeContext(currentMember, environment);
|
||||
|
||||
Member get currentMember => _currentMember;
|
||||
set currentMember(Member m) {
|
||||
Member get currentMember => _currentMember!;
|
||||
set currentMember(Member? m) {
|
||||
_currentMember = m;
|
||||
_staticTypeContext = null;
|
||||
}
|
||||
@@ -1022,7 +1017,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
List<Expression> _flattenArguments(Arguments arguments,
|
||||
{Expression receiver}) {
|
||||
{Expression? receiver}) {
|
||||
final args = <Expression>[];
|
||||
if (receiver != null) {
|
||||
args.add(receiver);
|
||||
@@ -1035,9 +1030,9 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
bool _isThrowExpression(Expression expr) {
|
||||
for (;;) {
|
||||
if (expr is Let) {
|
||||
expr = (expr as Let).body;
|
||||
expr = expr.body;
|
||||
} else if (expr is BlockExpression) {
|
||||
expr = (expr as BlockExpression).value;
|
||||
expr = expr.value;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -1068,7 +1063,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
return BlockExpression(Block(statements), value);
|
||||
}
|
||||
|
||||
TreeNode _makeUnreachableCall(List<Expression> args) {
|
||||
Expression _makeUnreachableCall(List<Expression> args) {
|
||||
Expression node;
|
||||
final int last = args.indexWhere(_isThrowExpression);
|
||||
if (last >= 0) {
|
||||
@@ -1090,12 +1085,12 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
new VariableDeclaration(null, initializer: _makeUnreachableCall(args)));
|
||||
}
|
||||
|
||||
NarrowNotNull _getNullTest(TreeNode node) =>
|
||||
NarrowNotNull? _getNullTest(TreeNode node) =>
|
||||
shaker.typeFlowAnalysis.nullTest(node);
|
||||
|
||||
TreeNode _visitAssertNode(TreeNode node, TreeNode removalSentinel) {
|
||||
TreeNode _visitAssertNode(TreeNode node, TreeNode? removalSentinel) {
|
||||
if (kRemoveAsserts) {
|
||||
return removalSentinel;
|
||||
return removalSentinel!;
|
||||
} else {
|
||||
node.transformOrRemoveChildren(this);
|
||||
return node;
|
||||
@@ -1103,31 +1098,31 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
DartType visitDartType(DartType node, DartType removalSentinel) {
|
||||
DartType visitDartType(DartType node, DartType? removalSentinel) {
|
||||
node.accept(shaker.typeVisitor);
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
Supertype visitSupertype(Supertype node, Supertype removalSentinel) {
|
||||
Supertype visitSupertype(Supertype node, Supertype? removalSentinel) {
|
||||
node.accept(shaker.typeVisitor);
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitTypedef(Typedef node, TreeNode removalSentinel) {
|
||||
TreeNode visitTypedef(Typedef node, TreeNode? removalSentinel) {
|
||||
return node; // Do not go deeper.
|
||||
}
|
||||
|
||||
@override
|
||||
Extension visitExtension(Extension node, TreeNode removalSentinel) {
|
||||
TreeNode visitExtension(Extension node, TreeNode? removalSentinel) {
|
||||
// The extension can be considered a weak node, we'll only retain it if
|
||||
// normal code references any of it's members.
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitClass(Class node, TreeNode removalSentinel) {
|
||||
TreeNode visitClass(Class node, TreeNode? removalSentinel) {
|
||||
if (shaker.isClassAllocated(node) ||
|
||||
shaker.isClassReferencedFromNativeCode(node)) {
|
||||
shaker.addClassUsedInType(node);
|
||||
@@ -1140,7 +1135,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode defaultMember(Member node, TreeNode removalSentinel) {
|
||||
TreeNode defaultMember(Member node, TreeNode? removalSentinel) {
|
||||
currentMember = node;
|
||||
if (shaker.isMemberBodyReachable(node)) {
|
||||
if (kPrintTrace) {
|
||||
@@ -1160,7 +1155,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitField(Field node, TreeNode removalSentinel) {
|
||||
TreeNode visitField(Field node, TreeNode? removalSentinel) {
|
||||
currentMember = node;
|
||||
if (shaker.retainField(node)) {
|
||||
if (kPrintTrace) {
|
||||
@@ -1185,21 +1180,21 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitInstanceInvocation(
|
||||
InstanceInvocation node, TreeNode removalSentinel) {
|
||||
InstanceInvocation node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall(
|
||||
_flattenArguments(node.arguments, receiver: node.receiver));
|
||||
}
|
||||
node.interfaceTarget =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
|
||||
node.interfaceTarget = fieldMorpher
|
||||
.adjustInstanceCallTarget(node.interfaceTarget) as Procedure;
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitDynamicInvocation(
|
||||
DynamicInvocation node, TreeNode removalSentinel) {
|
||||
DynamicInvocation node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall(
|
||||
@@ -1210,7 +1205,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitLocalFunctionInvocation(
|
||||
LocalFunctionInvocation node, TreeNode removalSentinel) {
|
||||
LocalFunctionInvocation node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall(_flattenArguments(node.arguments));
|
||||
@@ -1220,7 +1215,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitFunctionInvocation(
|
||||
FunctionInvocation node, TreeNode removalSentinel) {
|
||||
FunctionInvocation node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall(
|
||||
@@ -1230,24 +1225,24 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitEqualsCall(EqualsCall node, TreeNode removalSentinel) {
|
||||
TreeNode visitEqualsCall(EqualsCall node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.left, node.right]);
|
||||
}
|
||||
node.interfaceTarget =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
|
||||
node.interfaceTarget = fieldMorpher
|
||||
.adjustInstanceCallTarget(node.interfaceTarget) as Procedure;
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitEqualsNull(EqualsNull node, TreeNode removalSentinel) {
|
||||
TreeNode visitEqualsNull(EqualsNull node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.expression]);
|
||||
}
|
||||
final nullTest = _getNullTest(node);
|
||||
final nullTest = _getNullTest(node)!;
|
||||
if (nullTest.isAlwaysNull || nullTest.isAlwaysNotNull) {
|
||||
return _evaluateArguments([node.expression],
|
||||
BoolLiteral(nullTest.isAlwaysNull)..fileOffset = node.fileOffset);
|
||||
@@ -1256,13 +1251,13 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitInstanceGet(InstanceGet node, TreeNode removalSentinel) {
|
||||
TreeNode visitInstanceGet(InstanceGet node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.receiver]);
|
||||
} else {
|
||||
node.interfaceTarget =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget)!;
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
return node;
|
||||
}
|
||||
@@ -1270,20 +1265,20 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitInstanceTearOff(
|
||||
InstanceTearOff node, TreeNode removalSentinel) {
|
||||
InstanceTearOff node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.receiver]);
|
||||
} else {
|
||||
node.interfaceTarget =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
|
||||
node.interfaceTarget = fieldMorpher
|
||||
.adjustInstanceCallTarget(node.interfaceTarget) as Procedure;
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitDynamicGet(DynamicGet node, TreeNode removalSentinel) {
|
||||
TreeNode visitDynamicGet(DynamicGet node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.receiver]);
|
||||
@@ -1294,7 +1289,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitFunctionTearOff(
|
||||
FunctionTearOff node, TreeNode removalSentinel) {
|
||||
FunctionTearOff node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.receiver]);
|
||||
@@ -1304,20 +1299,20 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitInstanceSet(InstanceSet node, TreeNode removalSentinel) {
|
||||
TreeNode visitInstanceSet(InstanceSet node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.receiver, node.value]);
|
||||
} else {
|
||||
node.interfaceTarget = fieldMorpher
|
||||
.adjustInstanceCallTarget(node.interfaceTarget, isSetter: true);
|
||||
.adjustInstanceCallTarget(node.interfaceTarget, isSetter: true)!;
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitDynamicSet(DynamicSet node, TreeNode removalSentinel) {
|
||||
TreeNode visitDynamicSet(DynamicSet node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.receiver, node.value]);
|
||||
@@ -1328,15 +1323,15 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitSuperMethodInvocation(
|
||||
SuperMethodInvocation node, TreeNode removalSentinel) {
|
||||
SuperMethodInvocation node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall(_flattenArguments(node.arguments));
|
||||
} else {
|
||||
node.interfaceTarget =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
|
||||
node.interfaceTarget = fieldMorpher
|
||||
.adjustInstanceCallTarget(node.interfaceTarget) as Procedure?;
|
||||
if (node.interfaceTarget != null) {
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
shaker.addUsedMember(node.interfaceTarget!);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -1344,7 +1339,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitSuperPropertyGet(
|
||||
SuperPropertyGet node, TreeNode removalSentinel) {
|
||||
SuperPropertyGet node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([]);
|
||||
@@ -1352,7 +1347,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
node.interfaceTarget =
|
||||
fieldMorpher.adjustInstanceCallTarget(node.interfaceTarget);
|
||||
if (node.interfaceTarget != null) {
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
shaker.addUsedMember(node.interfaceTarget!);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -1360,7 +1355,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitSuperPropertySet(
|
||||
SuperPropertySet node, TreeNode removalSentinel) {
|
||||
SuperPropertySet node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.value]);
|
||||
@@ -1368,7 +1363,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
node.interfaceTarget = fieldMorpher
|
||||
.adjustInstanceCallTarget(node.interfaceTarget, isSetter: true);
|
||||
if (node.interfaceTarget != null) {
|
||||
shaker.addUsedMember(node.interfaceTarget);
|
||||
shaker.addUsedMember(node.interfaceTarget!);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -1376,7 +1371,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitStaticInvocation(
|
||||
StaticInvocation node, TreeNode removalSentinel) {
|
||||
StaticInvocation node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall(_flattenArguments(node.arguments));
|
||||
@@ -1390,7 +1385,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitStaticGet(StaticGet node, TreeNode removalSentinel) {
|
||||
TreeNode visitStaticGet(StaticGet node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([]);
|
||||
@@ -1405,13 +1400,13 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
Constant visitConstant(Constant node, Constant removalSentinel) {
|
||||
Constant visitConstant(Constant node, Constant? removalSentinel) {
|
||||
shaker.constantVisitor.analyzeConstant(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitStaticSet(StaticSet node, TreeNode removalSentinel) {
|
||||
TreeNode visitStaticSet(StaticSet node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall([node.value]);
|
||||
@@ -1428,7 +1423,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitConstructorInvocation(
|
||||
ConstructorInvocation node, TreeNode removalSentinel) {
|
||||
ConstructorInvocation node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableCall(_flattenArguments(node.arguments));
|
||||
@@ -1444,7 +1439,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitRedirectingInitializer(
|
||||
RedirectingInitializer node, TreeNode removalSentinel) {
|
||||
RedirectingInitializer node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableInitializer(_flattenArguments(node.arguments));
|
||||
@@ -1457,7 +1452,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitSuperInitializer(
|
||||
SuperInitializer node, TreeNode removalSentinel) {
|
||||
SuperInitializer node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableInitializer(_flattenArguments(node.arguments));
|
||||
@@ -1469,7 +1464,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitFieldInitializer(
|
||||
FieldInitializer node, TreeNode removalSentinel) {
|
||||
FieldInitializer node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
if (_isUnreachable(node)) {
|
||||
return _makeUnreachableInitializer([node.value]);
|
||||
@@ -1482,7 +1477,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
return LocalInitializer(
|
||||
VariableDeclaration(null, initializer: node.value));
|
||||
} else {
|
||||
return removalSentinel;
|
||||
return removalSentinel!;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
@@ -1491,18 +1486,18 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitAssertStatement(
|
||||
AssertStatement node, TreeNode removalSentinel) {
|
||||
AssertStatement node, TreeNode? removalSentinel) {
|
||||
return _visitAssertNode(node, removalSentinel);
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitAssertBlock(AssertBlock node, TreeNode removalSentinel) {
|
||||
TreeNode visitAssertBlock(AssertBlock node, TreeNode? removalSentinel) {
|
||||
return _visitAssertNode(node, removalSentinel);
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitAssertInitializer(
|
||||
AssertInitializer node, TreeNode removalSentinel) {
|
||||
AssertInitializer node, TreeNode? removalSentinel) {
|
||||
return _visitAssertNode(node, removalSentinel);
|
||||
}
|
||||
|
||||
@@ -1520,21 +1515,21 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
// Returns Block corresponding to the given extended bool literal,
|
||||
// or null if the expression is a simple bool literal.
|
||||
Block _getExtendedBoolLiteralBlock(Expression expr) =>
|
||||
Block? _getExtendedBoolLiteralBlock(Expression expr) =>
|
||||
(expr is BoolLiteral) ? null : (expr as BlockExpression).body;
|
||||
|
||||
@override
|
||||
TreeNode visitIfStatement(IfStatement node, TreeNode removalSentinel) {
|
||||
TreeNode visitIfStatement(IfStatement node, TreeNode? removalSentinel) {
|
||||
final condition = transform(node.condition);
|
||||
if (_isExtendedBoolLiteral(condition)) {
|
||||
final bool conditionValue = _getExtendedBoolLiteralValue(condition);
|
||||
final Block conditionBlock = _getExtendedBoolLiteralBlock(condition);
|
||||
ast.Statement body;
|
||||
final Block? conditionBlock = _getExtendedBoolLiteralBlock(condition);
|
||||
ast.Statement? body;
|
||||
if (conditionValue) {
|
||||
body = transform(node.then);
|
||||
} else {
|
||||
if (node.otherwise != null) {
|
||||
body = transformOrRemoveStatement(node.otherwise);
|
||||
body = transformOrRemoveStatement(node.otherwise!);
|
||||
}
|
||||
}
|
||||
if (conditionBlock != null) {
|
||||
@@ -1549,7 +1544,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
node.condition = condition..parent = node;
|
||||
node.then = transform(node.then)..parent = node;
|
||||
if (node.otherwise != null) {
|
||||
node.otherwise = transformOrRemoveStatement(node.otherwise);
|
||||
node.otherwise = transformOrRemoveStatement(node.otherwise!);
|
||||
node.otherwise?.parent = node;
|
||||
}
|
||||
return node;
|
||||
@@ -1557,7 +1552,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
visitConditionalExpression(
|
||||
ConditionalExpression node, TreeNode removalSentinel) {
|
||||
ConditionalExpression node, TreeNode? removalSentinel) {
|
||||
final condition = transform(node.condition);
|
||||
if (_isExtendedBoolLiteral(condition)) {
|
||||
final bool value = _getExtendedBoolLiteralValue(condition);
|
||||
@@ -1578,7 +1573,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitNot(Not node, TreeNode removalSentinel) {
|
||||
TreeNode visitNot(Not node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
final operand = node.operand;
|
||||
if (_isExtendedBoolLiteral(operand)) {
|
||||
@@ -1595,7 +1590,7 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
|
||||
@override
|
||||
TreeNode visitLogicalExpression(
|
||||
LogicalExpression node, TreeNode removalSentinel) {
|
||||
LogicalExpression node, TreeNode? removalSentinel) {
|
||||
final left = transform(node.left);
|
||||
final operatorEnum = node.operatorEnum;
|
||||
if (_isExtendedBoolLiteral(left)) {
|
||||
@@ -1621,8 +1616,8 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitIsExpression(IsExpression node, TreeNode removalSentinel) {
|
||||
TypeCheck check = shaker.typeFlowAnalysis.isTest(node);
|
||||
TreeNode visitIsExpression(IsExpression node, TreeNode? removalSentinel) {
|
||||
TypeCheck? check = shaker.typeFlowAnalysis.isTest(node);
|
||||
if (check != null && (check.alwaysFail || check.alwaysPass)) {
|
||||
final operand = transform(node.operand);
|
||||
final result = BoolLiteral(!check.alwaysFail)
|
||||
@@ -1634,9 +1629,9 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitAsExpression(AsExpression node, TreeNode removalSentinel) {
|
||||
TreeNode visitAsExpression(AsExpression node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
TypeCheck check = shaker.typeFlowAnalysis.explicitCast(node);
|
||||
TypeCheck? check = shaker.typeFlowAnalysis.explicitCast(node);
|
||||
if (check != null && check.alwaysPass) {
|
||||
return StaticInvocation(
|
||||
unsafeCast, Arguments([node.operand], types: [node.type]))
|
||||
@@ -1646,9 +1641,9 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitNullCheck(NullCheck node, TreeNode removalSentinel) {
|
||||
TreeNode visitNullCheck(NullCheck node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
final nullTest = _getNullTest(node);
|
||||
final nullTest = _getNullTest(node)!;
|
||||
if (nullTest.isAlwaysNotNull) {
|
||||
return StaticInvocation(
|
||||
unsafeCast,
|
||||
@@ -1659,12 +1654,9 @@ class _TreeShakerPass1 extends RemovingTransformer {
|
||||
return node;
|
||||
}
|
||||
|
||||
Procedure get unsafeCast {
|
||||
_unsafeCast ??= shaker.typeFlowAnalysis.environment.coreTypes.index
|
||||
.getTopLevelMember('dart:_internal', 'unsafeCast');
|
||||
assert(_unsafeCast != null);
|
||||
return _unsafeCast;
|
||||
}
|
||||
late final Procedure unsafeCast = shaker
|
||||
.typeFlowAnalysis.environment.coreTypes.index
|
||||
.getTopLevelProcedure('dart:_internal', 'unsafeCast');
|
||||
}
|
||||
|
||||
/// The second pass of [TreeShaker]. It is called after set of used
|
||||
@@ -1680,7 +1672,7 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
void transformComponent(Component component) {
|
||||
component.transformOrRemoveChildren(this);
|
||||
for (Source source in component.uriToSource.values) {
|
||||
source?.constantCoverageConstructors?.removeWhere((Reference reference) {
|
||||
source.constantCoverageConstructors?.removeWhere((Reference reference) {
|
||||
Member node = reference.asMember;
|
||||
return !shaker.isMemberUsed(node);
|
||||
});
|
||||
@@ -1688,7 +1680,7 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode visitLibrary(Library node, TreeNode removalSentinel) {
|
||||
TreeNode visitLibrary(Library node, TreeNode? removalSentinel) {
|
||||
node.transformOrRemoveChildren(this);
|
||||
// The transformer API does not iterate over `Library.additionalExports`,
|
||||
// so we manually delete the references to shaken nodes.
|
||||
@@ -1708,12 +1700,12 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
Typedef visitTypedef(Typedef node, TreeNode removalSentinel) {
|
||||
return shaker.isTypedefUsed(node) ? node : removalSentinel;
|
||||
TreeNode visitTypedef(Typedef node, TreeNode? removalSentinel) {
|
||||
return shaker.isTypedefUsed(node) ? node : removalSentinel!;
|
||||
}
|
||||
|
||||
@override
|
||||
Class visitClass(Class node, TreeNode removalSentinel) {
|
||||
TreeNode visitClass(Class node, TreeNode? removalSentinel) {
|
||||
if (!shaker.isClassUsed(node)) {
|
||||
debugPrint('Dropped class ${node.name}');
|
||||
// Ensure that kernel file writer will not be able to
|
||||
@@ -1724,7 +1716,7 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
"been repurposed for ${node.reference.node}.");
|
||||
node.reference.canonicalName?.unbind();
|
||||
Statistics.classesDropped++;
|
||||
return removalSentinel; // Remove the class.
|
||||
return removalSentinel!; // Remove the class.
|
||||
}
|
||||
|
||||
if (!shaker.isClassUsedInType(node)) {
|
||||
@@ -1755,7 +1747,7 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
Member defaultMember(Member node, TreeNode removalSentinel) {
|
||||
TreeNode defaultMember(Member node, TreeNode? removalSentinel) {
|
||||
if (!shaker.isMemberUsed(node)) {
|
||||
// Ensure that kernel file writer will not be able to
|
||||
// write a dangling reference to the deleted member.
|
||||
@@ -1767,10 +1759,10 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
node.getterReference.canonicalName?.unbind();
|
||||
if (node.hasSetter) {
|
||||
assert(
|
||||
node.setterReference.node == node,
|
||||
node.setterReference!.node == node,
|
||||
"Trying to remove canonical name from reference on $node which "
|
||||
"has been repurposed for ${node.setterReference.node}.");
|
||||
node.setterReference.canonicalName?.unbind();
|
||||
"has been repurposed for ${node.setterReference!.node}.");
|
||||
node.setterReference!.canonicalName?.unbind();
|
||||
}
|
||||
} else {
|
||||
assert(
|
||||
@@ -1780,13 +1772,13 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
node.reference.canonicalName?.unbind();
|
||||
}
|
||||
Statistics.membersDropped++;
|
||||
return removalSentinel;
|
||||
return removalSentinel!;
|
||||
}
|
||||
|
||||
if (!shaker.isMemberBodyReachable(node)) {
|
||||
if (node is Procedure) {
|
||||
// Remove body of unused member.
|
||||
if (!node.isStatic && node.enclosingClass.isAbstract) {
|
||||
if (!node.isStatic && node.enclosingClass!.isAbstract) {
|
||||
node.isAbstract = true;
|
||||
node.function.body = null;
|
||||
} else {
|
||||
@@ -1829,7 +1821,7 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
Extension visitExtension(Extension node, TreeNode removalSentinel) {
|
||||
TreeNode visitExtension(Extension node, TreeNode? removalSentinel) {
|
||||
if (shaker.isExtensionUsed(node)) {
|
||||
int writeIndex = 0;
|
||||
for (int i = 0; i < node.members.length; ++i) {
|
||||
@@ -1840,7 +1832,7 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
// member was already removed or it will be removed later.
|
||||
final Reference memberReference = descriptor.member;
|
||||
final bool isBound = memberReference.node != null;
|
||||
if (isBound && shaker.isMemberUsed(memberReference.node)) {
|
||||
if (isBound && shaker.isMemberUsed(memberReference.asMember)) {
|
||||
node.members[writeIndex++] = descriptor;
|
||||
}
|
||||
}
|
||||
@@ -1850,7 +1842,7 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
assert(node.members.length > 0);
|
||||
return node;
|
||||
}
|
||||
return removalSentinel;
|
||||
return removalSentinel!;
|
||||
}
|
||||
|
||||
void _makeUnreachableBody(FunctionNode function) {
|
||||
@@ -1871,7 +1863,7 @@ class _TreeShakerPass2 extends RemovingTransformer {
|
||||
}
|
||||
|
||||
@override
|
||||
TreeNode defaultTreeNode(TreeNode node, TreeNode removalSentinel) {
|
||||
TreeNode defaultTreeNode(TreeNode node, TreeNode? removalSentinel) {
|
||||
return node; // Do not traverse into other nodes.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
/// Declares the type system used by global type flow analysis.
|
||||
library vm.transformations.type_flow.types;
|
||||
|
||||
import 'dart:core' hide Type;
|
||||
|
||||
@@ -138,10 +135,7 @@ abstract class TypeHierarchy extends TypesBuilder
|
||||
/// values can flow through the program.
|
||||
Type specializeTypeCone(TFClass base, {bool allowWideCone = false});
|
||||
|
||||
Type _cachedIntType;
|
||||
Type get intType {
|
||||
return _cachedIntType ??= fromStaticType(coreTypes.intLegacyRawType, true);
|
||||
}
|
||||
late final Type intType = fromStaticType(coreTypes.intLegacyRawType, true);
|
||||
}
|
||||
|
||||
/// Base class for type expressions.
|
||||
@@ -151,7 +145,7 @@ abstract class TypeExpr {
|
||||
|
||||
/// Returns computed type of this type expression.
|
||||
/// [types] is the list of types computed for the statements in the summary.
|
||||
Type getComputedType(List<Type> types);
|
||||
Type getComputedType(List<Type?> types);
|
||||
}
|
||||
|
||||
/// Kind of a subtype test: subtype/cast/'as' test or instance check/'is' test.
|
||||
@@ -174,7 +168,7 @@ abstract class Type extends TypeExpr {
|
||||
/// Create a type representing arbitrary nullable object (`dynamic`).
|
||||
factory Type.nullableAny() => new NullableType(const AnyType());
|
||||
|
||||
Class getConcreteClass(TypeHierarchy typeHierarchy) => null;
|
||||
Class? getConcreteClass(TypeHierarchy typeHierarchy) => null;
|
||||
|
||||
bool isSubtypeOf(TypeHierarchy typeHierarchy, Class cls) => false;
|
||||
|
||||
@@ -185,7 +179,7 @@ abstract class Type extends TypeExpr {
|
||||
RuntimeType runtimeType, SubtypeTestKind kind);
|
||||
|
||||
@override
|
||||
Type getComputedType(List<Type> types) => this;
|
||||
Type getComputedType(List<Type?> types) => this;
|
||||
|
||||
/// Order of precedence for evaluation of union/intersection.
|
||||
int get order;
|
||||
@@ -260,7 +254,6 @@ class NullableType extends Type {
|
||||
final Type baseType;
|
||||
|
||||
NullableType(this.baseType) {
|
||||
assert(baseType != null);
|
||||
assert(baseType is! NullableType);
|
||||
}
|
||||
|
||||
@@ -387,7 +380,9 @@ class AnyType extends Type {
|
||||
class SetType extends Type {
|
||||
/// List of concrete types, sorted by classId.
|
||||
final List<ConcreteType> types;
|
||||
int _hashCode;
|
||||
|
||||
@override
|
||||
late final int hashCode = _computeHashCode();
|
||||
|
||||
/// Creates a new SetType using list of concrete types sorted by classId.
|
||||
SetType(this.types) {
|
||||
@@ -395,9 +390,6 @@ class SetType extends Type {
|
||||
assert(isSorted(types));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => _hashCode ??= _computeHashCode();
|
||||
|
||||
int _computeHashCode() {
|
||||
int hash = 1237;
|
||||
for (var t in types) {
|
||||
@@ -470,8 +462,8 @@ class SetType extends Type {
|
||||
return types;
|
||||
}
|
||||
|
||||
static List<ConcreteType> _intersectLists(
|
||||
List<ConcreteType> types1, List<ConcreteType> types2) {
|
||||
static List<ConcreteType> _intersectLists(List<ConcreteType> types1,
|
||||
List<ConcreteType> types2, TypeHierarchy typeHierarchy) {
|
||||
int i1 = 0;
|
||||
int i2 = 0;
|
||||
List<ConcreteType> types = <ConcreteType>[];
|
||||
@@ -491,9 +483,9 @@ class SetType extends Type {
|
||||
t2.constant == null) {
|
||||
types.add(t1);
|
||||
} else {
|
||||
final intersect = t1.intersection(t2, null);
|
||||
final intersect = t1.intersection(t2, typeHierarchy);
|
||||
if (intersect is! EmptyType) {
|
||||
types.add(intersect);
|
||||
types.add(intersect as ConcreteType);
|
||||
}
|
||||
}
|
||||
++i1;
|
||||
@@ -558,7 +550,8 @@ class SetType extends Type {
|
||||
return other.intersection(this, typeHierarchy);
|
||||
}
|
||||
if (other is SetType) {
|
||||
List<ConcreteType> list = _intersectLists(types, other.types);
|
||||
List<ConcreteType> list =
|
||||
_intersectLists(types, other.types, typeHierarchy);
|
||||
final size = list.length;
|
||||
if (size == 0) {
|
||||
return const EmptyType();
|
||||
@@ -594,7 +587,7 @@ class ConeType extends Type {
|
||||
ConeType(this.cls);
|
||||
|
||||
@override
|
||||
Class getConcreteClass(TypeHierarchy typeHierarchy) => typeHierarchy
|
||||
Class? getConcreteClass(TypeHierarchy typeHierarchy) => typeHierarchy
|
||||
.specializeTypeCone(cls, allowWideCone: true)
|
||||
.getConcreteClass(typeHierarchy);
|
||||
|
||||
@@ -696,7 +689,7 @@ class WideConeType extends ConeType {
|
||||
WideConeType(TFClass cls) : super(cls);
|
||||
|
||||
@override
|
||||
Class getConcreteClass(TypeHierarchy typeHierarchy) => null;
|
||||
Class? getConcreteClass(TypeHierarchy typeHierarchy) => null;
|
||||
|
||||
@override
|
||||
int get hashCode => (cls.id + 41) & kHashMask;
|
||||
@@ -783,7 +776,9 @@ class WideConeType extends ConeType {
|
||||
/// or `null` object).
|
||||
class ConcreteType extends Type implements Comparable<ConcreteType> {
|
||||
final TFClass cls;
|
||||
int _hashCode;
|
||||
|
||||
@override
|
||||
late final int hashCode = _computeHashCode();
|
||||
|
||||
// May be null if there are no type arguments constraints. The type arguments
|
||||
// should represent type sets, i.e. `UnknownType` or `RuntimeType`. The type
|
||||
@@ -796,19 +791,19 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
||||
// 'numImmediateTypeArgs' is the length of the prefix of 'typeArgs' which
|
||||
// holds the type arguments to the class itself.
|
||||
final int numImmediateTypeArgs;
|
||||
final List<Type> typeArgs;
|
||||
final List<Type>? typeArgs;
|
||||
|
||||
// May be null if constant value is not inferred.
|
||||
final Constant constant;
|
||||
final Constant? constant;
|
||||
|
||||
ConcreteType(this.cls, [List<Type> typeArgs_, this.constant])
|
||||
ConcreteType(this.cls, [List<Type>? typeArgs_, this.constant])
|
||||
: typeArgs = typeArgs_,
|
||||
numImmediateTypeArgs =
|
||||
typeArgs_ != null ? cls.classNode.typeParameters.length : 0 {
|
||||
// TODO(alexmarkov): support closures
|
||||
assert(!cls.classNode.isAbstract);
|
||||
assert(typeArgs == null || cls.classNode.typeParameters.isNotEmpty);
|
||||
assert(typeArgs == null || typeArgs.any((t) => t is RuntimeType));
|
||||
assert(typeArgs == null || typeArgs!.any((t) => t is RuntimeType));
|
||||
}
|
||||
|
||||
ConcreteType get raw => cls.concreteType;
|
||||
@@ -839,7 +834,7 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
||||
|
||||
if (rhs.typeArguments.isEmpty) return true;
|
||||
|
||||
List<Type> usableTypeArgs = typeArgs;
|
||||
List<Type>? usableTypeArgs = typeArgs;
|
||||
if (usableTypeArgs == null) {
|
||||
if (cls.classNode.typeParameters.isEmpty) {
|
||||
usableTypeArgs =
|
||||
@@ -862,7 +857,7 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
||||
}
|
||||
assert(ta is RuntimeType);
|
||||
if (!ta.isSubtypeOfRuntimeType(
|
||||
typeHierarchy, runtimeType.typeArgs[i], SubtypeTestKind.Subtype)) {
|
||||
typeHierarchy, runtimeType.typeArgs![i], SubtypeTestKind.Subtype)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -877,29 +872,26 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
||||
} else {
|
||||
final interfaceOffset = typeHierarchy.genericInterfaceOffsetFor(
|
||||
cls.classNode, typeHierarchy.coreTypes.futureClass);
|
||||
typeArg = typeArgs[interfaceOffset];
|
||||
typeArg = typeArgs![interfaceOffset];
|
||||
}
|
||||
final RuntimeType lhs =
|
||||
typeArg is RuntimeType ? typeArg : RuntimeType(DynamicType(), null);
|
||||
return lhs.isSubtypeOfRuntimeType(
|
||||
typeHierarchy, runtimeType.typeArgs[0], SubtypeTestKind.Subtype);
|
||||
typeHierarchy, runtimeType.typeArgs![0], SubtypeTestKind.Subtype);
|
||||
} else {
|
||||
return isSubtypeOfRuntimeType(
|
||||
typeHierarchy, runtimeType.typeArgs[0], SubtypeTestKind.Subtype);
|
||||
typeHierarchy, runtimeType.typeArgs![0], SubtypeTestKind.Subtype);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => _hashCode ??= _computeHashCode();
|
||||
|
||||
int _computeHashCode() {
|
||||
int hash = cls.hashCode ^ 0x1234 & kHashMask;
|
||||
// We only need to hash the first type arguments vector, since the type
|
||||
// arguments of the implemented interfaces are implied by it.
|
||||
for (int i = 0; i < numImmediateTypeArgs; ++i) {
|
||||
hash = (((hash * 31) & kHashMask) + typeArgs[i].hashCode) & kHashMask;
|
||||
hash = (((hash * 31) & kHashMask) + typeArgs![i].hashCode) & kHashMask;
|
||||
}
|
||||
hash = ((hash * 31) & kHashMask) + constant.hashCode;
|
||||
return hash;
|
||||
@@ -915,7 +907,7 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
||||
}
|
||||
if (this.typeArgs != null) {
|
||||
for (int i = 0; i < numImmediateTypeArgs; ++i) {
|
||||
if (this.typeArgs[i] != other.typeArgs[i]) {
|
||||
if (this.typeArgs![i] != other.typeArgs![i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -942,10 +934,10 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
buf.write("_T (${cls}");
|
||||
if (typeArgs != null) {
|
||||
buf.write("<${typeArgs.take(numImmediateTypeArgs).join(', ')}>");
|
||||
buf.write("<${typeArgs!.take(numImmediateTypeArgs).join(', ')}>");
|
||||
}
|
||||
if (constant != null) {
|
||||
buf.write(", ${nodeToText(constant)}");
|
||||
buf.write(", ${nodeToText(constant!)}");
|
||||
}
|
||||
buf.write(")");
|
||||
return buf.toString();
|
||||
@@ -997,17 +989,21 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
||||
return this;
|
||||
}
|
||||
|
||||
List<Type> mergedTypeArgs;
|
||||
if (typeArgs == null) {
|
||||
mergedTypeArgs = other.typeArgs;
|
||||
} else if (other.typeArgs == null) {
|
||||
mergedTypeArgs = typeArgs;
|
||||
List<Type>? mergedTypeArgs;
|
||||
final thisTypeArgs = this.typeArgs;
|
||||
final otherTypeArgs = other.typeArgs;
|
||||
if (thisTypeArgs == null) {
|
||||
mergedTypeArgs = otherTypeArgs;
|
||||
} else if (otherTypeArgs == null) {
|
||||
mergedTypeArgs = thisTypeArgs;
|
||||
} else {
|
||||
mergedTypeArgs = new List<Type>.filled(typeArgs.length, null);
|
||||
assert(thisTypeArgs.length == otherTypeArgs.length);
|
||||
mergedTypeArgs =
|
||||
new List<Type>.filled(thisTypeArgs.length, const EmptyType());
|
||||
bool hasRuntimeType = false;
|
||||
for (int i = 0; i < typeArgs.length; ++i) {
|
||||
for (int i = 0; i < thisTypeArgs.length; ++i) {
|
||||
final merged =
|
||||
typeArgs[i].intersection(other.typeArgs[i], typeHierarchy);
|
||||
thisTypeArgs[i].intersection(otherTypeArgs[i], typeHierarchy);
|
||||
if (merged is EmptyType) {
|
||||
return const EmptyType();
|
||||
} else if (merged is RuntimeType) {
|
||||
@@ -1020,7 +1016,7 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
||||
}
|
||||
}
|
||||
|
||||
Constant mergedConstant;
|
||||
Constant? mergedConstant;
|
||||
if (constant == null) {
|
||||
mergedConstant = other.constant;
|
||||
} else if (other.constant == null || constant == other.constant) {
|
||||
@@ -1062,7 +1058,7 @@ class RuntimeType extends Type {
|
||||
final DartType _type; // Doesn't contain type args.
|
||||
|
||||
final int numImmediateTypeArgs;
|
||||
final List<RuntimeType> typeArgs;
|
||||
final List<RuntimeType>? typeArgs;
|
||||
|
||||
RuntimeType(DartType type, this.typeArgs)
|
||||
: _type = type,
|
||||
@@ -1070,14 +1066,12 @@ class RuntimeType extends Type {
|
||||
? type.classNode.typeParameters.length
|
||||
: (type is FutureOrType ? 1 : 0) {
|
||||
if (_type is InterfaceType && numImmediateTypeArgs > 0) {
|
||||
assert(typeArgs != null);
|
||||
assert(typeArgs.length >= numImmediateTypeArgs);
|
||||
assert(typeArgs!.length >= numImmediateTypeArgs);
|
||||
assert((_type as InterfaceType)
|
||||
.typeArguments
|
||||
.every((t) => t == const DynamicType()));
|
||||
} else if (_type is FutureOrType) {
|
||||
assert(typeArgs != null);
|
||||
assert(typeArgs.length >= numImmediateTypeArgs);
|
||||
assert(typeArgs!.length >= numImmediateTypeArgs);
|
||||
DartType typeArgument = (_type as FutureOrType).typeArgument;
|
||||
assert(typeArgument == const DynamicType());
|
||||
} else {
|
||||
@@ -1098,25 +1092,27 @@ class RuntimeType extends Type {
|
||||
final type = _type;
|
||||
if (type is InterfaceType && typeArgs != null) {
|
||||
final klass = type.classNode;
|
||||
final typeArguments = typeArgs
|
||||
final typeArguments = typeArgs!
|
||||
.take(klass.typeParameters.length)
|
||||
.map((pt) => pt.representedType)
|
||||
.toList();
|
||||
return new InterfaceType(klass, type.nullability, typeArguments);
|
||||
} else if (type is FutureOrType) {
|
||||
return new FutureOrType(typeArgs[0].representedType, type.nullability);
|
||||
return new FutureOrType(typeArgs![0].representedType, type.nullability);
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
late final int hashCode = _computeHashCode();
|
||||
|
||||
int _computeHashCode() {
|
||||
int hash = _type.hashCode ^ 0x1234 & kHashMask;
|
||||
// Only hash by the type arguments of the class. The type arguments of
|
||||
// supertypes are are implied by them.
|
||||
for (int i = 0; i < numImmediateTypeArgs; ++i) {
|
||||
hash = (((hash * 31) & kHashMask) + typeArgs[i].hashCode) & kHashMask;
|
||||
hash = (((hash * 31) & kHashMask) + typeArgs![i].hashCode) & kHashMask;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
@@ -1127,7 +1123,7 @@ class RuntimeType extends Type {
|
||||
if (other is RuntimeType) {
|
||||
if (other._type != _type) return false;
|
||||
assert(numImmediateTypeArgs == other.numImmediateTypeArgs);
|
||||
return typeArgs == null || listEquals(typeArgs, other.typeArgs);
|
||||
return typeArgs == null || listEquals(typeArgs!, other.typeArgs!);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1139,7 +1135,7 @@ class RuntimeType extends Type {
|
||||
: "${nodeToText(_type)}";
|
||||
final typeArgsStrs = (numImmediateTypeArgs == 0)
|
||||
? ""
|
||||
: "<${typeArgs.take(numImmediateTypeArgs).map((t) => "$t").join(", ")}>";
|
||||
: "<${typeArgs!.take(numImmediateTypeArgs).map((t) => "$t").join(", ")}>";
|
||||
final nullability = _type.nullability.suffix;
|
||||
return "$head$typeArgsStrs$nullability";
|
||||
}
|
||||
@@ -1200,15 +1196,15 @@ class RuntimeType extends Type {
|
||||
if (_type is InterfaceType) {
|
||||
Class thisClass = (_type as InterfaceType).classNode;
|
||||
if (thisClass == typeHierarchy.coreTypes.futureClass) {
|
||||
return typeArgs[0].isSubtypeOfRuntimeType(
|
||||
typeHierarchy, runtimeType.typeArgs[0], SubtypeTestKind.Subtype);
|
||||
return typeArgs![0].isSubtypeOfRuntimeType(
|
||||
typeHierarchy, runtimeType.typeArgs![0], SubtypeTestKind.Subtype);
|
||||
} else {
|
||||
return isSubtypeOfRuntimeType(
|
||||
typeHierarchy, runtimeType.typeArgs[0], SubtypeTestKind.Subtype);
|
||||
typeHierarchy, runtimeType.typeArgs![0], SubtypeTestKind.Subtype);
|
||||
}
|
||||
} else if (_type is FutureOrType) {
|
||||
return typeArgs[0].isSubtypeOfRuntimeType(
|
||||
typeHierarchy, runtimeType.typeArgs[0], SubtypeTestKind.Subtype);
|
||||
return typeArgs![0].isSubtypeOfRuntimeType(
|
||||
typeHierarchy, runtimeType.typeArgs![0], SubtypeTestKind.Subtype);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1236,7 +1232,7 @@ class RuntimeType extends Type {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<Type> usableTypeArgs = typeArgs;
|
||||
List<Type>? usableTypeArgs = typeArgs;
|
||||
if (usableTypeArgs == null) {
|
||||
assert(thisClass.typeParameters.isEmpty);
|
||||
usableTypeArgs =
|
||||
@@ -1248,7 +1244,7 @@ class RuntimeType extends Type {
|
||||
runtimeType.numImmediateTypeArgs);
|
||||
for (int i = 0; i < runtimeType.numImmediateTypeArgs; ++i) {
|
||||
if (!usableTypeArgs[interfaceOffset + i].isSubtypeOfRuntimeType(
|
||||
typeHierarchy, runtimeType.typeArgs[i], SubtypeTestKind.Subtype)) {
|
||||
typeHierarchy, runtimeType.typeArgs![i], SubtypeTestKind.Subtype)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/external_name.dart' show getExternalName;
|
||||
@@ -30,12 +28,12 @@ class UnboxingInfoManager {
|
||||
_coreTypes = typeFlowAnalysis.environment.coreTypes,
|
||||
_nativeCodeOracle = typeFlowAnalysis.nativeCodeOracle;
|
||||
|
||||
UnboxingInfoMetadata getUnboxingInfoOfMember(Member member) {
|
||||
final UnboxingInfoMetadata info = _memberInfo[member];
|
||||
UnboxingInfoMetadata? getUnboxingInfoOfMember(Member member) {
|
||||
final UnboxingInfoMetadata? info = _memberInfo[member];
|
||||
if (member is Procedure && member.isGetter) {
|
||||
// Remove placeholder parameter info slot for setters that the getter is
|
||||
// grouped with.
|
||||
return UnboxingInfoMetadata(0)..returnInfo = info.returnInfo;
|
||||
return UnboxingInfoMetadata(0)..returnInfo = info!.returnInfo;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
@@ -81,8 +79,8 @@ class UnboxingInfoManager {
|
||||
? (member.hasSetter ? 1 : 0)
|
||||
: member is Procedure && member.isGetter
|
||||
? 1
|
||||
: member.function.requiredParameterCount;
|
||||
UnboxingInfoMetadata info;
|
||||
: member.function!.requiredParameterCount;
|
||||
UnboxingInfoMetadata? info;
|
||||
if (member.isInstanceMember) {
|
||||
int selectorId =
|
||||
member is Field || member is Procedure && member.isGetter
|
||||
@@ -121,20 +119,18 @@ class UnboxingInfoManager {
|
||||
void _updateUnboxingInfoOfMember(
|
||||
Member member, TypeFlowAnalysis typeFlowAnalysis) {
|
||||
if (typeFlowAnalysis.isMemberUsed(member)) {
|
||||
final UnboxingInfoMetadata unboxingInfo = _memberInfo[member];
|
||||
final UnboxingInfoMetadata unboxingInfo = _memberInfo[member]!;
|
||||
if (_cannotUnbox(member)) {
|
||||
unboxingInfo.unboxedArgsInfo.length = 0;
|
||||
unboxingInfo.returnInfo = UnboxingInfoMetadata.kBoxed;
|
||||
return;
|
||||
}
|
||||
if (member is Procedure || member is Constructor) {
|
||||
final Args<Type> argTypes = typeFlowAnalysis.argumentTypes(member);
|
||||
assert(argTypes != null);
|
||||
|
||||
final Args<Type> argTypes = typeFlowAnalysis.argumentTypes(member)!;
|
||||
final int firstParamIndex =
|
||||
numTypeParams(member) + (hasReceiverArg(member) ? 1 : 0);
|
||||
|
||||
final positionalParams = member.function.positionalParameters;
|
||||
final positionalParams = member.function!.positionalParameters;
|
||||
assert(argTypes.positionalCount ==
|
||||
firstParamIndex + positionalParams.length);
|
||||
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
// 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.
|
||||
|
||||
// @dart=2.9
|
||||
|
||||
/// Declares miscellaneous utility functions and constants for type flow
|
||||
/// analysis.
|
||||
library vm.transformations.type_flow.utils;
|
||||
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/src/printer.dart';
|
||||
|
||||
@@ -123,9 +122,8 @@ bool isSorted(List list) {
|
||||
return true;
|
||||
}
|
||||
|
||||
VariableDeclaration findNamedParameter(FunctionNode function, String name) {
|
||||
return function.namedParameters
|
||||
.firstWhere((p) => p.name == name, orElse: () => null);
|
||||
VariableDeclaration? findNamedParameter(FunctionNode function, String name) {
|
||||
return function.namedParameters.firstWhereOrNull((p) => p.name == name);
|
||||
}
|
||||
|
||||
class Histogram<K> {
|
||||
@@ -143,7 +141,7 @@ class Histogram<K> {
|
||||
print(
|
||||
'-------------------------------------------------------------------');
|
||||
List<K> keys = values.keys.toList();
|
||||
keys.sort((k1, k2) => values[k1].compareTo(values[k2]));
|
||||
keys.sort((k1, k2) => values[k1]!.compareTo(values[k2]!));
|
||||
final cut = keys.length < n ? 0 : keys.length - n;
|
||||
for (int i = keys.length - 1; i >= cut; --i) {
|
||||
final k = keys[i];
|
||||
@@ -376,7 +374,7 @@ const nullabilitySuffix = {
|
||||
};
|
||||
|
||||
extension NullabilitySuffix on Nullability {
|
||||
String get suffix => nullabilitySuffix[this];
|
||||
String get suffix => nullabilitySuffix[this]!;
|
||||
}
|
||||
|
||||
bool mayHaveSideEffects(Expression node) {
|
||||
|
||||
@@ -18,6 +18,7 @@ dependencies:
|
||||
meta:
|
||||
path: ../meta
|
||||
package_config: any
|
||||
collection: ^1.15.0
|
||||
|
||||
dev_dependencies:
|
||||
expect:
|
||||
|
||||
@@ -60,7 +60,7 @@ class FakeEntryPointsListener implements EntryPointsListener {
|
||||
void recordMemberCalledViaThis(Member target) {}
|
||||
|
||||
@override
|
||||
void recordTearOff(Procedure target) {}
|
||||
void recordTearOff(Member target) {}
|
||||
}
|
||||
|
||||
class PrintSummaries extends RecursiveVisitor {
|
||||
@@ -78,7 +78,7 @@ class PrintSummaries extends RecursiveVisitor {
|
||||
typesBuilder,
|
||||
new NativeCodeOracle(
|
||||
null, new ConstantPragmaAnnotationParser(coreTypes)),
|
||||
new GenericInterfacesInfoImpl(hierarchy),
|
||||
new GenericInterfacesInfoImpl(coreTypes, hierarchy),
|
||||
/*_protobufHandler=*/ null);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user