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

Source Code of ch.ethz.inf.iks.jvmai.jvmdi.AspectInterfaceImpl$CompoundAopTag

//
//  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 Developers of the Original Code are Andrei Popovici and
//  Angela Nicoara. Portions created by Andrei Popovici and Angela Nicoara
//  are Copyright (C) 2002 Andrei Popovici and Angela Nicoara.
//  All Rights Reserved.
//
//  Contributor(s):
//  $Id: AspectInterfaceImpl.java,v 1.5 2008/11/18 11:09:31 anicoara Exp $
//  =====================================================================
//

package ch.ethz.inf.iks.jvmai.jvmdi;

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.Iterator;

// used packages/classes
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

import java.net.URL;
import java.io.InputStream;
import java.io.IOException;

import ch.ethz.jvmai.JoinPointHook;
import ch.ethz.jvmai.JVMAIRuntimeException;
import ch.ethz.jvmai.JVMAspectInterface;
import ch.ethz.jvmai.WatchAlreadySetException;
import ch.ethz.jvmai.WatchNotSetException;
import ch.ethz.jvmai.NotInitializedException;
import ch.ethz.jvmai.CannotSetWatchException;
import ch.ethz.jvmai.SunBCELRepositoryInterface;

import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.ReturnInstruction;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;

import org.apache.bcel.util.Repository;
import org.apache.bcel.util.ClassLoaderRepository;

/**
* Interface AspectInterfaceImpl represents the aspect-interface to the jvmai-system.
* It's needed for initialization of the jvmai-system, management of joinpoint-watches and
* for enable or disable event-notification.
* <p>
* An instance of this interface can be obtained by calling the method
* <code>getAspectInterface()</code> of a jvmai-provider (class <code>DebuggerProvider</code>).
* <p>
* <h3>Implementation</h3>
* The implementation of this aspect interface is based on the debugger interface of the  JVM.
* To receive notifications from the JVM, it uses the native code available in this class.
* <p>
*
* <h3>Bugs</h3>
* The JVMDI is not <em> consistent </em> over several  J2SDK versions.
* <ul>
* <li> Startup ignores the openword/closed world assumption and the list of prefixes.
* <li> Under JDK 1.4.*, when throwing an exception within the notification, exception
* events are wrongly sent to the debugger
* <li> Under JDK 1.4.*, the 'GetThreadDepth' JVMDI function (name may be wrong),
* does not always work correctly. Consequently, the current implementation (as of prose 0.18.0)
* relies on the <em>height</em> of a frame relative to the frame which received
* the debugger notification.
* <li> Under JDK 1.4.*, the 'getBytecode' method exported by JVMDI works non-deterministically.
* (first time it works, subsequent times it generates illegal opcodes). The workaround
* uses BCEL and the class loader. However, this may incur performance penalties.
* </ul>
*
* @version  $Revision: 1.5 $
* @author  Andrei Popovici
* @author  Angela Nicoara
*/
public class AspectInterfaceImpl implements JVMAspectInterface, SunBCELRepositoryInterface {
  /**
   * Caches BCEL class definitions, which where fetched to analyze or
   * redefine java classes.
   */
  protected static Repository classRepository = null;//SyntheticRepository.getInstance();

  // load shared library upon class loading.
  static {
    Class x=java.lang.RuntimeException.class;
    try {
      // 1. assume we are an extension
      System.loadLibrary("prosevm");
    }
    catch (java.lang.UnsatisfiedLinkError notInstalled) {

      // 2. no extension, we are in the development tree
      try {
        System.load(System.getProperty("ch.ethz.inf.project.home") +
            System.getProperty("file.separator","/")       +
            "lib"                                          +
            System.getProperty("file.separator","/")       +
            System.getProperty("os.arch")                  +
            System.getProperty("file.separator","/")       +
            System.mapLibraryName("prosevm"));
      }
      catch (java.lang.UnsatisfiedLinkError notInProjectLib) {
        try {
          String path=System.getProperty("ch.ethz.inf.project.home") +
          System.getProperty("file.separator","/")       +
          "site"                                         +
          System.getProperty("file.separator","/")       +
          "lib"                                          +
          System.getProperty("file.separator","/")       +
          System.getProperty("os.arch")                  +
          System.getProperty("file.separator","/")       +
          System.mapLibraryName("prosevm");
          System.load(path);
        }
        catch (java.lang.UnsatisfiedLinkError noProse) {
          throw new RuntimeException(noProse.toString());
        }
      }

    }

  }

