org.summer.aop.ltw.AspectWeaver.java Source code

Java tutorial

Introduction

Here is the source code for org.summer.aop.ltw.AspectWeaver.java

Source

/***
 * Summer: An enhanced, non-invasive, and easy-to-use IoC container and
 * LTW-AOP framework enabling brand-new mocking capabilities in combination
 * with a lightweight rule engine and general meta expression language purely
 * written in Java.
 * It provides component composition at run-time as well as brand-new mocking
 * capabilities that are also applicable to binary third-party libraries, to name
 * only two of all its features.
 * There are barely limitations due to the lack of
 * Java in adding and removing fields and methods to and from a class at run-time.
 *
 * Copyright (C) 2011-2013  Sandro Sebastian Koll
 *
 * This file is part of Summer.
 *
 * Summer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Summer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Summer. If not, see <http://www.gnu.org/licenses/>.
 */
package org.summer.aop.ltw;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.AdviceAdapter;
import org.objectweb.asm.tree.*;
import org.summer.aop.AOPContext;
import org.summer.aop.Aspect;

import java.io.IOException;
import java.util.*;

/**
 * @author Sandro Sebastian Koll
 */
public final class AspectWeaver extends ClassVisitor implements Opcodes {
    public AspectWeaver(ClassVisitor cv) {
        super(ASM4, cv);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        this.className = name;
        isInterface = (access & ACC_INTERFACE) != 0;
        interceptedMethodInfos.clear();
        constructorCounter = 0;
        anonymousInnerClassCounter = 1;
        anonymousInnerClassName = className + "$" + anonymousInnerClassCounter;
        List<String> allInterfaces = new LinkedList<>();
        allInterfaces.addAll(Arrays.asList(interfaces));
        String[] additionalInterfaces = AOPContext.getInstance()
                .getAdditionalInterfaces(className.replace('/', '.'));
        if (additionalInterfaces != null) {
            for (String inter : additionalInterfaces) {
                inter = inter.replace('.', '/');
                if (!allInterfaces.contains(inter))
                    allInterfaces.add(inter);
            }
        }
        String mergingClassName = AOPContext.getInstance().getMergingClassName(className.replace('/', '.'));
        String replacingClassName = AOPContext.getInstance().getReplacingClassName(className.replace('/', '.'));
        if (mergingClassName != null) {
            mergingClassNode = mergeClassHierarchy(mergingClassName, allInterfaces);
            for (Object mn : mergingClassNode.methods) {
                reviseMethodNode(mergingClassNode, (MethodNode) mn, this.className);
            }
        } else if (replacingClassName != null) {
            allInterfaces.clear();
            replacingClassNode = mergeClassHierarchy(replacingClassName, allInterfaces);
            for (Object mn : replacingClassNode.methods) {
                reviseMethodNode(replacingClassNode, (MethodNode) mn, this.className);
            }
            superName = replacingClassNode.superName;
        }
        cv.visit(version, ACC_PUBLIC, name, signature, superName,
                allInterfaces.toArray(new String[allInterfaces.size()]));
    }

