In a large number of classes derived from the common ancestor class I have the method something()
whose behavior I want to modify to measure execution time.
I define two additional methods in the common ancestor class
// in the common ancestor class:
// this method is defined in all derived classes
public void something() {
System.out.println("something{");
System.out.println("something}");
}
// the body of this method will replace the body of something()
public void measuredSomething() {
System.out.println("measuredSomething{");
roomForSomething();
System.out.println("measuredSomething}");
}
// the body of this method will be replaced by the body of something()
public void roomForSomething() {
// this code will be replaced with something else
System.out.println("roomForSomething{");
System.out.println("roomForSomething}");
}
And I do the replacement with Javassist:
// someClass is known
String superClassName = someClass.getName();
String className = superClassName + "__proxy";
CtClass ctSuperClass = ClassPool.getDefault().get(superClassName);
CtClass ctClass = ClassPool.getDefault().makeClass(className);
ctClass.setSuperclass(ctSuperClass);
// roomForSomething := something
CtMethod methodSomething = ctSuperClass.getMethod("something", "()V");
CtMethod methodSomething2 = CtNewMethod.copy(methodSomething, "roomForSomething", ctClass, null);
ctClass.addMethod(methodSomething2);
// something := measuredSomething
// (and measuredSomething() will call roomForSomething())
CtMethod methodMeasuredSomething = ctSuperClass.getMethod("measuredSomething", "()V");
CtMethod methodMeasuredSomething2 = CtNewMethod.copy(methodMeasuredSomething, "something", ctClass, null);
ctClass.addMethod(methodMeasuredSomething2);
someClass = ctClass.toClass();
result = someClass.newInstance();
And it works. But the debugger cannot show the code that executes.
How do I copy the debugging information along with the byte code so that the debugger would show the source that executes?
(It is not possible when the method code is modified, but should be possible in this particular case.)
((I did propose to split the method something()
in two methods, in the same way as there are Thread.run()
to override, and Thread.start()
to invoke. I've been asked to avoid such change.))
UPDATE
The following method gives a bit better results: the debugger stops in the function generated via CtMethod.make()
only when it returns from the called function; the generated function is visible in the stack trace; the source of the generated function is not available.
// something := measuredSomething
CtMethod methodSomething = CtMethod.make("public void something() {super.measuredSomething();}", ctClass);
ctClass.addMethod(methodSomething);
// roomForSomething := something
CtMethod methodRoomForSomething = CtMethod.make("public void roomForSomething() {super.something();}", ctClass);
ctClass.addMethod(methodRoomForSomething);
It would be really good if it was possible to show the source of the generated function.