  // for JDK 1.5 or above:
  //Map<Field,Object> fieldAccessMap;
  //Map<Field,Object> fieldModificationMap;
  //Map<java.lang.reflect.Member,Object> methodExecutionMap;
  //Map<Class,Object> exceptionThrowMap;
  //Map<Class,Object> exceptionCatchMap;

  // for JDK < 1.5.0:
  Map fieldAccessMap;
  Map fieldModificationMap;
  Map methodExecutionMap;
  Map exceptionThrowMap;
  Map exceptionCatchMap; 

  protected static JoinPointHook hook = null;
  public boolean isInitialized = false; // FIXME; must be made not-public


  static class CompoundAopTag {
    Object entryTag = null;
    Object exitTag = null;
  }

  static ThreadLocal   cflows = new ThreadLocal() {
    public Object initialValue() {
      synchronized(AspectInterfaceImpl.class) {
        return new ControlFlow();
      }

    }
  };

  private ControlFlow getCflow() {
    ControlFlow crtFlow = (ControlFlow)cflows.get();
    if (crtFlow == null) {
      throw new Error("AspectInterface.getCflow: cflow should be always non-null\n");
    }
    return crtFlow;
  }
  private ClassLoader contingencyLoader = null;

  /**
   * Initializes the underlying jvmai system.
   * <p>
   * In addition, this method takes a list of java package-names and a
   * boolean indicating how to interpret this prefixes. The prefixes are
   * used by the jvmai system to determine the set of classes being
   * relevant to the system at all.
   * Depending on the value of <code>openWorldAssumption</code>, prefixes
   * are interpreted as followed:
   * <p>
   * <code>openWorldAssumption == true</code><br>
   * The jvmai-system is instructed to treat ALL classes as relevant,
   * EXCEPT the ones contained in a packages starting with one of the
   * supplied prefixes in <code>packagePrefixes</code>
   * <p>
   * <code>openWorldAssumption == false</code><br>
   * The jvmai-system is instructed to treat as relevant ONLY the classes
   * contained in a packages starting with one of the supplied prefixes
   * in <code>packagePrefixes</code>
   *
   * @param packagePrefixes List of prefixes for java package-names.
   * @param openWorldAssumption Specifies how the prefixes are interpreted.
   * @exception StartupException Use <code>StartupException.getMessage()</code> to get a detailed description
   */
  public synchronized void startup(String[] packagePrefixes, boolean openWorldAssumption) {
    // make sure all classes that are addressed during event notification are loaded already here
    Class toload;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.AbsentInformationException.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.AspectInterfaceImpl.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.AspectInterfaceImpl.CompoundAopTag.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.CodeJoinPointImpl.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.CodeSignatureImpl.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.ControlFlow.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.DebuggerProvider.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.ExceptionJoinPointImpl.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.ExceptionCatchJoinPointImpl.class
    toload=ch.ethz.inf.iks.jvmai.jvmdi.FieldAccessJoinPointImpl.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.FieldJoinPointImpl.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.FieldModificationJoinPointImpl.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.FieldSignatureImpl.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.InvalidObjectException.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.InvalidVmStateException.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.ItemManipulationException.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.JoinPointContext.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.JoinPointLocation.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.MethodExecutionJoinPointImpl.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.ProseVmException.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.SignatureFormatException.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.StackFrameException.class;
    toload=ch.ethz.inf.iks.jvmai.jvmdi.ThreadStateException.class;

    toload=ch.ethz.jvmai.CannotSetWatchException.class;
    toload=ch.ethz.jvmai.CatchClauseSignature.class;
    toload=ch.ethz.jvmai.ClassSpecific.class;
    toload=ch.ethz.jvmai.CodeJoinPoint.class;
    toload=ch.ethz.jvmai.ExceptionJoinPoint.class;
    toload=ch.ethz.jvmai.ExceptionCatchJoinPoint.class
    toload=ch.ethz.jvmai.FieldAccessJoinPoint.class;
    toload=ch.ethz.jvmai.FieldJoinPoint.class;
    toload=ch.ethz.jvmai.FieldModificationJoinPoint.class;
    toload=ch.ethz.jvmai.FieldSignature.class;
    toload=ch.ethz.jvmai.InvalidIdException.class;
    toload=ch.ethz.jvmai.JVMAIException.class;
    toload=ch.ethz.jvmai.JVMAIRuntimeException.class;
    toload=ch.ethz.jvmai.JVMAspectInterface.class;
    toload=ch.ethz.jvmai.JoinPoint.class;
    toload=ch.ethz.jvmai.JoinPointHook.class;
    toload=ch.ethz.jvmai.JoinPointStaticPart.class;
    toload=ch.ethz.jvmai.MethodEntryJoinPoint.class;
    toload=ch.ethz.jvmai.MethodExitJoinPoint.class;
    toload=ch.ethz.jvmai.MethodSignature.class;
    toload=ch.ethz.jvmai.NotInitializedException.class;
    toload=ch.ethz.jvmai.Provider.class;
    toload=ch.ethz.jvmai.Signature.class;
    toload=ch.ethz.jvmai.StartupException.class;
    toload=ch.ethz.jvmai.WatchAlreadySetException.class;
    toload=ch.ethz.jvmai.WatchNotSetException.class;

    Object crtClow=cflows.get();

    // JDK 1.5 or above
    //  methodExecutionMap   = new HashMap<java.lang.reflect.Member,Object>(); 
    //  fieldAccessMap       = new HashMap<Field,Object>();
    //  fieldModificationMap = new HashMap<Field,Object>();
    //  exceptionThrowMap    = new HashMap<Class,Object>();
    //  exceptionCatchMap    = new HashMap<Class,Object>();

    // JDK < 1.5
    methodExecutionMap   = new HashMap();
    fieldAccessMap       = new HashMap();
    fieldModificationMap = new HashMap();
    exceptionThrowMap    = new HashMap();
    exceptionCatchMap    = new HashMap();   

    if (contingencyLoader == null) {
      Iterator i = doGetClasses().iterator();
      while(i.hasNext() && contingencyLoader == null) {
        Class cls=(Class)i.next();
        contingencyLoader=cls.getClassLoader();
      }
    }

    classRepository = new ClassLoaderRepository( contingencyLoader );

    doStartup(packagePrefixes,openWorldAssumption);
    isInitialized = true;
  }

