From 60ab9f8f525d319aa5b6d5052018c6781da036eb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 29 Apr 2015 10:36:16 +0200 Subject: Pickling modularization reorg The pickling package got rather large and confusing with three separate tasks that each had their own conventions: read JVM classfiles, read Scala2 pickle info, read Tasty. The classes for each task are now in separate packages. --- src/dotty/tools/dotc/core/Definitions.scala | 2 +- src/dotty/tools/dotc/core/SymbolLoaders.scala | 2 +- .../dotc/core/classfile/AbstractFileReader.scala | 88 ++ .../tools/dotc/core/classfile/ByteCodecs.scala | 221 ++++ .../dotc/core/classfile/ClassfileConstants.scala | 383 ++++++ .../dotc/core/classfile/ClassfileParser.scala | 1078 +++++++++++++++++ .../dotc/core/pickling/AbstractFileReader.scala | 88 -- .../tools/dotc/core/pickling/ByteCodecs.scala | 221 ---- .../dotc/core/pickling/ClassfileConstants.scala | 382 ------ .../tools/dotc/core/pickling/ClassfileParser.scala | 1078 ----------------- .../tools/dotc/core/pickling/DottyUnpickler.scala | 59 - .../tools/dotc/core/pickling/NameBuffer.scala | 93 -- .../tools/dotc/core/pickling/PickleBuffer.scala | 299 ----- .../tools/dotc/core/pickling/PickleFormat.scala | 491 -------- .../tools/dotc/core/pickling/PositionPickler.scala | 75 -- .../dotc/core/pickling/PositionUnpickler.scala | 37 - .../tools/dotc/core/pickling/Scala2Unpickler.scala | 1216 ------------------- .../tools/dotc/core/pickling/TastyBuffer.scala | 188 --- src/dotty/tools/dotc/core/pickling/TastyName.scala | 30 - .../tools/dotc/core/pickling/TastyPickler.scala | 70 -- .../tools/dotc/core/pickling/TastyPrinter.scala | 122 -- .../tools/dotc/core/pickling/TastyReader.scala | 142 --- .../tools/dotc/core/pickling/TastyUnpickler.scala | 95 -- .../tools/dotc/core/pickling/TreeBuffer.scala | 179 --- .../tools/dotc/core/pickling/TreePickler.scala | 552 --------- .../tools/dotc/core/pickling/TreeUnpickler.scala | 890 -------------- .../tools/dotc/core/tasty/DottyUnpickler.scala | 60 + src/dotty/tools/dotc/core/tasty/NameBuffer.scala | 93 ++ .../tools/dotc/core/tasty/PositionPickler.scala | 75 ++ .../tools/dotc/core/tasty/PositionUnpickler.scala | 38 + src/dotty/tools/dotc/core/tasty/TastyBuffer.scala | 188 +++ src/dotty/tools/dotc/core/tasty/TastyFormat.scala | 491 ++++++++ src/dotty/tools/dotc/core/tasty/TastyName.scala | 30 + src/dotty/tools/dotc/core/tasty/TastyPickler.scala | 70 ++ src/dotty/tools/dotc/core/tasty/TastyPrinter.scala | 122 ++ src/dotty/tools/dotc/core/tasty/TastyReader.scala | 141 +++ .../tools/dotc/core/tasty/TastyUnpickler.scala | 95 ++ src/dotty/tools/dotc/core/tasty/TreeBuffer.scala | 179 +++ src/dotty/tools/dotc/core/tasty/TreePickler.scala | 551 +++++++++ .../tools/dotc/core/tasty/TreeUnpickler.scala | 889 ++++++++++++++ .../dotc/core/unpickleScala2/PickleBuffer.scala | 299 +++++ .../dotc/core/unpickleScala2/Scala2Unpickler.scala | 1217 ++++++++++++++++++++ 42 files changed, 6310 insertions(+), 6309 deletions(-) create mode 100644 src/dotty/tools/dotc/core/classfile/AbstractFileReader.scala create mode 100644 src/dotty/tools/dotc/core/classfile/ByteCodecs.scala create mode 100644 src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala create mode 100644 src/dotty/tools/dotc/core/classfile/ClassfileParser.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/AbstractFileReader.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/ByteCodecs.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/ClassfileParser.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/DottyUnpickler.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/NameBuffer.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/PickleBuffer.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/PickleFormat.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/PositionPickler.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/PositionUnpickler.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/Scala2Unpickler.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/TastyBuffer.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/TastyName.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/TastyPickler.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/TastyPrinter.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/TastyReader.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/TastyUnpickler.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/TreeBuffer.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/TreePickler.scala delete mode 100644 src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala create mode 100644 src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala create mode 100644 src/dotty/tools/dotc/core/tasty/NameBuffer.scala create mode 100644 src/dotty/tools/dotc/core/tasty/PositionPickler.scala create mode 100644 src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala create mode 100644 src/dotty/tools/dotc/core/tasty/TastyBuffer.scala create mode 100644 src/dotty/tools/dotc/core/tasty/TastyFormat.scala create mode 100644 src/dotty/tools/dotc/core/tasty/TastyName.scala create mode 100644 src/dotty/tools/dotc/core/tasty/TastyPickler.scala create mode 100644 src/dotty/tools/dotc/core/tasty/TastyPrinter.scala create mode 100644 src/dotty/tools/dotc/core/tasty/TastyReader.scala create mode 100644 src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala create mode 100644 src/dotty/tools/dotc/core/tasty/TreeBuffer.scala create mode 100644 src/dotty/tools/dotc/core/tasty/TreePickler.scala create mode 100644 src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala create mode 100644 src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala create mode 100644 src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala (limited to 'src/dotty/tools/dotc/core') diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 3c36b1a1e..784773fd3 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -4,7 +4,7 @@ package core import Types._, Contexts._, Symbols._, Denotations._, SymDenotations._, StdNames._, Names._ import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._ -import pickling.Scala2Unpickler.ensureConstructor +import unpickleScala2.Scala2Unpickler.ensureConstructor import scala.annotation.{ switch, meta } import scala.collection.{ mutable, immutable } import PartialFunction._ diff --git a/src/dotty/tools/dotc/core/SymbolLoaders.scala b/src/dotty/tools/dotc/core/SymbolLoaders.scala index 296c4ad1e..7fb44a106 100644 --- a/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -13,7 +13,7 @@ import dotty.tools.io.{ ClassPath, AbstractFile } import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, util.Positions._, Names._ import StdNames._, NameOps._ import Decorators.{StringDecorator, StringInterpolators} -import pickling.ClassfileParser +import classfile.ClassfileParser import scala.util.control.NonFatal object SymbolLoaders { diff --git a/src/dotty/tools/dotc/core/classfile/AbstractFileReader.scala b/src/dotty/tools/dotc/core/classfile/AbstractFileReader.scala new file mode 100644 index 000000000..cad3a4132 --- /dev/null +++ b/src/dotty/tools/dotc/core/classfile/AbstractFileReader.scala @@ -0,0 +1,88 @@ +package dotty.tools +package dotc +package core +package classfile + +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): Unit = { bp += n } + +} diff --git a/src/dotty/tools/dotc/core/classfile/ByteCodecs.scala b/src/dotty/tools/dotc/core/classfile/ByteCodecs.scala new file mode 100644 index 000000000..badd9e560 --- /dev/null +++ b/src/dotty/tools/dotc/core/classfile/ByteCodecs.scala @@ -0,0 +1,221 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2007-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package dotty.tools.dotc.core.classfile + +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/classfile/ClassfileConstants.scala b/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala new file mode 100644 index 000000000..b04ea99ac --- /dev/null +++ b/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala @@ -0,0 +1,383 @@ +package dotty.tools.dotc +package core +package classfile + +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 + + final val CONSTANT_METHODHANDLE = 15 + final val CONSTANT_METHODTYPE = 16 + final val CONSTANT_INVOKEDYNAMIC = 18 + + // 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 => JavaStatic + case JAVA_ACC_ABSTRACT => if (isAnnotation) EmptyFlags else if (isClass) Abstract else Deferred + case JAVA_ACC_INTERFACE => if (isAnnotation) EmptyFlags else PureInterfaceCreationFlags | JavaDefined + 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/classfile/ClassfileParser.scala b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala new file mode 100644 index 000000000..ee5453a8e --- /dev/null +++ b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -0,0 +1,1078 @@ +package dotty.tools +package dotc +package core +package classfile + +import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._ +import SymDenotations._, unpickleScala2.Scala2Unpickler._, Constants._, Annotations._, util.Positions._ +import ast.tpd._ +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 typer.Checking.checkNonCyclic +import io.AbstractFile +import scala.util.control.NonFatal + +object ClassfileParser { + /** Marker trait for unpicklers that can be embedded in classfiles. */ + trait Embedded +} + +class ClassfileParser( + classfile: AbstractFile, + classRoot: ClassDenotation, + moduleRoot: ClassDenotation)(ictx: Context) { + + import ClassfileConstants._ + import ClassfileParser._ + + protected val in = new AbstractFileReader(classfile) + + protected val staticModule: Symbol = moduleRoot.sourceModule(ictx) + + protected val instanceScope: MutableScope = newScope // the scope of all instance definitions + protected val staticScope: MutableScope = 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]() + + classRoot.info = (new NoCompleter).withDecls(instanceScope) + moduleRoot.info = (new NoCompleter).withDecls(staticScope).withSourceModule(_ => staticModule) + + private def currentIsTopLevel(implicit ctx: Context) = classRoot.owner is Flags.PackageClass + + private def mismatchError(c: Symbol) = + throw new IOException(s"class file '${in.file}' has location not matching its contents: contains $c") + + def run()(implicit ctx: Context): Option[Embedded] = try { + ctx.debuglog("[class] >> " + classRoot.fullName) + parseHeader + this.pool = new ConstantPool + parseClass() + } catch { + case e: RuntimeException => + if (ctx.debug) e.printStackTrace() + throw new IOException( + sm"""class file $classfile is broken, reading aborted with ${e.getClass} + |${Option(e.getMessage).getOrElse("")}""") + } + + private def parseHeader(): Unit = { + 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) + } + + /** Return the class symbol of the given name. */ + def classNameToSymbol(name: Name)(implicit ctx: Context): Symbol = innerClasses.get(name) match { + case Some(entry) => innerClasses.classSymbol(entry.externalName) + case None => ctx.requiredClass(name) + } + + var sawPrivateConstructor = false + + def parseClass()(implicit ctx: Context): Option[Embedded] = { + 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() + + /** 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.typeRef } + else pool.getSuperClass(in.nextChar).typeRef + val ifaceCount = in.nextChar + var ifaces = for (i <- (0 until ifaceCount).toList) yield pool.getSuperClass(in.nextChar).typeRef + // Dotty deviation: was + // var ifaces = for (i <- List.range(0 until ifaceCount)) ... + // This does not typecheck because the type parameter of List is now lower-bounded by Int | Char. + // Consequently, no best implicit for the "Integral" evidence parameter of "range" + // is found. If we treat constant subtyping specially, we might be able + // to do something there. But in any case, the until should be more efficient. + + if (isAnnotation) ifaces = defn.ClassfileAnnotationClass.typeRef :: ifaces + superType :: ifaces + } + + val result = unpickleOrParseInnerClasses() + if (!result.isDefined) { + var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol) + // might be reassigned by later parseAttributes + val staticInfo = TempClassInfoType(List(), staticScope, moduleRoot.symbol) + + enterOwnInnerClasses + + classRoot.setFlag(sflags) + moduleRoot.setFlag(Flags.JavaDefined | Flags.ModuleClassCreationFlags) + setPrivateWithin(classRoot, jflags) + setPrivateWithin(moduleRoot, jflags) + setPrivateWithin(moduleRoot.sourceModule, jflags) + + for (i <- 0 until in.nextChar) parseMember(method = false) + for (i <- 0 until in.nextChar) parseMember(method = true) + classInfo = parseAttributes(classRoot.symbol, classInfo) + if (isAnnotation) addAnnotationConstructor(classInfo) + + val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, classRoot, moduleRoot) + if (companionClassMethod.exists) companionClassMethod.entered + val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, moduleRoot, classRoot) + if (companionModuleMethod.exists) companionModuleMethod.entered + + setClassInfo(classRoot, classInfo) + setClassInfo(moduleRoot, staticInfo) + } + result + } + + /** Add type parameters of enclosing classes */ + def addEnclosingTParams()(implicit ctx: Context): Unit = { + var sym = classRoot.owner + while (sym.isClass && !(sym is Flags.ModuleClass)) { + for (tparam <- sym.typeParams) { + classTParams = classTParams.updated(tparam.name.unexpandedName, tparam) + } + sym = sym.owner + } + } + + def parseMember(method: Boolean)(implicit ctx: Context): Unit = { + val start = indexCoord(in.bp) + val jflags = in.nextChar + val sflags = + if (method) Flags.Method | FlagTranslation.methodFlags(jflags) + else FlagTranslation.fieldFlags(jflags) + val name = pool.getName(in.nextChar) + if (!(sflags is Flags.Private) || name == nme.CONSTRUCTOR || ctx.settings.optimise.value) { + val member = ctx.newSymbol( + getOwner(jflags), name, sflags, memberCompleter, coord = start) + getScope(jflags).enter(member) + } + // skip rest of member for now + in.nextChar // info + skipAttributes + } + + val memberCompleter = new LazyType { + + def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { + val oldbp = in.bp + try { + in.bp = denot.symbol.coord.toIndex + val sym = denot.symbol + val jflags = in.nextChar + val isEnum = (jflags & JAVA_ACC_ENUM) != 0 + val name = pool.getName(in.nextChar) + val isConstructor = name eq nme.CONSTRUCTOR + + /** Strip leading outer param from constructor. + * Todo: Also strip trailing access tag for private inner constructors? + */ + def stripOuterParamFromConstructor() = innerClasses.get(currentClassName) match { + case Some(entry) if !isStatic(entry.jflags) => + val mt @ MethodType(paramnames, paramtypes) = denot.info + denot.info = mt.derivedMethodType(paramnames.tail, paramtypes.tail, mt.resultType) + case _ => + } + + /** Make return type of constructor be the enclosing class type, + * and make constructor type polymorphic in the type parameters of the class + */ + def normalizeConstructorInfo() = { + val mt @ MethodType(paramnames, paramtypes) = denot.info + val rt = classRoot.typeRef appliedTo (classRoot.typeParams map (_.typeRef)) + denot.info = mt.derivedMethodType(paramnames, paramtypes, rt) + addConstructorTypeParams(denot) + } + + denot.info = pool.getType(in.nextChar) + if (isEnum) denot.info = ConstantType(Constant(sym)) + if (isConstructor) stripOuterParamFromConstructor() + setPrivateWithin(denot, jflags) + denot.info = depoly(parseAttributes(sym, denot.info), denot) + if (isConstructor) normalizeConstructorInfo() + + 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 + } + } + } + + /** Map direct references to Object to references to Any */ + final def objToAny(tp: Type)(implicit ctx: Context) = + if (tp.isDirectRef(defn.ObjectClass) && !ctx.phase.erasedTypes) defn.AnyType else tp + + private def sigToType(sig: TermName, owner: Symbol = null)(implicit ctx: Context): Type = { + var index = 0 + val end = sig.length + def accept(ch: Char): Unit = { + 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) + } + // Warning: sigToType contains nested completers which might be forced in a later run! + // So local methods need their own ctx parameters. + def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean)(implicit ctx: Context): 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 if !(tp.symbol.owner is Flags.ModuleClass) => + TypeRef(processInner(tp.prefix.widen), tp.name) + case _ => + tp + } + def processClassType(tp: Type): Type = tp match { + case tp: TypeRef => + 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 '+' => objToAny(TypeBounds.upper(sig2type(tparams, skiptvs))) + 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.isDirectRef(defn.AnyClass)) TypeBounds.empty + else TypeBounds.lower(tp) + case '*' => TypeBounds.empty + } + tp1 = RefinedType(tp1, formals.head.name, bounds) + case _ => + tp1 = RefinedType(tp1, 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.typeRef)) + while (sig(index) == '.') { + accept('.') + val name = subName(c => c == ';' || c == '<' || c == '.').toTypeName + val clazz = tpe.member(name).symbol + tpe = processClassType(processInner(clazz.typeRef)) + } + 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.derivesFrom(defn.ObjectClass))) { + elemtp = AndType(elemtp, defn.ObjectType) + } + defn.ArrayType(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 += objToAny(sig2type(tparams, skiptvs)) + } + index += 1 + val restype = sig2type(tparams, skiptvs) + JavaMethodType(paramnames.toList, paramtypes.toList)(_ => restype) + case 'T' => + val n = subName(';'.==).toTypeName + index += 1 + //assert(tparams contains n, s"classTparams = $classTParams, tparams = $tparams, key = $n") + if (skiptvs) defn.AnyType else tparams(n).typeRef + } + } // sig2type(tparams, skiptvs) + + def sig2typeBounds(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = { + val ts = new ListBuffer[Type] + while (sig(index) == ':') { + index += 1 + if (sig(index) != ':') // guard against empty class bound + ts += objToAny(sig2type(tparams, skiptvs)) + } + TypeBounds.upper(((NoType: Type) /: ts)(_ & _) orElse defn.AnyType) + } + + var tparams = classTParams + + def typeParamCompleter(start: Int) = new LazyType { + def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { + val savedIndex = index + try { + index = start + denot.info = + checkNonCyclic( // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles + denot.symbol, + sig2typeBounds(tparams, skiptvs = false), + reportErrors = 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 expname = if (owner.isClass) tpname.expandedName(owner) else tpname + val s = ctx.newSymbol( + owner, expname, owner.typeParamCreationFlags, + typeParamCompleter(index), coord = indexCoord(index)) + if (owner.isClass) owner.asClass.enter(s) + tparams = tparams + (tpname -> s) + sig2typeBounds(tparams, skiptvs = true) + newTParams += s + } + index += 1 + } + 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)(implicit ctx: Context): Option[Tree] = { + val tag = in.nextByte.toChar + val index = in.nextChar + tag match { + case STRING_TAG => + if (skip) None else Some(Literal(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(Literal(pool.getConstant(index))) + case CLASS_TAG => + if (skip) None else Some(Literal(Constant(pool.getType(index)))) + case ENUM_TAG => + val t = pool.getType(index) + val n = pool.getName(in.nextChar) + val module = t.typeSymbol.companionModule + val s = module.info.decls.lookup(n) + if (skip) { + None + } else if (s != NoSymbol) { + Some(Literal(Constant(s))) + } else { + ctx.warning(s"""While parsing annotations in ${in.file}, could not find $n in enum $module.\nThis is likely due to an implementation restriction: an annotation argument cannot refer to a member of the annotated class (SI-7014).""") + None + } + case ARRAY_TAG => + val arr = new ArrayBuffer[Tree]() + 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(JavaSeqLiteral(arr.toList)) + case ANNOTATION_TAG => + parseAnnotation(index, skip) map (_.tree) + } + } + + /** Parse and return a single annotation. If it is malformed, + * return None. + */ + def parseAnnotation(attrNameIndex: Char, skip: Boolean = false)(implicit ctx: Context): Option[Annotation] = try { + val attrType = pool.getType(attrNameIndex) + val nargs = in.nextChar + val argbuf = new ListBuffer[Tree] + var hasError = false + for (i <- 0 until nargs) { + val name = pool.getName(in.nextChar) + parseAnnotArg(skip) match { + case Some(arg) => argbuf += NamedArg(name, arg) + case None => hasError = !skip + } + } + if (hasError || skip) None + else Some(Annotation.deferredResolve(attrType, argbuf.toList)) + } catch { + case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found + case NonFatal(ex) => + // 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. + ctx.warning("Caught: " + ex + " while parsing annotations in " + in.file) + if (ctx.debug) ex.printStackTrace() + + None // ignore malformed annotations + } + + def parseAttributes(sym: Symbol, symtype: Type)(implicit ctx: Context): Type = { + def convertTo(c: Constant, pt: Type): Constant = { + if (pt == defn.BooleanType && c.tag == IntTag) + Constant(c.value != 0) + else + c convertTo pt + } + var newType = symtype + + def parseAttribute(): Unit = { + 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 (ctx.debug && ctx.verbose) + 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 = Literal(Constant("see corresponding Javadoc for more information.")) + val since = 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.CodeATTR => + if (sym.owner is Flags.JavaTrait) { + sym.resetFlag(Flags.Deferred) + sym.owner.resetFlag(Flags.PureInterface) + ctx.log(s"$sym in ${sym.owner} is a java8+ default method.") + } + in.skip(attrLen) + + case _ => + } + in.bp = end + } + + /** + * Parse the "Exceptions" attribute which denotes the exceptions + * thrown by a method. + */ + def parseExceptions(len: Int): Unit = { + 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 + } + + /** Add synthetic constructor(s) and potentially also default getters which + * reflects the fields of the annotation with given `classInfo`. + * Annotations in Scala are assumed to get all their arguments as constructor + * parameters. For Java annotations we need to fake it by making up the constructor. + * Note that default getters have type Nothing. That's OK because we need + * them only to signal that the corresponding parameter is optional. + * If the constructor takes as last parameter an array, it can also accept + * a vararg argument. We solve this by creating two constructors, one with + * an array, the other with a repeated parameter. + */ + def addAnnotationConstructor(classInfo: Type, tparams: List[Symbol] = Nil)(implicit ctx: Context): Unit = { + def addDefaultGetter(attr: Symbol, n: Int) = + ctx.newSymbol( + owner = moduleRoot.symbol, + name = nme.CONSTRUCTOR.defaultGetterName(n), + flags = attr.flags & Flags.AccessFlags, + info = defn.NothingType).entered + + classInfo match { + case classInfo @ TempPolyType(tparams, restpe) if tparams.isEmpty => + addAnnotationConstructor(restpe, tparams) + case classInfo: TempClassInfoType => + val attrs = classInfo.decls.toList.filter(_.isTerm) + val targs = tparams.map(_.typeRef) + val paramNames = attrs.map(_.name.asTermName) + val paramTypes = attrs.map(_.info.resultType) + + def addConstr(ptypes: List[Type]) = { + val mtype = MethodType(paramNames, ptypes, classRoot.typeRef.appliedTo(targs)) + val constrType = if (tparams.isEmpty) mtype else TempPolyType(tparams, mtype) + val constr = ctx.newSymbol( + owner = classRoot.symbol, + name = nme.CONSTRUCTOR, + flags = Flags.Synthetic, + info = constrType + ).entered + for ((attr, i) <- attrs.zipWithIndex) + if (attr.hasAnnotation(defn.AnnotationDefaultAnnot)) { + constr.setFlag(Flags.HasDefaultParams) + addDefaultGetter(attr, i) + } + } + + addConstr(paramTypes) + if (paramTypes.nonEmpty) + paramTypes.last match { + case defn.ArrayType(elemtp) => + addConstr(paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp)) + case _ => + } + + } + } + + /** Enter own inner classes in the right scope. It needs the scopes to be set up, + * and implicitly current class' superclasses. + */ + private def enterOwnInnerClasses()(implicit ctx: Context): Unit = { + def className(name: Name): Name = name.drop(name.lastIndexOf('.') + 1) + + def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile, jflags: Int) = { + ctx.base.loaders.enterClassAndModule( + getOwner(jflags), + entry.originalName, + new ClassfileLoader(file), + FlagTranslation.classFlags(jflags), + getScope(jflags)) + } + + for (entry <- innerClasses.values) { + // create a new class member for immediate inner classes + if (entry.outerName == currentClassName) { + val file = ctx.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()(implicit ctx: Context): Option[Embedded] = { + 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 unpickleScala(bytes: Array[Byte]): Some[Embedded] = { + val unpickler = new unpickleScala2.Scala2Unpickler(bytes, classRoot, moduleRoot)(ctx) + unpickler.run() + Some(unpickler) + } + + def unpickleTASTY(bytes: Array[Byte]): Some[Embedded] = { + val unpickler = new tasty.DottyUnpickler(bytes) + unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule)) + Some(unpickler) + } + + 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.TASTYATTR)) { + val attrLen = in.nextInt + return unpickleTASTY(in.nextBytes(attrLen)) + } + + 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 (argName == nme.bytes) + if (attrClass == defn.ScalaSignatureAnnot) + return unpickleScala(parseScalaSigBytes) + else if (attrClass == defn.ScalaLongSignatureAnnot) + return unpickleScala(parseScalaLongSigBytes) + else if (attrClass == defn.TASTYSignatureAnnot) + return unpickleTASTY(parseScalaSigBytes) + else if (attrClass == defn.TASTYLongSignatureAnnot) + return unpickleTASTY(parseScalaLongSigBytes) + parseAnnotArg(skip = true) + j += 1 + } + i += 1 + } + } + + if (scan(tpnme.InnerClassesATTR)) { + val attrLen = in.nextInt + 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 + } + } + } + None + } 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)(implicit ctx: Context): 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)(implicit ctx: Context): 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.stripModuleClassSuffix + val owner = classSymbol(outerName) + val result = ctx.atPhaseNotLaterThanTyper { implicit ctx => + getMember(owner, innerName.toTypeName) + } + assert(result ne NoSymbol, + sm"""failure to resolve inner class: + |externalName = $externalName, + |outerName = $outerName, + |innerName = $innerName + |owner.fullName = ${owner.showFullName} + |while parsing ${classfile}""") + 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(): Unit = { + val attrCount = in.nextChar + for (i <- 0 until attrCount) { + in.skip(2); in.skip(in.nextInt) + } + } + + def skipMembers(): Unit = { + val memberCount = in.nextChar + for (i <- 0 until memberCount) { + in.skip(6); skipAttributes() + } + } + + def skipSuperclasses(): Unit = { + 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): MutableScope = + if (isStatic(flags)) staticScope else instanceScope + + private def setPrivateWithin(denot: SymDenotation, jflags: Int)(implicit ctx: Context): Unit = { + if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PUBLIC)) == 0) + denot.privateWithin = denot.enclosingPackageClass + } + + 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 + + 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 | CONSTANT_METHODTYPE => + in.skip(2) + case CONSTANT_METHODHANDLE => + in.skip(3) + case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF + | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT + | CONSTANT_INVOKEDYNAMIC => + 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)(implicit ctx: Context): 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.isModuleClassName && (name ne nme.nothingRuntimeClass) && (name ne nme.nullRuntimeClass)) + // Null$ and Nothing$ ARE classes + c = ctx.requiredModule(name.sourceModuleName) + 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)(implicit ctx: Context): (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)(implicit ctx: Context): 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.typeRef + } + } else c = value match { + case tp: Type => tp + case cls: Symbol => cls.typeRef + } + c + } + + def getType(index: Int)(implicit ctx: Context): Type = + sigToType(getExternalName(index)) + + def getSuperClass(index: Int)(implicit ctx: Context): Symbol = { + assert(index != 0, "attempt to parse java.lang.Object from classfile") + getClassSymbol(index) + } + + def getConstant(index: Int)(implicit ctx: Context): 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.typeRef) + 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) + } +} + diff --git a/src/dotty/tools/dotc/core/pickling/AbstractFileReader.scala b/src/dotty/tools/dotc/core/pickling/AbstractFileReader.scala deleted file mode 100644 index dbde8548f..000000000 --- a/src/dotty/tools/dotc/core/pickling/AbstractFileReader.scala +++ /dev/null @@ -1,88 +0,0 @@ -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): Unit = { bp += n } - -} diff --git a/src/dotty/tools/dotc/core/pickling/ByteCodecs.scala b/src/dotty/tools/dotc/core/pickling/ByteCodecs.scala deleted file mode 100644 index 8b3e49bd0..000000000 --- a/src/dotty/tools/dotc/core/pickling/ByteCodecs.scala +++ /dev/null @@ -1,221 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ 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/ClassfileConstants.scala b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala deleted file mode 100644 index 158f6b409..000000000 --- a/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala +++ /dev/null @@ -1,382 +0,0 @@ -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 - - final val CONSTANT_METHODHANDLE = 15 - final val CONSTANT_METHODTYPE = 16 - final val CONSTANT_INVOKEDYNAMIC = 18 - - // 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 => JavaStatic - case JAVA_ACC_ABSTRACT => if (isAnnotation) EmptyFlags else if (isClass) Abstract else Deferred - case JAVA_ACC_INTERFACE => if (isAnnotation) EmptyFlags else PureInterfaceCreationFlags | JavaDefined - 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 deleted file mode 100644 index 918a97476..000000000 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ /dev/null @@ -1,1078 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._ -import SymDenotations._, Scala2Unpickler._, Constants._, Annotations._, util.Positions._ -import ast.tpd._ -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 typer.Checking.checkNonCyclic -import io.AbstractFile -import scala.util.control.NonFatal - -object ClassfileParser { - /** Marker trait for unpicklers that can be embedded in classfiles. */ - trait Embedded -} - -class ClassfileParser( - classfile: AbstractFile, - classRoot: ClassDenotation, - moduleRoot: ClassDenotation)(ictx: Context) { - - import ClassfileConstants._ - import ClassfileParser._ - - protected val in = new AbstractFileReader(classfile) - - protected val staticModule: Symbol = moduleRoot.sourceModule(ictx) - - protected val instanceScope: MutableScope = newScope // the scope of all instance definitions - protected val staticScope: MutableScope = 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]() - - classRoot.info = (new NoCompleter).withDecls(instanceScope) - moduleRoot.info = (new NoCompleter).withDecls(staticScope).withSourceModule(_ => staticModule) - - private def currentIsTopLevel(implicit ctx: Context) = classRoot.owner is Flags.PackageClass - - private def mismatchError(c: Symbol) = - throw new IOException(s"class file '${in.file}' has location not matching its contents: contains $c") - - def run()(implicit ctx: Context): Option[Embedded] = try { - ctx.debuglog("[class] >> " + classRoot.fullName) - parseHeader - this.pool = new ConstantPool - parseClass() - } catch { - case e: RuntimeException => - if (ctx.debug) e.printStackTrace() - throw new IOException( - sm"""class file $classfile is broken, reading aborted with ${e.getClass} - |${Option(e.getMessage).getOrElse("")}""") - } - - private def parseHeader(): Unit = { - 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) - } - - /** Return the class symbol of the given name. */ - def classNameToSymbol(name: Name)(implicit ctx: Context): Symbol = innerClasses.get(name) match { - case Some(entry) => innerClasses.classSymbol(entry.externalName) - case None => ctx.requiredClass(name) - } - - var sawPrivateConstructor = false - - def parseClass()(implicit ctx: Context): Option[Embedded] = { - 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() - - /** 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.typeRef } - else pool.getSuperClass(in.nextChar).typeRef - val ifaceCount = in.nextChar - var ifaces = for (i <- (0 until ifaceCount).toList) yield pool.getSuperClass(in.nextChar).typeRef - // Dotty deviation: was - // var ifaces = for (i <- List.range(0 until ifaceCount)) ... - // This does not typecheck because the type parameter of List is now lower-bounded by Int | Char. - // Consequently, no best implicit for the "Integral" evidence parameter of "range" - // is found. If we treat constant subtyping specially, we might be able - // to do something there. But in any case, the until should be more efficient. - - if (isAnnotation) ifaces = defn.ClassfileAnnotationClass.typeRef :: ifaces - superType :: ifaces - } - - val result = unpickleOrParseInnerClasses() - if (!result.isDefined) { - var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol) - // might be reassigned by later parseAttributes - val staticInfo = TempClassInfoType(List(), staticScope, moduleRoot.symbol) - - enterOwnInnerClasses - - classRoot.setFlag(sflags) - moduleRoot.setFlag(Flags.JavaDefined | Flags.ModuleClassCreationFlags) - setPrivateWithin(classRoot, jflags) - setPrivateWithin(moduleRoot, jflags) - setPrivateWithin(moduleRoot.sourceModule, jflags) - - for (i <- 0 until in.nextChar) parseMember(method = false) - for (i <- 0 until in.nextChar) parseMember(method = true) - classInfo = parseAttributes(classRoot.symbol, classInfo) - if (isAnnotation) addAnnotationConstructor(classInfo) - - val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, classRoot, moduleRoot) - if (companionClassMethod.exists) companionClassMethod.entered - val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, moduleRoot, classRoot) - if (companionModuleMethod.exists) companionModuleMethod.entered - - setClassInfo(classRoot, classInfo) - setClassInfo(moduleRoot, staticInfo) - } - result - } - - /** Add type parameters of enclosing classes */ - def addEnclosingTParams()(implicit ctx: Context): Unit = { - var sym = classRoot.owner - while (sym.isClass && !(sym is Flags.ModuleClass)) { - for (tparam <- sym.typeParams) { - classTParams = classTParams.updated(tparam.name.unexpandedName, tparam) - } - sym = sym.owner - } - } - - def parseMember(method: Boolean)(implicit ctx: Context): Unit = { - val start = indexCoord(in.bp) - val jflags = in.nextChar - val sflags = - if (method) Flags.Method | FlagTranslation.methodFlags(jflags) - else FlagTranslation.fieldFlags(jflags) - val name = pool.getName(in.nextChar) - if (!(sflags is Flags.Private) || name == nme.CONSTRUCTOR || ctx.settings.optimise.value) { - val member = ctx.newSymbol( - getOwner(jflags), name, sflags, memberCompleter, coord = start) - getScope(jflags).enter(member) - } - // skip rest of member for now - in.nextChar // info - skipAttributes - } - - val memberCompleter = new LazyType { - - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - val oldbp = in.bp - try { - in.bp = denot.symbol.coord.toIndex - val sym = denot.symbol - val jflags = in.nextChar - val isEnum = (jflags & JAVA_ACC_ENUM) != 0 - val name = pool.getName(in.nextChar) - val isConstructor = name eq nme.CONSTRUCTOR - - /** Strip leading outer param from constructor. - * Todo: Also strip trailing access tag for private inner constructors? - */ - def stripOuterParamFromConstructor() = innerClasses.get(currentClassName) match { - case Some(entry) if !isStatic(entry.jflags) => - val mt @ MethodType(paramnames, paramtypes) = denot.info - denot.info = mt.derivedMethodType(paramnames.tail, paramtypes.tail, mt.resultType) - case _ => - } - - /** Make return type of constructor be the enclosing class type, - * and make constructor type polymorphic in the type parameters of the class - */ - def normalizeConstructorInfo() = { - val mt @ MethodType(paramnames, paramtypes) = denot.info - val rt = classRoot.typeRef appliedTo (classRoot.typeParams map (_.typeRef)) - denot.info = mt.derivedMethodType(paramnames, paramtypes, rt) - addConstructorTypeParams(denot) - } - - denot.info = pool.getType(in.nextChar) - if (isEnum) denot.info = ConstantType(Constant(sym)) - if (isConstructor) stripOuterParamFromConstructor() - setPrivateWithin(denot, jflags) - denot.info = depoly(parseAttributes(sym, denot.info), denot) - if (isConstructor) normalizeConstructorInfo() - - 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 - } - } - } - - /** Map direct references to Object to references to Any */ - final def objToAny(tp: Type)(implicit ctx: Context) = - if (tp.isDirectRef(defn.ObjectClass) && !ctx.phase.erasedTypes) defn.AnyType else tp - - private def sigToType(sig: TermName, owner: Symbol = null)(implicit ctx: Context): Type = { - var index = 0 - val end = sig.length - def accept(ch: Char): Unit = { - 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) - } - // Warning: sigToType contains nested completers which might be forced in a later run! - // So local methods need their own ctx parameters. - def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean)(implicit ctx: Context): 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 if !(tp.symbol.owner is Flags.ModuleClass) => - TypeRef(processInner(tp.prefix.widen), tp.name) - case _ => - tp - } - def processClassType(tp: Type): Type = tp match { - case tp: TypeRef => - 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 '+' => objToAny(TypeBounds.upper(sig2type(tparams, skiptvs))) - 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.isDirectRef(defn.AnyClass)) TypeBounds.empty - else TypeBounds.lower(tp) - case '*' => TypeBounds.empty - } - tp1 = RefinedType(tp1, formals.head.name, bounds) - case _ => - tp1 = RefinedType(tp1, 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.typeRef)) - while (sig(index) == '.') { - accept('.') - val name = subName(c => c == ';' || c == '<' || c == '.').toTypeName - val clazz = tpe.member(name).symbol - tpe = processClassType(processInner(clazz.typeRef)) - } - 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.derivesFrom(defn.ObjectClass))) { - elemtp = AndType(elemtp, defn.ObjectType) - } - defn.ArrayType(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 += objToAny(sig2type(tparams, skiptvs)) - } - index += 1 - val restype = sig2type(tparams, skiptvs) - JavaMethodType(paramnames.toList, paramtypes.toList)(_ => restype) - case 'T' => - val n = subName(';'.==).toTypeName - index += 1 - //assert(tparams contains n, s"classTparams = $classTParams, tparams = $tparams, key = $n") - if (skiptvs) defn.AnyType else tparams(n).typeRef - } - } // sig2type(tparams, skiptvs) - - def sig2typeBounds(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = { - val ts = new ListBuffer[Type] - while (sig(index) == ':') { - index += 1 - if (sig(index) != ':') // guard against empty class bound - ts += objToAny(sig2type(tparams, skiptvs)) - } - TypeBounds.upper(((NoType: Type) /: ts)(_ & _) orElse defn.AnyType) - } - - var tparams = classTParams - - def typeParamCompleter(start: Int) = new LazyType { - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - val savedIndex = index - try { - index = start - denot.info = - checkNonCyclic( // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles - denot.symbol, - sig2typeBounds(tparams, skiptvs = false), - reportErrors = 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 expname = if (owner.isClass) tpname.expandedName(owner) else tpname - val s = ctx.newSymbol( - owner, expname, owner.typeParamCreationFlags, - typeParamCompleter(index), coord = indexCoord(index)) - if (owner.isClass) owner.asClass.enter(s) - tparams = tparams + (tpname -> s) - sig2typeBounds(tparams, skiptvs = true) - newTParams += s - } - index += 1 - } - 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)(implicit ctx: Context): Option[Tree] = { - val tag = in.nextByte.toChar - val index = in.nextChar - tag match { - case STRING_TAG => - if (skip) None else Some(Literal(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(Literal(pool.getConstant(index))) - case CLASS_TAG => - if (skip) None else Some(Literal(Constant(pool.getType(index)))) - case ENUM_TAG => - val t = pool.getType(index) - val n = pool.getName(in.nextChar) - val module = t.typeSymbol.companionModule - val s = module.info.decls.lookup(n) - if (skip) { - None - } else if (s != NoSymbol) { - Some(Literal(Constant(s))) - } else { - ctx.warning(s"""While parsing annotations in ${in.file}, could not find $n in enum $module.\nThis is likely due to an implementation restriction: an annotation argument cannot refer to a member of the annotated class (SI-7014).""") - None - } - case ARRAY_TAG => - val arr = new ArrayBuffer[Tree]() - 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(JavaSeqLiteral(arr.toList)) - case ANNOTATION_TAG => - parseAnnotation(index, skip) map (_.tree) - } - } - - /** Parse and return a single annotation. If it is malformed, - * return None. - */ - def parseAnnotation(attrNameIndex: Char, skip: Boolean = false)(implicit ctx: Context): Option[Annotation] = try { - val attrType = pool.getType(attrNameIndex) - val nargs = in.nextChar - val argbuf = new ListBuffer[Tree] - var hasError = false - for (i <- 0 until nargs) { - val name = pool.getName(in.nextChar) - parseAnnotArg(skip) match { - case Some(arg) => argbuf += NamedArg(name, arg) - case None => hasError = !skip - } - } - if (hasError || skip) None - else Some(Annotation.deferredResolve(attrType, argbuf.toList)) - } catch { - case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found - case NonFatal(ex) => - // 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. - ctx.warning("Caught: " + ex + " while parsing annotations in " + in.file) - if (ctx.debug) ex.printStackTrace() - - None // ignore malformed annotations - } - - def parseAttributes(sym: Symbol, symtype: Type)(implicit ctx: Context): Type = { - def convertTo(c: Constant, pt: Type): Constant = { - if (pt == defn.BooleanType && c.tag == IntTag) - Constant(c.value != 0) - else - c convertTo pt - } - var newType = symtype - - def parseAttribute(): Unit = { - 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 (ctx.debug && ctx.verbose) - 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 = Literal(Constant("see corresponding Javadoc for more information.")) - val since = 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.CodeATTR => - if (sym.owner is Flags.JavaTrait) { - sym.resetFlag(Flags.Deferred) - sym.owner.resetFlag(Flags.PureInterface) - ctx.log(s"$sym in ${sym.owner} is a java8+ default method.") - } - in.skip(attrLen) - - case _ => - } - in.bp = end - } - - /** - * Parse the "Exceptions" attribute which denotes the exceptions - * thrown by a method. - */ - def parseExceptions(len: Int): Unit = { - 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 - } - - /** Add synthetic constructor(s) and potentially also default getters which - * reflects the fields of the annotation with given `classInfo`. - * Annotations in Scala are assumed to get all their arguments as constructor - * parameters. For Java annotations we need to fake it by making up the constructor. - * Note that default getters have type Nothing. That's OK because we need - * them only to signal that the corresponding parameter is optional. - * If the constructor takes as last parameter an array, it can also accept - * a vararg argument. We solve this by creating two constructors, one with - * an array, the other with a repeated parameter. - */ - def addAnnotationConstructor(classInfo: Type, tparams: List[Symbol] = Nil)(implicit ctx: Context): Unit = { - def addDefaultGetter(attr: Symbol, n: Int) = - ctx.newSymbol( - owner = moduleRoot.symbol, - name = nme.CONSTRUCTOR.defaultGetterName(n), - flags = attr.flags & Flags.AccessFlags, - info = defn.NothingType).entered - - classInfo match { - case classInfo @ TempPolyType(tparams, restpe) if tparams.isEmpty => - addAnnotationConstructor(restpe, tparams) - case classInfo: TempClassInfoType => - val attrs = classInfo.decls.toList.filter(_.isTerm) - val targs = tparams.map(_.typeRef) - val paramNames = attrs.map(_.name.asTermName) - val paramTypes = attrs.map(_.info.resultType) - - def addConstr(ptypes: List[Type]) = { - val mtype = MethodType(paramNames, ptypes, classRoot.typeRef.appliedTo(targs)) - val constrType = if (tparams.isEmpty) mtype else TempPolyType(tparams, mtype) - val constr = ctx.newSymbol( - owner = classRoot.symbol, - name = nme.CONSTRUCTOR, - flags = Flags.Synthetic, - info = constrType - ).entered - for ((attr, i) <- attrs.zipWithIndex) - if (attr.hasAnnotation(defn.AnnotationDefaultAnnot)) { - constr.setFlag(Flags.HasDefaultParams) - addDefaultGetter(attr, i) - } - } - - addConstr(paramTypes) - if (paramTypes.nonEmpty) - paramTypes.last match { - case defn.ArrayType(elemtp) => - addConstr(paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp)) - case _ => - } - - } - } - - /** Enter own inner classes in the right scope. It needs the scopes to be set up, - * and implicitly current class' superclasses. - */ - private def enterOwnInnerClasses()(implicit ctx: Context): Unit = { - def className(name: Name): Name = name.drop(name.lastIndexOf('.') + 1) - - def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile, jflags: Int) = { - ctx.base.loaders.enterClassAndModule( - getOwner(jflags), - entry.originalName, - new ClassfileLoader(file), - FlagTranslation.classFlags(jflags), - getScope(jflags)) - } - - for (entry <- innerClasses.values) { - // create a new class member for immediate inner classes - if (entry.outerName == currentClassName) { - val file = ctx.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()(implicit ctx: Context): Option[Embedded] = { - 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 unpickleScala(bytes: Array[Byte]): Some[Embedded] = { - val unpickler = new Scala2Unpickler(bytes, classRoot, moduleRoot)(ctx) - unpickler.run() - Some(unpickler) - } - - def unpickleTASTY(bytes: Array[Byte]): Some[Embedded] = { - val unpickler = new DottyUnpickler(bytes) - unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule)) - Some(unpickler) - } - - 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.TASTYATTR)) { - val attrLen = in.nextInt - return unpickleTASTY(in.nextBytes(attrLen)) - } - - 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 (argName == nme.bytes) - if (attrClass == defn.ScalaSignatureAnnot) - return unpickleScala(parseScalaSigBytes) - else if (attrClass == defn.ScalaLongSignatureAnnot) - return unpickleScala(parseScalaLongSigBytes) - else if (attrClass == defn.TASTYSignatureAnnot) - return unpickleTASTY(parseScalaSigBytes) - else if (attrClass == defn.TASTYLongSignatureAnnot) - return unpickleTASTY(parseScalaLongSigBytes) - parseAnnotArg(skip = true) - j += 1 - } - i += 1 - } - } - - if (scan(tpnme.InnerClassesATTR)) { - val attrLen = in.nextInt - 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 - } - } - } - None - } 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)(implicit ctx: Context): 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)(implicit ctx: Context): 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.stripModuleClassSuffix - val owner = classSymbol(outerName) - val result = ctx.atPhaseNotLaterThanTyper { implicit ctx => - getMember(owner, innerName.toTypeName) - } - assert(result ne NoSymbol, - sm"""failure to resolve inner class: - |externalName = $externalName, - |outerName = $outerName, - |innerName = $innerName - |owner.fullName = ${owner.showFullName} - |while parsing ${classfile}""") - 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(): Unit = { - val attrCount = in.nextChar - for (i <- 0 until attrCount) { - in.skip(2); in.skip(in.nextInt) - } - } - - def skipMembers(): Unit = { - val memberCount = in.nextChar - for (i <- 0 until memberCount) { - in.skip(6); skipAttributes() - } - } - - def skipSuperclasses(): Unit = { - 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): MutableScope = - if (isStatic(flags)) staticScope else instanceScope - - private def setPrivateWithin(denot: SymDenotation, jflags: Int)(implicit ctx: Context): Unit = { - if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PUBLIC)) == 0) - denot.privateWithin = denot.enclosingPackageClass - } - - 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 - - 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 | CONSTANT_METHODTYPE => - in.skip(2) - case CONSTANT_METHODHANDLE => - in.skip(3) - case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF - | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT - | CONSTANT_INVOKEDYNAMIC => - 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)(implicit ctx: Context): 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.isModuleClassName && (name ne nme.nothingRuntimeClass) && (name ne nme.nullRuntimeClass)) - // Null$ and Nothing$ ARE classes - c = ctx.requiredModule(name.sourceModuleName) - 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)(implicit ctx: Context): (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)(implicit ctx: Context): 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.typeRef - } - } else c = value match { - case tp: Type => tp - case cls: Symbol => cls.typeRef - } - c - } - - def getType(index: Int)(implicit ctx: Context): Type = - sigToType(getExternalName(index)) - - def getSuperClass(index: Int)(implicit ctx: Context): Symbol = { - assert(index != 0, "attempt to parse java.lang.Object from classfile") - getClassSymbol(index) - } - - def getConstant(index: Int)(implicit ctx: Context): 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.typeRef) - 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) - } -} - diff --git a/src/dotty/tools/dotc/core/pickling/DottyUnpickler.scala b/src/dotty/tools/dotc/core/pickling/DottyUnpickler.scala deleted file mode 100644 index 2d8f571ec..000000000 --- a/src/dotty/tools/dotc/core/pickling/DottyUnpickler.scala +++ /dev/null @@ -1,59 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import Contexts._, SymDenotations._ -import dotty.tools.dotc.ast.tpd -import TastyUnpickler._, TastyBuffer._ -import util.Positions._ -import util.{SourceFile, NoSource} -import PositionUnpickler._ - -object DottyUnpickler { - - /** Exception thrown if classfile is corrupted */ - class BadSignature(msg: String) extends RuntimeException(msg) -} - -/** A class for unpickling Tasty trees and symbols. - * @param bytes the bytearray containing the Tasty file from which we unpickle - */ -class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded { - import tpd._ - - private val unpickler = new TastyUnpickler(bytes) - private val treeUnpickler = unpickler.unpickle(new TreeSectionUnpickler).get - - /** Enter all toplevel classes and objects into their scopes - * @param roots a set of SymDenotations that should be overwritten by unpickling - */ - def enter(roots: Set[SymDenotation])(implicit ctx: Context): Unit = - treeUnpickler.enterTopLevel(roots) - - /** The unpickled trees, and the source file they come from - * @param readPositions if true, trees get decorated with position information. - */ - def body(readPositions: Boolean = false)(implicit ctx: Context): (List[Tree], SourceFile) = { - val source = unpickler.unpickle(new SourceFileUnpickler).getOrElse(NoSource) - if (readPositions) - for ((totalRange, positions) <- unpickler.unpickle(new PositionsSectionUnpickler)) - treeUnpickler.usePositions(totalRange, positions) - (treeUnpickler.unpickle(), source) - } - - private class SourceFileUnpickler extends SectionUnpickler[SourceFile]("Sourcefile") { - def unpickle(reader: TastyReader, tastyName: TastyName.Table) = - new SourceFile(tastyName(reader.readNameRef()).toString, Seq()) - } - - private class TreeSectionUnpickler extends SectionUnpickler[TreeUnpickler]("ASTs") { - def unpickle(reader: TastyReader, tastyName: TastyName.Table) = - new TreeUnpickler(reader, tastyName) - } - - private class PositionsSectionUnpickler extends SectionUnpickler[(Position, AddrToPosition)]("Positions") { - def unpickle(reader: TastyReader, tastyName: TastyName.Table) = - new PositionUnpickler(reader).unpickle() - } -} diff --git a/src/dotty/tools/dotc/core/pickling/NameBuffer.scala b/src/dotty/tools/dotc/core/pickling/NameBuffer.scala deleted file mode 100644 index 7ea94089f..000000000 --- a/src/dotty/tools/dotc/core/pickling/NameBuffer.scala +++ /dev/null @@ -1,93 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import collection.mutable -import Names.{Name, chrs} -import Decorators._, NameOps._ -import TastyBuffer._ -import scala.io.Codec -import TastyName._ -import PickleFormat._ - -class NameBuffer extends TastyBuffer(10000) { - - private val nameRefs = new mutable.LinkedHashMap[TastyName, NameRef] - - def nameIndex(name: TastyName): NameRef = nameRefs.get(name) match { - case Some(ref) => - ref - case None => - val ref = NameRef(nameRefs.size) - nameRefs(name) = ref - ref - } - def nameIndex(name: Name): NameRef = { - val tname = - if (name.isShadowedName) Shadowed(nameIndex(name.revertShadowed)) - else Simple(name.toTermName) - nameIndex(tname) - } - - def nameIndex(str: String): NameRef = nameIndex(str.toTermName) - - def fullNameIndex(name: Name): NameRef = { - val pos = name.lastIndexOf('.') - if (pos > 0) - nameIndex(Qualified(fullNameIndex(name.take(pos)), nameIndex(name.drop(pos + 1)))) - else - nameIndex(name) - } - - private def withLength(op: => Unit): Unit = { - val lengthAddr = currentAddr - writeByte(0) - op - val length = currentAddr.index - lengthAddr.index - 1 - assert(length < 128) - putNat(lengthAddr, length, 1) - } - - def writeNameRef(ref: NameRef) = writeNat(ref.index) - - def pickleName(name: TastyName): Unit = name match { - case Simple(name) => - val bytes = - if (name.length == 0) new Array[Byte](0) - else Codec.toUTF8(chrs, name.start, name.length) - writeByte(UTF8) - writeNat(bytes.length) - writeBytes(bytes, bytes.length) - case Qualified(qualified, selector) => - writeByte(QUALIFIED) - withLength { writeNameRef(qualified); writeNameRef(selector) } - case Signed(original, params, result) => - writeByte(SIGNED) - withLength { writeNameRef(original); writeNameRef(result); params.foreach(writeNameRef) } - case Expanded(prefix, original) => - writeByte(EXPANDED) - withLength { writeNameRef(prefix); writeNameRef(original) } - case ModuleClass(module) => - writeByte(OBJECTCLASS) - withLength { writeNameRef(module) } - case SuperAccessor(accessed) => - writeByte(SUPERACCESSOR) - withLength { writeNameRef(accessed) } - case DefaultGetter(method, paramNumber) => - writeByte(DEFAULTGETTER) - withLength { writeNameRef(method); writeNat(paramNumber) } - case Shadowed(original) => - writeByte(SHADOWED) - withLength { writeNameRef(original) } - } - - override def assemble(): Unit = { - var i = 0 - for ((name, ref) <- nameRefs) { - assert(ref.index == i) - i += 1 - pickleName(name) - } - } -} diff --git a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala deleted file mode 100644 index 33ba4439b..000000000 --- a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala +++ /dev/null @@ -1,299 +0,0 @@ -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(): Unit = { - 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): Unit = { - 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): Unit = - 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): Unit = { - def writeNatPrefix(x: Long): Unit = { - 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 x at position pos. - * If number is more than one byte, shift rest of array to make space. - * - * @param pos ... - * @param x ... - */ - def patchNat(pos: Int, x: Int): Unit = { - def patchNatPrefix(x: Int): Unit = { - 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 x in signed big endian format, base 256. - * - * @param x The long number to be written. - */ - def writeLong(x: Long): Unit = { - 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 op until the condition - * readIndex == end 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 op 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) - - /** 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 - } -} - -object PickleBuffer { - - private final val ScalaFlagEnd = 48 - private final val ChunkBits = 8 - private final val ChunkSize = 1 << ChunkBits - private type FlagMap = Array[Array[Long]] - - private val (scalaTermFlagMap, scalaTypeFlagMap) = { - import scala.reflect.internal.Flags._ - - // The following vals are copy-pasted from reflect.internal.Flags. - // They are unfortunately private there, so we cannot get at them directly. - // Using the public method pickledToRawFlags instead looks unattractive - // because of performance. - val IMPLICIT_PKL = (1 << 0) - val FINAL_PKL = (1 << 1) - val PRIVATE_PKL = (1 << 2) - val PROTECTED_PKL = (1 << 3) - val SEALED_PKL = (1 << 4) - val OVERRIDE_PKL = (1 << 5) - val CASE_PKL = (1 << 6) - val ABSTRACT_PKL = (1 << 7) - val DEFERRED_PKL = (1 << 8) - val METHOD_PKL = (1 << 9) - val MODULE_PKL = (1 << 10) - val INTERFACE_PKL = (1 << 11) - - val corr = Map( - PROTECTED_PKL -> Protected, - OVERRIDE_PKL -> Override, - PRIVATE_PKL -> Private, - ABSTRACT_PKL -> Abstract, - DEFERRED_PKL -> Deferred, - FINAL_PKL -> Final, - METHOD_PKL -> Method, - INTERFACE_PKL -> PureInterface, - MODULE_PKL -> Module, - IMPLICIT_PKL -> Implicit, - SEALED_PKL -> Sealed, - CASE_PKL -> Case, - MUTABLE -> Mutable, - PARAM -> Param, - PACKAGE -> Package, - MACRO -> Macro, - BYNAMEPARAM -> (Method, Covariant), - LABEL -> (Label, Contravariant), - ABSOVERRIDE -> AbsOverride, - LOCAL -> Local, - JAVA -> JavaDefined, - SYNTHETIC -> Synthetic, - STABLE -> Stable, - STATIC -> JavaStatic, - CASEACCESSOR -> CaseAccessor, - DEFAULTPARAM -> (DefaultParameterized, Trait), - BRIDGE -> Bridge, - ACCESSOR -> Accessor, - SUPERACCESSOR -> SuperAccessor, - PARAMACCESSOR -> ParamAccessor, - MODULEVAR -> Scala2ModuleVar, - LAZY -> Lazy, - MIXEDIN -> (MixedIn, Scala2Existential), - EXPANDEDNAME -> ExpandedName, - IMPLCLASS -> (Scala2PreSuper, ImplClass), - SPECIALIZED -> Specialized, - VBRIDGE -> VBridge, - VARARGS -> JavaVarargs, - ENUM -> Enum) - - // generate initial maps from Scala flags to Dotty flags - val termMap, typeMap = new Array[Long](64) - for (idx <- 0 until ScalaFlagEnd) - corr get (1L << idx) match { - case Some((termFlag: FlagSet, typeFlag: FlagSet)) => - termMap(idx) |= termFlag.bits - typeMap(idx) |= typeFlag.bits - case Some(commonFlag: FlagSet) => - termMap(idx) |= commonFlag.toTermFlags.bits - typeMap(idx) |= commonFlag.toTypeFlags.bits - case _ => - } - - // Convert map so that it maps chunks of ChunkBits size at once - // instead of single bits. - def chunkMap(xs: Array[Long]): FlagMap = { - val chunked = Array.ofDim[Long]( - (xs.length + ChunkBits - 1) / ChunkBits, ChunkSize) - for (i <- 0 until chunked.length) - for (j <- 0 until ChunkSize) - for (k <- 0 until ChunkBits) - if ((j & (1 << k)) != 0) - chunked(i)(j) |= xs(i * ChunkBits + k) - chunked - } - - (chunkMap(termMap), chunkMap(typeMap)) - } - - def unpickleScalaFlags(sflags: Long, isType: Boolean): FlagSet = { - val map: FlagMap = if (isType) scalaTypeFlagMap else scalaTermFlagMap - val shift = ChunkBits - val mask = ChunkSize - 1 - assert(6 * ChunkBits == ScalaFlagEnd) - FlagSet( - map(0)((sflags >>> (shift * 0)).toInt & mask) | - map(1)((sflags >>> (shift * 1)).toInt & mask) | - map(2)((sflags >>> (shift * 2)).toInt & mask) | - map(3)((sflags >>> (shift * 3)).toInt & mask) | - map(4)((sflags >>> (shift * 4)).toInt & mask) | - map(5)((sflags >>> (shift * 5)).toInt & mask) - ) - } -} diff --git a/src/dotty/tools/dotc/core/pickling/PickleFormat.scala b/src/dotty/tools/dotc/core/pickling/PickleFormat.scala deleted file mode 100644 index 4f3841212..000000000 --- a/src/dotty/tools/dotc/core/pickling/PickleFormat.scala +++ /dev/null @@ -1,491 +0,0 @@ -package dotty.tools.dotc -package core -package pickling - -/************************************************************ -Notation: - -We use BNF notation. Terminal symbols start with at least two -consecutive upper case letters. Each terminal is represented as a -single byte tag. Non-terminals are mixed case. Prefixes of the form -lower case letter*_ are for explanation of semantic content only, they -can be dropped without changing the grammar. - -Micro-syntax: - - LongInt = Digit* StopDigit // big endian 2's complement, value fits in a Long w/o overflow - Int = LongInt // big endian 2's complement, fits in an Int w/o overflow - Nat = LongInt // non-negative value, fits in an Int without overflow - Digit = 0 | ... | 127 - StopDigit = 128 | ... | 255 // value = digit - 128 - -Macro-format: - - File = Header majorVersion_Nat minorVersion_Nat UUID - nameTable_Length Name* Section* - Header = 0x5CA1AB1F - UUID = Byte*16 // random UUID - - Section = NameRef Length Bytes - Length = Nat // length of rest of entry in bytes - - Name = UTF8 Length UTF8-CodePoint* - QUALIFIED Length qualified_NameRef selector_NameRef - SIGNED Length original_NameRef resultSig_NameRef paramSig_NameRef* - EXPANDED Length original_NameRef - OBJECTCLASS Length module_NameRef - SUPERACCESSOR Length accessed_NameRef - DEFAULTGETTER Length method_NameRef paramNumber_Nat - SHADOWED Length original_NameRef - MANGLED Length mangle_NameRef name_NameRef - ... - - NameRef = Nat // ordinal number of name in name table, starting from 1. - -Note: Unqualified names in the name table are strings. The context decides whether a name is -a type-name or a term-name. The same string can represent both. - -Standard-Section: "ASTs" TopLevelStat* - - TopLevelStat = PACKAGE Length Path TopLevelStat* - Stat - - Stat = Term - VALDEF Length NameRef Type rhs_Term? Modifier* - DEFDEF Length NameRef TypeParam* Params* return_Type rhs_Term? - Modifier* - TYPEDEF Length NameRef (Type | Template) Modifier* - IMPORT Length qual_Term Selector* - Selector = IMPORTED name_NameRef - RENAMED Length from_NameRef to_NameRef - // Imports are for scala.meta, they are not used in the backend - - TypeParam = TYPEPARAM Length NameRef Type Modifier* - Params = PARAMS Length Param* - Param = PARAM Length NameRef Type rhs_Term? Modifier* // rhs_Term is present in the case of an aliased class parameter - Template = TEMPLATE Length TypeParam* Param* Parent* Self? Stat* // Stat* always starts with the primary constructor. - Parent = Application - Type - Self = SELFDEF selfName_NameRef selfType_Type - - Term = Path - Application - IDENT NameRef Type // used when ident’s type is not a TermRef - SELECT possiblySigned_NameRef qual_Term - NEW cls_Type - SUPER Length this_Term mixinTrait_Type? - PAIR Length left_Term right_Term - TYPED Length expr_Term ascription_Type - NAMEDARG Length paramName_NameRef arg_Term - ASSIGN Length lhs_Term rhs_Term - BLOCK Length expr_Term Stat* - LAMBDA Length meth_Term target_Type - IF Length cond_Term then_Term else_Term - MATCH Length sel_Term CaseDef* - TRY Length expr_Term CaseDef* finalizer_Term? - RETURN Length meth_ASTRef expr_Term? - REPEATED Length elem_Term* - BIND Length boundName_NameRef patType_Type pat_Term - ALTERNATIVE Length alt_Term* - UNAPPLY Length fun_Term ImplicitArg* pat_Type pat_Term* - EMPTYTREE - SHARED term_ASTRef - Application = APPLY Length fn_Term arg_Term* - - TYPEAPPLY Length fn_Term arg_Type* - CaseDef = CASEDEF Length pat_Term rhs_Tree guard_Tree? - ImplicitArg = IMPLICITARG arg_Term - ASTRef = Nat // byte position in AST payload - - Path = Constant - TERMREFdirect sym_ASTRef - TERMREFsymbol sym_ASTRef qual_Type - TERMREFpkg fullyQualified_NameRef - TERMREF possiblySigned_NameRef qual_Type - THIS clsRef_Type - SKOLEMtype refinedType_ASTRef - SHARED path_ASTRef - - - Constant = UNITconst - FALSEconst - TRUEconst - BYTEconst Int - SHORTconst Int - CHARconst Nat - INTconst Int - LONGconst LongInt - FLOATconst Int - DOUBLEconst LongInt - STRINGconst NameRef - NULLconst - CLASSconst Type - ENUMconst Path - - Type = Path - TYPEREFdirect sym_ASTRef - TYPEREFsymbol sym_ASTRef qual_Type - TYPEREFpkg fullyQualified_NameRef - TYPEREF possiblySigned_NameRef qual_Type - SUPERtype Length this_Type underlying_Type - REFINEDtype Length underlying_Type refinement_NameRef info_Type - APPLIEDtype Length tycon_Type arg_Type* - TYPEBOUNDS Length low_Type high_Type - TYPEALIAS Length alias_Type (COVARIANT | CONTRAVARIANT)? - ANNOTATED Length fullAnnotation_Term underlying_Type - ANDtype Length left_Type right_Type - ORtype Length left_Type right_Type - BIND Length boundName_NameRef bounds_Type - // for type-variables defined in a type pattern - BYNAMEtype underlying_Type - POLYtype Length result_Type NamesTypes // needed for refinements - METHODtype Length result_Type NamesTypes // needed for refinements - PARAMtype Length binder_ASTref paramNum_Nat // needed for refinements - SHARED type_ASTRef - NamesTypes = ParamType* - NameType = paramName_NameRef typeOrBounds_ASTRef - - Modifier = PRIVATE - INTERNAL // package private - PROTECTED - PRIVATEqualified qualifier_Type // will be dropped - PROTECTEDqualified qualifier_Type // will be dropped - ABSTRACT - FINAL - SEALED - CASE - IMPLICIT - LAZY - OVERRIDE - INLINE // macro - ABSOVERRIDE // abstract override - STATIC // mapped to static Java member - OBJECT // an object or its class - TRAIT // a trait - LOCAL // private[this] or protected[this] - SYNTHETIC // generated by Scala compiler - ARTIFACT // to be tagged Java Synthetic - MUTABLE // a var - LABEL // method generated as a label - FIELDaccessor // getter or setter - CASEaccessor // getter for case class param - COVARIANT // type param marked “+” - CONTRAVARIANT // type param marked “-” - SCALA2X // Imported from Scala2.x - DEFAULTparameterized // Method with default params - INSUPERCALL // defined in the argument of a constructor supercall - Annotation - Annotation = ANNOTATION Length tycon_Type fullAnnotation_Term - -Note: Tree tags are grouped into 5 categories that determine what follows, and thus allow to compute the size of the tagged tree in a generic way. - - Category 1 (tags 0-63) : tag - Category 2 (tags 64-95) : tag Nat - Category 3 (tags 96-111) : tag AST - Category 4 (tags 112-127): tag Nat AST - Category 5 (tags 128-255): tag Length - -Standard Section: "Sourcefile" sourcefile_NameRef - -Standard Section: "Positions" sourceLength_Nat Assoc* - - Assoc = addr_Delta offset_Delta offset_Delta? - // addr_Delta : - // Difference of address to last recorded node. - // All but the first addr_Deltas are > 0, the first is >= 0. - // 2nd offset_Delta: - // Difference of end offset of addressed node vs parent node. Always <= 0 - // 1st offset Delta, if delta >= 0 or 2nd offset delta exists - // Difference of start offset of addressed node vs parent node. - // 1st offset Delta, if delta < 0 and 2nd offset delta does not exist: - // Difference of end offset of addressed node vs parent node. - // Offsets and addresses are difference encoded. - // Nodes which have the same positions as their parents are omitted. - Delta = Int // Difference between consecutive offsets / tree addresses, - -**************************************************************************************/ - -object PickleFormat { - - final val header = Array(0x5C, 0xA1, 0xAB, 0x1F) - final val MajorVersion = 0 - final val MinorVersion = 5 - - // Name tags - - final val UTF8 = 1 - final val QUALIFIED = 2 - final val SIGNED = 3 - final val EXPANDED = 4 - final val OBJECTCLASS = 5 - final val SUPERACCESSOR = 6 - final val DEFAULTGETTER = 7 - final val SHADOWED = 8 - -// AST tags - - final val UNITconst = 2 - final val FALSEconst = 3 - final val TRUEconst = 4 - final val NULLconst = 5 - final val PRIVATE = 6 - final val INTERNAL = 7 - final val PROTECTED = 8 - final val ABSTRACT = 9 - final val FINAL = 10 - final val SEALED = 11 - final val CASE = 12 - final val IMPLICIT = 13 - final val LAZY = 14 - final val OVERRIDE = 15 - final val INLINE = 16 - final val ABSOVERRIDE = 17 - final val STATIC = 18 - final val OBJECT = 19 - final val TRAIT = 20 - final val LOCAL = 21 - final val SYNTHETIC = 22 - final val ARTIFACT = 23 - final val MUTABLE = 24 - final val LABEL = 25 - final val FIELDaccessor = 26 - final val CASEaccessor = 27 - final val COVARIANT = 28 - final val CONTRAVARIANT = 29 - final val SCALA2X = 30 - final val DEFAULTparameterized = 31 - final val INSUPERCALL = 32 - - final val SHARED = 64 - final val TERMREFdirect = 65 - final val TYPEREFdirect = 66 - final val TERMREFpkg = 67 - final val TYPEREFpkg = 68 - final val SKOLEMtype = 69 - final val BYTEconst = 70 - final val SHORTconst = 71 - final val CHARconst = 72 - final val INTconst = 73 - final val LONGconst = 74 - final val FLOATconst = 75 - final val DOUBLEconst = 76 - final val STRINGconst = 77 - final val IMPORTED = 78 - - final val THIS = 96 - final val CLASSconst = 97 - final val ENUMconst = 98 - final val BYNAMEtype = 99 - final val NEW = 100 - final val IMPLICITarg = 101 - final val PRIVATEqualified = 102 - final val PROTECTEDqualified = 103 - - final val IDENT = 112 - final val SELECT = 113 - final val TERMREFsymbol = 114 - final val TERMREF = 115 - final val TYPEREFsymbol = 116 - final val TYPEREF = 117 - final val SELFDEF = 118 - - final val PACKAGE = 128 - final val VALDEF = 129 - final val DEFDEF = 130 - final val TYPEDEF = 131 - final val IMPORT = 132 - final val TYPEPARAM = 133 - final val PARAMS = 134 - final val PARAM = 136 - final val RENAMED = 138 - final val APPLY = 139 - final val TYPEAPPLY = 140 - final val PAIR = 142 - final val TYPED = 143 - final val NAMEDARG = 144 - final val ASSIGN = 145 - final val BLOCK = 146 - final val IF = 147 - final val LAMBDA = 148 - final val MATCH = 149 - final val RETURN = 150 - final val TRY = 151 - final val REPEATED = 153 - final val BIND = 154 - final val ALTERNATIVE = 155 - final val UNAPPLY = 156 - final val ANNOTATED = 157 - final val CASEDEF = 158 - final val TEMPLATE = 160 - final val SUPER = 163 - final val SUPERtype = 166 - final val REFINEDtype = 167 - final val APPLIEDtype = 168 - final val TYPEBOUNDS = 169 - final val TYPEALIAS = 170 - final val ANDtype = 171 - final val ORtype = 172 - final val METHODtype = 174 - final val POLYtype = 175 - final val PARAMtype = 176 - final val ANNOTATION = 178 - - final val firstSimpleTreeTag = UNITconst - final val firstNatTreeTag = SHARED - final val firstASTTreeTag = THIS - final val firstNatASTTreeTag = IDENT - final val firstLengthTreeTag = PACKAGE - - def isParamTag(tag: Int) = tag == PARAM || tag == TYPEPARAM - - def isModifierTag(tag: Int) = tag match { - case PRIVATE - | INTERNAL - | PROTECTED - | ABSTRACT - | FINAL - | SEALED - | CASE - | IMPLICIT - | LAZY - | OVERRIDE - | INLINE - | ABSOVERRIDE - | STATIC - | OBJECT - | TRAIT - | LOCAL - | SYNTHETIC - | ARTIFACT - | MUTABLE - | LABEL - | FIELDaccessor - | CASEaccessor - | COVARIANT - | CONTRAVARIANT - | SCALA2X - | DEFAULTparameterized - | INSUPERCALL - | ANNOTATION - | PRIVATEqualified - | PROTECTEDqualified => true - case _ => false - } - - def nameTagToString(tag: Int): String = tag match { - case UTF8 => "UTF8" - case QUALIFIED => "QUALIFIED" - case SIGNED => "SIGNED" - case EXPANDED => "EXPANDED" - case OBJECTCLASS => "OBJECTCLASS" - case SUPERACCESSOR => "SUPERACCESSOR" - case DEFAULTGETTER => "DEFAULTGETTER" - } - - def astTagToString(tag: Int): String = tag match { - case UNITconst => "UNITconst" - case FALSEconst => "FALSEconst" - case TRUEconst => "TRUEconst" - case NULLconst => "NULLconst" - case PRIVATE => "PRIVATE" - case INTERNAL => "INTERNAL" - case PROTECTED => "PROTECTED" - case ABSTRACT => "ABSTRACT" - case FINAL => "FINAL" - case SEALED => "SEALED" - case CASE => "CASE" - case IMPLICIT => "IMPLICIT" - case LAZY => "LAZY" - case OVERRIDE => "OVERRIDE" - case INLINE => "INLINE" - case ABSOVERRIDE => "ABSOVERRIDE" - case STATIC => "STATIC" - case OBJECT => "OBJECT" - case TRAIT => "TRAIT" - case LOCAL => "LOCAL" - case SYNTHETIC => "SYNTHETIC" - case ARTIFACT => "ARTIFACT" - case MUTABLE => "MUTABLE" - case LABEL => "LABEL" - case FIELDaccessor => "FIELDaccessor" - case CASEaccessor => "CASEaccessor" - case COVARIANT => "COVARIANT" - case CONTRAVARIANT => "CONTRAVARIANT" - case SCALA2X => "SCALA2X" - case DEFAULTparameterized => "DEFAULTparameterized" - case INSUPERCALL => "INSUPERCALL" - - case SHARED => "SHARED" - case TERMREFdirect => "TERMREFdirect" - case TYPEREFdirect => "TYPEREFdirect" - case TERMREFpkg => "TERMREFpkg" - case TYPEREFpkg => "TYPEREFpkg" - case SKOLEMtype => "SKOLEMtype" - case BYTEconst => "BYTEconst" - case SHORTconst => "SHORTconst" - case CHARconst => "CHARconst" - case INTconst => "INTconst" - case LONGconst => "LONGconst" - case FLOATconst => "FLOATconst" - case DOUBLEconst => "DOUBLEconst" - case STRINGconst => "STRINGconst" - - case IDENT => "IDENT" - case SELECT => "SELECT" - case TERMREFsymbol => "TERMREFsymbol" - case TERMREF => "TERMREF" - case TYPEREFsymbol => "TYPEREFsymbol" - case TYPEREF => "TYPEREF" - - case PACKAGE => "PACKAGE" - case VALDEF => "VALDEF" - case DEFDEF => "DEFDEF" - case TYPEDEF => "TYPEDEF" - case IMPORT => "IMPORT" - case TYPEPARAM => "TYPEPARAM" - case PARAMS => "PARAMS" - case PARAM => "PARAM" - case IMPORTED => "IMPORTED" - case RENAMED => "RENAMED" - case APPLY => "APPLY" - case TYPEAPPLY => "TYPEAPPLY" - case NEW => "NEW" - case PAIR => "PAIR" - case TYPED => "TYPED" - case NAMEDARG => "NAMEDARG" - case ASSIGN => "ASSIGN" - case BLOCK => "BLOCK" - case IF => "IF" - case LAMBDA => "LAMBDA" - case MATCH => "MATCH" - case RETURN => "RETURN" - case TRY => "TRY" - case REPEATED => "REPEATED" - case BIND => "BIND" - case ALTERNATIVE => "ALTERNATIVE" - case UNAPPLY => "UNAPPLY" - case ANNOTATED => "ANNOTATED" - case CASEDEF => "CASEDEF" - case IMPLICITarg => "IMPLICITarg" - case TEMPLATE => "TEMPLATE" - case SELFDEF => "SELFDEF" - case THIS => "THIS" - case SUPER => "SUPER" - case CLASSconst => "CLASSconst" - case ENUMconst => "ENUMconst" - case SUPERtype => "SUPERtype" - case REFINEDtype => "REFINEDtype" - case APPLIEDtype => "APPLIEDtype" - case TYPEBOUNDS => "TYPEBOUNDS" - case TYPEALIAS => "TYPEALIAS" - case ANDtype => "ANDtype" - case ORtype => "ORtype" - case BYNAMEtype => "BYNAMEtype" - case POLYtype => "POLYtype" - case METHODtype => "METHODtype" - case PARAMtype => "PARAMtype" - case ANNOTATION => "ANNOTATION" - case PRIVATEqualified => "PRIVATEqualified" - case PROTECTEDqualified => "PROTECTEDqualified" - } -} diff --git a/src/dotty/tools/dotc/core/pickling/PositionPickler.scala b/src/dotty/tools/dotc/core/pickling/PositionPickler.scala deleted file mode 100644 index 1e36105cb..000000000 --- a/src/dotty/tools/dotc/core/pickling/PositionPickler.scala +++ /dev/null @@ -1,75 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import ast.tpd._ -import ast.Trees.WithLazyField -import PickleFormat._ -import core._ -import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._ -import collection.mutable -import TastyBuffer._ -import util.Positions._ - -object PositionPickler { - - trait DeferredPosition { - var parentPos: Position = NoPosition - } - - def traverse(x: Any, parentPos: Position, op: (Tree, Position) => Unit)(implicit ctx: Context): Unit = - if (parentPos.exists) - x match { - case x: Tree @unchecked => - op(x, parentPos) - x match { - case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos, op) - case _ => - } - traverse(x.productIterator, x.pos, op) - case x: DeferredPosition => - x.parentPos = parentPos - case xs: TraversableOnce[_] => - xs.foreach(traverse(_, parentPos, op)) - case _ => - } -} -import PositionPickler._ - -class PositionPickler(pickler: TastyPickler, addrOfTree: Tree => Option[Addr]) { - val buf = new TastyBuffer(5000) - pickler.newSection("Positions", buf) - import buf._ - - def picklePositions(roots: List[Tree], totalRange: Position)(implicit ctx: Context) = { - var lastIndex = 0 - def record(tree: Tree, parentPos: Position): Unit = - if (tree.pos.exists) { - def msg = s"failure to pickle $tree at ${tree.pos}, parent = $parentPos" - val endPos = tree.pos.end min parentPos.end - // end positions can be larger than their parents - // e.g. in the case of synthetic empty ranges, which are placed at the next token after - // the current construct. - val endDelta = endPos - parentPos.end - val startPos = - if (endDelta == 0) tree.pos.start max parentPos.start else tree.pos.start min endPos - // Since end positions are corrected above, start positions have to follow suit. - val startDelta = startPos - parentPos.start - if (startDelta != 0 || endDelta != 0) - for (addr <- addrOfTree(tree)) { - buf.writeInt(addr.index - lastIndex) - lastIndex = addr.index - if (startDelta != 0) buf.writeInt(startDelta) - if (endDelta != 0) { - assert(endDelta < 0, msg) - buf.writeInt(endDelta) - } else - assert(startDelta >= 0, msg) - } - } - - buf.writeNat(totalRange.end) - traverse(roots, totalRange, record) - } -} diff --git a/src/dotty/tools/dotc/core/pickling/PositionUnpickler.scala b/src/dotty/tools/dotc/core/pickling/PositionUnpickler.scala deleted file mode 100644 index cfcc4a835..000000000 --- a/src/dotty/tools/dotc/core/pickling/PositionUnpickler.scala +++ /dev/null @@ -1,37 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import util.Positions._ -import collection.mutable -import TastyBuffer.Addr - -object PositionUnpickler { - type AddrToPosition = mutable.HashMap[Addr, Position] -} - -/** Unpickler for tree positions */ -class PositionUnpickler(reader: TastyReader) { - import PositionUnpickler._ - import reader._ - - def unpickle(): (Position, AddrToPosition) = { - val positions = new mutable.HashMap[Addr, Position] // Dotty deviation: Can't use new AddrToPosition here. TODO: fix this! - val sourceLength = readNat() - def readDelta() = if (isAtEnd) 0 else readInt() - var curIndex: Addr = Addr(readDelta()) - while (!isAtEnd) { - val delta1 = readDelta() - val delta2 = readDelta() - val (startDelta, endDelta, indexDelta) = - if (delta2 <= 0) (delta1, -delta2, readDelta()) - else if (delta1 < 0) (0, -delta1, delta2) - else (delta1, 0, delta2) - positions(curIndex) = Position(startDelta, endDelta, startDelta) - // make non-synthetic position; will be made synthetic by normalization. - curIndex += indexDelta - } - (Position(0, sourceLength), positions) - } -} diff --git a/src/dotty/tools/dotc/core/pickling/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/pickling/Scala2Unpickler.scala deleted file mode 100644 index 5fbb4b57f..000000000 --- a/src/dotty/tools/dotc/core/pickling/Scala2Unpickler.scala +++ /dev/null @@ -1,1216 +0,0 @@ -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._, NameOps._ -import StdNames._, Denotations._, NameOps._, Flags._, Constants._, Annotations._ -import dotty.tools.dotc.typer.ProtoTypes.{FunProtoTyped, FunProto} -import util.Positions._ -import dotty.tools.dotc.ast.{tpd, Trees, untpd}, ast.tpd._ -import printing.Texts._ -import printing.Printer -import io.AbstractFile -import util.common._ -import typer.Checking.checkNonCyclic -import PickleBuffer._ -import scala.reflect.internal.pickling.PickleFormat._ -import Decorators._ -import scala.collection.{ mutable, immutable } -import scala.collection.mutable.ListBuffer -import scala.annotation.switch - -object Scala2Unpickler { - - /** Exception thrown if classfile is corrupted */ - class BadSignature(msg: String) extends RuntimeException(msg) - - case class TempPolyType(tparams: List[Symbol], tpe: Type) extends UncachedGroundType { - override def fallbackToText(printer: Printer): Text = - "[" ~ printer.dclsText(tparams, ", ") ~ "]" ~ printer.toText(tpe) - } - - /** 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 temp poly type to some native Dotty idiom. - * @param denot The denotation that gets the converted type as info. - * If `denot` is not an abstract type, this simply returns an equivalent `PolyType`. - * If `denot` is an abstract type, it converts a - * - * TempPolyType(List(v_1 T_1, ..., v_n T_n), lo .. hi) - * - * to a type lambda using `parameterizeWith/LambdaAbstract`. - */ - def depoly(tp: Type, denot: SymDenotation)(implicit ctx: Context): Type = tp match { - case TempPolyType(tparams, restpe) => - if (denot.isAbstractType) - restpe.LambdaAbstract(tparams) // bounds needed? - else if (denot.isAliasType) { - var err: Option[(String, Position)] = None - val result = restpe.parameterizeWith(tparams) - for ((msg, pos) <- err) - ctx.warning( - sm"""$msg - |originally parsed type : ${tp.show} - |will be approximated by: ${result.show}. - |Proceed at own risk.""") - result - } - else - PolyType.fromSymbols(tparams, restpe) - case tp => tp - } - - def addConstructorTypeParams(denot: SymDenotation)(implicit ctx: Context) = { - assert(denot.isConstructor) - denot.info = PolyType.fromSymbols(denot.owner.typeParams, denot.info) - } - - /** Convert array parameters denoting a repeated parameter of a Java method - * to `RepeatedParamClass` types. - */ - def arrayToRepeated(tp: Type)(implicit ctx: Context): Type = tp match { - case tp @ MethodType(paramNames, paramTypes) => - val lastArg = paramTypes.last - assert(lastArg isRef defn.ArrayClass) - val elemtp0 :: Nil = lastArg.baseArgInfos(defn.ArrayClass) - val elemtp = elemtp0 match { - case AndType(t1, t2) if t1.typeSymbol.isAbstractType && (t2 isRef defn.ObjectClass) => - t1 // drop intersection with Object for abstract types in varargs. UnCurry can handle them. - case _ => - elemtp0 - } - tp.derivedMethodType( - paramNames, - paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp), - tp.resultType) - case tp @ PolyType(paramNames) => - tp.derivedPolyType(paramNames, tp.paramBounds, arrayToRepeated(tp.resultType)) - } - - def ensureConstructor(cls: ClassSymbol, scope: Scope)(implicit ctx: Context) = - if (scope.lookup(nme.CONSTRUCTOR) == NoSymbol) { - val constr = ctx.newDefaultConstructor(cls) - addConstructorTypeParams(constr) - cls.enter(constr, scope) - } - - def setClassInfo(denot: ClassDenotation, info: Type, selfInfo: Type = NoType)(implicit ctx: Context): Unit = { - val cls = denot.classSymbol - val (tparams, TempClassInfoType(parents, decls, clazz)) = info match { - case TempPolyType(tps, cinfo) => (tps, cinfo) - case cinfo => (Nil, cinfo) - } - var parentRefs = ctx.normalizeToClassRefs(parents, cls, decls) - if (parentRefs.isEmpty) parentRefs = defn.ObjectClass.typeRef :: Nil - for (tparam <- tparams) { - val tsym = decls.lookup(tparam.name) - if (tsym.exists) tsym.setFlag(TypeParam) - else denot.enter(tparam, decls) - } - val ost = - if ((selfInfo eq NoType) && (denot is ModuleClass)) - denot.owner.thisType select denot.sourceModule - else selfInfo - if (!(denot.flagsUNSAFE is JavaModule)) ensureConstructor(denot.symbol.asClass, decls) - - val scalacCompanion = denot.classSymbol.scalacLinkedClass - - def registerCompanionPair(module: Symbol, claz: Symbol) = { - val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, module) - if (companionClassMethod.exists) - companionClassMethod.entered - val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, module, claz) - if (companionModuleMethod.exists) - companionModuleMethod.entered - } - - if (denot.flagsUNSAFE is Module) { - registerCompanionPair(denot.classSymbol, scalacCompanion) - } else { - registerCompanionPair(scalacCompanion, denot.classSymbol) - } - - denot.info = ClassInfo(denot.owner.thisType, denot.classSymbol, parentRefs, decls, ost) - } -} - -/** 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 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 Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: ClassDenotation)(ictx: Context) - extends PickleBuffer(bytes, 0, -1) with ClassfileParser.Embedded { - - def showPickled() = { - atReadPos(0, () => { - println(s"classRoot = ${classRoot.debugString}, moduleClassRoot = ${moduleClassRoot.debugString}") - util.ShowPickled.printFile(this) - }) - } - - // print("unpickling "); showPickled() // !!! DEBUG - - import Scala2Unpickler._ - - val moduleRoot = moduleClassRoot.sourceModule(ictx).denot(ictx) - assert(moduleRoot.isTerm) - - checkVersion(ictx) - - private val loadingMirror = defn(ictx) // 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.AnyRefMap[Symbol, Scope]() - - protected def errorBadSignature(msg: String, original: Option[RuntimeException] = None)(implicit ctx: Context) = { - val ex = new BadSignature( - sm"""error reading Scala signature of $classRoot from $source: - |error occured at position $readIndex: $msg""") - /*if (debug)*/ original.getOrElse(ex).printStackTrace() // !!! DEBUG - throw ex - } - - protected def handleRuntimeException(ex: RuntimeException)(implicit ctx: Context) = ex match { - case ex: BadSignature => throw ex - case _ => errorBadSignature(s"a runtime exception occured: $ex", Some(ex)) - } - - private var postReadOp: Context => Unit = null - - def run()(implicit ctx: Context) = - try { - var i = 0 - while (i < index.length) { - if (entries(i) == null && isSymbolEntry(i)) { - val savedIndex = readIndex - readIndex = index(i) - entries(i) = readSymbol() - if (postReadOp != null) { - postReadOp(ctx) - postReadOp = null - } - 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 - } - } catch { - case ex: RuntimeException => handleRuntimeException(ex) - } - - def source(implicit ctx: Context): AbstractFile = { - val f = classRoot.symbol.associatedFile - if (f != null) f else moduleClassRoot.symbol.associatedFile - } - - private def checkVersion(implicit ctx: Context): Unit = { - 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) - } - - /** 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)(implicit ctx: Context): 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)(implicit ctx: Context): 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)(implicit ctx: Context): Boolean = - sym.name == tpnme.REFINE_CLASS - - protected def isLocal(sym: Symbol)(implicit ctx: Context) = isUnpickleRoot(sym.topLevelClass) - - protected def isUnpickleRoot(sym: Symbol)(implicit ctx: Context) = { - val d = sym.denot - d == moduleRoot || d == moduleClassRoot || d == classRoot - } - - /** If entry at i is undefined, define it by performing - * operation op with readIndex at start of i'th - * entry. Restore readIndex 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] - } - - protected def atReadPos[T](start: Int, op: () => T): T = { - val savedIndex = readIndex - readIndex = start - try op() - finally readIndex = savedIndex - } - - /** Read a name */ - protected def readName()(implicit ctx: Context): 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()(implicit ctx: Context): TermName = readName().toTermName - protected def readTypeName()(implicit ctx: Context): TypeName = readName().toTypeName - - /** Read a symbol */ - protected def readSymbol()(implicit ctx: Context): Symbol = readDisambiguatedSymbol(alwaysTrue)() - - /** Read a symbol, with possible disambiguation */ - protected def readDisambiguatedSymbol(p: Symbol => Boolean)()(implicit ctx: Context): Symbol = { - val start = indexCoord(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(d => p(d.symbol)) - val sym = denot1.symbol - if (denot.exists && !denot1.exists) { // !!!DEBUG - val alts = denot.alternatives map (d => d + ":" + d.info + "/" + d.signature) - System.err.println(s"!!! disambiguation failure: $alts") - val members = denot.alternatives.head.symbol.owner.info.decls.toList map (d => d + ":" + d.info + "/" + d.signature) - System.err.println(s"!!! all members: $members") - } - 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 _ => - def declIn(owner: Symbol) = adjust(owner.info.decl(name)) - val sym = declIn(owner) - if (sym.exists || owner.ne(defn.ObjectClass)) sym else declIn(defn.AnyClass) - } - - 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 module = owner.info.decl(name.toTermName).suchThat(_ is Module) - module.info // force it, as completer does not yet point to module class. - module.symbol.moduleClass - - /* was: - val moduleVar = owner.info.decl(name.toTermName.moduleVarName).symbol - if (moduleVar.isLazyAccessor) - return moduleVar.lazyAccessor.lazyAccessor - */ - } else NoSymbol - } - - // println(s"read ext symbol $name from ${owner.denot.debugString} in ${classRoot.debugString}") // !!! DEBUG - - // (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(ctx.base.missingHook(owner, name)) orElse { - // println(owner.info.decls.toList.map(_.debugString).mkString("\n ")) // !!! DEBUG - // } - // (5) Create a stub symbol to defer hard failure a little longer. - ctx.newStubSymbol(owner, name, source) - } - } - } - } - } - - tag match { - case NONEsym => return NoSymbol - case EXTref | EXTMODCLASSref => return readExtSymbol() - case _ => - } - - // symbols that were pickled with Pickler.writeSymInfo - val nameref = readNat() - val name0 = at(nameref, readName) - val owner = readSymbolRef() - - var flags = unpickleScalaFlags(readLongNat(), name0.isTypeName) - if (flags is DefaultParameter) { - // DefaultParameterized flag now on method, not parameter - //assert(flags is Param, s"$name0 in $owner") - flags = flags &~ DefaultParameterized - owner.setFlag(DefaultParameterized) - } - - val name1 = name0.adjustIfModuleClass(flags) - val name = if (name1 == nme.IMPLCLASS_CONSTRUCTOR) nme.CONSTRUCTOR else name1 - - def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner) && !(flags is ModuleClass) - def isModuleClassRoot = (name == moduleClassRoot.name) && (owner == moduleClassRoot.owner) && (flags is Module) - def isModuleRoot = (name == moduleClassRoot.name.sourceModuleName) && (owner == moduleClassRoot.owner) && (flags is Module) - - //if (isClassRoot) println(s"classRoot of $classRoot found at $readIndex, flags = $flags") // !!! DEBUG - //if (isModuleRoot) println(s"moduleRoot of $moduleRoot found at $readIndex, flags = $flags") // !!! DEBUG - //if (isModuleClassRoot) println(s"moduleClassRoot of $moduleClassRoot found at $readIndex, flags = $flags") // !!! DEBUG - - def completeRoot(denot: ClassDenotation, completer: LazyType): Symbol = { - denot.setFlag(flags) - denot.resetFlag(Touched) // allow one more completion - denot.info = completer - denot.symbol - } - - def finishSym(sym: Symbol): Symbol = { - val owner = sym.owner - if (owner.isClass && - !( isUnpickleRoot(sym) - || (sym is Scala2Existential) - || isRefinementClass(sym) - ) - ) - owner.asClass.enter(sym, symScope(owner)) - else if (isRefinementClass(owner)) - symScope(owner).openForMutations.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 |= owner.typeParamCreationFlags | ExpandedName - } - ctx.newSymbol(owner, name1, flags1, localMemberUnpickler, coord = start) - case CLASSsym => - val infoRef = readNat() - postReadOp = implicit ctx => atReadPos(index(infoRef), readTypeParams) // force reading type params early, so they get entered in the right order. - if (isClassRoot) - completeRoot( - classRoot, rootClassUnpickler(start, classRoot.symbol, NoSymbol)) - else if (isModuleClassRoot) - completeRoot( - moduleClassRoot, rootClassUnpickler(start, moduleClassRoot.symbol, moduleClassRoot.sourceModule)) - else if (name == tpnme.REFINE_CLASS) - // create a type alias instead - ctx.newSymbol(owner, name, flags, localMemberUnpickler, coord = start) - else { - def completer(cls: Symbol) = { - val unpickler = new LocalUnpickler() withDecls symScope(cls) - if (flags is ModuleClass) - unpickler withSourceModule (implicit ctx => - cls.owner.info.decls.lookup(cls.name.sourceModuleName) - .suchThat(_ is Module).symbol) - else unpickler - } - ctx.newClassSymbol(owner, name.asTypeName, flags, completer, coord = start) - } - case MODULEsym | VALsym => - if (isModuleRoot) { - moduleRoot setFlag flags - moduleRoot.symbol - } else ctx.newSymbol(owner, name.asTermName, flags, - new LocalUnpickler() withModuleClass(implicit ctx => - owner.info.decls.lookup(name.moduleClassName) - .suchThat(_ is Module).symbol) - , coord = start) - case _ => - errorBadSignature("bad symbol tag: " + tag) - }) - } - - class LocalUnpickler extends LazyType { - def startCoord(denot: SymDenotation): Coord = denot.symbol.coord - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = try { - def parseToCompletion(denot: SymDenotation) = { - 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 - } - // println("reading type for " + denot) // !!! DEBUG - val tp = at(inforef, readType) - denot match { - case denot: ClassDenotation => - val selfInfo = if (atEnd) NoType else readTypeRef() - setClassInfo(denot, tp, selfInfo) - denot setFlag Scala2x - case denot => - val tp1 = depoly(tp, denot) - denot.info = - if (tag == ALIASsym) TypeAlias(tp1) - else if (denot.isType) checkNonCyclic(denot.symbol, tp1, reportErrors = false) - // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles - else tp1 - if (denot.isConstructor) addConstructorTypeParams(denot) - if (atEnd) { - assert(!(denot is SuperAccessor), denot) - } else { - assert(denot is (SuperAccessor | ParamAccessor), denot) - def disambiguate(alt: Symbol) = { // !!! DEBUG - ctx.debugTraceIndented(s"disambiguating ${denot.info} =:= ${denot.owner.thisType.memberInfo(alt)} ${denot.owner}") { - denot.info matches denot.owner.thisType.memberInfo(alt) - } - } - val alias = readDisambiguatedSymbolRef(disambiguate).asTerm - denot.addAnnotation(Annotation.makeAlias(alias)) - } - } - // println(s"unpickled ${denot.debugString}, info = ${denot.info}") !!! DEBUG - } - atReadPos(startCoord(denot).toIndex, () => parseToCompletion(denot)) - } catch { - case ex: RuntimeException => handleRuntimeException(ex) - } - } - - object localMemberUnpickler extends LocalUnpickler - - def rootClassUnpickler(start: Coord, cls: Symbol, module: Symbol) = - (new LocalUnpickler with SymbolLoaders.SecondCompleter { - override def startCoord(denot: SymDenotation): Coord = start - }) withDecls symScope(cls) withSourceModule (_ => module) - - /** 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)(implicit ctx: Context): Type = { - def removeSingleton(tp: Type): Type = - if (tp isRef defn.SingletonClass) defn.AnyType else tp - def elim(tp: Type): Type = tp match { - case tp @ RefinedType(parent, name) => - val parent1 = elim(tp.parent) - tp.refinedInfo match { - case TypeAlias(info: TypeRef) if boundSyms contains info.symbol => - RefinedType(parent1, name, info.symbol.info) - case info: TypeRef if boundSyms contains info.symbol => - val info1 = info.symbol.info - assert(info1.derivesFrom(defn.SingletonClass)) - RefinedType(parent1, name, info1.mapReduceAnd(removeSingleton)(_ & _)) - case info => - tp.derivedRefinedType(parent1, name, info) - } - case tp @ TypeRef(pre, tpnme.Apply) if pre.isLambda => - elim(pre) - case _ => - tp - } - val tp1 = elim(tp) - val isBound = (tp: Type) => boundSyms contains tp.typeSymbol - if (tp1 existsPart isBound) { - val anyTypes = boundSyms map (_ => defn.AnyType) - val boundBounds = boundSyms map (_.info.bounds.hi) - val tp2 = tp1.subst(boundSyms, boundBounds).subst(boundSyms, anyTypes) - ctx.warning(s"""failure to eliminate existential - |original type : $tp forSome {${ctx.dclsText(boundSyms, "; ").show} - |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()(implicit ctx: Context): Type = { - val tag = readByte() - val end = readNat() + readIndex - (tag: @switch) match { - case NOtpe => - NoType - case NOPREFIXtpe => - NoPrefix - case THIStpe => - readSymbolRef().thisType - case SINGLEtpe => - val pre = readTypeRef() - val sym = readDisambiguatedSymbolRef(_.info.isParameterless) - if (isLocal(sym) || (pre == NoPrefix)) pre select sym - else TermRef.withSig(pre, sym.name.asTermName, Signature.NotAMethod) // !!! should become redundant - case SUPERtpe => - val thistpe = readTypeRef() - val supertpe = readTypeRef() - SuperType(thistpe, supertpe) - case CONSTANTtpe => - ConstantType(readConstantRef()) - case TYPEREFtpe => - var pre = readTypeRef() - val sym = readSymbolRef() - pre match { - case thispre: ThisType => - // The problem is that class references super.C get pickled as - // this.C. Dereferencing the member might then get an overriding class - // instance. The problem arises for instance for LinkedHashMap#MapValues - // and also for the inner Transform class in all views. We fix it by - // replacing the this with the appropriate super. - if (sym.owner != thispre.cls) { - val overriding = thispre.cls.info.decls.lookup(sym.name) - if (overriding.exists && overriding != sym) { - val base = pre.baseTypeWithArgs(sym.owner) - assert(base.exists) - pre = SuperType(thispre, base) - } - } - case _ => - } - val tycon = - if (isLocal(sym) || pre == NoPrefix) { - val pre1 = if ((pre eq NoPrefix) && (sym is TypeParam)) sym.owner.thisType else pre - pre1 select sym - } - else TypeRef(pre, sym.name.asTypeName) - val args = until(end, readTypeRef) - if (sym == defn.ByNameParamClass2x) ExprType(args.head) - else tycon.appliedTo(args) - 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(AndType(_, _)) - if (decls.isEmpty) parent - else { - def addRefinement(tp: Type, sym: Symbol) = { - def subst(info: Type, rt: RefinedType) = - if (clazz.isClass) info.substThis(clazz.asClass, SkolemType(rt)) - else info // turns out some symbols read into `clazz` are not classes, not sure why this is the case. - RefinedType(tp, sym.name, subst(sym.info, _)) - } - (parent /: decls.toList)(addRefinement).asInstanceOf[RefinedType] - } - case CLASSINFOtpe => - val clazz = readSymbolRef() - TempClassInfoType(until(end, readTypeRef), symScope(clazz), clazz) - case METHODtpe | IMPLICITMETHODtpe => - val restpe = readTypeRef() - val params = until(end, readSymbolRef) - def isImplicit = - tag == IMPLICITMETHODtpe || - params.nonEmpty && (params.head is Implicit) - val maker = if (isImplicit) ImplicitMethodType else MethodType - maker.fromSymbols(params, restpe) - case POLYtpe => - val restpe = readTypeRef() - val typeParams = until(end, readSymbolRef) - if (typeParams.nonEmpty) TempPolyType(typeParams, restpe.widenExpr) - 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.make(annots, tp) - case _ => - noSuchTypeTag(tag, end) - } - } - - def readTypeParams()(implicit ctx: Context): List[Symbol] = { - val tag = readByte() - val end = readNat() + readIndex - if (tag == POLYtpe) { - val unusedRestperef = readNat() - until(end, readSymbolRef) - } else Nil - } - - def noSuchTypeTag(tag: Int, end: Int)(implicit ctx: Context): Type = - errorBadSignature("bad type tag: " + tag) - - /** Read a constant */ - protected def readConstant()(implicit ctx: Context): 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)(implicit ctx: Context): Constant = - errorBadSignature("bad constant tag: " + tag) - - /** Read children and store them into the corresponding symbol. - */ - protected def readChildren()(implicit ctx: Context): Unit = { - 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()(implicit ctx: Context): 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 readDisambiguatedSymbolRef(p: Symbol => Boolean)(implicit ctx: Context): Symbol = - at(readNat(), readDisambiguatedSymbol(p)) - - protected def readNameRef()(implicit ctx: Context): Name = at(readNat(), readName) - protected def readTypeRef()(implicit ctx: Context): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... () - protected def readConstantRef()(implicit ctx: Context): Constant = at(readNat(), readConstant) - - protected def readTypeNameRef()(implicit ctx: Context): TypeName = readNameRef().toTypeName - protected def readTermNameRef()(implicit ctx: Context): TermName = readNameRef().toTermName - - protected def readAnnotationRef()(implicit ctx: Context): Annotation = at(readNat(), readAnnotation) - - protected def readModifiersRef(isType: Boolean)(implicit ctx: Context): Modifiers = at(readNat(), () => readModifiers(isType)) - protected def readTreeRef()(implicit ctx: Context): Tree = at(readNat(), readTree) - - /** Read an annotation argument, which is pickled either - * as a Constant or a Tree. - */ - protected def readAnnotArg(i: Int)(implicit ctx: Context): Tree = bytes(index(i)) match { - case TREE => at(i, readTree) - case _ => Literal(at(i, readConstant)) - } - - /** Read a ClassfileAnnotArg (argument to a classfile annotation) - */ - private def readArrayAnnotArg()(implicit ctx: Context): Tree = { - readByte() // skip the `annotargarray` tag - val end = readNat() + readIndex - // array elements are trees representing instances of scala.annotation.Annotation - SeqLiteral( - defn.SeqType.appliedTo(defn.AnnotationClass.typeRef :: Nil), - until(end, () => readClassfileAnnotArg(readNat()))) - } - - private def readAnnotInfoArg()(implicit ctx: Context): Tree = { - readByte() // skip the `annotinfo` tag - val end = readNat() + readIndex - readAnnotationContents(end) - } - - protected def readClassfileAnnotArg(i: Int)(implicit ctx: Context): Tree = 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)(implicit ctx: Context): Tree = { - val atp = readTypeRef() - val args = { - val t = new ListBuffer[Tree] - - while (readIndex != end) { - val argref = readNat() - t += { - if (isNameEntry(argref)) { - val name = at(argref, readName) - val arg = readClassfileAnnotArg(readNat()) - NamedArg(name.asTermName, arg) - } else readAnnotArg(argref) - } - } - t.toList - } - // println(atp) - val targs = atp.argTypes - - tpd.applyOverloaded(tpd.New(atp withoutArgs targs), nme.CONSTRUCTOR, args, targs, atp) -} - - /** 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()(implicit ctx: Context): Unit = { - val tag = readByte() - if (tag != SYMANNOT) - errorBadSignature("symbol annotation expected (" + tag + ")") - val end = readNat() + readIndex - val target = readSymbolRef() - target.addAnnotation(deferredAnnot(end)) - } - - /** Read an annotation and return it. Used when unpickling - * an ANNOTATED(WSELF)tpe or a NestedAnnotArg - */ - protected def readAnnotation()(implicit ctx: Context): Annotation = { - val tag = readByte() - if (tag != ANNOTINFO) - errorBadSignature("annotation expected (" + tag + ")") - val end = readNat() + readIndex - deferredAnnot(end) - } - - /** A deferred annotation that can be comleted by reading - * the bytes between `readIndex` and `end`. - */ - protected def deferredAnnot(end: Int)(implicit ctx: Context): Annotation = { - val start = readIndex - val atp = readTypeRef() - Annotation.deferred( - atp.typeSymbol, implicit ctx => atReadPos(start, () => readAnnotationContents(end))) - } - - /* Read an abstract syntax tree */ - protected def readTree()(implicit ctx: Context): 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(): Unit = { - symbol = readSymbolRef() - mods = readModifiersRef(symbol.isType) - name = readNameRef() - } - /** Read a Symbol and a Name */ - def setSymName(): Unit = { - symbol = readSymbolRef() - name = readNameRef() - } - /** Read a Symbol */ - def setSym(): Unit = { - symbol = readSymbolRef() - } - - implicit val pos: Position = NoPosition - - 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) - val cls = symbol.asClass - val ((constr: DefDef) :: Nil, stats) = - impl.body.partition(_.symbol == cls.primaryConstructor) - ClassDef(cls, constr, tparams ++ stats) - - case MODULEtree => - setSymModsName() - ModuleDef(symbol.asTerm, readTemplateRef().body) - - case VALDEFtree => - setSymModsName() - val tpt = readTreeRef() - val rhs = readTreeRef() - ValDef(symbol.asTerm, rhs) - - case DEFDEFtree => - setSymModsName() - val tparams = times(readNat(), readTypeDefRef) - val vparamss = times(readNat(), () => times(readNat(), readValDefRef)) - val tpt = readTreeRef() - val rhs = readTreeRef() - DefDef(symbol.asTerm, rhs) - - case TYPEDEFtree => - setSymModsName() - val rhs = readTreeRef() - val tparams = until(end, readTypeDefRef) - TypeDef(symbol.asType) - - case LABELtree => - setSymName() - val rhs = readTreeRef() - val params = until(end, readIdentRef) - val ldef = DefDef(symbol.asTerm, rhs) - def isCaseLabel(sym: Symbol) = sym.name.startsWith(nme.CASEkw) - if (isCaseLabel(symbol)) ldef - else Block(ldef :: Nil, Apply(Ident(symbol.termRef), Nil)) - - case IMPORTtree => - setSym() - val expr = readTreeRef() - val selectors = until(end, () => { - val fromName = readNameRef() - val toName = readNameRef() - val from = untpd.Ident(fromName) - val to = untpd.Ident(toName) - if (toName.isEmpty) from else untpd.Pair(from, untpd.Ident(toName)) - }) - - Import(expr, selectors) - - case TEMPLATEtree => - setSym() - val parents = times(readNat(), readTreeRef) - val self = readValDefRef() - val body = until(end, readTreeRef) - untpd.Template(???, parents, self, body) // !!! TODO: pull out primary constructor - .withType(symbol.namedType) - - 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 => - readTreeRef() - unimplementedTree("STAR") - - case BINDtree => - setSymName() - Bind(symbol.asTerm, readTreeRef()) - - case UNAPPLYtree => - val fun = readTreeRef() - val args = until(end, readTreeRef) - UnApply(fun, Nil, args, defn.AnyType) // !!! this is wrong in general - - case ARRAYVALUEtree => - val elemtpt = readTreeRef() - val trees = until(end, readTreeRef) - SeqLiteral(defn.SeqType.appliedTo(elemtpt.tpe :: Nil), trees) - // note can't deal with trees passed to Java methods as arrays here - - case FUNCTIONtree => - setSym() - val body = readTreeRef() - val vparams = until(end, readValDefRef) - val applyType = MethodType(vparams map (_.name), vparams map (_.tpt.tpe), body.tpe) - val applyMeth = ctx.newSymbol(symbol.owner, nme.apply, Method, applyType) - Closure(applyMeth, Function.const(body.changeOwner(symbol, applyMeth)) _) - - 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(), Ident(symbol.termRef)) - - 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().tpe) - - 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) // note: can't deal with overloaded syms yet - - case APPLYDYNAMICtree => - setSym() - val qual = readTreeRef() - val args = until(end, readTreeRef) - unimplementedTree("APPLYDYNAMIC") - - case SUPERtree => - setSym() - val qual = readTreeRef() - val mix = readTypeNameRef() - Super(qual, mix, inConstrCall = false) // todo: revise - - case THIStree => - setSym() - val name = readTypeNameRef() - This(symbol.asClass) - - case SELECTtree => - setSym() - val qualifier = readTreeRef() - val selector = readNameRef() - qualifier.select(symbol.namedType) - case IDENTtree => - setSymName() - Ident(symbol.namedType) - - case LITERALtree => - Literal(readConstantRef()) - - case TYPEtree => - TypeTree(tpe) - - 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, symbol.namedType) - - case COMPOUNDTYPEtree => - readTemplateRef() - TypeTree(tpe) - - 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) - TypeTree(tpe) - - case _ => - noSuchTreeTag(tag, end) - } - } - - def noSuchTreeTag(tag: Int, end: Int)(implicit ctx: Context) = - errorBadSignature("unknown tree type (" + tag + ")") - - def unimplementedTree(what: String)(implicit ctx: Context) = - errorBadSignature(s"cannot read $what trees from Scala 2.x signatures") - - def readModifiers(isType: Boolean)(implicit ctx: Context): 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 = unpickleScalaFlags(pflags, isType) - val privateWithin = readNameRef().asTypeName - Trees.Modifiers[Type](flags, privateWithin, Nil) - } - - protected def readTemplateRef()(implicit ctx: Context): Template = - readTreeRef() match { - case templ: Template => templ - case other => - errorBadSignature("expected a template (" + other + ")") - } - protected def readCaseDefRef()(implicit ctx: Context): CaseDef = - readTreeRef() match { - case tree: CaseDef => tree - case other => - errorBadSignature("expected a case def (" + other + ")") - } - protected def readValDefRef()(implicit ctx: Context): ValDef = - readTreeRef() match { - case tree: ValDef => tree - case other => - errorBadSignature("expected a ValDef (" + other + ")") - } - protected def readIdentRef()(implicit ctx: Context): Ident = - readTreeRef() match { - case tree: Ident => tree - case other => - errorBadSignature("expected an Ident (" + other + ")") - } - protected def readTypeDefRef()(implicit ctx: Context): TypeDef = - readTreeRef() match { - case tree: TypeDef => tree - case other => - errorBadSignature("expected an TypeDef (" + other + ")") - } - -} diff --git a/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala b/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala deleted file mode 100644 index f57c15a3d..000000000 --- a/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala +++ /dev/null @@ -1,188 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import util.Util.dble - -object TastyBuffer { - - /** The number of digits of the natural number `nat`, written in base 128 format. */ - def natSize(nat: Int): Int = - if (nat < 128) 1 else natSize(nat >>> 7) + 1 - - /** An address pointing to an index in a Tasty buffer's byte array */ - case class Addr(val index: Int) extends AnyVal { - def - (delta: Int): Addr = Addr(this.index - delta) - def + (delta: Int): Addr = Addr(this.index + delta) - - def relativeTo(base: Addr): Addr = this - base.index - AddrWidth - } - - val NoAddr = Addr(-1) - - /** The maximal number of address bytes. - * Since addresses are written as base-128 natural numbers, - * the value of 4 gives a maximal array size of 256M. - */ - final val AddrWidth = 4 -} -import TastyBuffer._ - -/** A byte array buffer that can be filled with bytes or natural numbers in TASTY format, - * and that supports reading and patching addresses represented as natural numbers. - */ -class TastyBuffer(initialSize: Int) { - - /** The current byte array, will be expanded as needed */ - var bytes = new Array[Byte](initialSize) - - /** The number of bytes written */ - var length = 0 - - // -- Output routines -------------------------------------------- - - /** Write a byte of data. */ - def writeByte(b: Int): Unit = { - if (length >= bytes.length) - bytes = dble(bytes) - bytes(length) = b.toByte - length += 1 - } - - /** Write the first `n` bytes of `data`. */ - def writeBytes(data: Array[Byte], n: Int): Unit = { - while (bytes.length < length + n) bytes = dble(bytes) - Array.copy(data, 0, bytes, length, n) - length += n - } - - /** Write a natural number in big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def writeNat(x: Int): Unit = - writeLongNat(x.toLong & 0x00000000FFFFFFFFL) - - /** Write a natural number in 2's complement big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def writeInt(x: Int): Unit = - writeLongInt(x) - - /** - * Like writeNat, but for longs. 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): Unit = { - def writePrefix(x: Long): Unit = { - val y = x >>> 7 - if (y != 0L) writePrefix(y) - writeByte((x & 0x7f).toInt) - } - val y = x >>> 7 - if (y != 0L) writePrefix(y) - writeByte(((x & 0x7f) | 0x80).toInt) - } - - /** Like writeInt, but for longs */ - def writeLongInt(x: Long): Unit = { - def writePrefix(x: Long): Unit = { - val y = x >> 7 - if (y != 0L - ((x >> 6) & 1)) writePrefix(y) - writeByte((x & 0x7f).toInt) - } - val y = x >> 7 - if (y != 0L - ((x >> 6) & 1)) writePrefix(y) - writeByte(((x & 0x7f) | 0x80).toInt) - } - - /** Write an uncompressed Long stored in 8 bytes in big endian format */ - def writeUncompressedLong(x: Long): Unit = { - var y = x - val bytes = new Array[Byte](8) - for (i <- 7 to 0 by -1) { - bytes(i) = (y & 0xff).toByte - y = y >>> 8 - } - writeBytes(bytes, 8) - } - - // -- Address handling -------------------------------------------- - - /** Write natural number `x` right-adjusted in a field of `width` bytes - * starting with address `at`. - */ - def putNat(at: Addr, x: Int, width: Int): Unit = { - var y = x - var w = width - if(at.index + w >= bytes.length) - bytes = dble(bytes) - var digit = y & 0x7f | 0x80 - while (w > 0) { - w -= 1 - bytes(at.index + w) = digit.toByte - y >>>= 7 - digit = y & 0x7f - } - assert(y == 0, s"number $x too large to fit in $width bytes") - } - - /** The byte at given address */ - def getByte(at: Addr): Int = bytes(at.index) - - /** The natural number at address `at` */ - def getNat(at: Addr): Int = getLongNat(at).toInt - - /** The long natural number at address `at` */ - def getLongNat(at: Addr): Long = { - var b = 0L - var x = 0L - var idx = at.index - do { - b = bytes(idx) - x = (x << 7) | (b & 0x7f) - idx += 1 - } while ((b & 0x80) == 0) - x - } - - /** The address (represented as a natural number) at address `at` */ - def getAddr(at: Addr) = Addr(getNat(at)) - - /** The smallest address equal to or following `at` which points to a non-zero byte */ - final def skipZeroes(at: Addr): Addr = - if (getByte(at) != 0) at else skipZeroes(at + 1) - - /** The address after the natural number found at address `at`. */ - final def skipNat(at: Addr): Addr = { - val next = at + 1 - if ((getByte(at) & 0x80) != 0) next else skipNat(next) - } - - /** The address referring to the end of data written so far */ - def currentAddr: Addr = Addr(length) - - /** Reserve `AddrWidth` bytes to write an address into */ - def reserveAddr(): Addr = { - val result = currentAddr - length += AddrWidth - result - } - - /** Fill reserved space at address `at` with address `target` */ - def fillAddr(at: Addr, target: Addr) = - putNat(at, target.index, AddrWidth) - - /** Write address without leading zeroes */ - def writeAddr(addr: Addr): Unit = writeNat(addr.index) - - // -- Finalization -------------------------------------------- - - /** Hook to be overridden in subclasses. - * Perform all actions necessary to assemble the final byte array. - * After `assemble` no more output actions to this buffer are permitted. - */ - def assemble(): Unit = () -} diff --git a/src/dotty/tools/dotc/core/pickling/TastyName.scala b/src/dotty/tools/dotc/core/pickling/TastyName.scala deleted file mode 100644 index e47ff9fc4..000000000 --- a/src/dotty/tools/dotc/core/pickling/TastyName.scala +++ /dev/null @@ -1,30 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import core.Names.TermName -import collection.mutable - -abstract class TastyName - -object TastyName { - - case class NameRef(val index: Int) extends AnyVal - - case class Simple(name: TermName) extends TastyName - case class Qualified(qualified: NameRef, selector: NameRef) extends TastyName - case class Signed(original: NameRef, params: List[NameRef], result: NameRef) extends TastyName - case class Expanded(prefix: NameRef, original: NameRef) extends TastyName - case class ModuleClass(module: NameRef) extends TastyName - case class SuperAccessor(accessed: NameRef) extends TastyName - case class DefaultGetter(method: NameRef, num: Int) extends TastyName - case class Shadowed(original: NameRef) extends TastyName - - class Table extends (NameRef => TastyName) { - private val names = new mutable.ArrayBuffer[TastyName] - def add(name: TastyName) = names += name - def apply(ref: NameRef) = names(ref.index) - def contents: Iterable[TastyName] = names - } -} diff --git a/src/dotty/tools/dotc/core/pickling/TastyPickler.scala b/src/dotty/tools/dotc/core/pickling/TastyPickler.scala deleted file mode 100644 index 74cc72b8c..000000000 --- a/src/dotty/tools/dotc/core/pickling/TastyPickler.scala +++ /dev/null @@ -1,70 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import PickleFormat._ -import collection.mutable -import TastyBuffer._ -import java.util.UUID -import core.Symbols.Symbol -import ast.tpd - -class TastyPickler { - - private val sections = new mutable.ArrayBuffer[(TastyName.NameRef, TastyBuffer)] - - private val headerBuffer = { - val buf = new TastyBuffer(24) - for (ch <- header) buf.writeByte(ch.toByte) - buf.writeNat(MajorVersion) - buf.writeNat(MinorVersion) - val uuid = UUID.randomUUID() - buf.writeUncompressedLong(uuid.getMostSignificantBits) - buf.writeUncompressedLong(uuid.getLeastSignificantBits) - buf - } - - val nameBuffer = new NameBuffer - - def newSection(name: String, buf: TastyBuffer) = - sections += ((nameBuffer.nameIndex(name), buf)) - - def assembleParts(): Array[Byte] = { - def lengthWithLength(buf: TastyBuffer) = { - buf.assemble() - buf.length + natSize(buf.length) - } - val totalSize = - headerBuffer.length + - lengthWithLength(nameBuffer) + { - for ((nameRef, buf) <- sections) yield - natSize(nameRef.index) + lengthWithLength(buf) - }.sum - val all = new TastyBuffer(totalSize) - all.writeBytes(headerBuffer.bytes, headerBuffer.length) - all.writeNat(nameBuffer.length) - all.writeBytes(nameBuffer.bytes, nameBuffer.length) - for ((nameRef, buf) <- sections) { - all.writeNat(nameRef.index) - all.writeNat(buf.length) - all.writeBytes(buf.bytes, buf.length) - } - assert(all.length == totalSize && all.bytes.length == totalSize, s"totalSize = $totalSize, all.length = ${all.length}, all.bytes.length = ${all.bytes.length}") - all.bytes - } - - /** - * Addresses in TASTY file of trees, stored by pickling. - * Note that trees are checked for reference equality, - * so one can reliably use this function only directly after `pickler` - */ - var addrOfTree: tpd.Tree => Option[Addr] = (_ => None) - - /** - * Addresses in TASTY file of symbols, stored by pickling. - * Note that trees are checked for reference equality, - * so one can reliably use this function only dirrectly after `pickler` - */ - var addrOfSym: Symbol => Option[Addr] = (_ => None) -} diff --git a/src/dotty/tools/dotc/core/pickling/TastyPrinter.scala b/src/dotty/tools/dotc/core/pickling/TastyPrinter.scala deleted file mode 100644 index 9d07fc5da..000000000 --- a/src/dotty/tools/dotc/core/pickling/TastyPrinter.scala +++ /dev/null @@ -1,122 +0,0 @@ -package dotty.tools.dotc -package core -package pickling - -import Contexts._, Decorators._ -import printing.Texts._ -import TastyName._ -import StdNames._ -import TastyUnpickler._ -import TastyBuffer.Addr -import util.Positions.{Position, offsetToInt} -import collection.mutable - -class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { - - val unpickler = new TastyUnpickler(bytes) - import unpickler.{tastyName, unpickle} - - def nameToString(name: TastyName): String = name match { - case Simple(name) => name.toString - case Qualified(qual, name) => nameRefToString(qual) + "." + nameRefToString(name) - case Signed(original, params, result) => - i"${nameRefToString(original)}@${params.map(nameRefToString)}%,%:${nameRefToString(result)}" - case Expanded(prefix, original) => s"$prefix${nme.EXPAND_SEPARATOR}$original" - case ModuleClass(original) => nameRefToString(original) + "/MODULECLASS" - case SuperAccessor(accessed) => nameRefToString(accessed) + "/SUPERACCESSOR" - case DefaultGetter(meth, num) => nameRefToString(meth) + "/DEFAULTGETTER" + num - case Shadowed(original) => nameRefToString(original) + "/SHADOWED" - } - - def nameRefToString(ref: NameRef): String = nameToString(tastyName(ref)) - - def printNames() = - for ((name, idx) <- tastyName.contents.zipWithIndex) - println(f"$idx%4d: " + nameToString(name)) - - def printContents(): Unit = { - println("Names:") - printNames() - println("Trees:") - unpickle(new TreeSectionUnpickler) - unpickle(new PositionSectionUnpickler) - } - - class TreeSectionUnpickler extends SectionUnpickler[Unit]("ASTs") { - import PickleFormat._ - def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = { - import reader._ - var indent = 0 - def newLine() = print(f"\n ${index(currentAddr) - index(startAddr)}%5d:" + " " * indent) - def printNat() = print(" " + readNat()) - def printName() = { - val idx = readNat() - print(" ") ;print(idx); print("["); print(nameRefToString(NameRef(idx))); print("]") - } - def printTree(): Unit = { - newLine() - val tag = readByte() - print(" ");print(astTagToString(tag)) - indent += 2 - if (tag >= firstLengthTreeTag) { - val len = readNat() - print(s"($len)") - val end = currentAddr + len - def printTrees() = until(end)(printTree()) - tag match { - case RENAMED => - printName(); printName() - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | BIND => - printName(); printTrees() - case REFINEDtype => - printTree(); printName(); printTrees() - case RETURN => - printNat(); printTrees() - case METHODtype | POLYtype => - printTree() - until(end) { printName(); printTree() } - case PARAMtype => - printNat(); printNat() - case _ => - printTrees() - } - if (currentAddr != end) { - println(s"incomplete read, current = $currentAddr, end = $end") - goto(end) - } - } - else if (tag >= firstNatASTTreeTag) { - tag match { - case IDENT | SELECT | TERMREF | TYPEREF | SELFDEF => printName() - case _ => printNat() - } - printTree() - } - else if (tag >= firstASTTreeTag) - printTree() - else if (tag >= firstNatTreeTag) - tag match { - case TERMREFpkg | TYPEREFpkg | STRINGconst | IMPORTED => printName() - case _ => printNat() - } - indent -= 2 - } - println(i"start = ${reader.startAddr}, base = $base, current = $currentAddr, end = $endAddr") - println(s"${endAddr.index - startAddr.index} bytes of AST, base = $currentAddr") - while (!isAtEnd) { - printTree() - newLine() - } - } - } - - class PositionSectionUnpickler extends SectionUnpickler[Unit]("Positions") { - def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = { - print(s"${reader.endAddr.index - reader.currentAddr.index}") - val (totalRange, positions) = new PositionUnpickler(reader).unpickle() - println(s" position bytes in $totalRange:") - val sorted = positions.toSeq.sortBy(_._1.index) - for ((addr, pos) <- sorted) println(s"${addr.index}: ${offsetToInt(pos.start)} .. ${pos.end}") - } - } -} diff --git a/src/dotty/tools/dotc/core/pickling/TastyReader.scala b/src/dotty/tools/dotc/core/pickling/TastyReader.scala deleted file mode 100644 index 35724e557..000000000 --- a/src/dotty/tools/dotc/core/pickling/TastyReader.scala +++ /dev/null @@ -1,142 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - - -import TastyBuffer._ -import TastyName.NameRef -import collection.mutable - -/** A byte array buffer that can be filled with bytes or natural numbers in TASTY format, - * and that supports reading and patching addresses represented as natural numbers. - * - * @param bytes The array containing data - * @param from The position from which to read - * @param end The position one greater than the last byte to be read - * @param base The index referenced by the logical zero address Addr(0) - */ -class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int = 0) { - - def this(bytes: Array[Byte]) = this(bytes, 0, bytes.length) - - private var bp: Int = start - - def addr(idx: Int) = Addr(idx - base) - def index(addr: Addr) = addr.index + base - - /** The address of the first byte to read, respectively byte that was read */ - def startAddr: Addr = addr(start) - - /** The address of the next byte to read */ - def currentAddr: Addr = addr(bp) - - /** the address one greater than the last brte to read */ - def endAddr: Addr = addr(end) - - /** Have all bytes been read? */ - def isAtEnd: Boolean = bp == end - - /** A new reader over the same array with the same address base, but with - * specified start and end positions - */ - def subReader(start: Addr, end: Addr): TastyReader = - new TastyReader(bytes, index(start), index(end), base) - - /** Read a byte of data. */ - def readByte(): Int = { - val result = bytes(bp) & 0xff - bp += 1 - result - } - - /** Returns the next byte of data as a natural number without advancing the read position */ - def nextByte: Int = bytes(bp) & 0xff - - /** Read the next `n` bytes of `data`. */ - def readBytes(n: Int): Array[Byte] = { - val result = new Array[Byte](n) - Array.copy(bytes, bp, result, 0, n) - bp += n - result - } - - /** Read a natural number fitting in an Int in big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def readNat(): Int = readLongNat.toInt - - /** Read an integer number in 2's complement big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def readInt(): Int = readLongInt.toInt - - /** Read a natural number fitting in a Long in big endian format, base 128. - * All but the last digits have bit 0x80 set. - */ - def readLongNat(): Long = { - var b = 0L - var x = 0L - do { - b = bytes(bp) - x = (x << 7) | (b & 0x7f) - bp += 1 - } while ((b & 0x80) == 0) - x - } - - /** Read a long integer number in 2's complement big endian format, base 128. */ - def readLongInt(): Long = { - var b = bytes(bp) - var x: Long = (b << 1).toByte >> 1 // sign extend with bit 6. - bp += 1 - while ((b & 0x80) == 0) { - b = bytes(bp) - x = (x << 7) | (b & 0x7f) - bp += 1 - } - x - } - - /** Read an uncompressed Long stored in 8 bytes in big endian format */ - def readUncompressedLong(): Long = { - var x = 0 - for (i <- 0 to 7) - x = (x << 8) | (readByte() & 0xff) - x - } - - /** Read a natural number and return as a NameRef */ - def readNameRef() = NameRef(readNat()) - - /** Read a natural number and return as an address */ - def readAddr() = Addr(readNat()) - - /** Read a length number and return the absolute end address implied by it, - * given as
+ . - */ - def readEnd(): Addr = addr(readNat() + bp) - - /** Set read position to the one pointed to by `addr` */ - def goto(addr: Addr): Unit = - bp = index(addr) - - /** Perform `op` until `end` address is reached and collect results in a list. */ - def until[T](end: Addr)(op: => T): List[T] = { - val buf = new mutable.ListBuffer[T] - while (bp < index(end)) buf += op - assert(bp == index(end)) - buf.toList - } - - /** If before given `end` address, the result of `op`, otherwise `default` */ - def ifBefore[T](end: Addr)(op: => T, default: T): T = - if (bp < index(end)) op else default - - /** Perform `op` while cindition `cond` holds and collect results in a list. */ - def collectWhile[T](cond: => Boolean)(op: => T): List[T] = { - val buf = new mutable.ListBuffer[T] - while (cond) buf += op - buf.toList - } -} diff --git a/src/dotty/tools/dotc/core/pickling/TastyUnpickler.scala b/src/dotty/tools/dotc/core/pickling/TastyUnpickler.scala deleted file mode 100644 index 5fbb85768..000000000 --- a/src/dotty/tools/dotc/core/pickling/TastyUnpickler.scala +++ /dev/null @@ -1,95 +0,0 @@ -package dotty.tools.dotc -package core -package pickling - -import scala.collection.mutable -import PickleFormat._ -import Names.{Name, termName} -import java.util.UUID - -object TastyUnpickler { - class UnpickleException(msg: String) extends Exception(msg) - - abstract class SectionUnpickler[R](val name: String) { - def unpickle(reader: TastyReader, tastyName: TastyName.Table): R - } -} - -import TastyUnpickler._ - -class TastyUnpickler(reader: TastyReader) { - import reader._ - - def this(bytes: Array[Byte]) = this(new TastyReader(bytes)) - - private val sectionReader = new mutable.HashMap[String, TastyReader] - val tastyName = new TastyName.Table - - def check(cond: Boolean, msg: => String) = - if (!cond) throw new UnpickleException(msg) - - def readString(): String = { - val TastyName.Simple(name) = tastyName(readNameRef()) - name.toString - } - - def readName(): TastyName = { - import TastyName._ - val tag = readByte() - val length = readNat() - val start = currentAddr - val end = start + length - val result = tag match { - case UTF8 => - goto(end) - Simple(termName(bytes, start.index, length)) - case QUALIFIED => - Qualified(readNameRef(), readNameRef()) - case SIGNED => - val original = readNameRef() - val result = readNameRef() - val params = until(end)(readNameRef()) - Signed(original, params, result) - case EXPANDED => - Expanded(readNameRef(), readNameRef()) - case OBJECTCLASS => - ModuleClass(readNameRef()) - case SUPERACCESSOR => - SuperAccessor(readNameRef()) - case DEFAULTGETTER => - DefaultGetter(readNameRef(), readNat()) - case SHADOWED => - Shadowed(readNameRef()) - } - assert(currentAddr == end, s"bad name $result $start $currentAddr $end") - result - } - - private def readHeader(): UUID = { - for (i <- 0 until header.length) - check(readByte() == header(i), "not a TASTy file") - val major = readNat() - val minor = readNat() - check(major == MajorVersion && minor <= MinorVersion, - s"""TASTy signature has wrong version. - | expected: $MajorVersion.$MinorVersion - | found : $major.$minor""".stripMargin) - new UUID(readUncompressedLong(), readUncompressedLong()) - } - - val uuid = readHeader() - - locally { - until(readEnd()) { tastyName.add(readName()) } - while (!isAtEnd) { - val secName = readString() - val secEnd = readEnd() - sectionReader(secName) = new TastyReader(bytes, currentAddr.index, secEnd.index, currentAddr.index) - goto(secEnd) - } - } - - def unpickle[R](sec: SectionUnpickler[R]): Option[R] = - for (reader <- sectionReader.get(sec.name)) yield - sec.unpickle(reader, tastyName) -} diff --git a/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala b/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala deleted file mode 100644 index 393ffd278..000000000 --- a/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala +++ /dev/null @@ -1,179 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import util.Util.{bestFit, dble} -import TastyBuffer.{Addr, AddrWidth} -import config.Printers.pickling -import ast.tpd.Tree - -class TreeBuffer extends TastyBuffer(50000) { - - private final val ItemsOverOffsets = 2 - private val initialOffsetSize = bytes.length / (AddrWidth * ItemsOverOffsets) - private var offsets = new Array[Int](initialOffsetSize) - private var isRelative = new Array[Boolean](initialOffsetSize) - private var delta: Array[Int] = _ - private var numOffsets = 0 - - private[pickling] val pickledTrees = new java.util.IdentityHashMap[Tree, Any] // Value type is really Addr, but that's not compatible with null - - def addrOfTree(tree: Tree): Option[Addr] = pickledTrees.get(tree) match { - case null => None - case n => Some(n.asInstanceOf[Addr]) - } - - private def offset(i: Int): Addr = Addr(offsets(i)) - - private def keepOffset(relative: Boolean): Unit = { - if (numOffsets == offsets.length) { - offsets = dble(offsets) - isRelative = dble(isRelative) - } - offsets(numOffsets) = length - isRelative(numOffsets) = relative - numOffsets += 1 - } - - /** Reserve space for a reference, to be adjusted later */ - def reserveRef(relative: Boolean): Addr = { - val addr = currentAddr - keepOffset(relative) - reserveAddr() - addr - } - - /** Write reference right adjusted into freshly reserved field. */ - def writeRef(target: Addr) = { - keepOffset(relative = false) - fillAddr(reserveAddr(), target) - } - - /** Fill previously reserved field with a reference */ - def fillRef(at: Addr, target: Addr, relative: Boolean) = { - val addr = if (relative) target.relativeTo(at) else target - fillAddr(at, addr) - } - - /** The amount by which the bytes at the given address are shifted under compression */ - def deltaAt(at: Addr): Int = { - val idx = bestFit(offsets, numOffsets, at.index - 1) - if (idx < 0) 0 else delta(idx) - } - - /** The address to which `x` is translated under compression */ - def adjusted(x: Addr): Addr = x - deltaAt(x) - - /** Compute all shift-deltas */ - private def computeDeltas() = { - delta = new Array[Int](numOffsets) - var lastDelta = 0 - var i = 0 - while (i < numOffsets) { - val off = offset(i) - val skippedOff = skipZeroes(off) - val skippedCount = skippedOff.index - off.index - assert(skippedCount < AddrWidth, s"unset field at position $off") - lastDelta += skippedCount - delta(i) = lastDelta - i += 1 - } - } - - /** The absolute or relative adjusted address at index `i` of `offsets` array*/ - private def adjustedOffset(i: Int): Addr = { - val at = offset(i) - val original = getAddr(at) - if (isRelative(i)) { - val start = skipNat(at) - val len1 = original + delta(i) - deltaAt(original + start.index) - val len2 = adjusted(original + start.index) - adjusted(start).index - assert(len1 == len2, - s"adjusting offset #$i: $at, original = $original, len1 = $len1, len2 = $len2") - len1 - } else adjusted(original) - } - - /** Adjust all offsets according to previously computed deltas */ - private def adjustOffsets(): Unit = { - for (i <- 0 until numOffsets) { - val corrected = adjustedOffset(i) - fillAddr(offset(i), corrected) - } - } - - /** Adjust deltas to also take account references that will shrink (and thereby - * generate additional zeroes that can be skipped) due to previously - * computed adjustements. - */ - private def adjustDeltas(): Int = { - val delta1 = new Array[Int](delta.length) - var lastDelta = 0 - var i = 0 - while (i < numOffsets) { - val corrected = adjustedOffset(i) - lastDelta += AddrWidth - TastyBuffer.natSize(corrected.index) - delta1(i) = lastDelta - i += 1 - } - val saved = - if (numOffsets == 0) 0 - else delta1(numOffsets - 1) - delta(numOffsets - 1) - delta = delta1 - saved - } - - /** Compress pickle buffer, shifting bytes to close all skipped zeroes. */ - private def compress(): Int = { - var lastDelta = 0 - var start = 0 - var i = 0 - var wasted = 0 - def shift(end: Int) = - Array.copy(bytes, start, bytes, start - lastDelta, end - start) - while (i < numOffsets) { - val next = offsets(i) - shift(next) - start = next + delta(i) - lastDelta - val pastZeroes = skipZeroes(Addr(next)).index - assert(pastZeroes >= start, s"something's wrong: eliminated non-zero") - wasted += (pastZeroes - start) - lastDelta = delta(i) - i += 1 - } - shift(length) - length -= lastDelta - wasted - } - - def adjustPickledTrees(): Unit = { - val it = pickledTrees.keySet.iterator - while (it.hasNext) { - val tree = it.next - pickledTrees.put(tree, adjusted(pickledTrees.get(tree).asInstanceOf[Addr])) - } - } - - /** Final assembly, involving the following steps: - * - compute deltas - * - adjust deltas until additional savings are < 1% of total - * - adjust offsets according to the adjusted deltas - * - shrink buffer, skipping zeroes. - */ - def compactify(): Unit = { - val origLength = length - computeDeltas() - //println(s"offsets: ${offsets.take(numOffsets).deep}") - //println(s"deltas: ${delta.take(numOffsets).deep}") - var saved = 0 - do { - saved = adjustDeltas() - pickling.println(s"adjusting deltas, saved = $saved") - } while (saved > 0 && length / saved < 100) - adjustOffsets() - adjustPickledTrees() - val wasted = compress() - pickling.println(s"original length: $origLength, compressed to: $length, wasted: $wasted") // DEBUG, for now. - } -} diff --git a/src/dotty/tools/dotc/core/pickling/TreePickler.scala b/src/dotty/tools/dotc/core/pickling/TreePickler.scala deleted file mode 100644 index e463b4447..000000000 --- a/src/dotty/tools/dotc/core/pickling/TreePickler.scala +++ /dev/null @@ -1,552 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import ast.Trees._ -import PickleFormat._ -import core._ -import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._, StdNames.tpnme, NameOps._ -import collection.mutable -import NameOps._ -import TastyBuffer._ - -class TreePickler(pickler: TastyPickler) { - val buf = new TreeBuffer - pickler.newSection("ASTs", buf) - import buf._ - import pickler.nameBuffer.{nameIndex, fullNameIndex} - import ast.tpd._ - - private val symRefs = new mutable.HashMap[Symbol, Addr] - private val forwardSymRefs = new mutable.HashMap[Symbol, List[Addr]] - private val pickledTypes = new java.util.IdentityHashMap[Type, Any] // Value type is really Addr, but that's not compatible with null - - private def withLength(op: => Unit) = { - val lengthAddr = reserveRef(relative = true) - op - fillRef(lengthAddr, currentAddr, relative = true) - } - - def addrOfSym(sym: Symbol): Option[Addr] = { - symRefs.get(sym) - } - - def preRegister(tree: Tree)(implicit ctx: Context): Unit = tree match { - case tree: MemberDef => - if (!symRefs.contains(tree.symbol)) symRefs(tree.symbol) = NoAddr - case _ => - } - - def registerDef(sym: Symbol): Unit = { - symRefs(sym) = currentAddr - forwardSymRefs.get(sym) match { - case Some(refs) => - refs.foreach(fillRef(_, currentAddr, relative = false)) - forwardSymRefs -= sym - case None => - } - } - - private def pickleName(name: Name): Unit = writeNat(nameIndex(name).index) - private def pickleName(name: TastyName): Unit = writeNat(nameIndex(name).index) - private def pickleNameAndSig(name: Name, sig: Signature) = { - val Signature(params, result) = sig - pickleName(TastyName.Signed(nameIndex(name), params.map(fullNameIndex), fullNameIndex(result))) - } - - private def pickleName(sym: Symbol)(implicit ctx: Context): Unit = - if (sym is Flags.ExpandedName) - pickleName(TastyName.Expanded( - nameIndex(sym.name.expandedPrefix), nameIndex(sym.name.unexpandedName))) - else pickleName(sym.name) - - private def pickleSymRef(sym: Symbol)(implicit ctx: Context) = symRefs.get(sym) match { - case Some(label) => - if (label != NoAddr) writeRef(label) else pickleForwardSymRef(sym) - case None => - ctx.log(i"pickling reference to as yet undefined $sym in ${sym.owner}", sym.pos) - pickleForwardSymRef(sym) - } - - private def pickleForwardSymRef(sym: Symbol)(implicit ctx: Context) = { - val ref = reserveRef(relative = false) - assert(!sym.is(Flags.Package), sym) - forwardSymRefs(sym) = ref :: forwardSymRefs.getOrElse(sym, Nil) - } - - private def isLocallyDefined(sym: Symbol)(implicit ctx: Context) = symRefs.get(sym) match { - case Some(label) => assert(sym.exists); label != NoAddr - case None => false - } - - def pickle(trees: List[Tree])(implicit ctx: Context) = { - - def qualifiedName(sym: Symbol): TastyName = - if (sym.isRoot || sym.owner.isRoot) TastyName.Simple(sym.name.toTermName) - else TastyName.Qualified(nameIndex(qualifiedName(sym.owner)), nameIndex(sym.name)) - - def pickleConstant(c: Constant): Unit = c.tag match { - case UnitTag => - writeByte(UNITconst) - case BooleanTag => - writeByte(if (c.booleanValue) TRUEconst else FALSEconst) - case ByteTag => - writeByte(BYTEconst) - writeInt(c.byteValue) - case ShortTag => - writeByte(SHORTconst) - writeInt(c.shortValue) - case CharTag => - writeByte(CHARconst) - writeNat(c.charValue) - case IntTag => - writeByte(INTconst) - writeInt(c.intValue) - case LongTag => - writeByte(LONGconst) - writeLongInt(c.longValue) - case FloatTag => - writeByte(FLOATconst) - writeInt(java.lang.Float.floatToRawIntBits(c.floatValue)) - case DoubleTag => - writeByte(DOUBLEconst) - writeLongInt(java.lang.Double.doubleToRawLongBits(c.doubleValue)) - case StringTag => - writeByte(STRINGconst) - writeNat(nameIndex(c.stringValue).index) - case NullTag => - writeByte(NULLconst) - case ClazzTag => - writeByte(CLASSconst) - pickleType(c.typeValue) - case EnumTag => - writeByte(ENUMconst) - pickleType(c.symbolValue.termRef) - } - - def pickleType(tpe0: Type, richTypes: Boolean = false): Unit = try { - val tpe = tpe0.stripTypeVar - val prev = pickledTypes.get(tpe) - if (prev == null) { - pickledTypes.put(tpe, currentAddr) - pickleNewType(tpe, richTypes) - } - else { - writeByte(SHARED) - writeRef(prev.asInstanceOf[Addr]) - } - } catch { - case ex: AssertionError => - println(i"error when pickling type $tpe0") - throw ex - } - - def pickleNewType(tpe: Type, richTypes: Boolean): Unit = try { tpe match { - case ConstantType(value) => - pickleConstant(value) - case tpe: TypeRef if tpe.info.isAlias && tpe.symbol.is(Flags.AliasPreferred) => - pickleType(tpe.info.bounds.hi) - case tpe: WithFixedSym => - val sym = tpe.symbol - if (sym.is(Flags.Package)) { - writeByte(if (tpe.isType) TYPEREFpkg else TERMREFpkg) - pickleName(qualifiedName(sym)) - } - else { - assert(tpe.prefix == NoPrefix) - def pickleRef() = { - writeByte(if (tpe.isType) TYPEREFdirect else TERMREFdirect) - pickleSymRef(sym) - } - if (sym is Flags.BindDefinedType) { - registerDef(sym) - writeByte(BIND) - withLength { - pickleName(sym.name) - pickleType(sym.info) - pickleRef() - } - } - else pickleRef() - } - case tpe: TermRefWithSignature => - if (tpe.symbol.is(Flags.Package)) picklePackageRef(tpe.symbol) - else { - writeByte(TERMREF) - pickleNameAndSig(tpe.name, tpe.signature); pickleType(tpe.prefix) - } - case tpe: NamedType => - if (tpe.name == tpnme.Apply && tpe.prefix.argInfos.nonEmpty && tpe.prefix.isInstantiatedLambda) - // instantiated lambdas are pickled as APPLIEDTYPE; #Apply will - // be reconstituted when unpickling. - pickleType(tpe.prefix) - else if (isLocallyDefined(tpe.symbol)) { - writeByte(if (tpe.isType) TYPEREFsymbol else TERMREFsymbol) - pickleSymRef(tpe.symbol); pickleType(tpe.prefix) - } - else { - writeByte(if (tpe.isType) TYPEREF else TERMREF) - pickleName(tpe.name); pickleType(tpe.prefix) - } - case tpe: ThisType => - if (tpe.cls.is(Flags.Package) && !tpe.cls.isEffectiveRoot) - picklePackageRef(tpe.cls) - else { - writeByte(THIS) - pickleType(tpe.tref) - } - case tpe: SuperType => - writeByte(SUPERtype) - withLength { pickleType(tpe.thistpe); pickleType(tpe.supertpe)} - case tpe: SkolemType => - writeByte(SKOLEMtype) - writeRef(pickledTypes.get(tpe.binder).asInstanceOf[Addr]) - case tpe: RefinedType => - val args = tpe.argInfos(interpolate = false) - if (args.isEmpty) { - writeByte(REFINEDtype) - withLength { - pickleType(tpe.parent) - pickleName(tpe.refinedName) - pickleType(tpe.refinedInfo, richTypes = true) - } - } - else { - writeByte(APPLIEDtype) - withLength { pickleType(tpe.withoutArgs(args)); args.foreach(pickleType(_)) } - } - case tpe: TypeAlias => - writeByte(TYPEALIAS) - withLength { - pickleType(tpe.alias, richTypes) - tpe.variance match { - case 1 => writeByte(COVARIANT) - case -1 => writeByte(CONTRAVARIANT) - case 0 => - } - } - case tpe: TypeBounds => - writeByte(TYPEBOUNDS) - withLength { pickleType(tpe.lo, richTypes); pickleType(tpe.hi, richTypes) } - case tpe: AnnotatedType => - writeByte(ANNOTATED) - withLength { pickleTree(tpe.annot.tree); pickleType(tpe.tpe, richTypes) } - case tpe: AndOrType => - writeByte(if (tpe.isAnd) ANDtype else ORtype) - withLength { pickleType(tpe.tp1, richTypes); pickleType(tpe.tp2, richTypes) } - case tpe: ExprType => - writeByte(BYNAMEtype) - pickleType(tpe.underlying) - case tpe: MethodType if richTypes => - writeByte(METHODtype) - pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramTypes) - case tpe: PolyType if richTypes => - writeByte(POLYtype) - pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramBounds) - case tpe: PolyParam => - if (!pickleParamType(tpe)) - // TODO figure out why this case arises in e.g. pickling AbstractFileReader. - ctx.typerState.constraint.entry(tpe) match { - case TypeBounds(lo, hi) if lo eq hi => pickleNewType(lo, richTypes) - case _ => assert(false, s"orphan poly parameter: $tpe") - } - case tpe: MethodParam => - assert(pickleParamType(tpe), s"orphan method parameter: $tpe") - case tpe: LazyRef => - pickleType(tpe.ref) - }} catch { - case ex: AssertionError => - println(i"error while pickling type $tpe") - throw ex - } - - def picklePackageRef(pkg: Symbol): Unit = { - writeByte(TERMREFpkg) - pickleName(qualifiedName(pkg)) - } - - def pickleMethodic(result: Type, names: List[Name], types: List[Type]) = - withLength { - pickleType(result, richTypes = true) - (names, types).zipped.foreach { (name, tpe) => - pickleName(name); pickleType(tpe) - } - } - - def pickleParamType(tpe: ParamType): Boolean = { - val binder = pickledTypes.get(tpe.binder) - val pickled = binder != null - if (pickled) { - writeByte(PARAMtype) - withLength { writeRef(binder.asInstanceOf[Addr]); writeNat(tpe.paramNum) } - } - pickled - } - - def pickleTpt(tpt: Tree): Unit = pickleType(tpt.tpe) // TODO correlate with original when generating positions - - def pickleTreeUnlessEmpty(tree: Tree): Unit = - if (!tree.isEmpty) pickleTree(tree) - - def pickleTree(tree: Tree): Unit = try { - pickledTrees.put(tree, currentAddr) - tree match { - case Ident(name) => - tree.tpe match { - case tp: TermRef => pickleType(tp) - case _ => - writeByte(IDENT) - pickleName(name) - pickleType(tree.tpe) - } - case This(_) => - pickleType(tree.tpe) - case Select(qual, name) => - writeByte(SELECT) - val realName = tree.tpe match { - case tp: NamedType if tp.name.isShadowedName => tp.name - case _ => name - } - val sig = tree.tpe.signature - if (sig == Signature.NotAMethod) pickleName(realName) - else pickleNameAndSig(realName, sig) - pickleTree(qual) - case Apply(fun, args) => - writeByte(APPLY) - withLength { - pickleTree(fun) - args.foreach(pickleTree) - } - case TypeApply(fun, args) => - writeByte(TYPEAPPLY) - withLength { - pickleTree(fun) - args.foreach(pickleTpt) - } - case Literal(const1) => - pickleConstant { - tree.tpe match { - case ConstantType(const2) => const2 - case _ => const1 - } - } - case Super(qual, mix) => - writeByte(SUPER) - withLength { - pickleTree(qual); - if (!mix.isEmpty) { - val SuperType(_, mixinType) = tree.tpe - pickleType(mixinType) - } - } - case New(tpt) => - writeByte(NEW) - pickleTpt(tpt) - case Pair(left, right) => - writeByte(PAIR) - withLength { pickleTree(left); pickleTree(right) } - case Typed(expr, tpt) => - writeByte(TYPED) - withLength { pickleTree(expr); pickleTpt(tpt) } - case NamedArg(name, arg) => - writeByte(NAMEDARG) - withLength { pickleName(name); pickleTree(arg) } - case Assign(lhs, rhs) => - writeByte(ASSIGN) - withLength { pickleTree(lhs); pickleTree(rhs) } - case Block(stats, expr) => - writeByte(BLOCK) - stats.foreach(preRegister) - withLength { pickleTree(expr); stats.foreach(pickleTree) } - case If(cond, thenp, elsep) => - writeByte(IF) - withLength{ pickleTree(cond); pickleTree(thenp); pickleTree(elsep) } - case Closure(env, meth, tpt) => - writeByte(LAMBDA) - assert(env.isEmpty) - withLength{ - pickleTree(meth) - if (tpt.tpe.exists) pickleTpt(tpt) - } - case Match(selector, cases) => - writeByte(MATCH) - withLength { pickleTree(selector); cases.foreach(pickleTree) } - case CaseDef(pat, guard, rhs) => - writeByte(CASEDEF) - withLength { pickleTree(pat); pickleTree(rhs); pickleTreeUnlessEmpty(guard) } - case Return(expr, from) => - writeByte(RETURN) - withLength { pickleSymRef(from.symbol); pickleTreeUnlessEmpty(expr) } - case Try(block, cases, finalizer) => - writeByte(TRY) - withLength { pickleTree(block); cases.foreach(pickleTree); pickleTreeUnlessEmpty(finalizer) } - case SeqLiteral(elems) => - writeByte(REPEATED) - withLength { elems.foreach(pickleTree) } - case TypeTree(original) => - pickleTpt(tree) - case Bind(name, body) => - registerDef(tree.symbol) - writeByte(BIND) - withLength { pickleName(name); pickleType(tree.symbol.info); pickleTree(body) } - case Alternative(alts) => - writeByte(ALTERNATIVE) - withLength { alts.foreach(pickleTree) } - case UnApply(fun, implicits, patterns) => - writeByte(UNAPPLY) - withLength { - pickleTree(fun) - for (implicitArg <- implicits) { - writeByte(IMPLICITarg) - pickleTree(implicitArg) - } - pickleType(tree.tpe) - patterns.foreach(pickleTree) - } - case tree: ValDef => - pickleDef(VALDEF, tree.symbol, tree.tpt, tree.rhs) - case tree: DefDef => - def pickleAllParams = { - pickleParams(tree.tparams) - for (vparams <- tree.vparamss) { - writeByte(PARAMS) - withLength { pickleParams(vparams) } - } - } - pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleAllParams) - case tree: TypeDef => - pickleDef(TYPEDEF, tree.symbol, tree.rhs) - case tree: Template => - registerDef(tree.symbol) - writeByte(TEMPLATE) - val (params, rest) = tree.body partition { - case stat: TypeDef => stat.symbol is Flags.Param - case stat: ValOrDefDef => - stat.symbol.is(Flags.ParamAccessor) && !stat.symbol.isSetter - case _ => false - } - withLength { - pickleParams(params) - tree.parents.foreach(pickleTree) - val cinfo @ ClassInfo(_, _, _, _, selfInfo) = tree.symbol.owner.info - if ((selfInfo ne NoType) || !tree.self.isEmpty) { - writeByte(SELFDEF) - pickleName(tree.self.name) - pickleType { - cinfo.selfInfo match { - case sym: Symbol => sym.info - case tp: Type => tp - } - } - } - pickleStats(tree.constr :: rest) - } - case Import(expr, selectors) => - writeByte(IMPORT) - withLength { - pickleTree(expr) - selectors foreach { - case Pair(Ident(from), Ident(to)) => - writeByte(RENAMED) - withLength { pickleName(from); pickleName(to) } - case Ident(name) => - writeByte(IMPORTED) - pickleName(name) - } - } - case PackageDef(pid, stats) => - writeByte(PACKAGE) - withLength { pickleType(pid.tpe); pickleStats(stats) } - }} - catch { - case ex: AssertionError => - println(i"error when pickling tree $tree") - throw ex - } - - def pickleDef(tag: Int, sym: Symbol, tpt: Tree, rhs: Tree = EmptyTree, pickleParams: => Unit = ()) = { - assert(symRefs(sym) == NoAddr) - registerDef(sym) - writeByte(tag) - withLength { - pickleName(sym) - pickleParams - tpt match { - case tpt: TypeTree => pickleTpt(tpt) - case _ => pickleTree(tpt) - } - pickleTreeUnlessEmpty(rhs) - pickleModifiers(sym) - } - } - - def pickleParam(tree: Tree): Unit = tree match { - case tree: ValDef => pickleDef(PARAM, tree.symbol, tree.tpt) - case tree: DefDef => pickleDef(PARAM, tree.symbol, tree.tpt, tree.rhs) - case tree: TypeDef => pickleDef(TYPEPARAM, tree.symbol, tree.rhs) - } - - def pickleParams(trees: List[Tree]): Unit = { - trees.foreach(preRegister) - trees.foreach(pickleParam) - } - - def pickleStats(stats: List[Tree]) = { - stats.foreach(preRegister) - stats.foreach(stat => if (!stat.isEmpty) pickleTree(stat)) - } - - def pickleModifiers(sym: Symbol): Unit = { - import Flags._ - val flags = sym.flags - val privateWithin = sym.privateWithin - if (privateWithin.exists) { - writeByte(if (flags is Protected) PROTECTEDqualified else PRIVATEqualified) - pickleType(privateWithin.typeRef) - } - if (flags is Private) writeByte(PRIVATE) - if (flags is Protected) if (!privateWithin.exists) writeByte(PROTECTED) - if ((flags is Final) && !(sym is Module)) writeByte(FINAL) - if (flags is Case) writeByte(CASE) - if (flags is Override) writeByte(OVERRIDE) - if (flags is Inline) writeByte(INLINE) - if (flags is JavaStatic) writeByte(STATIC) - if (flags is Module) writeByte(OBJECT) - if (flags is Local) writeByte(LOCAL) - if (flags is Synthetic) writeByte(SYNTHETIC) - if (flags is Artifact) writeByte(ARTIFACT) - if (flags is Scala2x) writeByte(SCALA2X) - if (flags is InSuperCall) writeByte(INSUPERCALL) - if (sym.isTerm) { - if (flags is Implicit) writeByte(IMPLICIT) - if ((flags is Lazy) && !(sym is Module)) writeByte(LAZY) - if (flags is AbsOverride) writeByte(ABSOVERRIDE) - if (flags is Mutable) writeByte(MUTABLE) - if (flags is Accessor) writeByte(FIELDaccessor) - if (flags is CaseAccessor) writeByte(CASEaccessor) - if (flags is DefaultParameterized) writeByte(DEFAULTparameterized) - } else { - if (flags is Sealed) writeByte(SEALED) - if (flags is Abstract) writeByte(ABSTRACT) - if (flags is Trait) writeByte(TRAIT) - if (flags is Covariant) writeByte(COVARIANT) - if (flags is Contravariant) writeByte(CONTRAVARIANT) - } - sym.annotations.foreach(pickleAnnotation) - } - - def pickleAnnotation(ann: Annotation) = { - writeByte(ANNOTATION) - withLength { pickleType(ann.symbol.typeRef); pickleTree(ann.tree) } - } - - def updateMapWithDeltas[T](mp: collection.mutable.Map[T, Addr]) = - for (key <- mp.keysIterator.toBuffer[T]) mp(key) = adjusted(mp(key)) - - trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree)) - assert(forwardSymRefs.isEmpty, i"unresolved symbols: ${forwardSymRefs.keySet.toList}%, %") - compactify() - updateMapWithDeltas(symRefs) - } -} diff --git a/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala b/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala deleted file mode 100644 index 7b143dfad..000000000 --- a/src/dotty/tools/dotc/core/pickling/TreeUnpickler.scala +++ /dev/null @@ -1,890 +0,0 @@ -package dotty.tools -package dotc -package core -package pickling - -import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._ -import StdNames._, Denotations._, Flags._, Constants._, Annotations._ -import util.Positions._ -import dotty.tools.dotc.ast.{tpd, Trees, untpd} -import Trees._ -import Decorators._ -import TastyUnpickler._, TastyBuffer._ -import annotation.switch -import scala.collection.{ mutable, immutable } -import typer.Mode -import config.Printers.pickling -import PositionPickler._ - -/** Unpickler for typed trees - * @param reader the reader from which to unpickle - * @param tastyName the nametable - */ -class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { - import dotty.tools.dotc.core.pickling.PickleFormat._ - import TastyName._ - import tpd._ - - private var readPositions = false - private var totalRange = NoPosition - private var positions: collection.Map[Addr, Position] = _ - - /** Make a subsequent call to `unpickle` return trees with positions - * @param totalRange the range position enclosing all returned trees, - * or NoPosition if positions should not be unpickled - * @param positions a map from tree addresses to their positions relative - * to positions of parent nodes. - */ - def usePositions(totalRange: Position, positions: collection.Map[Addr, Position]): Unit = { - readPositions = true - this.totalRange = totalRange - this.positions = positions - } - - private val symAtAddr = new mutable.HashMap[Addr, Symbol] - private val treeAtAddr = new mutable.HashMap[Addr, Tree] - private val typeAtAddr = new mutable.HashMap[Addr, Type] // currently populated only for types that are known to be SHAREd. - private var stubs: Set[Symbol] = Set() - - private var roots: Set[SymDenotation] = null - - /** Enter all toplevel classes and objects into their scopes - * @param roots a set of SymDenotations that should be overwritten by unpickling - */ - def enterTopLevel(roots: Set[SymDenotation])(implicit ctx: Context): Unit = { - this.roots = roots - new TreeReader(reader).fork.indexStats(reader.endAddr) - } - - /** The unpickled trees */ - def unpickle()(implicit ctx: Context): List[Tree] = { - assert(roots != null, "unpickle without previous enterTopLevel") - val stats = new TreeReader(reader) - .readIndexedStats(NoSymbol, reader.endAddr)(ctx.addMode(Mode.AllowDependentFunctions)) - normalizePos(stats, totalRange) - stats - } - - def toTermName(tname: TastyName): TermName = tname match { - case Simple(name) => name - case Qualified(qual, name) => toTermName(qual) ++ "." ++ toTermName(name) - case Signed(original, params, result) => toTermName(original) - case Shadowed(original) => toTermName(original).shadowedName - case Expanded(prefix, original) => toTermName(original).expandedName(toTermName(prefix)) - case ModuleClass(original) => toTermName(original).moduleClassName.toTermName - case SuperAccessor(accessed) => ??? - case DefaultGetter(meth, num) => ??? - } - - def toTermName(ref: NameRef): TermName = toTermName(tastyName(ref)) - def toTypeName(ref: NameRef): TypeName = toTermName(ref).toTypeName - - class Completer(reader: TastyReader) extends LazyType { - import reader._ - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - treeAtAddr(currentAddr) = new TreeReader(reader).readIndexedDef() - } - } - - class TreeReader(val reader: TastyReader) { - import reader._ - - def forkAt(start: Addr) = new TreeReader(subReader(start, endAddr)) - def fork = forkAt(currentAddr) - - def skipTree(tag: Int): Unit = - if (tag >= firstLengthTreeTag) goto(readEnd()) - else if (tag >= firstNatASTTreeTag) { readNat(); skipTree() } - else if (tag >= firstASTTreeTag) skipTree() - else if (tag >= firstNatTreeTag) readNat() - def skipTree(): Unit = skipTree(readByte()) - - def skipParams(): Unit = - while (nextByte == PARAMS || nextByte == TYPEPARAM) skipTree() - - /** The next tag, following through SHARED tags */ - def nextUnsharedTag: Int = { - val tag = nextByte - if (tag == SHARED) { - val lookAhead = fork - lookAhead.reader.readByte() - forkAt(lookAhead.reader.readAddr()).nextUnsharedTag - } - else tag - } - - def readName(): TermName = toTermName(readNameRef()) - - def readNameSplitSig()(implicit ctx: Context): Any /* TermName | (TermName, Signature) */ = - tastyName(readNameRef()) match { - case Signed(original, params, result) => - var sig = Signature(params map toTypeName, toTypeName(result)) - if (sig == Signature.NotAMethod) sig = Signature.NotAMethod - (toTermName(original), sig) - case name => - toTermName(name) - } - -// ------ Reading types ----------------------------------------------------- - - /** Read names in an interleaved sequence of (parameter) names and types/bounds */ - def readParamNames[N <: Name](end: Addr): List[N] = - until(end) { - val name = readName().asInstanceOf[N] - skipTree() - name - } - - /** Read types or bounds in an interleaved sequence of (parameter) names and types/bounds */ - def readParamTypes[T <: Type](end: Addr)(implicit ctx: Context): List[T] = - until(end) { readNat(); readType().asInstanceOf[T] } - - /** Read referece to definition and return symbol created at that definition */ - def readSymRef()(implicit ctx: Context): Symbol = { - val start = currentAddr - val addr = readAddr() - symAtAddr get addr match { - case Some(sym) => sym - case None => - // Create a stub; owner might be wrong but will be overwritten later. - forkAt(addr).createSymbol() - val sym = symAtAddr(addr) - ctx.log(i"forward reference to $sym") - stubs += sym - sym - } - } - - /** Read a type */ - def readType()(implicit ctx: Context): Type = { - val start = currentAddr - val tag = readByte() - pickling.println(s"reading type ${astTagToString(tag)} at $start") - - def registeringType[T](tp: Type, op: => T): T = { - typeAtAddr(start) = tp - op - } - - def readLengthType(): Type = { - val end = readEnd() - - def readNamesSkipParams[N <: Name]: (List[N], TreeReader) = { - val nameReader = fork - nameReader.skipTree() // skip result - val paramReader = nameReader.fork - (nameReader.readParamNames[N](end), paramReader) - } - - val result = - (tag: @switch) match { - case SUPERtype => - SuperType(readType(), readType()) - case REFINEDtype => - val parent = readType() - var name: Name = readName() - val ttag = nextUnsharedTag - if (ttag == TYPEBOUNDS || ttag == TYPEALIAS) name = name.toTypeName - RefinedType(parent, name, rt => registeringType(rt, readType())) - // Note that the lambda "rt => ..." is not equivalent to a wildcard closure! - // Eta expansion of the latter puts readType() out of the expression. - case APPLIEDtype => - readType().appliedTo(until(end)(readType())) - case TYPEBOUNDS => - TypeBounds(readType(), readType()) - case TYPEALIAS => - val alias = readType() - val variance = - if (nextByte == COVARIANT) { readByte(); 1 } - else if (nextByte == CONTRAVARIANT) { readByte(); -1 } - else 0 - TypeAlias(alias, variance) - case ANNOTATED => - AnnotatedType(Annotation(readTerm()), readType()) - case ANDtype => - AndType(readType(), readType()) - case ORtype => - OrType(readType(), readType()) - case BIND => - val sym = ctx.newSymbol(ctx.owner, readName().toTypeName, BindDefinedType, readType()) - symAtAddr(start) = sym - TypeRef.withFixedSym(NoPrefix, sym.name, sym) - case POLYtype => - val (names, paramReader) = readNamesSkipParams[TypeName] - val result = PolyType(names)( - pt => registeringType(pt, paramReader.readParamTypes[TypeBounds](end)), - pt => readType()) - goto(end) - result - case METHODtype => - val (names, paramReader) = readNamesSkipParams[TermName] - val result = MethodType(names, paramReader.readParamTypes[Type](end))( - mt => registeringType(mt, readType())) - goto(end) - result - case PARAMtype => - readTypeRef() match { - case binder: PolyType => PolyParam(binder, readNat()) - case binder: MethodType => MethodParam(binder, readNat()) - } - case CLASSconst => - ConstantType(Constant(readType())) - case ENUMconst => - ConstantType(Constant(readTermRef().termSymbol)) - } - assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}") - result - } - - def readSimpleType(): Type = (tag: @switch) match { - case TYPEREFdirect | TERMREFdirect => - NamedType.withFixedSym(NoPrefix, readSymRef()) - case TYPEREFsymbol | TERMREFsymbol => - readSymNameRef() - case TYPEREFpkg => - readPackageRef().moduleClass.typeRef - case TERMREFpkg => - readPackageRef().termRef - case TYPEREF => - val name = readName().toTypeName - TypeRef(readType(), name) - case TERMREF => - readNameSplitSig() match { - case name: TermName => TermRef.all(readType(), name) - case (name: TermName, sig: Signature) => TermRef.withSig(readType(), name, sig) - } - case THIS => - ThisType.raw(readType().asInstanceOf[TypeRef]) - case SKOLEMtype => - SkolemType(readTypeRef()) - case SHARED => - val ref = readAddr() - typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) - case UNITconst => - ConstantType(Constant(())) - case TRUEconst => - ConstantType(Constant(true)) - case FALSEconst => - ConstantType(Constant(false)) - case BYTEconst => - ConstantType(Constant(readInt().toByte)) - case SHORTconst => - ConstantType(Constant(readInt().toShort)) - case CHARconst => - ConstantType(Constant(readNat().toChar)) - case INTconst => - ConstantType(Constant(readInt())) - case LONGconst => - ConstantType(Constant(readLongInt())) - case FLOATconst => - ConstantType(Constant(java.lang.Float.intBitsToFloat(readInt()))) - case DOUBLEconst => - ConstantType(Constant(java.lang.Double.longBitsToDouble(readLongInt()))) - case STRINGconst => - ConstantType(Constant(readName().toString)) - case NULLconst => - ConstantType(Constant(null)) - case BYNAMEtype => - ExprType(readType()) - } - - if (tag < firstLengthTreeTag) readSimpleType() else readLengthType() - } - - private def readSymNameRef()(implicit ctx: Context): Type = { - val sym = readSymRef() - val prefix = readType() - val res = NamedType.withSymAndName(prefix, sym, sym.name) - prefix match { - case prefix: ThisType if prefix.cls eq sym.owner => res.withDenot(sym.denot) - // without this precaution we get an infinite cycle when unpickling pos/extmethods.scala - // the problem arises when a self type of a trait is a type parameter of the same trait. - case _ => res - } - } - - private def readPackageRef()(implicit ctx: Context): TermSymbol = { - val name = readName() - if (name == nme.ROOT) defn.RootPackage - else if (name == nme.EMPTY_PACKAGE) defn.EmptyPackageVal - else ctx.requiredPackage(name) - } - - def readTypeRef(): Type = - typeAtAddr(readAddr()) - - def readPath()(implicit ctx: Context): Type = { - val tp = readType() - assert(tp.isInstanceOf[SingletonType]) - tp - } - - def readTermRef()(implicit ctx: Context): TermRef = - readType().asInstanceOf[TermRef] - -// ------ Reading definitions ----------------------------------------------------- - - private def noRhs(end: Addr): Boolean = - currentAddr == end || isModifierTag(nextByte) - - private def localContext(owner: Symbol)(implicit ctx: Context) = { - val lctx = ctx.fresh.setOwner(owner) - if (owner.isClass) lctx.setScope(owner.unforcedDecls) else lctx.setNewScope - } - - private def normalizeFlags(tag: Int, givenFlags: FlagSet, name: Name, isAbstractType: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): FlagSet = { - val lacksDefinition = - rhsIsEmpty && - name.isTermName && !name.isConstructorName && !givenFlags.is(ParamOrAccessor) || - isAbstractType - var flags = givenFlags - if (lacksDefinition && tag != PARAM) flags |= Deferred - if (tag == DEFDEF) flags |= Method - if (givenFlags is Module) - flags = flags | (if (tag == VALDEF) ModuleCreationFlags else ModuleClassCreationFlags) - if (ctx.owner.isClass) { - if (tag == TYPEPARAM) flags |= Param - else if (tag == PARAM) flags |= ParamAccessor - } - else if (isParamTag(tag)) flags |= Param - flags - } - - /** Create symbol of definition node and enter in symAtAddr map - * @return true iff the definition does not contain initialization code - */ - def createSymbol()(implicit ctx: Context): Boolean = { - val start = currentAddr - val tag = readByte() - val end = readEnd() - val rawName = tastyName(readNameRef()) - var name: Name = toTermName(rawName) - if (tag == TYPEDEF || tag == TYPEPARAM) name = name.toTypeName - skipParams() - val ttag = nextUnsharedTag - val isAbstractType = ttag == TYPEBOUNDS - val isClass = ttag == TEMPLATE - val templateStart = currentAddr - skipTree() // tpt - val rhsIsEmpty = noRhs(end) - if (!rhsIsEmpty) skipTree() - val (givenFlags, annots, privateWithin) = readModifiers(end) - val expandedFlag = if (rawName.isInstanceOf[TastyName.Expanded]) ExpandedName else EmptyFlags - pickling.println(i"creating symbol $name at $start with flags $givenFlags") - val flags = normalizeFlags(tag, givenFlags | expandedFlag, name, isAbstractType, rhsIsEmpty) - def adjustIfModule(completer: LazyType) = - if (flags is Module) ctx.adjustModuleCompleter(completer, name) else completer - val sym = - roots.find(root => (root.owner eq ctx.owner) && root.name == name) match { - case Some(rootd) => - pickling.println(i"overwriting ${rootd.symbol} # ${rootd.hashCode}") - rootd.info = adjustIfModule( - new Completer(subReader(start, end)) with SymbolLoaders.SecondCompleter) - rootd.flags = flags &~ Touched // allow one more completion - rootd.privateWithin = privateWithin - rootd.symbol - case _ => - val completer = adjustIfModule(new Completer(subReader(start, end))) - if (isClass) - ctx.newClassSymbol(ctx.owner, name.asTypeName, flags, completer, - privateWithin, coord = start.index) - else { - val sym = symAtAddr.get(start) match { - case Some(preExisting) => - assert(stubs contains preExisting) - stubs -= preExisting - preExisting - case none => - ctx.newNakedSymbol(start.index) - } - val denot = ctx.SymDenotation(symbol = sym, owner = ctx.owner, name, flags, completer, privateWithin) - sym.denot = denot - sym - } - } // TODO set position - sym.annotations = annots - ctx.enter(sym) - symAtAddr(start) = sym - if (isClass) { - sym.completer.withDecls(newScope) - forkAt(templateStart).indexTemplateParams()(localContext(sym)) - } - tag != VALDEF || rhsIsEmpty - } - - /** Read modifier list into triplet of flags, annotations and a privateWithin - * boindary symbol. - */ - def readModifiers(end: Addr)(implicit ctx: Context): (FlagSet, List[Annotation], Symbol) = { - var flags: FlagSet = EmptyFlags - var annots = new mutable.ListBuffer[Annotation] - var privateWithin: Symbol = NoSymbol - while (currentAddr.index != end.index) { - def addFlag(flag: FlagSet) = { - flags |= flag - readByte() - } - nextByte match { - case PRIVATE => addFlag(Private) - case INTERNAL => ??? // addFlag(Internal) - case PROTECTED => addFlag(Protected) - case ABSTRACT => addFlag(Abstract) - case FINAL => addFlag(Final) - case SEALED => addFlag(Sealed) - case CASE => addFlag(Case) - case IMPLICIT => addFlag(Implicit) - case LAZY => addFlag(Lazy) - case OVERRIDE => addFlag(Override) - case INLINE => addFlag(Inline) - case ABSOVERRIDE => addFlag(AbsOverride) - case STATIC => addFlag(JavaStatic) - case OBJECT => addFlag(Module) - case TRAIT => addFlag(Trait) - case LOCAL => addFlag(Local) - case SYNTHETIC => addFlag(Synthetic) - case ARTIFACT => addFlag(Artifact) - case MUTABLE => addFlag(Mutable) - case LABEL => addFlag(Label) - case FIELDaccessor => addFlag(Accessor) - case CASEaccessor => addFlag(CaseAccessor) - case COVARIANT => addFlag(Covariant) - case CONTRAVARIANT => addFlag(Contravariant) - case SCALA2X => addFlag(Scala2x) - case DEFAULTparameterized => addFlag(DefaultParameterized) - case INSUPERCALL => addFlag(InSuperCall) - case PRIVATEqualified => - readByte() - privateWithin = readType().typeSymbol - case PROTECTEDqualified => - addFlag(Protected) - privateWithin = readType().typeSymbol - case ANNOTATION => - readByte() - val end = readEnd() - val sym = readType().typeSymbol - val lazyAnnotTree = readLater(end, rdr => ctx => rdr.readTerm()(ctx)) - annots += Annotation.deferred(sym, _ => lazyAnnotTree.complete) - case _ => - assert(false, s"illegal modifier tag at $currentAddr") - } - } - (flags, annots.toList, privateWithin) - } - - /** Create symbols for a definitions in statement sequence between - * current address and `end`. - * @return true iff none of the statements contains initialization code - */ - def indexStats(end: Addr)(implicit ctx: Context): Boolean = { - val noInitss = - until(end) { - nextByte match { - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM => - createSymbol() - case IMPORT => - skipTree() - true - case PACKAGE => - processPackage { (pid, end) => implicit ctx => indexStats(end) } - case _ => - skipTree() - false - } - } - noInitss.forall(_ == true) - } - - /** Process package with given operation `op`. The operation takes as arguments - * - a `RefTree` representing the `pid` of the package, - * - an end address, - * - a context which has the processd package as owner - */ - def processPackage[T](op: (RefTree, Addr) => Context => T)(implicit ctx: Context): T = { - readByte() - val end = readEnd() - val pid = ref(readTermRef()).asInstanceOf[RefTree] - op(pid, end)(localContext(pid.symbol.moduleClass)) - } - - /** Create symbols the longest consecutive sequence of parameters with given - * `tag starting at current address. - */ - def indexParams(tag: Int)(implicit ctx: Context) = - while (nextByte == tag) createSymbol() - - /** Create symbols for all type and value parameters of template starting - * at current address. - */ - def indexTemplateParams()(implicit ctx: Context) = { - assert(readByte() == TEMPLATE) - readEnd() - indexParams(TYPEPARAM) - indexParams(PARAM) - } - - /** If definition was already read by a completer, return the previously read tree - * or else read definition. - */ - def readIndexedDef()(implicit ctx: Context): Tree = treeAtAddr.remove(currentAddr) match { - case Some(tree) => skipTree(); tree - case none => readNewDef() - } - - private def readNewDef()(implicit ctx: Context): Tree = { - val start = currentAddr - val sym = symAtAddr(start) - val tag = readByte() - val end = readEnd() - - def readParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = { - fork.indexParams(tag) - readIndexedParams(tag) - } - - def readParamss(implicit ctx: Context): List[List[ValDef]] = { - collectWhile(nextByte == PARAMS) { - readByte() - readEnd() - readParams[ValDef](PARAM) - } - } - - def readRhs(implicit ctx: Context) = - if (noRhs(end)) EmptyTree - else readLater(end, rdr => ctx => rdr.readTerm()(ctx)) - - def localCtx = localContext(sym) - - def DefDef(tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree) = - ta.assignType( - untpd.DefDef( - sym.name.asTermName, tparams, vparamss, tpt, readRhs(localCtx)), - sym) - - def ta = ctx.typeAssigner - - val name = readName() - pickling.println(s"reading def of $name at $start") - val tree: MemberDef = tag match { - case DEFDEF => - val tparams = readParams[TypeDef](TYPEPARAM)(localCtx) - val vparamss = readParamss(localCtx) - val tpt = readTpt() - val typeParams = tparams.map(_.symbol) - val valueParamss = ctx.normalizeIfConstructor( - vparamss.nestedMap(_.symbol), name == nme.CONSTRUCTOR) - val resType = ctx.effectiveResultType(sym, typeParams, tpt.tpe) - sym.info = ctx.methodType(typeParams, valueParamss, resType) - DefDef(tparams, vparamss, tpt) - case VALDEF => - sym.info = readType() - ValDef(sym.asTerm, readRhs(localCtx)) - case TYPEDEF | TYPEPARAM => - if (sym.isClass) - ta.assignType(untpd.TypeDef(sym.name.asTypeName, readTemplate(localCtx)), sym) - else { - sym.info = readType() - TypeDef(sym.asType) - } - case PARAM => - val info = readType() - if (noRhs(end)) { - sym.info = info - ValDef(sym.asTerm) - } - else { - sym.setFlag(Method) - sym.info = ExprType(info) - pickling.println(i"reading param alias $name -> $currentAddr") - DefDef(Nil, Nil, TypeTree(info)) - } - } - val mods = - if (sym.annotations.isEmpty) EmptyModifiers - else Modifiers(annotations = sym.annotations.map(_.tree)) - tree.withMods(mods) // record annotations in tree so that tree positions can be filled in. - goto(end) - setPos(start, tree) - } - - private def readTemplate(implicit ctx: Context): Template = { - val start = currentAddr - val cls = ctx.owner.asClass - def setClsInfo(parents: List[TypeRef], selfType: Type) = - cls.info = ClassInfo(cls.owner.thisType, cls, parents, cls.unforcedDecls, selfType) - setClsInfo(Nil, NoType) - val localDummy = ctx.newLocalDummy(cls) - assert(readByte() == TEMPLATE) - val end = readEnd() - val tparams = readIndexedParams[TypeDef](TYPEPARAM) - val vparams = readIndexedParams[ValDef](PARAM) - val parents = collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) { - nextByte match { - case APPLY | TYPEAPPLY => readTerm() - case _ => readTpt() - } - } - val parentRefs = ctx.normalizeToClassRefs(parents.map(_.tpe), cls, cls.unforcedDecls) - val self = - if (nextByte == SELFDEF) { - readByte() - untpd.ValDef(readName(), readTpt(), EmptyTree).withType(NoType) - } - else EmptyValDef - setClsInfo(parentRefs, if (self.isEmpty) NoType else self.tpt.tpe) - val noInits = fork.indexStats(end) - if (noInits) cls.setFlag(NoInits) - val constr = readIndexedDef().asInstanceOf[DefDef] - - def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree]): (List[Tree], List[Tree]) = - (tparams, stats) match { - case (tparam :: tparams1, (alias: TypeDef) :: stats1) - if tparam.name == alias.name.expandedName(cls) => - val (tas, stats2) = mergeTypeParamsAndAliases(tparams1, stats1) - (tparam :: alias :: tas, stats2) - case _ => - (tparams, stats) - } - - val lazyStats = readLater(end, rdr => implicit ctx => { - val stats0 = rdr.readIndexedStats(localDummy, end) - val (tparamsAndAliases, stats) = mergeTypeParamsAndAliases(tparams, stats0) - tparamsAndAliases ++ vparams ++ stats - }) - setPos(start, - untpd.Template(constr, parents, self, lazyStats) - .withType(localDummy.nonMemberTermRef)) - } - - def readIndexedStat(exprOwner: Symbol)(implicit ctx: Context): Tree = nextByte match { - case TYPEDEF | VALDEF | DEFDEF => - readIndexedDef() - case IMPORT => - readImport() - case PACKAGE => - val start = currentAddr - processPackage { (pid, end) => implicit ctx => - setPos(start, PackageDef(pid, readIndexedStats(exprOwner, end)(ctx))) - } - case _ => - readTerm()(ctx.withOwner(exprOwner)) - } - - def readImport()(implicit ctx: Context): Tree = { - readByte() - readEnd() - val expr = readTerm() - def readSelectors(): List[untpd.Tree] = nextByte match { - case RENAMED => - readByte() - readEnd() - untpd.Pair(untpd.Ident(readName()), untpd.Ident(readName())) :: readSelectors() - case IMPORTED => - readByte() - untpd.Ident(readName()) :: readSelectors() - case _ => - Nil - } - Import(expr, readSelectors()) - } - - def readIndexedStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = - until(end)(readIndexedStat(exprOwner)) - - def readStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = { - fork.indexStats(end) - readIndexedStats(exprOwner, end) - } - - def readIndexedParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = - collectWhile(nextByte == tag) { readIndexedDef().asInstanceOf[T] } - -// ------ Reading terms ----------------------------------------------------- - - def readTerm()(implicit ctx: Context): Tree = { - val start = currentAddr - val tag = readByte() - pickling.println(s"reading term ${astTagToString(tag)} at $start") - - def readPathTerm(): Tree = { - goto(start) - readPath() match { - case path: TermRef => ref(path) - case path: ThisType => This(path.cls) - case path: ConstantType => Literal(path.value) - } - } - - def readSimpleTerm(): Tree = tag match { - case IDENT => - untpd.Ident(readName()).withType(readType()) - case SELECT => - def readQual(name: Name) = { - val localCtx = - if (name == nme.CONSTRUCTOR) ctx.fresh.addMode(Mode.InSuperCall) else ctx - readTerm()(localCtx) - } - def readRest(name: Name, sig: Signature) = { - val unshadowed = if (name.isShadowedName) name.revertShadowed else name - val qual = readQual(name) - untpd.Select(qual, unshadowed) - .withType(TermRef.withSig(qual.tpe.widenIfUnstable, name.asTermName, sig)) - } - readNameSplitSig match { - case name: Name => readRest(name, Signature.NotAMethod) - case (name: Name, sig: Signature) => readRest(name, sig) - } - - case NEW => - New(readTpt()) - case _ => - readPathTerm() - } - - def readLengthTerm(): Tree = { - val end = readEnd() - - val result = - (tag: @switch) match { - case SUPER => - val qual = readTerm() - val mixClass = ifBefore(end)(readType().typeSymbol, NoSymbol) - val mixName = if (mixClass.exists) mixClass.name.asTypeName else tpnme.EMPTY - tpd.Super(qual, mixName, ctx.mode.is(Mode.InSuperCall), mixClass) - case APPLY => - val fn = readTerm() - val isJava = fn.tpe.isInstanceOf[JavaMethodType] - def readArg() = readTerm() match { - case SeqLiteral(elems) if isJava => JavaSeqLiteral(elems) - case arg => arg - } - tpd.Apply(fn, until(end)(readArg())) - case TYPEAPPLY => - tpd.TypeApply(readTerm(), until(end)(readTpt())) - case PAIR => - Pair(readTerm(), readTerm()) - case TYPED => - Typed(readTerm(), readTpt()) - case NAMEDARG => - NamedArg(readName(), readTerm()) - case ASSIGN => - Assign(readTerm(), readTerm()) - case BLOCK => - val exprReader = fork - skipTree() - val localCtx = ctx.fresh.setNewScope - val stats = readStats(ctx.owner, end)(localCtx) - val expr = exprReader.readTerm()(localCtx) - Block(stats, expr) - case IF => - If(readTerm(), readTerm(), readTerm()) - case LAMBDA => - val meth = readTerm() - val tpt = ifBefore(end)(readTpt(), EmptyTree) - Closure(Nil, meth, tpt) - case MATCH => - Match(readTerm(), readCases(end)) - case RETURN => - val from = readSymRef() - val expr = ifBefore(end)(readTerm(), EmptyTree) - Return(expr, Ident(from.termRef)) - case TRY => - Try(readTerm(), readCases(end), ifBefore(end)(readTerm(), EmptyTree)) - case REPEATED => - SeqLiteral(until(end)(readTerm())) - case BIND => - val name = readName() - val info = readType() - val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, info) - symAtAddr(start) = sym - Bind(sym, readTerm()) - case ALTERNATIVE => - Alternative(until(end)(readTerm())) - case UNAPPLY => - val fn = readTerm() - val implicitArgs = - collectWhile(nextByte == IMPLICITarg) { - readByte() - readTerm() - } - val patType = readType() - val argPats = until(end)(readTerm()) - UnApply(fn, implicitArgs, argPats, patType) - case _ => - readPathTerm() - } - assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}") - result - } - - val tree = if (tag < firstLengthTreeTag) readSimpleTerm() else readLengthTerm() - tree.overwriteType(tree.tpe.simplified) - setPos(start, tree) - } - - def readTpt()(implicit ctx: Context) = { - val start = currentAddr - val tp = readType() - if (tp.exists) setPos(start, TypeTree(tp)) else EmptyTree - } - - def readCases(end: Addr)(implicit ctx: Context): List[CaseDef] = - collectWhile(nextByte == CASEDEF && currentAddr != end) { readCase()(ctx.fresh.setNewScope) } - - def readCase()(implicit ctx: Context): CaseDef = { - val start = currentAddr - readByte() - val end = readEnd() - val pat = readTerm() - val rhs = readTerm() - val guard = ifBefore(end)(readTerm(), EmptyTree) - setPos(start, CaseDef(pat, guard, rhs)) - } - - def readLater[T <: AnyRef](end: Addr, op: TreeReader => Context => T): Trees.Lazy[T] = { - val localReader = fork - goto(end) - new LazyReader(localReader, op) - } - -// ------ Hooks for positions ------------------------------------------------ - - /** Record address from which tree was created as a temporary position in the tree. - * The temporary position contains deltas relative to the position of the (as yet unknown) - * parent node. It is marked as a non-synthetic source position. - */ - def setPos[T <: Tree](addr: Addr, tree: T): T = { - if (readPositions) - tree.setPosUnchecked(positions.getOrElse(addr, Position(0, 0, 0))) - tree - } - } - - private def setNormalized(tree: Tree, parentPos: Position): Unit = - tree.setPosUnchecked( - if (tree.pos.exists) - Position(parentPos.start + offsetToInt(tree.pos.start), parentPos.end - tree.pos.end) - else - parentPos) - - def normalizePos(x: Any, parentPos: Position)(implicit ctx: Context): Unit = - traverse(x, parentPos, setNormalized) - - class LazyReader[T <: AnyRef](reader: TreeReader, op: TreeReader => Context => T) extends Trees.Lazy[T] with DeferredPosition { - def complete(implicit ctx: Context): T = { - pickling.println(i"starting to read at ${reader.reader.currentAddr}") - val res = op(reader)(ctx.addMode(Mode.AllowDependentFunctions)) - normalizePos(res, parentPos) - res - } - } - - class LazyAnnotationReader(sym: Symbol, reader: TreeReader) - extends LazyAnnotation(sym) with DeferredPosition { - def complete(implicit ctx: Context) = { - val res = reader.readTerm() - normalizePos(res, parentPos) - res - } - } -} diff --git a/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala b/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala new file mode 100644 index 000000000..ccd3f78e8 --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala @@ -0,0 +1,60 @@ +package dotty.tools +package dotc +package core +package tasty + +import Contexts._, SymDenotations._ +import dotty.tools.dotc.ast.tpd +import TastyUnpickler._, TastyBuffer._ +import util.Positions._ +import util.{SourceFile, NoSource} +import PositionUnpickler._ +import classfile.ClassfileParser + +object DottyUnpickler { + + /** Exception thrown if classfile is corrupted */ + class BadSignature(msg: String) extends RuntimeException(msg) +} + +/** A class for unpickling Tasty trees and symbols. + * @param bytes the bytearray containing the Tasty file from which we unpickle + */ +class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded { + import tpd._ + + private val unpickler = new TastyUnpickler(bytes) + private val treeUnpickler = unpickler.unpickle(new TreeSectionUnpickler).get + + /** Enter all toplevel classes and objects into their scopes + * @param roots a set of SymDenotations that should be overwritten by unpickling + */ + def enter(roots: Set[SymDenotation])(implicit ctx: Context): Unit = + treeUnpickler.enterTopLevel(roots) + + /** The unpickled trees, and the source file they come from + * @param readPositions if true, trees get decorated with position information. + */ + def body(readPositions: Boolean = false)(implicit ctx: Context): (List[Tree], SourceFile) = { + val source = unpickler.unpickle(new SourceFileUnpickler).getOrElse(NoSource) + if (readPositions) + for ((totalRange, positions) <- unpickler.unpickle(new PositionsSectionUnpickler)) + treeUnpickler.usePositions(totalRange, positions) + (treeUnpickler.unpickle(), source) + } + + private class SourceFileUnpickler extends SectionUnpickler[SourceFile]("Sourcefile") { + def unpickle(reader: TastyReader, tastyName: TastyName.Table) = + new SourceFile(tastyName(reader.readNameRef()).toString, Seq()) + } + + private class TreeSectionUnpickler extends SectionUnpickler[TreeUnpickler]("ASTs") { + def unpickle(reader: TastyReader, tastyName: TastyName.Table) = + new TreeUnpickler(reader, tastyName) + } + + private class PositionsSectionUnpickler extends SectionUnpickler[(Position, AddrToPosition)]("Positions") { + def unpickle(reader: TastyReader, tastyName: TastyName.Table) = + new PositionUnpickler(reader).unpickle() + } +} diff --git a/src/dotty/tools/dotc/core/tasty/NameBuffer.scala b/src/dotty/tools/dotc/core/tasty/NameBuffer.scala new file mode 100644 index 000000000..69fd63805 --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/NameBuffer.scala @@ -0,0 +1,93 @@ +package dotty.tools +package dotc +package core +package tasty + +import collection.mutable +import Names.{Name, chrs} +import Decorators._, NameOps._ +import TastyBuffer._ +import scala.io.Codec +import TastyName._ +import TastyFormat._ + +class NameBuffer extends TastyBuffer(10000) { + + private val nameRefs = new mutable.LinkedHashMap[TastyName, NameRef] + + def nameIndex(name: TastyName): NameRef = nameRefs.get(name) match { + case Some(ref) => + ref + case None => + val ref = NameRef(nameRefs.size) + nameRefs(name) = ref + ref + } + def nameIndex(name: Name): NameRef = { + val tname = + if (name.isShadowedName) Shadowed(nameIndex(name.revertShadowed)) + else Simple(name.toTermName) + nameIndex(tname) + } + + def nameIndex(str: String): NameRef = nameIndex(str.toTermName) + + def fullNameIndex(name: Name): NameRef = { + val pos = name.lastIndexOf('.') + if (pos > 0) + nameIndex(Qualified(fullNameIndex(name.take(pos)), nameIndex(name.drop(pos + 1)))) + else + nameIndex(name) + } + + private def withLength(op: => Unit): Unit = { + val lengthAddr = currentAddr + writeByte(0) + op + val length = currentAddr.index - lengthAddr.index - 1 + assert(length < 128) + putNat(lengthAddr, length, 1) + } + + def writeNameRef(ref: NameRef) = writeNat(ref.index) + + def pickleName(name: TastyName): Unit = name match { + case Simple(name) => + val bytes = + if (name.length == 0) new Array[Byte](0) + else Codec.toUTF8(chrs, name.start, name.length) + writeByte(UTF8) + writeNat(bytes.length) + writeBytes(bytes, bytes.length) + case Qualified(qualified, selector) => + writeByte(QUALIFIED) + withLength { writeNameRef(qualified); writeNameRef(selector) } + case Signed(original, params, result) => + writeByte(SIGNED) + withLength { writeNameRef(original); writeNameRef(result); params.foreach(writeNameRef) } + case Expanded(prefix, original) => + writeByte(EXPANDED) + withLength { writeNameRef(prefix); writeNameRef(original) } + case ModuleClass(module) => + writeByte(OBJECTCLASS) + withLength { writeNameRef(module) } + case SuperAccessor(accessed) => + writeByte(SUPERACCESSOR) + withLength { writeNameRef(accessed) } + case DefaultGetter(method, paramNumber) => + writeByte(DEFAULTGETTER) + withLength { writeNameRef(method); writeNat(paramNumber) } + case Shadowed(original) => + writeByte(SHADOWED) + withLength { writeNameRef(original) } + } + + override def assemble(): Unit = { + var i = 0 + for ((name, ref) <- nameRefs) { + assert(ref.index == i) + i += 1 + pickleName(name) + } + } +} diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala new file mode 100644 index 000000000..b0550b70a --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -0,0 +1,75 @@ +package dotty.tools +package dotc +package core +package tasty + +import ast.tpd._ +import ast.Trees.WithLazyField +import TastyFormat._ +import core._ +import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._ +import collection.mutable +import TastyBuffer._ +import util.Positions._ + +object PositionPickler { + + trait DeferredPosition { + var parentPos: Position = NoPosition + } + + def traverse(x: Any, parentPos: Position, op: (Tree, Position) => Unit)(implicit ctx: Context): Unit = + if (parentPos.exists) + x match { + case x: Tree @unchecked => + op(x, parentPos) + x match { + case x: MemberDef @unchecked => traverse(x.symbol.annotations, x.pos, op) + case _ => + } + traverse(x.productIterator, x.pos, op) + case x: DeferredPosition => + x.parentPos = parentPos + case xs: TraversableOnce[_] => + xs.foreach(traverse(_, parentPos, op)) + case _ => + } +} +import PositionPickler._ + +class PositionPickler(pickler: TastyPickler, addrOfTree: Tree => Option[Addr]) { + val buf = new TastyBuffer(5000) + pickler.newSection("Positions", buf) + import buf._ + + def picklePositions(roots: List[Tree], totalRange: Position)(implicit ctx: Context) = { + var lastIndex = 0 + def record(tree: Tree, parentPos: Position): Unit = + if (tree.pos.exists) { + def msg = s"failure to pickle $tree at ${tree.pos}, parent = $parentPos" + val endPos = tree.pos.end min parentPos.end + // end positions can be larger than their parents + // e.g. in the case of synthetic empty ranges, which are placed at the next token after + // the current construct. + val endDelta = endPos - parentPos.end + val startPos = + if (endDelta == 0) tree.pos.start max parentPos.start else tree.pos.start min endPos + // Since end positions are corrected above, start positions have to follow suit. + val startDelta = startPos - parentPos.start + if (startDelta != 0 || endDelta != 0) + for (addr <- addrOfTree(tree)) { + buf.writeInt(addr.index - lastIndex) + lastIndex = addr.index + if (startDelta != 0) buf.writeInt(startDelta) + if (endDelta != 0) { + assert(endDelta < 0, msg) + buf.writeInt(endDelta) + } else + assert(startDelta >= 0, msg) + } + } + + buf.writeNat(totalRange.end) + traverse(roots, totalRange, record) + } +} diff --git a/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala b/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala new file mode 100644 index 000000000..fa80a2769 --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala @@ -0,0 +1,38 @@ +package dotty.tools +package dotc +package core +package tasty + + +import util.Positions._ +import collection.mutable +import TastyBuffer.Addr + +object PositionUnpickler { + type AddrToPosition = mutable.HashMap[Addr, Position] +} + +/** Unpickler for tree positions */ +class PositionUnpickler(reader: TastyReader) { + import PositionUnpickler._ + import reader._ + + def unpickle(): (Position, AddrToPosition) = { + val positions = new mutable.HashMap[Addr, Position] // Dotty deviation: Can't use new AddrToPosition here. TODO: fix this! + val sourceLength = readNat() + def readDelta() = if (isAtEnd) 0 else readInt() + var curIndex: Addr = Addr(readDelta()) + while (!isAtEnd) { + val delta1 = readDelta() + val delta2 = readDelta() + val (startDelta, endDelta, indexDelta) = + if (delta2 <= 0) (delta1, -delta2, readDelta()) + else if (delta1 < 0) (0, -delta1, delta2) + else (delta1, 0, delta2) + positions(curIndex) = Position(startDelta, endDelta, startDelta) + // make non-synthetic position; will be made synthetic by normalization. + curIndex += indexDelta + } + (Position(0, sourceLength), positions) + } +} diff --git a/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala b/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala new file mode 100644 index 000000000..07442f63b --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala @@ -0,0 +1,188 @@ +package dotty.tools +package dotc +package core +package tasty + +import util.Util.dble + +object TastyBuffer { + + /** The number of digits of the natural number `nat`, written in base 128 format. */ + def natSize(nat: Int): Int = + if (nat < 128) 1 else natSize(nat >>> 7) + 1 + + /** An address pointing to an index in a Tasty buffer's byte array */ + case class Addr(val index: Int) extends AnyVal { + def - (delta: Int): Addr = Addr(this.index - delta) + def + (delta: Int): Addr = Addr(this.index + delta) + + def relativeTo(base: Addr): Addr = this - base.index - AddrWidth + } + + val NoAddr = Addr(-1) + + /** The maximal number of address bytes. + * Since addresses are written as base-128 natural numbers, + * the value of 4 gives a maximal array size of 256M. + */ + final val AddrWidth = 4 +} +import TastyBuffer._ + +/** A byte array buffer that can be filled with bytes or natural numbers in TASTY format, + * and that supports reading and patching addresses represented as natural numbers. + */ +class TastyBuffer(initialSize: Int) { + + /** The current byte array, will be expanded as needed */ + var bytes = new Array[Byte](initialSize) + + /** The number of bytes written */ + var length = 0 + + // -- Output routines -------------------------------------------- + + /** Write a byte of data. */ + def writeByte(b: Int): Unit = { + if (length >= bytes.length) + bytes = dble(bytes) + bytes(length) = b.toByte + length += 1 + } + + /** Write the first `n` bytes of `data`. */ + def writeBytes(data: Array[Byte], n: Int): Unit = { + while (bytes.length < length + n) bytes = dble(bytes) + Array.copy(data, 0, bytes, length, n) + length += n + } + + /** Write a natural number in big endian format, base 128. + * All but the last digits have bit 0x80 set. + */ + def writeNat(x: Int): Unit = + writeLongNat(x.toLong & 0x00000000FFFFFFFFL) + + /** Write a natural number in 2's complement big endian format, base 128. + * All but the last digits have bit 0x80 set. + */ + def writeInt(x: Int): Unit = + writeLongInt(x) + + /** + * Like writeNat, but for longs. 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): Unit = { + def writePrefix(x: Long): Unit = { + val y = x >>> 7 + if (y != 0L) writePrefix(y) + writeByte((x & 0x7f).toInt) + } + val y = x >>> 7 + if (y != 0L) writePrefix(y) + writeByte(((x & 0x7f) | 0x80).toInt) + } + + /** Like writeInt, but for longs */ + def writeLongInt(x: Long): Unit = { + def writePrefix(x: Long): Unit = { + val y = x >> 7 + if (y != 0L - ((x >> 6) & 1)) writePrefix(y) + writeByte((x & 0x7f).toInt) + } + val y = x >> 7 + if (y != 0L - ((x >> 6) & 1)) writePrefix(y) + writeByte(((x & 0x7f) | 0x80).toInt) + } + + /** Write an uncompressed Long stored in 8 bytes in big endian format */ + def writeUncompressedLong(x: Long): Unit = { + var y = x + val bytes = new Array[Byte](8) + for (i <- 7 to 0 by -1) { + bytes(i) = (y & 0xff).toByte + y = y >>> 8 + } + writeBytes(bytes, 8) + } + + // -- Address handling -------------------------------------------- + + /** Write natural number `x` right-adjusted in a field of `width` bytes + * starting with address `at`. + */ + def putNat(at: Addr, x: Int, width: Int): Unit = { + var y = x + var w = width + if(at.index + w >= bytes.length) + bytes = dble(bytes) + var digit = y & 0x7f | 0x80 + while (w > 0) { + w -= 1 + bytes(at.index + w) = digit.toByte + y >>>= 7 + digit = y & 0x7f + } + assert(y == 0, s"number $x too large to fit in $width bytes") + } + + /** The byte at given address */ + def getByte(at: Addr): Int = bytes(at.index) + + /** The natural number at address `at` */ + def getNat(at: Addr): Int = getLongNat(at).toInt + + /** The long natural number at address `at` */ + def getLongNat(at: Addr): Long = { + var b = 0L + var x = 0L + var idx = at.index + do { + b = bytes(idx) + x = (x << 7) | (b & 0x7f) + idx += 1 + } while ((b & 0x80) == 0) + x + } + + /** The address (represented as a natural number) at address `at` */ + def getAddr(at: Addr) = Addr(getNat(at)) + + /** The smallest address equal to or following `at` which points to a non-zero byte */ + final def skipZeroes(at: Addr): Addr = + if (getByte(at) != 0) at else skipZeroes(at + 1) + + /** The address after the natural number found at address `at`. */ + final def skipNat(at: Addr): Addr = { + val next = at + 1 + if ((getByte(at) & 0x80) != 0) next else skipNat(next) + } + + /** The address referring to the end of data written so far */ + def currentAddr: Addr = Addr(length) + + /** Reserve `AddrWidth` bytes to write an address into */ + def reserveAddr(): Addr = { + val result = currentAddr + length += AddrWidth + result + } + + /** Fill reserved space at address `at` with address `target` */ + def fillAddr(at: Addr, target: Addr) = + putNat(at, target.index, AddrWidth) + + /** Write address without leading zeroes */ + def writeAddr(addr: Addr): Unit = writeNat(addr.index) + + // -- Finalization -------------------------------------------- + + /** Hook to be overridden in subclasses. + * Perform all actions necessary to assemble the final byte array. + * After `assemble` no more output actions to this buffer are permitted. + */ + def assemble(): Unit = () +} diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala new file mode 100644 index 000000000..106a6510d --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -0,0 +1,491 @@ +package dotty.tools.dotc +package core +package tasty + +/************************************************************ +Notation: + +We use BNF notation. Terminal symbols start with at least two +consecutive upper case letters. Each terminal is represented as a +single byte tag. Non-terminals are mixed case. Prefixes of the form +lower case letter*_ are for explanation of semantic content only, they +can be dropped without changing the grammar. + +Micro-syntax: + + LongInt = Digit* StopDigit // big endian 2's complement, value fits in a Long w/o overflow + Int = LongInt // big endian 2's complement, fits in an Int w/o overflow + Nat = LongInt // non-negative value, fits in an Int without overflow + Digit = 0 | ... | 127 + StopDigit = 128 | ... | 255 // value = digit - 128 + +Macro-format: + + File = Header majorVersion_Nat minorVersion_Nat UUID + nameTable_Length Name* Section* + Header = 0x5CA1AB1F + UUID = Byte*16 // random UUID + + Section = NameRef Length Bytes + Length = Nat // length of rest of entry in bytes + + Name = UTF8 Length UTF8-CodePoint* + QUALIFIED Length qualified_NameRef selector_NameRef + SIGNED Length original_NameRef resultSig_NameRef paramSig_NameRef* + EXPANDED Length original_NameRef + OBJECTCLASS Length module_NameRef + SUPERACCESSOR Length accessed_NameRef + DEFAULTGETTER Length method_NameRef paramNumber_Nat + SHADOWED Length original_NameRef + MANGLED Length mangle_NameRef name_NameRef + ... + + NameRef = Nat // ordinal number of name in name table, starting from 1. + +Note: Unqualified names in the name table are strings. The context decides whether a name is +a type-name or a term-name. The same string can represent both. + +Standard-Section: "ASTs" TopLevelStat* + + TopLevelStat = PACKAGE Length Path TopLevelStat* + Stat + + Stat = Term + VALDEF Length NameRef Type rhs_Term? Modifier* + DEFDEF Length NameRef TypeParam* Params* return_Type rhs_Term? + Modifier* + TYPEDEF Length NameRef (Type | Template) Modifier* + IMPORT Length qual_Term Selector* + Selector = IMPORTED name_NameRef + RENAMED Length from_NameRef to_NameRef + // Imports are for scala.meta, they are not used in the backend + + TypeParam = TYPEPARAM Length NameRef Type Modifier* + Params = PARAMS Length Param* + Param = PARAM Length NameRef Type rhs_Term? Modifier* // rhs_Term is present in the case of an aliased class parameter + Template = TEMPLATE Length TypeParam* Param* Parent* Self? Stat* // Stat* always starts with the primary constructor. + Parent = Application + Type + Self = SELFDEF selfName_NameRef selfType_Type + + Term = Path + Application + IDENT NameRef Type // used when ident’s type is not a TermRef + SELECT possiblySigned_NameRef qual_Term + NEW cls_Type + SUPER Length this_Term mixinTrait_Type? + PAIR Length left_Term right_Term + TYPED Length expr_Term ascription_Type + NAMEDARG Length paramName_NameRef arg_Term + ASSIGN Length lhs_Term rhs_Term + BLOCK Length expr_Term Stat* + LAMBDA Length meth_Term target_Type + IF Length cond_Term then_Term else_Term + MATCH Length sel_Term CaseDef* + TRY Length expr_Term CaseDef* finalizer_Term? + RETURN Length meth_ASTRef expr_Term? + REPEATED Length elem_Term* + BIND Length boundName_NameRef patType_Type pat_Term + ALTERNATIVE Length alt_Term* + UNAPPLY Length fun_Term ImplicitArg* pat_Type pat_Term* + EMPTYTREE + SHARED term_ASTRef + Application = APPLY Length fn_Term arg_Term* + + TYPEAPPLY Length fn_Term arg_Type* + CaseDef = CASEDEF Length pat_Term rhs_Tree guard_Tree? + ImplicitArg = IMPLICITARG arg_Term + ASTRef = Nat // byte position in AST payload + + Path = Constant + TERMREFdirect sym_ASTRef + TERMREFsymbol sym_ASTRef qual_Type + TERMREFpkg fullyQualified_NameRef + TERMREF possiblySigned_NameRef qual_Type + THIS clsRef_Type + SKOLEMtype refinedType_ASTRef + SHARED path_ASTRef + + + Constant = UNITconst + FALSEconst + TRUEconst + BYTEconst Int + SHORTconst Int + CHARconst Nat + INTconst Int + LONGconst LongInt + FLOATconst Int + DOUBLEconst LongInt + STRINGconst NameRef + NULLconst + CLASSconst Type + ENUMconst Path + + Type = Path + TYPEREFdirect sym_ASTRef + TYPEREFsymbol sym_ASTRef qual_Type + TYPEREFpkg fullyQualified_NameRef + TYPEREF possiblySigned_NameRef qual_Type + SUPERtype Length this_Type underlying_Type + REFINEDtype Length underlying_Type refinement_NameRef info_Type + APPLIEDtype Length tycon_Type arg_Type* + TYPEBOUNDS Length low_Type high_Type + TYPEALIAS Length alias_Type (COVARIANT | CONTRAVARIANT)? + ANNOTATED Length fullAnnotation_Term underlying_Type + ANDtype Length left_Type right_Type + ORtype Length left_Type right_Type + BIND Length boundName_NameRef bounds_Type + // for type-variables defined in a type pattern + BYNAMEtype underlying_Type + POLYtype Length result_Type NamesTypes // needed for refinements + METHODtype Length result_Type NamesTypes // needed for refinements + PARAMtype Length binder_ASTref paramNum_Nat // needed for refinements + SHARED type_ASTRef + NamesTypes = ParamType* + NameType = paramName_NameRef typeOrBounds_ASTRef + + Modifier = PRIVATE + INTERNAL // package private + PROTECTED + PRIVATEqualified qualifier_Type // will be dropped + PROTECTEDqualified qualifier_Type // will be dropped + ABSTRACT + FINAL + SEALED + CASE + IMPLICIT + LAZY + OVERRIDE + INLINE // macro + ABSOVERRIDE // abstract override + STATIC // mapped to static Java member + OBJECT // an object or its class + TRAIT // a trait + LOCAL // private[this] or protected[this] + SYNTHETIC // generated by Scala compiler + ARTIFACT // to be tagged Java Synthetic + MUTABLE // a var + LABEL // method generated as a label + FIELDaccessor // getter or setter + CASEaccessor // getter for case class param + COVARIANT // type param marked “+” + CONTRAVARIANT // type param marked “-” + SCALA2X // Imported from Scala2.x + DEFAULTparameterized // Method with default params + INSUPERCALL // defined in the argument of a constructor supercall + Annotation + Annotation = ANNOTATION Length tycon_Type fullAnnotation_Term + +Note: Tree tags are grouped into 5 categories that determine what follows, and thus allow to compute the size of the tagged tree in a generic way. + + Category 1 (tags 0-63) : tag + Category 2 (tags 64-95) : tag Nat + Category 3 (tags 96-111) : tag AST + Category 4 (tags 112-127): tag Nat AST + Category 5 (tags 128-255): tag Length + +Standard Section: "Sourcefile" sourcefile_NameRef + +Standard Section: "Positions" sourceLength_Nat Assoc* + + Assoc = addr_Delta offset_Delta offset_Delta? + // addr_Delta : + // Difference of address to last recorded node. + // All but the first addr_Deltas are > 0, the first is >= 0. + // 2nd offset_Delta: + // Difference of end offset of addressed node vs parent node. Always <= 0 + // 1st offset Delta, if delta >= 0 or 2nd offset delta exists + // Difference of start offset of addressed node vs parent node. + // 1st offset Delta, if delta < 0 and 2nd offset delta does not exist: + // Difference of end offset of addressed node vs parent node. + // Offsets and addresses are difference encoded. + // Nodes which have the same positions as their parents are omitted. + Delta = Int // Difference between consecutive offsets / tree addresses, + +**************************************************************************************/ + +object TastyFormat { + + final val header = Array(0x5C, 0xA1, 0xAB, 0x1F) + final val MajorVersion = 0 + final val MinorVersion = 5 + + // Name tags + + final val UTF8 = 1 + final val QUALIFIED = 2 + final val SIGNED = 3 + final val EXPANDED = 4 + final val OBJECTCLASS = 5 + final val SUPERACCESSOR = 6 + final val DEFAULTGETTER = 7 + final val SHADOWED = 8 + +// AST tags + + final val UNITconst = 2 + final val FALSEconst = 3 + final val TRUEconst = 4 + final val NULLconst = 5 + final val PRIVATE = 6 + final val INTERNAL = 7 + final val PROTECTED = 8 + final val ABSTRACT = 9 + final val FINAL = 10 + final val SEALED = 11 + final val CASE = 12 + final val IMPLICIT = 13 + final val LAZY = 14 + final val OVERRIDE = 15 + final val INLINE = 16 + final val ABSOVERRIDE = 17 + final val STATIC = 18 + final val OBJECT = 19 + final val TRAIT = 20 + final val LOCAL = 21 + final val SYNTHETIC = 22 + final val ARTIFACT = 23 + final val MUTABLE = 24 + final val LABEL = 25 + final val FIELDaccessor = 26 + final val CASEaccessor = 27 + final val COVARIANT = 28 + final val CONTRAVARIANT = 29 + final val SCALA2X = 30 + final val DEFAULTparameterized = 31 + final val INSUPERCALL = 32 + + final val SHARED = 64 + final val TERMREFdirect = 65 + final val TYPEREFdirect = 66 + final val TERMREFpkg = 67 + final val TYPEREFpkg = 68 + final val SKOLEMtype = 69 + final val BYTEconst = 70 + final val SHORTconst = 71 + final val CHARconst = 72 + final val INTconst = 73 + final val LONGconst = 74 + final val FLOATconst = 75 + final val DOUBLEconst = 76 + final val STRINGconst = 77 + final val IMPORTED = 78 + + final val THIS = 96 + final val CLASSconst = 97 + final val ENUMconst = 98 + final val BYNAMEtype = 99 + final val NEW = 100 + final val IMPLICITarg = 101 + final val PRIVATEqualified = 102 + final val PROTECTEDqualified = 103 + + final val IDENT = 112 + final val SELECT = 113 + final val TERMREFsymbol = 114 + final val TERMREF = 115 + final val TYPEREFsymbol = 116 + final val TYPEREF = 117 + final val SELFDEF = 118 + + final val PACKAGE = 128 + final val VALDEF = 129 + final val DEFDEF = 130 + final val TYPEDEF = 131 + final val IMPORT = 132 + final val TYPEPARAM = 133 + final val PARAMS = 134 + final val PARAM = 136 + final val RENAMED = 138 + final val APPLY = 139 + final val TYPEAPPLY = 140 + final val PAIR = 142 + final val TYPED = 143 + final val NAMEDARG = 144 + final val ASSIGN = 145 + final val BLOCK = 146 + final val IF = 147 + final val LAMBDA = 148 + final val MATCH = 149 + final val RETURN = 150 + final val TRY = 151 + final val REPEATED = 153 + final val BIND = 154 + final val ALTERNATIVE = 155 + final val UNAPPLY = 156 + final val ANNOTATED = 157 + final val CASEDEF = 158 + final val TEMPLATE = 160 + final val SUPER = 163 + final val SUPERtype = 166 + final val REFINEDtype = 167 + final val APPLIEDtype = 168 + final val TYPEBOUNDS = 169 + final val TYPEALIAS = 170 + final val ANDtype = 171 + final val ORtype = 172 + final val METHODtype = 174 + final val POLYtype = 175 + final val PARAMtype = 176 + final val ANNOTATION = 178 + + final val firstSimpleTreeTag = UNITconst + final val firstNatTreeTag = SHARED + final val firstASTTreeTag = THIS + final val firstNatASTTreeTag = IDENT + final val firstLengthTreeTag = PACKAGE + + def isParamTag(tag: Int) = tag == PARAM || tag == TYPEPARAM + + def isModifierTag(tag: Int) = tag match { + case PRIVATE + | INTERNAL + | PROTECTED + | ABSTRACT + | FINAL + | SEALED + | CASE + | IMPLICIT + | LAZY + | OVERRIDE + | INLINE + | ABSOVERRIDE + | STATIC + | OBJECT + | TRAIT + | LOCAL + | SYNTHETIC + | ARTIFACT + | MUTABLE + | LABEL + | FIELDaccessor + | CASEaccessor + | COVARIANT + | CONTRAVARIANT + | SCALA2X + | DEFAULTparameterized + | INSUPERCALL + | ANNOTATION + | PRIVATEqualified + | PROTECTEDqualified => true + case _ => false + } + + def nameTagToString(tag: Int): String = tag match { + case UTF8 => "UTF8" + case QUALIFIED => "QUALIFIED" + case SIGNED => "SIGNED" + case EXPANDED => "EXPANDED" + case OBJECTCLASS => "OBJECTCLASS" + case SUPERACCESSOR => "SUPERACCESSOR" + case DEFAULTGETTER => "DEFAULTGETTER" + } + + def astTagToString(tag: Int): String = tag match { + case UNITconst => "UNITconst" + case FALSEconst => "FALSEconst" + case TRUEconst => "TRUEconst" + case NULLconst => "NULLconst" + case PRIVATE => "PRIVATE" + case INTERNAL => "INTERNAL" + case PROTECTED => "PROTECTED" + case ABSTRACT => "ABSTRACT" + case FINAL => "FINAL" + case SEALED => "SEALED" + case CASE => "CASE" + case IMPLICIT => "IMPLICIT" + case LAZY => "LAZY" + case OVERRIDE => "OVERRIDE" + case INLINE => "INLINE" + case ABSOVERRIDE => "ABSOVERRIDE" + case STATIC => "STATIC" + case OBJECT => "OBJECT" + case TRAIT => "TRAIT" + case LOCAL => "LOCAL" + case SYNTHETIC => "SYNTHETIC" + case ARTIFACT => "ARTIFACT" + case MUTABLE => "MUTABLE" + case LABEL => "LABEL" + case FIELDaccessor => "FIELDaccessor" + case CASEaccessor => "CASEaccessor" + case COVARIANT => "COVARIANT" + case CONTRAVARIANT => "CONTRAVARIANT" + case SCALA2X => "SCALA2X" + case DEFAULTparameterized => "DEFAULTparameterized" + case INSUPERCALL => "INSUPERCALL" + + case SHARED => "SHARED" + case TERMREFdirect => "TERMREFdirect" + case TYPEREFdirect => "TYPEREFdirect" + case TERMREFpkg => "TERMREFpkg" + case TYPEREFpkg => "TYPEREFpkg" + case SKOLEMtype => "SKOLEMtype" + case BYTEconst => "BYTEconst" + case SHORTconst => "SHORTconst" + case CHARconst => "CHARconst" + case INTconst => "INTconst" + case LONGconst => "LONGconst" + case FLOATconst => "FLOATconst" + case DOUBLEconst => "DOUBLEconst" + case STRINGconst => "STRINGconst" + + case IDENT => "IDENT" + case SELECT => "SELECT" + case TERMREFsymbol => "TERMREFsymbol" + case TERMREF => "TERMREF" + case TYPEREFsymbol => "TYPEREFsymbol" + case TYPEREF => "TYPEREF" + + case PACKAGE => "PACKAGE" + case VALDEF => "VALDEF" + case DEFDEF => "DEFDEF" + case TYPEDEF => "TYPEDEF" + case IMPORT => "IMPORT" + case TYPEPARAM => "TYPEPARAM" + case PARAMS => "PARAMS" + case PARAM => "PARAM" + case IMPORTED => "IMPORTED" + case RENAMED => "RENAMED" + case APPLY => "APPLY" + case TYPEAPPLY => "TYPEAPPLY" + case NEW => "NEW" + case PAIR => "PAIR" + case TYPED => "TYPED" + case NAMEDARG => "NAMEDARG" + case ASSIGN => "ASSIGN" + case BLOCK => "BLOCK" + case IF => "IF" + case LAMBDA => "LAMBDA" + case MATCH => "MATCH" + case RETURN => "RETURN" + case TRY => "TRY" + case REPEATED => "REPEATED" + case BIND => "BIND" + case ALTERNATIVE => "ALTERNATIVE" + case UNAPPLY => "UNAPPLY" + case ANNOTATED => "ANNOTATED" + case CASEDEF => "CASEDEF" + case IMPLICITarg => "IMPLICITarg" + case TEMPLATE => "TEMPLATE" + case SELFDEF => "SELFDEF" + case THIS => "THIS" + case SUPER => "SUPER" + case CLASSconst => "CLASSconst" + case ENUMconst => "ENUMconst" + case SUPERtype => "SUPERtype" + case REFINEDtype => "REFINEDtype" + case APPLIEDtype => "APPLIEDtype" + case TYPEBOUNDS => "TYPEBOUNDS" + case TYPEALIAS => "TYPEALIAS" + case ANDtype => "ANDtype" + case ORtype => "ORtype" + case BYNAMEtype => "BYNAMEtype" + case POLYtype => "POLYtype" + case METHODtype => "METHODtype" + case PARAMtype => "PARAMtype" + case ANNOTATION => "ANNOTATION" + case PRIVATEqualified => "PRIVATEqualified" + case PROTECTEDqualified => "PROTECTEDqualified" + } +} diff --git a/src/dotty/tools/dotc/core/tasty/TastyName.scala b/src/dotty/tools/dotc/core/tasty/TastyName.scala new file mode 100644 index 000000000..6d37c8bca --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/TastyName.scala @@ -0,0 +1,30 @@ +package dotty.tools +package dotc +package core +package tasty + +import core.Names.TermName +import collection.mutable + +abstract class TastyName + +object TastyName { + + case class NameRef(val index: Int) extends AnyVal + + case class Simple(name: TermName) extends TastyName + case class Qualified(qualified: NameRef, selector: NameRef) extends TastyName + case class Signed(original: NameRef, params: List[NameRef], result: NameRef) extends TastyName + case class Expanded(prefix: NameRef, original: NameRef) extends TastyName + case class ModuleClass(module: NameRef) extends TastyName + case class SuperAccessor(accessed: NameRef) extends TastyName + case class DefaultGetter(method: NameRef, num: Int) extends TastyName + case class Shadowed(original: NameRef) extends TastyName + + class Table extends (NameRef => TastyName) { + private val names = new mutable.ArrayBuffer[TastyName] + def add(name: TastyName) = names += name + def apply(ref: NameRef) = names(ref.index) + def contents: Iterable[TastyName] = names + } +} diff --git a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala new file mode 100644 index 000000000..f53a97c0c --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala @@ -0,0 +1,70 @@ +package dotty.tools +package dotc +package core +package tasty + +import TastyFormat._ +import collection.mutable +import TastyBuffer._ +import java.util.UUID +import core.Symbols.Symbol +import ast.tpd + +class TastyPickler { + + private val sections = new mutable.ArrayBuffer[(TastyName.NameRef, TastyBuffer)] + + private val headerBuffer = { + val buf = new TastyBuffer(24) + for (ch <- header) buf.writeByte(ch.toByte) + buf.writeNat(MajorVersion) + buf.writeNat(MinorVersion) + val uuid = UUID.randomUUID() + buf.writeUncompressedLong(uuid.getMostSignificantBits) + buf.writeUncompressedLong(uuid.getLeastSignificantBits) + buf + } + + val nameBuffer = new NameBuffer + + def newSection(name: String, buf: TastyBuffer) = + sections += ((nameBuffer.nameIndex(name), buf)) + + def assembleParts(): Array[Byte] = { + def lengthWithLength(buf: TastyBuffer) = { + buf.assemble() + buf.length + natSize(buf.length) + } + val totalSize = + headerBuffer.length + + lengthWithLength(nameBuffer) + { + for ((nameRef, buf) <- sections) yield + natSize(nameRef.index) + lengthWithLength(buf) + }.sum + val all = new TastyBuffer(totalSize) + all.writeBytes(headerBuffer.bytes, headerBuffer.length) + all.writeNat(nameBuffer.length) + all.writeBytes(nameBuffer.bytes, nameBuffer.length) + for ((nameRef, buf) <- sections) { + all.writeNat(nameRef.index) + all.writeNat(buf.length) + all.writeBytes(buf.bytes, buf.length) + } + assert(all.length == totalSize && all.bytes.length == totalSize, s"totalSize = $totalSize, all.length = ${all.length}, all.bytes.length = ${all.bytes.length}") + all.bytes + } + + /** + * Addresses in TASTY file of trees, stored by pickling. + * Note that trees are checked for reference equality, + * so one can reliably use this function only directly after `pickler` + */ + var addrOfTree: tpd.Tree => Option[Addr] = (_ => None) + + /** + * Addresses in TASTY file of symbols, stored by pickling. + * Note that trees are checked for reference equality, + * so one can reliably use this function only dirrectly after `pickler` + */ + var addrOfSym: Symbol => Option[Addr] = (_ => None) +} diff --git a/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala new file mode 100644 index 000000000..915ae3f21 --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -0,0 +1,122 @@ +package dotty.tools.dotc +package core +package tasty + +import Contexts._, Decorators._ +import printing.Texts._ +import TastyName._ +import StdNames._ +import TastyUnpickler._ +import TastyBuffer.Addr +import util.Positions.{Position, offsetToInt} +import collection.mutable + +class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { + + val unpickler = new TastyUnpickler(bytes) + import unpickler.{tastyName, unpickle} + + def nameToString(name: TastyName): String = name match { + case Simple(name) => name.toString + case Qualified(qual, name) => nameRefToString(qual) + "." + nameRefToString(name) + case Signed(original, params, result) => + i"${nameRefToString(original)}@${params.map(nameRefToString)}%,%:${nameRefToString(result)}" + case Expanded(prefix, original) => s"$prefix${nme.EXPAND_SEPARATOR}$original" + case ModuleClass(original) => nameRefToString(original) + "/MODULECLASS" + case SuperAccessor(accessed) => nameRefToString(accessed) + "/SUPERACCESSOR" + case DefaultGetter(meth, num) => nameRefToString(meth) + "/DEFAULTGETTER" + num + case Shadowed(original) => nameRefToString(original) + "/SHADOWED" + } + + def nameRefToString(ref: NameRef): String = nameToString(tastyName(ref)) + + def printNames() = + for ((name, idx) <- tastyName.contents.zipWithIndex) + println(f"$idx%4d: " + nameToString(name)) + + def printContents(): Unit = { + println("Names:") + printNames() + println("Trees:") + unpickle(new TreeSectionUnpickler) + unpickle(new PositionSectionUnpickler) + } + + class TreeSectionUnpickler extends SectionUnpickler[Unit]("ASTs") { + import TastyFormat._ + def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = { + import reader._ + var indent = 0 + def newLine() = print(f"\n ${index(currentAddr) - index(startAddr)}%5d:" + " " * indent) + def printNat() = print(" " + readNat()) + def printName() = { + val idx = readNat() + print(" ") ;print(idx); print("["); print(nameRefToString(NameRef(idx))); print("]") + } + def printTree(): Unit = { + newLine() + val tag = readByte() + print(" ");print(astTagToString(tag)) + indent += 2 + if (tag >= firstLengthTreeTag) { + val len = readNat() + print(s"($len)") + val end = currentAddr + len + def printTrees() = until(end)(printTree()) + tag match { + case RENAMED => + printName(); printName() + case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | BIND => + printName(); printTrees() + case REFINEDtype => + printTree(); printName(); printTrees() + case RETURN => + printNat(); printTrees() + case METHODtype | POLYtype => + printTree() + until(end) { printName(); printTree() } + case PARAMtype => + printNat(); printNat() + case _ => + printTrees() + } + if (currentAddr != end) { + println(s"incomplete read, current = $currentAddr, end = $end") + goto(end) + } + } + else if (tag >= firstNatASTTreeTag) { + tag match { + case IDENT | SELECT | TERMREF | TYPEREF | SELFDEF => printName() + case _ => printNat() + } + printTree() + } + else if (tag >= firstASTTreeTag) + printTree() + else if (tag >= firstNatTreeTag) + tag match { + case TERMREFpkg | TYPEREFpkg | STRINGconst | IMPORTED => printName() + case _ => printNat() + } + indent -= 2 + } + println(i"start = ${reader.startAddr}, base = $base, current = $currentAddr, end = $endAddr") + println(s"${endAddr.index - startAddr.index} bytes of AST, base = $currentAddr") + while (!isAtEnd) { + printTree() + newLine() + } + } + } + + class PositionSectionUnpickler extends SectionUnpickler[Unit]("Positions") { + def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = { + print(s"${reader.endAddr.index - reader.currentAddr.index}") + val (totalRange, positions) = new PositionUnpickler(reader).unpickle() + println(s" position bytes in $totalRange:") + val sorted = positions.toSeq.sortBy(_._1.index) + for ((addr, pos) <- sorted) println(s"${addr.index}: ${offsetToInt(pos.start)} .. ${pos.end}") + } + } +} diff --git a/src/dotty/tools/dotc/core/tasty/TastyReader.scala b/src/dotty/tools/dotc/core/tasty/TastyReader.scala new file mode 100644 index 000000000..311097148 --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/TastyReader.scala @@ -0,0 +1,141 @@ +package dotty.tools +package dotc +package core +package tasty + +import TastyBuffer._ +import TastyName.NameRef +import collection.mutable + +/** A byte array buffer that can be filled with bytes or natural numbers in TASTY format, + * and that supports reading and patching addresses represented as natural numbers. + * + * @param bytes The array containing data + * @param from The position from which to read + * @param end The position one greater than the last byte to be read + * @param base The index referenced by the logical zero address Addr(0) + */ +class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int = 0) { + + def this(bytes: Array[Byte]) = this(bytes, 0, bytes.length) + + private var bp: Int = start + + def addr(idx: Int) = Addr(idx - base) + def index(addr: Addr) = addr.index + base + + /** The address of the first byte to read, respectively byte that was read */ + def startAddr: Addr = addr(start) + + /** The address of the next byte to read */ + def currentAddr: Addr = addr(bp) + + /** the address one greater than the last brte to read */ + def endAddr: Addr = addr(end) + + /** Have all bytes been read? */ + def isAtEnd: Boolean = bp == end + + /** A new reader over the same array with the same address base, but with + * specified start and end positions + */ + def subReader(start: Addr, end: Addr): TastyReader = + new TastyReader(bytes, index(start), index(end), base) + + /** Read a byte of data. */ + def readByte(): Int = { + val result = bytes(bp) & 0xff + bp += 1 + result + } + + /** Returns the next byte of data as a natural number without advancing the read position */ + def nextByte: Int = bytes(bp) & 0xff + + /** Read the next `n` bytes of `data`. */ + def readBytes(n: Int): Array[Byte] = { + val result = new Array[Byte](n) + Array.copy(bytes, bp, result, 0, n) + bp += n + result + } + + /** Read a natural number fitting in an Int in big endian format, base 128. + * All but the last digits have bit 0x80 set. + */ + def readNat(): Int = readLongNat.toInt + + /** Read an integer number in 2's complement big endian format, base 128. + * All but the last digits have bit 0x80 set. + */ + def readInt(): Int = readLongInt.toInt + + /** Read a natural number fitting in a Long in big endian format, base 128. + * All but the last digits have bit 0x80 set. + */ + def readLongNat(): Long = { + var b = 0L + var x = 0L + do { + b = bytes(bp) + x = (x << 7) | (b & 0x7f) + bp += 1 + } while ((b & 0x80) == 0) + x + } + + /** Read a long integer number in 2's complement big endian format, base 128. */ + def readLongInt(): Long = { + var b = bytes(bp) + var x: Long = (b << 1).toByte >> 1 // sign extend with bit 6. + bp += 1 + while ((b & 0x80) == 0) { + b = bytes(bp) + x = (x << 7) | (b & 0x7f) + bp += 1 + } + x + } + + /** Read an uncompressed Long stored in 8 bytes in big endian format */ + def readUncompressedLong(): Long = { + var x = 0 + for (i <- 0 to 7) + x = (x << 8) | (readByte() & 0xff) + x + } + + /** Read a natural number and return as a NameRef */ + def readNameRef() = NameRef(readNat()) + + /** Read a natural number and return as an address */ + def readAddr() = Addr(readNat()) + + /** Read a length number and return the absolute end address implied by it, + * given as
+ . + */ + def readEnd(): Addr = addr(readNat() + bp) + + /** Set read position to the one pointed to by `addr` */ + def goto(addr: Addr): Unit = + bp = index(addr) + + /** Perform `op` until `end` address is reached and collect results in a list. */ + def until[T](end: Addr)(op: => T): List[T] = { + val buf = new mutable.ListBuffer[T] + while (bp < index(end)) buf += op + assert(bp == index(end)) + buf.toList + } + + /** If before given `end` address, the result of `op`, otherwise `default` */ + def ifBefore[T](end: Addr)(op: => T, default: T): T = + if (bp < index(end)) op else default + + /** Perform `op` while cindition `cond` holds and collect results in a list. */ + def collectWhile[T](cond: => Boolean)(op: => T): List[T] = { + val buf = new mutable.ListBuffer[T] + while (cond) buf += op + buf.toList + } +} diff --git a/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala new file mode 100644 index 000000000..8a1f58acd --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala @@ -0,0 +1,95 @@ +package dotty.tools.dotc +package core +package tasty + +import scala.collection.mutable +import TastyFormat._ +import Names.{Name, termName} +import java.util.UUID + +object TastyUnpickler { + class UnpickleException(msg: String) extends Exception(msg) + + abstract class SectionUnpickler[R](val name: String) { + def unpickle(reader: TastyReader, tastyName: TastyName.Table): R + } +} + +import TastyUnpickler._ + +class TastyUnpickler(reader: TastyReader) { + import reader._ + + def this(bytes: Array[Byte]) = this(new TastyReader(bytes)) + + private val sectionReader = new mutable.HashMap[String, TastyReader] + val tastyName = new TastyName.Table + + def check(cond: Boolean, msg: => String) = + if (!cond) throw new UnpickleException(msg) + + def readString(): String = { + val TastyName.Simple(name) = tastyName(readNameRef()) + name.toString + } + + def readName(): TastyName = { + import TastyName._ + val tag = readByte() + val length = readNat() + val start = currentAddr + val end = start + length + val result = tag match { + case UTF8 => + goto(end) + Simple(termName(bytes, start.index, length)) + case QUALIFIED => + Qualified(readNameRef(), readNameRef()) + case SIGNED => + val original = readNameRef() + val result = readNameRef() + val params = until(end)(readNameRef()) + Signed(original, params, result) + case EXPANDED => + Expanded(readNameRef(), readNameRef()) + case OBJECTCLASS => + ModuleClass(readNameRef()) + case SUPERACCESSOR => + SuperAccessor(readNameRef()) + case DEFAULTGETTER => + DefaultGetter(readNameRef(), readNat()) + case SHADOWED => + Shadowed(readNameRef()) + } + assert(currentAddr == end, s"bad name $result $start $currentAddr $end") + result + } + + private def readHeader(): UUID = { + for (i <- 0 until header.length) + check(readByte() == header(i), "not a TASTy file") + val major = readNat() + val minor = readNat() + check(major == MajorVersion && minor <= MinorVersion, + s"""TASTy signature has wrong version. + | expected: $MajorVersion.$MinorVersion + | found : $major.$minor""".stripMargin) + new UUID(readUncompressedLong(), readUncompressedLong()) + } + + val uuid = readHeader() + + locally { + until(readEnd()) { tastyName.add(readName()) } + while (!isAtEnd) { + val secName = readString() + val secEnd = readEnd() + sectionReader(secName) = new TastyReader(bytes, currentAddr.index, secEnd.index, currentAddr.index) + goto(secEnd) + } + } + + def unpickle[R](sec: SectionUnpickler[R]): Option[R] = + for (reader <- sectionReader.get(sec.name)) yield + sec.unpickle(reader, tastyName) +} diff --git a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala new file mode 100644 index 000000000..6aba44d3b --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala @@ -0,0 +1,179 @@ +package dotty.tools +package dotc +package core +package tasty + +import util.Util.{bestFit, dble} +import TastyBuffer.{Addr, AddrWidth} +import config.Printers.pickling +import ast.tpd.Tree + +class TreeBuffer extends TastyBuffer(50000) { + + private final val ItemsOverOffsets = 2 + private val initialOffsetSize = bytes.length / (AddrWidth * ItemsOverOffsets) + private var offsets = new Array[Int](initialOffsetSize) + private var isRelative = new Array[Boolean](initialOffsetSize) + private var delta: Array[Int] = _ + private var numOffsets = 0 + + private[tasty] val pickledTrees = new java.util.IdentityHashMap[Tree, Any] // Value type is really Addr, but that's not compatible with null + + def addrOfTree(tree: Tree): Option[Addr] = pickledTrees.get(tree) match { + case null => None + case n => Some(n.asInstanceOf[Addr]) + } + + private def offset(i: Int): Addr = Addr(offsets(i)) + + private def keepOffset(relative: Boolean): Unit = { + if (numOffsets == offsets.length) { + offsets = dble(offsets) + isRelative = dble(isRelative) + } + offsets(numOffsets) = length + isRelative(numOffsets) = relative + numOffsets += 1 + } + + /** Reserve space for a reference, to be adjusted later */ + def reserveRef(relative: Boolean): Addr = { + val addr = currentAddr + keepOffset(relative) + reserveAddr() + addr + } + + /** Write reference right adjusted into freshly reserved field. */ + def writeRef(target: Addr) = { + keepOffset(relative = false) + fillAddr(reserveAddr(), target) + } + + /** Fill previously reserved field with a reference */ + def fillRef(at: Addr, target: Addr, relative: Boolean) = { + val addr = if (relative) target.relativeTo(at) else target + fillAddr(at, addr) + } + + /** The amount by which the bytes at the given address are shifted under compression */ + def deltaAt(at: Addr): Int = { + val idx = bestFit(offsets, numOffsets, at.index - 1) + if (idx < 0) 0 else delta(idx) + } + + /** The address to which `x` is translated under compression */ + def adjusted(x: Addr): Addr = x - deltaAt(x) + + /** Compute all shift-deltas */ + private def computeDeltas() = { + delta = new Array[Int](numOffsets) + var lastDelta = 0 + var i = 0 + while (i < numOffsets) { + val off = offset(i) + val skippedOff = skipZeroes(off) + val skippedCount = skippedOff.index - off.index + assert(skippedCount < AddrWidth, s"unset field at position $off") + lastDelta += skippedCount + delta(i) = lastDelta + i += 1 + } + } + + /** The absolute or relative adjusted address at index `i` of `offsets` array*/ + private def adjustedOffset(i: Int): Addr = { + val at = offset(i) + val original = getAddr(at) + if (isRelative(i)) { + val start = skipNat(at) + val len1 = original + delta(i) - deltaAt(original + start.index) + val len2 = adjusted(original + start.index) - adjusted(start).index + assert(len1 == len2, + s"adjusting offset #$i: $at, original = $original, len1 = $len1, len2 = $len2") + len1 + } else adjusted(original) + } + + /** Adjust all offsets according to previously computed deltas */ + private def adjustOffsets(): Unit = { + for (i <- 0 until numOffsets) { + val corrected = adjustedOffset(i) + fillAddr(offset(i), corrected) + } + } + + /** Adjust deltas to also take account references that will shrink (and thereby + * generate additional zeroes that can be skipped) due to previously + * computed adjustements. + */ + private def adjustDeltas(): Int = { + val delta1 = new Array[Int](delta.length) + var lastDelta = 0 + var i = 0 + while (i < numOffsets) { + val corrected = adjustedOffset(i) + lastDelta += AddrWidth - TastyBuffer.natSize(corrected.index) + delta1(i) = lastDelta + i += 1 + } + val saved = + if (numOffsets == 0) 0 + else delta1(numOffsets - 1) - delta(numOffsets - 1) + delta = delta1 + saved + } + + /** Compress pickle buffer, shifting bytes to close all skipped zeroes. */ + private def compress(): Int = { + var lastDelta = 0 + var start = 0 + var i = 0 + var wasted = 0 + def shift(end: Int) = + Array.copy(bytes, start, bytes, start - lastDelta, end - start) + while (i < numOffsets) { + val next = offsets(i) + shift(next) + start = next + delta(i) - lastDelta + val pastZeroes = skipZeroes(Addr(next)).index + assert(pastZeroes >= start, s"something's wrong: eliminated non-zero") + wasted += (pastZeroes - start) + lastDelta = delta(i) + i += 1 + } + shift(length) + length -= lastDelta + wasted + } + + def adjustPickledTrees(): Unit = { + val it = pickledTrees.keySet.iterator + while (it.hasNext) { + val tree = it.next + pickledTrees.put(tree, adjusted(pickledTrees.get(tree).asInstanceOf[Addr])) + } + } + + /** Final assembly, involving the following steps: + * - compute deltas + * - adjust deltas until additional savings are < 1% of total + * - adjust offsets according to the adjusted deltas + * - shrink buffer, skipping zeroes. + */ + def compactify(): Unit = { + val origLength = length + computeDeltas() + //println(s"offsets: ${offsets.take(numOffsets).deep}") + //println(s"deltas: ${delta.take(numOffsets).deep}") + var saved = 0 + do { + saved = adjustDeltas() + pickling.println(s"adjusting deltas, saved = $saved") + } while (saved > 0 && length / saved < 100) + adjustOffsets() + adjustPickledTrees() + val wasted = compress() + pickling.println(s"original length: $origLength, compressed to: $length, wasted: $wasted") // DEBUG, for now. + } +} diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala new file mode 100644 index 000000000..365b5d268 --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -0,0 +1,551 @@ +package dotty.tools +package dotc +package core +package tasty + +import ast.Trees._ +import TastyFormat._ +import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._, StdNames.tpnme, NameOps._ +import collection.mutable +import NameOps._ +import TastyBuffer._ + +class TreePickler(pickler: TastyPickler) { + val buf = new TreeBuffer + pickler.newSection("ASTs", buf) + import buf._ + import pickler.nameBuffer.{nameIndex, fullNameIndex} + import ast.tpd._ + + private val symRefs = new mutable.HashMap[Symbol, Addr] + private val forwardSymRefs = new mutable.HashMap[Symbol, List[Addr]] + private val pickledTypes = new java.util.IdentityHashMap[Type, Any] // Value type is really Addr, but that's not compatible with null + + private def withLength(op: => Unit) = { + val lengthAddr = reserveRef(relative = true) + op + fillRef(lengthAddr, currentAddr, relative = true) + } + + def addrOfSym(sym: Symbol): Option[Addr] = { + symRefs.get(sym) + } + + def preRegister(tree: Tree)(implicit ctx: Context): Unit = tree match { + case tree: MemberDef => + if (!symRefs.contains(tree.symbol)) symRefs(tree.symbol) = NoAddr + case _ => + } + + def registerDef(sym: Symbol): Unit = { + symRefs(sym) = currentAddr + forwardSymRefs.get(sym) match { + case Some(refs) => + refs.foreach(fillRef(_, currentAddr, relative = false)) + forwardSymRefs -= sym + case None => + } + } + + private def pickleName(name: Name): Unit = writeNat(nameIndex(name).index) + private def pickleName(name: TastyName): Unit = writeNat(nameIndex(name).index) + private def pickleNameAndSig(name: Name, sig: Signature) = { + val Signature(params, result) = sig + pickleName(TastyName.Signed(nameIndex(name), params.map(fullNameIndex), fullNameIndex(result))) + } + + private def pickleName(sym: Symbol)(implicit ctx: Context): Unit = + if (sym is Flags.ExpandedName) + pickleName(TastyName.Expanded( + nameIndex(sym.name.expandedPrefix), nameIndex(sym.name.unexpandedName))) + else pickleName(sym.name) + + private def pickleSymRef(sym: Symbol)(implicit ctx: Context) = symRefs.get(sym) match { + case Some(label) => + if (label != NoAddr) writeRef(label) else pickleForwardSymRef(sym) + case None => + ctx.log(i"pickling reference to as yet undefined $sym in ${sym.owner}", sym.pos) + pickleForwardSymRef(sym) + } + + private def pickleForwardSymRef(sym: Symbol)(implicit ctx: Context) = { + val ref = reserveRef(relative = false) + assert(!sym.is(Flags.Package), sym) + forwardSymRefs(sym) = ref :: forwardSymRefs.getOrElse(sym, Nil) + } + + private def isLocallyDefined(sym: Symbol)(implicit ctx: Context) = symRefs.get(sym) match { + case Some(label) => assert(sym.exists); label != NoAddr + case None => false + } + + def pickle(trees: List[Tree])(implicit ctx: Context) = { + + def qualifiedName(sym: Symbol): TastyName = + if (sym.isRoot || sym.owner.isRoot) TastyName.Simple(sym.name.toTermName) + else TastyName.Qualified(nameIndex(qualifiedName(sym.owner)), nameIndex(sym.name)) + + def pickleConstant(c: Constant): Unit = c.tag match { + case UnitTag => + writeByte(UNITconst) + case BooleanTag => + writeByte(if (c.booleanValue) TRUEconst else FALSEconst) + case ByteTag => + writeByte(BYTEconst) + writeInt(c.byteValue) + case ShortTag => + writeByte(SHORTconst) + writeInt(c.shortValue) + case CharTag => + writeByte(CHARconst) + writeNat(c.charValue) + case IntTag => + writeByte(INTconst) + writeInt(c.intValue) + case LongTag => + writeByte(LONGconst) + writeLongInt(c.longValue) + case FloatTag => + writeByte(FLOATconst) + writeInt(java.lang.Float.floatToRawIntBits(c.floatValue)) + case DoubleTag => + writeByte(DOUBLEconst) + writeLongInt(java.lang.Double.doubleToRawLongBits(c.doubleValue)) + case StringTag => + writeByte(STRINGconst) + writeNat(nameIndex(c.stringValue).index) + case NullTag => + writeByte(NULLconst) + case ClazzTag => + writeByte(CLASSconst) + pickleType(c.typeValue) + case EnumTag => + writeByte(ENUMconst) + pickleType(c.symbolValue.termRef) + } + + def pickleType(tpe0: Type, richTypes: Boolean = false): Unit = try { + val tpe = tpe0.stripTypeVar + val prev = pickledTypes.get(tpe) + if (prev == null) { + pickledTypes.put(tpe, currentAddr) + pickleNewType(tpe, richTypes) + } + else { + writeByte(SHARED) + writeRef(prev.asInstanceOf[Addr]) + } + } catch { + case ex: AssertionError => + println(i"error when pickling type $tpe0") + throw ex + } + + def pickleNewType(tpe: Type, richTypes: Boolean): Unit = try { tpe match { + case ConstantType(value) => + pickleConstant(value) + case tpe: TypeRef if tpe.info.isAlias && tpe.symbol.is(Flags.AliasPreferred) => + pickleType(tpe.info.bounds.hi) + case tpe: WithFixedSym => + val sym = tpe.symbol + if (sym.is(Flags.Package)) { + writeByte(if (tpe.isType) TYPEREFpkg else TERMREFpkg) + pickleName(qualifiedName(sym)) + } + else { + assert(tpe.prefix == NoPrefix) + def pickleRef() = { + writeByte(if (tpe.isType) TYPEREFdirect else TERMREFdirect) + pickleSymRef(sym) + } + if (sym is Flags.BindDefinedType) { + registerDef(sym) + writeByte(BIND) + withLength { + pickleName(sym.name) + pickleType(sym.info) + pickleRef() + } + } + else pickleRef() + } + case tpe: TermRefWithSignature => + if (tpe.symbol.is(Flags.Package)) picklePackageRef(tpe.symbol) + else { + writeByte(TERMREF) + pickleNameAndSig(tpe.name, tpe.signature); pickleType(tpe.prefix) + } + case tpe: NamedType => + if (tpe.name == tpnme.Apply && tpe.prefix.argInfos.nonEmpty && tpe.prefix.isInstantiatedLambda) + // instantiated lambdas are pickled as APPLIEDTYPE; #Apply will + // be reconstituted when unpickling. + pickleType(tpe.prefix) + else if (isLocallyDefined(tpe.symbol)) { + writeByte(if (tpe.isType) TYPEREFsymbol else TERMREFsymbol) + pickleSymRef(tpe.symbol); pickleType(tpe.prefix) + } + else { + writeByte(if (tpe.isType) TYPEREF else TERMREF) + pickleName(tpe.name); pickleType(tpe.prefix) + } + case tpe: ThisType => + if (tpe.cls.is(Flags.Package) && !tpe.cls.isEffectiveRoot) + picklePackageRef(tpe.cls) + else { + writeByte(THIS) + pickleType(tpe.tref) + } + case tpe: SuperType => + writeByte(SUPERtype) + withLength { pickleType(tpe.thistpe); pickleType(tpe.supertpe)} + case tpe: SkolemType => + writeByte(SKOLEMtype) + writeRef(pickledTypes.get(tpe.binder).asInstanceOf[Addr]) + case tpe: RefinedType => + val args = tpe.argInfos(interpolate = false) + if (args.isEmpty) { + writeByte(REFINEDtype) + withLength { + pickleType(tpe.parent) + pickleName(tpe.refinedName) + pickleType(tpe.refinedInfo, richTypes = true) + } + } + else { + writeByte(APPLIEDtype) + withLength { pickleType(tpe.withoutArgs(args)); args.foreach(pickleType(_)) } + } + case tpe: TypeAlias => + writeByte(TYPEALIAS) + withLength { + pickleType(tpe.alias, richTypes) + tpe.variance match { + case 1 => writeByte(COVARIANT) + case -1 => writeByte(CONTRAVARIANT) + case 0 => + } + } + case tpe: TypeBounds => + writeByte(TYPEBOUNDS) + withLength { pickleType(tpe.lo, richTypes); pickleType(tpe.hi, richTypes) } + case tpe: AnnotatedType => + writeByte(ANNOTATED) + withLength { pickleTree(tpe.annot.tree); pickleType(tpe.tpe, richTypes) } + case tpe: AndOrType => + writeByte(if (tpe.isAnd) ANDtype else ORtype) + withLength { pickleType(tpe.tp1, richTypes); pickleType(tpe.tp2, richTypes) } + case tpe: ExprType => + writeByte(BYNAMEtype) + pickleType(tpe.underlying) + case tpe: MethodType if richTypes => + writeByte(METHODtype) + pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramTypes) + case tpe: PolyType if richTypes => + writeByte(POLYtype) + pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramBounds) + case tpe: PolyParam => + if (!pickleParamType(tpe)) + // TODO figure out why this case arises in e.g. pickling AbstractFileReader. + ctx.typerState.constraint.entry(tpe) match { + case TypeBounds(lo, hi) if lo eq hi => pickleNewType(lo, richTypes) + case _ => assert(false, s"orphan poly parameter: $tpe") + } + case tpe: MethodParam => + assert(pickleParamType(tpe), s"orphan method parameter: $tpe") + case tpe: LazyRef => + pickleType(tpe.ref) + }} catch { + case ex: AssertionError => + println(i"error while pickling type $tpe") + throw ex + } + + def picklePackageRef(pkg: Symbol): Unit = { + writeByte(TERMREFpkg) + pickleName(qualifiedName(pkg)) + } + + def pickleMethodic(result: Type, names: List[Name], types: List[Type]) = + withLength { + pickleType(result, richTypes = true) + (names, types).zipped.foreach { (name, tpe) => + pickleName(name); pickleType(tpe) + } + } + + def pickleParamType(tpe: ParamType): Boolean = { + val binder = pickledTypes.get(tpe.binder) + val pickled = binder != null + if (pickled) { + writeByte(PARAMtype) + withLength { writeRef(binder.asInstanceOf[Addr]); writeNat(tpe.paramNum) } + } + pickled + } + + def pickleTpt(tpt: Tree): Unit = pickleType(tpt.tpe) // TODO correlate with original when generating positions + + def pickleTreeUnlessEmpty(tree: Tree): Unit = + if (!tree.isEmpty) pickleTree(tree) + + def pickleTree(tree: Tree): Unit = try { + pickledTrees.put(tree, currentAddr) + tree match { + case Ident(name) => + tree.tpe match { + case tp: TermRef => pickleType(tp) + case _ => + writeByte(IDENT) + pickleName(name) + pickleType(tree.tpe) + } + case This(_) => + pickleType(tree.tpe) + case Select(qual, name) => + writeByte(SELECT) + val realName = tree.tpe match { + case tp: NamedType if tp.name.isShadowedName => tp.name + case _ => name + } + val sig = tree.tpe.signature + if (sig == Signature.NotAMethod) pickleName(realName) + else pickleNameAndSig(realName, sig) + pickleTree(qual) + case Apply(fun, args) => + writeByte(APPLY) + withLength { + pickleTree(fun) + args.foreach(pickleTree) + } + case TypeApply(fun, args) => + writeByte(TYPEAPPLY) + withLength { + pickleTree(fun) + args.foreach(pickleTpt) + } + case Literal(const1) => + pickleConstant { + tree.tpe match { + case ConstantType(const2) => const2 + case _ => const1 + } + } + case Super(qual, mix) => + writeByte(SUPER) + withLength { + pickleTree(qual); + if (!mix.isEmpty) { + val SuperType(_, mixinType) = tree.tpe + pickleType(mixinType) + } + } + case New(tpt) => + writeByte(NEW) + pickleTpt(tpt) + case Pair(left, right) => + writeByte(PAIR) + withLength { pickleTree(left); pickleTree(right) } + case Typed(expr, tpt) => + writeByte(TYPED) + withLength { pickleTree(expr); pickleTpt(tpt) } + case NamedArg(name, arg) => + writeByte(NAMEDARG) + withLength { pickleName(name); pickleTree(arg) } + case Assign(lhs, rhs) => + writeByte(ASSIGN) + withLength { pickleTree(lhs); pickleTree(rhs) } + case Block(stats, expr) => + writeByte(BLOCK) + stats.foreach(preRegister) + withLength { pickleTree(expr); stats.foreach(pickleTree) } + case If(cond, thenp, elsep) => + writeByte(IF) + withLength{ pickleTree(cond); pickleTree(thenp); pickleTree(elsep) } + case Closure(env, meth, tpt) => + writeByte(LAMBDA) + assert(env.isEmpty) + withLength{ + pickleTree(meth) + if (tpt.tpe.exists) pickleTpt(tpt) + } + case Match(selector, cases) => + writeByte(MATCH) + withLength { pickleTree(selector); cases.foreach(pickleTree) } + case CaseDef(pat, guard, rhs) => + writeByte(CASEDEF) + withLength { pickleTree(pat); pickleTree(rhs); pickleTreeUnlessEmpty(guard) } + case Return(expr, from) => + writeByte(RETURN) + withLength { pickleSymRef(from.symbol); pickleTreeUnlessEmpty(expr) } + case Try(block, cases, finalizer) => + writeByte(TRY) + withLength { pickleTree(block); cases.foreach(pickleTree); pickleTreeUnlessEmpty(finalizer) } + case SeqLiteral(elems) => + writeByte(REPEATED) + withLength { elems.foreach(pickleTree) } + case TypeTree(original) => + pickleTpt(tree) + case Bind(name, body) => + registerDef(tree.symbol) + writeByte(BIND) + withLength { pickleName(name); pickleType(tree.symbol.info); pickleTree(body) } + case Alternative(alts) => + writeByte(ALTERNATIVE) + withLength { alts.foreach(pickleTree) } + case UnApply(fun, implicits, patterns) => + writeByte(UNAPPLY) + withLength { + pickleTree(fun) + for (implicitArg <- implicits) { + writeByte(IMPLICITarg) + pickleTree(implicitArg) + } + pickleType(tree.tpe) + patterns.foreach(pickleTree) + } + case tree: ValDef => + pickleDef(VALDEF, tree.symbol, tree.tpt, tree.rhs) + case tree: DefDef => + def pickleAllParams = { + pickleParams(tree.tparams) + for (vparams <- tree.vparamss) { + writeByte(PARAMS) + withLength { pickleParams(vparams) } + } + } + pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleAllParams) + case tree: TypeDef => + pickleDef(TYPEDEF, tree.symbol, tree.rhs) + case tree: Template => + registerDef(tree.symbol) + writeByte(TEMPLATE) + val (params, rest) = tree.body partition { + case stat: TypeDef => stat.symbol is Flags.Param + case stat: ValOrDefDef => + stat.symbol.is(Flags.ParamAccessor) && !stat.symbol.isSetter + case _ => false + } + withLength { + pickleParams(params) + tree.parents.foreach(pickleTree) + val cinfo @ ClassInfo(_, _, _, _, selfInfo) = tree.symbol.owner.info + if ((selfInfo ne NoType) || !tree.self.isEmpty) { + writeByte(SELFDEF) + pickleName(tree.self.name) + pickleType { + cinfo.selfInfo match { + case sym: Symbol => sym.info + case tp: Type => tp + } + } + } + pickleStats(tree.constr :: rest) + } + case Import(expr, selectors) => + writeByte(IMPORT) + withLength { + pickleTree(expr) + selectors foreach { + case Pair(Ident(from), Ident(to)) => + writeByte(RENAMED) + withLength { pickleName(from); pickleName(to) } + case Ident(name) => + writeByte(IMPORTED) + pickleName(name) + } + } + case PackageDef(pid, stats) => + writeByte(PACKAGE) + withLength { pickleType(pid.tpe); pickleStats(stats) } + }} + catch { + case ex: AssertionError => + println(i"error when pickling tree $tree") + throw ex + } + + def pickleDef(tag: Int, sym: Symbol, tpt: Tree, rhs: Tree = EmptyTree, pickleParams: => Unit = ()) = { + assert(symRefs(sym) == NoAddr) + registerDef(sym) + writeByte(tag) + withLength { + pickleName(sym) + pickleParams + tpt match { + case tpt: TypeTree => pickleTpt(tpt) + case _ => pickleTree(tpt) + } + pickleTreeUnlessEmpty(rhs) + pickleModifiers(sym) + } + } + + def pickleParam(tree: Tree): Unit = tree match { + case tree: ValDef => pickleDef(PARAM, tree.symbol, tree.tpt) + case tree: DefDef => pickleDef(PARAM, tree.symbol, tree.tpt, tree.rhs) + case tree: TypeDef => pickleDef(TYPEPARAM, tree.symbol, tree.rhs) + } + + def pickleParams(trees: List[Tree]): Unit = { + trees.foreach(preRegister) + trees.foreach(pickleParam) + } + + def pickleStats(stats: List[Tree]) = { + stats.foreach(preRegister) + stats.foreach(stat => if (!stat.isEmpty) pickleTree(stat)) + } + + def pickleModifiers(sym: Symbol): Unit = { + import Flags._ + val flags = sym.flags + val privateWithin = sym.privateWithin + if (privateWithin.exists) { + writeByte(if (flags is Protected) PROTECTEDqualified else PRIVATEqualified) + pickleType(privateWithin.typeRef) + } + if (flags is Private) writeByte(PRIVATE) + if (flags is Protected) if (!privateWithin.exists) writeByte(PROTECTED) + if ((flags is Final) && !(sym is Module)) writeByte(FINAL) + if (flags is Case) writeByte(CASE) + if (flags is Override) writeByte(OVERRIDE) + if (flags is Inline) writeByte(INLINE) + if (flags is JavaStatic) writeByte(STATIC) + if (flags is Module) writeByte(OBJECT) + if (flags is Local) writeByte(LOCAL) + if (flags is Synthetic) writeByte(SYNTHETIC) + if (flags is Artifact) writeByte(ARTIFACT) + if (flags is Scala2x) writeByte(SCALA2X) + if (flags is InSuperCall) writeByte(INSUPERCALL) + if (sym.isTerm) { + if (flags is Implicit) writeByte(IMPLICIT) + if ((flags is Lazy) && !(sym is Module)) writeByte(LAZY) + if (flags is AbsOverride) writeByte(ABSOVERRIDE) + if (flags is Mutable) writeByte(MUTABLE) + if (flags is Accessor) writeByte(FIELDaccessor) + if (flags is CaseAccessor) writeByte(CASEaccessor) + if (flags is DefaultParameterized) writeByte(DEFAULTparameterized) + } else { + if (flags is Sealed) writeByte(SEALED) + if (flags is Abstract) writeByte(ABSTRACT) + if (flags is Trait) writeByte(TRAIT) + if (flags is Covariant) writeByte(COVARIANT) + if (flags is Contravariant) writeByte(CONTRAVARIANT) + } + sym.annotations.foreach(pickleAnnotation) + } + + def pickleAnnotation(ann: Annotation) = { + writeByte(ANNOTATION) + withLength { pickleType(ann.symbol.typeRef); pickleTree(ann.tree) } + } + + def updateMapWithDeltas[T](mp: collection.mutable.Map[T, Addr]) = + for (key <- mp.keysIterator.toBuffer[T]) mp(key) = adjusted(mp(key)) + + trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree)) + assert(forwardSymRefs.isEmpty, i"unresolved symbols: ${forwardSymRefs.keySet.toList}%, %") + compactify() + updateMapWithDeltas(symRefs) + } +} diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala new file mode 100644 index 000000000..9d2ac2f23 --- /dev/null +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -0,0 +1,889 @@ +package dotty.tools +package dotc +package core +package tasty + +import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._ +import StdNames._, Denotations._, Flags._, Constants._, Annotations._ +import util.Positions._ +import dotty.tools.dotc.ast.{tpd, Trees, untpd} +import Trees._ +import Decorators._ +import TastyUnpickler._, TastyBuffer._, PositionPickler._ +import annotation.switch +import scala.collection.{ mutable, immutable } +import typer.Mode +import config.Printers.pickling + +/** Unpickler for typed trees + * @param reader the reader from which to unpickle + * @param tastyName the nametable + */ +class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { + import TastyFormat._ + import TastyName._ + import tpd._ + + private var readPositions = false + private var totalRange = NoPosition + private var positions: collection.Map[Addr, Position] = _ + + /** Make a subsequent call to `unpickle` return trees with positions + * @param totalRange the range position enclosing all returned trees, + * or NoPosition if positions should not be unpickled + * @param positions a map from tree addresses to their positions relative + * to positions of parent nodes. + */ + def usePositions(totalRange: Position, positions: collection.Map[Addr, Position]): Unit = { + readPositions = true + this.totalRange = totalRange + this.positions = positions + } + + private val symAtAddr = new mutable.HashMap[Addr, Symbol] + private val treeAtAddr = new mutable.HashMap[Addr, Tree] + private val typeAtAddr = new mutable.HashMap[Addr, Type] // currently populated only for types that are known to be SHAREd. + private var stubs: Set[Symbol] = Set() + + private var roots: Set[SymDenotation] = null + + /** Enter all toplevel classes and objects into their scopes + * @param roots a set of SymDenotations that should be overwritten by unpickling + */ + def enterTopLevel(roots: Set[SymDenotation])(implicit ctx: Context): Unit = { + this.roots = roots + new TreeReader(reader).fork.indexStats(reader.endAddr) + } + + /** The unpickled trees */ + def unpickle()(implicit ctx: Context): List[Tree] = { + assert(roots != null, "unpickle without previous enterTopLevel") + val stats = new TreeReader(reader) + .readIndexedStats(NoSymbol, reader.endAddr)(ctx.addMode(Mode.AllowDependentFunctions)) + normalizePos(stats, totalRange) + stats + } + + def toTermName(tname: TastyName): TermName = tname match { + case Simple(name) => name + case Qualified(qual, name) => toTermName(qual) ++ "." ++ toTermName(name) + case Signed(original, params, result) => toTermName(original) + case Shadowed(original) => toTermName(original).shadowedName + case Expanded(prefix, original) => toTermName(original).expandedName(toTermName(prefix)) + case ModuleClass(original) => toTermName(original).moduleClassName.toTermName + case SuperAccessor(accessed) => ??? + case DefaultGetter(meth, num) => ??? + } + + def toTermName(ref: NameRef): TermName = toTermName(tastyName(ref)) + def toTypeName(ref: NameRef): TypeName = toTermName(ref).toTypeName + + class Completer(reader: TastyReader) extends LazyType { + import reader._ + def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { + treeAtAddr(currentAddr) = new TreeReader(reader).readIndexedDef() + } + } + + class TreeReader(val reader: TastyReader) { + import reader._ + + def forkAt(start: Addr) = new TreeReader(subReader(start, endAddr)) + def fork = forkAt(currentAddr) + + def skipTree(tag: Int): Unit = + if (tag >= firstLengthTreeTag) goto(readEnd()) + else if (tag >= firstNatASTTreeTag) { readNat(); skipTree() } + else if (tag >= firstASTTreeTag) skipTree() + else if (tag >= firstNatTreeTag) readNat() + def skipTree(): Unit = skipTree(readByte()) + + def skipParams(): Unit = + while (nextByte == PARAMS || nextByte == TYPEPARAM) skipTree() + + /** The next tag, following through SHARED tags */ + def nextUnsharedTag: Int = { + val tag = nextByte + if (tag == SHARED) { + val lookAhead = fork + lookAhead.reader.readByte() + forkAt(lookAhead.reader.readAddr()).nextUnsharedTag + } + else tag + } + + def readName(): TermName = toTermName(readNameRef()) + + def readNameSplitSig()(implicit ctx: Context): Any /* TermName | (TermName, Signature) */ = + tastyName(readNameRef()) match { + case Signed(original, params, result) => + var sig = Signature(params map toTypeName, toTypeName(result)) + if (sig == Signature.NotAMethod) sig = Signature.NotAMethod + (toTermName(original), sig) + case name => + toTermName(name) + } + +// ------ Reading types ----------------------------------------------------- + + /** Read names in an interleaved sequence of (parameter) names and types/bounds */ + def readParamNames[N <: Name](end: Addr): List[N] = + until(end) { + val name = readName().asInstanceOf[N] + skipTree() + name + } + + /** Read types or bounds in an interleaved sequence of (parameter) names and types/bounds */ + def readParamTypes[T <: Type](end: Addr)(implicit ctx: Context): List[T] = + until(end) { readNat(); readType().asInstanceOf[T] } + + /** Read referece to definition and return symbol created at that definition */ + def readSymRef()(implicit ctx: Context): Symbol = { + val start = currentAddr + val addr = readAddr() + symAtAddr get addr match { + case Some(sym) => sym + case None => + // Create a stub; owner might be wrong but will be overwritten later. + forkAt(addr).createSymbol() + val sym = symAtAddr(addr) + ctx.log(i"forward reference to $sym") + stubs += sym + sym + } + } + + /** Read a type */ + def readType()(implicit ctx: Context): Type = { + val start = currentAddr + val tag = readByte() + pickling.println(s"reading type ${astTagToString(tag)} at $start") + + def registeringType[T](tp: Type, op: => T): T = { + typeAtAddr(start) = tp + op + } + + def readLengthType(): Type = { + val end = readEnd() + + def readNamesSkipParams[N <: Name]: (List[N], TreeReader) = { + val nameReader = fork + nameReader.skipTree() // skip result + val paramReader = nameReader.fork + (nameReader.readParamNames[N](end), paramReader) + } + + val result = + (tag: @switch) match { + case SUPERtype => + SuperType(readType(), readType()) + case REFINEDtype => + val parent = readType() + var name: Name = readName() + val ttag = nextUnsharedTag + if (ttag == TYPEBOUNDS || ttag == TYPEALIAS) name = name.toTypeName + RefinedType(parent, name, rt => registeringType(rt, readType())) + // Note that the lambda "rt => ..." is not equivalent to a wildcard closure! + // Eta expansion of the latter puts readType() out of the expression. + case APPLIEDtype => + readType().appliedTo(until(end)(readType())) + case TYPEBOUNDS => + TypeBounds(readType(), readType()) + case TYPEALIAS => + val alias = readType() + val variance = + if (nextByte == COVARIANT) { readByte(); 1 } + else if (nextByte == CONTRAVARIANT) { readByte(); -1 } + else 0 + TypeAlias(alias, variance) + case ANNOTATED => + AnnotatedType(Annotation(readTerm()), readType()) + case ANDtype => + AndType(readType(), readType()) + case ORtype => + OrType(readType(), readType()) + case BIND => + val sym = ctx.newSymbol(ctx.owner, readName().toTypeName, BindDefinedType, readType()) + symAtAddr(start) = sym + TypeRef.withFixedSym(NoPrefix, sym.name, sym) + case POLYtype => + val (names, paramReader) = readNamesSkipParams[TypeName] + val result = PolyType(names)( + pt => registeringType(pt, paramReader.readParamTypes[TypeBounds](end)), + pt => readType()) + goto(end) + result + case METHODtype => + val (names, paramReader) = readNamesSkipParams[TermName] + val result = MethodType(names, paramReader.readParamTypes[Type](end))( + mt => registeringType(mt, readType())) + goto(end) + result + case PARAMtype => + readTypeRef() match { + case binder: PolyType => PolyParam(binder, readNat()) + case binder: MethodType => MethodParam(binder, readNat()) + } + case CLASSconst => + ConstantType(Constant(readType())) + case ENUMconst => + ConstantType(Constant(readTermRef().termSymbol)) + } + assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}") + result + } + + def readSimpleType(): Type = (tag: @switch) match { + case TYPEREFdirect | TERMREFdirect => + NamedType.withFixedSym(NoPrefix, readSymRef()) + case TYPEREFsymbol | TERMREFsymbol => + readSymNameRef() + case TYPEREFpkg => + readPackageRef().moduleClass.typeRef + case TERMREFpkg => + readPackageRef().termRef + case TYPEREF => + val name = readName().toTypeName + TypeRef(readType(), name) + case TERMREF => + readNameSplitSig() match { + case name: TermName => TermRef.all(readType(), name) + case (name: TermName, sig: Signature) => TermRef.withSig(readType(), name, sig) + } + case THIS => + ThisType.raw(readType().asInstanceOf[TypeRef]) + case SKOLEMtype => + SkolemType(readTypeRef()) + case SHARED => + val ref = readAddr() + typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) + case UNITconst => + ConstantType(Constant(())) + case TRUEconst => + ConstantType(Constant(true)) + case FALSEconst => + ConstantType(Constant(false)) + case BYTEconst => + ConstantType(Constant(readInt().toByte)) + case SHORTconst => + ConstantType(Constant(readInt().toShort)) + case CHARconst => + ConstantType(Constant(readNat().toChar)) + case INTconst => + ConstantType(Constant(readInt())) + case LONGconst => + ConstantType(Constant(readLongInt())) + case FLOATconst => + ConstantType(Constant(java.lang.Float.intBitsToFloat(readInt()))) + case DOUBLEconst => + ConstantType(Constant(java.lang.Double.longBitsToDouble(readLongInt()))) + case STRINGconst => + ConstantType(Constant(readName().toString)) + case NULLconst => + ConstantType(Constant(null)) + case BYNAMEtype => + ExprType(readType()) + } + + if (tag < firstLengthTreeTag) readSimpleType() else readLengthType() + } + + private def readSymNameRef()(implicit ctx: Context): Type = { + val sym = readSymRef() + val prefix = readType() + val res = NamedType.withSymAndName(prefix, sym, sym.name) + prefix match { + case prefix: ThisType if prefix.cls eq sym.owner => res.withDenot(sym.denot) + // without this precaution we get an infinite cycle when unpickling pos/extmethods.scala + // the problem arises when a self type of a trait is a type parameter of the same trait. + case _ => res + } + } + + private def readPackageRef()(implicit ctx: Context): TermSymbol = { + val name = readName() + if (name == nme.ROOT) defn.RootPackage + else if (name == nme.EMPTY_PACKAGE) defn.EmptyPackageVal + else ctx.requiredPackage(name) + } + + def readTypeRef(): Type = + typeAtAddr(readAddr()) + + def readPath()(implicit ctx: Context): Type = { + val tp = readType() + assert(tp.isInstanceOf[SingletonType]) + tp + } + + def readTermRef()(implicit ctx: Context): TermRef = + readType().asInstanceOf[TermRef] + +// ------ Reading definitions ----------------------------------------------------- + + private def noRhs(end: Addr): Boolean = + currentAddr == end || isModifierTag(nextByte) + + private def localContext(owner: Symbol)(implicit ctx: Context) = { + val lctx = ctx.fresh.setOwner(owner) + if (owner.isClass) lctx.setScope(owner.unforcedDecls) else lctx.setNewScope + } + + private def normalizeFlags(tag: Int, givenFlags: FlagSet, name: Name, isAbstractType: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): FlagSet = { + val lacksDefinition = + rhsIsEmpty && + name.isTermName && !name.isConstructorName && !givenFlags.is(ParamOrAccessor) || + isAbstractType + var flags = givenFlags + if (lacksDefinition && tag != PARAM) flags |= Deferred + if (tag == DEFDEF) flags |= Method + if (givenFlags is Module) + flags = flags | (if (tag == VALDEF) ModuleCreationFlags else ModuleClassCreationFlags) + if (ctx.owner.isClass) { + if (tag == TYPEPARAM) flags |= Param + else if (tag == PARAM) flags |= ParamAccessor + } + else if (isParamTag(tag)) flags |= Param + flags + } + + /** Create symbol of definition node and enter in symAtAddr map + * @return true iff the definition does not contain initialization code + */ + def createSymbol()(implicit ctx: Context): Boolean = { + val start = currentAddr + val tag = readByte() + val end = readEnd() + val rawName = tastyName(readNameRef()) + var name: Name = toTermName(rawName) + if (tag == TYPEDEF || tag == TYPEPARAM) name = name.toTypeName + skipParams() + val ttag = nextUnsharedTag + val isAbstractType = ttag == TYPEBOUNDS + val isClass = ttag == TEMPLATE + val templateStart = currentAddr + skipTree() // tpt + val rhsIsEmpty = noRhs(end) + if (!rhsIsEmpty) skipTree() + val (givenFlags, annots, privateWithin) = readModifiers(end) + val expandedFlag = if (rawName.isInstanceOf[TastyName.Expanded]) ExpandedName else EmptyFlags + pickling.println(i"creating symbol $name at $start with flags $givenFlags") + val flags = normalizeFlags(tag, givenFlags | expandedFlag, name, isAbstractType, rhsIsEmpty) + def adjustIfModule(completer: LazyType) = + if (flags is Module) ctx.adjustModuleCompleter(completer, name) else completer + val sym = + roots.find(root => (root.owner eq ctx.owner) && root.name == name) match { + case Some(rootd) => + pickling.println(i"overwriting ${rootd.symbol} # ${rootd.hashCode}") + rootd.info = adjustIfModule( + new Completer(subReader(start, end)) with SymbolLoaders.SecondCompleter) + rootd.flags = flags &~ Touched // allow one more completion + rootd.privateWithin = privateWithin + rootd.symbol + case _ => + val completer = adjustIfModule(new Completer(subReader(start, end))) + if (isClass) + ctx.newClassSymbol(ctx.owner, name.asTypeName, flags, completer, + privateWithin, coord = start.index) + else { + val sym = symAtAddr.get(start) match { + case Some(preExisting) => + assert(stubs contains preExisting) + stubs -= preExisting + preExisting + case none => + ctx.newNakedSymbol(start.index) + } + val denot = ctx.SymDenotation(symbol = sym, owner = ctx.owner, name, flags, completer, privateWithin) + sym.denot = denot + sym + } + } // TODO set position + sym.annotations = annots + ctx.enter(sym) + symAtAddr(start) = sym + if (isClass) { + sym.completer.withDecls(newScope) + forkAt(templateStart).indexTemplateParams()(localContext(sym)) + } + tag != VALDEF || rhsIsEmpty + } + + /** Read modifier list into triplet of flags, annotations and a privateWithin + * boindary symbol. + */ + def readModifiers(end: Addr)(implicit ctx: Context): (FlagSet, List[Annotation], Symbol) = { + var flags: FlagSet = EmptyFlags + var annots = new mutable.ListBuffer[Annotation] + var privateWithin: Symbol = NoSymbol + while (currentAddr.index != end.index) { + def addFlag(flag: FlagSet) = { + flags |= flag + readByte() + } + nextByte match { + case PRIVATE => addFlag(Private) + case INTERNAL => ??? // addFlag(Internal) + case PROTECTED => addFlag(Protected) + case ABSTRACT => addFlag(Abstract) + case FINAL => addFlag(Final) + case SEALED => addFlag(Sealed) + case CASE => addFlag(Case) + case IMPLICIT => addFlag(Implicit) + case LAZY => addFlag(Lazy) + case OVERRIDE => addFlag(Override) + case INLINE => addFlag(Inline) + case ABSOVERRIDE => addFlag(AbsOverride) + case STATIC => addFlag(JavaStatic) + case OBJECT => addFlag(Module) + case TRAIT => addFlag(Trait) + case LOCAL => addFlag(Local) + case SYNTHETIC => addFlag(Synthetic) + case ARTIFACT => addFlag(Artifact) + case MUTABLE => addFlag(Mutable) + case LABEL => addFlag(Label) + case FIELDaccessor => addFlag(Accessor) + case CASEaccessor => addFlag(CaseAccessor) + case COVARIANT => addFlag(Covariant) + case CONTRAVARIANT => addFlag(Contravariant) + case SCALA2X => addFlag(Scala2x) + case DEFAULTparameterized => addFlag(DefaultParameterized) + case INSUPERCALL => addFlag(InSuperCall) + case PRIVATEqualified => + readByte() + privateWithin = readType().typeSymbol + case PROTECTEDqualified => + addFlag(Protected) + privateWithin = readType().typeSymbol + case ANNOTATION => + readByte() + val end = readEnd() + val sym = readType().typeSymbol + val lazyAnnotTree = readLater(end, rdr => ctx => rdr.readTerm()(ctx)) + annots += Annotation.deferred(sym, _ => lazyAnnotTree.complete) + case _ => + assert(false, s"illegal modifier tag at $currentAddr") + } + } + (flags, annots.toList, privateWithin) + } + + /** Create symbols for a definitions in statement sequence between + * current address and `end`. + * @return true iff none of the statements contains initialization code + */ + def indexStats(end: Addr)(implicit ctx: Context): Boolean = { + val noInitss = + until(end) { + nextByte match { + case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM => + createSymbol() + case IMPORT => + skipTree() + true + case PACKAGE => + processPackage { (pid, end) => implicit ctx => indexStats(end) } + case _ => + skipTree() + false + } + } + noInitss.forall(_ == true) + } + + /** Process package with given operation `op`. The operation takes as arguments + * - a `RefTree` representing the `pid` of the package, + * - an end address, + * - a context which has the processd package as owner + */ + def processPackage[T](op: (RefTree, Addr) => Context => T)(implicit ctx: Context): T = { + readByte() + val end = readEnd() + val pid = ref(readTermRef()).asInstanceOf[RefTree] + op(pid, end)(localContext(pid.symbol.moduleClass)) + } + + /** Create symbols the longest consecutive sequence of parameters with given + * `tag starting at current address. + */ + def indexParams(tag: Int)(implicit ctx: Context) = + while (nextByte == tag) createSymbol() + + /** Create symbols for all type and value parameters of template starting + * at current address. + */ + def indexTemplateParams()(implicit ctx: Context) = { + assert(readByte() == TEMPLATE) + readEnd() + indexParams(TYPEPARAM) + indexParams(PARAM) + } + + /** If definition was already read by a completer, return the previously read tree + * or else read definition. + */ + def readIndexedDef()(implicit ctx: Context): Tree = treeAtAddr.remove(currentAddr) match { + case Some(tree) => skipTree(); tree + case none => readNewDef() + } + + private def readNewDef()(implicit ctx: Context): Tree = { + val start = currentAddr + val sym = symAtAddr(start) + val tag = readByte() + val end = readEnd() + + def readParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = { + fork.indexParams(tag) + readIndexedParams(tag) + } + + def readParamss(implicit ctx: Context): List[List[ValDef]] = { + collectWhile(nextByte == PARAMS) { + readByte() + readEnd() + readParams[ValDef](PARAM) + } + } + + def readRhs(implicit ctx: Context) = + if (noRhs(end)) EmptyTree + else readLater(end, rdr => ctx => rdr.readTerm()(ctx)) + + def localCtx = localContext(sym) + + def DefDef(tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree) = + ta.assignType( + untpd.DefDef( + sym.name.asTermName, tparams, vparamss, tpt, readRhs(localCtx)), + sym) + + def ta = ctx.typeAssigner + + val name = readName() + pickling.println(s"reading def of $name at $start") + val tree: MemberDef = tag match { + case DEFDEF => + val tparams = readParams[TypeDef](TYPEPARAM)(localCtx) + val vparamss = readParamss(localCtx) + val tpt = readTpt() + val typeParams = tparams.map(_.symbol) + val valueParamss = ctx.normalizeIfConstructor( + vparamss.nestedMap(_.symbol), name == nme.CONSTRUCTOR) + val resType = ctx.effectiveResultType(sym, typeParams, tpt.tpe) + sym.info = ctx.methodType(typeParams, valueParamss, resType) + DefDef(tparams, vparamss, tpt) + case VALDEF => + sym.info = readType() + ValDef(sym.asTerm, readRhs(localCtx)) + case TYPEDEF | TYPEPARAM => + if (sym.isClass) + ta.assignType(untpd.TypeDef(sym.name.asTypeName, readTemplate(localCtx)), sym) + else { + sym.info = readType() + TypeDef(sym.asType) + } + case PARAM => + val info = readType() + if (noRhs(end)) { + sym.info = info + ValDef(sym.asTerm) + } + else { + sym.setFlag(Method) + sym.info = ExprType(info) + pickling.println(i"reading param alias $name -> $currentAddr") + DefDef(Nil, Nil, TypeTree(info)) + } + } + val mods = + if (sym.annotations.isEmpty) EmptyModifiers + else Modifiers(annotations = sym.annotations.map(_.tree)) + tree.withMods(mods) // record annotations in tree so that tree positions can be filled in. + goto(end) + setPos(start, tree) + } + + private def readTemplate(implicit ctx: Context): Template = { + val start = currentAddr + val cls = ctx.owner.asClass + def setClsInfo(parents: List[TypeRef], selfType: Type) = + cls.info = ClassInfo(cls.owner.thisType, cls, parents, cls.unforcedDecls, selfType) + setClsInfo(Nil, NoType) + val localDummy = ctx.newLocalDummy(cls) + assert(readByte() == TEMPLATE) + val end = readEnd() + val tparams = readIndexedParams[TypeDef](TYPEPARAM) + val vparams = readIndexedParams[ValDef](PARAM) + val parents = collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) { + nextByte match { + case APPLY | TYPEAPPLY => readTerm() + case _ => readTpt() + } + } + val parentRefs = ctx.normalizeToClassRefs(parents.map(_.tpe), cls, cls.unforcedDecls) + val self = + if (nextByte == SELFDEF) { + readByte() + untpd.ValDef(readName(), readTpt(), EmptyTree).withType(NoType) + } + else EmptyValDef + setClsInfo(parentRefs, if (self.isEmpty) NoType else self.tpt.tpe) + val noInits = fork.indexStats(end) + if (noInits) cls.setFlag(NoInits) + val constr = readIndexedDef().asInstanceOf[DefDef] + + def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree]): (List[Tree], List[Tree]) = + (tparams, stats) match { + case (tparam :: tparams1, (alias: TypeDef) :: stats1) + if tparam.name == alias.name.expandedName(cls) => + val (tas, stats2) = mergeTypeParamsAndAliases(tparams1, stats1) + (tparam :: alias :: tas, stats2) + case _ => + (tparams, stats) + } + + val lazyStats = readLater(end, rdr => implicit ctx => { + val stats0 = rdr.readIndexedStats(localDummy, end) + val (tparamsAndAliases, stats) = mergeTypeParamsAndAliases(tparams, stats0) + tparamsAndAliases ++ vparams ++ stats + }) + setPos(start, + untpd.Template(constr, parents, self, lazyStats) + .withType(localDummy.nonMemberTermRef)) + } + + def readIndexedStat(exprOwner: Symbol)(implicit ctx: Context): Tree = nextByte match { + case TYPEDEF | VALDEF | DEFDEF => + readIndexedDef() + case IMPORT => + readImport() + case PACKAGE => + val start = currentAddr + processPackage { (pid, end) => implicit ctx => + setPos(start, PackageDef(pid, readIndexedStats(exprOwner, end)(ctx))) + } + case _ => + readTerm()(ctx.withOwner(exprOwner)) + } + + def readImport()(implicit ctx: Context): Tree = { + readByte() + readEnd() + val expr = readTerm() + def readSelectors(): List[untpd.Tree] = nextByte match { + case RENAMED => + readByte() + readEnd() + untpd.Pair(untpd.Ident(readName()), untpd.Ident(readName())) :: readSelectors() + case IMPORTED => + readByte() + untpd.Ident(readName()) :: readSelectors() + case _ => + Nil + } + Import(expr, readSelectors()) + } + + def readIndexedStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = + until(end)(readIndexedStat(exprOwner)) + + def readStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = { + fork.indexStats(end) + readIndexedStats(exprOwner, end) + } + + def readIndexedParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = + collectWhile(nextByte == tag) { readIndexedDef().asInstanceOf[T] } + +// ------ Reading terms ----------------------------------------------------- + + def readTerm()(implicit ctx: Context): Tree = { + val start = currentAddr + val tag = readByte() + pickling.println(s"reading term ${astTagToString(tag)} at $start") + + def readPathTerm(): Tree = { + goto(start) + readPath() match { + case path: TermRef => ref(path) + case path: ThisType => This(path.cls) + case path: ConstantType => Literal(path.value) + } + } + + def readSimpleTerm(): Tree = tag match { + case IDENT => + untpd.Ident(readName()).withType(readType()) + case SELECT => + def readQual(name: Name) = { + val localCtx = + if (name == nme.CONSTRUCTOR) ctx.fresh.addMode(Mode.InSuperCall) else ctx + readTerm()(localCtx) + } + def readRest(name: Name, sig: Signature) = { + val unshadowed = if (name.isShadowedName) name.revertShadowed else name + val qual = readQual(name) + untpd.Select(qual, unshadowed) + .withType(TermRef.withSig(qual.tpe.widenIfUnstable, name.asTermName, sig)) + } + readNameSplitSig match { + case name: Name => readRest(name, Signature.NotAMethod) + case (name: Name, sig: Signature) => readRest(name, sig) + } + + case NEW => + New(readTpt()) + case _ => + readPathTerm() + } + + def readLengthTerm(): Tree = { + val end = readEnd() + + val result = + (tag: @switch) match { + case SUPER => + val qual = readTerm() + val mixClass = ifBefore(end)(readType().typeSymbol, NoSymbol) + val mixName = if (mixClass.exists) mixClass.name.asTypeName else tpnme.EMPTY + tpd.Super(qual, mixName, ctx.mode.is(Mode.InSuperCall), mixClass) + case APPLY => + val fn = readTerm() + val isJava = fn.tpe.isInstanceOf[JavaMethodType] + def readArg() = readTerm() match { + case SeqLiteral(elems) if isJava => JavaSeqLiteral(elems) + case arg => arg + } + tpd.Apply(fn, until(end)(readArg())) + case TYPEAPPLY => + tpd.TypeApply(readTerm(), until(end)(readTpt())) + case PAIR => + Pair(readTerm(), readTerm()) + case TYPED => + Typed(readTerm(), readTpt()) + case NAMEDARG => + NamedArg(readName(), readTerm()) + case ASSIGN => + Assign(readTerm(), readTerm()) + case BLOCK => + val exprReader = fork + skipTree() + val localCtx = ctx.fresh.setNewScope + val stats = readStats(ctx.owner, end)(localCtx) + val expr = exprReader.readTerm()(localCtx) + Block(stats, expr) + case IF => + If(readTerm(), readTerm(), readTerm()) + case LAMBDA => + val meth = readTerm() + val tpt = ifBefore(end)(readTpt(), EmptyTree) + Closure(Nil, meth, tpt) + case MATCH => + Match(readTerm(), readCases(end)) + case RETURN => + val from = readSymRef() + val expr = ifBefore(end)(readTerm(), EmptyTree) + Return(expr, Ident(from.termRef)) + case TRY => + Try(readTerm(), readCases(end), ifBefore(end)(readTerm(), EmptyTree)) + case REPEATED => + SeqLiteral(until(end)(readTerm())) + case BIND => + val name = readName() + val info = readType() + val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, info) + symAtAddr(start) = sym + Bind(sym, readTerm()) + case ALTERNATIVE => + Alternative(until(end)(readTerm())) + case UNAPPLY => + val fn = readTerm() + val implicitArgs = + collectWhile(nextByte == IMPLICITarg) { + readByte() + readTerm() + } + val patType = readType() + val argPats = until(end)(readTerm()) + UnApply(fn, implicitArgs, argPats, patType) + case _ => + readPathTerm() + } + assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}") + result + } + + val tree = if (tag < firstLengthTreeTag) readSimpleTerm() else readLengthTerm() + tree.overwriteType(tree.tpe.simplified) + setPos(start, tree) + } + + def readTpt()(implicit ctx: Context) = { + val start = currentAddr + val tp = readType() + if (tp.exists) setPos(start, TypeTree(tp)) else EmptyTree + } + + def readCases(end: Addr)(implicit ctx: Context): List[CaseDef] = + collectWhile(nextByte == CASEDEF && currentAddr != end) { readCase()(ctx.fresh.setNewScope) } + + def readCase()(implicit ctx: Context): CaseDef = { + val start = currentAddr + readByte() + val end = readEnd() + val pat = readTerm() + val rhs = readTerm() + val guard = ifBefore(end)(readTerm(), EmptyTree) + setPos(start, CaseDef(pat, guard, rhs)) + } + + def readLater[T <: AnyRef](end: Addr, op: TreeReader => Context => T): Trees.Lazy[T] = { + val localReader = fork + goto(end) + new LazyReader(localReader, op) + } + +// ------ Hooks for positions ------------------------------------------------ + + /** Record address from which tree was created as a temporary position in the tree. + * The temporary position contains deltas relative to the position of the (as yet unknown) + * parent node. It is marked as a non-synthetic source position. + */ + def setPos[T <: Tree](addr: Addr, tree: T): T = { + if (readPositions) + tree.setPosUnchecked(positions.getOrElse(addr, Position(0, 0, 0))) + tree + } + } + + private def setNormalized(tree: Tree, parentPos: Position): Unit = + tree.setPosUnchecked( + if (tree.pos.exists) + Position(parentPos.start + offsetToInt(tree.pos.start), parentPos.end - tree.pos.end) + else + parentPos) + + def normalizePos(x: Any, parentPos: Position)(implicit ctx: Context): Unit = + traverse(x, parentPos, setNormalized) + + class LazyReader[T <: AnyRef](reader: TreeReader, op: TreeReader => Context => T) extends Trees.Lazy[T] with DeferredPosition { + def complete(implicit ctx: Context): T = { + pickling.println(i"starting to read at ${reader.reader.currentAddr}") + val res = op(reader)(ctx.addMode(Mode.AllowDependentFunctions)) + normalizePos(res, parentPos) + res + } + } + + class LazyAnnotationReader(sym: Symbol, reader: TreeReader) + extends LazyAnnotation(sym) with DeferredPosition { + def complete(implicit ctx: Context) = { + val res = reader.readTerm() + normalizePos(res, parentPos) + res + } + } +} diff --git a/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala b/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala new file mode 100644 index 000000000..aa1fd9a90 --- /dev/null +++ b/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala @@ -0,0 +1,299 @@ +package dotty.tools +package dotc +package core +package unpickleScala2 + +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(): Unit = { + 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): Unit = { + 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): Unit = + 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): Unit = { + def writeNatPrefix(x: Long): Unit = { + 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 x at position pos. + * If number is more than one byte, shift rest of array to make space. + * + * @param pos ... + * @param x ... + */ + def patchNat(pos: Int, x: Int): Unit = { + def patchNatPrefix(x: Int): Unit = { + 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 x in signed big endian format, base 256. + * + * @param x The long number to be written. + */ + def writeLong(x: Long): Unit = { + 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 op until the condition + * readIndex == end 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 op 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) + + /** 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 + } +} + +object PickleBuffer { + + private final val ScalaFlagEnd = 48 + private final val ChunkBits = 8 + private final val ChunkSize = 1 << ChunkBits + private type FlagMap = Array[Array[Long]] + + private val (scalaTermFlagMap, scalaTypeFlagMap) = { + import scala.reflect.internal.Flags._ + + // The following vals are copy-pasted from reflect.internal.Flags. + // They are unfortunately private there, so we cannot get at them directly. + // Using the public method pickledToRawFlags instead looks unattractive + // because of performance. + val IMPLICIT_PKL = (1 << 0) + val FINAL_PKL = (1 << 1) + val PRIVATE_PKL = (1 << 2) + val PROTECTED_PKL = (1 << 3) + val SEALED_PKL = (1 << 4) + val OVERRIDE_PKL = (1 << 5) + val CASE_PKL = (1 << 6) + val ABSTRACT_PKL = (1 << 7) + val DEFERRED_PKL = (1 << 8) + val METHOD_PKL = (1 << 9) + val MODULE_PKL = (1 << 10) + val INTERFACE_PKL = (1 << 11) + + val corr = Map( + PROTECTED_PKL -> Protected, + OVERRIDE_PKL -> Override, + PRIVATE_PKL -> Private, + ABSTRACT_PKL -> Abstract, + DEFERRED_PKL -> Deferred, + FINAL_PKL -> Final, + METHOD_PKL -> Method, + INTERFACE_PKL -> PureInterface, + MODULE_PKL -> Module, + IMPLICIT_PKL -> Implicit, + SEALED_PKL -> Sealed, + CASE_PKL -> Case, + MUTABLE -> Mutable, + PARAM -> Param, + PACKAGE -> Package, + MACRO -> Macro, + BYNAMEPARAM -> (Method, Covariant), + LABEL -> (Label, Contravariant), + ABSOVERRIDE -> AbsOverride, + LOCAL -> Local, + JAVA -> JavaDefined, + SYNTHETIC -> Synthetic, + STABLE -> Stable, + STATIC -> JavaStatic, + CASEACCESSOR -> CaseAccessor, + DEFAULTPARAM -> (DefaultParameterized, Trait), + BRIDGE -> Bridge, + ACCESSOR -> Accessor, + SUPERACCESSOR -> SuperAccessor, + PARAMACCESSOR -> ParamAccessor, + MODULEVAR -> Scala2ModuleVar, + LAZY -> Lazy, + MIXEDIN -> (MixedIn, Scala2Existential), + EXPANDEDNAME -> ExpandedName, + IMPLCLASS -> (Scala2PreSuper, ImplClass), + SPECIALIZED -> Specialized, + VBRIDGE -> VBridge, + VARARGS -> JavaVarargs, + ENUM -> Enum) + + // generate initial maps from Scala flags to Dotty flags + val termMap, typeMap = new Array[Long](64) + for (idx <- 0 until ScalaFlagEnd) + corr get (1L << idx) match { + case Some((termFlag: FlagSet, typeFlag: FlagSet)) => + termMap(idx) |= termFlag.bits + typeMap(idx) |= typeFlag.bits + case Some(commonFlag: FlagSet) => + termMap(idx) |= commonFlag.toTermFlags.bits + typeMap(idx) |= commonFlag.toTypeFlags.bits + case _ => + } + + // Convert map so that it maps chunks of ChunkBits size at once + // instead of single bits. + def chunkMap(xs: Array[Long]): FlagMap = { + val chunked = Array.ofDim[Long]( + (xs.length + ChunkBits - 1) / ChunkBits, ChunkSize) + for (i <- 0 until chunked.length) + for (j <- 0 until ChunkSize) + for (k <- 0 until ChunkBits) + if ((j & (1 << k)) != 0) + chunked(i)(j) |= xs(i * ChunkBits + k) + chunked + } + + (chunkMap(termMap), chunkMap(typeMap)) + } + + def unpickleScalaFlags(sflags: Long, isType: Boolean): FlagSet = { + val map: FlagMap = if (isType) scalaTypeFlagMap else scalaTermFlagMap + val shift = ChunkBits + val mask = ChunkSize - 1 + assert(6 * ChunkBits == ScalaFlagEnd) + FlagSet( + map(0)((sflags >>> (shift * 0)).toInt & mask) | + map(1)((sflags >>> (shift * 1)).toInt & mask) | + map(2)((sflags >>> (shift * 2)).toInt & mask) | + map(3)((sflags >>> (shift * 3)).toInt & mask) | + map(4)((sflags >>> (shift * 4)).toInt & mask) | + map(5)((sflags >>> (shift * 5)).toInt & mask) + ) + } +} diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala new file mode 100644 index 000000000..56c1d90df --- /dev/null +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -0,0 +1,1217 @@ +package dotty.tools +package dotc +package core +package unpickleScala2 + +import java.io.IOException +import java.lang.Float.intBitsToFloat +import java.lang.Double.longBitsToDouble + +import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._ +import StdNames._, Denotations._, NameOps._, Flags._, Constants._, Annotations._ +import dotty.tools.dotc.typer.ProtoTypes.{FunProtoTyped, FunProto} +import util.Positions._ +import dotty.tools.dotc.ast.{tpd, Trees, untpd}, ast.tpd._ +import printing.Texts._ +import printing.Printer +import io.AbstractFile +import util.common._ +import typer.Checking.checkNonCyclic +import PickleBuffer._ +import scala.reflect.internal.pickling.PickleFormat._ +import Decorators._ +import classfile.ClassfileParser +import scala.collection.{ mutable, immutable } +import scala.collection.mutable.ListBuffer +import scala.annotation.switch + +object Scala2Unpickler { + + /** Exception thrown if classfile is corrupted */ + class BadSignature(msg: String) extends RuntimeException(msg) + + case class TempPolyType(tparams: List[Symbol], tpe: Type) extends UncachedGroundType { + override def fallbackToText(printer: Printer): Text = + "[" ~ printer.dclsText(tparams, ", ") ~ "]" ~ printer.toText(tpe) + } + + /** 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 temp poly type to some native Dotty idiom. + * @param denot The denotation that gets the converted type as info. + * If `denot` is not an abstract type, this simply returns an equivalent `PolyType`. + * If `denot` is an abstract type, it converts a + * + * TempPolyType(List(v_1 T_1, ..., v_n T_n), lo .. hi) + * + * to a type lambda using `parameterizeWith/LambdaAbstract`. + */ + def depoly(tp: Type, denot: SymDenotation)(implicit ctx: Context): Type = tp match { + case TempPolyType(tparams, restpe) => + if (denot.isAbstractType) + restpe.LambdaAbstract(tparams) // bounds needed? + else if (denot.isAliasType) { + var err: Option[(String, Position)] = None + val result = restpe.parameterizeWith(tparams) + for ((msg, pos) <- err) + ctx.warning( + sm"""$msg + |originally parsed type : ${tp.show} + |will be approximated by: ${result.show}. + |Proceed at own risk.""") + result + } + else + PolyType.fromSymbols(tparams, restpe) + case tp => tp + } + + def addConstructorTypeParams(denot: SymDenotation)(implicit ctx: Context) = { + assert(denot.isConstructor) + denot.info = PolyType.fromSymbols(denot.owner.typeParams, denot.info) + } + + /** Convert array parameters denoting a repeated parameter of a Java method + * to `RepeatedParamClass` types. + */ + def arrayToRepeated(tp: Type)(implicit ctx: Context): Type = tp match { + case tp @ MethodType(paramNames, paramTypes) => + val lastArg = paramTypes.last + assert(lastArg isRef defn.ArrayClass) + val elemtp0 :: Nil = lastArg.baseArgInfos(defn.ArrayClass) + val elemtp = elemtp0 match { + case AndType(t1, t2) if t1.typeSymbol.isAbstractType && (t2 isRef defn.ObjectClass) => + t1 // drop intersection with Object for abstract types in varargs. UnCurry can handle them. + case _ => + elemtp0 + } + tp.derivedMethodType( + paramNames, + paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp), + tp.resultType) + case tp @ PolyType(paramNames) => + tp.derivedPolyType(paramNames, tp.paramBounds, arrayToRepeated(tp.resultType)) + } + + def ensureConstructor(cls: ClassSymbol, scope: Scope)(implicit ctx: Context) = + if (scope.lookup(nme.CONSTRUCTOR) == NoSymbol) { + val constr = ctx.newDefaultConstructor(cls) + addConstructorTypeParams(constr) + cls.enter(constr, scope) + } + + def setClassInfo(denot: ClassDenotation, info: Type, selfInfo: Type = NoType)(implicit ctx: Context): Unit = { + val cls = denot.classSymbol + val (tparams, TempClassInfoType(parents, decls, clazz)) = info match { + case TempPolyType(tps, cinfo) => (tps, cinfo) + case cinfo => (Nil, cinfo) + } + var parentRefs = ctx.normalizeToClassRefs(parents, cls, decls) + if (parentRefs.isEmpty) parentRefs = defn.ObjectClass.typeRef :: Nil + for (tparam <- tparams) { + val tsym = decls.lookup(tparam.name) + if (tsym.exists) tsym.setFlag(TypeParam) + else denot.enter(tparam, decls) + } + val ost = + if ((selfInfo eq NoType) && (denot is ModuleClass)) + denot.owner.thisType select denot.sourceModule + else selfInfo + if (!(denot.flagsUNSAFE is JavaModule)) ensureConstructor(denot.symbol.asClass, decls) + + val scalacCompanion = denot.classSymbol.scalacLinkedClass + + def registerCompanionPair(module: Symbol, claz: Symbol) = { + val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, module) + if (companionClassMethod.exists) + companionClassMethod.entered + val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, module, claz) + if (companionModuleMethod.exists) + companionModuleMethod.entered + } + + if (denot.flagsUNSAFE is Module) { + registerCompanionPair(denot.classSymbol, scalacCompanion) + } else { + registerCompanionPair(scalacCompanion, denot.classSymbol) + } + + denot.info = ClassInfo(denot.owner.thisType, denot.classSymbol, parentRefs, decls, ost) + } +} + +/** 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 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 Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: ClassDenotation)(ictx: Context) + extends PickleBuffer(bytes, 0, -1) with ClassfileParser.Embedded { + + def showPickled() = { + atReadPos(0, () => { + println(s"classRoot = ${classRoot.debugString}, moduleClassRoot = ${moduleClassRoot.debugString}") + util.ShowPickled.printFile(this) + }) + } + + // print("unpickling "); showPickled() // !!! DEBUG + + import Scala2Unpickler._ + + val moduleRoot = moduleClassRoot.sourceModule(ictx).denot(ictx) + assert(moduleRoot.isTerm) + + checkVersion(ictx) + + private val loadingMirror = defn(ictx) // 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.AnyRefMap[Symbol, Scope]() + + protected def errorBadSignature(msg: String, original: Option[RuntimeException] = None)(implicit ctx: Context) = { + val ex = new BadSignature( + sm"""error reading Scala signature of $classRoot from $source: + |error occured at position $readIndex: $msg""") + /*if (debug)*/ original.getOrElse(ex).printStackTrace() // !!! DEBUG + throw ex + } + + protected def handleRuntimeException(ex: RuntimeException)(implicit ctx: Context) = ex match { + case ex: BadSignature => throw ex + case _ => errorBadSignature(s"a runtime exception occured: $ex", Some(ex)) + } + + private var postReadOp: Context => Unit = null + + def run()(implicit ctx: Context) = + try { + var i = 0 + while (i < index.length) { + if (entries(i) == null && isSymbolEntry(i)) { + val savedIndex = readIndex + readIndex = index(i) + entries(i) = readSymbol() + if (postReadOp != null) { + postReadOp(ctx) + postReadOp = null + } + 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 + } + } catch { + case ex: RuntimeException => handleRuntimeException(ex) + } + + def source(implicit ctx: Context): AbstractFile = { + val f = classRoot.symbol.associatedFile + if (f != null) f else moduleClassRoot.symbol.associatedFile + } + + private def checkVersion(implicit ctx: Context): Unit = { + 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) + } + + /** 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)(implicit ctx: Context): 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)(implicit ctx: Context): 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)(implicit ctx: Context): Boolean = + sym.name == tpnme.REFINE_CLASS + + protected def isLocal(sym: Symbol)(implicit ctx: Context) = isUnpickleRoot(sym.topLevelClass) + + protected def isUnpickleRoot(sym: Symbol)(implicit ctx: Context) = { + val d = sym.denot + d == moduleRoot || d == moduleClassRoot || d == classRoot + } + + /** If entry at i is undefined, define it by performing + * operation op with readIndex at start of i'th + * entry. Restore readIndex 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] + } + + protected def atReadPos[T](start: Int, op: () => T): T = { + val savedIndex = readIndex + readIndex = start + try op() + finally readIndex = savedIndex + } + + /** Read a name */ + protected def readName()(implicit ctx: Context): 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()(implicit ctx: Context): TermName = readName().toTermName + protected def readTypeName()(implicit ctx: Context): TypeName = readName().toTypeName + + /** Read a symbol */ + protected def readSymbol()(implicit ctx: Context): Symbol = readDisambiguatedSymbol(alwaysTrue)() + + /** Read a symbol, with possible disambiguation */ + protected def readDisambiguatedSymbol(p: Symbol => Boolean)()(implicit ctx: Context): Symbol = { + val start = indexCoord(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(d => p(d.symbol)) + val sym = denot1.symbol + if (denot.exists && !denot1.exists) { // !!!DEBUG + val alts = denot.alternatives map (d => d + ":" + d.info + "/" + d.signature) + System.err.println(s"!!! disambiguation failure: $alts") + val members = denot.alternatives.head.symbol.owner.info.decls.toList map (d => d + ":" + d.info + "/" + d.signature) + System.err.println(s"!!! all members: $members") + } + 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 _ => + def declIn(owner: Symbol) = adjust(owner.info.decl(name)) + val sym = declIn(owner) + if (sym.exists || owner.ne(defn.ObjectClass)) sym else declIn(defn.AnyClass) + } + + 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 module = owner.info.decl(name.toTermName).suchThat(_ is Module) + module.info // force it, as completer does not yet point to module class. + module.symbol.moduleClass + + /* was: + val moduleVar = owner.info.decl(name.toTermName.moduleVarName).symbol + if (moduleVar.isLazyAccessor) + return moduleVar.lazyAccessor.lazyAccessor + */ + } else NoSymbol + } + + // println(s"read ext symbol $name from ${owner.denot.debugString} in ${classRoot.debugString}") // !!! DEBUG + + // (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(ctx.base.missingHook(owner, name)) orElse { + // println(owner.info.decls.toList.map(_.debugString).mkString("\n ")) // !!! DEBUG + // } + // (5) Create a stub symbol to defer hard failure a little longer. + ctx.newStubSymbol(owner, name, source) + } + } + } + } + } + + tag match { + case NONEsym => return NoSymbol + case EXTref | EXTMODCLASSref => return readExtSymbol() + case _ => + } + + // symbols that were pickled with Pickler.writeSymInfo + val nameref = readNat() + val name0 = at(nameref, readName) + val owner = readSymbolRef() + + var flags = unpickleScalaFlags(readLongNat(), name0.isTypeName) + if (flags is DefaultParameter) { + // DefaultParameterized flag now on method, not parameter + //assert(flags is Param, s"$name0 in $owner") + flags = flags &~ DefaultParameterized + owner.setFlag(DefaultParameterized) + } + + val name1 = name0.adjustIfModuleClass(flags) + val name = if (name1 == nme.IMPLCLASS_CONSTRUCTOR) nme.CONSTRUCTOR else name1 + + def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner) && !(flags is ModuleClass) + def isModuleClassRoot = (name == moduleClassRoot.name) && (owner == moduleClassRoot.owner) && (flags is Module) + def isModuleRoot = (name == moduleClassRoot.name.sourceModuleName) && (owner == moduleClassRoot.owner) && (flags is Module) + + //if (isClassRoot) println(s"classRoot of $classRoot found at $readIndex, flags = $flags") // !!! DEBUG + //if (isModuleRoot) println(s"moduleRoot of $moduleRoot found at $readIndex, flags = $flags") // !!! DEBUG + //if (isModuleClassRoot) println(s"moduleClassRoot of $moduleClassRoot found at $readIndex, flags = $flags") // !!! DEBUG + + def completeRoot(denot: ClassDenotation, completer: LazyType): Symbol = { + denot.setFlag(flags) + denot.resetFlag(Touched) // allow one more completion + denot.info = completer + denot.symbol + } + + def finishSym(sym: Symbol): Symbol = { + val owner = sym.owner + if (owner.isClass && + !( isUnpickleRoot(sym) + || (sym is Scala2Existential) + || isRefinementClass(sym) + ) + ) + owner.asClass.enter(sym, symScope(owner)) + else if (isRefinementClass(owner)) + symScope(owner).openForMutations.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 |= owner.typeParamCreationFlags | ExpandedName + } + ctx.newSymbol(owner, name1, flags1, localMemberUnpickler, coord = start) + case CLASSsym => + val infoRef = readNat() + postReadOp = implicit ctx => atReadPos(index(infoRef), readTypeParams) // force reading type params early, so they get entered in the right order. + if (isClassRoot) + completeRoot( + classRoot, rootClassUnpickler(start, classRoot.symbol, NoSymbol)) + else if (isModuleClassRoot) + completeRoot( + moduleClassRoot, rootClassUnpickler(start, moduleClassRoot.symbol, moduleClassRoot.sourceModule)) + else if (name == tpnme.REFINE_CLASS) + // create a type alias instead + ctx.newSymbol(owner, name, flags, localMemberUnpickler, coord = start) + else { + def completer(cls: Symbol) = { + val unpickler = new LocalUnpickler() withDecls symScope(cls) + if (flags is ModuleClass) + unpickler withSourceModule (implicit ctx => + cls.owner.info.decls.lookup(cls.name.sourceModuleName) + .suchThat(_ is Module).symbol) + else unpickler + } + ctx.newClassSymbol(owner, name.asTypeName, flags, completer, coord = start) + } + case MODULEsym | VALsym => + if (isModuleRoot) { + moduleRoot setFlag flags + moduleRoot.symbol + } else ctx.newSymbol(owner, name.asTermName, flags, + new LocalUnpickler() withModuleClass(implicit ctx => + owner.info.decls.lookup(name.moduleClassName) + .suchThat(_ is Module).symbol) + , coord = start) + case _ => + errorBadSignature("bad symbol tag: " + tag) + }) + } + + class LocalUnpickler extends LazyType { + def startCoord(denot: SymDenotation): Coord = denot.symbol.coord + def complete(denot: SymDenotation)(implicit ctx: Context): Unit = try { + def parseToCompletion(denot: SymDenotation) = { + 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 + } + // println("reading type for " + denot) // !!! DEBUG + val tp = at(inforef, readType) + denot match { + case denot: ClassDenotation => + val selfInfo = if (atEnd) NoType else readTypeRef() + setClassInfo(denot, tp, selfInfo) + denot setFlag Scala2x + case denot => + val tp1 = depoly(tp, denot) + denot.info = + if (tag == ALIASsym) TypeAlias(tp1) + else if (denot.isType) checkNonCyclic(denot.symbol, tp1, reportErrors = false) + // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles + else tp1 + if (denot.isConstructor) addConstructorTypeParams(denot) + if (atEnd) { + assert(!(denot is SuperAccessor), denot) + } else { + assert(denot is (SuperAccessor | ParamAccessor), denot) + def disambiguate(alt: Symbol) = { // !!! DEBUG + ctx.debugTraceIndented(s"disambiguating ${denot.info} =:= ${denot.owner.thisType.memberInfo(alt)} ${denot.owner}") { + denot.info matches denot.owner.thisType.memberInfo(alt) + } + } + val alias = readDisambiguatedSymbolRef(disambiguate).asTerm + denot.addAnnotation(Annotation.makeAlias(alias)) + } + } + // println(s"unpickled ${denot.debugString}, info = ${denot.info}") !!! DEBUG + } + atReadPos(startCoord(denot).toIndex, () => parseToCompletion(denot)) + } catch { + case ex: RuntimeException => handleRuntimeException(ex) + } + } + + object localMemberUnpickler extends LocalUnpickler + + def rootClassUnpickler(start: Coord, cls: Symbol, module: Symbol) = + (new LocalUnpickler with SymbolLoaders.SecondCompleter { + override def startCoord(denot: SymDenotation): Coord = start + }) withDecls symScope(cls) withSourceModule (_ => module) + + /** 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)(implicit ctx: Context): Type = { + def removeSingleton(tp: Type): Type = + if (tp isRef defn.SingletonClass) defn.AnyType else tp + def elim(tp: Type): Type = tp match { + case tp @ RefinedType(parent, name) => + val parent1 = elim(tp.parent) + tp.refinedInfo match { + case TypeAlias(info: TypeRef) if boundSyms contains info.symbol => + RefinedType(parent1, name, info.symbol.info) + case info: TypeRef if boundSyms contains info.symbol => + val info1 = info.symbol.info + assert(info1.derivesFrom(defn.SingletonClass)) + RefinedType(parent1, name, info1.mapReduceAnd(removeSingleton)(_ & _)) + case info => + tp.derivedRefinedType(parent1, name, info) + } + case tp @ TypeRef(pre, tpnme.Apply) if pre.isLambda => + elim(pre) + case _ => + tp + } + val tp1 = elim(tp) + val isBound = (tp: Type) => boundSyms contains tp.typeSymbol + if (tp1 existsPart isBound) { + val anyTypes = boundSyms map (_ => defn.AnyType) + val boundBounds = boundSyms map (_.info.bounds.hi) + val tp2 = tp1.subst(boundSyms, boundBounds).subst(boundSyms, anyTypes) + ctx.warning(s"""failure to eliminate existential + |original type : $tp forSome {${ctx.dclsText(boundSyms, "; ").show} + |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()(implicit ctx: Context): Type = { + val tag = readByte() + val end = readNat() + readIndex + (tag: @switch) match { + case NOtpe => + NoType + case NOPREFIXtpe => + NoPrefix + case THIStpe => + readSymbolRef().thisType + case SINGLEtpe => + val pre = readTypeRef() + val sym = readDisambiguatedSymbolRef(_.info.isParameterless) + if (isLocal(sym) || (pre == NoPrefix)) pre select sym + else TermRef.withSig(pre, sym.name.asTermName, Signature.NotAMethod) // !!! should become redundant + case SUPERtpe => + val thistpe = readTypeRef() + val supertpe = readTypeRef() + SuperType(thistpe, supertpe) + case CONSTANTtpe => + ConstantType(readConstantRef()) + case TYPEREFtpe => + var pre = readTypeRef() + val sym = readSymbolRef() + pre match { + case thispre: ThisType => + // The problem is that class references super.C get pickled as + // this.C. Dereferencing the member might then get an overriding class + // instance. The problem arises for instance for LinkedHashMap#MapValues + // and also for the inner Transform class in all views. We fix it by + // replacing the this with the appropriate super. + if (sym.owner != thispre.cls) { + val overriding = thispre.cls.info.decls.lookup(sym.name) + if (overriding.exists && overriding != sym) { + val base = pre.baseTypeWithArgs(sym.owner) + assert(base.exists) + pre = SuperType(thispre, base) + } + } + case _ => + } + val tycon = + if (isLocal(sym) || pre == NoPrefix) { + val pre1 = if ((pre eq NoPrefix) && (sym is TypeParam)) sym.owner.thisType else pre + pre1 select sym + } + else TypeRef(pre, sym.name.asTypeName) + val args = until(end, readTypeRef) + if (sym == defn.ByNameParamClass2x) ExprType(args.head) + else tycon.appliedTo(args) + 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(AndType(_, _)) + if (decls.isEmpty) parent + else { + def addRefinement(tp: Type, sym: Symbol) = { + def subst(info: Type, rt: RefinedType) = + if (clazz.isClass) info.substThis(clazz.asClass, SkolemType(rt)) + else info // turns out some symbols read into `clazz` are not classes, not sure why this is the case. + RefinedType(tp, sym.name, subst(sym.info, _)) + } + (parent /: decls.toList)(addRefinement).asInstanceOf[RefinedType] + } + case CLASSINFOtpe => + val clazz = readSymbolRef() + TempClassInfoType(until(end, readTypeRef), symScope(clazz), clazz) + case METHODtpe | IMPLICITMETHODtpe => + val restpe = readTypeRef() + val params = until(end, readSymbolRef) + def isImplicit = + tag == IMPLICITMETHODtpe || + params.nonEmpty && (params.head is Implicit) + val maker = if (isImplicit) ImplicitMethodType else MethodType + maker.fromSymbols(params, restpe) + case POLYtpe => + val restpe = readTypeRef() + val typeParams = until(end, readSymbolRef) + if (typeParams.nonEmpty) TempPolyType(typeParams, restpe.widenExpr) + 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.make(annots, tp) + case _ => + noSuchTypeTag(tag, end) + } + } + + def readTypeParams()(implicit ctx: Context): List[Symbol] = { + val tag = readByte() + val end = readNat() + readIndex + if (tag == POLYtpe) { + val unusedRestperef = readNat() + until(end, readSymbolRef) + } else Nil + } + + def noSuchTypeTag(tag: Int, end: Int)(implicit ctx: Context): Type = + errorBadSignature("bad type tag: " + tag) + + /** Read a constant */ + protected def readConstant()(implicit ctx: Context): 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)(implicit ctx: Context): Constant = + errorBadSignature("bad constant tag: " + tag) + + /** Read children and store them into the corresponding symbol. + */ + protected def readChildren()(implicit ctx: Context): Unit = { + 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()(implicit ctx: Context): 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 readDisambiguatedSymbolRef(p: Symbol => Boolean)(implicit ctx: Context): Symbol = + at(readNat(), readDisambiguatedSymbol(p)) + + protected def readNameRef()(implicit ctx: Context): Name = at(readNat(), readName) + protected def readTypeRef()(implicit ctx: Context): Type = at(readNat(), () => readType()) // after the NMT_TRANSITION period, we can leave off the () => ... () + protected def readConstantRef()(implicit ctx: Context): Constant = at(readNat(), readConstant) + + protected def readTypeNameRef()(implicit ctx: Context): TypeName = readNameRef().toTypeName + protected def readTermNameRef()(implicit ctx: Context): TermName = readNameRef().toTermName + + protected def readAnnotationRef()(implicit ctx: Context): Annotation = at(readNat(), readAnnotation) + + protected def readModifiersRef(isType: Boolean)(implicit ctx: Context): Modifiers = at(readNat(), () => readModifiers(isType)) + protected def readTreeRef()(implicit ctx: Context): Tree = at(readNat(), readTree) + + /** Read an annotation argument, which is pickled either + * as a Constant or a Tree. + */ + protected def readAnnotArg(i: Int)(implicit ctx: Context): Tree = bytes(index(i)) match { + case TREE => at(i, readTree) + case _ => Literal(at(i, readConstant)) + } + + /** Read a ClassfileAnnotArg (argument to a classfile annotation) + */ + private def readArrayAnnotArg()(implicit ctx: Context): Tree = { + readByte() // skip the `annotargarray` tag + val end = readNat() + readIndex + // array elements are trees representing instances of scala.annotation.Annotation + SeqLiteral( + defn.SeqType.appliedTo(defn.AnnotationClass.typeRef :: Nil), + until(end, () => readClassfileAnnotArg(readNat()))) + } + + private def readAnnotInfoArg()(implicit ctx: Context): Tree = { + readByte() // skip the `annotinfo` tag + val end = readNat() + readIndex + readAnnotationContents(end) + } + + protected def readClassfileAnnotArg(i: Int)(implicit ctx: Context): Tree = 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)(implicit ctx: Context): Tree = { + val atp = readTypeRef() + val args = { + val t = new ListBuffer[Tree] + + while (readIndex != end) { + val argref = readNat() + t += { + if (isNameEntry(argref)) { + val name = at(argref, readName) + val arg = readClassfileAnnotArg(readNat()) + NamedArg(name.asTermName, arg) + } else readAnnotArg(argref) + } + } + t.toList + } + // println(atp) + val targs = atp.argTypes + + tpd.applyOverloaded(tpd.New(atp withoutArgs targs), nme.CONSTRUCTOR, args, targs, atp) +} + + /** 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()(implicit ctx: Context): Unit = { + val tag = readByte() + if (tag != SYMANNOT) + errorBadSignature("symbol annotation expected (" + tag + ")") + val end = readNat() + readIndex + val target = readSymbolRef() + target.addAnnotation(deferredAnnot(end)) + } + + /** Read an annotation and return it. Used when unpickling + * an ANNOTATED(WSELF)tpe or a NestedAnnotArg + */ + protected def readAnnotation()(implicit ctx: Context): Annotation = { + val tag = readByte() + if (tag != ANNOTINFO) + errorBadSignature("annotation expected (" + tag + ")") + val end = readNat() + readIndex + deferredAnnot(end) + } + + /** A deferred annotation that can be comleted by reading + * the bytes between `readIndex` and `end`. + */ + protected def deferredAnnot(end: Int)(implicit ctx: Context): Annotation = { + val start = readIndex + val atp = readTypeRef() + Annotation.deferred( + atp.typeSymbol, implicit ctx => atReadPos(start, () => readAnnotationContents(end))) + } + + /* Read an abstract syntax tree */ + protected def readTree()(implicit ctx: Context): 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(): Unit = { + symbol = readSymbolRef() + mods = readModifiersRef(symbol.isType) + name = readNameRef() + } + /** Read a Symbol and a Name */ + def setSymName(): Unit = { + symbol = readSymbolRef() + name = readNameRef() + } + /** Read a Symbol */ + def setSym(): Unit = { + symbol = readSymbolRef() + } + + implicit val pos: Position = NoPosition + + 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) + val cls = symbol.asClass + val ((constr: DefDef) :: Nil, stats) = + impl.body.partition(_.symbol == cls.primaryConstructor) + ClassDef(cls, constr, tparams ++ stats) + + case MODULEtree => + setSymModsName() + ModuleDef(symbol.asTerm, readTemplateRef().body) + + case VALDEFtree => + setSymModsName() + val tpt = readTreeRef() + val rhs = readTreeRef() + ValDef(symbol.asTerm, rhs) + + case DEFDEFtree => + setSymModsName() + val tparams = times(readNat(), readTypeDefRef) + val vparamss = times(readNat(), () => times(readNat(), readValDefRef)) + val tpt = readTreeRef() + val rhs = readTreeRef() + DefDef(symbol.asTerm, rhs) + + case TYPEDEFtree => + setSymModsName() + val rhs = readTreeRef() + val tparams = until(end, readTypeDefRef) + TypeDef(symbol.asType) + + case LABELtree => + setSymName() + val rhs = readTreeRef() + val params = until(end, readIdentRef) + val ldef = DefDef(symbol.asTerm, rhs) + def isCaseLabel(sym: Symbol) = sym.name.startsWith(nme.CASEkw) + if (isCaseLabel(symbol)) ldef + else Block(ldef :: Nil, Apply(Ident(symbol.termRef), Nil)) + + case IMPORTtree => + setSym() + val expr = readTreeRef() + val selectors = until(end, () => { + val fromName = readNameRef() + val toName = readNameRef() + val from = untpd.Ident(fromName) + val to = untpd.Ident(toName) + if (toName.isEmpty) from else untpd.Pair(from, untpd.Ident(toName)) + }) + + Import(expr, selectors) + + case TEMPLATEtree => + setSym() + val parents = times(readNat(), readTreeRef) + val self = readValDefRef() + val body = until(end, readTreeRef) + untpd.Template(???, parents, self, body) // !!! TODO: pull out primary constructor + .withType(symbol.namedType) + + 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 => + readTreeRef() + unimplementedTree("STAR") + + case BINDtree => + setSymName() + Bind(symbol.asTerm, readTreeRef()) + + case UNAPPLYtree => + val fun = readTreeRef() + val args = until(end, readTreeRef) + UnApply(fun, Nil, args, defn.AnyType) // !!! this is wrong in general + + case ARRAYVALUEtree => + val elemtpt = readTreeRef() + val trees = until(end, readTreeRef) + SeqLiteral(defn.SeqType.appliedTo(elemtpt.tpe :: Nil), trees) + // note can't deal with trees passed to Java methods as arrays here + + case FUNCTIONtree => + setSym() + val body = readTreeRef() + val vparams = until(end, readValDefRef) + val applyType = MethodType(vparams map (_.name), vparams map (_.tpt.tpe), body.tpe) + val applyMeth = ctx.newSymbol(symbol.owner, nme.apply, Method, applyType) + Closure(applyMeth, Function.const(body.changeOwner(symbol, applyMeth)) _) + + 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(), Ident(symbol.termRef)) + + 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().tpe) + + 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) // note: can't deal with overloaded syms yet + + case APPLYDYNAMICtree => + setSym() + val qual = readTreeRef() + val args = until(end, readTreeRef) + unimplementedTree("APPLYDYNAMIC") + + case SUPERtree => + setSym() + val qual = readTreeRef() + val mix = readTypeNameRef() + Super(qual, mix, inConstrCall = false) // todo: revise + + case THIStree => + setSym() + val name = readTypeNameRef() + This(symbol.asClass) + + case SELECTtree => + setSym() + val qualifier = readTreeRef() + val selector = readNameRef() + qualifier.select(symbol.namedType) + case IDENTtree => + setSymName() + Ident(symbol.namedType) + + case LITERALtree => + Literal(readConstantRef()) + + case TYPEtree => + TypeTree(tpe) + + 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, symbol.namedType) + + case COMPOUNDTYPEtree => + readTemplateRef() + TypeTree(tpe) + + 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) + TypeTree(tpe) + + case _ => + noSuchTreeTag(tag, end) + } + } + + def noSuchTreeTag(tag: Int, end: Int)(implicit ctx: Context) = + errorBadSignature("unknown tree type (" + tag + ")") + + def unimplementedTree(what: String)(implicit ctx: Context) = + errorBadSignature(s"cannot read $what trees from Scala 2.x signatures") + + def readModifiers(isType: Boolean)(implicit ctx: Context): 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 = unpickleScalaFlags(pflags, isType) + val privateWithin = readNameRef().asTypeName + Trees.Modifiers[Type](flags, privateWithin, Nil) + } + + protected def readTemplateRef()(implicit ctx: Context): Template = + readTreeRef() match { + case templ: Template => templ + case other => + errorBadSignature("expected a template (" + other + ")") + } + protected def readCaseDefRef()(implicit ctx: Context): CaseDef = + readTreeRef() match { + case tree: CaseDef => tree + case other => + errorBadSignature("expected a case def (" + other + ")") + } + protected def readValDefRef()(implicit ctx: Context): ValDef = + readTreeRef() match { + case tree: ValDef => tree + case other => + errorBadSignature("expected a ValDef (" + other + ")") + } + protected def readIdentRef()(implicit ctx: Context): Ident = + readTreeRef() match { + case tree: Ident => tree + case other => + errorBadSignature("expected an Ident (" + other + ")") + } + protected def readTypeDefRef()(implicit ctx: Context): TypeDef = + readTreeRef() match { + case tree: TypeDef => tree + case other => + errorBadSignature("expected an TypeDef (" + other + ")") + } + +} -- cgit v1.2.3