How use bytebuddy to detect unreachable code? - java

I am struggling here with ASM to create a visitor that can remove unreachable code. For now, the code is the same as provided in ASM 4.0 Guide, that is:
public class RemoveDeadCodeAdapter extends MethodVisitor {
String owner;
MethodVisitor next;
public RemoveDeadCodeAdapter(String owner, int access, String name,
String desc, MethodVisitor mv) {
super(Opcodes.ASM4, new MethodNode(access, name, desc, null, null));
this.owner = owner;
next = mv;
}
#Override public void visitEnd() {
MethodNode mn = (MethodNode) mv;
Analyzer<BasicValue> a =
new Analyzer<BasicValue>(new BasicInterpreter());
try {
a.analyze(owner, mn);
Frame<BasicValue>[] frames = a.getFrames();
AbstractInsnNode[] insns = mn.instructions.toArray();
for (int i = 0; i < frames.length; ++i) {
if (frames[i] == null && !(insns[i] instanceof LabelNode)) {
mn.instructions.remove(insns[i]);
}
}
} catch (AnalyzerException ignored) {
}
mn.accept(next);
}
}
So, the question is: is there any way to achieve this with Bytebuddy? Because Bytebuddy seems to be pretty easy to work. If yes, could anybody tell me what would be the process?

Byte Buddy is no code analysis tool, it is intended for code generation based on a class's API, i.e. it operates based on fields and methods. For deleting dead code, you should find a static tool or a code coverage agent for doing so.

Related

Java 8 Lambda Expression validation