  /**
   * Adds a class file to BCELs repository. This is only required for remote class files,
   * which may not be found in the local class path.
   * <P>
   * Used by {@link ch.ethz.prose.tools.RemoteAspectManagerImpl} to add <CODE>
   * RedefineCut</CODE>s to a remote aspect interface.
   * <P>
   * If the class file can not be readed or added to the repository an error message
   * will be written to stdout, but no exception will be thrown to notify the caller.
   *
   * @param definition the raw class file bytes wrapped in an <CODE>InputStream</CODE>
   * @param name (full qualified) class name in binary notation (p.a. <CODE>ch.ethz.prose.LocalAspectManager</CODE>)
   */
  public void addBCELClassDefiniton( InputStream definition, String name ) {
    if(isClassLoaded(name)) return;

    //System.err.println("AspectInterfaceImpl - addBCELClassDefiniton CLASS NAME = " + name);
    //System.err.println("AspectInterfaceImpl - addBCELClassDefiniton => CLASS = " + definition.getClass().getName());       

    ClassParser cp = new ClassParser(definition,name);
    try{ classRepository.storeClass( cp.parse() ); }
    catch(IOException e) { System.err.println("AspectInterfaceImpl.addBCELClassDefinition(): could not add class definition for " + name); }
  }

  /**
   * Checks if the class is already into the PROSE BCEL repository.
   */
  public boolean isClassLoaded(String name) {
    return null != classRepository.findClass(name);
  }

  /**
   * Sets a JoinPointHook as listener for jvmai-events.
   * Whenever a watched joinpoint is reached or a class
   * is loaded into the virtual machine, this JoinPointHook
   * is notified by the aspect-interface. As long as no
   * JoinPointHook is set (or after setting <code>null</code>),
   * processing of joinpoint- and classload-events is disabled
   * in the jvmai-system.
   *
   * @param jpHook JoinPointHook to set as listener.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   */

  public void setJoinPointHook(JoinPointHook jpHook) {
    if (!isInitialized)
    {
      throw  new NotInitializedException("AspectInterfaceImpl.setJoinPointHook: JVMAI not initialized");
    }
    hook = jpHook;
  }

  public void teardown() {
    isInitialized=false;
    fieldAccessMap = null;
    fieldModificationMap = null;
    methodExecutionMap = null;
    exceptionThrowMap = null;
    classRepository.clear();
    doTeardown();
  }

  private void setWatchPrecondition(Object arg, Object aopTag) {
    if (!isInitialized)
      throw new NotInitializedException("JVMAspectInterface.setWatch: jvmai not initialized");
    if (arg == null)
      throw new NullPointerException("JVMAspectInterface.setWatch: null argument parameter");
    if (aopTag == null)
      throw new IllegalArgumentException("JVMAspectInterface.setWatch: null aopTag value");
  }

