Package de.petris.dynamicaspects.util

Source Code of de.petris.dynamicaspects.util.InstructionSearcher$MethodCallConstraint

/*
* 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.util;

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

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ConstantCP;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.ASTORE;
import org.apache.bcel.generic.CPInstruction;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.LocalVariableInstruction;
import org.apache.bcel.util.InstructionFinder;

import de.petris.dynamicaspects.AspectException;
import de.petris.dynamicaspects.wrapper.CallInfo;
import de.petris.dynamicaspects.wrapper.CallMatch;

/**
* A searcher of instructions.
*
* @author Marco Petris
*/
public class InstructionSearcher implements Constants {

 
  private  ConstantPoolGen constPoolGen;
  private  InstructionList iList;
 
 
  /**
   * Constructor.
   *
   * @param constPoolGen the Constant Pool Generator
   *                     which belongs to the instruction list.
   * @param iList the instruction list to search in.
   */
  public InstructionSearcher( ConstantPoolGen constPoolGen, InstructionList iList ) {
   
    this.constPoolGen = constPoolGen;
    this.iList = iList;
  }
 
 
  /**
   * Returns a handle to the super/this(...) constructor call.
   *
   * @return the handle to the super/this(...) constructor call.
   */
  public InstructionHandle lookUpSuperConstructorCall() {
    // construct a finder
    InstructionFinder finder =
      new InstructionFinder( iList );
   
    // do the search
    Iterator iter
      = finder.search(
        Constants.OPCODE_NAMES[Constants.INVOKESPECIAL] );
 
    if( iter.hasNext() ) {
      InstructionHandle[] ih
        = (InstructionHandle[])iter.next();
   
      return ih[0];
     
    }
   
    throw new AspectException(
      "unexpected situation: " +
      "expected super() or this(...) statement but found none!" );
  }

 
  /**
   * Returns a list of {@link CallMatch CallMatch}es matching the given
   * pattern of method invokes/calls.
   *
   * @param invokePattern the pattern for the method calls
   * @return a list of matches
   */
  public List<CallMatch> lookUpMethodCall( Pattern invokePattern ) {
   
    // construct a finder
    InstructionFinder finder =
      new InstructionFinder( iList );
   
    // search all invokes
    Iterator iter
      = finder.search(
        Constants.OPCODE_NAMES[Constants.INVOKEINTERFACE]
        + "|"
        + Constants.OPCODE_NAMES[Constants.INVOKESPECIAL]
        + "|"
        + Constants.OPCODE_NAMES[Constants.INVOKESTATIC]
        + "|"
        + Constants.OPCODE_NAMES[Constants.INVOKEVIRTUAL]
      );
   
    // the return list
    List<CallMatch> rc = new ArrayList<CallMatch>();

    // loop over the search results
    while( iter.hasNext() ) {
     
      InstructionHandle[] ih
        = (InstructionHandle[])iter.next();

      // construct a call info of the invoke
      CallInfo ci = createCallInfo( ih[0] );

      // does this invoke match our pattern?
      if( invokePattern.matcher( ci.getMethodDeclaration() ).matches() ) {
        Logger.debug( "%s matching",
          ci.getMethodDeclaration()  );
        rc.add(
          new CallMatch( ih[0], ci ) );
      }
      else {
        Logger.debug( "%s not matching",
          ci.getMethodDeclaration() );
      }
    }
   
    return rc;
  }
 
  /**
   * Builds a {@link de.petris.dynamicaspects.Wrapper.CallInfo CallInfo} from the
   * InstructionHandle of the method call.
   *
   * @param invoke the InstructionHandle of the method call.
   * @return the callinfo containing details about the method called.
   */
  private CallInfo createCallInfo( InstructionHandle invoke ) {
   
    CPInstruction methodCall =
      (CPInstruction)invoke.getInstruction();
   
    // get infos from the constant pool:
   
    ConstantCP methodConst =
      (ConstantCP)constPoolGen.getConstant(
          methodCall.getIndex() );
   
    ConstantClass classConst =
      (ConstantClass)constPoolGen.getConstant(
        methodConst.getClassIndex() );

    ConstantNameAndType ntConst =
      (ConstantNameAndType)constPoolGen.getConstant(
        methodConst.getNameAndTypeIndex() );
   
      return new CallInfo(
      ((ConstantUtf8)constPoolGen.getConstant(
        classConst.getNameIndex() )).getBytes(),
      ntConst.getName( constPoolGen.getConstantPool() ),
      ntConst.getSignature( constPoolGen.getConstantPool() ) );
  }
 
 
 
