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