I am using Javassist to extend certain classes at runtime .
In a couple of places (in the generation code) I need to create instances of the Javassist ConstPool class.
For example, to mark a generated class as synthetic, I wrote something like this:
CtClass ctClassToExtend = ... //class to extend
CtClass newCtClass = extend(ctClassToExtend, ...); //method to create a new ctClass extending ctClassToExtend
SyntheticAttribute syntheticAttribute = new SyntheticAttribute(ctClassToExtend.getClassFile().getConstPool()); //creating a synthetic attribute using an instance of ConstPool
newCtClass.setAttribute(syntheticAttribute.getName(), syntheticAttribute.get()); //marking the generated class as synthetic
This is working as expected, but I have certain doubts about this being entirely correct. Concretely, my main question is:
Is the call to CtClass.getClassFile().getConstPool() the correct way to get a constant pool in this example?. If not, what is the general proper way to get the right instance of a constant pool when creating a new class at runtime using Javassist?
Also, I am a bit lost regarding what is happening behind the curtains here: Why do we need a constant pool to create a instance of a synthetic attribute ?, or in general, of any other kind of class attributes ?
Thanks for any clarification.
Don’t know if you’re still interested in the answer, but at least might help others that
find this question.
First of all, a small suggestion to everyone that starts creating/modifying bytecode
and needs more in-depth information on how the JVM internals works, the JVM’s specification documentation might look bulky and scary at first but it’s invaluable help!
Yes, it is. Each Java Class has a single constant pool, so basicaly every time you need to access the constant
pool for a given class you can do
ctClass.getClassFile().getConstPool(), although you must keep in mind thefollowing:
In javassist the constant pool field from
CtClassis an instance field, that means that if you have twoCtClassobjectsrepresenting the same Class you’ll have two diferrent instances of constant pool (even though they represent
the constant pool in the actual class file). When modifying one of the
CtClassinstances you must use theassociated constant pool instance in order to have the expected behaviour.
There are times where you might not have the
CtClassbut rather aCtMethodor aCtFieldwhich don’t let you backtrace to theCtClassinstance, in such cases you can usectMethod.getMethodInfo().getConstPool()andctField.getFieldInfo().getConstPool()to retrieve the correct constant pool.Since I’ve mentioned
CtMethodandCtField, keep in mind that if you want to add attributes to any of these, it can’t be through theClassFileObject, but throughMethodInfoandFieldInforespectively.To answer this question, I’ll start quoting section 4.4 regarding JVM 7 specs (like I said, this documentation is quite helpful):
With this in mind, I think the best way to shed some light on this subject is to look at a class file dump. We can achieve this by running the following command:
Javap comes with the java SDK and it’s a good tool to analyse classes at this level, the explanation of each switch
Here’s the output for
test.Test1class that I modified via javassist to have the synthetic attribute both in the class and in theinjectedMethodNotice that both the class and the method have the attribute Synthetic: true which mean they are Synthetic but, you the synthetic symbol must also be present in the constant pool (check #33).
Another example regarding the use of constant pool and class/method attributes is the annotation added to someInjectedMethod with runtime retention policy. The method’s bytecode only has a reference to the constant pool #32 symbol, and only there you learn that
the annotation is from the type test/TestAnnotationToShowItInConstantTable;
Hope things got a bit more clear for you now.