dyco4j.instrumentation.LoggingHelper.java Source code

Java tutorial

Introduction

Here is the source code for dyco4j.instrumentation.LoggingHelper.java

Source

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

package dyco4j.instrumentation;

import dyco4j.logging.Logger;
import dyco4j.logging.LoggerInitializer;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;

import java.text.MessageFormat;
import java.util.OptionalInt;

public class LoggingHelper {
    public static final String UNINITIALIZED_THIS = "<uninitializedThis>";
    private static final String LOGGER;
    private static final String LOGGER_INITIALIZER;
    private static final Method LOGGER_INITIALIZER_INITIALIZE;
    private static final Method LOG_ARGUMENT;
    private static final Method LOG_ARRAY;
    private static final Method LOG_EXCEPTION;
    private static final Method LOG_FIELD;
    private static final Method LOG_METHOD_CALL;
    private static final Method LOG_METHOD_ENTRY;
    private static final Method LOG_METHOD_EXIT;
    private static final Method LOG_RETURN;
    private static final Method LOG_STRING;

    static {
        try {
            LOGGER = Logger.class.getName().replace(".", "/");
            LOG_STRING = Method.getMethod(Logger.class.getMethod("log", String.class));
            LOG_METHOD_ENTRY = Method.getMethod(Logger.class.getMethod("logMethodEntry", String.class));
            LOG_METHOD_EXIT = Method.getMethod(Logger.class.getMethod("logMethodExit", String.class, String.class));
            LOG_ARGUMENT = Method.getMethod(Logger.class.getMethod("logArgument", Byte.TYPE, String.class));
            LOG_RETURN = Method.getMethod(Logger.class.getMethod("logReturn", String.class));
            LOG_METHOD_CALL = Method.getMethod(Logger.class.getMethod("logMethodCall", String.class));
            LOG_FIELD = Method.getMethod(Logger.class.getMethod("logField", Object.class, String.class,
                    String.class, Logger.FieldAction.class));
            LOG_ARRAY = Method.getMethod(Logger.class.getMethod("logArray", Object.class, Integer.TYPE,
                    String.class, Logger.ArrayAction.class));
            LOG_EXCEPTION = Method.getMethod(Logger.class.getMethod("logException", Throwable.class));
            LOGGER_INITIALIZER = LoggerInitializer.class.getName().replace("" + ".", "/");
            LOGGER_INITIALIZER_INITIALIZE = Method.getMethod(LoggerInitializer.class.getMethod("initialize"));
        } catch (final NoSuchMethodException | SecurityException _ex) {
            throw new RuntimeException(_ex);
        }
    }

    private LoggingHelper() {
    }

