Java tutorial
/*** * 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; }