Search code examples
javabcel

Identify Unused variables in Java code


I need to identify unused variables ( arguments, local variables, class member variables) unused in Java code. Basically i have to use BCEL to access the byte code and reach my objective.

I have managed using ClassGen to get all the methods called and then by using MethodGen i managed to get all the local variables and function arguments. However i still can't differentiate between a used and an unused variable.

I am guessing i have to access the JVM stack to see what variables are actually being loaded and what is not being loaded are not being used.

So the question is simple: How do i access the JVM stack using BCEL?


Solution

  • It it possible to detect the used/unused variables from the bytecode by obtaining the instruction list of the method.

    I have not tried this before, and this is only an experiment that shows that it is possible at all, but I hope there is an easier and more elegant way than this:

    import org.apache.bcel.Repository;
    import org.apache.bcel.classfile.Constant;
    import org.apache.bcel.classfile.ConstantFieldref;
    import org.apache.bcel.classfile.ConstantNameAndType;
    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.GETFIELD;
    import org.apache.bcel.generic.Instruction;
    import org.apache.bcel.generic.InstructionHandle;
    import org.apache.bcel.generic.InstructionList;
    import org.apache.bcel.generic.LoadInstruction;
    import org.apache.bcel.generic.LocalVariableInstruction;
    import org.apache.bcel.generic.MethodGen;
    
    public class UnusedVariablesTest
    {
        private int usedInt;
        private String usedString;
        private int unusedInt;
        private String unusedString;
    
        public static void main(String[] args) throws ClassNotFoundException
        {
            String className = "UnusedVariablesTest";
            JavaClass c = Repository.lookupClass(className);
            ConstantPool cp = c.getConstantPool();
            ConstantPoolGen cpg = new ConstantPoolGen(cp);
            for (Method m : c.getMethods())
            {
                //System.out.println("Method "+m);
                MethodGen mg = new MethodGen(m, className, cpg);
                InstructionList il = mg.getInstructionList();
                InstructionHandle[] ihs = il.getInstructionHandles();
                 for(int i=0; i < ihs.length; i++) {
                    InstructionHandle ih = ihs[i];
                    Instruction instruction = ih.getInstruction();
                    //System.out.println("    "+instruction);
                    if (instruction instanceof LocalVariableInstruction)
                    {
                        LocalVariableInstruction lvi = (LocalVariableInstruction)instruction;
                        LocalVariableTable lvt = m.getLocalVariableTable();
                        int index = lvi.getIndex();
                        LocalVariable lv = lvt.getLocalVariable(index, ih.getPosition());
                        if (lv != null)
                        {
                            System.out.println("Using "+lv.getName());
                        }
                    }
                    else if (instruction instanceof GETFIELD)
                    {
                        GETFIELD getfield = (GETFIELD)instruction;
                        int index = getfield.getIndex();
                        Constant constant = cp.getConstant(index);
                        if (constant instanceof ConstantFieldref)
                        {
                            ConstantFieldref cfr = (ConstantFieldref)constant;
                            Constant constant2 = cp.getConstant(cfr.getNameAndTypeIndex());
                            if (constant2 instanceof ConstantNameAndType)
                            {
                                ConstantNameAndType cnat = (ConstantNameAndType)constant2;
                                System.out.println("Using "+cnat.getName(cp));
                            }
                        }
                    }
                 }
            }
        }
    
        void someMethod(int usedIntArgument, int unusedIntArgument)
        {
            System.out.println(usedInt+usedString+usedIntArgument);
        }
    }
    

    At least it prints the fields, arguments and local variables that are used, which could serve as a basis to detect the UNused ones.