  /**
   * Sets a watch on a field access joinpoint.
   *
   * @param f the field to be watched.
   * @param aopTag A user-defined object saved with this watch.
   *               When the joinpoint is reached, this object is
   *               passed to the JoinPointHook as part of the
   *               JoinPoint-instance. This object may be <code>null</code>.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception InvalidIdException There exists no field with id <code>fieldId</code> in class <code>cls</code>.
   * @exception CannotSetWatchException ?
   * @exception WatchAlreadySetException There already exists a access-watch on the field.
   */
  public void setFieldAccessWatch(Field f, Object aopTag) {
    setWatchPrecondition(f,aopTag);
    synchronized(fieldAccessMap) {
      try {
        doSetFieldAccessWatch(f.getDeclaringClass(),f,aopTag);
        fieldAccessMap.put(f,aopTag);
      }
      catch (NullPointerException e)
      { e.printStackTrace();}
    }
  }

  /**
   * Clears a watch on a field access joinpoint.
   *
   * @param field the field beeing watched.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception InvalidIdException There exists no field with id <code>fieldId</code> in class <code>cls</code>.
   * @exception WatchNotSetException There exists no access-watch on the field.
   */
  public void clearFieldAccessWatch(Field field) {
    if (!isInitialized)
      throw new NotInitializedException("JVMAspectInterface.clearFieldAccessWatch: jvmai not initialized");
    if (field == null)
      throw new NullPointerException("JVMAspectInterface.clearFieldAccessWatch: null cls parameter");

    synchronized(fieldAccessMap) {
      doClearFieldAccessWatch(field.getDeclaringClass(),field);
      fieldAccessMap.remove(field);
    }
  }

  /**
   * Sets a watch on a field modification joinpoint.
   *
   * @param field the field to be watched.
   * @param aopTag A user-defined object saved with this watch.
   *               When the joinpoint is reached, this object is
   *               passed to the JoinPointHook as part of the
   *               JoinPoint-instance. This object may be <code>null</code>.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception InvalidIdException There exist no field with id <code>fieldId</code> in class <code>cls</code>.
   * @exception CannotSetWatchException ?
   * @exception WatchAlreadySetException There already exists a modification-watch on the field.
   */
  public void setFieldModificationWatch(Field field, Object aopTag) {
    setWatchPrecondition(field,aopTag);

    synchronized(fieldModificationMap) {
      doSetFieldModificationWatch(field.getDeclaringClass(),field,aopTag);
      fieldModificationMap.put(field,aopTag);
    }
  }

  /**
   * Clears a watch on a field modification joinpoint.
   *
   * @param field the field beeing watched.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception InvalidIdException There exists no field with id <code>fieldId</code> in class <code>cls</code>.
   * @exception WatchNotSetException There exists no modification-watch on the field.
   */
  public void clearFieldModificationWatch(Field field) {

    if (!isInitialized)
      throw new NotInitializedException("JVMAspectInterface.clearFielddModificationWatch: jvmai not initialized");

    synchronized(fieldModificationMap) {
      doClearFieldModificationWatch(field.getDeclaringClass(),field);
      fieldModificationMap.remove(field);
    }
  }


  // workaround for bug in 1.4.1. Subsequent invocations
  // to 'getByteCode' delivers *something else* than
  // the original bytecode. As a consequence, we
  // cache the end locations in a map, to avoid
  // asking the vm twice to obtain the bytecodes for a method.
  //  private HashMap<Method,List> endLocationsMap = new HashMap<Method,List>();  //for JDK 1.5.0   
  private HashMap endLocationsMap = new HashMap();


  /// returns a list of Integer values with the bci of locations
  private List returnLocations(Method m) {

    // use the cache, if possible
    List endLocations = (List)endLocationsMap.get(m);
    if (endLocations != null)
      return endLocations;
    else
      endLocations = new java.util.Vector();


    // obtain the bytecode for the method
    InstructionList iList = null;
    byte[] code = null;
    try {
      code = doGetByteCode(m.getDeclaringClass(),m);
      iList=new InstructionList(code);
    }
    catch (Throwable e) {
      code = doGetByteCodeWithoutJvmdi(m);
      iList= new InstructionList(code);
    }

    // search the bytecode for the method for 'return' bytecodes
    InstructionHandle[] instructions = iList.getInstructionHandles();
    for (int i=0; i < instructions.length; i++) {
      if (instructions[i].getInstruction() instanceof ReturnInstruction)
        endLocations.add(new Integer(instructions[i].getPosition()));
    }

    // treat special case of empty methods
    if ( (code.length == 1 || code.length == 0) && (!endLocations.contains(new Integer(0)))) {
      endLocations.add(new Integer(0));
    }
    endLocationsMap.put(m,endLocations);
    return endLocations;
  }



