diff options
author | Martin Odersky <odersky@gmail.com> | 2013-02-15 16:07:36 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2013-02-15 16:07:36 +0100 |
commit | 5e219c1d8426da4fba6c4604e24f4bceb3573392 (patch) | |
tree | c62cccb5b0c31e3bab8c5b5e77a5911a5a8c6bbd | |
parent | 11c5251de18aab187646e3f58612b457349ebe6a (diff) | |
download | dotty-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.scala | 81 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/AbstractFileReader.scala | 88 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala | 378 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/ClassfileParser.scala | 944 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/PickleBuffer.scala | 18 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/UnPickler.scala | 1149 |
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) } } |