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?