javastackinstruction-setjvm-bytecodemachine-instruction

Transform a stack using Java Virtual Machine Instruction Set


I need to transform 654321 into 654321321, using only DUP2_X1, POP, DUP_X2, POP2. I have a really difficult time with the DUP. Can anyone help?

I tried to go like this: 654321 DUP2_X1 654654321, then POP goes to 65465432, then DUP_X2 we'd have 6543265432... but I don't think I am doing them correctly.


Solution

  • So, basically you want to duplicate the last three elements of the operand stack while keeping all other elements intact and not use local variables nor heap space.

    One sequence to achieve this would be

    Instruction Resulting Stack
    (initial) ...321
    DUP2_X1 ...21321
    POP2 ...213
    DUP_X2 ...3213
    DUP_X2 ...33213
    POP ...3321
    DUP2_X1 ...321321

    A straight-forward program to test it using the ASM library looks like

    package asm;
    
    import static org.objectweb.asm.Opcodes.*;
    import java.lang.invoke.*;
    import org.objectweb.asm.*;
    
    public class DuplicateThreeOperands {
      public static void main(String[] args) throws ReflectiveOperationException {
        final String s = "Ljava/lang/String;";
        ClassWriter cw = new ClassWriter(0);
        cw.visit(55, ACC_INTERFACE | ACC_ABSTRACT,
                 "asm/Test", null, "java/lang/Object", args);
        MethodVisitor mv = cw.visitMethod(ACC_STATIC | ACC_PUBLIC,
                 "test", "()" + s, null, null);
        mv.visitCode();
        mv.visitLdcInsn("6");
        mv.visitLdcInsn("5");
        mv.visitLdcInsn("4");
        mv.visitLdcInsn("3");
        mv.visitLdcInsn("2");
        mv.visitLdcInsn("1");
    
        stackManipulation(mv);
    
        mv.visitInvokeDynamicInsn("concat", "(" + s.repeat(9) + ")" + s, new Handle(
            H_INVOKESTATIC, "java/lang/invoke/StringConcatFactory", "makeConcat",
            MethodType.methodType(CallSite.class, MethodHandles.Lookup.class,
               String.class, MethodType.class).toMethodDescriptorString(), false));
        mv.visitInsn(ARETURN);
        mv.visitMaxs(9, 0);
    
        String result = (String)MethodHandles.lookup().defineClass(cw.toByteArray())
            .getMethod("test").invoke(null);
        System.out.println(result);
      }
    
      private static void stackManipulation(MethodVisitor mv) {
                               // ...321
        mv.visitInsn(DUP2_X1); // ...21321
        mv.visitInsn(POP2);    // ...213
        mv.visitInsn(DUP_X2);  // ...3213
        mv.visitInsn(DUP_X2);  // ...33213
        mv.visitInsn(POP);     // ...3321
        mv.visitInsn(DUP2_X1); // ...321321
      }
    }
    

    and prints

    654321321
    

    You may look at the dup instruction and following sections in JVM specification for further reference.