Skip to content

Commit 0ff2ad3

Browse files
committed
track JDK version only when runtime exception occurs
1 parent 5d7e63d commit 0ff2ad3

File tree

3 files changed

+38
-11
lines changed

3 files changed

+38
-11
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ object ClassfileConstants {
1111
inline val JAVA_MINOR_VERSION = 3
1212

1313
inline val JAVA8_MAJOR_VERSION = 52
14-
inline val JAVA_LATEST_MAJOR_VERSION = 65
1514

1615
/** (see http://java.sun.com/docs/books/jvms/second_edition/jvms-clarify.html)
1716
*

Diff for: compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

+30-7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,28 @@ import dotty.tools.dotc.classpath.FileUtils.classToTasty
2727

2828
object ClassfileParser {
2929

30+
object Header:
31+
opaque type Version = Long
32+
33+
object Version:
34+
val Unknown: Version = -1L
35+
36+
def brokenVersionAddendum(classfileVersion: Version)(using Context): String =
37+
if classfileVersion.exists then
38+
val (maj, min) = (classfileVersion.majorVersion, classfileVersion.minorVersion)
39+
val scalaVersion = config.Properties.versionNumberString
40+
i""" (version $maj.$min),
41+
| please check the JDK compatibility of your Scala version ($scalaVersion)"""
42+
else
43+
""
44+
45+
def apply(major: Int, minor: Int): Version =
46+
(major.toLong << 32) | (minor.toLong & 0xFFFFFFFFL)
47+
extension (version: Version)
48+
def exists: Boolean = version != Unknown
49+
def majorVersion: Int = (version >> 32).toInt
50+
def minorVersion: Int = (version & 0xFFFFFFFFL).toInt
51+
3052
import ClassfileConstants._
3153

3254
/** Marker trait for unpicklers that can be embedded in classfiles. */
@@ -55,7 +77,7 @@ object ClassfileParser {
5577
}
5678
}
5779

58-
private[classfile] def parseHeader(classfile: AbstractFile)(using in: DataReader): Unit = {
80+
private[classfile] def parseHeader(classfile: AbstractFile)(using in: DataReader): Header.Version = {
5981
val magic = in.nextInt
6082
if (magic != JAVA_MAGIC)
6183
throw new IOException(s"class file '${classfile}' has wrong magic number 0x${toHexString(magic)}, should be 0x${toHexString(JAVA_MAGIC)}")
@@ -66,9 +88,7 @@ object ClassfileParser {
6688
(minorVersion < JAVA_MINOR_VERSION)))
6789
throw new IOException(
6890
s"class file '${classfile}' has unknown version $majorVersion.$minorVersion, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION")
69-
if majorVersion > JAVA_LATEST_MAJOR_VERSION then
70-
throw new IOException(
71-
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.")
91+
Header.Version(majorVersion, minorVersion)
7292
}
7393

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

263283
private var Scala2UnpicklingMode = Mode.Scala2Unpickling
284+
private var classfileVersion: Header.Version = Header.Version.Unknown
264285

265286
classRoot.info = NoLoader().withDecls(instanceScope)
266287
moduleRoot.info = NoLoader().withDecls(staticScope).withSourceModule(staticModule)
@@ -273,7 +294,7 @@ class ClassfileParser(
273294
def run()(using Context): Option[Embedded] = try ctx.base.reusableDataReader.withInstance { reader =>
274295
implicit val reader2 = reader.reset(classfile)
275296
report.debuglog("[class] >> " + classRoot.fullName)
276-
parseHeader(classfile)
297+
classfileVersion = parseHeader(classfile)
277298
this.pool = new ConstantPool
278299
val res = parseClass()
279300
this.pool = null
@@ -282,9 +303,11 @@ class ClassfileParser(
282303
catch {
283304
case e: RuntimeException =>
284305
if (ctx.debug) e.printStackTrace()
306+
val addendum = Header.Version.brokenVersionAddendum(classfileVersion)
285307
throw new IOException(
286-
i"""class file ${classfile.canonicalPath} is broken, reading aborted with ${e.getClass}
287-
|${Option(e.getMessage).getOrElse("")}""")
308+
i""" class file ${classfile.canonicalPath} is broken$addendum,
309+
| reading aborted with ${e.getClass}:
310+
| ${Option(e.getMessage).getOrElse("")}""")
288311
}
289312

290313
/** Return the class symbol of the given name. */

Diff for: compiler/src/dotty/tools/dotc/core/classfile/ClassfileTastyUUIDParser.scala

+8-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import dotty.tools.dotc.util._
1313
import dotty.tools.io.AbstractFile
1414
import dotty.tools.tasty.TastyReader
1515

16+
import ClassfileParser.Header
17+
1618
import java.io.IOException
1719
import java.lang.Integer.toHexString
1820
import java.util.UUID
@@ -22,20 +24,23 @@ class ClassfileTastyUUIDParser(classfile: AbstractFile)(ictx: Context) {
2224
import ClassfileConstants._
2325

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

2629
def checkTastyUUID(tastyUUID: UUID)(using Context): Unit = try ctx.base.reusableDataReader.withInstance { reader =>
2730
implicit val reader2 = reader.reset(classfile)
28-
ClassfileParser.parseHeader(classfile)
31+
this.classfileVersion = ClassfileParser.parseHeader(classfile)
2932
this.pool = new ConstantPool
3033
checkTastyAttr(tastyUUID)
3134
this.pool = null
3235
}
3336
catch {
3437
case e: RuntimeException =>
3538
if (ctx.debug) e.printStackTrace()
39+
val addendum = Header.Version.brokenVersionAddendum(classfileVersion)
3640
throw new IOException(
37-
i"""class file ${classfile.canonicalPath} is broken, reading aborted with ${e.getClass}
38-
|${Option(e.getMessage).getOrElse("")}""")
41+
i""" class file ${classfile.canonicalPath} is broken$addendum,
42+
| reading aborted with ${e.getClass}:
43+
| ${Option(e.getMessage).getOrElse("")}""")
3944
}
4045

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

0 commit comments

Comments
 (0)