/**************************************************************************************
 * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
 * http://aspectwerkz.codehaus.org                                                    *
 * ---------------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the LGPL license      *
 * a copy of which has been included with this distribution in the license.txt file.  *
 **************************************************************************************/
package org.codehaus.aspectwerkz.transform.inlining;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.CodeVisitor;
import org.objectweb.asm.Constants;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.codehaus.aspectwerkz.CrossCuttingInfo;
import org.codehaus.aspectwerkz.DeploymentModel;
import org.codehaus.aspectwerkz.AdviceInfo;
import org.codehaus.aspectwerkz.transform.TransformationUtil;
import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
import org.codehaus.aspectwerkz.joinpoint.management.JoinPointManager;
import org.codehaus.aspectwerkz.joinpoint.management.AdviceIndexInfo;
import org.codehaus.aspectwerkz.joinpoint.Signature;
import org.codehaus.aspectwerkz.joinpoint.Rtti;
import org.codehaus.aspectwerkz.aspect.AspectContainer;
import org.codehaus.aspectwerkz.expression.ExpressionContext;

import java.util.List;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;

/**
 * A compiler that compiles/generates a class that represents a specific join point, a class which invokes the advices
 * and the target join point statically.
 * 
 * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur </a>
 */
public class JoinPointCompiler implements Constants, TransformationConstants {

    private static final String ARGUMENT_FIELD = "m_argument";

    private static final String INVOKE_METHOD_NAME = "invoke";

    private static final String SIGNATURE_CLASS_SIGNATURE = "Lorg/codehaus/aspectwerkz/joinpoint/Signature;";

    private static final String NEW_SIGNATURE_METHOD_SIGNATURE = "(Ljava/lang/Class;I)Lorg/codehaus/aspectwerkz/joinpoint/Signature;";

    private static final String SIGNATURE_FACTORY_CLASS = "org/codehaus/aspectwerkz/joinpoint/management/SignatureFactory";

    // static and member field names
    private static final String TARGET_CLASS_FIELD_NAME = "TARGET_CLASS";

    private static final String SYSTEM_FIELD_NAME = "SYSTEM";

    private static final String SIGNATURE_FIELD_NAME = "SIGNATURE";

    private static final String META_DATA_FIELD_NAME = "META_DATA";

    private static final String STATIC_JOIN_POINT_INSTANCE_FIELD_NAME = "STATIC_JOIN_POINT";

    private static final String STACK_FRAME_FIELD_NAME = "m_stackFrame";

    private static final String TARGET_INSTANCE_FIELD_NAME = "m_target";

    private static final String THIS_INSTANCE_FIELD_NAME = "m_this";

    private static final String AROUND_ADVICE_FIELD_PREFIX = "s_around";

    private static final String BEFORE_ADVICE_FIELD_PREFIX = "s_before";

    private static final String AFTER_ADVICE_FIELD_PREFIX = "s_after";

    // runtime system signatures and types
    private static final String AROUND_ADVICE_METHOD_SIGNATURE = "(Lorg/codehaus/aspectwerkz/joinpoint/JoinPoint;)Ljava/lang/Object;";

    private static final String BEFORE_ADVICE_METHOD_SIGNATURE = "(Lorg/codehaus/aspectwerkz/joinpoint/JoinPoint;)V";

    private static final String AFTER_ADVICE_METHOD_SIGNATURE = "(Lorg/codehaus/aspectwerkz/joinpoint/JoinPoint;)V";

    private static final String SYSTEM_CLASS_SIGNATURE = "Lorg/codehaus/aspectwerkz/AspectSystem;";

    private static final String SYSTEM_CLASS_NAME = "org/codehaus/aspectwerkz/AspectSystem";

    private static final String ASPECT_MANAGER_CLASS_NAME = "org/codehaus/aspectwerkz/aspect/management/AspectManager";

    private static final String ASPECT_CONTAINER_CLASS_NAME = "org/codehaus/aspectwerkz/aspect/AspectContainer";

    private static final String GET_ASPECT_MANAGER_METHOD_NAME = "getAspectManager";

    private static final String GET_ASPECT_MANAGER_METHOD_SIGNATURE = "(Ljava/lang/String;)Lorg/codehaus/aspectwerkz/aspect/management/AspectManager;";

    private static final String GET_ASPECT_CONTAINER_METHOD_NAME = "getAspectContainer";

    private static final String GET_ASPECT_CONTAINER_METHOD_SIGNATURE = "(I)Lorg/codehaus/aspectwerkz/aspect/AspectContainer;";

    private static final String GET_PER_JVM_ASPECT_METHOD_NAME = "createPerJvmAspect";

    private static final String GET_PER_JVM_ASPECT_METHOD_SIGNATURE = "()Ljava/lang/Object;";

    private static final String GET_PER_CLASS_ASPECT_METHOD_NAME = "createPerClassAspect";

    private static final String GET_PER_CLASS_ASPECT_METHOD_SIGNATURE = "(Ljava/lang/Class;)Ljava/lang/Object;";

    private static final String SYSTEM_LOADER_CLASS_NAME = "org/codehaus/aspectwerkz/SystemLoader";

    private static final String GET_SYSTEM_METHOD_NAME = "getSystem";

    private static final String GET_SYSTEM_METHOD_NAME_SIGNATURE = "(Ljava/lang/Class;)Lorg/codehaus/aspectwerkz/AspectSystem;";

    private static final String GET_SIGNATURE_METHOD_NAME = "getSignature";

    private static final String GET_SIGNATURE_METHOD_SIGNATURE = "()Lorg/codehaus/aspectwerkz/joinpoint/Signature;";

    private static final String GET_RTTI_METHOD_NAME = "getRtti";

    private static final String GET_RTTI_METHOD_SIGNATURE = "()Lorg/codehaus/aspectwerkz/joinpoint/Rtti;";

    private static final String PROCEED_METHOD_NAME = "proceed";

    private static final String PROCEED_METHOD_SIGNATURE = "()Ljava/lang/Object;";

    private static final String GET_PARAMETER_VALUES_METHOD_NAME = "getParameterValues";

    private static final String GET_PARAMETER_VALUES_METHOD_SIGNATURE = "()[Ljava/lang/Object;";

    private static final String INVOKE_TARGET_METHOD_EXECUTION_METHOD_NAME = "invokeTargetMethodExecution";

    private static final String INVOKE_TARGET_METHOD_CALL_METHOD_NAME = "invokeTargetMethodCall";

    private static final String INVOKE_TARGET_METHOD_EXECUTION_METHOD_SIGNATURE = "(Lorg/codehaus/aspectwerkz/joinpoint/JoinPoint;)Ljava/lang/Object;";

    private static final String INVOKE_TARGET_METHOD_CALL_METHOD_SIGNATURE = "(Lorg/codehaus/aspectwerkz/joinpoint/JoinPoint;)Ljava/lang/Object;";

    private static final String INVOKE_TARGET_CONSTRUCTOR_EXECUTION_METHOD_NAME = "invokeTargetConstructorExecution";

    private static final String INVOKE_TARGET_CONSTRUCTOR_EXECUTION_METHOD_SIGNATURE = "(Lorg/codehaus/aspectwerkz/joinpoint/JoinPoint;)Ljava/lang/Object;";

    private static final String INVOKE_TARGET_CONSTRUCTOR_CALL_METHOD_NAME = "invokeTargetConstructorCall";

