Skip to content

Instantly share code, notes, and snippets.

@asdf913
Last active June 29, 2023 22:42
Show Gist options
  • Save asdf913/bf5c207f9c9dd69f46882fe778cde5d7 to your computer and use it in GitHub Desktop.
Save asdf913/bf5c207f9c9dd69f46882fe778cde5d7 to your computer and use it in GitHub Desktop.
IsRaiseThrowableOnly - Detect if a particular method only contains a statement which throws java.lang.Throwable only - Detection is done by Apache BCEL libary
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.ATHROW;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.DUP;
import org.apache.bcel.generic.FieldOrMethod;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.NEW;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.function.FailableFunction;
public class IsRaiseThrowableOnly {
public static void main(final String[] args) throws NoSuchMethodException {
//
System.out.println(isRaiseThrowableOnly(null, null));// false
//
// sun.awt.HeadlessToolkit
//
final Class<?> clz = forName("sun.awt.HeadlessToolkit");
//
// sun.awt.HeadlessToolkit.sync()
//
System.out.println(isRaiseThrowableOnly(clz, clz != null ? clz.getDeclaredMethod("sync") : null));// false
//
// sun.awt.HeadlessToolkit.getSystemClipboard()
//
System.out.println(isRaiseThrowableOnly(clz, clz != null ? clz.getDeclaredMethod("getSystemClipboard") : null));// true
//
}
private static boolean isRaiseThrowableOnly(final Class<?> clz, final Method method) {
//
try (final InputStream is = getResourceAsStream(clz,
String.format("/%1$s.class", StringUtils.replace(getName(clz), ".", "/")))) {
//
final org.apache.bcel.classfile.Method m = getMethod(
parse(testAndApply(Objects::nonNull, is, x -> new ClassParser(x, null), null)), method);
//
final MethodGen mg = testAndApply(Objects::nonNull, m, x -> new MethodGen(x, null, null), null);
//
final InstructionList il = mg != null ? mg.getInstructionList() : null;
//
final Instruction[] ins = il != null ? il.getInstructions() : null;
//
final ConstantPool cp = m != null ? m.getConstantPool() : null;
//
ConstantPoolGen cpg = null;
//
final int length = ins != null ? ins.length : 0;
//
String className = null;
//
for (int i = 0; i < length; i++) {
//
if (ins[i] instanceof InvokeInstruction ii) {
//
className = getClassName(ii, cpg = ObjectUtils.getIfNull(cpg,
() -> testAndApply(Objects::nonNull, cp, ConstantPoolGen::new, null)));
//
} // if
//
} // for
//
// The below method
//
// void methodA(){throw new RuntimeException();}
//
// generates
//
// new[187](3) 371
// dup[89](1)
// invokespecial[183](3) 373
// athrow[191](1)
//
// instructions
//
if (Objects.equals(Arrays.asList(NEW.class, DUP.class, INVOKESPECIAL.class, ATHROW.class),
toList(map(testAndApply(Objects::nonNull, ins, Arrays::stream, null), x -> getClass(x))))) {
//
final Class<?> c = forName(className);
//
if (isAssignableFrom(Throwable.class, c)) {
//
return true;
//
} // if
//
} // if
//
} catch (final IOException e) {
//
e.printStackTrace();
//
} // try
//
return false;
//
}
private static String getName(final Class<?> instance) {
return instance != null ? instance.getName() : null;
}
private static InputStream getResourceAsStream(final Class<?> instance, final String name) {
return instance != null && name != null ? instance.getResourceAsStream(name) : null;
}
private static <T, R, E extends Throwable> R testAndApply(final Predicate<T> predicate, final T value,
final FailableFunction<T, R, E> functionTrue, final FailableFunction<T, R, E> functionFalse) throws E {
return predicate != null && predicate.test(value) ? apply(functionTrue, value) : apply(functionFalse, value);
}
private static <T, R, E extends Throwable> R apply(final FailableFunction<T, R, E> instance, final T value)
throws E {
return instance != null ? instance.apply(value) : null;
}
private static JavaClass parse(final ClassParser instance) throws IOException {
return instance != null ? instance.parse() : null;
}
private static org.apache.bcel.classfile.Method getMethod(final JavaClass instance, final Method method) {
return instance != null && method != null ? instance.getMethod(method) : null;
}
private static String getClassName(final FieldOrMethod instance, final ConstantPoolGen cpg) {
return instance != null ? instance.getClassName(cpg) : null;
}
private static Class<?> forName(final String className) {
try {
return StringUtils.isNotBlank(className) ? Class.forName(className) : null;
} catch (final ClassNotFoundException e) {
return null;
}
}
private static boolean isAssignableFrom(final Class<?> a, final Class<?> b) {
return a != null && b != null && a.isAssignableFrom(b);
}
private static Class<?> getClass(final Object instance) {
return instance != null ? instance.getClass() : null;
}
private static <T, R> Stream<R> map(final Stream<T> instance, final Function<? super T, ? extends R> mapper) {
//
return instance != null && (Proxy.isProxyClass(getClass(instance)) || mapper != null) ? instance.map(mapper)
: null;
//
}
private static <T> List<T> toList(final Stream<T> instance) {
return instance != null ? instance.toList() : null;
}
}
@asdf913
Copy link
Author

asdf913 commented Jun 29, 2023

Maven Dependencies

<!-- https://mvnrepository.com/artifact/org.apache.bcel/bcel -->
<dependency>
 <groupId>org.apache.bcel</groupId>
 <artifactId>bcel</artifactId>
 <version>6.7.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-lang3</artifactId>
 <version>3.12.0</version>
</dependency>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment