@@ -29,6 +29,28 @@ import scala.compiletime.uninitialized
29
29
30
30
object ClassfileParser {
31
31
32
+ object Header :
33
+ opaque type Version = Long
34
+
35
+ object Version :
36
+ val Unknown : Version = - 1L
37
+
38
+ def brokenVersionAddendum (classfileVersion : Version )(using Context ): String =
39
+ if classfileVersion.exists then
40
+ val (maj, min) = (classfileVersion.majorVersion, classfileVersion.minorVersion)
41
+ val scalaVersion = config.Properties .versionNumberString
42
+ i """ (version $maj. $min),
43
+ | please check the JDK compatibility of your Scala version ( $scalaVersion) """
44
+ else
45
+ " "
46
+
47
+ def apply (major : Int , minor : Int ): Version =
48
+ (major.toLong << 32 ) | (minor.toLong & 0xFFFFFFFFL)
49
+ extension (version : Version )
50
+ def exists : Boolean = version != Unknown
51
+ def majorVersion : Int = (version >> 32 ).toInt
52
+ def minorVersion : Int = (version & 0xFFFFFFFFL).toInt
53
+
32
54
import ClassfileConstants ._
33
55
34
56
/** Marker trait for unpicklers that can be embedded in classfiles. */
@@ -57,6 +79,20 @@ object ClassfileParser {
57
79
}
58
80
}
59
81
82
+ private [classfile] def parseHeader (classfile : AbstractFile )(using in : DataReader ): Header .Version = {
83
+ val magic = in.nextInt
84
+ if (magic != JAVA_MAGIC )
85
+ throw new IOException (s " class file ' ${classfile}' has wrong magic number 0x ${toHexString(magic)}, should be 0x ${toHexString(JAVA_MAGIC )}" )
86
+ val minorVersion = in.nextChar.toInt
87
+ val majorVersion = in.nextChar.toInt
88
+ if ((majorVersion < JAVA_MAJOR_VERSION ) ||
89
+ ((majorVersion == JAVA_MAJOR_VERSION ) &&
90
+ (minorVersion < JAVA_MINOR_VERSION )))
91
+ throw new IOException (
92
+ s " class file ' ${classfile}' has unknown version $majorVersion. $minorVersion, should be at least $JAVA_MAJOR_VERSION. $JAVA_MINOR_VERSION" )
93
+ Header .Version (majorVersion, minorVersion)
94
+ }
95
+
60
96
abstract class AbstractConstantPool (using in : DataReader ) {
61
97
protected val len = in.nextChar
62
98
protected val starts = new Array [Int ](len)
@@ -247,6 +283,7 @@ class ClassfileParser(
247
283
protected var classTParams : Map [Name , Symbol ] = Map ()
248
284
249
285
private var Scala2UnpicklingMode = Mode .Scala2Unpickling
286
+ private var classfileVersion : Header .Version = Header .Version .Unknown
250
287
251
288
classRoot.info = NoLoader ().withDecls(instanceScope)
252
289
moduleRoot.info = NoLoader ().withDecls(staticScope).withSourceModule(staticModule)
@@ -259,7 +296,7 @@ class ClassfileParser(
259
296
def run ()(using Context ): Option [Embedded ] = try ctx.base.reusableDataReader.withInstance { reader =>
260
297
implicit val reader2 = reader.reset(classfile)
261
298
report.debuglog(" [class] >> " + classRoot.fullName)
262
- parseHeader()
299
+ classfileVersion = parseHeader(classfile )
263
300
this .pool = new ConstantPool
264
301
val res = parseClass()
265
302
this .pool = null
@@ -268,22 +305,11 @@ class ClassfileParser(
268
305
catch {
269
306
case e : RuntimeException =>
270
307
if (ctx.debug) e.printStackTrace()
308
+ val addendum = Header .Version .brokenVersionAddendum(classfileVersion)
271
309
throw new IOException (
272
- i """ class file ${classfile.canonicalPath} is broken, reading aborted with ${e.getClass}
273
- | ${Option (e.getMessage).getOrElse(" " )}""" )
274
- }
275
-
276
- private def parseHeader ()(using in : DataReader ): Unit = {
277
- val magic = in.nextInt
278
- if (magic != JAVA_MAGIC )
279
- throw new IOException (s " class file ' ${classfile}' has wrong magic number 0x ${toHexString(magic)}, should be 0x ${toHexString(JAVA_MAGIC )}" )
280
- val minorVersion = in.nextChar.toInt
281
- val majorVersion = in.nextChar.toInt
282
- if ((majorVersion < JAVA_MAJOR_VERSION ) ||
283
- ((majorVersion == JAVA_MAJOR_VERSION ) &&
284
- (minorVersion < JAVA_MINOR_VERSION )))
285
- throw new IOException (
286
- s " class file ' ${classfile}' has unknown version $majorVersion. $minorVersion, should be at least $JAVA_MAJOR_VERSION. $JAVA_MINOR_VERSION" )
310
+ i """ class file ${classfile.canonicalPath} is broken $addendum,
311
+ | reading aborted with ${e.getClass}:
312
+ | ${Option (e.getMessage).getOrElse(" " )}""" )
287
313
}
288
314
289
315
/** Return the class symbol of the given name. */
0 commit comments