    @SuppressWarnings("unchecked")
    private ClassNode mergeClassHierarchy(String className, List<String> interfaces) {
        ClassNode classNode = null;
        try {
            ClassReader cr = new ClassReader(className);
            cr.accept(classNode = new ClassNode(), ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES);
            if (!classNode.superName.equals("java/lang/Object")) {
                boolean flag;
                ClassNode superClassNode = mergeClassHierarchy(classNode.superName, interfaces);
                for (Object superMethodNode : superClassNode.methods) {
                    flag = false;
                    MethodNode smn = (MethodNode) superMethodNode;
                    for (Object methodNode : classNode.methods) {
                        MethodNode mn = (MethodNode) methodNode;
                        if (mn.name.equals(smn.name) && mn.desc.equals(smn.desc)) {
                            flag = true;
                            switch (mn.name) {
                            case "<clinit>":
                                mergeInitializingInstructions(superClassNode.name, smn.maxStack, smn.maxLocals,
                                        classNode.name, mn, smn.instructions.getFirst());
                                break;
                            case "<init>":
                                AbstractInsnNode insn = smn.instructions.getFirst();
                                while (!(insn instanceof MethodInsnNode)
                                        || !((MethodInsnNode) insn).owner.equals(superClassNode.superName)
                                        || ((MethodInsnNode) insn).getOpcode() != INVOKESPECIAL
                                        || !((MethodInsnNode) insn).name.equals("<init>")) {
                                    insn = insn.getNext();
                                }
                                mergeInitializingInstructions(superClassNode.name, smn.maxStack, smn.maxLocals,
                                        classNode.name, mn, insn.getNext());
                                break;
                            default:
                                break;
                            }
                            break;
                        }
                    }
                    if (!flag) {
                        reviseMethodNode(superClassNode, smn, classNode.name);
                        classNode.methods.add(smn);
                    }
                }
            }
            for (Object inter : classNode.interfaces) {
                if (!interfaces.contains(inter))
                    interfaces.add((String) inter);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return classNode;
    }

    private void mergeInitializingInstructions(String superClassName, int maxStack, int maxLocals, String className,
            MethodNode methodNode, AbstractInsnNode firstSuperInsn) {
        InsnList superInsnList = new InsnList();
        while (!(firstSuperInsn instanceof InsnNode) || (((InsnNode) firstSuperInsn).getOpcode() != RETURN
                && ((InsnNode) firstSuperInsn).getOpcode() != ATHROW)) {
            if (firstSuperInsn instanceof MethodInsnNode
                    && ((MethodInsnNode) firstSuperInsn).owner.equals(superClassName))
                ((MethodInsnNode) firstSuperInsn).owner = className;
            else if (firstSuperInsn instanceof FieldInsnNode
                    && ((FieldInsnNode) firstSuperInsn).owner.equals(superClassName))
                ((FieldInsnNode) firstSuperInsn).owner = className;
            superInsnList.add(firstSuperInsn);
            firstSuperInsn = firstSuperInsn.getNext();
        }
        firstSuperInsn = methodNode.instructions.getFirst();
        while (!(firstSuperInsn instanceof MethodInsnNode)
                || !((MethodInsnNode) firstSuperInsn).owner.equals(superClassName)
                || ((MethodInsnNode) firstSuperInsn).getOpcode() != INVOKESPECIAL
                || !((MethodInsnNode) firstSuperInsn).name.equals("<init>")) {
            firstSuperInsn = firstSuperInsn.getNext();
        }
        firstSuperInsn = firstSuperInsn.getNext();
        methodNode.instructions.insertBefore(firstSuperInsn, superInsnList);
        methodNode.maxStack = Math.max(methodNode.maxStack, maxStack);
        methodNode.maxLocals = Math.max(methodNode.maxLocals, maxLocals);
        for (Iterator<?> it = methodNode.localVariables.iterator(); it.hasNext();) {
            LocalVariableNode localVar = (LocalVariableNode) it.next();
            if (localVar.desc.equals("L" + superClassName + ";"))
                localVar.desc = "L" + className + ";";
        }
    }

    @Override
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
        if (Character.isDigit(name.charAt(name.length() - 1)))
            anonymousInnerClassName = className + "$" + (++anonymousInnerClassCounter);
        cv.visitInnerClass(name, outerName, innerName, access);
    }

    /**
     * Displaces the original method body to a corresponding private redirection method. However, if the visited class
     * has been mocked and the mock class overwrites this method, the original method body will be replaced entirely by
     * the corresponding mock method body.
     * <p/>
     * Note: As constructors are nothing else than methods, they are also taken into account.
     *
     * @param access     the original method access
     * @param name       the original method name
     * @param desc       the original method description
     * @param signature  the original method signature
     * @param exceptions the original exceptions
     * @return a method visitor that points to the private redirected method
     */
    @Override
    public MethodVisitor visitMethod(int access, final String name, final String desc, String signature,
            String[] exceptions) {
        // If this class should be replaced, skip all original methods
        if (replacingClassNode != null && !isVisitEndReached)
            return null;
        // If this class should be merged and this method isn't the static initializer, and a corresponding merging method is defined, merge their exceptions
        if (mergingClassNode != null && !name.equals("<clinit>")) {
            for (Object methodNode : mergingClassNode.methods) {
                MethodNode mn = (MethodNode) methodNode;
                if (mn.name.equals(name) && mn.desc.equals(desc)) {
                    List<String> mergedExceptions = exceptions == null ? new ArrayList<String>()
                            : Arrays.asList(exceptions);
                    for (Object e : mn.exceptions) {
                        String ex = (String) e;
                        if (!mergedExceptions.contains(ex))
                            mergedExceptions.add(ex);
                    }
                    exceptions = mergedExceptions.toArray(new String[mergedExceptions.size()]);
                    break;
                }
            }
        }
        MethodVisitor mv;
        //Currently ALL constructors and methods will be transformed for retransformation purposes, because
        //the Java Instrumentation API doesn't yet allow the addition of new methods (or constructors).
        //If future Java versions cancel this restriction you can safely enable the following three lines in place of
        //the three lines after next!
        //    List<Aspect> matchingAspects = getMatchingAspects(access,name,desc,signature,exceptions);
        //    if(!isInterface && !matchingAspects.isEmpty())
        //    {
        if (!isInterface && !name.equals("<clinit>")) {
            List<Aspect> matchingAspects = getMatchingAspects(access, name, desc, signature, exceptions);
            if (name.equals("<init>")) {
                // Transform the constructor and add a constructor redirected method
                mv = new ConstructorSplitter(className, cv, access, ++constructorCounter, desc, signature,
                        exceptions, matchingAspects);
            } else {
                // Rename original non-constructor methods
                mv = cv.visitMethod(toPrivateAccess(access), AOPContext.toRedirectedMethodName(name, -1), desc,
                        signature, exceptions);
            }
            // Remember the intercepted original method details
            interceptedMethodInfos.add(new AspectWeaver.MethodInfo(access, name,
                    name.equals("<init>") ? constructorCounter : -1, desc, signature, exceptions, matchingAspects));
            // If this class should be merged...
            if (mergingClassNode != null) {
                if (name.equals("<init>")) {
                    visitedMergingClassMethods.add(name + desc);
                    // Merge the original constructor body with ALL those defined in the merging class
                    return new AdviceAdapter(ASM4, mv, access, "<init>", desc) {
                        @Override
                        protected void onMethodExit(int opcode) {
                            for (Object methodNode : mergingClassNode.methods) {
                                MethodNode mn = (MethodNode) methodNode;
                                if (mn.name.equals("<init>")) {
                                    // Insert everything between the super call invocation and RETURN or ATHROW instruction
                                    AbstractInsnNode insn = mn.instructions.getFirst();
                                    while (!(insn instanceof MethodInsnNode)
                                            || !((MethodInsnNode) insn).owner.equals(mergingClassNode.superName)
                                            || ((MethodInsnNode) insn).getOpcode() != INVOKESPECIAL
                                            || !((MethodInsnNode) insn).name.equals("<init>")) {
                                        insn = insn.getNext();
                                    }
                                    insn = insn.getNext();
                                    while (!(insn instanceof InsnNode) || (((InsnNode) insn).getOpcode() != RETURN
                                            && ((InsnNode) insn).getOpcode() != ATHROW)) {
                                        if (insn instanceof MethodInsnNode
                                                && ((MethodInsnNode) insn).owner.equals(mergingClassNode.name))
                                            ((MethodInsnNode) insn).owner = className;
                                        else if (insn instanceof FieldInsnNode
                                                && ((FieldInsnNode) insn).owner.equals(mergingClassNode.name))
                                            ((FieldInsnNode) insn).owner = className;
                                        insn.accept(this);
                                        insn = insn.getNext();
                                    }
                                    maxStack = Math.max(maxStack, mn.maxStack);
                                    maxLocals = Math.max(maxLocals, mn.maxLocals);
                                    for (Iterator<?> it = mn.localVariables.iterator(); it.hasNext();) {
                                        LocalVariableNode localVar = (LocalVariableNode) it.next();
                                        if (localVar.desc.equals("L" + mergingClassNode.name + ";"))
                                            localVar.desc = "L" + className + ";";
                                    }
                                }
                            }
                        }

                        @Override
                        public void visitMaxs(int nStack, int nLocals) {
                            super.visitMaxs(Math.max(nStack, maxStack), Math.max(nLocals, maxLocals));
                        }

                        private int maxStack;
                        private int maxLocals;
                    };
                } else {
                    for (Object methodNode : mergingClassNode.methods) {
                        MethodNode mn = (MethodNode) methodNode;
                        if (mn.name.equals(name) && mn.desc.equals(desc)) {
                            visitedMergingClassMethods.add(name + desc);
                            if (shouldInsertRedirectionCondition) {
                                // Insert a redirection condition, which is based on the STATIC_IS_MERGING_REVERTED_FIELD boolean value.
                                // If the flag is false then call the original method and skip the merging one, otherwise do the opposite.
                                Type returnType = Type.getReturnType(desc);
                                boolean isVoid = returnType.getSort() == Type.VOID;
                                int returnOpcode = isVoid ? RETURN : returnType.getOpcode(IRETURN);
                                AbstractInsnNode first = mn.instructions.getFirst();
                                mn.instructions.insertBefore(first, new FieldInsnNode(GETSTATIC, className,
                                        AOPContext.STATIC_IS_MERGING_REVERTED_FIELD, "Z"));
                                LabelNode labelNode = new LabelNode();
                                mn.instructions.insertBefore(first, new JumpInsnNode(IFEQ, labelNode));
                                boolean isStatic = (access & ACC_STATIC) != 0;
                                int inc = 0;
                                if (!isStatic) {
                                    mn.instructions.insertBefore(first, new VarInsnNode(ALOAD, 0));
                                    mn.maxStack++;
                                    inc = 1;
                                }
                                Type[] argTypes = Type.getArgumentTypes(desc);
                                for (int i = 0; i < argTypes.length; ++i) {
                                    mn.instructions.insertBefore(first,
                                            new VarInsnNode(argTypes[i].getOpcode(ILOAD), inc + i));
                                    if (argTypes[i].getInternalName().equals("J")
                                            || argTypes[i].getInternalName().equals("D"))
                                        ++inc;
                                }
                                mn.instructions.insertBefore(first,
                                        new MethodInsnNode(isStatic ? INVOKESTATIC : INVOKEVIRTUAL, className,
                                                AOPContext.toMergedMethodName(name, -1), desc));
                                mn.instructions.insertBefore(first, new InsnNode(returnOpcode));
                                mn.instructions.insertBefore(first, labelNode);
                            }
                            // A corresponding merging method has been found, so let's replace the original method body
                            // with the merging method body
                            mn.accept(mv);
                            // Rename the original method if existent, otherwise return null
                            return shouldInsertRedirectionCondition
                                    ? cv.visitMethod(access, AOPContext.toMergedMethodName(name, -1), desc,
                                            signature, exceptions)
                                    : null;
                        }
                    }
                }
            }
        } else {
            mv = cv.visitMethod(access, name, desc, signature, exceptions);
            if (mergingClassNode != null)
                visitedMergingClassMethods.add(name + desc);
            if (name.equals("<clinit>")) {
                isStaticBlockVisited = true;
                mv = new AdviceAdapter(ASM4, mv, access, name, desc) {
                    @Override
                    protected void onMethodEnter() {
                        initializeStaticAspectsField(this);
                        if (mergingClassNode != null)
                            initializeStaticIsMergingActiveField(this);
                    }

                    @Override
                    protected void onMethodExit(int opcode) {
                        if (mergingClassNode != null) {
                            for (Object methodNode : mergingClassNode.methods) {
                                MethodNode mn = (MethodNode) methodNode;
                                if (mn.name.equals("<clinit>")) {
                                    AbstractInsnNode insn = mn.instructions.getFirst();
                                    while (!(insn instanceof InsnNode) || (((InsnNode) insn).getOpcode() != RETURN
                                            && ((InsnNode) insn).getOpcode() != ATHROW)) {
                                        if (insn instanceof MethodInsnNode
                                                && ((MethodInsnNode) insn).owner.equals(mergingClassNode.name))
                                            ((MethodInsnNode) insn).owner = className;
                                        else if (insn instanceof FieldInsnNode
                                                && ((FieldInsnNode) insn).owner.equals(mergingClassNode.name))
                                            ((FieldInsnNode) insn).owner = className;
                                        insn.accept(this);
                                        insn = insn.getNext();
                                    }
                                    maxStack = Math.max(maxStack, mn.maxStack);
                                    maxLocals = Math.max(maxLocals, mn.maxLocals);
                                    for (Iterator<?> it = mn.localVariables.iterator(); it.hasNext();) {
                                        LocalVariableNode localVar = (LocalVariableNode) it.next();
                                        if (localVar.desc.equals("L" + mergingClassNode.name + ";"))
                                            localVar.desc = "L" + className + ";";
                                    }
                                    break;
                                }
                            }
                        }
                    }

                    @Override
                    public void visitMaxs(int nStack, int nLocals) {
                        if (mergingClassNode != null)
                            super.visitMaxs(Math.max(nStack, maxStack), Math.max(nLocals, maxLocals));
                        else
                            super.visitMaxs(nStack + 2, nLocals);
                    }

                    private int maxStack;
                    private int maxLocals;
                };
            }
        }
        return mv;
    }

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        // If this class should be merged...
        if (mergingClassNode != null) {
            // Replace all fields with the matching ones in the merging class
            for (Object fieldNode : mergingClassNode.fields) {
                FieldNode fn = (FieldNode) fieldNode;
                if (fn.name.equals(name)) {
                    visitedMergingClassFields.add(fn.name);
                    return cv.visitField(fn.access, fn.name, fn.desc, fn.signature, fn.value);
                }
            }
        } else if (replacingClassNode != null) // If this class should be replaced...
        {
            // ...skip all fields
            return null;
        }
        return cv.visitField(access, name, desc, signature, value);
    }

