Package de.petris.dynamicaspects.wrapper

Source Code of de.petris.dynamicaspects.wrapper.CallWrapperVariableHandler

/*
* Copyright (c) 2004, Marco Petris
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     - Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     - Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     - Neither the name of Marco Petris nor the names of its
*       contributors may be used to endorse or promote products derived from
*       this software without specific prior written permission.
*      
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

package de.petris.dynamicaspects.wrapper;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.LocalVariableGen;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;

import de.petris.dynamicaspects.ArgumentInfo;
import de.petris.dynamicaspects.AspectException;
import de.petris.dynamicaspects.util.Reflection;

/**
* Handler for the variable creation used for a
* {@link de.petris.dynamicaspects.wrapper.CallWrapper CallWrapper}.
* During installation of the wrapper the wrapper variable and an instance of
* {@link de.petris.dynamicaspects.ArgumentInfo ArgumentInfo} is created.
* During deinstallation the variables are removed.
*
* @author Marco Petris
*/
class CallWrapperVariableHandler implements Constants {
 
  // the type name of the argument list
  private final static String ARGLIST_TYPENAME =
      ArgumentInfo.class.getName();
 
  // the type of the argument list
  final static Type ARGLIST_TYPE = new ObjectType( ARGLIST_TYPENAME );
 
 
  // the method names called at the argument list
  private final static String ARGLIST_REVERT = "revert";
  private final static String ARGLIST_ADD = "add";
  private final static String ARGLIST_SETTARGET = "setTarget";
  private final static String ARGLIST_SETSTATICTARGET = "setStaticTarget";
  private final static String ARGLIST_GETTARGET = "getTarget";
  private final static String ARGLIST_SETMETHODNAMESIGNATURE =
    "setMethodNameSignature";
 
 
  // static part of variable name of the wrapper:
  // !use member wrapperVarName!
  private final static
    String CALLWRAPPER_VARNAME_STATICPART =
      "de_petris_dynamicaspects_CallWrapper";
 
  // static part of variable name of the argument list
  private final static
    String ARGLIST_VARNAME_STATICPART = "_ARGUMENTLIST";
 
  private final static
    char NEGATIVEHASHCODE = '_';
 
  // the type of the call wrapper
  private final static Type CALLWRAPPER_TYPE =
    new ObjectType( CallWrapper.class.getName() );

 
  private MethodGen mGen; // the method generator for this method
  private InstructionList iList;
  private InstructionFactory factory; 
  private ConstantPoolGen constPoolGen; // constant pool generator
    // the pattern for the joinpoints matched by the targetcalls
  private String joinpointPattern; 
    // variable name of the wrapper
  private String wrapperVarName;
    // next index of the argument list
  private int argListVarNameIdx;
    // base of the name of the argument list variable
  private String argListVarNameBase;
 
  /**
     * Constructs an instance of this class.
     *
     * @param mGen the method generator
     * @param iList the instruction list
     * @param factory the instruction factory
     * @param constPoolGen the constant pool generator
   * @param joinpointPattern the joinpoint pattern for the target calls
     *                          ( is used for loading the wrapper )
   */
  CallWrapperVariableHandler(
    MethodGen mGen, InstructionList iList,
    InstructionFactory factory, ConstantPoolGen constPoolGen,
    String joinpointPattern ) {
   
    this.mGen = mGen;
    this.iList = iList;
    this.factory = factory;
    this.constPoolGen = constPoolGen;
    this.joinpointPattern = joinpointPattern;
    this.wrapperVarName = createVariableName();
    this.argListVarNameBase = wrapperVarName + ARGLIST_VARNAME_STATICPART;
    this.argListVarNameIdx = 1;
  }
 
