Skip to content

Commit

Permalink
track JDK version only when runtime exception occurs
Browse files Browse the repository at this point in the history
  • Loading branch information
bishabosha committed Oct 31, 2023
1 parent 5d7e63d commit 0ff2ad3
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ object ClassfileConstants {
inline val JAVA_MINOR_VERSION = 3

inline val JAVA8_MAJOR_VERSION = 52
inline val JAVA_LATEST_MAJOR_VERSION = 65

/** (see http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html)
*
Expand Down
37 changes: 30 additions & 7 deletions compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,28 @@ import dotty.tools.dotc.classpath.FileUtils.classToTasty

object ClassfileParser {

object Header:
opaque type Version = Long

object Version:
val Unknown: Version = -1L

def brokenVersionAddendum(classfileVersion: Version)(using Context): String =
if classfileVersion.exists then
val (maj, min) = (classfileVersion.majorVersion, classfileVersion.minorVersion)
val scalaVersion = config.Properties.versionNumberString
i""" (version $maj.$min),
| please check the JDK compatibility of your Scala version ($scalaVersion)"""
else
""

def apply(major: Int, minor: Int): Version =
(major.toLong << 32) | (minor.toLong & 0xFFFFFFFFL)
extension (version: Version)
def exists: Boolean = version != Unknown
def majorVersion: Int = (version >> 32).toInt
def minorVersion: Int = (version & 0xFFFFFFFFL).toInt

import ClassfileConstants._

/** Marker trait for unpicklers that can be embedded in classfiles. */
Expand Down Expand Up @@ -55,7 +77,7 @@ object ClassfileParser {
}
}

private[classfile] def parseHeader(classfile: AbstractFile)(using in: DataReader): Unit = {
private[classfile] def parseHeader(classfile: AbstractFile)(using in: DataReader): Header.Version = {
val magic = in.nextInt
if (magic != JAVA_MAGIC)
throw new IOException(s"class file '${classfile}' has wrong magic number 0x${toHexString(magic)}, should be 0x${toHexString(JAVA_MAGIC)}")
Expand All @@ -66,9 +88,7 @@ object ClassfileParser {
(minorVersion < JAVA_MINOR_VERSION)))
throw new IOException(
s"class file '${classfile}' has unknown version $majorVersion.$minorVersion, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION")
if majorVersion > JAVA_LATEST_MAJOR_VERSION then
throw new IOException(
s"class file '${classfile}' has unknown version $majorVersion.$minorVersion, and was compiled by a newer JDK than supported by this Scala version, please update to a newer Scala version.")
Header.Version(majorVersion, minorVersion)
}

abstract class AbstractConstantPool(using in: DataReader) {
Expand Down Expand Up @@ -261,6 +281,7 @@ class ClassfileParser(
protected var classTParams: Map[Name, Symbol] = Map()

private var Scala2UnpicklingMode = Mode.Scala2Unpickling
private var classfileVersion: Header.Version = Header.Version.Unknown

classRoot.info = NoLoader().withDecls(instanceScope)
moduleRoot.info = NoLoader().withDecls(staticScope).withSourceModule(staticModule)
Expand All @@ -273,7 +294,7 @@ class ClassfileParser(
def run()(using Context): Option[Embedded] = try ctx.base.reusableDataReader.withInstance { reader =>
implicit val reader2 = reader.reset(classfile)
report.debuglog("[class] >> " + classRoot.fullName)
parseHeader(classfile)
classfileVersion = parseHeader(classfile)
this.pool = new ConstantPool
val res = parseClass()
this.pool = null
Expand All @@ -282,9 +303,11 @@ class ClassfileParser(
catch {
case e: RuntimeException =>
if (ctx.debug) e.printStackTrace()
val addendum = Header.Version.brokenVersionAddendum(classfileVersion)
throw new IOException(
i"""class file ${classfile.canonicalPath} is broken, reading aborted with ${e.getClass}
|${Option(e.getMessage).getOrElse("")}""")
i""" class file ${classfile.canonicalPath} is broken$addendum,
| reading aborted with ${e.getClass}:
| ${Option(e.getMessage).getOrElse("")}""")
}

/** Return the class symbol of the given name. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import dotty.tools.dotc.util._
import dotty.tools.io.AbstractFile
import dotty.tools.tasty.TastyReader

import ClassfileParser.Header

import java.io.IOException
import java.lang.Integer.toHexString
import java.util.UUID
Expand All @@ -22,20 +24,23 @@ class ClassfileTastyUUIDParser(classfile: AbstractFile)(ictx: Context) {
import ClassfileConstants._

private var pool: ConstantPool = _ // the classfile's constant pool
private var classfileVersion: Header.Version = Header.Version.Unknown

def checkTastyUUID(tastyUUID: UUID)(using Context): Unit = try ctx.base.reusableDataReader.withInstance { reader =>
implicit val reader2 = reader.reset(classfile)
ClassfileParser.parseHeader(classfile)
this.classfileVersion = ClassfileParser.parseHeader(classfile)
this.pool = new ConstantPool
checkTastyAttr(tastyUUID)
this.pool = null
}
catch {
case e: RuntimeException =>
if (ctx.debug) e.printStackTrace()
val addendum = Header.Version.brokenVersionAddendum(classfileVersion)
throw new IOException(
i"""class file ${classfile.canonicalPath} is broken, reading aborted with ${e.getClass}
|${Option(e.getMessage).getOrElse("")}""")
i""" class file ${classfile.canonicalPath} is broken$addendum,
| reading aborted with ${e.getClass}:
| ${Option(e.getMessage).getOrElse("")}""")
}

private def checkTastyAttr(tastyUUID: UUID)(using ctx: Context, in: DataReader): Unit = {
Expand Down

0 comments on commit 0ff2ad3

Please sign in to comment.