JVM Bytecode, how can I find the type of local variables?

I am working on a Jetbrains FernFlower plug and I have added minor improvements to it.

One thing that really annoys me in FernFlower is that it bases the type of the local variable based on its value in bpush / spush, etc. So far, Jode and Procyon are somehow finding a way to find the original value of the local variable.

Here is the source code.

public static void main(String[] args) throws Exception { int hello = 100; char a2 = 100; short y1o = 100; int hei = 100; System.out.println(a2+" "+y1o+", "+hei+", "+hello); } 

When decompiling with FernFlower, it outputs this:

 public static void main(String[] args) throws Exception { byte hello = 100; char a2 = 100; byte y1o = 100; byte hei = 100; System.out.println(a2 + " " + y1o + ", " + hei + ", " + hello); } 

But when decompiling with Jode / Procyon, it outputs the original local variable types:

  public static void main(String[] args) throws Exception { int hello = 100; char a2 = 'd'; short y1o = 100; byte hei = 100; System.out.println(a2 + " " + y1o + ", " + hei + ", " + hello); } 

I was wondering how this is possible because I thought that during compilation, information about local variables was not stored? How to add the same features to FernFlower?

+6
source share
2 answers

So, after inspecting and debugging, I found that for some reason FernFlower decides to completely ignore some data in LocalVariableTable.

Here is the fern source code for decoding LocalVariableTable:

 public void initContent(ConstantPool pool) throws IOException { DataInputFullStream data = stream(); int len = data.readUnsignedShort(); if (len > 0) { mapVarNames = new HashMap<Integer, String>(len); for (int i = 0; i < len; i++) { data.discard(4); int nameIndex = data.readUnsignedShort(); data.discard(2); int varIndex = data.readUnsignedShort(); mapVarNames.put(varIndex, pool.getPrimitiveConstant(nameIndex).getString()); } } else { mapVarNames = Collections.emptyMap(); } } 

If you need type information, you need to add the following:

 @Override public void initContent(ConstantPool pool) throws IOException { DataInputFullStream data = stream(); int len = data.readUnsignedShort(); if (len > 0) { mapVarNames = new HashMap<Integer, String>(len); mapVarTypes = new HashMap<Integer, String>(len); for (int i = 0; i < len; i++) { int start = data.readUnsignedShort(); int end = start + data.readUnsignedShort(); int nameIndex = data.readUnsignedShort(); int typeIndex = data.readUnsignedShort(); int varIndex = data.readUnsignedShort(); mapVarNames.put(varIndex, pool.getPrimitiveConstant(nameIndex).getString()); mapVarTypes.put(varIndex, pool.getPrimitiveConstant(typeIndex).getString()); } } else { mapVarNames = Collections.emptyMap(); mapVarTypes = Collections.emptyMap(); } } 

Now it outputs the same code as Jode, with the corresponding types of variables :)

I wonder why FernFlower decided to ignore this information.

+2
source

.class files do not necessarily contain the LocalVariableTable attribute for debugging purposes. If you call javap -l <Class>.class , you can see the data, if any.

+4
source

All Articles