| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of types; | |
| 6 | |
| 7 /** | |
| 8 * A flat type mask is a type mask that has been flatten to contain a | |
| 9 * base type. | |
| 10 */ | |
| 11 class FlatTypeMask implements TypeMask { | |
| 12 static const int EMPTY = 0; | |
| 13 static const int EXACT = 1; | |
| 14 static const int SUBCLASS = 2; | |
| 15 static const int SUBTYPE = 3; | |
| 16 | |
| 17 final ClassElement base; | |
| 18 final int flags; | |
| 19 | |
| 20 FlatTypeMask(ClassElement base, int kind, bool isNullable) | |
| 21 : this.internal(base, (kind << 1) | (isNullable ? 1 : 0)); | |
| 22 | |
| 23 FlatTypeMask.exact(ClassElement base) | |
| 24 : this.internal(base, (EXACT << 1) | 1); | |
| 25 FlatTypeMask.subclass(ClassElement base) | |
| 26 : this.internal(base, (SUBCLASS << 1) | 1); | |
| 27 FlatTypeMask.subtype(ClassElement base) | |
| 28 : this.internal(base, (SUBTYPE << 1) | 1); | |
| 29 | |
| 30 const FlatTypeMask.nonNullEmpty(): base = null, flags = 0; | |
| 31 const FlatTypeMask.empty() : base = null, flags = 1; | |
| 32 | |
| 33 FlatTypeMask.nonNullExact(ClassElement base) | |
| 34 : this.internal(base, EXACT << 1); | |
| 35 FlatTypeMask.nonNullSubclass(ClassElement base) | |
| 36 : this.internal(base, SUBCLASS << 1); | |
| 37 FlatTypeMask.nonNullSubtype(ClassElement base) | |
| 38 : this.internal(base, SUBTYPE << 1); | |
| 39 | |
| 40 FlatTypeMask.internal(this.base, this.flags) { | |
| 41 assert(base == null || base.isDeclaration); | |
| 42 } | |
| 43 | |
| 44 /** | |
| 45 * Ensures that the generated mask is normalized, i.e., a call to | |
| 46 * [TypeMask.assertIsNormalized] with the factory's result returns `true`. | |
| 47 */ | |
| 48 factory FlatTypeMask.normalized(ClassElement base, int flags, World world) { | |
| 49 if ((flags >> 1) == EMPTY || ((flags >> 1) == EXACT)) { | |
| 50 return new FlatTypeMask.internal(base, flags); | |
| 51 } | |
| 52 if ((flags >> 1) == SUBTYPE) { | |
| 53 if (!world.hasAnySubtype(base) || world.hasOnlySubclasses(base)) { | |
| 54 flags = (flags & 0x1) | (SUBCLASS << 1); | |
| 55 } | |
| 56 } | |
| 57 if (((flags >> 1) == SUBCLASS) && !world.hasAnySubclass(base)) { | |
| 58 flags = (flags & 0x1) | (EXACT << 1); | |
| 59 } | |
| 60 return new FlatTypeMask.internal(base, flags); | |
| 61 } | |
| 62 | |
| 63 bool get isEmpty => (flags >> 1) == EMPTY; | |
| 64 bool get isExact => (flags >> 1) == EXACT; | |
| 65 bool get isNullable => (flags & 1) != 0; | |
| 66 | |
| 67 bool get isUnion => false; | |
| 68 bool get isContainer => false; | |
| 69 bool get isMap => false; | |
| 70 bool get isDictionary => false; | |
| 71 bool get isForwarding => false; | |
| 72 bool get isValue => false; | |
| 73 | |
| 74 // TODO(kasperl): Get rid of these. They should not be a visible | |
| 75 // part of the implementation because they make it hard to add | |
| 76 // proper union types if we ever want to. | |
| 77 bool get isSubclass => (flags >> 1) == SUBCLASS; | |
| 78 bool get isSubtype => (flags >> 1) == SUBTYPE; | |
| 79 | |
| 80 TypeMask nullable() { | |
| 81 return isNullable ? this : new FlatTypeMask.internal(base, flags | 1); | |
| 82 } | |
| 83 | |
| 84 TypeMask nonNullable() { | |
| 85 return isNullable ? new FlatTypeMask.internal(base, flags & ~1) : this; | |
| 86 } | |
| 87 | |
| 88 bool contains(ClassElement type, ClassWorld classWorld) { | |
| 89 assert(type.isDeclaration); | |
| 90 if (isEmpty) { | |
| 91 return false; | |
| 92 } else if (identical(base, type)) { | |
| 93 return true; | |
| 94 } else if (isExact) { | |
| 95 return false; | |
| 96 } else if (isSubclass) { | |
| 97 return classWorld.isSubclassOf(type, base); | |
| 98 } else { | |
| 99 assert(isSubtype); | |
| 100 return classWorld.isSubtypeOf(type, base); | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 bool isSingleImplementationOf(ClassElement cls, ClassWorld classWorld) { | |
| 105 // Special case basic types so that, for example, JSString is the | |
| 106 // single implementation of String. | |
| 107 // The general optimization is to realize there is only one class that | |
| 108 // implements [base] and [base] is not instantiated. We however do | |
| 109 // not track correctly the list of truly instantiated classes. | |
| 110 Backend backend = classWorld.backend; | |
| 111 if (containsOnlyString(classWorld)) { | |
| 112 return cls == classWorld.stringClass || | |
| 113 cls == backend.stringImplementation; | |
| 114 } | |
| 115 if (containsOnlyBool(classWorld)) { | |
| 116 return cls == classWorld.boolClass || cls == backend.boolImplementation; | |
| 117 } | |
| 118 if (containsOnlyInt(classWorld)) { | |
| 119 return cls == classWorld.intClass | |
| 120 || cls == backend.intImplementation | |
| 121 || cls == backend.positiveIntImplementation | |
| 122 || cls == backend.uint32Implementation | |
| 123 || cls == backend.uint31Implementation; | |
| 124 } | |
| 125 if (containsOnlyDouble(classWorld)) { | |
| 126 return cls == classWorld.doubleClass | |
| 127 || cls == backend.doubleImplementation; | |
| 128 } | |
| 129 return false; | |
| 130 } | |
| 131 | |
| 132 bool isInMask(TypeMask other, ClassWorld classWorld) { | |
| 133 // null is treated separately, so the empty mask might still contain it. | |
| 134 if (isEmpty) return isNullable ? other.isNullable : true; | |
| 135 // The empty type contains no classes. | |
| 136 if (other.isEmpty) return false; | |
| 137 // Quick check whether to handle null. | |
| 138 if (isNullable && !other.isNullable) return false; | |
| 139 other = TypeMask.nonForwardingMask(other); | |
| 140 // If other is union, delegate to UnionTypeMask.containsMask. | |
| 141 if (other is! FlatTypeMask) return other.containsMask(this, classWorld); | |
| 142 // The other must be flat, so compare base and flags. | |
| 143 FlatTypeMask flatOther = other; | |
| 144 ClassElement otherBase = flatOther.base; | |
| 145 // If other is exact, it only contains its base. | |
| 146 // TODO(herhut): Get rid of isSingleImplementationOf. | |
| 147 if (flatOther.isExact) { | |
| 148 return (isExact && base == otherBase) | |
| 149 || isSingleImplementationOf(otherBase, classWorld); | |
| 150 } | |
| 151 // If other is subclass, this has to be subclass, as well. Unless | |
| 152 // flatOther.base covers all subtypes of this. Currently, we only | |
| 153 // consider object to behave that way. | |
| 154 // TODO(herhut): Add check whether flatOther.base is superclass of | |
| 155 // all subclasses of this.base. | |
| 156 if (flatOther.isSubclass) { | |
| 157 if (isSubtype) return (otherBase == classWorld.objectClass); | |
| 158 return classWorld.isSubclassOf(base, otherBase); | |
| 159 } | |
| 160 assert(flatOther.isSubtype); | |
| 161 // Check whether this TypeMask satisfies otherBase's interface. | |
| 162 return satisfies(otherBase, classWorld); | |
| 163 } | |
| 164 | |
| 165 bool containsMask(TypeMask other, ClassWorld classWorld) { | |
| 166 return other.isInMask(this, classWorld); | |
| 167 } | |
| 168 | |
| 169 bool containsOnlyInt(ClassWorld classWorld) { | |
| 170 Backend backend = classWorld.backend; | |
| 171 return base == classWorld.intClass | |
| 172 || base == backend.intImplementation | |
| 173 || base == backend.positiveIntImplementation | |
| 174 || base == backend.uint31Implementation | |
| 175 || base == backend.uint32Implementation; | |
| 176 } | |
| 177 | |
| 178 bool containsOnlyDouble(ClassWorld classWorld) { | |
| 179 Backend backend = classWorld.backend; | |
| 180 return base == classWorld.doubleClass | |
| 181 || base == backend.doubleImplementation; | |
| 182 } | |
| 183 | |
| 184 bool containsOnlyNum(ClassWorld classWorld) { | |
| 185 Backend backend = classWorld.backend; | |
| 186 return containsOnlyInt(classWorld) | |
| 187 || containsOnlyDouble(classWorld) | |
| 188 || base == classWorld.numClass | |
| 189 || base == backend.numImplementation; | |
| 190 } | |
| 191 | |
| 192 bool containsOnlyBool(ClassWorld classWorld) { | |
| 193 Backend backend = classWorld.backend; | |
| 194 return base == classWorld.boolClass | |
| 195 || base == backend.boolImplementation; | |
| 196 } | |
| 197 | |
| 198 bool containsOnlyString(ClassWorld classWorld) { | |
| 199 Backend backend = classWorld.backend; | |
| 200 return base == classWorld.stringClass | |
| 201 || base == backend.stringImplementation; | |
| 202 } | |
| 203 | |
| 204 bool containsOnly(ClassElement cls) { | |
| 205 assert(cls.isDeclaration); | |
| 206 return base == cls; | |
| 207 } | |
| 208 | |
| 209 bool satisfies(ClassElement cls, ClassWorld classWorld) { | |
| 210 assert(cls.isDeclaration); | |
| 211 if (isEmpty) return false; | |
| 212 if (classWorld.isSubtypeOf(base, cls)) return true; | |
| 213 return false; | |
| 214 } | |
| 215 | |
| 216 /** | |
| 217 * Returns the [ClassElement] if this type represents a single class, | |
| 218 * otherwise returns `null`. This method is conservative. | |
| 219 */ | |
| 220 ClassElement singleClass(ClassWorld classWorld) { | |
| 221 if (isEmpty) return null; | |
| 222 if (isNullable) return null; // It is Null and some other class. | |
| 223 if (isExact) { | |
| 224 return base; | |
| 225 } else if (isSubclass) { | |
| 226 return classWorld.hasAnyStrictSubclass(base) ? null : base; | |
| 227 } else { | |
| 228 assert(isSubtype); | |
| 229 return null; | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 /** | |
| 234 * Returns whether or not this type mask contains all types. | |
| 235 */ | |
| 236 bool containsAll(ClassWorld classWorld) { | |
| 237 if (isEmpty || isExact) return false; | |
| 238 return identical(base, classWorld.objectClass); | |
| 239 } | |
| 240 | |
| 241 TypeMask union(TypeMask other, ClassWorld classWorld) { | |
| 242 assert(other != null); | |
| 243 assert(TypeMask.assertIsNormalized(this, classWorld)); | |
| 244 assert(TypeMask.assertIsNormalized(other, classWorld)); | |
| 245 if (other is! FlatTypeMask) return other.union(this, classWorld); | |
| 246 FlatTypeMask flatOther = other; | |
| 247 if (isEmpty) { | |
| 248 return isNullable ? flatOther.nullable() : flatOther; | |
| 249 } else if (flatOther.isEmpty) { | |
| 250 return flatOther.isNullable ? nullable() : this; | |
| 251 } else if (base == flatOther.base) { | |
| 252 return unionSame(flatOther, classWorld); | |
| 253 } else if (classWorld.isSubclassOf(flatOther.base, base)) { | |
| 254 return unionStrictSubclass(flatOther, classWorld); | |
| 255 } else if (classWorld.isSubclassOf(base, flatOther.base)) { | |
| 256 return flatOther.unionStrictSubclass(this, classWorld); | |
| 257 } else if (classWorld.isSubtypeOf(flatOther.base, base)) { | |
| 258 return unionStrictSubtype(flatOther, classWorld); | |
| 259 } else if (classWorld.isSubtypeOf(base, flatOther.base)) { | |
| 260 return flatOther.unionStrictSubtype(this, classWorld); | |
| 261 } else { | |
| 262 return new UnionTypeMask._internal(<FlatTypeMask>[this, flatOther]); | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 TypeMask unionSame(FlatTypeMask other, ClassWorld classWorld) { | |
| 267 assert(base == other.base); | |
| 268 assert(TypeMask.assertIsNormalized(this, classWorld)); | |
| 269 assert(TypeMask.assertIsNormalized(other, classWorld)); | |
| 270 // The two masks share the base type, so we must chose the least | |
| 271 // constraining kind (the highest) of the two. If either one of | |
| 272 // the masks are nullable the result should be nullable too. | |
| 273 // As both masks are normalized, the result will be, too. | |
| 274 int combined = (flags > other.flags) | |
| 275 ? flags | (other.flags & 1) | |
| 276 : other.flags | (flags & 1); | |
| 277 if (flags == combined) { | |
| 278 return this; | |
| 279 } else if (other.flags == combined) { | |
| 280 return other; | |
| 281 } else { | |
| 282 return new FlatTypeMask.normalized(base, combined, classWorld); | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 TypeMask unionStrictSubclass(FlatTypeMask other, ClassWorld classWorld) { | |
| 287 assert(base != other.base); | |
| 288 assert(classWorld.isSubclassOf(other.base, base)); | |
| 289 assert(TypeMask.assertIsNormalized(this, classWorld)); | |
| 290 assert(TypeMask.assertIsNormalized(other, classWorld)); | |
| 291 int combined; | |
| 292 if ((isExact && other.isExact) || base == classWorld.objectClass) { | |
| 293 // Since the other mask is a subclass of this mask, we need the | |
| 294 // resulting union to be a subclass too. If either one of the | |
| 295 // masks are nullable the result should be nullable too. | |
| 296 combined = (SUBCLASS << 1) | ((flags | other.flags) & 1); | |
| 297 } else { | |
| 298 // Both masks are at least subclass masks, so we pick the least | |
| 299 // constraining kind (the highest) of the two. If either one of | |
| 300 // the masks are nullable the result should be nullable too. | |
| 301 combined = (flags > other.flags) | |
| 302 ? flags | (other.flags & 1) | |
| 303 : other.flags | (flags & 1); | |
| 304 } | |
| 305 // If we weaken the constraint on this type, we have to make sure that | |
| 306 // the result is normalized. | |
| 307 return (flags != combined) | |
| 308 ? new FlatTypeMask.normalized(base, combined, classWorld) | |
| 309 : this; | |
| 310 } | |
| 311 | |
| 312 TypeMask unionStrictSubtype(FlatTypeMask other, ClassWorld classWorld) { | |
| 313 assert(base != other.base); | |
| 314 assert(!classWorld.isSubclassOf(other.base, base)); | |
| 315 assert(classWorld.isSubtypeOf(other.base, base)); | |
| 316 assert(TypeMask.assertIsNormalized(this, classWorld)); | |
| 317 assert(TypeMask.assertIsNormalized(other, classWorld)); | |
| 318 // Since the other mask is a subtype of this mask, we need the | |
| 319 // resulting union to be a subtype too. If either one of the masks | |
| 320 // are nullable the result should be nullable too. | |
| 321 int combined = (SUBTYPE << 1) | ((flags | other.flags) & 1); | |
| 322 // We know there is at least one subtype, [other.base], so no need | |
| 323 // to normalize. | |
| 324 return (flags != combined) | |
| 325 ? new FlatTypeMask.normalized(base, combined, classWorld) | |
| 326 : this; | |
| 327 } | |
| 328 | |
| 329 TypeMask intersection(TypeMask other, ClassWorld classWorld) { | |
| 330 assert(other != null); | |
| 331 if (other is! FlatTypeMask) return other.intersection(this, classWorld); | |
| 332 assert(TypeMask.assertIsNormalized(this, classWorld)); | |
| 333 assert(TypeMask.assertIsNormalized(other, classWorld)); | |
| 334 FlatTypeMask flatOther = other; | |
| 335 if (isEmpty) { | |
| 336 return flatOther.isNullable ? this : nonNullable(); | |
| 337 } else if (flatOther.isEmpty) { | |
| 338 return isNullable ? flatOther : other.nonNullable(); | |
| 339 } else if (base == flatOther.base) { | |
| 340 return intersectionSame(flatOther, classWorld); | |
| 341 } else if (classWorld.isSubclassOf(flatOther.base, base)) { | |
| 342 return intersectionStrictSubclass(flatOther, classWorld); | |
| 343 } else if (classWorld.isSubclassOf(base, flatOther.base)) { | |
| 344 return flatOther.intersectionStrictSubclass(this, classWorld); | |
| 345 } else if (classWorld.isSubtypeOf(flatOther.base, base)) { | |
| 346 return intersectionStrictSubtype(flatOther, classWorld); | |
| 347 } else if (classWorld.isSubtypeOf(base, flatOther.base)) { | |
| 348 return flatOther.intersectionStrictSubtype(this, classWorld); | |
| 349 } else { | |
| 350 return intersectionDisjoint(flatOther, classWorld); | |
| 351 } | |
| 352 } | |
| 353 | |
| 354 TypeMask intersectionSame(FlatTypeMask other, ClassWorld classWorld) { | |
| 355 assert(base == other.base); | |
| 356 // The two masks share the base type, so we must chose the most | |
| 357 // constraining kind (the lowest) of the two. Only if both masks | |
| 358 // are nullable, will the result be nullable too. | |
| 359 // The result will be normalized, as the two inputs are normalized, too. | |
| 360 int combined = (flags < other.flags) | |
| 361 ? flags & ((other.flags & 1) | ~1) | |
| 362 : other.flags & ((flags & 1) | ~1); | |
| 363 if (flags == combined) { | |
| 364 return this; | |
| 365 } else if (other.flags == combined) { | |
| 366 return other; | |
| 367 } else { | |
| 368 return new FlatTypeMask.normalized(base, combined, classWorld); | |
| 369 } | |
| 370 } | |
| 371 | |
| 372 TypeMask intersectionStrictSubclass(FlatTypeMask other, | |
| 373 ClassWorld classWorld) { | |
| 374 assert(base != other.base); | |
| 375 assert(classWorld.isSubclassOf(other.base, base)); | |
| 376 // If this mask isn't at least a subclass mask, then the | |
| 377 // intersection with the other mask is empty. | |
| 378 if (isExact) return intersectionEmpty(other); | |
| 379 // Only the other mask puts constraints on the intersection mask, | |
| 380 // so base the combined flags on the other mask. Only if both | |
| 381 // masks are nullable, will the result be nullable too. | |
| 382 // The result is guaranteed to be normalized, as the other type | |
| 383 // was normalized. | |
| 384 int combined = other.flags & ((flags & 1) | ~1); | |
| 385 if (other.flags == combined) { | |
| 386 return other; | |
| 387 } else { | |
| 388 return new FlatTypeMask.normalized(other.base, combined, classWorld); | |
| 389 } | |
| 390 } | |
| 391 | |
| 392 TypeMask intersectionStrictSubtype(FlatTypeMask other, | |
| 393 ClassWorld classWorld) { | |
| 394 assert(base != other.base); | |
| 395 assert(classWorld.isSubtypeOf(other.base, base)); | |
| 396 if (!isSubtype) return intersectionHelper(other, classWorld); | |
| 397 // Only the other mask puts constraints on the intersection mask, | |
| 398 // so base the combined flags on the other mask. Only if both | |
| 399 // masks are nullable, will the result be nullable too. | |
| 400 // The result is guaranteed to be normalized, as the other type | |
| 401 // was normalized. | |
| 402 int combined = other.flags & ((flags & 1) | ~1); | |
| 403 if (other.flags == combined) { | |
| 404 return other; | |
| 405 } else { | |
| 406 return new FlatTypeMask.normalized(other.base, combined, classWorld); | |
| 407 } | |
| 408 } | |
| 409 | |
| 410 TypeMask intersectionDisjoint(FlatTypeMask other, ClassWorld classWorld) { | |
| 411 assert(base != other.base); | |
| 412 assert(!classWorld.isSubtypeOf(base, other.base)); | |
| 413 assert(!classWorld.isSubtypeOf(other.base, base)); | |
| 414 return intersectionHelper(other, classWorld); | |
| 415 } | |
| 416 | |
| 417 TypeMask intersectionHelper(FlatTypeMask other, ClassWorld classWorld) { | |
| 418 assert(base != other.base); | |
| 419 assert(!classWorld.isSubclassOf(base, other.base)); | |
| 420 assert(!classWorld.isSubclassOf(other.base, base)); | |
| 421 // If one of the masks are exact or if both of them are subclass | |
| 422 // masks, then the intersection is empty. | |
| 423 if (isExact || other.isExact) return intersectionEmpty(other); | |
| 424 if (isSubclass && other.isSubclass) return intersectionEmpty(other); | |
| 425 assert(isSubtype || other.isSubtype); | |
| 426 int kind = (isSubclass || other.isSubclass) ? SUBCLASS : SUBTYPE; | |
| 427 // Compute the set of classes that are contained in both type masks. | |
| 428 Set<ClassElement> common = commonContainedClasses(this, other, classWorld); | |
| 429 if (common == null || common.isEmpty) return intersectionEmpty(other); | |
| 430 // Narrow down the candidates by only looking at common classes | |
| 431 // that do not have a superclass or supertype that will be a | |
| 432 // better candidate. | |
| 433 Iterable<ClassElement> candidates = common.where((ClassElement each) { | |
| 434 bool containsSuperclass = common.contains(each.supertype.element); | |
| 435 // If the superclass is also a candidate, then we don't want to | |
| 436 // deal with this class. If we're only looking for a subclass we | |
| 437 // know we don't have to look at the list of interfaces because | |
| 438 // they can never be in the common set. | |
| 439 if (containsSuperclass || kind == SUBCLASS) return !containsSuperclass; | |
| 440 // Run through the direct supertypes of the class. If the common | |
| 441 // set contains the direct supertype of the class, we ignore the | |
| 442 // the class because the supertype is a better candidate. | |
| 443 for (Link link = each.interfaces; !link.isEmpty; link = link.tail) { | |
| 444 if (common.contains(link.head.element)) return false; | |
| 445 } | |
| 446 return true; | |
| 447 }); | |
| 448 // Run through the list of candidates and compute the union. The | |
| 449 // result will only be nullable if both masks are nullable. We have | |
| 450 // to normalize here, as we generate types based on new base classes. | |
| 451 int combined = (kind << 1) | (flags & other.flags & 1); | |
| 452 Iterable<TypeMask> masks = candidates.map((ClassElement cls) { | |
| 453 return new FlatTypeMask.normalized(cls, combined, classWorld); | |
| 454 }); | |
| 455 return UnionTypeMask.unionOf(masks, classWorld); | |
| 456 } | |
| 457 | |
| 458 TypeMask intersectionEmpty(FlatTypeMask other) { | |
| 459 return isNullable && other.isNullable ? new TypeMask.empty() | |
| 460 : new TypeMask.nonNullEmpty(); | |
| 461 } | |
| 462 | |
| 463 /** | |
| 464 * Returns whether [element] will be the one used at runtime when being | |
| 465 * invoked on an instance of [cls]. [selector] is used to ensure library | |
| 466 * privacy is taken into account. | |
| 467 */ | |
| 468 static bool hasElementIn(ClassElement cls, | |
| 469 Selector selector, | |
| 470 Element element) { | |
| 471 // Use [:implementation:] of [element] | |
| 472 // because our function set only stores declarations. | |
| 473 Element result = findMatchIn(cls, selector); | |
| 474 return result == null | |
| 475 ? false | |
| 476 : result.implementation == element.implementation; | |
| 477 } | |
| 478 | |
| 479 static Element findMatchIn(ClassElement cls, | |
| 480 Selector selector) { | |
| 481 // Use the [:implementation] of [cls] in case the found [element] | |
| 482 // is in the patch class. | |
| 483 return cls.implementation.lookupSelector(selector); | |
| 484 } | |
| 485 | |
| 486 /** | |
| 487 * Returns whether [element] is a potential target when being | |
| 488 * invoked on this type mask. [selector] is used to ensure library | |
| 489 * privacy is taken into account. | |
| 490 */ | |
| 491 bool canHit(Element element, Selector selector, ClassWorld classWorld) { | |
| 492 Backend backend = classWorld.backend; | |
| 493 assert(element.name == selector.name); | |
| 494 if (isEmpty) { | |
| 495 if (!isNullable) return false; | |
| 496 return hasElementIn(backend.nullImplementation, selector, element); | |
| 497 } | |
| 498 | |
| 499 // TODO(kasperl): Can't we just avoid creating typed selectors | |
| 500 // based of function types? | |
| 501 Element self = base; | |
| 502 if (self.isTypedef) { | |
| 503 // A typedef is a function type that doesn't have any | |
| 504 // user-defined members. | |
| 505 return false; | |
| 506 } | |
| 507 | |
| 508 ClassElement other = element.enclosingClass; | |
| 509 if (other == backend.nullImplementation) { | |
| 510 return isNullable; | |
| 511 } else if (isExact) { | |
| 512 return hasElementIn(self, selector, element); | |
| 513 } else if (isSubclass) { | |
| 514 assert(classWorld.isClosed); | |
| 515 return hasElementIn(self, selector, element) | |
| 516 || other.isSubclassOf(self) | |
| 517 || classWorld.hasAnySubclassThatMixes(self, other); | |
| 518 } else { | |
| 519 assert(isSubtype); | |
| 520 assert(classWorld.isClosed); | |
| 521 bool result = hasElementIn(self, selector, element) | |
| 522 || other.implementsInterface(self) | |
| 523 || classWorld.hasAnySubclassThatImplements(other, base) | |
| 524 || classWorld.hasAnySubclassOfMixinUseThatImplements(other, base); | |
| 525 if (result) return true; | |
| 526 // If the class is used as a mixin, we have to check if the element | |
| 527 // can be hit from any of the mixin applications. | |
| 528 Iterable<ClassElement> mixinUses = classWorld.mixinUsesOf(self); | |
| 529 return mixinUses.any((mixinApplication) => | |
| 530 hasElementIn(mixinApplication, selector, element) | |
| 531 || other.isSubclassOf(mixinApplication) | |
| 532 || classWorld.hasAnySubclassThatMixes(mixinApplication, other)); | |
| 533 } | |
| 534 } | |
| 535 | |
| 536 /** | |
| 537 * Returns whether a [selector] call on an instance of [cls] | |
| 538 * will hit a method at runtime, and not go through [noSuchMethod]. | |
| 539 */ | |
| 540 static bool hasConcreteMatch(ClassElement cls, | |
| 541 Selector selector, | |
| 542 World world) { | |
| 543 assert(invariant(cls, | |
| 544 world.compiler.resolverWorld.isInstantiated(cls), | |
| 545 message: '$cls has not been instantiated.')); | |
| 546 Element element = findMatchIn(cls, selector); | |
| 547 if (element == null) return false; | |
| 548 | |
| 549 if (element.isAbstract) { | |
| 550 ClassElement enclosingClass = element.enclosingClass; | |
| 551 return hasConcreteMatch(enclosingClass.superclass, selector, world); | |
| 552 } | |
| 553 return selector.appliesUntyped(element, world); | |
| 554 } | |
| 555 | |
| 556 bool needsNoSuchMethodHandling(Selector selector, ClassWorld classWorld) { | |
| 557 // A call on an empty type mask is either dead code, or a call on | |
| 558 // `null`. | |
| 559 if (isEmpty) return false; | |
| 560 // A call on an exact mask for an abstract class is dead code. | |
| 561 if (isExact && base.isAbstract) return false; | |
| 562 // If the receiver is guaranteed to have a member that | |
| 563 // matches what we're looking for, there's no need to | |
| 564 // introduce a noSuchMethod handler. It will never be called. | |
| 565 // | |
| 566 // As an example, consider this class hierarchy: | |
| 567 // | |
| 568 // A <-- noSuchMethod | |
| 569 // / \ | |
| 570 // C B <-- foo | |
| 571 // | |
| 572 // If we know we're calling foo on an object of type B we | |
| 573 // don't have to worry about the noSuchMethod method in A | |
| 574 // because objects of type B implement foo. On the other hand, | |
| 575 // if we end up calling foo on something of type C we have to | |
| 576 // add a handler for it. | |
| 577 | |
| 578 // If the holders of all user-defined noSuchMethod | |
| 579 // implementations that might be applicable to the receiver | |
| 580 // type have a matching member for the current name and | |
| 581 // selector, we avoid introducing a noSuchMethod handler. | |
| 582 // | |
| 583 // As an example, consider this class hierarchy: | |
| 584 // | |
| 585 // A <-- foo | |
| 586 // / \ | |
| 587 // noSuchMethod --> B C <-- bar | |
| 588 // | | | |
| 589 // C D <-- noSuchMethod | |
| 590 // | |
| 591 // When calling foo on an object of type A, we know that the | |
| 592 // implementations of noSuchMethod are in the classes B and D | |
| 593 // that also (indirectly) implement foo, so we do not need a | |
| 594 // handler for it. | |
| 595 // | |
| 596 // If we're calling bar on an object of type D, we don't need | |
| 597 // the handler either because all objects of type D implement | |
| 598 // bar through inheritance. | |
| 599 // | |
| 600 // If we're calling bar on an object of type A we do need the | |
| 601 // handler because we may have to call B.noSuchMethod since B | |
| 602 // does not implement bar. | |
| 603 | |
| 604 /// Returns `true` if [cls] is an instantiated class that does not have | |
| 605 /// a concrete method matching [selector]. | |
| 606 bool needsNoSuchMethod(ClassElement cls) { | |
| 607 // We can skip uninstantiated subclasses. | |
| 608 // TODO(johnniwinther): Put filtering into the (Class)World. | |
| 609 if (!classWorld.isInstantiated(cls)) { | |
| 610 return false; | |
| 611 } | |
| 612 // We can just skip abstract classes because we know no | |
| 613 // instance of them will be created at runtime, and | |
| 614 // therefore there is no instance that will require | |
| 615 // [noSuchMethod] handling. | |
| 616 return !cls.isAbstract | |
| 617 && !hasConcreteMatch(cls, selector, classWorld); | |
| 618 } | |
| 619 | |
| 620 bool baseNeedsNoSuchMethod = needsNoSuchMethod(base); | |
| 621 if (isExact || baseNeedsNoSuchMethod) { | |
| 622 return baseNeedsNoSuchMethod; | |
| 623 } | |
| 624 | |
| 625 Iterable<ClassElement> subclassesToCheck; | |
| 626 if (isSubtype) { | |
| 627 subclassesToCheck = classWorld.subtypesOf(base); | |
| 628 } else { | |
| 629 assert(isSubclass); | |
| 630 subclassesToCheck = classWorld.subclassesOf(base); | |
| 631 } | |
| 632 | |
| 633 return subclassesToCheck != null && | |
| 634 subclassesToCheck.any(needsNoSuchMethod); | |
| 635 } | |
| 636 | |
| 637 Element locateSingleElement(Selector selector, Compiler compiler) { | |
| 638 if (isEmpty) return null; | |
| 639 Iterable<Element> targets = compiler.world.allFunctions.filter(selector); | |
| 640 if (targets.length != 1) return null; | |
| 641 Element result = targets.first; | |
| 642 ClassElement enclosing = result.enclosingClass; | |
| 643 // We only return the found element if it is guaranteed to be | |
| 644 // implemented on the exact receiver type. It could be found in a | |
| 645 // subclass or in an inheritance-wise unrelated class in case of | |
| 646 // subtype selectors. | |
| 647 return (base.isSubclassOf(enclosing)) ? result : null; | |
| 648 } | |
| 649 | |
| 650 bool operator ==(var other) { | |
| 651 if (other is !FlatTypeMask) return false; | |
| 652 FlatTypeMask otherMask = other; | |
| 653 return (flags == otherMask.flags) && (base == otherMask.base); | |
| 654 } | |
| 655 | |
| 656 int get hashCode { | |
| 657 return (base == null ? 0 : base.hashCode) + 31 * flags.hashCode; | |
| 658 } | |
| 659 | |
| 660 String toString() { | |
| 661 if (isEmpty) return isNullable ? '[null]' : '[empty]'; | |
| 662 StringBuffer buffer = new StringBuffer(); | |
| 663 if (isNullable) buffer.write('null|'); | |
| 664 if (isExact) buffer.write('exact='); | |
| 665 if (isSubclass) buffer.write('subclass='); | |
| 666 if (isSubtype) buffer.write('subtype='); | |
| 667 buffer.write(base.name); | |
| 668 return "[$buffer]"; | |
| 669 } | |
| 670 | |
| 671 static Set<ClassElement> commonContainedClasses(FlatTypeMask x, | |
| 672 FlatTypeMask y, | |
| 673 ClassWorld classWorld) { | |
| 674 Iterable<ClassElement> xSubset = containedSubset(x, classWorld); | |
| 675 if (xSubset == null) return null; | |
| 676 Iterable<ClassElement> ySubset = containedSubset(y, classWorld); | |
| 677 if (ySubset == null) return null; | |
| 678 Iterable<ClassElement> smallSet, largeSet; | |
| 679 if (xSubset.length <= ySubset.length) { | |
| 680 smallSet = xSubset; | |
| 681 largeSet = ySubset; | |
| 682 } else { | |
| 683 smallSet = ySubset; | |
| 684 largeSet = xSubset; | |
| 685 } | |
| 686 var result = smallSet.where((ClassElement each) => largeSet.contains(each)); | |
| 687 return result.toSet(); | |
| 688 } | |
| 689 | |
| 690 static Iterable<ClassElement> containedSubset(FlatTypeMask x, | |
| 691 ClassWorld classWorld) { | |
| 692 ClassElement element = x.base; | |
| 693 if (x.isExact) { | |
| 694 return null; | |
| 695 } else if (x.isSubclass) { | |
| 696 return classWorld.subclassesOf(element); | |
| 697 } else { | |
| 698 assert(x.isSubtype); | |
| 699 return classWorld.subtypesOf(element); | |
| 700 } | |
| 701 } | |
| 702 } | |
| OLD | NEW |