dyco4j.instrumentation.internals.TracingMethodVisitor.java Source code

Java tutorial

Introduction

Here is the source code for dyco4j.instrumentation.internals.TracingMethodVisitor.java

Source

/*
 * Copyright (c) 2016, Venkatesh-Prasad Ranganath
 *
 * BSD 3-clause License
 *
 * Author: Venkatesh-Prasad Ranganath (rvprasad)
 */

package dyco4j.instrumentation.internals;

import dyco4j.instrumentation.LoggingHelper;
import dyco4j.logging.Logger;
import dyco4j.utility.ClassNameHelper;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.Method;

import java.util.HashMap;
import java.util.Map;
import java.util.OptionalInt;

final class TracingMethodVisitor extends MethodVisitor {
    private final String methodId;
    private final Method method;
    private final boolean isStatic;
    private final TracingClassVisitor cv;
    private final Map<Label, Label> beginLabel2endLabel;
    private int callsiteId;
    private boolean thisInitialized;
    private Label outermostExceptionHandlerBeginLabel;

    TracingMethodVisitor(final int access, final String name, final String desc, final MethodVisitor mv,
            final TracingClassVisitor owner, final boolean thisInitialized) {
        super(CLI.ASM_VERSION, mv);
        this.method = new Method(name, desc);
        this.isStatic = (access & Opcodes.ACC_STATIC) != 0;
        this.methodId = owner.getMethodId(name, desc);
        this.cv = owner;
        this.thisInitialized = thisInitialized;
        this.beginLabel2endLabel = new HashMap<>();
    }

    @Override
    public void visitCode() {
        beginOutermostExceptionHandler();
        emitLogMethodEntry();
        emitLogMethodArguments();
    }

    @Override
    public final void visitInsn(final int opcode) {
        if (opcode == Opcodes.IRETURN || opcode == Opcodes.LRETURN || opcode == Opcodes.FRETURN
                || opcode == Opcodes.DRETURN || opcode == Opcodes.ARETURN || opcode == Opcodes.RETURN) {
            if (this.cv.cmdLineOptions.traceMethodRetValue)
                LoggingHelper.emitLogReturn(this.mv, method.getReturnType());
            LoggingHelper.emitLogMethodExit(this.mv, this.methodId, LoggingHelper.ExitKind.NORMAL);
            super.visitInsn(opcode);
        } else if (opcode == Opcodes.AASTORE || opcode == Opcodes.BASTORE || opcode == Opcodes.CASTORE
                || opcode == Opcodes.DASTORE || opcode == Opcodes.FASTORE || opcode == Opcodes.IASTORE
                || opcode == Opcodes.LASTORE || opcode == Opcodes.SASTORE) {
            if (this.cv.cmdLineOptions.traceArrayAccess)
                visitArrayStoreInsn(opcode);
            else
                super.visitInsn(opcode);
        } else if (opcode == Opcodes.AALOAD || opcode == Opcodes.BALOAD || opcode == Opcodes.CALOAD
                || opcode == Opcodes.DALOAD || opcode == Opcodes.FALOAD || opcode == Opcodes.IALOAD
                || opcode == Opcodes.LALOAD || opcode == Opcodes.SALOAD) {
            if (this.cv.cmdLineOptions.traceArrayAccess)
                visitArrayLoadInsn(opcode);
            else
                super.visitInsn(opcode);
        } else
            super.visitInsn(opcode);
    }

