Last active
June 29, 2023 22:42
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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>