Use cases of jvm dup instruction

saga :

Java bytecode instruction set provides various forms of dup instruction. I'm having trouble understanding how these instructions and the swap instruction might be useful. What java code would produce bytecode with these instructions when compiled?

Holger :

Variants of dup can appear in ordinary Java code.

E.g. as elaborated in this answer, object instantiation typically uses dup, as new Object() gets compiled to

new #n              // n referencing Class java.lang.Object in the pool
dup
invokespecial #m    // m referencing Method java.lang.Object.<init>()V

Further, intArray[expression]++ gets compiled to

… (code pushing the results of intArray and expression)
dup2
iaload
iconst_1
iadd
iastore

and, a bit fancier

public static long example3(Long[] array, int i, long l) {
    return array[i]=l;
}

compiles to

   0: aload_0
   1: iload_1
   2: lload_2
   3: invokestatic  #3  // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
   6: dup_x2
   7: aastore
   8: invokevirtual #4  // Method java/lang/Long.longValue:()J
  11: lreturn

Changing the array type to long[] produces an example of dup2_x2

As discussed in this Q&A, javac does never use swap or nop (in the current implementation). But just because javac does not use a particular instruction, you can not assume that no compiler uses it.

E.g. there are other Java compilers, like ECJ, but there can be class files created by other programming languages or being already the result of an instrumentation tool, which becomes relevant when you want to instrument code at runtime. And future versions of javac also can use instructions they didn’t use before, just like before Java 8, Java code did not use invokedynamic.

This discussion points to a scenario, where swap would be appropriate. When using try-with-resource, there will be generated code, handling an exception caught while there is already a caught exception. Current javac compiles it (basically) to

astore        n
aload         o
aload         n
invokevirtual #x // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V

where o is the old variable holding the already caught exception and n will be an entirely new variable, which would not be needed when compiling this to

aload         o
swap
invokevirtual #x // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V

instead. So it’s not like these instruction were never-needed exotic constructs. It’s just an implementation detail when a particular code generator doesn’t use them.

Speaking of Instrumentation, it’s also important to keep in mind that a ClassFileTransformer is not guaranteed to receive exactly the same bytecode as produced by the compiler. It might be an equivalent byte code.

So the bottom line is, if you want to implement a ClassFileTransformer, you should be prepared to handle every legal byte code.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=93588&siteId=1