check it out:

http://jakarta.apache.org/bcel/xref/org/apache/bcel/generic/MethodGen.html#321
and
http://jakarta.apache.org/bcel/xref/org/apache/bcel/generic/MethodGen.html#282

it seems that: a) bcel in fact does not look at variable scopes when giving you a local variable number; and b) it doesn't adjust max_locals when giving you a local variable, so you'll have to call setMaxLocals even if you use this technique

I'm pretty sure the start and end arguments to addLocalVariable are required becuase this info ends up in the local variable table for the method, not becuase bcel has any interest in knowing internally where particula rvariables are in scope.

btw, sun's jvm does not require that the local variable table makes any sense, or is internally consistent, or correct, or even present, when running a class, and i'm pretty sure you can tell javac not to produce a lv table. So it's kind of risky to base the correctness of your generated code o nthe correctness of the lv table in the input code.

also, your code uses start == null and end == null in addLocalVariable, so even if bcel took scopes into account in assigning lv indices, it would have to assign different indices to sss and rrr.

[EMAIL PROTECTED] wrote:
you could just use MethodGen.getMaxLocals() as a local variable number
that is not used throughout the method.  Make sure you keep getMaxLocals
up to date by calling setMaxLocals when appropriate.


That is indeed a solution but the manually calling setMaxLocals leaves a
bad taste in my mouth (this is just something that I'll forget).

When I was browsing the API I ran into the addLocalVariable method of
MethodGen that does what I want, but I it seems that it doesn't really
take into account scopes of local variables. Consider the following code:

public double foo (int i, double d, int ii, double dd)
{
  {//scope block 1
   String sss = "NEW SSS";
   System.out.println(sss);
  }
  {//scope block 2
   String rrr = "NEW RRR";
   System.out.println(rrr);
  }
  return i + d + ii + dd;
}

I think we all can agree that the size of the local variables at the
beginning of the body is 7 {0=this, 1=i, 2-3=d, 4=ii, 5-6=dd} so the
variable sss should be stored at position 7 which is the 8th slot, but
since the scope blocks are used that location should be reused to store
variable rrr.

When compiling this code with javac 1.4.2 it'll nicely generate the
following for the two scope blocks and reuses the slot for the local
variables sss and rrr:

   0 ldc #25 <String "NEW SSS">
   2 astore 7
   4 getstatic #11 <Field java.io.PrintStream out>
   7 aload 7
   9 invokevirtual #16 <Method void println(java.lang.String)>
  12 ldc #26 <String "NEW RRR">
  14 astore 7
  16 getstatic #11 <Field java.io.PrintStream out>
  19 aload 7
  21 invokevirtual #16 <Method void println(java.lang.String)>
  <...> cut out

Local variables for method double foo(int, double, int, double)
   ClassB this  pc=0, length=36, slot=0
   int i  pc=0, length=36, slot=1
   double d  pc=0, length=36, slot=2
   int ii  pc=0, length=36, slot=4
   double dd  pc=0, length=36, slot=5
   java.lang.String sss  pc=4, length=8, slot=7
   java.lang.String rrr  pc=16, length=8, slot=7

But using the BCEL API to instrument the two scope blocks into an existing
method using this code:

            //sss
            LocalVariableGen lg = mg.addLocalVariable("sss", Type.STRING,
null, null);
            int sss = lg.getIndex();
            print.append(new PUSH(_cp, "NEW SSS"));
            print.append(_factory.createStore(Type.STRING, sss)); // "sss"
valid from here
            lg.setStart(print.append(_factory.createFieldAccess("java.lang.System",
"out", new ObjectType("java.io.PrintStream"),
Constants.GETSTATIC)));
            print.append(_factory.createLoad(Type.OBJECT, sss));
            lg.setEnd(print.append(_factory.createInvoke("java.io.PrintStream",
"println", Type.VOID, new Type[] { Type.STRING },
Constants.INVOKEVIRTUAL)));
            //rrr
            lg = mg.addLocalVariable("rrr", Type.STRING, null, null);
            int rrr = lg.getIndex();
            print.append(new PUSH(_cp, "NEW RRR"));
            print.append(_factory.createStore(Type.STRING, rrr)); // "rrr"
valid from here
            lg.setStart(print.append(_factory.createFieldAccess("java.lang.System",
"out", new ObjectType("java.io.PrintStream"),
Constants.GETSTATIC)));
            print.append(_factory.createLoad(Type.OBJECT, rrr));
            lg.setEnd(print.append(_factory.createInvoke("java.io.PrintStream",
"println", Type.VOID, new Type[] { Type.STRING },
Constants.INVOKEVIRTUAL)));

also works fine, but does not take into account the fact that the scopes
and yields:

Method double foo(int, double, int, double)
   0 ldc #90 <String "NEW SSS">
   2 astore 7
   4 getstatic #11 <Field java.io.PrintStream out>
   7 aload 7
   9 invokevirtual #16 <Method void println(java.lang.String)>
  12 ldc #92 <String "NEW RRR">
  14 astore 8
  16 getstatic #11 <Field java.io.PrintStream out>
  19 aload 8
  21 invokevirtual #16 <Method void println(java.lang.String)>
  <...> cut out

Local variables for method double foo(int, double, int, double)
   ClassB this  pc=24, length=12, slot=0
   int i  pc=24, length=12, slot=1
   double d  pc=24, length=12, slot=2
   int ii  pc=24, length=12, slot=4
   double dd  pc=24, length=12, slot=5
   java.lang.String sss  pc=4, length=8, slot=7
   java.lang.String rrr  pc=16, length=8, slot=8

and thus does not reuse the slot, which it should! Is this a bug? Is this
a feature? If it is shouldn't this be documented? What is the use of
setting the 'begin' and 'end' InstructionHandles if they are not used?



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]


--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]



Reply via email to