    @Override
    public final void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
        if (this.cv.cmdLineOptions.traceFieldAccess) {
            final Type _fieldType = Type.getType(desc);
            final String _fieldId = this.cv.getFieldId(name, owner, desc);
            final boolean _isFieldStatic = opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC;
            if (opcode == Opcodes.GETSTATIC || opcode == Opcodes.GETFIELD) {
                if (_isFieldStatic)
                    super.visitInsn(Opcodes.ACONST_NULL);
                else if (this.thisInitialized)
                    super.visitInsn(Opcodes.DUP);
                else {
                    super.visitLdcInsn(LoggingHelper.UNINITIALIZED_THIS);
                    super.visitInsn(Opcodes.SWAP);
                }

                super.visitFieldInsn(opcode, owner, name, desc);
                LoggingHelper.emitLogField(this.mv, _fieldId, _fieldType, Logger.FieldAction.GETF);
            } else if (opcode == Opcodes.PUTSTATIC || opcode == Opcodes.PUTFIELD) {
                if (_isFieldStatic) {
                    super.visitInsn(Opcodes.ACONST_NULL);
                } else if (this.thisInitialized) {
                    LoggingHelper.emitSwapTwoWordsAndOneWord(this.mv, _fieldType);
                    final int _fieldSort = _fieldType.getSort();
                    if (_fieldSort == Type.LONG || _fieldSort == Type.DOUBLE)
                        super.visitInsn(Opcodes.DUP_X2);
                    else
                        super.visitInsn(Opcodes.DUP_X1);
                } else {
                    super.visitLdcInsn(LoggingHelper.UNINITIALIZED_THIS);
                }

                LoggingHelper.emitSwapOneWordAndTwoWords(this.mv, _fieldType);
                LoggingHelper.emitLogField(this.mv, _fieldId, _fieldType, Logger.FieldAction.PUTF);
                super.visitFieldInsn(opcode, owner, name, desc);
            }
        } else
            super.visitFieldInsn(opcode, owner, name, desc);
    }

    @Override
    public final void visitMaxs(final int maxStack, final int maxLocals) {
        endOutermostExceptionHandler();
        for (final Map.Entry<Label, Label> _e : beginLabel2endLabel.entrySet()) {
            final Label _handlerLabel = new Label();
            super.visitLabel(_handlerLabel);
            super.visitTryCatchBlock(_e.getKey(), _e.getValue(), _handlerLabel, "java/lang/Throwable");
            LoggingHelper.emitLogException(this.mv);
            LoggingHelper.emitLogMethodExit(this.mv, this.methodId, LoggingHelper.ExitKind.EXCEPTIONAL);
            super.visitInsn(Opcodes.ATHROW);
        }
        super.visitMaxs(maxStack, maxLocals);
    }

    @Override
    public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc,
            final boolean itf) {
        if (this.cv.cmdLineOptions.traceMethodCall)
            LoggingHelper.emitLogMethodCall(this.mv, this.cv.getMethodId(name, owner, desc), this.callsiteId++);
        super.visitMethodInsn(opcode, owner, name, desc, itf);
    }

    @Override
    public void visitInvokeDynamicInsn(final String name, final String desc, final Handle bsm,
            final Object... bsmArgs) {
        if (this.cv.cmdLineOptions.traceMethodCall)
            LoggingHelper.emitLogMethodCall(this.mv,
                    this.cv.getMethodId(name, ClassNameHelper.DYNAMIC_METHOD_OWNER, desc), this.callsiteId++);
        super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
    }

    void setThisInitialized() {
        this.thisInitialized = true;
    }

    void beginOutermostExceptionHandler() {
        this.outermostExceptionHandlerBeginLabel = new Label();
        super.visitLabel(this.outermostExceptionHandlerBeginLabel);
    }

    void endOutermostExceptionHandler() {
        assert this.outermostExceptionHandlerBeginLabel != null;
        final Label _l = new Label();
        super.visitLabel(_l);
        this.beginLabel2endLabel.put(this.outermostExceptionHandlerBeginLabel, _l);
        this.outermostExceptionHandlerBeginLabel = null;
    }

    private void emitLogMethodEntry() {
        super.visitCode();
        LoggingHelper.emitLogMethodEntry(this.mv, this.methodId);
    }

    private void emitLogMethodArguments() {
        if (this.cv.cmdLineOptions.traceMethodArgs) {
            // emit code to trace each arg
            int _position = 0;
            int _localVarIndex = 0;
            if (!this.isStatic) {
                final OptionalInt _tmp1 = this.thisInitialized ? OptionalInt.of(_localVarIndex)
                        : OptionalInt.empty();
                _localVarIndex += LoggingHelper.emitLogArgument(this.mv, _position, _tmp1,
                        Type.getType(Object.class));
                _position++;
            }

            for (final Type _argType : this.method.getArgumentTypes()) {
                _localVarIndex += LoggingHelper.emitLogArgument(this.mv, _position, OptionalInt.of(_localVarIndex),
                        _argType);
                _position++;
            }
        }
    }

    private void visitArrayStoreInsn(final int opcode) {
        if (opcode == Opcodes.LASTORE || opcode == Opcodes.DASTORE) {
            super.visitInsn(Opcodes.DUP2_X2);
            super.visitInsn(Opcodes.POP2);
            super.visitInsn(Opcodes.DUP2_X2);
            super.visitInsn(Opcodes.DUP2_X2);
            super.visitInsn(Opcodes.POP2);
            super.visitInsn(Opcodes.DUP2_X2);
        } else {
            super.visitInsn(Opcodes.DUP_X2);
            super.visitInsn(Opcodes.POP);
            super.visitInsn(Opcodes.DUP2_X1);
            super.visitInsn(Opcodes.DUP2_X1);
            super.visitInsn(Opcodes.POP2);
            super.visitInsn(Opcodes.DUP_X2);
        }

        switch (opcode) {
        case Opcodes.AASTORE:
            LoggingHelper.emitConvertToString(this.mv, Type.getObjectType("java/lang/Object"));
            break;
        case Opcodes.BASTORE:
            LoggingHelper.emitConvertToString(this.mv, Type.BYTE_TYPE);
            break;
        case Opcodes.CASTORE:
            LoggingHelper.emitConvertToString(this.mv, Type.CHAR_TYPE);
            break;
        case Opcodes.FASTORE:
            LoggingHelper.emitConvertToString(this.mv, Type.FLOAT_TYPE);
            break;
        case Opcodes.IASTORE:
            LoggingHelper.emitConvertToString(this.mv, Type.INT_TYPE);
            break;
        case Opcodes.SASTORE:
            LoggingHelper.emitConvertToString(this.mv, Type.SHORT_TYPE);
            break;
        case Opcodes.DASTORE:
            LoggingHelper.emitConvertToString(this.mv, Type.DOUBLE_TYPE);
            break;
        case Opcodes.LASTORE:
            LoggingHelper.emitConvertToString(this.mv, Type.LONG_TYPE);
            break;
        }

        LoggingHelper.emitLogArray(this.mv, Logger.ArrayAction.PUTA);

        super.visitInsn(opcode);
    }

    private void visitArrayLoadInsn(final int opcode) {
        super.visitInsn(Opcodes.DUP2);

        super.visitInsn(opcode);

        if (opcode == Opcodes.LALOAD || opcode == Opcodes.DALOAD)
            super.visitInsn(Opcodes.DUP2_X2);
        else
            super.visitInsn(Opcodes.DUP_X2);

        switch (opcode) {
        case Opcodes.AALOAD:
            LoggingHelper.emitConvertToString(this.mv, Type.getObjectType("java/lang/Object"));
            break;
        case Opcodes.BALOAD:
            LoggingHelper.emitConvertToString(this.mv, Type.BYTE_TYPE);
            break;
        case Opcodes.CALOAD:
            LoggingHelper.emitConvertToString(this.mv, Type.CHAR_TYPE);
            break;
        case Opcodes.FALOAD:
            LoggingHelper.emitConvertToString(this.mv, Type.FLOAT_TYPE);
            break;
        case Opcodes.IALOAD:
            LoggingHelper.emitConvertToString(this.mv, Type.INT_TYPE);
            break;
        case Opcodes.SALOAD:
            LoggingHelper.emitConvertToString(this.mv, Type.SHORT_TYPE);
            break;
        case Opcodes.DALOAD:
            LoggingHelper.emitConvertToString(this.mv, Type.DOUBLE_TYPE);
            break;
        case Opcodes.LALOAD:
            LoggingHelper.emitConvertToString(this.mv, Type.LONG_TYPE);
            break;
        }

        LoggingHelper.emitLogArray(this.mv, Logger.ArrayAction.GETA);
    }

}