Package de.petris.dynamicaspects.wrapper

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

/*
* 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 class installs the
* {@link de.petris.dynamicaspects.wrapper.ExecutionWrapper ExecutionWrapper} and the
* {@link de.petris.dynamicaspects.wrapper.ExecutionMapper ExecutionMapper} to a target method.
* The mapper loads the wrapper which belongs to the target method. The wrapper
* then calls the before()-method of all installed
* {@link de.petris.dynamicaspects.BeforeAfterAdvice BeforeAfterAdvices}
* prior to target method execution and it calls the after()-methods of all isntalled aspects
* after the target method execution.
*
* @author Marco Petris
* @see de.petris.dynamicaspects.BeforeAfterAdvice
* @see de.petris.dynamicaspects.WeaveType
*/
public class ExecutionWrapperMethodPatcher implements Constants {

  // the type of the ExecutionWrapper
  private final static Type EXECUTIONWRAPPERTYPE =
    new ObjectType( ExecutionWrapper.class.getName() );
 
  private MethodGen mGen;   // a method generator
  private InstructionFactory factory;
  private InstructionList iList;
  private ConstantPoolGen constPoolGen; // a ConstantPool generator
  /**
   * Constructs an instance of this class.
   *
   * @param m the method which shall be patched.
   * @param javaClassName the name of the class of this method
   * @param constPoolGen the ConstantPool generator for this method
   */
  public ExecutionWrapperMethodPatcher(
    Method m, String javaClassName,
    ConstantPoolGen constPoolGen ) {

    // construct the method generator
        this.constPoolGen = constPoolGen;
       
    mGen = new MethodGen( m, javaClassName, constPoolGen );
    this.factory = new InstructionFactory( constPoolGen );
    this.iList = mGen.getInstructionList();
  }
 
  /**
   * Installs the {@link de.petris.dynamicaspects.wrapper.ExecutionWrapper ExecutionWrapper}.
   *
   * @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.ExecutionWrapper ExecutionWrapper}.
   *
   * @return the unpatched method.
   */
  public Method deinstall() {
   
    unpatchMethod();
   
    mGen.setMaxStack();
    mGen.setMaxLocals();
    Method newMethod  = mGen.getMethod();

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

    return newMethod;
  }
   
  /**
   * Patches the method. Installs  a wrapper around the execution of the target method.
   */
  private void patchMethod() {
   
    // patch before starts here
   
    InstructionSearcher aFinder =
      new InstructionSearcher( constPoolGen, iList );
   
    InstructionHandle first = null;
   
    // respect the super() and this(...)-call at the
    // beginning of constructors
    if( mGen.getName().equals( CONSTRUCTOR_NAME ) ) {
      first = aFinder.lookUpSuperConstructorCall().getNext();
    }
    else
      first = iList.getStart();
    }
   
    // save return statement
    InstructionHandle last = iList.getEnd();

    ExecutionWrapperVariableHandler vHandler
      = new ExecutionWrapperVariableHandler(
          mGen, iList, factory, constPoolGen );
   
    // create variable for argumentList
    int argListVarIdx =
      vHandler.createArgumentList( first );
   
    // create variable for executionwrapper
    int executionWrapperIdx =
      vHandler.createExecutionWrapper( first );
   
    // BEFORE CALL
   
    // load the executionwrapper
    iList.insert( first,
      InstructionFactory.createLoad(
        EXECUTIONWRAPPERTYPE,  executionWrapperIdx ) );
   
    // load parameter for before()-call
    iList.insert(
      first,
      InstructionFactory.createLoad(
        ExecutionWrapperVariableHandler.ARGLIST_TYPE,
        argListVarIdx ) );
     
    // call before()
    iList.insert(
      first,
      factory.createInvoke(
        ExecutionWrapper.class.getName(),
        Wrapper.getBeforeName(),
        Type.VOID,
        new Type[] {
          ExecutionWrapperVariableHandler.ARGLIST_TYPE },
        INVOKEVIRTUAL ) );

   
    // look for all return-statements
    List<InstructionHandle> returnStatements =
      aFinder.lookUpReturnStatements();
    Iterator<InstructionHandle> iter = returnStatements.iterator();
   
