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

Java tutorial

Introduction

Here is the source code for org.summer.aop.ltw.AuxiliaryWeaver.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.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;
}