Modifying CtClass
starksm64 Dec 7, 2005 11:01 AMI'm trying to use the following code fragment to write out multiple versions of a classes bytecode to a test classpath:
File libDir = new File(jbosstestDeployDir);
File classes1 = new File(libDir, "classes1");
classes1.mkdir();
// Create a test.Info class with a static String version = "Version 1.0"
ClassPool pool = ClassPool.getDefault();
CtClass info = pool.makeClass("test.Info");
// Don't let javassist optimize the class so we can modify it after writing it out
info.stopPruning(true);
CtClass s = pool.get("java.lang.String");
CtField version = new CtField(s, "version", info);
version.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
info.addField(version, CtField.Initializer.constant("Version 1.0"));
info.writeFile(classes1.getAbsolutePath());
// Create a test.Info class with a static String version = "Version 2.0"
File classes2 = new File(libDir, "classes2");
classes2.mkdir();
info.defrost();
info.removeField(version);
version = new CtField(s, "version", info);
version.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
info.addField(version, CtField.Initializer.constant("Version 2.0"));
info.writeFile(classes2.getAbsolutePath());
The intent is that the classes1/test/Info.class corresponds to:
package test;
public class Info
{
public static String version = "Version 1.0";
}
while classes2/test/Info.class corresponds to:
package test;
public class Info
{
public static String version = "Version 2.0";
}
The javap output from the classes2/test/Info.class is showing that the version field is being initialized twice, once with the expected "Version 2.0" string and then this is overwritten with the old ""Version 1.0" value:
[starksm@banshee9100 testsuite]$ javap -verbose -classpath output/lib/classes2 test.Info
Compiled from "Info.java"
public class test.Info extends java.lang.Object
SourceFile: "Info.java"
minor version: 3
major version: 45
Constant pool:
const #1 = Asciz test/Info;
const #2 = class #1; // test/Info
const #3 = Asciz java/lang/Object;
const #4 = class #3; // java/lang/Object
const #5 = Asciz <init>;
const #6 = Asciz ()V;
const #7 = Asciz Code;
const #8 = class #3; // java/lang/Object
const #9 = NameAndType #5:#6;// "<init>":()V
const #10 = Method #8.#9; // java/lang/Object."<init>":()V
const #11 = Asciz <clinit>;
const #12 = Asciz Version 1.0;
const #13 = String #12; // Version 1.0
const #14 = class #1; // test/Info
const #15 = Asciz version;
const #16 = Asciz Ljava/lang/String;;
const #17 = NameAndType #15:#16;// version:Ljava/lang/String;
const #18 = Field #14.#17; // test/Info.version:Ljava/lang/String;
const #19 = Asciz SourceFile;
const #20 = Asciz Info.java;
const #21 = Asciz Version 2.0;
const #22 = String #21; // Version 2.0
const #23 = NameAndType #15:#16;// version:Ljava/lang/String;
const #24 = Field #2.#23; // test/Info.version:Ljava/lang/String;
{
public static java.lang.String version;
public test.Info();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: return
static {};
Code:
Stack=1, Locals=0, Args_size=0
0: ldc #22; //String Version 2.0
2: putstatic #24; //Field version:Ljava/lang/String;
5: ldc #13; //String Version 1.0
7: putstatic #18; //Field test/Info.version:Ljava/lang/String;
10: return
}
Is that expected?
Is there a variation of this code fragment that will produce the desired output bytecode?