MethodWriter.visitFrame(F_NEW,...) requires explicit invocation for the implicit frame at offset zero
I am using ClassReader with EXPAND_FRAMES followed by some method adapters
followed by ClassWriter with COMPUTE_MAXS.
My method adapters require the insertion of new frames, and I accomplish this
by invoking super.visitFrame(F_NEW,...) to insert
uncompressed frames and allowing ASM to compress the frames.
I noticed that most of the time the first frame that was supposed to appear in
the generated bytecode was missing.
I studied the source code of MethodWriter and I understand why:
writeFrame() expects previousFrame to be set, previousFrame is set in
endFrame(),
and with F_NEW and COMPUTE_MAXS, endFrame is only ever invoked from from the
public visitFrame().
This implies that visitFrame() must be explicitly invoked for the implicit
frame at offset zero.
But its not that simple. At first I tried always explicitly invoking
visitFrame() for the implicit frame at offset zero.
But the problem is that there is some bytecode (I think it is bytecode
generated from Java source
in which the method starts with a while loop) in which ClassReader will
explicitly invoke
visitFrame() itself for the implicit frame at offset zero.
This meant that, along with my invocation of visitFrame(), there were two
invocations of
visitFrame() for the implicit frame, and so there actually was a frame at
offset zero in the generated bytecode.
It turns out that this is OK, the Verifier doesn't mind the explicit frame at
offset zero.
But the problem is worse. It turns out that there is some bytecode for which
ClassReader will invoke visitFrame() *twice* for the implicit frame at offset
zero.
Along with my invocation of visitFrame() this means that visitFrame() was being
invoked *three* times for the implicit frame at offset zero.
This is a problem.:
The first invocation causes MethodWriter to set previousFrame.
The second invocation causes MethodWriter to write an explicit frame at offset
zero.
The third invocation causes MethodWrtier to write a second explicit frame
with an offset delta of -1.
The offset delta of -1 causes the bytecode to be invalid. Even javap gets an
exception trying to parse it.
I see at least two ASM bugs here:
(1) MethodWriter should not require an explcit invocation of visitFrame() for
the implicit frame at offset zero.
(2) MethodWriter should not write an offset delta of -1 if visitFrame() is
invoked twice at the same offset.
My work around for this bug is the following: I have a visitor called
InitialStackFrameVisitor
which sits just before ClassWriter. InitialStackFrameVisitor will explicitly
invoke visitFrame for the implicit frame at offset zero. But it will also keep
track of every visit call and it will remember whether there has been an
instruction written since the last time a frame was written. If it detects
that visitFrame() is called twice at the same offset,
it will not forward the second call to visitFrame() on to ClassWriter.