package alt.jiapi.interceptor;
import java.util.List;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import alt.jiapi.InstrumentationException;
import alt.jiapi.event.EventInstrumentor;
import alt.jiapi.event.EventRuntime;
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.Loader;
import alt.jiapi.reflect.Signature;
import alt.jiapi.reflect.SignatureUtil;
import alt.jiapi.reflect.FieldExistsException;
import alt.jiapi.reflect.MethodExistsException;
import alt.jiapi.reflect.instruction.FieldAccess;
import alt.jiapi.reflect.instruction.Invocation;
import alt.jiapi.reflect.instruction.OpcodeGroups;
import alt.jiapi.reflect.instruction.Opcodes;
import alt.jiapi.util.HotSpot;
import alt.jiapi.util.HotSpotLocator;
/**
* Class FieldInstrumentor.
*
* @author Mika Riekkinen
*/
class FieldAdvisorInstrumentor extends EventInstrumentor {
private AccessAdvisor advisor;
private JiapiMethod getMethod;
private JiapiMethod setMethod;
FieldAdvisorInstrumentor(FieldInterceptor fi, AccessAdvisor advisor) {
super(fi);
this.advisor = advisor;
JiapiClass jc = getEventProducer();
try {
this.getMethod =
jc.getDeclaredMethod("getField",
new String[] { "java.lang.Object",
"java.lang.String",
"java.lang.Object"});
this.setMethod =
jc.getDeclaredMethod("setField",
new String[] { "java.lang.Object",
"java.lang.String",
"java.lang.Object"});
}
catch(Exception e) {
e.printStackTrace();
}
}
public void instrument(JiapiMethod jm) {
JiapiClass clazz = jm.getDeclaringClass();
JiapiField interceptor = getEventProducerField();
InstructionList il = jm.getInstructionList();
InstructionFactory factory = il.getInstructionFactory();
HotSpotLocator hsl =
new HotSpotLocator(il, new byte[]{Opcodes.GETSTATIC,
Opcodes.GETFIELD});
HotSpot[] hotSpots = hsl.getHotSpots();
for (int i = 0; i < hotSpots.length; i++) {
FieldAccess fa = (FieldAccess)hotSpots[i].getHotSpotInstruction();
short opCode = fa.getOpcode();
if (!match(fa.getClassName() + "." + fa.getFieldName())) {
continue;
}
InstructionList hsList = hotSpots[i].getInstructionList();
InstructionList nList = il.createEmptyList();
boolean primitive = SignatureUtil.isPrimitive(fa.getTypeName());
if (opCode == Opcodes.GETSTATIC ||
opCode == Opcodes.GETFIELD) {
nList.add(factory.getField(interceptor)); // Interceptor
// NOTE: We could store Class into variable,
// and use getField() instead of Class.forName() call
// Class or objref; 1st parameter to interceptor
if (opCode == Opcodes.GETSTATIC) {
short lvIdx =
addClassForNameInstructions(fa.getClassName(), jm);
nList.add(factory.aload(lvIdx));
// addClassForNameInstructions(fa.getClassName(), nList);
// nList.add(factory.pushNull());
}
else {
//addClassForNameInstructions(fa.getClassName(), nList);
nList.add(hsList.get(0)); // objref
}
// Name of the field; 2nd parameter to interceptor
nList.add(factory.pushConstant(fa.getFieldName()));
// 3rd parameter; field value
Instruction pIns = null;
if (primitive) {
// Provide wrapper for primitive types
pIns = handlePrimitiveType(fa.getTypeName(), nList);
}
//nList.add(fa);
//nList.add(hsList);
nList.add(hotSpots[i].getInstructionList());
if (pIns != null) {
nList.add(pIns);
}
// call Interceptor
nList.add(factory.invoke(getMethod));
handleReturnValue(nList, fa.getTypeName());
//nList.add(factory.cast(fa.getTypeName()));
hotSpots[i].getInstructionList()/* hsList*/.replace(nList);
}
}
}
/**
* Add Class.forName(name) instructions in fhe beginning of method.
* Class is stored in next available local-variable
* We could also store this into class-variable, and initialize
* values in <clinit>
*
* @return index of the local variable, where Class is stored
*/
private short addClassForNameInstructions(String name, JiapiMethod jm) {
InstructionList il = jm.getInstructionList();
InstructionFactory f = il.getInstructionFactory();
InstructionList nl = il.createEmptyList();
short maxLocals = (short)jm.getMaxLocals();
// NOTE: We do not create exception handlers for
// Class.forName(...) invocation.
// However, we use this to get Class of the running object,
// so its Class is allways found.
try {
nl.add(f.pushConstant(name));
nl.add(f.invoke(Modifier.STATIC, "java.lang.Class",
"forName", new Signature("java.lang.Class",
new String[] {"java.lang.String"})));
nl.add(f.astore(maxLocals));
}
catch(Exception e) {
e.printStackTrace();
il.add(f.pushNull());
}
// il.add(nl);
il.insert(0, nl);
return maxLocals;
}
private boolean isPublicField(FieldAccess fa) {
try {
JiapiClass jc = new Loader().loadClass(fa.getClassName());
JiapiField jf = jc.getField(fa.getName());
return Modifier.isPublic(jf.getModifiers());
}
catch(Exception e) {
e.printStackTrace();
}
return false;
}
private void handleReturnValue(InstructionList il, String rType) {
// Convert return value if needed.
InstructionFactory factory = il.getInstructionFactory();
if ("int".equals(rType)) {
try {
JiapiClass jc = new Loader().loadClass("java.lang.Integer");
JiapiMethod jm =
jc.getDeclaredMethod("intValue", new String[0]);
il.add(factory.cast("java.lang.Integer"));
il.add(factory.invoke(jm));
}
catch(Exception e) {
e.printStackTrace();
}
}
else if ("long".equals(rType)) {
try {
JiapiClass jc = new Loader().loadClass("java.lang.Long");
JiapiMethod jm =
jc.getDeclaredMethod("longValue", new String[0]);
il.add(factory.cast("java.lang.Long"));
il.add(factory.invoke(jm));
}
catch(Exception e) {
e.printStackTrace();
}
}
else if ("char".equals(rType)) {
try {
JiapiClass jc = new Loader().loadClass("java.lang.Character");
JiapiMethod jm =
jc.getDeclaredMethod("charValue", new String[0]);
il.add(factory.cast("java.lang.Character"));
il.add(factory.invoke(jm));
}
catch(Exception e) {
e.printStackTrace();
}
}
else if ("boolean".equals(rType)) {
try {
JiapiClass jc = new Loader().loadClass("java.lang.Boolean");
JiapiMethod jm =
jc.getDeclaredMethod("booleanValue", new String[0]);
il.add(factory.cast("java.lang.Boolean"));
il.add(factory.invoke(jm));
}
catch(Exception e) {
e.printStackTrace();
}
}
else if ("byte".equals(rType)) {
try {
JiapiClass jc = new Loader().loadClass("java.lang.Byte");
JiapiMethod jm =
jc.getDeclaredMethod("byteValue", new String[0]);
il.add(factory.cast("java.lang.Byte"));
il.add(factory.invoke(jm));
}
catch(Exception e) {
e.printStackTrace();
}
}
else if ("float".equals(rType)) {
try {
JiapiClass jc = new Loader().loadClass("java.lang.Float");
JiapiMethod jm =
jc.getDeclaredMethod("floatValue", new String[0]);
il.add(factory.cast("java.lang.Float"));
il.add(factory.invoke(jm));
}
catch(Exception e) {
e.printStackTrace();
}
}
else if ("double".equals(rType)) {
try {
JiapiClass jc = new Loader().loadClass("java.lang.Double");
JiapiMethod jm =
jc.getDeclaredMethod("doubleValue", new String[0]);
il.add(factory.cast("java.lang.Double"));
il.add(factory.invoke(jm));
}
catch(Exception e) {
e.printStackTrace();
}
}
else if ("void".equals(rType)){
// Pop out the return value(probably null) of
// the invocation handler if it was a 'void' method
il.add(new Instruction(new byte[]{Opcodes.POP}));
}
else { // Cast to correct Object
il.add(factory.cast(rType));
}
}
/**
* @return an Invocation to constructor of primitive wrapper
*/
private Instruction handlePrimitiveType(String type, InstructionList il) {
InstructionFactory f = il.getInstructionFactory();
String cName = null;
Signature s = new Signature("void", new String[]{ type });
if ("int".equals(type)) {
cName = "java.lang.Integer";
}
else if ("long".equals(type)) {
cName = "java.lang.Long";
}
else if ("char".equals(type)) {
cName = "java.lang.Character";
}
else if ("boolean".equals(type)) {
cName = "java.lang.Boolean";
}
else if ("byte".equals(type)) {
cName = "java.lang.Byte";
}
else if ("float".equals(type)) {
cName = "java.lang.Float";
}
else if ("double".equals(type)) {
cName = "java.lang.Double";
}
il.add(f.newClass(cName));
il.add(f.dup());
Instruction ins = f.invoke(Modifier.PUBLIC, cName, "<init>", s);
return ins;
}
}