Package alt.jiapi.interceptor

Source Code of alt.jiapi.interceptor.InvocationInstrumentor

package alt.jiapi.interceptor;

import java.util.List;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
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.Invocation;
import alt.jiapi.reflect.instruction.OpcodeGroups;
import alt.jiapi.reflect.instruction.Opcodes;

import alt.jiapi.instrumentor.HotSpot;

import org.apache.log4j.Category;

/**
* Class InvocationInstrumentor.
*
* @author Mika Riekkinen
*/
class InvocationInstrumentor extends EventInstrumentor {
    private static Category log = Category.getInstance(InvocationInstrumentor.class);

    private InvocationHandler handler;

    InvocationInstrumentor(InvocationInterceptor ii, InvocationHandler handler) {
        super(ii);
        this.handler = handler;
    }

    public void instrument(JiapiMethod jm) {
        InstructionList il = jm.getInstructionList();

        if ("<clinit>".equals(jm.getName())) {
            return;
        }

        InstructionFactory factory = il.getInstructionFactory();
        JiapiClass ii = getEventProducer();
        JiapiMethod invokeMethod = null;

        try {
            invokeMethod =
                ii.getDeclaredMethod("invokeMethod",
                                     new String[] { "java.lang.Object",
                                                    "java.lang.String",
                                                    "java.lang.Object[]",
                                                    "java.lang.String"});
        }
        catch(Exception e) {
            e.printStackTrace();
        }


        int idx = -1;
        while ((idx = il.indexOf(OpcodeGroups.INVOKE_INSTRUCTIONS, idx+1)) != -1) {
            Invocation ins = (Invocation)il.get(idx);
            if (!match(ins.getClassName() + "." + ins.getMethodName())) {
                continue;
            }


//             if (System.getProperty("no-lfix") == null) {
//                 // --- bug workaround  for long/doubles in method params
//                 boolean bailOutForLongDoubleSyndrome = false;
//                 String[] i_params = ins.getParameterTypes();
//                 for (int i = 0; i < i_params.length; i++) {
//                     if ("double".equals(i_params[i]) ||
//                         "long".equals(i_params[i])) {
//                         bailOutForLongDoubleSyndrome = true;
//                         break;
//                     }
//                 }
//                 if (bailOutForLongDoubleSyndrome) {
//                     log.warn("Will not instrument invocations to methods with long or double as parameter: In " + jm.getDeclaringClass().getName() + "#" + jm +
//                              ", a call to " + ins + ". This is a workaround for bug in jiapi.");
//                     continue;
//                 }
//                 // --- bug workaround  for long/doubles in method params
//             }

            // We support only these methods at the moment.
            if (ins.getOpcode() != Opcodes.INVOKESTATIC &&
                ins.getOpcode() != Opcodes.INVOKEVIRTUAL) {
                continue;
            }

            JiapiField interceptor = getEventProducerField();
       
//              InstructionList nList = il.createEmptyList();
            InstructionList nList = new InstructionList();


            // each entry in pList holds creation of one argument
            // to method invocation
//             InstructionList[] pLists = createArgumentLists(il, idx);
            ArgumentList al = createArgumentLists(il, idx);
            InstructionList[] pLists = al.arguments;
            int paramIdx = al.paramIndex;

            short opCode = ins.getOpcode();
            if (opCode == Opcodes.INVOKEVIRTUAL ||
                opCode == Opcodes.INVOKESPECIAL) {
                paramIdx--; // Include object ref
            }

            // Generate code, that replaces invocation with a new
            // invocation to InvokeHandler
            switch(opCode) {
            case Opcodes.INVOKEVIRTUAL:
            case Opcodes.INVOKESTATIC:

                nList.add(factory.getField(interceptor)); // Interceptor
                if (opCode == Opcodes.INVOKESTATIC) {
//                     addClassForNameInstructions(jm.getDeclaringClass().getName(), nList);
                    addClassForNameInstructions(ins.getClassName(), nList);
                   
                }
                else {
                    nList.add(il.get(paramIdx)); // objref
                }

                String mName = ins.getMethodName();
                //nList.add(factory.getField(rField));
                nList.add(factory.pushConstant(mName));
                nList.add(factory.newArray("java.lang.Object",
                                           ins.getParameterTypes().length));

                String[] i_params = ins.getParameterTypes();
                // Populate Object array with call parameters
                for (int i = 0; i < pLists.length; i++) {
                    if ("long".equals(i_params[i])) {
                        nList.add(factory.dup());
                    }
                    else if ("double".equals(i_params[i])) {
                        nList.add(factory.dup());
                    }
                    else {
                        nList.add(factory.dup());
                    }
                    nList.add(factory.pushConstant(i));
                    nList.add(pLists[i]);
                    nList.add(factory.aastore());
                }

                // Add Methods signature
//                 nList.add(factory.pushConstant(ins.getDescriptor()));
                // Add cache-key
                nList.add(factory.pushConstant(ins.getClassName() + ins.getMethodName() + ins.getDescriptor()));

                // call Interceptor
                nList.add(factory.invoke(invokeMethod));

                handleReturnValue(nList, ins);


                // Replace invocation and its parameters with new
                // instruction list
                il.replace(paramIdx, idx + 1, nList);

                break;
            }

            // Next index. Skip Instructions created above.
            //idx += nList.size() - (idx - paramIdx) - 1;
            idx = paramIdx + nList.size() - 1;
        }
    }




