jakarta-bcel-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Huw Evans <...@dcs.gla.ac.uk>
Subject Re: Simple Example
Date Sat, 17 Aug 2002 13:45:18 GMT

Hi BCELers, I would like to take two minutes to share some observations with 
you about using BCEL and how I debug my BCEL-based programs.

As a followup to what Burt has said, in Ian's code:

  private Method addcall(MethodGen mg, ConstantPoolGen cpg) {

    InstructionList    il      = mg.getInstructionList();
    InstructionFactory factory = new InstructionFactory(cpg);

    Type[] argTypes = new Type[] {};
    InvokeInstruction is =
      factory.createInvoke("System",
			   "currentTimeMillis",
			   Type.LONG,
			   argTypes,
			   Constants.INVOKESTATIC);

    il.append(is);

    Method result = mg.getMethod();

    il.dispose();

    return(result);
  }

you can always print out the instruction list you have, with:

  System.out.println("Code is now:\n" + il)

this will print the bytecode stream for the method.  Your invokestatic will be
seen as the very last statement, beyond the return as Burt says.

Also, something to keep in mind when changing Java code is this: Not 
everything you need to change is in the bytecode, some of it is elsewhere, 
e.g., the constant pool.  These tend to be invariants that describe features 
of the class, such as exceptions.

For example, in your code above, you would insert the 'is' instruction
somewhere into the stream.  This will cause later instructions in the stream to
now be offset later by the number of bytes you have just added, i.e., they are 
further down the stream.  This can affect the target of branches, and 
exception handlers.  Target branches are things such as GOTO and are 
instructions in the stream, exception handlers are associated with the method 
(and the class) and BCEL can handle these for you.  If you change the stream 
by adding code, the exception handles will be moved for you as you have not 
taken a copy of the stream above in the first line of your addcall method.  
This is a good thing for you.  In terms of branch handles consider this piece 
of code:

  public void m1() {
    boolean b = true;
    int i = 0;

    for(;;) {
      if(b) {
        break;
      }
    }

    i++;    
  }

Let's say when you process the above method, you want to put your invoke static
before the i++, to say you are about the increment i.  This means the i++
bytecode has moved down the stream.  However, there will be a branch
instruction that references i++ due to the code above it, which breaks out of
the infinite for-loop if b is true (which it is).  Therefore, in the general
case, you need to make sure new code becomes the target of a branch to.  If you
do not, the verifier may not allow it, or, at worst, you have code that
wouldn't be executed (perhaps BCEL could help you out here).

I think it's also orth point out that anything in the BCEL api that ends in
Gen, e.g., MethodGen can be used to produce some kind of Java related object,
such as a method.  Once you have done that you can then generate another kind
of object from it, e.g., an org.apache.bcel.classfile.Method object which is a 
read-only version of the thing you just created.  Therefore, when you are 
manipulation Java classes, you tend to work from the ...Gen side of the API, 
over to the other side.

As a debug tool for the new bytecode that I construct I use jad, which is a 
Java decompiler, built in C++.  Its website is:

http://kpdus.tripod.com/jad.html

I find it useful to pass the code I build through it to see what the Java 
would look like.

Hope these hints help people in using BCEL.  I find BCEL to be a great tool.

Huw Evans



--
To unsubscribe, e-mail:   <mailto:bcel-user-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:bcel-user-help@jakarta.apache.org>


Mime
View raw message