You are viewing a plain text version of this content. The canonical link for it is here.
Posted to bcel-user@jakarta.apache.org by Huw Evans <hu...@dcs.gla.ac.uk> on 2002/10/01 17:03:47 UTC

Re: Instrumenting a constructor

> So, my primary question is this: What's the easiest way
> to instrument the constructor to add the new logic?

Rather than rename the constructor which generates the problems you mention,
you could call a private method that you define which contains your code.  The
private method would have to be added to the class for which you are changing
the constructor.    For example, this constructor:

public class Foo {
  public Foo() {
    if(thing) {
      i++;
      return;
    }

    switch(j) {
      case 0 : return;
      case 1 : j--;
               break;
      default : j++;
                return;
    }
}

would be instrumented to look like this:

public class Foo {
  public Foo() {
    if(thing) {
      i++;
      my_method();
      return;
    }

    switch(j) {
      case 0 : my_method();
               return;
      case 1 : j--;
               break;
      default : j++;
                my_method();
                return;
    }

    my_method(); // needed at end, due to implicit return
} 

and my_method added to class Foo as a private method.

I think you only have to identify the return statements (and the end of the 
constructor) and do an insert of the call to your method (taking care about 
pushing any necessary parameters).

You will have to be careful about branches, e.g., in the switch.  If j is 1, 
we do j-- and then break to the end of the constructor where we do the 
my_method().  If j is zero or any other non-zero value, you need to call the 
correct block of code which has to have a call to my_method embedded within 
it.  However, this is achievable as you are only looking for one kind of 
bytecode, a return type.  You just then need to insert the correct bytecodes 
to call the new method and patch up any branches and any try-catch blocks.  
However, BCEL helps *A LOT* with the branches and try-catch clauses (which are 
also branches as they cause jumps to different targets which you will change 
as you are putting code in.

Be sure to only call insert on the instruction list object and the other 
bytecode in it will be OK, e.g., targets for try should still work as these 
bytecodes have just been shifted in the list.  Do NOT work with a copy of the 
list or else you inserts will not work as copies of the bytecodes have been 
taken and then BCEL looses its notion of which bytecode it's dealing with (as 
the instruction handle has changed) and so you have to do a lot of your own 
work patching up the branches and try-catch blocks.

> Some other questions:
> 
> There's no guarantee that the "this" reference will be in
> slot 0, by the time my code executes, right? (There
> doesn't seem to be any hard and fast rule that says
> argument slots shouldn't be overwritten - I've seen javac
> overwrite them with impugnity). So I'll have to add some
> instructions at the top of the method to immediately
> store it to an unused slot, correct?

I'm not sure about this, I would hope this would be in slot 0 right from the 
start, as there is an implicit call to the super constructor which is 
dispatched via the VM with respect to this (i.e., there is nothing at the 
bytecode level to indicate super, 'this' is passed to the VM and it does the 
step up to super).

> When instrumenting a method, I need to create a new one
> and selectively copy instructions from the old one. Will
> the jump targets remain correct when I add or remove
> instructions as long as I don't remove the targetted
> instructions? 

No, see above.  Copying destroys all of this as you copy the underlying 
instruction handle.

> If I use InstructionFactory.addLocalVariable before I
> begin adding any of the old instructions to the new
> method, will the old instructions retarget adjusted
> variable slots?

If you leave the original alone and insert into its list I believe the 
branches to the original bytecodes should be OK.

Try all of this out on a very simple example first, get the fundamental 
questions answered first and gradually increase the generality from there.

Huw



--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>


Re: Instrumenting a constructor

Posted by Volker Leidl <le...@nt.imp.univie.ac.at>.
Hi!

In my project I solved a similar problem by implementing a try finally 
block with jsr and ret instructions.
To identify all exit points in an InstructionList I have written a 
Visitor that visits all return statements to insert code to store away 
possible stack values, jump to a finally block that has been appended to 
the instruction list previously, and to load a previously stored value 
back onto the stack again when execution returns. An exception handler 
 for type "any" (MethodGen.addExceptionHandler(..., null)) that causes 
the finally block to
be executed if an exception is thrown has to be added also.
Unfortunately this solution is much more complicated on the bytecode 
level than on the Java language level.

Regards,
Volker.

Huw Evans wrote:

>>So, my primary question is this: What's the easiest way
>>to instrument the constructor to add the new logic?
>>    
>>
>
>Rather than rename the constructor which generates the problems you mention,
>you could call a private method that you define which contains your code.  The
>private method would have to be added to the class for which you are changing
>the constructor.    For example, this constructor:
>
>public class Foo {
>  public Foo() {
>    if(thing) {
>      i++;
>      return;
>    }
>
>    switch(j) {
>      case 0 : return;
>      case 1 : j--;
>               break;
>      default : j++;
>                return;
>    }
>}
>
>would be instrumented to look like this:
>
>public class Foo {
>  public Foo() {
>    if(thing) {
>      i++;
>      my_method();
>      return;
>    }
>
>    switch(j) {
>      case 0 : my_method();
>               return;
>      case 1 : j--;
>               break;
>      default : j++;
>                my_method();
>                return;
>    }
>
>    my_method(); // needed at end, due to implicit return
>} 
>
>and my_method added to class Foo as a private method.
>
>I think you only have to identify the return statements (and the end of the 
>constructor) and do an insert of the call to your method (taking care about 
>pushing any necessary parameters).
>
>You will have to be careful about branches, e.g., in the switch.  If j is 1, 
>we do j-- and then break to the end of the constructor where we do the 
>my_method().  If j is zero or any other non-zero value, you need to call the 
>correct block of code which has to have a call to my_method embedded within 
>it.  However, this is achievable as you are only looking for one kind of 
>bytecode, a return type.  You just then need to insert the correct bytecodes 
>to call the new method and patch up any branches and any try-catch blocks.  
>However, BCEL helps *A LOT* with the branches and try-catch clauses (which are 
>also branches as they cause jumps to different targets which you will change 
>as you are putting code in.
>
>Be sure to only call insert on the instruction list object and the other 
>bytecode in it will be OK, e.g., targets for try should still work as these 
>bytecodes have just been shifted in the list.  Do NOT work with a copy of the 
>list or else you inserts will not work as copies of the bytecodes have been 
>taken and then BCEL looses its notion of which bytecode it's dealing with (as 
>the instruction handle has changed) and so you have to do a lot of your own 
>work patching up the branches and try-catch blocks.
>
>  
>
>>Some other questions:
>>
>>There's no guarantee that the "this" reference will be in
>>slot 0, by the time my code executes, right? (There
>>doesn't seem to be any hard and fast rule that says
>>argument slots shouldn't be overwritten - I've seen javac
>>overwrite them with impugnity). So I'll have to add some
>>instructions at the top of the method to immediately
>>store it to an unused slot, correct?
>>    
>>
>
>I'm not sure about this, I would hope this would be in slot 0 right from the 
>start, as there is an implicit call to the super constructor which is 
>dispatched via the VM with respect to this (i.e., there is nothing at the 
>bytecode level to indicate super, 'this' is passed to the VM and it does the 
>step up to super).
>
>  
>
>>When instrumenting a method, I need to create a new one
>>and selectively copy instructions from the old one. Will
>>the jump targets remain correct when I add or remove
>>instructions as long as I don't remove the targetted
>>instructions? 
>>    
>>
>
>No, see above.  Copying destroys all of this as you copy the underlying 
>instruction handle.
>
>  
>
>>If I use InstructionFactory.addLocalVariable before I
>>begin adding any of the old instructions to the new
>>method, will the old instructions retarget adjusted
>>variable slots?
>>    
>>
>
>If you leave the original alone and insert into its list I believe the 
>branches to the original bytecodes should be OK.
>
>Try all of this out on a very simple example first, get the fundamental 
>questions answered first and gradually increase the generality from there.
>
>Huw
>
>
>
>--
>To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
>For additional commands, e-mail: <ma...@jakarta.apache.org>
>
>
>  
>


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>