Skip to content

Commit 081130c

Browse files
authored
Warn incompatible class files (#2391)
Fixes #2390
1 parent a290256 commit 081130c

1 file changed

Lines changed: 28 additions & 12 deletions

File tree

  • dependencies/src/main/java/com/google/cloud/tools/opensource/classpath

dependencies/src/main/java/com/google/cloud/tools/opensource/classpath/ClassDumper.java

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ static ClassDumper create(List<ClassPathEntry> entries) throws IOException {
8989
.collect(toImmutableList());
9090
checkArgument(
9191
unreadableFiles.isEmpty(), "Some jar files are not readable: %s", unreadableFiles);
92-
92+
9393
Map<String, ClassPathEntry> map = new HashMap<>();
9494
for (ClassPathEntry entry : entries) {
9595
for (String className : entry.getFileNames()) {
@@ -98,7 +98,7 @@ static ClassDumper create(List<ClassPathEntry> entries) throws IOException {
9898
}
9999
}
100100
}
101-
101+
102102
return new ClassDumper(entries, extensionClassLoader, map);
103103
}
104104

@@ -141,37 +141,54 @@ static boolean isArrayClass(String className) {
141141
return className.startsWith("[");
142142
}
143143

144-
/**
145-
* Returns a map from classes to the symbol references they contain.
146-
*/
144+
/** Returns a map from classes to the symbol references they contain. */
147145
SymbolReferences findSymbolReferences() throws IOException {
148146
SymbolReferences.Builder builder = new SymbolReferences.Builder();
149147

150148
for (ClassPathEntry jar : inputClassPath) {
149+
int totalClassFileCount = 0;
150+
int incompatibleClassFileCount = 0;
151151
for (JavaClass javaClass : listClasses(jar)) {
152+
totalClassFileCount++;
152153
if (isCompatibleClassFileVersion(javaClass)) {
153154
String className = javaClass.getClassName();
154155
// In listClasses(jar), ClassPathRepository creates JavaClass through the first JAR file
155156
// that contains the class. It may be different from "jar" for an overlapping class.
156157
ClassFile source = new ClassFile(findClassLocation(className), className);
157158
builder.addAll(findSymbolReferences(source, javaClass));
159+
} else {
160+
incompatibleClassFileCount++;
158161
}
159162
}
163+
if (incompatibleClassFileCount > 0) {
164+
logger.warning(
165+
String.format(
166+
"%s has %d (out of %d) incompatible class files (class file major version is outside %d <= v <= %d).",
167+
jar,
168+
incompatibleClassFileCount,
169+
totalClassFileCount,
170+
MINIMUM_CLASS_FILE_MAJOR_VERSION,
171+
MAXIMUM_CLASS_FILE_MAJOR_VERSION));
172+
}
160173
}
161174

162175
return builder.build();
163176
}
164177

178+
private static final int MINIMUM_CLASS_FILE_MAJOR_VERSION = 45;
179+
private static final int MAXIMUM_CLASS_FILE_MAJOR_VERSION = 52;
180+
165181
/**
166-
* Returns true if {@code javaClass} file format is compatible with this tool. Currently
167-
* Java 8 and earlier are supported.
182+
* Returns true if {@code javaClass} file format is compatible with this tool. Currently Java 8
183+
* (class file major version 52) and earlier are supported.
168184
*
169185
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.1">Java
170186
* Virtual Machine Specification: The ClassFile Structure: minor_version, major_version</a>
171187
*/
172188
private static boolean isCompatibleClassFileVersion(JavaClass javaClass) {
173189
int classFileMajorVersion = javaClass.getMajor();
174-
return 45 <= classFileMajorVersion && classFileMajorVersion <= 52;
190+
return MINIMUM_CLASS_FILE_MAJOR_VERSION <= classFileMajorVersion
191+
&& classFileMajorVersion <= MAXIMUM_CLASS_FILE_MAJOR_VERSION;
175192
}
176193

177194
private static SymbolReferences.Builder findSymbolReferences(
@@ -182,7 +199,7 @@ private static SymbolReferences.Builder findSymbolReferences(
182199
Constant[] constants = constantPool.getConstantPool();
183200
for (Constant constant : constants) {
184201
if (constant == null) {
185-
continue;
202+
continue;
186203
}
187204

188205
byte constantTag = constant.getTag();
@@ -262,8 +279,7 @@ private static ClassSymbol makeSymbol(
262279
return new ClassSymbol(targetClassName);
263280
}
264281

265-
private static MethodSymbol makeSymbol(
266-
ConstantCP constantMethodref, ConstantPool constantPool) {
282+
private static MethodSymbol makeSymbol(ConstantCP constantMethodref, ConstantPool constantPool) {
267283
String className = constantMethodref.getClass(constantPool);
268284
ConstantNameAndType constantNameAndType = constantNameAndType(constantMethodref, constantPool);
269285
String methodName = constantNameAndType.getName(constantPool);
@@ -303,7 +319,7 @@ static ImmutableSet<String> listInnerClassNames(JavaClass javaClass) {
303319
continue;
304320
}
305321
}
306-
322+
307323
// Class names stored in constant pool have '/' as separator. We want '.' (as binary name)
308324
String normalInnerClassName = innerClassName.replace('/', '.');
309325
innerClassNames.add(normalInnerClassName);

0 commit comments

Comments
 (0)