Package alt.jiapi.instrumentor

Source Code of alt.jiapi.instrumentor.MethodCallInstrumentor

/*
* Copyright (C) 2001 Mika Riekkinen, Joni Suominen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package alt.jiapi.instrumentor;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;

//import java.util.logging.Logger;
import org.apache.log4j.Category;

import alt.jiapi.Runtime;
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.JiapiRuntimeException;
import alt.jiapi.reflect.Loader;
import alt.jiapi.reflect.SignatureUtil;

/**
* This instrumentor creates method calls.
* It will create calls to either static or virtual calls.<p>
* If a virtual call is made, a class is added an extra field
* to hold a reference to Object specified by hook-method.<p>
*
*
* NOTE : MethodCallInstrumentor is still under construction on some parts
* of it.
* At the moment, it is assumed that hookmethod has exactly two
* arguments, first of which will be 'this' if available, or String
* if not available,
* and second the name of the method being processed.
* This is likely to change to a more dynamic thing.
*
* @author Mika Riekkinen
* @author Joni Suominen
* @version $Revision: 1.26 $ $Date: 2004/03/21 12:50:08 $
*/
public class MethodCallInstrumentor extends AbstractInstrumentor {
    private static Category log = Category.getInstance(MethodCallInstrumentor.class);

    protected Hook hook;
    protected Class hookClass;
    protected Method hookMethod;
    protected boolean isDynamic;
   
    private String jiapiFieldName;

    // Dynamic initializer
    {
        // Later on, we could ask Runtime to provide other
        // field names.
        jiapiFieldName = "__jiapi_field";
    }

    /**
     * Chain which is used to pre-instrument a field to the target
     * class when making dynamic calls.
     */
    protected InstrumentorChain preChain;

    /**
     * Chain which is used to post-instrument an initialization
     * for the field which will hold a reference to an target instance
     * which will be called when making dynamic calls.
     */
    protected InstrumentorChain postChain;

    /**
     * Empty constructor needed when the patch is dynamically instantiated.
     * The hook has to be set afterwards with setHook method.
     * @see #setHook
     */
    public MethodCallInstrumentor() {
    }

    /**
     * Constructor. Hook defines a method, that will be called
     * by the bytecode instrumented.
     *
     * @param hook A Hook to be called by this Instrumentor.
     * @see Hook
     */
    public MethodCallInstrumentor(Hook hook) {
        setHook(hook);
    }

    /**
     * Constructor. This constructor creates MethodCallInstrumentor
     * with the help of java.lang.reflect.Method.
     *
     * @param hookClass A class of the hook, that is to be called
     * @param hookMethod A method, that is called. The method must
     *        a member of the class.
     * @see java.lang.reflect.Method
     */
    public MethodCallInstrumentor(Class hookClass,
                                  java.lang.reflect.Method hookMethod) {
        // A bug in here. We don't have Hooks' instance.
        // postInstrument will fail. 'Class hookClass' should be changed to
        // 'Object instance'
        this.hookClass = hookClass;
        this.hookMethod = hookMethod;
        this.isDynamic = !Modifier.isStatic(hookMethod.getModifiers());
    }


    /**
     * Instruments given instruction list so, that it will make a
     * call to specified by Hook given to this class. Instructions will be
     * added to the end of the instruction list.<p>
     * If a method being instrumented is native or abstract, instrumentation
     * is skipped and instruction list is just forwarded to next in chain.
     *
     * @param il InstructionList to add new instructions to.
     */
    public void instrument(InstructionList il) {
        log.info("Instrumenting " + getCurrentClass().getName() + "." +
                 il.getDeclaringMethod().getName());

        JiapiMethod jm = il.getDeclaringMethod();
        // Native or abstract methods can't be instrumented.
        int modifiers = jm.getModifiers();
        if (Modifier.isNative(modifiers) || Modifier.isAbstract(modifiers)) {
            log.info("skipping abstract or native method: " +
                     getCurrentClass().getName() + "." + jm.getName());

            forward(il);
            return;
        }

        if (Modifier.isInterface(modifiers)) {
            log.info("Will not instrument interface " +
                     getCurrentClass().getName());
            forward(il);
            return;
        }

//         if ("<clinit>".equals(jm.getName())) {
//             System.out.println("Will not instrument <clinit>");
//             log.debug("Will not instrument <clinit>");
//             forward(il);
//             return;
//         }


        patchInstructionList(il, getInstrumentation());
       
        forward(il);
    }


