From f9b80d9beef469f3ed9fcbba77fdcdbabdbd12d0 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Wed, 2 Sep 2020 12:30:03 +0200 Subject: [PATCH] Add better reporting if ASM fails to parse a class with an unspecified RuntimeException --- .../de/thetaphi/forbiddenapis/AsmUtils.java | 9 ++++++++ .../de/thetaphi/forbiddenapis/Checker.java | 22 ++++++++++++++++++- .../RelatedClassLoadingException.java | 5 +++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/AsmUtils.java b/src/main/java/de/thetaphi/forbiddenapis/AsmUtils.java index e7f0fb79..2aeaeb02 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/AsmUtils.java +++ b/src/main/java/de/thetaphi/forbiddenapis/AsmUtils.java @@ -24,6 +24,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Locale; +import java.util.Objects; import java.util.regex.Pattern; import org.objectweb.asm.ClassReader; @@ -181,5 +182,13 @@ public static ClassReader readAndPatchClass(InputStream in) throws IOException { if (false) patchClassMajorVersion(bytecode, Opcodes.V15 + 1, Opcodes.V15); return new ClassReader(bytecode); } + + /** Returns true, if the given {@link RuntimeException} was caused by ASM's ClassReader */ + public static boolean isExceptionInAsmClassReader(RuntimeException re) { + // Because of javac bugs some class files are broken and cause RuntimeExceptions like AIOOBE + // We analyze stack trace if this is caused by ASM's ClassReader and not our code: + final StackTraceElement[] stack = re.getStackTrace(); + return stack.length > 0 && Objects.equals(ClassReader.class.getName(), stack[0].getClassName()); + } } diff --git a/src/main/java/de/thetaphi/forbiddenapis/Checker.java b/src/main/java/de/thetaphi/forbiddenapis/Checker.java index 3f236224..a2633d17 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/Checker.java +++ b/src/main/java/de/thetaphi/forbiddenapis/Checker.java @@ -309,6 +309,11 @@ public ClassSignature lookupRelatedClass(String internalName, String internalNam } } catch (IOException ioe) { throw new RelatedClassLoadingException(ioe, Type.getObjectType(internalNameOrig).getClassName()); + } catch (RuntimeException re) { + if (AsmUtils.isExceptionInAsmClassReader(re)) { + throw new RelatedClassLoadingException(re, Type.getObjectType(internalNameOrig).getClassName()); + } + throw re; } } @@ -419,8 +424,23 @@ private int checkClass(final ClassReader reader, Pattern suppressAnnotationsPatt } msg.append(": ").append(cause); msg.append(" (while looking up details about referenced class '").append(rcle.getClassName()).append("')"); - assert cause != null && (cause instanceof IOException || cause instanceof ClassNotFoundException); + assert cause != null && (cause instanceof IOException || cause instanceof ClassNotFoundException || cause instanceof RuntimeException); throw new ForbiddenApiException(msg.toString(), cause); + } catch (RuntimeException re) { + if (AsmUtils.isExceptionInAsmClassReader(re)) { + final StringBuilder msg = new StringBuilder() + .append("Failed to parse class '") + .append(className) + .append('\''); + final String source = scanner.getSourceFile(); + if (source != null) { + msg.append(" (").append(source).append(')'); + } + msg.append(": ").append(re); + throw new ForbiddenApiException(msg.toString(), re); + } + // else rethrow (it's occuring in our code): + throw re; } final List violations = scanner.getSortedViolations(); final Pattern splitter = Pattern.compile(Pattern.quote(ForbiddenViolation.SEPARATOR)); diff --git a/src/main/java/de/thetaphi/forbiddenapis/RelatedClassLoadingException.java b/src/main/java/de/thetaphi/forbiddenapis/RelatedClassLoadingException.java index ffe1eb98..4a1cf660 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/RelatedClassLoadingException.java +++ b/src/main/java/de/thetaphi/forbiddenapis/RelatedClassLoadingException.java @@ -33,6 +33,11 @@ public RelatedClassLoadingException(IOException e, String className) { this.className = className; } + public RelatedClassLoadingException(RuntimeException e, String className) { + super(e); + this.className = className; + } + public Exception getException() { return (Exception) getCause(); }