    @Override
    public void visitEnd() {
        isVisitEndReached = true;
        // If this class should be merged...
        if (mergingClassNode != null) {
            // Add all remaining non matching merging fields
            for (Object fieldNode : mergingClassNode.fields) {
                FieldNode fn = (FieldNode) fieldNode;
                if (!visitedMergingClassFields.contains(fn.name))
                    fn.accept(cv);
            }
            // Add all remaining non matching merging methods
            for (Object methodNode : mergingClassNode.methods) {
                shouldInsertRedirectionCondition = false;
                MethodNode mn = (MethodNode) methodNode;
                if (!visitedMergingClassMethods.contains(mn.name + mn.desc))
                    mn.accept(this);
            }
        } else if (replacingClassNode != null) // If this class should be replaced...
        {
            // Add all replacing fields
            for (Object fieldNode : replacingClassNode.fields) {
                ((FieldNode) fieldNode).accept(cv);
            }
            // Add all remaining replacing non matching replacing methods
            for (Object methodNode : replacingClassNode.methods) {
                MethodNode mn = (MethodNode) methodNode;
                if (!mn.name.equals("<clinit>"))
                    mn.accept(this);
            }
        }
        MethodVisitor mv;
        // Split the advised methods
        for (AspectWeaver.MethodInfo info : interceptedMethodInfos) {
            if (!info.methodName.equals("<clinit>")) {
                int privateAccess = toPrivateAccess(info.access);
                // For non constructor methods replace the original method body
                if (!info.methodName.equals("<init>")) {
                    mv = cv.visitMethod(info.access, info.methodName, info.desc, info.signature, info.exceptions);
                    mv = new MethodReplacer(className, mv, privateAccess, info.methodName, info.desc,
                            info.signature, info.exceptions, info.matchingAspects);
                    mv.visitCode();
                    mv.visitMaxs(-1, -1);
                    mv.visitEnd();
                }
                // Add a corresponding proceeding method
                mv = cv.visitMethod(privateAccess,
                        AOPContext.toProceedingMethodName(info.methodName, info.constructorNumber), info.desc,
                        info.signature, info.exceptions);
                mv = new ProceedingMethodGenerator(className, mv, privateAccess, info.methodName,
                        info.constructorNumber, info.desc, info.signature, info.exceptions, info.matchingAspects);
                mv.visitCode();
                mv.visitMaxs(-1, -1);
                mv.visitEnd();
            }
        }
        // Add the STATIC_ASPECTS_FIELD
        cv.visitField(ACC_PRIVATE + ACC_STATIC, AOPContext.STATIC_ASPECTS_FIELD, "Ljava/util/Map;",
                "Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;", null).visitEnd();
        if (mergingClassNode != null) {
            // Add the STATIC_IS_MERGING_REVERTED_FIELD
            cv.visitField(ACC_PRIVATE + ACC_STATIC, AOPContext.STATIC_IS_MERGING_REVERTED_FIELD, "Z", null, null)
                    .visitEnd();
        }
        // Declare the static aspects field within the class' static block if not done yet
        if (!isStaticBlockVisited) {
            mv = cv.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
            if (!tryToAppendReplacingClassStaticBlock(mv)) {
                mv.visitCode();
                initializeStaticAspectsField(mv);
                int maxStack = 2;
                if (mergingClassNode != null) {
                    initializeStaticIsMergingActiveField(mv);
                    maxStack++;
                }
                mv.visitInsn(RETURN);
                mv.visitMaxs(maxStack, 0);
                mv.visitEnd();
            }
            isStaticBlockVisited = true;
        }
        cv.visitInnerClass(anonymousInnerClassName, null, null, 0);
        cv.visitEnd();
        // Instantiate the static aspects field via a dynamic anonymous inner class and populate it with
        // all advised methods and their matching aspects
        ClassWriter cw = new ClassWriter(0);
        //    cv = new org.objectweb.asm.util.CheckClassAdapter(cw); // ASM Tree API (asm-tree.jar) is needed for this
        cw.visit(V1_5, ACC_PUBLIC, anonymousInnerClassName,
                "Ljava/util/HashMap<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;", "java/util/HashMap",
                null);
        cw.visitOuterClass(className, null, null);
        cw.visitInnerClass(anonymousInnerClassName, null, null, 0);
        mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
        for (AspectWeaver.MethodInfo info : interceptedMethodInfos) {
            mv.visitTypeInsn(NEW, "java/util/LinkedList");
            mv.visitInsn(DUP);
            mv.visitMethodInsn(INVOKESPECIAL, "java/util/LinkedList", "<init>", "()V");
            mv.visitVarInsn(ASTORE, 1);
            for (Aspect aspect : info.matchingAspects) {
                mv.visitVarInsn(ALOAD, 1);
                mv.visitLdcInsn(aspect.getId());
                mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z");
                mv.visitInsn(POP);
            }
            mv.visitVarInsn(ALOAD, 0);
            mv.visitLdcInsn(info.methodName + info.desc);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, anonymousInnerClassName, "put",
                    "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
            mv.visitInsn(POP);
        }
        mv.visitInsn(RETURN);
        mv.visitMaxs(3, 2);
        mv.visitEnd();
        // Finish up class writer
        cw.visitEnd();
        // Register the dynamic anonymous inner class within the top level class loader for further class resolving issues
        TopLevelClassLoader.registerAnonymousInnerClass(className, anonymousInnerClassCounter, cw.toByteArray());
    }

