/* __ *\
** ________ ___ / / ___ __ ____ Scala.js IR **
** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
** /____/\___/_/ |_/____/_/ | |__/ /____/ **
** |/____/ **
\* */
package scala.scalajs.ir
import scala.annotation.tailrec
object Types {
/** Type of an expression in the IR. */
abstract sealed class Type {
def show(): String = {
val writer = new java.io.StringWriter
val printer = new Printers.IRTreePrinter(writer)
printer.printType(this)
writer.toString()
}
}
/** Any type (the top type of this type system).
* A variable of this type can contain any value, including `undefined`
* and `null` and any raw JS value. This type supports a very limited set
* of Scala operations, the ones common to all values. Basically only
* reference equality tests and instance tests. It also supports all
* JavaScript operations, since all Scala objects are also genuine
* JavaScript objects.
* The type java.lang.Object in the back-end maps to [[AnyType]] because it
* can hold raw JS values (not only instances of Scala.js classes).
*/
case object AnyType extends Type
/** Nothing type (the bottom type of this type system).
* Expressions from which one can never come back are typed as [[Nothing]].
* For example, `throw` and `return`.
*/
case object NothingType extends Type
/** The type of `undefined`. */
case object UndefType extends Type
/** Boolean type.
* It does not accept `null` nor `undefined`.
*/
case object BooleanType extends Type
/** 32-bit signed integer type.
* It does not accept `null` nor `undefined`.
*/
case object IntType extends Type
/** 64-bit signed integer type.
* It does not accept `null` nor `undefined`.
*/
case object LongType extends Type
/** Float type (32-bit).
* It does not accept `null` nor `undefined`.
*/
case object FloatType extends Type
/** Double type (64-bit).
* It does not accept `null` nor `undefined`.
*/
case object DoubleType extends Type
/** String type.
* It does not accept `null` nor `undefined`.
*/
case object StringType extends Type
/** The type of `null`.
* It does not accept `undefined`.
* The null type is a subtype of all class types and array types.
*/
case object NullType extends Type
/** Reference types (allowed for classOf[], is/asInstanceOf[]). */
sealed abstract class ReferenceType extends Type
/** Class (or interface) type. */
final case class ClassType(className: String) extends ReferenceType
/** Array type. */
final case class ArrayType(baseClassName: String, dimensions: Int) extends ReferenceType
object ArrayType {
def apply(innerType: ReferenceType): ArrayType = innerType match {
case ClassType(className) => ArrayType(className, 1)
case ArrayType(className, dim) => ArrayType(className, dim + 1)
}
}
/** Record type.
* Used by the optimizer to inline classes as records with multiple fields.
* They are desugared as several local variables by JSDesugaring.
* Record types cannot cross method boundaries, so they cannot appear as
* the type of fields or parameters, nor as result types of methods.
* The compiler itself never generates record types.
*/
final case class RecordType(fields: List[RecordType.Field]) extends Type {
def findField(name: String): RecordType.Field =
fields.find(_.name == name).get
}
object RecordType {
final case class Field(name: String, originalName: Option[String],
tpe: Type, mutable: Boolean)
}
/** No type. */
case object NoType extends Type
/** Tests whether a type `lhs` is a subtype of `rhs` (or equal).
* [[NoType]] is never a subtype or supertype of anything (including
* itself). All other types are subtypes of themselves.
* @param isSubclass A function testing whether a class/interface is a
* subclass of another class/interface.
*/
def isSubtype(lhs: Type, rhs: Type)(
isSubclass: (String, String) => Boolean): Boolean = {
import Definitions._
(lhs != NoType && rhs != NoType) && {
(lhs == rhs) ||
((lhs, rhs) match {
case (_, AnyType) => true
case (NothingType, _) => true
case (ClassType(lhsClass), ClassType(rhsClass)) =>
isSubclass(lhsClass, rhsClass)
case (NullType, ClassType(_)) => true
case (NullType, ArrayType(_, _)) => true
case (UndefType, ClassType(cls)) =>
isSubclass(BoxedUnitClass, cls)
case (BooleanType, ClassType(cls)) =>
isSubclass(BoxedBooleanClass, cls)
case (IntType, ClassType(cls)) =>
isSubclass(BoxedIntegerClass, cls) ||
cls == BoxedByteClass ||
cls == BoxedShortClass ||
cls == BoxedDoubleClass
case (LongType, ClassType(cls)) =>
isSubclass(BoxedLongClass, cls)
case (FloatType, ClassType(cls)) =>
isSubclass(BoxedFloatClass, cls) ||
cls == BoxedDoubleClass
case (DoubleType, ClassType(cls)) =>
isSubclass(BoxedDoubleClass, cls)
case (StringType, ClassType(cls)) =>
isSubclass(StringClass, cls)
case (IntType, DoubleType) => true
case (FloatType, DoubleType) => true
case (ArrayType(lhsBase, lhsDims), ArrayType(rhsBase, rhsDims)) =>
if (lhsDims < rhsDims) {
false // because Array[A] </: Array[Array[A]]
} else if (lhsDims > rhsDims) {
rhsBase == ObjectClass // because Array[Array[A]] <: Array[Object]
} else { // lhsDims == rhsDims
// lhsBase must be <: rhsBase
if (isPrimitiveClass(lhsBase) || isPrimitiveClass(rhsBase)) {
lhsBase == rhsBase
} else {
isSubclass(lhsBase, rhsBase)
}
}
case _ =>
false
})
}
}
}