mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:53:55 +00:00
[vm/tfa] Implementation of type arguments tracking in TFA.
Change-Id: I9398186e27ae7a040e249df010ae16fb6ab6da89 Cq-Include-Trybots: luci.dart.try:vm-kernel-win-release-x64-try, vm-kernel-optcounter-threshold-linux-release-x64-try, vm-kernel-precomp-linux-debug-x64-try, vm-kernel-precomp-linux-release-simarm-try, vm-kernel-precomp-linux-release-simarm64-try, vm-kernel-precomp-linux-release-x64-try, vm-kernel-precomp-win-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/74962 Commit-Queue: Samir Jindel <sjindel@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
This commit is contained in:
parent
001343ce06
commit
3e7ce992cf
|
@ -42,6 +42,11 @@ abstract class ClassHierarchy {
|
|||
/// True if the component contains another class that is a subtype of given one.
|
||||
bool hasProperSubtypes(Class class_);
|
||||
|
||||
// Returns the instantition of each generic supertype implemented by this
|
||||
// class (e.g. getClassAsInstanceOf applied to all superclasses and
|
||||
// interfaces).
|
||||
List<Supertype> genericSupertypesOf(Class class_);
|
||||
|
||||
/// Returns the least upper bound of two interface types, as defined by Dart
|
||||
/// 1.0.
|
||||
///
|
||||
|
@ -699,6 +704,15 @@ class ClosedWorldClassHierarchy implements ClassHierarchy {
|
|||
info.directMixers.isNotEmpty;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Supertype> genericSupertypesOf(Class class_) {
|
||||
final supertypes = _infoFor[class_].genericSuperTypes;
|
||||
if (supertypes == null) return const <Supertype>[];
|
||||
// Multiple supertypes can arise from ambiguous supertypes. The first
|
||||
// supertype is the real one; the others are purely informational.
|
||||
return supertypes.values.map((v) => v.first).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
ClassHierarchy applyTreeChanges(Iterable<Library> removedLibraries,
|
||||
Iterable<Library> ensureKnownLibraries,
|
||||
|
@ -1339,7 +1353,7 @@ class _ClassInfo {
|
|||
/// Maps generic supertype classes to the instantiation implemented by this
|
||||
/// class.
|
||||
///
|
||||
/// E.g. `List` maps to `List<String>` for a class that directly of indirectly
|
||||
/// E.g. `List` maps to `List<String>` for a class that directly or indirectly
|
||||
/// implements `List<String>`.
|
||||
Map<Class, List<Supertype>> genericSuperTypes;
|
||||
|
||||
|
|
|
@ -14,20 +14,54 @@ class InferredType {
|
|||
static const int flagNullable = 1 << 0;
|
||||
static const int flagInt = 1 << 1;
|
||||
|
||||
InferredType(Class concreteClass, bool nullable, bool isInt)
|
||||
: this._byReference(getClassReference(concreteClass),
|
||||
(nullable ? flagNullable : 0) | (isInt ? flagInt : 0));
|
||||
// For Parameters and Fields, whether a type-check is required at assignment
|
||||
// (invocation/setter). Not meaningful on other kernel nodes.
|
||||
static const int flagSkipCheck = 1 << 2;
|
||||
|
||||
InferredType._byReference(this._concreteClassReference, this._flags);
|
||||
// Entire list may be null if no type arguments were inferred.
|
||||
// Will always be null if `concreteClass` is null.
|
||||
//
|
||||
// Each component may be null if that particular type argument was not
|
||||
// inferred.
|
||||
//
|
||||
// Otherwise, a non-null type argument indicates that that particular type
|
||||
// argument (in the runtime type) is always exactly a particular `DartType`.
|
||||
final List<DartType> exactTypeArguments;
|
||||
|
||||
InferredType(Class concreteClass, bool nullable, bool isInt,
|
||||
{List<DartType> exactTypeArguments, bool skipCheck: false})
|
||||
: this._byReference(
|
||||
getClassReference(concreteClass),
|
||||
(nullable ? flagNullable : 0) |
|
||||
(isInt ? flagInt : 0) |
|
||||
(skipCheck ? flagSkipCheck : 0),
|
||||
exactTypeArguments);
|
||||
|
||||
InferredType._byReference(
|
||||
this._concreteClassReference, this._flags, this.exactTypeArguments) {
|
||||
assert(exactTypeArguments == null || _concreteClassReference != null);
|
||||
}
|
||||
|
||||
Class get concreteClass => _concreteClassReference?.asClass;
|
||||
|
||||
bool get nullable => (_flags & flagNullable) != 0;
|
||||
bool get isInt => (_flags & flagInt) != 0;
|
||||
bool get skipCheck => (_flags & flagSkipCheck) != 0;
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
"${concreteClass != null ? concreteClass : (isInt ? 'int' : '!')}${nullable ? '?' : ''}";
|
||||
String toString() {
|
||||
final base =
|
||||
"${concreteClass != null ? concreteClass : (isInt ? 'int' : '!')}";
|
||||
final suffix = "${nullable ? '?' : ''}";
|
||||
String typeArgs = "";
|
||||
if (exactTypeArguments != null) {
|
||||
typeArgs =
|
||||
exactTypeArguments.map((t) => t != null ? "$t" : "?").join(", ");
|
||||
typeArgs = "<" + typeArgs + ">";
|
||||
}
|
||||
final skip = skipCheck ? " (skip check)" : "";
|
||||
return base + suffix + typeArgs + skip;
|
||||
}
|
||||
}
|
||||
|
||||
/// Repository for [InferredType].
|
||||
|
@ -40,6 +74,8 @@ class InferredTypeMetadataRepository extends MetadataRepository<InferredType> {
|
|||
|
||||
@override
|
||||
void writeToBinary(InferredType metadata, Node node, BinarySink sink) {
|
||||
// TODO(sjindel/tfa): Implement serialization of type arguments when can use
|
||||
// them for optimizations.
|
||||
sink.writeCanonicalNameReference(
|
||||
getCanonicalNameOfClass(metadata.concreteClass));
|
||||
sink.writeByte(metadata._flags);
|
||||
|
@ -47,9 +83,11 @@ class InferredTypeMetadataRepository extends MetadataRepository<InferredType> {
|
|||
|
||||
@override
|
||||
InferredType readFromBinary(Node node, BinarySource source) {
|
||||
// TODO(sjindel/tfa): Implement serialization of type arguments when can use
|
||||
// them for optimizations.
|
||||
final concreteClassReference =
|
||||
source.readCanonicalNameReference()?.getReference();
|
||||
final flags = source.readByte();
|
||||
return new InferredType._byReference(concreteClassReference, flags);
|
||||
return new InferredType._byReference(concreteClassReference, flags, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,7 +171,8 @@ class _DirectInvocation extends _Invocation {
|
|||
assertx(args.values.length == firstParamIndex + 1);
|
||||
assertx(args.names.isEmpty);
|
||||
final Type setterArg = args.values[firstParamIndex];
|
||||
fieldValue.setValue(setterArg, typeFlowAnalysis);
|
||||
fieldValue.setValue(
|
||||
setterArg, typeFlowAnalysis, field.isStatic ? null : args.receiver);
|
||||
return const EmptyType();
|
||||
|
||||
case CallKind.Method:
|
||||
|
@ -202,7 +203,8 @@ class _DirectInvocation extends _Invocation {
|
|||
// does not throw exception.
|
||||
initializerResult = new Type.nullable(initializerResult);
|
||||
}
|
||||
fieldValue.setValue(initializerResult, typeFlowAnalysis);
|
||||
fieldValue.setValue(initializerResult, typeFlowAnalysis,
|
||||
field.isStatic ? null : args.receiver);
|
||||
return const EmptyType();
|
||||
}
|
||||
|
||||
|
@ -251,7 +253,8 @@ class _DirectInvocation extends _Invocation {
|
|||
|
||||
final int positionalArguments = args.positionalCount;
|
||||
|
||||
final int firstParamIndex = hasReceiverArg(selector.member) ? 1 : 0;
|
||||
final int firstParamIndex = numTypeParams(selector.member) +
|
||||
(hasReceiverArg(selector.member) ? 1 : 0);
|
||||
final int requiredParameters =
|
||||
firstParamIndex + function.requiredParameterCount;
|
||||
if (positionalArguments < requiredParameters) {
|
||||
|
@ -442,12 +445,7 @@ class _DispatchableInvocation extends _Invocation {
|
|||
ConcreteType receiver,
|
||||
Map<Member, _ReceiverTypeBuilder> targets,
|
||||
TypeFlowAnalysis typeFlowAnalysis) {
|
||||
DartType receiverDartType = receiver.dartType;
|
||||
|
||||
assertx(receiverDartType is! FunctionType);
|
||||
assertx(receiverDartType is InterfaceType); // TODO(alexmarkov)
|
||||
|
||||
Class class_ = (receiverDartType as InterfaceType).classNode;
|
||||
Class class_ = receiver.classNode;
|
||||
|
||||
Member target = typeFlowAnalysis.hierarchyCache.hierarchy
|
||||
.getDispatchTarget(class_, selector.name, setter: selector.isSetter);
|
||||
|
@ -698,9 +696,11 @@ class _InvocationsCache {
|
|||
class _FieldValue extends _DependencyTracker {
|
||||
final Field field;
|
||||
final Type staticType;
|
||||
final Summary typeGuardSummary;
|
||||
Type value;
|
||||
|
||||
_FieldValue(this.field) : staticType = new Type.fromStatic(field.type) {
|
||||
_FieldValue(this.field, this.typeGuardSummary)
|
||||
: staticType = new Type.fromStatic(field.type) {
|
||||
if (field.initializer == null && _isDefaultValueOfFieldObservable()) {
|
||||
value = new Type.nullable(const EmptyType());
|
||||
} else {
|
||||
|
@ -708,6 +708,14 @@ class _FieldValue extends _DependencyTracker {
|
|||
}
|
||||
}
|
||||
|
||||
bool get staticCallSiteSkipCheck {
|
||||
if (typeGuardSummary != null) {
|
||||
return (typeGuardSummary.result as TypeCheck).canSkipOnStaticCallSite;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool _isDefaultValueOfFieldObservable() {
|
||||
if (field.isStatic) {
|
||||
return true;
|
||||
|
@ -750,7 +758,8 @@ class _FieldValue extends _DependencyTracker {
|
|||
return value;
|
||||
}
|
||||
|
||||
void setValue(Type newValue, TypeFlowAnalysis typeFlowAnalysis) {
|
||||
void setValue(
|
||||
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.
|
||||
|
@ -776,11 +785,15 @@ class _FieldValue extends _DependencyTracker {
|
|||
// is established.
|
||||
//
|
||||
final hierarchy = typeFlowAnalysis.hierarchyCache;
|
||||
Type newType = value
|
||||
.union(
|
||||
newValue.specialize(hierarchy).intersection(staticType, hierarchy),
|
||||
hierarchy)
|
||||
.specialize(hierarchy);
|
||||
// TODO(sjindel/tfa): Perform narrowing inside 'TypeCheck'.
|
||||
final narrowedNewValue = typeGuardSummary != null
|
||||
? typeGuardSummary
|
||||
.apply(
|
||||
new Args([receiverType, newValue]), hierarchy, typeFlowAnalysis)
|
||||
.intersection(staticType, hierarchy)
|
||||
: newValue.specialize(hierarchy).intersection(staticType, hierarchy);
|
||||
Type newType =
|
||||
value.union(narrowedNewValue, hierarchy).specialize(hierarchy);
|
||||
assertx(newType.isSpecialized);
|
||||
|
||||
if (newType != value) {
|
||||
|
@ -812,7 +825,7 @@ class _ClassData extends _DependencyTracker implements ClassId<_ClassData> {
|
|||
|
||||
/// Flag indicating if this class has a noSuchMethod() method not inherited
|
||||
/// from Object.
|
||||
/// Lazy initialized by _ClassHierarchyCache.hasNonTrivialNoSuchMethod().
|
||||
/// Lazy initialized by ClassHierarchyCache.hasNonTrivialNoSuchMethod().
|
||||
bool hasNonTrivialNoSuchMethod;
|
||||
|
||||
_ClassData(this._id, this.class_, this.supertypes) {
|
||||
|
@ -821,7 +834,7 @@ class _ClassData extends _DependencyTracker implements ClassId<_ClassData> {
|
|||
|
||||
ConcreteType _concreteType;
|
||||
ConcreteType get concreteType =>
|
||||
_concreteType ??= new ConcreteType(this, class_.rawType);
|
||||
_concreteType ??= new ConcreteType(this, class_, null);
|
||||
|
||||
Type _specializedConeType;
|
||||
Type get specializedConeType =>
|
||||
|
@ -865,11 +878,78 @@ class _ClassData extends _DependencyTracker implements ClassId<_ClassData> {
|
|||
String dump() => "$this {supers: $supertypes}";
|
||||
}
|
||||
|
||||
class GenericInterfacesInfoImpl implements GenericInterfacesInfo {
|
||||
final ClosedWorldClassHierarchy hierarchy;
|
||||
|
||||
final supertypeOffsetsCache = <SubtypePair, int>{};
|
||||
final cachedFlattenedTypeArgs = <Class, List<DartType>>{};
|
||||
final cachedFlattenedTypeArgsForNonGeneric = <Class, List<Type>>{};
|
||||
|
||||
RuntimeTypeTranslator closedTypeTranslator;
|
||||
|
||||
GenericInterfacesInfoImpl(this.hierarchy) {
|
||||
closedTypeTranslator = RuntimeTypeTranslator.forClosedTypes(this);
|
||||
}
|
||||
|
||||
List<DartType> flattenedTypeArgumentsFor(Class klass, {bool useCache: true}) {
|
||||
final cached = useCache ? cachedFlattenedTypeArgs[klass] : null;
|
||||
if (cached != null) return cached;
|
||||
|
||||
final flattenedTypeArguments = List<DartType>.from(
|
||||
klass.typeParameters.map((t) => new TypeParameterType(t)));
|
||||
|
||||
for (final Supertype intf in hierarchy.genericSupertypesOf(klass)) {
|
||||
int offset = findOverlap(flattenedTypeArguments, intf.typeArguments);
|
||||
flattenedTypeArguments.addAll(
|
||||
intf.typeArguments.skip(flattenedTypeArguments.length - offset));
|
||||
supertypeOffsetsCache[SubtypePair(klass, intf.classNode)] = offset;
|
||||
}
|
||||
|
||||
return flattenedTypeArguments;
|
||||
}
|
||||
|
||||
int genericInterfaceOffsetFor(Class klass, Class iface) {
|
||||
if (klass == iface) return 0;
|
||||
|
||||
final pair = new SubtypePair(klass, iface);
|
||||
int offset = supertypeOffsetsCache[pair];
|
||||
|
||||
if (offset != null) return offset;
|
||||
|
||||
flattenedTypeArgumentsFor(klass);
|
||||
offset = supertypeOffsetsCache[pair];
|
||||
|
||||
if (offset == null) {
|
||||
throw "Invalid call to genericInterfaceOffsetFor.";
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
List<Type> flattenedTypeArgumentsForNonGeneric(Class klass) {
|
||||
List<Type> result = cachedFlattenedTypeArgsForNonGeneric[klass];
|
||||
if (result != null) return result;
|
||||
|
||||
List<DartType> flattenedTypeArgs =
|
||||
flattenedTypeArgumentsFor(klass, useCache: false);
|
||||
result = new List<Type>(flattenedTypeArgs.length);
|
||||
for (int i = 0; i < flattenedTypeArgs.length; ++i) {
|
||||
final translated = closedTypeTranslator.translate(flattenedTypeArgs[i]);
|
||||
assertx(translated is RuntimeType || translated is AnyType);
|
||||
result[i] = translated;
|
||||
}
|
||||
cachedFlattenedTypeArgsForNonGeneric[klass] = result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class _ClassHierarchyCache implements TypeHierarchy {
|
||||
final TypeFlowAnalysis _typeFlowAnalysis;
|
||||
final ClosedWorldClassHierarchy hierarchy;
|
||||
final TypeEnvironment environment;
|
||||
final Set<Class> allocatedClasses = new Set<Class>();
|
||||
final Map<Class, _ClassData> classes = <Class, _ClassData>{};
|
||||
final GenericInterfacesInfo genericInterfacesInfo;
|
||||
|
||||
/// Object.noSuchMethod().
|
||||
final Member objectNoSuchMethod;
|
||||
|
@ -887,10 +967,10 @@ class _ClassHierarchyCache implements TypeHierarchy {
|
|||
final Map<DynamicSelector, _DynamicTargetSet> _dynamicTargets =
|
||||
<DynamicSelector, _DynamicTargetSet>{};
|
||||
|
||||
_ClassHierarchyCache(this._typeFlowAnalysis, this.hierarchy)
|
||||
_ClassHierarchyCache(this._typeFlowAnalysis, this.hierarchy,
|
||||
this.genericInterfacesInfo, this.environment)
|
||||
: objectNoSuchMethod = hierarchy.getDispatchTarget(
|
||||
_typeFlowAnalysis.environment.coreTypes.objectClass,
|
||||
noSuchMethodName) {
|
||||
environment.coreTypes.objectClass, noSuchMethodName) {
|
||||
assertx(objectNoSuchMethod != null);
|
||||
}
|
||||
|
||||
|
@ -943,6 +1023,9 @@ class _ClassHierarchyCache implements TypeHierarchy {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (superType is DynamicType || superType is VoidType) return true;
|
||||
if (subType is DynamicType || subType is VoidType) return false;
|
||||
|
||||
// TODO(alexmarkov): handle function types properly
|
||||
if (subType is FunctionType) {
|
||||
subType = _typeFlowAnalysis.environment.rawFunctionType;
|
||||
|
@ -951,16 +1034,21 @@ class _ClassHierarchyCache implements TypeHierarchy {
|
|||
superType = _typeFlowAnalysis.environment.rawFunctionType;
|
||||
}
|
||||
// TODO(alexmarkov): handle generic types properly.
|
||||
if (subType is TypeParameterType) {
|
||||
subType = (subType as TypeParameterType).bound;
|
||||
}
|
||||
if (superType is TypeParameterType) {
|
||||
superType = (superType as TypeParameterType).bound;
|
||||
}
|
||||
assertx(subType is! TypeParameterType);
|
||||
assertx(superType is! TypeParameterType);
|
||||
|
||||
assertx(subType is InterfaceType, details: subType); // TODO(alexmarkov)
|
||||
assertx(superType is InterfaceType, details: superType); // TODO(alexmarkov)
|
||||
|
||||
// InterfaceTypes should be raw, since we don't handle type arguments
|
||||
// (although frankly we can't distinguish between raw C and C<dynamic).
|
||||
assertx((subType as InterfaceType)
|
||||
.typeArguments
|
||||
.every((t) => t == const DynamicType()));
|
||||
assertx((superType as InterfaceType)
|
||||
.typeArguments
|
||||
.every((t) => t == const DynamicType()));
|
||||
|
||||
Class subClass = (subType as InterfaceType).classNode;
|
||||
Class superClass = (superType as InterfaceType).classNode;
|
||||
if (subClass == superClass) {
|
||||
|
@ -1056,10 +1144,22 @@ class _ClassHierarchyCache implements TypeHierarchy {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<DartType> flattenedTypeArgumentsFor(Class klass) =>
|
||||
genericInterfacesInfo.flattenedTypeArgumentsFor(klass);
|
||||
|
||||
@override
|
||||
int genericInterfaceOffsetFor(Class klass, Class iface) =>
|
||||
genericInterfacesInfo.genericInterfaceOffsetFor(klass, iface);
|
||||
|
||||
@override
|
||||
List<Type> flattenedTypeArgumentsForNonGeneric(Class klass) =>
|
||||
genericInterfacesInfo.flattenedTypeArgumentsForNonGeneric(klass);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.write("_ClassHierarchyCache {\n");
|
||||
buf.write("ClassHierarchyCache {\n");
|
||||
buf.write(" allocated classes:\n");
|
||||
allocatedClasses.forEach((c) {
|
||||
buf.write(" $c\n");
|
||||
|
@ -1071,6 +1171,9 @@ class _ClassHierarchyCache implements TypeHierarchy {
|
|||
buf.write("}\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
Class get futureOrClass => environment.coreTypes.futureOrClass;
|
||||
Class get futureClass => environment.coreTypes.futureClass;
|
||||
}
|
||||
|
||||
class _WorkList {
|
||||
|
@ -1196,6 +1299,7 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
|||
SummaryCollector summaryCollector;
|
||||
_InvocationsCache _invocationsCache;
|
||||
_WorkList workList;
|
||||
GenericInterfacesInfo _genericInterfacesInfo;
|
||||
|
||||
final Map<Member, Summary> _summaries = <Member, Summary>{};
|
||||
final Map<Field, _FieldValue> _fieldValues = <Field, _FieldValue>{};
|
||||
|
@ -1204,15 +1308,22 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
|||
final Set<Member> _calledViaInterfaceSelector = new Set<Member>();
|
||||
final Set<Member> _calledViaThis = new Set<Member>();
|
||||
|
||||
TypeFlowAnalysis(this.target, Component component, CoreTypes coreTypes,
|
||||
ClosedWorldClassHierarchy hierarchy, this.environment, this.libraryIndex,
|
||||
TypeFlowAnalysis(
|
||||
this.target,
|
||||
Component component,
|
||||
CoreTypes coreTypes,
|
||||
ClosedWorldClassHierarchy hierarchy,
|
||||
this._genericInterfacesInfo,
|
||||
this.environment,
|
||||
this.libraryIndex,
|
||||
{PragmaAnnotationParser matcher})
|
||||
: annotationMatcher =
|
||||
matcher ?? new ConstantPragmaAnnotationParser(coreTypes) {
|
||||
nativeCodeOracle = new NativeCodeOracle(libraryIndex, annotationMatcher);
|
||||
hierarchyCache = new _ClassHierarchyCache(this, hierarchy);
|
||||
summaryCollector =
|
||||
new SummaryCollector(target, environment, this, nativeCodeOracle);
|
||||
hierarchyCache = new _ClassHierarchyCache(
|
||||
this, hierarchy, _genericInterfacesInfo, environment);
|
||||
summaryCollector = new SummaryCollector(
|
||||
target, environment, this, nativeCodeOracle, hierarchyCache);
|
||||
_invocationsCache = new _InvocationsCache(this);
|
||||
workList = new _WorkList(this);
|
||||
|
||||
|
@ -1227,7 +1338,12 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
|||
}
|
||||
|
||||
_FieldValue getFieldValue(Field field) {
|
||||
return _fieldValues[field] ??= new _FieldValue(field);
|
||||
Summary setterSummary = null;
|
||||
if (field.isGenericCovariantImpl) {
|
||||
setterSummary = summaryCollector.createSummary(field,
|
||||
fieldSummaryType: FieldSummaryType.kFieldGuard);
|
||||
}
|
||||
return _fieldValues[field] ??= new _FieldValue(field, setterSummary);
|
||||
}
|
||||
|
||||
void process() {
|
||||
|
@ -1249,6 +1365,12 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
|||
|
||||
Type fieldType(Field field) => _fieldValues[field]?.value;
|
||||
|
||||
// True if a the runtime type-check for this field can be skipped on
|
||||
// statically-typed setter calls. (The type-check is only simulated after
|
||||
// narrowing by the static parameter type.)
|
||||
bool fieldStaticCallSiteSkipCheck(Field field) =>
|
||||
_fieldValues[field]?.staticCallSiteSkipCheck;
|
||||
|
||||
Args<Type> argumentTypes(Member member) => _summaries[member]?.argumentTypes;
|
||||
|
||||
bool isTearOffTaken(Member member) => _tearOffTaken.contains(member);
|
||||
|
@ -1268,6 +1390,12 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
|||
_calledViaDynamicSelector.contains(member) ||
|
||||
_calledViaInterfaceSelector.contains(member);
|
||||
|
||||
// Returns parameters for which a runtime type-check can be skipped on
|
||||
// statically-typed call-sites. (The type-check is only simulated after
|
||||
// narrowing by the static parameter type.)
|
||||
List<VariableDeclaration> staticCallSiteSkipCheckParams(Member member) =>
|
||||
_summaries[member]?.staticCallSiteSkipCheckParams;
|
||||
|
||||
/// ---- Implementation of [CallHandler] interface. ----
|
||||
|
||||
@override
|
||||
|
@ -1309,7 +1437,13 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
|
|||
|
||||
@override
|
||||
void addDirectFieldAccess(Field field, Type value) {
|
||||
getFieldValue(field).setValue(value, this);
|
||||
final fieldValue = getFieldValue(field);
|
||||
if (field.isStatic) {
|
||||
fieldValue.setValue(value, this, /*receiver_type=*/ null);
|
||||
} else {
|
||||
final receiver = new Type.cone(new InterfaceType(field.parent));
|
||||
fieldValue.setValue(value, this, receiver);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -55,12 +55,19 @@ class StatementVisitor {
|
|||
void visitJoin(Join expr) => visitDefault(expr);
|
||||
void visitUse(Use expr) => visitDefault(expr);
|
||||
void visitCall(Call expr) => visitDefault(expr);
|
||||
void visitExtract(Extract expr) => visitDefault(expr);
|
||||
void visitCreateConcreteType(CreateConcreteType expr) => visitDefault(expr);
|
||||
void visitCreateRuntimeType(CreateRuntimeType expr) => visitDefault(expr);
|
||||
void visitTypeCheck(TypeCheck expr) => visitDefault(expr);
|
||||
}
|
||||
|
||||
/// Input parameter of the summary.
|
||||
class Parameter extends Statement {
|
||||
final String name;
|
||||
|
||||
// 'staticType' is null for type parameters to factory constructors.
|
||||
final Type staticType;
|
||||
|
||||
Type defaultValue;
|
||||
Type _argumentType = const EmptyType();
|
||||
|
||||
|
@ -261,6 +268,182 @@ class Call extends Statement {
|
|||
}
|
||||
}
|
||||
|
||||
// Extract a type argument from a ConcreteType (used to extract type arguments
|
||||
// from receivers of methods).
|
||||
class Extract extends Statement {
|
||||
TypeExpr arg;
|
||||
|
||||
final Class referenceClass;
|
||||
final int paramIndex;
|
||||
|
||||
Extract(this.arg, this.referenceClass, this.paramIndex);
|
||||
|
||||
@override
|
||||
void accept(StatementVisitor visitor) => visitor.visitExtract(this);
|
||||
|
||||
@override
|
||||
String dump() => "$label = _Extract ($arg[$referenceClass/$paramIndex])";
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
Type argType = arg.getComputedType(computedTypes);
|
||||
Type extractedType;
|
||||
|
||||
void extractType(ConcreteType c) {
|
||||
if (c.typeArgs == null) {
|
||||
extractedType = const AnyType();
|
||||
} else {
|
||||
final interfaceOffset = typeHierarchy.genericInterfaceOffsetFor(
|
||||
c.classNode, referenceClass);
|
||||
final extract = c.typeArgs[interfaceOffset + paramIndex];
|
||||
assertx(extract is AnyType || extract is RuntimeType);
|
||||
if (extractedType == null || extract == extractedType) {
|
||||
extractedType = extract;
|
||||
} else {
|
||||
extractedType = const AnyType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(sjindel/tfa): Support more types here if possible.
|
||||
if (argType is ConcreteType) {
|
||||
extractType(argType);
|
||||
} else if (argType is SetType) {
|
||||
argType.types.forEach(extractType);
|
||||
}
|
||||
|
||||
return extractedType ?? const AnyType();
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate a concrete type with type arguments. For example, used to fill in
|
||||
// "T = int" in "C<T>" to create "C<int>".
|
||||
//
|
||||
// The type arguments are factored against the generic interfaces; for more
|
||||
// details see 'ClassHierarchyCache.factoredGenericInterfacesOf'.
|
||||
class CreateConcreteType extends Statement {
|
||||
final ConcreteType type;
|
||||
final List<TypeExpr> flattenedTypeArgs;
|
||||
|
||||
CreateConcreteType(this.type, this.flattenedTypeArgs);
|
||||
|
||||
@override
|
||||
void accept(StatementVisitor visitor) =>
|
||||
visitor.visitCreateConcreteType(this);
|
||||
|
||||
@override
|
||||
String dump() {
|
||||
int numImmediateTypeArgs = type.classNode.typeParameters.length;
|
||||
return "$label = _CreateConcreteType (${type.classNode} @ "
|
||||
"${flattenedTypeArgs.take(numImmediateTypeArgs)})";
|
||||
}
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
bool hasRuntimeType = false;
|
||||
final types = new List<Type>(flattenedTypeArgs.length);
|
||||
for (int i = 0; i < types.length; ++i) {
|
||||
final computed = flattenedTypeArgs[i].getComputedType(computedTypes);
|
||||
assertx(computed is RuntimeType || computed is AnyType);
|
||||
if (computed is RuntimeType) hasRuntimeType = true;
|
||||
types[i] = computed;
|
||||
}
|
||||
return new ConcreteType(
|
||||
type.classId, type.classNode, hasRuntimeType ? types : null);
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to "CreateConcreteType", but creates a "RuntimeType" rather than a
|
||||
// "ConcreteType". Unlike a "ConcreteType", none of the type arguments can be
|
||||
// missing ("AnyType").
|
||||
class CreateRuntimeType extends Statement {
|
||||
final Class klass;
|
||||
final List<TypeExpr> flattenedTypeArgs;
|
||||
|
||||
CreateRuntimeType(this.klass, this.flattenedTypeArgs);
|
||||
|
||||
@override
|
||||
void accept(StatementVisitor visitor) => visitor.visitCreateRuntimeType(this);
|
||||
|
||||
@override
|
||||
String dump() => "$label = _CreateRuntimeType ($klass @ "
|
||||
"${flattenedTypeArgs.take(klass.typeParameters.length)})";
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
final types = new List<RuntimeType>(flattenedTypeArgs.length);
|
||||
for (int i = 0; i < types.length; ++i) {
|
||||
final computed = flattenedTypeArgs[i].getComputedType(computedTypes);
|
||||
assertx(computed is RuntimeType || computed is AnyType);
|
||||
if (computed is AnyType) return const AnyType();
|
||||
types[i] = computed;
|
||||
}
|
||||
return new RuntimeType(new InterfaceType(klass), types);
|
||||
}
|
||||
}
|
||||
|
||||
// Used to simulate a runtime type-check, to determine when it can be skipped.
|
||||
// TODO(sjindel/tfa): Unify with Narrow.
|
||||
class TypeCheck extends Statement {
|
||||
TypeExpr arg;
|
||||
TypeExpr type;
|
||||
|
||||
bool _canSkip = true;
|
||||
|
||||
// True if a the runtime type-check for this parameter can be skipped on
|
||||
// statically-typed call-sites. (The type-check is only simulated after
|
||||
// narrowing by the static parameter type.)
|
||||
bool get canSkipOnStaticCallSite => _canSkip;
|
||||
|
||||
final VariableDeclaration parameter;
|
||||
|
||||
TypeCheck(this.arg, this.type, this.parameter);
|
||||
|
||||
@override
|
||||
void accept(StatementVisitor visitor) => visitor.visitTypeCheck(this);
|
||||
|
||||
@override
|
||||
String dump() {
|
||||
String result = "$label = _TypeCheck ($arg against $type)";
|
||||
if (parameter != null) {
|
||||
result += " (for parameter ${parameter.name})";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Type apply(List<Type> computedTypes, TypeHierarchy typeHierarchy,
|
||||
CallHandler callHandler) {
|
||||
Type argType = arg.getComputedType(computedTypes);
|
||||
Type checkType = type.getComputedType(computedTypes);
|
||||
// TODO(sjindel/tfa): Narrow the result if possible.
|
||||
assertx(checkType is AnyType || checkType is RuntimeType);
|
||||
if (_canSkip) {
|
||||
if (checkType is AnyType) {
|
||||
// If we don't know what the RHS of the check is going to be, we can't
|
||||
// guarantee that it will pass.
|
||||
if (kPrintTrace) {
|
||||
tracePrint("TypeCheck failed, type is unknown");
|
||||
}
|
||||
_canSkip = false;
|
||||
} else if (checkType is RuntimeType) {
|
||||
_canSkip = argType.isSubtypeOfRuntimeType(typeHierarchy, checkType);
|
||||
if (kPrintTrace && !_canSkip) {
|
||||
tracePrint("TypeCheck of $argType against $checkType failed.");
|
||||
}
|
||||
argType = argType.intersection(
|
||||
Type.fromStatic(checkType.representedTypeRaw), typeHierarchy);
|
||||
} else {
|
||||
assertx(false, details: "Cannot see $checkType on RHS of TypeCheck.");
|
||||
}
|
||||
}
|
||||
return argType;
|
||||
}
|
||||
}
|
||||
|
||||
/// Summary is a linear sequence of statements representing a type flow in
|
||||
/// one member, function or initializer.
|
||||
class Summary {
|
||||
|
@ -318,13 +501,19 @@ class Summary {
|
|||
|
||||
for (int i = 0; i < positionalArgCount; i++) {
|
||||
final Parameter param = _statements[i] as Parameter;
|
||||
final argType = args[i].specialize(typeHierarchy);
|
||||
param._observeArgumentType(argType, typeHierarchy);
|
||||
types[i] = argType.intersection(param.staticType, typeHierarchy);
|
||||
if (param.staticType != null) {
|
||||
final argType = args[i].specialize(typeHierarchy);
|
||||
param._observeArgumentType(argType, typeHierarchy);
|
||||
// TODO(sjindel/tfa): Perform narrowing inside 'TypeCheck'.
|
||||
types[i] = argType.intersection(param.staticType, typeHierarchy);
|
||||
} else {
|
||||
types[i] = args[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = positionalArgCount; i < positionalParameterCount; i++) {
|
||||
final Parameter param = _statements[i] as Parameter;
|
||||
assertx(param.staticType != null);
|
||||
final argType = param.defaultValue.specialize(typeHierarchy);
|
||||
param._observeArgumentType(argType, typeHierarchy);
|
||||
types[i] = argType;
|
||||
|
@ -380,4 +569,15 @@ class Summary {
|
|||
}
|
||||
return new Args<Type>(argTypes, names: argNames);
|
||||
}
|
||||
|
||||
List<VariableDeclaration> get staticCallSiteSkipCheckParams {
|
||||
final vars = <VariableDeclaration>[];
|
||||
for (final statement in _statements) {
|
||||
if (statement is TypeCheck && statement.canSkipOnStaticCallSite) {
|
||||
final decl = statement.parameter;
|
||||
if (decl != null) vars.add(decl);
|
||||
}
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:kernel/target/targets.dart';
|
|||
import 'package:kernel/ast.dart' hide Statement, StatementVisitor;
|
||||
import 'package:kernel/ast.dart' as ast show Statement, StatementVisitor;
|
||||
import 'package:kernel/type_environment.dart' show TypeEnvironment;
|
||||
import 'package:kernel/type_algebra.dart' show Substitution;
|
||||
|
||||
import 'calls.dart';
|
||||
import 'native_code.dart';
|
||||
|
@ -155,6 +156,36 @@ class _SummaryNormalizer extends StatementVisitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitCreateConcreteType(CreateConcreteType expr) {
|
||||
for (int i = 0; i < expr.flattenedTypeArgs.length; ++i) {
|
||||
expr.flattenedTypeArgs[i] =
|
||||
_normalizeExpr(expr.flattenedTypeArgs[i], true);
|
||||
if (_inLoop) return;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitCreateRuntimeType(CreateRuntimeType expr) {
|
||||
for (int i = 0; i < expr.flattenedTypeArgs.length; ++i) {
|
||||
expr.flattenedTypeArgs[i] =
|
||||
_normalizeExpr(expr.flattenedTypeArgs[i], true);
|
||||
if (_inLoop) return;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitTypeCheck(TypeCheck expr) {
|
||||
expr.arg = _normalizeExpr(expr.arg, true);
|
||||
if (_inLoop) return;
|
||||
expr.type = _normalizeExpr(expr.type, true);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitExtract(Extract expr) {
|
||||
expr.arg = _normalizeExpr(expr.arg, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Detects whether the control flow can pass through the function body and
|
||||
|
@ -241,32 +272,43 @@ class _FallthroughDetector extends ast.StatementVisitor<bool> {
|
|||
bool visitFunctionDeclaration(FunctionDeclaration node) => true;
|
||||
}
|
||||
|
||||
enum FieldSummaryType { kFieldGuard, kInitializer }
|
||||
|
||||
/// Create a type flow summary for a member from the kernel AST.
|
||||
class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
||||
final Target target;
|
||||
final TypeEnvironment _environment;
|
||||
final EntryPointsListener _entryPointsListener;
|
||||
final NativeCodeOracle _nativeCodeOracle;
|
||||
final GenericInterfacesInfo _genericInterfacesInfo;
|
||||
|
||||
final Map<TreeNode, Call> callSites = <TreeNode, Call>{};
|
||||
final _FallthroughDetector _fallthroughDetector = new _FallthroughDetector();
|
||||
|
||||
Summary _summary;
|
||||
Map<VariableDeclaration, Join> _variables;
|
||||
Map<VariableDeclaration, Join> _variableJoins;
|
||||
Map<VariableDeclaration, TypeExpr> _variables;
|
||||
Join _returnValue;
|
||||
Parameter _receiver;
|
||||
ConstantAllocationCollector constantAllocationCollector;
|
||||
RuntimeTypeTranslator _translator;
|
||||
|
||||
// Currently only used for factory constructors.
|
||||
Map<TypeParameter, TypeExpr> _fnTypeVariables;
|
||||
|
||||
SummaryCollector(this.target, this._environment, this._entryPointsListener,
|
||||
this._nativeCodeOracle) {
|
||||
this._nativeCodeOracle, this._genericInterfacesInfo) {
|
||||
assertx(_genericInterfacesInfo != null);
|
||||
constantAllocationCollector = new ConstantAllocationCollector(this);
|
||||
}
|
||||
|
||||
Summary createSummary(Member member) {
|
||||
Summary createSummary(Member member,
|
||||
{fieldSummaryType: FieldSummaryType.kInitializer}) {
|
||||
debugPrint("===== ${member} =====");
|
||||
assertx(!member.isAbstract);
|
||||
|
||||
_variables = <VariableDeclaration, Join>{};
|
||||
_variableJoins = <VariableDeclaration, Join>{};
|
||||
_variables = <VariableDeclaration, TypeExpr>{};
|
||||
_returnValue = null;
|
||||
_receiver = null;
|
||||
|
||||
|
@ -274,7 +316,10 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
|
||||
if (member is Field) {
|
||||
if (hasReceiver) {
|
||||
_summary = new Summary(parameterCount: 1, positionalParameterCount: 1);
|
||||
final int numArgs =
|
||||
fieldSummaryType == FieldSummaryType.kInitializer ? 1 : 2;
|
||||
_summary = new Summary(
|
||||
parameterCount: numArgs, positionalParameterCount: numArgs);
|
||||
// TODO(alexmarkov): subclass cone
|
||||
_receiver = _declareParameter(
|
||||
"this", member.enclosingClass.rawType, null,
|
||||
|
@ -283,12 +328,25 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
} else {
|
||||
_summary = new Summary();
|
||||
}
|
||||
assertx(member.initializer != null);
|
||||
_summary.result = _visit(member.initializer);
|
||||
|
||||
_translator = new RuntimeTypeTranslator(member.enclosingClass, _summary,
|
||||
_receiver, null, _genericInterfacesInfo);
|
||||
|
||||
if (fieldSummaryType == FieldSummaryType.kInitializer) {
|
||||
assertx(member.initializer != null);
|
||||
_summary.result = _visit(member.initializer);
|
||||
} else {
|
||||
Parameter valueParam = _declareParameter("value", member.type, null);
|
||||
TypeExpr runtimeType = _translator.translate(member.type);
|
||||
final check = new TypeCheck(valueParam, runtimeType, null);
|
||||
_summary.add(check);
|
||||
_summary.result = check;
|
||||
}
|
||||
} else {
|
||||
FunctionNode function = member.function;
|
||||
|
||||
final firstParamIndex = hasReceiver ? 1 : 0;
|
||||
final numTypeParameters = numTypeParams(member);
|
||||
final firstParamIndex = (hasReceiver ? 1 : 0) + numTypeParameters;
|
||||
|
||||
_summary = new Summary(
|
||||
parameterCount: firstParamIndex +
|
||||
|
@ -299,6 +357,14 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
requiredParameterCount:
|
||||
firstParamIndex + function.requiredParameterCount);
|
||||
|
||||
if (numTypeParameters > 0) {
|
||||
_fnTypeVariables = <TypeParameter, TypeExpr>{};
|
||||
for (int i = 0; i < numTypeParameters; ++i) {
|
||||
_fnTypeVariables[function.typeParameters[i]] =
|
||||
_declareParameter(function.typeParameters[i].name, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasReceiver) {
|
||||
// TODO(alexmarkov): subclass cone
|
||||
_receiver = _declareParameter(
|
||||
|
@ -307,6 +373,9 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
_environment.thisType = member.enclosingClass?.thisType;
|
||||
}
|
||||
|
||||
_translator = new RuntimeTypeTranslator(member.enclosingClass, _summary,
|
||||
_receiver, _fnTypeVariables, _genericInterfacesInfo);
|
||||
|
||||
for (VariableDeclaration param in function.positionalParameters) {
|
||||
_declareParameter(param.name, param.type, param.initializer);
|
||||
}
|
||||
|
@ -316,11 +385,13 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
|
||||
int count = firstParamIndex;
|
||||
for (VariableDeclaration param in function.positionalParameters) {
|
||||
Join v = _declareVariable(param);
|
||||
Join v =
|
||||
_declareVariable(param, useTypeCheck: param.isGenericCovariantImpl);
|
||||
v.values.add(_summary.statements[count++]);
|
||||
}
|
||||
for (VariableDeclaration param in function.namedParameters) {
|
||||
Join v = _declareVariable(param);
|
||||
Join v =
|
||||
_declareVariable(param, useTypeCheck: param.isGenericCovariantImpl);
|
||||
v.values.add(_summary.statements[count++]);
|
||||
}
|
||||
assertx(count == _summary.parameterCount);
|
||||
|
@ -377,6 +448,11 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
final List<Type> args = <Type>[];
|
||||
final List<String> names = <String>[];
|
||||
|
||||
final numTypeParameters = numTypeParams(member);
|
||||
for (int i = 0; i < numTypeParameters; ++i) {
|
||||
args.add(const AnyType());
|
||||
}
|
||||
|
||||
if (hasReceiverArg(member)) {
|
||||
assertx(member.enclosingClass != null);
|
||||
Type receiver = new Type.cone(member.enclosingClass.rawType);
|
||||
|
@ -422,8 +498,14 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
|
||||
TypeExpr _visit(TreeNode node) => node.accept(this);
|
||||
|
||||
Args<TypeExpr> _visitArguments(TypeExpr receiver, Arguments arguments) {
|
||||
Args<TypeExpr> _visitArguments(TypeExpr receiver, Arguments arguments,
|
||||
{bool passTypeArguments: false}) {
|
||||
final args = <TypeExpr>[];
|
||||
if (passTypeArguments) {
|
||||
for (var type in arguments.types) {
|
||||
args.add(_translator.translate(type));
|
||||
}
|
||||
}
|
||||
if (receiver != null) {
|
||||
args.add(receiver);
|
||||
}
|
||||
|
@ -451,8 +533,10 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
Parameter _declareParameter(
|
||||
String name, DartType type, Expression initializer,
|
||||
{bool isReceiver: false}) {
|
||||
Type staticType =
|
||||
isReceiver ? new ConeType(type) : new Type.fromStatic(type);
|
||||
Type staticType;
|
||||
if (type != null) {
|
||||
staticType = isReceiver ? new ConeType(type) : new Type.fromStatic(type);
|
||||
}
|
||||
final param = new Parameter(name, staticType);
|
||||
_summary.add(param);
|
||||
assertx(param.index < _summary.parameterCount);
|
||||
|
@ -474,17 +558,30 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
return param;
|
||||
}
|
||||
|
||||
Join _declareVariable(VariableDeclaration decl, {bool addInitType: false}) {
|
||||
Join v = new Join(decl.name, decl.type);
|
||||
_summary.add(v);
|
||||
_variables[decl] = v;
|
||||
Join _declareVariable(VariableDeclaration decl,
|
||||
{bool addInitType: false, bool useTypeCheck: false}) {
|
||||
Join join = new Join(decl.name, decl.type);
|
||||
_summary.add(join);
|
||||
_variableJoins[decl] = join;
|
||||
|
||||
TypeExpr variable = join;
|
||||
if (useTypeCheck) {
|
||||
TypeExpr runtimeType = _translator.translate(decl.type);
|
||||
variable = new TypeCheck(variable, runtimeType, decl);
|
||||
_summary.add(variable);
|
||||
_summary.add(new Use(variable));
|
||||
}
|
||||
|
||||
_variables[decl] = variable;
|
||||
|
||||
if (decl.initializer != null) {
|
||||
TypeExpr initType = _visit(decl.initializer);
|
||||
if (addInitType) {
|
||||
v.values.add(initType);
|
||||
join.values.add(initType);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
|
||||
return join;
|
||||
}
|
||||
|
||||
// TODO(alexmarkov): Avoid declaring variables with static types.
|
||||
|
@ -520,7 +617,7 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
void _addUse(TypeExpr arg) {
|
||||
if (arg is Narrow) {
|
||||
_addUse(arg.arg);
|
||||
} else if (arg is Join || arg is Call) {
|
||||
} else if (arg is Join || arg is Call || arg is TypeCheck) {
|
||||
_summary.add(new Use(arg));
|
||||
} else {
|
||||
assertx(arg is Type || arg is Parameter);
|
||||
|
@ -571,19 +668,24 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
}
|
||||
|
||||
void _handleNestedFunctionNode(FunctionNode node) {
|
||||
var oldReturn = _returnValue;
|
||||
var oldVariables = _variables;
|
||||
final oldReturn = _returnValue;
|
||||
final oldVariableJoins = _variableJoins;
|
||||
final oldVariables = _variables;
|
||||
_returnValue = null;
|
||||
_variables = <VariableDeclaration, Join>{};
|
||||
_variableJoins = <VariableDeclaration, Join>{};
|
||||
_variableJoins.addAll(oldVariableJoins);
|
||||
_variables = <VariableDeclaration, TypeExpr>{};
|
||||
_variables.addAll(oldVariables);
|
||||
|
||||
// Approximate parameters of nested functions with static types.
|
||||
// TODO(sjindel/tfa): Use TypeCheck for closure parameters.
|
||||
node.positionalParameters.forEach(_declareVariableWithStaticType);
|
||||
node.namedParameters.forEach(_declareVariableWithStaticType);
|
||||
|
||||
_visit(node.body);
|
||||
|
||||
_returnValue = oldReturn;
|
||||
_variableJoins = oldVariableJoins;
|
||||
_variables = oldVariables;
|
||||
}
|
||||
|
||||
|
@ -595,7 +697,16 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
TypeExpr visitAsExpression(AsExpression node) {
|
||||
TypeExpr operand = _visit(node.operand);
|
||||
Type type = new Type.fromStatic(node.type);
|
||||
return _makeNarrow(operand, type);
|
||||
|
||||
TypeExpr result = _makeNarrow(operand, type);
|
||||
|
||||
TypeExpr runtimeType = _translator.translate(node.type);
|
||||
if (runtimeType is Statement) {
|
||||
result = new TypeCheck(operand, runtimeType, /*parameter=*/ null);
|
||||
_summary.add(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -626,9 +737,10 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
|
||||
@override
|
||||
TypeExpr visitConstructorInvocation(ConstructorInvocation node) {
|
||||
final receiver =
|
||||
ConcreteType klass =
|
||||
_entryPointsListener.addAllocatedClass(node.constructedType.classNode);
|
||||
|
||||
TypeExpr receiver =
|
||||
_translator.instantiateConcreteType(klass, node.arguments.types);
|
||||
final args = _visitArguments(receiver, node.arguments);
|
||||
_makeCall(node, new DirectSelector(node.target), args);
|
||||
return receiver;
|
||||
|
@ -718,9 +830,12 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
node.expressions.forEach(_visit);
|
||||
Class concreteClass =
|
||||
target.concreteListLiteralClass(_environment.coreTypes);
|
||||
return concreteClass != null
|
||||
? _entryPointsListener.addAllocatedClass(concreteClass)
|
||||
: _staticType(node);
|
||||
if (concreteClass != null) {
|
||||
return _translator.instantiateConcreteType(
|
||||
_entryPointsListener.addAllocatedClass(concreteClass),
|
||||
[node.typeArgument]);
|
||||
}
|
||||
return _staticType(node);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -738,9 +853,12 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
}
|
||||
Class concreteClass =
|
||||
target.concreteMapLiteralClass(_environment.coreTypes);
|
||||
return concreteClass != null
|
||||
? _entryPointsListener.addAllocatedClass(concreteClass)
|
||||
: _staticType(node);
|
||||
if (concreteClass != null) {
|
||||
return _translator.instantiateConcreteType(
|
||||
_entryPointsListener.addAllocatedClass(concreteClass),
|
||||
[node.keyType, node.valueType]);
|
||||
}
|
||||
return _staticType(node);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -917,7 +1035,8 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
|
||||
@override
|
||||
TypeExpr visitStaticInvocation(StaticInvocation node) {
|
||||
final args = _visitArguments(null, node.arguments);
|
||||
final args = _visitArguments(null, node.arguments,
|
||||
passTypeArguments: node.target.isFactory);
|
||||
final target = node.target;
|
||||
assertx((target is! Field) && !target.isGetter && !target.isSetter);
|
||||
return _makeCall(node, new DirectSelector(target), args);
|
||||
|
@ -969,7 +1088,7 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
|
||||
@override
|
||||
TypeExpr visitVariableGet(VariableGet node) {
|
||||
Join v = _variables[node.variable];
|
||||
final v = _variables[node.variable];
|
||||
if (v == null) {
|
||||
throw 'Unable to find variable ${node.variable}';
|
||||
}
|
||||
|
@ -984,7 +1103,7 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
|
||||
@override
|
||||
TypeExpr visitVariableSet(VariableSet node) {
|
||||
Join v = _variables[node.variable];
|
||||
Join v = _variableJoins[node.variable];
|
||||
assertx(v != null, details: node);
|
||||
|
||||
TypeExpr value = _visit(node.value);
|
||||
|
@ -1221,6 +1340,141 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
}
|
||||
}
|
||||
|
||||
class RuntimeTypeTranslator extends DartTypeVisitor<TypeExpr> {
|
||||
final Class enclosingClass;
|
||||
final Summary summary;
|
||||
final Map<TypeParameter, TypeExpr> functionTypeVariables;
|
||||
final Map<DartType, TypeExpr> typesCache = <DartType, TypeExpr>{};
|
||||
final TypeExpr receiver;
|
||||
final GenericInterfacesInfo genericInterfacesInfo;
|
||||
|
||||
RuntimeTypeTranslator(this.enclosingClass, this.summary, this.receiver,
|
||||
this.functionTypeVariables, this.genericInterfacesInfo) {}
|
||||
|
||||
// Create a type translator which can be used only for types with no free type
|
||||
// variables.
|
||||
RuntimeTypeTranslator.forClosedTypes(this.genericInterfacesInfo)
|
||||
: enclosingClass = null,
|
||||
summary = null,
|
||||
functionTypeVariables = null,
|
||||
receiver = null {}
|
||||
|
||||
TypeExpr instantiateConcreteType(ConcreteType type, List<DartType> typeArgs) {
|
||||
if (typeArgs.isEmpty) return type;
|
||||
|
||||
// This function is very similar to 'visitInterfaceType', but with
|
||||
// many small differences.
|
||||
final klass = type.classNode;
|
||||
final substitution = Substitution.fromPairs(klass.typeParameters, typeArgs);
|
||||
final flattenedTypeArgs =
|
||||
genericInterfacesInfo.flattenedTypeArgumentsFor(klass);
|
||||
final flattenedTypeExprs = new List<TypeExpr>(flattenedTypeArgs.length);
|
||||
|
||||
bool createConcreteType = true;
|
||||
bool allAnyType = true;
|
||||
for (int i = 0; i < flattenedTypeArgs.length; ++i) {
|
||||
final typeExpr =
|
||||
translate(substitution.substituteType(flattenedTypeArgs[i]));
|
||||
if (typeExpr != const AnyType()) allAnyType = false;
|
||||
if (typeExpr is Statement) createConcreteType = false;
|
||||
flattenedTypeExprs[i] = typeExpr;
|
||||
}
|
||||
|
||||
if (allAnyType) return type;
|
||||
|
||||
if (createConcreteType) {
|
||||
return new ConcreteType(type.classId, type.classNode,
|
||||
new List<Type>.from(flattenedTypeExprs));
|
||||
} else {
|
||||
final instantiate = new CreateConcreteType(type, flattenedTypeExprs);
|
||||
summary.add(instantiate);
|
||||
return instantiate;
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a TypeExpr representing the set of types which can flow through a
|
||||
// given DartType.
|
||||
//
|
||||
// Will return AnyType, RuntimeType or Statement.
|
||||
TypeExpr translate(DartType type) {
|
||||
final cached = typesCache[type];
|
||||
if (cached != null) return cached;
|
||||
|
||||
// During type translation, loops can arise via super-bounded types:
|
||||
//
|
||||
// class A<T> extends Comparable<A<T>> {}
|
||||
//
|
||||
// Creating the factored type arguments of A will lead to an infinite loop.
|
||||
// We break such loops by inserting an 'AnyType' in place of the currently
|
||||
// processed type, ensuring we try to build 'A<T>' in the process of
|
||||
// building 'A<T>'.
|
||||
typesCache[type] = const AnyType();
|
||||
final result = type.accept(this);
|
||||
assertx(result is AnyType || result is RuntimeType || result is Statement);
|
||||
typesCache[type] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
TypeExpr defaultDartType(DartType node) => const AnyType();
|
||||
|
||||
@override
|
||||
TypeExpr visitDynamicType(DynamicType type) => new RuntimeType(type, null);
|
||||
@override
|
||||
TypeExpr visitVoidType(VoidType type) => new RuntimeType(type, null);
|
||||
@override
|
||||
TypeExpr visitBottomType(BottomType type) => new RuntimeType(type, null);
|
||||
|
||||
@override
|
||||
visitTypedefType(TypedefType node) => translate(node.unalias);
|
||||
|
||||
@override
|
||||
visitInterfaceType(InterfaceType type) {
|
||||
if (type.typeArguments.isEmpty) return new RuntimeType(type, null);
|
||||
|
||||
final substitution = Substitution.fromPairs(
|
||||
type.classNode.typeParameters, type.typeArguments);
|
||||
final flattenedTypeArgs =
|
||||
genericInterfacesInfo.flattenedTypeArgumentsFor(type.classNode);
|
||||
final flattenedTypeExprs = new List<TypeExpr>(flattenedTypeArgs.length);
|
||||
|
||||
bool createRuntimeType = true;
|
||||
for (var i = 0; i < flattenedTypeArgs.length; ++i) {
|
||||
final typeExpr =
|
||||
translate(substitution.substituteType(flattenedTypeArgs[i]));
|
||||
if (typeExpr == const AnyType()) return const AnyType();
|
||||
if (typeExpr is! RuntimeType) createRuntimeType = false;
|
||||
flattenedTypeExprs[i] = typeExpr;
|
||||
}
|
||||
|
||||
if (createRuntimeType) {
|
||||
return new RuntimeType(new InterfaceType(type.classNode),
|
||||
new List<RuntimeType>.from(flattenedTypeExprs));
|
||||
} else {
|
||||
final instantiate =
|
||||
new CreateRuntimeType(type.classNode, flattenedTypeExprs);
|
||||
summary.add(instantiate);
|
||||
return instantiate;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
visitTypeParameterType(TypeParameterType type) {
|
||||
if (functionTypeVariables != null) {
|
||||
final result = functionTypeVariables[type.parameter];
|
||||
if (result != null) return result;
|
||||
}
|
||||
if (type.parameter.parent is! Class) return const AnyType();
|
||||
assertx(type.parameter.parent == enclosingClass);
|
||||
|
||||
assertx(receiver != null);
|
||||
final extract = new Extract(receiver, enclosingClass,
|
||||
enclosingClass.typeParameters.indexOf(type.parameter));
|
||||
summary.add(extract);
|
||||
return extract;
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyEntryPointsListener implements EntryPointsListener {
|
||||
final Map<Class, IntClassId> _classIds = <Class, IntClassId>{};
|
||||
int _classIdCounter = 0;
|
||||
|
@ -1234,7 +1488,7 @@ class EmptyEntryPointsListener implements EntryPointsListener {
|
|||
@override
|
||||
ConcreteType addAllocatedClass(Class c) {
|
||||
final classId = (_classIds[c] ??= new IntClassId(++_classIdCounter));
|
||||
return new ConcreteType(classId, c.rawType);
|
||||
return new ConcreteType(classId, c, null);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1246,16 +1500,21 @@ class EmptyEntryPointsListener implements EntryPointsListener {
|
|||
|
||||
class CreateAllSummariesVisitor extends RecursiveVisitor<Null> {
|
||||
final TypeEnvironment _environment;
|
||||
final SummaryCollector _summaryColector;
|
||||
final SummaryCollector _summaryCollector;
|
||||
|
||||
CreateAllSummariesVisitor(Target target, this._environment)
|
||||
: _summaryColector = new SummaryCollector(target, _environment,
|
||||
new EmptyEntryPointsListener(), new NativeCodeOracle(null, null));
|
||||
CreateAllSummariesVisitor(
|
||||
Target target, this._environment, GenericInterfacesInfo hierarchy)
|
||||
: _summaryCollector = new SummaryCollector(
|
||||
target,
|
||||
_environment,
|
||||
new EmptyEntryPointsListener(),
|
||||
new NativeCodeOracle(null, null),
|
||||
hierarchy);
|
||||
|
||||
@override
|
||||
defaultMember(Member m) {
|
||||
if (!m.isAbstract && !(m is Field && m.initializer == null)) {
|
||||
_summaryColector.createSummary(m);
|
||||
_summaryCollector.createSummary(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,18 +41,20 @@ Component transformComponent(
|
|||
onAmbiguousSupertypes: ignoreAmbiguousSupertypes);
|
||||
final types = new TypeEnvironment(coreTypes, hierarchy, strongMode: true);
|
||||
final libraryIndex = new LibraryIndex.all(component);
|
||||
final genericInterfacesInfo = new GenericInterfacesInfoImpl(hierarchy);
|
||||
|
||||
if (kDumpAllSummaries) {
|
||||
Statistics.reset();
|
||||
new CreateAllSummariesVisitor(target, types).visitComponent(component);
|
||||
new CreateAllSummariesVisitor(target, types, genericInterfacesInfo)
|
||||
.visitComponent(component);
|
||||
Statistics.print("All summaries statistics");
|
||||
}
|
||||
|
||||
Statistics.reset();
|
||||
final analysisStopWatch = new Stopwatch()..start();
|
||||
|
||||
final typeFlowAnalysis = new TypeFlowAnalysis(
|
||||
target, component, coreTypes, hierarchy, types, libraryIndex,
|
||||
final typeFlowAnalysis = new TypeFlowAnalysis(target, component, coreTypes,
|
||||
hierarchy, genericInterfacesInfo, types, libraryIndex,
|
||||
matcher: matcher);
|
||||
|
||||
Procedure main = component.mainMethod;
|
||||
|
@ -127,7 +129,7 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
|
|||
component.addMetadataRepository(_procedureAttributesMetadata);
|
||||
}
|
||||
|
||||
InferredType _convertType(Type type) {
|
||||
InferredType _convertType(Type type, {bool skipCheck: false}) {
|
||||
assertx(type != null);
|
||||
|
||||
Class concreteClass;
|
||||
|
@ -148,15 +150,25 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
|
|||
}
|
||||
}
|
||||
|
||||
List<DartType> typeArgs;
|
||||
if (type is ConcreteType && type.typeArgs != null) {
|
||||
typeArgs = type.typeArgs
|
||||
.take(type.numImmediateTypeArgs)
|
||||
.map((t) => t is AnyType ? null : (t as RuntimeType).representedType)
|
||||
.toList();
|
||||
}
|
||||
|
||||
if ((concreteClass != null) || !nullable || isInt) {
|
||||
return new InferredType(concreteClass, nullable, isInt);
|
||||
return new InferredType(concreteClass, nullable, isInt,
|
||||
exactTypeArguments: typeArgs, skipCheck: skipCheck);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void _setInferredType(TreeNode node, Type type) {
|
||||
final inferredType = _convertType(type);
|
||||
void _setInferredType(TreeNode node, Type type, {bool skipCheck: false}) {
|
||||
assertx(skipCheck == false || node is VariableDeclaration || node is Field);
|
||||
final inferredType = _convertType(type, skipCheck: skipCheck);
|
||||
if (inferredType != null) {
|
||||
_inferredTypeMetadata.mapping[node] = inferredType;
|
||||
}
|
||||
|
@ -182,12 +194,17 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
|
|||
void _annotateMember(Member member) {
|
||||
if (_typeFlowAnalysis.isMemberUsed(member)) {
|
||||
if (member is Field) {
|
||||
_setInferredType(member, _typeFlowAnalysis.fieldType(member));
|
||||
_setInferredType(member, _typeFlowAnalysis.fieldType(member),
|
||||
skipCheck: _typeFlowAnalysis.fieldStaticCallSiteSkipCheck(member));
|
||||
} else {
|
||||
Args<Type> argTypes = _typeFlowAnalysis.argumentTypes(member);
|
||||
assertx(argTypes != null);
|
||||
|
||||
final int firstParamIndex = hasReceiverArg(member) ? 1 : 0;
|
||||
final skipCheckParams = new Set<VariableDeclaration>.from(
|
||||
_typeFlowAnalysis.staticCallSiteSkipCheckParams(member));
|
||||
|
||||
final int firstParamIndex =
|
||||
numTypeParams(member) + (hasReceiverArg(member) ? 1 : 0);
|
||||
|
||||
final positionalParams = member.function.positionalParameters;
|
||||
assertx(argTypes.positionalCount ==
|
||||
|
@ -195,7 +212,8 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
|
|||
|
||||
for (int i = 0; i < positionalParams.length; i++) {
|
||||
_setInferredType(
|
||||
positionalParams[i], argTypes.values[firstParamIndex + i]);
|
||||
positionalParams[i], argTypes.values[firstParamIndex + i],
|
||||
skipCheck: skipCheckParams.contains(positionalParams[i]));
|
||||
}
|
||||
|
||||
// TODO(dartbug.com/32292): make sure parameters are sorted in kernel
|
||||
|
@ -205,7 +223,8 @@ class AnnotateKernel extends RecursiveVisitor<Null> {
|
|||
final param = findNamedParameter(member.function, names[i]);
|
||||
assertx(param != null);
|
||||
_setInferredType(param,
|
||||
argTypes.values[firstParamIndex + positionalParams.length + i]);
|
||||
argTypes.values[firstParamIndex + positionalParams.length + i],
|
||||
skipCheck: skipCheckParams.contains(param));
|
||||
}
|
||||
|
||||
// TODO(alexmarkov): figure out how to pass receiver type.
|
||||
|
|
|
@ -11,14 +11,40 @@ import 'package:kernel/ast.dart';
|
|||
|
||||
import 'utils.dart';
|
||||
|
||||
abstract class GenericInterfacesInfo {
|
||||
// Return a type arguments vector which contains the immediate type parameters
|
||||
// to 'klass' as well as the type arguments to all generic supertypes of
|
||||
// 'klass', instantiated in terms of the type parameters on 'klass'.
|
||||
//
|
||||
// The offset into this vector from which a specific generic supertype's type
|
||||
// arguments can be found is given by 'genericInterfaceOffsetFor'.
|
||||
List<DartType> flattenedTypeArgumentsFor(Class klass);
|
||||
|
||||
// Return the offset into the flattened type arguments vector from which a
|
||||
// specific generic supertype's type arguments can be found. The flattened
|
||||
// type arguments vector is given by 'flattenedTypeArgumentsFor'.
|
||||
int genericInterfaceOffsetFor(Class klass, Class iface);
|
||||
|
||||
// Similar to 'flattenedTypeArgumentsFor', but works for non-generic classes
|
||||
// which may have recursive substitutions, e.g. 'class num implements
|
||||
// Comparable<num>'.
|
||||
//
|
||||
// Since there are no free type variables in the result, 'RuntimeType' is
|
||||
// returned instead of 'DartType'.
|
||||
List<Type> flattenedTypeArgumentsForNonGeneric(Class klass);
|
||||
}
|
||||
|
||||
/// Abstract interface to type hierarchy information used by types.
|
||||
abstract class TypeHierarchy {
|
||||
abstract class TypeHierarchy implements GenericInterfacesInfo {
|
||||
/// Test if [subType] is a subtype of [superType].
|
||||
bool isSubtype(DartType subType, DartType superType);
|
||||
|
||||
/// Return a more specific type for the type cone with [base] root.
|
||||
/// May return EmptyType, AnyType, ConcreteType or a SetType.
|
||||
Type specializeTypeCone(DartType base);
|
||||
|
||||
Class get futureOrClass;
|
||||
Class get futureClass;
|
||||
}
|
||||
|
||||
/// Basic normalization of Dart types.
|
||||
|
@ -97,6 +123,12 @@ abstract class Type extends TypeExpr {
|
|||
|
||||
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) => false;
|
||||
|
||||
// Returns 'true' if this type will definitely pass a runtime type-check
|
||||
// against 'runtimeType'. Returns 'false' if the test might fail (e.g. due to
|
||||
// an approximation).
|
||||
bool isSubtypeOfRuntimeType(
|
||||
TypeHierarchy typeHierarchy, RuntimeType runtimeType);
|
||||
|
||||
@override
|
||||
Type getComputedType(List<Type> types) => this;
|
||||
|
||||
|
@ -118,6 +150,7 @@ abstract class Type extends TypeExpr {
|
|||
|
||||
/// Order of precedence between types for evaluation of union/intersection.
|
||||
enum TypeOrder {
|
||||
RuntimeType,
|
||||
Empty,
|
||||
Nullable,
|
||||
Any,
|
||||
|
@ -147,6 +180,10 @@ class EmptyType extends Type {
|
|||
|
||||
@override
|
||||
Type intersection(Type other, TypeHierarchy typeHierarchy) => this;
|
||||
|
||||
bool isSubtypeOfRuntimeType(TypeHierarchy typeHierarchy, RuntimeType other) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Nullable type represents a union of a (non-nullable) type and the `null`
|
||||
|
@ -173,6 +210,9 @@ class NullableType extends Type {
|
|||
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
|
||||
baseType.isSubtypeOf(typeHierarchy, dartType);
|
||||
|
||||
bool isSubtypeOfRuntimeType(TypeHierarchy typeHierarchy, RuntimeType other) =>
|
||||
baseType.isSubtypeOfRuntimeType(typeHierarchy, other);
|
||||
|
||||
@override
|
||||
int get order => TypeOrder.Nullable.index;
|
||||
|
||||
|
@ -212,6 +252,7 @@ class NullableType extends Type {
|
|||
|
||||
/// Type representing any instance except `null`.
|
||||
/// Semantically equivalent to ConeType of Object, but more efficient.
|
||||
/// Can also represent a set of types, the set of all types.
|
||||
class AnyType extends Type {
|
||||
const AnyType();
|
||||
|
||||
|
@ -242,6 +283,10 @@ class AnyType extends Type {
|
|||
}
|
||||
return other;
|
||||
}
|
||||
|
||||
bool isSubtypeOfRuntimeType(TypeHierarchy typeHierarchy, RuntimeType other) {
|
||||
return typeHierarchy.isSubtype(const DynamicType(), other._type);
|
||||
}
|
||||
}
|
||||
|
||||
/// SetType is a union of concrete types T1, T2, ..., Tn, where n >= 2.
|
||||
|
@ -263,7 +308,7 @@ class SetType extends Type {
|
|||
int _computeHashCode() {
|
||||
int hash = 1237;
|
||||
for (var t in types) {
|
||||
hash = (((hash * 31) & kHashMask) + t.classId.hashCode) & kHashMask;
|
||||
hash = (((hash * 31) & kHashMask) + t.hashCode) & kHashMask;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
@ -272,7 +317,7 @@ class SetType extends Type {
|
|||
bool operator ==(other) {
|
||||
if ((other is SetType) && (types.length == other.types.length)) {
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (types[i].classId != other.types[i].classId) {
|
||||
if (types[i] != other.types[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -288,6 +333,9 @@ class SetType extends Type {
|
|||
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
|
||||
types.every((ConcreteType t) => t.isSubtypeOf(typeHierarchy, dartType));
|
||||
|
||||
bool isSubtypeOfRuntimeType(TypeHierarchy typeHierarchy, RuntimeType other) =>
|
||||
types.every((t) => t.isSubtypeOfRuntimeType(typeHierarchy, other));
|
||||
|
||||
@override
|
||||
int get order => TypeOrder.Set.index;
|
||||
|
||||
|
@ -307,8 +355,13 @@ class SetType extends Type {
|
|||
types.add(t2);
|
||||
++i2;
|
||||
} else {
|
||||
assertx(t1 == t2);
|
||||
types.add(t1);
|
||||
if (t1 == t2) {
|
||||
types.add(t1);
|
||||
} else {
|
||||
// TODO(sjindel/tfa): Merge the type arguments vectors.
|
||||
// (e.g., Map<?, int> vs Map<String, int> can become Map<?, int>)
|
||||
types.add(t1.raw);
|
||||
}
|
||||
++i1;
|
||||
++i2;
|
||||
}
|
||||
|
@ -335,8 +388,14 @@ class SetType extends Type {
|
|||
} else if (relation > 0) {
|
||||
++i2;
|
||||
} else {
|
||||
assertx(t1 == t2);
|
||||
types.add(t1);
|
||||
if (t1.typeArgs == null && t2.typeArgs == null) {
|
||||
types.add(t1);
|
||||
} else {
|
||||
final intersect = t1.intersection(t2, null);
|
||||
if (intersect is! EmptyType) {
|
||||
types.add(intersect);
|
||||
}
|
||||
}
|
||||
++i1;
|
||||
++i2;
|
||||
}
|
||||
|
@ -380,7 +439,13 @@ class SetType extends Type {
|
|||
return new SetType(list);
|
||||
}
|
||||
} else if (other is ConcreteType) {
|
||||
return types.contains(other) ? other : const EmptyType();
|
||||
for (var type in types) {
|
||||
if (type == other) return other;
|
||||
if (type.classId == other.classId) {
|
||||
return type.intersection(other, typeHierarchy);
|
||||
}
|
||||
}
|
||||
return EmptyType();
|
||||
} else if (other is ConeType) {
|
||||
return typeHierarchy
|
||||
.specializeTypeCone(other.dartType)
|
||||
|
@ -410,6 +475,14 @@ class ConeType extends Type {
|
|||
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
|
||||
typeHierarchy.isSubtype(this.dartType, dartType);
|
||||
|
||||
bool isSubtypeOfRuntimeType(TypeHierarchy typeHierarchy, RuntimeType other) {
|
||||
if (!typeHierarchy.isSubtype(dartType, other._type)) return false;
|
||||
if (dartType is InterfaceType) {
|
||||
return (dartType as InterfaceType).classNode.typeParameters.isEmpty;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => (dartType.hashCode + 37) & kHashMask;
|
||||
|
||||
|
@ -446,7 +519,7 @@ class ConeType extends Type {
|
|||
return other;
|
||||
}
|
||||
} else if (other is ConcreteType) {
|
||||
if (typeHierarchy.isSubtype(other.dartType, this.dartType)) {
|
||||
if (typeHierarchy.isSubtype(other.classNode.rawType, this.dartType)) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -471,7 +544,7 @@ class ConeType extends Type {
|
|||
return this;
|
||||
}
|
||||
} else if (other is ConcreteType) {
|
||||
if (typeHierarchy.isSubtype(other.dartType, this.dartType)) {
|
||||
if (typeHierarchy.isSubtype(other.classNode.rawType, this.dartType)) {
|
||||
return other;
|
||||
} else {
|
||||
return const EmptyType();
|
||||
|
@ -503,37 +576,138 @@ class IntClassId extends ClassId<IntClassId> {
|
|||
/// or `null` object).
|
||||
class ConcreteType extends Type implements Comparable<ConcreteType> {
|
||||
final ClassId classId;
|
||||
final DartType dartType;
|
||||
final Class classNode;
|
||||
int _hashCode;
|
||||
|
||||
ConcreteType(this.classId, this.dartType) {
|
||||
// TODO(alexmarkov): support generics & closures
|
||||
assertx(dartType is InterfaceType);
|
||||
assertx(!(dartType as InterfaceType).classNode.isAbstract);
|
||||
assertx((dartType as InterfaceType)
|
||||
.typeArguments
|
||||
.every((t) => t == const DynamicType()));
|
||||
// May be null if there are no type arguments constraints. The type arguments
|
||||
// should represent type sets, i.e. `AnyType` or `RuntimeType`. The type
|
||||
// arguments vector is factored against the generic interfaces implemented by
|
||||
// the class (see [TypeHierarchy.flattenedTypeArgumentsFor]).
|
||||
//
|
||||
// The 'typeArgs' vector is null for non-generic classes, even if they
|
||||
// implement a generic interface.
|
||||
//
|
||||
// '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;
|
||||
|
||||
ConcreteType(this.classId, this.classNode, [List<Type> typeArgs_])
|
||||
: typeArgs = typeArgs_,
|
||||
numImmediateTypeArgs =
|
||||
typeArgs_ != null ? classNode.typeParameters.length : 0 {
|
||||
// TODO(alexmarkov): support closures
|
||||
assertx(!classNode.isAbstract);
|
||||
assertx(typeArgs == null || classNode.typeParameters.isNotEmpty);
|
||||
assertx(typeArgs == null || typeArgs.any((t) => t is RuntimeType));
|
||||
}
|
||||
|
||||
ConcreteType get raw => new ConcreteType(classId, classNode, null);
|
||||
|
||||
@override
|
||||
Class getConcreteClass(TypeHierarchy typeHierarchy) =>
|
||||
(dartType as InterfaceType).classNode;
|
||||
Class getConcreteClass(TypeHierarchy typeHierarchy) => classNode;
|
||||
|
||||
@override
|
||||
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
|
||||
typeHierarchy.isSubtype(this.dartType, dartType);
|
||||
typeHierarchy.isSubtype(classNode.rawType, dartType);
|
||||
|
||||
bool isSubtypeOfRuntimeType(
|
||||
TypeHierarchy typeHierarchy, RuntimeType runtimeType) {
|
||||
if (!typeHierarchy.isSubtype(this.classNode.rawType, runtimeType._type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InterfaceType runtimeDartType;
|
||||
if (runtimeType._type is InterfaceType) {
|
||||
runtimeDartType = runtimeType._type;
|
||||
if (runtimeDartType.typeArguments.isEmpty) return true;
|
||||
if (runtimeDartType.classNode == typeHierarchy.futureOrClass) {
|
||||
if (typeHierarchy.isSubtype(
|
||||
classNode.rawType, typeHierarchy.futureClass.rawType) ||
|
||||
classNode == typeHierarchy.futureOrClass) {
|
||||
final RuntimeType lhs =
|
||||
typeArgs == null ? RuntimeType(DynamicType(), null) : typeArgs[0];
|
||||
return lhs.isSubtypeOfRuntimeType(
|
||||
typeHierarchy, runtimeType.typeArgs[0]);
|
||||
} else {
|
||||
return isSubtypeOfRuntimeType(typeHierarchy, runtimeType.typeArgs[0]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The TypeHierarchy result may be inaccurate only if there are type
|
||||
// arguments which it doesn't examine.
|
||||
return true;
|
||||
}
|
||||
|
||||
List<Type> usableTypeArgs = typeArgs;
|
||||
if (usableTypeArgs == null) {
|
||||
if (classNode.typeParameters.isEmpty) {
|
||||
usableTypeArgs =
|
||||
typeHierarchy.flattenedTypeArgumentsForNonGeneric(classNode);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final interfaceOffset = typeHierarchy.genericInterfaceOffsetFor(
|
||||
classNode, runtimeDartType.classNode);
|
||||
|
||||
assertx(usableTypeArgs.length - interfaceOffset >=
|
||||
runtimeType.numImmediateTypeArgs);
|
||||
|
||||
for (int i = 0; i < runtimeType.numImmediateTypeArgs; ++i) {
|
||||
if (usableTypeArgs[i + interfaceOffset] == const AnyType()) return false;
|
||||
assertx(usableTypeArgs[i + interfaceOffset] is RuntimeType);
|
||||
if (!usableTypeArgs[i + interfaceOffset]
|
||||
.isSubtypeOfRuntimeType(typeHierarchy, runtimeType.typeArgs[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => (classId.hashCode ^ 0x1234) & kHashMask;
|
||||
int get hashCode => _hashCode ??= _computeHashCode();
|
||||
|
||||
int _computeHashCode() {
|
||||
int hash = classId.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;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
(other is ConcreteType) && (this.classId == other.classId);
|
||||
bool operator ==(other) {
|
||||
if (other is ConcreteType) {
|
||||
if (this.classId != other.classId ||
|
||||
this.numImmediateTypeArgs != other.numImmediateTypeArgs) {
|
||||
return false;
|
||||
}
|
||||
if (this.typeArgs != null) {
|
||||
for (int i = 0; i < numImmediateTypeArgs; ++i) {
|
||||
if (this.typeArgs[i] != other.typeArgs[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that this may return 0 for concrete types which are not equal if the
|
||||
// difference is only in type arguments.
|
||||
@override
|
||||
int compareTo(ConcreteType other) => classId.compareTo(other.classId);
|
||||
|
||||
@override
|
||||
String toString() => "_T (${dartType})";
|
||||
String toString() => typeArgs == null
|
||||
? "_T (${classNode})"
|
||||
: "_T (${classNode}<${typeArgs.take(numImmediateTypeArgs).join(', ')}>)";
|
||||
|
||||
@override
|
||||
int get order => TypeOrder.Concrete.index;
|
||||
|
@ -546,13 +720,14 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
|||
if (other is ConcreteType) {
|
||||
if (this == other) {
|
||||
return this;
|
||||
} else {
|
||||
assertx(this.classId != other.classId);
|
||||
final List<ConcreteType> types =
|
||||
(this.classId.compareTo(other.classId) < 0)
|
||||
? <ConcreteType>[this, other]
|
||||
: <ConcreteType>[other, this];
|
||||
} else if (this.classId != other.classId) {
|
||||
final types = (this.classId.compareTo(other.classId) < 0)
|
||||
? <ConcreteType>[this, other]
|
||||
: <ConcreteType>[other, this];
|
||||
return new SetType(types);
|
||||
} else {
|
||||
assertx(typeArgs != null || other.typeArgs != null);
|
||||
return raw;
|
||||
}
|
||||
} else {
|
||||
throw 'Unexpected type $other';
|
||||
|
@ -567,11 +742,201 @@ class ConcreteType extends Type implements Comparable<ConcreteType> {
|
|||
if (other is ConcreteType) {
|
||||
if (this == other) {
|
||||
return this;
|
||||
} else {
|
||||
return const EmptyType();
|
||||
}
|
||||
if (this.classId != other.classId) {
|
||||
return EmptyType();
|
||||
}
|
||||
assertx(typeArgs != null || other.typeArgs != null);
|
||||
if (typeArgs == null) {
|
||||
return other;
|
||||
} else if (other.typeArgs == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
final mergedTypeArgs = new List<Type>(typeArgs.length);
|
||||
bool hasRuntimeType = false;
|
||||
for (int i = 0; i < typeArgs.length; ++i) {
|
||||
final merged =
|
||||
typeArgs[i].intersection(other.typeArgs[i], typeHierarchy);
|
||||
if (merged is EmptyType) {
|
||||
return EmptyType();
|
||||
} else if (merged is RuntimeType) {
|
||||
hasRuntimeType = true;
|
||||
}
|
||||
mergedTypeArgs[i] = merged;
|
||||
}
|
||||
if (!hasRuntimeType) return raw;
|
||||
return new ConcreteType(classId, classNode, mergedTypeArgs);
|
||||
} else {
|
||||
throw 'Unexpected type $other';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unlike the other 'Type's, this represents a single type, not a set of
|
||||
// values. It is used as the right-hand-side of type-tests.
|
||||
//
|
||||
// The type arguments are represented in a form that is factored against the
|
||||
// generic interfaces implemented by the type to enable efficient type-test
|
||||
// against its interfaces. See 'TypeHierarchy.flattenedTypeArgumentsFor' for
|
||||
// more details.
|
||||
//
|
||||
// This factored representation can have cycles for some types:
|
||||
//
|
||||
// class num implements Comparable<num> {}
|
||||
// class A<T> extends Comparable<A<T>> {}
|
||||
//
|
||||
// To avoid these cycles, we approximate generic super-bounded types (the second
|
||||
// case), so the representation for 'A<String>' would be simply 'AnyType'.
|
||||
// However, approximating non-generic types like 'int' and 'num' (the first
|
||||
// case) would be too coarse, so we leave an null 'typeArgs' field for these
|
||||
// types. As a result, when doing an 'isSubtypeOfRuntimeType' against
|
||||
// their interfaces (e.g. 'int' vs 'Comparable<int>') we approximate the result
|
||||
// as 'false'.
|
||||
//
|
||||
// So, the invariant about 'typeArgs' is that they will be 'null' iff the class
|
||||
// is non-generic, and non-null (with at least one vector) otherwise.
|
||||
class RuntimeType extends Type {
|
||||
final DartType _type; // Doesn't contain type args.
|
||||
|
||||
final int numImmediateTypeArgs;
|
||||
final List<RuntimeType> typeArgs;
|
||||
|
||||
RuntimeType(DartType type, this.typeArgs)
|
||||
: _type = type,
|
||||
numImmediateTypeArgs =
|
||||
type is InterfaceType ? type.classNode.typeParameters.length : 0 {
|
||||
if (_type is InterfaceType && numImmediateTypeArgs > 0) {
|
||||
assertx(typeArgs != null);
|
||||
assertx(typeArgs.length >= numImmediateTypeArgs);
|
||||
assertx((_type as InterfaceType)
|
||||
.typeArguments
|
||||
.every((t) => t == const DynamicType()));
|
||||
} else {
|
||||
assertx(typeArgs == null);
|
||||
}
|
||||
}
|
||||
|
||||
int get order => TypeOrder.RuntimeType.index;
|
||||
|
||||
DartType get representedTypeRaw => _type;
|
||||
|
||||
DartType get representedType {
|
||||
if (_type is InterfaceType && typeArgs != null) {
|
||||
final klass = (_type as InterfaceType).classNode;
|
||||
final typeArguments = typeArgs
|
||||
.take(klass.typeParameters.length)
|
||||
.map((pt) => pt.representedType)
|
||||
.toList();
|
||||
return new InterfaceType(klass, typeArguments);
|
||||
} else {
|
||||
return _type;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
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;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@override
|
||||
operator ==(other) {
|
||||
if (other is RuntimeType) {
|
||||
if (other._type != _type) return false;
|
||||
assertx(numImmediateTypeArgs == other.numImmediateTypeArgs);
|
||||
return typeArgs == null || listEquals(typeArgs, other.typeArgs);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final head = _type is InterfaceType
|
||||
? "${(_type as InterfaceType).classNode}"
|
||||
: "$_type";
|
||||
if (numImmediateTypeArgs == 0) return head;
|
||||
final typeArgsStrs =
|
||||
typeArgs.take(numImmediateTypeArgs).map((t) => "$t").join(", ");
|
||||
return "_TS {$head<$typeArgsStrs>}";
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isSpecialized =>
|
||||
throw "ERROR: RuntimeType does not support isSpecialized.";
|
||||
|
||||
@override
|
||||
bool isSubtypeOf(TypeHierarchy typeHierarchy, DartType dartType) =>
|
||||
throw "ERROR: RuntimeType does not support isSubtypeOf.";
|
||||
|
||||
@override
|
||||
Type union(Type other, TypeHierarchy typeHierarchy) =>
|
||||
throw "ERROR: RuntimeType does not support union.";
|
||||
|
||||
// This only works between "type-set" representations ('AnyType' and
|
||||
// 'RuntimeType') and is used when merging type arguments.
|
||||
@override
|
||||
Type intersection(Type other, TypeHierarchy typeHierarchy) {
|
||||
if (other is AnyType) {
|
||||
return this;
|
||||
} else if (other is RuntimeType) {
|
||||
return this == other ? this : const EmptyType();
|
||||
}
|
||||
throw "ERROR: RuntimeType cannot intersect with ${other.runtimeType}";
|
||||
}
|
||||
|
||||
@override
|
||||
Type specialize(TypeHierarchy typeHierarchy) =>
|
||||
throw "ERROR: RuntimeType does not support specialize.";
|
||||
|
||||
@override
|
||||
Class getConcreteClass(TypeHierarchy typeHierarchy) =>
|
||||
throw "ERROR: ConcreteClass does not support getConcreteClass.";
|
||||
|
||||
bool isSubtypeOfRuntimeType(
|
||||
TypeHierarchy typeHierarchy, RuntimeType runtimeType) {
|
||||
if (!typeHierarchy.isSubtype(this._type, runtimeType._type)) return false;
|
||||
|
||||
// The typeHierarchy result maybe be inaccurate only if there are type
|
||||
// arguments which need to be examined.
|
||||
if (_type is! InterfaceType || runtimeType.numImmediateTypeArgs == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final thisClass = (_type as InterfaceType).classNode;
|
||||
final otherClass = (runtimeType._type as InterfaceType).classNode;
|
||||
|
||||
if (otherClass == typeHierarchy.futureOrClass) {
|
||||
if (thisClass == typeHierarchy.futureClass ||
|
||||
thisClass == typeHierarchy.futureOrClass) {
|
||||
return typeArgs[0]
|
||||
.isSubtypeOfRuntimeType(typeHierarchy, runtimeType.typeArgs[0]);
|
||||
} else {
|
||||
return isSubtypeOfRuntimeType(typeHierarchy, runtimeType.typeArgs[0]);
|
||||
}
|
||||
}
|
||||
|
||||
List<Type> usableTypeArgs = typeArgs;
|
||||
if (usableTypeArgs == null) {
|
||||
assertx(thisClass.typeParameters.isEmpty);
|
||||
usableTypeArgs =
|
||||
typeHierarchy.flattenedTypeArgumentsForNonGeneric(thisClass);
|
||||
}
|
||||
final interfaceOffset =
|
||||
typeHierarchy.genericInterfaceOffsetFor(thisClass, otherClass);
|
||||
assertx(usableTypeArgs.length - interfaceOffset >=
|
||||
runtimeType.numImmediateTypeArgs);
|
||||
for (int i = 0; i < runtimeType.numImmediateTypeArgs; ++i) {
|
||||
if (!usableTypeArgs[interfaceOffset + i]
|
||||
.isSubtypeOfRuntimeType(typeHierarchy, runtimeType.typeArgs[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,14 @@
|
|||
library vm.transformations.type_flow.utils;
|
||||
|
||||
import 'package:kernel/ast.dart'
|
||||
show Constructor, FunctionNode, Member, VariableDeclaration;
|
||||
show
|
||||
Class,
|
||||
Constructor,
|
||||
DartType,
|
||||
Procedure,
|
||||
FunctionNode,
|
||||
Member,
|
||||
VariableDeclaration;
|
||||
|
||||
const bool kPrintTrace =
|
||||
const bool.fromEnvironment('global.type.flow.print.trace');
|
||||
|
@ -51,6 +58,14 @@ const int kHashMask = 0x3fffffff;
|
|||
bool hasReceiverArg(Member member) =>
|
||||
member.isInstanceMember || (member is Constructor);
|
||||
|
||||
// Type arguments to procedures is only supported for factory constructors of
|
||||
// generic classes at the moment.
|
||||
//
|
||||
// TODO(sjindel/tfa): Extend suport to normal generic functions.
|
||||
int numTypeParams(Member member) => member is Procedure && member.isFactory
|
||||
? member.function.typeParameters.length
|
||||
: 0;
|
||||
|
||||
/// Returns true if elements in [list] are in strictly increasing order.
|
||||
/// List with duplicates is considered not sorted.
|
||||
bool isSorted(List list) {
|
||||
|
@ -148,3 +163,43 @@ class Statistics {
|
|||
""");
|
||||
}
|
||||
}
|
||||
|
||||
int typeArgumentsHash(List<DartType> typeArgs) {
|
||||
int hash = 1237;
|
||||
for (var t in typeArgs) {
|
||||
hash = (((hash * 31) & kHashMask) + t.hashCode) & kHashMask;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
class SubtypePair {
|
||||
final Class subtype;
|
||||
final Class supertype;
|
||||
|
||||
SubtypePair(this.subtype, this.supertype);
|
||||
|
||||
int get hashCode {
|
||||
return subtype.hashCode ^ supertype.hashCode;
|
||||
}
|
||||
|
||||
bool operator ==(Object other) {
|
||||
if (other is SubtypePair) {
|
||||
return subtype == other.subtype && supertype == other.supertype;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the smallest index 'i' such that 'list.skip(i)' is a prefix of
|
||||
// 'sublist'.
|
||||
int findOverlap(List list, List sublist) {
|
||||
for (int i = 0; i < list.length; ++i)
|
||||
outer:
|
||||
{
|
||||
for (int j = 0; j < sublist.length && i + j < list.length; ++j) {
|
||||
if (list[i + j] != sublist[j]) continue outer;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
return list.length;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:kernel/type_environment.dart';
|
|||
import 'package:test/test.dart';
|
||||
import 'package:vm/transformations/type_flow/native_code.dart';
|
||||
import 'package:vm/transformations/type_flow/summary_collector.dart';
|
||||
import 'package:vm/transformations/type_flow/analysis.dart';
|
||||
import 'annotation_matcher.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
|
||||
|
@ -29,7 +30,8 @@ class PrintSummaries extends RecursiveVisitor<Null> {
|
|||
environment,
|
||||
new EmptyEntryPointsListener(),
|
||||
new NativeCodeOracle(
|
||||
null, new ExpressionPragmaAnnotationParser(coreTypes)));
|
||||
null, new ExpressionPragmaAnnotationParser(coreTypes)),
|
||||
new GenericInterfacesInfoImpl(environment.hierarchy));
|
||||
|
||||
String print(TreeNode node) {
|
||||
visitLibrary(node);
|
||||
|
|
|
@ -39,8 +39,9 @@ main() {
|
|||
final testCasesDir = new Directory(
|
||||
pkgVmDir + '/testcases/transformations/type_flow/transformer');
|
||||
|
||||
for (var entry
|
||||
in testCasesDir.listSync(recursive: true, followLinks: false)) {
|
||||
for (var entry in testCasesDir
|
||||
.listSync(recursive: true, followLinks: false)
|
||||
.reversed) {
|
||||
if (entry.path.endsWith(".dart")) {
|
||||
test(entry.path, () => runTestCase(entry.uri));
|
||||
}
|
||||
|
|
|
@ -26,6 +26,19 @@ class TestTypeHierarchy implements TypeHierarchy {
|
|||
reason: "specializeTypeCone($base) is not defined");
|
||||
return result;
|
||||
}
|
||||
|
||||
List<DartType> flattenedTypeArgumentsFor(Class klass) =>
|
||||
throw "flattenedTypeArgumentsFor is not supported in the types test.";
|
||||
|
||||
int genericInterfaceOffsetFor(Class klass, Class iface) =>
|
||||
throw "genericInterfaceOffsetFor is not supported in the types test.";
|
||||
|
||||
List<Type> flattenedTypeArgumentsForNonGeneric(Class klass) =>
|
||||
throw "flattenedTypeArgumentsFor is not supported in the types test.";
|
||||
|
||||
Class get futureOrClass =>
|
||||
throw "futureOrClass not supported in the types test.";
|
||||
Class get futureClass => throw "futureClass not supported in the types test.";
|
||||
}
|
||||
|
||||
main() {
|
||||
|
@ -75,10 +88,10 @@ main() {
|
|||
|
||||
final empty = new EmptyType();
|
||||
final any = new AnyType();
|
||||
final concreteT1 = new ConcreteType(const IntClassId(1), t1);
|
||||
final concreteT2 = new ConcreteType(const IntClassId(2), t2);
|
||||
final concreteT3 = new ConcreteType(const IntClassId(3), t3);
|
||||
final concreteT4 = new ConcreteType(const IntClassId(4), t4);
|
||||
final concreteT1 = new ConcreteType(const IntClassId(1), t1.classNode);
|
||||
final concreteT2 = new ConcreteType(const IntClassId(2), t2.classNode);
|
||||
final concreteT3 = new ConcreteType(const IntClassId(3), t3.classNode);
|
||||
final concreteT4 = new ConcreteType(const IntClassId(4), t4.classNode);
|
||||
final coneT1 = new ConeType(t1);
|
||||
final coneT2 = new ConeType(t2);
|
||||
final coneT3 = new ConeType(t3);
|
||||
|
@ -261,7 +274,6 @@ main() {
|
|||
final t1a = new InterfaceType(c1);
|
||||
final t1b = new InterfaceType(c1);
|
||||
final t2 = new InterfaceType(c2);
|
||||
final t3 = new InterfaceType(c3);
|
||||
final f1a = new FunctionType([t1a], const VoidType());
|
||||
final f1b = new FunctionType([t1b], const VoidType());
|
||||
final f2 = new FunctionType([t1a, t1a], const VoidType());
|
||||
|
@ -292,27 +304,27 @@ main() {
|
|||
|
||||
eq(new EmptyType(), new EmptyType());
|
||||
ne(new EmptyType(), new AnyType());
|
||||
ne(new EmptyType(), new ConcreteType(cid1, t1a));
|
||||
ne(new EmptyType(), new ConcreteType(cid1, c1));
|
||||
ne(new EmptyType(), new ConeType(t1a));
|
||||
ne(new EmptyType(),
|
||||
new SetType([new ConcreteType(cid1, t1a), new ConcreteType(cid2, t2)]));
|
||||
new SetType([new ConcreteType(cid1, c1), new ConcreteType(cid2, c2)]));
|
||||
ne(new EmptyType(), new NullableType(new EmptyType()));
|
||||
|
||||
eq(new AnyType(), new AnyType());
|
||||
ne(new AnyType(), new ConcreteType(cid1, t1a));
|
||||
ne(new AnyType(), new ConcreteType(cid1, c1));
|
||||
ne(new AnyType(), new ConeType(t1a));
|
||||
ne(new AnyType(),
|
||||
new SetType([new ConcreteType(cid1, t1a), new ConcreteType(cid2, t2)]));
|
||||
new SetType([new ConcreteType(cid1, c1), new ConcreteType(cid2, c2)]));
|
||||
ne(new AnyType(), new NullableType(new EmptyType()));
|
||||
|
||||
eq(new ConcreteType(cid1, t1a), new ConcreteType(cid1, t1b));
|
||||
ne(new ConcreteType(cid1, t1a), new ConcreteType(cid2, t2));
|
||||
ne(new ConcreteType(cid1, t1a), new ConeType(t1a));
|
||||
ne(new ConcreteType(cid1, t1a), new ConeType(t2));
|
||||
ne(new ConcreteType(cid1, t1a),
|
||||
new SetType([new ConcreteType(cid1, t1a), new ConcreteType(cid2, t2)]));
|
||||
ne(new ConcreteType(cid1, t1a),
|
||||
new NullableType(new ConcreteType(cid1, t1a)));
|
||||
eq(new ConcreteType(cid1, c1), new ConcreteType(cid1, c1));
|
||||
ne(new ConcreteType(cid1, c1), new ConcreteType(cid2, c2));
|
||||
ne(new ConcreteType(cid1, c1), new ConeType(t1a));
|
||||
ne(new ConcreteType(cid1, c1), new ConeType(t2));
|
||||
ne(new ConcreteType(cid1, c1),
|
||||
new SetType([new ConcreteType(cid1, c1), new ConcreteType(cid2, c2)]));
|
||||
ne(new ConcreteType(cid1, c1),
|
||||
new NullableType(new ConcreteType(cid1, c1)));
|
||||
|
||||
eq(new ConeType(t1a), new ConeType(t1b));
|
||||
eq(new ConeType(f1a), new ConeType(f1b));
|
||||
|
@ -320,34 +332,34 @@ main() {
|
|||
ne(new ConeType(f1a), new ConeType(f2));
|
||||
ne(new ConeType(t1a), new ConeType(f1a));
|
||||
ne(new ConeType(t1a),
|
||||
new SetType([new ConcreteType(cid1, t1a), new ConcreteType(cid2, t2)]));
|
||||
new SetType([new ConcreteType(cid1, c1), new ConcreteType(cid2, c2)]));
|
||||
ne(new ConeType(t1a), new NullableType(new ConeType(t1a)));
|
||||
|
||||
eq(new SetType([new ConcreteType(cid1, t1a), new ConcreteType(cid2, t2)]),
|
||||
new SetType([new ConcreteType(cid1, t1b), new ConcreteType(cid2, t2)]));
|
||||
eq(new SetType([new ConcreteType(cid1, c1), new ConcreteType(cid2, c2)]),
|
||||
new SetType([new ConcreteType(cid1, c1), new ConcreteType(cid2, c2)]));
|
||||
eq(
|
||||
new SetType([
|
||||
new ConcreteType(cid1, t1a),
|
||||
new ConcreteType(cid2, t2),
|
||||
new ConcreteType(cid3, t3)
|
||||
new ConcreteType(cid1, c1),
|
||||
new ConcreteType(cid2, c2),
|
||||
new ConcreteType(cid3, c3)
|
||||
]),
|
||||
new SetType([
|
||||
new ConcreteType(cid1, t1b),
|
||||
new ConcreteType(cid2, t2),
|
||||
new ConcreteType(cid3, t3)
|
||||
new ConcreteType(cid1, c1),
|
||||
new ConcreteType(cid2, c2),
|
||||
new ConcreteType(cid3, c3)
|
||||
]));
|
||||
ne(
|
||||
new SetType([new ConcreteType(cid1, t1a), new ConcreteType(cid2, t2)]),
|
||||
new SetType([new ConcreteType(cid1, c1), new ConcreteType(cid2, c2)]),
|
||||
new SetType([
|
||||
new ConcreteType(cid1, t1a),
|
||||
new ConcreteType(cid2, t2),
|
||||
new ConcreteType(cid3, t3)
|
||||
new ConcreteType(cid1, c1),
|
||||
new ConcreteType(cid2, c2),
|
||||
new ConcreteType(cid3, c3)
|
||||
]));
|
||||
ne(new SetType([new ConcreteType(cid1, t1a), new ConcreteType(cid2, t2)]),
|
||||
new SetType([new ConcreteType(cid1, t1a), new ConcreteType(cid3, t3)]));
|
||||
ne(new SetType([new ConcreteType(cid1, c1), new ConcreteType(cid2, c2)]),
|
||||
new SetType([new ConcreteType(cid1, c1), new ConcreteType(cid3, c3)]));
|
||||
ne(
|
||||
new SetType([new ConcreteType(cid1, t1a), new ConcreteType(cid2, t2)]),
|
||||
new SetType([new ConcreteType(cid1, c1), new ConcreteType(cid2, c2)]),
|
||||
new NullableType(new SetType(
|
||||
[new ConcreteType(cid1, t1a), new ConcreteType(cid2, t2)])));
|
||||
[new ConcreteType(cid1, c1), new ConcreteType(cid2, c2)])));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
------------ #lib::C:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::C<dynamic>)+]
|
||||
t1 = _Call direct [dart.core::Object::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::C::foo ------------
|
||||
%this = _Parameter #0 [_T (#lib::C<dynamic>)+]
|
||||
t1 = _Extract (%this[#lib::C/0])
|
||||
t2 = _Instantiate (#lib::D @ [t1])
|
||||
t3 = _Call direct [#lib::D::] (t2)
|
||||
RESULT: t2
|
||||
------------ #lib::D:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::D<dynamic>)+]
|
||||
t1 = _Call direct [dart.core::Object::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::E:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::E<dynamic, dynamic>)+]
|
||||
t1 = _Call direct [#lib::C::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::E::foo ------------
|
||||
%this = _Parameter #0 [_T (#lib::E<dynamic, dynamic>)+]
|
||||
t1* = _Call direct [#lib::C::foo] (%this)
|
||||
RESULT: t1
|
||||
------------ #lib::E::bar ------------
|
||||
%this = _Parameter #0 [_T (#lib::E<dynamic, dynamic>)+]
|
||||
t1 = _Extract (%this[#lib::E/1])
|
||||
t2 = _Instantiate (#lib::D @ [t1])
|
||||
t3 = _Call direct [#lib::D::] (t2)
|
||||
RESULT: t2
|
||||
------------ #lib::E::baz ------------
|
||||
%this = _Parameter #0 [_T (#lib::E<dynamic, dynamic>)+]
|
||||
t1 = _Extract (%this[#lib::E/2])
|
||||
t2 = _Instantiate (#lib::D @ [t1])
|
||||
t3 = _Call direct [#lib::D::] (t2)
|
||||
RESULT: t2
|
||||
------------ #lib::main ------------
|
||||
t0 = _Call direct [#lib::C::] (_T (#lib::C<_TS (dart.core::int)>))
|
||||
t1 = _Call [#lib::C::foo] (_T (#lib::C<_TS (dart.core::int)>))
|
||||
t2 = _Call direct [#lib::E::] (_T (#lib::E<_TS (dart.core::String), _TS (dart.core::int), _TS (dart.core::String)>))
|
||||
t3 = _Call [#lib::E::foo] (_T (#lib::E<_TS (dart.core::String), _TS (dart.core::int), _TS (dart.core::String)>))
|
||||
RESULT: _T {}?
|
|
@ -0,0 +1 @@
|
|||
../transformer/class_generics_basic.dart
|
|
@ -0,0 +1,109 @@
|
|||
------------ #lib::C:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::C<dynamic>)+]
|
||||
t1 = _Call direct [dart.core::Object::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::C::foo ------------
|
||||
%this = _Parameter #0 [_T (#lib::C<dynamic>)+]
|
||||
t1 = _Extract (%this[#lib::C/0])
|
||||
t2 = _CreateConcreteType (#lib::D @ (t1))
|
||||
t3 = _Call direct [#lib::D::] (t2)
|
||||
RESULT: t2
|
||||
------------ #lib::C::id1 ------------
|
||||
%this = _Parameter #0 [_T (#lib::C<dynamic>)+]
|
||||
%x = _Parameter #1 [_T (dart.core::Object)+?]
|
||||
t2 = _Extract (%this[#lib::C/0])
|
||||
t3 = _TypeCheck (%x against t2) (for parameter x)
|
||||
RESULT: t3
|
||||
------------ #lib::C::id2 ------------
|
||||
%this = _Parameter #0 [_T (#lib::C<dynamic>)+]
|
||||
%x = _Parameter #1 [_T (dart.core::Object)+?]
|
||||
t2 = _Extract (%this[#lib::C/0])
|
||||
t3 = _TypeCheck (%x against t2) (for parameter x)
|
||||
RESULT: t3
|
||||
------------ #lib::D:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::D<dynamic>)+]
|
||||
t1 = _Call direct [dart.core::Object::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::E:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::E<dynamic, dynamic>)+]
|
||||
t1 = _Call direct [#lib::C::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::E::foo ------------
|
||||
%this = _Parameter #0 [_T (#lib::E<dynamic, dynamic>)+]
|
||||
t1* = _Call direct [#lib::C::foo] (%this)
|
||||
RESULT: t1
|
||||
------------ #lib::E::bar ------------
|
||||
%this = _Parameter #0 [_T (#lib::E<dynamic, dynamic>)+]
|
||||
t1 = _Extract (%this[#lib::E/0])
|
||||
t2 = _CreateConcreteType (#lib::D @ (t1))
|
||||
t3 = _Call direct [#lib::D::] (t2)
|
||||
RESULT: t2
|
||||
------------ #lib::E::baz ------------
|
||||
%this = _Parameter #0 [_T (#lib::E<dynamic, dynamic>)+]
|
||||
t1 = _Extract (%this[#lib::E/1])
|
||||
t2 = _CreateConcreteType (#lib::D @ (t1))
|
||||
t3 = _Call direct [#lib::D::] (t2)
|
||||
RESULT: t2
|
||||
------------ #lib::X:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::X)+]
|
||||
t1 = _Call direct [dart.core::Object::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::Y:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::Y)+]
|
||||
t1 = _Call direct [#lib::X::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::Z:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::Z)+]
|
||||
t1 = _Call direct [#lib::X::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::I:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::I<dynamic>)+]
|
||||
t1 = _Call direct [dart.core::Object::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::J:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::J)+]
|
||||
t1 = _Call direct [#lib::I::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::K:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::K<dynamic>)+]
|
||||
t1 = _Call direct [dart.core::Object::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::C2:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::C2<dynamic>)+]
|
||||
t1 = _Call direct [dart.core::Object::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::C2::id3 ------------
|
||||
%this = _Parameter #0 [_T (#lib::C2<dynamic>)+]
|
||||
%x = _Parameter #1 [_T (dart.core::Comparable<dynamic>)+?]
|
||||
t2 = _Extract (%this[#lib::C2/0])
|
||||
t3 = _CreateRuntimeType (dart.core::Comparable @ (t2))
|
||||
t4 = _TypeCheck (%x against t3) (for parameter x)
|
||||
RESULT: t4
|
||||
------------ #lib::C2::id4 ------------
|
||||
%this = _Parameter #0 [_T (#lib::C2<dynamic>)+]
|
||||
%x = _Parameter #1 [_T (#lib::K<dynamic>)+?]
|
||||
t2 = _Extract (%this[#lib::C2/0])
|
||||
t3 = _CreateRuntimeType (#lib::I @ (t2))
|
||||
t4 = _CreateRuntimeType (#lib::K @ (t3))
|
||||
t5 = _TypeCheck (%x against t4) (for parameter x)
|
||||
RESULT: t5
|
||||
------------ #lib::main ------------
|
||||
t0 = _Call direct [#lib::C::] (_T (#lib::C<dart.core::int>))
|
||||
t1* = _Call [#lib::C::foo] (_T (#lib::C<dart.core::int>))
|
||||
t2 = _Call direct [#lib::E::] (_T (#lib::E<dart.core::int, dart.core::String>))
|
||||
t3* = _Call [#lib::E::foo] (_T (#lib::E<dart.core::int, dart.core::String>))
|
||||
t4 = _Call direct [#lib::E::] (_T (#lib::E<dart.core::int, dart.core::String>))
|
||||
t5* = _Call [#lib::E::bar] (_T (#lib::E<dart.core::int, dart.core::String>))
|
||||
t6 = _Call direct [#lib::E::] (_T (#lib::E<dart.core::int, dart.core::String>))
|
||||
t7* = _Call [#lib::E::baz] (_T (#lib::E<dart.core::int, dart.core::String>))
|
||||
t8 = _Call direct [#lib::C::] (_T (#lib::C<#lib::Y>))
|
||||
t9 = _Call direct [#lib::Y::] (_T (#lib::Y))
|
||||
t10 = _Call [#lib::C::id1] (_T (#lib::C<#lib::Y>), _T (#lib::Y))
|
||||
t11 = _Call direct [#lib::Z::] (_T (#lib::Z))
|
||||
t12 = _Call [#lib::C::id2] (_T (#lib::C<#lib::Y>), _T (#lib::Z))
|
||||
t13 = _Call direct [#lib::C2::] (_T (#lib::C2<dart.core::num>))
|
||||
t14 = _Call [#lib::C2::id3] (_T (#lib::C2<dart.core::num>), _T (dart.core::double)+)
|
||||
t15 = _Call direct [#lib::K::] (_T (#lib::K<#lib::J>))
|
||||
t16 = _Call [#lib::C2::id4] (_T (#lib::C2<dart.core::num>), _T (#lib::K<#lib::J>))
|
||||
used = _Join [dynamic] (_T {}?, t1, t3, t5, t7)
|
||||
RESULT: used
|
|
@ -0,0 +1 @@
|
|||
../transformer/class_generics_case1.dart
|
|
@ -0,0 +1,44 @@
|
|||
------------ #lib::Element:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::Element)+]
|
||||
t1 = _Call direct [dart.core::Object::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::MockHashMap:: ------------
|
||||
%K = _Parameter #0 [null]
|
||||
%V = _Parameter #1 [null]
|
||||
t2 = _CreateConcreteType (#lib::_NotRealHashMap @ (%K, %V))
|
||||
t3 = _Call direct [#lib::_NotRealHashMap::] (t2)
|
||||
RESULT: t2
|
||||
------------ #lib::_NotRealHashMap:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::_NotRealHashMap<dynamic, dynamic>)+]
|
||||
t1 = _Call direct [dart.core::Object::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::_NotRealHashMap::setEntry ------------
|
||||
%this = _Parameter #0 [_T (#lib::_NotRealHashMap<dynamic, dynamic>)+]
|
||||
%key = _Parameter #1 [_T (dart.core::Object)+?]
|
||||
%value = _Parameter #2 [_T (dart.core::Object)+?]
|
||||
t3 = _Extract (%this[#lib::_NotRealHashMap/0])
|
||||
t4 = _TypeCheck (%key against t3) (for parameter key)
|
||||
t5 = _Extract (%this[#lib::_NotRealHashMap/1])
|
||||
t6 = _TypeCheck (%value against t5) (for parameter value)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::InheritedElement:: ------------
|
||||
%this = _Parameter #0 [_T (#lib::InheritedElement)+]
|
||||
t1 = _Call direct [#lib::Element::] (%this)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::InheritedElement::setDependencies ------------
|
||||
%this = _Parameter #0 [_T (#lib::InheritedElement)+]
|
||||
%dependent = _Parameter #1 [_T (#lib::Element)+?]
|
||||
%value = _Parameter #2 [_T (dart.core::Object)+?]
|
||||
t3* = _Call virtual get [#lib::InheritedElement::_dependents] (%this)
|
||||
t4 = _Call [#lib::MockHashMap::setEntry] (t3, %dependent, %value)
|
||||
RESULT: _T {}?
|
||||
------------ #lib::InheritedElement::_dependents ------------
|
||||
%this = _Parameter #0 [_T (#lib::InheritedElement)+]
|
||||
t1* = _Call direct [#lib::MockHashMap::] (#lib::Element, dart.core::Object)
|
||||
RESULT: t1
|
||||
------------ #lib::main ------------
|
||||
t0 = _Call direct [#lib::InheritedElement::] (_T (#lib::InheritedElement))
|
||||
t1 = _Call [#lib::InheritedElement::setDependencies] (_T (#lib::InheritedElement), _T (#lib::InheritedElement), _T (dart.core::_Smi))
|
||||
t2 = _Call direct [#lib::Element::] (_T (#lib::Element))
|
||||
t3 = _Call [#lib::InheritedElement::setDependencies] (_T (#lib::InheritedElement), _T (#lib::Element), _T {}?)
|
||||
RESULT: _T {}?
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
class C<T> {
|
||||
foo() => D<T>();
|
||||
dynamic id1(T x) => x;
|
||||
dynamic id2(T x) => x;
|
||||
}
|
||||
|
||||
class D<T> {}
|
||||
|
||||
class E<S, T> extends C<T> {
|
||||
foo() => super.foo();
|
||||
bar() => D<S>();
|
||||
baz() => D<T>();
|
||||
}
|
||||
|
||||
class X {}
|
||||
|
||||
class Y extends X {}
|
||||
|
||||
class Z extends X {}
|
||||
|
||||
class I<T> {}
|
||||
|
||||
class J extends I<int> {}
|
||||
|
||||
class K<T> {}
|
||||
|
||||
class C2<T> {
|
||||
dynamic id3(Comparable<T> x) => x;
|
||||
dynamic id4(K<I<T>> x) => x;
|
||||
}
|
||||
|
||||
main() {
|
||||
// Test that type arguments are instantiated correctly on concrete types.
|
||||
dynamic used;
|
||||
used = C<int>().foo();
|
||||
used = E<int, String>().foo();
|
||||
used = E<int, String>().bar();
|
||||
used = E<int, String>().baz();
|
||||
|
||||
// Test that narrow against type-parameters works.
|
||||
C<X> c = new C<Y>();
|
||||
c.id1(Y());
|
||||
c.id2(Z());
|
||||
|
||||
// Test that generic supertypes of non-generic types are handled correctly.
|
||||
C2<num> c2 = new C2<num>();
|
||||
c2.id3(3.0);
|
||||
c2.id4(K<J>());
|
||||
|
||||
return used;
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
library #lib;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class C<T extends core::Object = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::C<self::C::T>
|
||||
: super core::Object::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasTearOffUses:false] method foo() → dynamic
|
||||
return new self::D::•<self::C::T>();
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id1([@vm.inferred-type.metadata=#lib::Y (skip check)] generic-covariant-impl self::C::T x) → dynamic
|
||||
return x;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id2([@vm.inferred-type.metadata=#lib::Z] generic-covariant-impl self::C::T x) → dynamic
|
||||
return x;
|
||||
}
|
||||
class D<T extends core::Object = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::D<self::D::T>
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
class E<S extends core::Object = dynamic, T extends core::Object = dynamic> extends self::C<self::E::T> {
|
||||
synthetic constructor •() → self::E<self::E::S, self::E::T>
|
||||
: super self::C::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method foo() → dynamic
|
||||
return [@vm.inferred-type.metadata=#lib::D<dart.core::String>] super.{self::C::foo}();
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method bar() → dynamic
|
||||
return new self::D::•<self::E::S>();
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method baz() → dynamic
|
||||
return new self::D::•<self::E::T>();
|
||||
}
|
||||
abstract class X extends core::Object {
|
||||
synthetic constructor •() → self::X
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
class Y extends self::X {
|
||||
synthetic constructor •() → self::Y
|
||||
: super self::X::•()
|
||||
;
|
||||
}
|
||||
class Z extends self::X {
|
||||
synthetic constructor •() → self::Z
|
||||
: super self::X::•()
|
||||
;
|
||||
}
|
||||
abstract class I<T extends core::Object = dynamic> extends core::Object {
|
||||
}
|
||||
abstract class J extends self::I<core::int> {
|
||||
}
|
||||
class K<T extends core::Object = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::K<self::K::T>
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
class C2<T extends core::Object = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::C2<self::C2::T>
|
||||
: super core::Object::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id3([@vm.inferred-type.metadata=dart.core::_Double (skip check)] generic-covariant-impl core::Comparable<self::C2::T> x) → dynamic
|
||||
return x;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method id4([@vm.inferred-type.metadata=#lib::K<#lib::J> (skip check)] generic-covariant-impl self::K<self::I<self::C2::T>> x) → dynamic
|
||||
return x;
|
||||
}
|
||||
static method main() → dynamic {
|
||||
dynamic used;
|
||||
used = [@vm.direct-call.metadata=#lib::C::foo] [@vm.inferred-type.metadata=#lib::D<dart.core::int>] new self::C::•<core::int>().{self::C::foo}();
|
||||
used = [@vm.direct-call.metadata=#lib::E::foo] [@vm.inferred-type.metadata=#lib::D<dart.core::String>] new self::E::•<core::int, core::String>().{self::E::foo}();
|
||||
used = [@vm.direct-call.metadata=#lib::E::bar] [@vm.inferred-type.metadata=#lib::D<dart.core::int>] new self::E::•<core::int, core::String>().{self::E::bar}();
|
||||
used = [@vm.direct-call.metadata=#lib::E::baz] [@vm.inferred-type.metadata=#lib::D<dart.core::String>] new self::E::•<core::int, core::String>().{self::E::baz}();
|
||||
self::C<self::X> c = new self::C::•<self::Y>();
|
||||
[@vm.call-site-attributes.metadata=receiverType:#lib::C<#lib::X>] [@vm.direct-call.metadata=#lib::C::id1] c.{self::C::id1}(new self::Y::•());
|
||||
[@vm.call-site-attributes.metadata=receiverType:#lib::C<#lib::X>] [@vm.direct-call.metadata=#lib::C::id2] c.{self::C::id2}(new self::Z::•());
|
||||
self::C2<core::num> c2 = new self::C2::•<core::num>();
|
||||
[@vm.call-site-attributes.metadata=receiverType:#lib::C2<dart.core::num>] [@vm.direct-call.metadata=#lib::C2::id3] c2.{self::C2::id3}(3.0);
|
||||
[@vm.call-site-attributes.metadata=receiverType:#lib::C2<dart.core::num>] [@vm.direct-call.metadata=#lib::C2::id4] c2.{self::C2::id4}(new self::K::•<self::J>());
|
||||
return used;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
//
|
||||
// This test checks that TFA works as expected on an example imitating the
|
||||
// InheritedElement.setDependencies hotspot in Flutter. The example is modified
|
||||
// to use a custom class 'MockHashMap' rather than the regular 'HashMap' since
|
||||
// we want to print out the inferred type of the '_dependents' field, which
|
||||
// would be a 'SetType' under the regular 'HashMap' (and set types aren't
|
||||
// translated into 'InferredType'). Also, []= is the target of a truly-dynamic
|
||||
// call, and we want to make sure there is only one call-site in this example
|
||||
// (call-site level info is not available yet).
|
||||
|
||||
import 'dart:collection';
|
||||
|
||||
class Element {}
|
||||
|
||||
abstract class MockHashMap<K, V> {
|
||||
factory MockHashMap() {
|
||||
return _NotRealHashMap<K, V>();
|
||||
}
|
||||
|
||||
void setEntry(K key, V value);
|
||||
}
|
||||
|
||||
class _NotRealHashMap<K, V> implements MockHashMap<K, V> {
|
||||
void setEntry(K key, V value) {}
|
||||
}
|
||||
|
||||
class InheritedElement extends Element {
|
||||
// The inferred type for '_dependents' needs to be concrete and have exact
|
||||
// type arguments.
|
||||
final MockHashMap<Element, Object> _dependents =
|
||||
MockHashMap<Element, Object>();
|
||||
|
||||
void setDependencies(Element dependent, Object value) {
|
||||
_dependents.setEntry(dependent, value);
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
var ie = InheritedElement();
|
||||
ie.setDependencies(ie, 0);
|
||||
ie.setDependencies(Element(), null);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
library #lib;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Element extends core::Object {
|
||||
synthetic constructor •() → self::Element
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
abstract class MockHashMap<K extends core::Object = dynamic, V extends core::Object = dynamic> extends core::Object {
|
||||
static factory •<K extends core::Object = dynamic, V extends core::Object = dynamic>() → self::MockHashMap<self::MockHashMap::•::K, self::MockHashMap::•::V> {
|
||||
return new self::_NotRealHashMap::•<self::MockHashMap::•::K, self::MockHashMap::•::V>();
|
||||
}
|
||||
abstract method setEntry(generic-covariant-impl self::MockHashMap::K key, generic-covariant-impl self::MockHashMap::V value) → void;
|
||||
}
|
||||
class _NotRealHashMap<K extends core::Object = dynamic, V extends core::Object = dynamic> extends core::Object implements self::MockHashMap<self::_NotRealHashMap::K, self::_NotRealHashMap::V> {
|
||||
synthetic constructor •() → self::_NotRealHashMap<self::_NotRealHashMap::K, self::_NotRealHashMap::V>
|
||||
: super core::Object::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method setEntry([@vm.inferred-type.metadata=! (skip check)] generic-covariant-impl self::_NotRealHashMap::K key, [@vm.inferred-type.metadata=dart.core::_Smi? (skip check)] generic-covariant-impl self::_NotRealHashMap::V value) → void {}
|
||||
}
|
||||
class InheritedElement extends self::Element {
|
||||
[@vm.inferred-type.metadata=#lib::_NotRealHashMap<#lib::Element, dart.core::Object>] [@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false] final field self::MockHashMap<self::Element, core::Object> _dependents = [@vm.inferred-type.metadata=#lib::_NotRealHashMap<#lib::Element, dart.core::Object>] self::MockHashMap::•<self::Element, core::Object>();
|
||||
synthetic constructor •() → self::InheritedElement
|
||||
: super self::Element::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] method setDependencies([@vm.inferred-type.metadata=!] self::Element dependent, [@vm.inferred-type.metadata=dart.core::_Smi?] core::Object value) → void {
|
||||
[@vm.call-site-attributes.metadata=receiverType:#lib::MockHashMap<#lib::Element, dart.core::Object>] [@vm.direct-call.metadata=#lib::_NotRealHashMap::setEntry] [@vm.direct-call.metadata=#lib::InheritedElement::_dependents] [@vm.inferred-type.metadata=#lib::_NotRealHashMap<#lib::Element, dart.core::Object>] this.{self::InheritedElement::_dependents}.{self::MockHashMap::setEntry}(dependent, value);
|
||||
}
|
||||
}
|
||||
static method main() → dynamic {
|
||||
self::InheritedElement ie = new self::InheritedElement::•();
|
||||
[@vm.direct-call.metadata=#lib::InheritedElement::setDependencies] ie.{self::InheritedElement::setDependencies}(ie, 0);
|
||||
[@vm.direct-call.metadata=#lib::InheritedElement::setDependencies] ie.{self::InheritedElement::setDependencies}(new self::Element::•(), null);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
class C<T> {
|
||||
void test2c(FutureOr<T> x) {}
|
||||
void test3c(Future<T> x) {}
|
||||
void test4c(FutureOr<T> x) {}
|
||||
|
||||
void test2r(C<FutureOr<T>> x) {}
|
||||
void test3r(C<Future<T>> x) {}
|
||||
void test4r(C<FutureOr<T>> x) {}
|
||||
void test5r(C<Future<T>> x) {}
|
||||
void test6r(C<FutureOr<T>> x) {}
|
||||
void test7r(C<T> x) {}
|
||||
void test8r(C<T> x) {}
|
||||
}
|
||||
|
||||
main() {
|
||||
dynamic c = C<int>();
|
||||
|
||||
c.test2c(3);
|
||||
c.test3c(Future.value(3));
|
||||
c.test4c(Future.value(3));
|
||||
|
||||
c.test2r(C<int>());
|
||||
c.test3r(C<Future<int>>());
|
||||
c.test4r(C<Future<int>>());
|
||||
c.test5r(C<FutureOr<int>>());
|
||||
c.test6r(C<FutureOr<int>>());
|
||||
c.test7r(C<FutureOr<int>>());
|
||||
c.test8r(C<Future<int>>());
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
library #lib;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:async" as asy;
|
||||
|
||||
class C<T extends core::Object = dynamic> extends core::Object {
|
||||
synthetic constructor •() → self::C<self::C::T>
|
||||
: super core::Object::•()
|
||||
;
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test2c([@vm.inferred-type.metadata=dart.core::_Smi (skip check)] generic-covariant-impl asy::FutureOr<self::C::T> x) → void {}
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test3c([@vm.inferred-type.metadata=dart.async::_Future<dart.core::int> (skip check)] generic-covariant-impl asy::Future<self::C::T> x) → void {}
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test4c([@vm.inferred-type.metadata=dart.async::_Future<dart.core::int> (skip check)] generic-covariant-impl asy::FutureOr<self::C::T> x) → void {}
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test2r([@vm.inferred-type.metadata=#lib::C<dart.core::int> (skip check)] generic-covariant-impl self::C<asy::FutureOr<self::C::T>> x) → void {}
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test3r([@vm.inferred-type.metadata=#lib::C<dart.async::Future<dart.core::int>> (skip check)] generic-covariant-impl self::C<asy::Future<self::C::T>> x) → void {}
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test4r([@vm.inferred-type.metadata=#lib::C<dart.async::Future<dart.core::int>> (skip check)] generic-covariant-impl self::C<asy::FutureOr<self::C::T>> x) → void {}
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test5r([@vm.inferred-type.metadata=#lib::C<dart.async::FutureOr<dart.core::int>>] generic-covariant-impl self::C<asy::Future<self::C::T>> x) → void {}
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test6r([@vm.inferred-type.metadata=#lib::C<dart.async::FutureOr<dart.core::int>> (skip check)] generic-covariant-impl self::C<asy::FutureOr<self::C::T>> x) → void {}
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test7r([@vm.inferred-type.metadata=#lib::C<dart.async::FutureOr<dart.core::int>>] generic-covariant-impl self::C<self::C::T> x) → void {}
|
||||
[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false] method test8r([@vm.inferred-type.metadata=#lib::C<dart.async::Future<dart.core::int>>] generic-covariant-impl self::C<self::C::T> x) → void {}
|
||||
}
|
||||
static method main() → dynamic {
|
||||
dynamic c = new self::C::•<core::int>();
|
||||
[@vm.direct-call.metadata=#lib::C::test2c] c.test2c(3);
|
||||
[@vm.direct-call.metadata=#lib::C::test3c] c.test3c([@vm.inferred-type.metadata=dart.async::_Future<dart.core::int>] asy::Future::value<core::int>(3));
|
||||
[@vm.direct-call.metadata=#lib::C::test4c] c.test4c([@vm.inferred-type.metadata=dart.async::_Future<dart.core::int>] asy::Future::value<core::int>(3));
|
||||
[@vm.direct-call.metadata=#lib::C::test2r] c.test2r(new self::C::•<core::int>());
|
||||
[@vm.direct-call.metadata=#lib::C::test3r] c.test3r(new self::C::•<asy::Future<core::int>>());
|
||||
[@vm.direct-call.metadata=#lib::C::test4r] c.test4r(new self::C::•<asy::Future<core::int>>());
|
||||
[@vm.direct-call.metadata=#lib::C::test5r] c.test5r(new self::C::•<asy::FutureOr<core::int>>());
|
||||
[@vm.direct-call.metadata=#lib::C::test6r] c.test6r(new self::C::•<asy::FutureOr<core::int>>());
|
||||
[@vm.direct-call.metadata=#lib::C::test7r] c.test7r(new self::C::•<asy::FutureOr<core::int>>());
|
||||
[@vm.direct-call.metadata=#lib::C::test8r] c.test8r(new self::C::•<asy::Future<core::int>>());
|
||||
}
|
|
@ -14,11 +14,11 @@ class B extends self::A {
|
|||
;
|
||||
}
|
||||
[@vm.inferred-type.metadata=dart.core::Null?]static field core::Function unknown;
|
||||
static method foo1_a1([@vm.inferred-type.metadata=dart.async::_Future] dynamic x) → void {}
|
||||
static method foo1_a1([@vm.inferred-type.metadata=dart.async::_Future<#lib::B>] dynamic x) → void {}
|
||||
static method foo1_a2([@vm.inferred-type.metadata=#lib::B] dynamic x) → void {}
|
||||
static method foo1_a3([@vm.inferred-type.metadata=dart.async::_Future] dynamic x) → void {}
|
||||
static method foo1_a3([@vm.inferred-type.metadata=dart.async::_Future<#lib::B>] dynamic x) → void {}
|
||||
static method foo1_a4([@vm.inferred-type.metadata=#lib::B] dynamic x) → void {}
|
||||
static method foo1([@vm.inferred-type.metadata=dart.async::_Future] asy::Future<self::A> a1, [@vm.inferred-type.metadata=#lib::B] self::A a2, [@vm.inferred-type.metadata=dart.async::_Future] asy::FutureOr<self::A> a3, [@vm.inferred-type.metadata=#lib::B] asy::FutureOr<self::A> a4) → void {
|
||||
static method foo1([@vm.inferred-type.metadata=dart.async::_Future<#lib::B>] asy::Future<self::A> a1, [@vm.inferred-type.metadata=#lib::B] self::A a2, [@vm.inferred-type.metadata=dart.async::_Future<#lib::B>] asy::FutureOr<self::A> a3, [@vm.inferred-type.metadata=#lib::B] asy::FutureOr<self::A> a4) → void {
|
||||
self::foo1_a1(a1);
|
||||
self::foo1_a2(a2);
|
||||
self::foo1_a3(a3);
|
||||
|
@ -37,6 +37,6 @@ static method foo2([@vm.inferred-type.metadata=dart.async::_Future?] asy::Future
|
|||
static method getDynamic() → dynamic
|
||||
return [@vm.call-site-attributes.metadata=receiverType:dart.core::Function] self::unknown.call();
|
||||
static method main(core::List<core::String> args) → dynamic {
|
||||
self::foo1([@vm.inferred-type.metadata=dart.async::_Future] asy::Future::value<self::B>(new self::B::•()), new self::B::•(), [@vm.inferred-type.metadata=dart.async::_Future] asy::Future::value<self::B>(new self::B::•()), new self::B::•());
|
||||
self::foo1([@vm.inferred-type.metadata=dart.async::_Future<#lib::B>] asy::Future::value<self::B>(new self::B::•()), new self::B::•(), [@vm.inferred-type.metadata=dart.async::_Future<#lib::B>] asy::Future::value<self::B>(new self::B::•()), new self::B::•());
|
||||
self::foo2(self::getDynamic() as{TypeError} asy::Future<self::A>, self::getDynamic() as{TypeError} self::A, self::getDynamic() as{TypeError} asy::FutureOr<self::A>, self::getDynamic() as{TypeError} asy::FutureOr<self::A>);
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ class Q<T extends core::Object = dynamic> extends core::Object {
|
|||
: self::Q::result = result, super core::Object::•()
|
||||
;
|
||||
}
|
||||
static method foo1([@vm.inferred-type.metadata=dart.core::_GrowableList] core::List<self::T1> list) → dynamic {
|
||||
[@vm.direct-call.metadata=#lib::T3::run] [@vm.direct-call.metadata=#lib::T1::go??] [@vm.inferred-type.metadata=#lib::T3] [@vm.direct-call.metadata=#lib::Q::result??] [@vm.direct-call.metadata=dart._internal::ListIterable::first] [@vm.direct-call.metadata=dart.collection::_ListBase&Object&ListMixin::map] [@vm.inferred-type.metadata=dart._internal::MappedListIterable] list.{core::Iterable::map}<self::Q<self::T1>>((self::T1 t1) → self::Q<self::T1> => new self::Q::•<self::T1>(t1)).{core::Iterable::first}.{self::Q::result}.{self::T1::go}().{self::T3::run}();
|
||||
static method foo1([@vm.inferred-type.metadata=dart.core::_GrowableList<#lib::T1>] core::List<self::T1> list) → dynamic {
|
||||
[@vm.direct-call.metadata=#lib::T3::run] [@vm.direct-call.metadata=#lib::T1::go??] [@vm.inferred-type.metadata=#lib::T3] [@vm.direct-call.metadata=#lib::Q::result??] [@vm.direct-call.metadata=dart._internal::ListIterable::first] [@vm.direct-call.metadata=dart.collection::_ListBase&Object&ListMixin::map] [@vm.inferred-type.metadata=dart._internal::MappedListIterable<#lib::T1, ?>] list.{core::Iterable::map}<self::Q<self::T1>>((self::T1 t1) → self::Q<self::T1> => new self::Q::•<self::T1>(t1)).{core::Iterable::first}.{self::Q::result}.{self::T1::go}().{self::T3::run}();
|
||||
}
|
||||
static method foo2NewValue() → self::Q<dynamic>
|
||||
return new self::Q::•<self::T2>(new self::T2::•());
|
||||
|
|
|
@ -48,11 +48,11 @@ class B extends self::A {
|
|||
return new self::T1::•();
|
||||
}
|
||||
no-such-method-forwarder get bar() → dynamic
|
||||
return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withoutType("get:bar", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withoutType("get:bar", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] no-such-method-forwarder method foo() → dynamic
|
||||
return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] no-such-method-forwarder method bazz([@vm.inferred-type.metadata=dart.core::_Smi] dynamic a1, [@vm.inferred-type.metadata=dart.core::_Smi] dynamic a2, [@vm.inferred-type.metadata=dart.core::_Smi] dynamic a3, [[@vm.inferred-type.metadata=dart.core::_Smi] dynamic a4 = null, [@vm.inferred-type.metadata=dart.core::Null?] dynamic a5 = null]) → dynamic
|
||||
return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withoutType("bazz", const <core::Type>[], core::List::unmodifiable<dynamic>(<dynamic>[a1, a2, a3, a4, a5]), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
return [@vm.direct-call.metadata=#lib::B::noSuchMethod] [@vm.inferred-type.metadata=#lib::T1] this.{self::B::noSuchMethod}(new core::_InvocationMirror::_withoutType("bazz", const <core::Type>[], core::List::unmodifiable<dynamic>(<dynamic>[a1, a2, a3, a4, a5]), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
}
|
||||
abstract class C extends core::Object {
|
||||
synthetic constructor •() → self::C
|
||||
|
@ -67,11 +67,11 @@ class D extends self::C implements self::A {
|
|||
: super self::C::•()
|
||||
;
|
||||
no-such-method-forwarder get bar() → dynamic
|
||||
return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withoutType("get:bar", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withoutType("get:bar", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] no-such-method-forwarder method foo() → dynamic
|
||||
return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false] no-such-method-forwarder method bazz([@vm.inferred-type.metadata=dart.core::_Smi] dynamic a1, [@vm.inferred-type.metadata=dart.core::_Smi] dynamic a2, [@vm.inferred-type.metadata=dart.core::_Smi] dynamic a3, [[@vm.inferred-type.metadata=dart.core::_Smi] dynamic a4 = null, [@vm.inferred-type.metadata=dart.core::Null?] dynamic a5 = null]) → dynamic
|
||||
return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withoutType("bazz", const <core::Type>[], core::List::unmodifiable<dynamic>(<dynamic>[a1, a2, a3, a4, a5]), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
return [@vm.direct-call.metadata=#lib::C::noSuchMethod] [@vm.inferred-type.metadata=#lib::T2] this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withoutType("bazz", const <core::Type>[], core::List::unmodifiable<dynamic>(<dynamic>[a1, a2, a3, a4, a5]), [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
}
|
||||
class E extends core::Object implements self::A {
|
||||
synthetic constructor •() → self::E
|
||||
|
@ -81,7 +81,7 @@ class E extends core::Object implements self::A {
|
|||
return new self::T4::•();
|
||||
}
|
||||
no-such-method-forwarder get bar() → dynamic
|
||||
return [@vm.direct-call.metadata=#lib::E::noSuchMethod] [@vm.inferred-type.metadata=#lib::T4] this.{self::E::noSuchMethod}(new core::_InvocationMirror::_withoutType("get:bar", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
return [@vm.direct-call.metadata=#lib::E::noSuchMethod] [@vm.inferred-type.metadata=#lib::T4] this.{self::E::noSuchMethod}(new core::_InvocationMirror::_withoutType("get:bar", const <core::Type>[], const <dynamic>[], [@vm.inferred-type.metadata=dart.collection::UnmodifiableMapView<dart.core::Symbol, dynamic>] core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), false)) as{TypeError} dynamic;
|
||||
}
|
||||
class F extends core::Object {
|
||||
synthetic constructor •() → self::F
|
||||
|
|
Loading…
Reference in a new issue