    private boolean tryToAppendReplacingClassStaticBlock(MethodVisitor staticBlockVisitor) {
        if (replacingClassNode != null) {
            for (Object methodNode : replacingClassNode.methods) {
                MethodNode mn = (MethodNode) methodNode;
                if (mn.name.equals("<clinit>")) {
                    if (mn.instructions.size() > 0) {
                        AbstractInsnNode first = mn.instructions.getFirst();
                        mn.instructions.insertBefore(first, new TypeInsnNode(NEW, anonymousInnerClassName));
                        mn.instructions.insertBefore(first, new InsnNode(DUP));
                        mn.instructions.insertBefore(first,
                                new MethodInsnNode(INVOKESPECIAL, anonymousInnerClassName, "<init>", "()V"));
                        mn.instructions.insertBefore(first, new FieldInsnNode(PUTSTATIC, className,
                                AOPContext.STATIC_ASPECTS_FIELD, "Ljava/util/Map;"));
                    }
                    mn.accept(staticBlockVisitor);
                    return true;
                }
            }
        }
        return false;
    }

    private void initializeStaticAspectsField(MethodVisitor mv) {
        mv.visitTypeInsn(NEW, anonymousInnerClassName);
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, anonymousInnerClassName, "<init>", "()V");
        mv.visitFieldInsn(PUTSTATIC, className, AOPContext.STATIC_ASPECTS_FIELD, "Ljava/util/Map;");
    }

    private void initializeStaticIsMergingActiveField(MethodVisitor mv) {
        mv.visitInsn(ICONST_0);
        mv.visitFieldInsn(PUTSTATIC, className, AOPContext.STATIC_IS_MERGING_REVERTED_FIELD, "Z");
    }

    private int toPrivateAccess(int access) {
        int privateAccess = ACC_PRIVATE;
        if ((access & ACC_STATIC) != 0)
            privateAccess += ACC_STATIC;
        if ((access & ACC_FINAL) != 0)
            privateAccess += ACC_FINAL;
        if ((access & ACC_SYNCHRONIZED) != 0)
            privateAccess += ACC_SYNCHRONIZED;
        return privateAccess;
    }

    private List<Aspect> getMatchingAspects(int access, String originalName, String desc, String signature,
            String[] exceptions) {
        List<Aspect> list = new LinkedList<>();
        for (Aspect aspect : AOPContext.getInstance().getAspects()) {
            if (aspect.getPointcutExpression().matchMethod(access, originalName, desc, signature, exceptions))
                list.add(aspect);
        }
        return list;
    }

    private void reviseMethodNode(ClassNode classNode, MethodNode methodNode, String owner) {
        for (Iterator<?> it = methodNode.instructions.iterator(); it.hasNext();) {
            AbstractInsnNode insn = (AbstractInsnNode) it.next();
            if (insn instanceof MethodInsnNode) {
                MethodInsnNode methodInsn = (MethodInsnNode) insn;
                if (methodInsn.owner.equals(classNode.name))
                    methodInsn.owner = owner;
            } else if (insn instanceof FieldInsnNode) {
                FieldInsnNode fieldInsn = (FieldInsnNode) insn;
                if (fieldInsn.owner.equals(classNode.name))
                    fieldInsn.owner = owner;
            }
        }
        for (Iterator<?> it = methodNode.localVariables.iterator(); it.hasNext();) {
            LocalVariableNode localVar = (LocalVariableNode) it.next();
            if (localVar.desc.equals("L" + classNode.name + ";"))
                localVar.desc = "L" + owner + ";";
        }
    }

    private static class MethodInfo {
        private MethodInfo(int access, String methodName, int constructorNumber, String desc, String signature,
                String[] exceptions, List<Aspect> matchingAspects) {
            this.access = access;
            this.methodName = methodName;
            this.constructorNumber = constructorNumber;
            this.desc = desc;
            this.signature = signature;
            this.exceptions = exceptions;
            this.matchingAspects = matchingAspects;
        }

        private int access;
        private String methodName;
        private int constructorNumber;
        private String desc;
        private String signature;
        private String[] exceptions;
        private List<Aspect> matchingAspects;
    }

    private String className;
    private boolean isInterface;
    private List<AspectWeaver.MethodInfo> interceptedMethodInfos = new LinkedList<>();
    private int constructorCounter;
    private int anonymousInnerClassCounter;
    private String anonymousInnerClassName;
    private ClassNode mergingClassNode;
    private ClassNode replacingClassNode;
    private List<String> visitedMergingClassFields = new LinkedList<>();
    private List<String> visitedMergingClassMethods = new LinkedList<>();
    private boolean isStaticBlockVisited;
    private boolean isVisitEndReached;
    private boolean shouldInsertRedirectionCondition = true;
}