package com.redhat.ceylon.eclipse.core.debug.model;
import java.util.List;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
import org.eclipse.jdt.internal.debug.core.model.PatchedForCeylonJDIThread;
import com.redhat.ceylon.eclipse.core.debug.DebugUtils;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.request.StepRequest;
public class CeylonJDIThread extends PatchedForCeylonJDIThread {
public CeylonJDIThread(CeylonJDIDebugTarget debugTarget, ThreadReference reference) {
super(debugTarget, reference);
}
private IJavaStackFrame[] startStackFrame = new IJavaStackFrame[1];
@Override
protected synchronized List<IJavaStackFrame> computeStackFrames(
boolean refreshChildren) throws DebugException {
List<IJavaStackFrame> stackFrames = super.computeStackFrames(refreshChildren);
if (refreshChildren) {
synchronized (startStackFrame) {
try {
CeylonJDIDebugTarget target = getDebugTarget();
if (target.isMainThread(this)) {
for (int i=0; i<stackFrames.size(); i++) {
JDIStackFrame frame = (JDIStackFrame)stackFrames.get(i);
Location location = frame.getUnderlyingMethod().location();
String locationString = new StringBuilder()
.append(location.declaringType().name())
.append('/')
.append(location.method().name())
.toString();
if (locationString.equals(target.getStartLocation())) {
startStackFrame[0] = frame;
break;
}
}
}
} catch(Throwable t) {
t.printStackTrace();
startStackFrame[0] = null;
}
}
}
return stackFrames;
}
@Override
public CeylonJDIDebugTarget getDebugTarget() {
return (CeylonJDIDebugTarget) super.getDebugTarget();
}
public boolean isBeforeStart(IJavaStackFrame aFrame) {
CeylonJDIDebugTarget target = getDebugTarget();
if (target.isMainThread(this)) {
try {
List<IJavaStackFrame> stackFrames = computeStackFrames();
synchronized (startStackFrame) {
if (startStackFrame[0] != null) {
for (int i=stackFrames.size() - 1; i >= 0; i--) {
if (stackFrames.get(i).equals(startStackFrame[0])) {
return false;
}
if (stackFrames.get(i).equals(aFrame)) {
return true;
}
}
return true;
}
}
} catch (DebugException e) {
e.printStackTrace();
}
}
return false;
}
@Override
public synchronized IStackFrame getTopStackFrame() throws DebugException {
IStackFrame topStackFrame = super.getTopStackFrame();
return topStackFrame;
}
boolean steppingThroughLocation = false;
boolean originalLocationIsAnAncestor = false;
private int changeSecondaryStepRequestKindForCeylon(int currentStepKind) throws DebugException {
if (steppingThroughLocation) {
steppingThroughLocation = false;
return StepRequest.STEP_INTO;
} else {
return currentStepKind;
}
}
@Override
protected StepIntoHandler newStepIntoHandler() {
return new StepIntoHandler() {
@Override
protected boolean locationIsFiltered(Method method) {
return DebugUtils.isMethodFiltered(method);
}
@SuppressWarnings("unused")
protected boolean locationIsFiltered(Method method, boolean orig) {
return DebugUtils.isMethodFiltered(method);
}
@Override
protected boolean locationShouldBeFiltered(Location location)
throws DebugException {
storeAdditionalLocationData(location);
return super.locationShouldBeFiltered(location);
}
@Override
protected void createSecondaryStepRequest() throws DebugException {
createSecondaryStepRequest(changeSecondaryStepRequestKindForCeylon(getStepKind()));
}
};
}
@Override
protected StepOverHandler newStepOverHandler() {
return new StepOverHandler() {
@Override
protected boolean locationIsFiltered(Method method) {
return DebugUtils.isMethodFiltered(method);
}
@SuppressWarnings("unused")
protected boolean locationIsFiltered(Method method, boolean orig) {
return DebugUtils.isMethodFiltered(method);
}
@Override
protected boolean locationShouldBeFiltered(Location location)
throws DebugException {
storeAdditionalLocationData(location);
return super.locationShouldBeFiltered(location);
}
@Override
protected void createSecondaryStepRequest() throws DebugException {
createSecondaryStepRequest(changeSecondaryStepRequestKindForCeylon(getStepKind()));
}
};
}
@Override
protected StepReturnHandler newStepReturnHandler() {
return new StepReturnHandler() {
@Override
protected boolean locationIsFiltered(Method method) {
return DebugUtils.isMethodFiltered(method);
}
@SuppressWarnings("unused")
protected boolean locationIsFiltered(Method method, boolean orig) {
return DebugUtils.isMethodFiltered(method);
}
@Override
protected boolean locationShouldBeFiltered(Location location)
throws DebugException {
storeAdditionalLocationData(location);
return super.locationShouldBeFiltered(location);
}
@Override
protected void createSecondaryStepRequest() throws DebugException {
createSecondaryStepRequest(changeSecondaryStepRequestKindForCeylon(getStepKind()));
}
};
}
protected boolean shouldDoStepReturn() throws DebugException {
boolean shouldDoStepReturn = super.shouldDoStepReturn();
if (shouldDoStepReturn) {
try {
StackFrame previousFrame = getUnderlyingThread().frame(1);
if (DebugUtils.isMethodToStepThrough(previousFrame.location().method())) {
return false;
}
} catch (IncompatibleThreadStateException e) {
e.printStackTrace();
}
return true;
}
return false;
}
@Override
protected boolean shouldDoExtraStepInto(Location location)
throws DebugException {
return super.shouldDoExtraStepInto(location) && originalLocationIsAnAncestor;
}
public void storeAdditionalLocationData(Location location)
throws DebugException {
steppingThroughLocation = DebugUtils.isCeylonGeneratedMethodToStepThrough(location.method());
if (getUnderlyingFrameCount() < getOriginalStepStackDepth()) {
originalLocationIsAnAncestor = false;
}
}
@Override
protected void setOriginalStepLocation(Location location) {
originalLocationIsAnAncestor = true;
super.setOriginalStepLocation(location);
}
}