Hi Volker,
I have a question regarding inserting code before the return statement. How do you
handle branch instructions that targets the return instruction like in the method
below?
boolean bool = false;
synchronized public void test11(String tmp) {
if (bool) {
System.out.println(tmp);
return;
}
}
public synchronized void test11(String arg0), (Ljava/lang/String;)V
0: aload_0[42](1)
1: getfield[180](3) 70
4: ifeq[153](3) -> return
7: getstatic[178](3) 14
10: aload_1[43](1)
11: invokevirtual[182](3) 16
14: return[177](1)
15: return[177](1)
When bool is false the next instruction would be a branch to the last return
instruction which would lead to that the jsr to the finally clause is never executed.
I had a go too at the dreadful trace by injecting try finally problem but could not
get all enter and exit traces to match until I retargeted all branch instructions
targeting return instructions to the injected jsr instruction. Basically I looked at
the targeters of the return instrucions (which is visible through the getTargeters
method on InstructionHandle) and retargeted them. I must say that injecting try
finally around the body of a method never stops to surprise me and I still do not
trust it completly. Injecting try finally around a method call I gave up on and
instead switched the invoke instruction to a injected dummy method invoking the call
from there instead.
Regards
/Peter
-----Original Message-----
From: Volker Leidl <[EMAIL PROTECTED]>
To: [EMAIL PROTECTED]
Date: Mon, 21 Oct 2002 13:21:25 +0200
Subject: Re: Instrumenting a constructor
Hi Amol!
First of all, I would suggest to read through the section of the JVM
specification that describes how a try-finally block is to be compiled
into bytecode by use of the jsr and ret instructions
(http://java.sun.com/docs/books/vmspec/2nd-edition/html/Compiling.doc.html#13789).
As you correctly stated there are two ways to exit a method on the
bytcode level: by explicitly returning
or by throwing an exception. In BCEL you can cope with the second one
easily by adding a new exception handler that encloses the critical code
section. You can do so by calling MethodGen.addExceptionHandler(...).
The last parameter to this method takes the type of exception
to be caught by this handler. To accept any type, simply use null. At
the target of the exception handler you
have to jump to your finally block.
Secondly you have to insert code before each return instruction in the
method code, which is slightly more complicated. I did so by utilizing
the Visitor facility of BCEL. A convenient way is to sublcass
org.apache.bcel.generic.EmptyVisitor and overwrite all visit* methods
that handle return instructions, e.g. visitRETURN, visitARETURN, etc.
For example in my code the visitDRETURN (for double values) looks like
the following:
//double has a word size of 2
public void visitDRETURN(DRETURN obj) {
if (max - offset < 2) max += (2 - max + offset);
//store away return value on the stack
code.insert(handle, new DSTORE(offset + 1));
code.insert(handle, new JSR(finallyClause));
code.insert(handle, new DLOAD(offset + 1));
}
Since you need to store away the return value that is on the top of the
stack before the respective return instruction is called, you need some
extra local variables. In the case of a double value you will need 2
slots. The first line of the method is dedicated to reserving enough
slots (Don't mind the details). Then the value on top of the stack is
stored away with DSTORE, the finally block is called with JSR
(finallyClasue contains the instruction handle to the first instruction
of the finally clause) and in the end the previously stored value will
be pushed back on the stack again. The handle variable in the code above
holds an instruction handle to the actual return instruction. I
initialize it in a visitInstructionList() method in the visitor, which
iterates over all instructions within a given interval of instruction
handles of an InstructionList, and which I call initially to modify all
return instructions.
So to sum up here is what I do in my instrumentation code:
*) Retrieve MethodGen of the method to be instrumented
*) Append instructions of finally block. See the link to the JVM
specification above how to code a finally clause. Store away the
instruction handle to the first instruction of the block.
*) Insert instructions for exception handler before the finally block.
There you have to ASTORE the exception on the top of the stack, call JSR
to jump to the first instruction of the finally block, ALOAD the
exception back on the stack and ATHROW it.
*) Run the whole original code through an instance of the return
instruction visitor. You will need to pass the instruction handle to the
finally block to the visitor by some way.
*) Make sure that maxLocals and maxStack properties of the MethodGen get
the correct values assigned.
Cheers,
Volker.
P.S. I would suggest to transfer the thread to the bcel users list, so
that others could possibly profit from it, or perhaps someone points me
to a mistake in my solution I'm not aware of yet. If you have any
questions, it would be best to post it there.
Amol Kulkarni wrote:
>Hi Volker,
>
>I'm working on bytecode instrumentation of java
>classes. I'm facing a problem tracing the method exit
>points. As you must be fully aware that method can
>exit from nay point due to exception and/or multiple
>return statements. So I plan to encapsulate the whole
>method body in to try-finally block and add my code
>snippet in the finally block. I understand that its
>very very complicated and found that you have already
>worked out on this from the link
>http://www.mail-archive.com/bcel-user@;jakarta.apache.org/msg00132.html.
>
>Can you help me in this problem by providing the
>details of "How to add try-finally block to a
>method?".
>
>Thanx.
>
>Regards,
>Amol
>
>__________________________________________________
>Do you Yahoo!?
>Y! Web Hosting - Let the expert host your web site
>http://webhosting.yahoo.com/
>
>
>
--
To unsubscribe, e-mail: <mailto:bcel-user-unsubscribe@;jakarta.apache.org>
For additional commands, e-mail: <mailto:bcel-user-help@;jakarta.apache.org>
--
To unsubscribe, e-mail: <mailto:bcel-user-unsubscribe@;jakarta.apache.org>
For additional commands, e-mail: <mailto:bcel-user-help@;jakarta.apache.org>