I was reading the article about validation using Predicates here. I am trying to implement it in Spring Boot framework where I am having some questions.
In the code:
public class LamdaPersonValidator implements PersonValidator {
public void validate(Person person) {
notNull.and(between(2, 12)).test(person.getFirstName()).throwIfInvalid("firstname");
notNull.and(between(4, 30)).test(person.getLastName()).throwIfInvalid("secondname");
notNull.and(between(3, 50)).and(contains("#")).test(person.getEmail()).throwIfInvalid("email");
intBetween(0, 110).test(person.getAge()).throwIfInvalid("age");
}
}
it is not mentioned on what could be the standard way to check if the person object in the validate method is itself is null. Is it OK to just put a null check like if(persone != null) { // notNull.and..} or there could be some better way to do null check.
Another thing is suppose, I want to do some custom checks like if person exists in the database or not. In this case, I need to connect to the database to check so. In this case, I need to Autowire the interface where static variable and method is not possible.
So, what could be best approach to use this when doing validation from the database?
We are not the code judges of the holy inquisition, so it’s not our duty to tell you, whether it is “OK to just put a null check”.
Of course, it is ok to write is as an ordinary if statement, like we did the last 25 years, just like it is ok to invent a verbose framework encapsulating the null check and bringing the term “lambda” somehow into it. The only remaining question would be if you really intent to write if(person != null) { /* do the checks */ }, in other words, allow a null person to pass the test.
In case, you want to reject null persons (which would be more reasonable), there is already a possibility to write it without an explicit test, Objects.requireNonNull, since Java 7, which demonstrates that you don’t need an “everything’s better with lambdas” framework to achieve that goal. Generally, you can write validating code reasonably with conventional code, contrary to the article’s example, utilizing simple tools like the && operator and putting common code into methods:
public void validate(Person person) {
Objects.requireNonNull(person, "person is null");
checkString(person.getFirstName(), "first name", 2, 12);
checkString(person.getLastName(), "last name", 4, 30);
checkString(person.getEmail(), "email", 3, 50);
if(!person.getEmail().contains("#"))
throw new IllegalArgumentException("invalid email format");
checkBounds(person.getAge(), "age", 0, 110);
}
private void checkString(String nameValue, String nameType, int min, int max) {
Objects.requireNonNull(nameValue, () -> nameType+" is null");
checkBounds(nameValue.length(), nameType, min, max);
}
private void checkBounds(int value, String valueType, int min, int max) {
if(value < min || value > max)
throw new IllegalArgumentException(valueType+" is not within ["+min+" "+max+']');
}
This does the same as your code, without any framework with “Lambda” in its name, still having readable validation code and allowing to reuse the checking code. That said, instead of a class name LamdaPersonValidator, which reflects how you implemented it, you should use class names reflecting the responsibilities of a class. Clearly, a validator responsible for validating some properties of an object should not get mixed up with a validator checking the presence of an entity in the database. The latter is an entirely different topic on its own and should also be in a question on its own.
The code above is only meant to be an example how to achieve the same as the original code. It should never appear in production code in this form, as it is a demonstration of a widespread anti-pattern, to apply arbitrary unreasonable constraints to properties, most likely invented by the programmer while writing the code.
Why does it assume that a person must have a first name and a last name and why does it assume that a first name must have at least two and at most twelve characters, while the last name must be between four and thirty characters?
It’s actually not even characters, as the association between char units and actual characters is not 1:1.
A must read for every programmer thinking about implementing name validation, is Falsehoods Programmers Believe About Names (With Examples).
Likewise, Wikipedia’s List of the verified oldest people lists one hundred people having an age above 110.
And there is no reason to assume that an email address can’t have more than fifty characters. A true validation of the correct Email pattern may turn out to be something to omit deliberately…
You can write GenericValidator like that also:
Write AbstractValidator class for common work:
public abstract class AbstractValidator {
private Map<Predicate, String> validatorMap = new LinkedHashMap<>();
protected List<String> messages;
public AbstractValidator() {
this.messages = new ArrayList<>();
}
protected <E> AbstractValidator add(Predicate<E> predicate, String reason) {
validatorMap.put(predicate, reason);
return this;
}
protected AbstractValidator apply(String fieldName, Object val) {
AtomicBoolean flag= new AtomicBoolean(true);
this.validatorMap.forEach((modifier, reason) -> {
if (flag.get() && !modifier.test(val)) {
String message = MessageFormat.format("{0} {1}", fieldName, reason);
messages.add(message);
flag.set(false);
}
});
this.validatorMap.clear();
return this;
}
public void end(String exceptionStatus) {
Optional.ofNullable(messages).filter(CollectionUtils::isEmpty)
.orElseThrow(() -> {
RuntimeException ex = new RuntimeException(exceptionStatus, messages);
messages.clear();
return ex;
});
}
}
Write GenericValidator class which will extend the AbstractValidator for your validation implementation:
public class GenericValidator extends AbstractValidator {
private GenericValidator() {
super();
}
public static GenericValidator of() {
return new GenericValidator();
}
public GenericValidator nonNull() {
add(Objects::nonNull, "Field value is null");
return this;
}
public GenericValidator notEmpty() {
add(StringUtils::isNotEmpty, "Field is empty");
return this;
}
public GenericValidator min(int min) {
add(s -> ((String) s).length() >= min, "Field min size is " + min);
return this;
}
public GenericValidator max(int max) {
add(s -> ((String) s).length() <= max, "Field max size is " + max);
return this;
}
public GenericValidator notEmptyList() {
add(CollectionUtils::isNotEmpty, "Field List is null/Empty");
return this;
}
public GenericValidator apply(String fieldName, Object val) {
return (GenericValidator) super.apply(fieldName, val);
}
}
Please test accordingly. Example for test cases:
class GenericValidatorTest {
#Test
void genericValidationSuccessCase() {
Abc abc = new Abc();
abc.setName("a");
abc.setVal(1);
abc.setAbslist(Collections.singletonList(new ChildAbc()));
GenericValidator of = GenericValidator.of();
of.nonNull().apply("abc", abc).end(GENERIC_JSON_SERIALIZATION);
of.notEmpty().min(1).max(1).apply("name", abc.getName())
.nonNull().apply("value", abc.getVal())
.notEmptyList().apply("childAbc", abc.getAbslist())
.end(GENERIC_JSON_SERIALIZATION);
}
#Test
void genericValidationWhenObjectNull() {
GenericValidator of = GenericValidator.of();
Assertions.assertThrows(BusinessException.class, () -> of.nonNull()
.apply("abc", null).end(GENERIC_JSON_SERIALIZATION));
}
#Test
void genericValidationWithExceptionInput() {
Abc abc = new Abc();
abc.setName("a");
abc.setVal(1);
GenericValidator of = GenericValidator.of();
of.nonNull().apply("abc", abc).end(GENERIC_JSON_SERIALIZATION);
GenericValidator apply = of.notEmpty().min(1).max(1).apply("name", abc.getName())
.nonNull().apply("value", abc.getVal())
.notEmptyList().apply("childAbc", abc.getAbslist());
Assertions.assertThrows(BusinessException.class, () -> apply.end(GENERIC_JSON_SERIALIZATION));
}
}
class Abc {
String name;
Integer val;
List<ChildAbc> abslist;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getVal() {
return val;
}
public void setVal(Integer val) {
this.val = val;
}
public List<ChildAbc> getAbslist() {
return abslist;
}
public void setAbslist(List<ChildAbc> abslist) {
this.abslist = abslist;
}
}
class ChildAbc {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

Getting verify error when working with asm java

So basicly Im trying to add a simple System.out.println("hey");
at the end of a method. I used the tree API. I do however keep getting this error:
java.lang.VerifyError: Expecting a stackmap frame at branch target 38
This is my code:
public class MethodNodeCustom extends MethodNode {
public MethodNodeCustom(int paramInt, String paramString1, String paramString2, String paramString3, String[] paramArrayOfString) {
this(327680, paramInt, paramString1, paramString2, paramString3, paramArrayOfString);
return;
}
#SuppressWarnings({ "unchecked", "rawtypes" })
public MethodNodeCustom(int paramInt1, int paramInt2, String paramString1, String paramString2, String paramString3,
String[] paramArrayOfString) {
super(paramInt1);
this.access = paramInt2;
this.name = paramString1;
this.desc = paramString2;
this.signature = paramString3;
this.exceptions = new ArrayList((paramArrayOfString == null) ? 0 : paramArrayOfString.length);
int i = ((paramInt2 & 0x400) != 0) ? 1 : 0;
if (i == 0)
this.localVariables = new ArrayList(5);
this.tryCatchBlocks = new ArrayList();
if (paramArrayOfString != null)
this.exceptions.addAll(Arrays.asList(paramArrayOfString));
this.instructions = new InsnList();
}
#Override
public void visitEnd() {
AbstractInsnNode label = instructions.getLast();
instructions.remove(instructions.getLast());
instructions.remove(instructions.getLast());
visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", Type.getDescriptor(PrintStream.class));
visitLdcInsn("Cracked by damm ass pro skills");
visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
visitInsn(Opcodes.RETURN);
instructions.add(label);
super.visitEnd();
}
}
And this is my class node:
public class ClassNodeCustom extends ClassNode {
public ClassNodeCustom() {
super(ASMContentHandler.ASM4);
}
#SuppressWarnings("unchecked")
#Override
public MethodVisitor visitMethod(int paramInt, String paramString1, String paramString2, String paramString3, String[] paramArrayOfString) {
MethodNode localMethodNode = new MethodNodeCustom(paramInt, paramString1, paramString2, paramString3, paramArrayOfString);
this.methods.add(localMethodNode);
return localMethodNode;
}
}
And this is how I "inject" the code (I load it directly from the jar thats why it is using a zipFile)
InputStream in = zipFile.getInputStream(entry);
ClassReader cr = new ClassReader(in);
ClassNodeCustom node = new ClassNodeCustom();
cr.accept(node, 0);
ClassWriter cw = new ClassWriter(0);
node.accept(cw);
And like I said when ever I run it I get the verify error is there any way for me to solve it or any smarter way for me to "inject" that code ?
If you are adding code at the end of a method, you are adding it after its last instruction which is always a goto, switch, throw or return statement when compiling Java code. Even when compiling a method without an explicit return statement like
void foo() { }
you are actully compiling
void foo() { return; }
where the final return is implicit. With your additions, you change the method to
void foo() {
return;
System.out.println("hey");
}
Such unreachable code is forbidden by javac but perfectly legal in byte code. For unreachable code, it is however required that you are prepending it with a stack map frame that describes the state of the stack and the local variable array at that point. It would be easy enough to add a description of an empty frame at this point but I assume that you want to add the code before the return statement.
To implement this, ASM offers an AdviceAdapter that allows you to add code before return statements. As far as I know, there is nothing similar for the tree API but you can simply look for a return node within any method's instruction list and add the code prior to it.

Using instances of a class as reference

I need some help on my class design or better said a reference to a common design pattern for a problem.
I am working in the aircraft industry. So far my programming skills are VBA and basic JAVA applications.
As an engineer my task is to create CAD Models for fixating components in and on to aircraft kitchens. To ensure a high reusability and to reduce development time I want to create a program which can recommend previous solutions.
Basically each aircraft operator can select from a catalog which galleys/kitchens (Monument) it would like to have installed. Inside these Monuments are multiple compartments. Inside a compartment we can install multiple equipment’s/components.
I would like to write a program which can tell me "you have installed these components together before -> In this compartment -> in that aircraft for that customer"
I have modeled the compartment, the monuments, and the aircraft. Each class extends form the same class BaseHolder:
public abstract class BaseHolder <I> {
private final ArrayList <I> heldItems = new ArrayList<I>();
public boolean addItem(final I i){
Objects.requireNonNull(i, "cannot add NULL");
return heldItems.add(i);
}
public boolean removeItem(I i){
return heldItems.remove(i);
}
public boolean contains(I i){
return heldItems.contains(i);
}
public int itemCount(){
return heldItems.size();
}
public boolean isEmpty(){
return heldItems.isEmpty();
}
public void Clear() {
heldItems.clear();
}
protected List<I> getHeldItems(){
return heldItems;
}
public I getElement(int n){
return heldItems.get(n);
}
}
public class Aircraft extends BaseHolder<Monument> {
// code
}
public class Monument extends BaseHolder<Compartment> {
private String name;
public Monument (String name){
this.setName(name);
}
// code
#Override
public boolean addItem(final Compartment c) {
Objects.requireNonNull(c, "cannot add NULL");
if (contains (c) ){
throw new IllegalArgumentException("Compartment already added!");
};
for(Compartment ctmp : getHeldItems()){
if (ctmp.getName().equals(c.getName() ) ) {
throw new IllegalArgumentException("Compartment with an identical name already exits");
}
}
return getHeldItems().add(c);
}
public Compartment getCompartment(int n){
return getHeldItems().get(n);
}
public Compartment getCompartment(String name){
for(Compartment ctmp : getHeldItems()){
if (ctmp.getName().equals(name) ) {
return ctmp;
}
}
return null;
}
}
public class Compartment extends BaseHolder<IWeighable>{
private String name = "";
private double MAX_LOAD = 0.0;
public Compartment (String name ,final double max_load){
this.setName(name);
updateMaxLoad(max_load);
}
// code
protected double getTotalLoad(){
// code
}
/**
*
* #param load
* #throws InvalidParameterException if max load not >= than 0.0
*/
public void setMaxLoad(final double load){
if (load >= 0.0){
this.MAX_LOAD = load;
} else {
throw new InvalidParameterException("max load must be greater than 0.0");
}
}
public boolean isOverloaded(){
return (getTotalLoad() > MAX_LOAD ) ;
}
}
The problem I am having is that this design seems to have many flaws. Apart from it being rather tedious: getElement(n).getElement(n).getElement(n)
Adding elements to a compartment results in all aircrafts using the same compartment, having all the same equipment’s/components installed. As it is the same object in the DB. An instance of the compartment would be need. Cloning the DB Compartment before adding it to an aircraft is no option. I need to be able to change the allowable loads, a change it for all. To resolve this I thought of using some type of “wrapper” class as in:
public class MonumentManager {
public ArrayList <Monument> monuments = new ArrayList<>();
public ArrayList <LinkObect> links;
class LinkObect{
private Compartment c;
private IWeighable e;
LinkObect(Compartment c, IWeighable e){
this.c = c;
this.e = e;
}
}
public boolean addMonument(Monument m){
return monuments.add(m);
}
public void addElementToCompartment(IWeighable e, Compartment c){
boolean known = false; //to check if the passed compartment is known/handeld to/by the MonumentManager
for (Monument m : monuments){
if ( m.getCompartment(c.getName() ) != null ) known = true;
}
if (known){
links.add(new LinkObect(c, e));
} else {
throw new IllegalArgumentException("Compartment is not inside a managed Monument!");
}
}
public List<Compartment> whereUsed(IWeighable e){
// TODO
}
}
This class might solve the problem but it is feels odd. Can anybody point me in the right direction towards a common design pattern etc. I am reading a book from the local library on design patterns. But it seems to be slightly above me. (as is maybe my task).
Any suggestions / help etc would be highly appreciated.
I hope I'm understanding this correctly.
One thing is the Component you want to install that has certain characteristics and another thing is some representation of what you have installed.
The information of your installation does not need to be in your Component but in something else, let's call it Installation.
Your Installation has to know 2 things:
What kind of Component it is.
What other Installations it has inside.
The installation will look something like this.
public class Installation {
private Component type;
private List<Installation> content;
public Installation(Component type){
this.type = type;
this.content = new ArrayList<Component>();
}
//you can have methods for add, remove, etc...
}
Feel free to ask further clarifications.

Implementing abstract methods at runtime?

Let's say I have an abstract class:
abstract class Foo extends Bar {
public abstract int foo();
}
that I want to extend at runtime to create a Class object. The hope would be that I could have a dynamically generated class:
class FooImpl extends Foo {
#Override
public int foo() {
return 5;
}
}
that would be represented by a Class object and that I could then use reflection to create new instances of. The key is that I would like to decide the return value of the method foo() at runtime. My thought is to use ASM to create the bytecode for the class and then use reflection on a ClassLoader object to define the Class.
Is using ASM and then reflection of the method ClassLoader#defineClass on the generated bytes the best way to implement abstract methods at runtime with non-hardcoded values?
If yes, how would I go about doing that. My gut is to utilize the ASMifierClassVisitor, but I'm not quite sure on the exact method of doing that. I know that if all else fails I can manually go through the JVM instructions required to define a specific class but I feel there must be an easier way.
If no, what is the best way and how would I go about using the best way?
EDIT: I checked out all of the answers and I decided that none of them were exactly what I was looking for. I ended up creating a small implementation of what I was talking about with ASM. I figured I should post it here:
import org.objectweb.asm.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
/**
* Created by IntelliJ IDEA.
* User: Matt
* Date: 9/17/11
* Time: 12:42 PM
*/
public class OverrideClassAdapter extends ClassAdapter {
private final HashMap<String, Object> code;
private final String className;
private final ClassWriter writer;
private String superName;
public OverrideClassAdapter(ClassWriter writer, String className, Queue<int[]> constructorCode, HashMap<String, Object> code) {
super(writer);
this.writer = writer;
this.className = className;
this.code = code;
}
#Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.superName = name;
if((access & Opcodes.ACC_ABSTRACT) != 0)
access &= ~Opcodes.ACC_ABSTRACT;
if((access & Opcodes.ACC_INTERFACE) != 0)
access &= ~Opcodes.ACC_INTERFACE;
cv.visit(version, access, className, signature, name, null);
}
#Override
public void visitSource(String source, String debug) {
}
#Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
boolean isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0;
if(isAbstract)
access &= ~Opcodes.ACC_ABSTRACT;
MethodWriter mw = (MethodWriter) cv.visitMethod(access, name, desc, signature, exceptions);
Object value = code.get(name);
if(isAbstract || value != null) {
if(value instanceof BytecodeValue) {
BytecodeValue returnableValue = (BytecodeValue) value;
int[] byteCode = new int[returnableValue.getValueCode().length + 1];
System.arraycopy(returnableValue.getValueCode(), 0, byteCode, 0, returnableValue.getValueCode().length);
if(returnableValue.getValueCode().length > 1 && returnableValue.getValueCode()[1] == 0) {
byteCode[1] = writer.newConst(returnableValue.getValue());
}
byteCode[byteCode.length - 1] = returnableValue.getReturnCode();
value = byteCode;
}
return new OverrideMethodAdapter(mw, (int[]) value);
}
return mw;
}
private class OverrideMethodAdapter extends MethodAdapter {
private final int[] code;
private final MethodWriter writer;
public OverrideMethodAdapter(MethodWriter writer, int[] code) {
super(writer);
this.writer = writer;
this.code = code;
}
#Override
public void visitEnd() {
try {
Field code = MethodWriter.class.getDeclaredField("code");
code.setAccessible(true);
ByteVector bytes = new ByteVector();
for(int b : this.code)
bytes.putByte(b);
code.set(writer, bytes);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static byte[] extendClassBytes(Class clazz, String className, HashMap<String, Object> methodImpls) throws IOException {
ClassReader cr = new ClassReader(clazz.getName());
ClassWriter cw = new ClassWriter(0);
cr.accept(new OverrideClassAdapter(cw, className, methodImpls), ClassReader.SKIP_DEBUG);
cr = new ClassReader(cw.toByteArray());
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cr.accept(cw, ClassReader.SKIP_DEBUG);
//CheckClassAdapter.verify(new org.objectweb.asm.ClassReader(cw.toByteArray()), true, new PrintWriter(System.out));
/*File file = new File(className + ".class");
new FileOutputStream(file).write(cw.toByteArray());*/
return cw.toByteArray();
}
public static Class extendClass(Class clazz, String className, HashMap<String, Object> methodImpls) throws IOException {
return defineClass(extendClassBytes(clazz, className, methodImpls), className);
}
public static Class defineClass(byte[] code, String name) {
try {
Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
method.setAccessible(true);
return (Class) method.invoke(Thread.currentThread().getContextClassLoader(), name, code, 0, code.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
You might want to look at using CGLib. It can do what Java's dynamic proxies can do but for abstract classes as well as interfaces, and it has a similar API to java.lang.reflect.Proxy for doing this as well. CGLib uses ASM behind the scenes anyway, but by using CGLib you wont have to craft bytecode directly.
Here's an example of how to use CGLib to do this:
package cglibtest;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CGLibTest
{
public static void main(String... args)
{
MyAbstract instance = (MyAbstract)Enhancer.create(MyAbstract.class, new MyInterceptor(42));
System.out.println("Value from instance: " + instance.valueMethod());
}
public static class MyInterceptor implements MethodInterceptor
{
private final Object constantValue;
public MyInterceptor(Object constantValue)
{
this.constantValue = constantValue;
}
#Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable
{
if ("valueMethod".equals(method.getName()))
return(constantValue);
else
return(null);
}
}
public static abstract class MyAbstract
{
public abstract int valueMethod();
}
}
What's stopping you from reading the value 5 from say properties and return it back? That's too simple so, I guess you must have something more complex than returning an int that you want to accomplish here. I agree with the posts above that generating classes at runtime would be very expensive. If you know your business logic in advance, you can apply the Factory pattern to load the desired implementation of defined interfaces at runtime. That's how JDBC libraries work.
If you do not know the business logic in advance and have lot's of it then, you might benefit from using an off the shelf Rule Engine to process the logic and return results back to your Java program. It is much easier to maintain this logic in a Rule Engine specially if it is changing frequently.
Yes, that approach should work. But it will be expensive if you do a lot of class generation. (We are probably talking about hundreds of thousands of instructions to generate the bytecode file and then load it. And then there's the memory need to represent the class when it is loaded.)
Another approach (also expensive) is to generate source code and compile and load it at runtime.
Finally, you should consider the approach of making the logic of the objects table-driven or implementing it using some kind of interpreter. If you actually need to have different classes, you could wrap this up using Java's dynamic proxy class mechanism; e.g. see java.lang.reflect.Proxy

How can I find all the methods that call a given method in Java?

I need to get a list of all caller methods for a method of interest for me in Java. Is there a tool that can help me with this?
Edit: I forgot to mention that I need to do this from a program. I'm usig Java Pathfinder and I want to run it an all the methods that call my method of interest.
For analyzing bytecode, I would recommend ASM. Given a list of Classes to analyze, a visitor can be made which finds the method calls you're interested in. One implementation which analyses classes in a jar file is below.
Note that ASM uses internalNames with '/' instead of '.' as a separator. Specify the target method as a standard declaration without modifiers.
For example, to list methods that could be calling System.out.println("foo") in the java runtime jar:
java -cp "classes;asm-3.1.jar;asm-commons-3.1.jar" App \
c:/java/jdk/jre/lib/rt.jar \
java/io/PrintStream "void println(String)"
Edit: source and line numbers added: Note that this only indicates the last target method invocation per calling method - the original q only wanted to know which methods. I leave it as an exercise for the reader to show line numbers of the calling method declaration, or the line numbers of every target invocation, depending on what you're actually after. :)
results in:
LogSupport.java:44 com/sun/activation/registries/LogSupport log (Ljava/lang/String;)V
LogSupport.java:50 com/sun/activation/registries/LogSupport log (Ljava/lang/String;Ljava/lang/Throwable;)V
...
Throwable.java:498 java/lang/Throwable printStackTraceAsCause (Ljava/io/PrintStream;[Ljava/lang/StackTraceElement;)V
--
885 methods invoke java/io/PrintStream println (Ljava/lang/String;)V
source:
public class App {
private String targetClass;
private Method targetMethod;
private AppClassVisitor cv;
private ArrayList<Callee> callees = new ArrayList<Callee>();
private static class Callee {
String className;
String methodName;
String methodDesc;
String source;
int line;
public Callee(String cName, String mName, String mDesc, String src, int ln) {
className = cName; methodName = mName; methodDesc = mDesc; source = src; line = ln;
}
}
private class AppMethodVisitor extends MethodAdapter {
boolean callsTarget;
int line;
public AppMethodVisitor() { super(new EmptyVisitor()); }
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(targetClass)
&& name.equals(targetMethod.getName())
&& desc.equals(targetMethod.getDescriptor())) {
callsTarget = true;
}
}
public void visitCode() {
callsTarget = false;
}
public void visitLineNumber(int line, Label start) {
this.line = line;
}
public void visitEnd() {
if (callsTarget)
callees.add(new Callee(cv.className, cv.methodName, cv.methodDesc,
cv.source, line));
}
}
private class AppClassVisitor extends ClassAdapter {
private AppMethodVisitor mv = new AppMethodVisitor();
public String source;
public String className;
public String methodName;
public String methodDesc;
public AppClassVisitor() { super(new EmptyVisitor()); }
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
className = name;
}
public void visitSource(String source, String debug) {
this.source = source;
}
public MethodVisitor visitMethod(int access, String name,
String desc, String signature,
String[] exceptions) {
methodName = name;
methodDesc = desc;
return mv;
}
}
public void findCallingMethodsInJar(String jarPath, String targetClass,
String targetMethodDeclaration) throws Exception {
this.targetClass = targetClass;
this.targetMethod = Method.getMethod(targetMethodDeclaration);
this.cv = new AppClassVisitor();
JarFile jarFile = new JarFile(jarPath);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
InputStream stream = new BufferedInputStream(jarFile.getInputStream(entry), 1024);
ClassReader reader = new ClassReader(stream);
reader.accept(cv, 0);
stream.close();
}
}
}
public static void main( String[] args ) {
try {
App app = new App();
app.findCallingMethodsInJar(args[0], args[1], args[2]);
for (Callee c : app.callees) {
System.out.println(c.source+":"+c.line+" "+c.className+" "+c.methodName+" "+c.methodDesc);
}
System.out.println("--\n"+app.callees.size()+" methods invoke "+
app.targetClass+" "+
app.targetMethod.getName()+" "+app.targetMethod.getDescriptor());
} catch(Exception x) {
x.printStackTrace();
}
}
}
Edit: the original question was edited to indicate a runtime solution was needed - this answer was given before that edit and only indicates how to do it during development.
If you are using Eclipse you can right click the method and choose "Open call hierarchy" to get this information.
Updated after reading comments: Other IDEs support this as well in a similar fashion (at least Netbeans and IntelliJ do)
Annotate the method with #Deprecated ( or tag it with #deprecated ), turn on deprecation warnings, run your compile and see which warnings get triggered.
The run your compile bit can be done either by invoking an external ant process or by using the Java 6 compiler API.
right click on method
Go to references and (depending on your requirement)
choose workspace/project/Hierarchy.
This pops up a panel that shows all references to this functions. Eclipse FTW !
In eclipse, highlight the method name and then Ctrl+Shift+G
There isn't a way to do this (programmatically) via the Java reflection libraries - you can't ask a java.lang.reflect.Method "which methods do you call?"
That leaves two other options I can think of:
Static analysis of the source code. I'm sure this is what the Eclipse Java toolset does - you could look at the Eclipse source behind the JDT, and find what it does when you ask Eclipse to "Find References" to a method.
Bytecode analysis. You could inspect the bytecode for calls to the method. I'm not sure what libraries or examples are out there to help with this - but I can't imagine that something doesn't exist.
Yes, most modern IDE:s will let you either search for usages of a method or variable. Alternatively, you could use a debugger and set a trace point on the method entry, printing a stack trace or whatever every time the method is invoked.
Finally, you could use some simple shell util to just grep for the method, such as
find . -name '*.java' -exec grep -H methodName {} ;
The only method that will let you find invokations made through some reflection method, though, would be using the debugger.
I made a small example using #Chadwick's one. It's a test that assesses if calls to getDatabaseEngine() are made by methods that implement #Transaction.
/**
* Ensures that methods that call {#link DatabaseProvider#getDatabaseEngine()}
* implement the {#link #Transaction} annotation.
*
* #throws Exception If something occurs while testing.
*/
#Test
public void ensure() throws Exception {
final Method method = Method.getMethod(
DatabaseEngine.class.getCanonicalName() + " getDatabaseEngine()");
final ArrayList<java.lang.reflect.Method> faultyMethods = Lists.newArrayList();
for (Path p : getAllClasses()) {
try (InputStream stream = new BufferedInputStream(Files.newInputStream(p))) {
ClassReader reader = new ClassReader(stream);
reader.accept(new ClassAdapter(new EmptyVisitor()) {
#Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
return new MethodAdapter(new EmptyVisitor()) {
#Override
public void visitMethodInsn(int opcode, String owner, String nameCode, String descCode) {
try {
final Class<?> klass = Class.forName(Type.getObjectType(owner).getClassName());
if (DatabaseProvider.class.isAssignableFrom(klass) &&
nameCode.equals(method.getName()) &&
descCode.equals(method.getDescriptor())) {
final java.lang.reflect.Method method = klass.getDeclaredMethod(name,
getParameters(desc).toArray(new Class[]{}));
for (Annotation annotation : method.getDeclaredAnnotations()) {
if (annotation.annotationType().equals(Transaction.class)) {
return;
}
}
faultyMethods.add(method);
}
} catch (Exception e) {
Throwables.propagate(e);
}
}
};
}
}, 0);
}
}
if (!faultyMethods.isEmpty()) {
fail("\n\nThe following methods must implement #Transaction because they're calling getDatabaseEngine().\n\n" + Joiner.on("\n").join
(faultyMethods) + "\n\n");
}
}
/**
* Gets all the classes from target.
*
* #return The list of classes.
* #throws IOException If something occurs while collecting those classes.
*/
private List<Path> getAllClasses() throws IOException {
final ImmutableList.Builder<Path> builder = new ImmutableList.Builder<>();
Files.walkFileTree(Paths.get("target", "classes"), new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
if (file.getFileName().toString().endsWith(".class")) {
builder.add(file);
}
return FileVisitResult.CONTINUE;
}
});
return builder.build();
}
/**
* Gets the list of parameters given the description.
*
* #param desc The method description.
* #return The list of parameters.
* #throws Exception If something occurs getting the parameters.
*/
private List<Class<?>> getParameters(String desc) throws Exception {
ImmutableList.Builder<Class<?>> obj = new ImmutableList.Builder<>();
for (Type type : Type.getArgumentTypes(desc)) {
obj.add(ClassUtils.getClass(type.getClassName()));
}
return obj.build();
}
1)In eclipse it is ->right click on the method and select open call hierarchy or CLT+ALT+H
2)In jdeveloper it is -> right click on the method and select calls or ALT+SHIFT+H
The closest that I could find was the method described in this StackOverflow questions selected answer.check this out
You can do this with something in your IDE such as "Find Usages" (which is what it is called in Netbeans and JDeveloper). A couple of things to note:
If your method implements a method from an interface or base class, you can only know that your method is POSSIBLY called.
A lot of Java frameworks use Reflection to call your method (IE Spring, Hibernate, JSF, etc), so be careful of that.
On the same note, your method could be called by some framework, reflectively or not, so again be careful.

Categories

Resources