com.google.code.jtracert.instrument.impl.asm.JTracertMethodAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.google.code.jtracert.instrument.impl.asm.JTracertMethodAdapter.java

Source

package com.google.code.jtracert.instrument.impl.asm;

import com.google.code.jtracert.config.InstrumentationProperties;
import com.google.code.jtracert.instrument.ConfigurableTransformer;
import com.google.code.jtracert.util.ClassUtils;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;

import java.util.Queue;
import java.util.LinkedList;

/**
 * Distributed under GNU GENERAL PUBLIC LICENSE Version 3
 *
 * @author Dmitry.Bedrin@gmail.com
 */
public class JTracertMethodAdapter extends AdviceAdapter implements ConfigurableTransformer {

    private final String className;
    private String parentClassName;
    private final String methodName;
    private final boolean isConstructor;

    private InstrumentationProperties instrumentationProperties;

    private final Label startFinallyLabel = new Label();

    /**
     * @param mv
     * @param access
     * @param name
     * @param desc
     * @param className
     */
    public JTracertMethodAdapter(MethodVisitor mv, int access, String name, String desc, String className) {
        super(mv, access, name, desc);
        this.className = className;
        this.methodName = name;
        this.isConstructor = name.equals(ClassUtils.CONSTRUCTOR_METHOD_NAME);
    }

    /**
     * @param mv
     * @param access
     * @param name
     * @param desc
     * @param className
     * @param instrumentationProperties
     * @param parentClassName
     */
    public JTracertMethodAdapter(MethodVisitor mv, int access, String name, String desc, String className,
            InstrumentationProperties instrumentationProperties, String parentClassName) {
        this(mv, access, name, desc, className);
        this.instrumentationProperties = instrumentationProperties;
        this.parentClassName = parentClassName;
    }

    /**
     *
     */
    @Override
    public void visitCode() {
        super.visitCode();
        mv.visitLabel(startFinallyLabel);
    }

    /**
     * @param maxStack
     * @param maxLocals
     */
    @Override
    public void visitMaxs(int maxStack, int maxLocals) {
        Label endFinallyLabel = new Label();
        mv.visitTryCatchBlock(startFinallyLabel, endFinallyLabel, endFinallyLabel, null);
        mv.visitLabel(endFinallyLabel);
        onFinally(ATHROW);
        mv.visitInsn(ATHROW);
        mv.visitMaxs(maxStack, maxLocals);
    }

