views:

352

answers:

6

I want to replace calls to a given class with calls to anther class within a method body whilst parsing compiled class files...
or put another way, is there a method of detecting usages of a given class in a method and replacing just that part of the method using something like javaassist.

for example.. if I had the compiled version of

class A { public int m() { int i = 2; B.multiply(i,i); return i; } }

is there a method of detecting the use of B and then altering the code to perform

class A { public int m() { int i = 2; C.divide(i,i); return i; } }

I know the alternative would be to write a parser to grep the source files for usages but I would prefer a more elegant solution such as using reflection to generate new compiled class files.

Any thoughts ?

+1  A: 

The format of byte code for compiled Java is specified and products exist that manipulate it.

This library appears to have the capability you need. I've no idea how easy it is to do these transformations reliably.

djna
A: 

It's much easier to perform these operations ahead-of-time, where the executable on disk is modified before launching the application. Manipulating the code in memory at run time is even more prone to errors than manipulating code in memory in C/C++. Why do you need to do this?

280Z28
+1  A: 

As @djna says, it is possible to modify bytecode files before you load them, but you probably do not want to do this:

  • The code that does the code modification is likely to be complex and hard to maintain.
  • The code that has been modified is likely to be difficult to debug. For a start, a source level debugger will show you source code that no longer corresponds to the code that you are actually editing.

Bytecode rewriting is useful in certain cases. For example, JDO implementations use bytecode rewriting to replace object member fetches with calls into the persistence libraries. However, if you have access to the source code for these files, you'll get a better (i.e. more maintainable) solution by preprocessing (or generating) the source code.

EDIT: and AOP or Groovy sound like viable alternatives too, depending on the extent of rewriting that you anticipate doing.

Stephen C
Down voters - care to comment why?
Stephen C
+1  A: 

An AOP framework, like AspectJ, should be able to do what you want.

gustafc
A: 

If you don't mind using Groovy, you can intercept the call to B.multiply and replace it with C.divide. You can find an example here.

Geo
A: 

BCEL or ASM.

I recently looked at a number of libraries for reading Java class files. BCEL was the fastest, had the least number of dependencies, compiled out of the box, and had a deliciously simple API. I preferred BCEL to ASM because ASM has more dependencies (although the API is reputedly simpler).

AspectJ, as previously mentioned, is another viable option.

BCEL is truly simple. You can get a list of methods in three lines of code:

ClassParser cp = new ClassParser( "A.class" );
JavaClass jc = cp.parse();
Method[] m = jc.getMethods();

There are other API facilities for further introspection, including, I believe, ways to get the instructions in a method. However, this solution will likely be more laborious than AspectJ.

Another possibility is to change the multiply or divide methods themselves, rather than trying to change all instances of the code that calls the operation. That would be an easier road to take with BCEL (or ASM).

Dave Jarvis