  private byte[] doGetByteCodeWithoutJvmdi (Method m) {
    ClassLoader cl;

    cl= m.getDeclaringClass().getClassLoader();
    if (cl == null)
      cl = contingencyLoader;

    String fileName = m.getDeclaringClass().getName().replace('.','/') + ".class";
    URL resource = cl.getResource(fileName);
    InputStream classStream = cl.getResourceAsStream(fileName);
    try  {
      ClassParser cparser=new ClassParser(classStream,fileName);
      JavaClass   parsedClass = cparser.parse();
      org.apache.bcel.classfile.Method[] methods = parsedClass.getMethods();
      org.apache.bcel.classfile.Method   javaMethod = null;
      for (int i = 0; i < methods.length && javaMethod == null; i++) {
        if (methods[i].getName().equals(m.getName()) &&
            JNIUtil.jniSignature(m).equals(methods[i].getSignature())) {
          javaMethod=methods[i];
        }
      }
      classStream.close();

      if (javaMethod != null) {
        return javaMethod.getCode().getCode();
      }
      else {
        throw new Error("could not find method in code");
      }

    }
    catch (IOException cannotAccessClassCode) {
      throw new RuntimeException(cannotAccessClassCode.toString());
    }
  }


  /**
   * Sets a watch on a method entry joinpoint.
   *
   * @param m the method to be watched.
   * @param aopTag A user-defined object saved with this watch.
   *               When the joinpoint is reached, this object is
   *               passed to the JoinPointHook as part of the
   *               JoinPoint-instance. This object may be <code>null</code>.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception InvalidIdException There exists no method with id <code>methodId</code> in class <code>cls</code>.
   * @exception CannotSetWatchException The method must not be abstract or native.
   * @exception WatchAlreadySetException There already exists a entry-watch on the method.
   */
  public synchronized void setMethodEntryWatch(Method m, Object aopTag) {
    // preconditions
    setWatchPrecondition(m,aopTag);
    if ((m.getModifiers() & (Modifier.ABSTRACT | Modifier.NATIVE)) != 0)
      throw new CannotSetWatchException("JVMAspectInterface.setMethodEntryWatch: cannot set watches on interfaces");

    // 1. build the aop tag (if not existent); check, if existent the invariant
    CompoundAopTag executionTag = (CompoundAopTag)methodExecutionMap.get(m);


    if (executionTag == null)
      executionTag = new CompoundAopTag();
    if (executionTag.entryTag != null)
      throw new WatchAlreadySetException("JVMAspectInterface.setMethodEntryWatch:" + m);
    else
      executionTag.entryTag=aopTag;

    // if the exit tag is set and 'bci=0' is an end location, we use the
    // execution tag; otherwise we use our own tag
    CompoundAopTag tagToBeSet;
    boolean tagIsCommonToEntryExit = false;
    if (returnLocations(m).contains(new Integer(0))) {
      tagToBeSet=executionTag;
      tagIsCommonToEntryExit = true;
    }
    else {
      tagToBeSet=new CompoundAopTag();
      tagToBeSet.entryTag=aopTag;
      tagToBeSet.exitTag=null;
      tagIsCommonToEntryExit = false;
    }

    // 2. activate the location watch
    try {
      doSetLocationWatch(m.getDeclaringClass(),m,0,tagToBeSet);

    }
    catch (WatchAlreadySetException e) {
      if (!(tagIsCommonToEntryExit && executionTag.exitTag!=null))
        throw new Error("JVMAspectInteface.setMethodEntryWatch:" +
        "JMD reports watch already set, but the exit tag is null");
    }
    methodExecutionMap.put(m,executionTag);

  }

