Support or explicitly prohibit insertion of new code in MethodVisitor.visitEnd()
There is quite a common (mis)use of Asm library: insertion of a new code at the end of the instrumented method at MethodVisitor.visitEnd()
. For example, see https://stackoverflow.com/a/23558972/7889332
The "reasons" for such use are obvious:
- it is not prohibited in Javadoc of
visitEnd()
; - based on method names,
visitEnd()
just sounds more logical than use ofvisitMaxs()
, which would be better alternative.
The issue with visitEnd()
is that the frame and maxs computation actually occurs in visitMaxs()
, which is invoked before visitEnd()
. So the code inserted in visitEnd()
is skipped from the computation. Depending on the inserted code and the current computation mode, this could result in
- correct code emitted (e.g. if no frames required in new code and maxs are not actually affected by it);
-
java.lang.VerifyError
on verification of the instrumented class (e.g. if maxs are affected, or new frames are required but not inserted); -
java.lang.NullPointerException
during the frames computation, if thevisitEnd()
is supposed to insert Labels used in the above code, like
java.lang.NullPointerException
at org.objectweb.asm.MethodWriter.computeAllFrames(MethodWriter.java:1566)
at org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1540)
at org.objectweb.asm.MethodVisitor.visitMaxs(MethodVisitor.java:773)
at ...
The easiest fix of the problem would be to explicitly prohibit use MethodVisitor.visitEnd
(and anything else after visitMaxs()
) for inserting new code. Optionally, this could be forced by throwing IllegalStateException
with the if such insertion is detected.
A better fix would be to support insertion of the new code at MethodVisitor.visitEnd
by moving the actual computations to MethodWriter.visitEnd()
. Unfortunately, there is existing code which do not invoke visitEnd()
when creating new methods (at least, in Asm tests). Although this still could be solved, the solution itself would become quite complicated, like
- try to perform computation at
visitMaxs
in try-catch block (to catch potential NPE); - if failed or detected that some new code is inserted after that computation, repeat the computation at
visitEnd
.