0001: // This file is part of the prose package.
0002: //
0003: // The contents of this file are subject to the Mozilla Public License
0004: // Version 1.1 (the "License"); you may not use this file except in
0005: // compliance with the License. You may obtain a copy of the License at
0006: // http://www.mozilla.org/MPL//
0007: // Software distributed under the License is distributed on an "AS IS" basis,
0008: // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0009: // for the specific language governing rights and limitations under the
0010: // License.
0011: //
0012: // The Original Code is prose.
0013: //
0014: // The Initial Developer of the Original Code is Andrei Popovici.
0015: // The code has been extended by Angela Nicoara.
0016: // Portions created by Andrei Popovici are Copyright (C) 2002 Andrei Popovici.
0017: // All Rights Reserved.
0018: //
0019: // Contributor(s):
0020: // $Id: AspectInterfaceImpl.java,v 1.3 2005/06/26 18:06:03 anicoara Exp $
0021: // =====================================================================
0022: //
0023: // (history at end)
0024: //
0025:
0026: package ch.ethz.inf.iks.jvmai.jvmdi;
0027:
0028: import java.util.Map;
0029: import java.util.HashMap;
0030: import java.util.List;
0031: import java.util.Iterator;
0032:
0033: // used packages/classes
0034: import java.lang.reflect.Field;
0035: import java.lang.reflect.Method;
0036: import java.lang.reflect.Constructor;
0037: import java.lang.reflect.Modifier;
0038:
0039: import java.net.URL;
0040: import java.io.InputStream;
0041: import java.io.IOException;
0042:
0043: import ch.ethz.jvmai.JoinPointHook;
0044: import ch.ethz.jvmai.JVMAIRuntimeException;
0045: import ch.ethz.jvmai.JVMAspectInterface;
0046: import ch.ethz.jvmai.WatchAlreadySetException;
0047: import ch.ethz.jvmai.WatchNotSetException;
0048: import ch.ethz.jvmai.NotInitializedException;
0049: import ch.ethz.jvmai.CannotSetWatchException;
0050: import ch.ethz.jvmai.SunBCELRepositoryInterface;
0051:
0052: import org.apache.bcel.generic.InstructionHandle;
0053: import org.apache.bcel.generic.InstructionList;
0054: import org.apache.bcel.generic.ReturnInstruction;
0055: import org.apache.bcel.classfile.ClassParser;
0056: import org.apache.bcel.classfile.JavaClass;
0057:
0058: import org.apache.bcel.util.Repository;
0059: import org.apache.bcel.util.ClassLoaderRepository;
0060:
0061: /**
0062: * Interface AspectInterfaceImpl represents the aspect-interface to the jvmai-system.
0063: * It's needed for initialization of the jvmai-system, management of joinpoint-watches and
0064: * for enable or disable event-notification.
0065: * <p>
0066: * An instance of this interface can be obtained by calling the method
0067: * <code>getAspectInterface()</code> of a jvmai-provider (class <code>DebuggerProvider</code>).
0068: * <p>
0069: * <h3>Implementation</h3>
0070: * The implementation of this aspect interface is based on the debugger interface of the JVM.
0071: * To receive notifications from the JVM, it uses the native code available in this class.
0072: * <p>
0073: *
0074: * <h3>Bugs</h3>
0075: * The JVMDI is not <em> consistent </em> over serveral J2SDK versions.
0076: * <ul>
0077: * <li> Startup ignores the openword/closed world assumption and the list of prefixes.
0078: * <li> Under JDK 1.4.*, when throwing an exception within the notificaion, exception
0079: * events are wrongly sent to the debugger
0080: * <li> Under JDK 1.4.*, the 'GetThreadDepth' JVMDI function (name may be wrong),
0081: * does not always work correclty. Consequently, the current implementation (as of prose 0.18.0)
0082: * relies on the <em>height</em> of a frame relative to the frame which received
0083: * the debugger notifcation.
0084: * <li> Under JDK 1.4.*, the 'getBytecode' method exported by JVMDI works non-deterministically.
0085: * (first time it works, subsequent times it generates illegal opcodes). The workaround
0086: * uses BCEL and the class loader. However, this may incurr performance penalties.
0087: * </ul>
0088: *
0089: * @version $Revision: 1.3 $
0090: * @author Andrei Popovici
0091: * @author Angela Nicoara
0092: */
0093: public class AspectInterfaceImpl implements JVMAspectInterface,
0094: SunBCELRepositoryInterface {
0095: /**
0096: * Caches BCEL class definitions, which where fetched to analyze or
0097: * redefine java classes.
0098: */
0099: protected static Repository classRepository = null;//SyntheticRepository.getInstance();
0100:
0101: // load shared library upon class loading.
0102: static {
0103: Class x = java.lang.RuntimeException.class;
0104: try {
0105: // 1. assume we are an extension
0106: System.loadLibrary("prosevm");
0107: } catch (java.lang.UnsatisfiedLinkError notInstalled) {
0108:
0109: // 2. no extension, we are in the development tree
0110: try {
0111: System.load(System
0112: .getProperty("ch.ethz.inf.project.home")
0113: + System.getProperty("file.separator", "/")
0114: + "lib"
0115: + System.getProperty("file.separator", "/")
0116: + System.getProperty("os.arch")
0117: + System.getProperty("file.separator", "/")
0118: + System.mapLibraryName("prosevm"));
0119: } catch (java.lang.UnsatisfiedLinkError notInProjectLib) {
0120: try {
0121: String path = System
0122: .getProperty("ch.ethz.inf.project.home")
0123: + System.getProperty("file.separator", "/")
0124: + "site"
0125: + System.getProperty("file.separator", "/")
0126: + "lib"
0127: + System.getProperty("file.separator", "/")
0128: + System.getProperty("os.arch")
0129: + System.getProperty("file.separator", "/")
0130: + System.mapLibraryName("prosevm");
0131: System.load(path);
0132: } catch (java.lang.UnsatisfiedLinkError noProse) {
0133: throw new RuntimeException(noProse.toString());
0134: }
0135: }
0136:
0137: }
0138:
0139: }
0140:
0141: // for JDK 1.5 or above:
0142: //Map<Field,Object> fieldAccessMap;
0143: //Map<Field,Object> fieldModificationMap;
0144: //Map<java.lang.reflect.Member,Object> methodExecutionMap;
0145: //Map<Class,Object> exceptionThrowMap;
0146: //Map<Class,Object> exceptionCatchMap;
0147:
0148: // for JDK < 1.5.0:
0149: Map fieldAccessMap;
0150: Map fieldModificationMap;
0151: Map methodExecutionMap;
0152: Map exceptionThrowMap;
0153: Map exceptionCatchMap;
0154:
0155: protected static JoinPointHook hook = null;
0156: public boolean isInitialized = false; // FIXME; must be made not-public
0157:
0158: static class CompoundAopTag {
0159: Object entryTag = null;
0160: Object exitTag = null;
0161: }
0162:
0163: static ThreadLocal cflows = new ThreadLocal() {
0164: public Object initialValue() {
0165: synchronized (AspectInterfaceImpl.class) {
0166: return new ControlFlow();
0167: }
0168:
0169: }
0170: };
0171:
0172: private ControlFlow getCflow() {
0173: ControlFlow crtFlow = (ControlFlow) cflows.get();
0174: if (crtFlow == null) {
0175: throw new Error(
0176: "AspectInterface.getCflow: cflow should be always non-null\n");
0177: }
0178: return crtFlow;
0179: }
0180:
0181: private ClassLoader contingencyLoader = null;
0182:
0183: /**
0184: * Initializes the underlying jvmai system.
0185: * <p>
0186: * In addition, this method takes a list of java package-names and a
0187: * boolean indicating how to interpret this prefixes. The prefixes are
0188: * used by the jvmai system to determine the set of classes beeing
0189: * relevant to the system at all.
0190: * Depeding on the value of <code>openWorldAssumption</code>, prefixes
0191: * are interpreted as followed:
0192: * <p>
0193: * <code>openWorldAssumption == true</code><br>
0194: * The jvmai-system is instructed to treat ALL classes as relevant,
0195: * EXCEPT the ones contained in a packages starting with one of the
0196: * supplied prefixes in <code>packagePrefixes</code>
0197: * <p>
0198: * <code>openWorldAssumption == false</code><br>
0199: * The jvmai-system is instructed to treat as relevant ONLY the classes
0200: * contained in a packages starting with one of the supplied prefixes
0201: * in <code>packagePrefixes</code>
0202: *
0203: * @param packagePrefixes List of prefixes for java package-names.
0204: * @param openWorldAssumption Specifies how the prefixes are interpreted.
0205: * @exception StartupException Use <code>StartupException.getMessage()</code> to get a detailed description
0206: */
0207: public synchronized void startup(String[] packagePrefixes,
0208: boolean openWorldAssumption) {
0209: // make sure all classes that are addressed during event notification are loaded already here
0210: Class toload;
0211: toload = ch.ethz.inf.iks.jvmai.jvmdi.AbsentInformationException.class;
0212: toload = ch.ethz.inf.iks.jvmai.jvmdi.AspectInterfaceImpl.class;
0213: toload = ch.ethz.inf.iks.jvmai.jvmdi.AspectInterfaceImpl.CompoundAopTag.class;
0214: toload = ch.ethz.inf.iks.jvmai.jvmdi.CodeJoinPointImpl.class;
0215: toload = ch.ethz.inf.iks.jvmai.jvmdi.CodeSignatureImpl.class;
0216: toload = ch.ethz.inf.iks.jvmai.jvmdi.ControlFlow.class;
0217: toload = ch.ethz.inf.iks.jvmai.jvmdi.DebuggerProvider.class;
0218: toload = ch.ethz.inf.iks.jvmai.jvmdi.ExceptionJoinPointImpl.class;
0219: toload = ch.ethz.inf.iks.jvmai.jvmdi.ExceptionCatchJoinPointImpl.class;
0220: toload = ch.ethz.inf.iks.jvmai.jvmdi.FieldAccessJoinPointImpl.class;
0221: toload = ch.ethz.inf.iks.jvmai.jvmdi.FieldJoinPointImpl.class;
0222: toload = ch.ethz.inf.iks.jvmai.jvmdi.FieldModificationJoinPointImpl.class;
0223: toload = ch.ethz.inf.iks.jvmai.jvmdi.FieldSignatureImpl.class;
0224: toload = ch.ethz.inf.iks.jvmai.jvmdi.InvalidObjectException.class;
0225: toload = ch.ethz.inf.iks.jvmai.jvmdi.InvalidVmStateException.class;
0226: toload = ch.ethz.inf.iks.jvmai.jvmdi.ItemManipulationException.class;
0227: toload = ch.ethz.inf.iks.jvmai.jvmdi.JoinPointContext.class;
0228: toload = ch.ethz.inf.iks.jvmai.jvmdi.JoinPointLocation.class;
0229: toload = ch.ethz.inf.iks.jvmai.jvmdi.MethodExecutionJoinPointImpl.class;
0230: toload = ch.ethz.inf.iks.jvmai.jvmdi.ProseVmException.class;
0231: toload = ch.ethz.inf.iks.jvmai.jvmdi.SignatureFormatException.class;
0232: toload = ch.ethz.inf.iks.jvmai.jvmdi.StackFrameException.class;
0233: toload = ch.ethz.inf.iks.jvmai.jvmdi.ThreadStateException.class;
0234:
0235: toload = ch.ethz.jvmai.CannotSetWatchException.class;
0236: toload = ch.ethz.jvmai.CatchClauseSignature.class;
0237: toload = ch.ethz.jvmai.ClassSpecific.class;
0238: toload = ch.ethz.jvmai.CodeJoinPoint.class;
0239: toload = ch.ethz.jvmai.ExceptionJoinPoint.class;
0240: toload = ch.ethz.jvmai.ExceptionCatchJoinPoint.class;
0241: toload = ch.ethz.jvmai.FieldAccessJoinPoint.class;
0242: toload = ch.ethz.jvmai.FieldJoinPoint.class;
0243: toload = ch.ethz.jvmai.FieldModificationJoinPoint.class;
0244: toload = ch.ethz.jvmai.FieldSignature.class;
0245: toload = ch.ethz.jvmai.InvalidIdException.class;
0246: toload = ch.ethz.jvmai.JVMAIException.class;
0247: toload = ch.ethz.jvmai.JVMAIRuntimeException.class;
0248: toload = ch.ethz.jvmai.JVMAspectInterface.class;
0249: toload = ch.ethz.jvmai.JoinPoint.class;
0250: toload = ch.ethz.jvmai.JoinPointHook.class;
0251: toload = ch.ethz.jvmai.JoinPointStaticPart.class;
0252: toload = ch.ethz.jvmai.MethodEntryJoinPoint.class;
0253: toload = ch.ethz.jvmai.MethodExitJoinPoint.class;
0254: toload = ch.ethz.jvmai.MethodSignature.class;
0255: toload = ch.ethz.jvmai.NotInitializedException.class;
0256: toload = ch.ethz.jvmai.Provider.class;
0257: toload = ch.ethz.jvmai.Signature.class;
0258: toload = ch.ethz.jvmai.StartupException.class;
0259: toload = ch.ethz.jvmai.WatchAlreadySetException.class;
0260: toload = ch.ethz.jvmai.WatchNotSetException.class;
0261:
0262: Object crtClow = cflows.get();
0263:
0264: // JDK 1.5 or above
0265: // methodExecutionMap = new HashMap<java.lang.reflect.Member,Object>();
0266: // fieldAccessMap = new HashMap<Field,Object>();
0267: // fieldModificationMap = new HashMap<Field,Object>();
0268: // exceptionThrowMap = new HashMap<Class,Object>();
0269: // exceptionCatchMap = new HashMap<Class,Object>();
0270:
0271: // JDK < 1.5
0272: methodExecutionMap = new HashMap();
0273: fieldAccessMap = new HashMap();
0274: fieldModificationMap = new HashMap();
0275: exceptionThrowMap = new HashMap();
0276: exceptionCatchMap = new HashMap();
0277:
0278: if (contingencyLoader == null) {
0279: Iterator i = doGetClasses().iterator();
0280: while (i.hasNext() && contingencyLoader == null) {
0281: Class cls = (Class) i.next();
0282: contingencyLoader = cls.getClassLoader();
0283: }
0284: }
0285:
0286: classRepository = new ClassLoaderRepository(contingencyLoader);
0287:
0288: doStartup(packagePrefixes, openWorldAssumption);
0289: isInitialized = true;
0290: }
0291:
0292: /**
0293: * Adds a class file to BCELs repository. This is only required for remote class files,
0294: * which may not be found in the local class path.
0295: * <P>
0296: * Used by {@link ch.ethz.prose.tools.RemoteAspectManagerImpl} to add <CODE>
0297: * RedefineCut</CODE>s to a remote aspect interface.
0298: * <P>
0299: * If the class file can not be readed or added to the repository an error message
0300: * will be written to stdout, but no exception will be thrown to notify the caller.
0301: *
0302: * @param definition the raw class file bytes wrapped in an <CODE>InputStream</CODE>
0303: * @param name (full qualified) class name in binary notation (p.a. <CODE>ch.ethz.prose.LocalAspectManager</CODE>)
0304: */
0305: public void addBCELClassDefiniton(InputStream definition,
0306: String name) {
0307: if (isClassLoaded(name))
0308: return;
0309: ClassParser cp = new ClassParser(definition, name);
0310: try {
0311: classRepository.storeClass(cp.parse());
0312: } catch (IOException e) {
0313: System.err
0314: .println("AspectInterfaceImpl.addBCELClassDefinition(): could not add class definition for "
0315: + name);
0316: }
0317: }
0318:
0319: /**
0320: * Checks if the class is already into the PROSE BCEL repository.
0321: */
0322: public boolean isClassLoaded(String name) {
0323: return null != classRepository.findClass(name);
0324: }
0325:
0326: /**
0327: * Sets a JoinPointHook as listener for jvmai-events.
0328: * Whenever a watched joinpoint is reached or a class
0329: * is loaded into the virtual machine, this JoinPointHook
0330: * is notified by the aspect-interface. As long as no
0331: * JoinPointHook is set (or after setting <code>null</code>),
0332: * processing of joinpoint- and classload-events is disabled
0333: * in the jvmai-system.
0334: *
0335: * @param jpHook JoinPointHook to set as listener.
0336: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0337: */
0338:
0339: public void setJoinPointHook(JoinPointHook jpHook) {
0340: if (!isInitialized) {
0341: throw new NotInitializedException(
0342: "AspectInterfaceImpl.setJoinPointHook: JVMAI not initialized");
0343: }
0344: hook = jpHook;
0345: }
0346:
0347: public void teardown() {
0348: isInitialized = false;
0349: fieldAccessMap = null;
0350: fieldModificationMap = null;
0351: methodExecutionMap = null;
0352: exceptionThrowMap = null;
0353: classRepository.clear();
0354: doTeardown();
0355: }
0356:
0357: private void setWatchPrecondition(Object arg, Object aopTag) {
0358: if (!isInitialized)
0359: throw new NotInitializedException(
0360: "JVMAspectInterface.setWatch: jvmai not initialized");
0361: if (arg == null)
0362: throw new NullPointerException(
0363: "JVMAspectInterface.setWatch: null argument parameter");
0364: if (aopTag == null)
0365: throw new IllegalArgumentException(
0366: "JVMAspectInterface.setWatch: null aopTag value");
0367: }
0368:
0369: /**
0370: * Sets a watch on a field access joinpoint.
0371: *
0372: * @param f the field to be watched.
0373: * @param aopTag A user-defined object saved with this watch.
0374: * When the joinpoint is reached, this object is
0375: * passed to the JoinPointHook as part of the
0376: * JoinPoint-instance. This object may be <code>null</code>.
0377: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0378: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0379: * @exception InvalidIdException There exists no field with id <code>fieldId</code> in class <code>cls</code>.
0380: * @exception CannotSetWatchException ?
0381: * @exception WatchAlreadySetException There already exists a access-watch on the field.
0382: */
0383: public void setFieldAccessWatch(Field f, Object aopTag) {
0384: setWatchPrecondition(f, aopTag);
0385: synchronized (fieldAccessMap) {
0386: try {
0387: doSetFieldAccessWatch(f.getDeclaringClass(), f, aopTag);
0388: fieldAccessMap.put(f, aopTag);
0389: } catch (NullPointerException e) {
0390: e.printStackTrace();
0391: }
0392: }
0393: }
0394:
0395: /**
0396: * Clears a watch on a field access joinpoint.
0397: *
0398: * @param field the field beeing watched.
0399: * @exception NotInitializedException Aspect-interface has not been intialized yet. Call <code>setup</code> first.
0400: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0401: * @exception InvalidIdException There exists no field with id <code>fieldId</code> in class <code>cls</code>.
0402: * @exception WatchNotSetException There exists no access-watch on the field.
0403: */
0404: public void clearFieldAccessWatch(Field field) {
0405: if (!isInitialized)
0406: throw new NotInitializedException(
0407: "JVMAspectInterface.clearFieldAccessWatch: jvmai not initialized");
0408: if (field == null)
0409: throw new NullPointerException(
0410: "JVMAspectInterface.clearFieldAccessWatch: null cls parameter");
0411:
0412: synchronized (fieldAccessMap) {
0413: doClearFieldAccessWatch(field.getDeclaringClass(), field);
0414: fieldAccessMap.remove(field);
0415: }
0416: }
0417:
0418: /**
0419: * Sets a watch on a field modification joinpoint.
0420: *
0421: * @param field the field to be watched.
0422: * @param aopTag A user-defined object saved with this watch.
0423: * When the joinpoint is reached, this object is
0424: * passed to the JoinPointHook as part of the
0425: * JoinPoint-instance. This object may be <code>null</code>.
0426: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0427: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0428: * @exception InvalidIdException There exist no field with id <code>fieldId</code> in class <code>cls</code>.
0429: * @exception CannotSetWatchException ?
0430: * @exception WatchAlreadySetException There already exists a modification-watch on the field.
0431: */
0432: public void setFieldModificationWatch(Field field, Object aopTag) {
0433: setWatchPrecondition(field, aopTag);
0434:
0435: synchronized (fieldModificationMap) {
0436: doSetFieldModificationWatch(field.getDeclaringClass(),
0437: field, aopTag);
0438: fieldModificationMap.put(field, aopTag);
0439: }
0440: }
0441:
0442: /**
0443: * Clears a watch on a field modification joinpoint.
0444: *
0445: * @param field the field beeing watched.
0446: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0447: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0448: * @exception InvalidIdException There exists no field with id <code>fieldId</code> in class <code>cls</code>.
0449: * @exception WatchNotSetException There exists no modification-watch on the field.
0450: */
0451: public void clearFieldModificationWatch(Field field) {
0452:
0453: if (!isInitialized)
0454: throw new NotInitializedException(
0455: "JVMAspectInterface.clearFielddModificationWatch: jvmai not initialized");
0456:
0457: synchronized (fieldModificationMap) {
0458: doClearFieldModificationWatch(field.getDeclaringClass(),
0459: field);
0460: fieldModificationMap.remove(field);
0461: }
0462: }
0463:
0464: // workaround for bug in 1.4.1. Subsequent invocations
0465: // to 'getByteCode' deliveres *something else* than
0466: // the original bytecode. As a consequence, we
0467: // cache the end locations in a map, to avoid
0468: // asking the vm twice to obtain the bytecodes for a method.
0469: // private HashMap<Method,List> endLocationsMap = new HashMap<Method,List>(); //for JDK 1.5.0
0470: private HashMap endLocationsMap = new HashMap();
0471:
0472: /// returns a list of Integer values with the bci of locations
0473: private List returnLocations(Method m) {
0474:
0475: // use the cache, if possible
0476: List endLocations = (List) endLocationsMap.get(m);
0477: if (endLocations != null)
0478: return endLocations;
0479: else
0480: endLocations = new java.util.Vector();
0481:
0482: // obtain the bytecode for the method
0483: InstructionList iList = null;
0484: byte[] code = null;
0485: try {
0486: code = doGetByteCode(m.getDeclaringClass(), m);
0487: iList = new InstructionList(code);
0488: } catch (Throwable e) {
0489: code = doGetByteCodeWithoutJvmdi(m);
0490: iList = new InstructionList(code);
0491: }
0492:
0493: // search the bytecode for the method for 'return' bytecodes
0494: InstructionHandle[] instructions = iList
0495: .getInstructionHandles();
0496: for (int i = 0; i < instructions.length; i++) {
0497: if (instructions[i].getInstruction() instanceof ReturnInstruction)
0498: endLocations.add(new Integer(instructions[i]
0499: .getPosition()));
0500: }
0501:
0502: // treat special case of empty methods
0503: if ((code.length == 1 || code.length == 0)
0504: && (!endLocations.contains(new Integer(0)))) {
0505: endLocations.add(new Integer(0));
0506: }
0507: endLocationsMap.put(m, endLocations);
0508: return endLocations;
0509: }
0510:
0511: private byte[] doGetByteCodeWithoutJvmdi(Method m) {
0512: ClassLoader cl;
0513:
0514: cl = m.getDeclaringClass().getClassLoader();
0515: if (cl == null)
0516: cl = contingencyLoader;
0517:
0518: String fileName = m.getDeclaringClass().getName().replace('.',
0519: '/')
0520: + ".class";
0521: URL resource = cl.getResource(fileName);
0522: InputStream classStream = cl.getResourceAsStream(fileName);
0523: try {
0524: ClassParser cparser = new ClassParser(classStream, fileName);
0525: JavaClass parsedClass = cparser.parse();
0526: org.apache.bcel.classfile.Method[] methods = parsedClass
0527: .getMethods();
0528: org.apache.bcel.classfile.Method javaMethod = null;
0529: for (int i = 0; i < methods.length && javaMethod == null; i++) {
0530: if (methods[i].getName().equals(m.getName())
0531: && JNIUtil.jniSignature(m).equals(
0532: methods[i].getSignature())) {
0533: javaMethod = methods[i];
0534: }
0535: }
0536: classStream.close();
0537:
0538: if (javaMethod != null) {
0539: return javaMethod.getCode().getCode();
0540: } else {
0541: throw new Error("could not find method in code");
0542: }
0543:
0544: } catch (IOException cannotAccessClassCode) {
0545: throw new RuntimeException(cannotAccessClassCode.toString());
0546: }
0547: }
0548:
0549: /**
0550: * Sets a watch on a method entry joinpoint.
0551: *
0552: * @param m the method to be watched.
0553: * @param aopTag A user-defined object saved with this watch.
0554: * When the joinpoint is reached, this object is
0555: * passed to the JoinPointHook as part of the
0556: * JoinPoint-instance. This object may be <code>null</code>.
0557: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0558: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0559: * @exception InvalidIdException There exists no method with id <code>methodId</code> in class <code>cls</code>.
0560: * @exception CannotSetWatchException The method must not be abstract or native.
0561: * @exception WatchAlreadySetException There already exists a entry-watch on the method.
0562: */
0563: public synchronized void setMethodEntryWatch(Method m, Object aopTag) {
0564: // preconditions
0565: setWatchPrecondition(m, aopTag);
0566: if ((m.getModifiers() & (Modifier.ABSTRACT | Modifier.NATIVE)) != 0)
0567: throw new CannotSetWatchException(
0568: "JVMAspectInterface.setMethodEntryWatch: cannot set watches on interfaces");
0569:
0570: // 1. build the aop tag (if not existent); check, if existent the invariant
0571: CompoundAopTag executionTag = (CompoundAopTag) methodExecutionMap
0572: .get(m);
0573:
0574: if (executionTag == null)
0575: executionTag = new CompoundAopTag();
0576: if (executionTag.entryTag != null)
0577: throw new WatchAlreadySetException(
0578: "JVMAspectInterface.setMethodEntryWatch:" + m);
0579: else
0580: executionTag.entryTag = aopTag;
0581:
0582: // if the exit tag is set and 'bci=0' is an end location, we use the
0583: // execution tag; otherwise we use our own tag
0584: CompoundAopTag tagToBeSet;
0585: boolean tagIsCommonToEntryExit = false;
0586: if (returnLocations(m).contains(new Integer(0))) {
0587: tagToBeSet = executionTag;
0588: tagIsCommonToEntryExit = true;
0589: } else {
0590: tagToBeSet = new CompoundAopTag();
0591: tagToBeSet.entryTag = aopTag;
0592: tagToBeSet.exitTag = null;
0593: tagIsCommonToEntryExit = false;
0594: }
0595:
0596: // 2. activate the location watch
0597: try {
0598: doSetLocationWatch(m.getDeclaringClass(), m, 0, tagToBeSet);
0599:
0600: } catch (WatchAlreadySetException e) {
0601: if (!(tagIsCommonToEntryExit && executionTag.exitTag != null))
0602: throw new Error(
0603: "JVMAspectInteface.setMethodEntryWatch:"
0604: + "JMD reports watch already set, but the exit tag is null");
0605: }
0606: methodExecutionMap.put(m, executionTag);
0607:
0608: }
0609:
0610: /**
0611: * Clears a watch on a method entry joinpoint.
0612: *
0613: * @param m the method beeing watched.
0614: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0615: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0616: * @exception InvalidIdException There exists no method with id <code>methodId</code> in class <code>cls</code>.
0617: * @exception WatchNotSetException There exists no entry-watch on the method.
0618: */
0619: public synchronized void clearMethodEntryWatch(Method m) {
0620:
0621: // preconditions
0622: if (!isInitialized)
0623: throw new NotInitializedException(
0624: "JVMAspectInterface.clearMethodEntryWatch: jvmai not initialized");
0625: if (m == null)
0626: throw new NullPointerException(
0627: "JVMAspectInterface.clearMethodEntryWatch: null m parameter");
0628: if ((m.getModifiers() & (Modifier.ABSTRACT | Modifier.NATIVE)) != 0)
0629: throw new CannotSetWatchException(
0630: "JVMAspectInterface.setMethodEntryWatch: cannot clear watches on interfaces");
0631:
0632: // 1. first check whether we have anything to delete
0633: CompoundAopTag oldTag = (CompoundAopTag) methodExecutionMap
0634: .get(m);
0635: if (oldTag == null) {
0636: throw new WatchNotSetException(
0637: "JVMAspectInterface.clearMethodEntryWatch");
0638: }
0639:
0640: // 2. then we delete the aopTag
0641: if (oldTag.entryTag == null)
0642: throw new WatchNotSetException(
0643: "JVMAspectInterface.clearMethodEntryWatch:" + m);
0644: else
0645: oldTag.entryTag = null;
0646:
0647: // 3. we actually remove the watch if
0648: // - no method exit tag is set OR
0649: // - '0' is not an end location
0650: if (oldTag.exitTag == null
0651: || (!returnLocations(m).contains(new Integer(0)))) {
0652: doClearLocationWatch(m.getDeclaringClass(), m, 0);
0653: }
0654:
0655: // 4. clear the tag map, if noboy is interested in this method
0656: if (oldTag.exitTag == null)
0657: methodExecutionMap.remove(m);
0658:
0659: }
0660:
0661: /**
0662: * Sets a watch on a constructor joinpoint.
0663: *
0664: * @param m the constructor to be watched.
0665: * @param aopTag A user-defined object saved with this watch.
0666: * When the joinpoint is reached, this object is
0667: * passed to the JoinPointHook as part of the
0668: * JoinPoint-instance. This object may be <code>null</code>.
0669: */
0670: public void setConstructorWatch(Constructor m, Object aopTag) {
0671: throw new JVMAIRuntimeException("not implemented");
0672: }
0673:
0674: /**
0675: * Clears a watch on a constructor joinpoint.
0676: *
0677: * @param m the constructor beeing watched.
0678: */
0679: public void clearConstructorWatch(Constructor m) {
0680: throw new JVMAIRuntimeException("not implemented");
0681: }
0682:
0683: // remove the map only if there are no breakpoints left in the exits;
0684: /**
0685: * Sets a watch on a method exit joinpoint.
0686: *
0687: * @param m the method beeing watched.
0688: * @param aopTag A user-defined object saved with this watch.
0689: * When the joinpoint is reached, this object is
0690: * passed to the JoinPointHook as part of the
0691: * JoinPoint-instance. This object may be <code>null</code>.
0692: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0693: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0694: * @exception InvalidIdException There exists no method with id <code>methodId</code> in class <code>cls</code>.
0695: * @exception CannotSetWatchException The method must not be abstract or native.
0696: * @exception WatchAlreadySetException There already exists a exit-watch on the method.
0697: */
0698: public synchronized void setMethodExitWatch(Method m, Object aopTag) {
0699: // preconditions
0700: setWatchPrecondition(m, aopTag);
0701: if ((m.getModifiers() & (Modifier.ABSTRACT | Modifier.NATIVE)) != 0)
0702: throw new CannotSetWatchException(
0703: "JVMAspectInterface.setMethodExitWatch: cannot set watches on interfaces");
0704:
0705: // 1. check whether it is already set, and then set the tag
0706: CompoundAopTag compoundTag = (CompoundAopTag) methodExecutionMap
0707: .get(m);
0708: if (compoundTag == null) {
0709: compoundTag = new CompoundAopTag();
0710: methodExecutionMap.put(m, compoundTag);
0711: }
0712:
0713: if (compoundTag.exitTag != null)
0714: throw new WatchAlreadySetException(
0715: "JVMAspectInteface.setMethodExitWatch" + m);
0716: compoundTag.exitTag = aopTag;
0717:
0718: // 2. find out the end locations of this method (everywhere were 'return' exists + '0' if
0719: // the method is empty
0720:
0721: List endLocations = returnLocations(m);
0722:
0723: // 3. iterate over locations and set the watch
0724: Iterator j = endLocations.iterator();
0725: while (j.hasNext()) {
0726:
0727: CompoundAopTag tagToBeSet;
0728: int bci = ((Integer) (j.next())).intValue();
0729:
0730: if (bci == 0) {
0731: // if this return location is ALSO an entry location, we use a (X,Y) compound tag
0732: tagToBeSet = compoundTag;
0733: } else {
0734: // if this return location is NOT an entry location, we use a (null,Y) compound tag
0735: tagToBeSet = new CompoundAopTag();
0736: tagToBeSet.entryTag = null;
0737: tagToBeSet.exitTag = aopTag;
0738: }
0739:
0740: try {
0741: doSetLocationWatch(m.getDeclaringClass(), m, bci,
0742: tagToBeSet);
0743: } catch (WatchAlreadySetException entryWatchOnZeroLengthMethod) {
0744: // this exception is ok only if the return location is a entry location, too,
0745: // and the entry location is already set.
0746: if (compoundTag.entryTag != null && bci == 0) {
0747: }// everyhting is fine
0748: else {
0749: // this is an error, it should have been caputred earlier
0750: Error x = new Error(
0751: "JVMAspectInterface.setMethodEntryWatch: "
0752: + " watch set on exit in spite of non-zero length method:"
0753: + m);
0754: x.printStackTrace();
0755: throw x;
0756: }
0757: }
0758: }
0759: }
0760:
0761: /**
0762: * Clears a watch on a method exit joinpoint.
0763: *
0764: * @param m the method beeing watched.
0765: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0766: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0767: * @exception InvalidIdException There exists no method with id <code>methodId</code> in class <code>cls</code>.
0768: * @exception WatchNotSetException There exists no exit-watch on the method.
0769: */
0770: public synchronized void clearMethodExitWatch(Method m) {
0771: // preconditions
0772: if (!isInitialized)
0773: throw new NotInitializedException(
0774: "JVMAspectInterface.clearMethodExitWatch: jvmai not initialized");
0775: if (m == null)
0776: throw new NullPointerException(
0777: "JVMAspectInterface.clearMethodExitWatch: null Method parameter");
0778: if (m.getDeclaringClass().isInterface())
0779: throw new WatchNotSetException(
0780: "JVMAspectInterface.clearMethodExitWatch: cannot clear watches on interfaces");
0781:
0782: // 1. obtain the tag to delete
0783: CompoundAopTag compoundTag = (CompoundAopTag) methodExecutionMap
0784: .get(m);
0785:
0786: if (compoundTag == null || compoundTag.exitTag == null) {
0787: throw new WatchNotSetException(
0788: "JVMAspectInterface.clearMethodExitWatch: " + m);
0789: }
0790: compoundTag.exitTag = null;
0791:
0792: // 2. iterate over the 'return' locations and delete the watches
0793:
0794: Iterator j = returnLocations(m).iterator();
0795:
0796: while (j.hasNext()) {
0797: int bci = ((Integer) (j.next())).intValue();
0798: if (bci != 0 || (compoundTag.entryTag == null)) {
0799: doClearLocationWatch(m.getDeclaringClass(), m, bci);
0800: }
0801: }
0802:
0803: // 3. if nobody is interested in this method, delete the compound tag, too
0804: if (compoundTag.entryTag == null)
0805: methodExecutionMap.remove(m);
0806: }
0807:
0808: /**
0809: * Sets a watch on a exception throw joinpoint.
0810: *
0811: * @param cls Exception Class that should be watched.
0812: * @param aopTag A user-defined object saved with this watch.
0813: * When the joinpoint is reached, this object is
0814: * passed to the JoinPointHook as part of the
0815: * JoinPoint-instance. This object may be <code>null</code>.
0816: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0817: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0818: * @exception WatchAlreadySetException There already exists a exceptionThrow-watch at this point.
0819: */
0820: public void setExceptionThrowWatch(Class cls, Object aopTag) {
0821: setWatchPrecondition(cls, aopTag);
0822:
0823: synchronized (exceptionThrowMap) {
0824: doSetExceptionWatch(cls, aopTag);
0825: exceptionThrowMap.put(cls, aopTag);
0826: }
0827:
0828: }
0829:
0830: /**
0831: * Clears a watch on a exception throw joinpoint.
0832: *
0833: * @param cls Exception Class that should be watched.
0834: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0835: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0836: * @exception WatchNotSetException There exists no exceptionThrow-watch at this point.
0837: */
0838: public void clearExceptionThrowWatch(Class cls) {
0839:
0840: if (!isInitialized)
0841: throw new NotInitializedException(
0842: "JVMAspectInterface.clearExceptionThrowWatch: jvmai not initialized");
0843: if (cls == null)
0844: throw new NullPointerException(
0845: "JVMAspectInterface.clearExceptionThrowWatch: null cls parameter");
0846:
0847: synchronized (exceptionThrowMap) {
0848: doClearExceptionWatch(cls);
0849: if (exceptionThrowMap.containsKey(cls))
0850: exceptionThrowMap.remove(cls);
0851: else
0852: throw new WatchNotSetException();
0853: }
0854:
0855: }
0856:
0857: /**
0858: * Sets a watch on a exception catch joinpoint.
0859: *
0860: * @param cls Exception Class that should be watched.
0861: * @param aopTag A user-defined object saved with this watch.
0862: * When the joinpoint is reached, this object is
0863: * passed to the JoinPointHook as part of the
0864: * JoinPoint-instance. This object may be <code>null</code>.
0865: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0866: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0867: * @exception WatchAlreadySetException There already exists a exceptionCatch-watch at this point.
0868: */
0869: public void setExceptionCatchWatch(Class cls, Object aopTag) {
0870: setWatchPrecondition(cls, aopTag);
0871:
0872: synchronized (exceptionCatchMap) {
0873: doSetExceptionCatchWatch(cls, aopTag);
0874: exceptionCatchMap.put(cls, aopTag);
0875: }
0876: }
0877:
0878: /**
0879: * Clears a watch on a exception catch joinpoint.
0880: *
0881: * @param cls Exception Class that should be watched.
0882: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0883: * @exception NullPointerException <code>cls</code> is <code>null</code>.
0884: * @exception WatchNotSetException There exists no exceptionCatch-watch at this point.
0885: */
0886: public void clearExceptionCatchWatch(Class cls) {
0887:
0888: if (!isInitialized)
0889: throw new NotInitializedException(
0890: "JVMAspectInterface.clearExceptionCatchWatch: jvmai not initialized");
0891: if (cls == null)
0892: throw new NullPointerException(
0893: "JVMAspectInterface.clearExceptionCatchWatch: null cls parameter");
0894:
0895: synchronized (exceptionCatchMap) {
0896: doClearExceptionCatchWatch(cls);
0897: if (exceptionCatchMap.containsKey(cls))
0898: exceptionCatchMap.remove(cls);
0899: else
0900: throw new WatchNotSetException();
0901: }
0902: }
0903:
0904: /**
0905: * Suspend notification regarding the specified thread.
0906: * Successive calls to <code>suspendNotification</code>
0907: * have to be balanced by (at least) the same number of calls to
0908: * <code>resumeNotification</code>, or the jvmai-system
0909: * will not resume notification.
0910: *
0911: * @param thread Thread for which to disable notification.
0912: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0913: * @exception NullPointerException <code>thread</code> is <code>null</code>.
0914: */
0915: public void suspendNotification(Thread thread) {
0916: // jvmdi implementation
0917: if (!isInitialized)
0918: throw new NotInitializedException(
0919: "JVMAspectInterface.resumeNotification: jvmai not initialized");
0920:
0921: doSuspendNotification(thread);
0922: }
0923:
0924: /**
0925: * Resumes notification regarding the specified thread.
0926: * Successive calls to <code>resumeNotification</code>
0927: * without preceding calls to <code>suspendNotification</code>
0928: * will be ignored by the jvmai-system.
0929: *
0930: * @param thread Thread for which to reenable notification.
0931: * @exception NotInitializedException Aspect-interface has not been initialized yet. Call <code>setup</code> first.
0932: * @exception NullPointerException <code>thread</code> is <code>null</code>.
0933: */
0934: public void resumeNotification(Thread thread) {
0935: // jvmdi implementation
0936: if (!isInitialized)
0937: throw new NotInitializedException(
0938: "JVMAspectInterface.resumeNotification: jvmai not initialized");
0939: doResumeNotification(thread);
0940: }
0941:
0942: public List getLoadedClasses() {
0943: return doGetClasses();
0944: }
0945:
0946: private void doOnClassLoad(Class cls) {
0947:
0948: if (hook == null)
0949: return;
0950:
0951: hook.onClassLoad(cls);
0952: }
0953:
0954: private void doOnMethodExecution(MethodExecutionJoinPointImpl jp) {
0955: // find out whether it is entry, exit, or both
0956: if (hook == null)
0957: return;
0958:
0959: Method method = jp.getMethod();
0960: CompoundAopTag compoundTag = (CompoundAopTag) jp.getAopTag();
0961:
0962: if (compoundTag.entryTag != null) {
0963: jp.aopTag = compoundTag.entryTag;
0964: jp.setKind(true);
0965: hook.onMethodEntry(jp);
0966: }
0967: if (compoundTag.exitTag != null) {
0968: jp.aopTag = compoundTag.exitTag;
0969: jp.setKind(false);
0970: hook.onMethodExit(jp);
0971: }
0972: }
0973:
0974: private void doOnFieldAccess(FieldAccessJoinPointImpl jp) {
0975: if (hook == null)
0976: return;
0977: hook.onFieldAccess(jp);
0978: }
0979:
0980: private void doOnFieldModification(FieldModificationJoinPointImpl jp) {
0981: if (hook == null)
0982: return;
0983:
0984: hook.onFieldModification(jp);
0985: }
0986:
0987: private void doOnExceptionThrow(ExceptionJoinPointImpl jp) {
0988: if (hook == null)
0989: return;
0990:
0991: try {
0992: hook.onExceptionThrow(jp);
0993: } catch (Throwable e) {
0994: System.err.println("doOnExceptionThrow throws "
0995: + e.getClass().getName() + ": " + e.getMessage());
0996: StackTraceElement[] stackTrace = e.getStackTrace();
0997: for (int i = 0; i < stackTrace.length; i++) {
0998: System.err.println(" " + stackTrace[i]);
0999: }
1000: }
1001: }
1002:
1003: private void doOnExceptionCatch(ExceptionCatchJoinPointImpl jp) {
1004:
1005: if (hook == null)
1006: return;
1007:
1008: try {
1009: hook.onExceptionCatch(jp);
1010: } catch (Throwable x) {
1011: System.err.println("doOnExceptionCatch throws"
1012: + x.getClass().getName() + ": " + x.getMessage());
1013: }
1014: }
1015:
1016: private native void doStartup(Object[] prefixes, boolean openWorld);
1017:
1018: private native void doTeardown();
1019:
1020: private native void doSetLocationWatch(Class c, Method m, int bci,
1021: Object tag);
1022:
1023: private native void doClearLocationWatch(Class c, Method m, int bci);
1024:
1025: private native void doSetFieldAccessWatch(Class c, Field f,
1026: Object tag);
1027:
1028: private native void doClearFieldAccessWatch(Class c, Field f);
1029:
1030: private native void doSetFieldModificationWatch(Class c, Field f,
1031: Object tag);
1032:
1033: private native void doClearFieldModificationWatch(Class c, Field f);
1034:
1035: private native void doSetExceptionWatch(Class throwableClass,
1036: Object tag);
1037:
1038: private native void doClearExceptionWatch(Class trowableClass);
1039:
1040: private native void doSetExceptionCatchWatch(Class throwableClass,
1041: Object tag);
1042:
1043: private native void doClearExceptionCatchWatch(Class trowableClass);
1044:
1045: private native byte[] doGetByteCode(Class c, Method m);
1046:
1047: private native List doGetClasses();
1048:
1049: private native void doSuspendNotification(Thread t);
1050:
1051: private native void doResumeNotification(Thread t);
1052: }
1053:
1054: //======================================================================
1055: //
1056: // $Log: AspectInterfaceImpl.java,v $
1057: // Revision 1.3 2005/06/26 18:06:03 anicoara
1058: // Cleaned up the code
1059: //
1060: // Revision 1.2 2004/05/12 09:41:54 anicoara
1061: // Remove the README.RVM file
1062: //
1063: // Revision 1.1.1.1 2003/07/02 15:30:49 apopovic
1064: // Imported from ETH Zurich
1065: //
1066: // Revision 1.7 2003/07/02 12:42:44 anicoara
1067: // Added CatchJoinPoint Functionality (Requests, Join-Points, Filters, CatchCuts, Tests)
1068: //
1069: // Revision 1.6 2003/05/06 15:08:21 popovici
1070: // Input stream now directly from class-loader, not from URL (addressing a problem of midas)
1071: //
1072: // Revision 1.5 2003/05/05 17:46:22 popovici
1073: // Refactorization step (runes->prose) cleanup
1074: //
1075: // Revision 1.4 2003/04/30 14:44:19 popovici
1076: // potential bug fix: after teardown, the state of the maps (e.g., fieldAccessMap) was kept; fixed
1077: //
1078: // Revision 1.3 2003/04/17 08:47:02 popovici
1079: // Important functionality additions
1080: // - Cflow specializers
1081: // - Restructuring of the MethodCut, SetCut, ExceptionCut, and GetCut (they are much smaller)
1082: // - Transactional capabilities
1083: // - Total refactoring of Specializer evaluation, which permits fine-grained distinction
1084: // between static and dynamic specializers.
1085: // - Functionality pulled up in abstract classes
1086: // - Uniformization of advice methods patterns and names
1087: //
1088: // Revision 1.2 2003/03/04 16:09:31 popovici
1089: // Documentation improvements
1090: //
1091: // Revision 1.1 2003/03/04 11:26:19 popovici
1092: // Important refactorization step (march):
1093: // - removal of 'JoinPointEvents'; JoinPoints now have the same function as events
1094: // - reimplementation of the JVMAIDebuggerAspectInterface (better performance, coding conventions, removal of ProseVM
1095: // structures
1096: //
1097: // Revision 1.8 2002/11/27 17:09:43 popovici
1098: // Minor chages; Teardown no sets isTeardown to false
1099: //
1100: // Revision 1.7 2002/10/17 12:23:38 pschoch
1101: // Added throw capabability to JVMAI
1102: //
1103: // Revision 1.6 2002/09/21 14:04:31 popovici
1104: // Bug 0000010 fixed. Added 'teardown' procedure
1105: // in the JVMAI, Jikes & JDK prose implementation
1106: //
1107: // Revision 1.5 2002/03/28 13:48:11 popovici
1108: // Mozilla-ified
1109: //
1110: // Revision 1.4 2002/02/18 15:09:59 popovici
1111: // Typo in DebuggerAspectInterface corrected
1112: //
1113: // Revision 1.3 2002/02/18 14:38:43 popovici
1114: // - Class comments added for DebuggerAspectInterface, ProseVM, ProseJPM and DebuggerInfoInterface
1115: // - Bug fix in ProseVM that appeared when the boot classes where a VM extnesion
1116: // - minor changes (edits) in prosevm.c
1117: //
1118: // Revision 1.2 2002/02/14 16:02:23 popovici
1119: // Bug fixes, PROSEVM moved to boot
1120: //
1121: // Revision 1.1 2002/02/06 11:53:46 popovici
1122: // Refactoring from prose classical to jvmai
1123: //
1124: // Revision 1.2 2002/01/24 12:59:38 smarkwal
1125: // Comments added.
1126: //
1127: // Revision 1.1 2001/12/14 15:01:17 smarkwal
1128: // Initial Revision
1129: //
|