Package ch.ethz.inf.iks.jvmai.jvmdi

Source Code of ch.ethz.inf.iks.jvmai.jvmdi.HotSwapLocalVariableMapper

//
//  This file is part of the prose package.
//
//  The contents of this file are subject to the Mozilla Public License
//  Version 1.1 (the "License"); you may not use this file except in
//  compliance with the License. You may obtain a copy of the License at
//  http://www.mozilla.org/MPL/
//
//  Software distributed under the License is distributed on an "AS IS" basis,
//  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
//  for the specific language governing rights and limitations under the
//  License.
//
//  The Original Code is prose.
//
//  The Initial Developer of the Original Code is Angela Nicoara. Portions
//  created by Angela Nicoara are Copyright (C) 2004 Angela Nicoara.
//  All Rights Reserved.
//
//  Contributor(s):
//  $Id: HotSwapLocalVariableMapper.java,v 1.1 2008/11/18 11:12:07 anicoara Exp $
// =====================================================================
//
package ch.ethz.inf.iks.jvmai.jvmdi;

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

import ch.ethz.jvmai.IllegalSignaturePatternException;
import ch.ethz.jvmai.IllegalRedefinitionAdviceException;

/**
*  HotSwapLocalVariableMapper class.
<P>
*  Transforms the slot numbers of local variables in Method Redefinition Advices
*  to the slot numbers that will be used in the redefined method.
<P>
*  Detects accesses to ANY and REST arguments in method redefine advice bodies.
<P>
*  Detects accesses self-references (this) in method redefine advice bodies.
*
* @version $Revision: 1.1 $
* @author   Angela Nicoara
* @author  Gerald Linhofer
*/
class HotSwapLocalVariableMapper {
  //
  // The slot numbers for method arguments are mapped by 'slotMap'.
  // All other slot number are calculated by adding 'stddifference'
  // to the original slot number.
  //

  // ERROR_CODE values are used to map arguments
  // that shouldn't be accessed in advice bodies.
  /// Index for the (implicit) this argument of member methods (may not be referenced in the advice code)
  private final static int ERROR_CODE_THIS = -1;
  /// Index for arguments of type 'ANY' (may not be referenced in the advice code)
  private final static int ERROR_CODE_ANY = -2;
  /// Index for arguments of type 'REST' (may not be referenced in the advice code)
  private final static int ERROR_CODE_REST = -3;
  /// Index for the first argument when a static method is redefined (may not be referenced in the advice code)
  private final static int ERROR_CODE_STATIC_THIS = -4;

  // Default difference between the old slot number of a local variable
  // and the new one. This is used to transform all local variable indexes
  // except that, which are used for method arguments.
  private int defaultOffset;
  // map holding the mappings for the first local variables (usually the arguments of a method)
  private int[] slotMap;
  // advice method, just to print more significant error messages.
  private final Method adviceMethod;
  // target method
  private final Method targetMethod;

  /**
   * Creates a <CODE>HotSwapLocalVariableMapper</CODE> for a method
   * redefinition advice redefining <CODE>target</CODE>.
   *
   * @param advice method that will redefine <CODE>target</CODE>
   * @param target method that will be redefined.
   *
   * @throws JVMAIRuntimeException parameter lists can not be matched
   */
  public HotSwapLocalVariableMapper(Method advice, Method target) {
    adviceMethod = advice;
    targetMethod = target;
    init(advice.getParameterTypes(), target.getParameterTypes());

    // System.out.println("!!!Slot Mappings from " +advice + " to " + target );
    // System.out.println("   std difference: " + stddifference );
    // System.out.println( Arrays.toString(slotMap) );
  }

