org.apache.directmemory.lightning.internal.generator.BytecodeMarshallerGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.directmemory.lightning.internal.generator.BytecodeMarshallerGenerator.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.directmemory.lightning.internal.generator;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.directmemory.lightning.Marshaller;
import org.apache.directmemory.lightning.MarshallerStrategy;
import org.apache.directmemory.lightning.SerializationStrategy;
import org.apache.directmemory.lightning.exceptions.SerializerMarshallerGeneratorException;
import org.apache.directmemory.lightning.instantiator.ObjectInstantiatorFactory;
import org.apache.directmemory.lightning.internal.ClassDescriptorAwareSerializer;
import org.apache.directmemory.lightning.internal.InternalMarshallerStrategy;
import org.apache.directmemory.lightning.internal.util.ClassUtil;
import org.apache.directmemory.lightning.metadata.PropertyDescriptor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

public class BytecodeMarshallerGenerator implements Opcodes, GeneratorConstants, MarshallerGenerator {

    private final GeneratorClassLoader classloader = CreateClassLoader
            .createClassLoader(getClass().getClassLoader());

    @Override
    public Marshaller generateMarshaller(Class<?> type, List<PropertyDescriptor> propertyDescriptors,
            Map<Class<?>, Marshaller> marshallers, ClassDescriptorAwareSerializer serializer,
            SerializationStrategy serializationStrategy, ObjectInstantiatorFactory objectInstantiatorFactory,
            File debugCacheDirectory) {

        try {
            ClassWriter cw = new ClassWriter(0);

            // Copy properties and sort them by name
            List<PropertyDescriptor> propertyDescriptorsCopy = new ArrayList<PropertyDescriptor>(
                    propertyDescriptors);
            Collections.sort(propertyDescriptorsCopy);

            // Build className e.g. "SomeTypeMarshaller$$X$$Lightning"
            String className = new StringBuilder(
                    !type.isArray() ? type.getSimpleName() : type.getComponentType().getSimpleName() + "Array")
                            .append("Marshaller").append(GENEREATED_CLASS_ID.getAndIncrement()).append("Lightning")
                            .toString();

            // Build class
            cw.visit(V1_6, ACC_PUBLIC & ACC_SUPER, className, null, SUPER_CLASS_INTERNAL_TYPE, null);

            // Build marshaller fields
            createMarshallerFields(cw, propertyDescriptorsCopy);

            // Build constructor
            createConstructor(cw, className, propertyDescriptorsCopy);

            // Build Marshaller#marshall method
            createMarshallMethod(cw, className, type, serializationStrategy, propertyDescriptorsCopy);

            // Build Marshaller#unmarshall method
            createUnmarshallMethod(cw, className, type, propertyDescriptorsCopy);

            // Closing class visit
            cw.visitEnd();

            final byte[] bytecode = cw.toByteArray();

            if (debugCacheDirectory != null) {
                File file = new File(debugCacheDirectory, className + ".class");
                FileOutputStream out = new FileOutputStream(file);
                out.write(bytecode);
                out.flush();
                out.close();
            }

            Class<? extends Marshaller> generatedClass = classloader.loadClass(bytecode);
            Constructor<? extends Marshaller> constructor = generatedClass.getConstructor(Class.class, Map.class,
                    ClassDescriptorAwareSerializer.class, ObjectInstantiatorFactory.class, List.class,
                    MarshallerStrategy.class);

            constructor.setAccessible(true);
            return constructor.newInstance(type, marshallers, serializer, objectInstantiatorFactory,
                    propertyDescriptorsCopy, new InternalMarshallerStrategy());
        } catch (Exception e) {
            throw new SerializerMarshallerGeneratorException(
                    "Marshaller for type " + type + " could not be generated", e);
        }
    }

