@@ -25,6 +25,30 @@ import io.{AbstractFile, ZipArchive}
2525import scala .util .control .NonFatal
2626
2727object 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
5594class 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