  /**
   * Initializes <CODE>HotSwapLocalVariableMapper</CODE>.
   * This sets the fields <CODE>stddifference</CODE> and <CODE>slotMap</CODE>.
   *
   * @param adviceParam paramter list of the advice.
   * @param targetParam paramter list of the method beeing redefined by the advice.
   */
  private void init(Class[] adviceParam, Class[] targetParam) {
    if(0 == adviceParam.length) {
      // a. advice has no parameters: METHOD_ARGS()

      // slightly faster to do this here than to use 'createSlotMap()'
      // a.1. invalidate 'this'
      slotMap = new int[]{ERROR_CODE_THIS};             
      // a.2. increment the slot numbers for local variables,
      //      so that they dont overwrite the parameters.
      defaultOffset = sizeOfParameterList(targetParam);
    }
    else if("ch.ethz.prose.crosscut.REST".equals(adviceParam[adviceParam.length-1].getName())) {
      // b. advice last paramter is 'REST': METHOD_ARGS(...,REST)

      // b.1. create slot mapping for all parameters except the last one (which is of type 'REST')
      createSlotMap(adviceParam, targetParam, adviceParam.length - 1);
      // b.2. invalidate access to 'REST'
      slotMap[slotMap.length - 1] = ERROR_CODE_REST; 
    }
    else if(targetParam.length + 1 == adviceParam.length) {
      // c. advice defines the same number of arguments (plus one additional
      //    for 'this') as target

      // c.1. create slot mapping for all all parameters
      createSlotMap(adviceParam, targetParam, adviceParam.length);
    }
    else
      // d. advice defines incompatible arguments (incompatible number of arguments)
      throw new IllegalSignaturePatternException("Cannot map the paramter list of " + adviceMethod + " for " + targetMethod);
  }

  /**
   * Initializes the field <CODE>slotMap</CODE>.
   * <P>
   * slotMap is used to translate slot numbers in cases where 'ANY' stands for
   * a parameter which requires more than one slot in the local variable table
   * (parameters of type <CODE>long</CODE> and <CODE>double</CODE>).
   * <P>
   * Slot map may be used to detect accesses to <CODE>ANY</CODE> and <CODE>REST</CODE>
   * arguments in method redefine advice bodies.
   * <P>
   * slotMap may detect accesses to 'this' in the body of a method redefine advice.
   * <P>
   * NOTE: THIS METHOD MAY ONLY BE CALLED IF THE ADVICE WAS DEFINED WITH A NON EMPTY
   * PARAMETER LIST.
   *
   * @param adviceParam parameter list of the advice.
   * @param targetParam parameter list of the method being redefined by the advice.
   * @param end last parameter to process, the mappings for all other parameters will be left empty.
   */
  private void createSlotMap(Class[] adviceParam, Class[] targetParam, int end) {
    // 0. create the map (+1 for the explicit 'this' parameter)
    final int maxAdviceArgSlot = sizeOfParameterList(adviceParam);
    defaultOffset = sizeOfParameterList(targetParam) - maxAdviceArgSlot;

    slotMap = new int[maxAdviceArgSlot + 1];
    int newSlot = 0; // slot for use in the redefined method
    int oldSlot = 0; // slot used in the advice

    // 1. Invalidate references to 'this' (slotMap[0])
    slotMap[oldSlot++] = ERROR_CODE_THIS;

    // 2. Handle the first explicit parameter.
    //    It represents the methods owner, there's
    //    no corresponding parameter in targets parameter
    //    list, so it may not be processed like the other
    //    parameters, which denote arguments of the target
    //    method

    // 2.1. If the advice has no explicit parameters, we are done
    if(0 == adviceParam.length) {
      return;
    }

    if(Modifier.isStatic(targetMethod.getModifiers())) {
      // 2.2.a. Invalidate accesses to the first parameter for static
      //        target methods (static methods have no 'this' to which
      //        this parameter may be mapped).
      slotMap[oldSlot++] = ERROR_CODE_STATIC_THIS;
      defaultOffset--;
    }
    else {
      if("ch.ethz.prose.crosscut.ANY".equals(adviceParam[0].getName()))
        // 2.2.b. Invalidate accesses to the first parameter, because mapping
        //        of 'ANY' to 'this' will not work.
        slotMap[oldSlot++] = ERROR_CODE_ANY;
      else
        // 2.2.c. Map the first explicit parameter to 'this'
        slotMap[oldSlot++] = 0;
      newSlot++;
    }
    // assert(2 == oldSlot);  // the first two elements of the slot map are inserted.

    // 3. calculate the slot number for each parameter
    for(int i = 0; i < end-1; i++) {
      // 'i' is the index for 'targetParam', while 'i+1' is used for 'adviceParam'.
      // The slot number are not the same like the index in the parameter list,
      // because some types (long and double) need more than just one slot.

      // 3.1. set the slot
      if("ch.ethz.prose.crosscut.ANY".equals(adviceParam[i+1].getName())) {
        // 3.1.a. invalidate access to 'ANY'
        slotMap[oldSlot] = ERROR_CODE_ANY;     
      }
      else if("ch.ethz.prose.crosscut.REST".equals(adviceParam[i+1].getName())) {
        // 3.1.b. 'REST' is only allowed as last parameter
        System.err.println("ERROR: can not apply " + adviceMethod);
        System.err.println("       'REST' is only allowed as last argument of an advice, but not before.");
        throw new IllegalSignaturePatternException("'REST' may only be used as last parameter of an advice but not as in: " + adviceMethod);
      }
      else
        // 3.1.c. map acces to the parameter (that is not of type 'ANY' or 'REST')
        slotMap[oldSlot] = newSlot;

      // 3.2. increments the slot numbers
      int d = sizeOfVariable(adviceParam[i+1]);
      // 3.3. just in case some optimizing compiler may reuse
      //      the slots used for the parameters.
      if(2 == d && (slotMap.length > oldSlot + 2))
        // insert also a mapping for the second slot, if a variable needs more than one.
        slotMap[oldSlot + 1] = newSlot + 1;
      // 3.4. increment slot numbers
      oldSlot += d;
      newSlot += sizeOfVariable(targetParam[i]);
    }
  }

