| /* |
| * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * This source code is provided to illustrate the usage of a given feature |
| * or technique and has been deliberately simplified. Additional steps |
| * required for a production-quality application, such as security checks, |
| * input validation and proper error handling, might not be present in |
| * this sample code. |
| */ |
| |
| |
| package com.sun.tools.example.debug.bdi; |
| |
| import com.sun.jdi.*; |
| import com.sun.jdi.request.*; |
| import com.sun.jdi.connect.*; |
| import com.sun.tools.example.debug.expr.ExpressionParser; |
| import com.sun.tools.example.debug.expr.ParseException; |
| |
| import java.io.*; |
| import java.util.*; |
| |
| import com.sun.tools.example.debug.event.*; |
| |
| import javax.swing.SwingUtilities; |
| |
| /** |
| * Move this towards being only state and functionality |
| * that spans across Sessions (and thus VMs). |
| */ |
| public class ExecutionManager { |
| |
| private Session session; |
| |
| /** |
| * Get/set JDI trace mode. |
| */ |
| int traceMode = VirtualMachine.TRACE_NONE; |
| |
| ////////////////// Listener registration ////////////////// |
| |
| // Session Listeners |
| |
| ArrayList<SessionListener> sessionListeners = new ArrayList<SessionListener>(); |
| |
| public void addSessionListener(SessionListener listener) { |
| sessionListeners.add(listener); |
| } |
| |
| public void removeSessionListener(SessionListener listener) { |
| sessionListeners.remove(listener); |
| } |
| |
| // Spec Listeners |
| |
| ArrayList<SpecListener> specListeners = new ArrayList<SpecListener>(); |
| |
| public void addSpecListener(SpecListener cl) { |
| specListeners.add(cl); |
| } |
| |
| public void removeSpecListener(SpecListener cl) { |
| specListeners.remove(cl); |
| } |
| |
| // JDI Listeners |
| |
| ArrayList<JDIListener> jdiListeners = new ArrayList<JDIListener>(); |
| |
| /** |
| * Adds a JDIListener |
| */ |
| public void addJDIListener(JDIListener jl) { |
| jdiListeners.add(jl); |
| } |
| |
| /** |
| * Adds a JDIListener - at the specified position |
| */ |
| public void addJDIListener(int index, JDIListener jl) { |
| jdiListeners.add(index, jl); |
| } |
| |
| /** |
| * Removes a JDIListener |
| */ |
| public void removeJDIListener(JDIListener jl) { |
| jdiListeners.remove(jl); |
| } |
| |
| // App Echo Listeners |
| |
| private ArrayList<OutputListener> appEchoListeners = new ArrayList<OutputListener>(); |
| |
| public void addApplicationEchoListener(OutputListener l) { |
| appEchoListeners.add(l); |
| } |
| |
| public void removeApplicationEchoListener(OutputListener l) { |
| appEchoListeners.remove(l); |
| } |
| |
| // App Output Listeners |
| |
| private ArrayList<OutputListener> appOutputListeners = new ArrayList<OutputListener>(); |
| |
| public void addApplicationOutputListener(OutputListener l) { |
| appOutputListeners.add(l); |
| } |
| |
| public void removeApplicationOutputListener(OutputListener l) { |
| appOutputListeners.remove(l); |
| } |
| |
| // App Error Listeners |
| |
| private ArrayList<OutputListener> appErrorListeners = new ArrayList<OutputListener>(); |
| |
| public void addApplicationErrorListener(OutputListener l) { |
| appErrorListeners.add(l); |
| } |
| |
| public void removeApplicationErrorListener(OutputListener l) { |
| appErrorListeners.remove(l); |
| } |
| |
| // Diagnostic Listeners |
| |
| private ArrayList<OutputListener> diagnosticsListeners = new ArrayList<OutputListener>(); |
| |
| public void addDiagnosticsListener(OutputListener l) { |
| diagnosticsListeners.add(l); |
| } |
| |
| public void removeDiagnosticsListener(OutputListener l) { |
| diagnosticsListeners.remove(l); |
| } |
| |
| /////////// End Listener Registration ////////////// |
| |
| //### We probably don't want this public |
| public VirtualMachine vm() { |
| return session == null ? null : session.vm; |
| } |
| |
| void ensureActiveSession() throws NoSessionException { |
| if (session == null) { |
| throw new NoSessionException(); |
| } |
| } |
| |
| public EventRequestManager eventRequestManager() { |
| return vm() == null ? null : vm().eventRequestManager(); |
| } |
| |
| /** |
| * Get JDI trace mode. |
| */ |
| public int getTraceMode(int mode) { |
| return traceMode; |
| } |
| |
| /** |
| * Set JDI trace mode. |
| */ |
| public void setTraceMode(int mode) { |
| traceMode = mode; |
| if (session != null) { |
| session.setTraceMode(mode); |
| } |
| } |
| |
| /** |
| * Determine if VM is interrupted, i.e, present and not running. |
| */ |
| public boolean isInterrupted() /* should: throws NoSessionException */ { |
| // ensureActiveSession(); |
| return session.interrupted; |
| } |
| |
| /** |
| * Return a list of ReferenceType objects for all |
| * currently loaded classes and interfaces. |
| * Array types are not returned. |
| */ |
| public List<ReferenceType> allClasses() throws NoSessionException { |
| ensureActiveSession(); |
| return vm().allClasses(); |
| } |
| |
| /** |
| * Return a ReferenceType object for the currently |
| * loaded class or interface whose fully-qualified |
| * class name is specified, else return null if there |
| * is none. |
| * |
| * In general, we must return a list of types, because |
| * multiple class loaders could have loaded a class |
| * with the same fully-qualified name. |
| */ |
| public List<ReferenceType> findClassesByName(String name) throws NoSessionException { |
| ensureActiveSession(); |
| return vm().classesByName(name); |
| } |
| |
| /** |
| * Return a list of ReferenceType objects for all |
| * currently loaded classes and interfaces whose name |
| * matches the given pattern. The pattern syntax is |
| * open to some future revision, but currently consists |
| * of a fully-qualified class name in which the first |
| * component may optionally be a "*" character, designating |
| * an arbitrary prefix. |
| */ |
| public List<ReferenceType> findClassesMatchingPattern(String pattern) |
| throws NoSessionException { |
| ensureActiveSession(); |
| List<ReferenceType> result = new ArrayList<ReferenceType>(); //### Is default size OK? |
| if (pattern.startsWith("*.")) { |
| // Wildcard matches any leading package name. |
| pattern = pattern.substring(1); |
| for (ReferenceType type : vm().allClasses()) { |
| if (type.name().endsWith(pattern)) { |
| result.add(type); |
| } |
| } |
| return result; |
| } else { |
| // It's a class name. |
| return vm().classesByName(pattern); |
| } |
| } |
| |
| /* |
| * Return a list of ThreadReference objects corresponding |
| * to the threads that are currently active in the VM. |
| * A thread is removed from the list just before the |
| * thread terminates. |
| */ |
| |
| public List<ThreadReference> allThreads() throws NoSessionException { |
| ensureActiveSession(); |
| return vm().allThreads(); |
| } |
| |
| /* |
| * Return a list of ThreadGroupReference objects corresponding |
| * to the top-level threadgroups that are currently active in the VM. |
| * Note that a thread group may be empty, or contain no threads as |
| * descendents. |
| */ |
| |
| public List<ThreadGroupReference> topLevelThreadGroups() throws NoSessionException { |
| ensureActiveSession(); |
| return vm().topLevelThreadGroups(); |
| } |
| |
| /* |
| * Return the system threadgroup. |
| */ |
| |
| public ThreadGroupReference systemThreadGroup() |
| throws NoSessionException { |
| ensureActiveSession(); |
| return vm().topLevelThreadGroups().get(0); |
| } |
| |
| /* |
| * Evaluate an expression. |
| */ |
| |
| public Value evaluate(final StackFrame f, String expr) |
| throws ParseException, |
| InvocationException, |
| InvalidTypeException, |
| ClassNotLoadedException, |
| NoSessionException, |
| IncompatibleThreadStateException { |
| ExpressionParser.GetFrame frameGetter = null; |
| ensureActiveSession(); |
| if (f != null) { |
| frameGetter = new ExpressionParser.GetFrame() { |
| @Override |
| public StackFrame get() /* throws IncompatibleThreadStateException */ { |
| return f; |
| } |
| }; |
| } |
| return ExpressionParser.evaluate(expr, vm(), frameGetter); |
| } |
| |
| |
| /* |
| * Start a new VM. |
| */ |
| |
| public void run(boolean suspended, |
| String vmArgs, |
| String className, |
| String args) throws VMLaunchFailureException { |
| |
| endSession(); |
| |
| //### Set a breakpoint on 'main' method. |
| //### Would be cleaner if we could just bring up VM already suspended. |
| if (suspended) { |
| //### Set breakpoint at 'main(java.lang.String[])'. |
| List<String> argList = new ArrayList<String>(1); |
| argList.add("java.lang.String[]"); |
| createMethodBreakpoint(className, "main", argList); |
| } |
| |
| String cmdLine = className + " " + args; |
| |
| startSession(new ChildSession(this, vmArgs, cmdLine, |
| appInput, appOutput, appError, |
| diagnostics)); |
| } |
| |
| /* |
| * Attach to an existing VM. |
| */ |
| public void attach(String portName) throws VMLaunchFailureException { |
| endSession(); |
| |
| //### Changes made here for connectors have broken the |
| //### the 'Session' abstraction. The 'Session.attach()' |
| //### method is intended to encapsulate all of the various |
| //### ways in which session start-up can fail. (maddox 12/18/98) |
| |
| /* |
| * Now that attaches and launches both go through Connectors, |
| * it may be worth creating a new subclass of Session for |
| * attach sessions. |
| */ |
| VirtualMachineManager mgr = Bootstrap.virtualMachineManager(); |
| AttachingConnector connector = mgr.attachingConnectors().get(0); |
| Map<String, Connector.Argument> arguments = connector.defaultArguments(); |
| arguments.get("port").setValue(portName); |
| |
| Session newSession = internalAttach(connector, arguments); |
| if (newSession != null) { |
| startSession(newSession); |
| } |
| } |
| |
| private Session internalAttach(AttachingConnector connector, |
| Map<String, Connector.Argument> arguments) { |
| try { |
| VirtualMachine vm = connector.attach(arguments); |
| return new Session(vm, this, diagnostics); |
| } catch (IOException ioe) { |
| diagnostics.putString("\n Unable to attach to target VM: " + |
| ioe.getMessage()); |
| } catch (IllegalConnectorArgumentsException icae) { |
| diagnostics.putString("\n Invalid connector arguments: " + |
| icae.getMessage()); |
| } |
| return null; |
| } |
| |
| private Session internalListen(ListeningConnector connector, |
| Map<String, Connector.Argument> arguments) { |
| try { |
| VirtualMachine vm = connector.accept(arguments); |
| return new Session(vm, this, diagnostics); |
| } catch (IOException ioe) { |
| diagnostics.putString( |
| "\n Unable to accept connection to target VM: " + |
| ioe.getMessage()); |
| } catch (IllegalConnectorArgumentsException icae) { |
| diagnostics.putString("\n Invalid connector arguments: " + |
| icae.getMessage()); |
| } |
| return null; |
| } |
| |
| /* |
| * Connect via user specified arguments |
| * @return true on success |
| */ |
| public boolean explictStart(Connector connector, Map<String, Connector.Argument> arguments) |
| throws VMLaunchFailureException { |
| Session newSession = null; |
| |
| endSession(); |
| |
| if (connector instanceof LaunchingConnector) { |
| // we were launched, use ChildSession |
| newSession = new ChildSession(this, (LaunchingConnector)connector, |
| arguments, |
| appInput, appOutput, appError, |
| diagnostics); |
| } else if (connector instanceof AttachingConnector) { |
| newSession = internalAttach((AttachingConnector)connector, |
| arguments); |
| } else if (connector instanceof ListeningConnector) { |
| newSession = internalListen((ListeningConnector)connector, |
| arguments); |
| } else { |
| diagnostics.putString("\n Unknown connector: " + connector); |
| } |
| if (newSession != null) { |
| startSession(newSession); |
| } |
| return newSession != null; |
| } |
| |
| /* |
| * Detach from VM. If VM was started by debugger, terminate it. |
| */ |
| public void detach() throws NoSessionException { |
| ensureActiveSession(); |
| endSession(); |
| } |
| |
| private void startSession(Session s) throws VMLaunchFailureException { |
| if (!s.attach()) { |
| throw new VMLaunchFailureException(); |
| } |
| session = s; |
| EventRequestManager em = vm().eventRequestManager(); |
| ClassPrepareRequest classPrepareRequest = em.createClassPrepareRequest(); |
| //### We must allow the deferred breakpoints to be resolved before |
| //### we continue executing the class. We could optimize if there |
| //### were no deferred breakpoints outstanding for a particular class. |
| //### Can we do this with JDI? |
| classPrepareRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); |
| classPrepareRequest.enable(); |
| ClassUnloadRequest classUnloadRequest = em.createClassUnloadRequest(); |
| classUnloadRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); |
| classUnloadRequest.enable(); |
| ThreadStartRequest threadStartRequest = em.createThreadStartRequest(); |
| threadStartRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); |
| threadStartRequest.enable(); |
| ThreadDeathRequest threadDeathRequest = em.createThreadDeathRequest(); |
| threadDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE); |
| threadDeathRequest.enable(); |
| ExceptionRequest exceptionRequest = |
| em.createExceptionRequest(null, false, true); |
| exceptionRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL); |
| exceptionRequest.enable(); |
| validateThreadInfo(); |
| session.interrupted = true; |
| notifySessionStart(); |
| } |
| |
| void endSession() { |
| if (session != null) { |
| session.detach(); |
| session = null; |
| invalidateThreadInfo(); |
| notifySessionDeath(); |
| } |
| } |
| |
| /* |
| * Suspend all VM activity. |
| */ |
| |
| public void interrupt() throws NoSessionException { |
| ensureActiveSession(); |
| vm().suspend(); |
| //### Is it guaranteed that the interrupt has happened? |
| validateThreadInfo(); |
| session.interrupted = true; |
| notifyInterrupted(); |
| } |
| |
| /* |
| * Resume interrupted VM. |
| */ |
| |
| public void go() throws NoSessionException, VMNotInterruptedException { |
| ensureActiveSession(); |
| invalidateThreadInfo(); |
| session.interrupted = false; |
| notifyContinued(); |
| vm().resume(); |
| } |
| |
| /* |
| * Stepping. |
| */ |
| void clearPreviousStep(ThreadReference thread) { |
| /* |
| * A previous step may not have completed on this thread; |
| * if so, it gets removed here. |
| */ |
| EventRequestManager mgr = vm().eventRequestManager(); |
| for (StepRequest request : mgr.stepRequests()) { |
| if (request.thread().equals(thread)) { |
| mgr.deleteEventRequest(request); |
| break; |
| } |
| } |
| } |
| |
| private void generalStep(ThreadReference thread, int size, int depth) |
| throws NoSessionException { |
| ensureActiveSession(); |
| invalidateThreadInfo(); |
| session.interrupted = false; |
| notifyContinued(); |
| |
| clearPreviousStep(thread); |
| EventRequestManager reqMgr = vm().eventRequestManager(); |
| StepRequest request = reqMgr.createStepRequest(thread, |
| size, depth); |
| // We want just the next step event and no others |
| request.addCountFilter(1); |
| request.enable(); |
| vm().resume(); |
| } |
| |
| public void stepIntoInstruction(ThreadReference thread) |
| throws NoSessionException { |
| generalStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO); |
| } |
| |
| public void stepOverInstruction(ThreadReference thread) |
| throws NoSessionException { |
| generalStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER); |
| } |
| |
| public void stepIntoLine(ThreadReference thread) |
| throws NoSessionException, |
| AbsentInformationException { |
| generalStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO); |
| } |
| |
| public void stepOverLine(ThreadReference thread) |
| throws NoSessionException, |
| AbsentInformationException { |
| generalStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER); |
| } |
| |
| public void stepOut(ThreadReference thread) |
| throws NoSessionException { |
| generalStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OUT); |
| } |
| |
| /* |
| * Thread control. |
| */ |
| |
| public void suspendThread(ThreadReference thread) throws NoSessionException { |
| ensureActiveSession(); |
| thread.suspend(); |
| } |
| |
| public void resumeThread(ThreadReference thread) throws NoSessionException { |
| ensureActiveSession(); |
| thread.resume(); |
| } |
| |
| public void stopThread(ThreadReference thread) throws NoSessionException { |
| ensureActiveSession(); |
| //### Need an exception now. Which one to use? |
| //thread.stop(); |
| } |
| |
| /* |
| * ThreadInfo objects -- Allow query of thread status and stack. |
| */ |
| |
| private List<ThreadInfo> threadInfoList = new LinkedList<ThreadInfo>(); |
| //### Should be weak! (in the value, not the key) |
| private HashMap<ThreadReference, ThreadInfo> threadInfoMap = new HashMap<ThreadReference, ThreadInfo>(); |
| |
| public ThreadInfo threadInfo(ThreadReference thread) { |
| if (session == null || thread == null) { |
| return null; |
| } |
| ThreadInfo info = threadInfoMap.get(thread); |
| if (info == null) { |
| //### Should not hardcode initial frame count and prefetch here! |
| //info = new ThreadInfo(thread, 10, 10); |
| info = new ThreadInfo(thread); |
| if (session.interrupted) { |
| info.validate(); |
| } |
| threadInfoList.add(info); |
| threadInfoMap.put(thread, info); |
| } |
| return info; |
| } |
| |
| void validateThreadInfo() { |
| session.interrupted = true; |
| for (ThreadInfo threadInfo : threadInfoList) { |
| threadInfo.validate(); |
| } |
| } |
| |
| private void invalidateThreadInfo() { |
| if (session != null) { |
| session.interrupted = false; |
| for (ThreadInfo threadInfo : threadInfoList) { |
| threadInfo.invalidate(); |
| } |
| } |
| } |
| |
| void removeThreadInfo(ThreadReference thread) { |
| ThreadInfo info = threadInfoMap.get(thread); |
| if (info != null) { |
| info.invalidate(); |
| threadInfoMap.remove(thread); |
| threadInfoList.remove(info); |
| } |
| } |
| |
| /* |
| * Listen for Session control events. |
| */ |
| |
| private void notifyInterrupted() { |
| ArrayList<SessionListener> l = new ArrayList<SessionListener>(sessionListeners); |
| EventObject evt = new EventObject(this); |
| for (int i = 0; i < l.size(); i++) { |
| l.get(i).sessionInterrupt(evt); |
| } |
| } |
| |
| private void notifyContinued() { |
| ArrayList<SessionListener> l = new ArrayList<SessionListener>(sessionListeners); |
| EventObject evt = new EventObject(this); |
| for (int i = 0; i < l.size(); i++) { |
| l.get(i).sessionContinue(evt); |
| } |
| } |
| |
| private void notifySessionStart() { |
| ArrayList<SessionListener> l = new ArrayList<SessionListener>(sessionListeners); |
| EventObject evt = new EventObject(this); |
| for (int i = 0; i < l.size(); i++) { |
| l.get(i).sessionStart(evt); |
| } |
| } |
| |
| private void notifySessionDeath() { |
| /*** noop for now |
| ArrayList<SessionListener> l = new ArrayList<SessionListener>(sessionListeners); |
| EventObject evt = new EventObject(this); |
| for (int i = 0; i < l.size(); i++) { |
| ((SessionListener)l.get(i)).sessionDeath(evt); |
| } |
| ****/ |
| } |
| |
| /* |
| * Listen for input and output requests from the application |
| * being debugged. These are generated only when the debuggee |
| * is spawned as a child of the debugger. |
| */ |
| |
| private Object inputLock = new Object(); |
| private LinkedList<String> inputBuffer = new LinkedList<String>(); |
| |
| private void resetInputBuffer() { |
| synchronized (inputLock) { |
| inputBuffer = new LinkedList<String>(); |
| } |
| } |
| |
| public void sendLineToApplication(String line) { |
| synchronized (inputLock) { |
| inputBuffer.addFirst(line); |
| inputLock.notifyAll(); |
| } |
| } |
| |
| private InputListener appInput = new InputListener() { |
| @Override |
| public String getLine() { |
| // Don't allow reader to be interrupted -- catch and retry. |
| String line = null; |
| while (line == null) { |
| synchronized (inputLock) { |
| try { |
| while (inputBuffer.size() < 1) { |
| inputLock.wait(); |
| } |
| line = inputBuffer.removeLast(); |
| } catch (InterruptedException e) {} |
| } |
| } |
| // We must not be holding inputLock here, as the listener |
| // that we call to echo a line might call us re-entrantly |
| // to provide another line of input. |
| // Run in Swing event dispatcher thread. |
| final String input = line; |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| echoInputLine(input); |
| } |
| }); |
| return line; |
| } |
| }; |
| |
| private static String newline = System.getProperty("line.separator"); |
| |
| private void echoInputLine(String line) { |
| ArrayList<OutputListener> l = new ArrayList<OutputListener>(appEchoListeners); |
| for (int i = 0; i < l.size(); i++) { |
| OutputListener ol = l.get(i); |
| ol.putString(line); |
| ol.putString(newline); |
| } |
| } |
| |
| private OutputListener appOutput = new OutputListener() { |
| @Override |
| public void putString(String string) { |
| ArrayList<OutputListener> l = new ArrayList<OutputListener>(appEchoListeners); |
| for (int i = 0; i < l.size(); i++) { |
| l.get(i).putString(string); |
| } |
| } |
| }; |
| |
| private OutputListener appError = new OutputListener() { |
| @Override |
| public void putString(String string) { |
| ArrayList<OutputListener> l = new ArrayList<OutputListener>(appEchoListeners); |
| for (int i = 0; i < l.size(); i++) { |
| l.get(i).putString(string); |
| } |
| } |
| }; |
| |
| private OutputListener diagnostics = new OutputListener() { |
| @Override |
| public void putString(String string) { |
| ArrayList<OutputListener> l = new ArrayList<OutputListener>(diagnosticsListeners); |
| for (int i = 0; i < l.size(); i++) { |
| l.get(i).putString(string); |
| } |
| } |
| }; |
| |
| ///////////// Spec Request Creation/Deletion/Query /////////// |
| |
| private EventRequestSpecList specList = new EventRequestSpecList(this); |
| |
| public BreakpointSpec |
| createSourceLineBreakpoint(String sourceName, int line) { |
| return specList.createSourceLineBreakpoint(sourceName, line); |
| } |
| |
| public BreakpointSpec |
| createClassLineBreakpoint(String classPattern, int line) { |
| return specList.createClassLineBreakpoint(classPattern, line); |
| } |
| |
| public BreakpointSpec |
| createMethodBreakpoint(String classPattern, |
| String methodId, List<String> methodArgs) { |
| return specList.createMethodBreakpoint(classPattern, |
| methodId, methodArgs); |
| } |
| |
| public ExceptionSpec |
| createExceptionIntercept(String classPattern, |
| boolean notifyCaught, |
| boolean notifyUncaught) { |
| return specList.createExceptionIntercept(classPattern, |
| notifyCaught, |
| notifyUncaught); |
| } |
| |
| public AccessWatchpointSpec |
| createAccessWatchpoint(String classPattern, String fieldId) { |
| return specList.createAccessWatchpoint(classPattern, fieldId); |
| } |
| |
| public ModificationWatchpointSpec |
| createModificationWatchpoint(String classPattern, String fieldId) { |
| return specList.createModificationWatchpoint(classPattern, |
| fieldId); |
| } |
| |
| public void delete(EventRequestSpec spec) { |
| specList.delete(spec); |
| } |
| |
| void resolve(ReferenceType refType) { |
| specList.resolve(refType); |
| } |
| |
| public void install(EventRequestSpec spec) { |
| specList.install(spec, vm()); |
| } |
| |
| public List<EventRequestSpec> eventRequestSpecs() { |
| return specList.eventRequestSpecs(); |
| } |
| } |