    /**
     * Instrument instruction list.
     */
    private void patchInstructionList(InstructionList il,
                                      Instrumentation instrumentation) {
        //System.out.println(">> " + il.getDeclaringMethod());
        InstructionFactory factory = il.getInstructionFactory();
        if (factory == null) {
            throw new NullPointerException("Got null factory");
        }

        int currentMethodModifiers = il.getDeclaringMethod().getModifiers();

        JiapiClass clazz = getCurrentClass();
        Class[] hookParams = hookMethod.getParameterTypes();

        if (isDynamic) {
            log.debug("Hook is dynamic");
            // Obtain a field, that holds a reference to the method
            // to be called.
            JiapiField field = null;
            try {
                field = clazz.getDeclaredField(jiapiFieldName);
            }
            catch (NoSuchFieldException nsfe) {
                throw new JiapiRuntimeException("No such field: " + nsfe.getMessage());
            }

            il.add(factory.getField(field));

            if (Modifier.isStatic(currentMethodModifiers)) {
                il.add(factory.pushConstant(clazz.getName()));
            }
            else {
                il.add(factory.pushThis()); // First argument is 'this'
            }
        }
        else {
            log.debug("Hook is static");
            // On static methods, First argument is name of the current class.
            // This should be Class object.
            // Runtime.forName(clazz.getName())
            // factory.forName(clazz.getName())
            il.add(factory.pushConstant(clazz.getName()));
        }

        // Skip first param (source object made above)
        for (int i = 1; i < hookParams.length; i++) {
            if (hookParams[i].equals(String.class)) {
                // ---  target name  ---
                String targetName = instrumentation.getTargetName();
                if (targetName == null) {
                    targetName = "???";
                }

                il.add(factory.pushConstant(targetName));
            }
            else if (hookParams[i].equals(Object.class)) {
                // ---  target Object  ---
                InstructionList targetCode = instrumentation.getTargetCode();
                if (targetCode != null) {
                    log.debug("Got target code: " + targetCode);
                    il.add(targetCode);
                }
                else {
                    log.debug("No target code");
                    il.add(factory.pushNull());
                }
            }
            else if (hookParams[i].equals(Object[].class)) {
                // ---  target Object[]  ---
                log.warn("target arguments are not supported");
                il.add(factory.pushNull());
            }
            else {
                log.error("Invalid Hook method: " + hookMethod);
            }
        }

        Loader l = new Loader();
        try {
            JiapiClass hClass = l.loadClass(hookClass.getName());
            Class[] hpTypes = hookMethod.getParameterTypes();
            String[] pTypes = new String[hpTypes.length];
           
            for (int i = 0; i < pTypes.length; i++) {
                pTypes[i] = hpTypes[i].getName();
            }
           
            JiapiMethod hMethod =hClass.getDeclaredMethod(hookMethod.getName(),
                                                          pTypes);
            il.add(factory.invoke(hMethod));
        }
        catch(Exception e) {
            log.error("Failed to add invoke instruction: " + e);
        }
    }

    // Removed static below. -- mcr70 -- 02032004
    private /*static*/ HashMap preInstrumentations = new HashMap();
    private /*static*/ HashMap postInstrumentations = new HashMap();

    /**
     * Creates a chain which is used to add a new field to a target
     * class. This field will hold a reference to an instance for
     * which the calls will be made. On static calls there's no
     * need for this field.
     *
     * @return A chain, that will be processed before current Instrumentor.
     */
    public InstrumentorChain preInstrument() {
        if (!isDynamic) {
            log.info("Will not pre instrument for static calls");
            // Static calls don't need any helper instrumentors.
            return null;
        }

        JiapiClass c = getCurrentClass();

        // Make sure pre instrumentation is done only once per JiapiClass
        if (preInstrumentations.containsKey(c)) {
            return null;
        }
        else {
            preInstrumentations.put(c, null);
        }
       
        int modifiers = c.getModifiers();
        if (Modifier.isInterface(modifiers)) {
            log.info("Will not pre instrument interface " + c.getName());
            return null;
        }

        preChain = new InstrumentorChain();

        // Add an instrumentor which will create a new field to hold
        // reference to hook instance in dynamic calls.
        preChain.add(new CreateFieldInstrumentor(Modifier.PUBLIC +
                                                 Modifier.STATIC,
                                                 hookClass.getName(),
                                                 jiapiFieldName));
       
//          preChain.add(new CreateMethodInstrumentor(Modifier.STATIC,
//                                                     "<clinit>"));
       
//          preChain.add(new HeadInstrumentor());
       
//          // Add an instrumentor which will initialize the field in
//          // a static initializer.
//          preChain.add(new FieldAssignInstrumentor(jiapiFieldName,
//                                                    hook.getInstance()));
       
        return preChain;
    }


    /**
     * Creates a chain which properly initializes the field at
     * static initializer.
     */
    public InstrumentorChain postInstrument() {
        //if (true) return null;
        if (!isDynamic) {
            // Static calls don't need any helper instrumentors.
            return null;
        }

        JiapiClass c = getCurrentClass();
        // Make sure pre instrumentation is done only once per JiapiClass
        if (postInstrumentations.containsKey(c)) {
            return null;
        }
        else {
            postInstrumentations.put(c, null);
        }

        int modifiers = c.getModifiers();
        if (Modifier.isInterface(modifiers)) {
            log.info("Will not post instrument interface " + c.getName());
            return null;
        }

        postChain = new InstrumentorChain();

        // Add an instrumentor which will create a static initializer
        // if it doesn't exist yet.
        // NOTE! Mode must be FORWARD_NEW:
        postChain.add(new CreateMethodInstrumentor(Modifier.STATIC,
                                                   "<clinit>"));
       
        postChain.add(new HeadInstrumentor());
       
        // Add an instrumentor which will initialize the field in
        // a static initializer.
        postChain.add(new FieldAssignInstrumentor(jiapiFieldName,
                                                  hook.getInstance()));
       
        return postChain;
    }
   

    /**
     * Sets the for which the method calls are delegated at runtime.
     * @param hook A Hook to be called by this patch.
     */
    public void setHook(Hook hook) {
        this.hook = hook;
        this.hookMethod = hook.getHookMethod();
        this.hookClass = hookMethod.getDeclaringClass();

        this.jiapiFieldName = hookClass.getName().replace('.', '_') +
            "_" + hook.getInstance().hashCode();
        log.debug("Jiapi field name is '" + jiapiFieldName + "'");

        this.isDynamic = !Modifier.isStatic(hookMethod.getModifiers());
    }
   
    /**
     * Get the associated Hook.
     * @return Hook
     */
    public Hook getHook() {
        return hook;
    }



    public String toString() {
        return getClass().getName() + "#" + hook.getInstance().toString();//getClass().getName();
    }
}
TOP

Related Classes of alt.jiapi.instrumentor.MethodCallInstrumentor

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.