  /**
   * Looks up a method call according to the given arguments.
   *
   * @param classname the name of the class the method belongs to
   * @param name the name of the method
   * @param signature the signature of the method
   * @return a list of InstructionHandles matching calls the given method
   */
  public List<InstructionHandle> lookUpMethodCall(
      String classname, String name, String signature  ) {
   
    // get the index of the method
    int idx =
      constPoolGen.lookupMethodref(
        classname, name, signature );
   
    // construct a finder
    InstructionFinder finder =
      new InstructionFinder( iList );
   
    // do the search
    Iterator iter
      = finder.search(
        Constants.OPCODE_NAMES[Constants.INVOKEVIRTUAL],
        new MethodCallConstraint( idx ) );
   
    // the return list
    List<InstructionHandle> rc = new ArrayList<InstructionHandle>();
   
    // loop over the search results
    while( iter.hasNext() ) {
      rc.add( ((InstructionHandle[])iter.next())[0] );
    }
   
    return rc;
  }

  /**
   * Look up all return statments.
   *
   * @return a list of return statments.
   */
  public List<InstructionHandle> lookUpReturnStatements() {
    return lookUpReturnStatements( false );
  }
   
  /**
   * Look up all return statement except the last one.
   *
   * @return a list of return statements
   */
  public List<InstructionHandle> lookUpReturnStatementsSkipLast() {
    return lookUpReturnStatements( true );
  }
 
  /**
   * Look up all return statments.
   *
   * @param skipLast if true the last found return statement
   *           is skipped from the list.
   * @return a list of return statments.
   */
  private List<InstructionHandle> lookUpReturnStatements( boolean skipLast ) {
   
    // construct a finder
    InstructionFinder finder =
      new InstructionFinder( iList );
   
    // do the search
    Iterator iter
      = finder.search( "IRETURN|LRETURN|FRETURN|DRETURN|ARETURN|RETURN" );
   
    List<InstructionHandle> rc = new ArrayList<InstructionHandle>();
   
    while( iter.hasNext() ) {
      InstructionHandle[] ih
        = (InstructionHandle[])iter.next();     
      rc.add( ih[0] );
    }
   
    if( skipLast ) {
      rc.remove( rc.size()-1 );
    }
   
    return rc;
  }
 
 
  /**
   * Looks up the initialization point of the argumentlist with the given index.
   *
   * @param argListVarIdx the local variable index of the argument list
   * @return the InstructionHandle of the initialization
   */
  public InstructionHandle lookUpArgumentListInit( int argListVarIdx ) {
   
    // do the search
    Iterator iter = 
      searchStoreInstruction( argListVarIdx, "ACONST_NULL", "", 1 );
   
    if( iter.hasNext() ) {
      return ((InstructionHandle[])iter.next())[0];
    }
   
    throw new AspectException(
      "unexpected situation: could not find " +
      " initialization of argument info with idx: "
      + ((Integer)argListVarIdx).toString() );
   
  }
 
 
  /**
   * Sets the load points of the call wrapper at the corresponding
   * {@link de.petris.dynamicaspects.wrapper.CallMatch CallMatch}.
   * Each call match leads to a wrapper which handles the aspect
   * execution at the call match. There have to be three loads of the wrapper:
   * a load for the before()-call, a load for the after()-call and a load for the
   * afterException()-call.
   * We need to know these loadpoints to remove the wrapper. 
   *
   * @param wrapperVarIdx the local variable index of the wrapper
   * @param callMatches a list of call matches
   */
  public void setCallWrapperLoads(
      int wrapperVarIdx, List<CallMatch> callMatches ) {
 
    // do the search
    Iterator iter =
      searchLoadInstruction( wrapperVarIdx, "", "", 0 );
   
    Iterator<CallMatch> callMatchesIterator = callMatches.iterator();
   
    while( iter.hasNext() ) {
      InstructionHandle[] beforeLoad
        = (InstructionHandle[])iter.next();
      InstructionHandle[] afterLoad
        = (InstructionHandle[])iter.next();
      InstructionHandle[] afterExcpetionLoad
        = (InstructionHandle[])iter.next();
      callMatchesIterator.next().setLoadInstructions(
        beforeLoad[0], afterLoad[0], afterExcpetionLoad[0] );
     
    }
  }
 
