/**
*
*/
package org.keplerproject.ldt.debug.core.model;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.debug.core.model.IVariable;
/**
* @author jasonsantos Heavily based in source code from IBM Corporation and
* Bjorn Freeman-Benson made available under the EPL software license
*/
public class LuaDebugThread extends LuaDebugElement implements IThread,
ILuaEventListener {
/**
* Breakpoint this thread is suspended at or <code>null</code> if none.
*/
private IBreakpoint fBreakpoint;
/**
* Whether this thread is stepping
*/
private boolean fStepping = false;
/**
* Whether this thread is suspended
*/
private boolean fSuspended = false;
/**
* Most recent error event or <code>null</code>
*/
private String fErrorEvent;
/**
* Stack data obtained during a stack request event
*/
private String fStackData;
private IStackFrame[] fStackFrames;
/**
* Table mapping stack frames to current variables
*/
private final Map<IStackFrame, IVariable[]> fVariables = new HashMap<IStackFrame, IVariable[]>();
private String fFramesData;
public LuaDebugThread(LuaDebugTarget target) {
super(target);
getLuaDebugTarget().addEventListener(this);
}
/**
* Returns the current variables for the given stack frame, or
* <code>null</code> if none.
*
* @param frame
* stack frame
* @return variables or <code>null</code>
*/
protected IVariable[] getVariables(IStackFrame frame) {
synchronized (fVariables) {
IVariable[] variables = fVariables.get(frame);
if (variables == null) {
return new IVariable[0];
}
return variables;
}
}
/**
* Sets the current variables for the given stack frame. Called by the Lua
* stack frame when it is created.
*
* @param frame
* @param variables
*/
protected void setVariables(IStackFrame frame, IVariable[] variables) {
synchronized (fVariables) {
fVariables.put(frame, variables);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IThread#getBreakpoints()
*/
public IBreakpoint[] getBreakpoints() {
if (fBreakpoint == null) {
return new IBreakpoint[0];
}
return new IBreakpoint[] { fBreakpoint };
}
/**
* Notifies this thread it has been suspended by the given breakpoint.
*
* @param breakpoint
* breakpoint
*/
public void suspendedBy(IBreakpoint breakpoint) {
fBreakpoint = breakpoint;
suspended(DebugEvent.BREAKPOINT);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IThread#getName()
*/
public String getName() {
return "Lua Main Thread";
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IThread#getPriority()
*/
public int getPriority() {
return 0; // irrelevant
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IThread#getStackFrames()
*/
public IStackFrame[] getStackFrames() throws DebugException {
if (fStackFrames == null)
return new IStackFrame[0];
return fStackFrames;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IThread#getTopStackFrame()
*/
public IStackFrame getTopStackFrame() throws DebugException {
IStackFrame[] frames = getStackFrames();
if (frames.length > 0) {
return frames[0];
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IThread#hasStackFrames()
*/
public boolean hasStackFrames() throws DebugException {
return isSuspended();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@Override
public Object getAdapter(Class adapter) {
if (adapter == IThread.class) {
return this;
}
return super.getAdapter(adapter);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.ISuspendResume#canResume()
*/
public boolean canResume() {
return isSuspended();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
*/
public boolean canSuspend() {
return !isSuspended();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
*/
public boolean isSuspended() {
return fSuspended && !isTerminated();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.ISuspendResume#resume()
*/
public void resume() throws DebugException {
try {
System.out.println(sendRequest("RUN"));
} catch (Exception e) {
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.ISuspendResume#suspend()
*/
public void suspend() throws DebugException {
try {
System.out.println(sendRequest("STEP"));
} catch (IOException e) {
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IStep#canStepInto()
*/
public boolean canStepInto() {
return isSuspended();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IStep#canStepOver()
*/
public boolean canStepOver() {
return isSuspended();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IStep#canStepReturn()
*/
public boolean canStepReturn() {
return true;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IStep#isStepping()
*/
public boolean isStepping() {
return fStepping;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IStep#stepInto()
*/
public void stepInto() throws DebugException {
try {
System.out.println(sendRequest("STEP"));
} catch (IOException e) {
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IStep#stepOver()
*/
public void stepOver() throws DebugException {
try {
System.out.println(sendRequest("OVER"));
} catch (IOException e) {
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.IStep#stepReturn()
*/
public void stepReturn() throws DebugException {
try {
System.out.println(sendRequest("OUT"));
} catch (IOException e) {
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.ITerminate#canTerminate()
*/
public boolean canTerminate() {
return !isTerminated();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.ITerminate#isTerminated()
*/
public boolean isTerminated() {
return getDebugTarget().isTerminated();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.debug.core.model.ITerminate#terminate()
*/
public void terminate() throws DebugException {
if (getDebugTarget().canTerminate()) {
try {
System.out.println(sendRequest("EXIT"));
} catch (IOException e) {
}
getDebugTarget().terminate();
}
}
/*
* (non-Javadoc)
*
* @see org.keplerproject.ldt.debug.core.model.ILuaEventListener#handleEvent(java.lang.String)
*/
public void handleEvent(String event) {
// clear previous state
fBreakpoint = null;
setStepping(false);
// Running event
if (event.startsWith("210")) {
setSuspended(false);
setStepping(false);
resumed(DebugEvent.RESUME);
// Stepping event
} else if (event.startsWith("211")) {
setSuspended(false);
setStepping(true);
resumed(DebugEvent.STEP_OVER);
// Break event
} else if (event.startsWith("202")) {
setSuspended(true);
setStepping(false);
try {
String framesData = sendRequest("STACK");
fStackFrames = parseFramesData(framesData);
} catch (Exception e) {
}
suspended(DebugEvent.STEP_END);
} else if (event.equals("started")) {
fireCreationEvent();
} else if (event.matches("^4\\d\\d.*$")) {
setError(event);
}
}
/**
* @param event
* @return
*/
public IStackFrame[] parseFramesData(String event) {
if (event != null && event.startsWith("101")) {
String framesData = event.substring("101 Stack ".length());
String[] frames = framesData.split("#");
IStackFrame[] theFrames = new IStackFrame[frames.length];
for (int i = 0; i < frames.length; i++) {
theFrames[i] = new LuaStackFrame(this, frames[i], i);
}
return theFrames;
} else
return new IStackFrame[0];
}
/**
* Sets whether this thread is stepping
*
* @param stepping
* whether stepping
*/
private void setStepping(boolean stepping) {
fStepping = stepping;
}
/**
* Sets whether this thread is suspended
*
* @param suspended
* whether suspended
*/
private void setSuspended(boolean suspended) {
fSuspended = suspended;
}
/**
* Sets the most recent error event encountered, or <code>null</code> to
* clear the most recent error
*
* @param event
* one of 'unimpinstr' or 'nosuchlabel' or <code>null</code>
*/
private void setError(String event) {
fErrorEvent = event;
}
/**
* Returns the most revent error event encountered since the last suspend,
* or <code>null</code> if none.
*
* @return the most revent error event encountered since the last suspend,
* or <code>null</code> if none
*/
public Object getError() {
return fErrorEvent;
}
/**
* Notification the target has resumed for the given reason. Clears any
* error condition that was last encountered and fires a resume event, and
* clears all cached variables for stack frames.
*
* @param detail
* reason for the resume
*/
private void resumed(int detail) {
setError(null);
synchronized (fVariables) {
fVariables.clear();
}
fireResumeEvent(detail);
}
/**
* Notification the target has suspended for the given reason
*
* @param detail
* reason for the suspend
*/
private void suspended(int detail) {
fireSuspendEvent(detail);
}
/**
* Notification an error was encountered. Fires a breakpoint suspend event.
*/
private void exceptionHit() {
suspended(DebugEvent.BREAKPOINT);
}
}