    /**
     * @param opcode
     * @param owner
     * @param name
     * @param desc
     */
    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc) {

        if (isConstructor && name.equals(ClassUtils.CONSTRUCTOR_METHOD_NAME) && (owner.equals(parentClassName)
                || ClassUtils.getFullyQualifiedName(owner).equals(getClassName()))) {

            super.visitMethodInsn(INVOKESTATIC,
                    "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilderFactory",
                    "getMethodCallTraceBuilder",
                    "()Lcom/google/code/jtracert/traceBuilder/MethodCallTraceBuilder;");

            super.visitLdcInsn(getClassName());
            super.visitLdcInsn(getMethodDescriptor());

            super.visitMethodInsn(INVOKEINTERFACE, "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilder",
                    "preEnterConstructor", "(Ljava/lang/String;Ljava/lang/String;)V");

        }

        super.visitMethodInsn(opcode, owner, name, desc);

        /*if (Opcodes.INVOKESPECIAL == opcode) {
            
        if (!owner.equals(parentClassName) && !ClassUtils.getFullyQualifiedName(owner).equals(getClassName())) {
            
            System.out.println("Instrumenting constructor " + owner + "." + name + getMethodDescriptor() + " inside " + getClassName() + "." + getMethodName());
            
            Integer variable = objects.poll();
            
            if (null != variable) {
            
                super.visitMethodInsn(
                        INVOKESTATIC,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilderFactory",
                        "getMethodCallTraceBuilder",
                        "()Lcom/google/code/jtracert/traceBuilder/MethodCallTraceBuilder;"
                );
            
                super.visitVarInsn(ALOAD, variable);
            
                super.visitMethodInsn(
                        INVOKEINTERFACE,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilder",
                        "newObject",
                        "(Ljava/lang/Object;)V"
                );
            
            } else {
                System.err.println("Variable is null");
            }
            
        }
            
        }*/

    }

    /*private Queue<Integer> objects = new LinkedList<Integer>();
        
    @Override
    public void visitTypeInsn(int i, String s) {
    super.visitTypeInsn(i, s);
    if (Opcodes.NEW == i) {
        
        mv.visitInsn(Opcodes.DUP);
        int objectVar = newLocal(Type.getType(Object.class));
        super.visitVarInsn(ASTORE, objectVar);
        objects.offer(objectVar);
        
        System.out.println("Newly created object is stored in local variable");
    }
    }*/

    /**
     * @param opcode
     */
    private void onFinally(int opcode) {

        if (isConstructor) {

            if (opcode == ATHROW) {

                super.visitInsn(DUP);

                int exceptionVar = newLocal(Type.getType(Throwable.class));
                super.visitVarInsn(ASTORE, exceptionVar);

                super.visitMethodInsn(INVOKESTATIC,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilderFactory",
                        "getMethodCallTraceBuilder",
                        "()Lcom/google/code/jtracert/traceBuilder/MethodCallTraceBuilder;");

                super.visitLdcInsn(getClassName());
                super.visitLdcInsn(getMethodName());
                super.visitLdcInsn(getMethodDescriptor());
                super.visitVarInsn(ALOAD, exceptionVar);

                super.visitMethodInsn(INVOKEINTERFACE,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilder", "leaveConstructor",
                        "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V");

            } else {

                mv.visitMethodInsn(INVOKESTATIC,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilderFactory",
                        "getMethodCallTraceBuilder",
                        "()Lcom/google/code/jtracert/traceBuilder/MethodCallTraceBuilder;");

                mv.visitLdcInsn(getMethodDescriptor());

                mv.visitMethodInsn(INVOKEINTERFACE, "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilder",
                        "leaveConstructor", "(Ljava/lang/String;)V");

            }

        } else {

            if (opcode == ATHROW) {

                super.visitInsn(DUP);

                int exceptionVar = newLocal(Type.getType(Throwable.class));
                super.visitVarInsn(ASTORE, exceptionVar);

                super.visitMethodInsn(INVOKESTATIC,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilderFactory",
                        "getMethodCallTraceBuilder",
                        "()Lcom/google/code/jtracert/traceBuilder/MethodCallTraceBuilder;");

                super.visitVarInsn(ALOAD, exceptionVar);

                super.visitMethodInsn(INVOKEINTERFACE,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilder", "exception",
                        "(Ljava/lang/Throwable;)V");

            } else if (opcode == RETURN) {

                super.visitMethodInsn(INVOKESTATIC,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilderFactory",
                        "getMethodCallTraceBuilder",
                        "()Lcom/google/code/jtracert/traceBuilder/MethodCallTraceBuilder;");

                super.visitMethodInsn(INVOKEINTERFACE,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilder", "leave", "()V");

            } else {

                if (opcode == LRETURN || opcode == DRETURN) {
                    super.visitInsn(DUP2);
                } else {
                    super.visitInsn(DUP);
                }
                box(Type.getReturnType(this.methodDesc));

                int returnVar = newLocal(Type.getType(Throwable.class));
                super.visitVarInsn(ASTORE, returnVar);

                super.visitMethodInsn(INVOKESTATIC,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilderFactory",
                        "getMethodCallTraceBuilder",
                        "()Lcom/google/code/jtracert/traceBuilder/MethodCallTraceBuilder;");

                super.visitVarInsn(ALOAD, returnVar);

                super.visitMethodInsn(INVOKEINTERFACE,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilder", "leave",
                        "(Ljava/lang/Object;)V");

            }

        }

    }

    /**
     * @param opcode
     */
    @Override
    protected void onMethodExit(int opcode) {
        if (opcode != ATHROW) {
            onFinally(opcode);
        }
    }

    /**
     *
     */
    @Override
    protected void onMethodEnter() {
        try {

            if ((ACC_STATIC & methodAccess) == 0) {

                int argIndex = generateArgumentsArray(1);

                super.visitMethodInsn(INVOKESTATIC,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilderFactory",
                        "getMethodCallTraceBuilder",
                        "()Lcom/google/code/jtracert/traceBuilder/MethodCallTraceBuilder;");

                super.visitLdcInsn(getClassName());
                super.visitLdcInsn(getMethodName());
                super.visitLdcInsn(getMethodDescriptor());
                super.visitVarInsn(ALOAD, 0);
                super.visitVarInsn(ALOAD, argIndex);

                super.visitMethodInsn(INVOKEINTERFACE,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilder", "enter",
                        "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V");

            } else {

                int argIndex = generateArgumentsArray(0);

                super.visitMethodInsn(INVOKESTATIC,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilderFactory",
                        "getMethodCallTraceBuilder",
                        "()Lcom/google/code/jtracert/traceBuilder/MethodCallTraceBuilder;");

                super.visitLdcInsn(getClassName());
                super.visitLdcInsn(getMethodName());
                super.visitLdcInsn(getMethodDescriptor());
                super.visitInsn(ACONST_NULL);
                super.visitVarInsn(ALOAD, argIndex);

                super.visitMethodInsn(INVOKEINTERFACE,
                        "com/google/code/jtracert/traceBuilder/MethodCallTraceBuilder", "enter",
                        "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V");
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }

    }

    /**
     * @param argIndex
     * @return
     */
    private int generateArgumentsArray(int argIndex) {

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

        super.visitIntInsn(BIPUSH, argumentTypes.length);
        super.visitTypeInsn(ANEWARRAY, "java/lang/Object");

        for (int i = 0; i < argumentTypes.length; i++) {
            Type argumentType = argumentTypes[i];

            super.visitInsn(DUP);
            super.visitIntInsn(BIPUSH, i);
            super.visitVarInsn(argumentType.getOpcode(ILOAD), argIndex);

            // boxing start

            switch (argumentType.getSort()) {
            case Type.BYTE:
                super.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
                break;
            case Type.BOOLEAN:
                super.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
                break;
            case Type.SHORT:
                super.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
                break;
            case Type.CHAR:
                super.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
                break;
            case Type.INT:
                super.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
                break;
            case Type.FLOAT:
                super.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
                break;
            case Type.LONG:
                super.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
                break;
            case Type.DOUBLE:
                super.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
                break;
            }

            // boxing end

            super.visitInsn(AASTORE);

            argIndex += argumentType.getSize();
        }

        super.visitVarInsn(ASTORE, argIndex);
        return argIndex;
    }

    /**
     * @return
     */
    private String getClassName() {
        return this.className;
    }

    /**
     * @return
     */
    private String getMethodName() {
        return this.methodName;
    }

    /**
     * @return
     */
    private String getMethodDescriptor() {
        return super.methodDesc;
    }

    /**
     * @return
     */
    private int getMethodAccess() {
        return super.methodAccess;
    }

    /**
     * @return
     */
    public InstrumentationProperties getInstrumentationProperties() {
        return instrumentationProperties;
    }

    /**
     * @param instrumentationProperties
     */
    public void setInstrumentationProperties(InstrumentationProperties instrumentationProperties) {
        this.instrumentationProperties = instrumentationProperties;
    }

}