aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/classfile
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/core/classfile
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/core/classfile')
-rw-r--r--compiler/src/dotty/tools/dotc/core/classfile/AbstractFileReader.scala88
-rw-r--r--compiler/src/dotty/tools/dotc/core/classfile/ByteCodecs.scala221
-rw-r--r--compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala378
-rw-r--r--compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala1100
4 files changed, 1787 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/classfile/AbstractFileReader.scala b/compiler/src/dotty/tools/dotc/core/classfile/AbstractFileReader.scala
new file mode 100644
index 000000000..cad3a4132
--- /dev/null
+++ b/compiler/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/compiler/src/dotty/tools/dotc/core/classfile/ByteCodecs.scala b/compiler/src/dotty/tools/dotc/core/classfile/ByteCodecs.scala
new file mode 100644
index 000000000..badd9e560
--- /dev/null
+++ b/compiler/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/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala
new file mode 100644
index 000000000..dd29fa49d
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala
@@ -0,0 +1,378 @@
+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
+
+ import Flags._
+ abstract class FlagTranslation {
+
+ protected def baseFlags(jflags: Int) = EmptyFlags
+ protected def isClass: Boolean = 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 (isClass) Abstract else Deferred
+ case JAVA_ACC_INTERFACE => 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 = {
+ val nflags =
+ if ((jflags & JAVA_ACC_ANNOTATION) == 0) jflags
+ else jflags & ~(JAVA_ACC_ABSTRACT | JAVA_ACC_INTERFACE) // annotations are neither abstract nor interfaces
+ var res: FlagSet = baseFlags | JavaDefined
+ res = addFlag(res, nflags & JAVA_ACC_PRIVATE)
+ res = addFlag(res, nflags & JAVA_ACC_PROTECTED)
+ res = addFlag(res, nflags & JAVA_ACC_FINAL)
+ res = addFlag(res, nflags & JAVA_ACC_SYNTHETIC)
+ res = addFlag(res, nflags & JAVA_ACC_STATIC)
+ res = addFlag(res, nflags & JAVA_ACC_ABSTRACT)
+ res = addFlag(res, nflags & JAVA_ACC_INTERFACE)
+ res
+ }
+
+ def flags(jflags: Int): FlagSet = translateFlags(jflags, baseFlags(jflags))
+ }
+ val classTranslation = new FlagTranslation {
+ override def isClass = true
+ }
+ val fieldTranslation = new FlagTranslation {
+ override def baseFlags(jflags: Int) = if ((jflags & JAVA_ACC_FINAL) == 0) Mutable else EmptyFlags
+ }
+ val methodTranslation = new FlagTranslation {
+ override def baseFlags(jflags: Int) = if ((jflags & JAVA_ACC_BRIDGE) != 0) Bridge else EmptyFlags
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
new file mode 100644
index 000000000..97a82e80d
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
@@ -0,0 +1,1100 @@
+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(
+ i"""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(s"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(
+ s"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 = classTranslation.flags(jflags)
+ val isEnum = (jflags & JAVA_ACC_ENUM) != 0
+ 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.AnnotationType }
+ 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.ClassfileAnnotationType :: 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)
+ }
+
+ // eager load java enum definitions for exhaustivity check of pattern match
+ if (isEnum) {
+ instanceScope.toList.map(_.ensureCompleted())
+ staticScope.toList.map(_.ensureCompleted())
+ classRoot.setFlag(Flags.Enum)
+ moduleRoot.setFlag(Flags.Enum)
+ }
+
+ 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 | methodTranslation.flags(jflags)
+ else fieldTranslation.flags(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 = translateTempPoly(parseAttributes(sym, denot.info))
+ 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.typeParamSymbols
+ 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.ArrayOf(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.asInstanceOf[List[TypeSymbol]]
+ 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 {
+ val elems = arr.toList
+ val elemType =
+ if (elems.isEmpty) defn.ObjectType
+ else ctx.typeComparer.lub(elems.tpes).widen
+ Some(JavaSeqLiteral(elems, TypeTree(elemType)))
+ }
+ 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.
+ */
+ def addAnnotationConstructor(classInfo: Type, tparams: List[TypeSymbol] = 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)
+
+ // The code below added an extra constructor to annotations where the
+ // last parameter of the constructor is an Array[X] for some X, the
+ // array was replaced by a vararg argument. Unfortunately this breaks
+ // inference when doing:
+ // @Annot(Array())
+ // The constructor is overloaded so the expected type of `Array()` is
+ // WildcardType, and the type parameter of the Array apply method gets
+ // instantiated to `Nothing` instead of `X`.
+ // I'm leaving this commented out in case we improve inference to make this work.
+ // Note that if this is reenabled then JavaParser will also need to be modified
+ // to add the extra constructor (this was not implemented before).
+ /*
+ if (paramTypes.nonEmpty)
+ paramTypes.last match {
+ case defn.ArrayOf(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),
+ classTranslation.flags(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()(ctx.addMode(Mode.Scala2Unpickling))
+ 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,
+ i"""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)
+ }
+}
+