  /**
   * Returns the number of slots that a variable of type <CODE>type</CODE>
   * uses in the local variable table.
   *
   * @param type
   * @return number of slots required for a local variable of type <CODE>type</CODE>.
   */
  private final static int sizeOfVariable(Class type) {
    int result;
    // only primitives of type long and double use two slots, all other types
    // need only one.
    if(type.isPrimitive() && (Long.TYPE.equals(type) || Double.TYPE.equals(type)))
      result = 2;
    else
      result = 1;

    return result;
  }

  /**
   * Returns the number of slots that the parameter list <CODE>params</CODE>
   * uses in the local variable table.
   *
   * @param params parameter list
   * @return number of slots used by <CODE>params</CODE>
   */
  private final static int sizeOfParameterList(Class[] params) {
    int result = 0;
    for(int i = 0; i < params.length; i++)
      result += sizeOfVariable(params[i]);

    return result;
  }

  /**
   * Returns the slot number (variable index) that must
   * be used in a redefined method instead of the slot
   * number defined in the advice (<CODE>oldSlot</CODE>).
   *
   * @param oldSlot slot used by the advice
   * @return slot used by the redefined method
   * @throws IllegalRedefinitionAdviceException if the slot is used for
   *       a local variable that may not be accessed in an advice body
   */
  public int getNewSlot(int oldSlot) {
    int result;
    // 1. get the new slot number
    if(slotMap.length > oldSlot)
      // 1.a. Slot number for an argument
      result = slotMap[oldSlot];
    else
      // 1.b. Slot number for a variable defined in the method body.
      result = oldSlot + defaultOffset;

    // 2. check for illegal slots (illegal local variable access)
    if(0 > result) {
      switch(result) {
      case ERROR_CODE_THIS:
        throw new IllegalRedefinitionAdviceException("No (implicit) `this' usage allowed in advice method: " + adviceMethod);
      case ERROR_CODE_ANY:
        throw new IllegalRedefinitionAdviceException("No `ANY' usage allowed in advice method: " + adviceMethod);
      case ERROR_CODE_REST:
        throw new IllegalRedefinitionAdviceException("No `REST' usage allowed in advice method: " + adviceMethod);
      case ERROR_CODE_STATIC_THIS:
        throw new IllegalRedefinitionAdviceException("No usage of the first parameter allowed in advice methods that redefine static methods: " + adviceMethod);
      default:
        throw new IllegalRedefinitionAdviceException("Illegal local variable access (unknown error code: " + result + ") in advice method: " + adviceMethod);
      }
    }

    return result;
  }

  /**
   * Returns the number of local variable slots (<CODE>MaxLocals</CODE>) used for the
   * redefined method.
   * <P>
   * NOTE: 'proceed()' statements may require additional local variables.
   *
   * @param oldMaxLocals <CODE>MaxLocals</CODE> of the method redefinition advice.
   * @return <CODE>MaxLocals</CODE> for the redefined method.
   */
  public int getNewMaxLocals(int oldMaxLocals) {
    int retVal = oldMaxLocals + defaultOffset;
    if(0 > retVal)
      retVal = 0;
    return retVal;
  }
}
TOP

Related Classes of ch.ethz.inf.iks.jvmai.jvmdi.HotSwapLocalVariableMapper

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.