    while( iter.hasNext() ) {
     
      InstructionHandle curReturnStatement = iter.next();
     
      // AFTER CALLs
     
      // load executionwrapper for after()-call
      iList.insert( curReturnStatement,
        InstructionFactory.createLoad(
          EXECUTIONWRAPPERTYPE,  executionWrapperIdx ) );
     
      // load parameter for after()-call
            // using the return value on the operand stack
      if( !mGen.getReturnType().equals( Type.VOID ) ) {

        if( Reflection.isCat2Type(
            mGen.getReturnType() ) ) {
          iList.insert( curReturnStatement,
            InstructionConstants.DUP_X2 );
         
          iList.insert( curReturnStatement,
            InstructionConstants.POP );
        }
        else {
          iList.insert( curReturnStatement,
            InstructionConstants.SWAP );             
        }
       
        // call after() within try-block
        iList.insert( curReturnStatement, 
          factory.createInvoke(
            ExecutionWrapper.class.getName(),
            Wrapper.getAfterName(),
            Wrapper.getAfterParamType(
                mGen.getReturnType() ),
            new Type[] {
              Wrapper.getAfterParamType(
                mGen.getReturnType() ) },
            INVOKEVIRTUAL )
        );
       
        if( !(mGen.getReturnType() instanceof BasicType) ) {
          iList.insert( curReturnStatement, 
              factory.createCast(
                Type.OBJECT,
                mGen.getReturnType() ) );
        }
      }
      else {
        // call afterVoid() within try-block
        iList.insert( curReturnStatement, 
          factory.createInvoke(
            ExecutionWrapper.class.getName(),
            Wrapper.getAfterVoidName(),
            Type.VOID,
            Type.NO_ARGS,
            INVOKEVIRTUAL )
        );
      }
    }
   
   
    // add afterException()-call within catch-block
   
    // load executionwrapper
    InstructionHandle handler = 
      iList.append(
        InstructionFactory.createLoad(
          EXECUTIONWRAPPERTYPE,  executionWrapperIdx ) );
   
    // load exception
    iList.appendInstructionConstants.SWAP );             
   
    iList.append(
      new PUSH( constPoolGen,
        mGen.getReturnType().equals( Type.VOID ) ) );
       
    // call after() within catch-block
    iList.append
      factory.createInvoke(
        ExecutionWrapper.class.getName(),
        Wrapper.getAfterExceptionName(),
        Type.THROWABLE,
        new Type[] {
          Type.THROWABLE, Type.BOOLEAN },
        INVOKEVIRTUAL )
    );
   
    // rethrow the exception
    iList.append( InstructionConstants.ATHROW );
    iList.append( InstructionConstants.RETURN );
   
    // register excpeptionhandler
    mGen.addExceptionHandler(
      first, last, handler,
      Type.THROWABLE );
   
  }

  /**
   * Unpatches the method.
   */
  private void unpatchMethod() {

    // a finder for instructions
    InstructionSearcher aFinder =
      new InstructionSearcher( constPoolGen, iList );
   
    // lookup before() call
    List<InstructionHandle> beforeList =
      aFinder.lookUpMethodCall(
        ExecutionWrapper.class.getName(),
        Wrapper.getBeforeName(),
        Wrapper.getBeforeSignature() )
   
    // save before() call
    InstructionHandle beforeCall = beforeList.get(0);
   
    // look for all return-statements
    List<InstructionHandle> returnStatements =
      aFinder.lookUpReturnStatementsSkipLast();
    Iterator<InstructionHandle> iter = returnStatements.iterator();

    InstructionHandle exceptionHandler = null;
   
    while( iter.hasNext() ) {
     
      InstructionHandle curReturnStatement = iter.next();
     
      InstructionHandle last = curReturnStatement.getPrev();
      // save the exceptionhandler for the removeExceptionHandler-call,
      // ( we want the statement before the last return )
     
      exceptionHandler = curReturnStatement.getNext();
     
      InstructionHandle first = last;
     
            // after()-call may have been complex or simple
            // depending on the return type of the target method
      if( mGen.getReturnType().equals( Type.VOID ) ) {
        first = first.getPrev();
      }
      else {
                // two instructions backward for cat2type-operation
        if( Reflection.isCat2Type(
            mGen.getReturnType() ) ) {
          first = first.getPrev().getPrev();
        }
        else {
          first = first.getPrev();
        }

        first = first.getPrev();
       
        if( !(mGen.getReturnType() instanceof BasicType) ) {
          first = first.getPrev();
        }
      }
   
      // remove the after()-calls
      try {
        iList.delete( first, last );
      }
      catch( TargetLostException tle ) {
        Logger.debug( "removing after()-calls: %s", tle.toString() );
      }
    }
   
    removeExceptionHandler( exceptionHandler );
   
    // remove catch() block
    try {
      iList.delete(
        exceptionHandler,
        iList.getEnd() );
    }
    catch( TargetLostException tle ) {
      Logger.debug( "removing catch-block: %s", tle.toString() );
    }
   
   
    // remove variable creation and before call
   
    InstructionHandle first = null;
   
    // respect the super() and this(...)-call at the
    // beginning of constructors
    if( mGen.getName().equals( CONSTRUCTOR_NAME ) ) {
      first = aFinder.lookUpSuperConstructorCall().getNext();
    }
    else
      first = iList.getStart();
    }
   
    try {
      iList.delete(
        first,
        beforeCall );
    }
    catch( TargetLostException tle ) {
      Logger.debug(
        "removing variable creation and before()-call %s",
        tle.toString() );
    }
   
   
    ExecutionWrapperVariableHandler vHandler
      = new ExecutionWrapperVariableHandler(
        mGen, iList, factory, constPoolGen );
   
    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;
      }
    }
  }
}
TOP

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

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.