package rocks.inspectit.agent.java.instrumentation.asm;
import info.novatec.inspectit.org.objectweb.asm.Label;
import info.novatec.inspectit.org.objectweb.asm.MethodVisitor;
import info.novatec.inspectit.org.objectweb.asm.Opcodes;
import info.novatec.inspectit.org.objectweb.asm.commons.AdviceAdapter;
import java.util.HashSet;
import java.util.Set;
import rocks.inspectit.agent.java.hooking.IHookDispatcher;
/**
* Base class for both method instrumenter and constructor instrumenter.
*
* @author Ivan Senic
*
*/
public abstract class AbstractMethodInstrumenter extends AdviceAdapter {
/**
* If method is static or not.
*/
protected boolean isStatic;
/**
* Id of the method. This id will be passed to the dispatcher.
*/
protected long methodId;
/**
* Marker declaring if enhanced exception sensor is active.
*/
protected boolean enhancedExceptionSensor;
/**
* The label for the start of the try/finally or try/catch/finally block that we are adding.
*/
protected Label tryBlockStart = new Label();
/**
* The label for the start of the catch block in try/catch/finally.
*/
protected Label catchHandler = new Label();
/**
* The label for the start of the finally block in try/finally or try/catch/finally.
*/
protected Label finallyHandler = new Label();
/**
* Set of labels that denote the start of catch blocks in the method that are not ours.
*/
private final Set<Label> handlers = new HashSet<Label>(1);
/**
* Default constructor for the method instrumenter. Defines method id that will be used during
* instrumentation and if enhanced exception sensor is active or not.
*
* @param mv
* Super method visitor.
* @param access
* Method access code.
* @param name
* Method name.
* @param desc
* Method description.
* @param methodId
* Method id that will be passed to {@link IHookDispatcher}.
* @param enhancedExceptionSensor
* Marker declaring if enhanced exception sensor is active.
*/
protected AbstractMethodInstrumenter(MethodVisitor mv, int access, String name, String desc, long methodId, boolean enhancedExceptionSensor) {
super(Opcodes.ASM5, mv, access, name, desc);
this.methodId = methodId;
this.enhancedExceptionSensor = enhancedExceptionSensor;
this.isStatic = (access & Opcodes.ACC_STATIC) != 0;
}
/**
* Generates code for before catch call. Calling this method expects an exception on the stack
* that can be consumed.
*/
protected abstract void generateBeforeCatchCall();
/**
* {@inheritDoc}
*/
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
super.visitTryCatchBlock(start, end, handler, type);
// if enhanced sensor is on we must add beforeCatch call to start of each catch block
// thus we are saving labels that denote start of handler blocks
// note we are skipping the finally blocks
// and skipping one handler that denotes our catch block
// comparing references is fine here
if (enhancedExceptionSensor && (handler != catchHandler) && (null != type)) { // NOPMD
handlers.add(handler);
}
}
/**
* {@inheritDoc}
*/
@Override
public void visitLabel(Label label) {
super.visitLabel(label);
// any label is saved to the handlers set we must add call to beforeCatch
// note that these are catch handling blocks so exception is on stack
if (handlers.contains(label)) {
// duplicate the exception on the stack and call
dup();
generateBeforeCatchCall();
}
}
/**
* Loads hook dispatcher on the stack so that methods can be executed on it.
* <p>
* Protected access so we can change in tests.
*/
protected void loadHookDispatcher() {
// load first the Agent.agent static field
mv.visitFieldInsn(Opcodes.GETSTATIC, IInstrumenterConstant.AGENT_INTERNAL_NAME, "agent", IInstrumenterConstant.IAGENT_DESCRIPTOR);
// now invoke getHookDispatcher() method (no parameters here)
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, IInstrumenterConstant.IAGENT_INTERNAL_NAME, "getHookDispatcher", IInstrumenterConstant.GET_IHOOK_DISPATCHER_DESCRIPTOR, true);
}
/**
* Pushes null to stack.
*/
protected void pushNull() {
mv.visitInsn(Opcodes.ACONST_NULL);
}
/**
* Gets {@link #methodId}.
*
* @return {@link #methodId}
*/
public long getMethodId() {
return this.methodId;
}
/**
* Gets {@link #enhancedExceptionSensor}.
*
* @return {@link #enhancedExceptionSensor}
*/
public boolean isEnhancedExceptionSensor() {
return this.enhancedExceptionSensor;
}
}