blob: a8e4238037eaf27f98432046eab0c09502bbca41 [file] [log] [blame]
// Copyright (c) 2012, 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.
part of ssa;
abstract class HType {
const HType();
/**
* Returns an [HType] with the given type mask. The factory method
* takes care to track whether or not the resulting type may be a
* primitive type.
*/
factory HType.fromMask(TypeMask mask, Compiler compiler) {
bool isNullable = mask.isNullable;
JavaScriptBackend backend = compiler.backend;
if (mask.isEmpty) {
return isNullable ? backend.nullType : backend.emptyType;
}
if (mask.containsOnlyInt(compiler)) {
return mask.isNullable
? new HBoundedType(new TypeMask.exact(backend.jsIntClass))
: backend.intType;
} else if (mask.containsOnlyDouble(compiler)) {
return mask.isNullable
? new HBoundedType(new TypeMask.exact(backend.jsDoubleClass))
: backend.doubleType;
} else if (mask.containsOnlyNum(compiler)
|| mask.satisfies(backend.jsNumberClass, compiler)) {
return mask.isNullable
? new HBoundedType(new TypeMask.subclass(backend.jsNumberClass))
: backend.numType;
} else if (mask.containsOnlyString(compiler)) {
// TODO(ngeoffray): Avoid creating [TypeMask]s with the string
// class as base.
return isNullable
? new HBoundedType(new TypeMask.exact(backend.jsStringClass))
: backend.stringType;
} else if (mask.containsOnlyBool(compiler)) {
return isNullable
? new HBoundedType(new TypeMask.exact(backend.jsBoolClass))
: backend.boolType;
} else if (mask.containsOnlyNull(compiler)) {
return backend.nullType;
}
// TODO(kasperl): A lot of the code in the system currently
// expects the top type to be 'unknown'. I'll rework this.
if (mask.containsAll(compiler)) {
return isNullable ? backend.dynamicType : backend.nonNullType;
}
return new HBoundedType(mask);
}
factory HType.exact(ClassElement type, Compiler compiler) {
TypeMask mask = new TypeMask.exact(type.declaration);
return new HType.fromMask(mask, compiler);
}
factory HType.subclass(ClassElement type, Compiler compiler) {
TypeMask mask = new TypeMask.subclass(type.declaration);
return new HType.fromMask(mask, compiler);
}
factory HType.subtype(ClassElement type, Compiler compiler) {
TypeMask mask = new TypeMask.subtype(type.declaration);
return new HType.fromMask(mask, compiler);
}
factory HType.nonNullExact(ClassElement type, Compiler compiler) {
TypeMask mask = new TypeMask.nonNullExact(type.declaration);
return new HType.fromMask(mask, compiler);
}
factory HType.nonNullSubclass(ClassElement type, Compiler compiler) {
TypeMask mask = new TypeMask.nonNullSubclass(type.declaration);
return new HType.fromMask(mask, compiler);
}
factory HType.nonNullSubtype(ClassElement type, Compiler compiler) {
TypeMask mask = new TypeMask.nonNullSubtype(type.declaration);
return new HType.fromMask(mask, compiler);
}
factory HType.fromInferredType(TypeMask mask, Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
if (mask == null) return backend.dynamicType;
return new HType.fromMask(mask, compiler);
}
factory HType.inferredReturnTypeForElement(
Element element, Compiler compiler) {
return new HType.fromInferredType(
compiler.typesTask.getGuaranteedReturnTypeOfElement(element),
compiler);
}
factory HType.inferredTypeForElement(Element element, Compiler compiler) {
return new HType.fromInferredType(
compiler.typesTask.getGuaranteedTypeOfElement(element),
compiler);
}
factory HType.inferredTypeForSelector(Selector selector, Compiler compiler) {
return new HType.fromInferredType(
compiler.typesTask.getGuaranteedTypeOfSelector(selector),
compiler);
}
factory HType.inferredForNode(Element owner, Node node, Compiler compiler) {
return new HType.fromInferredType(
compiler.typesTask.getGuaranteedTypeOfNode(owner, node),
compiler);
}
factory HType.fromNativeBehavior(native.NativeBehavior nativeBehavior,
Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
if (nativeBehavior.typesReturned.isEmpty) return backend.dynamicType;
HType result = nativeBehavior.typesReturned
.map((type) => fromNativeType(type, compiler))
.reduce((t1, t2) => t1.union(t2, compiler));
assert(!result.isConflicting());
return result;
}
// [type] is either an instance of [DartType] or special objects
// like [native.SpecialType.JsObject].
static HType fromNativeType(type, Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
if (type == native.SpecialType.JsObject) {
return new HType.nonNullExact(compiler.objectClass, compiler);
} else if (type.isVoid) {
return backend.nullType;
} else if (type.element == compiler.nullClass) {
return backend.nullType;
} else if (type.treatAsDynamic) {
return backend.dynamicType;
} else if (compiler.world.hasAnySubtype(type.element)) {
return new HType.nonNullSubtype(type.element, compiler);
} else if (compiler.world.hasAnySubclass(type.element)) {
return new HType.nonNullSubclass(type.element, compiler);
} else {
return new HType.nonNullExact(type.element, compiler);
}
}
bool isConflicting() => false;
bool isExact() => false;
bool isNull() => false;
bool isBoolean(Compiler compiler) => false;
bool isNumber(Compiler compiler) => false;
bool isInteger(Compiler compiler) => false;
bool isDouble(Compiler compiler) => false;
bool isString(Compiler compiler) => false;
bool isFixedArray(Compiler compiler) => false;
bool isReadableArray(Compiler compiler) => false;
bool isMutableArray(Compiler compiler) => false;
bool isExtendableArray(Compiler compiler) => false;
bool isPrimitive(Compiler compiler) => false;
bool isPrimitiveOrNull(Compiler compiler) => false;
bool isBooleanOrNull(Compiler compiler) => false;
bool isNumberOrNull(Compiler compiler) => false;
bool isIntegerOrNull(Compiler compiler) => false;
bool isDoubleOrNull(Compiler compiler) => false;
// TODO(kasperl): Get rid of this one.
bool isIndexablePrimitive(Compiler compiler) => false;
bool isIndexable(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return implementsInterface(backend.jsIndexableClass, compiler);
}
bool isMutableIndexable(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return implementsInterface(backend.jsMutableIndexableClass, compiler);
}
bool implementsInterface(ClassElement interfaceElement, Compiler compiler) {
TypeMask mask = new TypeMask.subtype(interfaceElement);
return mask == mask.union(computeMask(compiler), compiler);
}
bool canBeNull() => false;
bool canBePrimitive(Compiler compiler) => false;
bool canBePrimitiveNumber(Compiler compiler) => false;
bool canBePrimitiveString(Compiler compiler) => false;
bool canBePrimitiveArray(Compiler compiler) => false;
bool canBePrimitiveBoolean(Compiler compiler) => false;
/** Alias for isReadableArray. */
bool isArray(Compiler compiler) => isReadableArray(compiler);
TypeMask computeMask(Compiler compiler);
Selector refine(Selector selector, Compiler compiler) {
// TODO(kasperl): Should we check if the refinement really is more
// specialized than the starting point?
TypeMask mask = computeMask(compiler);
if (selector.mask == mask) return selector;
return new TypedSelector(mask, selector);
}
/**
* The intersection of two types is the intersection of its values. For
* example:
* * INTEGER.intersect(NUMBER) => INTEGER.
* * DOUBLE.intersect(INTEGER) => CONFLICTING.
* * MUTABLE_ARRAY.intersect(READABLE_ARRAY) => MUTABLE_ARRAY.
*
* When there is no predefined type to represent the intersection returns
* [CONFLICTING].
*
* An intersection with [UNKNOWN] returns the non-UNKNOWN type. An
* intersection with [CONFLICTING] returns [CONFLICTING].
*/
HType intersection(HType other, Compiler compiler) {
TypeMask mask = computeMask(compiler);
TypeMask otherMask = other.computeMask(compiler);
TypeMask intersection = mask.intersection(otherMask, compiler);
return new HType.fromMask(intersection, compiler);
}
/**
* The union of two types is the union of its values. For example:
* * INTEGER.union(NUMBER) => NUMBER.
* * DOUBLE.union(INTEGER) => NUMBER.
* * MUTABLE_ARRAY.union(READABLE_ARRAY) => READABLE_ARRAY.
*
* When there is no predefined type to represent the union returns
* [UNKNOWN].
*
* A union with [UNKNOWN] returns [UNKNOWN].
* A union of [CONFLICTING] with any other types returns the other type.
*/
HType union(HType other, Compiler compiler) {
TypeMask mask = computeMask(compiler);
TypeMask otherMask = other.computeMask(compiler);
TypeMask union = mask.union(otherMask, compiler);
return new HType.fromMask(union, compiler);
}
HType simplify(Compiler compiler) => this;
HType nonNullable(compiler) {
return new HType.fromMask(computeMask(compiler).nonNullable(), compiler);
}
bool containsAll(Compiler compiler) {
return computeMask(compiler).containsAll(compiler);
}
}
class HBoundedType extends HType {
final TypeMask mask;
const HBoundedType(this.mask);
bool isExact() => mask.isExact || isNull();
bool canBeNull() => mask.isNullable;
bool isNull() => mask.isEmpty && mask.isNullable;
bool isConflicting() => mask.isEmpty && !mask.isNullable;
bool canBePrimitive(Compiler compiler) {
return canBePrimitiveNumber(compiler)
|| canBePrimitiveArray(compiler)
|| canBePrimitiveBoolean(compiler)
|| canBePrimitiveString(compiler)
|| isNull();
}
bool canBePrimitiveNumber(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return mask.contains(backend.jsNumberClass, compiler)
|| mask.contains(backend.jsIntClass, compiler)
|| mask.contains(backend.jsDoubleClass, compiler);
}
bool canBePrimitiveBoolean(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return mask.contains(backend.jsBoolClass, compiler);
}
bool canBePrimitiveArray(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return mask.contains(backend.jsArrayClass, compiler)
|| mask.contains(backend.jsFixedArrayClass, compiler)
|| mask.contains(backend.jsExtendableArrayClass, compiler);
}
bool isIndexablePrimitive(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return mask.satisfies(backend.jsIndexableClass, compiler);
}
bool isFixedArray(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return mask.containsOnly(backend.jsFixedArrayClass);
}
bool isExtendableArray(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return mask.containsOnly(backend.jsExtendableArrayClass);
}
bool isMutableArray(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return mask.satisfies(backend.jsMutableArrayClass, compiler);
}
bool isReadableArray(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return mask.satisfies(backend.jsArrayClass, compiler);
}
bool canBePrimitiveString(Compiler compiler) {
JavaScriptBackend backend = compiler.backend;
return mask.contains(backend.jsStringClass, compiler);
}
TypeMask computeMask(Compiler compiler) => mask;
HType simplify(Compiler compiler) {
return new HType.fromMask(mask.simplify(compiler), compiler);
}
bool isInteger(Compiler compiler) {
return mask.containsOnlyInt(compiler) && !mask.isNullable;
}
bool isIntegerOrNull(Compiler compiler) {
return mask.containsOnlyInt(compiler);
}
bool isNumber(Compiler compiler) {
return mask.containsOnlyNum(compiler) && !mask.isNullable;
}
bool isNumberOrNull(Compiler compiler) {
return mask.containsOnlyNum(compiler);
}
bool isDouble(Compiler compiler) {
return mask.containsOnlyDouble(compiler) && !mask.isNullable;
}
bool isDoubleOrNull(Compiler compiler) {
return mask.containsOnlyDouble(compiler);
}
bool isBoolean(Compiler compiler) {
return mask.containsOnlyBool(compiler) && !mask.isNullable;
}
bool isBooleanOrNull(Compiler compiler) {
return mask.containsOnlyBool(compiler);
}
bool isString(Compiler compiler) {
return mask.containsOnlyString(compiler);
}
bool isPrimitive(Compiler compiler) {
return (isPrimitiveOrNull(compiler) && !mask.isNullable)
|| isNull();
}
bool isPrimitiveOrNull(Compiler compiler) {
return isIndexablePrimitive(compiler)
|| isNumberOrNull(compiler)
|| isBooleanOrNull(compiler)
|| isNull();
}
bool operator ==(other) {
if (other is !HBoundedType) return false;
HBoundedType bounded = other;
return mask == bounded.mask;
}
int get hashCode => mask.hashCode;
String toString() {
return 'BoundedType(mask=$mask)';
}
}