  /**
   * Creates the variable for the call wrapper
   *
   * @param insertPoint where to insert the creation instructions
   * @return the local variable index of the variable
   */
  int createCallWrapper(
    InstructionHandle insertPoint, int methodIdx ) {
   
    // the local variable for our callwrapper
    LocalVariableGen callWrapperVarGen =
      mGen.addLocalVariable(
        createVariableName(),
        CALLWRAPPER_TYPE, null, null );
   
    int callWrapperIdx = callWrapperVarGen.getIndex();
   
    // set the variable to null first
    iList.insert( insertPoint,
      InstructionFactory.createNull( CALLWRAPPER_TYPE ) );
   
    // store variable name
    callWrapperVarGen.setStart(
      iList.insert( insertPoint,
        InstructionFactory.createStore(
          CALLWRAPPER_TYPE, callWrapperIdx ) ) );

        // put the arguments for the mapper on the stack
       
        // classname
      iList.insert(
        insertPoint,
      new PUSH( constPoolGen, mGen.getClassName() ) );
        // method  index
      iList.insert(
        insertPoint,
      new PUSH( constPoolGen, methodIdx ) );
        // join point pattern
      iList.insert(
          insertPoint,
        new PUSH( constPoolGen, joinpointPattern ) );
     
        // query  the mapper for the wrapper
      iList.insert( insertPoint, factory.createInvoke(
        CallMapper.class.getName(),
      CallMapper.GETWRAPPER_METHODNAME,
      CALLWRAPPER_TYPE,
      new Type[] { Type.STRING, Type.INT, Type.STRING },
      INVOKESTATIC) );
   
    // store initialized callwrapper
      iList.insert( insertPoint,
      InstructionFactory.createStore(
        CALLWRAPPER_TYPE, callWrapperIdx ) );
       
    return callWrapperIdx;     
  }
 
  /**
     * The name of the wrapper variable is constructed by a static part of the name
     * and a string representation of the hashcode of the joinpoint pattern.
     *
   * @return the name of the wrapper variable.
   */
  private String createVariableName() {
    int hashCode = joinpointPattern.hashCode();
    StringBuffer buf = new StringBuffer(CALLWRAPPER_VARNAME_STATICPART);
    if( hashCode < 0 ) {
      buf.append( NEGATIVEHASHCODE );
    }
   
    buf.append( Math.abs( hashCode ) );
   
    return buf.toString();
  }
 
  /**
     * Looks for the local variable index of the given variable.
     *
   * @param variableName the name of the variable
   * @return the local variable index of given variable.
   */
  private int searchVariableIndex( String variableName ) {

        for( LocalVariableGen localVarsGen : mGen.getLocalVariables() ) {
      if( localVarsGen.getName().equals( variableName ) ) {
        return localVarsGen.getIndex();
      }
    }
   
    throw new AspectException(
      "unexpected situation: could not find variable "
      +  variableName + " in local variable table!" );
   
  }
 
  /**
   * @return the local variable index of wrapper variable.
   */
  int getCallWrapperVariableIndex() {
    return searchVariableIndex( this.wrapperVarName );
  }
 
  /**
   * @return the local variable index of the next argument list variable.
   */
  int getNextArgumentListVariableIndex() {
    return searchVariableIndex( getNextArgListVarName() );
  }
 
  /**
   * @return the name of the next argument list variable
   */
  private String getNextArgListVarName() {
    StringBuffer buf = new StringBuffer( argListVarNameBase );
    buf.append( argListVarNameIdx++ );
    return buf.toString();
  }
 

  /**
   * Creates the variable for the next argument list
   *
   * @param insertPoint where to insert the creation instructions
     * @param callMatch information about the target call
   * @return the local variable index of the variable
   */
  int createArgumentList(
      InstructionHandle insertPoint, CallMatch callMatch ) {
   
    // get number of arguments for the method
    int paramCount = callMatch.getCallInfo().getArgTypes().length;
   
    String curArgListVarName = getNextArgListVarName();
   
    // create a variable for the argument list
    LocalVariableGen argListVarGen =
      mGen.addLocalVariable(
        curArgListVarName,
        ARGLIST_TYPE,
        null, null ); // todo: insertPoint
   
    int argListVarIdx = argListVarGen.getIndex();

    // init variable
    iList.insert( insertPoint,
      InstructionFactory.createNull( ARGLIST_TYPE ) );
   
    // set visibility start
    argListVarGen.setStart(
      iList.insert( insertPoint,
        InstructionFactory.createStore(
          ARGLIST_TYPE, argListVarIdx ) ) );
   
    //todo: setEnd for localVars

   
    // 'new' call
    iList.insert( insertPoint,
       factory.createNew( ARGLIST_TYPENAME ) );
        
    iList.insert( insertPoint, InstructionConstants.DUP);
   
    // init call
    iList.insert( insertPoint,
      factory.createInvoke(
        ARGLIST_TYPENAME,
        CONSTRUCTOR_NAME,
        Type.VOID,
        Type.NO_ARGS,
        INVOKESPECIAL ) );
   
    // store argument list
    iList.insert( insertPoint,
      InstructionFactory.createStore(
        ARGLIST_TYPE, argListVarIdx ) );
   
    // add parameters to argument list
    for( int count = paramCount-1; count >=0; count-- ) {
       
      addParam(
        insertPoint, argListVarIdx,
        callMatch.getCallInfo().getArgTypes()[count] );
    }

       
        // the arguments are in the wrong order
        // so we revert the argument list
       
    iList.insert(
      insertPoint,
      InstructionFactory.createLoad(
          Type.OBJECT, argListVarIdx ) );
   
    iList.insert(
      insertPoint,
      factory.createInvoke(
        ARGLIST_TYPENAME, ARGLIST_REVERT,
        Type.VOID,
        Type.NO_ARGS,
        INVOKEVIRTUAL ) );

        // set the target of the call
       
    setTarget(
      insertPoint, argListVarIdx,
      callMatch );
   
        // set the name and the signature of the target method
    iList.insert(
        insertPoint,
        InstructionFactory.createLoad(
            Type.OBJECT, argListVarIdx ) );
   
    iList.insert(
        insertPoint,
        new PUSH(
          constPoolGen,
          callMatch.getCallInfo().getMethodName() ) );
   
    iList.insert(
        insertPoint,
        new PUSH(
          constPoolGen,
          callMatch.getCallInfo().getMethodSignature() ) );
   
    iList.insert(
        insertPoint,
        factory.createInvoke(
          ARGLIST_TYPENAME, ARGLIST_SETMETHODNAMESIGNATURE,
          Type.VOID,
          new Type[] { Type.STRING, Type.STRING },
          INVOKEVIRTUAL ) );
   
   
    return argListVarIdx;
  }
 
