如何从编译时未知的索引处的局部变量加载引用?
How can I load a reference from a local variable at an index which is unknown at compile time?
据我了解 JVM 字节码规范,
aload index
将在 index
找到的引用推送到当前堆栈帧。在我的例子中,我需要 aload
在存储在局部变量中的索引处找到的引用 - 像这样:aload (iload 2)
.
(如何)这可能吗?
这是不可能的。否则验证者将无法证明字节码的正确性。
要通过索引访问值,请使用具有相应字节码的数组对象,例如aaload
.
没有专门的字节码指令来执行这种选择器,所以如果你想根据另一个变量的内容加载一个局部变量,你必须自己编写这个操作,例如
// pick #1 or #2 based on #0 in [0..1]
0: iload_0
1: ifne 8
4: aload_1
5: goto 9
8: aload_2
9: // value is now on the stack, ready for use
或
// pick #1, #2, or #3 based on #0 in [0..2]
0: iload_0
1: ifne 8
4: aload_1
5: goto 18
8: iload_0
9: iconst_1
10: if_icmpne 17
13: aload_2
14: goto 18
17: aload_3
18: // value is now on the stack, ready for use
或者,要有一个随更多变量而缩放的变体:
// pick #1, #2, or #3 based on #0 in [0..2]
0: iload_0
1: tableswitch { // 0 to 2
0: 28
1: 32
2: 36
default: 36
}
28: aload_1
29: goto 37
32: aload_2
33: goto 37
36: aload_3
37: // value is now on the stack, ready for use
最后一个变体是用 ASM 库和下面的辅助方法生成的,调用方式类似于 loadDynamic(…, 0, 1, 3);
/**
* Load variable #(firstChoice + value(#selector))
*/
public static void loadDynamic(
MethodVisitor target, int selector, int firstChoice, int numChoices) {
Label[] labels = new Label[numChoices];
for(int ix = 0; ix < numChoices; ix++) labels[ix] = new Label();
Label end = new Label();
target.visitVarInsn(Opcodes.ILOAD, selector);
target.visitTableSwitchInsn(0, numChoices - 1, labels[numChoices - 1], labels);
target.visitLabel(labels[0]);
target.visitVarInsn(Opcodes.ALOAD, firstChoice);
for(int ix = 1; ix < numChoices; ix++) {
target.visitJumpInsn(Opcodes.GOTO, end);
target.visitLabel(labels[ix]);
target.visitVarInsn(Opcodes.ALOAD, firstChoice + ix);
}
target.visitLabel(end); // choosen value is now on the stack
}
显然,在大多数情况下,使用基于内部索引的访问指令的基于数组的代码更加简单和高效。
据我了解 JVM 字节码规范,
aload index
将在 index
找到的引用推送到当前堆栈帧。在我的例子中,我需要 aload
在存储在局部变量中的索引处找到的引用 - 像这样:aload (iload 2)
.
(如何)这可能吗?
这是不可能的。否则验证者将无法证明字节码的正确性。
要通过索引访问值,请使用具有相应字节码的数组对象,例如aaload
.
没有专门的字节码指令来执行这种选择器,所以如果你想根据另一个变量的内容加载一个局部变量,你必须自己编写这个操作,例如
// pick #1 or #2 based on #0 in [0..1]
0: iload_0
1: ifne 8
4: aload_1
5: goto 9
8: aload_2
9: // value is now on the stack, ready for use
或
// pick #1, #2, or #3 based on #0 in [0..2]
0: iload_0
1: ifne 8
4: aload_1
5: goto 18
8: iload_0
9: iconst_1
10: if_icmpne 17
13: aload_2
14: goto 18
17: aload_3
18: // value is now on the stack, ready for use
或者,要有一个随更多变量而缩放的变体:
// pick #1, #2, or #3 based on #0 in [0..2]
0: iload_0
1: tableswitch { // 0 to 2
0: 28
1: 32
2: 36
default: 36
}
28: aload_1
29: goto 37
32: aload_2
33: goto 37
36: aload_3
37: // value is now on the stack, ready for use
最后一个变体是用 ASM 库和下面的辅助方法生成的,调用方式类似于 loadDynamic(…, 0, 1, 3);
/**
* Load variable #(firstChoice + value(#selector))
*/
public static void loadDynamic(
MethodVisitor target, int selector, int firstChoice, int numChoices) {
Label[] labels = new Label[numChoices];
for(int ix = 0; ix < numChoices; ix++) labels[ix] = new Label();
Label end = new Label();
target.visitVarInsn(Opcodes.ILOAD, selector);
target.visitTableSwitchInsn(0, numChoices - 1, labels[numChoices - 1], labels);
target.visitLabel(labels[0]);
target.visitVarInsn(Opcodes.ALOAD, firstChoice);
for(int ix = 1; ix < numChoices; ix++) {
target.visitJumpInsn(Opcodes.GOTO, end);
target.visitLabel(labels[ix]);
target.visitVarInsn(Opcodes.ALOAD, firstChoice + ix);
}
target.visitLabel(end); // choosen value is now on the stack
}
显然,在大多数情况下,使用基于内部索引的访问指令的基于数组的代码更加简单和高效。