  /**
   * Looks up the stores of the wrapper variable. There a two stores of the
   * wrapper: a null-init-store and a store after the the constructor call.
   * We need  these stores to remove the init of the wrapper variable.  
   *
   * @param wrapperVarIdx the local variable index of the wrapper
   * @return a list of InstructionHandles of the stores
   */
  public List<InstructionHandle> lookUpCallWrapperStores( int wrapperVarIdx ) {
   
    // do the search
    Iterator iter =
      searchStoreInstruction( wrapperVarIdx, "", "", 0 );
   
    List<InstructionHandle> rc = new ArrayList<InstructionHandle>();
   
    while( iter.hasNext() ) {
      InstructionHandle[] ih
        = (InstructionHandle[])iter.next();     
      rc.add( ih[0] );
    }
   
    return rc;
  }
 
  /**
   * Searches load-instructions.
   *
   * @param varIdx the variable index of the variable which will be loaded
   * @param prefix a prefix search string
   * @param postfix a postfix search string
   * @param instructionIdx the index of the instruction in the result list.
   * @return an iterator of the search-results.
   */
  private Iterator searchLoadInstruction(
    int varIdx, String prefix, String postfix, int instructionIdx ) {

    InstructionFinder finder =
      new InstructionFinder( iList );
 
    if( varIdx > 3 ) { // a constraint checks the variable index
      return finder.search(
        prefix + " " +
        "ALOAD " + postfix,
        new LoadStoreConstraint( varIdx, instructionIdx ) );
    }
    else { // the variable index is included in the instruction
      return finder.search(
        prefix + " " 
        + new ALOAD( varIdx ).toString( false )
        + " " + postfix );
    }
  }
 
  /**
   * Searches store-instructions.
   *
   * @param varIdx the variable index of the variable which will be stored
   * @param prefix a prefix search string
   * @param postfix a postfix search string
   * @param instructionIdx the index of the instruction in the result list.
   * @return an iterator of the search-results.
   */
  private Iterator searchStoreInstruction(
      int varIdx, String prefix, String postfix, int instructionIdx ) {

    InstructionFinder finder =
      new InstructionFinder( iList );
 
    if( varIdx > 3 ) {  // a constraint checks the variable index
      return finder.search(
        prefix + " " +
        "ASTORE " + postfix,
        new LoadStoreConstraint( varIdx, instructionIdx ) );
    }
    else { // the variable index is included in the instruction
      return finder.search(
        prefix + " " 
        + new ASTORE( varIdx ).toString( false )
        + " " + postfix );
    }
  }
 
  /**
   * A constraint which checks the local variable index
   * of a load- or store-instruction. We want a specific load or store so we
   * need to check the variable index which is not included in the instruction
   * for load/stores with indices higher than 3, but given as an argument:
   * ASTORE_2 but ASTORE 5
   *
   * @author Marco Petris
   */
  private static class LoadStoreConstraint
    implements InstructionFinder.CodeConstraint {
   
    private int targetIdx;
    private int instructionIdx;
   
    /**
     * Constructs an instance of this constraint.
     *
     * @param targetIdx the index of the local variable
     * @param instructionIdx the index of the load/store-instruction
     *               in the result list
     */
    public LoadStoreConstraint( int targetIdx, int instructionIdx ) {
      this.targetIdx = targetIdx;
      this.instructionIdx = instructionIdx;
    }
   
    /* (non-Javadoc)
     * @see org.apache.bcel.util.InstructionFinder$CodeConstraint#checkCode(org.apache.bcel.generic.InstructionHandle[])
     */
    public boolean checkCode( InstructionHandle[] match ) {
     
      if( ( match[instructionIdx].getInstruction()
          instanceof LocalVariableInstruction )
        &&   
        ( ((LocalVariableInstruction)match[instructionIdx].
            getInstruction()).getIndex() == targetIdx ) ) {
     
       
        return true;
      }
     
      return false;
    }
   
  }
 
  /**
   * A constraint which checks the constant pool index of the target instance
   * of the method call.
   *
   * @author Marco Petris
   */
  private static class MethodCallConstraint
    implements InstructionFinder.CodeConstraint {
     
    private int targetIdx;
   
    /**
     * Constructs an instance of this constraint.
     *
     * @param targetIdx the constant pool index of the target instance
     */
    public MethodCallConstraint( int targetIdx ) {
      this.targetIdx = targetIdx;
    }
   
    /* (non-Javadoc)
     * @see org.apache.bcel.util.InstructionFinder$CodeConstraint#checkCode(org.apache.bcel.generic.InstructionHandle[])
     */
    public boolean checkCode( InstructionHandle[] match ) {
 
      if( ((CPInstruction)match[0].
          getInstruction()).getIndex() == targetIdx ) {
        return true;
      }
     
      return false;
    }
  }
 
}
TOP

Related Classes of de.petris.dynamicaspects.util.InstructionSearcher$MethodCallConstraint

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.