JVM Bytecode,如何找到局部变量的类型?
JVM Bytecode, how can I find the type of local variables?
我正在研究 Jetbrains 的 FernFlower a fork,我一直在对其进行小的改进。
关于 FernFlower 真正让我恼火的一件事是它根据局部变量在 bpush/spush 等中的值来确定局部变量的类型。而 Jode 和 Procyon 不知何故找到了一种方法来找到 a 的原始值局部变量。
这里是原始源代码。
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);
}
使用 FernFlower 反编译时,输出如下:
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);
}
但是当用Jode/Procyon反编译时它输出原始局部变量类型:
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);
}
我想知道这怎么可能,因为我认为在编译时没有存储局部变量类型信息?如何向 FernFlower 添加相同的功能?
.class
文件可选地包含一个 'LocalVariableTable' 属性用于调试目的。如果您调用命令 javap -l <Class>.class
,您可以看到数据(如果存在)。
所以在查看和调试之后,我发现出于某种原因,FernFlower 决定完全忽略 LocalVariableTable 中的一些数据。
这里是 ferns 解码 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();
}
}
如果您需要类型信息,您需要添加以下内容:
@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();
}
}
它现在输出与具有适当变量类型的 Jode 相同的代码:)
我想知道为什么 FernFlower 选择忽略此信息。
我正在研究 Jetbrains 的 FernFlower a fork,我一直在对其进行小的改进。
关于 FernFlower 真正让我恼火的一件事是它根据局部变量在 bpush/spush 等中的值来确定局部变量的类型。而 Jode 和 Procyon 不知何故找到了一种方法来找到 a 的原始值局部变量。
这里是原始源代码。
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);
}
使用 FernFlower 反编译时,输出如下:
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);
}
但是当用Jode/Procyon反编译时它输出原始局部变量类型:
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);
}
我想知道这怎么可能,因为我认为在编译时没有存储局部变量类型信息?如何向 FernFlower 添加相同的功能?
.class
文件可选地包含一个 'LocalVariableTable' 属性用于调试目的。如果您调用命令 javap -l <Class>.class
,您可以看到数据(如果存在)。
所以在查看和调试之后,我发现出于某种原因,FernFlower 决定完全忽略 LocalVariableTable 中的一些数据。
这里是 ferns 解码 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();
}
}
如果您需要类型信息,您需要添加以下内容:
@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();
}
}
它现在输出与具有适当变量类型的 Jode 相同的代码:)
我想知道为什么 FernFlower 选择忽略此信息。