001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.debugger.jpda;
043:
044: import com.sun.jdi.IncompatibleThreadStateException;
045: import com.sun.jdi.event.LocatableEvent;
046: import java.awt.Color;
047: import java.awt.Dialog;
048: import java.awt.GridBagLayout;
049: import java.awt.GridBagConstraints;
050: import java.awt.event.ActionEvent;
051: import java.awt.event.ActionListener;
052: import java.util.ArrayList;
053: import java.util.HashMap;
054: import java.util.HashSet;
055: import java.util.Iterator;
056: import java.util.List;
057: import java.util.Map;
058: import java.util.Set;
059: import java.util.logging.Level;
060: import java.util.logging.Logger;
061:
062: import javax.swing.JCheckBox;
063: import javax.swing.JPanel;
064: import javax.swing.JTextArea;
065: import javax.swing.UIManager;
066:
067: import org.netbeans.api.debugger.DebuggerManager;
068: import org.netbeans.api.debugger.Session;
069: import org.netbeans.api.debugger.jpda.JPDABreakpoint;
070: import org.netbeans.api.debugger.jpda.JPDAStep;
071: import org.netbeans.api.debugger.jpda.JPDAThread;
072: import org.netbeans.api.debugger.jpda.JPDADebugger;
073: import com.sun.jdi.event.Event;
074: import com.sun.jdi.request.BreakpointRequest;
075: import com.sun.jdi.request.StepRequest;
076: import com.sun.jdi.request.EventRequest;
077: import com.sun.jdi.request.EventRequestManager;
078: import com.sun.jdi.ClassType;
079: import com.sun.jdi.Location;
080: import com.sun.jdi.Method;
081: import com.sun.jdi.ReferenceType;
082: import com.sun.jdi.StackFrame;
083: import com.sun.jdi.ThreadReference;
084: import com.sun.jdi.VirtualMachine;
085: import org.netbeans.api.debugger.jpda.JPDAThreadGroup;
086: import org.netbeans.api.debugger.jpda.MethodBreakpoint;
087: import org.netbeans.api.debugger.jpda.Variable;
088: import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
089: import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener;
090: import org.netbeans.modules.debugger.jpda.JPDAStepImpl.SingleThreadedStepWatch;
091: import org.netbeans.modules.debugger.jpda.breakpoints.MethodBreakpointImpl;
092: import org.netbeans.modules.debugger.jpda.util.Executor;
093: import org.netbeans.modules.debugger.jpda.actions.StepIntoActionProvider;
094: import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
095: import org.netbeans.spi.debugger.jpda.EditorContext.Operation;
096: import org.openide.DialogDescriptor;
097: import org.openide.ErrorManager;
098: import org.openide.NotifyDescriptor;
099: import org.openide.util.NbBundle;
100: import org.openide.util.RequestProcessor;
101:
102: public class JPDAStepImpl extends JPDAStep implements Executor {
103:
104: private static Logger logger = Logger
105: .getLogger("org.netbeans.modules.debugger.jpda.step"); // NOI18N
106:
107: /** The source tree with location info of this step */
108: //private ASTL stepASTL;
109: private Operation[] currentOperations;
110: private Operation lastOperation;
111: private MethodExitBreakpointListener lastMethodExitBreakpointListener;
112: private Set<BreakpointRequest> operationBreakpoints;
113: private StepRequest boundaryStepRequest;
114: private SingleThreadedStepWatch stepWatch;
115:
116: private Session session;
117:
118: public JPDAStepImpl(JPDADebugger debugger, Session session,
119: int size, int depth) {
120: super (debugger, size, depth);
121: this .session = session;
122: }
123:
124: public void addStep(JPDAThread tr) {
125: JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger;
126: JPDAThreadImpl trImpl = (JPDAThreadImpl) tr;
127: VirtualMachine vm = debuggerImpl.getVirtualMachine();
128: if (vm == null) {
129: return; // The session has finished
130: }
131: SourcePath sourcePath = ((JPDADebuggerImpl) debugger)
132: .getEngineContext();
133: boolean[] setStoppedStateNoContinue = new boolean[] { false };
134: synchronized (((JPDADebuggerImpl) debugger).LOCK) {
135: // synchronize on debugger LOCK first so that it can not happen that we
136: // take locks in the oposite order
137: synchronized (tr) {
138: ((JPDAThreadImpl) tr).waitUntilMethodInvokeDone();
139: EventRequestManager erm = vm.eventRequestManager();
140: //Remove all step requests -- TODO: Do we want it?
141: List<StepRequest> stepRequests = erm.stepRequests();
142: erm.deleteEventRequests(stepRequests);
143: for (StepRequest stepRequest : stepRequests) {
144: SingleThreadedStepWatch
145: .stepRequestDeleted(stepRequest);
146: debuggerImpl.getOperator().unregister(stepRequest);
147: }
148: int size = getSize();
149: boolean stepAdded = false;
150: logger
151: .log(
152: Level.FINE,
153: "Step "
154: + ((size == JPDAStep.STEP_OPERATION) ? "operation"
155: : "line")
156: + " "
157: + ((getDepth() == JPDAStep.STEP_INTO) ? "into"
158: : ((getDepth() == JPDAStep.STEP_OVER) ? "over"
159: : "out"))
160: + " in thread " + tr.getName());
161: if (size == JPDAStep.STEP_OPERATION) {
162: stepAdded = addOperationStep(trImpl, false,
163: sourcePath, setStoppedStateNoContinue);
164: if (!stepAdded) {
165: size = JPDAStep.STEP_LINE;
166: logger.log(Level.FINE,
167: "Operation step changed to line step");
168: }
169: }
170: if (!stepAdded) {
171: StepRequest stepRequest = vm.eventRequestManager()
172: .createStepRequest(
173: trImpl.getThreadReference(), size,
174: getDepth());
175: stepRequest.addCountFilter(1);
176: debuggerImpl.getOperator().register(stepRequest,
177: this );
178: stepRequest.setSuspendPolicy(debugger.getSuspend());
179:
180: try {
181: stepRequest.enable();
182: } catch (IllegalThreadStateException itsex) {
183: // the thread named in the request has died.
184: debuggerImpl.getOperator().unregister(
185: stepRequest);
186: stepRequest = null;
187: }
188:
189: if (stepRequest != null
190: && stepRequest.suspendPolicy() == StepRequest.SUSPEND_EVENT_THREAD) {
191: stepWatch = new SingleThreadedStepWatch(
192: debuggerImpl, stepRequest);
193: }
194: }
195: }
196: }
197: if (setStoppedStateNoContinue[0]) {
198: debuggerImpl.setStoppedStateNoContinue(trImpl
199: .getThreadReference());
200: }
201: }
202:
203: private boolean addOperationStep(JPDAThreadImpl tr,
204: boolean lineStepExec, SourcePath sourcePath,
205: boolean[] setStoppedStateNoContinue) {
206: ThreadReference trRef = tr.getThreadReference();
207: StackFrame sf;
208: try {
209: sf = trRef.frame(0);
210: } catch (IncompatibleThreadStateException itsex) {
211: return false;
212: }
213: Location loc = sf.location();
214: Session currentSession = DebuggerManager.getDebuggerManager()
215: .getCurrentSession();
216: String language = currentSession == null ? null
217: : currentSession.getCurrentLanguage();
218: String url = sourcePath.getURL(loc, language);
219: ExpressionPool exprPool = ((JPDADebuggerImpl) debugger)
220: .getExpressionPool();
221: ExpressionPool.Expression expr = exprPool.getExpressionAt(loc,
222: url);
223: if (expr == null) {
224: return false;
225: }
226: Operation[] ops = expr.getOperations();
227:
228: //Operation operation = null;
229: int opIndex = -1;
230: int codeIndex = (int) loc.codeIndex();
231: if (codeIndex <= ops[0].getBytecodeIndex()) {
232: if (!lineStepExec) {
233: tr.clearLastOperations();
234: }
235: // We're at the beginning. Just take the first operation
236: if (!ops[0].equals(tr.getCurrentOperation())) {
237: opIndex = expr.findNextOperationIndex(codeIndex - 1);
238: if (opIndex >= 0
239: && ops[opIndex].getBytecodeIndex() == codeIndex) {
240: tr.setCurrentOperation(ops[opIndex]);
241: if (lineStepExec) {
242: return false;
243: }
244: if (!getHidden()) {
245: setStoppedStateNoContinue[0] = true;
246: }
247: return true;
248: }
249: }
250: }
251: Operation currentOp = tr.getCurrentOperation();
252: if (currentOp != null) {
253: Operation theLastOperation = null;
254: java.util.List<Operation> lastOperations = tr
255: .getLastOperations();
256: if (lastOperations != null && lastOperations.size() > 0) {
257: theLastOperation = lastOperations.get(lastOperations
258: .size() - 1);
259: }
260: if (theLastOperation == currentOp) {
261: // We're right after some operation
262: // Check, whether there is some other operation directly on this
263: // position. If yes, it must be executed next.
264: for (Operation op : ops) {
265: if (op.getBytecodeIndex() == codeIndex) {
266: tr.setCurrentOperation(op);
267: if (!getHidden()) {
268: setStoppedStateNoContinue[0] = true;
269: }
270: return true;
271: }
272: }
273: }
274: }
275: this .lastOperation = currentOp;
276: VirtualMachine vm = loc.virtualMachine();
277: if (lastOperation != null) {
278: // Set the method exit breakpoint to get the return value
279: String methodName = lastOperation.getMethodName();
280: if (methodName != null
281: && MethodBreakpointImpl
282: .canGetMethodReturnValues(vm)) {
283: // TODO: Would be nice to know which ObjectReference we're executing the method on
284: MethodBreakpoint mb = MethodBreakpoint.create(
285: lastOperation.getMethodClassType(), methodName);
286: mb.setClassFilters(createClassFilters(vm, lastOperation
287: .getMethodClassType(), methodName));
288: mb.setThreadFilters(debugger, new JPDAThread[] { tr });
289: //mb.setMethodName(methodName);
290: mb.setBreakpointType(MethodBreakpoint.TYPE_METHOD_EXIT);
291: mb.setHidden(true);
292: mb.setSuspend(JPDABreakpoint.SUSPEND_NONE);
293: lastMethodExitBreakpointListener = new MethodExitBreakpointListener(
294: mb);
295: mb
296: .addJPDABreakpointListener(lastMethodExitBreakpointListener);
297: DebuggerManager.getDebuggerManager().addBreakpoint(mb);
298: }
299: }
300: tr.holdLastOperations(true);
301: ExpressionPool.OperationLocation[] nextOperationLocations;
302: if (opIndex < 0) {
303: nextOperationLocations = expr
304: .findNextOperationLocations(codeIndex);
305: } else {
306: Location[] locations = expr.getLocations();
307: nextOperationLocations = new ExpressionPool.OperationLocation[] { new ExpressionPool.OperationLocation(
308: ops[opIndex], locations[opIndex], opIndex) };
309: }
310: boolean isNextOperationFromDifferentExpression = false;
311: if (nextOperationLocations != null) {
312: //Location[] locations = expr.getLocations();
313: /*if (opIndex < 0) {
314: // search for an operation on the next line
315: expr = exprPool.getExpressionAt(locations[locations.length - 1], url);
316: if (expr == null) {
317: logger.log(Level.FINE, "No next operation is available.");
318: return false;
319: }
320: ops = expr.getOperations();
321: opIndex = 0;
322: locations = expr.getLocations();
323: }*/
324: this .operationBreakpoints = new HashSet<BreakpointRequest>();
325: // We need to submit breakpoints on the desired operation and all subsequent ones,
326: // because some might be skipped due to conditional execution.
327: for (int ni = 0; ni < nextOperationLocations.length; ni++) {
328: Location nloc = nextOperationLocations[ni]
329: .getLocation();
330: if (nextOperationLocations[ni].getIndex() < 0) {
331: isNextOperationFromDifferentExpression = true;
332: Operation[] newOps = new Operation[ops.length + 1];
333: System.arraycopy(ops, 0, newOps, 0, ops.length);
334: newOps[ops.length] = nextOperationLocations[ni]
335: .getOperation();
336: ops = newOps;
337: }
338: BreakpointRequest brReq = vm.eventRequestManager()
339: .createBreakpointRequest(nloc);
340: operationBreakpoints.add(brReq);
341: ((JPDADebuggerImpl) debugger).getOperator().register(
342: brReq, this );
343: brReq.setSuspendPolicy(debugger.getSuspend());
344: brReq.addThreadFilter(trRef);
345: brReq.enable();
346: }
347: } else if (lineStepExec) {
348: return false;
349: }
350:
351: // We need to also submit a step request so that we're sure that we end up at least on the next execution line
352: boundaryStepRequest = vm.eventRequestManager()
353: .createStepRequest(tr.getThreadReference(),
354: StepRequest.STEP_LINE, StepRequest.STEP_OVER);
355: if (isNextOperationFromDifferentExpression) {
356: boundaryStepRequest.addCountFilter(2);
357: } else {
358: boundaryStepRequest.addCountFilter(1);
359: }
360: ((JPDADebuggerImpl) debugger).getOperator().register(
361: boundaryStepRequest, this );
362: boundaryStepRequest.setSuspendPolicy(debugger.getSuspend());
363: try {
364: boundaryStepRequest.enable();
365: } catch (IllegalThreadStateException itsex) {
366: // the thread named in the request has died.
367: ((JPDADebuggerImpl) debugger).getOperator().unregister(
368: boundaryStepRequest);
369: boundaryStepRequest = null;
370: return false;
371: }
372:
373: this .currentOperations = ops;
374: return true;
375: }
376:
377: public boolean exec(Event event) {
378: if (stepWatch != null) {
379: stepWatch.done();
380: stepWatch = null;
381: }
382: // TODO: Check the location, follow the smart-stepping logic!
383: SourcePath sourcePath = ((JPDADebuggerImpl) debugger)
384: .getEngineContext();
385: boolean stepAdded = false;
386: boolean[] setStoppedStateNoContinue = new boolean[] { false };
387: JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger;
388: JPDAThreadImpl tr;
389: synchronized (debuggerImpl.LOCK) {
390: tr = (JPDAThreadImpl) debuggerImpl.getCurrentThread();
391: VirtualMachine vm = debuggerImpl.getVirtualMachine();
392: if (vm == null) {
393: return false; // The session has finished
394: }
395: if (lastMethodExitBreakpointListener != null) {
396: Variable returnValue = lastMethodExitBreakpointListener
397: .getReturnValue();
398: lastMethodExitBreakpointListener.destroy();
399: lastMethodExitBreakpointListener = null;
400: lastOperation.setReturnValue(returnValue);
401: }
402: if (lastOperation != null) {
403: tr.addLastOperation(lastOperation);
404: }
405: Operation currentOperation = null;
406: boolean addExprStep = false;
407: if (currentOperations != null) {
408: if (event.request() instanceof BreakpointRequest) {
409: long codeIndex = ((BreakpointRequest) event
410: .request()).location().codeIndex();
411: for (int i = 0; i < currentOperations.length; i++) {
412: if (currentOperations[i].getBytecodeIndex() == codeIndex) {
413: currentOperation = currentOperations[i];
414: break;
415: }
416: }
417: } else {
418: // A line step was finished, the execution of current expression
419: // has finished, we need to check the expression on this line.
420: addExprStep = true;
421: }
422: this .currentOperations = null;
423: }
424: tr.setCurrentOperation(currentOperation);
425: EventRequestManager erm = vm.eventRequestManager();
426: EventRequest eventRequest = event.request();
427: erm.deleteEventRequest(eventRequest);
428: debuggerImpl.getOperator().unregister(eventRequest);
429: if (eventRequest instanceof StepRequest) {
430: SingleThreadedStepWatch
431: .stepRequestDeleted((StepRequest) eventRequest);
432: }
433: removed(eventRequest); // Clean-up
434: int suspendPolicy = debugger.getSuspend();
435: if (addExprStep) {
436: stepAdded = addOperationStep(tr, true, sourcePath,
437: setStoppedStateNoContinue);
438: }
439: if (!stepAdded) {
440: if ((event.request() instanceof StepRequest)
441: && shouldNotStopHere(event)) {
442: return true; // Resume
443: }
444: }
445: }
446: if (stepAdded) {
447: if (setStoppedStateNoContinue[0]) {
448: debuggerImpl.setStoppedStateNoContinue(tr
449: .getThreadReference());
450: }
451: return true; // Resume
452: }
453: firePropertyChange(PROP_STATE_EXEC, null, null);
454: if (!getHidden()) {
455: DebuggerManager.getDebuggerManager().setCurrentSession(
456: session);
457: debuggerImpl.setStoppedState(tr.getThreadReference());
458: }
459: if (getHidden()) {
460: return true; // Resume
461: } else {
462: tr.holdLastOperations(false);
463: return false;
464: }
465: }
466:
467: public void removed(EventRequest eventRequest) {
468: if (stepWatch != null) {
469: stepWatch.done();
470: stepWatch = null;
471: }
472: if (lastMethodExitBreakpointListener != null) {
473: lastMethodExitBreakpointListener.destroy();
474: lastMethodExitBreakpointListener = null;
475: }
476: JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger;
477: VirtualMachine vm = debuggerImpl.getVirtualMachine();
478: if (vm == null) {
479: return; // The session has finished
480: }
481: EventRequestManager erm = vm.eventRequestManager();
482: if (operationBreakpoints != null) {
483: for (Iterator<BreakpointRequest> it = operationBreakpoints
484: .iterator(); it.hasNext();) {
485: BreakpointRequest br = it.next();
486: erm.deleteEventRequest(br);
487: debuggerImpl.getOperator().unregister(br);
488: }
489: this .operationBreakpoints = null;
490: }
491: if (boundaryStepRequest != null) {
492: erm.deleteEventRequest(boundaryStepRequest);
493: SingleThreadedStepWatch
494: .stepRequestDeleted(boundaryStepRequest);
495: debuggerImpl.getOperator().unregister(boundaryStepRequest);
496: }
497:
498: }
499:
500: /**
501: * Returns all class names, which are subclasses of <code>className</code>
502: * and contain method <code>methodName</code>
503: */
504: private static String[] createClassFilters(VirtualMachine vm,
505: String className, String methodName) {
506: return createClassFilters(vm, className, methodName,
507: new ArrayList<String>()).toArray(new String[] {});
508: }
509:
510: private static List<String> createClassFilters(VirtualMachine vm,
511: String className, String methodName, List<String> filters) {
512: List<ReferenceType> classTypes = vm.classesByName(className);
513: for (ReferenceType type : classTypes) {
514: List<Method> methods = type.methodsByName(methodName);
515: boolean hasNonStatic = methods.isEmpty();
516: for (Method method : methods) {
517: if (!filters.contains(type.name())) {
518: filters.add(type.name());
519: }
520: if (!method.isStatic()) {
521: hasNonStatic = true;
522: }
523: }
524: if (hasNonStatic && type instanceof ClassType) {
525: ClassType clazz = (ClassType) type;
526: ClassType super Class = clazz.super class();
527: if (super Class != null) {
528: createClassFilters(vm, super Class.name(),
529: methodName, filters);
530: }
531: }
532: }
533: return filters;
534: }
535:
536: /**
537: * Checks for synthetic methods and smart-stepping...
538: */
539: private boolean shouldNotStopHere(Event ev) {
540: JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger;
541: synchronized (debuggerImpl.LOCK) {
542: // 2) init info about current state
543: LocatableEvent event = (LocatableEvent) ev;
544: String className = event.location().declaringType().name();
545: ThreadReference tr = event.thread();
546: //JPDAThreadImpl ct = (JPDAThreadImpl) debuggerImpl.getCurrentThread();
547:
548: // Synthetic method?
549: try {
550: if (tr.frame(0).location().method().isSynthetic()) {
551: //S ystem.out.println("In synthetic method -> STEP OVER/OUT again");
552:
553: VirtualMachine vm = debuggerImpl
554: .getVirtualMachine();
555: if (vm == null) {
556: return false; // The session has finished
557: }
558: StepRequest stepRequest = vm.eventRequestManager()
559: .createStepRequest(tr,
560: StepRequest.STEP_LINE, getDepth());
561: stepRequest.addCountFilter(1);
562: debuggerImpl.getOperator().register(stepRequest,
563: this );
564: stepRequest.setSuspendPolicy(debugger.getSuspend());
565: try {
566: stepRequest.enable();
567: } catch (IllegalThreadStateException itsex) {
568: // the thread named in the request has died.
569: debuggerImpl.getOperator().unregister(
570: stepRequest);
571: }
572: return true;
573: }
574: } catch (IncompatibleThreadStateException e) {
575: ErrorManager.getDefault().notify(e);
576: }
577:
578: // Not synthetic
579: JPDAThread t = debuggerImpl.getThread(tr);
580: if (debuggerImpl.stopHere(t)) {
581: //S ystem.out.println("/nStepAction.exec end - do not resume");
582: return false; // do not resume
583: }
584:
585: // do not stop here -> start smart stepping!
586: VirtualMachine vm = debuggerImpl.getVirtualMachine();
587: if (vm == null) {
588: return false; // The session has finished
589: }
590: int depth;
591: Map properties = session.lookupFirst(null, Map.class);
592: if (properties != null
593: && properties
594: .containsKey(StepIntoActionProvider.SS_STEP_OUT)) {
595: depth = StepRequest.STEP_OUT;
596: } else {
597: depth = StepRequest.STEP_INTO;
598: }
599: StepRequest stepRequest = vm
600: .eventRequestManager()
601: .createStepRequest(tr, StepRequest.STEP_LINE, depth);
602: if (logger.isLoggable(Level.FINE)) {
603: try {
604: logger.fine("Can not stop at " + tr.frame(0)
605: + ", smart-stepping. Submitting step = "
606: + stepRequest + "; depth = " + depth);
607: } catch (IncompatibleThreadStateException ex) {
608: logger.throwing(getClass().getName(),
609: "shouldNotStopHere", ex);
610: }
611: }
612: String[] exclusionPatterns = debuggerImpl
613: .getSmartSteppingFilter().getExclusionPatterns();
614: for (int i = 0; i < exclusionPatterns.length; i++) {
615: stepRequest
616: .addClassExclusionFilter(exclusionPatterns[i]);
617: logger.finer(" add pattern: " + exclusionPatterns[i]);
618: }
619:
620: debuggerImpl.getOperator().register(stepRequest, this );
621: stepRequest.setSuspendPolicy(debugger.getSuspend());
622: try {
623: stepRequest.enable();
624: } catch (IllegalThreadStateException itsex) {
625: // the thread named in the request has died.
626: debuggerImpl.getOperator().unregister(stepRequest);
627: }
628: return true; // resume
629: }
630: }
631:
632: public static final class MethodExitBreakpointListener implements
633: JPDABreakpointListener {
634:
635: private MethodBreakpoint mb;
636: private Variable returnValue;
637:
638: public MethodExitBreakpointListener(MethodBreakpoint mb) {
639: this .mb = mb;
640: }
641:
642: public void breakpointReached(JPDABreakpointEvent event) {
643: returnValue = event.getVariable();
644: }
645:
646: public Variable getReturnValue() {
647: return returnValue;
648: }
649:
650: public void destroy() {
651: mb.removeJPDABreakpointListener(this );
652: DebuggerManager.getDebuggerManager().removeBreakpoint(mb);
653: }
654:
655: }
656:
657: public static final class SingleThreadedStepWatch implements
658: Runnable {
659:
660: private static final int DELAY = 5000;
661:
662: private static final RequestProcessor stepWatchRP = new RequestProcessor(
663: "Debugger Step Watch", 1);
664:
665: private static final Map<StepRequest, SingleThreadedStepWatch> STEP_WATCH_POOL = new HashMap<StepRequest, SingleThreadedStepWatch>();
666:
667: private RequestProcessor.Task watchTask;
668: private JPDADebuggerImpl debugger;
669: private StepRequest request;
670: private Dialog dialog;
671: private List<JPDAThread> resumedThreads;
672:
673: public SingleThreadedStepWatch(JPDADebuggerImpl debugger,
674: StepRequest request) {
675: this .debugger = debugger;
676: this .request = request;
677: watchTask = stepWatchRP.post(this , DELAY);
678: synchronized (STEP_WATCH_POOL) {
679: STEP_WATCH_POOL.put(request, this );
680: }
681: }
682:
683: public static void stepRequestDeleted(StepRequest request) {
684: SingleThreadedStepWatch stepWatch;
685: synchronized (STEP_WATCH_POOL) {
686: stepWatch = STEP_WATCH_POOL.remove(request);
687: }
688: if (stepWatch != null)
689: stepWatch.done();
690: }
691:
692: public void done() {
693: synchronized (this ) {
694: watchTask.cancel();
695: watchTask = null;
696: if (dialog != null) {
697: dialog.setVisible(false);
698: }
699: if (resumedThreads != null) {
700: synchronized (debugger.LOCK) {
701: suspendThreads(resumedThreads);
702: }
703: resumedThreads = null;
704: }
705: }
706: synchronized (STEP_WATCH_POOL) {
707: STEP_WATCH_POOL.remove(request);
708: }
709: }
710:
711: public void run() {
712: synchronized (this ) {
713: if (watchTask == null)
714: return; // We're done
715: if (request.thread().isSuspended()) {
716: watchTask.schedule(DELAY);
717: return;
718: }
719: if (request.thread().status() == ThreadReference.THREAD_STATUS_ZOMBIE) {
720: // Do not wait for zombie!
721: return;
722: }
723: if (!request.isEnabled()) {
724: return;
725: }
726: Boolean resumeDecision = debugger
727: .getSingleThreadStepResumeDecision();
728: if (resumeDecision != null) {
729: if (resumeDecision.booleanValue()) {
730: doResume();
731: }
732: return;
733: }
734: }
735: String message = NbBundle.getMessage(JPDAStepImpl.class,
736: "SingleThreadedStepBlocked");
737: JCheckBox cb = new JCheckBox(NbBundle.getMessage(
738: JPDAStepImpl.class, "RememberDecision"));
739: final boolean[] yes = new boolean[] { false, false };
740: DialogDescriptor dd = new DialogDescriptor(
741: //message,
742: createDlgPanel(message, cb),
743: new NotifyDescriptor.Confirmation(message,
744: NotifyDescriptor.YES_NO_OPTION).getTitle(),
745: true, NotifyDescriptor.YES_NO_OPTION, null,
746: new ActionListener() {
747: public void actionPerformed(ActionEvent evt) {
748: synchronized (yes) {
749: yes[0] = evt.getSource() == NotifyDescriptor.YES_OPTION;
750: yes[1] = evt.getSource() == NotifyDescriptor.NO_OPTION;
751: }
752: }
753: });
754: dd.setMessageType(NotifyDescriptor.QUESTION_MESSAGE);
755: Dialog theDialog;
756: synchronized (this ) {
757: dialog = org.openide.DialogDisplayer.getDefault()
758: .createDialog(dd);
759: theDialog = dialog;
760: }
761: theDialog.setVisible(true);
762: boolean doResume;
763: synchronized (yes) {
764: doResume = yes[0];
765: }
766: synchronized (this ) {
767: dialog = null;
768: if (watchTask == null)
769: return;
770: if ((yes[0] || yes[1]) && cb.isSelected()) {
771: debugger.setSingleThreadStepResumeDecision(Boolean
772: .valueOf(yes[0]));
773: }
774: if (doResume) {
775: doResume();
776: }
777: }
778: /*
779: Object option = org.openide.DialogDisplayer.getDefault().notify(
780: new NotifyDescriptor.Confirmation(message, NotifyDescriptor.YES_NO_OPTION));
781: if (NotifyDescriptor.YES_OPTION == option) {
782: debugger.resume();
783: }
784: */
785: }
786:
787: private static JPanel createDlgPanel(String message,
788: JCheckBox cb) {
789: JPanel panel = new JPanel();
790: panel.setLayout(new GridBagLayout());
791: GridBagConstraints c = new GridBagConstraints();
792: c.anchor = GridBagConstraints.WEST;
793: JTextArea area = new JTextArea(message);
794: Color color = UIManager.getColor("Label.background"); // NOI18N
795: if (color != null) {
796: area.setBackground(color);
797: }
798: //area.setLineWrap(true);
799: //area.setWrapStyleWord(true);
800: area.setEditable(false);
801: area.setTabSize(4); // looks better for module sys messages than 8
802: panel.add(area, c);
803: c = new GridBagConstraints();
804: c.gridx = 0;
805: c.gridy = 1;
806: c.anchor = GridBagConstraints.WEST;
807: c.insets = new java.awt.Insets(12, 0, 0, 0);
808: panel.add(cb, c);
809: return panel;
810: }
811:
812: private void doResume() {
813: synchronized (debugger.LOCK) {
814: List<JPDAThread> suspendedThreads = new ArrayList<JPDAThread>();
815: JPDAThreadGroup[] tgs = debugger
816: .getTopLevelThreadGroups();
817: for (JPDAThreadGroup tg : tgs) {
818: fillSuspendedThreads(tg, suspendedThreads);
819: }
820: resumeThreads(suspendedThreads);
821: resumedThreads = suspendedThreads;
822: }
823: }
824:
825: private static void fillSuspendedThreads(JPDAThreadGroup tg,
826: List<JPDAThread> sts) {
827: for (JPDAThread t : tg.getThreads()) {
828: if (t.isSuspended())
829: sts.add(t);
830: }
831: for (JPDAThreadGroup tgg : tg.getThreadGroups()) {
832: fillSuspendedThreads(tgg, sts);
833: }
834: }
835:
836: private static void suspendThreads(List<JPDAThread> ts) {
837: for (JPDAThread t : ts) {
838: t.suspend();
839: }
840: }
841:
842: private static void resumeThreads(List<JPDAThread> ts) {
843: for (JPDAThread t : ts) {
844: t.resume();
845: }
846: }
847:
848: }
849:
850: }
|