    private void createMarshallerFields(ClassWriter cw, List<PropertyDescriptor> propertyDescriptors) {
        for (int i = 0; i < propertyDescriptors.size(); i++) {
            PropertyDescriptor propertyDescriptor = propertyDescriptors.get(i);
            FieldVisitor fv = null;

            // Write PropertyDescriptor field
            fv = cw.visitField(ACC_FINAL & ACC_PRIVATE, toFinalFieldName("descriptor", propertyDescriptor),
                    PROPERTYDESCRIPTOR_CLASS_DESCRIPTOR, null, null);
            fv.visitEnd();

            if (propertyDescriptor.getType().isArray()
                    && !propertyDescriptor.getType().getComponentType().isPrimitive()) {
                // Write ComponentType PropertyDescriptor field
                fv = cw.visitField(ACC_FINAL & ACC_PRIVATE, toFinalFieldName("component", propertyDescriptor),
                        CHEATINGPROPERTYDESCRIPTOR_CLASS_DESCRIPTOR, null, null);
                fv.visitEnd();
            }

            // Write Marshaller field
            fv = cw.visitField(ACC_FINAL & ACC_PRIVATE, toFinalFieldName("marshaller", propertyDescriptor),
                    MARSHALLER_CLASS_DESCRIPTOR, null, null);
            fv.visitEnd();

            // Write PropertyAccessor field
            fv = cw.visitField(ACC_FINAL & ACC_PRIVATE, toFinalFieldName("accessor", propertyDescriptor),
                    PROPERTYACCESSOR_CLASS_DESCRIPTOR, null, null);
            fv.visitEnd();
        }
    }

    private void createConstructor(ClassWriter cw, String className, List<PropertyDescriptor> propertyDescriptors) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", MARSHALLER_CONSTRUCTOR_SIGNATURE, null, null);
        mv.visitCode();

        // Load this
        mv.visitVarInsn(ALOAD, 0);

        // Load first parameter type (Class)
        mv.visitVarInsn(ALOAD, 1);

        // Load second parameter type (Map)
        mv.visitVarInsn(ALOAD, 2);

        // Load third parameter type (ClassDescriptorAwaySerializer)
        mv.visitVarInsn(ALOAD, 3);

        // Load fourth parameter type (ObjenesisSerializer)
        mv.visitVarInsn(ALOAD, 4);

        // Call super(Class, Map)
        mv.visitMethodInsn(INVOKESPECIAL, SUPER_CLASS_INTERNAL_TYPE, "<init>",
                MARSHALLER_SUPER_CONSTRUCTOR_SIGNATURE);

        // Fill fields with marshallers
        for (int i = 0; i < propertyDescriptors.size(); i++) {
            PropertyDescriptor propertyDescriptor = propertyDescriptors.get(i);
            String fieldName = toFinalFieldName("marshaller", propertyDescriptor);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 0);

            // Load property type
            mv.visitVarInsn(ALOAD, 5);
            mv.visitIntInsn(BIPUSH, i);
            mv.visitMethodInsn(INVOKEINTERFACE, LIST_CLASS_INTERNAL_TYPE, "get", "(I)Ljava/lang/Object;");

            // Store PropertyDescriptor
            mv.visitTypeInsn(CHECKCAST, PROPERTYDESCRIPTOR_CLASS_INTERNAL_TYPE);
            mv.visitFieldInsn(PUTFIELD, className, toFinalFieldName("descriptor", propertyDescriptor),
                    PROPERTYDESCRIPTOR_CLASS_DESCRIPTOR);

            if (propertyDescriptor.getType().isArray()
                    && !propertyDescriptor.getType().getComponentType().isPrimitive()) {
                Label labelNonNull = new Label();

                // Get type from PropertyAccessor
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("descriptor", propertyDescriptor),
                        PROPERTYDESCRIPTOR_CLASS_DESCRIPTOR);
                mv.visitMethodInsn(INVOKEINTERFACE, PROPERTYDESCRIPTOR_CLASS_INTERNAL_TYPE, "getType",
                        "()Ljava/lang/Class;");

                // Get array component type
                mv.visitMethodInsn(INVOKEVIRTUAL, CLASS_CLASS_INTERNAL_TYPE, "getComponentType",
                        CLASS_GET_COMPONENT_TYPE);
                mv.visitVarInsn(ASTORE, 9);

