Package de.petris.dynamicaspects.wrapper

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

/*
* 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.Iterator;
import java.util.List;

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.generic.CodeExceptionGen;
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.TargetLostException;
import org.apache.bcel.generic.Type;

import de.petris.dynamicaspects.util.InstructionSearcher;
import de.petris.dynamicaspects.util.LVTTRemover;
import de.petris.dynamicaspects.util.Logger;
import de.petris.dynamicaspects.util.Reflection;

/**
* This method patcher de-/installs the {@link de.petris.dynamicaspects.wrapper.CallWrapper CallWrapper}s
* around the matching target calls within a method.
*
* @author Marco Petris
* @see de.petris.dynamicaspects.wrapper.CallMapper
* @see de.petris.dynamicaspects.wrapper.CallWrapper
* @see de.petris.dynamicaspects.BeforeAfterAdvice
* @see de.petris.dynamicaspects.WeaveType
*/
public class CallWrapperMethodPatcher implements Constants {
 
  // the type of the CallWrapper
  private final static Type CALLWRAPPERTYPE =
    new ObjectType( CallWrapper.class.getName() );
 
  private int methodIdx; // index of the method to be patched
  private MethodGen mGen; // method generator for this method
  private ConstantPoolGen constPoolGen; // the constant pool generator
  //  the joinpoint pattern for the target calls within this method
    private String joinpointPattern;
  private List<CallMatch> targets; // the list of the matching calls
  private InstructionFactory factory; 
  private InstructionList iList;
 

  /**
     * Creates an instance of this class.
     *
   * @param methodIdx the index of the method to be patched
   * @param mGen method generator for this method
   * @param constPoolGen the constant pool generator
   * @param joinpointPattern the joinpoint pattern for
     *                          the target calls within this method
   * @param targets the list of the matching calls
   */
  public CallWrapperMethodPatcher(
    int methodIdx, MethodGen mGen,
    ConstantPoolGen constPoolGen,
    String joinpointPattern,
    List<CallMatch> targets )  {

    this.methodIdx = methodIdx;
    this.mGen = mGen;
    this.joinpointPattern = joinpointPattern;
    this.targets = targets;
    this.constPoolGen = constPoolGen;
    this.factory = new InstructionFactory( constPoolGen );
    this.iList = mGen.getInstructionList();
  }
 
  /**
   * Installs the {@link de.petris.dynamicaspects.wrapper.CallWrapper CallWrapper}.
   *
   * @return the patched method.
   */
  public Method install() {
   
    patchMethod();
   
    mGen.setMaxStack();
    mGen.setMaxLocals();
   
    Method newMethod  = mGen.getMethod();
       
        LVTTRemover.removeLVTTs( newMethod, constPoolGen );
       
    iList.dispose();

    return newMethod;
  }
 
  /**
     * Deinstalls the {@link de.petris.dynamicaspects.wrapper.CallWrapper CallWrapper}.
   * @return the unpatched method
   */
  public Method deinstall() {
   
    unpatchMethod();
   
    mGen.setMaxStack();
    mGen.setMaxLocals();
   
    Method newMethod  = mGen.getMethod();

        LVTTRemover.removeLVTTs( newMethod, constPoolGen );       
       
    iList.dispose();

    return newMethod;
  }
 
  /**
   * Installs the wrapper for all the target calls within this method.
   */
  private void patchMethod() {
   
    InstructionSearcher aFinder =
      new InstructionSearcher( constPoolGen, iList );
   
    InstructionHandle first = null;
   
    // respect the super() or this(...)-call at the
    // beginning of constructors
    if( mGen.getName().equals( CONSTRUCTOR_NAME ) ) {
      first = aFinder.lookUpSuperConstructorCall().getNext();
    }
    else
      first = iList.getStart();
    }
   
    CallWrapperVariableHandler vHandler =
      new CallWrapperVariableHandler(
        mGen, iList,
        factory, constPoolGen,
        joinpointPattern );
   
        // create the wrapper
    int callWrapperIdx =
      vHandler.createCallWrapper(
        first, methodIdx );
   
    Iterator<CallMatch> cmIter = targets.iterator();
   
