diff options
author | Martin Odersky <odersky@gmail.com> | 2013-02-06 15:39:54 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2013-02-06 15:39:54 +0100 |
commit | f8e3a9bf3a5f2550a20d59c8d025f2c7247ed1fd (patch) | |
tree | a46ef4912be650bc61fec7748874a98529524c23 | |
parent | 6aee0dcb43feb12984d65d198210b90f7704c3a4 (diff) | |
download | dotty-f8e3a9bf3a5f2550a20d59c8d025f2c7247ed1fd.tar.gz dotty-f8e3a9bf3a5f2550a20d59c8d025f2c7247ed1fd.tar.bz2 dotty-f8e3a9bf3a5f2550a20d59c8d025f2c7247ed1fd.zip |
Integration of unpicklers
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/ByteCodecs.scala | 221 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/PickleBuffer.scala | 188 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/UnPickler.scala | 1008 |
3 files changed, 1417 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/core/pickling/ByteCodecs.scala b/src/dotty/tools/dotc/core/pickling/ByteCodecs.scala new file mode 100644 index 000000000..0cffe43bc --- /dev/null +++ b/src/dotty/tools/dotc/core/pickling/ByteCodecs.scala @@ -0,0 +1,221 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2007-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package dotty.tools.dotc.core.pickling + +object ByteCodecs { + + def avoidZero(src: Array[Byte]): Array[Byte] = { + var i = 0 + val srclen = src.length + var count = 0 + while (i < srclen) { + if (src(i) == 0x7f) count += 1 + i += 1 + } + val dst = new Array[Byte](srclen + count) + i = 0 + var j = 0 + while (i < srclen) { + val in = src(i) + if (in == 0x7f) { + dst(j) = (0xc0).toByte + dst(j + 1) = (0x80).toByte + j += 2 + } else { + dst(j) = (in + 1).toByte + j += 1 + } + i += 1 + } + dst + } + + def regenerateZero(src: Array[Byte]): Int = { + var i = 0 + val srclen = src.length + var j = 0 + while (i < srclen) { + val in: Int = src(i) & 0xff + if (in == 0xc0 && (src(i + 1) & 0xff) == 0x80) { + src(j) = 0x7f + i += 2 + } else if (in == 0) { + src(j) = 0x7f + i += 1 + } else { + src(j) = (in - 1).toByte + i += 1 + } + j += 1 + } + j + } + + def encode8to7(src: Array[Byte]): Array[Byte] = { + val srclen = src.length + val dstlen = (srclen * 8 + 6) / 7 + val dst = new Array[Byte](dstlen) + var i = 0 + var j = 0 + while (i + 6 < srclen) { + var in: Int = src(i) & 0xff + dst(j) = (in & 0x7f).toByte + var out: Int = in >>> 7 + in = src(i + 1) & 0xff + dst(j + 1) = (out | (in << 1) & 0x7f).toByte + out = in >>> 6 + in = src(i + 2) & 0xff + dst(j + 2) = (out | (in << 2) & 0x7f).toByte + out = in >>> 5 + in = src(i + 3) & 0xff + dst(j + 3) = (out | (in << 3) & 0x7f).toByte + out = in >>> 4 + in = src(i + 4) & 0xff + dst(j + 4) = (out | (in << 4) & 0x7f).toByte + out = in >>> 3 + in = src(i + 5) & 0xff + dst(j + 5) = (out | (in << 5) & 0x7f).toByte + out = in >>> 2 + in = src(i + 6) & 0xff + dst(j + 6) = (out | (in << 6) & 0x7f).toByte + out = in >>> 1 + dst(j + 7) = out.toByte + i += 7 + j += 8 + } + if (i < srclen) { + var in: Int = src(i) & 0xff + dst(j) = (in & 0x7f).toByte; j += 1 + var out: Int = in >>> 7 + if (i + 1 < srclen) { + in = src(i + 1) & 0xff + dst(j) = (out | (in << 1) & 0x7f).toByte; j += 1 + out = in >>> 6 + if (i + 2 < srclen) { + in = src(i + 2) & 0xff + dst(j) = (out | (in << 2) & 0x7f).toByte; j += 1 + out = in >>> 5 + if (i + 3 < srclen) { + in = src(i + 3) & 0xff + dst(j) = (out | (in << 3) & 0x7f).toByte; j += 1 + out = in >>> 4 + if (i + 4 < srclen) { + in = src(i + 4) & 0xff + dst(j) = (out | (in << 4) & 0x7f).toByte; j += 1 + out = in >>> 3 + if (i + 5 < srclen) { + in = src(i + 5) & 0xff + dst(j) = (out | (in << 5) & 0x7f).toByte; j += 1 + out = in >>> 2 + } + } + } + } + } + if (j < dstlen) dst(j) = out.toByte + } + dst + } + + def decode7to8(src: Array[Byte], srclen: Int): Int = { + var i = 0 + var j = 0 + val dstlen = (srclen * 7 + 7) / 8 + while (i + 7 < srclen) { + var out: Int = src(i) + var in: Byte = src(i + 1) + src(j) = (out | (in & 0x01) << 7).toByte + out = in >>> 1 + in = src(i + 2) + src(j + 1) = (out | (in & 0x03) << 6).toByte + out = in >>> 2 + in = src(i + 3) + src(j + 2) = (out | (in & 0x07) << 5).toByte + out = in >>> 3 + in = src(i + 4) + src(j + 3) = (out | (in & 0x0f) << 4).toByte + out = in >>> 4 + in = src(i + 5) + src(j + 4) = (out | (in & 0x1f) << 3).toByte + out = in >>> 5 + in = src(i + 6) + src(j + 5) = (out | (in & 0x3f) << 2).toByte + out = in >>> 6 + in = src(i + 7) + src(j + 6) = (out | in << 1).toByte + i += 8 + j += 7 + } + if (i < srclen) { + var out: Int = src(i) + if (i + 1 < srclen) { + var in: Byte = src(i + 1) + src(j) = (out | (in & 0x01) << 7).toByte; j += 1 + out = in >>> 1 + if (i + 2 < srclen) { + in = src(i + 2) + src(j) = (out | (in & 0x03) << 6).toByte; j += 1 + out = in >>> 2 + if (i + 3 < srclen) { + in = src(i + 3) + src(j) = (out | (in & 0x07) << 5).toByte; j += 1 + out = in >>> 3 + if (i + 4 < srclen) { + in = src(i + 4) + src(j) = (out | (in & 0x0f) << 4).toByte; j += 1 + out = in >>> 4 + if (i + 5 < srclen) { + in = src(i + 5) + src(j) = (out | (in & 0x1f) << 3).toByte; j += 1 + out = in >>> 5 + if (i + 6 < srclen) { + in = src(i + 6) + src(j) = (out | (in & 0x3f) << 2).toByte; j += 1 + out = in >>> 6 + } + } + } + } + } + } + if (j < dstlen) src(j) = out.toByte + } + dstlen + } + + def encode(xs: Array[Byte]): Array[Byte] = avoidZero(encode8to7(xs)) + + /** + * Destructively decodes array xs and returns the length of the decoded array. + * + * Sometimes returns (length+1) of the decoded array. Example: + * + * scala> val enc = reflect.generic.ByteCodecs.encode(Array(1,2,3)) + * enc: Array[Byte] = Array(2, 5, 13, 1) + * + * scala> reflect.generic.ByteCodecs.decode(enc) + * res43: Int = 4 + * + * scala> enc + * res44: Array[Byte] = Array(1, 2, 3, 0) + * + * However, this does not always happen. + */ + def decode(xs: Array[Byte]): Int = { + val len = regenerateZero(xs) + decode7to8(xs, len) + } +} + + + + + + + + diff --git a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala new file mode 100644 index 000000000..b291b8545 --- /dev/null +++ b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala @@ -0,0 +1,188 @@ +package dotty.tools +package dotc +package core +package pickling + +import Flags._ + +/** Variable length byte arrays, with methods for basic pickling and unpickling. + * + * @param data The initial buffer + * @param from The first index where defined data are found + * @param to The first index where new data can be written + */ +class PickleBuffer(data: Array[Byte], from: Int, to: Int) { + + var bytes = data + var readIndex = from + var writeIndex = to + + /** Double bytes array */ + private def dble() { + val bytes1 = new Array[Byte](bytes.length * 2) + Array.copy(bytes, 0, bytes1, 0, writeIndex) + bytes = bytes1 + } + + def ensureCapacity(capacity: Int) = + while (bytes.length < writeIndex + capacity) dble() + + // -- Basic output routines -------------------------------------------- + + /** Write a byte of data */ + def writeByte(b: Int) { + if (writeIndex == bytes.length) dble() + bytes(writeIndex) = b.toByte + writeIndex += 1 + } + + /** Write a natural number in big endian format, base 128. + * All but the last digits have bit 0x80 set. + */ + def writeNat(x: Int) = + writeLongNat(x.toLong & 0x00000000FFFFFFFFL) + + /** + * Like writeNat, but for longs. This is not the same as + * writeLong, which writes in base 256. Note that the + * binary representation of LongNat is identical to Nat + * if the long value is in the range Int.MIN_VALUE to + * Int.MAX_VALUE. + */ + def writeLongNat(x: Long) { + def writeNatPrefix(x: Long) { + val y = x >>> 7 + if (y != 0L) writeNatPrefix(y) + writeByte(((x & 0x7f) | 0x80).toInt) + } + val y = x >>> 7 + if (y != 0L) writeNatPrefix(y) + writeByte((x & 0x7f).toInt) + } + + /** Write a natural number <code>x</code> at position <code>pos</code>. + * If number is more than one byte, shift rest of array to make space. + * + * @param pos ... + * @param x ... + */ + def patchNat(pos: Int, x: Int) { + def patchNatPrefix(x: Int) { + writeByte(0) + Array.copy(bytes, pos, bytes, pos+1, writeIndex - (pos+1)) + bytes(pos) = ((x & 0x7f) | 0x80).toByte + val y = x >>> 7 + if (y != 0) patchNatPrefix(y) + } + bytes(pos) = (x & 0x7f).toByte + val y = x >>> 7 + if (y != 0) patchNatPrefix(y) + } + + /** Write a long number <code>x</code> in signed big endian format, base 256. + * + * @param x The long number to be written. + */ + def writeLong(x: Long) { + val y = x >> 8 + val z = x & 0xff + if (-y != (z >> 7)) writeLong(y) + writeByte(z.toInt) + } + + // -- Basic input routines -------------------------------------------- + + /** Peek at the current byte without moving the read index */ + def peekByte(): Int = bytes(readIndex) + + /** Read a byte */ + def readByte(): Int = { + val x = bytes(readIndex); readIndex += 1; x + } + + /** Read a natural number in big endian format, base 128. + * All but the last digits have bit 0x80 set.*/ + def readNat(): Int = readLongNat().toInt + + def readLongNat(): Long = { + var b = 0L + var x = 0L + do { + b = readByte() + x = (x << 7) + (b & 0x7f) + } while ((b & 0x80) != 0L); + x + } + + /** Read a long number in signed big endian format, base 256. */ + def readLong(len: Int): Long = { + var x = 0L + var i = 0 + while (i < len) { + x = (x << 8) + (readByte() & 0xff) + i += 1 + } + val leading = 64 - (len << 3) + x << leading >> leading + } + + /** Returns the buffer as a sequence of (Int, Array[Byte]) representing + * (tag, data) of the individual entries. Saves and restores buffer state. + */ + + def toIndexedSeq: IndexedSeq[(Int, Array[Byte])] = { + val saved = readIndex + readIndex = 0 + readNat() ; readNat() // discarding version + val result = new Array[(Int, Array[Byte])](readNat()) + + result.indices foreach { index => + val tag = readNat() + val len = readNat() + val bytes = data.slice(readIndex, len + readIndex) + readIndex += len + + result(index) = tag -> bytes + } + + readIndex = saved + result.toIndexedSeq + } + + /** Perform operation <code>op</code> until the condition + * <code>readIndex == end</code> is satisfied. + * Concatenate results into a list. + * + * @param end ... + * @param op ... + * @return ... + */ + def until[T](end: Int, op: () => T): List[T] = + if (readIndex == end) List() else op() :: until(end, op); + + /** Perform operation <code>op</code> the number of + * times specified. Concatenate the results into a list. + */ + def times[T](n: Int, op: ()=>T): List[T] = + 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 new file mode 100644 index 000000000..2142df937 --- /dev/null +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -0,0 +1,1008 @@ +package dotty.tools +package dotc +package core +package pickling + +import java.io.IOException +import java.lang.Float.intBitsToFloat +import java.lang.Double.longBitsToDouble + +import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._ +import StdNames._, Denotations._, NameOps._, Flags._, Constants._, Annotations._ +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 + */ + 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()) + } + } + + 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 + + protected def debug = ctx.settings.debug.value + + checkVersion() + + 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 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 refinement classes to their associated refinement types */ + private val refinementTypes = mutable.HashMap[Symbol, RefinedType]() + + //println("unpickled " + classRoot + ":" + classRoot.rawInfo + ", " + moduleRoot + ":" + moduleRoot.rawInfo);//debug + + // Laboriously unrolled for performance. + def run() { + var i = 0 + while (i < index.length) { + if (entries(i) == null && isSymbolEntry(i)) { + val savedIndex = readIndex + readIndex = index(i) + entries(i) = readSymbol() + readIndex = savedIndex + } + i += 1 + } + // read children last, fix for #3951 + i = 0 + while (i < index.length) { + if (entries(i) == null) { + if (isSymbolAnnotationEntry(i)) { + val savedIndex = readIndex + readIndex = index(i) + readSymbolAnnotation() + readIndex = savedIndex + } + else if (isChildrenEntry(i)) { + val savedIndex = readIndex + readIndex = index(i) + readChildren() + readIndex = savedIndex + } + } + i += 1 + } + } + + 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) + + /** 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 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 name? */ + protected def isNameEntry(i: Int): Boolean = { + val tag = bytes(index(i)).toInt + tag == TERMname || tag == TYPEname + } + + /** Does entry represent a symbol annotation? */ + protected def isSymbolAnnotationEntry(i: Int): Boolean = { + val tag = bytes(index(i)).toInt + tag == SYMANNOT + } + + /** 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 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 + } + + protected def isRefinementClass(sym: Symbol): Boolean = + sym.name == tpnme.REFINE_CLASS + + protected def isLocal(sym: Symbol) = { + val root = sym.topLevelClass + sym == moduleRoot || sym == classRoot + } + + /** 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] + } + + /** 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(disambiguate: Symbol => Boolean)(): Symbol = { + 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 = 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 + } + + 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) { + ??? +/* + val moduleVar = owner.info.decl(name.toTermName.moduleVarName).symbol + if (moduleVar.isLazyAccessor) + return moduleVar.lazyAccessor.lazyAccessor +*/ + } + 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) + } + } + } + } + + 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()) + var inforef = readNat() + val 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 = + if (atEnd) { + assert(!(flags is SuperAccessor), name) + -1 + } else { + assert(flags is (SuperAccessor | ParamAccessor), name) + readNat() + } + 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 + } + + 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 + } + + finishSym(tag match { + case TYPEsym | ALIASsym => + var name1 = name.asTypeName + var flags1 = flags + if (flags is TypeParam) { + name1 = name1.expandedName(owner) + flags1 |= ProtectedLocal + } + ctx.newLazyTypeSymbol(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.newTermSymbol(owner, name.asTermName, flags, info) + case VALsym => + if (isModuleRoot) { assert(false); NoSymbol } + else ctx.newLazyTermSymbol(owner, name.asTermName, flags, completeSym(tag)) + + case _ => + errorBadSignature("bad symbol tag: " + tag) + }) + } + + 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, "; ")}} + |reduces to : $tp1 + |type used instead: $tp2 + |proceed at own risk.""".stripMargin) + 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.applyToArgs(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 + } + TempPolyType(typeParams, transitionNMT(restpe)) + } else ExprType(restpe) + case EXISTENTIALtpe => + val restpe = readTypeRef() + val boundSyms = until(end, readSymbolRef) + elimExistentials(boundSyms, restpe) + case ANNOTATEDtpe => + ??? + /* + var typeRef = readNat() + val selfsym = if (isSymbolRef(typeRef)) { + val s = at(typeRef, readSymbol) + typeRef = readNat() + s + } else NoSymbol // selfsym can go. + val tp = at(typeRef, () => readType(forceProperType)) // NMT_TRANSITION + val annots = until(end, readAnnotationRef) + if (selfsym == NoSymbol) AnnotatedType(annots, tp, selfsym) + else 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 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(Child(readSymbolRef().asClass)) + } + + /* 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 readTypeNameRef(): TypeName = readNameRef().toTypeName + protected def readTermNameRef(): TermName = readNameRef().toTermName + + protected def readSymbolAnnotation(): Unit = ??? + protected def readAnnotationRef(): Annotation = ??? // at(readNat(), readAnnotation) +// protected def readModifiersRef(): Modifiers = at(readNat(), readModifiers) +// protected def readTreeRef(): Tree = at(readNat(), readTree) +/* + /** Read an annotation argument, which is pickled either + * as a Constant or a Tree. + */ + protected def readAnnotArg(i: Int): Tree = bytes(index(i)) match { + case TREE => at(i, readTree) + case _ => + val const = at(i, readConstant) + Literal(const) setType const.tpe + } + + /** Read a ClassfileAnnotArg (argument to a classfile annotation) + */ + private def readArrayAnnot() = { + readByte() // skip the `annotargarray` tag + val end = readNat() + readIndex + until(end, () => readClassfileAnnotArg(readNat())).toArray(JavaArgumentTag) + } + protected def readClassfileAnnotArg(i: Int): ClassfileAnnotArg = bytes(index(i)) match { + case ANNOTINFO => NestedAnnotArg(at(i, readAnnotation)) + case ANNOTARGARRAY => at(i, () => ArrayAnnotArg(readArrayAnnot())) + case _ => LiteralAnnotArg(at(i, readConstant)) + } + + /** Read an AnnotationInfo. Not to be called directly, use + * readAnnotation or readSymbolAnnotation + */ + protected def readAnnotationInfo(end: Int): AnnotationInfo = { + val atp = readTypeRef() + val args = new ListBuffer[Tree] + val assocs = new ListBuffer[(Name, ClassfileAnnotArg)] + while (readIndex != end) { + val argref = readNat() + if (isNameEntry(argref)) { + val name = at(argref, readName) + val arg = readClassfileAnnotArg(readNat()) + assocs += ((name, arg)) + } + else + args += readAnnotArg(argref) + } + AnnotationInfo(atp, args.toList, assocs.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() { + val tag = readByte() + if (tag != SYMANNOT) + errorBadSignature("symbol annotation expected ("+ tag +")") + val end = readNat() + readIndex + val target = readSymbolRef() + target.addAnnotation(readAnnotationInfo(end)) + } + + /** Read an annotation and return it. Used when unpickling + * an ANNOTATED(WSELF)tpe or a NestedAnnotArg */ + protected def readAnnotation(): AnnotationInfo = { + val tag = readByte() + if (tag != ANNOTINFO) + errorBadSignature("annotation expected (" + tag + ")") + val end = readNat() + readIndex + readAnnotationInfo(end) + } + + /* Read an abstract syntax tree */ + protected def readTree(): Tree = { + val outerTag = readByte() + if (outerTag != TREE) + errorBadSignature("tree expected (" + outerTag + ")") + val end = readNat() + readIndex + val tag = readByte() + val tpe = if (tag == EMPTYtree) NoType else readTypeRef() + + // Set by the three functions to follow. If symbol is non-null + // after the new tree 't' has been created, t has its Symbol + // set to symbol; and it always has its Type set to tpe. + var symbol: Symbol = null + var mods: Modifiers = null + var name: Name = null + + /** Read a Symbol, Modifiers, and a Name */ + def setSymModsName() { + symbol = readSymbolRef() + mods = readModifiersRef() + name = readNameRef() + } + /** Read a Symbol and a Name */ + def setSymName() { + symbol = readSymbolRef() + name = readNameRef() + } + /** Read a Symbol */ + def setSym() { + symbol = readSymbolRef() + } + + val t = tag match { + case EMPTYtree => + EmptyTree + + case PACKAGEtree => + setSym() + val pid = readTreeRef().asInstanceOf[RefTree] + val stats = until(end, readTreeRef) + PackageDef(pid, stats) + + case CLASStree => + setSymModsName() + val impl = readTemplateRef() + val tparams = until(end, readTypeDefRef) + ClassDef(mods, name.toTypeName, tparams, impl) + + case MODULEtree => + setSymModsName() + ModuleDef(mods, name.toTermName, readTemplateRef()) + + case VALDEFtree => + setSymModsName() + val tpt = readTreeRef() + val rhs = readTreeRef() + ValDef(mods, name.toTermName, tpt, rhs) + + case DEFDEFtree => + setSymModsName() + val tparams = times(readNat(), readTypeDefRef) + val vparamss = times(readNat(), () => times(readNat(), readValDefRef)) + val tpt = readTreeRef() + val rhs = readTreeRef() + DefDef(mods, name.toTermName, tparams, vparamss, tpt, rhs) + + case TYPEDEFtree => + setSymModsName() + val rhs = readTreeRef() + val tparams = until(end, readTypeDefRef) + TypeDef(mods, name.toTypeName, tparams, rhs) + + case LABELtree => + setSymName() + val rhs = readTreeRef() + val params = until(end, readIdentRef) + LabelDef(name.toTermName, params, rhs) + + case IMPORTtree => + setSym() + val expr = readTreeRef() + val selectors = until(end, () => { + val from = readNameRef() + val to = readNameRef() + ImportSelector(from, -1, to, -1) + }) + + Import(expr, selectors) + + case TEMPLATEtree => + setSym() + val parents = times(readNat(), readTreeRef) + val self = readValDefRef() + val body = until(end, readTreeRef) + + Template(parents, self, body) + + case BLOCKtree => + val expr = readTreeRef() + val stats = until(end, readTreeRef) + Block(stats, expr) + + case CASEtree => + val pat = readTreeRef() + val guard = readTreeRef() + val body = readTreeRef() + CaseDef(pat, guard, body) + + case ALTERNATIVEtree => + Alternative(until(end, readTreeRef)) + + case STARtree => + Star(readTreeRef()) + + case BINDtree => + setSymName() + Bind(name, readTreeRef()) + + case UNAPPLYtree => + val fun = readTreeRef() + val args = until(end, readTreeRef) + UnApply(fun, args) + + case ARRAYVALUEtree => + val elemtpt = readTreeRef() + val trees = until(end, readTreeRef) + ArrayValue(elemtpt, trees) + + case FUNCTIONtree => + setSym() + val body = readTreeRef() + val vparams = until(end, readValDefRef) + Function(vparams, body) + + case ASSIGNtree => + val lhs = readTreeRef() + val rhs = readTreeRef() + Assign(lhs, rhs) + + case IFtree => + val cond = readTreeRef() + val thenp = readTreeRef() + val elsep = readTreeRef() + If(cond, thenp, elsep) + + case MATCHtree => + val selector = readTreeRef() + val cases = until(end, readCaseDefRef) + Match(selector, cases) + + case RETURNtree => + setSym() + Return(readTreeRef()) + + case TREtree => + val block = readTreeRef() + val finalizer = readTreeRef() + val catches = until(end, readCaseDefRef) + Try(block, catches, finalizer) + + case THROWtree => + Throw(readTreeRef()) + + case NEWtree => + New(readTreeRef()) + + case TYPEDtree => + val expr = readTreeRef() + val tpt = readTreeRef() + Typed(expr, tpt) + + case TYPEAPPLYtree => + val fun = readTreeRef() + val args = until(end, readTreeRef) + TypeApply(fun, args) + + case APPLYtree => + val fun = readTreeRef() + val args = until(end, readTreeRef) + if (fun.symbol.isOverloaded) { + fun.setType(fun.symbol.info) + inferMethodAlternative(fun, args map (_.tpe), tpe) + } + Apply(fun, args) + + case APPLYDYNAMICtree => + setSym() + val qual = readTreeRef() + val args = until(end, readTreeRef) + ApplyDynamic(qual, args) + + case SUPERtree => + setSym() + val qual = readTreeRef() + val mix = readTypeNameRef() + Super(qual, mix) + + case THIStree => + setSym() + This(readTypeNameRef()) + + case SELECTtree => + setSym() + val qualifier = readTreeRef() + val selector = readNameRef() + Select(qualifier, selector) + + case IDENTtree => + setSymName() + Ident(name) + + case LITERALtree => + Literal(readConstantRef()) + + case TYPEtree => + TypeTree() + + case ANNOTATEDtree => + val annot = readTreeRef() + val arg = readTreeRef() + Annotated(annot, arg) + + case SINGLETONTYPEtree => + SingletonTypeTree(readTreeRef()) + + case SELECTFROMTYPEtree => + val qualifier = readTreeRef() + val selector = readTypeNameRef() + SelectFromTypeTree(qualifier, selector) + + case COMPOUNDTYPEtree => + CompoundTypeTree(readTemplateRef()) + + case APPLIEDTYPEtree => + val tpt = readTreeRef() + val args = until(end, readTreeRef) + AppliedTypeTree(tpt, args) + + case TYPEBOUNDStree => + val lo = readTreeRef() + val hi = readTreeRef() + TypeBoundsTree(lo, hi) + + case EXISTENTIALTYPEtree => + val tpt = readTreeRef() + val whereClauses = until(end, readTreeRef) + ExistentialTypeTree(tpt, whereClauses) + + case _ => + noSuchTreeTag(tag, end) + } + + if (symbol == null) t setType tpe + else t setSymbol symbol setType tpe + } + + def noSuchTreeTag(tag: Int, end: Int) = + errorBadSignature("unknown tree type (" + tag + ")") + + def readModifiers(): Modifiers = { + val tag = readNat() + if (tag != MODIFIERS) + errorBadSignature("expected a modifiers tag (" + tag + ")") + val end = readNat() + readIndex + val pflagsHi = readNat() + val pflagsLo = readNat() + val pflags = (pflagsHi.toLong << 32) + pflagsLo + val flags = pickledToRawFlags(pflags) + val privateWithin = readNameRef() + Modifiers(flags, privateWithin, Nil) + } + + protected def readTemplateRef(): Template = + readTreeRef() match { + case templ:Template => templ + case other => + errorBadSignature("expected a template (" + other + ")") + } + protected def readCaseDefRef(): CaseDef = + readTreeRef() match { + case tree:CaseDef => tree + case other => + errorBadSignature("expected a case def (" + other + ")") + } + protected def readValDefRef(): ValDef = + readTreeRef() match { + case tree:ValDef => tree + case other => + errorBadSignature("expected a ValDef (" + other + ")") + } + protected def readIdentRef(): Ident = + readTreeRef() match { + case tree:Ident => tree + case other => + errorBadSignature("expected an Ident (" + other + ")") + } + protected def readTypeDefRef(): TypeDef = + readTreeRef() match { + case tree:TypeDef => tree + case other => + 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.showNameDetailed(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) + } + + def depoly(tp: Type): Type = tp match { + case TempPolyType(tparams, restpe) => PolyType.fromSymbols(tparams, restpe) + case tp => tp + } + + /** 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)) + denot.setAlias(alias) + } + } catch { + case e: MissingRequirementError => throw toTypeError(e) + } + } + + /** 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 + var refinements = Map[TypeName, Type]().withDefaultValue(NoType) + def normalizeToRef(tp: Type): TypeRef = tp match { + case tp @ RefinedType(tp1, name: TypeName) => + refinements = (refinements + (name -> (refinements(name) & tp.info))) + .withDefaultValue(NoType) + normalizeToRef(tp1) + case tp: TypeRef => + tp + case _ => + throw new TypeError(s"unexpected parent type: $tp") + } + denot.parents = parents map normalizeToRef + denot.selfType = selfType + denot.decls = newScope + tparams foreach denot.decls.enter + for ((name, tpe) <- refinements) denot.decls.enter { + val formal = cls.info.member(name).symbol + val bounds = tpe.toAlias(formal) + ctx.newTypeSymbol(cls, name, formal.flags & RetainedTypeArgFlags, bounds) + } + } catch { + case e: MissingRequirementError => throw toTypeError(e) + } + } + } +} |