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.FieldExistsException;
import alt.jiapi.reflect.MethodExistsException;
import alt.jiapi.reflect.instruction.FieldAccess;
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 FieldInstrumentor extends EventInstrumentor {
private FieldHandler handler;
private JiapiMethod getMethod;
private JiapiMethod setMethod;
FieldInstrumentor(FieldInterceptor2 ii, FieldHandler handler) {
super(ii);
this.handler = handler;
JiapiClass jc = getEventProducer();
try {
this.getMethod =
jc.getDeclaredMethod("getField",
new String[] { "java.lang.Object",
"java.lang.String"});
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) {
JiapiField interceptor = getEventProducerField();
InstructionList il = jm.getInstructionList();
InstructionFactory factory = il.getInstructionFactory();
HotSpotLocator hsl =
new HotSpotLocator(il,
new byte[]{Opcodes.GETSTATIC/*,Opcodes.PUTSTATIC*/});
HotSpot[] hotSpots = hsl.getHotSpots();
for (int i = 0; i < hotSpots.length; i++) {
FieldAccess fa = (FieldAccess)hotSpots[i].getHotSpotInstruction();
if (fa.getName().startsWith("__jiapi")) {
continue;
}
if (!match(fa.getClassName() + "." + fa.getFieldName())) {
continue;
}
// BUG: We cannot use reflection in this interceptor, since it
// is allowed only for public fields.
if (!isPublicField(fa)) {
continue;
}
InstructionList hsList = hotSpots[i].getInstructionList();
InstructionList nList = il.createEmptyList();
nList.add(factory.getField(interceptor)); // Interceptor
// Class or objref; 1st parameter to interceptor
if ((fa.getOpcode() == Opcodes.GETSTATIC) ||
(fa.getOpcode() == Opcodes.PUTSTATIC)) {
addClassForNameInstructions(fa.getClassName(), nList);
}
else {
nList.add(il.get(0)); // objref
}
// Name of the field; 2nd parameter to interceptor
nList.add(factory.pushConstant(fa.getClassName() + "." +
fa.getFieldName()));
// call Interceptor
if ((fa.getOpcode() == Opcodes.GETSTATIC) ||
(fa.getOpcode() == Opcodes.GETFIELD)) {
nList.add(factory.invoke(getMethod));
}
else {
nList.add(il.get(1)); // value ???
nList.add(factory.invoke(setMethod));
}
handleReturnValue(nList, fa.getTypeName());
//nList.add(factory.cast(fa.getTypeName()));
hsList.replace(nList);
}
}
private void addClassForNameInstructions(String name, InstructionList il) {
InstructionFactory f = il.getInstructionFactory();
InstructionList nl = il.createEmptyList();
// 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"})));
}
catch(Exception e) {
e.printStackTrace();
il.add(f.pushNull());
}
il.add(nl);
}
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));
}
}
}