    private static final String INVOKE_TARGET_CONSTRUCTOR_CALL_METHOD_SIGNATURE = "(Lorg/codehaus/aspectwerkz/joinpoint/JoinPoint;)Ljava/lang/Object;";

    private static final String GET_TARGET_FIELD_METHOD_NAME = "getTargetField";

    private static final String GET_TARGET_FIELD_METHOD_SIGNATURE = "(Lorg/codehaus/aspectwerkz/joinpoint/JoinPoint;)Ljava/lang/Object;";

    private static final String SET_TARGET_FIELD_METHOD_NAME = "setTargetField";

    private static final String SET_TARGET_FIELD_METHOD_SIGNATURE = "(Lorg/codehaus/aspectwerkz/joinpoint/JoinPoint;)V";

    private static final String METHOD_SIGNATURE_IMPL_CLASS_NAME = "org/codehaus/aspectwerkz/joinpoint/impl/MethodSignatureImpl";

    private static final String CONSTRUCTOR_SIGNATURE_IMPL_CLASS_NAME = "org/codehaus/aspectwerkz/joinpoint/impl/ConstructorSignatureImpl";

    private static final String FIELD_SIGNATURE_IMPL_CLASS_NAME = "org/codehaus/aspectwerkz/joinpoint/impl/FieldSignatureImpl";

    private static final String METHOD_SIGNATURE_IMPL_CLASS_SIGNATURE = "Lorg/codehaus/aspectwerkz/joinpoint/impl/MethodSignatureImpl;";

    private static final String CONSTRUCTOR_SIGNATURE_IMPL_CLASS_SIGNATURE = "Lorg/codehaus/aspectwerkz/joinpoint/impl/ConstructorSignatureImpl;";

    private static final String FIELD_SIGNATURE_IMPL_CLASS_SIGNATURE = "Lorg/codehaus/aspectwerkz/joinpoint/impl/FieldSignatureImpl;";

    private static final String METHOD_RTTI_IMPL_CLASS_NAME = "org/codehaus/aspectwerkz/joinpoint/impl/MethodRttiImpl";

    private static final String CONSTRUCTOR_RTTI_IMPL_CLASS_NAME = "org/codehaus/aspectwerkz/joinpoint/impl/ConstructorRttiImpl";

    private static final String FIELD_RTTI_IMPL_CLASS_NAME = "org/codehaus/aspectwerkz/joinpoint/impl/FieldRttiImpl";

    private static final String METHOD_RTTI_IMPL_CLASS_SIGNATURE = "Lorg/codehaus/aspectwerkz/joinpoint/impl/MethodRttiImpl;";

    private static final String CONSTRUCTOR_RTTI_IMPL_CLASS_SIGNATURE = "Lorg/codehaus/aspectwerkz/joinpoint/impl/ConstructorRttiImpl;";

    private static final String FIELD_RTTI_IMPL_CLASS_SIGNATURE = "Lorg/codehaus/aspectwerkz/joinpoint/impl/FieldRttiImpl;";

    private static final String SET_RETURN_VALUE_METHOD_NAME = "setReturnValue";

    private static final String SET_RETURN_VALUE_METHOD_SIGNATURE = "(Ljava/lang/Object;)V";

    private static final String SET_NEW_INSTANCE_METHOD_NAME = "setNewInstance";

    private static final String SET_NEW_INSTANCE_METHOD_SIGNATURE = "(Ljava/lang/Object;)V";

    private static final String SET_FIELD_VALUE_METHOD_NAME = "setFieldValue";

    private static final String SET_FIELD_VALUE_METHOD_SIGNATURE = "(Ljava/lang/Object;)V";

    private static final String IS_IN_CFLOW_METOD_NAME = "isInCflow";

    private static final String IS_IN_CFLOW_METOD_SIGNATURE = "()Z";

    private static final String JOIN_POINT_CLASS_NAME = "org/codehaus/aspectwerkz/joinpoint/JoinPoint";

    // FIXME define using VM option
    private static final boolean DUMP_CLASSES = true;

    private static final String DUMP_DIR = "_dump/jp";

