package alt.jiapi.event;
import java.lang.reflect.Modifier;
import alt.jiapi.Instrumentor;
import alt.jiapi.reflect.Instruction;
import alt.jiapi.reflect.InstructionFactory;
import alt.jiapi.reflect.InstructionList;
import alt.jiapi.reflect.JiapiClass;
import alt.jiapi.reflect.JiapiField;
import alt.jiapi.reflect.JiapiMethod;
import alt.jiapi.reflect.MethodExistsException;
import alt.jiapi.reflect.Signature;
import alt.jiapi.reflect.instruction.OpcodeGroups;
import alt.jiapi.reflect.instruction.Opcodes;
import alt.jiapi.reflect.instruction.FieldAccess;
/**
* MethodEventInstrumentor. This class instruments
* target classes so, that method entries and exits are trapped
* to MethodEventProducer.
*
* @author Mika Riekkinen
*/
class MethodEventInstrumentor extends EventInstrumentor {
MethodEventInstrumentor(MethodEventProducer mep) {
super(mep);
}
public void instrument(JiapiMethod jm) {
InstructionList il = jm.getInstructionList();
InstructionFactory factory = il.getInstructionFactory();
JiapiClass mep = getEventProducer();
JiapiMethod methodEntered = null;
JiapiMethod methodExited = null;
JiapiField jiapiField = getEventProducerField();
// a flag, that tells whether or not InstructionList being
// processed is in a static method.
boolean isStatic = Modifier.isStatic(il.getDeclaringMethod().getModifiers());
try {
methodEntered =
mep.getDeclaredMethod("methodEntered",
new String[] { "java.lang.Object",
"java.lang.String" });
methodExited =
mep.getDeclaredMethod("methodExited",
new String[] { "java.lang.Object",
"java.lang.String" });
}
catch(Exception e) {
System.out.println("ERROR: " + e);
}
// Create instructions for method entrance
InstructionList entryList = il.createEmptyList();
entryList.add(factory.getField(jiapiField));
if (isStatic) {
entryList.add(factory.pushConstant(il.getDeclaringMethod().getDeclaringClass().getName())); // BUG: we should pass a Class
}
else {
entryList.add(factory.pushThis());
}
entryList.add(factory.pushConstant(il.getDeclaringMethod().getName()));
entryList.add(factory.invoke(methodEntered));
// Skip super(....) call, if in <init> method
int superIdx = il.indexOf(Opcodes.INVOKESPECIAL);
// BUG: we should insert entrylist on <clinit> pass the
// __jiapi_field initialization
if (!"<clinit>".equals(il.getDeclaringMethod().getName())) {
// FIXME: See bug above
il.insert(superIdx+1, entryList);
}
else {
int idx = findFieldInitIndex(il, jiapiField);
if (idx != -1) {
il.insert(idx + 1, entryList);
}
}
// Create instructions for method exits
InstructionList exitList = il.createEmptyList();
exitList.add(factory.getField(jiapiField));
if (isStatic) {
exitList.add(factory.pushConstant(il.getDeclaringMethod().getDeclaringClass().getName())); // BUG: we should pass a Class
}
else {
exitList.add(factory.pushThis());
}
exitList.add(factory.pushConstant(il.getDeclaringMethod().getName()));
exitList.add(factory.invoke(methodExited));
// Find all method exits
int idx = il.indexOf(OpcodeGroups.RETURN_INSTRUCTIONS, 0);
while (idx != -1) {
Instruction ins = il.get(idx);
il.insert(idx, exitList);
// Next index. Skip Instructions created above.
idx = il.indexOf(OpcodeGroups.RETURN_INSTRUCTIONS,
idx + exitList.size() + 1);
}
}
// Find event producer initialization on <clinit>
private int findFieldInitIndex(InstructionList il, JiapiField jiapiField) {
int idx = -1;
while ((idx = il.indexOf(Opcodes.PUTSTATIC, idx+1)) != -1) {
FieldAccess fa = (FieldAccess)il.get(idx);
if (fa.getFieldName().equals(jiapiField.getName())) {
break;
}
}
return idx;
}
}