summaryrefslogtreecommitdiff
path: root/examples/scala-js/ir/src/main/scala/scala/scalajs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/ir/src/main/scala/scala/scalajs')
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ClassKind.scala53
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Definitions.scala144
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Hashers.scala459
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala180
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Infos.scala118
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Position.scala42
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Printers.scala709
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala25
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Serializers.scala790
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Tags.scala107
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Transformers.scala218
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Traversers.scala197
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Trees.scala536
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Types.scala182
-rw-r--r--examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Utils.scala110
15 files changed, 3870 insertions, 0 deletions
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ClassKind.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ClassKind.scala
new file mode 100644
index 0000000..5092d2c
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ClassKind.scala
@@ -0,0 +1,53 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import scala.annotation.switch
+
+sealed abstract class ClassKind {
+ import ClassKind._
+
+ def isClass = this match {
+ case Class | ModuleClass => true
+ case _ => false
+ }
+
+ def isType = this match {
+ case TraitImpl => false
+ case _ => true
+ }
+}
+
+object ClassKind {
+ case object Class extends ClassKind
+ case object ModuleClass extends ClassKind
+ case object Interface extends ClassKind
+ case object RawJSType extends ClassKind
+ case object HijackedClass extends ClassKind
+ case object TraitImpl extends ClassKind
+
+ private[ir] def toByte(kind: ClassKind): Byte = kind match {
+ case ClassKind.Class => 1
+ case ClassKind.ModuleClass => 2
+ case ClassKind.Interface => 3
+ case ClassKind.RawJSType => 4
+ case ClassKind.HijackedClass => 5
+ case ClassKind.TraitImpl => 6
+ }
+
+ private[ir] def fromByte(b: Byte): ClassKind = (b: @switch) match {
+ case 1 => ClassKind.Class
+ case 2 => ClassKind.ModuleClass
+ case 3 => ClassKind.Interface
+ case 4 => ClassKind.RawJSType
+ case 5 => ClassKind.HijackedClass
+ case 6 => ClassKind.TraitImpl
+ }
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Definitions.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Definitions.scala
new file mode 100644
index 0000000..0762602
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Definitions.scala
@@ -0,0 +1,144 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+object Definitions {
+ val ObjectClass = "O"
+ val ClassClass = "jl_Class"
+
+ val StringClass = "T"
+
+ val PrimitiveClasses = Set("V", "Z", "C", "B", "S", "I", "J", "F", "D")
+
+ def isPrimitiveClass(encodedName: String): Boolean =
+ PrimitiveClasses.contains(encodedName)
+
+ val BoxedUnitClass = "sr_BoxedUnit"
+ val BoxedBooleanClass = "jl_Boolean"
+ val BoxedCharacterClass = "jl_Character"
+ val BoxedByteClass = "jl_Byte"
+ val BoxedShortClass = "jl_Short"
+ val BoxedIntegerClass = "jl_Integer"
+ val BoxedLongClass = "jl_Long"
+ val BoxedFloatClass = "jl_Float"
+ val BoxedDoubleClass = "jl_Double"
+
+ val CharSequenceClass = "jl_CharSequence"
+ val SerializableClass = "Ljava_io_Serializable"
+ val ComparableClass = "jl_Comparable"
+ val NumberClass = "jl_Number"
+
+ val HijackedBoxedClasses = Set(
+ BoxedUnitClass, BoxedBooleanClass, BoxedByteClass, BoxedShortClass,
+ BoxedIntegerClass, BoxedLongClass, BoxedFloatClass, BoxedDoubleClass)
+ val HijackedClasses =
+ HijackedBoxedClasses + StringClass
+
+ val AncestorsOfStringClass = Set(
+ CharSequenceClass, ComparableClass, SerializableClass)
+ val AncestorsOfHijackedNumberClasses = Set(
+ NumberClass, ComparableClass, SerializableClass)
+ val AncestorsOfBoxedBooleanClass = Set(
+ ComparableClass, SerializableClass)
+
+ val AncestorsOfHijackedClasses =
+ AncestorsOfStringClass ++ AncestorsOfHijackedNumberClasses ++
+ AncestorsOfBoxedBooleanClass
+
+ val RuntimeNullClass = "sr_Null$"
+ val RuntimeNothingClass = "sr_Nothing$"
+
+ val ThrowableClass = "jl_Throwable"
+
+ /** Encodes a class name. */
+ def encodeClassName(fullName: String): String = {
+ val base = fullName.replace("_", "$und").replace(".", "_")
+ val encoded = compressedClasses.getOrElse(base, {
+ compressedPrefixes collectFirst {
+ case (prefix, compressed) if base.startsWith(prefix) =>
+ compressed + base.substring(prefix.length)
+ } getOrElse {
+ "L"+base
+ }
+ })
+ if (Trees.isKeyword(encoded) || encoded.charAt(0).isDigit ||
+ encoded.charAt(0) == '$') {
+ "$" + encoded
+ } else encoded
+ }
+
+ // !!! Duplicate logic: this code must be in sync with runtime.StackTrace
+
+ /** Decodes a class name encoded with [[encodeClassName]]. */
+ def decodeClassName(encodedName: String): String = {
+ val encoded =
+ if (encodedName.charAt(0) == '$') encodedName.substring(1)
+ else encodedName
+ val base = decompressedClasses.getOrElse(encoded, {
+ decompressedPrefixes collectFirst {
+ case (prefix, decompressed) if encoded.startsWith(prefix) =>
+ decompressed + encoded.substring(prefix.length)
+ } getOrElse {
+ assert(!encoded.isEmpty && encoded.charAt(0) == 'L',
+ s"Cannot decode invalid encoded name '$encodedName'")
+ encoded.substring(1)
+ }
+ })
+ base.replace("_", ".").replace("$und", "_")
+ }
+
+ private val compressedClasses: Map[String, String] = Map(
+ "java_lang_Object" -> "O",
+ "java_lang_String" -> "T",
+ "scala_Unit" -> "V",
+ "scala_Boolean" -> "Z",
+ "scala_Char" -> "C",
+ "scala_Byte" -> "B",
+ "scala_Short" -> "S",
+ "scala_Int" -> "I",
+ "scala_Long" -> "J",
+ "scala_Float" -> "F",
+ "scala_Double" -> "D"
+ ) ++ (
+ for (index <- 2 to 22)
+ yield s"scala_Tuple$index" -> ("T"+index)
+ ) ++ (
+ for (index <- 0 to 22)
+ yield s"scala_Function$index" -> ("F"+index)
+ )
+
+ private val decompressedClasses: Map[String, String] =
+ compressedClasses map { case (a, b) => (b, a) }
+
+ private val compressedPrefixes = Seq(
+ "scala_scalajs_runtime_" -> "sjsr_",
+ "scala_scalajs_" -> "sjs_",
+ "scala_collection_immutable_" -> "sci_",
+ "scala_collection_mutable_" -> "scm_",
+ "scala_collection_generic_" -> "scg_",
+ "scala_collection_" -> "sc_",
+ "scala_runtime_" -> "sr_",
+ "scala_" -> "s_",
+ "java_lang_" -> "jl_",
+ "java_util_" -> "ju_"
+ )
+
+ private val decompressedPrefixes: Seq[(String, String)] =
+ compressedPrefixes map { case (a, b) => (b, a) }
+
+ /* Common predicates on encoded names */
+
+ def isConstructorName(name: String): Boolean =
+ name.startsWith("init___")
+
+ def isReflProxyName(name: String): Boolean =
+ name.endsWith("__") && !isConstructorName(name)
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Hashers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Hashers.scala
new file mode 100644
index 0000000..168d7c1
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Hashers.scala
@@ -0,0 +1,459 @@
+package scala.scalajs.ir
+
+import java.security.{MessageDigest, DigestOutputStream}
+import java.io.{OutputStream, DataOutputStream}
+import java.util.Arrays
+
+import Trees._
+import Types._
+import Tags._
+
+object Hashers {
+
+ def hashMethodDef(methodDef: MethodDef): MethodDef = {
+ if (methodDef.hash.isDefined) methodDef
+ else {
+ val hasher = new TreeHasher()
+ val MethodDef(name, args, resultType, body) = methodDef
+
+ hasher.mixPos(methodDef.pos)
+ hasher.mixPropertyName(name)
+ hasher.mixTrees(args)
+ hasher.mixType(resultType)
+ hasher.mixTree(body)
+
+ val hash = hasher.finalizeHash()
+
+ MethodDef(name, args, resultType, body)(Some(hash))(methodDef.pos)
+ }
+ }
+
+ /** Hash definitions from a ClassDef where applicable */
+ def hashDefs(defs: List[Tree]): List[Tree] = defs map {
+ case methodDef: MethodDef => hashMethodDef(methodDef)
+ case otherDef => otherDef
+ }
+
+ /** Hash the definitions in a ClassDef (where applicable) */
+ def hashClassDef(classDef: ClassDef): ClassDef =
+ classDef.copy(defs = hashDefs(classDef.defs))(classDef.pos)
+
+ def hashesEqual(x: TreeHash, y: TreeHash, considerPos: Boolean): Boolean = {
+ Arrays.equals(x.treeHash, y.treeHash) &&
+ (!considerPos || Arrays.equals(x.posHash, y.posHash))
+ }
+
+ private final class TreeHasher {
+ private def newDigest = MessageDigest.getInstance("SHA-1")
+ private def newDigestStream(digest: MessageDigest) = {
+ val out = new OutputStream {
+ def write(b: Int): Unit = ()
+ }
+ val digOut = new DigestOutputStream(out, digest)
+ new DataOutputStream(digOut)
+ }
+
+ private[this] val treeDigest = newDigest
+ private[this] val treeStream = newDigestStream(treeDigest)
+
+ private[this] val posDigest = newDigest
+ private[this] val posStream = newDigestStream(posDigest)
+
+ def finalizeHash(): TreeHash =
+ new TreeHash(treeDigest.digest(), posDigest.digest())
+
+ def mixTree(tree: Tree): Unit = {
+ mixPos(tree.pos)
+ tree match {
+ case EmptyTree =>
+ mixTag(TagEmptyTree)
+
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ mixTag(TagVarDef)
+ mixIdent(ident)
+ mixType(vtpe)
+ mixBoolean(mutable)
+ mixTree(rhs)
+
+ case ParamDef(ident, ptpe, mutable) =>
+ mixTag(TagParamDef)
+ mixIdent(ident)
+ mixType(ptpe)
+ mixBoolean(mutable)
+
+ case Skip() =>
+ mixTag(TagSkip)
+
+ case Block(stats) =>
+ mixTag(TagBlock)
+ mixTrees(stats)
+
+ case Labeled(label, tpe, body) =>
+ mixTag(TagLabeled)
+ mixIdent(label)
+ mixType(tpe)
+ mixTree(body)
+
+ case Assign(lhs, rhs) =>
+ mixTag(TagAssign)
+ mixTree(lhs)
+ mixTree(rhs)
+
+ case Return(expr, label) =>
+ mixTag(TagReturn)
+ mixTree(expr)
+ mixOptIdent(label)
+
+ case If(cond, thenp, elsep) =>
+ mixTag(TagIf)
+ mixTree(cond)
+ mixTree(thenp)
+ mixTree(elsep)
+ mixType(tree.tpe)
+
+ case While(cond, body, label) =>
+ mixTag(TagWhile)
+ mixTree(cond)
+ mixTree(body)
+ mixOptIdent(label)
+
+ case DoWhile(body, cond, label) =>
+ mixTag(TagDoWhile)
+ mixTree(body)
+ mixTree(cond)
+ mixOptIdent(label)
+
+ case Try(block, errVar, handler, finalizer) =>
+ mixTag(TagTry)
+ mixTree(block)
+ mixIdent(errVar)
+ mixTree(handler)
+ mixTree(finalizer)
+ mixType(tree.tpe)
+
+ case Throw(expr) =>
+ mixTag(TagThrow)
+ mixTree(expr)
+
+ case Continue(label) =>
+ mixTag(TagContinue)
+ mixOptIdent(label)
+
+ case Match(selector, cases, default) =>
+ mixTag(TagMatch)
+ mixTree(selector)
+ cases foreach { case (patterns, body) =>
+ mixTrees(patterns)
+ mixTree(body)
+ }
+ mixTree(default)
+ mixType(tree.tpe)
+
+ case Debugger() =>
+ mixTag(TagDebugger)
+
+ case New(cls, ctor, args) =>
+ mixTag(TagNew)
+ mixType(cls)
+ mixIdent(ctor)
+ mixTrees(args)
+
+ case LoadModule(cls) =>
+ mixTag(TagLoadModule)
+ mixType(cls)
+
+ case StoreModule(cls, value) =>
+ mixTag(TagStoreModule)
+ mixType(cls)
+ mixTree(value)
+
+ case Select(qualifier, item, mutable) =>
+ mixTag(TagSelect)
+ mixTree(qualifier)
+ mixIdent(item)
+ mixBoolean(mutable)
+ mixType(tree.tpe)
+
+ case Apply(receiver, method, args) =>
+ mixTag(TagApply)
+ mixTree(receiver)
+ mixIdent(method)
+ mixTrees(args)
+ mixType(tree.tpe)
+
+ case StaticApply(receiver, cls, method, args) =>
+ mixTag(TagStaticApply)
+ mixTree(receiver)
+ mixType(cls)
+ mixIdent(method)
+ mixTrees(args)
+ mixType(tree.tpe)
+
+ case TraitImplApply(impl, method, args) =>
+ mixTag(TagTraitImplApply)
+ mixType(impl)
+ mixIdent(method)
+ mixTrees(args)
+ mixType(tree.tpe)
+
+ case UnaryOp(op, lhs) =>
+ mixTag(TagUnaryOp)
+ mixInt(op)
+ mixTree(lhs)
+
+ case BinaryOp(op, lhs, rhs) =>
+ mixTag(TagBinaryOp)
+ mixInt(op)
+ mixTree(lhs)
+ mixTree(rhs)
+
+ case NewArray(tpe, lengths) =>
+ mixTag(TagNewArray)
+ mixType(tpe)
+ mixTrees(lengths)
+
+ case ArrayValue(tpe, elems) =>
+ mixTag(TagArrayValue)
+ mixType(tpe)
+ mixTrees(elems)
+
+ case ArrayLength(array) =>
+ mixTag(TagArrayLength)
+ mixTree(array)
+
+ case ArraySelect(array, index) =>
+ mixTag(TagArraySelect)
+ mixTree(array)
+ mixTree(index)
+ mixType(tree.tpe)
+
+ case RecordValue(tpe, elems) =>
+ mixTag(TagRecordValue)
+ mixType(tpe)
+ mixTrees(elems)
+
+ case IsInstanceOf(expr, cls) =>
+ mixTag(TagIsInstanceOf)
+ mixTree(expr)
+ mixType(cls)
+
+ case AsInstanceOf(expr, cls) =>
+ mixTag(TagAsInstanceOf)
+ mixTree(expr)
+ mixType(cls)
+
+ case Unbox(expr, charCode) =>
+ mixTag(TagUnbox)
+ mixTree(expr)
+ mixInt(charCode)
+
+ case GetClass(expr) =>
+ mixTag(TagGetClass)
+ mixTree(expr)
+
+ case CallHelper(helper, args) =>
+ mixTag(TagCallHelper)
+ mixString(helper)
+ mixTrees(args)
+ mixType(tree.tpe)
+
+ case JSNew(ctor, args) =>
+ mixTag(TagJSNew)
+ mixTree(ctor)
+ mixTrees(args)
+
+ case JSDotSelect(qualifier, item) =>
+ mixTag(TagJSDotSelect)
+ mixTree(qualifier)
+ mixIdent(item)
+
+ case JSBracketSelect(qualifier, item) =>
+ mixTag(TagJSBracketSelect)
+ mixTree(qualifier)
+ mixTree(item)
+
+ case JSFunctionApply(fun, args) =>
+ mixTag(TagJSFunctionApply)
+ mixTree(fun)
+ mixTrees(args)
+
+ case JSDotMethodApply(receiver, method, args) =>
+ mixTag(TagJSDotMethodApply)
+ mixTree(receiver)
+ mixIdent(method)
+ mixTrees(args)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ mixTag(TagJSBracketMethodApply)
+ mixTree(receiver)
+ mixTree(method)
+ mixTrees(args)
+
+ case JSDelete(prop) =>
+ mixTag(TagJSDelete)
+ mixTree(prop)
+
+ case JSUnaryOp(op, lhs) =>
+ mixTag(TagJSUnaryOp)
+ mixString(op)
+ mixTree(lhs)
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ mixTag(TagJSBinaryOp)
+ mixString(op)
+ mixTree(lhs)
+ mixTree(rhs)
+
+ case JSArrayConstr(items) =>
+ mixTag(TagJSArrayConstr)
+ mixTrees(items)
+
+ case JSObjectConstr(fields) =>
+ mixTag(TagJSObjectConstr)
+ fields foreach { case (pn, value) =>
+ mixPropertyName(pn)
+ mixTree(value)
+ }
+
+ case JSEnvInfo() =>
+ mixTag(TagJSEnvInfo)
+
+ case Undefined() =>
+ mixTag(TagUndefined)
+
+ case UndefinedParam() =>
+ mixTag(TagUndefinedParam)
+ mixType(tree.tpe)
+
+ case Null() =>
+ mixTag(TagNull)
+
+ case BooleanLiteral(value) =>
+ mixTag(TagBooleanLiteral)
+ mixBoolean(value)
+
+ case IntLiteral(value) =>
+ mixTag(TagIntLiteral)
+ mixInt(value)
+
+ case LongLiteral(value) =>
+ mixTag(TagLongLiteral)
+ mixLong(value)
+
+ case FloatLiteral(value) =>
+ mixTag(TagFloatLiteral)
+ mixFloat(value)
+
+ case DoubleLiteral(value) =>
+ mixTag(TagDoubleLiteral)
+ mixDouble(value)
+
+ case StringLiteral(value) =>
+ mixTag(TagStringLiteral)
+ mixString(value)
+
+ case ClassOf(cls) =>
+ mixTag(TagClassOf)
+ mixType(cls)
+
+ case VarRef(ident, mutable) =>
+ mixTag(TagVarRef)
+ mixIdent(ident)
+ mixBoolean(mutable)
+ mixType(tree.tpe)
+
+ case This() =>
+ mixTag(TagThis)
+ mixType(tree.tpe)
+
+ case Closure(captureParams, params, body, captureValues) =>
+ mixTag(TagClosure)
+ mixTrees(captureParams)
+ mixTrees(params)
+ mixTree(body)
+ mixTrees(captureValues)
+
+ case _ =>
+ sys.error(s"Unable to hash tree of class ${tree.getClass}")
+
+ }
+ }
+
+ def mixTrees(trees: List[Tree]): Unit =
+ trees.foreach(mixTree)
+
+ def mixType(tpe: Type): Unit = tpe match {
+ case AnyType => mixTag(TagAnyType)
+ case NothingType => mixTag(TagNothingType)
+ case UndefType => mixTag(TagUndefType)
+ case BooleanType => mixTag(TagBooleanType)
+ case IntType => mixTag(TagIntType)
+ case LongType => mixTag(TagLongType)
+ case FloatType => mixTag(TagFloatType)
+ case DoubleType => mixTag(TagDoubleType)
+ case StringType => mixTag(TagStringType)
+ case NullType => mixTag(TagNullType)
+ case NoType => mixTag(TagNoType)
+
+ case tpe: ClassType =>
+ mixTag(TagClassType)
+ mixString(tpe.className)
+
+ case tpe: ArrayType =>
+ mixTag(TagArrayType)
+ mixString(tpe.baseClassName)
+ mixInt(tpe.dimensions)
+
+ case RecordType(fields) =>
+ mixTag(TagRecordType)
+ for (RecordType.Field(name, originalName, tpe, mutable) <- fields) {
+ mixString(name)
+ originalName.foreach(mixString)
+ mixType(tpe)
+ mixBoolean(mutable)
+ }
+ }
+
+ def mixIdent(ident: Ident): Unit = {
+ mixPos(ident.pos)
+ mixString(ident.name)
+ ident.originalName.foreach(mixString)
+ }
+
+ def mixOptIdent(optIdent: Option[Ident]): Unit = optIdent.foreach(mixIdent)
+
+ def mixPropertyName(name: PropertyName): Unit = name match {
+ case name: Ident => mixIdent(name)
+ case name: StringLiteral => mixTree(name)
+ }
+
+ def mixPos(pos: Position): Unit = {
+ posStream.writeUTF(pos.source.toString)
+ posStream.writeInt(pos.line)
+ posStream.writeInt(pos.column)
+ }
+
+ @inline
+ private final def mixTag(tag: Int): Unit = mixInt(tag)
+
+ @inline
+ private final def mixString(str: String): Unit = treeStream.writeUTF(str)
+
+ @inline
+ private final def mixInt(i: Int): Unit = treeStream.writeInt(i)
+
+ @inline
+ private final def mixLong(l: Long): Unit = treeStream.writeLong(l)
+
+ @inline
+ private final def mixBoolean(b: Boolean): Unit = treeStream.writeBoolean(b)
+
+ @inline
+ private final def mixFloat(f: Float): Unit = treeStream.writeFloat(f)
+
+ @inline
+ private final def mixDouble(d: Double): Unit = treeStream.writeDouble(d)
+
+ }
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala
new file mode 100644
index 0000000..dfb520f
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/InfoSerializers.scala
@@ -0,0 +1,180 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import java.io._
+
+import Infos._
+
+object InfoSerializers {
+
+ /** Scala.js IR File Magic Number
+ *
+ * CA FE : first part of magic number of Java class files
+ * 4A 53 : "JS" in ASCII
+ *
+ */
+ final val IRMagicNumber = 0xCAFE4A53
+
+ def serialize(stream: OutputStream, classInfo: ClassInfo): Unit = {
+ new Serializer().serialize(stream, classInfo)
+ }
+
+ def deserializeRoughInfo(stream: InputStream): RoughClassInfo = {
+ deserializeVersionRoughInfo(stream)._2
+ }
+
+ def deserializeFullInfo(stream: InputStream): ClassInfo = {
+ deserializeVersionFullInfo(stream)._2
+ }
+
+ def deserializeVersionRoughInfo(stream: InputStream): (String, RoughClassInfo) = {
+ new Deserializer(stream).deserializeRough()
+ }
+
+ def deserializeVersionFullInfo(stream: InputStream): (String, ClassInfo) = {
+ new Deserializer(stream).deserializeFull()
+ }
+
+ private final class Serializer {
+ def serialize(stream: OutputStream, classInfo: ClassInfo): Unit = {
+ val s = new DataOutputStream(stream)
+
+ def writeSeq[A](seq: Seq[A])(writeElem: A => Unit): Unit = {
+ s.writeInt(seq.size)
+ seq.foreach(writeElem)
+ }
+
+ def writeStrings(seq: Seq[String]): Unit =
+ writeSeq(seq)(s.writeUTF(_))
+
+ // Write the Scala.js IR magic number
+ s.writeInt(IRMagicNumber)
+
+ // Write the Scala.js Version
+ s.writeUTF(ScalaJSVersions.binaryEmitted)
+
+ import classInfo._
+ s.writeUTF(name)
+ s.writeUTF(encodedName)
+ s.writeBoolean(isExported)
+ s.writeInt(ancestorCount)
+ s.writeByte(ClassKind.toByte(kind))
+ s.writeUTF(superClass)
+ writeStrings(ancestors)
+ s.writeInt(optimizerHints.bits)
+
+ def writeMethodInfo(methodInfo: MethodInfo): Unit = {
+ import methodInfo._
+ s.writeUTF(encodedName)
+ s.writeBoolean(isAbstract)
+ s.writeBoolean(isExported)
+ writeSeq(calledMethods.toSeq) {
+ case (caller, callees) => s.writeUTF(caller); writeStrings(callees)
+ }
+ writeSeq(calledMethodsStatic.toSeq) {
+ case (caller, callees) => s.writeUTF(caller); writeStrings(callees)
+ }
+ writeStrings(instantiatedClasses)
+ writeStrings(accessedModules)
+ writeStrings(accessedClassData)
+ s.writeInt(optimizerHints.bits)
+ }
+
+ writeSeq(methods)(writeMethodInfo(_))
+
+ s.flush()
+ }
+ }
+
+ private final class Deserializer(stream: InputStream) {
+ private[this] val input = new DataInputStream(stream)
+
+ def readList[A](readElem: => A): List[A] =
+ List.fill(input.readInt())(readElem)
+
+ def readStrings(): List[String] =
+ readList(input.readUTF())
+
+ def deserializeRough(): (String, RoughClassInfo) = {
+ val version = readHeader()
+
+ import input._
+ val name = readUTF()
+ val encodedName = readUTF()
+ val isExported = readBoolean()
+ val ancestorCount = readInt()
+ val info = RoughClassInfo(name, encodedName, isExported, ancestorCount)
+
+ (version, info)
+ }
+
+ def deserializeFull(): (String, ClassInfo) = {
+ val version = readHeader()
+
+ import input._
+
+ val name = readUTF()
+ val encodedName = readUTF()
+ val isExported = readBoolean()
+ val ancestorCount = readInt()
+ val kind = ClassKind.fromByte(readByte())
+ val superClass = readUTF()
+ val ancestors = readList(readUTF())
+
+ val optimizerHints =
+ if (version == "0.5.0" || version == "0.5.2") OptimizerHints.empty
+ else new OptimizerHints(readInt())
+
+ def readMethod(): MethodInfo = {
+ val encodedName = readUTF()
+ val isAbstract = readBoolean()
+ val isExported = readBoolean()
+ val calledMethods = readList(readUTF() -> readStrings()).toMap
+ val calledMethodsStatic = readList(readUTF() -> readStrings()).toMap
+ val instantiatedClasses = readStrings()
+ val accessedModules = readStrings()
+ val accessedClassData = readStrings()
+ val optimizerHints = new OptimizerHints(readInt())
+ MethodInfo(encodedName, isAbstract, isExported,
+ calledMethods, calledMethodsStatic,
+ instantiatedClasses, accessedModules, accessedClassData,
+ optimizerHints)
+ }
+
+ val methods = readList(readMethod())
+
+ val info = ClassInfo(name, encodedName, isExported, ancestorCount, kind,
+ superClass, ancestors, optimizerHints, methods)
+
+ (version, info)
+ }
+
+ /** Reads the Scala.js IR header and verifies the version compatibility.
+ * Returns the emitted binary version.
+ */
+ def readHeader(): String = {
+ // Check magic number
+ if (input.readInt() != IRMagicNumber)
+ throw new IOException("Not a Scala.js IR file")
+
+ // Check that we support this version of the IR
+ val version = input.readUTF()
+ val supported = ScalaJSVersions.binarySupported
+ if (!supported.contains(version)) {
+ throw new IOException(
+ s"This version ($version) of Scala.js IR is not supported. " +
+ s"Supported versions are: ${supported.mkString(", ")}")
+ }
+
+ version
+ }
+ }
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Infos.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Infos.scala
new file mode 100644
index 0000000..66feec2
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Infos.scala
@@ -0,0 +1,118 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+object Infos {
+
+ sealed class RoughClassInfo protected (
+ val name: String,
+ val encodedName: String,
+ val isExported: Boolean,
+ val ancestorCount: Int
+ )
+
+ object RoughClassInfo {
+ def apply(name: String, encodedName: String, isExported: Boolean,
+ ancestorCount: Int): RoughClassInfo = {
+ new RoughClassInfo(name, encodedName, isExported, ancestorCount)
+ }
+ }
+
+ final class ClassInfo protected (
+ name: String,
+ encodedName: String,
+ isExported: Boolean,
+ ancestorCount: Int,
+ val kind: ClassKind,
+ val superClass: String,
+ val ancestors: List[String], // includes this class
+ val optimizerHints: OptimizerHints,
+ val methods: List[MethodInfo]
+ ) extends RoughClassInfo(name, encodedName, isExported, ancestorCount)
+
+ object ClassInfo {
+ def apply(
+ name: String,
+ encodedName: String,
+ isExported: Boolean = false,
+ ancestorCount: Int = 0,
+ kind: ClassKind = ClassKind.Class,
+ superClass: String = "",
+ ancestors: List[String] = Nil,
+ optimizerHints: OptimizerHints = OptimizerHints.empty,
+ methods: List[MethodInfo] = Nil): ClassInfo = {
+ new ClassInfo(name, encodedName, isExported, ancestorCount,
+ kind, superClass, ancestors, optimizerHints, methods)
+ }
+ }
+
+ final class MethodInfo private (
+ val encodedName: String,
+ val isAbstract: Boolean,
+ val isExported: Boolean,
+ val calledMethods: Map[String, List[String]],
+ val calledMethodsStatic: Map[String, List[String]],
+ val instantiatedClasses: List[String],
+ val accessedModules: List[String],
+ val accessedClassData: List[String],
+ val optimizerHints: OptimizerHints
+ )
+
+ object MethodInfo {
+ def apply(
+ encodedName: String,
+ isAbstract: Boolean = false,
+ isExported: Boolean = false,
+ calledMethods: Map[String, List[String]] = Map.empty,
+ calledMethodsStatic: Map[String, List[String]] = Map.empty,
+ instantiatedClasses: List[String] = Nil,
+ accessedModules: List[String] = Nil,
+ accessedClassData: List[String] = Nil,
+ optimizerHints: OptimizerHints = OptimizerHints.empty): MethodInfo = {
+ new MethodInfo(encodedName, isAbstract, isExported, calledMethods,
+ calledMethodsStatic, instantiatedClasses, accessedModules,
+ accessedClassData, optimizerHints)
+ }
+ }
+
+ final class OptimizerHints(val bits: Int) extends AnyVal {
+ import OptimizerHints._
+
+ private[scalajs] def isAccessor: Boolean = (bits & AccessorMask) != 0
+ private[scalajs] def hasInlineAnnot: Boolean = (bits & InlineAnnotMask) != 0
+
+ private[scalajs] def copy(
+ isAccessor: Boolean = this.isAccessor,
+ hasInlineAnnot: Boolean = this.hasInlineAnnot
+ ): OptimizerHints = {
+ var bits: Int = 0
+ if (isAccessor)
+ bits |= AccessorMask
+ if (hasInlineAnnot)
+ bits |= InlineAnnotMask
+ new OptimizerHints(bits)
+ }
+
+ override def toString(): String =
+ s"OptimizerHints($bits)"
+ }
+
+ object OptimizerHints {
+ private final val AccessorShift = 0
+ private final val AccessorMask = 1 << AccessorShift
+
+ private final val InlineAnnotShift = 1
+ private final val InlineAnnotMask = 1 << InlineAnnotShift
+
+ final val empty: OptimizerHints =
+ new OptimizerHints(0)
+ }
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Position.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Position.scala
new file mode 100644
index 0000000..3b6d0a2
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Position.scala
@@ -0,0 +1,42 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+final case class Position(
+ /** Source file. */
+ source: Position.SourceFile,
+ /** Zero-based line number. */
+ line: Int,
+ /** Zero-based column number. */
+ column: Int
+) {
+ def show: String = s"$line:$column"
+
+ def isEmpty: Boolean = {
+ source.getScheme == null && source.getRawAuthority == null &&
+ source.getRawPath == "" && source.getRawQuery == null &&
+ source.getRawFragment == null
+ }
+
+ def isDefined: Boolean = !isEmpty
+
+ def orElse(that: => Position): Position = if (isDefined) this else that
+}
+
+object Position {
+ type SourceFile = java.net.URI
+
+ object SourceFile {
+ def apply(f: java.io.File): SourceFile = f.toURI
+ def apply(f: String): SourceFile = new java.net.URI(f)
+ }
+
+ val NoPosition = Position(SourceFile(""), 0, 0)
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Printers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Printers.scala
new file mode 100644
index 0000000..6208d5f
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Printers.scala
@@ -0,0 +1,709 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import scala.annotation.{switch, tailrec}
+
+import java.io.Writer
+
+import Position._
+import Trees._
+import Types._
+import Infos._
+import Utils.escapeJS
+
+object Printers {
+
+ /** Basically copied from scala.reflect.internal.Printers */
+ trait IndentationManager {
+ protected val out: Writer
+
+ protected var indentMargin = 0
+ protected val indentStep = 2
+ protected var indentString = " " // 40
+
+ protected def indent(): Unit = indentMargin += indentStep
+ protected def undent(): Unit = indentMargin -= indentStep
+
+ protected def println(): Unit = {
+ out.write('\n')
+ while (indentMargin > indentString.length())
+ indentString += indentString
+ if (indentMargin > 0)
+ out.write(indentString, 0, indentMargin)
+ }
+
+ @tailrec
+ protected final def printSeq[A](ls: List[A])(printelem: A => Unit)(
+ printsep: A => Unit): Unit = {
+ ls match {
+ case Nil =>
+ case x :: Nil =>
+ printelem(x)
+ case x :: rest =>
+ printelem(x)
+ printsep(x)
+ printSeq(rest)(printelem)(printsep)
+ }
+ }
+
+ protected def printColumn(ts: List[Any], start: String, sep: String,
+ end: String): Unit = {
+ print(start); indent(); println()
+ printSeq(ts) { x =>
+ print(x)
+ } { _ =>
+ print(sep)
+ println()
+ }
+ undent(); println(); print(end)
+ }
+
+ protected def printRow(ts: List[Any], start: String, sep: String,
+ end: String): Unit = {
+ print(start)
+ printSeq(ts) { x =>
+ print(x)
+ } { _ =>
+ print(sep)
+ }
+ print(end)
+ }
+
+ protected def printRow(ts: List[Any], sep: String): Unit =
+ printRow(ts, "", sep, "")
+
+ protected def print(args: Any*): Unit =
+ args.foreach(printOne)
+
+ protected def printOne(arg: Any): Unit
+ }
+
+ class IRTreePrinter(protected val out: Writer) extends IndentationManager {
+ def printTopLevelTree(tree: Tree) {
+ tree match {
+ case Skip() =>
+ // do not print anything
+ case Block(stats) =>
+ for (stat <- stats)
+ printTopLevelTree(stat)
+ case _ =>
+ printTree(tree)
+ println()
+ }
+ }
+
+ protected def printBlock(tree: Tree): Unit = {
+ val trees = tree match {
+ case Block(trees) => trees
+ case _ => List(tree)
+ }
+ printColumn(trees, "{", ";", "}")
+ }
+
+ protected def printSig(args: List[ParamDef], resultType: Type): Unit = {
+ printRow(args, "(", ", ", ")")
+ if (resultType != NoType)
+ print(": ", resultType, " = ")
+ else
+ print(" ")
+ }
+
+ protected def printArgs(args: List[Tree]): Unit = {
+ printRow(args, "(", ", ", ")")
+ }
+
+ def printTree(tree: Tree): Unit = {
+ tree match {
+ case EmptyTree =>
+ print("<empty>")
+
+ // Definitions
+
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ if (mutable)
+ print("var ")
+ else
+ print("val ")
+ print(ident, ": ", vtpe)
+ if (rhs != EmptyTree)
+ print(" = ", rhs)
+
+ case ParamDef(ident, ptpe, mutable) =>
+ if (mutable)
+ print("var ")
+ print(ident, ": ", ptpe)
+
+ // Control flow constructs
+
+ case Skip() =>
+ print("/*<skip>*/")
+
+ case tree: Block =>
+ printBlock(tree)
+
+ case Labeled(label, tpe, body) =>
+ print(label)
+ if (tpe != NoType)
+ print("[", tpe, "]")
+ print(": ")
+ printBlock(body)
+
+ case Assign(lhs, rhs) =>
+ print(lhs, " = ", rhs)
+
+ case Return(expr, label) =>
+ if (label.isEmpty) print("return ", expr)
+ else print("return(", label.get, ") ", expr)
+
+ case If(cond, BooleanLiteral(true), elsep) =>
+ print(cond, " || ", elsep)
+ case If(cond, thenp, BooleanLiteral(false)) =>
+ print(cond, " && ", thenp)
+
+ case If(cond, thenp, elsep) =>
+ print("if (", cond, ") ")
+ printBlock(thenp)
+ elsep match {
+ case Skip() => ()
+ case If(_, _, _) =>
+ print(" else ")
+ printTree(elsep)
+ case _ =>
+ print(" else ")
+ printBlock(elsep)
+ }
+
+ case While(cond, body, label) =>
+ if (label.isDefined)
+ print(label.get, ": ")
+ print("while (", cond, ") ")
+ printBlock(body)
+
+ case DoWhile(body, cond, label) =>
+ if (label.isDefined)
+ print(label.get, ": ")
+ print("do ")
+ printBlock(body)
+ print(" while (", cond, ")")
+
+ case Try(block, errVar, handler, finalizer) =>
+ print("try ")
+ printBlock(block)
+ if (handler != EmptyTree) {
+ print(" catch (", errVar, ") ")
+ printBlock(handler)
+ }
+ if (finalizer != EmptyTree) {
+ print(" finally ")
+ printBlock(finalizer)
+ }
+
+ case Throw(expr) =>
+ print("throw ", expr)
+
+ case Continue(label) =>
+ if (label.isEmpty) print("continue")
+ else print("continue ", label.get)
+
+ case Match(selector, cases, default) =>
+ print("match (", selector, ") ")
+ print("{"); indent
+ for ((values, body) <- cases) {
+ println()
+ printRow(values, "case ", " | ", ":"); indent; println()
+ printTree(body)
+ print(";")
+ undent
+ }
+ if (default != EmptyTree) {
+ println()
+ print("default:"); indent; println()
+ printTree(default)
+ print(";")
+ undent
+ }
+ undent; println(); print("}")
+
+ case Debugger() =>
+ print("debugger")
+
+ // Scala expressions
+
+ case New(cls, ctor, args) =>
+ print("new ", cls, "().", ctor)
+ printArgs(args)
+
+ case LoadModule(cls) =>
+ print("mod:", cls)
+
+ case StoreModule(cls, value) =>
+ print("mod:", cls, "<-", value)
+
+ case Select(qualifier, item, _) =>
+ print(qualifier, ".", item)
+
+ case Apply(receiver, method, args) =>
+ print(receiver, ".", method)
+ printArgs(args)
+
+ case StaticApply(receiver, cls, method, args) =>
+ print(receiver, ".", cls, "::", method)
+ printArgs(args)
+
+ case TraitImplApply(impl, method, args) =>
+ print(impl, "::", method)
+ printArgs(args)
+
+ case UnaryOp(op, lhs) =>
+ import UnaryOp._
+ print("(", (op: @switch) match {
+ case `typeof` => "typeof"
+ case Boolean_! => "!"
+ case IntToLong | DoubleToLong => "(long)"
+ case DoubleToInt | LongToInt => "(int)"
+ case DoubleToFloat => "(float)"
+ case LongToDouble => "(double)"
+ }, lhs, ")")
+
+ case BinaryOp(BinaryOp.Int_-, IntLiteral(0), rhs) =>
+ print("(-", rhs, ")")
+ case BinaryOp(BinaryOp.Int_^, IntLiteral(-1), rhs) =>
+ print("(~", rhs, ")")
+ case BinaryOp(BinaryOp.Long_-, LongLiteral(0L), rhs) =>
+ print("(-", rhs, ")")
+ case BinaryOp(BinaryOp.Long_^, LongLiteral(-1L), rhs) =>
+ print("(~", rhs, ")")
+ case BinaryOp(BinaryOp.Float_-, FloatLiteral(0.0f), rhs) =>
+ print("(-", rhs, ")")
+ case BinaryOp(BinaryOp.Double_-,
+ IntLiteral(0) | FloatLiteral(0.0f) | DoubleLiteral(0.0), rhs) =>
+ print("(-", rhs, ")")
+
+ case BinaryOp(op, lhs, rhs) =>
+ import BinaryOp._
+ print("(", lhs, " ", (op: @switch) match {
+ case === => "==="
+ case !== => "!=="
+
+ case String_+ => "+[string]"
+
+ case `in` => "in"
+ case `instanceof` => "instanceof"
+
+ case Int_+ => "+[int]"
+ case Int_- => "-[int]"
+ case Int_* => "*[int]"
+ case Int_/ => "/[int]"
+ case Int_% => "%[int]"
+
+ case Int_| => "|"
+ case Int_& => "&"
+ case Int_^ => "^"
+ case Int_<< => "<<"
+ case Int_>>> => ">>>"
+ case Int_>> => ">>"
+
+ case Float_+ => "+[float]"
+ case Float_- => "-[float]"
+ case Float_* => "*[float]"
+ case Float_/ => "/[float]"
+ case Float_% => "%[float]"
+
+ case Double_+ => "+"
+ case Double_- => "-"
+ case Double_* => "*"
+ case Double_/ => "/"
+ case Double_% => "%"
+
+ case Num_== => "=="
+ case Num_!= => "!="
+ case Num_< => "<"
+ case Num_<= => "<="
+ case Num_> => ">"
+ case Num_>= => ">="
+
+ case Long_+ => "+[long]"
+ case Long_- => "-[long]"
+ case Long_* => "*[long]"
+ case Long_/ => "/[long]"
+ case Long_% => "%[long]"
+
+ case Long_| => "|[long]"
+ case Long_& => "&[long]"
+ case Long_^ => "^[long]"
+ case Long_<< => "<<[long]"
+ case Long_>>> => ">>>[long]"
+ case Long_>> => ">>[long]"
+
+ case Long_== => "==[long]"
+ case Long_!= => "!=[long]"
+ case Long_< => "<[long]"
+ case Long_<= => "<=[long]"
+ case Long_> => ">[long]"
+ case Long_>= => ">=[long]"
+
+ case Boolean_== => "==[bool]"
+ case Boolean_!= => "!=[bool]"
+ case Boolean_| => "|[bool]"
+ case Boolean_& => "&[bool]"
+ }, " ", rhs, ")")
+
+ case NewArray(tpe, lengths) =>
+ print("new ", tpe.baseClassName)
+ for (length <- lengths)
+ print("[", length, "]")
+ for (dim <- lengths.size until tpe.dimensions)
+ print("[]")
+
+ case ArrayValue(tpe, elems) =>
+ print(tpe)
+ printArgs(elems)
+
+ case ArrayLength(array) =>
+ print(array, ".length")
+
+ case ArraySelect(array, index) =>
+ print(array, "[", index, "]")
+
+ case RecordValue(tpe, elems) =>
+ print("(")
+ var first = true
+ for ((field, value) <- tpe.fields zip elems) {
+ if (first) first = false
+ else print(", ")
+ print(field.name, " = ", value)
+ }
+ print(")")
+
+ case IsInstanceOf(expr, cls) =>
+ print(expr, ".isInstanceOf[", cls, "]")
+
+ case AsInstanceOf(expr, cls) =>
+ print(expr, ".asInstanceOf[", cls, "]")
+
+ case Unbox(expr, charCode) =>
+ print(expr, ".asInstanceOf[", charCode, "]")
+
+ case GetClass(expr) =>
+ print(expr, ".getClass()")
+
+ case CallHelper(helper, args) =>
+ print(helper)
+ printArgs(args)
+
+ // JavaScript expressions
+
+ case JSNew(ctor, args) =>
+ def containsOnlySelectsFromAtom(tree: Tree): Boolean = tree match {
+ case JSDotSelect(qual, _) => containsOnlySelectsFromAtom(qual)
+ case JSBracketSelect(qual, _) => containsOnlySelectsFromAtom(qual)
+ case VarRef(_, _) => true
+ case This() => true
+ case _ => false // in particular, Apply
+ }
+ if (containsOnlySelectsFromAtom(ctor))
+ print("new ", ctor)
+ else
+ print("new (", ctor, ")")
+ printArgs(args)
+
+ case JSDotSelect(qualifier, item) =>
+ print(qualifier, ".", item)
+
+ case JSBracketSelect(qualifier, item) =>
+ print(qualifier, "[", item, "]")
+
+ case JSFunctionApply(fun, args) =>
+ fun match {
+ case _:JSDotSelect | _:JSBracketSelect | _:Select =>
+ print("protect(", fun, ")")
+ case _ =>
+ print(fun)
+ }
+ printArgs(args)
+
+ case JSDotMethodApply(receiver, method, args) =>
+ print(receiver, ".", method)
+ printArgs(args)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ print(receiver, "[", method, "]")
+ printArgs(args)
+
+ case JSDelete(prop) =>
+ print("delete ", prop)
+
+ case JSUnaryOp("typeof", lhs) =>
+ print("typeof(", lhs, ")")
+
+ case JSUnaryOp(op, lhs) =>
+ print("(", op, lhs, ")")
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ print("(", lhs, " ", op, " ", rhs, ")")
+
+ case JSArrayConstr(items) =>
+ printRow(items, "[", ", ", "]")
+
+ case JSObjectConstr(Nil) =>
+ print("{}")
+
+ case JSObjectConstr(fields) =>
+ print("{"); indent; println()
+ printSeq(fields) {
+ case (name, value) => print(name, ": ", value)
+ } { _ =>
+ print(",")
+ println()
+ }
+ undent; println(); print("}")
+
+ case JSEnvInfo() =>
+ print("<envinfo>")
+
+ // Literals
+
+ case Undefined() =>
+ print("(void 0)")
+
+ case UndefinedParam() =>
+ print("<undefined param>")
+
+ case Null() =>
+ print("null")
+
+ case BooleanLiteral(value) =>
+ print(if (value) "true" else "false")
+
+ case IntLiteral(value) =>
+ if (value >= 0)
+ print(value)
+ else
+ print("(", value, ")")
+
+ case FloatLiteral(value) =>
+ if (value == 0.0f && 1.0f / value < 0.0f)
+ print("(-0)")
+ else if (value >= 0.0f)
+ print(value)
+ else
+ print("(", value, ")")
+
+ case DoubleLiteral(value) =>
+ if (value == 0.0 && 1.0 / value < 0.0)
+ print("(-0)")
+ else if (value >= 0.0)
+ print(value)
+ else
+ print("(", value, ")")
+
+ case StringLiteral(value) =>
+ print("\"", escapeJS(value), "\"")
+
+ case ClassOf(cls) =>
+ print("classOf[", cls, "]")
+
+ // Atomic expressions
+
+ case VarRef(ident, _) =>
+ print(ident)
+
+ case This() =>
+ print("this")
+
+ case Closure(captureParams, params, body, captureValues) =>
+ print("(lambda")
+ printRow(captureValues, "<", ", ", ">")
+ printRow(captureParams ++ params, "(", ", ", ") = ")
+ printBlock(body)
+ print(")")
+
+ // Classes
+
+ case ClassDef(name, kind, parent, ancestors, defs) =>
+ kind match {
+ case ClassKind.Class => print("class ")
+ case ClassKind.ModuleClass => print("module class ")
+ case ClassKind.Interface => print("interface ")
+ case ClassKind.RawJSType => print("jstype ")
+ case ClassKind.HijackedClass => print("hijacked class ")
+ case ClassKind.TraitImpl => print("trait impl ")
+ }
+ print(name)
+ parent.foreach(print(" extends ", _))
+ if (ancestors.nonEmpty)
+ printRow(ancestors, " ancestors ", ", ", "")
+ print(" ")
+ printColumn(defs, "{", "", "}")
+ println()
+
+ case MethodDef(name, args, resultType, body) =>
+ print(name)
+ printSig(args, resultType)
+ printBlock(body)
+
+ case PropertyDef(name, _, _, _) =>
+ // TODO
+ print(s"<property: $name>")
+
+ case ConstructorExportDef(fullName, args, body) =>
+ print("export \"", escapeJS(fullName), "\"")
+ printSig(args, NoType) // NoType as trick not to display a type
+ printBlock(body)
+
+ case ModuleExportDef(fullName) =>
+ print("export \"", escapeJS(fullName), "\"")
+
+ case _ =>
+ print(s"<error, elem of class ${tree.getClass()}>")
+ }
+ }
+
+ def printType(tpe: Type): Unit = tpe match {
+ case AnyType => print("any")
+ case NothingType => print("nothing")
+ case UndefType => print("void")
+ case BooleanType => print("boolean")
+ case IntType => print("int")
+ case LongType => print("long")
+ case FloatType => print("float")
+ case DoubleType => print("number")
+ case StringType => print("string")
+ case NullType => print("null")
+ case ClassType(className) => print(className)
+ case NoType => print("<notype>")
+
+ case ArrayType(base, dims) =>
+ print(base)
+ for (i <- 1 to dims)
+ print("[]")
+
+ case RecordType(fields) =>
+ print("(")
+ var first = false
+ for (RecordType.Field(name, _, tpe, mutable) <- fields) {
+ if (first) first = false
+ else print(", ")
+ if (mutable)
+ print("var ")
+ print(name, ": ", tpe)
+ }
+ print(")")
+ }
+
+ protected def printIdent(ident: Ident): Unit =
+ printString(escapeJS(ident.name))
+
+ protected def printOne(arg: Any): Unit = arg match {
+ case tree: Tree =>
+ printTree(tree)
+ case tpe: Type =>
+ printType(tpe)
+ case ident: Ident =>
+ printIdent(ident)
+ case arg =>
+ printString(if (arg == null) "null" else arg.toString)
+ }
+
+ protected def printString(s: String): Unit = {
+ out.write(s)
+ }
+
+ // Make it public
+ override def println(): Unit = super.println()
+
+ def complete(): Unit = ()
+ }
+
+ class InfoPrinter(protected val out: Writer) extends IndentationManager {
+ def printClassInfo(classInfo: ClassInfo): Unit = {
+ import classInfo._
+ println("name: ", escapeJS(name))
+ println("encodedName: ", escapeJS(encodedName))
+ println("isExported: ", isExported)
+ println("ancestorCount: ", ancestorCount)
+ println("kind: ", kind)
+ println("superClass: ", superClass)
+
+ if (ancestors.nonEmpty) {
+ println("ancestors: ",
+ ancestors.map(escapeJS).mkString("[", ", ", "]"))
+ }
+
+ if (optimizerHints != OptimizerHints.empty)
+ println("optimizerHints: ", optimizerHints)
+
+ print("methods:")
+ indent(); println()
+ methods.foreach(printMethodInfo)
+ undent(); println()
+ }
+
+ def printMethodInfo(methodInfo: MethodInfo): Unit = {
+ import methodInfo._
+ print(escapeJS(encodedName), ":")
+ indent(); println()
+
+ if (isAbstract)
+ println("isAbstract: ", isAbstract)
+ if (isExported)
+ println("isExported: ", isExported)
+ if (calledMethods.nonEmpty) {
+ print("calledMethods:")
+ indent(); println()
+ printSeq(calledMethods.toList) { case (caller, callees) =>
+ print(escapeJS(caller), ": ")
+ print(callees.map(escapeJS).mkString("[", ", ", "]"))
+ } { _ => println() }
+ undent(); println()
+ }
+ if (calledMethodsStatic.nonEmpty) {
+ print("calledMethodsStatic:")
+ indent(); println()
+ printSeq(calledMethodsStatic.toList) { case (caller, callees) =>
+ print(escapeJS(caller), ": ")
+ print(callees.map(escapeJS).mkString("[", ", ", "]"))
+ } { _ => println() }
+ undent(); println()
+ }
+ if (instantiatedClasses.nonEmpty) {
+ println("instantiatedClasses: ",
+ instantiatedClasses.map(escapeJS).mkString("[", ", ", "]"))
+ }
+ if (accessedModules.nonEmpty) {
+ println("accessedModules: ",
+ accessedModules.map(escapeJS).mkString("[", ", ", "]"))
+ }
+ if (accessedClassData.nonEmpty) {
+ println("accessedClassData: ",
+ accessedClassData.map(escapeJS).mkString("[", ", ", "]"))
+ }
+ if (optimizerHints != OptimizerHints.empty)
+ println("optimizerHints: ", optimizerHints)
+
+ undent(); println()
+ }
+
+ private def println(arg1: Any, args: Any*): Unit = {
+ print((arg1 +: args): _*)
+ println()
+ }
+
+ protected def printOne(arg: Any): Unit = arg match {
+ case classInfo: ClassInfo => printClassInfo(classInfo)
+ case methodInfo: MethodInfo => printMethodInfo(methodInfo)
+ case arg => out.write(arg.toString())
+ }
+
+ def complete(): Unit = ()
+ }
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala
new file mode 100644
index 0000000..2690939
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/ScalaJSVersions.scala
@@ -0,0 +1,25 @@
+package scala.scalajs.ir
+
+object ScalaJSVersions {
+
+ /** the Scala.js version of this build */
+ final val current = "0.6.0-SNAPSHOT"
+
+ /** true iff the Scala.js version of this build is a snapshot version. */
+ final val currentIsSnapshot = current endsWith "-SNAPSHOT"
+
+ /** Version of binary IR this Scala.js version emits
+ *
+ * This should be either of:
+ * - a prior release version (i.e. "0.5.0", *not* "0.5.0-SNAPSHOT")
+ * - `current`
+ */
+ final val binaryEmitted = current
+
+ /** Versions whose binary files we can support (used by deserializer) */
+ val binarySupported: Set[String] = Set(binaryEmitted)
+
+ // Just to be extra safe
+ assert(binarySupported contains binaryEmitted)
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Serializers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Serializers.scala
new file mode 100644
index 0000000..04ec5c2
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Serializers.scala
@@ -0,0 +1,790 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import scala.annotation.switch
+
+import java.io._
+import java.net.URI
+
+import scala.collection.mutable
+
+import Position._
+import Trees._
+import Types._
+import Tags._
+
+import Utils.JumpBackByteArrayOutputStream
+
+object Serializers {
+ def serialize(stream: OutputStream, tree: Tree): Unit = {
+ new Serializer().serialize(stream, tree)
+ }
+
+ def deserialize(stream: InputStream, version: String): Tree = {
+ new Deserializer(stream, version).deserialize()
+ }
+
+ // true for easier debugging (not for "production", it adds 8 bytes per node)
+ private final val UseDebugMagic = false
+ private final val DebugMagic = 0x3fa8ef84
+ private final val PosDebugMagic = 0x65f0ec32
+
+ private object PositionFormat {
+ /* Positions are serialized incrementally as diffs wrt the last position.
+ *
+ * Formats are (the first byte is decomposed in bits):
+ *
+ * 1st byte | next bytes | description
+ * -----------------------------------------
+ * ccccccc0 | | Column diff (7-bit signed)
+ * llllll01 | CC | Line diff (6-bit signed), column (8-bit unsigned)
+ * ____0011 | LL LL CC | Line diff (16-bit signed), column (8-bit unsigned)
+ * ____0111 | 12 bytes | File index, line, column (all 32-bit signed)
+ * 11111111 | | NoPosition (is not compared/stored in last position)
+ *
+ * Underscores are irrelevant and must be set to 0.
+ */
+
+ final val Format1Mask = 0x01
+ final val Format1MaskValue = 0x00
+ final val Format1Shift = 1
+
+ final val Format2Mask = 0x03
+ final val Format2MaskValue = 0x01
+ final val Format2Shift = 2
+
+ final val Format3Mask = 0x0f
+ final val Format3MaskValue = 0x03
+
+ final val FormatFullMask = 0x0f
+ final val FormatFullMaskValue = 0x7
+
+ final val FormatNoPositionValue = -1
+ }
+
+ private final class Serializer {
+ private[this] val bufferUnderlying = new JumpBackByteArrayOutputStream
+ private[this] val buffer = new DataOutputStream(bufferUnderlying)
+
+ private[this] val files = mutable.ListBuffer.empty[URI]
+ private[this] val fileIndexMap = mutable.Map.empty[URI, Int]
+ private def fileToIndex(file: URI): Int =
+ fileIndexMap.getOrElseUpdate(file, (files += file).size - 1)
+
+ private[this] val strings = mutable.ListBuffer.empty[String]
+ private[this] val stringIndexMap = mutable.Map.empty[String, Int]
+ private def stringToIndex(str: String): Int =
+ stringIndexMap.getOrElseUpdate(str, (strings += str).size - 1)
+
+ private[this] var lastPosition: Position = Position.NoPosition
+
+ def serialize(stream: OutputStream, tree: Tree): Unit = {
+ // Write tree to buffer and record files and strings
+ writeTree(tree)
+
+ val s = new DataOutputStream(stream)
+
+ // Emit the files
+ s.writeInt(files.size)
+ files.foreach(f => s.writeUTF(f.toString))
+
+ // Emit the strings
+ s.writeInt(strings.size)
+ strings.foreach(s.writeUTF)
+
+ // Paste the buffer
+ bufferUnderlying.writeTo(s)
+
+ s.flush()
+ }
+
+ def writeTree(tree: Tree): Unit = {
+ import buffer._
+ writePosition(tree.pos)
+ tree match {
+ case EmptyTree =>
+ writeByte(TagEmptyTree)
+
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ writeByte(TagVarDef)
+ writeIdent(ident); writeType(vtpe); writeBoolean(mutable); writeTree(rhs)
+
+ case ParamDef(ident, ptpe, mutable) =>
+ writeByte(TagParamDef)
+ writeIdent(ident); writeType(ptpe); writeBoolean(mutable)
+
+ case Skip() =>
+ writeByte(TagSkip)
+
+ case Block(stats) =>
+ writeByte(TagBlock)
+ writeTrees(stats)
+
+ case Labeled(label, tpe, body) =>
+ writeByte(TagLabeled)
+ writeIdent(label); writeType(tpe); writeTree(body)
+
+ case Assign(lhs, rhs) =>
+ writeByte(TagAssign)
+ writeTree(lhs); writeTree(rhs)
+
+ case Return(expr, label) =>
+ writeByte(TagReturn)
+ writeTree(expr); writeOptIdent(label)
+
+ case If(cond, thenp, elsep) =>
+ writeByte(TagIf)
+ writeTree(cond); writeTree(thenp); writeTree(elsep)
+ writeType(tree.tpe)
+
+ case While(cond, body, label) =>
+ writeByte(TagWhile)
+ writeTree(cond); writeTree(body); writeOptIdent(label)
+
+ case DoWhile(body, cond, label) =>
+ writeByte(TagDoWhile)
+ writeTree(body); writeTree(cond); writeOptIdent(label)
+
+ case Try(block, errVar, handler, finalizer) =>
+ writeByte(TagTry)
+ writeTree(block); writeIdent(errVar); writeTree(handler); writeTree(finalizer)
+ writeType(tree.tpe)
+
+ case Throw(expr) =>
+ writeByte(TagThrow)
+ writeTree(expr)
+
+ case Continue(label) =>
+ writeByte(TagContinue)
+ writeOptIdent(label)
+
+ case Match(selector, cases, default) =>
+ writeByte(TagMatch)
+ writeTree(selector)
+ writeInt(cases.size)
+ cases foreach { caze =>
+ writeTrees(caze._1); writeTree(caze._2)
+ }
+ writeTree(default)
+ writeType(tree.tpe)
+
+ case Debugger() =>
+ writeByte(TagDebugger)
+
+ case New(cls, ctor, args) =>
+ writeByte(TagNew)
+ writeClassType(cls); writeIdent(ctor); writeTrees(args)
+
+ case LoadModule(cls) =>
+ writeByte(TagLoadModule)
+ writeClassType(cls)
+
+ case StoreModule(cls, value) =>
+ writeByte(TagStoreModule)
+ writeClassType(cls); writeTree(value)
+
+ case Select(qualifier, item, mutable) =>
+ writeByte(TagSelect)
+ writeTree(qualifier); writeIdent(item); writeBoolean(mutable)
+ writeType(tree.tpe)
+
+ case Apply(receiver, method, args) =>
+ writeByte(TagApply)
+ writeTree(receiver); writeIdent(method); writeTrees(args)
+ writeType(tree.tpe)
+
+ case StaticApply(receiver, cls, method, args) =>
+ writeByte(TagStaticApply)
+ writeTree(receiver); writeClassType(cls); writeIdent(method); writeTrees(args)
+ writeType(tree.tpe)
+
+ case TraitImplApply(impl, method, args) =>
+ writeByte(TagTraitImplApply)
+ writeClassType(impl); writeIdent(method); writeTrees(args)
+ writeType(tree.tpe)
+
+ case UnaryOp(op, lhs) =>
+ writeByte(TagUnaryOp)
+ writeByte(op); writeTree(lhs)
+
+ case BinaryOp(op, lhs, rhs) =>
+ writeByte(TagBinaryOp)
+ writeByte(op); writeTree(lhs); writeTree(rhs)
+
+ case NewArray(tpe, lengths) =>
+ writeByte(TagNewArray)
+ writeArrayType(tpe); writeTrees(lengths)
+
+ case ArrayValue(tpe, elems) =>
+ writeByte(TagArrayValue)
+ writeArrayType(tpe); writeTrees(elems)
+
+ case ArrayLength(array) =>
+ writeByte(TagArrayLength)
+ writeTree(array)
+
+ case ArraySelect(array, index) =>
+ writeByte(TagArraySelect)
+ writeTree(array); writeTree(index)
+ writeType(tree.tpe)
+
+ case RecordValue(tpe, elems) =>
+ writeByte(TagRecordValue)
+ writeType(tpe); writeTrees(elems)
+
+ case IsInstanceOf(expr, cls) =>
+ writeByte(TagIsInstanceOf)
+ writeTree(expr); writeReferenceType(cls)
+
+ case AsInstanceOf(expr, cls) =>
+ writeByte(TagAsInstanceOf)
+ writeTree(expr); writeReferenceType(cls)
+
+ case Unbox(expr, charCode) =>
+ writeByte(TagUnbox)
+ writeTree(expr); writeByte(charCode.toByte)
+
+ case GetClass(expr) =>
+ writeByte(TagGetClass)
+ writeTree(expr)
+
+ case CallHelper(helper, args) =>
+ writeByte(TagCallHelper)
+ writeString(helper); writeTrees(args)
+ writeType(tree.tpe)
+
+ case JSNew(ctor, args) =>
+ writeByte(TagJSNew)
+ writeTree(ctor); writeTrees(args)
+
+ case JSDotSelect(qualifier, item) =>
+ writeByte(TagJSDotSelect)
+ writeTree(qualifier); writeIdent(item)
+
+ case JSBracketSelect(qualifier, item) =>
+ writeByte(TagJSBracketSelect)
+ writeTree(qualifier); writeTree(item)
+
+ case JSFunctionApply(fun, args) =>
+ writeByte(TagJSFunctionApply)
+ writeTree(fun); writeTrees(args)
+
+ case JSDotMethodApply(receiver, method, args) =>
+ writeByte(TagJSDotMethodApply)
+ writeTree(receiver); writeIdent(method); writeTrees(args)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ writeByte(TagJSBracketMethodApply)
+ writeTree(receiver); writeTree(method); writeTrees(args)
+
+ case JSDelete(prop) =>
+ writeByte(TagJSDelete)
+ writeTree(prop)
+
+ case JSUnaryOp(op, lhs) =>
+ writeByte(TagJSUnaryOp)
+ writeString(op); writeTree(lhs)
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ writeByte(TagJSBinaryOp)
+ writeString(op); writeTree(lhs); writeTree(rhs)
+
+ case JSArrayConstr(items) =>
+ writeByte(TagJSArrayConstr)
+ writeTrees(items)
+
+ case JSObjectConstr(fields) =>
+ writeByte(TagJSObjectConstr)
+ writeInt(fields.size)
+ fields foreach { field =>
+ writePropertyName(field._1); writeTree(field._2)
+ }
+
+ case JSEnvInfo() =>
+ writeByte(TagJSEnvInfo)
+
+ // Literals
+
+ case Undefined() =>
+ writeByte(TagUndefined)
+
+ case UndefinedParam() =>
+ writeByte(TagUndefinedParam)
+ writeType(tree.tpe)
+
+ case Null() =>
+ writeByte(TagNull)
+
+ case BooleanLiteral(value) =>
+ writeByte(TagBooleanLiteral)
+ writeBoolean(value)
+
+ case IntLiteral(value) =>
+ writeByte(TagIntLiteral)
+ writeInt(value)
+
+ case LongLiteral(value) =>
+ writeByte(TagLongLiteral)
+ writeLong(value)
+
+ case FloatLiteral(value) =>
+ writeByte(TagFloatLiteral)
+ writeFloat(value)
+
+ case DoubleLiteral(value) =>
+ writeByte(TagDoubleLiteral)
+ writeDouble(value)
+
+ case StringLiteral(value) =>
+ writeByte(TagStringLiteral)
+ writeString(value)
+
+ case ClassOf(cls) =>
+ writeByte(TagClassOf)
+ writeReferenceType(cls)
+
+ case VarRef(ident, mutable) =>
+ writeByte(TagVarRef)
+ writeIdent(ident); writeBoolean(mutable)
+ writeType(tree.tpe)
+
+ case This() =>
+ writeByte(TagThis)
+ writeType(tree.tpe)
+
+ case Closure(captureParams, params, body, captureValues) =>
+ writeByte(TagClosure)
+ writeTrees(captureParams)
+ writeTrees(params)
+ writeTree(body)
+ writeTrees(captureValues)
+
+ case ClassDef(name, kind, parent, ancestors, defs) =>
+ writeByte(TagClassDef)
+ writeIdent(name)
+ writeByte(ClassKind.toByte(kind))
+ writeOptIdent(parent)
+ writeIdents(ancestors)
+ writeTrees(defs)
+
+ case methodDef: MethodDef =>
+ val MethodDef(name, args, resultType, body) = methodDef
+
+ writeByte(TagMethodDef)
+ writeOptHash(methodDef.hash)
+
+ // Prepare for back-jump and write dummy length
+ bufferUnderlying.markJump()
+ writeInt(-1)
+
+ // Write out method def
+ writePropertyName(name); writeTrees(args); writeType(resultType); writeTree(body)
+
+ // Jump back and write true length
+ val length = bufferUnderlying.jumpBack()
+ writeInt(length)
+ bufferUnderlying.continue()
+
+ case PropertyDef(name, getter, arg, setter) =>
+ writeByte(TagPropertyDef)
+ writePropertyName(name); writeTree(getter); writeTree(arg); writeTree(setter)
+
+ case ConstructorExportDef(fullName, args, body) =>
+ writeByte(TagConstructorExportDef)
+ writeString(fullName); writeTrees(args); writeTree(body)
+
+ case ModuleExportDef(fullName) =>
+ writeByte(TagModuleExportDef)
+ writeString(fullName)
+ }
+ if (UseDebugMagic)
+ writeInt(DebugMagic)
+ }
+
+ def writeTrees(trees: List[Tree]): Unit = {
+ buffer.writeInt(trees.size)
+ trees.foreach(writeTree)
+ }
+
+ def writeIdent(ident: Ident): Unit = {
+ writePosition(ident.pos)
+ writeString(ident.name); writeString(ident.originalName.getOrElse(""))
+ }
+
+ def writeIdents(idents: List[Ident]): Unit = {
+ buffer.writeInt(idents.size)
+ idents.foreach(writeIdent)
+ }
+
+ def writeOptIdent(optIdent: Option[Ident]): Unit = {
+ buffer.writeBoolean(optIdent.isDefined)
+ optIdent.foreach(writeIdent)
+ }
+
+ def writeType(tpe: Type): Unit = {
+ tpe match {
+ case AnyType => buffer.write(TagAnyType)
+ case NothingType => buffer.write(TagNothingType)
+ case UndefType => buffer.write(TagUndefType)
+ case BooleanType => buffer.write(TagBooleanType)
+ case IntType => buffer.write(TagIntType)
+ case LongType => buffer.write(TagLongType)
+ case FloatType => buffer.write(TagFloatType)
+ case DoubleType => buffer.write(TagDoubleType)
+ case StringType => buffer.write(TagStringType)
+ case NullType => buffer.write(TagNullType)
+ case NoType => buffer.write(TagNoType)
+
+ case tpe: ClassType =>
+ buffer.write(TagClassType)
+ writeClassType(tpe)
+
+ case tpe: ArrayType =>
+ buffer.write(TagArrayType)
+ writeArrayType(tpe)
+
+ case RecordType(fields) =>
+ buffer.write(TagRecordType)
+ buffer.writeInt(fields.size)
+ for (RecordType.Field(name, originalName, tpe, mutable) <- fields) {
+ writeString(name)
+ writeString(originalName.getOrElse(""))
+ writeType(tpe)
+ buffer.writeBoolean(mutable)
+ }
+ }
+ }
+
+ def writeClassType(tpe: ClassType): Unit =
+ writeString(tpe.className)
+
+ def writeArrayType(tpe: ArrayType): Unit = {
+ writeString(tpe.baseClassName)
+ buffer.writeInt(tpe.dimensions)
+ }
+
+ def writeReferenceType(tpe: ReferenceType): Unit =
+ writeType(tpe)
+
+ def writePropertyName(name: PropertyName): Unit = {
+ name match {
+ case name: Ident => buffer.writeBoolean(true); writeIdent(name)
+ case name: StringLiteral => buffer.writeBoolean(false); writeTree(name)
+ }
+ }
+
+ def writePosition(pos: Position): Unit = {
+ import buffer._
+ import PositionFormat._
+
+ def writeFull(): Unit = {
+ writeByte(FormatFullMaskValue)
+ writeInt(fileToIndex(pos.source))
+ writeInt(pos.line)
+ writeInt(pos.column)
+ }
+
+ if (pos == Position.NoPosition) {
+ writeByte(FormatNoPositionValue)
+ } else if (lastPosition == Position.NoPosition ||
+ pos.source != lastPosition.source) {
+ writeFull()
+ lastPosition = pos
+ } else {
+ val line = pos.line
+ val column = pos.column
+ val lineDiff = line - lastPosition.line
+ val columnDiff = column - lastPosition.column
+ val columnIsByte = column >= 0 && column < 256
+
+ if (lineDiff == 0 && columnDiff >= -64 && columnDiff < 64) {
+ writeByte((columnDiff << Format1Shift) | Format1MaskValue)
+ } else if (lineDiff >= -32 && lineDiff < 32 && columnIsByte) {
+ writeByte((lineDiff << Format2Shift) | Format2MaskValue)
+ writeByte(column)
+ } else if (lineDiff >= Short.MinValue && lineDiff <= Short.MaxValue && columnIsByte) {
+ writeByte(Format3MaskValue)
+ writeShort(lineDiff)
+ writeByte(column)
+ } else {
+ writeFull()
+ }
+
+ lastPosition = pos
+ }
+
+ if (UseDebugMagic)
+ writeInt(PosDebugMagic)
+ }
+
+ def writeOptHash(optHash: Option[TreeHash]): Unit = {
+ buffer.writeBoolean(optHash.isDefined)
+ for (hash <- optHash) {
+ buffer.write(hash.treeHash)
+ buffer.write(hash.posHash)
+ }
+ }
+
+ def writeString(s: String): Unit =
+ buffer.writeInt(stringToIndex(s))
+ }
+
+ private final class Deserializer(stream: InputStream, sourceVersion: String) {
+ private[this] val input = new DataInputStream(stream)
+
+ private[this] val files =
+ Array.fill(input.readInt())(new URI(input.readUTF()))
+
+ private[this] val strings =
+ Array.fill(input.readInt())(input.readUTF())
+
+ private[this] var lastPosition: Position = Position.NoPosition
+
+ def deserialize(): Tree = {
+ readTree()
+ }
+
+ def readTree(): Tree = {
+ import input._
+ implicit val pos = readPosition()
+ val tag = readByte()
+ val result = (tag: @switch) match {
+ case TagEmptyTree => EmptyTree
+
+ case TagVarDef => VarDef(readIdent(), readType(), readBoolean(), readTree())
+ case TagParamDef => ParamDef(readIdent(), readType(), readBoolean())
+
+ case TagSkip => Skip()
+ case TagBlock => Block(readTrees())
+ case TagLabeled => Labeled(readIdent(), readType(), readTree())
+ case TagAssign => Assign(readTree(), readTree())
+ case TagReturn => Return(readTree(), readOptIdent())
+ case TagIf => If(readTree(), readTree(), readTree())(readType())
+ case TagWhile => While(readTree(), readTree(), readOptIdent())
+ case TagDoWhile => DoWhile(readTree(), readTree(), readOptIdent())
+ case TagTry => Try(readTree(), readIdent(), readTree(), readTree())(readType())
+ case TagThrow => Throw(readTree())
+ case TagContinue => Continue(readOptIdent())
+ case TagMatch =>
+ Match(readTree(), List.fill(readInt()) {
+ (readTrees().map(_.asInstanceOf[Literal]), readTree())
+ }, readTree())(readType())
+ case TagDebugger => Debugger()
+
+ case TagNew => New(readClassType(), readIdent(), readTrees())
+ case TagLoadModule => LoadModule(readClassType())
+ case TagStoreModule => StoreModule(readClassType(), readTree())
+ case TagSelect => Select(readTree(), readIdent(), readBoolean())(readType())
+ case TagApply => Apply(readTree(), readIdent(), readTrees())(readType())
+ case TagStaticApply => StaticApply(readTree(), readClassType(), readIdent(), readTrees())(readType())
+ case TagTraitImplApply => TraitImplApply(readClassType(), readIdent(), readTrees())(readType())
+ case TagUnaryOp => UnaryOp(readByte(), readTree())
+ case TagBinaryOp => BinaryOp(readByte(), readTree(), readTree())
+ case TagNewArray => NewArray(readArrayType(), readTrees())
+ case TagArrayValue => ArrayValue(readArrayType(), readTrees())
+ case TagArrayLength => ArrayLength(readTree())
+ case TagArraySelect => ArraySelect(readTree(), readTree())(readType())
+ case TagRecordValue => RecordValue(readType().asInstanceOf[RecordType], readTrees())
+ case TagIsInstanceOf => IsInstanceOf(readTree(), readReferenceType())
+ case TagAsInstanceOf => AsInstanceOf(readTree(), readReferenceType())
+ case TagUnbox => Unbox(readTree(), readByte().toChar)
+ case TagGetClass => GetClass(readTree())
+ case TagCallHelper => CallHelper(readString(), readTrees())(readType())
+
+ case TagJSNew => JSNew(readTree(), readTrees())
+ case TagJSDotSelect => JSDotSelect(readTree(), readIdent())
+ case TagJSBracketSelect => JSBracketSelect(readTree(), readTree())
+ case TagJSFunctionApply => JSFunctionApply(readTree(), readTrees())
+ case TagJSDotMethodApply => JSDotMethodApply(readTree(), readIdent(), readTrees())
+ case TagJSBracketMethodApply => JSBracketMethodApply(readTree(), readTree(), readTrees())
+ case TagJSDelete => JSDelete(readTree())
+ case TagJSUnaryOp => JSUnaryOp(readString(), readTree())
+ case TagJSBinaryOp => JSBinaryOp(readString(), readTree(), readTree())
+ case TagJSArrayConstr => JSArrayConstr(readTrees())
+ case TagJSObjectConstr =>
+ JSObjectConstr(List.fill(readInt())((readPropertyName(), readTree())))
+ case TagJSEnvInfo => JSEnvInfo()
+
+ case TagUndefined => Undefined()
+ case TagUndefinedParam => UndefinedParam()(readType())
+ case TagNull => Null()
+ case TagBooleanLiteral => BooleanLiteral(readBoolean())
+ case TagIntLiteral => IntLiteral(readInt())
+ case TagLongLiteral => LongLiteral(readLong())
+ case TagFloatLiteral => FloatLiteral(readFloat())
+ case TagDoubleLiteral => DoubleLiteral(readDouble())
+ case TagStringLiteral => StringLiteral(readString())
+ case TagClassOf => ClassOf(readReferenceType())
+
+ case TagVarRef => VarRef(readIdent(), readBoolean())(readType())
+ case TagThis => This()(readType())
+ case TagClosure =>
+ Closure(readParamDefs(), readParamDefs(), readTree(), readTrees())
+
+ case TagClassDef =>
+ val name = readIdent()
+ val kind = ClassKind.fromByte(readByte())
+ val parent = readOptIdent()
+ val ancestors = readIdents()
+ val defs = readTrees()
+ ClassDef(name, kind, parent, ancestors, defs)
+
+ case TagMethodDef =>
+ val optHash = readOptHash()
+ // read and discard the length
+ val len = readInt()
+ assert(len >= 0)
+ MethodDef(readPropertyName(), readParamDefs(), readType(), readTree())(optHash)
+ case TagPropertyDef =>
+ PropertyDef(readPropertyName(), readTree(),
+ readTree().asInstanceOf[ParamDef], readTree())
+ case TagConstructorExportDef =>
+ ConstructorExportDef(readString(), readParamDefs(), readTree())
+ case TagModuleExportDef =>
+ ModuleExportDef(readString())
+ }
+ if (UseDebugMagic) {
+ val magic = readInt()
+ assert(magic == DebugMagic,
+ s"Bad magic after reading a ${result.getClass}!")
+ }
+ result
+ }
+
+ def readTrees(): List[Tree] =
+ List.fill(input.readInt())(readTree())
+
+ def readParamDefs(): List[ParamDef] =
+ readTrees().map(_.asInstanceOf[ParamDef])
+
+ def readIdent(): Ident = {
+ implicit val pos = readPosition()
+ val name = readString()
+ val originalName = readString()
+ Ident(name, if (originalName.isEmpty) None else Some(originalName))
+ }
+
+ def readIdents(): List[Ident] =
+ List.fill(input.readInt())(readIdent())
+
+ def readOptIdent(): Option[Ident] = {
+ if (input.readBoolean()) Some(readIdent())
+ else None
+ }
+
+ def readType(): Type = {
+ val tag = input.readByte()
+ (tag: @switch) match {
+ case TagAnyType => AnyType
+ case TagNothingType => NothingType
+ case TagUndefType => UndefType
+ case TagBooleanType => BooleanType
+ case TagIntType => IntType
+ case TagLongType => LongType
+ case TagFloatType => FloatType
+ case TagDoubleType => DoubleType
+ case TagStringType => StringType
+ case TagNullType => NullType
+ case TagNoType => NoType
+
+ case TagClassType => readClassType()
+ case TagArrayType => readArrayType()
+
+ case TagRecordType =>
+ RecordType(List.fill(input.readInt()) {
+ val name = readString()
+ val originalName = readString()
+ val tpe = readType()
+ val mutable = input.readBoolean()
+ RecordType.Field(name,
+ if (originalName.isEmpty) None else Some(originalName),
+ tpe, mutable)
+ })
+ }
+ }
+
+ def readClassType(): ClassType =
+ ClassType(readString())
+
+ def readArrayType(): ArrayType =
+ ArrayType(readString(), input.readInt())
+
+ def readReferenceType(): ReferenceType =
+ readType().asInstanceOf[ReferenceType]
+
+ def readPropertyName(): PropertyName = {
+ if (input.readBoolean()) readIdent()
+ else readTree().asInstanceOf[StringLiteral]
+ }
+
+ def readPosition(): Position = {
+ import input._
+ import PositionFormat._
+
+ val first = readByte()
+
+ val result = if (first == FormatNoPositionValue) {
+ Position.NoPosition
+ } else {
+ val result = if ((first & FormatFullMask) == FormatFullMaskValue) {
+ val file = files(readInt())
+ val line = readInt()
+ val column = readInt()
+ Position(file, line, column)
+ } else {
+ assert(lastPosition != NoPosition,
+ "Position format error: first position must be full")
+ if ((first & Format1Mask) == Format1MaskValue) {
+ val columnDiff = first >> Format1Shift
+ Position(lastPosition.source, lastPosition.line,
+ lastPosition.column + columnDiff)
+ } else if ((first & Format2Mask) == Format2MaskValue) {
+ val lineDiff = first >> Format2Shift
+ val column = readByte() & 0xff // unsigned
+ Position(lastPosition.source,
+ lastPosition.line + lineDiff, column)
+ } else {
+ assert((first & Format3Mask) == Format3MaskValue,
+ s"Position format error: first byte $first does not match any format")
+ val lineDiff = readShort()
+ val column = readByte() & 0xff // unsigned
+ Position(lastPosition.source,
+ lastPosition.line + lineDiff, column)
+ }
+ }
+ lastPosition = result
+ result
+ }
+
+ if (UseDebugMagic) {
+ val magic = readInt()
+ assert(magic == PosDebugMagic,
+ s"Bad magic after reading position with first byte $first")
+ }
+
+ result
+ }
+
+ def readOptHash(): Option[TreeHash] = {
+ if (input.readBoolean()) {
+ val treeHash = new Array[Byte](20)
+ val posHash = new Array[Byte](20)
+ input.readFully(treeHash)
+ input.readFully(posHash)
+ Some(new TreeHash(treeHash, posHash))
+ } else None
+ }
+
+ def readString(): String = {
+ strings(input.readInt())
+ }
+ }
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Tags.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Tags.scala
new file mode 100644
index 0000000..a03926c
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Tags.scala
@@ -0,0 +1,107 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+/** Serialization and hashing tags for trees and types */
+private[ir] object Tags {
+
+ // Tags for Trees
+
+ final val TagEmptyTree = 1
+
+ final val TagVarDef = TagEmptyTree + 1
+ final val TagParamDef = TagVarDef + 1
+
+ final val TagSkip = TagParamDef + 1
+ final val TagBlock = TagSkip + 1
+ final val TagLabeled = TagBlock + 1
+ final val TagAssign = TagLabeled + 1
+ final val TagReturn = TagAssign + 1
+ final val TagIf = TagReturn + 1
+ final val TagWhile = TagIf + 1
+ final val TagDoWhile = TagWhile + 1
+ final val TagTry = TagDoWhile + 1
+ final val TagThrow = TagTry + 1
+ final val TagContinue = TagThrow + 1
+ final val TagMatch = TagContinue + 1
+ final val TagDebugger = TagMatch + 1
+
+ final val TagNew = TagDebugger + 1
+ final val TagLoadModule = TagNew + 1
+ final val TagStoreModule = TagLoadModule + 1
+ final val TagSelect = TagStoreModule + 1
+ final val TagApply = TagSelect + 1
+ final val TagStaticApply = TagApply + 1
+ final val TagTraitImplApply = TagStaticApply + 1
+ final val TagUnaryOp = TagTraitImplApply + 1
+ final val TagBinaryOp = TagUnaryOp + 1
+ final val TagNewArray = TagBinaryOp + 1
+ final val TagArrayValue = TagNewArray + 1
+ final val TagArrayLength = TagArrayValue + 1
+ final val TagArraySelect = TagArrayLength + 1
+ final val TagRecordValue = TagArraySelect + 1
+ final val TagIsInstanceOf = TagRecordValue + 1
+ final val TagAsInstanceOf = TagIsInstanceOf + 1
+ final val TagUnbox = TagAsInstanceOf + 1
+ final val TagGetClass = TagUnbox + 1
+ final val TagCallHelper = TagGetClass + 1
+
+ final val TagJSNew = TagCallHelper + 1
+ final val TagJSDotSelect = TagJSNew + 1
+ final val TagJSBracketSelect = TagJSDotSelect + 1
+ final val TagJSFunctionApply = TagJSBracketSelect + 1
+ final val TagJSDotMethodApply = TagJSFunctionApply + 1
+ final val TagJSBracketMethodApply = TagJSDotMethodApply + 1
+ final val TagJSDelete = TagJSBracketMethodApply + 1
+ final val TagJSUnaryOp = TagJSDelete + 1
+ final val TagJSBinaryOp = TagJSUnaryOp + 1
+ final val TagJSArrayConstr = TagJSBinaryOp + 1
+ final val TagJSObjectConstr = TagJSArrayConstr + 1
+ final val TagJSEnvInfo = TagJSObjectConstr + 1
+
+ final val TagUndefined = TagJSEnvInfo + 1
+ final val TagUndefinedParam = TagUndefined + 1
+ final val TagNull = TagUndefinedParam + 1
+ final val TagBooleanLiteral = TagNull + 1
+ final val TagIntLiteral = TagBooleanLiteral + 1
+ final val TagLongLiteral = TagIntLiteral + 1
+ final val TagFloatLiteral = TagLongLiteral + 1
+ final val TagDoubleLiteral = TagFloatLiteral + 1
+ final val TagStringLiteral = TagDoubleLiteral + 1
+ final val TagClassOf = TagStringLiteral + 1
+
+ final val TagVarRef = TagClassOf + 1
+ final val TagThis = TagVarRef + 1
+ final val TagClosure = TagThis + 1
+
+ final val TagClassDef = TagClosure + 1
+ final val TagMethodDef = TagClassDef + 1
+ final val TagPropertyDef = TagMethodDef + 1
+ final val TagConstructorExportDef = TagPropertyDef + 1
+ final val TagModuleExportDef = TagConstructorExportDef + 1
+
+ // Tags for Types
+
+ final val TagAnyType = 1
+ final val TagNothingType = TagAnyType + 1
+ final val TagUndefType = TagNothingType + 1
+ final val TagBooleanType = TagUndefType + 1
+ final val TagIntType = TagBooleanType + 1
+ final val TagLongType = TagIntType + 1
+ final val TagFloatType = TagLongType + 1
+ final val TagDoubleType = TagFloatType + 1
+ final val TagStringType = TagDoubleType + 1
+ final val TagNullType = TagStringType + 1
+ final val TagClassType = TagNullType + 1
+ final val TagArrayType = TagClassType + 1
+ final val TagRecordType = TagArrayType + 1
+ final val TagNoType = TagRecordType + 1
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Transformers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Transformers.scala
new file mode 100644
index 0000000..5e4f40c
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Transformers.scala
@@ -0,0 +1,218 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import Trees._
+
+object Transformers {
+
+ abstract class Transformer {
+ final def transformStat(tree: Tree): Tree =
+ transform(tree, isStat = true)
+
+ final def transformExpr(tree: Tree): Tree =
+ transform(tree, isStat = false)
+
+ def transform(tree: Tree, isStat: Boolean): Tree = {
+ implicit val pos = tree.pos
+
+ tree match {
+ // Definitions
+
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ VarDef(ident, vtpe, mutable, transformExpr(rhs))
+
+ // Control flow constructs
+
+ case Block(stats :+ expr) =>
+ Block(stats.map(transformStat) :+ transform(expr, isStat))
+
+ case Labeled(label, tpe, body) =>
+ Labeled(label, tpe, transform(body, isStat))
+
+ case Assign(lhs, rhs) =>
+ Assign(transformExpr(lhs), transformExpr(rhs))
+
+ case Return(expr, label) =>
+ Return(transformExpr(expr), label)
+
+ case If(cond, thenp, elsep) =>
+ If(transformExpr(cond), transform(thenp, isStat),
+ transform(elsep, isStat))(tree.tpe)
+
+ case While(cond, body, label) =>
+ While(transformExpr(cond), transformStat(body), label)
+
+ case DoWhile(body, cond, label) =>
+ DoWhile(transformStat(body), transformExpr(cond), label)
+
+ case Try(block, errVar, handler, finalizer) =>
+ Try(transform(block, isStat), errVar, transform(handler, isStat),
+ transformStat(finalizer))(tree.tpe)
+
+ case Throw(expr) =>
+ Throw(transformExpr(expr))
+
+ case Match(selector, cases, default) =>
+ Match(transformExpr(selector),
+ cases map (c => (c._1, transform(c._2, isStat))),
+ transform(default, isStat))(tree.tpe)
+
+ // Scala expressions
+
+ case New(cls, ctor, args) =>
+ New(cls, ctor, args map transformExpr)
+
+ case StoreModule(cls, value) =>
+ StoreModule(cls, transformExpr(value))
+
+ case Select(qualifier, item, mutable) =>
+ Select(transformExpr(qualifier), item, mutable)(tree.tpe)
+
+ case Apply(receiver, method, args) =>
+ Apply(transformExpr(receiver), method,
+ args map transformExpr)(tree.tpe)
+
+ case StaticApply(receiver, cls, method, args) =>
+ StaticApply(transformExpr(receiver), cls, method,
+ args map transformExpr)(tree.tpe)
+
+ case TraitImplApply(impl, method, args) =>
+ TraitImplApply(impl, method, args map transformExpr)(tree.tpe)
+
+ case UnaryOp(op, lhs) =>
+ UnaryOp(op, transformExpr(lhs))
+
+ case BinaryOp(op, lhs, rhs) =>
+ BinaryOp(op, transformExpr(lhs), transformExpr(rhs))
+
+ case NewArray(tpe, lengths) =>
+ NewArray(tpe, lengths map transformExpr)
+
+ case ArrayValue(tpe, elems) =>
+ ArrayValue(tpe, elems map transformExpr)
+
+ case ArrayLength(array) =>
+ ArrayLength(transformExpr(array))
+
+ case ArraySelect(array, index) =>
+ ArraySelect(transformExpr(array), transformExpr(index))(tree.tpe)
+
+ case RecordValue(tpe, elems) =>
+ RecordValue(tpe, elems map transformExpr)
+
+ case IsInstanceOf(expr, cls) =>
+ IsInstanceOf(transformExpr(expr), cls)
+
+ case AsInstanceOf(expr, cls) =>
+ AsInstanceOf(transformExpr(expr), cls)
+
+ case Unbox(expr, charCode) =>
+ Unbox(transformExpr(expr), charCode)
+
+ case GetClass(expr) =>
+ GetClass(transformExpr(expr))
+
+ case CallHelper(helper, args) =>
+ CallHelper(helper, args map transformExpr)(tree.tpe)
+
+ // JavaScript expressions
+
+ case JSNew(ctor, args) =>
+ JSNew(transformExpr(ctor), args map transformExpr)
+
+ case JSDotSelect(qualifier, item) =>
+ JSDotSelect(transformExpr(qualifier), item)
+
+ case JSBracketSelect(qualifier, item) =>
+ JSBracketSelect(transformExpr(qualifier), transformExpr(item))
+
+ case JSFunctionApply(fun, args) =>
+ JSFunctionApply(transformExpr(fun), args map transformExpr)
+
+ case JSDotMethodApply(receiver, method, args) =>
+ JSDotMethodApply(transformExpr(receiver), method,
+ args map transformExpr)
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ JSBracketMethodApply(transformExpr(receiver), transformExpr(method),
+ args map transformExpr)
+
+ case JSDelete(prop) =>
+ JSDelete(transformExpr(prop))
+
+ case JSUnaryOp(op, lhs) =>
+ JSUnaryOp(op, transformExpr(lhs))
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ JSBinaryOp(op, transformExpr(lhs), transformExpr(rhs))
+
+ case JSArrayConstr(items) =>
+ JSArrayConstr(items map transformExpr)
+
+ case JSObjectConstr(fields) =>
+ JSObjectConstr(fields map {
+ case (name, value) => (name, transformExpr(value))
+ })
+
+ // Atomic expressions
+
+ case Closure(captureParams, params, body, captureValues) =>
+ Closure(captureParams, params, transformExpr(body),
+ captureValues.map(transformExpr))
+
+ // Trees that need not be transformed
+
+ case _:Skip | _:Continue | _:LoadModule | _:JSEnvInfo |
+ _:Literal | _:VarRef | _:This | EmptyTree =>
+ tree
+
+ case _ =>
+ sys.error(s"Invalid tree in transform() of class ${tree.getClass}")
+ }
+ }
+ }
+
+ abstract class ClassTransformer extends Transformer {
+ def transformClassDef(tree: ClassDef): ClassDef = {
+ val ClassDef(name, kind, parent, ancestors, defs) = tree
+ ClassDef(name, kind, parent, ancestors, defs.map(transformDef))(tree.pos)
+ }
+
+ def transformDef(tree: Tree): Tree = {
+ implicit val pos = tree.pos
+
+ tree match {
+ case VarDef(name, vtpe, mutable, rhs) =>
+ VarDef(name, vtpe, mutable, transformExpr(rhs))
+
+ case MethodDef(name, args, resultType, body) =>
+ MethodDef(name, args, resultType, transformStat(body))(None)
+
+ case PropertyDef(name, getterBody, setterArg, setterBody) =>
+ PropertyDef(
+ name,
+ transformStat(getterBody),
+ setterArg,
+ transformStat(setterBody))
+
+ case ConstructorExportDef(fullName, args, body) =>
+ ConstructorExportDef(fullName, args, transformStat(body))
+
+ case ModuleExportDef(_) =>
+ tree
+
+ case _ =>
+ sys.error(s"Invalid tree in transformDef() of class ${tree.getClass}")
+ }
+ }
+ }
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Traversers.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Traversers.scala
new file mode 100644
index 0000000..1b77e5e
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Traversers.scala
@@ -0,0 +1,197 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import Trees._
+
+object Traversers {
+
+ class Traverser {
+ def traverse(tree: Tree): Unit = tree match {
+ // Definitions
+
+ case VarDef(ident, vtpe, mutable, rhs) =>
+ traverse(rhs)
+
+ // Control flow constructs
+
+ case Block(stats) =>
+ stats foreach traverse
+
+ case Labeled(label, tpe, body) =>
+ traverse(body)
+
+ case Assign(lhs, rhs) =>
+ traverse(lhs)
+ traverse(rhs)
+
+ case Return(expr, label) =>
+ traverse(expr)
+
+ case If(cond, thenp, elsep) =>
+ traverse(cond)
+ traverse(thenp)
+ traverse(elsep)
+
+ case While(cond, body, label) =>
+ traverse(cond)
+ traverse(body)
+
+ case DoWhile(body, cond, label) =>
+ traverse(body)
+ traverse(cond)
+
+ case Try(block, errVar, handler, finalizer) =>
+ traverse(block)
+ traverse(handler)
+ traverse(finalizer)
+
+ case Throw(expr) =>
+ traverse(expr)
+
+ case Match(selector, cases, default) =>
+ traverse(selector)
+ cases foreach (c => (c._1 map traverse, traverse(c._2)))
+ traverse(default)
+
+ // Scala expressions
+
+ case New(cls, ctor, args) =>
+ args foreach traverse
+
+ case StoreModule(cls, value) =>
+ traverse(value)
+
+ case Select(qualifier, item, mutable) =>
+ traverse(qualifier)
+
+ case Apply(receiver, method, args) =>
+ traverse(receiver)
+ args foreach traverse
+
+ case StaticApply(receiver, cls, method, args) =>
+ traverse(receiver)
+ args foreach traverse
+
+ case TraitImplApply(impl, method, args) =>
+ args foreach traverse
+
+ case UnaryOp(op, lhs) =>
+ traverse(lhs)
+
+ case BinaryOp(op, lhs, rhs) =>
+ traverse(lhs)
+ traverse(rhs)
+
+ case NewArray(tpe, lengths) =>
+ lengths foreach traverse
+
+ case ArrayValue(tpe, elems) =>
+ elems foreach traverse
+
+ case ArrayLength(array) =>
+ traverse(array)
+
+ case ArraySelect(array, index) =>
+ traverse(array)
+ traverse(index)
+
+ case RecordValue(tpe, elems) =>
+ elems foreach traverse
+
+ case IsInstanceOf(expr, cls) =>
+ traverse(expr)
+
+ case AsInstanceOf(expr, cls) =>
+ traverse(expr)
+
+ case Unbox(expr, charCode) =>
+ traverse(expr)
+
+ case GetClass(expr) =>
+ traverse(expr)
+
+ case CallHelper(helper, args) =>
+ args foreach traverse
+
+ // JavaScript expressions
+
+ case JSNew(ctor, args) =>
+ traverse(ctor)
+ args foreach traverse
+
+ case JSDotSelect(qualifier, item) =>
+ traverse(qualifier)
+
+ case JSBracketSelect(qualifier, item) =>
+ traverse(qualifier)
+ traverse(item)
+
+ case JSFunctionApply(fun, args) =>
+ traverse(fun)
+ args foreach traverse
+
+ case JSDotMethodApply(receiver, method, args) =>
+ traverse(receiver)
+ args foreach traverse
+
+ case JSBracketMethodApply(receiver, method, args) =>
+ traverse(receiver)
+ traverse(method)
+ args foreach traverse
+
+ case JSDelete(prop) =>
+ traverse(prop)
+
+ case JSUnaryOp(op, lhs) =>
+ traverse(lhs)
+
+ case JSBinaryOp(op, lhs, rhs) =>
+ traverse(lhs)
+ traverse(rhs)
+
+ case JSArrayConstr(items) =>
+ items foreach traverse
+
+ case JSObjectConstr(fields) =>
+ fields foreach { f => traverse(f._2) }
+
+ // Atomic expressions
+
+ case Closure(captureParams, params, body, captureValues) =>
+ traverse(body)
+ captureValues.foreach(traverse)
+
+ // Classes
+
+ case ClassDef(name, kind, parent, ancestors, defs) =>
+ defs foreach traverse
+
+ case MethodDef(name, args, resultType, body) =>
+ traverse(body)
+
+ case PropertyDef(name, getterBody, setterArg, setterBody) =>
+ traverse(getterBody)
+ traverse(setterBody)
+
+ case ConstructorExportDef(fullName, args, body) =>
+ traverse(body)
+
+ // Trees that need not be traversed
+
+ case _:Skip | _:Continue | _:LoadModule | _:JSEnvInfo |
+ _:Literal | _:VarRef | _:This | _:ModuleExportDef | EmptyTree =>
+
+ case _ =>
+ sys.error(s"Invalid tree in traverse() of class ${tree.getClass}")
+ }
+ }
+
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Trees.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Trees.scala
new file mode 100644
index 0000000..4f58ece
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Trees.scala
@@ -0,0 +1,536 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import scala.annotation.switch
+
+import Position.NoPosition
+import Types._
+
+object Trees {
+ /** AST node of the IR. */
+ abstract sealed class Tree {
+ val pos: Position
+ val tpe: Type
+
+ def show: String = {
+ val writer = new java.io.StringWriter
+ val printer = new Printers.IRTreePrinter(writer)
+ printer.printTree(this)
+ writer.toString()
+ }
+ }
+
+ case object EmptyTree extends Tree {
+ val pos = NoPosition
+ val tpe = NoType
+ }
+
+ // Identifiers and properties
+
+ sealed trait PropertyName {
+ def name: String
+ def pos: Position
+ }
+
+ case class Ident(name: String, originalName: Option[String])(
+ implicit val pos: Position) extends PropertyName {
+ requireValidIdent(name)
+ }
+
+ object Ident {
+ def apply(name: String)(implicit pos: Position): Ident =
+ new Ident(name, Some(name))
+ }
+
+ final def isValidIdentifier(name: String): Boolean = {
+ val c = name.head
+ (c == '$' || c == '_' || c.isUnicodeIdentifierStart) &&
+ name.tail.forall(c => (c == '$') || c.isUnicodeIdentifierPart) &&
+ !isKeyword(name)
+ }
+
+ @inline final def requireValidIdent(name: String) {
+ require(isValidIdentifier(name), s"${name} is not a valid identifier")
+ }
+
+ final val isKeyword: Set[String] = Set(
+ // Value keywords
+ "true", "false", "null", "undefined",
+
+ // Current JavaScript keywords
+ "break", "case", "catch", "continue", "debugger", "default", "delete",
+ "do", "else", "finally", "for", "function", "if", "in", "instanceof",
+ "new", "return", "switch", "this", "throw", "try", "typeof", "var",
+ "void", "while", "with",
+
+ // Future reserved keywords
+ "class", "const", "enum", "export", "extends", "import", "super",
+
+ // Future reserved keywords in Strict mode
+ "implements", "interface", "let", "package", "private", "protected",
+ "public", "static", "yield",
+
+ // Other reserved keywords found on the Web but not in the spec
+ "abstract", "boolean", "byte", "char", "double", "final", "float",
+ "goto", "int", "long", "native", "short", "synchronized", "throws",
+ "transient", "volatile"
+ )
+
+ // Definitions
+
+ case class VarDef(name: Ident, vtpe: Type, mutable: Boolean, rhs: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = NoType // cannot be in expression position
+
+ def ref(implicit pos: Position): VarRef =
+ VarRef(name, mutable = mutable)(vtpe)
+ }
+
+ case class ParamDef(name: Ident, ptpe: Type, mutable: Boolean)(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+
+ def ref(implicit pos: Position): VarRef =
+ VarRef(name, mutable = mutable)(ptpe)
+ }
+
+ // Control flow constructs
+
+ case class Skip()(implicit val pos: Position) extends Tree {
+ val tpe = NoType // cannot be in expression position
+ }
+
+ class Block private (val stats: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = stats.last.tpe
+
+ override def toString(): String =
+ stats.mkString("Block(", ",", ")")
+ }
+
+ object Block {
+ def apply(stats: List[Tree])(implicit pos: Position): Tree = {
+ val flattenedStats = stats flatMap {
+ case Skip() => Nil
+ case Block(subStats) => subStats
+ case other => other :: Nil
+ }
+ flattenedStats match {
+ case Nil => Skip()
+ case only :: Nil => only
+ case _ => new Block(flattenedStats)
+ }
+ }
+
+ def apply(stats: Tree*)(implicit pos: Position): Tree =
+ apply(stats.toList)
+
+ def unapply(block: Block): Some[List[Tree]] = Some(block.stats)
+ }
+
+ case class Labeled(label: Ident, tpe: Type, body: Tree)(implicit val pos: Position) extends Tree
+
+ case class Assign(lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree {
+ require(lhs match {
+ case _:VarRef | _:Select | _:ArraySelect |
+ _:JSDotSelect | _:JSBracketSelect => true
+ case _ => false
+ }, s"Invalid lhs for Assign: $lhs")
+
+ val tpe = NoType // cannot be in expression position
+ }
+
+ case class Return(expr: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree {
+ val tpe = NothingType
+ }
+
+ case class If(cond: Tree, thenp: Tree, elsep: Tree)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class While(cond: Tree, body: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree {
+ // cannot be in expression position, unless it is infinite
+ val tpe = cond match {
+ case BooleanLiteral(true) => NothingType
+ case _ => NoType
+ }
+ }
+
+ case class DoWhile(body: Tree, cond: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree {
+ val tpe = NoType // cannot be in expression position
+ }
+
+ case class Try(block: Tree, errVar: Ident, handler: Tree, finalizer: Tree)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class Throw(expr: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = NothingType
+ }
+
+ case class Continue(label: Option[Ident] = None)(implicit val pos: Position) extends Tree {
+ val tpe = NothingType
+ }
+
+ /** A break-free switch (without fallthrough behavior).
+ * Unlike a JavaScript switch, it can be used in expression position.
+ * It supports alternatives explicitly (hence the List[Tree] in cases),
+ * whereas in a switch one would use the fallthrough behavior to
+ * implement alternatives.
+ * (This is not a pattern matching construct like in Scala.)
+ */
+ case class Match(selector: Tree, cases: List[(List[Literal], Tree)], default: Tree)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class Debugger()(implicit val pos: Position) extends Tree {
+ val tpe = NoType // cannot be in expression position
+ }
+
+ // Scala expressions
+
+ case class New(cls: ClassType, ctor: Ident, args: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = cls
+ }
+
+ case class LoadModule(cls: ClassType)(implicit val pos: Position) extends Tree {
+ val tpe = cls
+ }
+
+ case class StoreModule(cls: ClassType, value: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = NoType // cannot be in expression position
+ }
+
+ case class Select(qualifier: Tree, item: Ident, mutable: Boolean)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class Apply(receiver: Tree, method: Ident, args: List[Tree])(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class StaticApply(receiver: Tree, cls: ClassType, method: Ident, args: List[Tree])(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class TraitImplApply(impl: ClassType, method: Ident, args: List[Tree])(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ /** Unary operation (always preserves pureness). */
+ case class UnaryOp(op: UnaryOp.Code, lhs: Tree)(implicit val pos: Position) extends Tree {
+ import UnaryOp._
+ val tpe = (op: @switch) match {
+ case `typeof` => StringType
+ case LongToInt | DoubleToInt => IntType
+ case IntToLong | DoubleToLong => LongType
+ case DoubleToFloat => FloatType
+ case LongToDouble => DoubleType
+ case Boolean_! => BooleanType
+ }
+ }
+
+ object UnaryOp {
+ /** Codes are raw Ints to be able to write switch matches on them. */
+ type Code = Int
+
+ final val typeof = 1
+
+ final val Boolean_! = 2
+
+ final val IntToLong = 3
+ final val LongToInt = 4
+ final val LongToDouble = 5
+ final val DoubleToInt = 6
+ final val DoubleToFloat = 7
+ final val DoubleToLong = 8
+ }
+
+ /** Binary operation (always preserves pureness). */
+ case class BinaryOp(op: BinaryOp.Code, lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree {
+ import BinaryOp._
+ val tpe = (op: @switch) match {
+ case === | !== |
+ `in` | `instanceof` |
+ Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>= |
+ Long_== | Long_!= | Long_< | Long_<= | Long_> | Long_>= |
+ Boolean_== | Boolean_!= | Boolean_| | Boolean_& =>
+ BooleanType
+ case String_+ =>
+ StringType
+ case Int_+ | Int_- | Int_* | Int_/ | Int_% |
+ Int_| | Int_& | Int_^ | Int_<< | Int_>>> | Int_>> =>
+ IntType
+ case Float_+ | Float_- | Float_* | Float_/ | Float_% =>
+ FloatType
+ case Double_+ | Double_- | Double_* | Double_/ | Double_% =>
+ DoubleType
+ case Long_+ | Long_- | Long_* | Long_/ | Long_% |
+ Long_| | Long_& | Long_^ | Long_<< | Long_>>> | Long_>> =>
+ LongType
+ }
+ }
+
+ object BinaryOp {
+ /** Codes are raw Ints to be able to write switch matches on them. */
+ type Code = Int
+
+ final val === = 1
+ final val !== = 2
+
+ final val String_+ = 3
+
+ final val in = 4
+ final val instanceof = 5
+
+ final val Int_+ = 6
+ final val Int_- = 7
+ final val Int_* = 8
+ final val Int_/ = 9
+ final val Int_% = 10
+
+ final val Int_| = 11
+ final val Int_& = 12
+ final val Int_^ = 13
+ final val Int_<< = 14
+ final val Int_>>> = 15
+ final val Int_>> = 16
+
+ final val Float_+ = 17
+ final val Float_- = 18
+ final val Float_* = 19
+ final val Float_/ = 20
+ final val Float_% = 21
+
+ final val Double_+ = 22
+ final val Double_- = 23
+ final val Double_* = 24
+ final val Double_/ = 25
+ final val Double_% = 26
+
+ final val Num_== = 27
+ final val Num_!= = 28
+ final val Num_< = 29
+ final val Num_<= = 30
+ final val Num_> = 31
+ final val Num_>= = 32
+
+ final val Long_+ = 33
+ final val Long_- = 34
+ final val Long_* = 35
+ final val Long_/ = 36
+ final val Long_% = 37
+
+ final val Long_| = 38
+ final val Long_& = 39
+ final val Long_^ = 40
+ final val Long_<< = 41
+ final val Long_>>> = 42
+ final val Long_>> = 43
+
+ final val Long_== = 44
+ final val Long_!= = 45
+ final val Long_< = 46
+ final val Long_<= = 47
+ final val Long_> = 48
+ final val Long_>= = 49
+
+ final val Boolean_== = 50
+ final val Boolean_!= = 51
+ final val Boolean_| = 52
+ final val Boolean_& = 53
+ }
+
+ case class NewArray(tpe: ArrayType, lengths: List[Tree])(implicit val pos: Position) extends Tree {
+ require(lengths.nonEmpty && lengths.size <= tpe.dimensions)
+ }
+
+ case class ArrayValue(tpe: ArrayType, elems: List[Tree])(implicit val pos: Position) extends Tree
+
+ case class ArrayLength(array: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = IntType
+ }
+
+ case class ArraySelect(array: Tree, index: Tree)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class RecordValue(tpe: RecordType, elems: List[Tree])(implicit val pos: Position) extends Tree
+
+ case class IsInstanceOf(expr: Tree, cls: ReferenceType)(implicit val pos: Position) extends Tree {
+ val tpe = BooleanType
+ }
+
+ case class AsInstanceOf(expr: Tree, cls: ReferenceType)(implicit val pos: Position) extends Tree {
+ val tpe = cls match {
+ case ClassType(Definitions.RuntimeNullClass) => NullType
+ case ClassType(Definitions.RuntimeNothingClass) => NothingType
+ case _ => cls
+ }
+ }
+
+ case class Unbox(expr: Tree, charCode: Char)(implicit val pos: Position) extends Tree {
+ val tpe = (charCode: @switch) match {
+ case 'Z' => BooleanType
+ case 'B' | 'S' | 'I' => IntType
+ case 'J' => LongType
+ case 'F' => FloatType
+ case 'D' => DoubleType
+ }
+ }
+
+ case class GetClass(expr: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = ClassType(Definitions.ClassClass)
+ }
+
+ case class CallHelper(helper: String, args: List[Tree])(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ object CallHelper {
+ def apply(helper: String, args: Tree*)(tpe: Type)(
+ implicit pos: Position): CallHelper = {
+ CallHelper(helper, args.toList)(tpe)
+ }
+ }
+
+ // JavaScript expressions
+
+ case class JSNew(ctor: Tree, args: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSDotSelect(qualifier: Tree, item: Ident)(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSBracketSelect(qualifier: Tree, item: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSFunctionApply(fun: Tree, args: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSDotMethodApply(receiver: Tree, method: Ident, args: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSBracketMethodApply(receiver: Tree, method: Tree, args: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSDelete(prop: Tree)(implicit val pos: Position) extends Tree {
+ require(prop match {
+ case _:JSDotSelect | _:JSBracketSelect => true
+ case _ => false
+ }, s"Invalid prop for JSDelete: $prop")
+
+ val tpe = NoType // cannot be in expression position
+ }
+
+ /** Unary operation (always preserves pureness).
+ *
+ * Operations which do not preserve pureness are not allowed in this tree.
+ * These are notably ++ and --
+ */
+ case class JSUnaryOp(op: String, lhs: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ /** Binary operation (always preserves pureness).
+ *
+ * Operations which do not preserve pureness are not allowed in this tree.
+ * These are notably +=, -=, *=, /= and %=
+ */
+ case class JSBinaryOp(op: String, lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSArrayConstr(items: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSObjectConstr(fields: List[(PropertyName, Tree)])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ case class JSEnvInfo()(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ // Literals
+
+ /** Marker for literals. Literals are always pure. */
+ sealed trait Literal extends Tree
+
+ case class Undefined()(implicit val pos: Position) extends Literal {
+ val tpe = UndefType
+ }
+
+ case class UndefinedParam()(val tpe: Type)(implicit val pos: Position) extends Literal
+
+ case class Null()(implicit val pos: Position) extends Literal {
+ val tpe = NullType
+ }
+
+ case class BooleanLiteral(value: Boolean)(implicit val pos: Position) extends Literal {
+ val tpe = BooleanType
+ }
+
+ case class IntLiteral(value: Int)(implicit val pos: Position) extends Literal {
+ val tpe = IntType
+ }
+
+ case class LongLiteral(value: Long)(implicit val pos: Position) extends Literal {
+ val tpe = LongType
+ }
+
+ case class FloatLiteral(value: Float)(implicit val pos: Position) extends Literal {
+ val tpe = FloatType
+ }
+
+ case class DoubleLiteral(value: Double)(implicit val pos: Position) extends Literal {
+ val tpe = DoubleType
+ }
+
+ case class StringLiteral(value: String)(
+ implicit val pos: Position) extends Literal with PropertyName {
+ val tpe = StringType
+ override def name = value
+ }
+
+ case class ClassOf(cls: ReferenceType)(implicit val pos: Position) extends Literal {
+ val tpe = ClassType(Definitions.ClassClass)
+ }
+
+ // Atomic expressions
+
+ case class VarRef(ident: Ident, mutable: Boolean)(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ case class This()(val tpe: Type)(implicit val pos: Position) extends Tree
+
+ /** Closure with explicit captures.
+ * The n captures map to the n first formal arguments.
+ */
+ case class Closure(captureParams: List[ParamDef], params: List[ParamDef],
+ body: Tree, captureValues: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = AnyType
+ }
+
+ // Classes
+
+ case class ClassDef(name: Ident, kind: ClassKind, parent: Option[Ident], ancestors: List[Ident], defs: List[Tree])(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+ }
+
+ case class MethodDef(name: PropertyName, args: List[ParamDef], resultType: Type, body: Tree)(
+ val hash: Option[TreeHash])(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+ }
+
+ case class PropertyDef(name: PropertyName, getterBody: Tree, setterArg: ParamDef, setterBody: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+ }
+
+ case class ConstructorExportDef(name: String, args: List[ParamDef], body: Tree)(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+ }
+
+ case class ModuleExportDef(fullName: String)(implicit val pos: Position) extends Tree {
+ val tpe = NoType
+ }
+
+ /** A hash of a tree (usually a MethodDef). Contains two SHA-1 hashes */
+ final class TreeHash(val treeHash: Array[Byte], val posHash: Array[Byte]) {
+ assert(treeHash.length == 20)
+ assert(posHash.length == 20)
+ }
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Types.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Types.scala
new file mode 100644
index 0000000..4af493a
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Types.scala
@@ -0,0 +1,182 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ 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
+ })
+ }
+ }
+}
diff --git a/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Utils.scala b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Utils.scala
new file mode 100644
index 0000000..d4769dc
--- /dev/null
+++ b/examples/scala-js/ir/src/main/scala/scala/scalajs/ir/Utils.scala
@@ -0,0 +1,110 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import java.net.URI
+
+object Utils {
+
+ /** Relativize target URI w.r.t. base URI */
+ def relativize(base0: URI, trgt0: URI): URI = {
+ val base = base0.normalize
+ val trgt = trgt0.normalize
+
+ if (base.isOpaque || !base.isAbsolute || base.getRawPath == null ||
+ trgt.isOpaque || !trgt.isAbsolute || trgt.getRawPath == null ||
+ base.getScheme != trgt.getScheme ||
+ base.getRawAuthority != trgt.getRawAuthority)
+ trgt
+ else {
+ val trgtCmps = trgt.getRawPath.split('/')
+ val baseCmps = base.getRawPath.split('/')
+
+ val prefixLen = (trgtCmps zip baseCmps).takeWhile(t => t._1 == t._2).size
+
+ val newPathCmps =
+ List.fill(baseCmps.size - prefixLen)("..") ++ trgtCmps.drop(prefixLen)
+
+ val newPath = newPathCmps.mkString("/")
+
+ // Relative URI does not have scheme or authority
+ new URI(null, null, newPath, trgt.getRawQuery, trgt.getRawFragment)
+ }
+ }
+
+ /** Adds an empty authority to URIs with the "file" scheme without authority.
+ * Some browsers don't fetch URIs without authority correctly.
+ */
+ def fixFileURI(uri: URI): URI =
+ if (uri.getScheme() != "file" || uri.getAuthority() != null) uri
+ else new URI("file", "", uri.getPath(), uri.getQuery(), uri.getFragment())
+
+ def escapeJS(str: String): String = {
+ /* Note that Java and JavaScript happen to use the same encoding for
+ * Unicode, namely UTF-16, which means that 1 char from Java always equals
+ * 1 char in JavaScript. */
+ val builder = new StringBuilder
+ str foreach {
+ case '\\' => builder.append("\\\\")
+ case '"' => builder.append("\\\"")
+ case '\u0007' => builder.append("\\a")
+ case '\u0008' => builder.append("\\b")
+ case '\u0009' => builder.append("\\t")
+ case '\u000A' => builder.append("\\n")
+ case '\u000B' => builder.append("\\v")
+ case '\u000C' => builder.append("\\f")
+ case '\u000D' => builder.append("\\r")
+ case c =>
+ if (c >= 32 && c <= 126) builder.append(c.toChar) // ASCII printable characters
+ else builder.append(f"\\u$c%04x")
+ }
+ builder.result()
+ }
+
+ /** A ByteArrayOutput stream that allows to jump back to a given
+ * position and complete some bytes. Methods must be called in the
+ * following order only:
+ * - [[markJump]]
+ * - [[jumpBack]]
+ * - [[continue]]
+ */
+ private[ir] class JumpBackByteArrayOutputStream
+ extends java.io.ByteArrayOutputStream {
+ protected var jumpBackPos: Int = -1
+ protected var headPos: Int = -1
+
+ /** Marks the current location for a jumpback */
+ def markJump(): Unit = {
+ assert(jumpBackPos == -1)
+ assert(headPos == -1)
+ jumpBackPos = count
+ }
+
+ /** Jumps back to the mark. Returns the number of bytes jumped */
+ def jumpBack(): Int = {
+ assert(jumpBackPos >= 0)
+ assert(headPos == -1)
+ val jumped = count - jumpBackPos
+ headPos = count
+ count = jumpBackPos
+ jumpBackPos = -1
+ jumped
+ }
+
+ /** Continues to write at the head. */
+ def continue(): Unit = {
+ assert(jumpBackPos == -1)
+ assert(headPos >= 0)
+ count = headPos
+ headPos = -1
+ }
+ }
+
+}