summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala')
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala282
1 files changed, 157 insertions, 125 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index 809effe18b..f146419a73 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -8,16 +8,18 @@ package tools.nsc
package symtab
package classfile
-import java.io.{ File, IOException }
+import java.io.{ByteArrayInputStream, DataInputStream, File, IOException}
import java.lang.Integer.toHexString
-import scala.collection.{ mutable, immutable }
-import scala.collection.mutable.{ ListBuffer, ArrayBuffer }
+
+import scala.collection.{immutable, mutable}
+import scala.collection.mutable.{ArrayBuffer, ListBuffer}
import scala.annotation.switch
-import scala.reflect.internal.{ JavaAccFlags }
-import scala.reflect.internal.pickling.{PickleBuffer, ByteCodecs}
+import scala.reflect.internal.JavaAccFlags
+import scala.reflect.internal.pickling.{ByteCodecs, PickleBuffer}
import scala.reflect.io.NoAbstractFile
+import scala.tools.nsc.util.ClassPath
import scala.tools.nsc.io.AbstractFile
-import scala.tools.nsc.util.ClassFileLookup
+import scala.util.control.NonFatal
/** This abstract class implements a class file parser.
*
@@ -43,8 +45,8 @@ abstract class ClassfileParser {
*/
protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol
- /** The way of the class file lookup used by the compiler. */
- def classFileLookup: ClassFileLookup[AbstractFile]
+ /** The compiler classpath. */
+ def classPath: ClassPath
import definitions._
import scala.reflect.internal.ClassfileConstants._
@@ -53,18 +55,18 @@ abstract class ClassfileParser {
protected type ThisConstantPool <: ConstantPool
protected def newConstantPool: ThisConstantPool
- protected var file: AbstractFile = _ // the class file
- protected var in: AbstractFileReader = _ // the class file reader
- protected var clazz: Symbol = _ // the class symbol containing dynamic members
- protected var staticModule: Symbol = _ // the module symbol containing static members
- protected var instanceScope: Scope = _ // the scope of all instance definitions
- protected var staticScope: Scope = _ // the scope of all static definitions
- protected var pool: ThisConstantPool = _ // the classfile's constant pool
- protected var isScala: Boolean = _ // does class file describe a scala class?
- protected var isScalaAnnot: Boolean = _ // does class file describe a scala class with its pickled info in an annotation?
- protected var isScalaRaw: Boolean = _ // this class file is a scala class with no pickled info
- protected var busy: Symbol = _ // lock to detect recursive reads
- protected var currentClass: Name = _ // JVM name of the current class
+ protected var file: AbstractFile = _ // the class file
+ protected var in: AbstractFileReader = _ // the class file reader
+ protected var clazz: ClassSymbol = _ // the class symbol containing dynamic members
+ protected var staticModule: ModuleSymbol = _ // the module symbol containing static members
+ protected var instanceScope: Scope = _ // the scope of all instance definitions
+ protected var staticScope: Scope = _ // the scope of all static definitions
+ protected var pool: ThisConstantPool = _ // the classfile's constant pool
+ protected var isScala: Boolean = _ // does class file describe a scala class?
+ protected var isScalaAnnot: Boolean = _ // does class file describe a scala class with its pickled info in an annotation?
+ protected var isScalaRaw: Boolean = _ // this class file is a scala class with no pickled info
+ protected var busy: Symbol = _ // lock to detect recursive reads
+ protected var currentClass: Name = _ // JVM name of the current class
protected var classTParams = Map[Name,Symbol]()
protected var srcfile0 : Option[AbstractFile] = None
protected def moduleClass: Symbol = staticModule.moduleClass
@@ -132,17 +134,21 @@ abstract class ClassfileParser {
finally loaders.parentsLevel -= 1
}
- def parse(file: AbstractFile, root: Symbol): Unit = {
- debuglog("[class] >> " + root.fullName)
-
+ /**
+ * `clazz` and `module` are the class and module symbols corresponding to the classfile being
+ * parsed. Note that the ClassfileLoader unconditionally creates both of these symbols, they may
+ * may get invalidated later on (.exists).
+ *
+ * Note that using `companionModule` / `companionClass` does not always work to navigate between
+ * those two symbols, namely when they are shadowed by a type / value in the a package object
+ * (scala-dev#248).
+ */
+ def parse(file: AbstractFile, clazz: ClassSymbol, module: ModuleSymbol): Unit = {
this.file = file
- pushBusy(root) {
+ pushBusy(clazz) {
this.in = new AbstractFileReader(file)
- this.clazz = if (root.isModule) root.companionClass else root
- // WARNING! do no use clazz.companionModule to find staticModule.
- // In a situation where root can be defined, but its companionClass not,
- // this would give incorrect results (see SI-5031 in separate compilation scenario)
- this.staticModule = if (root.isModule) root else root.companionModule
+ this.clazz = clazz
+ this.staticModule = module
this.isScala = false
parseHeader()
@@ -206,10 +212,14 @@ abstract class ClassfileParser {
case name: Name => name
case _ =>
val start = firstExpecting(index, CONSTANT_UTF8)
- recordAtIndex(newTermName(in.buf, start + 2, in.getChar(start).toInt), index)
+ val len = in.getChar(start).toInt
+ recordAtIndex(TermName(fromMUTF8(in.buf, start, len + 2)), index)
}
)
+ private def fromMUTF8(bytes: Array[Byte], offset: Int, len: Int): String =
+ new DataInputStream(new ByteArrayInputStream(bytes, offset, len)).readUTF
+
/** Return the name found at given index in the constant pool, with '/' replaced by '.'. */
def getExternalName(index: Int): Name = {
if (index <= 0 || len <= index)
@@ -271,7 +281,7 @@ abstract class ClassfileParser {
* 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)
else values(index) match {
case tp: Type => tp
@@ -283,7 +293,7 @@ abstract class ClassfileParser {
case _ => recordAtIndex(classNameToSymbol(name), index).tpe_*
}
}
- )
+ }
def getType(index: Int): Type = getType(null, index)
def getType(sym: Symbol, index: Int): Type = sigToType(sym, getExternalName(index))
@@ -356,63 +366,43 @@ abstract class ClassfileParser {
abort(s"bad constant pool tag ${in.buf(start)} at byte $start")
}
- private def loadClassSymbol(name: Name): Symbol = {
- val file = classFileLookup findClassFile name.toString getOrElse {
- // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented
- // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects
- // that are not in their correct place (see bug for details)
-
- // TODO More consistency with use of stub symbols in `Unpickler`
- // - better owner than `NoSymbol`
- // - remove eager warning
- val msg = s"Class $name not found - continuing with a stub."
- if ((!settings.isScaladoc) && (settings.verbose || settings.developer)) warning(msg)
- return NoSymbol.newStubSymbol(name.toTypeName, msg)
- }
- val completer = new loaders.ClassfileLoader(file)
- var owner: Symbol = rootMirror.RootClass
- var sym: Symbol = NoSymbol
- var ss: Name = null
- var start = 0
- var end = name indexOf '.'
-
- while (end > 0) {
- ss = name.subName(start, end)
- sym = owner.info.decls lookup ss
- if (sym == NoSymbol) {
- sym = owner.newPackage(ss.toTermName) setInfo completer
- sym.moduleClass setInfo completer
- owner.info.decls enter sym
- }
- owner = sym.moduleClass
- start = end + 1
- end = name.indexOf('.', start)
- }
- ss = name.subName(0, start)
- owner.info.decls lookup ss orElse {
- sym = owner.newClass(ss.toTypeName) setInfoAndEnter completer
- debuglog("loaded "+sym+" from file "+file)
- sym
- }
+ def stubClassSymbol(name: Name): Symbol = {
+ // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented
+ // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects
+ // that are not in their correct place (see bug for details)
+
+ // TODO More consistency with use of stub symbols in `Unpickler`
+ // - better owner than `NoSymbol`
+ // - remove eager warning
+ val msg = s"Class $name not found - continuing with a stub."
+ if ((!settings.isScaladoc) && (settings.verbose || settings.developer)) warning(msg)
+ NoSymbol.newStubSymbol(name.toTypeName, msg)
}
- /** FIXME - we shouldn't be doing ad hoc lookups in the empty package.
- * The method called "getClassByName" should either return the class or not.
- */
- private def lookupClass(name: Name) = (
+ private def lookupClass(name: Name) = try {
if (name containsChar '.')
- rootMirror getClassByName name // see tickets #2464, #3756
+ rootMirror getClassByName name
else
+ // FIXME - we shouldn't be doing ad hoc lookups in the empty package, getClassByName should return the class
definitions.getMember(rootMirror.EmptyPackageClass, name.toTypeName)
- )
+ } catch {
+ // The handler
+ // - prevents crashes with deficient InnerClassAttributes (SI-2464, 0ce0ad5)
+ // - was referenced in the bugfix commit for SI-3756 (4fb0d53), not sure why
+ // - covers the case when a type alias in a package object shadows a class symbol,
+ // getClassByName throws a MissingRequirementError (scala-dev#248)
+ case _: FatalError =>
+ // getClassByName can throw a MissingRequirementError (which extends FatalError)
+ // definitions.getMember can throw a FatalError, for example in pos/t5165b
+ stubClassSymbol(name)
+ }
/** Return the class symbol of the given name. */
def classNameToSymbol(name: Name): Symbol = {
if (innerClasses contains name)
innerClasses innerSymbol name
else
- try lookupClass(name)
- catch { case _: FatalError => loadClassSymbol(name) }
+ lookupClass(name)
}
def parseClass() {
@@ -441,13 +431,10 @@ abstract class ClassfileParser {
}
val isTopLevel = !(currentClass containsChar '$') // Java class name; *don't* try to to use Scala name decoding (SI-7532)
-
- val c = if (isTopLevel) pool.getClassSymbol(nameIdx) else clazz
if (isTopLevel) {
- if (c != clazz) {
- if ((clazz eq NoSymbol) && (c ne NoSymbol)) clazz = c
- else mismatchError(c)
- }
+ val c = pool.getClassSymbol(nameIdx)
+ // scala-dev#248: when a type alias (in a package object) shadows a class symbol, getClassSymbol returns a stub
+ if (!c.isInstanceOf[StubSymbol] && c != clazz) mismatchError(c)
}
addEnclosingTParams(clazz)
@@ -542,7 +529,7 @@ abstract class ClassfileParser {
devWarning(s"no linked class for java enum $sym in ${sym.owner}. A referencing class file might be missing an InnerClasses entry.")
case linked =>
if (!linked.isSealed)
- // Marking the enum class SEALED | ABSTRACT enables exhaustiveness checking.
+ // Marking the enum class SEALED | ABSTRACT enables exhaustiveness checking. See also JavaParsers.
// This is a bit of a hack and requires excluding the ABSTRACT flag in the backend, see method javaClassfileFlags.
linked setFlag (SEALED | ABSTRACT)
linked addChild sym
@@ -566,6 +553,7 @@ abstract class ClassfileParser {
val name = readName()
val sym = ownerForFlags(jflags).newMethod(name.toTermName, NoPosition, sflags)
var info = pool.getType(sym, u2)
+ var removedOuterParameter = false
if (name == nme.CONSTRUCTOR)
info match {
case MethodType(params, restpe) =>
@@ -580,6 +568,7 @@ abstract class ClassfileParser {
* ClassfileParser for 1 executes, and clazz.owner is the package.
*/
assert(params.head.tpe.typeSymbol == clazz.owner || clazz.owner.hasPackageFlag, params.head.tpe.typeSymbol + ": " + clazz.owner)
+ removedOuterParameter = true
params.tail
case _ =>
params
@@ -599,7 +588,7 @@ abstract class ClassfileParser {
// parsed from SignatureATTR
sym setInfo info
propagatePackageBoundary(jflags, sym)
- parseAttributes(sym, info)
+ parseAttributes(sym, info, removedOuterParameter)
if (jflags.isVarargs)
sym modifyInfo arrayToRepeated
@@ -782,7 +771,7 @@ abstract class ClassfileParser {
GenPolyType(ownTypeParams, tpe)
} // sigToType
- def parseAttributes(sym: Symbol, symtype: Type) {
+ def parseAttributes(sym: Symbol, symtype: Type, removedOuterParameter: Boolean = false) {
def convertTo(c: Constant, pt: Type): Constant = {
if (pt.typeSymbol == BooleanClass && c.tag == IntTag)
Constant(c.value != 0)
@@ -815,6 +804,31 @@ abstract class ClassfileParser {
val c1 = convertTo(c, symtype)
if (c1 ne null) sym.setInfo(ConstantType(c1))
else devWarning(s"failure to convert $c to $symtype")
+ case tpnme.MethodParametersATTR =>
+ def readParamNames(): Unit = {
+ import scala.tools.asm.Opcodes.ACC_SYNTHETIC
+ val paramCount = u1
+ var i = 0
+ if (removedOuterParameter && i < paramCount) {
+ in.skip(4)
+ i += 1
+ }
+ var remainingParams = sym.paramss.head // Java only has exactly one parameter list
+ while (i < paramCount) {
+ val name = pool.getName(u2)
+ val access = u2
+ if (remainingParams.nonEmpty) {
+ val param = remainingParams.head
+ remainingParams = remainingParams.tail
+ if ((access & ACC_SYNTHETIC) != ACC_SYNTHETIC) { // name not synthetic
+ param.name = name.encode
+ param.resetFlag(SYNTHETIC)
+ }
+ }
+ i += 1
+ }
+ }
+ readParamNames()
case tpnme.ScalaSignatureATTR =>
if (!isScalaAnnot) {
devWarning(s"symbol ${sym.fullName} has pickled signature in attribute")
@@ -830,16 +844,19 @@ abstract class ClassfileParser {
// Java annotations on classes / methods / fields with RetentionPolicy.RUNTIME
case tpnme.RuntimeAnnotationATTR =>
if (isScalaAnnot || !isScala) {
- val scalaSigAnnot = parseAnnotations(attrLen)
- if (isScalaAnnot)
- scalaSigAnnot match {
- case Some(san: AnnotationInfo) =>
- val bytes =
- san.assocs.find({ _._1 == nme.bytes }).get._2.asInstanceOf[ScalaSigBytes].bytes
- unpickler.unpickle(bytes, 0, clazz, staticModule, in.file.name)
- case None =>
- throw new RuntimeException("Scala class file does not contain Scala annotation")
- }
+ // For Scala classfiles we are only interested in the scala signature annotations. Other
+ // annotations should be skipped (the pickle contains the symbol's annotations).
+ // Skipping them also prevents some spurious warnings / errors related to SI-7014,
+ // SI-7551, pos/5165b
+ val scalaSigAnnot = parseAnnotations(onlyScalaSig = isScalaAnnot)
+ if (isScalaAnnot) scalaSigAnnot match {
+ case Some(san: AnnotationInfo) =>
+ val bytes =
+ san.assocs.find({ _._1 == nme.bytes }).get._2.asInstanceOf[ScalaSigBytes].bytes
+ unpickler.unpickle(bytes, 0, clazz, staticModule, in.file.name)
+ case None =>
+ throw new RuntimeException("Scala class file does not contain Scala annotation")
+ }
debuglog("[class] << " + sym.fullName + sym.annotationsString)
}
else
@@ -873,6 +890,24 @@ abstract class ClassfileParser {
}
}
+ def skipAnnotArg(): Unit = {
+ u1 match {
+ case STRING_TAG | BOOL_TAG | BYTE_TAG | CHAR_TAG | SHORT_TAG |
+ INT_TAG | LONG_TAG | FLOAT_TAG | DOUBLE_TAG | CLASS_TAG =>
+ in.skip(2)
+
+ case ENUM_TAG =>
+ in.skip(4)
+
+ case ARRAY_TAG =>
+ val num = u2
+ for (i <- 0 until num) skipAnnotArg()
+
+ case ANNOTATION_TAG =>
+ parseAnnotation(u2, onlyScalaSig = true)
+ }
+ }
+
def parseAnnotArg: Option[ClassfileAnnotArg] = {
val tag = u1
val index = u2
@@ -906,7 +941,7 @@ abstract class ClassfileParser {
if (hasError) None
else Some(ArrayAnnotArg(arr.toArray))
case ANNOTATION_TAG =>
- parseAnnotation(index) map (NestedAnnotArg(_))
+ parseAnnotation(index, onlyScalaSig = false) map (NestedAnnotArg(_))
}
}
@@ -933,7 +968,7 @@ abstract class ClassfileParser {
/* Parse and return a single annotation. If it is malformed,
* return None.
*/
- def parseAnnotation(attrNameIndex: Int): Option[AnnotationInfo] = try {
+ def parseAnnotation(attrNameIndex: Int, onlyScalaSig: Boolean): Option[AnnotationInfo] = try {
val attrType = pool.getType(attrNameIndex)
val nargs = u2
val nvpairs = new ListBuffer[(Name, ClassfileAnnotArg)]
@@ -954,18 +989,17 @@ abstract class ClassfileParser {
case None => hasError = true
}
else
- parseAnnotArg match {
+ if (onlyScalaSig) skipAnnotArg()
+ else parseAnnotArg match {
case Some(c) => nvpairs += ((name, c))
case None => hasError = true
}
}
if (hasError) None
else Some(AnnotationInfo(attrType, List(), nvpairs.toList))
- }
- catch {
- case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found
- case ex: java.lang.Error => throw ex
- case ex: Throwable =>
+ } 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
@@ -974,7 +1008,6 @@ abstract class ClassfileParser {
// and that should never be swallowed silently.
warning(s"Caught: $ex while parsing annotations in ${in.file}")
if (settings.debug) ex.printStackTrace()
-
None // ignore malformed annotations
}
@@ -996,19 +1029,18 @@ 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] = {
+ def parseAnnotations(onlyScalaSig: Boolean): Option[AnnotationInfo] = {
val nAttr = u2
var scalaSigAnnot: Option[AnnotationInfo] = None
- for (n <- 0 until nAttr)
- parseAnnotation(u2) match {
- case Some(scalaSig) if (scalaSig.atp == ScalaSignatureAnnotation.tpe) =>
- scalaSigAnnot = Some(scalaSig)
- case Some(scalaSig) if (scalaSig.atp == ScalaLongSignatureAnnotation.tpe) =>
- scalaSigAnnot = Some(scalaSig)
- case Some(annot) =>
- sym.addAnnotation(annot)
- case None =>
- }
+ for (n <- 0 until nAttr) parseAnnotation(u2, onlyScalaSig) match {
+ case Some(scalaSig) if scalaSig.atp == ScalaSignatureAnnotation.tpe =>
+ scalaSigAnnot = Some(scalaSig)
+ case Some(scalaSig) if scalaSig.atp == ScalaLongSignatureAnnotation.tpe =>
+ scalaSigAnnot = Some(scalaSig)
+ case Some(annot) =>
+ sym.addAnnotation(annot)
+ case None =>
+ }
scalaSigAnnot
}
@@ -1025,7 +1057,6 @@ abstract class ClassfileParser {
def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile) {
def jflags = entry.jflags
- val completer = new loaders.ClassfileLoader(file)
val name = entry.originalName
val sflags = jflags.toScalaFlags
val owner = ownerForFlags(jflags)
@@ -1039,8 +1070,11 @@ abstract class ClassfileParser {
val (innerClass, innerModule) = if (file == NoAbstractFile) {
(newStub(name.toTypeName), newStub(name.toTermName))
} else {
- val cls = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer
- val mod = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer
+ val cls = owner.newClass(name.toTypeName, NoPosition, sflags)
+ val mod = owner.newModule(name.toTermName, NoPosition, sflags)
+ val completer = new loaders.ClassfileLoader(file, cls, mod)
+ cls setInfo completer
+ mod setInfo completer
mod.moduleClass setInfo loaders.moduleClassLoader
List(cls, mod.moduleClass) foreach (_.associatedFile = file)
(cls, mod)
@@ -1064,7 +1098,7 @@ abstract class ClassfileParser {
for (entry <- innerClasses.entries) {
// create a new class member for immediate inner classes
if (entry.outerName == currentClass) {
- val file = classFileLookup.findClassFile(entry.externalName.toString)
+ val file = classPath.findClassFile(entry.externalName.toString)
enterClassAndModule(entry, file.getOrElse(NoAbstractFile))
}
}
@@ -1083,8 +1117,6 @@ abstract class ClassfileParser {
val attrName = readTypeName()
val attrLen = u4
attrName match {
- case tpnme.SignatureATTR =>
- in.skip(attrLen)
case tpnme.ScalaSignatureATTR =>
isScala = true
val pbuf = new PickleBuffer(in.buf, in.bp, in.bp + attrLen)
@@ -1151,10 +1183,10 @@ abstract class ClassfileParser {
private def innerSymbol(entry: InnerClassEntry): Symbol = {
val name = entry.originalName.toTypeName
val enclosing = entry.enclosing
- val member = (
+ val member = {
if (enclosing == clazz) entry.scope lookup name
else lookupMemberAtTyperPhaseIfPossible(enclosing, name)
- )
+ }
def newStub = {
enclosing
.newStubSymbol(name, s"Unable to locate class corresponding to inner class entry for $name in owner ${entry.outerName}")