        // wrap each target
    while( cmIter.hasNext() ) {
     
      CallMatch curMatch = cmIter.next();
     
      // BEFORE - call
     
      int argListVarIdx =
        vHandler.createArgumentList(
          curMatch.getCallInstructionHandle(), curMatch );
     
      // load the callwrapper
      iList.insert( curMatch.getCallInstructionHandle(),
        InstructionFactory.createLoad(
          CALLWRAPPERTYPE,  callWrapperIdx ) );
     
      // load parameter for before()-call
      iList.insert(
        curMatch.getCallInstructionHandle(),
        InstructionFactory.createLoad(
          CallWrapperVariableHandler.ARGLIST_TYPE,
          argListVarIdx ) );
      // call before()
      iList.insert(
        curMatch.getCallInstructionHandle(),
        factory.createInvoke(
          CallWrapper.class.getName(),
          Wrapper.getBeforeName(),
          Type.VOID,
          new Type[] {
            CallWrapperVariableHandler.ARGLIST_TYPE },
          INVOKEVIRTUAL ) );
     
            // restore the arguments on the operand stack
      vHandler.reloadArguments(
        curMatch.getCallInstructionHandle(),
        argListVarIdx, curMatch.getCallInfo() );

      // AFTER - call
     
      InstructionHandle appendHandle = iList.append(
        curMatch.getCallInstructionHandle(),
        InstructionFactory.createLoad(
          CALLWRAPPERTYPE,  callWrapperIdx ) );
     
            // call after() for void-target
      if( curMatch.getCallInfo().getReturnType().equals( Type.VOID ) ) {
        appendHandle = iList.append(
          appendHandle,
          factory.createInvoke(
            CallWrapper.class.getName(),
            Wrapper.getAfterVoidName(),
            Type.VOID,  Type.NO_ARGS,
            INVOKEVIRTUAL ) );
      }
      else {
          // use the return value on the operand stack for after() call
               
        if( Reflection.isCat2Type(
            curMatch.getCallInfo().getReturnType()  ) ) {
                    // duplicate the wrapper variable on the stack
                    // and store it below the cat2-returnvalue
          appendHandle = iList.append(
            appendHandle,
            InstructionConstants.DUP_X2 );
         
                    // remove the old wrapper variable from the stack
          appendHandle = iList.append(
            appendHandle,
            InstructionConstants.POP );
        }
        else {
                    // simple switch of the wrapper and the return value
                    // which becomes the argument of the wrapper-after()-call
          appendHandle = iList.append(
            appendHandle,
            InstructionConstants.SWAP );             
        }
       
                // call after()
        appendHandle = iList.append(
          appendHandle,
          factory.createInvoke(
            CallWrapper.class.getName(),
            Wrapper.getAfterName(),
            Wrapper.getAfterParamType(
                curMatch.getCallInfo().getReturnType() )
            new Type[] {
              Wrapper.getAfterParamType(
                curMatch.getCallInfo().getReturnType() ) },
            INVOKEVIRTUAL ) );
       
                // cast the return type of the after()-call if necessary
        if( !(curMatch.getCallInfo().
            getReturnType() instanceof BasicType) ) {
          appendHandle = iList.append(
            appendHandle,
              factory.createCast(
                Type.OBJECT,
                curMatch.getCallInfo().getReturnType() ) );
        }
      }
     
      // EXCEPTION - handler
     
            // jump after the exceptionhandler
      appendHandle =
        iList.append(
          appendHandle, InstructionFactory.createBranchInstruction(
              GOTO, appendHandle.getNext() ) );
       
      InstructionHandle endHandling = appendHandle;
     
            // load the wrapper
      appendHandle = iList.append(
        appendHandle,
        InstructionFactory.createLoad(
          CALLWRAPPERTYPE,  callWrapperIdx ) );
     
      InstructionHandle handler = appendHandle;

            // use the excpetion as an argument for afterException()-call
      appendHandle = iList.append(
        appendHandle,
        InstructionConstants.SWAP );
     
            // method returns void?
      appendHandle = iList.append(
        appendHandle,
        new PUSH( constPoolGen,
          curMatch.getCallInfo().getReturnType().equals(
              Type.VOID ) ) );
         
            // call afterException()
      appendHandle = iList.append(
        appendHandle,
        factory.createInvoke(
          CallWrapper.class.getName(),
          Wrapper.getAfterExceptionName(),
          Type.THROWABLE,
          new Type[] {
            Type.THROWABLE, Type.BOOLEAN },
          INVOKEVIRTUAL ) );
     
            //todo: this should be configurable to install an aspect-exception-handler:
            // rethrow the exception
      appendHandle =
        iList.append( appendHandle, InstructionConstants.ATHROW );
      appendHandle =
        iList.append( appendHandle, InstructionConstants.RETURN );
     
      // register excpeptionhandler
      mGen.addExceptionHandler(
        curMatch.getCallInstructionHandle(), endHandling, handler,
        Type.THROWABLE );
     
     
    }
  }
 
  /**
   * Removes the wrapper from all the target calls within this method.
   */
  private void unpatchMethod() {
   
    InstructionSearcher aFinder =
      new InstructionSearcher( constPoolGen, iList );
   
    CallWrapperVariableHandler vHandler =
      new CallWrapperVariableHandler(
        mGen, iList,
        factory, constPoolGen,
        joinpointPattern );
   
        // the index of the wrapper to be deinstalled
    int callWrapperIdx = vHandler.getCallWrapperVariableIndex();
   
        // get the loads of this wrapper variable
    aFinder.setCallWrapperLoads( callWrapperIdx, targets );
    Iterator<CallMatch> cmIter = targets.iterator();
   
        // loop over the target calls
    while( cmIter.hasNext() ) {
   
      // REMOVE before()-call
     
            // get the argumentList variable
      int curArgListVarIdx =
        vHandler.getNextArgumentListVariableIndex();
      InstructionHandle curArgListInit =
        aFinder.lookUpArgumentListInit( curArgListVarIdx );
     
      CallMatch curCallMatch = cmIter.next();

      InstructionHandle beforeLoad =
        curCallMatch.getBeforeLoad();
     
      try {
        iList.delete( curArgListInit,
          vHandler.getLastReloadInstruction(
            beforeLoad.getNext().getNext(),
            curCallMatch.getCallInfo() ) );
      }
      catch( TargetLostException tle ) {
        Logger.debug( "removing before()-calls: %s", tle.toString() );
      }
     
     
      // REMOVE after-call
      InstructionHandle afterLoad =
        curCallMatch.getAfterLoad();
     
      try {
        iList.delete( afterLoad,
          getLastAfterInstruction(
            afterLoad, curCallMatch.getCallInfo() ) );
      }
      catch( TargetLostException tle ) {
        Logger.debug( "removing after()-calls: %s", tle.toString() );
      }
     
     
      // REMOVE Exception-handler
      InstructionHandle afterExceptionLoad =
        curCallMatch.getAfterExceptionLoad();

      removeExceptionHandler( afterExceptionLoad );
     
      try {
        iList.delete( afterExceptionLoad.getPrev(),
          afterExceptionLoad.getNext().getNext().
            getNext().getNext().getNext() );
      }
      catch( TargetLostException tle ) {
        Logger.debug(
          "removing afterException()-calls: %s", tle.toString() );
      }
    }
   
        // remove the creation of the wrapper variable
    List<InstructionHandle> callWrapperStores =
      aFinder.lookUpCallWrapperStores( callWrapperIdx );
   
    try {
      iList.delete(
          callWrapperStores.get(0).getPrev(),
          callWrapperStores.get(1) );
    }
    catch( TargetLostException tle ) {
      Logger.debug(
        "removing call wrapper variable creation: %s",
        tle.toString() );
    }
   
    vHandler.removeVariables();
   
    updateLocalVariableTable();
  }
 
  /**
   * Updates the local variable table.
   */
  private void updateLocalVariableTable() {

    // save all other local variables
    LocalVariableGen[] localVars = mGen.getLocalVariables();
   
    // clear the local variables table
    mGen.removeLocalVariables();
   
    // add all other local variables with corrected end position
    for( int localVarIdx=localVars.length-1;
      localVarIdx>=0; localVarIdx-- ) {

      // correct the end position
      InstructionHandle end = localVars[localVarIdx].getEnd();
      if( ( end == null )
        || ( end.getPosition() > iList.getEnd().getPosition() ) ) {
        end = iList.getEnd();
      }
     
      // add the variable
      mGen.addLocalVariable(
        localVars[localVarIdx].getName(),
        localVars[localVarIdx].getType(),
        localVarIdx, localVars[localVarIdx].getStart(), end );
    }
  }
   
  /**
   * Removes the exception handler.
   *
   * @param handler the handler to be removed.
   */
  private void removeExceptionHandler( InstructionHandle handler ) {
   
        for( CodeExceptionGen ceg : mGen.getExceptionHandlers() ) {
       
      if( ceg.getHandlerPC().equals(
          handler ) ) {
        mGen.removeExceptionHandler( ceg );
        break;
      }
    }
  }
 
  /**
     * Returns the last instruction of the after()-call
     *
   * @param afterLoad the load position of the wrapper variable
   * @param callInfo information about the target call
   * @return the last instruction of the after()-call
   */
  private InstructionHandle getLastAfterInstruction(
      InstructionHandle afterLoad, CallInfo callInfo ) {
 
    InstructionHandle lastAfterInstruction = afterLoad;
 
    if( callInfo.getReturnType().equals( Type.VOID ) ) {
      // only the after()-invoke
      lastAfterInstruction = lastAfterInstruction.getNext();
    }
    else {
      if( Reflection.isCat2Type(
          callInfo.getReturnType()  ) ) {
        // cat2Type stack manipulation
        lastAfterInstruction =
          lastAfterInstruction.getNext().getNext();
       
      }
      else {
        // cat1Type stack manipulation
        lastAfterInstruction = lastAfterInstruction.getNext();
      }

      // invoke after()
      lastAfterInstruction = lastAfterInstruction.getNext();
     
      // did we need a typecast?
      if( !(callInfo.getReturnType() instanceof BasicType) ) {
        lastAfterInstruction = lastAfterInstruction.getNext();
      }
    }
   
    return lastAfterInstruction;
  }
}
TOP

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

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.