  /**
     * Sets the target instance of the target method call.
     *
   * @param insertPoint the insertion point
   * @param argListVarIdx the index of the argument list variable
   * @param callMatch the matching target
   */
  private void setTarget(
      InstructionHandle insertPoint, int argListVarIdx,
      CallMatch callMatch ) {
 
        // load argument list
    iList.insert(
      insertPoint,
      InstructionFactory.createLoad( Type.OBJECT, argListVarIdx ) );
     
        // do we have a static target call?
    if( callMatch.getCallInfo().isStatic() ) {
            // put target class name on stack
      iList.insert(
        insertPoint,
        new PUSH(
          constPoolGen,
          callMatch.getCallInfo().getClassName() ) );     
      // set the static target
      iList.insert(
        insertPoint,
        factory.createInvoke(
          ARGLIST_TYPENAME, ARGLIST_SETSTATICTARGET,
          Type.VOID,
          new Type[] { Type.STRING },
          INVOKEVIRTUAL ) );
     
    }
    else {
            // use the target instance on the operand stack as a parameter
            // for the setTarget()-call
      iList.insert( insertPoint, InstructionConstants.SWAP );
     
      iList.insert(
        insertPoint,
        factory.createInvoke(
          ARGLIST_TYPENAME, ARGLIST_SETTARGET,
          Type.VOID,
          new Type[] { Type.OBJECT },
          INVOKEVIRTUAL ) );
    }
   
  }
 
 
  /**
   * Adds a parameter to the argument list.
   *
   * @param insertPoint where to insert the add instructions
   * @param argListVarIdx the index of the argument list variable
   * @param paramType the type of the argument
   */
  private void addParam(
    InstructionHandle insertPoint, int argListVarIdx,
    Type paramType ) {
   
        // load argument list
    iList.insert(
      insertPoint,
      InstructionFactory.createLoad(
          Type.OBJECT, argListVarIdx ) );
   
        // use the arguments on the operand stack as arguments for the add()-call
    if( Reflection.isCat2Type( paramType ) ) {
      iList.insert(
        insertPoint,
        InstructionConstants.DUP_X2 );
     
      iList.insert(
          insertPoint,
          InstructionConstants.POP );
    }
    else {
      iList.insert( insertPoint, InstructionConstants.SWAP );
    }
   
    if( paramType instanceof ReferenceType ) {
      paramType = Type.OBJECT;
    }
   
    // add()-call
    iList.insert(
      insertPoint,
      factory.createInvoke(
        ARGLIST_TYPENAME, ARGLIST_ADD,
        Type.VOID,
        new Type[] { paramType },
        INVOKEVIRTUAL ) );
  }
 
 
  /**
     * Reloads the arguments used for the argument list of the before()-call on
     * the stack to satisfy the target call, which is using these arguments.
     *
   * @param insertPoint the insertion point
   * @param argListVarIdx the local variable index of the argument list
   * @param callInfo the information about the target call
   */
  void reloadArguments(
      InstructionHandle insertPoint, int argListVarIdx,
            CallInfo callInfo ) {
 
        // restore the target first for non.static target calls
    if( !callInfo.isStatic() ) {
      iList.insert(
        insertPoint,
        InstructionFactory.createLoad( Type.OBJECT, argListVarIdx ) );
       
      iList.insert(
        insertPoint,
        factory.createInvoke(
          ARGLIST_TYPENAME, ARGLIST_GETTARGET,
          Type.OBJECT,
          Type.NO_ARGS,
          INVOKEVIRTUAL ) );
       
            // cast the target instance to its original type
      iList.insert(
          insertPoint,
          factory.createCast(
            Type.OBJECT,
            new ObjectType(
              callInfo.getClassName() ) ) );
    }
   
    // get number of arguments for the method
    int paramCount = callInfo.getArgTypes().length;
   
    // restore each argument
    for( int count = 0; count <paramCount; count++ ) {
   
      String methodName =
        ArgumentInfo.getTypedGetMethodname(
          callInfo.getArgTypes()[count] );
     
      iList.insert(
          insertPoint,
          InstructionFactory.createLoad( Type.OBJECT, argListVarIdx ) );
     
      iList.insert(
          insertPoint,
          new PUSH( constPoolGen, count ) );
     
      iList.insert(
        insertPoint,
        factory.createInvoke(
          ARGLIST_TYPENAME, methodName,
          getParamType(
            callInfo.getArgTypes()[count] ),
          new Type[] { Type.INT },
          INVOKEVIRTUAL ) );

            // cast non-primitive types to there original type
      if( !(callInfo.
          getArgTypes()[count] instanceof BasicType) ) {
        iList.insert(
            insertPoint,
            factory.createCast(
              Type.OBJECT,
              callInfo.getArgTypes()[count] ) );
      }
    }
  }
 