  /**
   * Clears a watch on a method entry joinpoint.
   *
   * @param m the method beeing watched.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception InvalidIdException There exists no method with id <code>methodId</code> in class <code>cls</code>.
   * @exception WatchNotSetException There exists no entry-watch on the method.
   */
  public synchronized void clearMethodEntryWatch(Method m) {

    // preconditions
    if (!isInitialized)
      throw new NotInitializedException("JVMAspectInterface.clearMethodEntryWatch: jvmai not initialized");
    if (m == null)
      throw new NullPointerException("JVMAspectInterface.clearMethodEntryWatch: null m parameter");
    if ((m.getModifiers() & (Modifier.ABSTRACT | Modifier.NATIVE)) != 0)
      throw new CannotSetWatchException("JVMAspectInterface.setMethodEntryWatch: cannot clear watches on interfaces");

    // 1. first check whether we have anything to delete
    CompoundAopTag oldTag = (CompoundAopTag) methodExecutionMap.get(m);
    if (oldTag == null) {
      throw new WatchNotSetException("JVMAspectInterface.clearMethodEntryWatch");
    }

    // 2. then we delete the aopTag
    if (oldTag.entryTag == null)
      throw new WatchNotSetException("JVMAspectInterface.clearMethodEntryWatch:" + m);
    else
      oldTag.entryTag = null;

    // 3. we actually remove the watch if
    //  - no method exit tag is set  OR
    //  - '0' is not an end location
    if (oldTag.exitTag == null || (!returnLocations(m).contains(new Integer(0)))  ) {
      doClearLocationWatch(m.getDeclaringClass(),m,0);
    }

    // 4. clear the tag map, if noboy is interested in this method
    if (oldTag.exitTag == null)
      methodExecutionMap.remove(m);

  }

  /**
   * Sets a watch on a constructor joinpoint.
   *
   * @param m the constructor to be watched.
   * @param aopTag A user-defined object saved with this watch.
   *               When the joinpoint is reached, this object is
   *               passed to the JoinPointHook as part of the
   *               JoinPoint-instance. This object may be <code>null</code>.
   */   
  public void setConstructorWatch(Constructor m, Object aopTag) {
    throw new JVMAIRuntimeException("not implemented");
  }

  /**
   * Clears a watch on a constructor joinpoint.
   *
   * @param m the constructor beeing watched.
   */ 
  public void clearConstructorWatch(Constructor m) {
    throw new JVMAIRuntimeException("not implemented");
  }


  // remove the map only if there are no breakpoints left in the exits;
  /**
   * Sets a watch on a method exit joinpoint.
   *
   * @param m the method beeing watched.
   * @param aopTag A user-defined object saved with this watch.
   *               When the joinpoint is reached, this object is
   *               passed to the JoinPointHook as part of the
   *               JoinPoint-instance. This object may be <code>null</code>.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception InvalidIdException There exists no method with id <code>methodId</code> in class <code>cls</code>.
   * @exception CannotSetWatchException The method must not be abstract or native.
   * @exception WatchAlreadySetException There already exists a exit-watch on the method.
   */
  public synchronized void setMethodExitWatch(Method m, Object aopTag) {
    // preconditions
    setWatchPrecondition(m,aopTag);
    if ((m.getModifiers() & (Modifier.ABSTRACT | Modifier.NATIVE)) != 0)
      throw new CannotSetWatchException("JVMAspectInterface.setMethodExitWatch: cannot set watches on interfaces");

    // 1. check whether it is already set, and then set the tag
    CompoundAopTag compoundTag = (CompoundAopTag)methodExecutionMap.get(m);
    if (compoundTag == null) {
      compoundTag =  new CompoundAopTag();
      methodExecutionMap.put(m,compoundTag);
    }

    if (compoundTag.exitTag != null)
      throw new WatchAlreadySetException("JVMAspectInteface.setMethodExitWatch" + m);
    compoundTag.exitTag = aopTag;

    // 2. find out the end locations of this method (everywhere were 'return' exists + '0' if
    // the method is empty

    List endLocations = returnLocations(m);

    // 3. iterate over locations and set the watch
    Iterator j = endLocations.iterator();
    while (j.hasNext()) {

      CompoundAopTag tagToBeSet;
      int bci = ((Integer)(j.next())).intValue();

      if (bci == 0) {
        // if this return location is ALSO an entry location, we use a (X,Y) compound tag
        tagToBeSet=compoundTag;
      }
      else {
        // if this return location is NOT an entry location, we use a (null,Y) compound tag
        tagToBeSet=new CompoundAopTag();
        tagToBeSet.entryTag=null;
        tagToBeSet.exitTag=aopTag;
      }


      try {
        doSetLocationWatch(m.getDeclaringClass(),m,bci,tagToBeSet);
      }
      catch (WatchAlreadySetException entryWatchOnZeroLengthMethod) {
        // this exception is ok only if the return location is a entry location, too,
        // and the entry location is already set.
        if (compoundTag.entryTag != null && bci == 0)
        {}// everyhting is fine
        else {
          // this is an error, it should have been caputred earlier
          Error x =new Error("JVMAspectInterface.setMethodEntryWatch: " +
              " watch set on exit in spite of non-zero length method:" + m);
          x.printStackTrace();
          throw x;
        }
      }
    }
  }

