From 18c712cd99d1317d69f628fe58fd6ff9d10865c5 Mon Sep 17 00:00:00 2001 From: Muga Nishizawa <muga@f11vm.(none)> Date: Wed, 25 Aug 2010 21:35:52 +0900 Subject: [PATCH 1/2] object serialization with reflection and with dynamic code generation --- java/pom.xml | 12 + .../org/msgpack/tmp/DynamicCodeGenerator.java | 209 ++++++++++++++++++ .../src/main/java/org/msgpack/tmp/Image1.java | 47 ++++ .../src/main/java/org/msgpack/tmp/Image2.java | 85 +++++++ .../src/main/java/org/msgpack/tmp/Image3.java | 38 ++++ .../msgpack/tmp/ImagePackUnpackPerfTest.java | 134 +++++++++++ 6 files changed, 525 insertions(+) create mode 100644 java/src/main/java/org/msgpack/tmp/DynamicCodeGenerator.java create mode 100644 java/src/main/java/org/msgpack/tmp/Image1.java create mode 100644 java/src/main/java/org/msgpack/tmp/Image2.java create mode 100644 java/src/main/java/org/msgpack/tmp/Image3.java create mode 100644 java/src/main/java/org/msgpack/tmp/ImagePackUnpackPerfTest.java diff --git a/java/pom.xml b/java/pom.xml index 44806d51..6591a727 100755 --- a/java/pom.xml +++ b/java/pom.xml @@ -29,6 +29,12 @@ <version>4.8.1</version> <scope>test</scope> </dependency> + + <dependency> + <groupId>jboss</groupId> + <artifactId>javassist</artifactId> + <version>3.7.ga</version> + </dependency> </dependencies> <build> @@ -101,6 +107,12 @@ <name>MessagePack Maven2 Repository</name> <url>http://msgpack.org/maven2</url> </repository> + + <repository> + <id>repo2.maven.org</id> + <name>repo2.maven.org Maven2 Repository</name> + <url>http://repo2.maven.org/maven2</url> + </repository> </repositories> <distributionManagement> diff --git a/java/src/main/java/org/msgpack/tmp/DynamicCodeGenerator.java b/java/src/main/java/org/msgpack/tmp/DynamicCodeGenerator.java new file mode 100644 index 00000000..9e4336d3 --- /dev/null +++ b/java/src/main/java/org/msgpack/tmp/DynamicCodeGenerator.java @@ -0,0 +1,209 @@ +package org.msgpack.tmp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Field; +import java.util.HashMap; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.CtNewMethod; + +import org.msgpack.Packer; +import org.msgpack.Unpacker; + +public class DynamicCodeGenerator { + + private static final String ENHANCED_CLASS_NAME_POSTFIX = "_$$_Enhanced"; + + private static final String SPACE = " "; + + private static final String MODIFIER_PUBLIC = "public"; + + private static final String METHOD_NAME_MESSAGEPACK = "messagePack"; + + private static final String METHOD_NAME_MESSAGEUNPACK = "messageUnpack"; + + private static final String PACKER_CLASS_TYPE_NAME = Packer.class.getName(); + + private static final String PACKER_OBJECT_NAME = "pk"; + + private static final String UNPACKER_CLASS_TYPE_NAME = Unpacker.class + .getName(); + + private static final String UNPACKER_OBJECT_NAME = "pk"; + + private HashMap<String, Class<?>> classMap; + + private HashMap<String, Object> objectCacheMap; + + private ClassPool pool; + + public DynamicCodeGenerator() { + classMap = new HashMap<String, Class<?>>(); + objectCacheMap = new HashMap<String, Object>(); + pool = ClassPool.getDefault(); + } + + public Object newEnhancedInstance(Class<?> targetClass) throws Exception { + String targetClassName = targetClass.getName(); + //Class<?> enhancedClass = classMap.get(targetClassName); + Object enhancedObject = objectCacheMap.get(targetClassName); + //if (enhancedClass == null) { + if (enhancedObject == null) { + CtClass enhancedCtClass = createEnhancedCtClass(targetClassName); +// System.out.println("enhanced class name: " +// + enhancedCtClass.getName()); + addSuperclass(enhancedCtClass, targetClassName); + addConstructor(enhancedCtClass); + createMessagePackMethod(enhancedCtClass, targetClass); + createMessageUnpackMethod(enhancedCtClass, targetClass); + Class enhancedClass = loadEnhancedClass(enhancedCtClass); + //classMap.put(targetClassName, enhancedClass); + enhancedObject = enhancedClass.newInstance(); + objectCacheMap.put(targetClassName, enhancedObject); + } + //return newEnhancedInstance0(enhancedClass); + return enhancedObject; + } + + private CtClass createEnhancedCtClass(final String targetClassName) + throws Exception { + return pool.makeClass(targetClassName + ENHANCED_CLASS_NAME_POSTFIX); + } + + private void addSuperclass(CtClass enhancedCtClass, + final String targetClassName) throws Exception { + CtClass targetCtClass = pool.get(targetClassName); + enhancedCtClass.setSuperclass(targetCtClass); + } + + private void addConstructor(CtClass enhancedCtClass) throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append(MODIFIER_PUBLIC).append(SPACE).append( + // enhancedCtClass.getName()).append("(").append(")") + "Image3_$$_Enhanced").append("(").append(")").append(SPACE) + .append("{ super(); }"); // TODO +// System.out.println("cons: " + sb.toString()); + CtConstructor newCtConstructor = CtNewConstructor.make(sb.toString(), + enhancedCtClass); + enhancedCtClass.addConstructor(newCtConstructor); + } + + private void createMessagePackMethod(CtClass enhancedCtClass, + Class<?> targetClass) throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append(MODIFIER_PUBLIC).append(SPACE).append("void").append(SPACE) + .append(METHOD_NAME_MESSAGEPACK).append("(").append( + PACKER_CLASS_TYPE_NAME).append(SPACE).append( + PACKER_OBJECT_NAME).append(")").append(SPACE).append( + "throws").append(SPACE).append("java.io.IOException") + .append(SPACE).append("{"); + Field[] fields = targetClass.getFields(); + sb.append(PACKER_OBJECT_NAME).append(".").append("packArray").append( + "(").append(fields.length).append(")").append(";"); + for (Field field : fields) { + insertCodeOfMessagePack(field, sb); + } + sb.append("}"); +// System.out.println("messagePack method: " + sb.toString()); + CtMethod newCtMethod = CtNewMethod.make(sb.toString(), enhancedCtClass); + enhancedCtClass.addMethod(newCtMethod); + } + + private void insertCodeOfMessagePack(Field field, StringBuilder sb) { + Class<?> type = field.getType(); + if (type.equals(int.class)) { + sb.append(PACKER_OBJECT_NAME).append(".").append("pack") + .append("(").append(field.getName()).append(")") + .append(";"); + } else if (type.equals(String.class)) { + sb.append(PACKER_OBJECT_NAME).append(".").append("pack") + .append("(").append(field.getName()).append(")") + .append(";"); + } else { + throw new UnsupportedOperationException(); + } + } + + private void createMessageUnpackMethod(CtClass enhancedCtClass, + Class<?> targetClass) throws Exception { + StringBuilder sb = new StringBuilder(); + sb + .append(MODIFIER_PUBLIC) + .append(SPACE) + .append("void") + .append(SPACE) + .append(METHOD_NAME_MESSAGEUNPACK) + .append("(") + .append(UNPACKER_CLASS_TYPE_NAME) + .append(SPACE) + .append(UNPACKER_OBJECT_NAME) + .append(")") + .append(SPACE) + .append("throws") + .append(SPACE) + .append("org.msgpack.MessageTypeException, java.io.IOException") + .append(SPACE).append("{"); + Field[] fields = targetClass.getFields(); + sb.append(UNPACKER_OBJECT_NAME).append(".").append("unpackArray()").append(";"); + // TODO + for (Field field : fields) { + insertCodeOfMessageUnpack(field, sb); + } + sb.append("}"); +// System.out.println("messageUnpack method: " + sb.toString()); + CtMethod newCtMethod = CtNewMethod.make(sb.toString(), enhancedCtClass); + enhancedCtClass.addMethod(newCtMethod); + } + + private void insertCodeOfMessageUnpack(Field field, StringBuilder sb) { + Class<?> type = field.getType(); + if (type.equals(int.class)) { + sb.append(field.getName()).append(SPACE).append("=").append(SPACE) + .append(PACKER_OBJECT_NAME).append(".").append("unpackInt") + .append("(").append(")").append(";"); + } else if (type.equals(String.class)) { + sb.append(field.getName()).append(SPACE).append("=").append(SPACE) + .append(PACKER_OBJECT_NAME).append(".").append( + "unpackString").append("(").append(")").append(";"); + } else { + throw new UnsupportedOperationException(); + } + } + + private Class<?> loadEnhancedClass(CtClass enhancedCtClass) + throws Exception { + return enhancedCtClass.toClass(null, null); + } + + private Object newEnhancedInstance0(Class<?> enhancedClass) { + try { + return enhancedClass.newInstance(); + } catch (Exception e) { + throw new UnsupportedOperationException(); + } + } + + public static void main(String[] args) throws Exception { + DynamicCodeGenerator gen = new DynamicCodeGenerator(); + Image3 src = (Image3) gen.newEnhancedInstance(Image3.class); + src.title = "msgpack"; + src.uri = "http://msgpack.org/"; + src.width = 2560; + src.height = 1600; + src.size = 4096000; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + src.messagePack(new Packer(out)); + Image3 dst = (Image3) gen.newEnhancedInstance(Image3.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + dst.messageUnpack(pac); + +// System.out.println(dst.equals(src)); + } +} diff --git a/java/src/main/java/org/msgpack/tmp/Image1.java b/java/src/main/java/org/msgpack/tmp/Image1.java new file mode 100644 index 00000000..f1819ed6 --- /dev/null +++ b/java/src/main/java/org/msgpack/tmp/Image1.java @@ -0,0 +1,47 @@ +package org.msgpack.tmp; + +import org.msgpack.*; +import java.io.*; + +public class Image1 implements MessagePackable, MessageUnpackable { + public String uri = ""; + public String title = ""; + public int width = 0; + public int height = 0; + public int size = 0; + + public void messagePack(Packer pk) throws IOException { + pk.packArray(5); + pk.pack(uri); + pk.pack(title); + pk.pack(width); + pk.pack(height); + pk.pack(size); + } + + public void messageUnpack(Unpacker pac) throws IOException, + MessageTypeException { + int length = pac.unpackArray(); + if (length != 5) { + throw new MessageTypeException(); + } + uri = pac.unpackString(); + title = pac.unpackString(); + width = pac.unpackInt(); + height = pac.unpackInt(); + size = pac.unpackInt(); + } + + public boolean equals(Image1 obj) { + return uri.equals(obj.uri) && title.equals(obj.title) + && width == obj.width && height == obj.height + && size == obj.size; + } + + public boolean equals(Object obj) { + if (obj.getClass() != Image1.class) { + return false; + } + return equals((Image1) obj); + } +} diff --git a/java/src/main/java/org/msgpack/tmp/Image2.java b/java/src/main/java/org/msgpack/tmp/Image2.java new file mode 100644 index 00000000..9c357bf3 --- /dev/null +++ b/java/src/main/java/org/msgpack/tmp/Image2.java @@ -0,0 +1,85 @@ +package org.msgpack.tmp; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.msgpack.MessageTypeException; +import org.msgpack.Packer; +import org.msgpack.Unpacker; + +public class Image2 { + public String uri = ""; + public String title = ""; + public int width = 0; + public int height = 0; + public int size = 0; + + public void messagePack(Packer pk) throws IOException { + messagePackWithReflection(pk); + } + + public void messagePackWithReflection(Packer pk) throws IOException { + Class<?> cl = this.getClass(); + Field[] fields = cl.getFields(); + pk.packArray(fields.length); + for (Field field : fields) { + try { + Object obj = field.get(this); + pk.pack(obj); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + public void messageUnpack(Unpacker pac) throws IOException, + MessageTypeException { + messageUnpackWithReflection(pac); + } + + public void messageUnpackWithReflection(Unpacker pac) throws IOException, + MessageTypeException { + Class<?> cl = this.getClass(); + Field[] fields = cl.getFields(); + int length = pac.unpackArray(); + if (length != fields.length) { + throw new MessageTypeException(); + } + for (Field field : fields) { + try { + field.set(this, unpack(pac, field.getType())); + } catch (IOException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + public static Object unpack(Unpacker pk, Class<?> cl) throws IOException { + if (cl == int.class) { + return pk.unpackInt(); + } else if (cl == String.class) { + return pk.unpackString(); + } else { + throw new UnsupportedOperationException(); + } + } + + public boolean equals(Image2 obj) { + return uri.equals(obj.uri) && title.equals(obj.title) + && width == obj.width && height == obj.height + && size == obj.size; + } + + public boolean equals(Object obj) { + if (obj.getClass() != Image2.class) { + return false; + } + return equals((Image2) obj); + } +} diff --git a/java/src/main/java/org/msgpack/tmp/Image3.java b/java/src/main/java/org/msgpack/tmp/Image3.java new file mode 100644 index 00000000..a28ff53f --- /dev/null +++ b/java/src/main/java/org/msgpack/tmp/Image3.java @@ -0,0 +1,38 @@ +package org.msgpack.tmp; + +import java.io.IOException; +import org.msgpack.MessagePackable; +import org.msgpack.MessageTypeException; +import org.msgpack.MessageUnpackable; +import org.msgpack.Packer; +import org.msgpack.Unpacker; + +public class Image3 implements MessagePackable, MessageUnpackable { + public String uri = ""; + public String title = ""; + public int width = 0; + public int height = 0; + public int size = 0; + + public void messagePack(Packer pk) throws IOException { + // empty + } + + public void messageUnpack(Unpacker pac) throws IOException, + MessageTypeException { + // empty + } + + public boolean equals(Image3 obj) { + return uri.equals(obj.uri) && title.equals(obj.title) + && width == obj.width && height == obj.height + && size == obj.size; + } + + public boolean equals(Object obj) { + if (obj.getClass() != Image3.class) { + return false; + } + return equals((Image3) obj); + } +} \ No newline at end of file diff --git a/java/src/main/java/org/msgpack/tmp/ImagePackUnpackPerfTest.java b/java/src/main/java/org/msgpack/tmp/ImagePackUnpackPerfTest.java new file mode 100644 index 00000000..1f27c1b1 --- /dev/null +++ b/java/src/main/java/org/msgpack/tmp/ImagePackUnpackPerfTest.java @@ -0,0 +1,134 @@ +package org.msgpack.tmp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +import org.msgpack.Packer; +import org.msgpack.Unpacker; + +public class ImagePackUnpackPerfTest { + + private static int BIG_LOOP = 5; + + private static int SMALL_LOOP = 50000; + + private static DynamicCodeGenerator gen; + + private static void doGc() { + try { + Thread.sleep(50L); + } catch (InterruptedException ie) { + System.err + .println("Interrupted while sleeping in serializers.BenchmarkRunner.doGc()"); + } + System.gc(); + try { // longer sleep afterwards (not needed by GC, but may help with + // scheduling) + Thread.sleep(200L); + } catch (InterruptedException ie) { + System.err + .println("Interrupted while sleeping in serializers.BenchmarkRunner.doGc()"); + } + } + + public void doIt(int versionID) { + try { + doIt0(versionID); + } catch (Exception e) { + e.printStackTrace(); + } + try { + for (int j = 0; j < BIG_LOOP; ++j) { + long t = System.currentTimeMillis(); + doIt0(versionID); + t = System.currentTimeMillis() - t; + System.out.println("time: " + t); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void doIt0(int versionID) throws Exception { + for (int i = 0; i < SMALL_LOOP; ++i) { + switch (versionID) { + case 0: + doCurrentVersion(); + break; + case 1: + doWithReflection(); + break; + case 2: + doWithDynamicCodeGeneration(); + break; + default: + throw new RuntimeException(); + } + } + } + + public void doCurrentVersion() throws Exception { + Image1 src = new Image1(); + src.title = "msgpack"; + src.uri = "http://msgpack.org/"; + src.width = 2560; + src.height = 1600; + src.size = 4096000; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + src.messagePack(new Packer(out)); + Image1 dst = new Image1(); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + dst.messageUnpack(pac); + } + + public void doWithReflection() throws Exception { + Image2 src = new Image2(); + src.title = "msgpack"; + src.uri = "http://msgpack.org/"; + src.width = 2560; + src.height = 1600; + src.size = 4096000; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + src.messagePack(new Packer(out)); + Image2 dst = new Image2(); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + dst.messageUnpack(pac); + } + + public void doWithDynamicCodeGeneration() throws Exception { + Image3 src = (Image3) gen.newEnhancedInstance(Image3.class); + src.title = "msgpack"; + src.uri = "http://msgpack.org/"; + src.width = 2560; + src.height = 1600; + src.size = 4096000; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + src.messagePack(new Packer(out)); + Image3 dst = (Image3) gen.newEnhancedInstance(Image3.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + dst.messageUnpack(pac); + } + + public static void main(String[] args) { + ImagePackUnpackPerfTest test = new ImagePackUnpackPerfTest(); + + doGc(); + System.out.println("test current version"); + test.doIt(0); + + doGc(); + System.out.println("test with reflection"); + test.doIt(1); + + doGc(); + System.out.println("test with dynamic codegen"); + gen = new DynamicCodeGenerator(); + test.doIt(2); + } +} From ff0e1bbbc070e95be3631459815b0f765c3aab8d Mon Sep 17 00:00:00 2001 From: Muga Nishizawa <muga@f11vm.(none)> Date: Thu, 26 Aug 2010 17:53:29 +0900 Subject: [PATCH 2/2] change the part for creating a new constructor within DynamicCodeGenerator --- .../org/msgpack/tmp/DynamicCodeGenerator.java | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/java/src/main/java/org/msgpack/tmp/DynamicCodeGenerator.java b/java/src/main/java/org/msgpack/tmp/DynamicCodeGenerator.java index 9e4336d3..78238b4f 100644 --- a/java/src/main/java/org/msgpack/tmp/DynamicCodeGenerator.java +++ b/java/src/main/java/org/msgpack/tmp/DynamicCodeGenerator.java @@ -29,6 +29,8 @@ public class DynamicCodeGenerator { private static final String PACKER_CLASS_TYPE_NAME = Packer.class.getName(); + private static final String VOID_TYPE_NAME = "void"; + private static final String PACKER_OBJECT_NAME = "pk"; private static final String UNPACKER_CLASS_TYPE_NAME = Unpacker.class @@ -37,7 +39,7 @@ public class DynamicCodeGenerator { private static final String UNPACKER_OBJECT_NAME = "pk"; private HashMap<String, Class<?>> classMap; - + private HashMap<String, Object> objectCacheMap; private ClassPool pool; @@ -50,23 +52,23 @@ public class DynamicCodeGenerator { public Object newEnhancedInstance(Class<?> targetClass) throws Exception { String targetClassName = targetClass.getName(); - //Class<?> enhancedClass = classMap.get(targetClassName); + // Class<?> enhancedClass = classMap.get(targetClassName); Object enhancedObject = objectCacheMap.get(targetClassName); - //if (enhancedClass == null) { + // if (enhancedClass == null) { if (enhancedObject == null) { CtClass enhancedCtClass = createEnhancedCtClass(targetClassName); -// System.out.println("enhanced class name: " -// + enhancedCtClass.getName()); + // System.out.println("enhanced class name: " + // + enhancedCtClass.getName()); addSuperclass(enhancedCtClass, targetClassName); addConstructor(enhancedCtClass); createMessagePackMethod(enhancedCtClass, targetClass); createMessageUnpackMethod(enhancedCtClass, targetClass); Class enhancedClass = loadEnhancedClass(enhancedCtClass); - //classMap.put(targetClassName, enhancedClass); + // classMap.put(targetClassName, enhancedClass); enhancedObject = enhancedClass.newInstance(); objectCacheMap.put(targetClassName, enhancedObject); } - //return newEnhancedInstance0(enhancedClass); + // return newEnhancedInstance0(enhancedClass); return enhancedObject; } @@ -82,24 +84,18 @@ public class DynamicCodeGenerator { } private void addConstructor(CtClass enhancedCtClass) throws Exception { - StringBuilder sb = new StringBuilder(); - sb.append(MODIFIER_PUBLIC).append(SPACE).append( - // enhancedCtClass.getName()).append("(").append(")") - "Image3_$$_Enhanced").append("(").append(")").append(SPACE) - .append("{ super(); }"); // TODO -// System.out.println("cons: " + sb.toString()); - CtConstructor newCtConstructor = CtNewConstructor.make(sb.toString(), - enhancedCtClass); + CtConstructor newCtConstructor = CtNewConstructor + .defaultConstructor(enhancedCtClass); enhancedCtClass.addConstructor(newCtConstructor); } private void createMessagePackMethod(CtClass enhancedCtClass, Class<?> targetClass) throws Exception { StringBuilder sb = new StringBuilder(); - sb.append(MODIFIER_PUBLIC).append(SPACE).append("void").append(SPACE) - .append(METHOD_NAME_MESSAGEPACK).append("(").append( - PACKER_CLASS_TYPE_NAME).append(SPACE).append( - PACKER_OBJECT_NAME).append(")").append(SPACE).append( + sb.append(MODIFIER_PUBLIC).append(SPACE).append(VOID_TYPE_NAME).append( + SPACE).append(METHOD_NAME_MESSAGEPACK).append("(").append( + PACKER_CLASS_TYPE_NAME).append(SPACE) + .append(PACKER_OBJECT_NAME).append(")").append(SPACE).append( "throws").append(SPACE).append("java.io.IOException") .append(SPACE).append("{"); Field[] fields = targetClass.getFields(); @@ -109,7 +105,7 @@ public class DynamicCodeGenerator { insertCodeOfMessagePack(field, sb); } sb.append("}"); -// System.out.println("messagePack method: " + sb.toString()); + // System.out.println("messagePack method: " + sb.toString()); CtMethod newCtMethod = CtNewMethod.make(sb.toString(), enhancedCtClass); enhancedCtClass.addMethod(newCtMethod); } @@ -135,7 +131,7 @@ public class DynamicCodeGenerator { sb .append(MODIFIER_PUBLIC) .append(SPACE) - .append("void") + .append(VOID_TYPE_NAME) .append(SPACE) .append(METHOD_NAME_MESSAGEUNPACK) .append("(") @@ -149,13 +145,14 @@ public class DynamicCodeGenerator { .append("org.msgpack.MessageTypeException, java.io.IOException") .append(SPACE).append("{"); Field[] fields = targetClass.getFields(); - sb.append(UNPACKER_OBJECT_NAME).append(".").append("unpackArray()").append(";"); + sb.append(UNPACKER_OBJECT_NAME).append(".").append("unpackArray()") + .append(";"); // TODO for (Field field : fields) { insertCodeOfMessageUnpack(field, sb); } sb.append("}"); -// System.out.println("messageUnpack method: " + sb.toString()); + // System.out.println("messageUnpack method: " + sb.toString()); CtMethod newCtMethod = CtNewMethod.make(sb.toString(), enhancedCtClass); enhancedCtClass.addMethod(newCtMethod); }