Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(856)

Unified Diff: sdk/lib/_internal/compiler/implementation/types/type_mask.dart

Issue 14416014: After a dynamic call, refine the receiver type by looking at the potential targets of that call. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: sdk/lib/_internal/compiler/implementation/types/type_mask.dart
===================================================================
--- sdk/lib/_internal/compiler/implementation/types/type_mask.dart (revision 22031)
+++ sdk/lib/_internal/compiler/implementation/types/type_mask.dart (working copy)
@@ -18,6 +18,7 @@
final DartType base;
final int flags;
+ final Set<ClassElement> refined;
TypeMask(DartType base, int kind, bool isNullable)
: this.internal(base, (kind << 1) | (isNullable ? 1 : 0));
@@ -40,9 +41,38 @@
TypeMask.nonNullSubtype(DartType base)
: this.internal(base, SUBTYPE << 1);
- TypeMask.internal(DartType base, this.flags)
+ TypeMask.internal(DartType base, this.flags, [this.refined = null])
: this.base = transformBase(base);
+ factory TypeMask.refineWith(TypeMask other,
+ Selector selector,
+ Compiler compiler) {
+ if (other.isExact) return other;
+ if (other.isEmpty) return other;
+
+ List<ClassElement> classes =
+ compiler.world.allFunctions.findTargets(selector);
+
+ // If it's not worth refining [other], just return it.
+ if (classes.isEmpty || classes[0] == compiler.objectClass) return other;
+
+ Set<ClassElement> refined = new Set<ClassElement>.from(classes);
+ if (other.refined != null) {
+ refined = refined.intersection(other.refined);
+ }
+
+ if (refined.length == 1) {
+ // If there is only one known target, use it as the new type. We
+ // know it has to be a non-null subclass.
+ ClassElement classFound = refined.elementAt(0);
+ if (isSubtypeOf(other.base, classFound.rawType, compiler)) return other;
+ if (classFound == other.base.element && other.isSubclass) return other;
+ return new TypeMask.nonNullSubclass(classFound.rawType);
+ }
+
+ return new TypeMask.internal(other.base, other.flags & ~1, refined);
+ }
+
// TODO(kasperl): We temporarily transform the base to be the raw
// variant of the type. Long term, we're going to keep the class
// element corresponding to the type in the mask instead.
@@ -157,9 +187,9 @@
int combined = (flags > other.flags)
? flags | (other.flags & 1)
: other.flags | (flags & 1);
- if (flags == combined) {
+ if (flags == combined && refined == null) {
return this;
- } else if (other.flags == combined) {
+ } else if (other.flags == combined && other.refined == null) {
return other;
} else {
return new TypeMask.internal(base, combined);
@@ -182,7 +212,7 @@
? flags | (other.flags & 1)
: other.flags | (flags & 1);
}
- return (flags != combined)
+ return (flags != combined || refined != null)
? new TypeMask.internal(base, combined)
: this;
}
@@ -193,7 +223,7 @@
// resulting union to be a subtype too. If either one of the masks
// are nullable the result should be nullable too.
int combined = (SUBTYPE << 1) | ((flags | other.flags) & 1);
- return (flags != combined)
+ return (flags != combined || refined != null)
? new TypeMask.internal(base, combined)
: this;
}
@@ -281,12 +311,13 @@
int combined = (flags < other.flags)
? flags & ((other.flags & 1) | ~1)
: other.flags & ((flags & 1) | ~1);
- if (flags == combined) {
+ if (flags == combined && setEquals(refined, other.refined)) {
return this;
- } else if (other.flags == combined) {
+ } else if (other.flags == combined && setEquals(refined, other.refined)) {
return other;
} else {
- return new TypeMask.internal(base, combined);
+ return new TypeMask.internal(
+ base, combined, setIntersection(refined, other.refined));
}
}
@@ -299,10 +330,11 @@
// so base the combined flags on the other mask. Only if both
// masks are nullable, will the result be nullable too.
int combined = other.flags & ((flags & 1) | ~1);
- if (other.flags == combined) {
+ if (other.flags == combined && setEquals(refined, other.refined)) {
return other;
} else {
- return new TypeMask.internal(other.base, combined);
+ return new TypeMask.internal(
+ other.base, combined, setIntersection(refined, other.refined));
}
}
@@ -315,10 +347,11 @@
// so base the combined flags on the other mask. Only if both
// masks are nullable, will the result be nullable too.
int combined = other.flags & ((flags & 1) | ~1);
- if (other.flags == combined) {
+ if (other.flags == combined && setEquals(refined, other.refined)) {
return other;
} else {
- return new TypeMask.internal(other.base, combined);
+ return new TypeMask.internal(
+ other.base, combined, setIntersection(refined, other.refined));
}
}
@@ -502,10 +535,24 @@
});
}
+ bool setEquals(Set<ClassElement> first, Set<ClassElement> second) {
+ if (first == null) return second == null;
+ if (second == null) return false;
+ return first.containsAll(second) && second.containsAll(first);
+ }
+
+ Set<ClassElement> setIntersection(Set<ClassElement> first,
+ Set<ClassElement> second) {
+ if (first == null || second == null) return null;
+ return first.intersection(second);
+ }
+
bool operator ==(var other) {
if (other is !TypeMask) return false;
TypeMask otherMask = other;
- return (flags == otherMask.flags) && (base == otherMask.base);
+ return (flags == otherMask.flags)
+ && (base == otherMask.base)
+ && setEquals(refined, other.refined);
}
int get hashCode {
@@ -520,6 +567,7 @@
if (isSubclass) buffer.write('subclass=');
if (isSubtype) buffer.write('subtype=');
buffer.write(base.element.name.slowToString());
+ buffer.write(', $refined');
return "[$buffer]";
}

Powered by Google App Engine
This is Rietveld 408576698