  /**
   * Clears a watch on a method exit joinpoint.
   *
   * @param m the method beeing watched.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception InvalidIdException There exists no method with id <code>methodId</code> in class <code>cls</code>.
   * @exception WatchNotSetException There exists no exit-watch on the method.
   */
  public synchronized void clearMethodExitWatch(Method m) {
    // preconditions
    if (!isInitialized)
      throw new NotInitializedException("JVMAspectInterface.clearMethodExitWatch: jvmai not initialized");
    if (m == null)
      throw new NullPointerException("JVMAspectInterface.clearMethodExitWatch: null Method parameter");
    if (m.getDeclaringClass().isInterface())
      throw new WatchNotSetException("JVMAspectInterface.clearMethodExitWatch: cannot clear watches on interfaces");

    // 1. obtain the tag to delete
    CompoundAopTag compoundTag = (CompoundAopTag)methodExecutionMap.get(m);

    if (compoundTag == null || compoundTag.exitTag == null) {
      throw new WatchNotSetException("JVMAspectInterface.clearMethodExitWatch: " + m);
    }
    compoundTag.exitTag = null;

    // 2. iterate over the 'return' locations and delete the watches

    Iterator j = returnLocations(m).iterator();


    while (j.hasNext()) {
      int bci = ((Integer)(j.next())).intValue();
      if (bci != || (compoundTag.entryTag == null)) {
        doClearLocationWatch(m.getDeclaringClass(),m,bci);
      }
    }

    // 3. if nobody is interested in this method, delete the compound tag, too
    if (compoundTag.entryTag == null)
      methodExecutionMap.remove(m);
  }

  /**
   * Sets a watch on a exception throw joinpoint.
   *
   * @param cls Exception Class that should be watched.
   * @param aopTag A user-defined object saved with this watch.
   *               When the joinpoint is reached, this object is
   *               passed to the JoinPointHook as part of the
   *               JoinPoint-instance. This object may be <code>null</code>.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception WatchAlreadySetException There already exists a exceptionThrow-watch at this point.
   */
  public void setExceptionThrowWatch(Class cls, Object aopTag) {
    setWatchPrecondition(cls,aopTag);

    synchronized(exceptionThrowMap) {
      doSetExceptionWatch(cls,aopTag);
      exceptionThrowMap.put(cls, aopTag);
    }

  }

  /**
   * Clears a watch on a exception throw joinpoint.
   *
   * @param cls Exception Class that should be watched.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception WatchNotSetException There exists no exceptionThrow-watch at this point.
   */
  public void clearExceptionThrowWatch(Class cls) {

    if (!isInitialized)
      throw new NotInitializedException("JVMAspectInterface.clearExceptionThrowWatch: jvmai not initialized");
    if (cls == null)
      throw new NullPointerException("JVMAspectInterface.clearExceptionThrowWatch: null cls parameter");

    synchronized(exceptionThrowMap) {
      doClearExceptionWatch(cls);
      if (exceptionThrowMap.containsKey(cls))
        exceptionThrowMap.remove(cls);
      else
        throw new WatchNotSetException();
    }

  }


  /**
   * Sets a watch on a exception catch joinpoint.
   *
   * @param cls Exception Class that should be watched.
   * @param aopTag A user-defined object saved with this watch.
   *               When the joinpoint is reached, this object is
   *               passed to the JoinPointHook as part of the
   *               JoinPoint-instance. This object may be <code>null</code>.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception WatchAlreadySetException There already exists a exceptionCatch-watch at this point.
   */
  public void setExceptionCatchWatch(Class cls, Object aopTag) {
    setWatchPrecondition(cls,aopTag);

    synchronized(exceptionCatchMap) {
      doSetExceptionCatchWatch(cls,aopTag);
      exceptionCatchMap.put(cls, aopTag);
    }
  }

  /**
   * Clears a watch on a exception catch joinpoint.
   *
   * @param cls Exception Class that should be watched.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>cls</code> is <code>null</code>.
   * @exception WatchNotSetException There exists no exceptionCatch-watch at this point.
   */
  public void clearExceptionCatchWatch(Class cls) {

    if (!isInitialized)
      throw new NotInitializedException("JVMAspectInterface.clearExceptionCatchWatch: jvmai not initialized");
    if (cls == null)
      throw new NullPointerException("JVMAspectInterface.clearExceptionCatchWatch: null cls parameter");

    synchronized(exceptionCatchMap) {
      doClearExceptionCatchWatch(cls);
      if (exceptionCatchMap.containsKey(cls))
        exceptionCatchMap.remove(cls);
      else
        throw new WatchNotSetException();
    }
  }


