001: //
002: // This file is part of the prose package.
003: //
004: // The contents of this file are subject to the Mozilla Public License
005: // Version 1.1 (the "License"); you may not use this file except in
006: // compliance with the License. You may obtain a copy of the License at
007: // http://www.mozilla.org/MPL/
008: //
009: // Software distributed under the License is distributed on an "AS IS" basis,
010: // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011: // for the specific language governing rights and limitations under the
012: // License.
013: //
014: // The Original Code is prose.
015: //
016: // The Initial Developer of the Original Code is Angela Nicoara. Portions
017: // created by Angela Nicoara are Copyright (C) 2002 Angela Nicoara.
018: // All Rights Reserved.
019: //
020: // Contributor(s):
021: // $Id$
022: // =====================================================================
023: //
024: // (history at end)
025: //
026:
027: package ch.ethz.prose.jvmai.jikesrvm.advice_weaver;
028:
029: import java.lang.reflect.*;
030: import java.util.*;
031:
032: import ch.ethz.jvmai.*;
033:
034: import com.ibm.JikesRVM.VM_Callbacks;
035: import com.ibm.JikesRVM.VM_Thread;
036: import com.ibm.JikesRVM.classloader.*;
037:
038: /**
039: * A JVMAspectInterface implementation that is based on advice weaving at
040: * bytecode level.
041: *
042: * @author Johann Gyger
043: */
044: public class AdviceJVMAI implements VM_Callbacks.ClassLoadedMonitor,
045: VM_Callbacks.ExceptionThrownMonitor,
046: VM_Callbacks.ExceptionCaughtMonitor, JVMAspectInterface {
047:
048: /**
049: * Sole instance of this class.
050: */
051: protected static AdviceJVMAI instance;
052:
053: /**
054: * Threads for which the aspect interface is suspended;
055: */
056: protected static BitSet suspendedThreads;
057:
058: /**
059: * Is this aspect interface up and running?
060: */
061: protected static boolean initialized;
062:
063: /**
064: * Currently registered join point hook.
065: */
066: protected static JoinPointHook hook;
067:
068: /**
069: * Storage for aopTags registered with join points. An ID of a method or a
070: * field is used as index into these arrays.
071: */
072: protected static Object[] fieldAccessTags;
073: protected static Object[] fieldModificationTags;
074: protected static Object[] methodEntryTags;
075: protected static Object[] methodExitTags;
076: protected static Object[] exceptionThrowTags;
077: protected static Object[] exceptionCatchTags;
078:
079: /**
080: * Dummy-tag used to mark join points registered with aopTag 'null' in the
081: * tag arrays.
082: */
083: protected static Object aopNullTag = new Object();
084:
085: protected static MethodEntryJoinPointImpl methodEntryJoinPoint = new MethodEntryJoinPointImpl();
086: protected static MethodExitJoinPointImpl methodExitJoinPoint = new MethodExitJoinPointImpl();
087: protected static FieldAccessJoinPointImpl fieldAccessJoinPoint = new FieldAccessJoinPointImpl();
088: protected static FieldModificationJoinPointImpl fieldModificationJoinPoint = new FieldModificationJoinPointImpl();
089: protected static ExceptionThrowJoinPointImpl exceptionThrowJoinPoint = new ExceptionThrowJoinPointImpl();
090: protected static ExceptionCatchJoinPointImpl exceptionCatchJoinPoint = new ExceptionCatchJoinPointImpl();
091:
092: /**
093: * Get sole instance (singleton) of this class.
094: *
095: * @return Sole instance of this class
096: */
097: public static AdviceJVMAI getInstance() {
098: if (instance == null) {
099: instance = new AdviceJVMAI();
100: VM_Callbacks.addClassLoadedMonitor(instance);
101: VM_Callbacks.addExceptionThrownMonitor(instance);
102: VM_Callbacks.addExceptionCaughtMonitor(instance);
103: }
104: return instance;
105: }
106:
107: /**
108: * This callback will be invoked at method entry join points. Invocation only
109: * takes place if the callback is woven in to the corresponding method.
110: *
111: * @param methodId method identifier
112: * @param this0 object on which the method is executed
113: * @param args actual arguments of method being intercepted
114: */
115: public static final void onMethodEntry(int methodId, Object this 0,
116: Object[] args) {
117: if (hook == null
118: || suspendedThreads.get(VM_Thread.getCurrentThread()
119: .getIndex()))
120: return;
121:
122: //System.out.println("AdviceJVMAI.onMethodEntry(): " + methodId + " / " + this0 + " / " + Arrays.asList(args));
123: methodEntryJoinPoint.init(methodId, methodEntryTags[methodId],
124: this 0, args);
125: hook.onMethodEntry(methodEntryJoinPoint);
126: }
127:
128: /**
129: * This callback will be invoked at method exit join points. Invocation only
130: * takes place if the callback is woven in to the corresponding method.
131: *
132: * @param methodId method identifier
133: * @param this0 object on which the method is executed
134: * @param args actual arguments of method being intercepted
135: */
136: public static final void onMethodExit(int methodId, Object this 0,
137: Object[] args) {
138: if (hook == null
139: || suspendedThreads.get(VM_Thread.getCurrentThread()
140: .getIndex()))
141: return;
142:
143: //System.out.println("AdviceJVMAI.onMethodExit(): " + methodId + " / " + this0 + " / " + Arrays.asList(args));
144: methodExitJoinPoint.init(methodId, methodExitTags[methodId],
145: this 0, args);
146: hook.onMethodExit(methodExitJoinPoint);
147: }
148:
149: /**
150: * This callback will be invoked at field access join points. Invocation only
151: * takes place if the callback is woven in to the corresponding method.
152: *
153: * @param owner object that contains the field
154: * @param fieldId field identifier
155: * @param methodId method identifier
156: * @param this0 object on which the method containing the field access is
157: * executed
158: * @param args actual arguments of method being intercepted
159: */
160: public static final void onFieldAccess(Object owner, int fieldId,
161: int methodId, Object this 0, Object[] args) {
162: if (hook == null
163: || suspendedThreads.get(VM_Thread.getCurrentThread()
164: .getIndex()))
165: return;
166:
167: fieldAccessJoinPoint.init(methodId, fieldAccessTags[fieldId],
168: this 0, args, fieldId, owner);
169: //System.out.println("AdviceJVMAI.onFieldAccess(): " + fieldAccessJoinPoint.getAopTag());
170: //System.out.println("AdviceJVMAI.onFieldAccess(): " + fieldAccessJoinPoint.getField() + " / " + this0);
171: hook.onFieldAccess(fieldAccessJoinPoint);
172: }
173:
174: /**
175: * This callback will be invoked at field modification join points.
176: * Invocation only takes place if the callback is woven in to the
177: * corresponding method.
178: *
179: * @param owner object that contains the field
180: * @param fieldId field identifier
181: * @param methodId method identifier
182: * @param this0 object on which the method containing the field access is
183: * executed
184: * @param args actual arguments of method being intercepted
185: */
186: public static final void onFieldModification(Object owner,
187: int fieldId, int methodId, Object this 0, Object[] args) {
188: if (hook == null
189: || suspendedThreads.get(VM_Thread.getCurrentThread()
190: .getIndex()))
191: return;
192:
193: //System.out.println("AdviceJVMAI.onMethodExit(): " + owner + " / " + fieldId + " / " + methodId + " / " + this0 + " / " + Arrays.asList(args));
194: fieldModificationJoinPoint.init(methodId,
195: fieldModificationTags[fieldId], this 0, args, fieldId,
196: owner);
197: //System.out.println("AdviceJVMAI.onFieldModification(): " + fieldModificationJoinPoint.getField());
198: hook.onFieldModification(fieldModificationJoinPoint);
199: }
200:
201: /**
202: * Create new instance. Used by singleton pattern.
203: */
204: protected AdviceJVMAI() {
205: }
206:
207: public void notifyClassLoaded(VM_Class klass) {
208: if (!initialized
209: || hook == null
210: || suspendedThreads.get(VM_Thread.getCurrentThread()
211: .getIndex()))
212: return;
213:
214: //System.out.println("AdviceJVMAI.notifyClassLoaded(): " + klass);
215: hook.onClassLoad(klass.getClassForType());
216: }
217:
218: public final void notifyExceptionThrown(Throwable exception,
219: VM_Method thrower) {
220: if (hook == null)
221: return;
222:
223: int id = java.lang.JikesRVMSupport.getTypeForClass(
224: exception.getClass()).getId();
225: if (id >= exceptionThrowTags.length)
226: return;
227:
228: Object tag = exceptionThrowTags[id];
229: if (tag == null)
230: return;
231:
232: if (suspendedThreads.get(VM_Thread.getCurrentThread()
233: .getIndex()))
234: return;
235:
236: // Ignore exceptions thrown in the VM
237: if (thrower.getDeclaringClass().toString().startsWith(
238: "com.ibm.JikesRVM."))
239: return;
240:
241: exceptionThrowJoinPoint.init(thrower.getId(), tag, null, null,
242: exception);
243: //System.out.println("AdviceJVMAI.notifyExceptionThrown(): " + exception + " / " + thrower);
244: hook.onExceptionThrow(exceptionThrowJoinPoint);
245: }
246:
247: public final void notifyExceptionCaught(Throwable exception,
248: VM_Method catcher) {
249: if (hook == null)
250: return;
251:
252: int id = java.lang.JikesRVMSupport.getTypeForClass(
253: exception.getClass()).getId();
254: if (id >= exceptionCatchTags.length)
255: return;
256:
257: Object tag = exceptionCatchTags[id];
258: if (tag == null)
259: return;
260:
261: if (suspendedThreads.get(VM_Thread.getCurrentThread()
262: .getIndex()))
263: return;
264:
265: // Ignore exceptions caught in the VM
266: if (catcher.getDeclaringClass().toString().startsWith(
267: "com.ibm.JikesRVM."))
268: return;
269:
270: exceptionCatchJoinPoint.init(catcher.getId(), tag, null, null,
271: exception);
272: //System.out.println("AdviceJVMAI.notifyExceptionCaught(): " + exception + " / " + catcher);
273: hook.onExceptionCatch(exceptionCatchJoinPoint);
274: }
275:
276: public void startup(String[] packagePrefixes,
277: boolean openWorldAssumption) {
278: fieldAccessTags = new Object[1024];
279: fieldModificationTags = new Object[1024];
280: methodEntryTags = new Object[1024];
281: methodExitTags = new Object[1024];
282: exceptionThrowTags = new Object[1024];
283: exceptionCatchTags = new Object[1024];
284: suspendedThreads = new BitSet(1024);
285:
286: initialized = true;
287: }
288:
289: public void teardown() {
290: //System.out.println("AdviceJVMAI.teardown()");
291: hook = null;
292: initialized = false;
293:
294: fieldAccessTags = null;
295: fieldModificationTags = null;
296: methodEntryTags = null;
297: methodExitTags = null;
298: exceptionThrowTags = null;
299: exceptionCatchTags = null;
300: suspendedThreads.clear();
301:
302: FieldWeaver.resetAll();
303: MethodWeaver.resetAll();
304: }
305:
306: public void setJoinPointHook(JoinPointHook h) {
307: if (!initialized)
308: throw new NotInitializedException();
309:
310: hook = h;
311: }
312:
313: public void setFieldAccessWatch(Field f, Object aopTag) {
314: if (!initialized)
315: throw new NotInitializedException();
316: if (f == null)
317: throw new NullPointerException(
318: "Parameter `f' must not be null.");
319: if (aopTag == null)
320: throw new NullPointerException(
321: "Parameter `aopTag' must not be null.");
322:
323: //System.out.println("AdviceJVMAI.setFieldAccessWatch(): " + f);
324: int id = java.lang.reflect.JikesRVMSupport.getFieldOf(f)
325: .getId();
326: fieldAccessTags = setWatch(fieldAccessTags, aopTag, id);
327: FieldWeaver.getWeaver(f).setFieldAccessEnabled(true);
328: }
329:
330: public void clearFieldAccessWatch(Field f) {
331: if (!initialized)
332: throw new NotInitializedException();
333: if (f == null)
334: throw new NullPointerException(
335: "Parameter `f' must not be null.");
336:
337: int id = java.lang.reflect.JikesRVMSupport.getFieldOf(f)
338: .getId();
339: clearWatch(fieldAccessTags, id);
340: FieldWeaver.getWeaver(f).setFieldAccessEnabled(false);
341: }
342:
343: public void setFieldModificationWatch(Field f, Object aopTag) {
344: if (!initialized)
345: throw new NotInitializedException();
346: if (f == null)
347: throw new NullPointerException(
348: "Parameter `f' must not be null.");
349: if (aopTag == null)
350: throw new NullPointerException(
351: "Parameter `aopTag' must not be null.");
352:
353: //System.out.println("AdviceJVMAI.setFieldModificationWatch(): " + f);
354: int id = java.lang.reflect.JikesRVMSupport.getFieldOf(f)
355: .getId();
356: fieldModificationTags = setWatch(fieldModificationTags, aopTag,
357: id);
358: FieldWeaver.getWeaver(f).setFieldModificationEnabled(true);
359: }
360:
361: public void clearFieldModificationWatch(Field f) {
362: if (!initialized)
363: throw new NotInitializedException();
364: if (f == null)
365: throw new NullPointerException(
366: "Parameter `f' must not be null.");
367:
368: int id = java.lang.reflect.JikesRVMSupport.getFieldOf(f)
369: .getId();
370: clearWatch(fieldModificationTags, id);
371: FieldWeaver.getWeaver(f).setFieldModificationEnabled(false);
372: }
373:
374: public void setMethodEntryWatch(Method m, Object aopTag) {
375: if (!initialized)
376: throw new NotInitializedException();
377: if (m == null)
378: throw new NullPointerException(
379: "Parameter `m' must not be null.");
380: if (aopTag == null)
381: throw new NullPointerException(
382: "Parameter `aopTag' must not be null.");
383: if (Modifier.isAbstract(m.getModifiers()))
384: throw new CannotSetWatchException("Method is abstract: "
385: + m);
386:
387: //System.out.println("AdviceJVMAI.setMethodEntryWatch(): " + m);
388: int id = java.lang.reflect.JikesRVMSupport.getMethodOf(m)
389: .getId();
390: methodEntryTags = setWatch(methodEntryTags, aopTag, id);
391: MethodWeaver.getWeaver(m).setMethodEntryEnabled(true);
392: }
393:
394: public void clearMethodEntryWatch(Method m) {
395: if (!initialized)
396: throw new NotInitializedException();
397: if (m == null)
398: throw new NullPointerException(
399: "Parameter `m' must not be null.");
400:
401: //System.out.println("AdviceJVMAI.clearMethodEntryWatch(): " + m);
402: int id = java.lang.reflect.JikesRVMSupport.getMethodOf(m)
403: .getId();
404: clearWatch(methodEntryTags, id);
405: MethodWeaver.getWeaver(m).setMethodEntryEnabled(false);
406: }
407:
408: public void setMethodExitWatch(Method m, Object aopTag) {
409: if (!initialized)
410: throw new NotInitializedException();
411: if (m == null)
412: throw new NullPointerException(
413: "Parameter `m' must not be null.");
414: if (aopTag == null)
415: throw new NullPointerException(
416: "Parameter `aopTag' must not be null.");
417: if (Modifier.isAbstract(m.getModifiers()))
418: throw new CannotSetWatchException("Method is abstract: "
419: + m);
420:
421: //System.out.println("AdviceJVMAI.setMethodExitWatch(): " + m);
422: int id = java.lang.reflect.JikesRVMSupport.getMethodOf(m)
423: .getId();
424: methodExitTags = setWatch(methodExitTags, aopTag, id);
425: MethodWeaver.getWeaver(m).setMethodExitEnabled(true);
426: }
427:
428: public void clearMethodExitWatch(Method m) {
429: if (!initialized)
430: throw new NotInitializedException();
431: if (m == null)
432: throw new NullPointerException(
433: "Parameter `m' must not be null.");
434:
435: //System.out.println("AdviceJVMAI.clearMethodEntryWatch(): " + m);
436: int id = java.lang.reflect.JikesRVMSupport.getMethodOf(m)
437: .getId();
438: clearWatch(methodExitTags, id);
439: MethodWeaver.getWeaver(m).setMethodExitEnabled(false);
440: }
441:
442: public void setExceptionThrowWatch(Class c, Object aopTag) {
443: if (!initialized)
444: throw new NotInitializedException();
445: if (c == null)
446: throw new NullPointerException(
447: "Parameter `c' must not be null.");
448: if (aopTag == null)
449: throw new NullPointerException(
450: "Parameter `aopTag' must not be null.");
451:
452: //System.out.println("AdviceJVMAI.setExceptionThrowWatch(): " + c);
453: int id = java.lang.JikesRVMSupport.getTypeForClass(c).getId();
454: exceptionThrowTags = setWatch(exceptionThrowTags, aopTag, id);
455: }
456:
457: public void clearExceptionThrowWatch(Class c) {
458: if (!initialized)
459: throw new NotInitializedException();
460: if (c == null)
461: throw new NullPointerException(
462: "Parameter `c' must not be null.");
463:
464: int id = java.lang.JikesRVMSupport.getTypeForClass(c).getId();
465: clearWatch(exceptionThrowTags, id);
466: }
467:
468: public void setExceptionCatchWatch(Class c, Object aopTag) {
469: if (!initialized)
470: throw new NotInitializedException();
471: if (c == null)
472: throw new NullPointerException(
473: "Parameter `c' must not be null.");
474: if (aopTag == null)
475: throw new NullPointerException(
476: "Parameter `aopTag' must not be null.");
477:
478: //System.out.println("AdviceJVMAI.setExceptionCatchWatch(): " + c);
479: int id = java.lang.JikesRVMSupport.getTypeForClass(c).getId();
480: exceptionCatchTags = setWatch(exceptionCatchTags, aopTag, id);
481: }
482:
483: public void clearExceptionCatchWatch(Class c) {
484: if (!initialized)
485: throw new NotInitializedException();
486: if (c == null)
487: throw new NullPointerException(
488: "Parameter `c' must not be null.");
489:
490: int id = java.lang.JikesRVMSupport.getTypeForClass(c).getId();
491: clearWatch(exceptionCatchTags, id);
492: }
493:
494: public void setConstructorWatch(java.lang.reflect.Constructor c,
495: Object aopTag) {
496: throw new JVMAIRuntimeException(
497: "Not implemented for the Stub and Advice Weavers implementation.");
498: }
499:
500: public void clearConstructorWatch(java.lang.reflect.Constructor c) {
501: throw new JVMAIRuntimeException(
502: "Not implemented for the Stub and Advice Weavers implementation.");
503: }
504:
505: public void suspendNotification(Thread thread) {
506: if (!initialized)
507: throw new NotInitializedException();
508: if (thread == null)
509: throw new NullPointerException(
510: "Parameter `thread' must not be null");
511:
512: suspendedThreads.set(thread.getIndex());
513: }
514:
515: public void resumeNotification(Thread thread) {
516: if (!initialized)
517: throw new NotInitializedException();
518: if (thread == null)
519: throw new NullPointerException(
520: "Parameter `thread' must not be null");
521:
522: FieldWeaver.commit();
523: MethodWeaver.commit();
524: suspendedThreads.set(thread.getIndex(), false);
525: }
526:
527: public List getLoadedClasses() {
528: if (!initialized)
529: throw new NotInitializedException();
530:
531: VM_Type[] types = VM_Type.getTypes();
532: Vector classes = new Vector();
533:
534: for (int t = 0; t < types.length; t++) {
535: if ((types[t] == null) || !types[t].isClassType())
536: continue;
537:
538: VM_Class klass = types[t].asClass();
539: if (!klass.isResolved()
540: || klass.toString().startsWith("com.ibm.JikesRVM."))
541: continue;
542:
543: classes.add(klass.getClassForType());
544: }
545:
546: return classes;
547: }
548:
549: /**
550: * Set `tags[id]' to `tag'. If `tags' is too small then resize it
551: * appriopriately.
552: */
553: protected Object[] setWatch(Object[] tags, Object tag, int id) {
554: synchronized (tags) {
555: try {
556: if (tags[id] != null)
557: throw new WatchAlreadySetException("<" + id + ">");
558: } catch (ArrayIndexOutOfBoundsException e) {
559: Object tmp[] = new Object[2 * id];
560: System.arraycopy(tags, 0, tmp, 0, tags.length);
561: tags = tmp;
562: }
563: tags[id] = (tag == null) ? aopNullTag : tag;
564: }
565: return tags;
566: }
567:
568: /**
569: * Set `tags[id] = null'. If `id' is out of bounds or the watch already
570: * cleared a WatchNotSetException is thrown.
571: *
572: * @param tags AOP tag array
573: * @param id Index into `tags'
574: */
575: protected void clearWatch(Object[] tags, int id) {
576: synchronized (tags) {
577: try {
578: if (tags[id] == null)
579: throw new WatchNotSetException("<" + id + ">");
580: tags[id] = null;
581: } catch (ArrayIndexOutOfBoundsException e) {
582: throw new WatchNotSetException("<" + id + ">");
583: }
584: }
585: }
586:
587: }
588:
589: //======================================================================
590: //
591: // $Log$
592: //
|