                // Generate cheating PropertyDescriptor for component type
                mv.visitVarInsn(ALOAD, 0);
                mv.visitTypeInsn(NEW, CHEATINGPROPERTYDESCRIPTOR_CLASS_INTERNAL_TYPE);
                mv.visitInsn(DUP);
                mv.visitLdcInsn(propertyDescriptor.getPropertyName() + "Element");
                mv.visitVarInsn(ALOAD, 9);
                mv.visitInsn(ACONST_NULL);
                mv.visitMethodInsn(INVOKESPECIAL, CHEATINGPROPERTYDESCRIPTOR_CLASS_INTERNAL_TYPE, "<init>",
                        CHEATINGPROPERTYDESCRIPTOR_CONSTRUCTOR);
                mv.visitFieldInsn(PUTFIELD, className, toFinalFieldName("component", propertyDescriptor),
                        CHEATINGPROPERTYDESCRIPTOR_CLASS_DESCRIPTOR);

                // Search marshaller by using interal ones
                mv.visitVarInsn(ALOAD, 6);
                mv.visitVarInsn(ALOAD, 9);
                mv.visitInsn(ACONST_NULL);
                mv.visitMethodInsn(INVOKEINTERFACE, MARSHALLERSTRATEGY_CLASS_INTERNAL_TYPE, "getMarshaller",
                        MARSHALLERSTRATEGY_GET_MARSHALLER_SIGNATURE);
                mv.visitVarInsn(ASTORE, 8);
                mv.visitVarInsn(ALOAD, 8);

