java - How to retrieve anything inside method

965 views Asked by At

From what i know, java cannot retrieve anything inside method. so i using option -g or -g:vars in javac.

for e.g :

class Test {
    int a=0;
    void method(boolean boo){
        String b;
        try
        {
            new Thread().sleep(1000);
        }
        catch(InterruptedException e){}
        JOptionPane.showMessageDialog(null,"test");
        BufferedImage image=ImageIO.read(new File("C:\\file.png"));
    }
}

So, i use BCEL to retrieve local variable.

import org.apache.bcel.classfile.*;
import org.apache.bcel.Repository;
class javap
{
    public static void main(String[]args)
    {
        try
        {
            JavaClass jc = Repository.lookupClass("test");
            ConstantPool constantPool = jc.getConstantPool();
            Method [] method=jc.getMethods();
            for (Method m : method) 
            {
                LocalVariableTable lvt=m.getLocalVariableTable();
                LocalVariable[] lv=lvt.getLocalVariableTable();
                for(LocalVariable l : lv)
                {   
                    System.out.println(l.getName()+" : "+l.getSignature());
                }
            }
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }
}

But it doesn't work if variable is not initialized like String b. Additionally I want to track constructor calls like new Thread() or new File() as well as invocations of static methods and inside intialize inside JFileChooser like new File and JOptionPane too. So I want to see in output Thread, String b, JOptionPane, ImageIO, and File.

What should I do, to make them are printed in my program?

1

There are 1 answers

9
Tagir Valeev On BEST ANSWER

You simply cannot get the b variable, because java compilers (at least javac and ecj) do not put it into the generated class file at all: if variable is not assigned, no variable slot is allocated and it's not stored in the LocalVariableTable. You can create unused variable with longer name like String blahblah;, compile the class, open the compiled .class-file in text editor and search for blahblah string. You will not found it. So BCEL cannot help you to find the variable which is absent.

If you want to track new objects creation and static methods invocation, you can do it scanning the method bytecode. The easiest way to do it with BCEL is to utilize the MethodGen (even though you don't want to generate the new method). Here's the full code:

import org.apache.bcel.Constants;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.ConstantMethodref;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.NEW;

class javap
{
    public static void main(String[]args)
    {
        try
        {
            JavaClass jc = Repository.lookupClass("Test");
            ConstantPool constantPool = jc.getConstantPool();
            Method [] method=jc.getMethods();
            for (Method m : method) 
            {
                LocalVariableTable lvt=m.getLocalVariableTable();
                LocalVariable[] lv=lvt.getLocalVariableTable();
                for(LocalVariable l : lv)
                {   
                    System.out.println(l.getName()+" : "+l.getSignature());
                }
            }
            ConstantPoolGen cpg = new ConstantPoolGen(constantPool);
            for(Method m : method)
            {
                MethodGen mg = new MethodGen(m, m.getName(), cpg);
                for(InstructionHandle ih = mg.getInstructionList().getStart(); 
                        ih != null; ih = ih.getNext())
                {
                    if(ih.getInstruction() instanceof NEW) 
                    {
                        NEW newInst = ((NEW)ih.getInstruction());
                        String className = constantPool.getConstantString(
                            newInst.getIndex(), Constants.CONSTANT_Class);
                        System.out.println("Class instantiation: "+className);
                    }
                    if(ih.getInstruction() instanceof INVOKESTATIC) 
                    {
                        INVOKESTATIC newInst = ((INVOKESTATIC)ih.getInstruction());
                        String className = constantPool.getConstantString(
                                ((ConstantMethodref) constantPool
                                        .getConstant(newInst.getIndex()))
                                        .getClassIndex(),
                                Constants.CONSTANT_Class);
                        System.out.println("Static call: "+className);
                    }
                }
            }
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }
}

The output is the following:

this : LTest;
this : LTest;
boo : Z
Class instantiation: java/lang/Thread
Static call: java/lang/Thread
Static call: javax/swing/JOptionPane
Class instantiation: java/io/File
Static call: javax/imageio/ImageIO

Note that you have java/lang/Thread twice, because new Thread() is catched as object creation and Thread.sleep() is catched as static method invocation.