Introduction to the ASM 2.0 Bytecode Framework
Pages: 1, 2, 3, 4, 5, 6, 7, 8
Visiting Method Code
In ASM, a method declaration is represented by the
ClassVisitor.visitMethod(), and the rest of the method
bytecode artifacts (Section [3] on class file
format diagram) are represented by number of the visit methods
in MethodVisitor. These methods are called in the
following order, where "*" marks repeated methods and "?" marks
methods that can be called once at most. In addition, the
visit...Insn and visitLabel
methods must be called in the sequential order of the bytecode
instructions of the visited code, and the
visitTryCatchBlock, visitLocalVariable,
and visitLineNumber methods must be called after the
labels passed as arguments have been visited.
| ? |
visitAnnotationDefault |
Visits the default value for annotation interface method |
| * |
visitAnnotation |
Visits a method annotation |
| * |
visitParameterAnnotation |
Visits a method parameter annotation |
| * |
visitAttribute |
Visits a non-standard method attribute |
| ? |
visitCode |
Starts the visit of the method's code for non-abstract and non-native methods |
| * | visitInsn |
Visits a zero operand instruction: NOP, ACONST_NULL, ICONST_M1,
ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5,
LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0,
DCONST_1, IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD,
SALOAD, IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE,
CASTORE, SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1,
DUP2_X2, SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB,
IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM,
DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR,
IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, L2I, L2F, L2D,
F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, LCMP, FCMPL, FCMPG,
DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, RETURN,
ARRAYLENGTH, ATHROW, MONITORENTER, or MONITOREXIT. |
visitFieldInsn |
Visits a field instruction: GETSTATIC, PUTSTATIC, GETFIELD or
PUTFIELD. |
|
visitIntInsn |
Visits an instruction with a single int operand: BIPUSH, SIPUSH,
or NEWARRAY. |
|
visitJumpInsn |
Visits a jump instruction: IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE,
IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE,
IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL, or IFNONNULL. |
|
visitTypeInsn |
Visits a type instruction: NEW, ANEWARRAY, CHECKCAST, or
INSTANCEOF. |
|
visitVarInsn |
Visits a local variable instruction: ILOAD, LLOAD, FLOAD,
DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE, or RET. |
|
visitMethodInsn |
Visits a method instruction: INVOKEVIRTUAL, INVOKESPECIAL,
INVOKESTATIC, or INVOKEINTERFACE. |
|
visitIincInsn |
Visits an IINC instruction. |
|
visitLdcInsn |
Visits an LDC instruction. |
|
visitMultiANewArrayInsn |
Visits a MULTIANEWARRAY instruction. |
|
visitLookupSwitchInsn |
Visits a LOOKUPSWITCH instruction. |
|
visitTableSwitchInsn |
Visits a TABLESWITCH instruction. |
|
visitLabel |
Visits a label. | |
visitLocalVariable |
Visits a local variable declaration. | |
visitLineNumber |
Visits a line number declaration. | |
visitTryCatchBlock |
Visits a try-catch block. | |
visitMaxs |
Visits the maximum stack size and the maximum number of local variables of the method. | |
visitEnd |
Visits the end of the method. | |
Note that the visitEnd method must
always be called at the end of method processing.
ClassReader does that for you, but it should be taken
care of in a custom bytecode producer; e.g., when a class is
generated from scratch or when new methods are introduced.
Also note that if a method actually has some bytecode (i.e.,
if it is not abstract and not a native method), then
visitCode must be called before the first
visit...Insn call, and the visitMaxs
method must be called after last visit...Insn
call.
Each of the visitIincInsn,
visitLdcInsn, visitMultiANewArrayInsn,
visitLookupSwitchInsn, and
visitTableSwitchInsn methods uniquely represent one
bytecode instruction. The rest of the visit...Insn
methods--namely, visitInsn,
visitFieldInsn, visitIntInsn,
visitJumpInsn, visitTypeInsn,
visitVarInsn, and visitMethodInsn--represent more then one bytecode instruction, with their opcodes
passed in a first method parameter. All constants for those opcodes
are defined in the Opcodes interface. This approach is
very performant for bytecode parsing and formatting. Unfortunately,
this could be a challenge to the developer who is trying to
generate code, because ClassWriter does not verify
these constraints. However, there is a
CheckClassAdapter that could be used during
development to test generated code.
Another challenge with any kind of bytecode generation or
transformation is that offsets within method code can change and
should be adjusted when additional instructions are inserted or
removed from the method code. This is applicable to parameters of
all jump opcodes (if, goto,
jsr, and switch), as well as to try-catch
blocks, line number and local variable declarations, and to some of
the special attributes (e.g., StackMap, used by CLDC).
However, ASM hides this complexity from the developer. In order to
specify positions in the method bytecode and not have to use
absolute offsets, a unique instance of the Label class
should be passed to the visitLabel method. Other
MethodVisitor methods such as
visitJumpInsn, visitLookupSwitchInsn,
visitTableSwitchInsn, visitTryCatchBlock,
visitLocalVariable, and visitLineNumber
can use these Label instances even before the
visitLabel call, as long as the instance will be called later in
a method.
The above may sound complicated, and at first glance requires
deep knowledge of the bytecode instructions. However, using
ASMifierClassVisitor on compiled classes allows you to
see how any given bytecode could be generated with ASM. Moreover,
applying ASMifier on two compiled classes (an original one and one
after applying the required transformation) and then running
diff on the output gives a good hint as to what ASM
calls should be used in the transformer. This process is explained in
more detail in several articles (see the Resources section below). There is even a plugin for the
Eclipse IDE, shown in Figure 4, that provides a great support
for generating ASM code and comparing ASMifier output right from
Java sources, and also includes a contextual bytecode reference.

Figure 4. Eclipse ASM plugin (Click on the picture to see a
full-size image)