    /**
     * Compiles the base class for the join points in a specific class.
     * 
     * @param targetClassName
     * @param loader
     * @return the compiled class
     */
    public static Class compileJoinPointBase(final String targetClassName, final ClassLoader loader) {

        // internal compiler class name format is ALWAYS using '/'
        String baseClassName = targetClassName.replace('.', '/') + JOIN_POINT_BASE_CLASS_SUFFIX;

        ClassWriter cw = new ClassWriter(true);
        CodeVisitor cv;
        try {
            // define the class
            cw.visit(ACC_PUBLIC + ACC_SUPER, baseClassName, OBJECT_CLASS, new String[] {
                JOIN_POINT_CLASS_NAME
            }, null);

            // define the fields
            cw.visitField(
                ACC_PROTECTED + ACC_FINAL + ACC_STATIC,
                TARGET_CLASS_FIELD_NAME,
                CLASS_CLASS_SIGNATURE,
                null,
                null);
            cw
                    .visitField(
                        ACC_PROTECTED + ACC_FINAL + ACC_STATIC,
                        SYSTEM_FIELD_NAME,
                        SYSTEM_CLASS_SIGNATURE,
                        null,
                        null);

            // <clinit>
            {
                cv = cw.visitMethod(ACC_STATIC, CLINIT_METHOD_NAME, "()V", null, null);
                Label l0 = new Label();
                cv.visitLabel(l0);
                cv.visitLdcInsn(targetClassName.replace('/', '.'));
                cv.visitMethodInsn(INVOKESTATIC, CLASS_CLASS, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
                cv.visitFieldInsn(PUTSTATIC, baseClassName, TARGET_CLASS_FIELD_NAME, CLASS_CLASS_SIGNATURE);
                Label l1 = new Label();
                cv.visitLabel(l1);
                Label l2 = new Label();
                cv.visitJumpInsn(GOTO, l2);
                Label l3 = new Label();
                cv.visitLabel(l3);
                cv.visitVarInsn(ASTORE, 0);
                cv.visitTypeInsn(NEW, "java/lang/RuntimeException");
                cv.visitInsn(DUP);
                cv.visitLdcInsn("could not load target class using Class.forName() in generated join point base class");
                cv.visitMethodInsn(
                    INVOKESPECIAL,
                    "java/lang/RuntimeException",
                    INIT_METHOD_NAME,
                    "(Ljava/lang/String;)V");
                cv.visitInsn(ATHROW);
                cv.visitLabel(l2);
                cv.visitFieldInsn(GETSTATIC, baseClassName, TARGET_CLASS_FIELD_NAME, CLASS_CLASS_SIGNATURE);
                cv.visitMethodInsn(
                    INVOKESTATIC,
                    SYSTEM_LOADER_CLASS_NAME,
                    GET_SYSTEM_METHOD_NAME,
                    GET_SYSTEM_METHOD_NAME_SIGNATURE);
                cv.visitFieldInsn(PUTSTATIC, baseClassName, SYSTEM_FIELD_NAME, SYSTEM_CLASS_SIGNATURE);
                cv.visitInsn(RETURN);
                cv.visitTryCatchBlock(l0, l1, l3, "java/lang/ClassNotFoundException");
                cv.visitMaxs(0, 0);
            }

            // <init>
            {
                cv = cw.visitMethod(ACC_PUBLIC, INIT_METHOD_NAME, "()V", null, null);
                cv.visitVarInsn(ALOAD, 0);
                cv.visitMethodInsn(INVOKESPECIAL, OBJECT_CLASS, INIT_METHOD_NAME, "()V");
                cv.visitInsn(RETURN);
                cv.visitMaxs(0, 0);
            }

            // getTargetClass
            {
                cv = cw.visitMethod(ACC_PUBLIC, "getTargetClass", "()Ljava/lang/Class;", null, null);
                cv.visitFieldInsn(GETSTATIC, baseClassName, TARGET_CLASS_FIELD_NAME, "Ljava/lang/Class;");
                cv.visitInsn(ARETURN);
                cv.visitMaxs(0, 0);
            }

            // getRtti
            {
                // FIXME missing implementation
                // FIXME should perhaps be in the subclass
                cv = cw.visitMethod(ACC_PUBLIC, "getRtti", "()Lorg/codehaus/aspectwerkz/joinpoint/Rtti;", null, null);
                cv.visitInsn(ACONST_NULL);
                cv.visitInsn(ARETURN);
                cv.visitMaxs(0, 0);
            }

            // getType
            {
                // FIXME missing implementation
                // FIXME should perhaps be in the subclass
                cv = cw.visitMethod(ACC_PUBLIC, "getType", "()Ljava/lang/String;", null, null);
                cv.visitInsn(ACONST_NULL);
                cv.visitInsn(ARETURN);
                cv.visitMaxs(0, 0);
            }

            // reset
            {
                // FIXME missing implementation
                // FIXME should perhaps be in the subclass
                cv = cw.visitMethod(ACC_PUBLIC, "reset", "()V", null, null);
                cv.visitInsn(RETURN);
                cv.visitMaxs(0, 0);
            }

            // setTarget
            {
                // FIXME REMOVE from both this class and the JoinPoint interface
                cv = cw.visitMethod(ACC_PUBLIC, "setTarget", "(Ljava/lang/Object;)V", null, null);
                cv.visitInsn(RETURN);
                cv.visitMaxs(0, 0);
            }

            cw.visitEnd();

            if (DUMP_CLASSES) {
                AsmHelper.dumpClass(DUMP_DIR, baseClassName, cw);
            }

        } catch (Throwable e) {
            e.printStackTrace();
            StringBuffer buf = new StringBuffer();
            buf.append("could not compile base join point class for for target class [");
            buf.append(targetClassName);
            buf.append("] due to: ");
            if (e instanceof InvocationTargetException) {
                buf.append(((InvocationTargetException) e).getTargetException().toString());
            } else {
                buf.append(e.toString());
            }
            throw new RuntimeException(buf.toString());
        }

        // load and return the generated class
        return AsmHelper.loadClass(loader, cw.toByteArray(), baseClassName);
    }

    /**
     * Compiles a join point class, one specific class for each distinct join point. The compiled join point class
     * inherits the base join point class.
     * 
     * @param joinPointType
     * @param joinPointHash
     * @param joinPointName
     * @param signature
     * @param modifiers
     * @param declaringClassName
     * @param advices
     * @param loader
     * @param joinPointSequence
     * @return the generated, compiled and loaded join point class
     */
    public static Class loadJoinPoint(
        final int joinPointType,
        final int joinPointHash,
        final String joinPointName,
        final String signature,
        final int modifiers,
        String declaringClassName,
        final AdviceIndexInfo[] advices,
        final ClassLoader loader,
        final int joinPointSequence) {

        String joinPointClassName = getJoinPointClassName(declaringClassName, joinPointType, joinPointHash);
        byte[] joinPointBytes = compileJoinPoint(
            joinPointClassName,
            joinPointType,
            joinPointHash,
            joinPointName,
            signature,
            modifiers,
            declaringClassName,
            advices,
            loader,
            joinPointSequence);
        // load and return the generated class
        return AsmHelper.loadClass(loader, joinPointBytes, joinPointClassName);
    }

    /**
     * Compiles a join point class, one specific class for each distinct join point. The compiled join point class
     * inherits the base join point class.
     * 
     * @param joinPointClassName
     * @param joinPointType
     * @param joinPointHash
     * @param joinPointName
     * @param signature
     * @param modifiers
     * @param declaringClassName
     * @param advices
     * @param loader
     * @param joinPointSequence
     * @return the generated, compiled and loaded join point class
     */
    public static byte[] compileJoinPoint(
        final String joinPointClassName,
        final int joinPointType,
        final int joinPointHash,
        final String joinPointName,
        final String signature,
        final int modifiers,
        String declaringClassName,
        final AdviceIndexInfo[] advices,
        final ClassLoader loader,
        final int joinPointSequence) {

        try {
            // NOTE: internal compiler class name format is ALWAYS using '/'
            declaringClassName = declaringClassName.replace('.', '/');

            // grab the specific advice
            AdviceInfo[] aroundAdvices = JoinPointManager.extractAroundAdvice(advices);
            AdviceInfo[] beforeAdvices = JoinPointManager.extractBeforeAdvice(advices);
            AdviceInfo[] afterAdvices = JoinPointManager.extractAfterAdvice(advices);
            String declaringClassSignature = L + declaringClassName + SEMICOLON;
            String baseJoinPointClassName = declaringClassName + JOIN_POINT_BASE_CLASS_SUFFIX;

            // start the class compilation
            ClassWriter cw = new ClassWriter(true);

            // define the class
            cw.visit(ACC_PUBLIC + ACC_SUPER, joinPointClassName, baseJoinPointClassName, null, null);

            createFieldsCommonToAllJoinPoints(cw, declaringClassSignature, joinPointClassName);

            String[] fieldNames = createJoinPointSpecificFields(cw, joinPointType, signature);

            createStaticInitializer(
                cw,
                joinPointClassName,
                joinPointType,
                joinPointHash,
                aroundAdvices,
                beforeAdvices,
                afterAdvices);

            createConstructor(cw, baseJoinPointClassName, joinPointClassName);

            createUtilityMethods(cw, joinPointClassName, declaringClassSignature);

            createInvokeMethod(
                cw,
                signature,
                joinPointName,
                joinPointClassName,
                joinPointType,
                joinPointHash,
                declaringClassSignature,
                fieldNames,
                joinPointSequence,
                modifiers,
                declaringClassName,
                declaringClassSignature,
                aroundAdvices,
                beforeAdvices,
                afterAdvices);

            if (aroundAdvices.length != 0) {
                createProceedMethod(
                    cw,
                    signature,
                    joinPointName,
                    joinPointClassName,
                    joinPointType,
                    joinPointHash,
                    declaringClassSignature,
                    fieldNames,
                    joinPointSequence,
                    modifiers,
                    declaringClassName,
                    declaringClassSignature,
                    aroundAdvices,
                    beforeAdvices,
                    afterAdvices,
                    null);
            }

            cw.visitEnd();

            if (DUMP_CLASSES) {
                AsmHelper.dumpClass(DUMP_DIR, joinPointClassName, cw);
            }
            return cw.toByteArray();

        } catch (Exception e) {
            e.printStackTrace();
            StringBuffer buf = new StringBuffer();
            buf.append("could not compile join point instance for join point with hash [");
            buf.append(joinPointHash);
            buf.append("] and declaring class [");
            buf.append(declaringClassName);
            buf.append("] due to: ");
            if (e instanceof InvocationTargetException) {
                buf.append(((InvocationTargetException) e).getTargetException().toString());
            } else {
                buf.append(e.toString());
            }
            throw new RuntimeException(buf.toString());
        }
    }

    /**
     * Computes the joinpoint classname : "target/class_type_hash_suffix"
     * 
     * @param declaringClassName
     * @param joinPointType
     * @param joinPointHash
     * @return the JIT joinpoint classname
     */
    public static String getJoinPointClassName(
        final String declaringClassName,
        final int joinPointType,
        final int joinPointHash) {

        StringBuffer classNameBuf = new StringBuffer(declaringClassName);
        classNameBuf.append('_');
        classNameBuf.append(joinPointType);
        classNameBuf.append('_');
        classNameBuf.append(joinPointHash);
        classNameBuf.append(JOIN_POINT_CLASS_SUFFIX);

        //replace minus signs on joinPointHash
        String joinPointClassName = classNameBuf.toString().replace('-', '_');
        return joinPointClassName.replace('.', '/');
    }

    /**
     * Creates fields common for all join point classes.
     * 
     * @param cw
     * @param declaringClassSignature
     */
    private static void createFieldsCommonToAllJoinPoints(
        final ClassWriter cw,
        final String declaringClassSignature,
        final String joinPointClassName) {
        cw
                .visitField(
                    ACC_PRIVATE + ACC_FINAL + ACC_STATIC,
                    SIGNATURE_FIELD_NAME,
                    SIGNATURE_CLASS_SIGNATURE,
                    null,
                    null);
        cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, META_DATA_FIELD_NAME, "Ljava/util/Map;", null, null);
        cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, STATIC_JOIN_POINT_INSTANCE_FIELD_NAME, L
            + joinPointClassName
            + SEMICOLON, null, null);
        cw.visitField(ACC_PRIVATE, TARGET_INSTANCE_FIELD_NAME, declaringClassSignature, null, null);
        cw.visitField(ACC_PRIVATE, THIS_INSTANCE_FIELD_NAME, declaringClassSignature, null, null);
        cw.visitField(ACC_PRIVATE, JoinPointCompiler.STACK_FRAME_FIELD_NAME, I, null, null);
    }

    /**
     * Creates join point specific fields.
     * 
     * @param cw
     * @param joinPointType
     * @param signature the join point signature
     * @return an array with the names of the fields
     */
    private static String[] createJoinPointSpecificFields(
        final ClassWriter cw,
        final int joinPointType,
        final String signature) {

        String[] fieldNames = null;
        switch (joinPointType) {
            case JoinPointType.METHOD_EXECUTION:
            case JoinPointType.METHOD_CALL:
            case JoinPointType.CONSTRUCTOR_CALL:
            case JoinPointType.CONSTRUCTOR_EXECUTION:
                // create the method argument fields
                Type[] argumentTypes = Type.getArgumentTypes(signature);
                fieldNames = new String[argumentTypes.length];
                for (int i = 0; i < argumentTypes.length; i++) {
                    Type argumentType = argumentTypes[i];
                    String fieldName = ARGUMENT_FIELD + i;
                    fieldNames[i] = fieldName;
                    cw.visitField(ACC_PRIVATE + ACC_FINAL, fieldName, argumentType.getDescriptor(), null, null);
                }
                break;
            case JoinPointType.FIELD_SET:
            case JoinPointType.FIELD_GET:
                break;
            case JoinPointType.HANDLER:
                break;
            case JoinPointType.STATIC_INITALIZATION:
                break;
        }
        return fieldNames;

    }

    /**
     * Creates the static initializer for the join point.
     * 
     * @param cw
     * @param joinPointClassName
     * @param joinPointType
     * @param joinPointHash
     * @param aroundAdvices
     * @param beforeAdvices
     * @param afterAdvices
     */
    private static void createStaticInitializer(
        final ClassWriter cw,
        final String joinPointClassName,
        final int joinPointType,
        final int joinPointHash,
        final AdviceInfo[] aroundAdvices,
        final AdviceInfo[] beforeAdvices,
        final AdviceInfo[] afterAdvices) {

        CodeVisitor cv;
        cv = cw.visitMethod(ACC_STATIC, CLINIT_METHOD_NAME, "()V", null, null);

        // create the metadata map
        cv.visitTypeInsn(NEW, "java/util/HashMap");
        cv.visitInsn(DUP);
        cv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", INIT_METHOD_NAME, "()V");
        cv.visitFieldInsn(PUTSTATIC, joinPointClassName, META_DATA_FIELD_NAME, "Ljava/util/Map;");

        // create the Signature instance
        createSignature(joinPointType, cv, joinPointClassName, joinPointHash);

        // create the static JoinPoint instance
        cv.visitTypeInsn(NEW, joinPointClassName);
        cv.visitInsn(DUP);
        cv.visitMethodInsn(INVOKESPECIAL, joinPointClassName, INIT_METHOD_NAME, "()V");
        cv.visitFieldInsn(PUTSTATIC, joinPointClassName, STATIC_JOIN_POINT_INSTANCE_FIELD_NAME, L
            + joinPointClassName
            + SEMICOLON);

        // retrieve the aspect instances
        for (int i = 0; i < aroundAdvices.length; i++) {
            initAspectField(aroundAdvices[i], cw, AROUND_ADVICE_FIELD_PREFIX + i, cv, joinPointClassName);
        }
        for (int i = 0; i < beforeAdvices.length; i++) {
            initAspectField(beforeAdvices[i], cw, BEFORE_ADVICE_FIELD_PREFIX + i, cv, joinPointClassName);
        }
        for (int i = 0; i < afterAdvices.length; i++) {
            initAspectField(afterAdvices[i], cw, AFTER_ADVICE_FIELD_PREFIX + i, cv, joinPointClassName);
        }
        cv.visitInsn(RETURN);
        cv.visitMaxs(0, 0);
    }

    /**
     * Creates the constructor for the join point.
     * 
     * @param cw
     * @param baseClassName
     * @param className
     */
    private static void createConstructor(final ClassWriter cw, final String baseClassName, final String className) {
        CodeVisitor cv = cw.visitMethod(ACC_PRIVATE, INIT_METHOD_NAME, "()V", null, null);
        cv.visitVarInsn(ALOAD, 0);
        cv.visitMethodInsn(INVOKESPECIAL, baseClassName, INIT_METHOD_NAME, "()V");
        cv.visitVarInsn(ALOAD, 0);
        cv.visitInsn(ICONST_M1);
        cv.visitFieldInsn(PUTFIELD, className, STACK_FRAME_FIELD_NAME, I);
        cv.visitInsn(RETURN);
        cv.visitMaxs(0, 0);
    }

    /**
     * Creates the signature for the join point.
     * 
     * @param joinPointType
     * @param cv
     * @param className
     * @param joinPointHash
     */
    private static void createSignature(
        final int joinPointType,
        final CodeVisitor cv,
        final String className,
        final int joinPointHash) {
        cv.visitFieldInsn(GETSTATIC, className, TARGET_CLASS_FIELD_NAME, CLASS_CLASS_SIGNATURE);
        cv.visitLdcInsn(new Integer(joinPointHash));
        switch (joinPointType) {
            case JoinPointType.METHOD_EXECUTION:
            case JoinPointType.METHOD_CALL:
                cv.visitMethodInsn(
                    INVOKESTATIC,
                    SIGNATURE_FACTORY_CLASS,
                    "newMethodSignature",
                    NEW_SIGNATURE_METHOD_SIGNATURE);
                break;
            case JoinPointType.CONSTRUCTOR_CALL:
            case JoinPointType.CONSTRUCTOR_EXECUTION:
                cv.visitMethodInsn(
                    INVOKESTATIC,
                    SIGNATURE_FACTORY_CLASS,
                    "newConstructorSignature",
                    NEW_SIGNATURE_METHOD_SIGNATURE);
                break;
            case JoinPointType.FIELD_SET:
            case JoinPointType.FIELD_GET:
                cv.visitMethodInsn(
                    INVOKESTATIC,
                    SIGNATURE_FACTORY_CLASS,
                    "newFieldSignature",
                    NEW_SIGNATURE_METHOD_SIGNATURE);
                break;
            case JoinPointType.HANDLER:
                cv.visitMethodInsn(
                    INVOKESTATIC,
                    SIGNATURE_FACTORY_CLASS,
                    "newCatchClauseSignature",
                    NEW_SIGNATURE_METHOD_SIGNATURE);
                break;
            case JoinPointType.STATIC_INITALIZATION:
                break;
        }
        cv.visitFieldInsn(PUTSTATIC, className, SIGNATURE_FIELD_NAME, SIGNATURE_CLASS_SIGNATURE);
    }

    /**
     * Create and initialize the aspect field for a specific advice.
     * 
     * @param adviceTuple
     * @param cw
     * @param aspectFieldName
     * @param cv
     * @param joinPointClassName
     */
    private static boolean initAspectField(
        final AdviceInfo adviceTuple,
        final ClassWriter cw,
        final String aspectFieldName,
        final CodeVisitor cv,
        final String joinPointClassName) {

        final CrossCuttingInfo info = adviceTuple.getAspectManager().getAspectContainer(adviceTuple.getAspectIndex())
                .getCrossCuttingInfo();
        final String aspectClassName = info.getAspectClass().getName().replace('.', '/');
        final String aspectClassSignature = L + aspectClassName + SEMICOLON;

        // add the aspect field
        cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, aspectFieldName, aspectClassSignature, null, null);

        // retrieve the aspect set it to the field
        cv.visitFieldInsn(GETSTATIC, joinPointClassName, SYSTEM_FIELD_NAME, SYSTEM_CLASS_SIGNATURE);
        cv.visitLdcInsn(info.getUuid());
        cv.visitMethodInsn(
            INVOKEVIRTUAL,
            SYSTEM_CLASS_NAME,
            GET_ASPECT_MANAGER_METHOD_NAME,
            GET_ASPECT_MANAGER_METHOD_SIGNATURE);
        cv.visitIntInsn(BIPUSH, adviceTuple.getAspectIndex());
        cv.visitMethodInsn(
            INVOKEVIRTUAL,
            ASPECT_MANAGER_CLASS_NAME,
            GET_ASPECT_CONTAINER_METHOD_NAME,
            GET_ASPECT_CONTAINER_METHOD_SIGNATURE);

        switch (info.getDeploymentModel()) {
            case DeploymentModel.PER_JVM:
                cv.visitMethodInsn(
                    INVOKEINTERFACE,
                    ASPECT_CONTAINER_CLASS_NAME,
                    GET_PER_JVM_ASPECT_METHOD_NAME,
                    GET_PER_JVM_ASPECT_METHOD_SIGNATURE);
                break;
            case DeploymentModel.PER_CLASS:
                cv.visitMethodInsn(
                    INVOKEINTERFACE,
                    ASPECT_CONTAINER_CLASS_NAME,
                    GET_PER_CLASS_ASPECT_METHOD_NAME,
                    GET_PER_CLASS_ASPECT_METHOD_SIGNATURE);
                break;
            default:
                return true;
        }

        cv.visitTypeInsn(CHECKCAST, aspectClassName);
        cv.visitFieldInsn(PUTSTATIC, joinPointClassName, aspectFieldName, aspectClassSignature);

        return false;
    }

    /**
     * Creates the static 'invoke' method.
     * 
     * @param cw
     * @param signature
     * @param joinPointName
     * @param joinPointClassName
     * @param joinPointType
     * @param joinPointHash
     * @param targetClassSignature
     * @param fieldNames
     * @param joinPointSequence
     * @param modifiers
     * @param declaringClassName
     * @param declaringClassSignature
     * @param aroundAdvice
     * @param beforeAdvice
     * @param afterAdvice
     */
    private static void createInvokeMethod(
        final ClassWriter cw,
        final String signature,
        final String joinPointName,
        final String joinPointClassName,
        final int joinPointType,
        final int joinPointHash,
        final String targetClassSignature,
        final String[] fieldNames,
        final int joinPointSequence,
        final int modifiers,
        final String declaringClassName,
        final String declaringClassSignature,
        final AdviceInfo[] aroundAdvice,
        final AdviceInfo[] beforeAdvice,
        final AdviceInfo[] afterAdvice) {

        // create the 'invoke' method signature
        Type[] argumentTypes = Type.getArgumentTypes(signature);
        StringBuffer invokeDesc = new StringBuffer();
        invokeDesc.append('(');
        for (int i = 0; i < argumentTypes.length; i++) {
            Type type = argumentTypes[i];
            invokeDesc.append(type.getDescriptor());
        }
        invokeDesc.append(targetClassSignature);
        invokeDesc.append(')');
        Type returnType = Type.getReturnType(signature);
        invokeDesc.append(returnType.getDescriptor());

        // create the method
        CodeVisitor cv = cw.visitMethod(
            ACC_PUBLIC + ACC_FINAL + ACC_STATIC,
            INVOKE_METHOD_NAME,
            invokeDesc.toString(),
            new String[] {
                THROWABLE_CLASS_NAME
            },
            null);

        final boolean hasAroundAdvice = aroundAdvice.length != 0;

        if (hasAroundAdvice) {
            // only create a new join point instance per invocation (and set the params)
            // if we have at least one around advice
            createInvocationLocalJoinPointInstance(
                cv,
                joinPointClassName,
                targetClassSignature,
                fieldNames,
                argumentTypes,
                aroundAdvice);
        }

        addBeforeAdviceInvocations(cv, beforeAdvice, joinPointClassName, argumentTypes);

        if (hasAroundAdvice) {
            // invoke proceed()
            int joinPointInstanceOpcode = argumentTypes.length + 1;
            cv.visitVarInsn(ALOAD, joinPointInstanceOpcode);
            cv.visitMethodInsn(INVOKEVIRTUAL, joinPointClassName, PROCEED_METHOD_NAME, PROCEED_METHOD_SIGNATURE);
            cv.visitVarInsn(ASTORE, joinPointInstanceOpcode + 1);
            cv.visitVarInsn(ALOAD, joinPointInstanceOpcode + 1);
            AsmHelper.unwrapType(cv, returnType);
        } else {
            // if no around advice then optimize by invoking the target JP directly and no call to proceed()
            createInlinedJoinPointInvocation(
                cv,
                joinPointType,
                joinPointName,
                joinPointSequence,
                signature,
                modifiers,
                declaringClassName,
                declaringClassSignature,
                joinPointClassName,
                argumentTypes);
        }

        addAfterAdviceInvocations(cv, afterAdvice, joinPointClassName, argumentTypes);

        AsmHelper.addReturnStatement(cv, returnType);

        cv.visitMaxs(0, 0);
    }

    /**
     * Creates an "invocation local" join point instance, e.g. one join point per invocation. Needed for thread-safety
     * when invoking around advice.
     * 
     * @param cv
     * @param joinPointClassName
     * @param targetClassSignature
     * @param fieldNames
     * @param argumentTypes
     * @param aroundAdvice
     */
    private static void createInvocationLocalJoinPointInstance(
        final CodeVisitor cv,
        final String joinPointClassName,
        final String targetClassSignature,
        final String[] fieldNames,
        final Type[] argumentTypes,
        final AdviceInfo[] aroundAdvice) {

        // create the join point instance
        cv.visitTypeInsn(NEW, joinPointClassName);
        cv.visitInsn(DUP);
        cv.visitMethodInsn(INVOKESPECIAL, joinPointClassName, INIT_METHOD_NAME, "()V");

        // set the arguments to the arg member fields
        int joinPointInstanceOpcode = argumentTypes.length + 1;
        cv.visitVarInsn(ASTORE, joinPointInstanceOpcode);
        cv.visitVarInsn(ALOAD, joinPointInstanceOpcode);
        cv.visitVarInsn(ALOAD, argumentTypes.length);
        cv.visitFieldInsn(PUTFIELD, joinPointClassName, TARGET_INSTANCE_FIELD_NAME, targetClassSignature);

        int opcode = 0;
        for (int i = 0; i < fieldNames.length; i++) {
            String fieldName = fieldNames[i];
            cv.visitVarInsn(ALOAD, joinPointInstanceOpcode);
            Type type = argumentTypes[i];
            opcode = AsmHelper.loadType(cv, opcode, type);
            cv.visitFieldInsn(PUTFIELD, joinPointClassName, fieldName, type.getDescriptor());
        }
    }

    /**
     * Create the proceed() method.
     * 
     * @param cw
     * @param signature
     * @param joinPointName
     * @param joinPointClassName
     * @param joinPointType
     * @param joinPointHash
     * @param targetClassSignature
     * @param fieldNames
     * @param joinPointSequence
     * @param modifiers
     * @param declaringClassName
     * @param declaringClassSignature
     * @param aroundAdvice
     * @param beforeAdvice
     * @param afterAdvice
     * @param signatureCflowExprStruct
     */
    private static void createProceedMethod(
        final ClassWriter cw,
        final String signature,
        final String joinPointName,
        final String joinPointClassName,
        final int joinPointType,
        final int joinPointHash,
        final String targetClassSignature,
        final String[] fieldNames,
        final int joinPointSequence,
        final int modifiers,
        final String declaringClassName,
        final String declaringClassSignature,
        final AdviceInfo[] aroundAdvice,
        final AdviceInfo[] beforeAdvice,
        final AdviceInfo[] afterAdvice,
        final JoinPointInfo signatureCflowExprStruct) {

        CodeVisitor cv = cw.visitMethod(
            ACC_PUBLIC | ACC_FINAL,
            PROCEED_METHOD_NAME,
            PROCEED_METHOD_SIGNATURE,
            new String[] {
                THROWABLE_CLASS_NAME
            },
            null);

        Type[] argumentTypes = Type.getArgumentTypes(signature);

        incrementStackFrameCounter(cv, joinPointClassName);

        // set up the labels
        Label tryLabel = new Label();
        Label defaultCaseLabel = new Label();
        Label gotoLabel = new Label();
        Label handlerLabel = new Label();
        Label endLabel = new Label();
        int nrOfCases = aroundAdvice.length;
        Label[] caseLabels = new Label[nrOfCases];
        Label[] returnLabels = new Label[nrOfCases];
        int[] caseNumbers = new int[nrOfCases];
        for (int i = 0; i < caseLabels.length; i++) {
            caseLabels[i] = new Label();
            caseNumbers[i] = i;
        }
        for (int i = 0; i < returnLabels.length; i++) {
            returnLabels[i] = new Label();
        }

        // start try-catch block
        cv.visitLabel(tryLabel);

        // start the switch block and set the stackframe as the param to the switch
        cv.visitVarInsn(ALOAD, 0);
        cv.visitFieldInsn(GETFIELD, joinPointClassName, STACK_FRAME_FIELD_NAME, I);
        cv.visitLookupSwitchInsn(defaultCaseLabel, caseNumbers, caseLabels);

        // add one case for each around advice invocation
        for (int i = 0; i < aroundAdvice.length; i++) {
            cv.visitLabel(caseLabels[i]);

            // gather advice info
            AdviceInfo adviceInfo = aroundAdvice[i];
            AspectContainer container = adviceInfo.getAspectManager().getAspectContainer(adviceInfo.getAspectIndex());
            Method adviceMethod = container.getAdvice(adviceInfo.getMethodIndex());
            String aspectClassName = container.getCrossCuttingInfo().getAspectClass().getName().replace('.', '/');
            String aspectFieldName = AROUND_ADVICE_FIELD_PREFIX + i;
            String aspectClassSignature = L + aspectClassName + SEMICOLON;

            // get the aspect instance
            cv.visitFieldInsn(GETSTATIC, joinPointClassName, AROUND_ADVICE_FIELD_PREFIX + i, aspectClassSignature);

            // get the join point instance (this)
            cv.visitVarInsn(ALOAD, 0);

            // load the arguments to the advice from the join point instance plus build up the advice method signature
            StringBuffer adviceMethodSignature = new StringBuffer();
            adviceMethodSignature.append("(Lorg/codehaus/aspectwerkz/joinpoint/JoinPoint;");
            int[] argIndexes = adviceInfo.getMethodToArgIndexes();
            for (int j = 0; j < argIndexes.length; j++) {
                int argIndex = argIndexes[j];
                if (argIndex != -1) {
                    adviceMethodSignature.append(argumentTypes[argIndex].getDescriptor());
                    Type argumentType = argumentTypes[argIndex];
                    cv.visitVarInsn(ALOAD, 0);
                    cv.visitFieldInsn(GETFIELD, joinPointClassName, ARGUMENT_FIELD + argIndex, argumentType
                            .getDescriptor());
                }
            }
            adviceMethodSignature.append(")Ljava/lang/Object;");

            // invoke the advice method
            cv
                    .visitMethodInsn(INVOKEVIRTUAL, aspectClassName, adviceMethod.getName(), adviceMethodSignature
                            .toString());

            cv.visitVarInsn(ASTORE, 1);
            cv.visitLabel(returnLabels[i]);

            decrementStackFrameCounter(cv, joinPointClassName);

            cv.visitVarInsn(ALOAD, 1);
            cv.visitInsn(ARETURN);
        }

        // invoke the target join point in the default case
        cv.visitLabel(defaultCaseLabel);
        AsmHelper.prepareWrappingOfPrimitiveType(cv, Type.getReturnType(signature));
        
        createJoinPointInvocation(
            cv,
            joinPointType,
            joinPointName,
            joinPointSequence,
            signature,
            modifiers,
            declaringClassName,
            declaringClassSignature,
            joinPointClassName,
            fieldNames);

        AsmHelper.wrapPrimitiveType(cv, Type.getReturnType(signature));
        cv.visitVarInsn(ASTORE, 1);

        cv.visitLabel(gotoLabel);

        decrementStackFrameCounter(cv, joinPointClassName);
        cv.visitVarInsn(ALOAD, 1);
        cv.visitInsn(ARETURN);

        // finally clause
        cv.visitLabel(handlerLabel);
        cv.visitVarInsn(ASTORE, 2);
        cv.visitLabel(endLabel);

        decrementStackFrameCounter(cv, joinPointClassName);
        cv.visitVarInsn(ALOAD, 2);
        cv.visitInsn(ATHROW);

        // set up the label table
        cv.visitTryCatchBlock(tryLabel, returnLabels[0], handlerLabel, null);
        for (int i = 1; i < caseLabels.length; i++) {
            Label caseLabel = caseLabels[i];
            Label returnLabel = returnLabels[i];
            cv.visitTryCatchBlock(caseLabel, returnLabel, handlerLabel, null);
        }
        cv.visitTryCatchBlock(defaultCaseLabel, gotoLabel, handlerLabel, null);
        cv.visitTryCatchBlock(handlerLabel, endLabel, handlerLabel, null);

        cv.visitMaxs(0, 0);
    }

    /**
     * Adds before advice invocations.
     * 
     * @param cv
     * @param advices
     * @param joinPointClassName
     * @param argumentTypes
     */
    private static void addBeforeAdviceInvocations(
        final CodeVisitor cv,
        final AdviceInfo[] advices,
        final String joinPointClassName,
        final Type[] argumentTypes) {

        for (int i = 0; i < advices.length; i++) {
            AdviceInfo advice = advices[i];
            AspectContainer container = advice.getAspectManager().getAspectContainer(advice.getAspectIndex());
            Method adviceMethod = container.getAdvice(advice.getMethodIndex());
            String aspectClassName = container.getCrossCuttingInfo().getAspectClass().getName().replace('.', '/');
            String aspectFieldName = BEFORE_ADVICE_FIELD_PREFIX + i;
            String aspectClassSignature = L + aspectClassName + SEMICOLON;

            //get the aspect instance
            cv.visitFieldInsn(GETSTATIC, joinPointClassName, aspectFieldName, aspectClassSignature);

            // get the static instance of the join point
            cv.visitFieldInsn(GETSTATIC, joinPointClassName, STATIC_JOIN_POINT_INSTANCE_FIELD_NAME, L
                + joinPointClassName
                + SEMICOLON);

            // load the arguments that should be passed to the advice
            int[] argIndexes = advice.getMethodToArgIndexes();
            for (int j = 0; j < argIndexes.length; j++) {
                int argIndex = argIndexes[j];
                if (argIndex != -1) {
                    Type argumentType = argumentTypes[argIndex];
                    AsmHelper.loadType(cv, argIndex, argumentType);
                }
            }

            cv.visitMethodInsn(INVOKEVIRTUAL, aspectClassName, adviceMethod.getName(), Type
                    .getMethodDescriptor(adviceMethod));
        }
    }

    /**
     * Adds after advice invocations.
     * 
     * @param cv
     * @param advices
     * @param joinPointClassName
     * @param argumentTypes
     */
    private static void addAfterAdviceInvocations(
        final CodeVisitor cv,
        final AdviceInfo[] advices,
        final String joinPointClassName,
        final Type[] argumentTypes) {

        // add after advice in reverse order
        for (int i = advices.length - 1; i >= 0; i--) {
            AdviceInfo advice = advices[i];
            AspectContainer container = advice.getAspectManager().getAspectContainer(advice.getAspectIndex());
            Method adviceMethod = container.getAdvice(advice.getMethodIndex());
            String aspectClassName = container.getCrossCuttingInfo().getAspectClass().getName().replace('.', '/');
            String aspectFieldName = AFTER_ADVICE_FIELD_PREFIX + i;
            String aspectClassSignature = L + aspectClassName + SEMICOLON;

            // get the aspect instance
            cv.visitFieldInsn(GETSTATIC, joinPointClassName, aspectFieldName, aspectClassSignature);

            // get the static instance of the join point
            cv.visitFieldInsn(GETSTATIC, joinPointClassName, STATIC_JOIN_POINT_INSTANCE_FIELD_NAME, L
                + joinPointClassName
                + SEMICOLON);

            // load the arguments that should be passed to the advice
            int[] argIndexes = advice.getMethodToArgIndexes();
            for (int j = 0; j < argIndexes.length; j++) {
                int argIndex = argIndexes[j];
                if (argIndex != -1) {
                    Type argumentType = argumentTypes[argIndex];
                    AsmHelper.loadType(cv, argIndex, argumentType);
                }
            }

            cv.visitMethodInsn(INVOKEVIRTUAL, aspectClassName, adviceMethod.getName(), Type
                    .getMethodDescriptor(adviceMethod));
        }
    }

    /**
     * Decrements the stack frame counter.
     * 
     * @param cv
     * @param joinPointClassName
     */
    private static void decrementStackFrameCounter(final CodeVisitor cv, final String joinPointClassName) {
        cv.visitVarInsn(ALOAD, 0);
        cv.visitInsn(DUP);
        cv.visitFieldInsn(GETFIELD, joinPointClassName, STACK_FRAME_FIELD_NAME, I);
        cv.visitInsn(ICONST_1);
        cv.visitInsn(ISUB);
        cv.visitFieldInsn(PUTFIELD, joinPointClassName, STACK_FRAME_FIELD_NAME, I);
    }

    /**
     * Optimized implementation that does not retrieve the parameters from the join point instance but is passed
     * directly to the method from the input parameters in the 'invoke' method. Can only be used if no around advice
     * exists.
     * 
     * @param cv
     * @param joinPointType
     * @param joinPointName
     * @param joinPointSequence
     * @param signature
     * @param modifiers
     * @param declaringClassName
     * @param declaringClassSignature
     * @param joinPointClassName
     * @param argumentTypes
     */
    private static void createInlinedJoinPointInvocation(
        final CodeVisitor cv,
        final int joinPointType,
        final String joinPointName,
        final int joinPointSequence,
        final String signature,
        final int modifiers,
        final String declaringClassName,
        final String declaringClassSignature,
        final String joinPointClassName,
        final Type[] argumentTypes) {

        switch (joinPointType) {
            case JoinPointType.METHOD_EXECUTION:

                // load the target instance member field
                cv.visitVarInsn(ALOAD, argumentTypes.length);

                if (Modifier.isStatic(modifiers)) {
                    cv.visitInsn(POP); // pop the target instance if static context
                }

                // FIXME might break for double and long types (opcode++), need to test
                // load the argument member fields
                for (int opcode = 0; opcode < argumentTypes.length; opcode++) {
                    Type argumentType = argumentTypes[opcode];
                    AsmHelper.loadType(cv, opcode, argumentType);
                }

                String prefixedMethodName = TransformationUtil.getPrefixedMethodName(
                    joinPointName,
                    joinPointSequence,
                    declaringClassName);

                // invoke the original join point
                if (Modifier.isStatic(modifiers)) {
                    cv.visitMethodInsn(INVOKESTATIC, declaringClassName, prefixedMethodName, signature);
                } else {
                    cv.visitMethodInsn(INVOKEVIRTUAL, declaringClassName, prefixedMethodName, signature);
                }

                break;

            case JoinPointType.METHOD_CALL:
                throw new UnsupportedOperationException("join point type is not supported: METHOD_CALL");

            case JoinPointType.CONSTRUCTOR_CALL:
                throw new UnsupportedOperationException("join point type is not supported: CONSTRUCTOR_CALL");

            case JoinPointType.CONSTRUCTOR_EXECUTION:
                throw new UnsupportedOperationException("join point type is not supported: CONSTRUCTOR_EXECUTION");

            case JoinPointType.FIELD_SET:
                throw new UnsupportedOperationException("join point type is not supported: FIELD_SET");

            case JoinPointType.FIELD_GET:
                throw new UnsupportedOperationException("join point type is not supported: FIELD_GET");

            case JoinPointType.HANDLER:
                throw new UnsupportedOperationException("join point type is not supported: HANDLER");

            case JoinPointType.STATIC_INITALIZATION:
                throw new UnsupportedOperationException("join point type is not supported: STATIC_INITALIZATION");
        }
    }

    /**
     * Creates a call to the target join point, the parameter(s) to the join point are retrieved from the invocation
     * local join point instance.
     * 
     * @param cv
     * @param joinPointType
     * @param joinPointName
     * @param joinPointSequence
     * @param signature
     * @param modifiers
     * @param declaringClassName
     * @param declaringClassSignature
     * @param joinPointClassName
     * @param fieldNames
     * @param joinPointInstanceOpcode (0 if used in member method context)
     */
    private static void createJoinPointInvocation(
        final CodeVisitor cv,
        final int joinPointType,
        final String joinPointName,
        final int joinPointSequence,
        final String signature,
        final int modifiers,
        final String declaringClassName,
        final String declaringClassSignature,
        final String joinPointClassName,
        final String[] fieldNames) {

        switch (joinPointType) {
            case JoinPointType.METHOD_EXECUTION:

                // load the target instance member field
                cv.visitVarInsn(ALOAD, 0);
                cv.visitFieldInsn(GETFIELD, joinPointClassName, TARGET_INSTANCE_FIELD_NAME, declaringClassSignature);

                if (Modifier.isStatic(modifiers)) {
                    cv.visitInsn(POP); // pop the target instance if static context
                }

                // load the argument member fields
                Type[] argumentTypes = Type.getArgumentTypes(signature);
                for (int i = 0; i < fieldNames.length; i++) {
                    String fieldName = fieldNames[i];
                    Type argumentType = argumentTypes[i];
                    cv.visitVarInsn(ALOAD, 0);
                    cv.visitFieldInsn(GETFIELD, joinPointClassName, fieldName, argumentType.getDescriptor());
                }

                String prefixedMethodName = TransformationUtil.getPrefixedMethodName(
                    joinPointName,
                    joinPointSequence,
                    declaringClassName);

                // invoke the original join point
                if (Modifier.isStatic(modifiers)) {
                    cv.visitMethodInsn(INVOKESTATIC, declaringClassName, prefixedMethodName, signature);
                } else {
                    cv.visitMethodInsn(INVOKEVIRTUAL, declaringClassName, prefixedMethodName, signature);
                }

                break;

            case JoinPointType.METHOD_CALL:
                throw new UnsupportedOperationException("join point type is not supported: METHOD_CALL");

            case JoinPointType.CONSTRUCTOR_CALL:
                throw new UnsupportedOperationException("join point type is not supported: CONSTRUCTOR_CALL");

            case JoinPointType.CONSTRUCTOR_EXECUTION:
                throw new UnsupportedOperationException("join point type is not supported: CONSTRUCTOR_EXECUTION");

            case JoinPointType.FIELD_SET:
                throw new UnsupportedOperationException("join point type is not supported: FIELD_SET");

            case JoinPointType.FIELD_GET:
                throw new UnsupportedOperationException("join point type is not supported: FIELD_GET");

            case JoinPointType.HANDLER:
                throw new UnsupportedOperationException("join point type is not supported: HANDLER");

            case JoinPointType.STATIC_INITALIZATION:
                throw new UnsupportedOperationException("join point type is not supported: STATIC_INITALIZATION");
        }
    }

    /**
     * Resets the stack frame counter.
     * 
     * @param cv
     * @param className
     */
    private static void resetStackFrameCounter(final CodeVisitor cv, final String className) {
        cv.visitVarInsn(ALOAD, 0);
        cv.visitInsn(ICONST_M1);
        cv.visitFieldInsn(PUTFIELD, className, STACK_FRAME_FIELD_NAME, I);
    }

    /**
     * Handles the incrementation of the stack frame.
     * 
     * @param cv
     * @param className
     */
    private static void incrementStackFrameCounter(final CodeVisitor cv, final String className) {
        cv.visitVarInsn(ALOAD, 0);
        cv.visitInsn(DUP);
        cv.visitFieldInsn(GETFIELD, className, STACK_FRAME_FIELD_NAME, I);
        cv.visitInsn(ICONST_1);
        cv.visitInsn(IADD);
        cv.visitFieldInsn(PUTFIELD, className, STACK_FRAME_FIELD_NAME, I);
    }

    /**
     * Creates utility methods for the join point (getter, setters etc.).
     * 
     * @param cw
     * @param className
     * @param targetClassSignature
     */
    private static void createUtilityMethods(
        final ClassWriter cw,
        final String className,
        final String targetClassSignature) {
        CodeVisitor cv;

        // getSignature
        {
            cv = cw.visitMethod(
                ACC_PUBLIC,
                "getSignature",
                "()Lorg/codehaus/aspectwerkz/joinpoint/Signature;",
                null,
                null);
            cv.visitFieldInsn(GETSTATIC, className, SIGNATURE_FIELD_NAME, SIGNATURE_CLASS_SIGNATURE);
            cv.visitInsn(ARETURN);
            cv.visitMaxs(0, 0);
        }

        // addMetaData
        {
            cv = cw.visitMethod(ACC_PUBLIC, "addMetaData", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null);
            cv.visitFieldInsn(GETSTATIC, className, META_DATA_FIELD_NAME, "Ljava/util/Map;");
            cv.visitVarInsn(ALOAD, 1);
            cv.visitVarInsn(ALOAD, 2);
            cv.visitMethodInsn(
                INVOKEINTERFACE,
                "java/util/Map",
                "put",
                "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
            cv.visitInsn(POP);
            cv.visitInsn(RETURN);
            cv.visitMaxs(0, 0);
        }

        // getMetaData
        {
            cv = cw.visitMethod(ACC_PUBLIC, "getMetaData", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null);
            cv.visitFieldInsn(GETSTATIC, className, META_DATA_FIELD_NAME, "Ljava/util/Map;");
            cv.visitVarInsn(ALOAD, 1);
            cv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
            cv.visitInsn(ARETURN);
            cv.visitMaxs(0, 0);
        }

        // getTarget
        {
            cv = cw.visitMethod(ACC_PUBLIC, "getTarget", "()" + targetClassSignature, null, null);
            cv.visitVarInsn(ALOAD, 0);
            cv.visitFieldInsn(GETFIELD, className, TARGET_INSTANCE_FIELD_NAME, targetClassSignature);
            cv.visitInsn(ARETURN);
            cv.visitMaxs(0, 0);
        }

        // getThis
        {
            cv = cw.visitMethod(ACC_PUBLIC, "getThis", "()" + targetClassSignature, null, null);
            cv.visitVarInsn(ALOAD, 0);
            cv.visitFieldInsn(GETFIELD, className, THIS_INSTANCE_FIELD_NAME, targetClassSignature);
            cv.visitInsn(ARETURN);
            cv.visitMaxs(0, 0);
        }
    }

    /**
     * Struct with join point info.
     * 
     * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
     */
    private static class JoinPointInfo {
        public Signature signature = null;

        public Rtti rtti = null;

        public List cflowExpressions = null;

        public ExpressionContext expressionContext = null;
    }
}