    /**
     * Create an array of InstructionLists. Each element in array
     * represents one argument of a method invocation.
     */
    private ArgumentList createArgumentLists(InstructionList il, int invocationIndex) {
        Invocation ins = (Invocation)il.get(invocationIndex);

//          System.out.println("Creating arg list for " + ins);

        String[] paramTypes = ins.getParameterTypes();
        InstructionList[] argList =
            new InstructionList[paramTypes.length];

        // First pIdx points to Instruction just before Invocation
        int pIdx = invocationIndex - 1;

//         for (int i = 0; i < argList.length; i++) {
         for (int i = argList.length - 1; i >= 0; i--) {
            int stackUsage = ins.stackConsumption();
            InstructionList pList = il.createEmptyList();

            boolean primitive = SignatureUtil.isPrimitive(paramTypes[i]);
            Instruction pr_ins = null;

            if (primitive) {
                pr_ins = handlePrimitiveType(paramTypes[i], pList);
            }

            int insertIdx = pList.size();
            // When stack usage is 1, we have reached an argument in stack
//             while (stackUsage != 1) {
             while ((stackUsage > 0 && ins.getOpcode() == Opcodes.INVOKESTATIC)||
                    (stackUsage > 1 && ins.getOpcode() == Opcodes.INVOKEVIRTUAL)) {
                Instruction pIns = il.get(pIdx);
//                  System.out.println(">> pIdx: " + pIdx + ": " + pIns +
//                                     ", stackusage: " + stackUsage);
                stackUsage -= pIns.stackUsage();

                // Insert pIns allways to same index. We are scanning
                // Instructions backwards!
//                  System.out.println("Adding " + pIns + " to plist");
                pList.insert(insertIdx, pIns);
                pIdx--;
            }

            if (primitive) {
                pList.add(pr_ins); // Call constructor of primitive wrapper
            }

            argList[i] = pList;
         }
        
         // pIdx + 1 points to first instruction, that counts
         // as an argument to method call.
         return new ArgumentList(argList, pIdx+1);
    }


    /**
     * @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;
    }


    private void handleReturnValue(InstructionList il, Invocation ins) {
        // Convert return value if needed.
        InstructionFactory factory = il.getInstructionFactory();
        String rType = ins.getReturnType();

        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(ins.getReturnType()));
        }
    }


    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 class ArgumentList {
        public InstructionList[] arguments;
        public int paramIndex;

        public ArgumentList(InstructionList[] args, int paramIndex) {
            this.arguments = args;
            this.paramIndex = paramIndex;
        }
    }
}
TOP

Related Classes of alt.jiapi.interceptor.InvocationInstrumentor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.