    public static void emitConvertToString(final MethodVisitor mv, final Type type) {
        switch (type.getSort()) {
        case Type.ARRAY:
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, "toString",
                    "(Ljava/lang/Object;)Ljava/lang/String;" + "", false);
            break;
        case Type.BOOLEAN:
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, "toString", "(Z)Ljava/lang/String;", false);
            break;
        case Type.BYTE:
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, "toString", "(B)Ljava/lang/String;", false);
            break;
        case Type.CHAR:
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, "toString", "(C)Ljava/lang/String;", false);
            break;
        case Type.DOUBLE:
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, "toString", "(D)Ljava/lang/String;", false);
            break;
        case Type.FLOAT:
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, "toString", "(F)Ljava/lang/String;", false);
            break;
        case Type.INT:
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, "toString", "(I)Ljava/lang/String;", false);
            break;
        case Type.LONG:
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, "toString", "(J)Ljava/lang/String;", false);
            break;
        case Type.SHORT:
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, "toString", "(S)Ljava/lang/String;", false);
            break;
        case Type.OBJECT:
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, "toString",
                    "(Ljava/lang/Object;)Ljava/lang/String;" + "", false);
            break;
        default:
            throw new RuntimeException("Unknown type" + type.getInternalName());
        }
    }

    public static int emitLogArgument(final MethodVisitor mv, final int position, final OptionalInt localVarIndex,
            final Type argType) {
        mv.visitLdcInsn(position);

        int _typeLength = 1;
        if (localVarIndex.isPresent()) {
            final int _tmp = localVarIndex.getAsInt();
            switch (argType.getSort()) {
            case Type.BOOLEAN:
            case Type.BYTE:
            case Type.CHAR:
            case Type.INT:
            case Type.SHORT:
                mv.visitVarInsn(Opcodes.ILOAD, _tmp);
                break;
            case Type.LONG:
                mv.visitVarInsn(Opcodes.LLOAD, _tmp);
                _typeLength++;
                break;
            case Type.FLOAT:
                mv.visitVarInsn(Opcodes.FLOAD, _tmp);
                break;
            case Type.DOUBLE:
                mv.visitVarInsn(Opcodes.DLOAD, _tmp);
                _typeLength++;
                break;
            case Type.ARRAY:
            case Type.OBJECT:
                mv.visitVarInsn(Opcodes.ALOAD, _tmp);
                break;
            }
        } else {
            mv.visitLdcInsn(Logger.UNINITIALIZED_THIS);
        }
        emitConvertToString(mv, argType);

        emitInvokeLog(mv, LOG_ARGUMENT);

        return _typeLength;
    }

    public static void emitLogArray(final MethodVisitor mv, final Logger.ArrayAction action) {
        final Type _a = Type.getType(Logger.ArrayAction.class);
        final String _name = action.toString();
        mv.visitFieldInsn(Opcodes.GETSTATIC, _a.getInternalName(), _name, _a.getDescriptor());
        emitInvokeLog(mv, LOG_ARRAY);
    }

    public static void emitLogException(final MethodVisitor mv) {
        mv.visitInsn(Opcodes.DUP);
        LoggingHelper.emitInvokeLog(mv, LoggingHelper.LOG_EXCEPTION);
    }

    public static void emitLogField(final MethodVisitor mv, final String fieldName, final Type fieldType,
            final Logger.FieldAction action) {
        final int _fieldSort = fieldType.getSort();
        if (_fieldSort == Type.LONG || _fieldSort == Type.DOUBLE) {
            mv.visitInsn(Opcodes.DUP2_X1);
        } else {
            mv.visitInsn(Opcodes.DUP_X1);
        }

        emitConvertToString(mv, fieldType);
        mv.visitLdcInsn(fieldName);

        final Type _a = Type.getType(Logger.FieldAction.class);
        final String _name = action.toString();
        mv.visitFieldInsn(Opcodes.GETSTATIC, _a.getInternalName(), _name, _a.getDescriptor());
        emitInvokeLog(mv, LOG_FIELD);
    }

    public static void emitLogMethodCall(final MethodVisitor mv, final String methodId, final int callsiteId) {
        final String _stmt = MessageFormat.format("{0},{1}", methodId, callsiteId);
        mv.visitLdcInsn(_stmt);
        emitInvokeLog(mv, LOG_METHOD_CALL);
    }

    public static void emitLogMethodEntry(final MethodVisitor mv, final String methodId) {
        mv.visitLdcInsn(methodId);
        emitInvokeLog(mv, LOG_METHOD_ENTRY);
    }

    public static void emitLogMethodExit(final MethodVisitor mv, final String methodId, final ExitKind exitKind) {
        mv.visitLdcInsn(methodId);
        mv.visitLdcInsn(exitKind.getAbbreviatedName());
        emitInvokeLog(mv, LOG_METHOD_EXIT);
    }

    public static void emitLogReturn(final MethodVisitor mv, final Type returnType) {
        final int _retSort = returnType.getSort();
        if (_retSort != Type.VOID) {
            if (_retSort == Type.LONG || _retSort == Type.DOUBLE) {
                mv.visitInsn(Opcodes.DUP2);
            } else {
                mv.visitInsn(Opcodes.DUP);
            }
            emitConvertToString(mv, returnType);
            emitInvokeLog(mv, LOG_RETURN);
        }
    }

    public static void emitLogString(final MethodVisitor mv, final String s) {
        mv.visitLdcInsn(s);
        emitInvokeLog(mv, LOG_STRING);
    }

    public static void emitSwapOneWordAndTwoWords(final MethodVisitor mv, final Type tos) {
        if (tos.getSort() == Type.LONG || tos.getSort() == Type.DOUBLE) {
            mv.visitInsn(Opcodes.DUP_X2);
            mv.visitInsn(Opcodes.POP);
        } else {
            mv.visitInsn(Opcodes.SWAP);
        }
    }

    public static void emitSwapTwoWordsAndOneWord(final MethodVisitor mv, final Type tos) {
        if (tos.getSort() == Type.LONG || tos.getSort() == Type.DOUBLE) {
            mv.visitInsn(Opcodes.DUP2_X1);
            mv.visitInsn(Opcodes.POP2);
        } else {
            mv.visitInsn(Opcodes.SWAP);
        }
    }

    static void emitInsnToLoadAndInitializeLogger(final MethodVisitor mv) {
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER_INITIALIZER, LOGGER_INITIALIZER_INITIALIZE.getName(),
                LOGGER_INITIALIZER_INITIALIZE.getDescriptor(), false);
    }

    private static void emitInvokeLog(final MethodVisitor mv, final Method method) {
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, LOGGER, method.getName(), method.getDescriptor(), false);
    }

    public enum ExitKind {
        NORMAL, EXCEPTIONAL;

        public String getAbbreviatedName() {
            return this.name().substring(0, 1);
        }
    }
}