Skip to content

Commit 9bdef0b

Browse files
Backport "error when reading class file with unknown newer jdk version" to LTS (#20862)
Backports #18618 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents f38e5e0 + 9c651d1 commit 9bdef0b

File tree

1 file changed

+45
-16
lines changed

1 file changed

+45
-16
lines changed

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,30 @@ import io.{AbstractFile, ZipArchive}
2525
import scala.util.control.NonFatal
2626

2727
object ClassfileParser {
28+
object Header:
29+
opaque type Version = Long
30+
31+
object Version:
32+
val Unknown: Version = -1L
33+
34+
def brokenVersionAddendum(classfileVersion: Version)(using Context): String =
35+
if classfileVersion.exists then
36+
val (maj, min) = (classfileVersion.majorVersion, classfileVersion.minorVersion)
37+
val scalaVersion = config.Properties.versionNumberString
38+
i""" (version $maj.$min),
39+
| please check the JDK compatibility of your Scala version ($scalaVersion)"""
40+
else
41+
""
42+
43+
def apply(major: Int, minor: Int): Version =
44+
(major.toLong << 32) | (minor.toLong & 0xFFFFFFFFL)
45+
extension (version: Version)
46+
def exists: Boolean = version != Unknown
47+
def majorVersion: Int = (version >> 32).toInt
48+
def minorVersion: Int = (version & 0xFFFFFFFFL).toInt
49+
50+
import ClassfileConstants.*
51+
2852
/** Marker trait for unpicklers that can be embedded in classfiles. */
2953
trait Embedded
3054

@@ -50,6 +74,21 @@ object ClassfileParser {
5074
mapOver(tp)
5175
}
5276
}
77+
78+
private[classfile] def parseHeader(classfile: AbstractFile)(using in: DataReader): Header.Version = {
79+
val magic = in.nextInt
80+
if (magic != JAVA_MAGIC)
81+
throw new IOException(s"class file '${classfile}' has wrong magic number 0x${toHexString(magic)}, should be 0x${toHexString(JAVA_MAGIC)}")
82+
val minorVersion = in.nextChar.toInt
83+
val majorVersion = in.nextChar.toInt
84+
if ((majorVersion < JAVA_MAJOR_VERSION) ||
85+
((majorVersion == JAVA_MAJOR_VERSION) &&
86+
(minorVersion < JAVA_MINOR_VERSION)))
87+
throw new IOException(
88+
s"class file '${classfile}' has unknown version $majorVersion.$minorVersion, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION")
89+
Header.Version(majorVersion, minorVersion)
90+
}
91+
5392
}
5493

5594
class ClassfileParser(
@@ -70,6 +109,7 @@ class ClassfileParser(
70109
protected var classTParams: Map[Name, Symbol] = Map()
71110

72111
private var Scala2UnpicklingMode = Mode.Scala2Unpickling
112+
private var classfileVersion: Header.Version = Header.Version.Unknown
73113

74114
classRoot.info = NoLoader().withDecls(instanceScope)
75115
moduleRoot.info = NoLoader().withDecls(staticScope).withSourceModule(staticModule)
@@ -82,7 +122,7 @@ class ClassfileParser(
82122
def run()(using Context): Option[Embedded] = try ctx.base.reusableDataReader.withInstance { reader =>
83123
implicit val reader2 = reader.reset(classfile)
84124
report.debuglog("[class] >> " + classRoot.fullName)
85-
parseHeader()
125+
classfileVersion = parseHeader(classfile)
86126
this.pool = new ConstantPool
87127
val res = parseClass()
88128
this.pool = null
@@ -91,22 +131,11 @@ class ClassfileParser(
91131
catch {
92132
case e: RuntimeException =>
93133
if (ctx.debug) e.printStackTrace()
134+
val addendum = Header.Version.brokenVersionAddendum(classfileVersion)
94135
throw new IOException(
95-
i"""class file ${classfile.canonicalPath} is broken, reading aborted with ${e.getClass}
96-
|${Option(e.getMessage).getOrElse("")}""")
97-
}
98-
99-
private def parseHeader()(using in: DataReader): Unit = {
100-
val magic = in.nextInt
101-
if (magic != JAVA_MAGIC)
102-
throw new IOException(s"class file '${classfile}' has wrong magic number 0x${toHexString(magic)}, should be 0x${toHexString(JAVA_MAGIC)}")
103-
val minorVersion = in.nextChar.toInt
104-
val majorVersion = in.nextChar.toInt
105-
if ((majorVersion < JAVA_MAJOR_VERSION) ||
106-
((majorVersion == JAVA_MAJOR_VERSION) &&
107-
(minorVersion < JAVA_MINOR_VERSION)))
108-
throw new IOException(
109-
s"class file '${classfile}' has unknown version $majorVersion.$minorVersion, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION")
136+
i""" class file ${classfile.canonicalPath} is broken$addendum,
137+
| reading aborted with ${e.getClass}:
138+
| ${Option(e.getMessage).getOrElse("")}""")
110139
}
111140

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

0 commit comments

Comments
 (0)