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?
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.
I don't know when javac uses it, but when we generate code we use DUP and SWAP a lot. For example if you're doing the equivalent of
x.setCharm(y);
x.setSpin(z);
then you would load x and immediately DUP it, because invoking the first method will take it off the stack and you want to use it twice.
SWAP comes in handy when you're doing something like
y = x.getCharm();
z.setCharm(y);
where the first instruction leaves y on top of stack, you then stack z, and SWAP, so you now have the right values on stack to invoke the second instruction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With