2

As far as I understood the JVM bytecode specification, aload index pushes a reference found at index onto the current stackframe. In my case, I need to aload a reference found at an index which is stored in a local variable - something like this: aload (iload 2).

(How) Is this possible?

NyxCode
  • 584
  • 1
  • 4
  • 13

2 Answers2

4

This is not possible. Otherwise verifier would not be able to prove the correctness of the bytecode.

To access values by index use array objects with the corresponding bytecodes, e.g. aaload.

apangin
  • 92,924
  • 10
  • 193
  • 247
0

There is no dedicated bytecode instruction to perform this kind of selector, so if you want to load a local variable based on the contents of another variable, you have to code this operation yourself, e.g.

// 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

or

// 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

or, to have a variant that scales with higher numbers of variables:

// 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

The last variant has been generated with the ASM library and the following helper method, calling it like 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
}

Obviously, array based code using the intrinsic index based access instructions is much simpler and more efficient in most cases.

Holger
  • 285,553
  • 42
  • 434
  • 765