                // Search marshaller for property type
                mv.visitJumpInsn(IFNONNULL, labelNonNull);
                mv.visitVarInsn(ALOAD, 0);
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("component", propertyDescriptor),
                        CHEATINGPROPERTYDESCRIPTOR_CLASS_DESCRIPTOR);
                mv.visitMethodInsn(INVOKEVIRTUAL, SUPER_CLASS_INTERNAL_TYPE, "findMarshaller",
                        MARSHALLER_FIND_MARSHALLER_SIGNATURE);
                mv.visitVarInsn(ASTORE, 8);

                // Save marshaller to field
                mv.visitLabel(labelNonNull);
                mv.visitVarInsn(ALOAD, 0);
                mv.visitVarInsn(ALOAD, 8);
                mv.visitFieldInsn(PUTFIELD, className, toFinalFieldName("marshaller", propertyDescriptor),
                        MARSHALLER_CLASS_DESCRIPTOR);
            } else {
                // Check if marshaller is defined
                mv.visitVarInsn(ALOAD, 0);
                mv.visitVarInsn(ALOAD, 0);
                mv.visitVarInsn(ALOAD, 0);
                mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("descriptor", propertyDescriptor),
                        PROPERTYDESCRIPTOR_CLASS_DESCRIPTOR);
                mv.visitMethodInsn(INVOKEVIRTUAL, SUPER_CLASS_INTERNAL_TYPE, "findMarshaller",
                        MARSHALLER_FIND_MARSHALLER_SIGNATURE);
                mv.visitFieldInsn(PUTFIELD, className, fieldName, MARSHALLER_CLASS_DESCRIPTOR);
            }

            // Load this to method stack
            mv.visitVarInsn(ALOAD, 0);
            mv.visitInsn(DUP);

            // Load property accessor
            mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("descriptor", propertyDescriptor),
                    PROPERTYDESCRIPTOR_CLASS_DESCRIPTOR);
            mv.visitMethodInsn(INVOKEINTERFACE, PROPERTYDESCRIPTOR_CLASS_INTERNAL_TYPE, "getPropertyAccessor",
                    PROPERTY_DESCRIPTOR_GET_PROPERTYACCESSOR_SIGNATURE);

            // Save PropertyAccessor to field
            mv.visitFieldInsn(PUTFIELD, className, toFinalFieldName("accessor", propertyDescriptor),
                    PROPERTYACCESSOR_CLASS_DESCRIPTOR);
        }

        mv.visitInsn(RETURN);
        mv.visitMaxs(-1, 12);
        mv.visitEnd();
    }

    private void createMarshallMethod(ClassWriter cw, String className, Class<?> type,
            SerializationStrategy serializationStrategy, List<PropertyDescriptor> propertyDescriptors) {

        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "marshall", MARSHALLER_MARSHALL_SIGNATURE, null,
                MARSHALLER_EXCEPTIONS);

        // If element type is not reference capable or SerializationStrategy is
        // not SizeOptimized just prevent generation of code
        if (serializationStrategy == SerializationStrategy.SizeOptimized && ClassUtil.isReferenceCapable(type)) {
            // Load this to method stack
            mv.visitVarInsn(ALOAD, 0);

            // Load value to method stack
            mv.visitVarInsn(ALOAD, 1);

            // Load type to method stack
            mv.visitVarInsn(ALOAD, 2);

            // Load dataOutput to method stack
            mv.visitVarInsn(ALOAD, 3);

            // Load serializationContext to method stack
            mv.visitVarInsn(ALOAD, 4);

            // Call super.isAlreadyMarshalled(...);
            mv.visitMethodInsn(INVOKEVIRTUAL, SUPER_CLASS_INTERNAL_TYPE, "isAlreadyMarshalled",
                    MARSHALLER_IS_ALREADY_MARSHALLED_SIGNATURE);

            // Label if value is not yet marshalled
            Label notYetMarshalled = new Label();

            // Test if already marshalled
            mv.visitJumpInsn(IFEQ, notYetMarshalled);

            // If marshalled - just return
            mv.visitInsn(RETURN);

            // Visit label - if not yet marshalled - marshall it
            mv.visitLabel(notYetMarshalled);
        }

        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            if (propertyDescriptor.getType().isArray()
                    && !propertyDescriptor.getType().getComponentType().isPrimitive()) {
                visitObjectArrayPropertyAccessorRead(mv, className, propertyDescriptor);
            } else {
                visitValuePropertyAccessorRead(mv, className, propertyDescriptor);
            }
        }

        // Add Return instruction
        mv.visitInsn(RETURN);

        // End visiting
        mv.visitMaxs(9, 9);
        mv.visitEnd();
    }

    private void visitValuePropertyAccessorRead(MethodVisitor mv, String className,
            PropertyDescriptor propertyDescriptor) {
        Class<?> propertyType = propertyDescriptor.getType();

        // Load this to method stack
        mv.visitVarInsn(ALOAD, 0);

        // Load property marshaller on stack
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("marshaller", propertyDescriptor),
                MARSHALLER_CLASS_DESCRIPTOR);

        // Load this to method stack
        mv.visitVarInsn(ALOAD, 0);

        // Read PropertyAccessor from field
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("accessor", propertyDescriptor),
                PROPERTYACCESSOR_CLASS_DESCRIPTOR);

        // Load value to method stack
        mv.visitVarInsn(ALOAD, 1);

        // Load value by type on stack
        visitPropertyAccessorValueRead(propertyType, mv);

        // If type is primitive add some "autoboxing" magic
        if (propertyType.isPrimitive()) {
            visitWrapperAutoboxing(propertyType, mv);
        }

        // Load type to method stack
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("descriptor", propertyDescriptor),
                PROPERTYDESCRIPTOR_CLASS_DESCRIPTOR);

        // Load DataOutput to method stack
        mv.visitVarInsn(ALOAD, 3);

        // Load SerializationContext to method stack
        mv.visitVarInsn(ALOAD, 4);

        // Call Marshaller#marshall on properties marshaller
        mv.visitMethodInsn(INVOKEINTERFACE, MARSHALLER_CLASS_INTERNAL_TYPE, "marshall",
                MARSHALLER_MARSHALL_SIGNATURE);
    }

    private void visitObjectArrayPropertyAccessorRead(MethodVisitor mv, String className,
            PropertyDescriptor propertyDescriptor) {
        Class<?> propertyType = propertyDescriptor.getType();

        // Load this to method stack
        mv.visitVarInsn(ALOAD, 0);

        // Read PropertyAccessor from field
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("accessor", propertyDescriptor),
                PROPERTYACCESSOR_CLASS_DESCRIPTOR);
        mv.visitTypeInsn(CHECKCAST, ARRAYPROPERTYACCESSOR_CLASS_INTERNAL_TYPE);
        mv.visitVarInsn(ASTORE, 8);

        // Load property type
        mv.visitVarInsn(ALOAD, 8);
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKEINTERFACE, PROPERTYACCESSOR_CLASS_INTERNAL_TYPE, "getType",
                OBJECT_GET_CLASS_SIGNATURE);
        mv.visitVarInsn(ASTORE, 5);

        // Load value to method stack
        mv.visitVarInsn(ALOAD, 1);

        // Save array to stack position 6
        visitPropertyAccessorValueRead(propertyType, mv);
        mv.visitTypeInsn(CHECKCAST, Type.getType(propertyType).getInternalName());
        mv.visitVarInsn(ASTORE, 6);

        // Save length to stream
        mv.visitVarInsn(ALOAD, 3);
        mv.visitVarInsn(ALOAD, 6);
        mv.visitInsn(ARRAYLENGTH);
        mv.visitMethodInsn(INVOKEINTERFACE, DATAOUTPUT_CLASS_INTERNAL_TYPE, "writeInt", "(I)V");

        // Loop over every element in array
        Label forLoopEnd = new Label();
        Label forLoopStart = new Label();
        mv.visitInsn(ICONST_0);
        mv.visitVarInsn(ISTORE, 7);
        mv.visitJumpInsn(GOTO, forLoopEnd);
        mv.visitLabel(forLoopStart);

        // Load this to method stack
        mv.visitVarInsn(ALOAD, 0);

        // Load property marshaller on stack
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("marshaller", propertyDescriptor),
                MARSHALLER_CLASS_DESCRIPTOR);

        // Load PropertyAccessor to method stack
        mv.visitVarInsn(ALOAD, 8);

        // Load array to method stack
        mv.visitVarInsn(ALOAD, 6);

        // Load index to method stack
        mv.visitVarInsn(ILOAD, 7);

        // Get value from array
        mv.visitMethodInsn(INVOKEINTERFACE, ARRAYPROPERTYACCESSOR_CLASS_INTERNAL_TYPE, "readObject",
                PROPERTY_ACCESSOR_ARRAY_READ_OBJECT_SIGNATURE);
        mv.visitTypeInsn(CHECKCAST, Type.getType(propertyType.getComponentType()).getInternalName());

        // If type is primitive add some "autoboxing" magic
        if (propertyType.getComponentType().isPrimitive()) {
            visitWrapperAutoboxing(propertyType.getComponentType(), mv);
        }

        // Load type to method stack
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("component", propertyDescriptor),
                CHEATINGPROPERTYDESCRIPTOR_CLASS_DESCRIPTOR);

        // Load DataOutput to method stack
        mv.visitVarInsn(ALOAD, 3);

        // Load SerializationContext to method stack
        mv.visitVarInsn(ALOAD, 4);

        // Call Marshaller#marshall on properties marshaller
        mv.visitMethodInsn(INVOKEINTERFACE, MARSHALLER_CLASS_INTERNAL_TYPE, "marshall",
                MARSHALLER_MARSHALL_SIGNATURE);

        // Test if loop ends
        mv.visitIincInsn(7, 1);
        mv.visitLabel(forLoopEnd);
        mv.visitVarInsn(ILOAD, 7);
        mv.visitVarInsn(ALOAD, 6);
        mv.visitInsn(ARRAYLENGTH);
        mv.visitJumpInsn(IF_ICMPLT, forLoopStart);
    }

    private void createUnmarshallMethod(ClassWriter cw, String className, Class<?> type,
            List<PropertyDescriptor> propertyDescriptors) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "unmarshall", MARSHALLER_UNMARSHALL_SIGNATURE, null,
                MARSHALLER_EXCEPTIONS);

        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            if (propertyDescriptor.getType().isArray()
                    && !propertyDescriptor.getType().getComponentType().isPrimitive()) {
                visitObjectArrayPropertyAccessorWrite(mv, className, propertyDescriptor);
            } else {
                visitValuePropertyAccessorWrite(mv, className, propertyDescriptor);
            }
        }

        // Load instance to method stack
        mv.visitVarInsn(ALOAD, 1);

        // Add Return statement
        visitReturn(type, mv);

        // End visiting
        mv.visitMaxs(11, 11);
        mv.visitEnd();
    }

    private void visitValuePropertyAccessorWrite(MethodVisitor mv, String className,
            PropertyDescriptor propertyDescriptor) {
        Class<?> propertyType = propertyDescriptor.getType();

        // Load this to method stack
        mv.visitVarInsn(ALOAD, 0);

        // Read PropertyAccessor from field
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("accessor", propertyDescriptor),
                PROPERTYACCESSOR_CLASS_DESCRIPTOR);

        // Store PropertyAccessor for later use
        mv.visitVarInsn(ASTORE, 5);

        // Load this to method stack
        mv.visitVarInsn(ALOAD, 0);

        // Load property marshaller to method stack
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("marshaller", propertyDescriptor),
                MARSHALLER_CLASS_DESCRIPTOR);

        // Load this to method stack
        mv.visitVarInsn(ALOAD, 0);

        // Load PropertyDescriptor to method stack
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("descriptor", propertyDescriptor),
                PROPERTYDESCRIPTOR_CLASS_DESCRIPTOR);

        // Load DataInput to method stack
        mv.visitVarInsn(ALOAD, 3);

        // Load SerializationContext to method stack
        mv.visitVarInsn(ALOAD, 4);

        // Call Marshaller#unmarshall on properties marshaller
        mv.visitMethodInsn(INVOKEINTERFACE, MARSHALLER_CLASS_INTERNAL_TYPE, "unmarshall",
                MARSHALLER_BASE_UNMARSHALL_SIGNATURE);

        // Save value
        mv.visitVarInsn(ASTORE, 6);

        // Load PropertyAccessor to method stack
        mv.visitVarInsn(ALOAD, 5);

        // Load instance to method stack
        mv.visitVarInsn(ALOAD, 1);

        // Load value to method stack
        mv.visitVarInsn(ALOAD, 6);

        // If type is primitive add some "autoboxing" magic
        if (propertyType.isPrimitive()) {
            visitPrimitiveAutoboxing(propertyType, mv);
        }

        // Call PropertyAccessor#writeX
        visitPropertyAccessorValueWrite(propertyType, mv);
    }

    private void visitObjectArrayPropertyAccessorWrite(MethodVisitor mv, String className,
            PropertyDescriptor propertyDescriptor) {
        Class<?> propertyType = propertyDescriptor.getType();
        Class<?> componentType = propertyType.getComponentType();

        // Read size
        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEINTERFACE, DATAINPUT_CLASS_INTERNAL_TYPE, "readInt", "()I");
        mv.visitInsn(DUP);
        mv.visitVarInsn(ISTORE, 5);

        // Instantiate array
        mv.visitTypeInsn(ANEWARRAY, Type.getType(componentType).getInternalName());
        mv.visitVarInsn(ASTORE, 6);

        // Read PropertyAccessor from field
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("accessor", propertyDescriptor),
                PROPERTYACCESSOR_CLASS_DESCRIPTOR);
        mv.visitVarInsn(ASTORE, 9);

        // Get component type
        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("component", propertyDescriptor),
                CHEATINGPROPERTYDESCRIPTOR_CLASS_DESCRIPTOR);
        mv.visitVarInsn(ASTORE, 8);

        mv.visitVarInsn(ALOAD, 0);
        mv.visitFieldInsn(GETFIELD, className, toFinalFieldName("marshaller", propertyDescriptor),
                MARSHALLER_CLASS_DESCRIPTOR);
        mv.visitVarInsn(ASTORE, 10);

        // For loop
        Label forLoopStart = new Label();
        Label forLoopEnd = new Label();
        mv.visitInsn(ICONST_0);
        mv.visitVarInsn(ISTORE, 7);
        mv.visitJumpInsn(GOTO, forLoopEnd);
        mv.visitLabel(forLoopStart);

        // Write value to array
        mv.visitVarInsn(ALOAD, 9);
        mv.visitVarInsn(ALOAD, 6);
        mv.visitVarInsn(ILOAD, 7);

        // Read value from stream to 4th stack position
        mv.visitVarInsn(ALOAD, 10);
        mv.visitVarInsn(ALOAD, 8);
        mv.visitVarInsn(ALOAD, 3);
        mv.visitVarInsn(ALOAD, 4);
        mv.visitMethodInsn(INVOKEINTERFACE, MARSHALLER_CLASS_INTERNAL_TYPE, "unmarshall",
                MARSHALLER_BASE_UNMARSHALL_SIGNATURE);

        mv.visitMethodInsn(INVOKEINTERFACE, ARRAYPROPERTYACCESSOR_CLASS_INTERNAL_TYPE, "writeObject",
                PROPERTY_ACCESSOR_ARRAY_WRITE_OBJECT_SIGNATURE);

        // Increment counter
        mv.visitIincInsn(7, 1);

        // Test for loop end
        mv.visitLabel(forLoopEnd);
        mv.visitVarInsn(ILOAD, 7);
        mv.visitVarInsn(ILOAD, 5);
        mv.visitJumpInsn(IF_ICMPLT, forLoopStart);

        // Write array to object
        mv.visitVarInsn(ALOAD, 9);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitVarInsn(ALOAD, 6);
        visitPropertyAccessorValueWrite(propertyType.getComponentType(), mv);
    }

    private void visitReturn(Class<?> type, MethodVisitor mv) {
        int returnOpcode = ARETURN;

        if (type == boolean.class) {
            returnOpcode = IRETURN;
        } else if (type == byte.class) {
            returnOpcode = IRETURN;
        } else if (type == char.class) {
            returnOpcode = IRETURN;
        } else if (type == short.class) {
            returnOpcode = IRETURN;
        } else if (type == int.class) {
            returnOpcode = IRETURN;
        } else if (type == long.class) {
            returnOpcode = LRETURN;
        } else if (type == float.class) {
            returnOpcode = FRETURN;
        } else if (type == double.class) {
            returnOpcode = DRETURN;
        } else {
            returnOpcode = ARETURN;
        }

        mv.visitInsn(returnOpcode);
    }

    private void visitPropertyAccessorValueRead(Class<?> type, MethodVisitor mv) {
        String methodName = null;
        String methodSignature = null;

        if (type == boolean.class) {
            methodName = "readBoolean";
            methodSignature = PROPERTY_ACCESSOR_READ_BOOLEAN_SIGNATURE;
        } else if (type == byte.class) {
            methodName = "readByte";
            methodSignature = PROPERTY_ACCESSOR_READ_BYTE_SIGNATURE;
        } else if (type == char.class) {
            methodName = "readChar";
            methodSignature = PROPERTY_ACCESSOR_READ_CHAR_SIGNATURE;
        } else if (type == short.class) {
            methodName = "readShort";
            methodSignature = PROPERTY_ACCESSOR_READ_SHORT_SIGNATURE;
        } else if (type == int.class) {
            methodName = "readInt";
            methodSignature = PROPERTY_ACCESSOR_READ_INT_SIGNATURE;
        } else if (type == long.class) {
            methodName = "readLong";
            methodSignature = PROPERTY_ACCESSOR_READ_LONG_SIGNATURE;
        } else if (type == float.class) {
            methodName = "readFloat";
            methodSignature = PROPERTY_ACCESSOR_READ_FLOAT_SIGNATURE;
        } else if (type == double.class) {
            methodName = "readDouble";
            methodSignature = PROPERTY_ACCESSOR_READ_DOUBLE_SIGNATURE;
        } else {
            methodName = "readObject";
            methodSignature = PROPERTY_ACCESSOR_READ_OBJECT_SIGNATURE;
        }

        mv.visitMethodInsn(INVOKEINTERFACE, VALUEPROPERTYACCESSOR_CLASS_INTERNAL_TYPE, methodName, methodSignature);
    }

    private void visitPropertyAccessorValueWrite(Class<?> type, MethodVisitor mv) {
        String methodName = null;
        String methodSignature = null;

        if (type == boolean.class) {
            methodName = "writeBoolean";
            methodSignature = PROPERTY_ACCESSOR_WRITE_BOOLEAN_SIGNATURE;
        } else if (type == byte.class) {
            methodName = "writeByte";
            methodSignature = PROPERTY_ACCESSOR_WRITE_BYTE_SIGNATURE;
        } else if (type == char.class) {
            methodName = "writeChar";
            methodSignature = PROPERTY_ACCESSOR_WRITE_CHAR_SIGNATURE;
        } else if (type == short.class) {
            methodName = "writeShort";
            methodSignature = PROPERTY_ACCESSOR_WRITE_SHORT_SIGNATURE;
        } else if (type == int.class) {
            methodName = "writeInt";
            methodSignature = PROPERTY_ACCESSOR_WRITE_INT_SIGNATURE;
        } else if (type == long.class) {
            methodName = "writeLong";
            methodSignature = PROPERTY_ACCESSOR_WRITE_LONG_SIGNATURE;
        } else if (type == float.class) {
            methodName = "writeFloat";
            methodSignature = PROPERTY_ACCESSOR_WRITE_FLOAT_SIGNATURE;
        } else if (type == double.class) {
            methodName = "writeDouble";
            methodSignature = PROPERTY_ACCESSOR_WRITE_DOUBLE_SIGNATURE;
        } else {
            methodName = "writeObject";
            methodSignature = PROPERTY_ACCESSOR_WRITE_OBJECT_SIGNATURE;
        }

        mv.visitMethodInsn(INVOKEINTERFACE, VALUEPROPERTYACCESSOR_CLASS_INTERNAL_TYPE, methodName, methodSignature);
    }

    private void visitPrimitiveAutoboxing(Class<?> type, MethodVisitor mv) {
        if (type == boolean.class) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
        } else if (type == byte.class) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
        } else if (type == char.class) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
        } else if (type == short.class) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
        } else if (type == int.class) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
        } else if (type == long.class) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
        } else if (type == float.class) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
        } else if (type == double.class) {
            mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
        }
    }

    private void visitWrapperAutoboxing(Class<?> type, MethodVisitor mv) {
        if (type == boolean.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", BOOLEAN_VALUE_OF_SIGNATURE);
        } else if (type == byte.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", BYTE_VALUE_OF_SIGNATURE);
        } else if (type == char.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", CHAR_VALUE_OF_SIGNATURE);
        } else if (type == short.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", SHORT_VALUE_OF_SIGNATURE);
        } else if (type == int.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", INT_VALUE_OF_SIGNATURE);
        } else if (type == long.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", LONG_VALUE_OF_SIGNATURE);
        } else if (type == float.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", FLOAT_VALUE_OF_SIGNATURE);
        } else if (type == double.class) {
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", DOUBLE_VALUE_OF_SIGNATURE);
        }
    }

    private String toFinalFieldName(String prefix, PropertyDescriptor propertyDescriptor) {
        return new StringBuilder(prefix.toUpperCase()).append("_")
                .append(propertyDescriptor.getPropertyName().toUpperCase()).append("_LIGHTNING").toString();
    }

    protected void visitSystemOutPrintln(MethodVisitor mv, int stackPosition) {
        mv.visitVarInsn(ASTORE, stackPosition);
        mv.visitFieldInsn(GETSTATIC, Type.getType(System.class).getInternalName(), "out",
                Type.getType(PrintStream.class).getDescriptor());
        mv.visitVarInsn(ALOAD, stackPosition);
        mv.visitMethodInsn(INVOKEVIRTUAL, Type.getType(Object.class).getInternalName(), "toString",
                "()Ljava/lang/String;");
        mv.visitMethodInsn(INVOKEVIRTUAL, Type.getType(PrintStream.class).getInternalName(), "println",
                "(Ljava/lang/String;)V");
        mv.visitVarInsn(ALOAD, stackPosition);
    }
}