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.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.summer.aop.Aspect; import java.util.LinkedList; import java.util.List; /** * @author Sandro Sebastian Koll */ class AuxiliaryWeaver extends MethodVisitor implements Opcodes { AuxiliaryWeaver(String className, MethodVisitor mv, int access, String methodName, int constructorNumber, String methodDesc, String methodSignature, String[] exceptions, List<Aspect> matchingAspects) { super(ASM4, mv); if (className == null || className.isEmpty()) throw new RuntimeException("Class name may not be null or empty"); if (className.indexOf(".") >= 0) throw new RuntimeException("Class name have to be in internal form"); this.className = className; this.access = access; this.methodName = methodName; this.constructorNumber = constructorNumber; this.methodDesc = methodDesc; this.methodSignature = methodSignature; this.exceptions = exceptions; this.matchingAspects = matchingAspects; isPublic = (access & ACC_PUBLIC) != 0; isPrivate = (access & ACC_PRIVATE) != 0; isProtected = (access & ACC_PROTECTED) != 0; isStatic = (access & ACC_STATIC) != 0; isFinal = (access & ACC_FINAL) != 0; isSynchronized = (access & ACC_SYNCHRONIZED) != 0; isVolatile = (access & ACC_VOLATILE) != 0; isTransient = (access & ACC_TRANSIENT) != 0; isStrict = (access & ACC_STRICT) != 0; isPackagePrivate = !isPrivate && !isProtected && !isPublic; argTypes = Type.getArgumentTypes(methodDesc); argCount = argTypes.length; loadArgOpcodes = new int[argCount]; storeArgOpcodes = new int[argCount]; for (int i = 0; i < argCount; ++i) { loadArgOpcodes[i] = argTypes[i].getOpcode(ILOAD); storeArgOpcodes[i] = argTypes[i].getOpcode(ISTORE); } returnType = Type.getReturnType(methodDesc); isPrimitiveReturn = returnType.getSort() == Type.BYTE || returnType.getSort() == Type.SHORT || returnType.getSort() == Type.INT || returnType.getSort() == Type.LONG || returnType.getSort() == Type.FLOAT || returnType.getSort() == Type.DOUBLE || returnType.getSort() == Type.BOOLEAN || returnType.getSort() == Type.CHAR; isVoid = returnType.getSort() == Type.VOID; returnOpcode = isVoid ? RETURN : returnType.getOpcode(IRETURN); loadReturnValueOpcode = returnType.getOpcode(ILOAD); storeReturnValueOpcode = returnType.getOpcode(ISTORE); frame = isStatic ? new Frame(argTypes) : new Frame(className, argTypes); returnTypeSlots = Frame.isLongOrDouble(returnType.getDescriptor()) ? 2 : 1; returnTypeInternalName = frame.toInternalName(returnType); argSlots = 0; for (Type type : Type.getArgumentTypes(methodDesc)) { argSlots += frame.getSlots(type); } } @Override public void visitInsn(int opcode) { frame.insn(opcode); mv.visitInsn(opcode); if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) { if (!exLabels.isEmpty()) { frame.resetStack(exTypes.get(0)); exTypes.remove(0); exLabels.remove(0); } else frame.resetStack(); } } @Override public void visitVarInsn(int opcode, int slot) { frame.varInsn(opcode, slot); mv.visitVarInsn(opcode, slot); } @Override public void visitIntInsn(int opcode, int operand) { frame.intInsn(opcode, operand); mv.visitIntInsn(opcode, operand); } @Override public void visitTypeInsn(int opcode, String type) { frame.typeInsn(opcode, type); mv.visitTypeInsn(opcode, type); } @Override public void visitLdcInsn(Object cst) { frame.ldcInsn(cst); mv.visitLdcInsn(cst); } @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { frame.fieldInsn(opcode, owner, name, desc); mv.visitFieldInsn(opcode, owner, name, desc); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc) { frame.methodInsn(opcode, owner, name, desc); mv.visitMethodInsn(opcode, owner, name, desc); } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String exType) { exLabels.add(handler); exTypes.add(exType); mv.visitTryCatchBlock(start, end, handler, exType); } @Override public void visitJumpInsn(int opcode, Label label) { mv.visitJumpInsn(opcode, label); if (opcode == GOTO) frame.resetStack(); } @Override public void visitLabel(Label label) { mv.visitLabel(label); if (exLabels.contains(label)) { int index = exLabels.indexOf(label); frame.resetStack(exTypes.get(index)); exTypes.remove(index); exLabels.remove(index); } } @Override public void visitMaxs(int nStack, int nLocal) { mv.visitMaxs(frame.getMaxStackSlots(), frame.getMaxLocalSlots()); } protected int getCurrentLocalSlots() { return frame.getCurrentLocalSlots(); } protected int getMaxLocalSlots() { return frame.getMaxLocalSlots(); } protected int getCurrentStackSlots() { return frame.getCurrentStackSlots(); } protected int getMaxStackSlots() { return frame.getMaxStackSlots(); } protected void visitFrame() { mv.visitFrame(Opcodes.F_NEW, frame.getLocalSize(), frame.getLocals(), frame.getStackSize(), frame.getStack()); } protected void pushWrapInsn() { String wrapperType = frame.toWrapperType(returnTypeInternalName); visitMethodInsn(INVOKESTATIC, wrapperType, "valueOf", "(" + returnTypeInternalName + ")L" + wrapperType + ";"); } protected void pushCheckcast() { switch (returnType.getSort()) { case Type.BYTE: visitTypeInsn(CHECKCAST, "java/lang/Byte"); visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B"); break; case Type.SHORT: visitTypeInsn(CHECKCAST, "java/lang/Short"); visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S"); break; case Type.INT: visitTypeInsn(CHECKCAST, "java/lang/Integer"); visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); break; case Type.BOOLEAN: visitTypeInsn(CHECKCAST, "java/lang/Boolean"); visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); break; case Type.CHAR: visitTypeInsn(CHECKCAST, "java/lang/Character"); visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C"); break; case Type.LONG: visitTypeInsn(CHECKCAST, "java/lang/Long"); visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J"); break; case Type.FLOAT: visitTypeInsn(CHECKCAST, "java/lang/Float"); visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F"); break; case Type.DOUBLE: visitTypeInsn(CHECKCAST, "java/lang/Double"); visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D"); break; default: visitTypeInsn(CHECKCAST, returnType.getInternalName()); } } protected void pushInteger(int i) { switch (i) { case 0: visitInsn(ICONST_0); break; case 1: visitInsn(ICONST_1); break; case 2: visitInsn(ICONST_2); break; case 3: visitInsn(ICONST_3); break; case 4: visitInsn(ICONST_4); break; case 5: visitInsn(ICONST_5); break; default: if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) visitIntInsn(BIPUSH, i); else visitIntInsn(SIPUSH, i); } } protected void pushArgumentTypes() { if (argCount > 0) { pushInteger(argCount); visitTypeInsn(ANEWARRAY, "java/lang/Class"); for (int i = 0; i < argCount; ++i) { storeArgumentTypeAt(i); } } else visitInsn(ACONST_NULL); } private void storeArgumentTypeAt(int index) { visitInsn(DUP); pushInteger(index); switch (argTypes[index].getSort()) { case Type.BYTE: visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;"); break; case Type.SHORT: visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;"); break; case Type.INT: visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); break; case Type.BOOLEAN: visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); break; case Type.CHAR: visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;"); break; case Type.LONG: visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); break; case Type.FLOAT: visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;"); break; case Type.DOUBLE: visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;"); break; default: visitLdcInsn(argTypes[index]); } visitInsn(AASTORE); } protected void pushArguments() { if (argCount > 0) { pushInteger(argCount); visitTypeInsn(ANEWARRAY, "java/lang/Object"); int localSlot = isStatic ? 0 : 1; for (byte i = 0; i < argCount; ++i) { localSlot = storeArgumentAt(i, localSlot); } } else visitInsn(ACONST_NULL); } private int storeArgumentAt(int index, int localSlot) { visitInsn(Opcodes.DUP); pushInteger(index); switch (argTypes[index].getSort()) { case Type.BYTE: visitVarInsn(ILOAD, localSlot); visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;"); break; case Type.SHORT: visitVarInsn(ILOAD, localSlot); visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;"); break; case Type.INT: visitVarInsn(ILOAD, localSlot); visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); break; case Type.BOOLEAN: visitVarInsn(ILOAD, localSlot); visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;"); break; case Type.CHAR: visitVarInsn(ILOAD, localSlot); visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;"); break; case Type.LONG: visitVarInsn(LLOAD, localSlot); ++localSlot; visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;"); break; case Type.FLOAT: visitVarInsn(FLOAD, localSlot); visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;"); break; case Type.DOUBLE: visitVarInsn(DLOAD, localSlot); ++localSlot; visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;"); break; default: visitVarInsn(ALOAD, localSlot); } visitInsn(AASTORE); return localSlot + 1; } protected int getSlots(Type type) { return frame.getSlots(type); } protected String className; protected int access; protected String methodName; protected int constructorNumber; protected String methodDesc; protected String methodSignature; protected String[] exceptions; protected List<Aspect> matchingAspects; protected boolean isPublic; protected boolean isPrivate; protected boolean isProtected; protected boolean isStatic; protected boolean isFinal; protected boolean isSynchronized; protected boolean isVolatile; protected boolean isTransient; protected boolean isStrict; protected boolean isPackagePrivate; protected Type[] argTypes; protected int argCount; protected int argSlots; protected int[] loadArgOpcodes; protected int[] storeArgOpcodes; protected Type returnType; protected String returnTypeInternalName; protected boolean isVoid; protected boolean isPrimitiveReturn; protected int returnOpcode; protected int loadReturnValueOpcode; protected int storeReturnValueOpcode; protected int returnTypeSlots; protected List<Label> exLabels = new LinkedList<>(); protected List<String> exTypes = new LinkedList<>(); private Frame frame; }