blob: b77cea22256fb7be84ba297df2ddee6637b1a962 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 IBM Corporation and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
*******************************************************************************/
package org.eclipse.dltk.internal.debug.core.model;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.dltk.compiler.util.Util;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IDLTKLanguageToolkit;
import org.eclipse.dltk.dbgp.IDbgpSession;
import org.eclipse.dltk.dbgp.breakpoints.IDbgpBreakpoint;
import org.eclipse.dltk.dbgp.commands.IDbgpExtendedCommands;
import org.eclipse.dltk.dbgp.exceptions.DbgpException;
import org.eclipse.dltk.dbgp.internal.IDbgpTerminationListener;
import org.eclipse.dltk.debug.core.DLTKDebugLaunchConstants;
import org.eclipse.dltk.debug.core.DLTKDebugPlugin;
import org.eclipse.dltk.debug.core.ExtendedDebugEventDetails;
import org.eclipse.dltk.debug.core.IHotCodeReplaceListener;
import org.eclipse.dltk.debug.core.ISmartStepEvaluator;
import org.eclipse.dltk.debug.core.eval.IScriptEvaluationEngine;
import org.eclipse.dltk.debug.core.model.IScriptDebugTarget;
import org.eclipse.dltk.debug.core.model.IScriptStackFrame;
import org.eclipse.dltk.debug.core.model.IScriptThread;
import org.eclipse.dltk.internal.debug.core.eval.ScriptEvaluationEngine;
import org.eclipse.dltk.internal.debug.core.model.operations.DbgpDebugger;
public class ScriptThread extends ScriptDebugElement
implements IScriptThread, IThreadManagement, IDbgpTerminationListener,
ScriptThreadStateManager.IStateChangeHandler, IHotCodeReplaceListener {
private ScriptThreadStateManager stateManager;
private final IScriptThreadManager manager;
private final ScriptStack stack;
// Session
private final IDbgpSession session;
// State variables
private final IScriptDebugTarget target;
private IScriptEvaluationEngine evalEngine;
private int currentStackLevel;
private boolean terminated = false;
private int propertyPageSize = 32;
// ScriptThreadStateManager.IStateChangeHandler
@Override
public void handleSuspend(int detail) {
DebugEventHelper.fireExtendedEvent(this,
ExtendedDebugEventDetails.BEFORE_SUSPEND);
stack.update(true);
if (handleSmartStepInto()) {
return;
}
DebugEventHelper.fireChangeEvent(this);
DebugEventHelper.fireSuspendEvent(this, detail);
}
private boolean handleSmartStepInto() {
if (stateManager.isStepInto()
&& getScriptDebugTarget().isUseStepFilters()
&& stack.getFrames().length > currentStackLevel) {
stateManager.setStepInto(false);
IScriptDebugTarget target = this.getScriptDebugTarget();
String[] filters = target.getFilters();
IDLTKLanguageToolkit toolkit = this.getScriptDebugTarget()
.getLanguageToolkit();
if (toolkit != null) {
ISmartStepEvaluator evaluator = SmartStepEvaluatorManager
.getEvaluator(toolkit.getNatureId());
if (evaluator != null) {
if (evaluator.isFiltered(filters, this)) {
try {
this.stepReturn();
return true;
} catch (DebugException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
}
}
}
return false;
}
@Override
public void handleResume(int detail) {
DebugEventHelper.fireExtendedEvent(this,
ExtendedDebugEventDetails.BEFORE_RESUME);
DebugEventHelper.fireResumeEvent(this, detail);
DebugEventHelper.fireChangeEvent(this);
}
@Override
public void handleTermination(DbgpException e) {
if (e != null) {
DLTKDebugPlugin.log(e);
IScriptStreamProxy proxy = getScriptDebugTarget().getStreamProxy();
if (proxy != null) {
proxy.writeStderr("\n" + e.getMessage() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
stack.update(false);
IStackFrame[] frames = stack.getFrames();
proxy.writeStderr("\nStack trace:\n"); //$NON-NLS-1$
try {
for (int i = 0; i < frames.length; i++) {
IScriptStackFrame frame = (IScriptStackFrame) frames[i];
String line = "\t#" + frame.getLevel() + " file:" //$NON-NLS-1$ //$NON-NLS-2$
+ frame.getSourceURI().getPath() + " [" //$NON-NLS-1$
+ frame.getLineNumber() + "]\n"; //$NON-NLS-1$
proxy.writeStderr(line);
}
} catch (DebugException e2) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
}
session.requestTermination();
try {
session.waitTerminated();
} catch (InterruptedException ee) {
ee.printStackTrace();
}
manager.terminateThread(this);
}
public ScriptThread(IScriptDebugTarget target, IDbgpSession session,
IScriptThreadManager manager) throws DbgpException, CoreException {
this.target = target;
this.manager = manager;
this.session = session;
this.session.addTerminationListener(this);
this.stateManager = new ScriptThreadStateManager(this);
this.stack = new ScriptStack(this);
}
public void initialize(IProgressMonitor monitor) throws DbgpException {
monitor.beginTask(Util.EMPTY_STRING, 10);
try {
final DbgpDebugger engine = this.stateManager.getEngine();
if (DLTKCore.DEBUG) {
DbgpDebugger.printEngineInfo(engine);
}
engine.setMaxChildren(propertyPageSize);
engine.setMaxDepth(2);
engine.setMaxData(8192);
monitor.worked(2);
manager.configureThread(engine, this);
monitor.worked(6);
final boolean isDebugConsole = DLTKDebugLaunchConstants
.isDebugConsole(target.getLaunch());
if (isDebugConsole && engine
.isFeatureSupported(IDbgpExtendedCommands.STDIN_COMMAND)) {
engine.redirectStdin();
}
engine.setNotifyOk(true);
if (isDebugConsole) {
engine.redirectStdout();
engine.redirectStderr();
}
monitor.worked(2);
HotCodeReplaceManager.getDefault().addHotCodeReplaceListener(this);
} finally {
monitor.done();
}
// final IDbgpExtendedCommands extended = session.getExtendedCommands();
// session.getNotificationManager().addNotificationListener(
// new IDbgpNotificationListener() {
// private final BufferedReader reader = new BufferedReader(
// new InputStreamReader(getStreamProxy().getStdin()));
//
// public void dbgpNotify(IDbgpNotification notification) {
// try {
// extended.sendStdin(reader.readLine() + "\n");
// } catch (IOException e) {
// // TODO: log exception
// e.printStackTrace();
// } catch (DbgpException e) {
// // TODO: log exception
// e.printStackTrace();
// }
// }
// });
}
@Override
public boolean hasStackFrames() {
return isSuspended() && !isTerminated() && stack.hasFrames();
}
boolean isStackInitialized() {
return stack.isInitialized();
}
// IThread
@Override
public IStackFrame[] getStackFrames() throws DebugException {
if (!isSuspended()) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
if (!isSuspended()) {
return ScriptStack.NO_STACK_FRAMES;
}
}
return session.getDebugOptions().filterStackLevels(stack.getFrames());
}
@Override
public int getPriority() throws DebugException {
return 0;
}
@Override
public IStackFrame getTopStackFrame() {
return stack.getTopFrame();
}
@Override
public String getName() {
return session.getInfo().getThreadId();
}
@Override
public IBreakpoint[] getBreakpoints() {
return DebugPlugin.getDefault().getBreakpointManager()
.getBreakpoints(getModelIdentifier());
}
// ISuspendResume
// Suspend
@Override
public int getModificationsCount() {
return stateManager.getModificationsCount();
}
@Override
public boolean isSuspended() {
return stateManager.isSuspended();
}
@Override
public boolean canSuspend() {
return stateManager.canSuspend();
}
@Override
public void suspend() throws DebugException {
stateManager.suspend();
}
// Resume
@Override
public boolean canResume() {
return stateManager.canResume();
}
@Override
public void resume() throws DebugException {
stateManager.resume();
}
public void initialStepInto() {
stateManager.setSuspended(false, DebugEvent.CLIENT_REQUEST);
stateManager.getEngine().initialStepInto();
}
// IStep
@Override
public boolean isStepping() {
return stateManager.isStepping();
}
boolean isStepInto() {
return stateManager.isStepInto();
}
// Step into
@Override
public boolean canStepInto() {
return stateManager.canStepInto();
}
@Override
public void stepInto() throws DebugException {
currentStackLevel = this.stack.getFrames().length;
stateManager.stepInto();
}
// Step over
@Override
public boolean canStepOver() {
return stateManager.canStepOver();
}
@Override
public void stepOver() throws DebugException {
stateManager.stepOver();
}
// Step return
@Override
public boolean canStepReturn() {
return stateManager.canStepReturn();
}
@Override
public void stepReturn() throws DebugException {
stateManager.stepReturn();
}
// ITerminate
@Override
public boolean isTerminated() {
return terminated || stateManager.isTerminated();
}
@Override
public boolean canTerminate() {
return !isTerminated();
}
@Override
public void terminate() throws DebugException {
target.terminate();
}
@Override
public void sendTerminationRequest() throws DebugException {
stateManager.terminate();
}
@Override
public IDbgpSession getDbgpSession() {
return session;
}
@Override
public IDbgpBreakpoint getDbgpBreakpoint(String id) {
try {
return session.getCoreCommands().getBreakpoint(id);
} catch (DbgpException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
return null;
}
@Override
public IScriptStreamProxy getStreamProxy() {
return target.getStreamProxy();
}
@Override
public IDebugTarget getDebugTarget() {
return target.getDebugTarget();
}
@Override
public IScriptEvaluationEngine getEvaluationEngine() {
if (evalEngine == null) {
evalEngine = new ScriptEvaluationEngine(this);
}
return evalEngine;
}
// IDbgpTerminationListener
@Override
public void objectTerminated(Object object, Exception e) {
terminated = true;
Assert.isTrue(object == session);
HotCodeReplaceManager.getDefault().removeHotCodeReplaceListener(this);
manager.terminateThread(this);
}
// Object
@Override
public String toString() {
return "Thread (" + session.getInfo().getThreadId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
public void notifyModified() {
stateManager.notifyModified();
}
@Override
public void hotCodeReplaceFailed(IScriptDebugTarget target,
DebugException exception) {
if (isSuspended()) {
stack.updateFrames();
DebugEventHelper.fireChangeEvent(this);
}
}
@Override
public void hotCodeReplaceSucceeded(IScriptDebugTarget target) {
if (isSuspended()) {
stack.updateFrames();
DebugEventHelper.fireChangeEvent(this);
}
}
@Override
public int getPropertyPageSize() {
return propertyPageSize;
}
@Override
public boolean retrieveGlobalVariables() {
return target.retrieveGlobalVariables();
}
@Override
public boolean retrieveClassVariables() {
return target.retrieveClassVariables();
}
@Override
public boolean retrieveLocalVariables() {
return target.retrieveLocalVariables();
}
@Override
public void updateStackFrames() {
stack.updateFrames();
DebugEventHelper.fireChangeEvent(ScriptThread.this.getDebugTarget());
}
void updateStack() {
stack.update(true);
}
boolean isValidStack() {
return session.getDebugOptions().isValidStack(stack.getFrames());
}
}