blob: d5de4219ef2796626328a6c43810a4cb35bcf61f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.debugger.jpda;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.ClassType;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.Locatable;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.StepEvent;
import com.sun.jdi.request.BreakpointRequest;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.EventRequestManager;
import com.sun.jdi.request.StepRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.debugger.DebuggerManager;
import org.netbeans.api.debugger.Properties;
import org.netbeans.api.debugger.Session;
import org.netbeans.api.debugger.jpda.CallStackFrame;
import org.netbeans.api.debugger.jpda.JPDABreakpoint;
import org.netbeans.api.debugger.jpda.JPDADebugger;
import org.netbeans.api.debugger.jpda.JPDAStep;
import org.netbeans.api.debugger.jpda.JPDAThread;
import org.netbeans.api.debugger.jpda.MethodBreakpoint;
import org.netbeans.api.debugger.jpda.SmartSteppingFilter;
import org.netbeans.api.debugger.jpda.Variable;
import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener;
import org.netbeans.modules.debugger.jpda.actions.CompoundSmartSteppingListener;
import org.netbeans.modules.debugger.jpda.actions.SmartSteppingFilterImpl;
import org.netbeans.modules.debugger.jpda.actions.StepIntoActionProvider;
import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ClassTypeWrapper;
import org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InvalidRequestStateExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.LocatableWrapper;
import org.netbeans.modules.debugger.jpda.jdi.LocationWrapper;
import org.netbeans.modules.debugger.jpda.jdi.MethodWrapper;
import org.netbeans.modules.debugger.jpda.jdi.MirrorWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ReferenceTypeWrapper;
import org.netbeans.modules.debugger.jpda.jdi.StackFrameWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ThreadReferenceWrapper;
import org.netbeans.modules.debugger.jpda.jdi.TypeComponentWrapper;
import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.VirtualMachineWrapper;
import org.netbeans.modules.debugger.jpda.jdi.event.EventWrapper;
import org.netbeans.modules.debugger.jpda.jdi.event.LocatableEventWrapper;
import org.netbeans.modules.debugger.jpda.jdi.request.BreakpointRequestWrapper;
import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestManagerWrapper;
import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestWrapper;
import org.netbeans.modules.debugger.jpda.jdi.request.StepRequestWrapper;
import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
import org.netbeans.modules.debugger.jpda.models.ReturnVariableImpl;
import org.netbeans.modules.debugger.jpda.util.Executor;
import org.netbeans.modules.debugger.jpda.util.Operator;
import org.netbeans.spi.debugger.jpda.EditorContext.Operation;
import org.netbeans.spi.debugger.jpda.SmartSteppingCallback.StopOrStep;
import org.openide.util.Exceptions;
public class JPDAStepImpl extends JPDAStep implements Executor {
private static final Logger logger = Logger.getLogger("org.netbeans.modules.debugger.jpda.step"); // NOI18N
private static final String INIT = "<init>"; // NOI18N
/** The source tree with location info of this step */
//private ASTL stepASTL;
private Operation[] currentOperations;
private Operation lastOperation;
private ExpressionPool.Interval currentExpInterval;
private MethodExitBreakpointListener lastMethodExitBreakpointListener;
private Set<BreakpointRequest> operationBreakpoints;
private StepRequest boundaryStepRequest;
//private SingleThreadedStepWatch stepWatch;
private final Set<EventRequest> requestsToCancel = new HashSet<EventRequest>();
private volatile StepPatternDepth stepPatternDepth;
private boolean ignoreStepFilters = false;
private boolean steppingFromFilteredLocation;
private boolean steppingFromCompoundFilteredLocation;
private boolean isInBoxingUnboxingLocation;
private boolean wasInBoxingUnboxingLocation;
private StopHereCheck stopHereCheck;
private final Properties p;
private final Session session;
public JPDAStepImpl(JPDADebugger debugger, Session session, int size, int depth) {
super(debugger, size, depth);
this.session = session;
p = Properties.getDefault().getProperties("debugger.options.JPDA"); // NOI18N
}
@Override
public void addStep(JPDAThread tr) {
JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger;
JPDAThreadImpl trImpl = (JPDAThreadImpl) tr;
VirtualMachine vm = debuggerImpl.getVirtualMachine();
if (vm == null) {
return ; // The session has finished
}
SourcePath sourcePath = ((JPDADebuggerImpl) debugger).getEngineContext();
boolean[] setStoppedStateNoContinue = new boolean[] { false };
int suspendPolicy = debuggerImpl.getSuspend();
Lock lock;
if (suspendPolicy == JPDADebugger.SUSPEND_EVENT_THREAD) {
lock = trImpl.accessLock.writeLock();
} else {
lock = debuggerImpl.accessLock.writeLock();
}
lock.lock();
try {
((JPDAThreadImpl) tr).waitUntilMethodInvokeDone();
EventRequestManager erm = VirtualMachineWrapper.eventRequestManager(vm);
//Remove all step requests -- TODO: Do we want it?
List<StepRequest> stepRequests = EventRequestManagerWrapper.stepRequests(erm);
try {
EventRequestManagerWrapper.deleteEventRequests(erm, stepRequests);
} catch (InvalidRequestStateExceptionWrapper irex) {
List<StepRequest> assureDeletedAllstepRequests = new ArrayList<StepRequest>(stepRequests);
for (StepRequest sr : assureDeletedAllstepRequests) {
try {
EventRequestManagerWrapper.deleteEventRequest(erm, sr);
} catch (InvalidRequestStateExceptionWrapper ex) {}
}
}
for (StepRequest stepRequest : stepRequests) {
//SingleThreadedStepWatch.stepRequestDeleted(stepRequest);
debuggerImpl.getOperator().unregister(stepRequest);
}
steppingFromFilteredLocation = !((SmartSteppingFilterImpl) debuggerImpl.getSmartSteppingFilter()).stopHere(tr.getClassName());
steppingFromCompoundFilteredLocation = !debuggerImpl.stopHere(tr).isStop();
int size = getSize();
boolean stepAdded = false;
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Step "+((size == JPDAStep.STEP_OPERATION) ? "operation" : "line")
+" "+((getDepth() == JPDAStep.STEP_INTO) ? "into" :
((getDepth() == JPDAStep.STEP_OVER) ? "over" : "out"))
+" in thread "+tr.getName()+", at: "+tr.getClassName()+"."+tr.getMethodName()+":"+tr.getLineNumber(null));
}
if (size == JPDAStep.STEP_OPERATION) {
stepAdded = addOperationStep(trImpl, false, sourcePath,
setStoppedStateNoContinue);
if (!stepAdded) {
size = JPDAStep.STEP_LINE;
logger.log(Level.FINE, "Operation step changed to line step");
}
}
logger.fine(" Operation step added = "+stepAdded);
if (!stepAdded) {
StepRequest stepRequest = EventRequestManagerWrapper.createStepRequest(
VirtualMachineWrapper.eventRequestManager(vm),
trImpl.getThreadReference(),
size,
getDepth()
);
//stepRequest.addCountFilter(1); - works bad with exclusion filters!
String[] exclusionPatterns;
if (ignoreStepFilters || steppingFromFilteredLocation) {
exclusionPatterns = null;
} else {
exclusionPatterns = debuggerImpl.getSmartSteppingFilter().getExclusionPatterns();
for (int i = 0; i < exclusionPatterns.length; i++) {
StepRequestWrapper.addClassExclusionFilter(stepRequest, exclusionPatterns [i]);
logger.finer(" add pattern: "+exclusionPatterns[i]);
}
}
debuggerImpl.getOperator().register(stepRequest, this);
EventRequestWrapper.setSuspendPolicy(stepRequest, debugger.getSuspend());
boolean useStepFilters = p.getBoolean("UseStepFilters", true);
boolean stepThrough = useStepFilters && p.getBoolean("StepThroughFilters", false);
if (!stepThrough && exclusionPatterns != null && exclusionPatterns.length > 0) {
StepPatternDepth spd = new StepPatternDepth();
spd.exclusionPatterns = exclusionPatterns;
spd.stackDepth = tr.getStackDepth();
stepPatternDepth = spd;
} else {
stepPatternDepth = null;
}
logger.fine("Set stepPatternDepth to "+stepPatternDepth);
try {
EventRequestWrapper.enable(stepRequest);
trImpl.setInStep(true, stepRequest);
requestsToCancel.add(stepRequest);
} catch (IllegalThreadStateException itsex) {
// the thread named in the request has died.
debuggerImpl.getOperator().unregister(stepRequest);
stepRequest = null;
} catch (ObjectCollectedExceptionWrapper ex) {
debuggerImpl.getOperator().unregister(stepRequest);
stepRequest = null;
} catch (InvalidRequestStateExceptionWrapper ex) {
debuggerImpl.getOperator().unregister(stepRequest);
stepRequest = null;
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("Step request submitted: "+stepRequest+", size = "+size+", depth = "+getDepth());
}
}
} catch (InternalExceptionWrapper ex) {
} catch (VMDisconnectedExceptionWrapper ex) {
} catch (ObjectCollectedExceptionWrapper ex) {
} finally {
lock.unlock();
}
if (setStoppedStateNoContinue[0]) {
debuggerImpl.setStoppedStateNoContinue(trImpl.getThreadReference());
}
}
private boolean addOperationStep(JPDAThreadImpl tr, boolean lineStepExec,
SourcePath sourcePath,
boolean[] setStoppedStateNoContinue) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper {
ThreadReference trRef = tr.getThreadReference();
StackFrame sf;
try {
sf = ThreadReferenceWrapper.frame(trRef, 0);
} catch (IncompatibleThreadStateException itsex) {
return false;
} catch (IllegalThreadStateExceptionWrapper itsex) {
return false;
} catch (IndexOutOfBoundsException ioobex) {
return false; // No frame exists?
}
Location loc = LocatableWrapper.location(sf);
Session currentSession = DebuggerManager.getDebuggerManager().getCurrentSession();
String language = currentSession == null ? null : currentSession.getCurrentLanguage();
String url = sourcePath.getURL(loc, language);
ExpressionPool exprPool = ((JPDADebuggerImpl) debugger).getExpressionPool();
ExpressionPool.Expression expr = exprPool.getExpressionAt(loc, url);
if (expr == null) {
return false;
}
Operation[] ops = expr.getOperations();
//Operation operation = null;
int opIndex = -1;
int codeIndex = (int) LocationWrapper.codeIndex(loc);
if (codeIndex <= ops[0].getBytecodeIndex()) {
if (!lineStepExec) {
tr.clearLastOperations();
}
// We're at the beginning. Just take the first operation
if (!ops[0].equals(tr.getCurrentOperation())) {
opIndex = expr.findNextOperationIndex(codeIndex - 1);
if (opIndex >= 0 && ops[opIndex].getBytecodeIndex() == codeIndex) {
tr.setCurrentOperation(ops[opIndex]);
if (lineStepExec) {
return false;
}
if (! getHidden()) {
setStoppedStateNoContinue[0] = true;
}
return true;
}
}
}
Operation currentOp = tr.getCurrentOperation();
if (currentOp != null) {
Operation theLastOperation = null;
java.util.List<Operation> lastOperations = tr.getLastOperations();
if (lastOperations != null && lastOperations.size() > 0) {
theLastOperation = lastOperations.get(lastOperations.size() - 1);
}
if (theLastOperation == currentOp) {
// We're right after some operation
// Check, whether there is some other operation directly on this
// position. If yes, it must be executed next.
for (Operation op : ops) {
if (op.getBytecodeIndex() == codeIndex) {
tr.setCurrentOperation(op);
if (! getHidden()) {
setStoppedStateNoContinue[0] = true;
}
return true;
}
}
}
}
this.lastOperation = currentOp;
VirtualMachine vm = MirrorWrapper.virtualMachine(loc);
if (lastOperation != null) {
// Set the method exit breakpoint to get the return value
String methodName = lastOperation.getMethodName();
// We can not get return values from constructors. Do not submit method exit breakpoint.
if (methodName != null && !INIT.equals(methodName) &&
vm.canGetMethodReturnValues()) {
// TODO: Would be nice to know which ObjectReference we're executing the method on
MethodBreakpoint mb = MethodBreakpoint.create(lastOperation.getMethodClassType(), methodName);
mb.setClassFilters(createClassFilters(vm, lastOperation.getMethodClassType(), methodName));
mb.setThreadFilters(debugger, new JPDAThread[] { tr });
//mb.setMethodName(methodName);
mb.setBreakpointType(MethodBreakpoint.TYPE_METHOD_EXIT);
mb.setHidden(true);
mb.setSuspend(JPDABreakpoint.SUSPEND_NONE);
lastMethodExitBreakpointListener = new MethodExitBreakpointListener(mb);
mb.addJPDABreakpointListener(lastMethodExitBreakpointListener);
mb.setSession(debugger);
DebuggerManager.getDebuggerManager().addBreakpoint(mb);
}
}
tr.holdLastOperations(true);
ExpressionPool.OperationLocation[] nextOperationLocations;
if (opIndex < 0) {
nextOperationLocations = expr.findNextOperationLocations(codeIndex);
} else {
Location[] locations = expr.getLocations();
ExpressionPool.OperationLocation[] opLoc = new ExpressionPool.OperationLocation[ops.length - opIndex];
int opLocIndex = 0;
for (int i = opIndex; i < opLoc.length; i++) {
if (i == opIndex || locations[i].codeIndex() > locations[opIndex].codeIndex()) {
opLoc[opLocIndex++] = new ExpressionPool.OperationLocation(ops[i], locations[i], i);
}
}
if (opLocIndex == opLoc.length) {
nextOperationLocations = opLoc;
} else {
nextOperationLocations = new ExpressionPool.OperationLocation[opLocIndex];
System.arraycopy(opLoc, 0, nextOperationLocations, 0, opLocIndex);
}
}
boolean isNextOperationFromDifferentExpression = false;
if (nextOperationLocations != null) {
//Location[] locations = expr.getLocations();
/*if (opIndex < 0) {
// search for an operation on the next line
expr = exprPool.getExpressionAt(locations[locations.length - 1], url);
if (expr == null) {
logger.log(Level.FINE, "No next operation is available.");
return false;
}
ops = expr.getOperations();
opIndex = 0;
locations = expr.getLocations();
}*/
this.operationBreakpoints = new HashSet<BreakpointRequest>();
// We need to submit breakpoints on the desired operation and all subsequent ones,
// because some might be skipped due to conditional execution.
for (int ni = 0; ni < nextOperationLocations.length; ni++) {
Location nloc = nextOperationLocations[ni].getLocation();
if (nextOperationLocations[ni].getIndex() < 0) {
isNextOperationFromDifferentExpression = true;
Operation[] newOps = new Operation[ops.length + 1];
System.arraycopy(ops, 0, newOps, 0, ops.length);
newOps[ops.length] = nextOperationLocations[ni].getOperation();
ops = newOps;
}
BreakpointRequest brReq = EventRequestManagerWrapper.createBreakpointRequest(
VirtualMachineWrapper.eventRequestManager(vm),
nloc);
operationBreakpoints.add(brReq);
((JPDADebuggerImpl) debugger).getOperator().register(brReq, this);
EventRequestWrapper.setSuspendPolicy(brReq, debugger.getSuspend());
BreakpointRequestWrapper.addThreadFilter(brReq, trRef);
EventRequestWrapper.putProperty(brReq, "thread", trRef); // NOI18N
try {
EventRequestWrapper.enable(brReq);
} catch (InvalidRequestStateExceptionWrapper ex) {
Exceptions.printStackTrace(ex);
}
tr.setInStep(true, brReq);
requestsToCancel.add(brReq);
}
} else if (lineStepExec) {
return false;
}
// We need to also submit a step request so that we're sure that we end up at least on the next execution line
/*
//Location lastLocation = nextOperationLocations[nextOperationLocations.length - 1].getOperation().getMethod;
int[] codeIndexIntervals = expr.getCodeIndexIntervals();
int lastCodeIndex = codeIndexIntervals[codeIndexIntervals.length - 1];
Location boundaryLocation = MethodWrapper.locationOfCodeIndex(LocationWrapper.method(loc), lastCodeIndex);
BreakpointRequest brReq = EventRequestManagerWrapper.createBreakpointRequest(
VirtualMachineWrapper.eventRequestManager(vm),
boundaryLocation);
((JPDADebuggerImpl) debugger).getOperator().register(brReq, this);
EventRequestWrapper.setSuspendPolicy(brReq, debugger.getSuspend());
BreakpointRequestWrapper.addThreadFilter(brReq, trRef);
EventRequestWrapper.putProperty(brReq, "thread", trRef); // NOI18N
try {
EventRequestWrapper.enable(brReq);
requestsToCancel.add(brReq);
} catch (InvalidRequestStateExceptionWrapper ex) {
Exceptions.printStackTrace(ex);
((JPDADebuggerImpl) debugger).getOperator().unregister(brReq);
brReq = null;
return false;
} finally {
boundaryBreakpointRequest = brReq;
}
*/
boolean isBSR = setUpBoundaryStepRequest(
VirtualMachineWrapper.eventRequestManager(vm),
trRef,
isNextOperationFromDifferentExpression);
if (!isBSR) {
return false;
}
this.currentOperations = ops;
this.currentExpInterval = expr.getInterval();
return true;
}
private boolean setUpBoundaryStepRequest(EventRequestManager erm,
ThreadReference trRef,
boolean isNextOperationFromDifferentExpression)
throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper {
boundaryStepRequest = EventRequestManagerWrapper.createStepRequest(
erm,
trRef,
StepRequest.STEP_LINE,
StepRequest.STEP_OVER
);
if (isNextOperationFromDifferentExpression) {
EventRequestWrapper.addCountFilter(boundaryStepRequest, 2);
} else {
EventRequestWrapper.addCountFilter(boundaryStepRequest, 1);
}
((JPDADebuggerImpl) debugger).getOperator().register(boundaryStepRequest, this);
EventRequestWrapper.setSuspendPolicy(boundaryStepRequest, debugger.getSuspend());
try {
EventRequestWrapper.enable (boundaryStepRequest);
requestsToCancel.add(boundaryStepRequest);
} catch (IllegalThreadStateException itsex) {
// the thread named in the request has died.
((JPDADebuggerImpl) debugger).getOperator().unregister(boundaryStepRequest);
boundaryStepRequest = null;
return false;
} catch (InvalidRequestStateExceptionWrapper ex) {
Exceptions.printStackTrace(ex);
((JPDADebuggerImpl) debugger).getOperator().unregister(boundaryStepRequest);
boundaryStepRequest = null;
return false;
}
return true;
}
@Override
public boolean exec (final Event event) {
final EventRequest eventRequest;
try {
eventRequest = EventWrapper.request(event);
stepDone(eventRequest);
} catch (InternalExceptionWrapper ex) {
return false;
} catch (VMDisconnectedExceptionWrapper ex) {
return false;
}
if (logger.isLoggable(Level.FINE)) {
logger.fine("JPDAStepImpl.exec("+event+"), is boundaryStepRequest = "+(eventRequest == boundaryStepRequest));
}
// TODO: Check the location, follow the smart-stepping logic!
SourcePath sourcePath = ((JPDADebuggerImpl) debugger).getEngineContext();
boolean stepAdded = false;
boolean[] setStoppedStateNoContinue = new boolean[] { false };
JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl)debugger;
JPDAThreadImpl tr = null;
try {
ThreadReference eventThreadReference = Operator.getEventThread(event);
if (eventThreadReference != null) {
tr = debuggerImpl.getThread(eventThreadReference);
}
} catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper ex) {}
if (tr == null) {
tr = (JPDAThreadImpl)debuggerImpl.getCurrentThread();
}
tr.accessLock.readLock().lock();
try {
wasInBoxingUnboxingLocation = isInBoxingUnboxingLocation;
isInBoxingUnboxingLocation = false;
VirtualMachine vm = debuggerImpl.getVirtualMachine();
if (vm == null) {
return false; // The session has finished
}
if (currentOperations != null) {
if (eventRequest == boundaryStepRequest) {
// A line step was finished, we need to check if the execution
// of current expression has finished or not...
try {
Location loc = LocatableWrapper.location((Locatable) event);
final String language = session == null ? null : session.getCurrentLanguage();
int l = LocationWrapper.lineNumber(loc, language);
if (currentExpInterval.contains(l)) {
// The expression did not finish yet, we're suspended
// somewhere in the middle. Continue...
EventRequestManager erm = VirtualMachineWrapper.eventRequestManager(vm);
try {
EventRequestManagerWrapper.deleteEventRequest(erm, eventRequest);
} catch (InvalidRequestStateExceptionWrapper ex) {}
// silently unregister the old boundary step
EventRequestWrapper.putProperty (eventRequest, "executor", null); // NOI18N
boolean isBSR;
try {
isBSR = setUpBoundaryStepRequest(
erm,
tr.getThreadReference(),
false);
} catch (ObjectCollectedExceptionWrapper ex) {
isBSR = false;
}
// We're in the middle of the expression,
// continue if we manage to submit another boundary step
return isBSR;
}
} catch (InternalExceptionWrapper ex) {
} catch (VMDisconnectedExceptionWrapper ex) {
return false;
}
}
}
Variable returnValue = null;
MethodExitBreakpointListener mebl = lastMethodExitBreakpointListener;
if (mebl != null) {
returnValue = mebl.getReturnValue();
}
try {
EventRequestManager erm = VirtualMachineWrapper.eventRequestManager(vm);
try {
EventRequestManagerWrapper.deleteEventRequest(erm, eventRequest);
} catch (InvalidRequestStateExceptionWrapper ex) {}
debuggerImpl.getOperator().unregister(eventRequest);
/*if (eventRequest instanceof StepRequest) {
SingleThreadedStepWatch.stepRequestDeleted((StepRequest) eventRequest);
}*/
removed(eventRequest); // Clean-up
} catch (InternalExceptionWrapper ex) {
} catch (VMDisconnectedExceptionWrapper ex) {
return false;
}
if (lastMethodExitBreakpointListener != null) {
lastMethodExitBreakpointListener.destroy();
lastMethodExitBreakpointListener = null;
}
if (mebl != null) {
lastOperation.setReturnValue(returnValue);
} else if (vm.canGetMethodReturnValues() &&
lastOperation != null && INIT.equals(lastOperation.getMethodName())) {
// Set Void as a return value of constructor:
lastOperation.setReturnValue(new ReturnVariableImpl((JPDADebuggerImpl) debugger, vm.mirrorOfVoid(), "", INIT));
}
if (lastOperation != null) {
tr.addLastOperation(lastOperation);
}
logger.fine("Have stepPatternDepth : "+stepPatternDepth);
int stepDepthDiff = 0;
if (stepPatternDepth != null) {
StepPatternDepth newStepPatternDepth = null;
try {
int sd = tr.getStackDepth();
logger.fine("Current stack depth = "+sd);
stepDepthDiff = sd - stepPatternDepth.stackDepth;
if (stepDepthDiff > 1) {
// There are some (possibly filtered) stack frames in between.
// StepThroughFilters is false, therefore we should step out if we can not stop here:
boolean haveFilteredClassOnStack = false;
if (!steppingFromFilteredLocation) {
CallStackFrame[] callStack = tr.getCallStack();
int c1 = 1;
int c2 = callStack.length - stepPatternDepth.stackDepth;
for (int i = c1; i < c2; i++) {
// TODO: use debuggerImpl.stopHere(callStack[i])
String className = callStack[i].getClassName();
if (stepPatternDepth.isFiltered(className)) {
haveFilteredClassOnStack = true;
break;
}
}
logger.fine("haveFilteredClassOnStack = "+haveFilteredClassOnStack);
}
if (haveFilteredClassOnStack) {
StepRequest stepRequest = EventRequestManagerWrapper.createStepRequest(
VirtualMachineWrapper.eventRequestManager(vm),
tr.getThreadReference(),
StepRequest.STEP_LINE,
StepRequest.STEP_OUT
);
EventRequestWrapper.addCountFilter(stepRequest, 1);
String[] exclusionPatterns = debuggerImpl.getSmartSteppingFilter().getExclusionPatterns();
// JDI is inconsistent!!! Step into steps *through* filters, but step out does *NOT*
//for (int i = 0; i < exclusionPatterns.length; i++) {
//StepRequestWrapper.addClassExclusionFilter(stepRequest, exclusionPatterns [i]);
//}
if (sd > (stepPatternDepth.stackDepth + 2)) {
// There's still something perhaps filterable in beteen
newStepPatternDepth = new StepPatternDepth();
newStepPatternDepth.exclusionPatterns = exclusionPatterns;
newStepPatternDepth.stackDepth = stepPatternDepth.stackDepth;
}
debuggerImpl.getOperator ().register (stepRequest, this);
EventRequestWrapper.setSuspendPolicy (stepRequest, debugger.getSuspend ());
try {
EventRequestWrapper.enable (stepRequest);
requestsToCancel.add(stepRequest);
} catch (IllegalThreadStateException itsex) {
// the thread named in the request has died.
debuggerImpl.getOperator ().unregister (stepRequest);
} catch (InvalidRequestStateExceptionWrapper irse) {
Exceptions.printStackTrace(irse);
}
return true;
}
}
} catch (AbsentInformationException aiex) {
} catch (InternalExceptionWrapper iex) {
} catch (ObjectCollectedExceptionWrapper ocex) {
} catch (VMDisconnectedExceptionWrapper vmdex) {
return false;
} finally {
stepPatternDepth = newStepPatternDepth;
}
}
Operation currentOperation = null;
boolean addExprStep = false;
if (currentOperations != null) {
try {
if (eventRequest instanceof BreakpointRequest) {
long codeIndex = LocationWrapper.codeIndex(
BreakpointRequestWrapper.location((BreakpointRequest) eventRequest));
for (int i = 0; i < currentOperations.length; i++) {
if (currentOperations[i].getBytecodeIndex() == codeIndex) {
currentOperation = currentOperations[i];
break;
}
}
} else {
// A line step was finished, the execution of current expression
// has finished, we need to check the expression on this line.
// We already know, that currentExpInterval does not contain the line
// Check expressions on this line
addExprStep = true;
}
} catch (InternalExceptionWrapper ex) {
} catch (VMDisconnectedExceptionWrapper ex) {
return false;
}
this.currentOperations = null;
}
logger.fine("Current operation = "+currentOperation+", addExprStep = "+addExprStep);
tr.setCurrentOperation(currentOperation);
try {
//int suspendPolicy = debugger.getSuspend();
if (addExprStep) {
try {
stepAdded = addOperationStep(tr, true, sourcePath,
setStoppedStateNoContinue);
} catch (InternalExceptionWrapper ex) {
return false;
} catch (ObjectCollectedExceptionWrapper ex) {
return false;
}
}
logger.fine("stepAdded = "+stepAdded);
if (!stepAdded) {
if ((eventRequest instanceof StepRequest) && shouldNotStopHere((StepEvent) event, stepDepthDiff)) {
logger.fine("We should not stop here => resuming");
return true; // Resume
}
}
} catch (VMDisconnectedExceptionWrapper ex) {
return false;
}
} finally {
tr.accessLock.readLock().unlock();
}
if (stepAdded) {
if (setStoppedStateNoContinue[0]) {
debuggerImpl.setStoppedStateNoContinue(tr.getThreadReference());
}
return true; // Resume
}
firePropertyChange(PROP_STATE_EXEC, null, null);
if (getHidden()) {
return true; // Resume
} else {
tr.holdLastOperations(false);
return false;
}
}
@Override
public void removed(EventRequest eventRequest) {
try {
stepDone(eventRequest);
} catch (InternalExceptionWrapper ex) {
} catch (VMDisconnectedExceptionWrapper ex) {
return ;
}
if (lastMethodExitBreakpointListener != null) {
lastMethodExitBreakpointListener.destroy();
lastMethodExitBreakpointListener = null;
}
JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl)debugger;
VirtualMachine vm = debuggerImpl.getVirtualMachine();
if (vm == null) {
return ; // The session has finished
}
try {
EventRequestManager erm = VirtualMachineWrapper.eventRequestManager(vm);
if (operationBreakpoints != null) {
for (Iterator<BreakpointRequest> it = operationBreakpoints.iterator(); it.hasNext(); ) {
BreakpointRequest br = it.next();
try {
EventRequestManagerWrapper.deleteEventRequest(erm, br);
} catch (InvalidRequestStateExceptionWrapper ex) {
Exceptions.printStackTrace(ex);
}
debuggerImpl.getOperator().unregister(br);
}
this.operationBreakpoints = null;
}
if (boundaryStepRequest != null) {
try {
EventRequestManagerWrapper.deleteEventRequest(erm, boundaryStepRequest);
} catch (InvalidRequestStateExceptionWrapper ex) {
Exceptions.printStackTrace(ex);
}
//SingleThreadedStepWatch.stepRequestDeleted(boundaryStepRequest);
debuggerImpl.getOperator().unregister(boundaryStepRequest);
}
} catch (VMDisconnectedExceptionWrapper e) {
} catch (InternalExceptionWrapper e) {
}
}
private void stepDone(EventRequest er) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper {
JPDAThreadImpl t;
if (er instanceof StepRequest) {
StepRequest sr = (StepRequest) er;
t = ((JPDADebuggerImpl) debugger).getThread(StepRequestWrapper.thread(sr));
} else {
ThreadReference tr = (ThreadReference) EventRequestWrapper.getProperty(er, "thread"); // NOI18N
if (tr != null) {
t = ((JPDADebuggerImpl) debugger).getThread(tr);
} else {
t = null;
}
}
if (t != null) {
t.setInStep(false, null);
}
}
/**
* Returns all class names, which are subclasses of <code>className</code>
* and contain method <code>methodName</code>
*/
private static String[] createClassFilters(VirtualMachine vm, String className, String methodName) throws VMDisconnectedExceptionWrapper {
return createClassFilters(vm, className, methodName, new ArrayList<String>()).toArray(new String[] {});
}
private static List<String> createClassFilters(VirtualMachine vm, String className, String methodName, List<String> filters) throws VMDisconnectedExceptionWrapper {
List<ReferenceType> classTypes = VirtualMachineWrapper.classesByName0(vm, className);
for (ReferenceType type : classTypes) {
try {
List<Method> methods;
try {
methods = ReferenceTypeWrapper.methodsByName0(type, methodName);
} catch (ClassNotPreparedExceptionWrapper ex) {
continue;
}
boolean hasNonStatic = methods.isEmpty();
for (Method method : methods) {
if (!filters.contains(ReferenceTypeWrapper.name(type))) {
filters.add(ReferenceTypeWrapper.name(type));
}
if (!TypeComponentWrapper.isStatic(method)) {
hasNonStatic = true;
}
}
if (hasNonStatic && type instanceof ClassType) {
ClassType clazz = (ClassType) type;
ClassType superClass;
superClass = ClassTypeWrapper.superclass(clazz);
if (superClass != null) {
createClassFilters(vm, ReferenceTypeWrapper.name(superClass), methodName, filters);
}
}
} catch (InternalExceptionWrapper ex) {
} catch (ObjectCollectedExceptionWrapper ex) {
}
}
return filters;
}
/**
* Checks for synthetic methods and smart-stepping...
*/
private boolean shouldNotStopHere(StepEvent event, int stepDepthDiff) {
JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger;
// 2) init info about current state
boolean useStepFilters = p.getBoolean("UseStepFilters", true);
boolean stepThrough = useStepFilters && p.getBoolean("StepThroughFilters", false);
try {
ThreadReference tr = LocatableEventWrapper.thread (event);
JPDAThreadImpl t = debuggerImpl.getThread (tr);
t.accessLock.readLock().lock();
try {
int doStepSize = (getSize() == JPDAStep.STEP_MIN) ? JPDAStep.STEP_MIN : JPDAStep.STEP_LINE;
try {
if (!ThreadReferenceWrapper.isSuspended(tr)) {
return false; // Already running.
}
// Synthetic method?
Location loc = StackFrameWrapper.location(ThreadReferenceWrapper.frame(tr, 0));
Method m = LocationWrapper.method(loc);
boolean doStepAgain = false;
int doStepDepth = getDepth();
boolean filterSyntheticMethods = useStepFilters && p.getBoolean("FilterSyntheticMethods", true);
boolean filterStaticInitializers = useStepFilters && p.getBoolean("FilterStaticInitializers", false);
boolean filterConstructors = useStepFilters && p.getBoolean("FilterConstructors", false);
int syntheticStep = isSyntheticMethod(m, loc);
if (filterSyntheticMethods && syntheticStep != 0) {
//S ystem.out.println("In synthetic method -> STEP INTO again");
doStepAgain = true;
if (syntheticStep > 0) {
doStepDepth = syntheticStep;
}
}
if (filterStaticInitializers && MethodWrapper.isStaticInitializer(m) ||
filterConstructors && MethodWrapper.isConstructor(m)) {
doStepAgain = true;
doStepDepth = StepRequest.STEP_OUT;
if (logger.isLoggable(Level.FINE)) {
logger.fine("Method"+m+" is a static initializer, or constructor - will step out.");
}
}
if (!doStepAgain) {
if (stepDepthDiff > 1 || wasInBoxingUnboxingLocation) {
// Check if we are in autoboxing or unboxing methods
// called by something that was skipped:
doStepAgain = checkBoxingUnboxingMethods(loc, m);
if (doStepAgain) {
isInBoxingUnboxingLocation = true;
}
}
}
if (useStepFilters && !ignoreStepFilters && !doStepAgain) {
StopOrStep stop;
if (steppingFromCompoundFilteredLocation) {
stop = StopOrStep.stop();
} else {
stop = debuggerImpl.stopHere(t);
}
if (stop.isStop() && !steppingFromFilteredLocation) {
String[] exclusionPatterns = debuggerImpl.getSmartSteppingFilter().getExclusionPatterns();
String className = ReferenceTypeWrapper.name(LocationWrapper.declaringType(loc));
for (String pattern : exclusionPatterns) {
if (match(className, pattern)) {
stop = StopOrStep.skip();
break;
}
}
}
if (!stop.isStop()) {
doStepAgain = true;
int sd = stop.getStepDepth();
if (sd != 0) {
doStepDepth = sd;
} else {
EventRequest request = EventWrapper.request(event);
if (request instanceof StepRequest) {
doStepDepth = ((StepRequest) request).depth();
}
}
int ss = stop.getStepSize();
if (ss != 0) {
doStepSize = ss;
}
}
}
if (stopHereCheck != null) {
doStepAgain = !stopHereCheck.stopHere(!doStepAgain);
}
if (doStepAgain) {
//S ystem.out.println("In synthetic method -> STEP OVER/OUT again");
VirtualMachine vm = debuggerImpl.getVirtualMachine ();
if (vm == null) {
return false; // The session has finished
}
StepRequest stepRequest = EventRequestManagerWrapper.createStepRequest(
VirtualMachineWrapper.eventRequestManager(vm),
tr,
doStepSize,
doStepDepth
);
//EventRequestWrapper.addCountFilter(stepRequest, 1);
String[] exclusionPatterns;
if (ignoreStepFilters || steppingFromFilteredLocation) {
exclusionPatterns = null;
} else {
exclusionPatterns = debuggerImpl.getSmartSteppingFilter().getExclusionPatterns();
if (doStepDepth != StepRequest.STEP_OUT) {
for (int i = 0; i < exclusionPatterns.length; i++) {
StepRequestWrapper.addClassExclusionFilter(stepRequest, exclusionPatterns [i]);
}
}
}
debuggerImpl.getOperator ().register (stepRequest, this);
EventRequestWrapper.setSuspendPolicy (stepRequest, debugger.getSuspend ());
if (!stepThrough && exclusionPatterns != null && exclusionPatterns.length > 0) {
StepPatternDepth spd = new StepPatternDepth();
spd.exclusionPatterns = exclusionPatterns;
spd.stackDepth = t.getStackDepth();
stepPatternDepth = spd;
} else {
stepPatternDepth = null;
}
logger.fine("Set stepPatternDepth to "+stepPatternDepth);
try {
EventRequestWrapper.enable (stepRequest);
requestsToCancel.add(stepRequest);
} catch (IllegalThreadStateException itsex) {
// the thread named in the request has died.
debuggerImpl.getOperator ().unregister (stepRequest);
} catch (InvalidRequestStateExceptionWrapper irse) {
Exceptions.printStackTrace(irse);
}
return true;
}
} catch (IncompatibleThreadStateException | InvalidStackFrameExceptionWrapper e) {
Exceptions.printStackTrace(e);
return false;
} catch (IllegalThreadStateExceptionWrapper | ObjectCollectedExceptionWrapper e) {
return false;
}
// Not synthetic
StopOrStep stop = debuggerImpl.stopHere(t);
if (stop.isStop()) {
//S ystem.out.println("/nStepAction.exec end - do not resume");
return false; // do not resume
}
// do not stop here -> start smart stepping!
VirtualMachine vm = debuggerImpl.getVirtualMachine ();
if (vm == null) {
return false; // The session has finished
}
int depth = stop.getStepDepth();
if (depth == 0) {
Map properties = session.lookupFirst(null, Map.class);
boolean smartSteppingStepOut = properties != null && properties.containsKey (StepIntoActionProvider.SS_STEP_OUT);
if (!stepThrough || smartSteppingStepOut) {
depth = StepRequest.STEP_OUT;
} else {
depth = ((StepRequest) event.request()).depth(); // Use the original depth instead of StepRequest.STEP_INTO, which we do not want when stepping over or out.
}
}
int ss = stop.getStepSize();
if (ss != 0) {
doStepSize = ss;
}
StepRequest stepRequest = EventRequestManagerWrapper.createStepRequest(
VirtualMachineWrapper.eventRequestManager(vm),
tr,
doStepSize,
depth
);
if (logger.isLoggable(Level.FINE)) {
try {
logger.fine("Can not stop at " + ThreadReferenceWrapper.frame(tr, 0) + ", smart-stepping. Submitting step = " + stepRequest + "; depth = " + depth);
} catch (InternalExceptionWrapper ex) {
logger.throwing(getClass().getName(), "shouldNotStopHere", ex);
} catch (VMDisconnectedExceptionWrapper ex) {
logger.throwing(getClass().getName(), "shouldNotStopHere", ex);
} catch (ObjectCollectedExceptionWrapper ex) {
logger.throwing(getClass().getName(), "shouldNotStopHere", ex);
} catch (IllegalThreadStateExceptionWrapper ex) {
logger.throwing(getClass().getName(), "shouldNotStopHere", ex);
} catch (IncompatibleThreadStateException ex) {
logger.throwing(getClass().getName(), "shouldNotStopHere", ex);
}
}
String[] exclusionPatterns;
if (steppingFromFilteredLocation) {
exclusionPatterns = null;
} else {
exclusionPatterns = debuggerImpl.getSmartSteppingFilter().getExclusionPatterns();
for (int i = 0; i < exclusionPatterns.length; i++) {
StepRequestWrapper.addClassExclusionFilter(stepRequest, exclusionPatterns [i]);
logger.finer(" add pattern: "+exclusionPatterns[i]);
}
}
if (!stepThrough && exclusionPatterns != null && exclusionPatterns.length > 0) {
StepPatternDepth spd = new StepPatternDepth();
spd.exclusionPatterns = exclusionPatterns;
spd.stackDepth = t.getStackDepth();
stepPatternDepth = spd;
} else {
stepPatternDepth = null;
}
logger.fine("Set stepPatternDepth to "+stepPatternDepth);
debuggerImpl.getOperator ().register (stepRequest, this);
EventRequestWrapper.setSuspendPolicy (stepRequest, debugger.getSuspend ());
try {
EventRequestWrapper.enable (stepRequest);
requestsToCancel.add(stepRequest);
} catch (IllegalThreadStateException itsex) {
// the thread named in the request has died.
debuggerImpl.getOperator ().unregister (stepRequest);
} catch (ObjectCollectedExceptionWrapper ocex) {
// the thread named in the request was collected.
debuggerImpl.getOperator ().unregister (stepRequest);
} catch (InvalidRequestStateExceptionWrapper irse) {
Exceptions.printStackTrace(irse);
}
} finally {
t.accessLock.readLock().unlock();
}
return true; // resume
} catch (InternalExceptionWrapper e) {
return false;
} catch (VMDisconnectedExceptionWrapper e) {
return false;
}
}
private static boolean match(String name, String pattern) {
if (pattern.startsWith ("*")) {
return name.endsWith (pattern.substring (1));
} else if (pattern.endsWith ("*")) {
return name.startsWith (
pattern.substring (0, pattern.length () - 1)
);
}
return name.equals (pattern);
}
/** Cancel this step - remove all submitted event requests. */
public void cancel() {
JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger;
VirtualMachine vm = debuggerImpl.getVirtualMachine();
if (vm == null) return ;
try {
EventRequestManager erm = VirtualMachineWrapper.eventRequestManager(vm);
for (EventRequest er : requestsToCancel) {
try {
EventRequestManagerWrapper.deleteEventRequest(erm, er);
} catch (InvalidRequestStateExceptionWrapper ex) {}
debuggerImpl.getOperator ().unregister (er);
}
} catch (VMDisconnectedExceptionWrapper e) {
} catch (InternalExceptionWrapper e) {
}
}
/**
* Test whether the method is considered to be synthetic
* @param m The method
* @param loc The current location in that method
* @return 0 when not synthetic
* positive when suggested step depth is returned
* negative when is synthetic and no further step depth is suggested.
*/
public static int isSyntheticMethod(Method m, Location loc) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper {
String name = TypeComponentWrapper.name(m);
if (name.startsWith("lambda$")) { // NOI18N
int lineNumber = LocationWrapper.lineNumber(loc);
if (lineNumber == 1) {
// We're in the initialization of the Lambda. We need to step over it.
return StepRequest.STEP_OVER;
}
return 0; // Do not treat Lambda methods as synthetic, because they contain user code.
} else {
// Do check the class for being Lambda synthetic class:
ReferenceType declaringType = LocationWrapper.declaringType(loc);
try {
String className = ReferenceTypeWrapper.name(declaringType);
if (className.contains("$$Lambda$")) { // NOI18N
// Lambda synthetic class
return -1;
}
} catch (ObjectCollectedExceptionWrapper ex) {
}
}
return TypeComponentWrapper.isSynthetic(m) ? -1 : 0;
}
private static CallStackFrame getTopFrame(JPDAThread thread) {
CallStackFrame topFrame = null;
try {
CallStackFrame[] topFrameArr = thread.getCallStack(0, 1);
if (topFrameArr.length > 0) {
topFrame = topFrameArr[0];
}
} catch (AbsentInformationException aiex) {}
return topFrame;
}
private SmartSteppingFilterImpl smartSteppingFilter;
private SmartSteppingFilterImpl getSmartSteppingFilterImpl () {
if (smartSteppingFilter == null) {
smartSteppingFilter = (SmartSteppingFilterImpl) session.lookupFirst(null, SmartSteppingFilter.class);
}
return smartSteppingFilter;
}
private CompoundSmartSteppingListener compoundSmartSteppingListener;
private CompoundSmartSteppingListener getCompoundSmartSteppingListener () {
if (compoundSmartSteppingListener == null)
compoundSmartSteppingListener = session.lookupFirst(null, CompoundSmartSteppingListener.class);
return compoundSmartSteppingListener;
}
public void setIgnoreStepFilters(boolean ignoreStepFilters) {
this.ignoreStepFilters = ignoreStepFilters;
}
public void setStopHereCheck(StopHereCheck stopHereCheck) {
this.stopHereCheck = stopHereCheck;
}
private boolean checkBoxingUnboxingMethods(Location loc, Method m) {
try {
String typeName = ReferenceTypeWrapper.name(LocationWrapper.declaringType(loc));
switch (typeName) {
case "java.lang.Boolean":
case "java.lang.Byte":
case "java.lang.Character":
case "java.lang.Short":
case "java.lang.Integer":
case "java.lang.Long":
case "java.lang.Float":
case "java.lang.Double":
break;
default:
return false;
}
String methodName = TypeComponentWrapper.name(m);
switch(methodName) {
case "booleanValue":
case "byteValue":
case "charValue":
case "shortValue":
case "intValue":
case "longValue":
case "floatValue":
case "doubleValue":
case "valueOf":
break;
default:
return false;
}
return true;
} catch (InternalExceptionWrapper |
ObjectCollectedExceptionWrapper |
VMDisconnectedExceptionWrapper ex) {
return false;
}
}
public static interface StopHereCheck {
public boolean stopHere(boolean willStop);
}
public static final class MethodExitBreakpointListener implements JPDABreakpointListener {
private final MethodBreakpoint mb;
private Variable returnValue;
public MethodExitBreakpointListener(MethodBreakpoint mb) {
this.mb = mb;
}
@Override
public void breakpointReached(JPDABreakpointEvent event) {
returnValue = event.getVariable();
}
public Variable getReturnValue() {
return returnValue;
}
public void destroy() {
mb.removeJPDABreakpointListener(this);
DebuggerManager.getDebuggerManager().removeBreakpoint(mb);
}
}
private static final class StepPatternDepth {
String[] exclusionPatterns;
int stackDepth;
private boolean isFiltered(String className) {
for (int i = 0; i < exclusionPatterns.length; i++) {
String p = exclusionPatterns[i];
if (p.startsWith("*") && className.endsWith(p.substring(1))) {
return true;
}
if (p.endsWith("*") && className.startsWith(p.substring(0, p.length() - 1))) {
return true;
}
if (className.equals(p)) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "StepPatternDepth: "+Arrays.asList(exclusionPatterns)+", stackDepth = "+stackDepth;
}
}
/*public static final class SingleThreadedStepWatch implements Runnable {
private static final int DELAY = 5000;
private static final RequestProcessor stepWatchRP = new RequestProcessor("Debugger Step Watch", 1);
private static final Map<StepRequest, SingleThreadedStepWatch> STEP_WATCH_POOL = new HashMap<StepRequest, SingleThreadedStepWatch>();
private RequestProcessor.Task watchTask;
private JPDADebuggerImpl debugger;
private StepRequest request;
private Dialog dialog;
private List<JPDAThread> resumedThreads;
public SingleThreadedStepWatch(JPDADebuggerImpl debugger, StepRequest request) {
this.debugger = debugger;
this.request = request;
watchTask = stepWatchRP.post(this, DELAY);
synchronized (STEP_WATCH_POOL) {
STEP_WATCH_POOL.put(request, this);
}
}
public static void stepRequestDeleted(StepRequest request) {
SingleThreadedStepWatch stepWatch;
synchronized (STEP_WATCH_POOL) {
stepWatch = STEP_WATCH_POOL.remove(request);
}
if (stepWatch != null) stepWatch.done();
}
public void done() {
synchronized (this) {
if (watchTask == null) return;
watchTask.cancel();
watchTask = null;
if (dialog != null) {
dialog.setVisible(false);
}
if (resumedThreads != null) {
synchronized (debugger.LOCK) {
suspendThreads(resumedThreads);
}
resumedThreads = null;
}
}
synchronized (STEP_WATCH_POOL) {
STEP_WATCH_POOL.remove(request);
}
}
public void run() {
synchronized (this) {
if (watchTask == null) return ; // We're done
try {
if (request.thread().isSuspended()) {
watchTask.schedule(DELAY);
return ;
}
if (request.thread().status() == ThreadReference.THREAD_STATUS_ZOMBIE) {
// Do not wait for zombie!
return ;
}
} catch (VMDisconnectedException vmdex) {
// Do not wait for finished/disconnected threads
return ;
}
if (!request.isEnabled()) {
return ;
}
Boolean resumeDecision = debugger.getSingleThreadStepResumeDecision();
if (resumeDecision != null) {
if (resumeDecision.booleanValue()) {
doResume();
}
return ;
}
}
String message = NbBundle.getMessage(JPDAStepImpl.class, "SingleThreadedStepBlocked");
JCheckBox cb = new JCheckBox(NbBundle.getMessage(JPDAStepImpl.class, "RememberDecision"));
final boolean[] yes = new boolean[] { false, false };
DialogDescriptor dd = new DialogDescriptor(
//message,
createDlgPanel(message, cb),
new NotifyDescriptor.Confirmation(message, NotifyDescriptor.YES_NO_OPTION).getTitle(),
true,
NotifyDescriptor.YES_NO_OPTION,
null,
new ActionListener() {
public void actionPerformed(ActionEvent evt) {
synchronized (yes) {
yes[0] = evt.getSource() == NotifyDescriptor.YES_OPTION;
yes[1] = evt.getSource() == NotifyDescriptor.NO_OPTION;
}
}
});
dd.setMessageType(NotifyDescriptor.QUESTION_MESSAGE);
Dialog theDialog;
synchronized (this) {
dialog = org.openide.DialogDisplayer.getDefault().createDialog(dd);
theDialog = dialog;
}
theDialog.setVisible(true);
boolean doResume;
synchronized (yes) {
doResume = yes[0];
}
synchronized (this) {
dialog = null;
if (watchTask == null) return ;
if ((yes[0] || yes[1]) && cb.isSelected()) {
debugger.setSingleThreadStepResumeDecision(Boolean.valueOf(yes[0]));
}
if (doResume) {
doResume();
}
}
/*
Object option = org.openide.DialogDisplayer.getDefault().notify(
new NotifyDescriptor.Confirmation(message, NotifyDescriptor.YES_NO_OPTION));
if (NotifyDescriptor.YES_OPTION == option) {
debugger.resume();
}
*/ /*
}
private static JPanel createDlgPanel(String message, JCheckBox cb) {
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.WEST;
JTextArea area = new JTextArea(message);
Color color = UIManager.getColor("Label.background"); // NOI18N
if (color != null) {
area.setBackground(color);
}
//area.setLineWrap(true);
//area.setWrapStyleWord(true);
area.setEditable(false);
area.setTabSize(4); // looks better for module sys messages than 8
panel.add(area, c);
c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 1;
c.anchor = GridBagConstraints.WEST;
c.insets = new java.awt.Insets(12, 0, 0, 0);
panel.add(cb, c);
return panel;
}
private void doResume() {
synchronized (debugger.LOCK) {
List<JPDAThread> suspendedThreads = new ArrayList<JPDAThread>();
JPDAThreadGroup[] tgs = debugger.getTopLevelThreadGroups();
for (JPDAThreadGroup tg: tgs) {
fillSuspendedThreads(tg, suspendedThreads);
}
resumeThreads(suspendedThreads);
resumedThreads = suspendedThreads;
}
}
private static void fillSuspendedThreads(JPDAThreadGroup tg, List<JPDAThread> sts) {
for (JPDAThread t : tg.getThreads()) {
if (t.isSuspended()) sts.add(t);
}
for (JPDAThreadGroup tgg : tg.getThreadGroups()) {
fillSuspendedThreads(tgg, sts);
}
}
private static void suspendThreads(List<JPDAThread> ts) {
for (JPDAThread t : ts) {
t.suspend();
}
}
private static void resumeThreads(List<JPDAThread> ts) {
for (JPDAThread t : ts) {
t.resume();
}
}
}*/
}