@@ -25,6 +25,30 @@ import io.{AbstractFile, ZipArchive}
25
25
import scala .util .control .NonFatal
26
26
27
27
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
+
28
52
/** Marker trait for unpicklers that can be embedded in classfiles. */
29
53
trait Embedded
30
54
@@ -50,6 +74,21 @@ object ClassfileParser {
50
74
mapOver(tp)
51
75
}
52
76
}
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
+
53
92
}
54
93
55
94
class ClassfileParser (
@@ -70,6 +109,7 @@ class ClassfileParser(
70
109
protected var classTParams : Map [Name , Symbol ] = Map ()
71
110
72
111
private var Scala2UnpicklingMode = Mode .Scala2Unpickling
112
+ private var classfileVersion : Header .Version = Header .Version .Unknown
73
113
74
114
classRoot.info = NoLoader ().withDecls(instanceScope)
75
115
moduleRoot.info = NoLoader ().withDecls(staticScope).withSourceModule(staticModule)
@@ -82,7 +122,7 @@ class ClassfileParser(
82
122
def run ()(using Context ): Option [Embedded ] = try ctx.base.reusableDataReader.withInstance { reader =>
83
123
implicit val reader2 = reader.reset(classfile)
84
124
report.debuglog(" [class] >> " + classRoot.fullName)
85
- parseHeader()
125
+ classfileVersion = parseHeader(classfile )
86
126
this .pool = new ConstantPool
87
127
val res = parseClass()
88
128
this .pool = null
@@ -91,22 +131,11 @@ class ClassfileParser(
91
131
catch {
92
132
case e : RuntimeException =>
93
133
if (ctx.debug) e.printStackTrace()
134
+ val addendum = Header .Version .brokenVersionAddendum(classfileVersion)
94
135
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(" " )}""" )
110
139
}
111
140
112
141
/** Return the class symbol of the given name. */
0 commit comments