View Javadoc
1   /*
2    * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  /*
27   * This source code is provided to illustrate the usage of a given feature
28   * or technique and has been deliberately simplified. Additional steps
29   * required for a production-quality application, such as security checks,
30   * input validation and proper error handling, might not be present in
31   * this sample code.
32   */
33  
34  
35  package com.sun.tools.example.debug.bdi;
36  
37  import com.sun.jdi.*;
38  import com.sun.jdi.request.*;
39  import com.sun.jdi.connect.*;
40  import com.sun.tools.example.debug.expr.ExpressionParser;
41  import com.sun.tools.example.debug.expr.ParseException;
42  
43  import java.io.*;
44  import java.util.*;
45  
46  import com.sun.tools.example.debug.event.*;
47  
48  import javax.swing.SwingUtilities;
49  
50  /**
51   * Move this towards being only state and functionality
52   * that spans across Sessions (and thus VMs).
53   */
54  public class ExecutionManager {
55  
56      private Session session;
57  
58      /**
59       * Get/set JDI trace mode.
60       */
61      int traceMode = VirtualMachine.TRACE_NONE;
62  
63    //////////////////    Listener registration    //////////////////
64  
65    // Session Listeners
66  
67      ArrayList<SessionListener> sessionListeners = new ArrayList<SessionListener>();
68  
69      public void addSessionListener(SessionListener listener) {
70          sessionListeners.add(listener);
71      }
72  
73      public void removeSessionListener(SessionListener listener) {
74          sessionListeners.remove(listener);
75      }
76  
77    // Spec Listeners
78  
79    ArrayList<SpecListener> specListeners = new ArrayList<SpecListener>();
80  
81      public void addSpecListener(SpecListener cl) {
82          specListeners.add(cl);
83      }
84  
85      public void removeSpecListener(SpecListener cl) {
86          specListeners.remove(cl);
87      }
88  
89      // JDI Listeners
90  
91      ArrayList<JDIListener> jdiListeners = new ArrayList<JDIListener>();
92  
93      /**
94       * Adds a JDIListener
95       */
96      public void addJDIListener(JDIListener jl) {
97          jdiListeners.add(jl);
98      }
99  
100     /**
101      * Adds a JDIListener - at the specified position
102      */
103     public void addJDIListener(int index, JDIListener jl) {
104         jdiListeners.add(index, jl);
105     }
106 
107     /**
108      * Removes a JDIListener
109      */
110     public void removeJDIListener(JDIListener jl) {
111         jdiListeners.remove(jl);
112     }
113 
114   // App Echo Listeners
115 
116     private ArrayList<OutputListener> appEchoListeners = new ArrayList<OutputListener>();
117 
118     public void addApplicationEchoListener(OutputListener l) {
119         appEchoListeners.add(l);
120     }
121 
122     public void removeApplicationEchoListener(OutputListener l) {
123         appEchoListeners.remove(l);
124     }
125 
126   // App Output Listeners
127 
128     private ArrayList<OutputListener> appOutputListeners = new ArrayList<OutputListener>();
129 
130     public void addApplicationOutputListener(OutputListener l) {
131         appOutputListeners.add(l);
132     }
133 
134     public void removeApplicationOutputListener(OutputListener l) {
135         appOutputListeners.remove(l);
136     }
137 
138   // App Error Listeners
139 
140     private ArrayList<OutputListener> appErrorListeners = new ArrayList<OutputListener>();
141 
142     public void addApplicationErrorListener(OutputListener l) {
143         appErrorListeners.add(l);
144     }
145 
146     public void removeApplicationErrorListener(OutputListener l) {
147         appErrorListeners.remove(l);
148     }
149 
150   // Diagnostic Listeners
151 
152     private ArrayList<OutputListener> diagnosticsListeners = new ArrayList<OutputListener>();
153 
154     public void addDiagnosticsListener(OutputListener l) {
155         diagnosticsListeners.add(l);
156     }
157 
158     public void removeDiagnosticsListener(OutputListener l) {
159         diagnosticsListeners.remove(l);
160     }
161 
162   ///////////    End Listener Registration    //////////////
163 
164     //### We probably don't want this public
165     public VirtualMachine vm() {
166         return session == null ? null : session.vm;
167     }
168 
169     void ensureActiveSession() throws NoSessionException {
170         if (session == null) {
171          throw new NoSessionException();
172       }
173     }
174 
175     public EventRequestManager eventRequestManager() {
176         return vm() == null ? null : vm().eventRequestManager();
177     }
178 
179     /**
180      * Get JDI trace mode.
181      */
182     public int getTraceMode(int mode) {
183         return traceMode;
184     }
185 
186     /**
187      * Set JDI trace mode.
188      */
189     public void setTraceMode(int mode) {
190         traceMode = mode;
191         if (session != null) {
192             session.setTraceMode(mode);
193         }
194     }
195 
196     /**
197      * Determine if VM is interrupted, i.e, present and not running.
198      */
199     public boolean isInterrupted() /* should: throws NoSessionException */ {
200 //      ensureActiveSession();
201         return session.interrupted;
202     }
203 
204     /**
205      * Return a list of ReferenceType objects for all
206      * currently loaded classes and interfaces.
207      * Array types are not returned.
208      */
209     public List<ReferenceType> allClasses() throws NoSessionException {
210         ensureActiveSession();
211         return vm().allClasses();
212     }
213 
214     /**
215      * Return a ReferenceType object for the currently
216      * loaded class or interface whose fully-qualified
217      * class name is specified, else return null if there
218      * is none.
219      *
220      * In general, we must return a list of types, because
221      * multiple class loaders could have loaded a class
222      * with the same fully-qualified name.
223      */
224     public List<ReferenceType> findClassesByName(String name) throws NoSessionException {
225         ensureActiveSession();
226         return vm().classesByName(name);
227     }
228 
229     /**
230      * Return a list of ReferenceType objects for all
231      * currently loaded classes and interfaces whose name
232      * matches the given pattern.  The pattern syntax is
233      * open to some future revision, but currently consists
234      * of a fully-qualified class name in which the first
235      * component may optionally be a "*" character, designating
236      * an arbitrary prefix.
237      */
238     public List<ReferenceType> findClassesMatchingPattern(String pattern)
239                                                 throws NoSessionException {
240         ensureActiveSession();
241         List<ReferenceType> result = new ArrayList<ReferenceType>();  //### Is default size OK?
242         if (pattern.startsWith("*.")) {
243             // Wildcard matches any leading package name.
244             pattern = pattern.substring(1);
245             for (ReferenceType type : vm().allClasses()) {
246                 if (type.name().endsWith(pattern)) {
247                     result.add(type);
248                 }
249             }
250             return result;
251         } else {
252             // It's a class name.
253             return vm().classesByName(pattern);
254         }
255     }
256 
257     /*
258      * Return a list of ThreadReference objects corresponding
259      * to the threads that are currently active in the VM.
260      * A thread is removed from the list just before the
261      * thread terminates.
262      */
263 
264     public List<ThreadReference> allThreads() throws NoSessionException {
265         ensureActiveSession();
266         return vm().allThreads();
267     }
268 
269     /*
270      * Return a list of ThreadGroupReference objects corresponding
271      * to the top-level threadgroups that are currently active in the VM.
272      * Note that a thread group may be empty, or contain no threads as
273      * descendents.
274      */
275 
276     public List<ThreadGroupReference> topLevelThreadGroups() throws NoSessionException {
277         ensureActiveSession();
278         return vm().topLevelThreadGroups();
279     }
280 
281     /*
282      * Return the system threadgroup.
283      */
284 
285     public ThreadGroupReference systemThreadGroup()
286                                                 throws NoSessionException {
287         ensureActiveSession();
288         return vm().topLevelThreadGroups().get(0);
289     }
290 
291     /*
292      * Evaluate an expression.
293      */
294 
295     public Value evaluate(final StackFrame f, String expr)
296         throws ParseException,
297                                             InvocationException,
298                                             InvalidTypeException,
299                                             ClassNotLoadedException,
300                                             NoSessionException,
301                                             IncompatibleThreadStateException {
302         ExpressionParser.GetFrame frameGetter = null;
303         ensureActiveSession();
304         if (f != null) {
305             frameGetter = new ExpressionParser.GetFrame() {
306                 @Override
307                 public StackFrame get() /* throws IncompatibleThreadStateException */ {
308                     return f;
309                 }
310             };
311         }
312         return ExpressionParser.evaluate(expr, vm(), frameGetter);
313     }
314 
315 
316     /*
317      * Start a new VM.
318      */
319 
320     public void run(boolean suspended,
321                     String vmArgs,
322                     String className,
323                     String args) throws VMLaunchFailureException {
324 
325         endSession();
326 
327         //### Set a breakpoint on 'main' method.
328         //### Would be cleaner if we could just bring up VM already suspended.
329         if (suspended) {
330             //### Set breakpoint at 'main(java.lang.String[])'.
331             List<String> argList = new ArrayList<String>(1);
332             argList.add("java.lang.String[]");
333             createMethodBreakpoint(className, "main", argList);
334         }
335 
336         String cmdLine = className + " " + args;
337 
338         startSession(new ChildSession(this, vmArgs, cmdLine,
339                                       appInput, appOutput, appError,
340                                       diagnostics));
341     }
342 
343     /*
344      * Attach to an existing VM.
345      */
346     public void attach(String portName) throws VMLaunchFailureException {
347         endSession();
348 
349         //### Changes made here for connectors have broken the
350         //### the 'Session' abstraction.  The 'Session.attach()'
351         //### method is intended to encapsulate all of the various
352         //### ways in which session start-up can fail. (maddox 12/18/98)
353 
354         /*
355          * Now that attaches and launches both go through Connectors,
356          * it may be worth creating a new subclass of Session for
357          * attach sessions.
358          */
359         VirtualMachineManager mgr = Bootstrap.virtualMachineManager();
360         AttachingConnector connector = mgr.attachingConnectors().get(0);
361         Map<String, Connector.Argument> arguments = connector.defaultArguments();
362         arguments.get("port").setValue(portName);
363 
364         Session newSession = internalAttach(connector, arguments);
365         if (newSession != null) {
366             startSession(newSession);
367         }
368     }
369 
370     private Session internalAttach(AttachingConnector connector,
371                                    Map<String, Connector.Argument> arguments) {
372         try {
373             VirtualMachine vm = connector.attach(arguments);
374             return new Session(vm, this, diagnostics);
375         } catch (IOException ioe) {
376             diagnostics.putString("\n Unable to attach to target VM: " +
377                                   ioe.getMessage());
378         } catch (IllegalConnectorArgumentsException icae) {
379             diagnostics.putString("\n Invalid connector arguments: " +
380                                   icae.getMessage());
381         }
382         return null;
383     }
384 
385     private Session internalListen(ListeningConnector connector,
386                                    Map<String, Connector.Argument> arguments) {
387         try {
388             VirtualMachine vm = connector.accept(arguments);
389             return new Session(vm, this, diagnostics);
390         } catch (IOException ioe) {
391             diagnostics.putString(
392                   "\n Unable to accept connection to target VM: " +
393                                   ioe.getMessage());
394         } catch (IllegalConnectorArgumentsException icae) {
395             diagnostics.putString("\n Invalid connector arguments: " +
396                                   icae.getMessage());
397         }
398         return null;
399     }
400 
401     /*
402      * Connect via user specified arguments
403      * @return true on success
404      */
405     public boolean explictStart(Connector connector, Map<String, Connector.Argument> arguments)
406                                            throws VMLaunchFailureException {
407         Session newSession = null;
408 
409         endSession();
410 
411         if (connector instanceof LaunchingConnector) {
412             // we were launched, use ChildSession
413             newSession = new ChildSession(this, (LaunchingConnector)connector,
414                                           arguments,
415                                           appInput, appOutput, appError,
416                                           diagnostics);
417         } else if (connector instanceof AttachingConnector) {
418             newSession = internalAttach((AttachingConnector)connector,
419                                         arguments);
420         } else if (connector instanceof ListeningConnector) {
421             newSession = internalListen((ListeningConnector)connector,
422                                         arguments);
423         } else {
424             diagnostics.putString("\n Unknown connector: " + connector);
425         }
426         if (newSession != null) {
427             startSession(newSession);
428         }
429         return newSession != null;
430     }
431 
432     /*
433      * Detach from VM.  If VM was started by debugger, terminate it.
434      */
435     public void detach() throws NoSessionException {
436         ensureActiveSession();
437         endSession();
438     }
439 
440     private void startSession(Session s) throws VMLaunchFailureException {
441         if (!s.attach()) {
442             throw new VMLaunchFailureException();
443         }
444         session = s;
445         EventRequestManager em = vm().eventRequestManager();
446         ClassPrepareRequest classPrepareRequest = em.createClassPrepareRequest();
447         //### We must allow the deferred breakpoints to be resolved before
448         //### we continue executing the class.  We could optimize if there
449         //### were no deferred breakpoints outstanding for a particular class.
450         //### Can we do this with JDI?
451         classPrepareRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);
452         classPrepareRequest.enable();
453         ClassUnloadRequest classUnloadRequest = em.createClassUnloadRequest();
454         classUnloadRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
455         classUnloadRequest.enable();
456         ThreadStartRequest threadStartRequest = em.createThreadStartRequest();
457         threadStartRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
458         threadStartRequest.enable();
459         ThreadDeathRequest threadDeathRequest = em.createThreadDeathRequest();
460         threadDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
461         threadDeathRequest.enable();
462         ExceptionRequest exceptionRequest =
463                                 em.createExceptionRequest(null, false, true);
464         exceptionRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);
465         exceptionRequest.enable();
466         validateThreadInfo();
467         session.interrupted = true;
468         notifySessionStart();
469     }
470 
471     void endSession() {
472         if (session != null) {
473             session.detach();
474             session = null;
475             invalidateThreadInfo();
476             notifySessionDeath();
477         }
478     }
479 
480     /*
481      * Suspend all VM activity.
482      */
483 
484     public void interrupt() throws NoSessionException {
485         ensureActiveSession();
486         vm().suspend();
487         //### Is it guaranteed that the interrupt has happened?
488         validateThreadInfo();
489         session.interrupted = true;
490         notifyInterrupted();
491     }
492 
493     /*
494      * Resume interrupted VM.
495      */
496 
497     public void go() throws NoSessionException, VMNotInterruptedException {
498         ensureActiveSession();
499         invalidateThreadInfo();
500         session.interrupted = false;
501         notifyContinued();
502         vm().resume();
503     }
504 
505     /*
506      * Stepping.
507      */
508     void clearPreviousStep(ThreadReference thread) {
509         /*
510          * A previous step may not have completed on this thread;
511          * if so, it gets removed here.
512          */
513          EventRequestManager mgr = vm().eventRequestManager();
514          for (StepRequest request : mgr.stepRequests()) {
515              if (request.thread().equals(thread)) {
516                  mgr.deleteEventRequest(request);
517                  break;
518              }
519          }
520     }
521 
522     private void generalStep(ThreadReference thread, int size, int depth)
523                         throws NoSessionException {
524         ensureActiveSession();
525         invalidateThreadInfo();
526         session.interrupted = false;
527         notifyContinued();
528 
529         clearPreviousStep(thread);
530         EventRequestManager reqMgr = vm().eventRequestManager();
531         StepRequest request = reqMgr.createStepRequest(thread,
532                                                        size, depth);
533         // We want just the next step event and no others
534         request.addCountFilter(1);
535         request.enable();
536         vm().resume();
537     }
538 
539     public void stepIntoInstruction(ThreadReference thread)
540                         throws NoSessionException {
541         generalStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO);
542     }
543 
544     public void stepOverInstruction(ThreadReference thread)
545                         throws NoSessionException {
546         generalStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER);
547     }
548 
549     public void stepIntoLine(ThreadReference thread)
550                         throws NoSessionException,
551                         AbsentInformationException {
552         generalStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO);
553     }
554 
555     public void stepOverLine(ThreadReference thread)
556                         throws NoSessionException,
557                         AbsentInformationException {
558         generalStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER);
559     }
560 
561     public void stepOut(ThreadReference thread)
562                         throws NoSessionException {
563         generalStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OUT);
564     }
565 
566     /*
567      * Thread control.
568      */
569 
570     public void suspendThread(ThreadReference thread) throws NoSessionException {
571         ensureActiveSession();
572         thread.suspend();
573     }
574 
575     public void resumeThread(ThreadReference thread) throws NoSessionException {
576         ensureActiveSession();
577         thread.resume();
578     }
579 
580     public void stopThread(ThreadReference thread) throws NoSessionException {
581         ensureActiveSession();
582         //### Need an exception now.  Which one to use?
583         //thread.stop();
584     }
585 
586     /*
587      * ThreadInfo objects -- Allow query of thread status and stack.
588      */
589 
590     private List<ThreadInfo> threadInfoList = new LinkedList<ThreadInfo>();
591     //### Should be weak! (in the value, not the key)
592     private HashMap<ThreadReference, ThreadInfo> threadInfoMap = new HashMap<ThreadReference, ThreadInfo>();
593 
594     public ThreadInfo threadInfo(ThreadReference thread) {
595         if (session == null || thread == null) {
596             return null;
597         }
598         ThreadInfo info = threadInfoMap.get(thread);
599         if (info == null) {
600             //### Should not hardcode initial frame count and prefetch here!
601             //info = new ThreadInfo(thread, 10, 10);
602             info = new ThreadInfo(thread);
603             if (session.interrupted) {
604                 info.validate();
605             }
606             threadInfoList.add(info);
607             threadInfoMap.put(thread, info);
608         }
609         return info;
610     }
611 
612      void validateThreadInfo() {
613         session.interrupted = true;
614         for (ThreadInfo threadInfo : threadInfoList) {
615             threadInfo.validate();
616             }
617     }
618 
619     private void invalidateThreadInfo() {
620         if (session != null) {
621             session.interrupted = false;
622             for (ThreadInfo threadInfo : threadInfoList) {
623                 threadInfo.invalidate();
624             }
625         }
626     }
627 
628     void removeThreadInfo(ThreadReference thread) {
629         ThreadInfo info = threadInfoMap.get(thread);
630         if (info != null) {
631             info.invalidate();
632             threadInfoMap.remove(thread);
633             threadInfoList.remove(info);
634         }
635     }
636 
637     /*
638      * Listen for Session control events.
639      */
640 
641     private void notifyInterrupted() {
642       ArrayList<SessionListener> l = new ArrayList<SessionListener>(sessionListeners);
643         EventObject evt = new EventObject(this);
644         for (int i = 0; i < l.size(); i++) {
645             l.get(i).sessionInterrupt(evt);
646         }
647     }
648 
649     private void notifyContinued() {
650         ArrayList<SessionListener> l = new ArrayList<SessionListener>(sessionListeners);
651         EventObject evt = new EventObject(this);
652         for (int i = 0; i < l.size(); i++) {
653             l.get(i).sessionContinue(evt);
654         }
655     }
656 
657     private void notifySessionStart() {
658         ArrayList<SessionListener> l = new ArrayList<SessionListener>(sessionListeners);
659         EventObject evt = new EventObject(this);
660         for (int i = 0; i < l.size(); i++) {
661             l.get(i).sessionStart(evt);
662         }
663     }
664 
665     private void notifySessionDeath() {
666 /*** noop for now
667         ArrayList<SessionListener> l = new ArrayList<SessionListener>(sessionListeners);
668         EventObject evt = new EventObject(this);
669         for (int i = 0; i < l.size(); i++) {
670             ((SessionListener)l.get(i)).sessionDeath(evt);
671         }
672 ****/
673     }
674 
675     /*
676      * Listen for input and output requests from the application
677      * being debugged.  These are generated only when the debuggee
678      * is spawned as a child of the debugger.
679      */
680 
681     private Object inputLock = new Object();
682     private LinkedList<String> inputBuffer = new LinkedList<String>();
683 
684     private void resetInputBuffer() {
685         synchronized (inputLock) {
686             inputBuffer = new LinkedList<String>();
687         }
688     }
689 
690     public void sendLineToApplication(String line) {
691         synchronized (inputLock) {
692             inputBuffer.addFirst(line);
693             inputLock.notifyAll();
694         }
695     }
696 
697     private InputListener appInput = new InputListener() {
698         @Override
699         public String getLine() {
700             // Don't allow reader to be interrupted -- catch and retry.
701             String line = null;
702             while (line == null) {
703                 synchronized (inputLock) {
704                     try {
705                         while (inputBuffer.size() < 1) {
706                             inputLock.wait();
707                         }
708                         line = inputBuffer.removeLast();
709                     } catch (InterruptedException e) {}
710                 }
711             }
712             // We must not be holding inputLock here, as the listener
713             // that we call to echo a line might call us re-entrantly
714             // to provide another line of input.
715             // Run in Swing event dispatcher thread.
716             final String input = line;
717             SwingUtilities.invokeLater(new Runnable() {
718                 @Override
719                 public void run() {
720                     echoInputLine(input);
721                 }
722             });
723             return line;
724         }
725     };
726 
727     private static String newline = System.getProperty("line.separator");
728 
729     private void echoInputLine(String line) {
730         ArrayList<OutputListener> l = new ArrayList<OutputListener>(appEchoListeners);
731         for (int i = 0; i < l.size(); i++) {
732             OutputListener ol = l.get(i);
733             ol.putString(line);
734             ol.putString(newline);
735         }
736     }
737 
738     private OutputListener appOutput = new OutputListener() {
739       @Override
740         public void putString(String string) {
741             ArrayList<OutputListener> l = new ArrayList<OutputListener>(appEchoListeners);
742             for (int i = 0; i < l.size(); i++) {
743                 l.get(i).putString(string);
744             }
745         }
746     };
747 
748     private OutputListener appError = new OutputListener() {
749       @Override
750         public void putString(String string) {
751             ArrayList<OutputListener> l = new ArrayList<OutputListener>(appEchoListeners);
752             for (int i = 0; i < l.size(); i++) {
753                 l.get(i).putString(string);
754             }
755         }
756     };
757 
758    private OutputListener diagnostics = new OutputListener() {
759       @Override
760         public void putString(String string) {
761             ArrayList<OutputListener> l = new ArrayList<OutputListener>(diagnosticsListeners);
762             for (int i = 0; i < l.size(); i++) {
763                 l.get(i).putString(string);
764             }
765         }
766    };
767 
768   /////////////    Spec Request Creation/Deletion/Query   ///////////
769 
770     private EventRequestSpecList specList = new EventRequestSpecList(this);
771 
772     public BreakpointSpec
773     createSourceLineBreakpoint(String sourceName, int line) {
774         return specList.createSourceLineBreakpoint(sourceName, line);
775     }
776 
777     public BreakpointSpec
778     createClassLineBreakpoint(String classPattern, int line) {
779         return specList.createClassLineBreakpoint(classPattern, line);
780     }
781 
782     public BreakpointSpec
783     createMethodBreakpoint(String classPattern,
784                            String methodId, List<String> methodArgs) {
785         return specList.createMethodBreakpoint(classPattern,
786                                                  methodId, methodArgs);
787     }
788 
789     public ExceptionSpec
790     createExceptionIntercept(String classPattern,
791                              boolean notifyCaught,
792                              boolean notifyUncaught) {
793         return specList.createExceptionIntercept(classPattern,
794                                                    notifyCaught,
795                                                    notifyUncaught);
796     }
797 
798     public AccessWatchpointSpec
799     createAccessWatchpoint(String classPattern, String fieldId) {
800         return specList.createAccessWatchpoint(classPattern, fieldId);
801     }
802 
803     public ModificationWatchpointSpec
804     createModificationWatchpoint(String classPattern, String fieldId) {
805         return specList.createModificationWatchpoint(classPattern,
806                                                        fieldId);
807     }
808 
809     public void delete(EventRequestSpec spec) {
810         specList.delete(spec);
811     }
812 
813     void resolve(ReferenceType refType) {
814         specList.resolve(refType);
815     }
816 
817     public void install(EventRequestSpec spec) {
818         specList.install(spec, vm());
819     }
820 
821     public List<EventRequestSpec> eventRequestSpecs() {
822         return specList.eventRequestSpecs();
823     }
824 }