aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-02-15 16:07:36 +0100
committerMartin Odersky <odersky@gmail.com>2013-02-15 16:07:36 +0100
commit5e219c1d8426da4fba6c4604e24f4bceb3573392 (patch)
treec62cccb5b0c31e3bab8c5b5e77a5911a5a8c6bbd
parent11c5251de18aab187646e3f58612b457349ebe6a (diff)
downloaddotty-5e219c1d8426da4fba6c4604e24f4bceb3573392.tar.gz
dotty-5e219c1d8426da4fba6c4604e24f4bceb3573392.tar.bz2
dotty-5e219c1d8426da4fba6c4604e24f4bceb3573392.zip
Almost completed code for unpickling and classfile loading.
Still remains: Unpicklign trees, dealing with sourcefile attributes.
-rw-r--r--src/dotty/tools/dotc/core/SymbolLoaders.scala81
-rw-r--r--src/dotty/tools/dotc/core/pickling/AbstractFileReader.scala88
-rw-r--r--src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala378
-rw-r--r--src/dotty/tools/dotc/core/pickling/ClassfileParser.scala944
-rw-r--r--src/dotty/tools/dotc/core/pickling/PickleBuffer.scala18
-rw-r--r--src/dotty/tools/dotc/core/pickling/UnPickler.scala1149
6 files changed, 2037 insertions, 621 deletions
diff --git a/src/dotty/tools/dotc/core/SymbolLoaders.scala b/src/dotty/tools/dotc/core/SymbolLoaders.scala
index fa888ae12..191772fd2 100644
--- a/src/dotty/tools/dotc/core/SymbolLoaders.scala
+++ b/src/dotty/tools/dotc/core/SymbolLoaders.scala
@@ -10,9 +10,9 @@ package core
import java.io.IOException
import scala.compat.Platform.currentTime
import dotty.tools.io.{ ClassPath, AbstractFile }
-import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._
+import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, Positions._, Names._
import Decorators.StringDecorator
-//import classfile.ClassfileParser
+import pickling.ClassfileParser
/** A base class for Symbol loaders with some overridable behavior */
class SymbolLoaders {
@@ -25,22 +25,22 @@ class SymbolLoaders {
/** Enter class with given `name` into scope of `owner`.
*/
- def enterClass(owner: Symbol, name: String, completer: SymbolLoader)(implicit ctx: Context): Symbol = {
- val cls = ctx.newLazyClassSymbol(owner, name.toTypeName, EmptyFlags, completer, assocFile = completer.sourceFileOrNull)
+ def enterClass(owner: Symbol, name: PreName, completer: SymbolLoader, flags: FlagSet = EmptyFlags)(implicit ctx: Context): Symbol = {
+ val cls = ctx.newLazyClassSymbol(owner, name.toTypeName, flags, completer, assocFile = completer.sourceFileOrNull)
enterIfNew(owner, cls, completer)
}
/** Enter module with given `name` into scope of `owner`.
*/
- def enterModule(owner: Symbol, name: String, completer: SymbolLoader)(implicit ctx: Context): Symbol = {
- val module = ctx.newLazyModuleSymbols(owner, name.toTermName, EmptyFlags, completer, assocFile = completer.sourceFileOrNull)._1
+ def enterModule(owner: Symbol, name: PreName, completer: SymbolLoader, flags: FlagSet = EmptyFlags)(implicit ctx: Context): Symbol = {
+ val module = ctx.newLazyModuleSymbols(owner, name.toTermName, flags, completer, assocFile = completer.sourceFileOrNull)._1
enterIfNew(owner, module, completer)
}
/** Enter package with given `name` into scope of `owner`
* and give them `completer` as type.
*/
- def enterPackage(owner: Symbol, name: String, completer: SymbolLoader)(implicit ctx: Context): Symbol = {
+ def enterPackage(owner: Symbol, name: PreName, completer: SymbolLoader)(implicit ctx: Context): Symbol = {
val pname = name.toTermName
val preExisting = owner.info.decls lookup pname
if (preExisting != NoSymbol) {
@@ -69,9 +69,9 @@ class SymbolLoaders {
/** Enter class and module with given `name` into scope of `owner`
* and give them `completer` as type.
*/
- def enterClassAndModule(owner: Symbol, name: String, completer: SymbolLoader)(implicit ctx: Context) {
- val clazz = enterClass(owner, name, completer)
- val module = enterModule(owner, name, completer)
+ def enterClassAndModule(owner: Symbol, name: PreName, completer: SymbolLoader, flags: FlagSet = EmptyFlags)(implicit ctx: Context) {
+ val clazz = enterClass(owner, name, completer, flags)
+ val module = enterModule(owner, name, completer, flags)
if (!clazz.isAnonymousClass) {
assert(clazz.companionModule == module, module)
assert(module.companionClass == clazz, clazz)
@@ -84,7 +84,7 @@ class SymbolLoaders {
* with source completer for given `src` as type.
* (overridden in interactive.Global).
*/
- def enterToplevelsFromSource(owner: Symbol, name: String, src: AbstractFile)(implicit ctx: Context) {
+ def enterToplevelsFromSource(owner: Symbol, name: PreName, src: AbstractFile)(implicit ctx: Context) {
??? // !!! enterClassAndModule(owner, name, new SourcefileLoader(src))
}
@@ -123,6 +123,8 @@ class SymbolLoaders {
implicit val ctx: Context = cctx
protected def description = "package loader " + classpath.name
+ protected def doLoad(root: LazyClassDenotation) = doComplete(root)
+
protected def doComplete(root: LazyClassDenotation) {
assert(root.isPackageClass, root)
root.parents = Nil
@@ -143,7 +145,6 @@ class SymbolLoaders {
}
def openPackageModule(pkgClass: Symbol): Unit = ???
-
}
/** A lazy type that completes itself by calling parameter doComplete.
* Any linked modules/classes or module classes are also initialized.
@@ -157,69 +158,59 @@ abstract class SymbolLoader extends ClassCompleter {
protected def doComplete(root: LazyClassDenotation): Unit
def sourceFileOrNull: AbstractFile = null
+
/** Description of the resource (ClassPath, AbstractFile, MsilFile)
* being processed by this loader
*/
protected def description: String
- private var ok = false
-
- override def complete(root: LazyClassDenotation) {
+ override def complete(root: LazyClassDenotation) = {
def signalError(ex: Exception) {
- ok = false
if (ctx.settings.debug.value) ex.printStackTrace()
val msg = ex.getMessage()
ctx.error(
if (msg eq null) "i/o error while loading " + root.name
- else "error while loading " + root.name + ", " + msg);
+ else "error while loading " + root.name + ", " + msg)
}
try {
val start = currentTime
doComplete(root)
ctx.informTime("loaded " + description, start)
- ok = true
} catch {
case ex: IOException =>
signalError(ex)
case ex: MissingRequirementError =>
signalError(ex)
- }
- // also mark linked class as completed if it exists
- root.linkedClass.denot match {
- case companion: LazyClassDenotation => companion.completer = null
+ } finally {
+ root.linkedClass.denot match {
+ case companion: LazyClassDenotation => companion.completer = null
+ }
}
}
}
+class ClassfileLoader(val classfile: AbstractFile)(cctx: CondensedContext) extends SymbolLoader {
+ implicit val ctx: Context = cctx
- /*
+ override def sourceFileOrNull: AbstractFile = classfile
+ protected def description = "class file "+ classfile.toString
- class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader with FlagAssigningCompleter {
- private object classfileParser extends ClassfileParser {
- val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global
+ def rootDenots(rootDenot: LazyClassDenotation): (LazyClassDenotation, LazyClassDenotation) = {
+ val linkedDenot = rootDenot.linkedClass.denot match {
+ case d: LazyClassDenotation => d
+ case d => throw new FatalError(s"linked class denot $d of $rootDenot is expected to be a LazyClassDenot, but is a ${d.getClass}")
}
-
- protected def description = "class file "+ classfile.toString
-
- protected def doComplete(root: Symbol) {
- val start = if (Statistics.canEnable) Statistics.startTimer(classReadNanos) else null
- classfileParser.parse(classfile, root)
- if (root.associatedFile eq null) {
- root match {
- // In fact, the ModuleSymbol forwards its setter to the module class
- case _: ClassSymbol | _: ModuleSymbol =>
- debuglog("ClassfileLoader setting %s.associatedFile = %s".format(root.name, classfile))
- root.associatedFile = classfile
- case _ =>
- debuglog("Not setting associatedFile to %s because %s is a %s".format(classfile, root.name, root.shortSymbolClass))
- }
- }
- if (Statistics.canEnable) Statistics.stopTimer(classReadNanos, start)
- }
- override def sourcefile: Option[AbstractFile] = classfileParser.srcfile
+ if (rootDenot.isModule) (linkedDenot, rootDenot)
+ else (rootDenot, linkedDenot)
}
+ protected def doComplete(root: LazyClassDenotation) {
+ val (classRoot, moduleRoot) = rootDenots(root)
+ new ClassfileParser(classfile, classRoot, moduleRoot)(cctx).run()
+ }
+}
+/*
class MsilFileLoader(msilFile: MsilFile) extends SymbolLoader with FlagAssigningCompleter {
private def typ = msilFile.msilType
private object typeParser extends clr.TypeParser {
diff --git a/src/dotty/tools/dotc/core/pickling/AbstractFileReader.scala b/src/dotty/tools/dotc/core/pickling/AbstractFileReader.scala
new file mode 100644
index 000000000..60633370d
--- /dev/null
+++ b/src/dotty/tools/dotc/core/pickling/AbstractFileReader.scala
@@ -0,0 +1,88 @@
+package dotty.tools
+package dotc
+package core
+package pickling
+
+import java.lang.Float.intBitsToFloat
+import java.lang.Double.longBitsToDouble
+
+import io.AbstractFile
+
+/**
+ * This class reads files byte per byte. Only used by ClassFileParser
+ *
+ * @author Philippe Altherr
+ * @version 1.0, 23/03/2004
+ */
+class AbstractFileReader(val file: AbstractFile) {
+
+ /** the buffer containing the file
+ */
+ val buf: Array[Byte] = file.toByteArray
+
+ /** the current input pointer
+ */
+ var bp: Int = 0
+
+ /** return byte at offset 'pos'
+ */
+ @throws(classOf[IndexOutOfBoundsException])
+ def byteAt(pos: Int): Byte = buf(pos)
+
+ /** read a byte
+ */
+ @throws(classOf[IndexOutOfBoundsException])
+ def nextByte: Byte = {
+ val b = buf(bp)
+ bp += 1
+ b
+ }
+
+ /** read some bytes
+ */
+ def nextBytes(len: Int): Array[Byte] = {
+ bp += len
+ buf.slice(bp - len, bp)
+ }
+
+ /** read a character
+ */
+ def nextChar: Char =
+ (((nextByte & 0xff) << 8) + (nextByte & 0xff)).toChar
+
+ /** read an integer
+ */
+ def nextInt: Int =
+ ((nextByte & 0xff) << 24) + ((nextByte & 0xff) << 16) +
+ ((nextByte & 0xff) << 8) + (nextByte & 0xff)
+
+
+ /** extract a character at position bp from buf
+ */
+ def getChar(mybp: Int): Char =
+ (((buf(mybp) & 0xff) << 8) + (buf(mybp+1) & 0xff)).toChar
+
+ /** extract an integer at position bp from buf
+ */
+ def getInt(mybp: Int): Int =
+ ((buf(mybp ) & 0xff) << 24) + ((buf(mybp+1) & 0xff) << 16) +
+ ((buf(mybp+2) & 0xff) << 8) + (buf(mybp+3) & 0xff)
+
+ /** extract a long integer at position bp from buf
+ */
+ def getLong(mybp: Int): Long =
+ (getInt(mybp).toLong << 32) + (getInt(mybp + 4) & 0xffffffffL)
+
+ /** extract a float at position bp from buf
+ */
+ def getFloat(mybp: Int): Float = intBitsToFloat(getInt(mybp))
+
+ /** extract a double at position bp from buf
+ */
+ def getDouble(mybp: Int): Double = longBitsToDouble(getLong(mybp))
+
+ /** skip next 'n' bytes
+ */
+ def skip(n: Int) { bp += n }
+
+}
diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala
new file mode 100644
index 000000000..5e97f373d
--- /dev/null
+++ b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala
@@ -0,0 +1,378 @@
+package dotty.tools.dotc.core
+package pickling
+
+import scala.annotation.switch
+
+object ClassfileConstants {
+
+ final val JAVA_MAGIC = 0xCAFEBABE
+ final val JAVA_MAJOR_VERSION = 45
+ final val JAVA_MINOR_VERSION = 3
+
+ /** (see http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html)
+ *
+ * If the `ACC_INTERFACE` flag is set, the `ACC_ABSTRACT` flag must also
+ * be set (ch. 2.13.1).
+ *
+ * A class file cannot have both its `ACC_FINAL` and `ACC_ABSTRACT` flags
+ * set (ch. 2.8.2).
+ *
+ * A field may have at most one of its `ACC_PRIVATE`, `ACC_PROTECTED`,
+ * `ACC_PUBLIC` flags set (ch. 2.7.4).
+ *
+ * A field may not have both its `ACC_FINAL` and `ACC_VOLATILE` flags set
+ * (ch. 2.9.1).
+ *
+ * If a method has its `ACC_ABSTRACT` flag set it must not have any of its
+ * `ACC_FINAL`, `ACC_NATIVE`, `ACC_PRIVATE`, `ACC_STATIC`, `ACC_STRICT`,
+ * or `ACC_SYNCHRONIZED` flags set (ch. 2.13.3.2).
+ *
+ * All interface methods must have their `ACC_ABSTRACT` and
+ * `ACC_PUBLIC` flags set.
+ *
+ * Note for future reference: see this thread on ACC_SUPER and
+ * how its enforcement differs on the android vm.
+ * https://groups.google.com/forum/?hl=en#!topic/jvm-languages/jVhzvq8-ZIk
+ *
+ */ // Class Field Method
+ final val JAVA_ACC_PUBLIC = 0x0001 // X X X
+ final val JAVA_ACC_PRIVATE = 0x0002 // X X
+ final val JAVA_ACC_PROTECTED = 0x0004 // X X
+ final val JAVA_ACC_STATIC = 0x0008 // X X
+ final val JAVA_ACC_FINAL = 0x0010 // X X X
+ final val JAVA_ACC_SUPER = 0x0020 // X
+ final val JAVA_ACC_SYNCHRONIZED = 0x0020 // X
+ final val JAVA_ACC_VOLATILE = 0x0040 // X
+ final val JAVA_ACC_BRIDGE = 0x0040 // X
+ final val JAVA_ACC_TRANSIENT = 0x0080 // X
+ final val JAVA_ACC_VARARGS = 0x0080 // X
+ final val JAVA_ACC_NATIVE = 0x0100 // X
+ final val JAVA_ACC_INTERFACE = 0x0200 // X
+ final val JAVA_ACC_ABSTRACT = 0x0400 // X X
+ final val JAVA_ACC_STRICT = 0x0800 // X
+ final val JAVA_ACC_SYNTHETIC = 0x1000 // X X X
+ final val JAVA_ACC_ANNOTATION = 0x2000 // X
+ final val JAVA_ACC_ENUM = 0x4000 // X X
+
+ // tags describing the type of a literal in the constant pool
+ final val CONSTANT_UTF8 = 1
+ final val CONSTANT_UNICODE = 2
+ final val CONSTANT_INTEGER = 3
+ final val CONSTANT_FLOAT = 4
+ final val CONSTANT_LONG = 5
+ final val CONSTANT_DOUBLE = 6
+ final val CONSTANT_CLASS = 7
+ final val CONSTANT_STRING = 8
+ final val CONSTANT_FIELDREF = 9
+ final val CONSTANT_METHODREF = 10
+ final val CONSTANT_INTFMETHODREF = 11
+ final val CONSTANT_NAMEANDTYPE = 12
+
+ // tags describing the type of a literal in attribute values
+ final val BYTE_TAG = 'B'
+ final val CHAR_TAG = 'C'
+ final val DOUBLE_TAG = 'D'
+ final val FLOAT_TAG = 'F'
+ final val INT_TAG = 'I'
+ final val LONG_TAG = 'J'
+ final val SHORT_TAG = 'S'
+ final val BOOL_TAG = 'Z'
+ final val STRING_TAG = 's'
+ final val ENUM_TAG = 'e'
+ final val CLASS_TAG = 'c'
+ final val ARRAY_TAG = '['
+ final val VOID_TAG = 'V'
+ final val TVAR_TAG = 'T'
+ final val OBJECT_TAG = 'L'
+ final val ANNOTATION_TAG = '@'
+ final val SCALA_NOTHING = "scala.runtime.Nothing$"
+ final val SCALA_NULL = "scala.runtime.Null$"
+
+
+ // tags describing the type of newarray
+ final val T_BOOLEAN = 4
+ final val T_CHAR = 5
+ final val T_FLOAT = 6
+ final val T_DOUBLE = 7
+ final val T_BYTE = 8
+ final val T_SHORT = 9
+ final val T_INT = 10
+ final val T_LONG = 11
+
+ // JVM mnemonics
+ final val nop = 0x00
+ final val aconst_null = 0x01
+ final val iconst_m1 = 0x02
+
+ final val iconst_0 = 0x03
+ final val iconst_1 = 0x04
+ final val iconst_2 = 0x05
+ final val iconst_3 = 0x06
+ final val iconst_4 = 0x07
+ final val iconst_5 = 0x08
+
+ final val lconst_0 = 0x09
+ final val lconst_1 = 0x0a
+ final val fconst_0 = 0x0b
+ final val fconst_1 = 0x0c
+ final val fconst_2 = 0x0d
+ final val dconst_0 = 0x0e
+ final val dconst_1 = 0x0f
+
+ final val bipush = 0x10
+ final val sipush = 0x11
+ final val ldc = 0x12
+ final val ldc_w = 0x13
+ final val ldc2_w = 0x14
+
+ final val iload = 0x15
+ final val lload = 0x16
+ final val fload = 0x17
+ final val dload = 0x18
+ final val aload = 0x19
+
+ final val iload_0 = 0x1a
+ final val iload_1 = 0x1b
+ final val iload_2 = 0x1c
+ final val iload_3 = 0x1d
+ final val lload_0 = 0x1e
+ final val lload_1 = 0x1f
+ final val lload_2 = 0x20
+ final val lload_3 = 0x21
+ final val fload_0 = 0x22
+ final val fload_1 = 0x23
+ final val fload_2 = 0x24
+ final val fload_3 = 0x25
+ final val dload_0 = 0x26
+ final val dload_1 = 0x27
+ final val dload_2 = 0x28
+ final val dload_3 = 0x29
+ final val aload_0 = 0x2a
+ final val aload_1 = 0x2b
+ final val aload_2 = 0x2c
+ final val aload_3 = 0x2d
+ final val iaload = 0x2e
+ final val laload = 0x2f
+ final val faload = 0x30
+ final val daload = 0x31
+ final val aaload = 0x32
+ final val baload = 0x33
+ final val caload = 0x34
+ final val saload = 0x35
+
+ final val istore = 0x36
+ final val lstore = 0x37
+ final val fstore = 0x38
+ final val dstore = 0x39
+ final val astore = 0x3a
+ final val istore_0 = 0x3b
+ final val istore_1 = 0x3c
+ final val istore_2 = 0x3d
+ final val istore_3 = 0x3e
+ final val lstore_0 = 0x3f
+ final val lstore_1 = 0x40
+ final val lstore_2 = 0x41
+ final val lstore_3 = 0x42
+ final val fstore_0 = 0x43
+ final val fstore_1 = 0x44
+ final val fstore_2 = 0x45
+ final val fstore_3 = 0x46
+ final val dstore_0 = 0x47
+ final val dstore_1 = 0x48
+ final val dstore_2 = 0x49
+ final val dstore_3 = 0x4a
+ final val astore_0 = 0x4b
+ final val astore_1 = 0x4c
+ final val astore_2 = 0x4d
+ final val astore_3 = 0x4e
+ final val iastore = 0x4f
+ final val lastore = 0x50
+ final val fastore = 0x51
+ final val dastore = 0x52
+ final val aastore = 0x53
+ final val bastore = 0x54
+ final val castore = 0x55
+ final val sastore = 0x56
+
+ final val pop = 0x57
+ final val pop2 = 0x58
+ final val dup = 0x59
+ final val dup_x1 = 0x5a
+ final val dup_x2 = 0x5b
+ final val dup2 = 0x5c
+ final val dup2_x1 = 0x5d
+ final val dup2_x2 = 0x5e
+ final val swap = 0x5f
+
+ final val iadd = 0x60
+ final val ladd = 0x61
+ final val fadd = 0x62
+ final val dadd = 0x63
+ final val isub = 0x64
+ final val lsub = 0x65
+ final val fsub = 0x66
+ final val dsub = 0x67
+ final val imul = 0x68
+ final val lmul = 0x69
+ final val fmul = 0x6a
+ final val dmul = 0x6b
+ final val idiv = 0x6c
+ final val ldiv = 0x6d
+ final val fdiv = 0x6e
+ final val ddiv = 0x6f
+ final val irem = 0x70
+ final val lrem = 0x71
+ final val frem = 0x72
+ final val drem = 0x73
+
+ final val ineg = 0x74
+ final val lneg = 0x75
+ final val fneg = 0x76
+ final val dneg = 0x77
+
+ final val ishl = 0x78
+ final val lshl = 0x79
+ final val ishr = 0x7a
+ final val lshr = 0x7b
+ final val iushr = 0x7c
+ final val lushr = 0x7d
+ final val iand = 0x7e
+ final val land = 0x7f
+ final val ior = 0x80
+ final val lor = 0x81
+ final val ixor = 0x82
+ final val lxor = 0x83
+ final val iinc = 0x84
+
+ final val i2l = 0x85
+ final val i2f = 0x86
+ final val i2d = 0x87
+ final val l2i = 0x88
+ final val l2f = 0x89
+ final val l2d = 0x8a
+ final val f2i = 0x8b
+ final val f2l = 0x8c
+ final val f2d = 0x8d
+ final val d2i = 0x8e
+ final val d2l = 0x8f
+ final val d2f = 0x90
+ final val i2b = 0x91
+ final val i2c = 0x92
+ final val i2s = 0x93
+
+ final val lcmp = 0x94
+ final val fcmpl = 0x95
+ final val fcmpg = 0x96
+ final val dcmpl = 0x97
+ final val dcmpg = 0x98
+
+ final val ifeq = 0x99
+ final val ifne = 0x9a
+ final val iflt = 0x9b
+ final val ifge = 0x9c
+ final val ifgt = 0x9d
+ final val ifle = 0x9e
+ final val if_icmpeq = 0x9f
+ final val if_icmpne = 0xa0
+ final val if_icmplt = 0xa1
+ final val if_icmpge = 0xa2
+ final val if_icmpgt = 0xa3
+ final val if_icmple = 0xa4
+ final val if_acmpeq = 0xa5
+ final val if_acmpne = 0xa6
+ final val goto = 0xa7
+ final val jsr = 0xa8
+ final val ret = 0xa9
+ final val tableswitch = 0xaa
+ final val lookupswitch = 0xab
+ final val ireturn = 0xac
+ final val lreturn = 0xad
+ final val freturn = 0xae
+ final val dreturn = 0xaf
+ final val areturn = 0xb0
+ final val return_ = 0xb1
+
+ final val getstatic = 0xb2
+ final val putstatic = 0xb3
+ final val getfield = 0xb4
+ final val putfield = 0xb5
+
+ final val invokevirtual = 0xb6
+ final val invokespecial = 0xb7
+ final val invokestatic = 0xb8
+ final val invokeinterface = 0xb9
+ final val xxxunusedxxxx = 0xba
+
+ final val new_ = 0xbb
+ final val newarray = 0xbc
+ final val anewarray = 0xbd
+ final val arraylength = 0xbe
+ final val athrow = 0xbf
+ final val checkcast = 0xc0
+ final val instanceof = 0xc1
+ final val monitorenter = 0xc2
+ final val monitorexit = 0xc3
+ final val wide = 0xc4
+ final val multianewarray = 0xc5
+ final val ifnull = 0xc6
+ final val ifnonnull = 0xc7
+ final val goto_w = 0xc8
+ final val jsr_w = 0xc9
+
+ // reserved opcodes
+ final val breakpoint = 0xca
+ final val impdep1 = 0xfe
+ final val impdep2 = 0xff
+
+ abstract class FlagTranslation {
+ import Flags._
+
+ private var isAnnotation = false
+ private var isClass = false
+ private def initFields(flags: Int) = {
+ isAnnotation = (flags & JAVA_ACC_ANNOTATION) != 0
+ isClass = false
+ }
+
+ private def translateFlag(jflag: Int): FlagSet = (jflag: @switch) match {
+ case JAVA_ACC_PRIVATE => Private
+ case JAVA_ACC_PROTECTED => Protected
+ case JAVA_ACC_FINAL => Final
+ case JAVA_ACC_SYNTHETIC => Synthetic
+ case JAVA_ACC_STATIC => Static
+ case JAVA_ACC_ABSTRACT => if (isAnnotation) EmptyFlags else if (isClass) Abstract else Deferred
+ case JAVA_ACC_INTERFACE => if (isAnnotation) EmptyFlags else JavaInterface
+ case _ => EmptyFlags
+ }
+
+ private def addFlag(base: FlagSet, jflag: Int): FlagSet =
+ if (jflag == 0) base else base | translateFlag(jflag)
+
+ private def translateFlags(jflags: Int, baseFlags: FlagSet): FlagSet = {
+ var res: FlagSet = baseFlags | JavaDefined
+ res = addFlag(res, jflags & JAVA_ACC_PRIVATE)
+ res = addFlag(res, jflags & JAVA_ACC_PROTECTED)
+ res = addFlag(res, jflags & JAVA_ACC_FINAL)
+ res = addFlag(res, jflags & JAVA_ACC_SYNTHETIC)
+ res = addFlag(res, jflags & JAVA_ACC_STATIC)
+ res = addFlag(res, jflags & JAVA_ACC_ABSTRACT)
+ res = addFlag(res, jflags & JAVA_ACC_INTERFACE)
+ res
+ }
+
+ def classFlags(jflags: Int): FlagSet = {
+ initFields(jflags)
+ isClass = true
+ translateFlags(jflags, EmptyFlags)
+ }
+ def fieldFlags(jflags: Int): FlagSet = {
+ initFields(jflags)
+ translateFlags(jflags, if ((jflags & JAVA_ACC_FINAL) == 0) Mutable else EmptyFlags)
+ }
+ def methodFlags(jflags: Int): FlagSet = {
+ initFields(jflags)
+ translateFlags(jflags, if ((jflags & JAVA_ACC_BRIDGE) != 0) Bridge else EmptyFlags)
+ }
+ }
+ object FlagTranslation extends FlagTranslation { }
+}
diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
new file mode 100644
index 000000000..2ece39dbb
--- /dev/null
+++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
@@ -0,0 +1,944 @@
+package dotty.tools
+package dotc
+package core
+package pickling
+
+import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._
+import SymDenotations._, UnPickler._, Constants._, Trees._, Annotations._, Positions._
+import java.io.{ File, IOException }
+import java.lang.Integer.toHexString
+import scala.collection.{ mutable, immutable }
+import scala.collection.mutable.{ ListBuffer, ArrayBuffer }
+import scala.annotation.switch
+import io.AbstractFile
+
+class ClassfileParser(
+ classfile: AbstractFile,
+ classRoot: LazyClassDenotation,
+ moduleRoot: LazyClassDenotation)(implicit cctx: CondensedContext) {
+
+ import ClassfileConstants._
+ import cctx.debuglog
+ import cctx.base.{settings, loaders, definitions => defn}
+
+ protected val in = new AbstractFileReader(classfile)
+
+ protected val staticModule: Symbol = moduleRoot.sourceModule
+
+ protected val instanceScope: Scope = newScope // the scope of all instance definitions
+ protected val staticScope: Scope = newScope // the scope of all static definitions
+ protected var pool: ConstantPool = _ // the classfile's constant pool
+
+ protected var currentClassName: Name = _ // JVM name of the current class
+ protected var classTParams = Map[Name,Symbol]()
+ protected var srcfile0 : Option[AbstractFile] = None //needs freshing out
+
+ def srcfile = srcfile0
+
+ private def currentIsTopLevel = !(classRoot.name contains '$')
+ private val mk = makeTypedTree
+
+ private def handleMissing(e: MissingRequirementError) = {
+ if (settings.debug.value) e.printStackTrace
+ throw new IOException("Missing dependency '" + e.req + "', required by " + in.file)
+ }
+ private def handleError(e: Exception) = {
+ if (settings.debug.value) e.printStackTrace()
+ throw new IOException("class file '%s' is broken\n(%s/%s)".format(
+ in.file,
+ e.getClass,
+ if (e.getMessage eq null) "" else e.getMessage)
+ )
+ }
+ private def mismatchError(c: Symbol) = {
+ throw new IOException("class file '%s' has location not matching its contents: contains ".format(in.file) + c)
+ }
+
+ private def parseErrorHandler[T]: PartialFunction[Throwable, T] = {
+ case e: MissingRequirementError => handleMissing(e)
+ case e: RuntimeException => handleError(e)
+ }
+
+ def run(): Unit = {
+ debuglog("[class] >> " + classRoot.fullName)
+ parseHeader
+ this.pool = new ConstantPool
+ parseClass()
+ }
+
+ private def parseHeader() {
+ val magic = in.nextInt
+ if (magic != JAVA_MAGIC)
+ throw new IOException("class file '" + in.file + "' "
+ + "has wrong magic number 0x" + toHexString(magic)
+ + ", should be 0x" + toHexString(JAVA_MAGIC))
+ val minorVersion = in.nextChar.toInt
+ val majorVersion = in.nextChar.toInt
+ if ((majorVersion < JAVA_MAJOR_VERSION) ||
+ ((majorVersion == JAVA_MAJOR_VERSION) &&
+ (minorVersion < JAVA_MINOR_VERSION)))
+ throw new IOException("class file '" + in.file + "' "
+ + "has unknown version "
+ + majorVersion + "." + minorVersion
+ + ", should be at least "
+ + JAVA_MAJOR_VERSION + "." + JAVA_MINOR_VERSION)
+ }
+
+ class ConstantPool {
+ private val len = in.nextChar
+ private val starts = new Array[Int](len)
+ private val values = new Array[AnyRef](len)
+ private val internalized = new Array[TermName](len)
+
+ { var i = 1
+ while (i < starts.length) {
+ starts(i) = in.bp
+ i += 1
+ (in.nextByte.toInt: @switch) match {
+ case CONSTANT_UTF8 | CONSTANT_UNICODE =>
+ in.skip(in.nextChar)
+ case CONSTANT_CLASS | CONSTANT_STRING =>
+ in.skip(2)
+ case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF
+ | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT =>
+ in.skip(4)
+ case CONSTANT_LONG | CONSTANT_DOUBLE =>
+ in.skip(8)
+ i += 1
+ case _ =>
+ errorBadTag(in.bp - 1)
+ }
+ }
+ }
+
+ /** Return the name found at given index. */
+ def getName(index: Int): TermName = {
+ if (index <= 0 || len <= index)
+ errorBadIndex(index)
+
+ values(index) match {
+ case name: TermName => name
+ case null =>
+ val start = starts(index)
+ if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start)
+ val name = termName(in.buf, start + 3, in.getChar(start + 1))
+ values(index) = name
+ name
+ }
+ }
+
+ /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */
+ def getExternalName(index: Int): TermName = {
+ if (index <= 0 || len <= index)
+ errorBadIndex(index)
+
+ if (internalized(index) == null)
+ internalized(index) = getName(index).replace('/', '.')
+
+ internalized(index)
+ }
+
+ def getClassSymbol(index: Int): Symbol = {
+ if (index <= 0 || len <= index) errorBadIndex(index)
+ var c = values(index).asInstanceOf[Symbol]
+ if (c eq null) {
+ val start = starts(index)
+ if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start)
+ val name = getExternalName(in.getChar(start + 1))
+ if (name.isModuleName) c = cctx.requiredModule(name.stripModuleSuffix.asTermName)
+ else c = classNameToSymbol(name)
+ values(index) = c
+ }
+ c
+ }
+
+ /** Return the external name of the class info structure found at 'index'.
+ * Use 'getClassSymbol' if the class is sure to be a top-level class.
+ */
+ def getClassName(index: Int): TermName = {
+ val start = starts(index)
+ if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start)
+ getExternalName(in.getChar(start + 1))
+ }
+
+ /** Return a name and a type at the given index.
+ */
+ private def getNameAndType(index: Int, ownerTpe: Type): (Name, Type) = {
+ if (index <= 0 || len <= index) errorBadIndex(index)
+ var p = values(index).asInstanceOf[(Name, Type)]
+ if (p eq null) {
+ val start = starts(index)
+ if (in.buf(start).toInt != CONSTANT_NAMEANDTYPE) errorBadTag(start)
+ val name = getName(in.getChar(start + 1).toInt)
+ var tpe = getType(in.getChar(start + 3).toInt)
+ // fix the return type, which is blindly set to the class currently parsed
+ if (name == nme.CONSTRUCTOR)
+ tpe match {
+ case tp: MethodType =>
+ tp.derivedMethodType(tp.paramNames, tp.paramTypes, ownerTpe)
+ }
+ p = (name, tpe)
+ values(index) = p
+ }
+ p
+ }
+
+ /** Return the type of a class constant entry. Since
+ * arrays are considered to be class types, they might
+ * appear as entries in 'newarray' or 'cast' opcodes.
+ */
+ def getClassOrArrayType(index: Int): Type = {
+ if (index <= 0 || len <= index) errorBadIndex(index)
+ val value = values(index)
+ var c: Type = null
+ if (value eq null) {
+ val start = starts(index)
+ if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start)
+ val name = getExternalName(in.getChar(start + 1))
+ if (name(0) == ARRAY_TAG) {
+ c = sigToType(name)
+ values(index) = c
+ } else {
+ val sym = classNameToSymbol(name)
+ values(index) = sym
+ c = sym.typeConstructor
+ }
+ } else c = value match {
+ case tp: Type => tp
+ case cls: Symbol => cls.typeConstructor
+ }
+ c
+ }
+
+ def getType(index: Int): Type =
+ sigToType(getExternalName(index))
+
+ def getSuperClass(index: Int): Symbol =
+ if (index == 0) defn.AnyClass else getClassSymbol(index)
+
+ def getConstant(index: Int): Constant = {
+ if (index <= 0 || len <= index) errorBadIndex(index)
+ var value = values(index)
+ if (value eq null) {
+ val start = starts(index)
+ value = (in.buf(start).toInt: @switch) match {
+ case CONSTANT_STRING =>
+ Constant(getName(in.getChar(start + 1).toInt).toString)
+ case CONSTANT_INTEGER =>
+ Constant(in.getInt(start + 1))
+ case CONSTANT_FLOAT =>
+ Constant(in.getFloat(start + 1))
+ case CONSTANT_LONG =>
+ Constant(in.getLong(start + 1))
+ case CONSTANT_DOUBLE =>
+ Constant(in.getDouble(start + 1))
+ case CONSTANT_CLASS =>
+ getClassOrArrayType(index).typeSymbol
+ case _ =>
+ errorBadTag(start)
+ }
+ values(index) = value
+ }
+ value match {
+ case ct: Constant => ct
+ case cls: Symbol => Constant(cls.typeConstructor)
+ case arr: Type => Constant(arr)
+ }
+ }
+
+ private def getSubArray(bytes: Array[Byte]): Array[Byte] = {
+ val decodedLength = ByteCodecs.decode(bytes)
+ val arr = new Array[Byte](decodedLength)
+ System.arraycopy(bytes, 0, arr, 0, decodedLength)
+ arr
+ }
+
+ def getBytes(index: Int): Array[Byte] = {
+ if (index <= 0 || len <= index) errorBadIndex(index)
+ var value = values(index).asInstanceOf[Array[Byte]]
+ if (value eq null) {
+ val start = starts(index)
+ if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start)
+ val len = in.getChar(start + 1)
+ val bytes = new Array[Byte](len)
+ System.arraycopy(in.buf, start + 3, bytes, 0, len)
+ value = getSubArray(bytes)
+ values(index) = value
+ }
+ value
+ }
+
+ def getBytes(indices: List[Int]): Array[Byte] = {
+ assert(!indices.isEmpty, indices)
+ var value = values(indices.head).asInstanceOf[Array[Byte]]
+ if (value eq null) {
+ val bytesBuffer = ArrayBuffer.empty[Byte]
+ for (index <- indices) {
+ if (index <= 0 || ConstantPool.this.len <= index) errorBadIndex(index)
+ val start = starts(index)
+ if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start)
+ val len = in.getChar(start + 1)
+ bytesBuffer ++= in.buf.view(start + 3, start + 3 + len)
+ }
+ value = getSubArray(bytesBuffer.toArray)
+ values(indices.head) = value
+ }
+ value
+ }
+
+ /** Throws an exception signaling a bad constant index. */
+ private def errorBadIndex(index: Int) =
+ throw new RuntimeException("bad constant pool index: " + index + " at pos: " + in.bp)
+
+ /** Throws an exception signaling a bad tag at given address. */
+ private def errorBadTag(start: Int) =
+ throw new RuntimeException("bad constant pool tag " + in.buf(start) + " at byte " + start)
+ }
+
+ /** Return the class symbol of the given name. */
+ def classNameToSymbol(name: Name): Symbol = innerClasses.get(name) match {
+ case Some(entry) => innerClasses.classSymbol(entry.externalName)
+ case None => cctx.requiredClass(name)
+ }
+
+ var sawPrivateConstructor = false
+
+ def parseClass() {
+ val jflags = in.nextChar
+ val isAnnotation = hasAnnotation(jflags)
+ val sflags = FlagTranslation.classFlags(jflags)
+ val nameIdx = in.nextChar
+ currentClassName = pool.getClassName(nameIdx)
+
+ if (currentIsTopLevel) {
+ val c = pool.getClassSymbol(nameIdx)
+ if (c != classRoot.symbol) mismatchError(c)
+ }
+
+ addEnclosingTParams
+
+ if (unpickleOrParseInnerClasses()) return
+
+ /** Parse parents for Java classes. For Scala, return AnyRef, since the real type will be unpickled.
+ * Updates the read pointer of 'in'. */
+ def parseParents: List[Type] = {
+ val superType = if (isAnnotation) { in.nextChar; defn.AnnotationClass.typeConstructor }
+ else pool.getSuperClass(in.nextChar).typeConstructor
+ val ifaceCount = in.nextChar
+ var ifaces = for (i <- List.range(0, ifaceCount)) yield pool.getSuperClass(in.nextChar).typeConstructor
+ if (isAnnotation) ifaces = defn.ClassfileAnnotationClass.typeConstructor :: ifaces
+ superType :: ifaces
+ }
+
+ var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol) // might be overridden by later parseAttributes
+ val staticInfo = TempClassInfoType(List(), staticScope, moduleRoot.symbol)
+
+ enterOwnInnerClasses
+
+ classRoot.flags = sflags
+ moduleRoot.flags = Flags.JavaDefined
+ setPrivateWithin(classRoot, jflags)
+ setPrivateWithin(moduleRoot, jflags)
+
+ for (i <- 0 until in.nextChar) parseMember(method = false)
+ for (i <- 0 until in.nextChar) parseMember(method = true)
+
+ classInfo = parseAttributes(classRoot.symbol, classInfo)
+ assignClassFields(classRoot, classInfo, classRoot.typeConstructor)
+ assignClassFields(moduleRoot, staticInfo, moduleRoot.typeConstructor)
+ }
+
+ /** Add type parameters of enclosing classes */
+ def addEnclosingTParams {
+ var sym = classRoot.owner
+ while (sym.isClass && !sym.isModuleClass) {
+ for (tparam <- sym.typeParams) {
+ classTParams = classTParams.updated(tparam.name, tparam)
+ }
+ sym = sym.owner
+ }
+ }
+
+ def parseMember(method: Boolean) = {
+ val start = new Offset(in.bp)
+ val jflags = in.nextChar
+ val sflags =
+ if (method) FlagTranslation.methodFlags(jflags)
+ else FlagTranslation.fieldFlags(jflags)
+ val name = pool.getName(in.nextChar)
+ cctx.newLazySymbol(getOwner(jflags), name, sflags, memberCompleter, start).entered
+ }
+
+ object memberCompleter extends SymCompleter {
+ def complete(denot: LazySymDenotation) = {
+ val oldbp = in.bp
+ try {
+ in.bp = denot.symbol.offset.value
+ val sym = denot.symbol
+ val jflags = in.nextChar
+ val isEnum = (jflags & JAVA_ACC_ENUM) != 0
+ val name = pool.getName(in.nextChar)
+ val info = pool.getType(in.nextChar)
+
+ denot.info = if (isEnum) ConstantType(Constant(sym)) else info
+ if (name == nme.CONSTRUCTOR)
+ // if this is a non-static inner class, remove the explicit outer parameter
+ innerClasses.get(currentClassName) match {
+ case Some(entry) if !isStatic(entry.jflags) =>
+ val mt @ MethodType(paramnames, paramtypes) = info
+ denot.info = mt.derivedMethodType(paramnames.tail, paramtypes.tail, mt.resultType)
+
+ }
+ setPrivateWithin(denot, jflags)
+ denot.info = parseAttributes(sym, info)
+
+ if ((denot is Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0)
+ denot.info = arrayToRepeated(denot.info)
+
+ // seal java enums
+ if (isEnum) {
+ val enumClass = sym.owner.linkedClass
+ if (!(enumClass is Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed)
+ enumClass.addAnnotation(Annotation.makeChild(sym))
+ }
+ } finally {
+ in.bp = oldbp
+ }
+ }
+ }
+
+ private def sigToType(sig: TermName, owner: Symbol = null): Type = {
+ var index = 0
+ val end = sig.length
+ def accept(ch: Char) {
+ assert(sig(index) == ch, (sig(index), ch))
+ index += 1
+ }
+ def subName(isDelimiter: Char => Boolean): TermName = {
+ val start = index
+ while (!isDelimiter(sig(index))) { index += 1 }
+ sig.slice(start, index)
+ }
+ def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean): Type = {
+ val tag = sig(index); index += 1
+ (tag: @switch) match {
+ case BYTE_TAG => defn.ByteType
+ case CHAR_TAG => defn.CharType
+ case DOUBLE_TAG => defn.DoubleType
+ case FLOAT_TAG => defn.FloatType
+ case INT_TAG => defn.IntType
+ case LONG_TAG => defn.LongType
+ case SHORT_TAG => defn.ShortType
+ case VOID_TAG => defn.UnitType
+ case BOOL_TAG => defn.BooleanType
+ case 'L' =>
+ def processInner(tp: Type): Type = tp match {
+ case tp @ TypeRef(pre, name) if !tp.symbol.isStatic =>
+ TypeRef(pre.widen, name)
+ case _ =>
+ tp
+ }
+ def processClassType(tp: Type): Type = tp match {
+ case tp @ TypeRef(pre, name) =>
+ if (sig(index) == '<') {
+ accept('<')
+ var tp1: Type = tp
+ var formals = tp.typeParams
+ while (sig(index) != '>') {
+ sig(index) match {
+ case variance @ ('+' | '-' | '*') =>
+ index += 1
+ val bounds = variance match {
+ case '+' => TypeBounds.upper(sig2type(tparams, skiptvs).objToAny)
+ case '-' =>
+ val tp = sig2type(tparams, skiptvs)
+ // sig2type seems to return AnyClass regardless of the situation:
+ // we don't want Any as a LOWER bound.
+ if (tp.typeSymbol == defn.AnyClass) TypeBounds.empty
+ else TypeBounds.lower(tp)
+ case '*' => TypeBounds.empty
+ }
+ tp1 = RefinedType(tp, formals.head.name, bounds)
+ case _ =>
+ tp1 = RefinedType(tp, formals.head.name, TypeAlias(sig2type(tparams, skiptvs)))
+ }
+ formals = formals.tail
+ }
+ accept('>')
+ tp1
+ } else tp
+ case tp =>
+ assert(sig(index) != '<', tp)
+ tp
+ }
+
+ val classSym = classNameToSymbol(subName(c => c == ';' || c == '<'))
+ var tpe = processClassType(processInner(classSym.typeConstructor))
+ while (sig(index) == '.') {
+ accept('.')
+ val name = subName(c => c == ';' || c == '<' || c == '.').toTypeName
+ val clazz = tpe.member(name).symbol
+ tpe = processClassType(processInner(clazz.typeConstructor))
+ }
+ accept(';')
+ tpe
+ case ARRAY_TAG =>
+ while ('0' <= sig(index) && sig(index) <= '9') index += 1
+ var elemtp = sig2type(tparams, skiptvs)
+ // make unbounded Array[T] where T is a type variable into Ar ray[T with Object]
+ // (this is necessary because such arrays have a representation which is incompatible
+ // with arrays of primitive types.
+ // NOTE that the comparison to Object only works for abstract types bounded by classes that are strict subclasses of Object
+ // if the bound is exactly Object, it will have been converted to Any, and the comparison will fail
+ // see also RestrictJavaArraysMap (when compiling java sources directly)
+ if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< defn.ObjectType)) {
+ elemtp = AndType(elemtp, defn.ObjectType)
+ }
+
+ defn.ArrayType.appliedTo(elemtp)
+ case '(' =>
+ // we need a method symbol. given in line 486 by calling getType(methodSym, ..)
+ val paramtypes = new ListBuffer[Type]()
+ var paramnames = new ListBuffer[TermName]()
+ while (sig(index) != ')') {
+ paramnames += nme.syntheticParamName(paramtypes.length)
+ paramtypes += sig2type(tparams, skiptvs).objToAny
+ }
+ index += 1
+ val restype = sig2type(tparams, skiptvs)
+ JavaMethodType(paramnames.toList, paramtypes.toList)(_ => restype)
+ case 'T' =>
+ val n = subName(';'.==).toTypeName
+ index += 1
+ if (skiptvs) defn.AnyType else tparams(n).typeConstructor
+ }
+ } // sig2type(tparams, skiptvs)
+
+ def sig2typeBounds(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean): Type = {
+ val ts = new ListBuffer[Type]
+ while (sig(index) == ':') {
+ index += 1
+ if (sig(index) != ':') // guard against empty class bound
+ ts += sig2type(tparams, skiptvs).objToAny
+ }
+ TypeBounds.upper((defn.AnyType /: ts)(_ & _))
+ }
+
+ var tparams = classTParams
+
+ class TypeParamCompleter(start: Int) extends SymCompleter {
+ override def complete(denot: LazySymDenotation): Unit = {
+ val savedIndex = index
+ try {
+ index = start
+ denot.info = sig2typeBounds(tparams, skiptvs = false)
+ } finally {
+ index = savedIndex
+ }
+ }
+ }
+
+ val newTParams = new ListBuffer[Symbol]()
+ if (sig(index) == '<') {
+ assert(owner != null)
+ index += 1
+ val start = index
+ while (sig(index) != '>') {
+ val tpname = subName(':'.==).toTypeName
+ val s = cctx.newLazySymbol(
+ owner, tpname, Flags.TypeParam, new TypeParamCompleter(index), new Offset(index))
+ tparams = tparams + (tpname -> s)
+ sig2typeBounds(tparams, skiptvs = true)
+ newTParams += s
+ }
+ }
+ val ownTypeParams = newTParams.toList
+ val tpe =
+ if ((owner == null) || !owner.isClass)
+ sig2type(tparams, skiptvs = false)
+ else {
+ classTParams = tparams
+ val parents = new ListBuffer[Type]()
+ while (index < end) {
+ parents += sig2type(tparams, skiptvs = false) // here the variance doesnt'matter
+ }
+ TempClassInfoType(parents.toList, instanceScope, owner)
+ }
+ if (ownTypeParams.isEmpty) tpe else TempPolyType(ownTypeParams, tpe)
+ } // sigToType
+
+ def parseAnnotArg(skip: Boolean = false): Option[TypedTree] = {
+ val tag = in.nextByte.toChar
+ val index = in.nextChar
+ tag match {
+ case STRING_TAG =>
+ if (skip) None else Some(makeLiteralAnnotArg(Constant(pool.getName(index).toString)))
+ case BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG | INT_TAG |
+ LONG_TAG | FLOAT_TAG | DOUBLE_TAG =>
+ if (skip) None else Some(makeLiteralAnnotArg(pool.getConstant(index)))
+ case CLASS_TAG =>
+ if (skip) None else Some(makeLiteralAnnotArg(Constant(pool.getType(index))))
+ case ENUM_TAG =>
+ val t = pool.getType(index)
+ val n = pool.getName(in.nextChar)
+ val s = t.typeSymbol.companionModule.info.decls.lookup(n)
+ assert(s != NoSymbol, t)
+ if (skip) None else Some(makeLiteralAnnotArg(Constant(s)))
+ case ARRAY_TAG =>
+ val arr = new ArrayBuffer[TypedTree]()
+ var hasError = false
+ for (i <- 0 until index)
+ parseAnnotArg(skip) match {
+ case Some(c) => arr += c
+ case None => hasError = true
+ }
+ if (hasError) None
+ else if (skip) None else Some(makeArrayAnnotArg(arr.toArray))
+ case ANNOTATION_TAG =>
+ parseAnnotation(index, skip) map makeNestedAnnotArg
+ }
+ }
+
+ /** Parse and return a single annotation. If it is malformed,
+ * return None.
+ */
+ def parseAnnotation(attrNameIndex: Char, skip: Boolean = false): Option[Annotation] = try {
+ val attrType = pool.getType(attrNameIndex)
+ val nargs = in.nextChar
+ val argbuf = new ListBuffer[TypedTree]
+ var hasError = false
+ for (i <- 0 until nargs) {
+ val name = pool.getName(in.nextChar)
+ parseAnnotArg(skip) match {
+ case Some(arg) => argbuf += mk.NamedArg(name, arg)
+ case None => hasError = !skip
+ }
+ }
+ if (hasError || skip) None
+ else Some(Annotation(attrType, argbuf.toList))
+ } catch {
+ case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found
+ case ex: Throwable =>
+ // We want to be robust when annotations are unavailable, so the very least
+ // we can do is warn the user about the exception
+ // There was a reference to ticket 1135, but that is outdated: a reference to a class not on
+ // the classpath would *not* end up here. A class not found is signaled
+ // with a `FatalError` exception, handled above. Here you'd end up after a NPE (for example),
+ // and that should never be swallowed silently.
+ cctx.warning("Caught: " + ex + " while parsing annotations in " + in.file)
+ if (settings.debug.value) ex.printStackTrace()
+
+ None // ignore malformed annotations
+ }
+
+ def parseAttributes(sym: Symbol, symtype: Type): Type = {
+ def convertTo(c: Constant, pt: Type): Constant = {
+ if (pt.typeSymbol == defn.BooleanClass && c.tag == IntTag)
+ Constant(c.value != 0)
+ else
+ c convertTo pt
+ }
+ var newType = symtype
+
+ def parseAttribute() {
+ val attrName = pool.getName(in.nextChar).toTypeName
+ val attrLen = in.nextInt
+ val end = in.bp + attrLen
+ attrName match {
+ case tpnme.SignatureATTR =>
+ val sig = pool.getExternalName(in.nextChar)
+ newType = sigToType(sig, sym)
+ if (settings.debug.value && settings.verbose.value)
+ println("" + sym + "; signature = " + sig + " type = " + newType)
+ case tpnme.SyntheticATTR =>
+ sym.setFlag(Flags.SyntheticArtifact)
+ case tpnme.BridgeATTR =>
+ sym.setFlag(Flags.Bridge)
+ case tpnme.DeprecatedATTR =>
+ val msg = mk.Literal(Constant("see corresponding Javadoc for more information."))
+ val since = mk.Literal(Constant(""))
+ sym.addAnnotation(Annotation(defn.DeprecatedAnnot, msg, since))
+ case tpnme.ConstantValueATTR =>
+ val c = pool.getConstant(in.nextChar)
+ val c1 = convertTo(c, symtype)
+ if (c1 ne null) newType = ConstantType(c1)
+ else println("failure to convert " + c + " to " + symtype); //debug
+ case tpnme.AnnotationDefaultATTR =>
+ sym.addAnnotation(Annotation(defn.AnnotationDefaultAnnot, Nil))
+ // Java annotations on classes / methods / fields with RetentionPolicy.RUNTIME
+ case tpnme.RuntimeAnnotationATTR =>
+ parseAnnotations(attrLen)
+
+ // TODO 1: parse runtime visible annotations on parameters
+ // case tpnme.RuntimeParamAnnotationATTR
+
+ // TODO 2: also parse RuntimeInvisibleAnnotation / RuntimeInvisibleParamAnnotation,
+ // i.e. java annotations with RetentionPolicy.CLASS?
+
+ case tpnme.ExceptionsATTR =>
+ parseExceptions(attrLen)
+
+ case tpnme.SourceFileATTR =>
+ val srcfileLeaf = pool.getName(in.nextChar).toString.trim
+ val pkg = sym.enclosingPackage
+ val srcpath =
+ if (pkg.isEffectiveRoot) srcfileLeaf
+ else pkg.fullName(File.separatorChar) + File.separator + srcfileLeaf
+
+ // !!! srcfile0 = settings.outputDirs.srcFilesFor(in.file, srcpath).find(_.exists)
+ case _ =>
+ }
+ in.bp = end
+ }
+
+ /**
+ * Parse the "Exceptions" attribute which denotes the exceptions
+ * thrown by a method.
+ */
+ def parseExceptions(len: Int) {
+ val nClasses = in.nextChar
+ for (n <- 0 until nClasses) {
+ // FIXME: this performs an equivalent of getExceptionTypes instead of getGenericExceptionTypes (SI-7065)
+ val cls = pool.getClassSymbol(in.nextChar.toInt)
+ sym.addAnnotation(ThrowsAnnotation(cls.asClass))
+ }
+ }
+
+ /** Parse a sequence of annotations and attaches them to the
+ * current symbol sym, except for the ScalaSignature annotation that it returns, if it is available. */
+ def parseAnnotations(len: Int): Unit = {
+ val nAttr = in.nextChar
+ for (n <- 0 until nAttr)
+ parseAnnotation(in.nextChar) match {
+ case Some(annot) =>
+ sym.addAnnotation(annot)
+ case None =>
+ }
+ }
+
+ // begin parseAttributes
+ for (i <- 0 until in.nextChar) {
+ parseAttribute()
+ }
+ newType
+ }
+
+ /** Enter own inner classes in the right scope. It needs the scopes to be set up,
+ * and implicitly current class' superclasses.
+ */
+ private def enterOwnInnerClasses() {
+ def className(name: Name): Name = name.drop(name.lastIndexOf('.') + 1)
+
+ def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile, jflags: Int) =
+ loaders.enterClassAndModule(
+ getOwner(jflags),
+ entry.originalName,
+ new ClassfileLoader(file)(cctx),
+ FlagTranslation.classFlags(jflags))
+
+ for (entry <- innerClasses.values) {
+ // create a new class member for immediate inner classes
+ if (entry.outerName == currentClassName) {
+ val file = cctx.platform.classPath.findSourceFile(entry.externalName.toString) getOrElse {
+ throw new AssertionError(entry.externalName)
+ }
+ enterClassAndModule(entry, file, entry.jflags)
+ }
+ }
+ }
+
+ /** Parse inner classes. Expects `in.bp` to point to the superclass entry.
+ * Restores the old `bp`.
+ * @return true iff classfile is from Scala, so no Java info needs to be read.
+ */
+ def unpickleOrParseInnerClasses(): Boolean = {
+ val oldbp = in.bp
+ try {
+ skipSuperclasses()
+ skipMembers() // fields
+ skipMembers() // methods
+ val attrs = in.nextChar
+ val attrbp = in.bp
+
+ def scan(target: TypeName): Boolean = {
+ in.bp = attrbp
+ var i = 0
+ while (i < attrs && pool.getName(in.nextChar).toTypeName != target) {
+ val attrLen = in.nextInt
+ in.skip(attrLen)
+ i += 1
+ }
+ i < attrs
+ }
+
+ def unpickle(bytes: Array[Byte]): Boolean = {
+ new UnPickler(bytes, classRoot, moduleRoot).run()
+ true
+ }
+
+ def parseScalaSigBytes: Array[Byte] = {
+ val tag = in.nextByte.toChar
+ assert(tag == STRING_TAG, tag)
+ pool getBytes in.nextChar
+ }
+
+ def parseScalaLongSigBytes: Array[Byte] = {
+ val tag = in.nextByte.toChar
+ assert(tag == ARRAY_TAG, tag)
+ val stringCount = in.nextChar
+ val entries =
+ for (i <- 0 until stringCount) yield {
+ val stag = in.nextByte.toChar
+ assert(stag == STRING_TAG, stag)
+ in.nextChar.toInt
+ }
+ pool.getBytes(entries.toList)
+ }
+
+ if (scan(tpnme.RuntimeAnnotationATTR)) {
+ val attrLen = in.nextInt
+ val nAnnots = in.nextChar
+ var i = 0
+ while (i < nAnnots) {
+ val attrClass = pool.getType(in.nextChar).typeSymbol
+ val nArgs = in.nextChar
+ var j = 0
+ while (j < nArgs) {
+ val argName = pool.getName(in.nextChar)
+ if (attrClass == defn.ScalaSignatureAnnot && argName == nme.bytes)
+ return unpickle(parseScalaSigBytes)
+ else if (attrClass == defn.ScalaLongSignatureAnnot && argName == nme.bytes)
+ return unpickle(parseScalaLongSigBytes)
+ else
+ parseAnnotArg(skip = true)
+ j += 1
+ }
+ i += 1
+ }
+ }
+
+ if (scan(tpnme.InnerClassesATTR)) {
+ val entries = in.nextChar.toInt
+ for (i <- 0 until entries) {
+ val innerIndex = in.nextChar
+ val outerIndex = in.nextChar
+ val nameIndex = in.nextChar
+ val jflags = in.nextChar
+ if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) {
+ val entry = InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags)
+ innerClasses(pool.getClassName(innerIndex)) = entry
+ }
+ }
+ }
+ false
+ } finally in.bp = oldbp
+ }
+
+ /** An entry in the InnerClasses attribute of this class file. */
+ case class InnerClassEntry(external: Int, outer: Int, name: Int, jflags: Int) {
+ def externalName = pool.getClassName(external)
+ def outerName = pool.getClassName(outer)
+ def originalName = pool.getName(name)
+
+ override def toString =
+ originalName + " in " + outerName + "(" + externalName +")"
+ }
+
+ object innerClasses extends scala.collection.mutable.HashMap[Name, InnerClassEntry] {
+ /** Return the Symbol of the top level class enclosing `name`,
+ * or 'name's symbol if no entry found for `name`.
+ */
+ def topLevelClass(name: Name): Symbol = {
+ val tlName = if (isDefinedAt(name)) {
+ var entry = this(name)
+ while (isDefinedAt(entry.outerName))
+ entry = this(entry.outerName)
+ entry.outerName
+ } else
+ name
+ classNameToSymbol(tlName)
+ }
+
+ /** Return the class symbol for `externalName`. It looks it up in its outer class.
+ * Forces all outer class symbols to be completed.
+ *
+ * If the given name is not an inner class, it returns the symbol found in `defn`.
+ */
+ def classSymbol(externalName: Name): Symbol = {
+ /** Return the symbol of `innerName`, having the given `externalName`. */
+ def innerSymbol(externalName: Name, innerName: Name, static: Boolean): Symbol = {
+ def getMember(sym: Symbol, name: Name): Symbol =
+ if (static)
+ if (sym == classRoot.symbol) staticScope.lookup(name)
+ else sym.companionModule.info.member(name).symbol
+ else
+ if (sym == classRoot.symbol) instanceScope.lookup(name)
+ else sym.info.member(name).symbol
+
+ innerClasses.get(externalName) match {
+ case Some(entry) =>
+ val outerName = entry.outerName.stripModuleSuffix
+ val owner = classSymbol(outerName)
+ val result = cctx.beforeTyper(getMember(owner, innerName.toTypeName))
+ assert(result ne NoSymbol,
+ s"""failure to resolve inner class:
+ |externalName = $externalName,
+ |outerName = $outerName,
+ |innerName = $innerName
+ |owner.fullName = owner.showFullName
+ |while parsing ${classfile}""".stripMargin)
+ result
+
+ case None =>
+ classNameToSymbol(externalName)
+ }
+ }
+
+ get(externalName) match {
+ case Some(entry) =>
+ innerSymbol(entry.externalName, entry.originalName, isStatic(entry.jflags))
+ case None =>
+ classNameToSymbol(externalName)
+ }
+ }
+ }
+
+ def skipAttributes() {
+ val attrCount = in.nextChar
+ for (i <- 0 until attrCount) {
+ in.skip(2); in.skip(in.nextInt)
+ }
+ }
+
+ def skipMembers() {
+ val memberCount = in.nextChar
+ for (i <- 0 until memberCount) {
+ in.skip(6); skipAttributes()
+ }
+ }
+
+ def skipSuperclasses() {
+ in.skip(2) // superclass
+ val ifaces = in.nextChar
+ in.skip(2 * ifaces)
+ }
+
+ protected def getOwner(flags: Int): Symbol =
+ if (isStatic(flags)) moduleRoot.symbol else classRoot.symbol
+
+ protected def getScope(flags: Int): Scope =
+ if (isStatic(flags)) staticScope else instanceScope
+
+ private def setPrivateWithin(denot: isLazy[_], jflags: Int) {
+ if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PUBLIC)) == 0)
+ // See ticket #1687 for an example of when topLevelClass is NoSymbol: it
+ // apparently occurs when processing v45.3 bytecode.
+ if (denot.topLevelClass != NoSymbol)
+ denot.privateWithin = denot.topLevelClass.owner
+ }
+
+ private def isPrivate(flags: Int) = (flags & JAVA_ACC_PRIVATE) != 0
+ private def isStatic(flags: Int) = (flags & JAVA_ACC_STATIC) != 0
+ private def hasAnnotation(flags: Int) = (flags & JAVA_ACC_ANNOTATION) != 0
+}
+
diff --git a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala
index b291b8545..9e58c5491 100644
--- a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala
+++ b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala
@@ -167,22 +167,4 @@ class PickleBuffer(data: Array[Byte], from: Int, to: Int) {
if (n == 0) List() else op() :: times(n-1, op)
def unpickleScalaFlags(sflags: Long): FlagSet = ???
-
- /** Pickle = majorVersion_Nat minorVersion_Nat nbEntries_Nat {Entry}
- * Entry = type_Nat length_Nat [actual entries]
- *
- * Assumes that the ..Version_Nat are already consumed.
- *
- * @return an array mapping entry numbers to locations in
- * the byte array where the entries start.
- */
- def createIndex: Array[Int] = {
- val index = new Array[Int](readNat()) // nbEntries_Nat
- for (i <- 0 until index.length) {
- index(i) = readIndex
- readByte() // skip type_Nat
- readIndex = readNat() + readIndex // read length_Nat, jump to next entry
- }
- index
- }
}
diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala
index 0183718e4..6a4dff9e2 100644
--- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala
+++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala
@@ -9,66 +9,97 @@ import java.lang.Double.longBitsToDouble
import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._
import StdNames._, Denotations._, NameOps._, Flags._, Constants._, Annotations._
-import Trees._
+import Trees._, Positions._
+import io.AbstractFile
import scala.reflect.internal.pickling.PickleFormat._
import scala.collection.{ mutable, immutable }
import scala.collection.mutable.ListBuffer
import scala.annotation.switch
-/** @author Martin Odersky
- * @version 1.0
- */
-abstract class UnPickler {
-
- /** Unpickle symbol table information descending from a class and/or module root
- * from an array of bytes.
- * @param bytes bytearray from which we unpickle
- * @param offset offset from which unpickling starts
- * @param classroot the top-level class which is unpickled, or NoSymbol if inapplicable
- * @param moduleroot the top-level module class which is unpickled, or NoSymbol if inapplicable
- * @param filename filename associated with bytearray, only used for error messages
+object UnPickler {
+
+ case class TempPolyType(tparams: List[Symbol], tpe: Type) extends UncachedGroundType
+
+ /** Temporary type for classinfos, will be decomposed on completion of the class */
+ case class TempClassInfoType(parentTypes: List[Type], decls: Scope, clazz: Symbol) extends UncachedGroundType
+
+ def depoly(tp: Type)(implicit ctx: Context): Type = tp match {
+ case TempPolyType(tparams, restpe) => PolyType.fromSymbols(tparams, restpe)
+ case tp => tp
+ }
+
+ /** Convert array parameters denoting a repeated parameter of a Java method
+ * to `JavaRepeatedParamClass` types.
*/
- def unpickle(bytes: Array[Byte], offset: Int, classRoot: Symbol, moduleRoot: Symbol, filename: String)(implicit ctx: Context) {
- try {
- new Scan(bytes, offset, classRoot, moduleRoot, filename).run()
- } catch {
- case ex: IOException =>
- throw ex
- case ex: MissingRequirementError =>
- throw ex
- case ex: Throwable =>
- /*if (settings.debug.value)*/ ex.printStackTrace()
- throw new RuntimeException("error reading Scala signature of "+filename+": "+ex.getMessage())
+ def arrayToRepeated(tp: Type)(implicit ctx: Context): Type = tp match {
+ case tp @ MethodType(paramNames, paramTypes) =>
+ val (tycon, elemtp0 :: Nil) = paramTypes.last.splitArgs
+ assert(tycon.typeSymbol == defn.ArrayClass, tp)
+ val elemtp = elemtp0 match {
+ case AndType(t1, t2) if t1.typeSymbol.isAbstractType && t2.typeSymbol == defn.ObjectClass =>
+ t1 // drop intersection with Object for abstract types in varargs. UnCurry can handle them.
+ case _ =>
+ elemtp0
+ }
+ tp.derivedMethodType(
+ paramNames,
+ paramTypes.init :+ defn.JavaRepeatedParamType.appliedTo(elemtp),
+ tp.resultType)
+ case tp @ PolyType(paramNames) =>
+ tp.derivedPolyType(paramNames, tp.paramBounds, arrayToRepeated(tp.resultType))
+ }
+
+ def assignClassFields(denot: LazyClassDenotation, info: Type, selfType: Type)(implicit ctx: Context): Unit = {
+ val cls = denot.symbol
+ val (tparams, TempClassInfoType(parents, decls, clazz)) = info match {
+ case TempPolyType(tps, cinfo) => (tps, cinfo)
+ case cinfo => (Nil, cinfo)
}
+ tparams foreach decls.enter
+ denot.parents = ctx.normalizeToRefs(parents, cls, decls)
+ denot.selfType = selfType
+ denot.decls = decls
}
+}
+
+/** Unpickle symbol table information descending from a class and/or module root
+ * from an array of bytes.
+ * @param bytes bytearray from which we unpickle
+ * @param offset offset from which unpickling starts
+ * @param classroot the top-level class which is unpickled, or NoSymbol if inapplicable
+ * @param moduleroot the top-level module class which is unpickled, or NoSymbol if inapplicable
+ * @param filename filename associated with bytearray, only used for error messages
+ */
+class UnPickler(bytes: Array[Byte], classRoot: LazyClassDenotation, moduleRoot: LazyClassDenotation)(implicit cctx: CondensedContext)
+ extends PickleBuffer(bytes, 0, -1) {
- class Scan(_bytes: Array[Byte], offset: Int, classRoot: Symbol, moduleRoot: Symbol, filename: String)(implicit ctx: Context) extends PickleBuffer(_bytes, offset, -1) {
- //println("unpickle " + classRoot + " and " + moduleRoot)//debug
+ import UnPickler._
- protected def debug = ctx.settings.debug.value
+ protected def debug = cctx.settings.debug.value
- checkVersion()
+ checkVersion()
- private val loadingMirror = defn // was: mirrorThatLoaded(classRoot)
+ private val loadingMirror = defn // was: mirrorThatLoaded(classRoot)
- /** A map from entry numbers to array offsets */
- private val index = createIndex
+ /** A map from entry numbers to array offsets */
+ private val index = createIndex
- /** A map from entry numbers to symbols, types, or annotations */
- private val entries = new Array[AnyRef](index.length)
+ /** A map from entry numbers to symbols, types, or annotations */
+ private val entries = new Array[AnyRef](index.length)
- /** A map from symbols to their associated `decls` scopes */
- private val symScopes = mutable.HashMap[Symbol, Scope]()
+ /** A map from symbols to their associated `decls` scopes */
+ private val symScopes = mutable.HashMap[Symbol, Scope]()
- /** A map from refinement classes to their associated refinement types */
- private val refinementTypes = mutable.HashMap[Symbol, RefinedType]()
+ /** A map from refinement classes to their associated refinement types */
+ private val refinementTypes = mutable.HashMap[Symbol, RefinedType]()
- private val mk = makeTypedTree
+ private val mk = makeTypedTree
- //println("unpickled " + classRoot + ":" + classRoot.rawInfo + ", " + moduleRoot + ":" + moduleRoot.rawInfo);//debug
+ //println("unpickled " + classRoot + ":" + classRoot.rawInfo + ", " + moduleRoot + ":" + moduleRoot.rawInfo);//debug
- // Laboriously unrolled for performance.
- def run() {
+ // Laboriously unrolled for performance.
+ def run() =
+ try {
var i = 0
while (i < index.length) {
if (entries(i) == null && isSymbolEntry(i)) {
@@ -88,8 +119,7 @@ abstract class UnPickler {
readIndex = index(i)
readSymbolAnnotation()
readIndex = savedIndex
- }
- else if (isChildrenEntry(i)) {
+ } else if (isChildrenEntry(i)) {
val savedIndex = readIndex
readIndex = index(i)
readChildren()
@@ -98,533 +128,582 @@ abstract class UnPickler {
}
i += 1
}
+ } catch {
+ case ex: IOException =>
+ throw ex
+ case ex: MissingRequirementError =>
+ throw ex
+ case ex: Throwable =>
+ /*if (settings.debug.value)*/ ex.printStackTrace()
+ throw new RuntimeException("error reading Scala signature of " + source + ": " + ex.getMessage())
}
- private def checkVersion() {
- val major = readNat()
- val minor = readNat()
- if (major != MajorVersion || minor > MinorVersion)
- throw new IOException("Scala signature " + classRoot.fullName.decode +
- " has wrong version\n expected: " +
- MajorVersion + "." + MinorVersion +
- "\n found: " + major + "." + minor +
- " in "+filename)
- }
-
- /** The `decls` scope associated with given symbol */
- protected def symScope(sym: Symbol) = symScopes.getOrElseUpdate(sym, newScope)
+ def source: AbstractFile = {
+ val f = classRoot.associatedFile
+ if (f != null) f else moduleRoot.associatedFile
+ }
- /** Does entry represent an (internal) symbol */
- protected def isSymbolEntry(i: Int): Boolean = {
- val tag = bytes(index(i)).toInt
- (firstSymTag <= tag && tag <= lastSymTag &&
- (tag != CLASSsym || !isRefinementSymbolEntry(i)))
- }
+ private def checkVersion() {
+ val major = readNat()
+ val minor = readNat()
+ if (major != MajorVersion || minor > MinorVersion)
+ throw new IOException("Scala signature " + classRoot.fullName.decode +
+ " has wrong version\n expected: " +
+ MajorVersion + "." + MinorVersion +
+ "\n found: " + major + "." + minor +
+ " in " + source)
+ }
- /** Does entry represent an (internal or external) symbol */
- protected def isSymbolRef(i: Int): Boolean = {
- val tag = bytes(index(i))
- (firstSymTag <= tag && tag <= lastExtSymTag)
+ /** Pickle = majorVersion_Nat minorVersion_Nat nbEntries_Nat {Entry}
+ * Entry = type_Nat length_Nat [actual entries]
+ *
+ * Assumes that the ..Version_Nat are already consumed.
+ *
+ * @return an array mapping entry numbers to locations in
+ * the byte array where the entries start.
+ */
+ def createIndex: Array[Int] = {
+ val index = new Array[Int](readNat()) // nbEntries_Nat
+ for (i <- 0 until index.length) {
+ index(i) = readIndex
+ readByte() // skip type_Nat
+ readIndex = readNat() + readIndex // read length_Nat, jump to next entry
}
+ index
+ }
- /** Does entry represent a name? */
- protected def isNameEntry(i: Int): Boolean = {
- val tag = bytes(index(i)).toInt
- tag == TERMname || tag == TYPEname
- }
+ /** The `decls` scope associated with given symbol */
+ protected def symScope(sym: Symbol) = symScopes.getOrElseUpdate(sym, newScope)
- /** Does entry represent a symbol annotation? */
- protected def isSymbolAnnotationEntry(i: Int): Boolean = {
- val tag = bytes(index(i)).toInt
- tag == SYMANNOT
- }
+ /** Does entry represent an (internal) symbol */
+ protected def isSymbolEntry(i: Int): Boolean = {
+ val tag = bytes(index(i)).toInt
+ (firstSymTag <= tag && tag <= lastSymTag &&
+ (tag != CLASSsym || !isRefinementSymbolEntry(i)))
+ }
- /** Does the entry represent children of a symbol? */
- protected def isChildrenEntry(i: Int): Boolean = {
- val tag = bytes(index(i)).toInt
- tag == CHILDREN
- }
+ /** Does entry represent an (internal or external) symbol */
+ protected def isSymbolRef(i: Int): Boolean = {
+ val tag = bytes(index(i))
+ (firstSymTag <= tag && tag <= lastExtSymTag)
+ }
- /** Does entry represent a refinement symbol?
- * pre: Entry is a class symbol
- */
- protected def isRefinementSymbolEntry(i: Int): Boolean = {
- val savedIndex = readIndex
- readIndex = index(i)
- val tag = readByte().toInt
- assert(tag == CLASSsym)
+ /** Does entry represent a name? */
+ protected def isNameEntry(i: Int): Boolean = {
+ val tag = bytes(index(i)).toInt
+ tag == TERMname || tag == TYPEname
+ }
- readNat(); // read length
- val result = readNameRef() == tpnme.REFINE_CLASS
- readIndex = savedIndex
- result
- }
+ /** Does entry represent a symbol annotation? */
+ protected def isSymbolAnnotationEntry(i: Int): Boolean = {
+ val tag = bytes(index(i)).toInt
+ tag == SYMANNOT
+ }
- protected def isRefinementClass(sym: Symbol): Boolean =
- sym.name == tpnme.REFINE_CLASS
+ /** Does the entry represent children of a symbol? */
+ protected def isChildrenEntry(i: Int): Boolean = {
+ val tag = bytes(index(i)).toInt
+ tag == CHILDREN
+ }
- protected def isLocal(sym: Symbol) = {
- val root = sym.topLevelClass
- sym == moduleRoot || sym == classRoot
- }
+ /** Does entry represent a refinement symbol?
+ * pre: Entry is a class symbol
+ */
+ protected def isRefinementSymbolEntry(i: Int): Boolean = {
+ val savedIndex = readIndex
+ readIndex = index(i)
+ val tag = readByte().toInt
+ assert(tag == CLASSsym)
+
+ readNat(); // read length
+ val result = readNameRef() == tpnme.REFINE_CLASS
+ readIndex = savedIndex
+ result
+ }
- /** If entry at <code>i</code> is undefined, define it by performing
- * operation <code>op</code> with <code>readIndex at start of i'th
- * entry. Restore <code>readIndex</code> afterwards.
- */
- protected def at[T <: AnyRef](i: Int, op: () => T): T = {
- var r = entries(i)
- if (r eq null) {
- val savedIndex = readIndex
- readIndex = index(i)
- r = op()
- assert(entries(i) eq null, entries(i))
- entries(i) = r
- readIndex = savedIndex
- }
- r.asInstanceOf[T]
- }
+ protected def isRefinementClass(sym: Symbol): Boolean =
+ sym.name == tpnme.REFINE_CLASS
- /** Read a name */
- protected def readName(): Name = {
- val tag = readByte()
- val len = readNat()
- tag match {
- case TERMname => termName(bytes, readIndex, len)
- case TYPEname => typeName(bytes, readIndex, len)
- case _ => errorBadSignature("bad name tag: " + tag)
- }
- }
- protected def readTermName(): TermName = readName().toTermName
- protected def readTypeName(): TypeName = readName().toTypeName
+ protected def isLocal(sym: Symbol) = isUnpickleRoot(sym.topLevelClass)
- /** Read a symbol */
- protected def readSymbol(): Symbol = readDisambiguatedSymbol(Function.const(true))()
+ protected def isUnpickleRoot(sym: Symbol) = {
+ val d = sym.denot
+ d == moduleRoot || d == classRoot
+ }
- /** Read a symbol, with possible disambiguation */
- protected def readDisambiguatedSymbol(disambiguate: Symbol => Boolean)(): Symbol = {
- val tag = readByte()
- val end = readNat() + readIndex
- def atEnd = readIndex == end
+ /** If entry at <code>i</code> is undefined, define it by performing
+ * operation <code>op</code> with <code>readIndex at start of i'th
+ * entry. Restore <code>readIndex</code> afterwards.
+ */
+ protected def at[T <: AnyRef](i: Int, op: () => T): T = {
+ var r = entries(i)
+ if (r eq null) {
+ r = atReadPos(index(i), op)
+ assert(entries(i) eq null, entries(i))
+ entries(i) = r
+ }
+ r.asInstanceOf[T]
+ }
- def readExtSymbol(): Symbol = {
- val name = readNameRef()
- val owner = if (atEnd) loadingMirror.RootClass else readSymbolRef()
+ protected def atReadPos[T](start: Int, op: () => T): T = {
+ val savedIndex = readIndex
+ readIndex = start
+ try op()
+ finally readIndex = savedIndex
+ }
- def adjust(denot: Denotation) = {
- val denot1 = if (denot.isOverloaded) denot filter disambiguate else denot
- if (denot1.isOverloaded)
- throw new TypeError(s"failure to disambiguate overloaded reference $denot1")
- val sym = denot1.symbol
- if (tag == EXTref) sym else sym.moduleClass
- }
+ /** Read a name */
+ protected def readName(): Name = {
+ val tag = readByte()
+ val len = readNat()
+ tag match {
+ case TERMname => termName(bytes, readIndex, len)
+ case TYPEname => typeName(bytes, readIndex, len)
+ case _ => errorBadSignature("bad name tag: " + tag)
+ }
+ }
+ protected def readTermName(): TermName = readName().toTermName
+ protected def readTypeName(): TypeName = readName().toTypeName
+
+ /** Read a symbol */
+ protected def readSymbol(): Symbol = readDisambiguatedSymbol(Function.const(true))()
+
+ /** Read a symbol, with possible disambiguation */
+ protected def readDisambiguatedSymbol(p: Symbol => Boolean)(): Symbol = {
+ val start = new Offset(readIndex)
+ val tag = readByte()
+ val end = readNat() + readIndex
+ def atEnd = readIndex == end
+
+ def readExtSymbol(): Symbol = {
+ val name = readNameRef()
+ val owner = if (atEnd) loadingMirror.RootClass else readSymbolRef()
+
+ def adjust(denot: Denotation) = {
+ val denot1 = denot.disambiguate(p)
+ val sym = denot1.symbol
+ if (tag == EXTref) sym else sym.moduleClass
+ }
- def fromName(name: Name): Symbol = name.toTermName match {
- case nme.ROOT => loadingMirror.RootClass
- case nme.ROOTPKG => loadingMirror.RootPackage
- case _ => adjust(owner.info.decl(name))
- }
+ def fromName(name: Name): Symbol = name.toTermName match {
+ case nme.ROOT => loadingMirror.RootClass
+ case nme.ROOTPKG => loadingMirror.RootPackage
+ case _ => adjust(owner.info.decl(name))
+ }
- def nestedObjectSymbol: Symbol = {
- // If the owner is overloaded (i.e. a method), it's not possible to select the
- // right member, so return NoSymbol. This can only happen when unpickling a tree.
- // the "case Apply" in readTree() takes care of selecting the correct alternative
- // after parsing the arguments.
- //if (owner.isOverloaded)
- // return NoSymbol
-
- if (tag == EXTMODCLASSref) {
- ???
-/*
+ def nestedObjectSymbol: Symbol = {
+ // If the owner is overloaded (i.e. a method), it's not possible to select the
+ // right member, so return NoSymbol. This can only happen when unpickling a tree.
+ // the "case Apply" in readTree() takes care of selecting the correct alternative
+ // after parsing the arguments.
+ //if (owner.isOverloaded)
+ // return NoSymbol
+
+ if (tag == EXTMODCLASSref) {
+ ???
+ /*
val moduleVar = owner.info.decl(name.toTermName.moduleVarName).symbol
if (moduleVar.isLazyAccessor)
return moduleVar.lazyAccessor.lazyAccessor
*/
- }
- NoSymbol
}
+ NoSymbol
+ }
- // (1) Try name.
- fromName(name) orElse {
- // (2) Try with expanded name. Can happen if references to private
- // symbols are read from outside: for instance when checking the children
- // of a class. See #1722.
- fromName(name.toTermName.expandedName(owner)) orElse {
- // (3) Try as a nested object symbol.
- nestedObjectSymbol orElse {
-// // (4) Call the mirror's "missing" hook.
-// adjust(mirrorThatLoaded(owner).missingHook(owner, name)) orElse {
-// }
- // (5) Create a stub symbol to defer hard failure a little longer.
- ctx.newStubSymbol(owner, name)
+ // (1) Try name.
+ fromName(name) orElse {
+ // (2) Try with expanded name. Can happen if references to private
+ // symbols are read from outside: for instance when checking the children
+ // of a class. See #1722.
+ fromName(name.toTermName.expandedName(owner)) orElse {
+ // (3) Try as a nested object symbol.
+ nestedObjectSymbol orElse {
+ // // (4) Call the mirror's "missing" hook.
+ adjust(cctx.base.missingHook(owner, name)) orElse {
+ // }
+ // (5) Create a stub symbol to defer hard failure a little longer.
+ cctx.newStubSymbol(owner, name, source)
}
}
}
}
+ }
- tag match {
- case NONEsym => return NoSymbol
- case EXTref | EXTMODCLASSref => return readExtSymbol()
- case _ => ()
- }
+ tag match {
+ case NONEsym => return NoSymbol
+ case EXTref | EXTMODCLASSref => return readExtSymbol()
+ case _ =>
+ }
+
+ // symbols that were pickled with Pickler.writeSymInfo
+ val nameref = readNat()
+ val name = at(nameref, readName)
+ val owner = readSymbolRef()
+ val flags = unpickleScalaFlags(readLongNat())
+
+ def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner)
+ def isModuleRoot = (name.toTermName == moduleRoot.name.toTermName) && (owner == moduleRoot.owner)
+
+ def completeRoot(denot: LazyClassDenotation): Symbol = {
+ atReadPos(start.value, () => completeLocal(denot))
+ denot.symbol
+ }
+
+ def finishSym(sym: Symbol): Symbol = {
+ if (sym.owner.isClass && !(
+ isUnpickleRoot(sym) ||
+ (sym is (ModuleClass | TypeParam | Scala2Existential)) ||
+ isRefinementClass(sym)))
+ symScope(sym.owner) enter sym
+ sym
+ }
+
+ finishSym(tag match {
+ case TYPEsym | ALIASsym =>
+ var name1 = name.asTypeName
+ var flags1 = flags
+ if (flags is TypeParam) {
+ name1 = name1.expandedName(owner)
+ flags1 |= TypeParamFlags
+ }
+ cctx.newLazySymbol(owner, name1, flags1, symUnpickler, off = start)
+ case CLASSsym =>
+ if (isClassRoot) completeRoot(classRoot)
+ else if (isModuleRoot) completeRoot(moduleRoot)
+ else cctx.newLazyClassSymbol(owner, name.asTypeName, flags, classUnpickler, off = start)
+ case MODULEsym | VALsym =>
+ if (isModuleRoot) {
+ moduleRoot.flags = flags
+ moduleRoot.symbol
+ } else cctx.newLazySymbol(owner, name.asTermName, flags, symUnpickler, off = start)
+ case _ =>
+ errorBadSignature("bad symbol tag: " + tag)
+ })
+ }
- // symbols that were pickled with Pickler.writeSymInfo
- val nameref = readNat()
- val name = at(nameref, readName)
- val owner = readSymbolRef()
- val flags = unpickleScalaFlags(readLongNat())
- var inforef = readNat()
- val privateWithin =
+ def completeLocal[D <: SymDenotation](denot: isLazy[D]): Unit = {
+ def parseToCompletion() = {
+ val tag = readByte()
+ val end = readNat() + readIndex
+ def atEnd = readIndex == end
+ val unusedNameref = readNat()
+ val unusedOwnerref = readNat()
+ val unusedFlags = readLongNat()
+ var inforef = readNat()
+ denot.privateWithin =
if (!isSymbolRef(inforef)) NoSymbol
else {
val pw = at(inforef, readSymbol)
inforef = readNat()
pw
}
-
- def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner)
- def isModuleRoot = (name.toTermName == moduleRoot.name.toTermName) && (owner == moduleRoot.owner)
-
- def completeSym(tag: Int): SymCompleter = {
- val aliasRef =
+ val tp = at(inforef, () => readType(forceProperType = denot.isTerm))
+ denot match {
+ case denot: LazySymDenotation =>
+ denot.info = if (tag == ALIASsym) TypeAlias(tp) else depoly(tp)
if (atEnd) {
- assert(!(flags is SuperAccessor), name)
- -1
+ assert(!(denot is SuperAccessor), denot)
} else {
- assert(flags is (SuperAccessor | ParamAccessor), name)
- readNat()
+ assert(denot is (SuperAccessor | ParamAccessor), denot)
+ def disambiguate(alt: Symbol) =
+ denot.info =:= denot.owner.thisType.memberInfo(alt)
+ val aliasRef = readNat()
+ val alias = at(aliasRef, readDisambiguatedSymbol(disambiguate)).asTerm
+ denot.addAnnotation(Annotation.makeAlias(alias))
}
- new SymRestCompleter(tag, inforef, aliasRef)
- }
-
- def completeClass: ClassCompleter = {
- val selfTypeRef = if (atEnd) -1 else readNat()
- new ClassRestCompleter(inforef, selfTypeRef)
- }
-
- def completeRoot(sym: Symbol): Symbol = {
- completeClass.complete(sym.denot.asInstanceOf[LazyClassDenotation])
- sym
+ case denot: LazyClassDenotation =>
+ val selfType = if (atEnd) denot.typeConstructor else readTypeRef()
+ assignClassFields(denot, tp, selfType)
}
+ }
+ try {
+ atReadPos(denot.symbol.offset.value, parseToCompletion)
+ } catch {
+ case e: MissingRequirementError => throw toTypeError(e)
+ }
+ }
- def finishSym(sym: Symbol): Symbol = {
- sym.denot.asInstanceOf[isLazy[_]].privateWithin = privateWithin
- if (sym.owner.isClass && !(
- sym == classRoot ||
- sym == moduleRoot ||
- (sym is (ModuleClass | TypeParam | Scala2Existential)) ||
- isRefinementClass(sym)))
- symScope(sym.owner) enter sym
- sym
- }
+ private object symUnpickler extends SymCompleter {
+ override def complete(denot: LazySymDenotation): Unit = completeLocal(denot)
+ }
- finishSym(tag match {
- case TYPEsym | ALIASsym =>
- var name1 = name.asTypeName
- var flags1 = flags
- if (flags is TypeParam) {
- name1 = name1.expandedName(owner)
- flags1 |= TypeParamFlags
- }
- ctx.newLazySymbol(owner, name1, flags1, completeSym(tag))
- case CLASSsym =>
- if (isClassRoot) completeRoot(classRoot)
- else if (isModuleRoot) completeRoot(moduleRoot)
- else ctx.newLazyClassSymbol(owner, name.asTypeName, flags, completeClass)
- case MODULEsym =>
- val info = at(inforef, () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... ()
- if (isModuleRoot) {
- moduleRoot.denot.asInstanceOf[LazyClassDenotation].flags = flags
- moduleRoot
- } else ctx.newSymbol(owner, name.asTermName, flags, info)
- case VALsym =>
- if (isModuleRoot) { assert(false); NoSymbol }
- else ctx.newLazySymbol(owner, name.asTermName, flags, completeSym(tag))
+ private object classUnpickler extends ClassCompleter {
+ override def complete(denot: LazyClassDenotation): Unit = completeLocal(denot)
+ }
- case _ =>
- errorBadSignature("bad symbol tag: " + tag)
- })
+ /** Convert
+ * tp { type name = sym } forSome { sym >: L <: H }
+ * to
+ * tp { name >: L <: H }
+ * and
+ * tp { name: sym } forSome { sym <: T with Singleton }
+ * to
+ * tp { name: T }
+ */
+ def elimExistentials(boundSyms: List[Symbol], tp: Type): Type = {
+ def removeSingleton(tp: Type): Type =
+ if (tp.typeSymbol == defn.SingletonClass) defn.AnyType else tp
+ def elim(tp: Type): Type = tp match {
+ case tp @ RefinedType(parent, name) =>
+ val parent1 = elim(tp.parent)
+ tp.info match {
+ case TypeAlias(info: TypeRefBySym) if boundSyms contains info.fixedSym =>
+ RefinedType(parent1, name, info.fixedSym.info)
+ case info: TypeRefBySym if boundSyms contains info.fixedSym =>
+ val info1 = info.fixedSym.info
+ assert(info1 <:< defn.SingletonClass.typeConstructor)
+ RefinedType(parent1, name, info1.mapAnd(removeSingleton))
+ case info =>
+ tp.derivedRefinedType(parent1, name, info)
+ }
+ case _ =>
+ tp
}
-
- case class TempPolyType(tparams: List[Symbol], tpe: Type) extends UncachedGroundType
-
- /** Temporary type for classinfos, will be decomposed on completion of the class */
- case class TempClassInfoType(parentTypes: List[Type], decls: Scope, clazz: Symbol) extends UncachedGroundType
-
- /** Convert
- * tp { type name = sym } forSome { sym >: L <: H }
- * to
- * tp { name >: L <: H }
- * and
- * tp { name: sym } forSome { sym <: T with Singleton }
- * to
- * tp { name: T }
- */
- def elimExistentials(boundSyms: List[Symbol], tp: Type): Type = {
- def removeSingleton(tp: Type): Type =
- if (tp.typeSymbol == defn.SingletonClass) defn.AnyType else tp
- def elim(tp: Type): Type = tp match {
- case tp @ RefinedType(parent, name) =>
- val parent1 = elim(tp.parent)
- tp.info match {
- case TypeAlias(info: TypeRefBySym) if boundSyms contains info.fixedSym =>
- RefinedType(parent1, name, info.fixedSym.info)
- case info: TypeRefBySym if boundSyms contains info.fixedSym =>
- val info1 = info.fixedSym.info
- assert(info1 <:< defn.SingletonClass.typeConstructor)
- RefinedType(parent1, name, info1.mapAnd(removeSingleton))
- case info =>
- tp.derivedRefinedType(parent1, name, info)
- }
- case _ =>
- tp
- }
- val tp1 = elim(tp)
- val isBound = (tp: Type) => boundSyms contains tp.typeSymbol
- if (tp1 exists isBound) {
- val tp2 = tp1.subst(boundSyms, boundSyms map (_ => defn.AnyType))
- ctx.warning(s"""failure to eliminate existential
- |original type : $tp forSome {${ctx.show(boundSyms, "; ")}}
+ val tp1 = elim(tp)
+ val isBound = (tp: Type) => boundSyms contains tp.typeSymbol
+ if (tp1 exists isBound) {
+ val tp2 = tp1.subst(boundSyms, boundSyms map (_ => defn.AnyType))
+ cctx.warning(s"""failure to eliminate existential
+ |original type : $tp forSome {${cctx.show(boundSyms, "; ")}}
|reduces to : $tp1
|type used instead: $tp2
|proceed at own risk.""".stripMargin)
- tp2
- } else tp1
- }
+ tp2
+ } else tp1
+ }
- /** Read a type
- *
- * @param forceProperType is used to ease the transition to NullaryMethodTypes (commentmarker: NMT_TRANSITION)
- * the flag say that a type of kind * is expected, so that PolyType(tps, restpe) can be disambiguated to PolyType(tps, NullaryMethodType(restpe))
- * (if restpe is not a ClassInfoType, a MethodType or a NullaryMethodType, which leaves TypeRef/SingletonType -- the latter would make the polytype a type constructor)
- */
- protected def readType(forceProperType: Boolean = false): Type = {
- val tag = readByte()
- val end = readNat() + readIndex
- (tag: @switch) match {
- case NOtpe =>
- NoType
- case NOPREFIXtpe =>
- NoPrefix
- case THIStpe =>
- val cls = readSymbolRef().asClass
- if (isRefinementClass(cls)) RefinedThis(refinementTypes(cls))
- else ThisType(cls)
- case SINGLEtpe =>
- val pre = readTypeRef()
- def notAMethod(sym: Symbol) = sym.info match {
- case _: PolyType | _: MethodType => false
- case _ => true
- }
- val sym = readDisambiguatedSymbol(notAMethod)
- if (isLocal(sym)) TermRef(pre, sym.asTerm)
- else TermRef(pre, sym.name.asTermName, NullSignature)
- case SUPERtpe =>
- val thistpe = readTypeRef()
- val supertpe = readTypeRef()
- SuperType(thistpe, supertpe)
- case CONSTANTtpe =>
- ConstantType(readConstantRef())
- case TYPEREFtpe =>
- val pre = readTypeRef()
- val sym = readSymbolRef()
- val tycon =
- if (isLocal(sym)) TypeRef(pre, sym.asType)
- else TypeRef(pre, sym.name.asTypeName)
- tycon.appliedTo(until(end, readTypeRef))
- case TYPEBOUNDStpe =>
- TypeBounds(readTypeRef(), readTypeRef())
- case REFINEDtpe =>
- val clazz = readSymbolRef()
- val decls = symScope(clazz)
- symScopes(clazz) = EmptyScope // prevent further additions
- val parents = until(end, readTypeRef)
- val parent = parents.reduceLeft(_ & _)
- if (decls.isEmpty) parent
- else {
- def addRefinement(tp: Type, sym: Symbol) =
- RefinedType(tp, sym.name, sym.info)
- val result = (parent /: decls.toList)(addRefinement).asInstanceOf[RefinedType]
- assert(!refinementTypes.isDefinedAt(clazz), clazz+"/"+decls)
- refinementTypes(clazz) = result
- result
+ /** Read a type
+ *
+ * @param forceProperType is used to ease the transition to NullaryMethodTypes (commentmarker: NMT_TRANSITION)
+ * the flag say that a type of kind * is expected, so that PolyType(tps, restpe) can be disambiguated to PolyType(tps, NullaryMethodType(restpe))
+ * (if restpe is not a ClassInfoType, a MethodType or a NullaryMethodType, which leaves TypeRef/SingletonType -- the latter would make the polytype a type constructor)
+ */
+ protected def readType(forceProperType: Boolean = false): Type = {
+ val tag = readByte()
+ val end = readNat() + readIndex
+ (tag: @switch) match {
+ case NOtpe =>
+ NoType
+ case NOPREFIXtpe =>
+ NoPrefix
+ case THIStpe =>
+ val cls = readSymbolRef().asClass
+ if (isRefinementClass(cls)) RefinedThis(refinementTypes(cls))
+ else ThisType(cls)
+ case SINGLEtpe =>
+ val pre = readTypeRef()
+ val sym = readDisambiguatedSymbol(_.isParameterless)
+ if (isLocal(sym)) TermRef(pre, sym.asTerm)
+ else TermRef(pre, sym.name.asTermName, NullSignature)
+ case SUPERtpe =>
+ val thistpe = readTypeRef()
+ val supertpe = readTypeRef()
+ SuperType(thistpe, supertpe)
+ case CONSTANTtpe =>
+ ConstantType(readConstantRef())
+ case TYPEREFtpe =>
+ val pre = readTypeRef()
+ val sym = readSymbolRef()
+ val tycon =
+ if (isLocal(sym)) TypeRef(pre, sym.asType)
+ else TypeRef(pre, sym.name.asTypeName)
+ tycon.appliedTo(until(end, readTypeRef))
+ case TYPEBOUNDStpe =>
+ TypeBounds(readTypeRef(), readTypeRef())
+ case REFINEDtpe =>
+ val clazz = readSymbolRef()
+ val decls = symScope(clazz)
+ symScopes(clazz) = EmptyScope // prevent further additions
+ val parents = until(end, readTypeRef)
+ val parent = parents.reduceLeft(_ & _)
+ if (decls.isEmpty) parent
+ else {
+ def addRefinement(tp: Type, sym: Symbol) =
+ RefinedType(tp, sym.name, sym.info)
+ val result = (parent /: decls.toList)(addRefinement).asInstanceOf[RefinedType]
+ assert(!refinementTypes.isDefinedAt(clazz), clazz + "/" + decls)
+ refinementTypes(clazz) = result
+ result
+ }
+ case CLASSINFOtpe =>
+ val clazz = readSymbolRef()
+ TempClassInfoType(until(end, readTypeRef), symScope(clazz), clazz)
+ case METHODtpe | IMPLICITMETHODtpe =>
+ val restpe = readTypeRef()
+ val params = until(end, readSymbolRef)
+ val maker = if (tag == METHODtpe) MethodType else ImplicitMethodType
+ maker.fromSymbols(params, restpe)
+ case POLYtpe =>
+ val restpe = readTypeRef()
+ val typeParams = until(end, readSymbolRef)
+ if (typeParams.nonEmpty) {
+ // NMT_TRANSITION: old class files denoted a polymorphic nullary method as PolyType(tps, restpe), we now require PolyType(tps, NullaryMethodType(restpe))
+ // when a type of kind * is expected (forceProperType is true), we know restpe should be wrapped in a NullaryMethodType (if it wasn't suitably wrapped yet)
+ def transitionNMT(restpe: Type) = {
+ val resTpeCls = restpe.getClass.toString // what's uglier than isInstanceOf? right! -- isInstanceOf does not work since the concrete types are defined in the compiler (not in scope here)
+ if (forceProperType /*&& pickleformat < 2.9 */ && !(resTpeCls.endsWith("MethodType"))) {
+ assert(!resTpeCls.contains("ClassInfoType"))
+ ExprType(restpe)
+ } else restpe
}
- case CLASSINFOtpe =>
- val clazz = readSymbolRef()
- TempClassInfoType(until(end, readTypeRef), symScope(clazz), clazz)
- case METHODtpe | IMPLICITMETHODtpe =>
- val restpe = readTypeRef()
- val params = until(end, readSymbolRef)
- val maker = if (tag == METHODtpe) MethodType else ImplicitMethodType
- maker.fromSymbols(params, restpe)
- case POLYtpe =>
- val restpe = readTypeRef()
- val typeParams = until(end, readSymbolRef)
- if (typeParams.nonEmpty) {
- // NMT_TRANSITION: old class files denoted a polymorphic nullary method as PolyType(tps, restpe), we now require PolyType(tps, NullaryMethodType(restpe))
- // when a type of kind * is expected (forceProperType is true), we know restpe should be wrapped in a NullaryMethodType (if it wasn't suitably wrapped yet)
- def transitionNMT(restpe: Type) = {
- val resTpeCls = restpe.getClass.toString // what's uglier than isInstanceOf? right! -- isInstanceOf does not work since the concrete types are defined in the compiler (not in scope here)
- if(forceProperType /*&& pickleformat < 2.9 */ && !(resTpeCls.endsWith("MethodType"))) { assert(!resTpeCls.contains("ClassInfoType"))
- ExprType(restpe) }
- else restpe
- }
- TempPolyType(typeParams, transitionNMT(restpe))
- } else ExprType(restpe)
- case EXISTENTIALtpe =>
- val restpe = readTypeRef()
- val boundSyms = until(end, readSymbolRef)
- elimExistentials(boundSyms, restpe)
- case ANNOTATEDtpe =>
- val tp = readTypeRef()
- // no annotation self type is supported, so no test whether this is a symbol ref
- val annots = until(end, readAnnotationRef)
- AnnotatedType(annots, tp)
- case _ =>
- noSuchTypeTag(tag, end)
- }
+ TempPolyType(typeParams, transitionNMT(restpe))
+ } else ExprType(restpe)
+ case EXISTENTIALtpe =>
+ val restpe = readTypeRef()
+ val boundSyms = until(end, readSymbolRef)
+ elimExistentials(boundSyms, restpe)
+ case ANNOTATEDtpe =>
+ val tp = readTypeRef()
+ // no annotation self type is supported, so no test whether this is a symbol ref
+ val annots = until(end, readAnnotationRef)
+ AnnotatedType(annots, tp)
+ case _ =>
+ noSuchTypeTag(tag, end)
}
+ }
- def noSuchTypeTag(tag: Int, end: Int): Type =
- errorBadSignature("bad type tag: " + tag)
-
- /** Read a constant */
- protected def readConstant(): Constant = {
- val tag = readByte().toInt
- val len = readNat()
- (tag: @switch) match {
- case LITERALunit => Constant(())
- case LITERALboolean => Constant(readLong(len) != 0L)
- case LITERALbyte => Constant(readLong(len).toByte)
- case LITERALshort => Constant(readLong(len).toShort)
- case LITERALchar => Constant(readLong(len).toChar)
- case LITERALint => Constant(readLong(len).toInt)
- case LITERALlong => Constant(readLong(len))
- case LITERALfloat => Constant(intBitsToFloat(readLong(len).toInt))
- case LITERALdouble => Constant(longBitsToDouble(readLong(len)))
- case LITERALstring => Constant(readNameRef().toString)
- case LITERALnull => Constant(null)
- case LITERALclass => Constant(readTypeRef())
- case LITERALenum => Constant(readSymbolRef())
- case _ => noSuchConstantTag(tag, len)
- }
+ def noSuchTypeTag(tag: Int, end: Int): Type =
+ errorBadSignature("bad type tag: " + tag)
+
+ /** Read a constant */
+ protected def readConstant(): Constant = {
+ val tag = readByte().toInt
+ val len = readNat()
+ (tag: @switch) match {
+ case LITERALunit => Constant(())
+ case LITERALboolean => Constant(readLong(len) != 0L)
+ case LITERALbyte => Constant(readLong(len).toByte)
+ case LITERALshort => Constant(readLong(len).toShort)
+ case LITERALchar => Constant(readLong(len).toChar)
+ case LITERALint => Constant(readLong(len).toInt)
+ case LITERALlong => Constant(readLong(len))
+ case LITERALfloat => Constant(intBitsToFloat(readLong(len).toInt))
+ case LITERALdouble => Constant(longBitsToDouble(readLong(len)))
+ case LITERALstring => Constant(readNameRef().toString)
+ case LITERALnull => Constant(null)
+ case LITERALclass => Constant(readTypeRef())
+ case LITERALenum => Constant(readSymbolRef())
+ case _ => noSuchConstantTag(tag, len)
}
+ }
- def noSuchConstantTag(tag: Int, len: Int): Constant =
- errorBadSignature("bad constant tag: " + tag)
+ def noSuchConstantTag(tag: Int, len: Int): Constant =
+ errorBadSignature("bad constant tag: " + tag)
- /** Read children and store them into the corresponding symbol.
- */
- protected def readChildren() {
- val tag = readByte()
- assert(tag == CHILDREN)
- val end = readNat() + readIndex
- val target = readSymbolRef()
- while (readIndex != end)
- target.addAnnotation(Annotation.makeChild(readSymbolRef().asClass))
- }
+ /** Read children and store them into the corresponding symbol.
+ */
+ protected def readChildren() {
+ val tag = readByte()
+ assert(tag == CHILDREN)
+ val end = readNat() + readIndex
+ val target = readSymbolRef()
+ while (readIndex != end)
+ target.addAnnotation(Annotation.makeChild(readSymbolRef()))
+ }
- /* Read a reference to a pickled item */
- protected def readSymbolRef(): Symbol = {//OPT inlined from: at(readNat(), readSymbol) to save on closure creation
- val i = readNat()
- var r = entries(i)
- if (r eq null) {
- val savedIndex = readIndex
- readIndex = index(i)
- r = readSymbol()
- assert(entries(i) eq null, entries(i))
- entries(i) = r
- readIndex = savedIndex
- }
- r.asInstanceOf[Symbol]
+ /* Read a reference to a pickled item */
+ protected def readSymbolRef(): Symbol = { //OPT inlined from: at(readNat(), readSymbol) to save on closure creation
+ val i = readNat()
+ var r = entries(i)
+ if (r eq null) {
+ val savedIndex = readIndex
+ readIndex = index(i)
+ r = readSymbol()
+ assert(entries(i) eq null, entries(i))
+ entries(i) = r
+ readIndex = savedIndex
}
+ r.asInstanceOf[Symbol]
+ }
- protected def readNameRef(): Name = at(readNat(), readName)
- protected def readTypeRef(): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... ()
- protected def readConstantRef(): Constant = at(readNat(), readConstant)
+ protected def readNameRef(): Name = at(readNat(), readName)
+ protected def readTypeRef(): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... ()
+ protected def readConstantRef(): Constant = at(readNat(), readConstant)
- protected def readTypeNameRef(): TypeName = readNameRef().toTypeName
- protected def readTermNameRef(): TermName = readNameRef().toTermName
+ protected def readTypeNameRef(): TypeName = readNameRef().toTypeName
+ protected def readTermNameRef(): TermName = readNameRef().toTermName
- protected def readAnnotationRef(): Annotation = at(readNat(), readAnnotation)
+ protected def readAnnotationRef(): Annotation = at(readNat(), readAnnotation)
-// protected def readModifiersRef(): Modifiers = at(readNat(), readModifiers)
- protected def readTreeRef(): TypedTree = at(readNat(), readTree)
+ // protected def readModifiersRef(): Modifiers = at(readNat(), readModifiers)
+ protected def readTreeRef(): TypedTree = at(readNat(), readTree)
- protected def readTree(): TypedTree = ???
+ protected def readTree(): TypedTree = ???
- /** Read an annotation argument, which is pickled either
- * as a Constant or a Tree.
- */
- protected def readAnnotArg(i: Int): TypedTree = bytes(index(i)) match {
- case TREE => at(i, readTree)
- case _ => mk.Literal(at(i, readConstant))
- }
+ /** Read an annotation argument, which is pickled either
+ * as a Constant or a Tree.
+ */
+ protected def readAnnotArg(i: Int): TypedTree = bytes(index(i)) match {
+ case TREE => at(i, readTree)
+ case _ => mk.Literal(at(i, readConstant))
+ }
- /** Read a ClassfileAnnotArg (argument to a classfile annotation)
- */
- private def readArrayAnnotArg(): TypedTree = {
- readByte() // skip the `annotargarray` tag
- val end = readNat() + readIndex
- // array elements are trees representing instances of scala.annotation.Annotation
- mk.ArrayValue(
- mk.TypeTree(defn.AnnotationClass.typeConstructor),
- until(end, () => readClassfileAnnotArg(readNat())))
- }
+ /** Read a ClassfileAnnotArg (argument to a classfile annotation)
+ */
+ private def readArrayAnnotArg(): TypedTree = {
+ readByte() // skip the `annotargarray` tag
+ val end = readNat() + readIndex
+ // array elements are trees representing instances of scala.annotation.Annotation
+ mk.ArrayValue(
+ mk.TypeTree(defn.AnnotationClass.typeConstructor),
+ until(end, () => readClassfileAnnotArg(readNat())))
+ }
- private def readAnnotInfoArg(): TypedTree = {
- readByte() // skip the `annotinfo` tag
- val end = readNat() + readIndex
- readAnnotationContents(end)
- }
+ private def readAnnotInfoArg(): TypedTree = {
+ readByte() // skip the `annotinfo` tag
+ val end = readNat() + readIndex
+ readAnnotationContents(end)
+ }
- protected def readClassfileAnnotArg(i: Int): TypedTree = bytes(index(i)) match {
- case ANNOTINFO => at(i, readAnnotInfoArg)
- case ANNOTARGARRAY => at(i, readArrayAnnotArg)
- case _ => readAnnotArg(i)
- }
+ protected def readClassfileAnnotArg(i: Int): TypedTree = bytes(index(i)) match {
+ case ANNOTINFO => at(i, readAnnotInfoArg)
+ case ANNOTARGARRAY => at(i, readArrayAnnotArg)
+ case _ => readAnnotArg(i)
+ }
- /** Read an annotation's contents. Not to be called directly, use
- * readAnnotation, readSymbolAnnotation, or readAnnotInfoArg
- */
- protected def readAnnotationContents(end: Int): TypedTree = {
- val atp = readTypeRef()
- val args = new ListBuffer[TypedTree]
- while (readIndex != end) {
- val argref = readNat()
- args += {
- if (isNameEntry(argref)) {
- val name = at(argref, readName)
- val arg = readClassfileAnnotArg(readNat())
- mk.NamedArg(name, arg)
- } else readAnnotArg(argref)
- }
+ /** Read an annotation's contents. Not to be called directly, use
+ * readAnnotation, readSymbolAnnotation, or readAnnotInfoArg
+ */
+ protected def readAnnotationContents(end: Int): TypedTree = {
+ val atp = readTypeRef()
+ val args = new ListBuffer[TypedTree]
+ while (readIndex != end) {
+ val argref = readNat()
+ args += {
+ if (isNameEntry(argref)) {
+ val name = at(argref, readName)
+ val arg = readClassfileAnnotArg(readNat())
+ mk.NamedArg(name, arg)
+ } else readAnnotArg(argref)
}
- mk.New(atp, args.toList)
}
+ mk.New(atp, args.toList)
+ }
- /** Read an annotation and as a side effect store it into
- * the symbol it requests. Called at top-level, for all
- * (symbol, annotInfo) entries. */
- protected def readSymbolAnnotation(): Unit = {
- val tag = readByte()
- if (tag != SYMANNOT)
- errorBadSignature("symbol annotation expected ("+ tag +")")
- val end = readNat() + readIndex
- val target = readSymbolRef()
- target.addAnnotation(ConcreteAnnotation(readAnnotationContents(end)))
- }
+ /** Read an annotation and as a side effect store it into
+ * the symbol it requests. Called at top-level, for all
+ * (symbol, annotInfo) entries.
+ */
+ protected def readSymbolAnnotation(): Unit = {
+ val tag = readByte()
+ if (tag != SYMANNOT)
+ errorBadSignature("symbol annotation expected (" + tag + ")")
+ val end = readNat() + readIndex
+ val target = readSymbolRef()
+ target.addAnnotation(ConcreteAnnotation(readAnnotationContents(end)))
+ }
- /** Read an annotation and return it. Used when unpickling
- * an ANNOTATED(WSELF)tpe or a NestedAnnotArg */
- protected def readAnnotation(): Annotation = {
- val tag = readByte()
- if (tag != ANNOTINFO)
- errorBadSignature("annotation expected (" + tag + ")")
- val end = readNat() + readIndex
- ConcreteAnnotation(readAnnotationContents(end))
- }
-/*
+ /** Read an annotation and return it. Used when unpickling
+ * an ANNOTATED(WSELF)tpe or a NestedAnnotArg
+ */
+ protected def readAnnotation(): Annotation = {
+ val tag = readByte()
+ if (tag != ANNOTINFO)
+ errorBadSignature("annotation expected (" + tag + ")")
+ val end = readNat() + readIndex
+ ConcreteAnnotation(readAnnotationContents(end))
+ }
+ /*
/* Read an abstract syntax tree */
protected def readTree(): Tree = {
val outerTag = readByte()
@@ -928,68 +1007,22 @@ abstract class UnPickler {
errorBadSignature("expected an TypeDef (" + other + ")")
}
*/
- protected def errorBadSignature(msg: String) =
- throw new RuntimeException("malformed Scala signature of " + classRoot.name + " at " + readIndex + "; " + msg)
-
- protected def errorMissingRequirement(name: Name, owner: Symbol): Symbol =
- MissingRequirementError.signal(
- s"bad reference while unpickling $filename: ${ctx.showDetailed(name)} not found in $owner"
- )
-
-// def inferMethodAlternative(fun: Tree, argtpes: List[Type], restpe: Type) {} // can't do it; need a compiler for that.
-
- /** Convert to a type error, that is printed gracefully instead of crashing.
- *
- * Similar in intent to what SymbolLoader does (but here we don't have access to
- * error reporting, so we rely on the typechecker to report the error).
- */
- def toTypeError(e: MissingRequirementError) = {
- // e.printStackTrace()
- new TypeError(e.msg)
- }
+ protected def errorBadSignature(msg: String) =
+ throw new RuntimeException("malformed Scala signature of " + classRoot.name + " at " + readIndex + "; " + msg)
- def depoly(tp: Type): Type = tp match {
- case TempPolyType(tparams, restpe) => PolyType.fromSymbols(tparams, restpe)
- case tp => tp
- }
+ protected def errorMissingRequirement(name: Name, owner: Symbol): Symbol =
+ MissingRequirementError.signal(
+ s"bad reference while unpickling source: ${cctx.showDetailed(name)} not found in $owner")
- /** A lazy type which when completed returns type at index `i` and
- * if `j >= 0`, set alias of completed symbol to symbol at index `j`.
- */
- private class SymRestCompleter(tag: Int, i: Int, j: Int) extends SymCompleter {
- override def complete(denot: LazySymDenotation) : Unit = try {
- val tp = at(i, () => readType(forceProperType = denot.isTerm))
- denot.info = if (tag == ALIASsym) TypeBounds(tp, tp) else depoly(tp)
- def disambiguate(alt: Symbol) =
- denot.info =:= denot.owner.thisType.memberInfo(alt)
- if (j >= 0) {
- val alias = at(j, readDisambiguatedSymbol(disambiguate)).asTerm
- denot.addAnnotation(Annotation.makeAlias(alias))
- }
- } catch {
- case e: MissingRequirementError => throw toTypeError(e)
- }
- }
+ // def inferMethodAlternative(fun: Tree, argtpes: List[Type], restpe: Type) {} // can't do it; need a compiler for that.
- /** A lazy type which when completed returns type at index `i` and
- * if `j >= 0`, set alias of completed symbol to symbol at index `j`.
- */
- private class ClassRestCompleter(i: Int, j: Int) extends ClassCompleter {
- override def complete(denot: LazyClassDenotation) : Unit = try {
- val cls = denot.symbol
- val (tparams, TempClassInfoType(parents, decls, clazz)) =
- at(i, () => readType()) match {
- case TempPolyType(tps, cinfo) => (tps, cinfo)
- case cinfo => (Nil, cinfo)
- }
- val selfType = if (j > 0) at(j, () => readType()) else denot.typeConstructor
- tparams foreach decls.enter
- denot.parents = ctx.normalizeToRefs(parents, cls, decls)
- denot.selfType = selfType
- denot.decls = decls
- } catch {
- case e: MissingRequirementError => throw toTypeError(e)
- }
- }
+ /** Convert to a type error, that is printed gracefully instead of crashing.
+ *
+ * Similar in intent to what SymbolLoader does (but here we don't have access to
+ * error reporting, so we rely on the typechecker to report the error).
+ */
+ def toTypeError(e: MissingRequirementError) = {
+ // e.printStackTrace()
+ new TypeError(e.msg)
}
}