package ex11;

import ch.usi.dag.disl.annotation.Before;
import ch.usi.dag.disl.annotation.ThreadLocal;
import ch.usi.dag.disl.marker.BodyMarker;
import ch.usi.dag.disl.marker.BytecodeMarker;
import ch.usi.dag.disl.staticcontext.InstructionStaticContext;
import ch.usi.dag.disl.staticcontext.InvocationStaticContext;
import ch.usi.dag.disl.staticcontext.MethodStaticContext;

public class Instrumentation {

    @ThreadLocal
    private static int opCode;

    @ThreadLocal
    private static int index;

    @ThreadLocal
    private static String callee;

    @ThreadLocal
    private static String caller;

    @ThreadLocal
    private static boolean initialized;

    @Before(marker = BytecodeMarker.class, args = "invokevirtual, invokeinterface")
    static void atObjectRefCallSite(final InstructionStaticContext isc,
                                    final MethodStaticContext msc,
                                    final InvocationStaticContext ivc) {
        opCode = isc.getOpcode();
        index = isc.getIndex();
        caller = msc.thisMethodFullName().concat(msc.thisMethodDescriptor());
        callee = ivc.getInternalName().concat(ivc.getDescriptor());
        initialized = true;
    }

    @Before(marker = BytecodeMarker.class, args = "invokedynamic")
    static void atObjectRefCallSiteDynamic(final InstructionStaticContext isc,
                                           final MethodStaticContext msc) {
        opCode = isc.getOpcode();
        index = isc.getIndex();
        caller = msc.thisMethodFullName().concat(msc.thisMethodDescriptor());
        callee = "[UNDETERMINED]";
        initialized = true;
    }

    @Before(marker = BodyMarker.class, guard = IsDynamicTargetGuard.class)
    static void beforeMethod(MethodStaticContext msc) {
        // we can ignore here constructors and private methods since we know they must be invoked with "invokespecial"
        // and static methods since they are called with "invokestatic" which performs static binding.
        // Instrumenting the constructor of java.lang.Object would actually cause a segmentation fault of the JVM.
        if (initialized) {
            Profiler.registerCall(new CallInfo(opCode, index, caller, callee, msc.getUniqueInternalName()));
            initialized = false;
        }
    }

    @Before(marker = BytecodeMarker.class, args = "invokestatic, invokespecial")
    static void atStaticCallSite(final InstructionStaticContext isc,
                                 final MethodStaticContext msc,
                                 final InvocationStaticContext ivc) {
        final String v = ivc.getInternalName().concat(ivc.getDescriptor());
        Profiler.registerCall(new CallInfo(
                isc.getOpcode(),
                isc.getIndex(),
                msc.thisMethodFullName().concat(msc.thisMethodDescriptor()),
                v,
                v
        ));
    }
}
