diff options
author | Paul Phillips <paulp@improving.org> | 2013-04-03 07:33:09 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2013-04-03 07:33:09 -0700 |
commit | 1ad75702a6b7e349b79bc7672034d42ecf34d8a1 (patch) | |
tree | 9d0043da9610e4664008b87fc0eef85d2299a202 | |
parent | b575c1ed131a578a18239e3922c1ea8b7dd188a2 (diff) | |
parent | da7e175b424610c6b416ee3c9f17a5d7cad20c15 (diff) | |
download | scala-1ad75702a6b7e349b79bc7672034d42ecf34d8a1.tar.gz scala-1ad75702a6b7e349b79bc7672034d42ecf34d8a1.tar.bz2 scala-1ad75702a6b7e349b79bc7672034d42ecf34d8a1.zip |
Merge pull request #2341 from paulp/pr/java-acc-flags
ClassfileParser work.
8 files changed, 592 insertions, 494 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index bc7c129c20..a8a47205dd 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -12,9 +12,11 @@ import java.lang.Integer.toHexString import scala.collection.{ mutable, immutable } import scala.collection.mutable.{ ListBuffer, ArrayBuffer } import scala.annotation.switch +import scala.reflect.internal.{ JavaAccFlags } import scala.reflect.internal.pickling.{PickleBuffer, ByteCodecs} import scala.tools.nsc.io.AbstractFile + /** This abstract class implements a class file parser. * * @author Martin Odersky @@ -23,6 +25,7 @@ import scala.tools.nsc.io.AbstractFile abstract class ClassfileParser { val global: Global import global._ + import definitions._ import scala.reflect.internal.ClassfileConstants._ import Flags._ @@ -40,11 +43,31 @@ abstract class ClassfileParser { protected var classTParams = Map[Name,Symbol]() protected var srcfile0 : Option[AbstractFile] = None protected def moduleClass: Symbol = staticModule.moduleClass + private var sawPrivateConstructor = false + + private def ownerForFlags(jflags: JavaAccFlags) = if (jflags.isStatic) moduleClass else clazz def srcfile = srcfile0 + private def optimized = global.settings.optimise.value private def currentIsTopLevel = !(currentClass.decodedName containsChar '$') + // u1, u2, and u4 are what these data types are called in the JVM spec. + // They are an unsigned byte, unsigned char, and unsigned int respectively. + // We bitmask u1 into an Int to make sure it's 0-255 (and u1 isn't used + // for much beyond tags) but leave u2 alone as it's already unsigned. + protected final def u1(): Int = in.nextByte & 0xFF + protected final def u2(): Char = in.nextChar + protected final def u4(): Int = in.nextInt + + private def readInnerClassFlags() = readClassFlags() + private def readClassFlags() = JavaAccFlags classFlags u2 + private def readMethodFlags() = JavaAccFlags methodFlags u2 + private def readFieldFlags() = JavaAccFlags fieldFlags u2 + private def readTypeName() = readName().toTypeName + private def readName() = pool getName u2 + private def readType() = pool getType u2 + private object unpickler extends scala.reflect.internal.pickling.UnPickler { val global: ClassfileParser.this.global.type = ClassfileParser.this.global } @@ -106,68 +129,60 @@ abstract class ClassfileParser { } private def parseHeader() { - val magic = in.nextInt + val magic = u4 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) + abort(s"class file ${in.file} has wrong magic number 0x${toHexString(magic)}") + + val minor, major = u2 + if (major < JAVA_MAJOR_VERSION || major == JAVA_MAJOR_VERSION && minor < JAVA_MINOR_VERSION) + abort(s"class file ${in.file} has unknown version $major.$minor, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION") } class ConstantPool { - private val len = in.nextChar - private val starts = new Array[Int](len) - private val values = new Array[AnyRef](len) + private val len = u2 + private val starts = new Array[Int](len) + private val values = new Array[AnyRef](len) private val internalized = new Array[Name](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) + (u1.toInt: @switch) match { + case CONSTANT_UTF8 | CONSTANT_UNICODE => in skip u2 + case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE => in skip 2 + case CONSTANT_METHODHANDLE => in skip 3 + case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF => in skip 4 + case CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT => in skip 4 + case 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): Name = { - if (index <= 0 || len <= index) - errorBadIndex(index) + def recordAtIndex[T <: AnyRef](value: T, idx: Int): T = { + values(idx) = value + value + } - values(index) match { + def firstExpecting(index: Int, expected: Int): Int = { + val start = starts(index) + val first = in.buf(start).toInt + if (first == expected) start + 1 + else this errorBadTag start + } + + /** Return the name found at given index. */ + def getName(index: Int): Name = ( + if (index <= 0 || len <= index) errorBadIndex(index) + else values(index) match { case name: Name => name - case null => - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) - val name = newTermName(in.buf, start + 3, in.getChar(start + 1)) - values(index) = name - name + case _ => + val start = firstExpecting(index, CONSTANT_UTF8) + recordAtIndex(newTermName(in.buf, start + 2, in.getChar(start)), index) } - } + ) /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */ def getExternalName(index: Int): Name = { @@ -182,28 +197,23 @@ abstract class ClassfileParser { def getClassSymbol(index: Int): Symbol = { if (index <= 0 || len <= index) errorBadIndex(index) - var c = values(index).asInstanceOf[Symbol] - if (c eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - val name = getExternalName(in.getChar(start + 1)) - if (nme.isModuleName(name)) - c = rootMirror.getModuleByName(name.dropModule) - else - c = classNameToSymbol(name) - - values(index) = c + values(index) match { + case sym: Symbol => sym + case _ => + val result = getClassName(index) match { + case name if nme.isModuleName(name) => rootMirror getModuleByName name.dropModule + case name => classNameToSymbol(name) + } + recordAtIndex(result, index) } - 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): Name = { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - getExternalName(in.getChar(start + 1)) + val start = firstExpecting(index, CONSTANT_CLASS) + getExternalName(in getChar start) } /** Return the symbol of the class member at `index`. @@ -279,94 +289,66 @@ abstract class ClassfileParser { */ private def getNameAndType(index: Int, ownerTpe: Type): (Name, Type) = { if (index <= 0 || len <= index) errorBadIndex(index) - var p = values(index).asInstanceOf[(Name, Type)] - if (p eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_NAMEANDTYPE) errorBadTag(start) - val name = getName(in.getChar(start + 1).toInt) - // create a dummy symbol for method types - val dummySym = ownerTpe.typeSymbol.newMethod(name.toTermName, ownerTpe.typeSymbol.pos) - var tpe = getType(dummySym, 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 MethodType(formals, restpe) => - tpe = MethodType(formals, ownerTpe) + (values(index): @unchecked) match { + case p: ((Name, Type)) => p + case _ => + val start = firstExpecting(index, CONSTANT_NAMEANDTYPE) + val name = getName(in.getChar(start).toInt) + // create a dummy symbol for method types + val dummy = ownerTpe.typeSymbol.newMethod(name.toTermName, ownerTpe.typeSymbol.pos) + val tpe = getType(dummy, in.getChar(start + 2).toInt) + // fix the return type, which is blindly set to the class currently parsed + val restpe = tpe match { + case MethodType(formals, _) if name == nme.CONSTRUCTOR => MethodType(formals, ownerTpe) + case _ => tpe } - - p = (name, tpe) + ((name, restpe)) } - p } /** Return the type of a class constant entry. Since * arrays are considered to be class types, they might * appear as entries in 'newarray' or 'cast' opcodes. */ - def getClassOrArrayType(index: Int): Type = { + def getClassOrArrayType(index: Int): Type = ( if (index <= 0 || len <= index) errorBadIndex(index) - val value = values(index) - var c: Type = null - if (value eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - val name = getExternalName(in.getChar(start + 1)) - if (name.charAt(0) == ARRAY_TAG) { - c = sigToType(null, name) - values(index) = c - } else { - val sym = classNameToSymbol(name) - /*if (name.endsWith("$")) definitions.getModule(name.subName(0, name.length - 1)) - else if (name.endsWith("$class")) definitions.getModule(name) - else definitions.getClass(name)*/ - values(index) = sym - c = sym.tpe - } - } else c = value match { - case tp: Type => tp - case cls: Symbol => cls.tpe + else values(index) match { + case tp: Type => tp + case cls: Symbol => cls.tpe_* + case _ => + val name = getClassName(index) + name charAt 0 match { + case ARRAY_TAG => recordAtIndex(sigToType(null, name), index) + case _ => recordAtIndex(classNameToSymbol(name), index).tpe_* + } } - c - } - - def getType(index: Int): Type = getType(null, index) - - def getType(sym: Symbol, index: Int): Type = - sigToType(sym, getExternalName(index)) + ) - def getSuperClass(index: Int): Symbol = - if (index == 0) definitions.AnyClass else getClassSymbol(index) + def getType(index: Int): Type = getType(null, index) + def getType(sym: Symbol, index: Int): Type = sigToType(sym, getExternalName(index)) + def getSuperClass(index: Int): Symbol = if (index == 0) AnyClass else getClassSymbol(index) - def getConstant(index: Int): Constant = { + private def createConstant(index: Int): Constant = { + val start = starts(index) + Constant((in.buf(start).toInt: @switch) match { + case CONSTANT_STRING => getName(in.getChar(start + 1).toInt).toString + case CONSTANT_INTEGER => in.getInt(start + 1) + case CONSTANT_FLOAT => in.getFloat(start + 1) + case CONSTANT_LONG => in.getLong(start + 1) + case CONSTANT_DOUBLE => in.getDouble(start + 1) + case CONSTANT_CLASS => getClassOrArrayType(index).typeSymbol.tpe_* // !!! Is this necessary or desirable? + case _ => errorBadTag(start) + }) + } + def getConstant(index: Int): Constant = ( if (index <= 0 || len <= index) errorBadIndex(index) - var value = values(index) - if (value eq null) { - val start = starts(index) - value = (in.buf(start).toInt: @switch) match { - case CONSTANT_STRING => - Constant(getName(in.getChar(start + 1).toInt).toString) - case CONSTANT_INTEGER => - Constant(in.getInt(start + 1)) - case CONSTANT_FLOAT => - Constant(in.getFloat(start + 1)) - case CONSTANT_LONG => - Constant(in.getLong(start + 1)) - case CONSTANT_DOUBLE => - Constant(in.getDouble(start + 1)) - case CONSTANT_CLASS => - getClassOrArrayType(index).typeSymbol - case _ => - errorBadTag(start) - } - values(index) = value - } - value match { - case ct: Constant => ct - case cls: Symbol => Constant(cls.tpe_*) - case arr: Type => Constant(arr) + else values(index) match { + case const: Constant => const + case sym: Symbol => Constant(sym.tpe_*) + case tpe: Type => Constant(tpe) + case _ => recordAtIndex(createConstant(index), index) } - } + ) private def getSubArray(bytes: Array[Byte]): Array[Byte] = { val decodedLength = ByteCodecs.decode(bytes) @@ -375,46 +357,41 @@ abstract class ClassfileParser { arr } - def getBytes(index: Int): Array[Byte] = { + 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 + else values(index) match { + case xs: Array[Byte] => xs + case _ => + val start = firstExpecting(index, CONSTANT_UTF8) + val len = in getChar start + val bytes = new Array[Byte](len) + System.arraycopy(in.buf, start + 2, bytes, 0, len) + recordAtIndex(getSubArray(bytes), index) } - 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 + val head = indices.head + values(head) match { + case xs: Array[Byte] => xs + case _ => + val arr: Array[Byte] = indices.toArray flatMap { index => + if (index <= 0 || ConstantPool.this.len <= index) errorBadIndex(index) + val start = firstExpecting(index, CONSTANT_UTF8) + val len = in getChar start + in.buf drop start + 2 take len + } + recordAtIndex(getSubArray(arr), head) } - 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) + abort(s"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) + abort("bad constant pool tag ${in.buf(start)} at byte $start") } /** Try to force the chain of enclosing classes for the given name. Otherwise @@ -493,30 +470,27 @@ abstract class ClassfileParser { catch { case _: FatalError => loadClassSymbol(name) } } - var sawPrivateConstructor = false - def parseClass() { - val jflags = in.nextChar - val isAnnotation = hasAnnotation(jflags) - val sflags = toScalaClassFlags(jflags) - val nameIdx = in.nextChar - currentClass = pool.getClassName(nameIdx) + val jflags = readClassFlags() + val sflags = jflags.toScalaFlags + val nameIdx = u2 + currentClass = pool.getClassName(nameIdx) /* 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] = { if (isScala) { - in.nextChar // skip superclass - val ifaces = in.nextChar - in.bp += ifaces * 2 // .. and iface count interfaces - List(definitions.AnyRefClass.tpe) // dummy superclass, will be replaced by pickled information + u2 // skip superclass + val ifaces = u2 + in.bp += ifaces * 2 // .. and iface count interfaces + List(AnyRefClass.tpe) // dummy superclass, will be replaced by pickled information } else raiseLoaderLevel { - val superType = if (isAnnotation) { in.nextChar; definitions.AnnotationClass.tpe } - else pool.getSuperClass(in.nextChar).tpe_* - val ifaceCount = in.nextChar - var ifaces = for (i <- List.range(0, ifaceCount)) yield pool.getSuperClass(in.nextChar).tpe_* - if (isAnnotation) ifaces = definitions.ClassfileAnnotationClass.tpe :: ifaces + val superType = if (jflags.isAnnotation) { u2; AnnotationClass.tpe } + else pool.getSuperClass(u2).tpe_* + val ifaceCount = u2 + var ifaces = for (i <- List.range(0, ifaceCount)) yield pool.getSuperClass(u2).tpe_* + if (jflags.isAnnotation) ifaces ::= ClassfileAnnotationClass.tpe superType :: ifaces } } @@ -546,21 +520,20 @@ abstract class ClassfileParser { skipMembers() // methods if (!isScala) { clazz setFlag sflags - importPrivateWithinFromJavaFlags(clazz, jflags) - importPrivateWithinFromJavaFlags(staticModule, jflags) - clazz.setInfo(classInfo) + propagatePackageBoundary(jflags, clazz, staticModule) + clazz setInfo classInfo moduleClass setInfo staticInfo - staticModule.setInfo(moduleClass.tpe) - staticModule.setFlag(JAVA) - staticModule.moduleClass.setFlag(JAVA) + staticModule setInfo moduleClass.tpe + staticModule setFlag JAVA + staticModule.moduleClass setFlag JAVA // attributes now depend on having infos set already parseAttributes(clazz, classInfo) def queueLoad() { in.bp = curbp - 0 until in.nextChar foreach (_ => parseField()) + 0 until u2 foreach (_ => parseField()) sawPrivateConstructor = false - 0 until in.nextChar foreach (_ => parseMethod()) + 0 until u2 foreach (_ => parseMethod()) val needsConstructor = ( !sawPrivateConstructor && !(instanceScope containsName nme.CONSTRUCTOR) @@ -596,28 +569,28 @@ abstract class ClassfileParser { } def parseField() { - val jflags = in.nextChar - val sflags = toScalaFieldFlags(jflags) - if ((sflags & PRIVATE) != 0L && !global.settings.optimise) { + val jflags = readFieldFlags() + val sflags = jflags.toScalaFlags + + if ((sflags & PRIVATE) != 0L && !optimized) { in.skip(4); skipAttributes() } else { - val name = pool.getName(in.nextChar) - val info = pool.getType(in.nextChar) - val sym = getOwner(jflags).newValue(name.toTermName, NoPosition, sflags) - val isEnum = (jflags & JAVA_ACC_ENUM) != 0 + val name = readName() + val info = readType() + val sym = ownerForFlags(jflags).newValue(name.toTermName, NoPosition, sflags) // Note: the info may be overrwritten later with a generic signature // parsed from SignatureATTR sym setInfo { - if (isEnum) ConstantType(Constant(sym)) + if (jflags.isEnum) ConstantType(Constant(sym)) else info } - importPrivateWithinFromJavaFlags(sym, jflags) + propagatePackageBoundary(jflags, sym) parseAttributes(sym, info) - getScope(jflags).enter(sym) + getScope(jflags) enter sym // sealed java enums - if (isEnum) { + if (jflags.isEnum) { val enumClass = sym.owner.linkedClassOfClass if (!enumClass.isSealed) enumClass setFlag (SEALED | ABSTRACT) @@ -628,26 +601,27 @@ abstract class ClassfileParser { } def parseMethod() { - val jflags = in.nextChar.toInt - val sflags = toScalaMethodFlags(jflags) - if (isPrivate(jflags) && !global.settings.optimise) { - val name = pool.getName(in.nextChar) + val jflags = readMethodFlags() + val sflags = jflags.toScalaFlags + if (jflags.isPrivate && !optimized) { + val name = readName() if (name == nme.CONSTRUCTOR) sawPrivateConstructor = true in.skip(2); skipAttributes() - } else { - if ((sflags & PRIVATE) != 0L && global.settings.optimise) { + } + else { + if ((sflags & PRIVATE) != 0L && optimized) { in.skip(4); skipAttributes() } else { - val name = pool.getName(in.nextChar) - val sym = getOwner(jflags).newMethod(name.toTermName, NoPosition, sflags) - var info = pool.getType(sym, (in.nextChar)) + val name = readName() + val sym = ownerForFlags(jflags).newMethod(name.toTermName, NoPosition, sflags) + var info = pool.getType(sym, u2) if (name == nme.CONSTRUCTOR) info match { case MethodType(params, restpe) => // if this is a non-static inner class, remove the explicit outer parameter val newParams = innerClasses getEntry currentClass match { - case Some(entry) if !isScalaRaw && !isStatic(entry.jflags) => + case Some(entry) if !isScalaRaw && !entry.jflags.isStatic => /* About `clazz.owner.isPackage` below: SI-5957 * For every nested java class A$B, there are two symbols in the scala compiler. * 1. created by SymbolLoader, because of the existence of the A$B.class file, owner: package @@ -664,13 +638,13 @@ abstract class ClassfileParser { } // Note: the info may be overrwritten later with a generic signature // parsed from SignatureATTR - sym.setInfo(info) - importPrivateWithinFromJavaFlags(sym, jflags) + sym setInfo info + propagatePackageBoundary(jflags, sym) parseAttributes(sym, info) - if ((jflags & JAVA_ACC_VARARGS) != 0) { - sym.setInfo(arrayToRepeated(sym.info)) - } - getScope(jflags).enter(sym) + if (jflags.isVarargs) + sym modifyInfo arrayToRepeated + + getScope(jflags) enter sym } } } @@ -690,15 +664,15 @@ abstract class ClassfileParser { def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean): Type = { val tag = sig.charAt(index); index += 1 tag match { - case BYTE_TAG => definitions.ByteClass.tpe - case CHAR_TAG => definitions.CharClass.tpe - case DOUBLE_TAG => definitions.DoubleClass.tpe - case FLOAT_TAG => definitions.FloatClass.tpe - case INT_TAG => definitions.IntClass.tpe - case LONG_TAG => definitions.LongClass.tpe - case SHORT_TAG => definitions.ShortClass.tpe - case VOID_TAG => definitions.UnitClass.tpe - case BOOL_TAG => definitions.BooleanClass.tpe + case BYTE_TAG => ByteClass.tpe + case CHAR_TAG => CharClass.tpe + case DOUBLE_TAG => DoubleClass.tpe + case FLOAT_TAG => FloatClass.tpe + case INT_TAG => IntClass.tpe + case LONG_TAG => LongClass.tpe + case SHORT_TAG => ShortClass.tpe + case VOID_TAG => UnitClass.tpe + case BOOL_TAG => BooleanClass.tpe case 'L' => def processInner(tp: Type): Type = tp match { case TypeRef(pre, sym, args) if (!sym.isStatic) => @@ -723,7 +697,7 @@ abstract class ClassfileParser { val tp = sig2type(tparams, skiptvs) // sig2type seems to return AnyClass regardless of the situation: // we don't want Any as a LOWER bound. - if (tp.typeSymbol == definitions.AnyClass) TypeBounds.empty + if (tp.typeSymbol == AnyClass) TypeBounds.empty else TypeBounds.lower(tp) case '*' => TypeBounds.empty } @@ -744,7 +718,7 @@ abstract class ClassfileParser { // or we'll create a boatload of needless existentials. else if (classSym.isMonomorphicType || classSym.unsafeTypeParams.isEmpty) tp // raw type - existentially quantify all type parameters - else logResult(s"raw type from $classSym")(definitions.unsafeClassExistentialType(classSym)) + else logResult(s"raw type from $classSym")(unsafeClassExistentialType(classSym)) case tp => assert(sig.charAt(index) != '<', s"sig=$sig, index=$index, tp=$tp") tp @@ -772,11 +746,11 @@ abstract class ClassfileParser { // 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 <:< definitions.ObjectClass.tpe)) { - elemtp = intersectionType(List(elemtp, definitions.ObjectClass.tpe)) + if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< ObjectClass.tpe)) { + elemtp = intersectionType(List(elemtp, ObjectClass.tpe)) } - definitions.arrayType(elemtp) + arrayType(elemtp) case '(' => // we need a method symbol. given in line 486 by calling getType(methodSym, ..) assert(sym ne null, sig) @@ -794,7 +768,7 @@ abstract class ClassfileParser { case 'T' => val n = subName(';'.==).toTypeName index += 1 - if (skiptvs) definitions.AnyClass.tpe + if (skiptvs) AnyClass.tpe else tparams(n).typeConstructor } } // sig2type(tparams, skiptvs) @@ -847,24 +821,20 @@ abstract class ClassfileParser { GenPolyType(ownTypeParams, tpe) } // sigToType - class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter { - override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") } - } - def parseAttributes(sym: Symbol, symtype: Type) { def convertTo(c: Constant, pt: Type): Constant = { - if (pt.typeSymbol == definitions.BooleanClass && c.tag == IntTag) + if (pt.typeSymbol == BooleanClass && c.tag == IntTag) Constant(c.value != 0) else c convertTo pt } def parseAttribute() { - val attrName = pool.getName(in.nextChar).toTypeName - val attrLen = in.nextInt + val attrName = readTypeName() + val attrLen = u4 attrName match { case tpnme.SignatureATTR => if (!isScala && !isScalaRaw) { - val sig = pool.getExternalName(in.nextChar) + val sig = pool.getExternalName(u2) val newType = sigToType(sym, sig) sym.setInfo(newType) if (settings.debug && settings.verbose) @@ -879,10 +849,10 @@ abstract class ClassfileParser { in.skip(attrLen) case tpnme.DeprecatedATTR => val arg = Literal(Constant("see corresponding Javadoc for more information.")) - sym.addAnnotation(definitions.DeprecatedAttr, arg, Literal(Constant(""))) + sym.addAnnotation(DeprecatedAttr, arg, Literal(Constant(""))) in.skip(attrLen) case tpnme.ConstantValueATTR => - val c = pool.getConstant(in.nextChar) + val c = pool.getConstant(u2) val c1 = convertTo(c, symtype) if (c1 ne null) sym.setInfo(ConstantType(c1)) else println("failure to convert " + c + " to " + symtype); //debug @@ -896,7 +866,7 @@ abstract class ClassfileParser { isScalaRaw = true // Attribute on methods of java annotation classes when that method has a default case tpnme.AnnotationDefaultATTR => - sym.addAnnotation(definitions.AnnotationDefaultAttr) + sym.addAnnotation(AnnotationDefaultAttr) in.skip(attrLen) // Java annotations on classes / methods / fields with RetentionPolicy.RUNTIME case tpnme.RuntimeAnnotationATTR => @@ -926,7 +896,7 @@ abstract class ClassfileParser { parseExceptions(attrLen) case tpnme.SourceFileATTR => - val srcfileLeaf = pool.getName(in.nextChar).toString.trim + val srcfileLeaf = readName().toString.trim val srcpath = sym.enclosingPackage match { case NoSymbol => srcfileLeaf case rootMirror.EmptyPackage => srcfileLeaf @@ -939,8 +909,8 @@ abstract class ClassfileParser { } def parseAnnotArg: Option[ClassfileAnnotArg] = { - val tag = in.nextByte.toChar - val index = in.nextChar + val tag = u1 + val index = u2 tag match { case STRING_TAG => Some(LiteralAnnotArg(Constant(pool.getName(index).toString))) @@ -951,7 +921,7 @@ abstract class ClassfileParser { Some(LiteralAnnotArg(Constant(pool.getType(index)))) case ENUM_TAG => val t = pool.getType(index) - val n = pool.getName(in.nextChar) + val n = readName() val s = t.typeSymbol.companionModule.info.decls.lookup(n) assert(s != NoSymbol, t) Some(LiteralAnnotArg(Constant(s))) @@ -971,20 +941,20 @@ abstract class ClassfileParser { } def parseScalaSigBytes: Option[ScalaSigBytes] = { - val tag = in.nextByte.toChar + val tag = u1 assert(tag == STRING_TAG, tag) - Some(ScalaSigBytes(pool getBytes in.nextChar)) + Some(ScalaSigBytes(pool getBytes u2)) } def parseScalaLongSigBytes: Option[ScalaSigBytes] = { - val tag = in.nextByte.toChar + val tag = u1 assert(tag == ARRAY_TAG, tag) - val stringCount = in.nextChar + val stringCount = u2 val entries = for (i <- 0 until stringCount) yield { - val stag = in.nextByte.toChar + val stag = u1 assert(stag == STRING_TAG, stag) - in.nextChar.toInt + u2.toInt } Some(ScalaSigBytes(pool.getBytes(entries.toList))) } @@ -994,20 +964,20 @@ abstract class ClassfileParser { */ def parseAnnotation(attrNameIndex: Char): Option[AnnotationInfo] = try { val attrType = pool.getType(attrNameIndex) - val nargs = in.nextChar + val nargs = u2 val nvpairs = new ListBuffer[(Name, ClassfileAnnotArg)] var hasError = false for (i <- 0 until nargs) { - val name = pool.getName(in.nextChar) + val name = readName() // The "bytes: String" argument of the ScalaSignature attribute is parsed specially so that it is // available as an array of bytes (the pickled Scala signature) instead of as a string. The pickled signature // is encoded as a string because of limitations in the Java class file format. - if ((attrType == definitions.ScalaSignatureAnnotation.tpe) && (name == nme.bytes)) + if ((attrType == ScalaSignatureAnnotation.tpe) && (name == nme.bytes)) parseScalaSigBytes match { case Some(c) => nvpairs += ((name, c)) case None => hasError = true } - else if ((attrType == definitions.ScalaLongSignatureAnnotation.tpe) && (name == nme.bytes)) + else if ((attrType == ScalaLongSignatureAnnotation.tpe) && (name == nme.bytes)) parseScalaLongSigBytes match { case Some(c) => nvpairs += ((name, c)) case None => hasError = true @@ -1040,10 +1010,10 @@ abstract class ClassfileParser { * thrown by a method. */ def parseExceptions(len: Int) { - val nClasses = in.nextChar + val nClasses = u2 for (n <- 0 until nClasses) { // FIXME: this performs an equivalent of getExceptionTypes instead of getGenericExceptionTypes (SI-7065) - val cls = pool.getClassSymbol(in.nextChar.toInt) + val cls = pool.getClassSymbol(u2) // we call initialize due to the fact that we call Symbol.isMonomorphicType in addThrowsAnnotation // and that method requires Symbol to be forced to give the right answers, see SI-7107 for details cls.initialize @@ -1054,13 +1024,13 @@ abstract class ClassfileParser { /* 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): Option[AnnotationInfo] = { - val nAttr = in.nextChar + val nAttr = u2 var scalaSigAnnot: Option[AnnotationInfo] = None for (n <- 0 until nAttr) - parseAnnotation(in.nextChar) match { - case Some(scalaSig) if (scalaSig.atp == definitions.ScalaSignatureAnnotation.tpe) => + parseAnnotation(u2) match { + case Some(scalaSig) if (scalaSig.atp == ScalaSignatureAnnotation.tpe) => scalaSigAnnot = Some(scalaSig) - case Some(scalaSig) if (scalaSig.atp == definitions.ScalaLongSignatureAnnotation.tpe) => + case Some(scalaSig) if (scalaSig.atp == ScalaLongSignatureAnnotation.tpe) => scalaSigAnnot = Some(scalaSig) case Some(annot) => sym.addAnnotation(annot) @@ -1070,7 +1040,7 @@ abstract class ClassfileParser { } // begin parseAttributes - for (i <- 0 until in.nextChar) parseAttribute() + for (i <- 0 until u2) parseAttribute() } /** Enter own inner classes in the right scope. It needs the scopes to be set up, @@ -1080,11 +1050,12 @@ abstract class ClassfileParser { def className(name: Name): Name = name.subName(name.lastPos('.') + 1, name.length) - def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile, jflags: Int) { + def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile) { + def jflags = entry.jflags val completer = new global.loaders.ClassfileLoader(file) val name = entry.originalName - val sflags = toScalaClassFlags(jflags) - val owner = getOwner(jflags) + val sflags = jflags.toScalaFlags + val owner = ownerForFlags(jflags) val scope = getScope(jflags) val innerClass = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer val innerModule = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer @@ -1113,7 +1084,7 @@ abstract class ClassfileParser { val file = global.classPath.findSourceFile(entry.externalName.toString) getOrElse { throw new AssertionError(entry.externalName) } - enterClassAndModule(entry, file, entry.jflags) + enterClassAndModule(entry, file) } } } @@ -1126,10 +1097,10 @@ abstract class ClassfileParser { skipSuperclasses() skipMembers() // fields skipMembers() // methods - val attrs = in.nextChar + val attrs = u2 for (i <- 0 until attrs) { - val attrName = pool.getName(in.nextChar).toTypeName - val attrLen = in.nextInt + val attrName = readTypeName() + val attrLen = u4 attrName match { case tpnme.SignatureATTR => in.skip(attrLen) @@ -1143,9 +1114,10 @@ abstract class ClassfileParser { case tpnme.ScalaATTR => isScalaRaw = true case tpnme.InnerClassesATTR if !isScala => - val entries = in.nextChar.toInt + val entries = u2 for (i <- 0 until entries) { - val innerIndex, outerIndex, nameIndex, jflags = in.nextChar.toInt + val innerIndex, outerIndex, nameIndex = u2 + val jflags = readInnerClassFlags() if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) innerClasses add InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) } @@ -1157,14 +1129,13 @@ abstract class ClassfileParser { } /** An entry in the InnerClasses attribute of this class file. */ - case class InnerClassEntry(external: Int, outer: Int, name: Int, jflags: Int) { + case class InnerClassEntry(external: Int, outer: Int, name: Int, jflags: JavaAccFlags) { def externalName = pool getClassName external def outerName = pool getClassName outer def originalName = pool getName name - def isStatic = ClassfileParser.this.isStatic(jflags) def isModule = originalName.isTermName - def scope = if (isStatic) staticScope else instanceScope - def enclosing = if (isStatic) enclModule else enclClass + def scope = if (jflags.isStatic) staticScope else instanceScope + def enclosing = if (jflags.isStatic) enclModule else enclClass // The name of the outer class, without its trailing $ if it has one. private def strippedOuter = nme stripModuleSuffix outerName @@ -1216,6 +1187,9 @@ abstract class ClassfileParser { } } + class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter { + override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") } + } class LazyAliasType(alias: Symbol) extends LazyType with FlagAgnosticCompleter { override def complete(sym: Symbol) { sym setInfo createFromClonedSymbols(alias.initialize.typeParams, alias.tpe)(typeFun) @@ -1223,16 +1197,16 @@ abstract class ClassfileParser { } def skipAttributes() { - var attrCount: Int = in.nextChar + var attrCount: Int = u2 while (attrCount > 0) { in skip 2 - in skip in.nextInt + in skip u4 attrCount -= 1 } } def skipMembers() { - var memberCount: Int = in.nextChar + var memberCount: Int = u2 while (memberCount > 0) { in skip 6 skipAttributes() @@ -1242,17 +1216,10 @@ abstract class ClassfileParser { def skipSuperclasses() { in.skip(2) // superclass - val ifaces = in.nextChar + val ifaces = u2 in.skip(2 * ifaces) } - protected def getOwner(flags: Int): Symbol = - if (isStatic(flags)) moduleClass else clazz - - protected def getScope(flags: Int): Scope = - if (isStatic(flags)) staticScope else instanceScope - - 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 + protected def getScope(flags: JavaAccFlags): Scope = + if (flags.isStatic) staticScope else instanceScope } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 80a810703c..50487ad123 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -10,6 +10,7 @@ package classfile import scala.collection.{ mutable, immutable } import mutable.ListBuffer import ClassfileConstants._ +import scala.reflect.internal.JavaAccFlags /** ICode reader from Java bytecode. * @@ -45,26 +46,19 @@ abstract class ICodeReader extends ClassfileParser { (staticCode, instanceCode) } - /** If we're parsing a scala module, the owner of members is always - * the module symbol. - */ - override def getOwner(jflags: Int): Symbol = - if (isScalaModule) this.staticModule - else super.getOwner(jflags) - override def parseClass() { this.instanceCode = new IClass(clazz) this.staticCode = new IClass(staticModule) - in.nextChar - pool getClassSymbol in.nextChar + u2 + pool getClassSymbol u2 parseInnerClasses() in.skip(2) // super class - in.skip(2 * in.nextChar) // interfaces - val fieldCount = in.nextChar + in.skip(2 * u2) // interfaces + val fieldCount = u2 for (i <- 0 until fieldCount) parseField() - val methodCount = in.nextChar + val methodCount = u2 for (i <- 0 until methodCount) parseMethod() instanceCode.methods = instanceCode.methods.reverse staticCode.methods = staticCode.methods.reverse @@ -76,25 +70,31 @@ abstract class ICodeReader extends ClassfileParser { skipAttributes() } - private def parseMember(field: Boolean): (Int, Symbol) = { - val jflags = in.nextChar - val name = pool getName in.nextChar - val owner = getOwner(jflags) - val dummySym = owner.newMethod(name.toTermName, owner.pos, toScalaMethodFlags(jflags)) + private def parseMember(field: Boolean): (JavaAccFlags, Symbol) = { + val jflags = JavaAccFlags(u2) + val name = pool getName u2 + /** If we're parsing a scala module, the owner of members is always + * the module symbol. + */ + val owner = ( + if (isScalaModule) staticModule + else if (jflags.isStatic) moduleClass + else clazz + ) + val dummySym = owner.newMethod(name.toTermName, owner.pos, jflags.toScalaFlags) try { - val ch = in.nextChar + val ch = u2 val tpe = pool.getType(dummySym, ch) if ("<clinit>" == name.toString) (jflags, NoSymbol) else { - val owner = getOwner(jflags) var sym = owner.info.findMember(name, 0, 0, stableOnly = false).suchThat(old => sameType(old.tpe, tpe)) if (sym == NoSymbol) sym = owner.info.findMember(newTermName(name + nme.LOCAL_SUFFIX_STRING), 0, 0, stableOnly = false).suchThat(_.tpe =:= tpe) if (sym == NoSymbol) { - sym = if (field) owner.newValue(name.toTermName, owner.pos, toScalaFieldFlags(jflags)) else dummySym + sym = if (field) owner.newValue(name.toTermName, owner.pos, jflags.toScalaFlags) else dummySym sym setInfoAndEnter tpe log(s"ICodeReader could not locate ${name.decode} in $owner. Created ${sym.defString}.") } @@ -126,9 +126,9 @@ abstract class ICodeReader extends ClassfileParser { this.method = new IMethod(sym) this.method.returnType = toTypeKind(sym.tpe.resultType) getCode(jflags).addMethod(this.method) - if ((jflags & JAVA_ACC_NATIVE) != 0) + if (jflags.isNative) this.method.native = true - val attributeCount = in.nextChar + val attributeCount = u2 for (i <- 0 until attributeCount) parseAttribute() } else { debuglog("Skipping non-existent method.") @@ -142,8 +142,8 @@ abstract class ICodeReader extends ClassfileParser { } def parseAttribute() { - val attrName = pool.getName(in.nextChar).toTypeName - val attrLen = in.nextInt + val attrName = pool.getName(u2).toTypeName + val attrLen = u4 attrName match { case tpnme.CodeATTR => parseByteCode() @@ -187,9 +187,9 @@ abstract class ICodeReader extends ClassfileParser { /** Parse java bytecode into ICode */ def parseByteCode() { - maxStack = in.nextChar - maxLocals = in.nextChar - val codeLength = in.nextInt + maxStack = u2 + maxLocals = u2 + val codeLength = u4 val code = new LinearCode def parseInstruction() { @@ -200,7 +200,7 @@ abstract class ICodeReader extends ClassfileParser { /* Parse 16 bit jump target. */ def parseJumpTarget = { size += 2 - val offset = in.nextChar.toShort + val offset = u2.toShort val target = pc + offset assert(target >= 0 && target < codeLength, "Illegal jump target: " + target) target @@ -209,14 +209,13 @@ abstract class ICodeReader extends ClassfileParser { /* Parse 32 bit jump target. */ def parseJumpTargetW: Int = { size += 4 - val offset = in.nextInt + val offset = u4 val target = pc + offset assert(target >= 0 && target < codeLength, "Illegal jump target: " + target + "pc: " + pc + " offset: " + offset) target } - val instr = toUnsignedByte(in.nextByte) - instr match { + u1 match { case JVM.nop => parseInstruction() case JVM.aconst_null => code emit CONSTANT(Constant(null)) case JVM.iconst_m1 => code emit CONSTANT(Constant(-1)) @@ -235,17 +234,17 @@ abstract class ICodeReader extends ClassfileParser { case JVM.dconst_0 => code emit CONSTANT(Constant(0.0)) case JVM.dconst_1 => code emit CONSTANT(Constant(1.0)) - case JVM.bipush => code.emit(CONSTANT(Constant(in.nextByte))); size += 1 - case JVM.sipush => code.emit(CONSTANT(Constant(in.nextChar))); size += 2 - case JVM.ldc => code.emit(CONSTANT(pool.getConstant(toUnsignedByte(in.nextByte)))); size += 1 - case JVM.ldc_w => code.emit(CONSTANT(pool.getConstant(in.nextChar))); size += 2 - case JVM.ldc2_w => code.emit(CONSTANT(pool.getConstant(in.nextChar))); size += 2 - case JVM.iload => code.emit(LOAD_LOCAL(code.getLocal(in.nextByte, INT))); size += 1 - case JVM.lload => code.emit(LOAD_LOCAL(code.getLocal(in.nextByte, LONG))); size += 1 - case JVM.fload => code.emit(LOAD_LOCAL(code.getLocal(in.nextByte, FLOAT))); size += 1 - case JVM.dload => code.emit(LOAD_LOCAL(code.getLocal(in.nextByte, DOUBLE))); size += 1 + case JVM.bipush => code.emit(CONSTANT(Constant(u1))); size += 1 + case JVM.sipush => code.emit(CONSTANT(Constant(u2))); size += 2 + case JVM.ldc => code.emit(CONSTANT(pool.getConstant(u1))); size += 1 + case JVM.ldc_w => code.emit(CONSTANT(pool.getConstant(u2))); size += 2 + case JVM.ldc2_w => code.emit(CONSTANT(pool.getConstant(u2))); size += 2 + case JVM.iload => code.emit(LOAD_LOCAL(code.getLocal(u1, INT))); size += 1 + case JVM.lload => code.emit(LOAD_LOCAL(code.getLocal(u1, LONG))); size += 1 + case JVM.fload => code.emit(LOAD_LOCAL(code.getLocal(u1, FLOAT))); size += 1 + case JVM.dload => code.emit(LOAD_LOCAL(code.getLocal(u1, DOUBLE))); size += 1 case JVM.aload => - val local = in.nextByte.toInt; size += 1 + val local = u1.toInt; size += 1 if (local == 0 && !method.isStatic) code.emit(THIS(method.symbol.owner)) else @@ -285,11 +284,11 @@ abstract class ICodeReader extends ClassfileParser { case JVM.caload => code.emit(LOAD_ARRAY_ITEM(CHAR)) case JVM.saload => code.emit(LOAD_ARRAY_ITEM(SHORT)) - case JVM.istore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, INT))); size += 1 - case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, LONG))); size += 1 - case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, FLOAT))); size += 1 - case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, DOUBLE))); size += 1 - case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, ObjectReference))); size += 1 + case JVM.istore => code.emit(STORE_LOCAL(code.getLocal(u1, INT))); size += 1 + case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(u1, LONG))); size += 1 + case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(u1, FLOAT))); size += 1 + case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(u1, DOUBLE))); size += 1 + case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(u1, ObjectReference))); size += 1 case JVM.istore_0 => code.emit(STORE_LOCAL(code.getLocal(0, INT))) case JVM.istore_1 => code.emit(STORE_LOCAL(code.getLocal(1, INT))) case JVM.istore_2 => code.emit(STORE_LOCAL(code.getLocal(2, INT))) @@ -373,9 +372,9 @@ abstract class ICodeReader extends ClassfileParser { case JVM.lxor => code.emit(CALL_PRIMITIVE(Logical(XOR, LONG))) case JVM.iinc => size += 2 - val local = code.getLocal(in.nextByte, INT) + val local = code.getLocal(u1, INT) code.emit(LOAD_LOCAL(local)) - code.emit(CONSTANT(Constant(in.nextByte))) + code.emit(CONSTANT(Constant(u1))) code.emit(CALL_PRIMITIVE(Arithmetic(ADD, INT))) code.emit(STORE_LOCAL(local)) @@ -425,14 +424,14 @@ abstract class ICodeReader extends ClassfileParser { size += padding in.bp += padding assert((pc + size % 4) != 0, pc) -/* var byte1 = in.nextByte; size += 1; - while (byte1 == 0) { byte1 = in.nextByte; size += 1; } - val default = byte1 << 24 | in.nextByte << 16 | in.nextByte << 8 | in.nextByte; +/* var byte1 = u1; size += 1; + while (byte1 == 0) { byte1 = u1; size += 1; } + val default = byte1 << 24 | u1 << 16 | u1 << 8 | u1; size = size + 3 */ - val default = pc + in.nextInt; size += 4 - val low = in.nextInt - val high = in.nextInt + val default = pc + u4; size += 4 + val low = u4 + val high = u4 size += 8 assert(low <= high, "Value low not <= high for tableswitch.") @@ -445,13 +444,13 @@ abstract class ICodeReader extends ClassfileParser { size += padding in.bp += padding assert((pc + size % 4) != 0, pc) - val default = pc + in.nextInt; size += 4 - val npairs = in.nextInt; size += 4 + val default = pc + u4; size += 4 + val npairs = u4; size += 4 var tags: List[List[Int]] = Nil var targets: List[Int] = Nil var i = 0 while (i < npairs) { - tags = List(in.nextInt) :: tags; size += 4 + tags = List(u4) :: tags; size += 4 targets = parseJumpTargetW :: targets; // parseJumpTargetW updates 'size' itself i += 1 } @@ -466,35 +465,35 @@ abstract class ICodeReader extends ClassfileParser { case JVM.return_ => code.emit(RETURN(UNIT)) case JVM.getstatic => - val field = pool.getMemberSymbol(in.nextChar, static = true); size += 2 + val field = pool.getMemberSymbol(u2, static = true); size += 2 if (field.hasModuleFlag) code emit LOAD_MODULE(field) else code emit LOAD_FIELD(field, isStatic = true) case JVM.putstatic => - val field = pool.getMemberSymbol(in.nextChar, static = true); size += 2 + val field = pool.getMemberSymbol(u2, static = true); size += 2 code.emit(STORE_FIELD(field, isStatic = true)) case JVM.getfield => - val field = pool.getMemberSymbol(in.nextChar, static = false); size += 2 + val field = pool.getMemberSymbol(u2, static = false); size += 2 code.emit(LOAD_FIELD(field, isStatic = false)) case JVM.putfield => - val field = pool.getMemberSymbol(in.nextChar, static = false); size += 2 + val field = pool.getMemberSymbol(u2, static = false); size += 2 code.emit(STORE_FIELD(field, isStatic = false)) case JVM.invokevirtual => - val m = pool.getMemberSymbol(in.nextChar, static = false); size += 2 + val m = pool.getMemberSymbol(u2, static = false); size += 2 code.emit(CALL_METHOD(m, Dynamic)) case JVM.invokeinterface => - val m = pool.getMemberSymbol(in.nextChar, static = false); size += 4 + val m = pool.getMemberSymbol(u2, static = false); size += 4 in.skip(2) code.emit(CALL_METHOD(m, Dynamic)) case JVM.invokespecial => - val m = pool.getMemberSymbol(in.nextChar, static = false); size += 2 + val m = pool.getMemberSymbol(u2, static = false); size += 2 val style = if (m.name == nme.CONSTRUCTOR || m.isPrivate) Static(onInstance = true) else SuperCall(m.owner.name) code.emit(CALL_METHOD(m, style)) case JVM.invokestatic => - val m = pool.getMemberSymbol(in.nextChar, static = true); size += 2 + val m = pool.getMemberSymbol(u2, static = true); size += 2 if (isBox(m)) code.emit(BOX(toTypeKind(m.info.paramTypes.head))) else if (isUnbox(m)) @@ -510,10 +509,10 @@ abstract class ICodeReader extends ClassfileParser { code.emit(INVOKE_DYNAMIC(poolEntry)) case JVM.new_ => - code.emit(NEW(REFERENCE(pool.getClassSymbol(in.nextChar)))) + code.emit(NEW(REFERENCE(pool.getClassSymbol(u2)))) size += 2 case JVM.newarray => - val kind = in.nextByte match { + val kind = u1 match { case T_BOOLEAN => BOOL case T_CHAR => CHAR case T_FLOAT => FLOAT @@ -527,35 +526,35 @@ abstract class ICodeReader extends ClassfileParser { code.emit(CREATE_ARRAY(kind, 1)) case JVM.anewarray => - val tpe = pool.getClassOrArrayType(in.nextChar); size += 2 + val tpe = pool.getClassOrArrayType(u2); size += 2 code.emit(CREATE_ARRAY(toTypeKind(tpe), 1)) case JVM.arraylength => code.emit(CALL_PRIMITIVE(ArrayLength(ObjectReference))); // the kind does not matter case JVM.athrow => code.emit(THROW(definitions.ThrowableClass)) case JVM.checkcast => - code.emit(CHECK_CAST(toTypeKind(pool.getClassOrArrayType(in.nextChar)))); size += 2 + code.emit(CHECK_CAST(toTypeKind(pool.getClassOrArrayType(u2)))); size += 2 case JVM.instanceof => - code.emit(IS_INSTANCE(toTypeKind(pool.getClassOrArrayType(in.nextChar)))); size += 2 + code.emit(IS_INSTANCE(toTypeKind(pool.getClassOrArrayType(u2)))); size += 2 case JVM.monitorenter => code.emit(MONITOR_ENTER()) case JVM.monitorexit => code.emit(MONITOR_EXIT()) case JVM.wide => size += 1 - toUnsignedByte(in.nextByte) match { - case JVM.iload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, INT))); size += 2 - case JVM.lload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, LONG))); size += 2 - case JVM.fload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, FLOAT))); size += 2 - case JVM.dload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, DOUBLE))); size += 2 - case JVM.aload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, ObjectReference))); size += 2 - case JVM.istore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, INT))); size += 2 - case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, LONG))); size += 2 - case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, FLOAT))); size += 2 - case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, DOUBLE))); size += 2 - case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, ObjectReference))); size += 2 + u1 match { + case JVM.iload => code.emit(LOAD_LOCAL(code.getLocal(u2, INT))); size += 2 + case JVM.lload => code.emit(LOAD_LOCAL(code.getLocal(u2, LONG))); size += 2 + case JVM.fload => code.emit(LOAD_LOCAL(code.getLocal(u2, FLOAT))); size += 2 + case JVM.dload => code.emit(LOAD_LOCAL(code.getLocal(u2, DOUBLE))); size += 2 + case JVM.aload => code.emit(LOAD_LOCAL(code.getLocal(u2, ObjectReference))); size += 2 + case JVM.istore => code.emit(STORE_LOCAL(code.getLocal(u2, INT))); size += 2 + case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(u2, LONG))); size += 2 + case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(u2, FLOAT))); size += 2 + case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(u2, DOUBLE))); size += 2 + case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(u2, ObjectReference))); size += 2 case JVM.ret => sys.error("Cannot handle jsr/ret") case JVM.iinc => size += 4 - val local = code.getLocal(in.nextChar, INT) - code.emit(CONSTANT(Constant(in.nextChar))) + val local = code.getLocal(u2, INT) + code.emit(CONSTANT(Constant(u2))) code.emit(CALL_PRIMITIVE(Arithmetic(ADD, INT))) code.emit(STORE_LOCAL(local)) case _ => sys.error("Invalid 'wide' operand") @@ -563,8 +562,8 @@ abstract class ICodeReader extends ClassfileParser { case JVM.multianewarray => size += 3 - val tpe = toTypeKind(pool getClassOrArrayType in.nextChar) - val dim = in.nextByte + val tpe = toTypeKind(pool getClassOrArrayType u2) + val dim = u1 // assert(dim == 1, "Cannot handle multidimensional arrays yet.") code emit CREATE_ARRAY(tpe, dim) @@ -590,14 +589,14 @@ abstract class ICodeReader extends ClassfileParser { pc = 0 while (pc < codeLength) parseInstruction() - val exceptionEntries = in.nextChar.toInt + val exceptionEntries = u2.toInt code.containsEHs = (exceptionEntries != 0) var i = 0 while (i < exceptionEntries) { // skip start end PC in.skip(4) // read the handler PC - code.jmpTargets += in.nextChar + code.jmpTargets += u2 // skip the exception type in.skip(2) i += 1 @@ -633,10 +632,8 @@ abstract class ICodeReader extends ClassfileParser { /** Return the icode class that should include members with the given flags. * There are two possible classes, the static part and the instance part. */ - def getCode(flags: Int): IClass = - if (isScalaModule) staticCode - else if ((flags & JAVA_ACC_STATIC) != 0) staticCode - else instanceCode + def getCode(flags: JavaAccFlags): IClass = + if (isScalaModule || flags.isStatic) staticCode else instanceCode class LinearCode { val instrs: ListBuffer[(Int, Instruction)] = new ListBuffer diff --git a/src/library/scala/reflect/package.scala b/src/library/scala/reflect/package.scala index 10e6d7d9a4..97d7da3f2d 100644 --- a/src/library/scala/reflect/package.scala +++ b/src/library/scala/reflect/package.scala @@ -1,5 +1,7 @@ package scala +import java.lang.reflect.{ AccessibleObject => jAccessibleObject } + package object reflect { // in the new scheme of things ClassManifests are aliased to ClassTags @@ -42,6 +44,18 @@ package object reflect { def classTag[T](implicit ctag: ClassTag[T]) = ctag + /** Make a java reflection object accessible, if it is not already + * and it is possible to do so. If a SecurityException is thrown in the + * attempt, it is caught and discarded. + */ + def ensureAccessible[T <: jAccessibleObject](m: T): T = { + if (!m.isAccessible) { + try m setAccessible true + catch { case _: SecurityException => } // does nothing + } + m + } + // anchor for the class tag materialization macro emitted during tag materialization in Implicits.scala // implementation is hardwired into `scala.reflect.reify.Taggers` // using the mechanism implemented in `scala.tools.reflect.FastTrack` diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index 753dd0205e..ea1f392e2b 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -158,13 +158,7 @@ object ScalaRunTime { // Java bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957 // More background at ticket #2318. - def ensureAccessible(m: JMethod): JMethod = { - if (!m.isAccessible) { - try m setAccessible true - catch { case _: SecurityException => () } - } - m - } + def ensureAccessible(m: JMethod): JMethod = scala.reflect.ensureAccessible(m) def checkInitialized[T <: AnyRef](x: T): T = if (x == null) throw new UninitializedError else x diff --git a/src/reflect/scala/reflect/internal/JMethodOrConstructor.scala b/src/reflect/scala/reflect/internal/JMethodOrConstructor.scala new file mode 100644 index 0000000000..3d1d1bf451 --- /dev/null +++ b/src/reflect/scala/reflect/internal/JMethodOrConstructor.scala @@ -0,0 +1,45 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ +package scala.reflect +package internal + +import java.lang.{ Class => jClass } +import java.lang.annotation.{ Annotation => jAnnotation } +import java.lang.reflect.{ + Member => jMember, Constructor => jConstructor, Method => jMethod, + AnnotatedElement => jAnnotatedElement, Type => jType, + TypeVariable => jTypeVariable +} + +/** This class tries to abstract over some of the duplication + * in java.lang.reflect.{ Method, Constructor }. + */ +class JMethodOrConstructor(val member: jMember with jAnnotatedElement) { + def isVarArgs: Boolean = member match { + case m: jMethod => m.isVarArgs + case m: jConstructor[_] => m.isVarArgs + } + def typeParams: Array[_ <: jTypeVariable[_]] = member match { + case m: jMethod => m.getTypeParameters + case m: jConstructor[_] => m.getTypeParameters + } + def paramTypes: Array[jType] = member match { + case m: jMethod => m.getGenericParameterTypes + case m: jConstructor[_] => m.getGenericParameterTypes + } + def paramAnnotations: Array[Array[jAnnotation]] = member match { + case m: jMethod => m.getParameterAnnotations + case m: jConstructor[_] => m.getParameterAnnotations + } + def resultType: jType = member match { + case m: jMethod => m.getGenericReturnType + case m: jConstructor[_] => classOf[Unit] + } +} + +object JMethodOrConstructor { + implicit def liftMethodToJmoc(m: jMethod): JMethodOrConstructor = new JMethodOrConstructor(m) + implicit def liftConstructorToJmoc(m: jConstructor[_]): JMethodOrConstructor = new JMethodOrConstructor(m) +} diff --git a/src/reflect/scala/reflect/internal/JavaAccFlags.scala b/src/reflect/scala/reflect/internal/JavaAccFlags.scala new file mode 100644 index 0000000000..4be1f828d3 --- /dev/null +++ b/src/reflect/scala/reflect/internal/JavaAccFlags.scala @@ -0,0 +1,83 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ +package scala.reflect +package internal + +import java.lang.{ Class => jClass } +import java.lang.reflect.{ Member => jMember, Constructor => jConstructor, Field => jField, Method => jMethod } +import JavaAccFlags._ +import ClassfileConstants._ + +/** A value class which encodes the access_flags (JVMS 4.1) + * for a field, method, or class. The low 16 bits are the same + * as those returned by java.lang.reflect.Member#getModifiers + * and found in the bytecode. + * + * The high bits encode whether the access flags are directly + * associated with a class, constructor, field, or method. + */ +final class JavaAccFlags private (val coded: Int) extends AnyVal { + private def has(mask: Int) = (flags & mask) != 0 + private def flagCarrierId = coded >>> 16 + private def flags = coded & 0xFFFF + + def isAbstract = has(JAVA_ACC_ABSTRACT) + def isAnnotation = has(JAVA_ACC_ANNOTATION) + def isBridge = has(JAVA_ACC_BRIDGE) + def isEnum = has(JAVA_ACC_ENUM) + def isFinal = has(JAVA_ACC_FINAL) + def isInterface = has(JAVA_ACC_INTERFACE) + def isNative = has(JAVA_ACC_NATIVE) + def isPrivate = has(JAVA_ACC_PRIVATE) + def isProtected = has(JAVA_ACC_PROTECTED) + def isPublic = has(JAVA_ACC_PUBLIC) + def isStatic = has(JAVA_ACC_STATIC) + def isStrictFp = has(JAVA_ACC_STRICT) + def isSuper = has(JAVA_ACC_SUPER) + def isSynchronized = has(JAVA_ACC_SYNCHRONIZED) + def isSynthetic = has(JAVA_ACC_SYNTHETIC) + def isTransient = has(JAVA_ACC_TRANSIENT) + def isVarargs = has(JAVA_ACC_VARARGS) + def isVolatile = has(JAVA_ACC_VOLATILE) + + /** Do these flags describe a member which has either protected or package access? + * Such access in java is encoded in scala as protected[foo] or private[foo], where + * `foo` is the defining package. + */ + def hasPackageAccessBoundary = !has(JAVA_ACC_PRIVATE | JAVA_ACC_PUBLIC) // equivalently, allows protected or package level access + def isPackageProtected = !has(JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC) + + def toJavaFlags: Int = flags + def toScalaFlags: Long = flagCarrierId match { + case Method | Constructor => FlagTranslation methodFlags flags + case Class => FlagTranslation classFlags flags + case _ => FlagTranslation fieldFlags flags + } +} + +object JavaAccFlags { + private val Unknown = 0 + private val Class = 1 + private val Field = 2 + private val Method = 3 + private val Constructor = 4 + + private def create(flagCarrier: Int, access_flags: Int): JavaAccFlags = + new JavaAccFlags((flagCarrier << 16) | (access_flags & 0xFFFF)) + + def classFlags(flags: Int): JavaAccFlags = create(Class, flags) + def methodFlags(flags: Int): JavaAccFlags = create(Method, flags) + def fieldFlags(flags: Int): JavaAccFlags = create(Field, flags) + def constructorFlags(flags: Int): JavaAccFlags = create(Constructor, flags) + + def apply(access_flags: Int): JavaAccFlags = create(Unknown, access_flags) + def apply(clazz: jClass[_]): JavaAccFlags = classFlags(clazz.getModifiers) + def apply(member: jMember): JavaAccFlags = member match { + case x: jConstructor[_] => constructorFlags(x.getModifiers) + case x: jMethod => methodFlags(x.getModifiers) + case x: jField => fieldFlags(x.getModifiers) + case _ => apply(member.getModifiers) + } +} diff --git a/src/reflect/scala/reflect/internal/PrivateWithin.scala b/src/reflect/scala/reflect/internal/PrivateWithin.scala index 9b99b94b41..5646ac82ae 100644 --- a/src/reflect/scala/reflect/internal/PrivateWithin.scala +++ b/src/reflect/scala/reflect/internal/PrivateWithin.scala @@ -1,23 +1,26 @@ package scala.reflect package internal -import ClassfileConstants._ +import java.lang.{ Class => jClass } +import java.lang.reflect.{ Member => jMember } trait PrivateWithin { self: SymbolTable => - def importPrivateWithinFromJavaFlags(sym: Symbol, jflags: Int): Symbol = { - if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC)) == 0) - // See ticket #1687 for an example of when topLevelClass is NoSymbol: it - // apparently occurs when processing v45.3 bytecode. - if (sym.enclosingTopLevelClass != NoSymbol) - sym.privateWithin = sym.enclosingTopLevelClass.owner - - // protected in java means package protected. #3946 - if ((jflags & JAVA_ACC_PROTECTED) != 0) - if (sym.enclosingTopLevelClass != NoSymbol) - sym.privateWithin = sym.enclosingTopLevelClass.owner - - sym + def propagatePackageBoundary(c: jClass[_], syms: Symbol*): Unit = + propagatePackageBoundary(JavaAccFlags(c), syms: _*) + def propagatePackageBoundary(m: jMember, syms: Symbol*): Unit = + propagatePackageBoundary(JavaAccFlags(m), syms: _*) + def propagatePackageBoundary(jflags: JavaAccFlags, syms: Symbol*) { + if (jflags.hasPackageAccessBoundary) + syms foreach setPackageAccessBoundary } -}
\ No newline at end of file + + // protected in java means package protected. #3946 + // See ticket #1687 for an example of when the enclosing top level class is NoSymbol; + // it apparently occurs when processing v45.3 bytecode. + def setPackageAccessBoundary(sym: Symbol): Symbol = ( + if (sym.enclosingTopLevelClass eq NoSymbol) sym + else sym setPrivateWithin sym.enclosingTopLevelClass.owner + ) +} diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index f324f5145a..3211bb7919 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -6,12 +6,14 @@ import scala.collection.mutable.WeakHashMap import java.lang.{Class => jClass, Package => jPackage} import java.lang.reflect.{ - Method => jMethod, Constructor => jConstructor, Modifier => jModifier, Field => jField, + Method => jMethod, Constructor => jConstructor, Field => jField, Member => jMember, Type => jType, TypeVariable => jTypeVariable, Array => jArray, + AccessibleObject => jAccessibleObject, GenericDeclaration, GenericArrayType, ParameterizedType, WildcardType, AnnotatedElement } import java.lang.annotation.{Annotation => jAnnotation} import java.io.IOException -import internal.MissingRequirementError +import scala.reflect.internal.{ MissingRequirementError, JavaAccFlags, JMethodOrConstructor } +import JavaAccFlags._ import internal.pickling.ByteCodecs import internal.ClassfileConstants._ import internal.pickling.UnPickler @@ -88,12 +90,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni // ----------- Caching ------------------------------------------------------------------ - private val classCache = new TwoWayCache[jClass[_], ClassSymbol] - private val packageCache = new TwoWayCache[Package, ModuleSymbol] - private val methodCache = new TwoWayCache[jMethod, MethodSymbol] + private val classCache = new TwoWayCache[jClass[_], ClassSymbol] + private val packageCache = new TwoWayCache[Package, ModuleSymbol] + private val methodCache = new TwoWayCache[jMethod, MethodSymbol] private val constructorCache = new TwoWayCache[jConstructor[_], MethodSymbol] - private val fieldCache = new TwoWayCache[jField, TermSymbol] - private val tparamCache = new TwoWayCache[jTypeVariable[_ <: GenericDeclaration], TypeSymbol] + private val fieldCache = new TwoWayCache[jField, TermSymbol] + private val tparamCache = new TwoWayCache[jTypeVariable[_ <: GenericDeclaration], TypeSymbol] private[runtime] def toScala[J: HasJavaClass, S](cache: TwoWayCache[J, S], key: J)(body: (JavaMirror, J) => S): S = cache.toScala(key){ @@ -101,38 +103,36 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni body(mirrorDefining(jclazz), key) } - private implicit val classHasJavaClass: HasJavaClass[jClass[_]] = - new HasJavaClass(identity) - private implicit val methHasJavaClass: HasJavaClass[jMethod] - = new HasJavaClass(_.getDeclaringClass) - private implicit val fieldHasJavaClass: HasJavaClass[jField] = - new HasJavaClass(_.getDeclaringClass) - private implicit val constrHasJavaClass: HasJavaClass[jConstructor[_]] = - new HasJavaClass(_.getDeclaringClass) + private implicit val classHasJavaClass: HasJavaClass[jClass[_]] = new HasJavaClass(identity) + private implicit val methHasJavaClass: HasJavaClass[jMethod] = new HasJavaClass(_.getDeclaringClass) + private implicit val fieldHasJavaClass: HasJavaClass[jField] = new HasJavaClass(_.getDeclaringClass) + private implicit val constrHasJavaClass: HasJavaClass[jConstructor[_]] = new HasJavaClass(_.getDeclaringClass) private implicit val tparamHasJavaClass: HasJavaClass[jTypeVariable[_ <: GenericDeclaration]] = new HasJavaClass ( (tparam: jTypeVariable[_ <: GenericDeclaration]) => { tparam.getGenericDeclaration match { - case jclazz: jClass[_] => jclazz - case jmeth: jMethod => jmeth.getDeclaringClass + case jclazz: jClass[_] => jclazz + case jmeth: jMethod => jmeth.getDeclaringClass case jconstr: jConstructor[_] => jconstr.getDeclaringClass } }) // ----------- Implementations of mirror operations and classes ------------------- - private def ErrorInnerClass(sym: Symbol) = throw new ScalaReflectionException(s"$sym is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") - private def ErrorInnerModule(sym: Symbol) = throw new ScalaReflectionException(s"$sym is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") - private def ErrorStaticClass(sym: Symbol) = throw new ScalaReflectionException(s"$sym is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") - private def ErrorStaticModule(sym: Symbol) = throw new ScalaReflectionException(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") - private def ErrorNotMember(sym: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}") - private def ErrorNotField(sym: Symbol) = throw new ScalaReflectionException(s"expected a field or an accessor method symbol, you provided $sym") - private def ErrorNonExistentField(sym: Symbol) = throw new ScalaReflectionException( + private def abort(msg: String) = throw new ScalaReflectionException(msg) + + private def ErrorInnerClass(sym: Symbol) = abort(s"$sym is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") + private def ErrorInnerModule(sym: Symbol) = abort(s"$sym is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") + private def ErrorStaticClass(sym: Symbol) = abort(s"$sym is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") + private def ErrorStaticModule(sym: Symbol) = abort(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") + private def ErrorNotMember(sym: Symbol, owner: Symbol) = abort(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}") + private def ErrorNotField(sym: Symbol) = abort(s"expected a field or an accessor method symbol, you provided $sym") + private def ErrorSetImmutableField(sym: Symbol) = abort(s"cannot set an immutable field ${sym.name}") + private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = abort(s"expected a constructor of $owner, you provided $sym") + private def ErrorFree(member: Symbol, freeType: Symbol) = abort(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}") + private def ErrorNonExistentField(sym: Symbol) = abort( sm"""Scala field ${sym.name} isn't represented as a Java field, neither it has a Java accessor method |note that private parameters of class constructors don't get mapped onto fields and/or accessors, |unless they are used outside of their declaring constructors.""") - private def ErrorSetImmutableField(sym: Symbol) = throw new ScalaReflectionException(s"cannot set an immutable field ${sym.name}") - private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a constructor of $owner, you provided $sym") - private def ErrorFree(member: Symbol, freeType: Symbol) = throw new ScalaReflectionException(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}") /** Helper functions for extracting typed values from a (Class[_], Any) * representing an annotation argument. @@ -210,7 +210,6 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case _ => body } } - private def checkMemberOf(sym: Symbol, owner: ClassSymbol) { if (sym.owner == AnyClass || sym.owner == AnyRefClass || sym.owner == ObjectClass) { // do nothing @@ -236,8 +235,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni if (staticClazz.isPrimitive) staticClazz else dynamicClazz } - private class JavaInstanceMirror[T: ClassTag](val instance: T) - extends InstanceMirror { + private class JavaInstanceMirror[T: ClassTag](val instance: T) extends InstanceMirror { def symbol = thisMirror.classSymbol(preciseClass(instance)) def reflectField(field: TermSymbol): FieldMirror = { checkMemberOf(field, symbol) @@ -269,12 +267,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol) extends FieldMirror { - lazy val jfield = { - val jfield = fieldToJava(symbol) - if (!jfield.isAccessible) jfield.setAccessible(true) - jfield - } - def get = jfield.get(receiver) + lazy val jfield = ensureAccessible(fieldToJava(symbol)) + def get = jfield get receiver def set(value: Any) = { if (!symbol.isMutable) ErrorSetImmutableField(symbol) jfield.set(receiver, value) @@ -338,15 +332,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } } } - - private abstract class JavaMethodMirror(val symbol: MethodSymbol) - extends MethodMirror { - lazy val jmeth = { - val jmeth = methodToJava(symbol) - if (!jmeth.isAccessible) jmeth.setAccessible(true) - jmeth - } - + private abstract class JavaMethodMirror(val symbol: MethodSymbol) extends MethodMirror { + lazy val jmeth = ensureAccessible(methodToJava(symbol)) def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) def jinvoke(jmeth: jMethod, receiver: Any, args: Seq[Any]): Any = { @@ -418,13 +405,13 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni if (!perfectMatch && !varargMatch) { val n_arguments = if (isVarArgsList(params)) s"${params.length - 1} or more" else s"${params.length}" val s_arguments = if (params.length == 1 && !isVarArgsList(params)) "argument" else "arguments" - throw new ScalaReflectionException(s"${showMethodSig(symbol)} takes $n_arguments $s_arguments") + abort(s"${showMethodSig(symbol)} takes $n_arguments $s_arguments") } def objReceiver = receiver.asInstanceOf[AnyRef] def objArg0 = args(0).asInstanceOf[AnyRef] def objArgs = args.asInstanceOf[Seq[AnyRef]] - def fail(msg: String) = throw new ScalaReflectionException(msg + ", it cannot be invoked with mirrors") + def fail(msg: String) = abort(msg + ", it cannot be invoked with mirrors") def invokePrimitiveMethod = { val jmeths = classOf[BoxesRunTime].getDeclaredMethods.filter(_.getName == nme.primitiveMethodName(symbol.name).toString) @@ -464,14 +451,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni extends MethodMirror { def bind(newReceiver: Any) = new JavaConstructorMirror(newReceiver.asInstanceOf[AnyRef], symbol) override val receiver = outer - lazy val jconstr = { - val jconstr = constructorToJava(symbol) - if (!jconstr.isAccessible) jconstr.setAccessible(true) - jconstr - } + lazy val jconstr = ensureAccessible(constructorToJava(symbol)) def apply(args: Any*): Any = { if (symbol.owner == ArrayClass) - throw new ScalaReflectionException("Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead") + abort("Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead") val effectiveArgs = if (outer == null) args.asInstanceOf[Seq[AnyRef]] @@ -532,7 +515,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } def javaClass(path: String): jClass[_] = - Class.forName(path, true, classLoader) + jClass.forName(path, true, classLoader) /** Does `path` correspond to a Java class with that fully qualified name in the current class loader? */ def tryJavaClass(path: String): Option[jClass[_]] = ( @@ -655,13 +638,22 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni sym setAnnotations (jann.getAnnotations map JavaAnnotationProxy).toList // SI-7065: we're not using getGenericExceptionTypes here to be consistent with ClassfileParser val jexTpes = jann match { - case jm: jMethod => jm.getExceptionTypes.toList + case jm: jMethod => jm.getExceptionTypes.toList case jconstr: jConstructor[_] => jconstr.getExceptionTypes.toList - case _ => Nil + case _ => Nil } jexTpes foreach (jexTpe => sym.addThrowsAnnotation(classSymbol(jexTpe))) } + private implicit class jClassOps(val clazz: jClass[_]) { + def javaFlags: JavaAccFlags = JavaAccFlags(clazz) + def scalaFlags: Long = javaFlags.toScalaFlags + } + private implicit class jMemberOps(val member: jMember) { + def javaFlags: JavaAccFlags = JavaAccFlags(member) + def scalaFlags: Long = javaFlags.toScalaFlags + } + /** * A completer that fills in the types of a Scala class and its companion object * by copying corresponding type info from a Java class. This completer is used @@ -681,14 +673,14 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni override def load(sym: Symbol): Unit = { debugInfo("completing from Java " + sym + "/" + clazz.fullName)//debug assert(sym == clazz || (module != NoSymbol && (sym == module || sym == module.moduleClass)), sym) - val flags = toScalaClassFlags(jclazz.getModifiers) + val flags = jclazz.scalaFlags clazz setFlag (flags | JAVA) if (module != NoSymbol) { module setFlag (flags & PRIVATE | JAVA) module.moduleClass setFlag (flags & PRIVATE | JAVA) } - relatedSymbols foreach (importPrivateWithinFromJavaFlags(_, jclazz.getModifiers)) + propagatePackageBoundary(jclazz, relatedSymbols: _*) copyAnnotations(clazz, jclazz) // to do: annotations to set also for module? @@ -720,28 +712,21 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni module.moduleClass setInfo new ClassInfoType(List(), newScope, module.moduleClass) } - def enter(sym: Symbol, mods: Int) = - (if (jModifier.isStatic(mods)) module.moduleClass else clazz).info.decls enter sym + def enter(sym: Symbol, mods: JavaAccFlags) = + ( if (mods.isStatic) module.moduleClass else clazz ).info.decls enter sym for (jinner <- jclazz.getDeclaredClasses) jclassAsScala(jinner) // inner class is entered as a side-effect // no need to call enter explicitly - pendingLoadActions = { () => - - for (jfield <- jclazz.getDeclaredFields) - enter(jfieldAsScala(jfield), jfield.getModifiers) - - for (jmeth <- jclazz.getDeclaredMethods) - enter(jmethodAsScala(jmeth), jmeth.getModifiers) - - for (jconstr <- jclazz.getConstructors) - enter(jconstrAsScala(jconstr), jconstr.getModifiers) - - } :: pendingLoadActions + pendingLoadActions ::= { () => + jclazz.getDeclaredFields foreach (f => enter(jfieldAsScala(f), f.javaFlags)) + jclazz.getDeclaredMethods foreach (m => enter(jmethodAsScala(m), m.javaFlags)) + jclazz.getConstructors foreach (c => enter(jconstrAsScala(c), c.javaFlags)) + } if (parentsLevel == 0) { - while (!pendingLoadActions.isEmpty) { + while (pendingLoadActions.nonEmpty) { val item = pendingLoadActions.head pendingLoadActions = pendingLoadActions.tail item() @@ -760,8 +745,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * If Java modifiers `mods` contain STATIC, return the module class * of the companion module of `clazz`, otherwise the class `clazz` itself. */ - private def followStatic(clazz: Symbol, mods: Int) = - if (jModifier.isStatic(mods)) clazz.companionModule.moduleClass else clazz + private def followStatic(clazz: Symbol, mods: JavaAccFlags) = + if (mods.isStatic) clazz.companionModule.moduleClass else clazz /** Methods which need to be treated with care * because they either are getSimpleName or call getSimpleName: @@ -797,7 +782,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni if (jclazz.isMemberClass) { val jEnclosingClass = jclazz.getEnclosingClass val sEnclosingClass = classToScala(jEnclosingClass) - followStatic(sEnclosingClass, jclazz.getModifiers) + followStatic(sEnclosingClass, jclazz.javaFlags) } else if (jclazz.isLocalClass0) { val jEnclosingMethod = jclazz.getEnclosingMethod if (jEnclosingMethod != null) { @@ -825,7 +810,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * The Scala owner of the Scala symbol corresponding to the Java member `jmember` */ private def sOwner(jmember: jMember): Symbol = { - followStatic(classToScala(jmember.getDeclaringClass), jmember.getModifiers) + followStatic(classToScala(jmember.getDeclaringClass), jmember.javaFlags) } /** @@ -866,7 +851,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def methodToScala1(jmeth: jMethod): MethodSymbol = { val jOwner = jmeth.getDeclaringClass val preOwner = classToScala(jOwner) - val owner = followStatic(preOwner, jmeth.getModifiers) + val owner = followStatic(preOwner, jmeth.javaFlags) (lookup(owner, jmeth.getName) suchThat (erasesTo(_, jmeth)) orElse jmethodAsScala(jmeth)) .asMethod } @@ -880,7 +865,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni toScala(constructorCache, jconstr)(_ constructorToScala1 _) private def constructorToScala1(jconstr: jConstructor[_]): MethodSymbol = { - val owner = followStatic(classToScala(jconstr.getDeclaringClass), jconstr.getModifiers) + val owner = followStatic(classToScala(jconstr.getDeclaringClass), jconstr.javaFlags) (lookup(owner, jconstr.getName) suchThat (erasesTo(_, jconstr)) orElse jconstrAsScala(jconstr)) .asMethod } @@ -1018,6 +1003,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case jmeth: jMethod => methodToScala(jmeth) case jconstr: jConstructor[_] => constructorToScala(jconstr) } + def reflectMemberToScala(m: jMember): Symbol = m match { + case x: GenericDeclaration => genericDeclarationToScala(x) + case x: jField => jfieldAsScala(x) + } /** * Given some Java type arguments, a corresponding list of Scala types, plus potentially @@ -1092,10 +1081,11 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def jfieldAsScala1(jfield: jField): TermSymbol = { val field = sOwner(jfield) - .newValue(newTermName(jfield.getName), NoPosition, toScalaFieldFlags(jfield.getModifiers)) + .newValue(newTermName(jfield.getName), NoPosition, jfield.scalaFlags) .setInfo(typeToScala(jfield.getGenericType)) - fieldCache enter (jfield, field) - importPrivateWithinFromJavaFlags(field, jfield.getModifiers) + + fieldCache.enter(jfield, field) + propagatePackageBoundary(jfield, field) copyAnnotations(field, jfield) field } @@ -1115,16 +1105,19 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def jmethodAsScala1(jmeth: jMethod): MethodSymbol = { val clazz = sOwner(jmeth) - val meth = clazz.newMethod(newTermName(jmeth.getName), NoPosition, toScalaMethodFlags(jmeth.getModifiers)) + val meth = clazz.newMethod(newTermName(jmeth.getName), NoPosition, jmeth.scalaFlags) methodCache enter (jmeth, meth) val tparams = jmeth.getTypeParameters.toList map createTypeParameter val paramtpes = jmeth.getGenericParameterTypes.toList map typeToScala val resulttpe = typeToScala(jmeth.getGenericReturnType) setMethType(meth, tparams, paramtpes, resulttpe) - importPrivateWithinFromJavaFlags(meth, jmeth.getModifiers) + propagatePackageBoundary(jmeth.javaFlags, meth) copyAnnotations(meth, jmeth) - if ((jmeth.getModifiers & JAVA_ACC_VARARGS) != 0) meth.setInfo(arrayToRepeated(meth.info)) - meth + + if (jmeth.javaFlags.isVarargs) + meth modifyInfo arrayToRepeated + else + meth } /** @@ -1139,13 +1132,13 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def jconstrAsScala1(jconstr: jConstructor[_]): MethodSymbol = { // [Martin] Note: I know there's a lot of duplication wrt jmethodAsScala, but don't think it's worth it to factor this out. val clazz = sOwner(jconstr) - val constr = clazz.newConstructor(NoPosition, toScalaMethodFlags(jconstr.getModifiers)) + val constr = clazz.newConstructor(NoPosition, jconstr.scalaFlags) constructorCache enter (jconstr, constr) val tparams = jconstr.getTypeParameters.toList map createTypeParameter val paramtpes = jconstr.getGenericParameterTypes.toList map typeToScala setMethType(constr, tparams, paramtpes, clazz.tpe_*) constr setInfo GenPolyType(tparams, MethodType(clazz.newSyntheticValueParams(paramtpes), clazz.tpe)) - importPrivateWithinFromJavaFlags(constr, jconstr.getModifiers) + propagatePackageBoundary(jconstr.javaFlags, constr) copyAnnotations(constr, jconstr) constr } @@ -1170,13 +1163,15 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni else if (clazz.isTopLevel) javaClass(clazz.javaClassName) else if (clazz.owner.isClass) { - val childOfClass = !clazz.owner.isModuleClass - val childOfTopLevel = clazz.owner.isTopLevel + val childOfClass = !clazz.owner.isModuleClass + val childOfTopLevel = clazz.owner.isTopLevel val childOfTopLevelObject = clazz.owner.isModuleClass && childOfTopLevel // suggested in https://issues.scala-lang.org/browse/SI-4023?focusedCommentId=54759#comment-54759 var ownerClazz = classToJava(clazz.owner.asClass) - if (childOfTopLevelObject) ownerClazz = Class.forName(ownerClazz.getName stripSuffix "$", true, ownerClazz.getClassLoader) + if (childOfTopLevelObject) + ownerClazz = jClass.forName(ownerClazz.getName stripSuffix "$", true, ownerClazz.getClassLoader) + val ownerChildren = ownerClazz.getDeclaredClasses var fullNameOfJavaClass = ownerClazz.getName @@ -1241,11 +1236,11 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * Pre: Scala type is already transformed to Java level. */ def typeToJavaClass(tpe: Type): jClass[_] = tpe match { - case ExistentialType(_, rtpe) => typeToJavaClass(rtpe) - case TypeRef(_, ArrayClass, List(elemtpe)) => jArrayClass(typeToJavaClass(elemtpe)) - case TypeRef(_, sym: ClassSymbol, _) => classToJava(sym.asClass) + case ExistentialType(_, rtpe) => typeToJavaClass(rtpe) + case TypeRef(_, ArrayClass, List(elemtpe)) => jArrayClass(typeToJavaClass(elemtpe)) + case TypeRef(_, sym: ClassSymbol, _) => classToJava(sym.asClass) case tpe @ TypeRef(_, sym: AliasTypeSymbol, _) => typeToJavaClass(tpe.dealias) - case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") + case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") } } |