  /**
   * Suspend notification regarding the specified thread.
   * Successive calls to <code>suspendNotification</code>
   * have to be balanced by (at least) the same number of calls to
   * <code>resumeNotification</code>, or the jvmai-system
   * will not resume notification.
   *
   * @param thread Thread for which to disable notification.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>thread</code> is <code>null</code>.
   */
  public void suspendNotification(Thread thread) {
    // jvmdi implementation
    if (!isInitialized)
      throw new NotInitializedException("JVMAspectInterface.resumeNotification: jvmai not initialized");

    doSuspendNotification(thread);
  }

  /**
   * Resumes notification regarding the specified thread.
   * Successive calls to <code>resumeNotification</code>
   * without preceding calls to <code>suspendNotification</code>
   * will be ignored by the jvmai-system.
   *
   * @param thread Thread for which to reenable notification.
   * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
   * @exception NullPointerException <code>thread</code> is <code>null</code>.
   */
  public void resumeNotification(Thread thread) {
    // jvmdi implementation     
    if (!isInitialized)
      throw new NotInitializedException("JVMAspectInterface.resumeNotification: jvmai not initialized");
    doResumeNotification(thread);
  }

  public List getLoadedClasses() {
    return doGetClasses();
  }

  //private void doOnClassLoad(Class cls)    //BEFORE-delete
  protected void doOnClassLoad(Class cls) {  //CORECT-BUGFIX ANGY - both methods (from AspectInterfaceImpl and HotswapAspectInterfaceImpl) need to have the same modifiers = have to be PROTECTED
    if (hook==null)
      return;

    hook.onClassLoad(cls);
  }

  private void doOnMethodExecution(MethodExecutionJoinPointImpl jp) {
    // find out whether it is entry, exit, or both
    if (hook==null)
      return;


    Method method = jp.getMethod();
    CompoundAopTag compoundTag = (CompoundAopTag)jp.getAopTag();

    if (compoundTag.entryTag!=null) {
      jp.aopTag = compoundTag.entryTag;
      jp.setKind(true);
      hook.onMethodEntry(jp);
    }
    if (compoundTag.exitTag != null) {
      jp.aopTag = compoundTag.exitTag;
      jp.setKind(false);
      hook.onMethodExit(jp);
    }
  }

  private void doOnFieldAccess(FieldAccessJoinPointImpl jp) {
    if (hook==null)
      return;
    hook.onFieldAccess(jp);
  }

  private void doOnFieldModification(FieldModificationJoinPointImpl jp) {
    if (hook==null)
      return;

    hook.onFieldModification(jp);
  }

  private void doOnExceptionThrow(ExceptionJoinPointImpl jp) {
    if (hook==null) return;

    try {
      hook.onExceptionThrow(jp);
    } catch(Throwable e) {
      System.err.println( "doOnExceptionThrow throws " + e.getClass().getName() + ": " + e.getMessage() );
      StackTraceElement[] stackTrace = e.getStackTrace();
      for( int i = 0; i < stackTrace.length; i++) {
        System.err.println("   " + stackTrace[i]);
      }
    }
  }

  private void doOnExceptionCatch(ExceptionCatchJoinPointImpl jp) {

    if (hook==null) return;

    try  {
      hook.onExceptionCatch(jp);
    } catch (Throwable x) {
      System.err.println("doOnExceptionCatch throws" + x.getClass().getName() + ": " + x.getMessage() );
    }
  }



  private native void doStartup(Object[] prefixes, boolean openWorld);
  private native void doTeardown();
  private native void doSetLocationWatch(Class c, Method m, int bci,Object tag);
  private native void doClearLocationWatch(Class c, Method m, int bci);
  private native void doSetFieldAccessWatch(Class c, Field f,Object tag);
  private native void doClearFieldAccessWatch(Class c, Field f);
  private native void doSetFieldModificationWatch(Class c, Field f, Object tag);
  private native void doClearFieldModificationWatch(Class c, Field f);
  private native void doSetExceptionWatch(Class throwableClass, Object tag);
  private native void doClearExceptionWatch(Class trowableClass);
  private native void doSetExceptionCatchWatch(Class throwableClass, Object tag)
  private native void doClearExceptionCatchWatch(Class trowableClass)
  private native byte [] doGetByteCode(Class c, Method m);
  private native List doGetClasses();
  private native void doSuspendNotification(Thread t);
  private native void doResumeNotification(Thread t);
}
TOP

Related Classes of ch.ethz.inf.iks.jvmai.jvmdi.AspectInterfaceImpl$CompoundAopTag

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.