Java tutorial
/** * Copyright 2008 Martin Traverso * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package mt.swift; import com.facebook.thrift.TException; import com.facebook.thrift.protocol.TProtocol; import mt.swift.model.BasicType; import mt.swift.model.Field; import mt.swift.model.ListType; import mt.swift.model.MapType; import mt.swift.model.SetType; import mt.swift.model.StructureType; import mt.swift.model.Type; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; import org.objectweb.asm.util.TraceClassVisitor; import java.io.PrintWriter; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; public class Serializer { private Map<String, StructureType> types = new ConcurrentHashMap<String, StructureType>(); private Map<String, Class<?>> classes = new ConcurrentHashMap<String, Class<?>>(); private Map<String, StructureSerializer> serializers = new HashMap<String, StructureSerializer>(); private AtomicInteger sequence = new AtomicInteger(); private boolean debug; public void setDebug(boolean debug) { this.debug = debug; } public void bind(StructureType type, Class clazz) { types.put(type.getName(), type); classes.put(type.getName(), clazz); } public void bindToMap(StructureType type) { types.put(type.getName(), type); classes.put(type.getName(), HashMap.class); } public void serialize(Object object, String name, TProtocol protocol) throws TException { StructureSerializer serializer = serializers.get(name); Class clazz = classes.get(name); if (clazz == null) { throw new IllegalStateException(String.format("Type '%s' not bound to a class", name)); } StructureType type = types.get(name); // construct deserializer if (serializer == null) { serializer = compileSerializer(type, clazz); serializers.put(name, serializer); } serializer.serialize(object, this, protocol); } private StructureSerializer compileSerializer(StructureType type, Class clazz) { ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); // TODO: compute this ourselves? // ClassWriter classWriter = new ClassWriter(0); // TODO: compute this ourselves? ClassVisitor writer = classWriter; if (debug) { // writer = new CheckClassAdapter(classWriter); // writer = new TraceClassVisitor(writer, new PrintWriter(System.out)); } String targetClassName = Util.getInternalName(clazz); String serializerClassName = "mt/swift/generated/Serializer" + clazz.getSimpleName() + "_" + sequence.incrementAndGet(); // class metadata writer.visit(V1_6, ACC_PUBLIC + ACC_SUPER, serializerClassName, null, "java/lang/Object", new String[] { Util.getInternalName(StructureSerializer.class) }); compileConstructor(writer); compileSerializeMethod(type, writer, targetClassName, clazz); writer.visitEnd(); if (debug) { ClassReader reader = new ClassReader(classWriter.toByteArray()); reader.accept(new TraceClassVisitor(new PrintWriter(System.out)), 0); } ByteArrayClassLoader loader = new ByteArrayClassLoader(); try { return (StructureSerializer) loader .defineClass(serializerClassName.replace('/', '.'), classWriter.toByteArray()).newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } private void compileSerializeMethod(StructureType type, ClassVisitor writer, String targetClassName, Class clazz) { MethodVisitor methodVisitor = writer.visitMethod(ACC_PUBLIC, "serialize", "(Ljava/lang/Object;L" + Util.getInternalName(Serializer.class) + ";Lcom/facebook/thrift/protocol/TProtocol;)V", null, new String[] { "com/facebook/thrift/TException" }); FrameRegisterManager context = new FrameRegisterManager(); context.bindSlot("this", 0); context.bindSlot("object", 1); context.bindSlot("serializer", 2); context.bindSlot("protocol", 3); methodVisitor.visitCode(); // protocol.writeStructBegin(new TStruct("name")) methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitTypeInsn(NEW, "com/facebook/thrift/protocol/TStruct"); methodVisitor.visitInsn(DUP); methodVisitor.visitLdcInsn(type.getName()); methodVisitor.visitMethodInsn(INVOKESPECIAL, "com/facebook/thrift/protocol/TStruct", "<init>", "(Ljava/lang/String;)V"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeStructBegin", "(Lcom/facebook/thrift/protocol/TStruct;)V"); // TField tfield = new TField() int fieldSlot = context.newAnonymousSlot(); methodVisitor.visitTypeInsn(NEW, "com/facebook/thrift/protocol/TField"); methodVisitor.visitInsn(DUP); methodVisitor.visitMethodInsn(INVOKESPECIAL, "com/facebook/thrift/protocol/TField", "<init>", "()V"); methodVisitor.visitVarInsn(ASTORE, fieldSlot); for (Field field : type.getFields()) { // tfield.id = ... methodVisitor.visitVarInsn(ALOAD, fieldSlot); pushValue(methodVisitor, (short) field.getId()); // TODO: field.getId() should return short methodVisitor.visitFieldInsn(PUTFIELD, "com/facebook/thrift/protocol/TField", "id", "S"); // tfield.type = ... methodVisitor.visitVarInsn(ALOAD, fieldSlot); pushValue(methodVisitor, field.getType().getTType()); methodVisitor.visitFieldInsn(PUTFIELD, "com/facebook/thrift/protocol/TField", "type", "B"); // tfield.name = ... methodVisitor.visitVarInsn(ALOAD, fieldSlot); methodVisitor.visitLdcInsn(field.getName()); methodVisitor.visitFieldInsn(PUTFIELD, "com/facebook/thrift/protocol/TField", "name", "Ljava/lang/String;"); methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitVarInsn(ALOAD, fieldSlot); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeFieldBegin", "(Lcom/facebook/thrift/protocol/TField;)V"); if (Map.class.isAssignableFrom(clazz)) { generateGetFromMap(methodVisitor, context, field); } else { generateGetField(targetClassName, methodVisitor, context, field); } // protocol.writeXXX(element) generateWriteElement(methodVisitor, context, field.getType()); methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeFieldEnd", "()V"); } methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeFieldStop", "()V"); methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeStructEnd", "()V"); context.release(fieldSlot); methodVisitor.visitInsn(RETURN); methodVisitor.visitMaxs(1, 1); // TODO: compute these methodVisitor.visitEnd(); } /** * Generates bytecode to write element at top of stack * * @param methodVisitor * @param context * @param type */ private void generateWriteElement(MethodVisitor methodVisitor, FrameRegisterManager context, Type type) { if (type == BasicType.BOOLEAN) { methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitInsn(SWAP); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeBool", "(Z)V"); } else if (type == BasicType.BYTE) { methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitInsn(SWAP); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeByte", "(B)V"); } else if (type == BasicType.I16) { methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitInsn(SWAP); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeI16", "(S)V"); } else if (type == BasicType.I32) { methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitInsn(SWAP); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeI32", "(I)V"); } else if (type == BasicType.I64) { // can't use swap for double... use a temp variable instead int slot = context.newAnonymousSlot(); methodVisitor.visitVarInsn(LSTORE, slot); methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitVarInsn(LLOAD, slot); context.release(slot); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeI64", "(J)V"); } else if (type == BasicType.DOUBLE) { // can't use swap for double... use a temp variable instead int slot = context.newAnonymousSlot(); methodVisitor.visitVarInsn(DSTORE, slot); methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitVarInsn(DLOAD, slot); context.release(slot); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeDouble", "(D)V"); } else if (type == BasicType.BINARY) { methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitInsn(SWAP); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeBinary", "([B)V"); } else if (type == BasicType.STRING) { methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitInsn(SWAP); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeString", "(Ljava/lang/String;)V"); } else if (type instanceof StructureType) { StructureType structureType = (StructureType) type; methodVisitor.visitVarInsn(ALOAD, context.getSlot("serializer")); methodVisitor.visitInsn(SWAP); // element, serializer => serializer, element methodVisitor.visitLdcInsn(structureType.getName()); methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, Util.getInternalName(Serializer.class), "serialize", "(Ljava/lang/Object;Ljava/lang/String;Lcom/facebook/thrift/protocol/TProtocol;)V"); } else if (type instanceof ListType) { ListType listType = (ListType) type; generateWriteList(methodVisitor, context, listType); } else if (type instanceof SetType) { SetType setType = (SetType) type; generateWriteSet(methodVisitor, context, setType); } else if (type instanceof MapType) { MapType mapType = (MapType) type; generateWriteMap(methodVisitor, context, mapType); } } private void generateWriteList(MethodVisitor methodVisitor, FrameRegisterManager context, ListType listType) { // top of stack is list we're serializing int listSlot = context.newAnonymousSlot(); methodVisitor.visitVarInsn(ASTORE, listSlot); // protocol.writeListBegin(new TList(ttype, object.size)) methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitTypeInsn(NEW, "com/facebook/thrift/protocol/TList"); methodVisitor.visitInsn(DUP); pushValue(methodVisitor, listType.getValueType().getTType()); methodVisitor.visitVarInsn(ALOAD, listSlot); methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "size", "()I"); methodVisitor.visitMethodInsn(INVOKESPECIAL, "com/facebook/thrift/protocol/TList", "<init>", "(BI)V"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeListBegin", "(Lcom/facebook/thrift/protocol/TList;)V"); // at this point, stack is empty // for (element : value), using a while (iterator.hasNext()) { ... } loop methodVisitor.visitVarInsn(ALOAD, listSlot); methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;"); generateWriteIteratorElements(methodVisitor, context, listType.getValueType()); // protocol.writeListEnd() methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeListEnd", "()V"); context.release(listSlot); } private void generateWriteSet(MethodVisitor methodVisitor, FrameRegisterManager context, SetType setType) { // top of stack is list we're serializing int setSlot = context.newAnonymousSlot(); methodVisitor.visitVarInsn(ASTORE, setSlot); // protocol.writeListBegin(new TList(ttype, object.size)) methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitTypeInsn(NEW, "com/facebook/thrift/protocol/TSet"); methodVisitor.visitInsn(DUP); pushValue(methodVisitor, setType.getValueType().getTType()); methodVisitor.visitVarInsn(ALOAD, setSlot); methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "size", "()I"); methodVisitor.visitMethodInsn(INVOKESPECIAL, "com/facebook/thrift/protocol/TSet", "<init>", "(BI)V"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeSetBegin", "(Lcom/facebook/thrift/protocol/TSet;)V"); // at this point, stack is empty // for (element : value), using a while (iterator.hasNext()) { ... } loop methodVisitor.visitVarInsn(ALOAD, setSlot); methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;"); generateWriteIteratorElements(methodVisitor, context, setType.getValueType()); // protocol.writeSetEnd() methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeSetEnd", "()V"); context.release(setSlot); } private void generateWriteMap(MethodVisitor methodVisitor, FrameRegisterManager context, MapType mapType) { // top of stack is list we're serializing int mapSlot = context.newAnonymousSlot(); methodVisitor.visitVarInsn(ASTORE, mapSlot); // protocol.writeListBegin(new TList(ttype, object.size)) methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitTypeInsn(NEW, "com/facebook/thrift/protocol/TMap"); methodVisitor.visitInsn(DUP); pushValue(methodVisitor, mapType.getKeyType().getTType()); pushValue(methodVisitor, mapType.getValueType().getTType()); methodVisitor.visitVarInsn(ALOAD, mapSlot); methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "size", "()I"); methodVisitor.visitMethodInsn(INVOKESPECIAL, "com/facebook/thrift/protocol/TMap", "<init>", "(BBI)V"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeMapBegin", "(Lcom/facebook/thrift/protocol/TMap;)V"); // at this point, stack is empty // for (element : value), using a while (iterator.hasNext()) { ... } loop methodVisitor.visitVarInsn(ALOAD, mapSlot); methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "entrySet", "()Ljava/util/Set;"); methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;"); // TODO: generalize generateIterator method to take a callback to generate the code for processing each // element Label loopLabel = new Label(); Label doneLabel = new Label(); methodVisitor.visitLabel(loopLabel); methodVisitor.visitInsn(DUP); // for iterator.hasNext() methodVisitor.visitInsn(DUP); // for iterator.next() // iterator.hasNext? methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z"); methodVisitor.visitJumpInsn(IFEQ, doneLabel); // if hasNext returned false (0), we're done methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;"); // element is Map.Entry methodVisitor.visitInsn(DUP); // for entry.getKey int entrySlot = context.newAnonymousSlot(); methodVisitor.visitVarInsn(ASTORE, entrySlot); // for entry.getValue methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Map$Entry", "getKey", "()Ljava/lang/Object;"); generateCast(methodVisitor, mapType.getKeyType()); generateWriteElement(methodVisitor, context, mapType.getKeyType()); methodVisitor.visitVarInsn(ALOAD, entrySlot); methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Map$Entry", "getValue", "()Ljava/lang/Object;"); generateCast(methodVisitor, mapType.getValueType()); generateWriteElement(methodVisitor, context, mapType.getValueType()); context.release(entrySlot); methodVisitor.visitJumpInsn(GOTO, loopLabel); methodVisitor.visitLabel(doneLabel); methodVisitor.visitInsn(POP); // lingering reference to iterator // protocol.writeSetEnd() methodVisitor.visitVarInsn(ALOAD, context.getSlot("protocol")); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/facebook/thrift/protocol/TProtocol", "writeMapEnd", "()V"); context.release(mapSlot); } /** * Generates code to write the elements of an iterator, one after another * * Assumes iterator is at top of stack * * @param methodVisitor * @param context * @param elementType */ private void generateWriteIteratorElements(MethodVisitor methodVisitor, FrameRegisterManager context, Type elementType) { Label loopLabel = new Label(); Label doneLabel = new Label(); methodVisitor.visitLabel(loopLabel); methodVisitor.visitInsn(DUP); // for iterator.hasNext() methodVisitor.visitInsn(DUP); // for iterator.next() // iterator.hasNext? methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z"); methodVisitor.visitJumpInsn(IFEQ, doneLabel); // if hasNext returned false (0), we're done methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;"); generateCast(methodVisitor, elementType); generateWriteElement(methodVisitor, context, elementType); methodVisitor.visitJumpInsn(GOTO, loopLabel); methodVisitor.visitLabel(doneLabel); methodVisitor.visitInsn(POP); // lingering reference to iterator methodVisitor.visitInsn(POP); // lingering reference to iterator } private void generateSystemErrPrintlnBoolean(MethodVisitor visitor) { visitor.visitInsn(DUP); visitor.visitFieldInsn(GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;"); visitor.visitInsn(SWAP); visitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Z)V"); } // Prints whatever's at the top of the stack to System.err private void generateSystemErrPrintlnConstant(MethodVisitor visitor, String value) { visitor.visitFieldInsn(GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;"); visitor.visitLdcInsn(value); visitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); } // Prints whatever's at the top of the stack to System.err private void generateSystemErrPrintln(MethodVisitor visitor) { visitor.visitInsn(DUP); visitor.visitFieldInsn(GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;"); visitor.visitInsn(SWAP); visitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V"); } /** * value for given field is read from the map and left at the top of the stack * * @param methodVisitor * @param context * @param field */ private void generateGetFromMap(MethodVisitor methodVisitor, FrameRegisterManager context, Field field) { // ((Map) object).get("field name") methodVisitor.visitVarInsn(ALOAD, context.getSlot("object")); methodVisitor.visitTypeInsn(CHECKCAST, "java/util/Map"); methodVisitor.visitLdcInsn(field.getName()); methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); generateCast(methodVisitor, field.getType()); } /** * Downcast from Object -> concrete type, depending on the type passed to the method. Unboxes boxed versions * of primitive types */ private void generateCast(MethodVisitor methodVisitor, Type type) { if (type == BasicType.BOOLEAN) { methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); } else if (type == BasicType.BYTE) { methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/Byte"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B"); } else if (type == BasicType.I16) { methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/Short"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S"); } else if (type == BasicType.I32) { methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/Integer"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); } else if (type == BasicType.I64) { methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/Long"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J"); } else if (type == BasicType.DOUBLE) { methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/Double"); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D"); } else if (type == BasicType.STRING) { methodVisitor.visitTypeInsn(CHECKCAST, "java/lang/String"); } else if (type == BasicType.BINARY) { methodVisitor.visitTypeInsn(CHECKCAST, "[B"); } // else if (type instanceof ListType) { // methodVisitor.visitTypeInsn(CHECKCAST, "java/util/List"); // } } /** * Generates code to get the corresponding field from the bean to serialize. * * @param targetClassName * @param methodVisitor * @param context * @param field */ private void generateGetField(String targetClassName, MethodVisitor methodVisitor, FrameRegisterManager context, Field field) { methodVisitor.visitVarInsn(ALOAD, context.getSlot("object")); methodVisitor.visitTypeInsn(CHECKCAST, targetClassName); String getter; if (field.getType() == BasicType.BOOLEAN) { getter = "is" + Util.toCamelCase(field.getName()); } else { getter = "get" + Util.toCamelCase(field.getName()); } if (field.getType() == BasicType.BOOLEAN) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()Z"); } else if (field.getType() == BasicType.BYTE) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()B"); } else if (field.getType() == BasicType.I16) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()S"); } else if (field.getType() == BasicType.I32) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()I"); } else if (field.getType() == BasicType.I64) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()J"); } else if (field.getType() == BasicType.DOUBLE) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()D"); } else if (field.getType() == BasicType.BINARY) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()[B"); } else if (field.getType() == BasicType.STRING) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()Ljava/lang/String;"); } else if (field.getType() instanceof StructureType) { final String name = ((StructureType) field.getType()).getName(); Class childClass = classes.get(name); if (childClass == null) { throw new IllegalStateException(String.format("Type '%s' not bound to a class", name)); } methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()L" + Util.getInternalName(childClass) + ";"); } else if (field.getType() instanceof ListType) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()L" + Util.getInternalName(List.class) + ";"); } else if (field.getType() instanceof SetType) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()L" + Util.getInternalName(Set.class) + ";"); } else if (field.getType() instanceof MapType) { methodVisitor.visitMethodInsn(INVOKEVIRTUAL, targetClassName, getter, "()L" + Util.getInternalName(java.util.Map.class) + ";"); } } private void pushValue(MethodVisitor methodVisitor, int value) { if (value <= Byte.MAX_VALUE) { pushValue(methodVisitor, (byte) value); } else { methodVisitor.visitIntInsn(SIPUSH, value); } } private void pushValue(MethodVisitor methodVisitor, byte value) { switch (value) { case -1: methodVisitor.visitInsn(ICONST_M1); break; case 0: methodVisitor.visitInsn(ICONST_0); break; case 1: methodVisitor.visitInsn(ICONST_1); break; case 2: methodVisitor.visitInsn(ICONST_2); break; case 3: methodVisitor.visitInsn(ICONST_3); break; case 4: methodVisitor.visitInsn(ICONST_4); break; case 5: methodVisitor.visitInsn(ICONST_5); break; default: methodVisitor.visitIntInsn(BIPUSH, value); } } private void compileConstructor(ClassVisitor writer) { // constructor MethodVisitor constructorVisitor = writer.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); constructorVisitor.visitCode(); constructorVisitor.visitVarInsn(ALOAD, 0); constructorVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); constructorVisitor.visitInsn(RETURN); constructorVisitor.visitMaxs(1, 1); constructorVisitor.visitEnd(); } }