0

Is there a way to remove a previously added code block in Javassist?

I'm working on a project which modifies .class files via Javassist. Among other things it adds some code into constructors. I want this process to be runnable on the same .class file again and again without any side effects. However at the moment, after each run the same code is added to constructor one more time.

Is there a way to prevent this?

r a f t
  • 411
  • 3
  • 9
  • Not sure when you exactly modify your class - before loading it with a custom classloader for an implementation of an injection mechanism? But it seems that you somehow reuse the modified version. Why not keep track of the classes that have been modified by javassist and skip modification if the class is within a list of already modified classes? It is more of a hack than a javassist solution but it should work. – Roman Vottner Dec 25 '13 at 17:38
  • [This](https://github.com/raftAtGit/Postvayler) is a post compilation process, modifies **.class** files and writes the modified classes to same place, very much like [JiBX](http://jibx.sourceforge.net/bindcomp.html)'s binding compiler. I guess your suggestion is the best option for now, i.e. keeping track of modified classes and not modifying them again. – r a f t Dec 26 '13 at 18:32
  • I think your best option is to mark the constructors that have already been injected with an annotation and skip them if they already have the annotation. The annotation would be injected through the javassist manipulation using AnnotationsAttribute. I can post a detailed answer with this solution if you want. – pabrantes Jan 02 '14 at 12:18
  • Thanks @pabrantes, thats's a good suggestion too. But for simplicity I will stick to skipping a previously instrumented class as a whole. I instrument most super [@Persistent](https://github.com/raftAtGit/Postvayler/blob/master/Postvayler/src/raft/postvayler/Persistent.java) classes and their subclasses differently and my library will break if one changes that inheritence hierarchy. But I can't see a way how one can do that without compiling both of them with **javac** and javac will wipe away all of my injected code. – r a f t Jan 03 '14 at 18:31
  • Ok. How are you keeping track of instrumented classes? You can still use the annotation idea to mark the class as a all instead of keeping a separate text file with the list or something, at least like that the class will be able to answer by itself if it was instrumented. Regarding the hierarchy problem, someone can change it also through instrumentation but I don't think that's a problem you have to worry about. – pabrantes Jan 06 '14 at 09:09
  • I don't use external text files or something similar to keep track of instrumented classes. Using injected annotations for marking a class is good idea, but I don't need that since I already inject some fields into the class. For hierarchy problem, yes I simply ignore that case for now. – r a f t Jan 07 '14 at 09:30

1 Answers1

0

If you know the code to remove you can do it with Javassist easily:

In the next example this will remove all lines that contains the method "printStackTrace" of any "Exception" class, all the magic occurs with the instrument and replace methods

 ... 
 ClassPool cp = ClassPool.getDefault();
 try{
     CtClass ct = cp.getCtClass("com.cm.main.ConcretClass");
     CtMethod m = ct.getDeclaredMethod("testException");
     m.instrument(new ExprEditor() {
            public void edit(MethodCall m) throws CannotCompileException {
                String regexPattern = ".*Exception";
                if (m.getClassName().matches(regexPattern) && m.getMethodName().matches("printStackTrace")) {
                    m.replace(";");
                }
            }

            ;
        });
    } catch (CannotCompileException e) {
        e.printStackTrace();
    } catch (NotFoundException e) {
        e.printStackTrace();
    } catch (BadBytecode badBytecode) {
        badBytecode.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
... 

The ConcretClass:

public class ConcretClass{

public String getName() {
    return this.name + "-Extra";
}

public void testException(){
    try {
        FileOutputStream file = new FileOutputStream("C:\\Temp\\downloads");
        file.close();
    } catch (FileNotFoundException e2) {
        e2.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
   }
 }
Rafael Aguilar
  • 3,084
  • 1
  • 25
  • 31