blob: 5fedf1cf1f8594a676e4b0794fa8863c11487b8c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 Christian Pontesegger and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Christian Pontesegger - initial API and implementation
*******************************************************************************/
package org.eclipse.ease.debugging.model;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
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.ease.debugging.IScriptDebugFrame;
import org.eclipse.ease.debugging.ScriptStackTrace;
import org.eclipse.ease.debugging.dispatcher.EventDispatchJob;
import org.eclipse.ease.debugging.dispatcher.IEventProcessor;
import org.eclipse.ease.debugging.events.IDebugEvent;
import org.eclipse.ease.debugging.events.debugger.ResumedEvent;
import org.eclipse.ease.debugging.events.debugger.StackFramesEvent;
import org.eclipse.ease.debugging.events.debugger.SuspendedEvent;
import org.eclipse.ease.debugging.events.debugger.ThreadTerminatedEvent;
import org.eclipse.ease.debugging.events.model.GetStackFramesRequest;
import org.eclipse.ease.debugging.events.model.ResumeRequest;
import org.eclipse.ease.debugging.events.model.SuspendRequest;
public class EaseDebugThread extends EaseDebugElement implements IThread, IEventProcessor {
private final Object fThread;
private final List<EaseDebugStackFrame> fStackFrames = new ArrayList<>();
public EaseDebugThread(final EaseDebugTarget target, final Object thread) {
super(target);
fThread = thread;
}
@Override
public String getName() {
if (fThread instanceof Thread)
return "Thread: " + ((Thread) fThread).getName();
return "Thread: " + fThread.toString();
}
@Override
public synchronized IStackFrame[] getStackFrames() {
if ((fStackFrames.isEmpty()) && (isSuspended())) {
getDebugTarget().fireDispatchEvent(new GetStackFramesRequest(getThread()));
return new IStackFrame[0];
}
return fStackFrames.toArray(new IStackFrame[fStackFrames.size()]);
}
@Override
public boolean hasStackFrames() {
return getStackFrames().length > 0;
}
@Override
public synchronized EaseDebugStackFrame getTopStackFrame() {
if (hasStackFrames())
return fStackFrames.get(0);
return null;
}
@Override
public int getPriority() throws DebugException {
return 0;
}
@Override
public IBreakpoint[] getBreakpoints() {
return new IBreakpoint[0];
}
public Object getThread() {
return fThread;
}
public synchronized void setStackFrames(final List<IScriptDebugFrame> debugFrames) {
// update stack frames
final List<EaseDebugStackFrame> oldStackFrames = new ArrayList<>(fStackFrames);
fStackFrames.clear();
for (final IScriptDebugFrame debugFrame : debugFrames) {
// find existing StackFrame
EaseDebugStackFrame stackFrame = null;
for (final EaseDebugStackFrame oldStackFrame : oldStackFrames) {
if (debugFrame.equals(oldStackFrame.getDebugFrame())) {
stackFrame = oldStackFrame;
fStackFrames.add(stackFrame);
oldStackFrame.setDirty();
break;
}
}
if (stackFrame == null) {
stackFrame = new EaseDebugStackFrame(this, debugFrame);
fStackFrames.add(stackFrame);
stackFrame.fireCreationEvent();
} else
oldStackFrames.remove(stackFrame);
}
for (final EaseDebugStackFrame stackFrame : oldStackFrames)
stackFrame.fireTerminateEvent();
fireChangeEvent(DebugEvent.CONTENT);
}
@Override
public String toString() {
return getName();
}
// ************************************************************
// IEventProcessor
// ************************************************************
@Override
public synchronized void handleEvent(final IDebugEvent event) {
if (event instanceof SuspendedEvent) {
setStackFrames(filterFrames(((SuspendedEvent) event).getDebugFrames()));
setSuspended();
} else if (event instanceof ResumedEvent) {
setResumed(((ResumedEvent) event).getType());
} else if (event instanceof StackFramesEvent) {
setStackFrames(filterFrames(((StackFramesEvent) event).getDebugFrames()));
// stack frames changed
fireChangeEvent(DebugEvent.CONTENT);
} else if (event instanceof ThreadTerminatedEvent) {
setStackFrames(new ScriptStackTrace());
setState(State.TERMINATED);
fireChangeEvent(DebugEvent.CONTENT);
}
}
@Override
public void setDispatcher(EventDispatchJob dispatcher) {
// noting to do
}
/**
* Remove dynamic code fragments in case they are disabled by the debug target.
*
* @param frames
* frames to be filtered
* @return filtered frames
*/
private List<IScriptDebugFrame> filterFrames(final List<IScriptDebugFrame> frames) {
if (getDebugTarget().isShowDynamicCode())
return frames;
return frames.stream().filter(frame -> (frame.getScript() != null) && (!frame.getScript().isDynamic())).collect(Collectors.toList());
}
// ************************************************************
// ITerminate
// ************************************************************
@Override
public boolean canTerminate() {
if (getState() != State.TERMINATED) {
if (getDebugTarget().getProcess().getThreads().length == 1) {
return getDebugTarget().getProcess().canTerminate();
}
}
return false;
}
@Override
public synchronized void terminate() {
getDebugTarget().getProcess().terminate();
}
@Override
public boolean isTerminated() {
return getState() == State.TERMINATED;
}
// ************************************************************
// ISuspendResume
// ************************************************************
@Override
public boolean canSuspend() {
return (!isTerminated()) && (!isSuspended());
}
@Override
public boolean canResume() {
return (!isTerminated()) && (isSuspended());
}
@Override
public boolean isSuspended() {
return State.SUSPENDED == getState();
}
@Override
public void resume() {
getDebugTarget().fireDispatchEvent(new ResumeRequest(DebugEvent.CLIENT_REQUEST, getThread()));
}
@Override
public void suspend() {
getDebugTarget().fireDispatchEvent(new SuspendRequest(getThread()));
}
// ************************************************************
// IStep
// ************************************************************
@Override
public boolean canStepInto() {
return isSuspended();
}
@Override
public boolean canStepOver() {
return isSuspended();
}
@Override
public boolean canStepReturn() {
return isSuspended();
}
@Override
public boolean isStepping() {
return State.STEPPING == getState();
}
@Override
public void stepInto() {
getDebugTarget().fireDispatchEvent(new ResumeRequest(DebugEvent.STEP_INTO, getThread()));
}
@Override
public void stepOver() {
getDebugTarget().fireDispatchEvent(new ResumeRequest(DebugEvent.STEP_OVER, getThread()));
}
@Override
public void stepReturn() {
getDebugTarget().fireDispatchEvent(new ResumeRequest(DebugEvent.STEP_RETURN, getThread()));
}
}