  /**
   * Returns the parameter type in correspondence to
   * the given type.
   *
   * @param type the type which should be passed
   * @return the type that is the right paramter type
   */
  private Type getParamType( Type type ) {
    if( ( type instanceof BasicType ) && !type.equals( Type.VOID ) ) {
      return type;
    }
    else {
      return Type.OBJECT;
    }
  }
 
  /**
     * Returns the last instruction of the argument reload.
     *
   * @param beforeCall the before()-call
   * @param callInfo the information about the target call
   * @return the last instruction of the argument reload
   */
  InstructionHandle getLastReloadInstruction(
    InstructionHandle beforeCall, CallInfo callInfo ) {
   
    InstructionHandle lastReloadInstruction = beforeCall;
 
    // get number of arguments for the method
    int paramCount = callInfo.getArgTypes().length;
   
    // add parameters to argument list
    for( int count = paramCount-1; count>=0; count-- ) {
     
      // argList-load, push param-idx on stack, invoke get   
      lastReloadInstruction =
        lastReloadInstruction.getNext().getNext().getNext();
     
      // did we insert a typecast?
      if( !(callInfo.getArgTypes()[count] instanceof BasicType) ) {
        lastReloadInstruction =
          lastReloadInstruction.getNext();
      }
    }

    // did we push the instance of the targetCall on the stack?
    if( !callInfo.isStatic() ) {

      // argList-load, invoke get, typeCast
      lastReloadInstruction =
        lastReloadInstruction.getNext().getNext().getNext();
    }
   
    return lastReloadInstruction;
  }
 
  /**
   * Removes all created variables.
   */
  public void removeVariables() {
    Iterator<LocalVariableGen> iter = lookUpVariables();
    while( iter.hasNext() ) {
      mGen.removeLocalVariable( iter.next() );
    }
  }
   
  /**
   * Looks up the variables in the local variables table.
   *
   * @return an iterator of the list of found variable generators.
   */
  private Iterator<LocalVariableGen> lookUpVariables() {
   
    List<LocalVariableGen> vars = new ArrayList<LocalVariableGen>();
   
        for( LocalVariableGen localVarsGen : mGen.getLocalVariables() ) {
           
      if( ( localVarsGen.getName().length() >= wrapperVarName.length() )
        && ( localVarsGen.getName().substring(
          0, wrapperVarName.length() ).equals( wrapperVarName ) ) ) {
        vars.add( localVarsGen );
      }
    }
   
    return vars.iterator();
  }
 
}
TOP

Related Classes of de.petris.dynamicaspects.wrapper.CallWrapperVariableHandler

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.