blob: 2ed823c4d2bd3b04829788304bd62f83eb5afccb [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.r.debug.core.model;
import static org.eclipse.statet.r.console.core.RWorkspace.RESOLVE_UPTODATE;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
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.IStackFrame;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.NonNull;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.status.ErrorStatus;
import org.eclipse.statet.jcommons.status.ProgressMonitor;
import org.eclipse.statet.jcommons.status.Status;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.jcommons.ts.core.SystemRunnable;
import org.eclipse.statet.jcommons.ts.core.Tool;
import org.eclipse.statet.jcommons.ts.core.ToolService;
import org.eclipse.statet.ecommons.debug.core.eval.IEvaluationListener;
import org.eclipse.statet.internal.r.debug.core.Messages;
import org.eclipse.statet.internal.r.debug.core.RDebugCorePlugin;
import org.eclipse.statet.internal.r.debug.core.eval.ExpressionManager;
import org.eclipse.statet.nico.core.runtime.ToolController.IToolStatusListener;
import org.eclipse.statet.nico.core.runtime.ToolStatus;
import org.eclipse.statet.r.console.core.RProcess;
import org.eclipse.statet.r.console.core.RProcessREnvironment;
import org.eclipse.statet.r.console.core.RWorkspace;
import org.eclipse.statet.r.console.core.util.LoadReferenceRunnable;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.core.model.RElementName;
import org.eclipse.statet.r.core.tool.TmpUtils;
import org.eclipse.statet.r.debug.core.IRStackFrame;
import org.eclipse.statet.r.debug.core.IRThread;
import org.eclipse.statet.r.debug.core.breakpoints.IRBreakpoint;
import org.eclipse.statet.r.debug.core.breakpoints.RBreakpointStatus;
import org.eclipse.statet.r.nico.AbstractRDbgController;
import org.eclipse.statet.rj.data.RDataUtils;
import org.eclipse.statet.rj.data.RLanguage;
import org.eclipse.statet.rj.data.RObject;
import org.eclipse.statet.rj.data.RReference;
import org.eclipse.statet.rj.data.UnexpectedRDataException;
import org.eclipse.statet.rj.data.impl.RLanguageImpl;
import org.eclipse.statet.rj.data.impl.RReferenceImpl;
import org.eclipse.statet.rj.server.dbg.CallStack;
import org.eclipse.statet.rj.server.dbg.DbgRequest;
import org.eclipse.statet.rj.server.dbg.Frame;
import org.eclipse.statet.rj.server.dbg.FrameRef;
import org.eclipse.statet.rj.services.BasicFQRObjectRef;
import org.eclipse.statet.rj.services.FQRObjectRef;
import org.eclipse.statet.rj.services.RService;
import org.eclipse.statet.rj.ts.core.RToolService;
@NonNullByDefault
public class RMainThread extends RDebugElement implements IRThread,
IToolStatusListener {
private static final byte SUSPENDED= 1;
private static final byte RUN_EVALUATING_SYSTEM= 2;
private static final byte RUN_EVALUATING_USER= 3;
private static final byte RUN_STEPPING= 4;
private static final byte RUN_OTHER= 5;
private static final byte TERMINATED= 6;
private static final byte getRunState(final int detail) {
switch (detail) {
case DebugEvent.STEP_INTO:
case DebugEvent.STEP_OVER:
case DebugEvent.STEP_RETURN:
return RUN_STEPPING;
case DebugEvent.EVALUATION:
return RUN_EVALUATING_USER;
case DebugEvent.EVALUATION_IMPLICIT:
return RUN_EVALUATING_SYSTEM;
default:
return RUN_OTHER;
}
}
private static class EnvItem {
final Long handle;
@Nullable Long prevHandle;
@Nullable RProcessREnvironment element;
@Nullable REnvValue value;
public EnvItem(final Long handle) {
this.handle= handle;
this.prevHandle= handle;
}
}
abstract class AccessDataRunnable<V extends RObject> implements SystemRunnable {
private boolean cancel;
private V data;
public AccessDataRunnable() {
}
@Override
public String getTypeId() {
return "r/dbg/stackframe/loadData"; //$NON-NLS-1$
}
@Override
public String getLabel() {
return Messages.DebugContext_UpdateVariables_task;
}
@Override
public boolean canRunIn(final Tool tool) {
return (tool == getTool());
}
@Override
public boolean changed(final int event, final Tool tool) {
switch (event) {
case REMOVING_FROM:
return this.cancel;
case MOVING_FROM:
return false;
case BEING_ABANDONED:
case FINISHING_OK:
case FINISHING_ERROR:
case FINISHING_CANCEL:
synchronized (AccessDataRunnable.this) {
AccessDataRunnable.this.notifyAll();
}
break;
default:
break;
}
return true;
}
@Override
public void run(final ToolService service, final ProgressMonitor m) throws StatusException {
final AbstractRDbgController controller= (AbstractRDbgController) service;
if (!controller.isSuspended() || getRequiredStamp() != controller.getChangeStamp()) {
return;
}
try {
this.data= doRun(controller, m);
}
catch (final UnexpectedRDataException e) {
throw new StatusException(new ErrorStatus(RDebugCorePlugin.BUNDLE_ID,
"Unexpected state", e ));
}
}
protected final RMainThread getThread() {
return RMainThread.this;
}
protected abstract int getRequiredStamp();
protected abstract V doRun(RToolService r,
ProgressMonitor m) throws StatusException, UnexpectedRDataException;
}
private static final @NonNull RStackFrame[] NO_FRAMES= new RStackFrame[0];
private static final @NonNull IRBreakpoint[] NO_BREAKPOINTS= new IRBreakpoint[0];
private final AbstractRDbgController controller;
private final String name;
private final Object stateLock= new Object();
private volatile byte state;
private final Object suspendLock= new Object();
private int stamp;
private @Nullable List<RStackFrame> framesStack;
private @NonNull RStackFrame[] frames;
private boolean stampChanged;
private Map<Long, @Nullable EnvItem> envItems;
private @Nullable Map<Long, @Nullable EnvItem> envPrevItems;
private @Nullable RReference rGlobelEnvRef;
private @Nullable RReference rjTmpEnvRef;
private final ExpressionManager expressionManager;
public RMainThread(final RDebugTarget target, final AbstractRDbgController controller,
final String name) {
super(target);
this.controller= controller;
this.name= name;
this.expressionManager= new ExpressionManager(this);
init();
}
protected void init() {
synchronized (this.suspendLock) {
this.frames= NO_FRAMES;
this.envItems= Collections.EMPTY_MAP;
}
this.controller.addSuspendUpdateRunnable(new SystemRunnable() {
@Override
public String getTypeId() {
return "r/dbg/thread"; //$NON-NLS-1$
}
@Override
public String getLabel() {
return "Main Thread"; //$NON-NLS-1$
}
@Override
public boolean canRunIn(final Tool tool) {
return (tool == RMainThread.this.controller.getTool());
}
@Override
public boolean changed(final int event, final Tool process) {
return true;
}
@Override
public void run(final ToolService service,
final ProgressMonitor m) throws StatusException {
checkInit(m);
aboutToSuspend(RMainThread.this.controller.getSuspendEnterDetail(),
RMainThread.this.controller.getSuspendEnterData(), m );
}
});
this.controller.addToolStatusListener(this);
}
public ExpressionManager getExpressionManager() {
return this.expressionManager;
}
@Override
public void controllerStatusChanged(final ToolStatus oldStatus,
final ToolStatus newStatus, final List<DebugEvent> eventCollection) {
synchronized (this.stateLock) {
if (this.state == TERMINATED) {
return;
}
int detail= 0;
switch (newStatus) {
case STARTED_SUSPENDED:
if (this.stampChanged) {
detail= this.controller.getSuspendEnterDetail();
if (detail == DebugEvent.UNSPECIFIED && this.state == RUN_STEPPING) {
detail= DebugEvent.STEP_END;
}
this.expressionManager.updateExpressions(eventCollection);
}
else {
detail= DebugEvent.EVALUATION_IMPLICIT;
}
this.state= SUSPENDED;
eventCollection.add(new DebugEvent(this, DebugEvent.SUSPEND, detail));
if (this.stampChanged) {
eventCollection.add(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
}
break;
case TERMINATED:
this.expressionManager.cleanExpressions(eventCollection);
break;
case STARTED_PAUSED:
if (this.controller.isSuspended()) {
break; // also pause debugging
}
//$FALL-THROUGH$
case STARTED_IDLING:
if (this.state != RUN_OTHER) {
detail= (this.controller.getSuspendExitDetail() & (DebugEvent.UNSPECIFIED | DebugEvent.CLIENT_REQUEST));
this.state= RUN_OTHER;
eventCollection.add(new DebugEvent(this, DebugEvent.RESUME, detail));
this.expressionManager.clearCache(0, null);
}
break;
default: // PROCESSING
detail= this.controller.getSuspendExitDetail();
final byte newState= getRunState(detail);
if (newState > this.state || (newState == this.state && newState == RUN_STEPPING)) {
this.state= newState;
eventCollection.add(new DebugEvent(this, DebugEvent.RESUME, detail));
this.expressionManager.clearCache(0, null);
}
break;
}
}
}
protected void checkInit(final ProgressMonitor m) {
try {
if (this.rjTmpEnvRef == null) {
{ final RObject rObject= this.controller.evalData(TmpUtils.ENV_FQ_NAME,
null, 0, RService.DEPTH_REFERENCE, m );
this.rjTmpEnvRef= RDataUtils.checkRReference(rObject, RObject.TYPE_ENVIRONMENT);
}
{ final RObject rObject= this.controller.evalData(".GlobalEnv", //$NON-NLS-1$
null, 0, RService.DEPTH_REFERENCE, m );
this.rGlobelEnvRef= RDataUtils.checkRReference(rObject, RObject.TYPE_ENVIRONMENT);
}
}
}
catch (final StatusException | UnexpectedRDataException e) {
RDebugCorePlugin.logError("An error occurred when initializing debug target.", e);
}
}
protected void aboutToSuspend(final int suspendDetail, final Object suspendData,
final ProgressMonitor m) throws StatusException {
final int prevStamp;
final int stamp= this.controller.getChangeStamp();
List<RStackFrame> prevFramesStack;
List<RStackFrame> newFramesStack= null;
RStackFrame[] newFrames= NO_FRAMES;
final Map<Long, EnvItem> newEnvItems= new HashMap<>();
synchronized (this.suspendLock) {
prevStamp= this.stamp;
prevFramesStack= this.framesStack;
}
try {
final CallStack rStack= this.controller.getCallStack(m);
if (rStack != null) {
final int l= rStack.getFrames().size();
if (l > 0) {
final boolean special= DebugPlugin.isUseStepFilters();
final List<RStackFrame> eStack= new ArrayList<>(l);
int startIdx= 0;
for (int i= 0; i < l; i++) {
Frame dbgFrame= rStack.getFrames().get(i);
String call;
Long handle= Long.valueOf(dbgFrame.getHandle());
if (i == 0) {
call= "[Console]";
if (handle == 0) {
handle= Long.valueOf(this.rGlobelEnvRef.getHandle());
}
}
else if (dbgFrame.getCall() != null) {
call= dbgFrame.getCall();
if (special && i + 2 < l) {
int flag= (dbgFrame.getFlags() & 0xff);
switch (flag) {
case CallStack.FLAG_SOURCE:
call= "[Sourcing Script]";
// dbgFrame.addFlags(CallStack.FLAG_NOSTEPPING);
break;
case CallStack.FLAG_COMMAND:
call= "[Running Command]";
break;
default:
flag= 0;
}
if (flag != 0) {
while (i + 1 < l) {
final Frame nextFrame= rStack.getFrames().get(i + 1);
if ((nextFrame.getFlags() & 0xff) != ++flag) {
break;
}
dbgFrame= nextFrame;
i++;
}
if ((flag & 0xf0) == CallStack.FLAG_COMMAND
&& eStack.size() == 1) {
startIdx= 1;
}
}
}
}
else {
call= "[Unkown]";
}
String fileName= dbgFrame.getFileName();
if (fileName != null) {
int idx= fileName.lastIndexOf('/');
{ final int idx2= fileName.lastIndexOf('\\');
if (idx2 > idx) {
idx= idx2;
}
}
if (idx >= 0) {
fileName= fileName.substring(idx + 1);
}
}
final RBreakpointStatus breakpoint= (dbgFrame.isTopFrame()
&& suspendData instanceof RBreakpointStatus) ?
(RBreakpointStatus) suspendData : null;
final EnvItem envItem= new EnvItem(handle);
newEnvItems.put(envItem.handle, envItem);
if (prevFramesStack != null) {
if (eStack.size() < prevFramesStack.size()) {
final RStackFrame prevFrame= prevFramesStack.get(eStack.size());
final EnvItem prevItem= getEnvItem(handle,
prevFrame.getHandle() );
if ((prevItem == null || prevItem.handle.longValue() == prevFrame.getHandle().longValue())
&& prevFrame.update(stamp, dbgFrame, handle, call, fileName, breakpoint)) {
if (prevItem != null) {
envItem.prevHandle= prevItem.handle;
}
eStack.add(prevFrame);
continue;
}
}
if (stamp == prevStamp) {
RDebugCorePlugin.logWarning("Frame stack changed, but controller.changeStamp is unchanged.", null);
}
prevFramesStack= null;
}
{ final RStackFrame newFrame= new RStackFrame(getDebugTarget(), this,
stamp, dbgFrame, handle, call, fileName, breakpoint );
eStack.add(newFrame);
continue;
}
}
{ newFramesStack= eStack;
newFrames= new RStackFrame[eStack.size() - startIdx];
final int endIdx= eStack.size() - 1;
for (int i= 0; i < newFrames.length; i++) {
newFrames[i]= eStack.get(endIdx - i);
}
}
}
}
}
finally {
synchronized (this.suspendLock) {
this.stamp= stamp;
this.framesStack= newFramesStack;
this.frames= newFrames;
this.envPrevItems= this.envItems;
this.envItems= newEnvItems;
this.stampChanged= (stamp != prevStamp);
}
if (this.frames.length > 0) {
this.frames[0].loadContext(this.controller, m);
}
if (this.stampChanged) {
this.expressionManager.clearCache(stamp, m);
}
}
}
private @Nullable EnvItem getEnvItem(final Long first, final Long second) {
synchronized (this.suspendLock) {
EnvItem envItem= this.envItems.get(first);
if (envItem == null && second != first) {
envItem= this.envItems.get(second);
}
return envItem;
}
}
public final RProcess getTool() {
return this.controller.getTool();
}
@Override
public String getName() throws DebugException {
return this.name;
}
@Override
public int getPriority() throws DebugException {
throw newNotSupported();
}
public int getCurrentStamp() {
synchronized (this.suspendLock) {
return this.stamp;
}
}
@Override
public boolean isTerminated() {
return (this.state == TERMINATED);
}
@Override
public boolean canTerminate() {
return false;
}
@Override
public void terminate() throws DebugException {
throw newNotSupported();
}
protected void setTerminated() {
synchronized (this.stateLock) {
this.state= TERMINATED;
}
synchronized (this.suspendLock) {
this.frames= new RStackFrame[0]; // use other instance
}
}
protected void exec(final DbgRequest request) throws DebugException {
try {
this.controller.exec(request);
}
catch (final StatusException e) {
throw new DebugException(new org.eclipse.core.runtime.Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID,
DebugException.REQUEST_FAILED,
"An error occurred when executing debug request in the R engine.",
e ));
}
}
@Override
public boolean isSuspended() {
final byte state= this.state;
return (state >= SUSPENDED && state <= RUN_EVALUATING_USER);
}
@Override
public boolean canSuspend() {
final byte state= this.state;
return (state >= RUN_STEPPING && state <= RUN_OTHER);
}
@Override
public boolean canResume() {
final byte state= this.state;
return (state >= SUSPENDED && state <= RUN_EVALUATING_USER);
}
@Override
public void suspend() throws DebugException {
if (canSuspend()) {
this.controller.debugSuspend();
}
}
@Override
public void resume() throws DebugException {
if (canResume()) {
exec(new DbgRequest.Resume());
}
}
@Override
public boolean isStepping() {
return (this.state == RUN_STEPPING);
}
@Override
public boolean canStepInto() {
final IStackFrame topFrame= getTopStackFrame();
return (topFrame != null && topFrame.canStepInto());
}
@Override
public boolean canStepOver() {
final IStackFrame topFrame= getTopStackFrame();
return (topFrame != null && topFrame.canStepOver());
}
@Override
public boolean canStepReturn() {
final IStackFrame topFrame= getTopStackFrame();
return (topFrame != null && topFrame.canStepReturn());
}
@Override
public void stepInto() throws DebugException {
if (canStepInto()) {
exec(new DbgRequest.StepInto());
}
}
@Override
public void stepOver() throws DebugException {
if (canStepOver()) {
exec(new DbgRequest.StepOver());
}
}
@Override
public void stepReturn() throws DebugException {
if (canStepReturn()) {
stepToFrame(null, 1);
}
}
/**
*
* @param refFrame reference frame, if <code>null</code> current top frame is used
* @param relIdx steps to move (number of index of stack frames, not positions)
* @throws DebugException
*/
public void stepToFrame(final @Nullable RStackFrame refFrame, final int relIdx)
throws DebugException {
ImList<RStackFrame> frames;
synchronized (this.suspendLock) {
frames= ImCollections.newList(this.frames);
}
if (frames.isEmpty()) {
return;
}
final int refIdx= (refFrame != null) ? frames.indexOf(refFrame) : 0;
final int targetIdx= refIdx + relIdx;
if (refIdx < 0 || targetIdx < 0 || targetIdx >= frames.size()) {
return;
}
if (refIdx == 0 && relIdx == 0) {
if (canStepOver()) {
exec(new DbgRequest.StepOver());
}
return;
}
if (canStepReturn()) {
exec(new DbgRequest.StepReturn(
new FrameRef.ByPosition(frames.get(targetIdx).getPosition()) ));
}
}
@Override
public boolean hasStackFrames() throws DebugException {
return isSuspended();
}
@Override
public @NonNull IRStackFrame[] getStackFrames() {
if (!isSuspended()) {
return NO_FRAMES;
}
synchronized (this.suspendLock) {
return this.frames;
}
}
public int checkStackFrame(final IRStackFrame frame) {
synchronized (this.suspendLock) {
for (int i= 0; i < this.frames.length; i++) {
if (this.frames[i] == frame) {
return this.stamp;
}
}
return 0;
}
}
@Override
public @Nullable IRStackFrame getTopStackFrame() {
if (!isSuspended()) {
return null;
}
synchronized (this.suspendLock) {
return (this.frames.length > 0) ? this.frames[0] : null;
}
}
public @Nullable FQRObjectRef createElementRef(@Nullable CombinedRElement element, final int stamp,
final ProgressMonitor m) {
if (stamp != getCurrentStamp()) {
return null;
}
try {
final List<RElementName> segments= new ArrayList<>();
while (element != null) {
if (element.getRObjectType() == RObject.TYPE_ENVIRONMENT) {
final RReference envRef= verifyEnv((RProcessREnvironment) element, m);
Collections.reverse(segments);
final RElementName name= RElementName.create(segments);
return new BasicFQRObjectRef(this.controller.getTool(), envRef,
createRefName(name) );
}
else {
final RElementName elementName= element.getElementName();
if (elementName.getNextSegment() != null) {
if (TmpUtils.isTmp(elementName)) {
segments.add(elementName.getNextSegment());
Collections.reverse(segments);
final RElementName name= RElementName.create(segments);
return new BasicFQRObjectRef(this.controller.getTool(), this.rjTmpEnvRef,
createRefName(name) );
}
break;
}
segments.add(elementName);
element= element.getModelParent();
}
}
throw new IllegalStateException("Unable to create name.");
}
catch (final Exception e) {
RDebugCorePlugin.logError("An error occurred when creating R element name to load data.", e);
return null;
}
}
private RReference verifyEnv(final RProcessREnvironment env,
final ProgressMonitor m) {
final RWorkspace workspace= this.controller.getWorkspaceData();
if (workspace.isUptodate(env)) {
return new RReferenceImpl(env.getHandle(),
RObject.TYPE_ENVIRONMENT, RObject.CLASSNAME_ENVIRONMENT );
}
throw new IllegalStateException("Unable to create name.");
}
private RLanguage createRefName(final RElementName name) {
return new RLanguageImpl((name.getNextSegment() == null) ? RLanguage.NAME : RLanguage.CALL,
name.getDisplayName(RElementName.DISPLAY_EXACT),
null );
}
private EnvItem getEnvItem(final Long handle) {
EnvItem item= this.envItems.get(handle);
if (item == null) {
item= new EnvItem(handle);
this.envItems.put(handle, item);
}
return item;
}
public CombinedRElement resolveReference(final CombinedRElement element, final int stamp) {
final RReference reference= (RReference) element;
final EnvItem envItem;
synchronized (this.suspendLock) {
if (stamp != this.stamp) {
return element;
}
envItem= getEnvItem(reference.getHandle());
}
synchronized (envItem) {
if (envItem.element != null) {
return envItem.element;
}
final RWorkspace workspace= getDebugTarget().getProcess().getWorkspaceData();
if (workspace != null) {
CombinedRElement resolved;
resolved= workspace.resolve(reference, 0);
if (resolved instanceof RProcessREnvironment
&& ((RProcessREnvironment) resolved).getStamp() == stamp) {
envItem.element= (RProcessREnvironment) resolved;
return envItem.element;
}
if (resolved != null) {
return resolved;
}
resolved= loadReference(reference, stamp);
if (resolved instanceof RProcessREnvironment) {
envItem.element= (RProcessREnvironment) resolved;
return envItem.element;
}
}
return element;
}
}
public CombinedRElement resolveReference(final CombinedRElement element, final int stamp,
final ProgressMonitor m) throws StatusException {
final RReference reference= (RReference) element;
final EnvItem envItem;
synchronized (this.suspendLock) {
if (stamp != this.stamp) {
return element;
}
envItem= getEnvItem(reference.getHandle());
}
synchronized (envItem) {
if (envItem.element != null) {
return envItem.element;
}
final RWorkspace workspace= getDebugTarget().getProcess().getWorkspaceData();
if (workspace != null) {
CombinedRElement resolved;
resolved= workspace.resolve((RReference) element,
RESOLVE_UPTODATE, 0, m );
if (resolved instanceof RProcessREnvironment) {
envItem.element= (RProcessREnvironment) resolved;
return envItem.element;
}
}
return element;
}
}
public @Nullable REnvValue getEnvValue(final RProcessREnvironment element, final int stamp) {
final EnvItem envItem;
final Map<Long, EnvItem> prevItems;
synchronized (this.suspendLock) {
if (stamp != this.stamp) {
return null;
}
envItem= getEnvItem(element.getHandle());
prevItems= this.envPrevItems;
}
synchronized (envItem) {
return doGetEnvValue(element, stamp, envItem, prevItems);
}
}
private REnvValue doGetEnvValue(final RProcessREnvironment element, final int stamp,
final EnvItem envItem, final Map<Long, @Nullable EnvItem> prevItems) {
if (envItem.value != null) {
return envItem.value;
}
REnvValue previousValue= null;
if (prevItems != null) {
final EnvItem prevItem= prevItems.get(envItem.prevHandle);
if (prevItem != null) {
previousValue= prevItem.value;
}
}
envItem.value= new REnvValue(element, this, stamp, previousValue);
return envItem.value;
}
public <V extends RObject> @Nullable V loadData(final AccessDataRunnable<V> runnable) {
if (runnable.getRequiredStamp() != getCurrentStamp()) {
return null;
}
final RProcess tool= this.controller.getTool();
synchronized (runnable) {
if (tool.getQueue().addHot(runnable).getSeverity() == Status.OK) {
try {
runnable.wait();
return runnable.data;
}
catch (final InterruptedException e) {
runnable.cancel= true;
getDebugTarget().getProcess().getQueue().removeHot(runnable);
}
}
return null;
}
}
private @Nullable CombinedRElement loadReference(final RReference reference, final int stamp) {
final RProcess tool= this.controller.getTool();
final LoadReferenceRunnable runnable= new LoadReferenceRunnable(reference, tool, stamp,
Messages.DebugContext_label );
synchronized (runnable) {
if (tool.getQueue().addHot(runnable).getSeverity() == Status.OK) {
try {
runnable.wait();
return runnable.getResolvedElement();
}
catch (final InterruptedException e) {
runnable.cancel();
getDebugTarget().getProcess().getQueue().removeHot(runnable);
}
}
return null;
}
}
@Override
public @NonNull IBreakpoint [] getBreakpoints() {
final IBreakpoint breakpoint;
synchronized (this.suspendLock) {
breakpoint= (this.frames.length > 0) ? this.frames[0].getAdapter(IBreakpoint.class) : null;
}
return (breakpoint != null) ?
new @NonNull IBreakpoint[] { breakpoint } :
NO_BREAKPOINTS;
}
@Override
public void evaluate(final String expressionText, final IRStackFrame stackFrame,
final boolean forceReevaluate, final IEvaluationListener listener) {
this.expressionManager.evaluate(expressionText, stackFrame, forceReevaluate, listener);
}
@Override
public <T> @Nullable T getAdapter(final Class<T> type) {
if (type == IRThread.class) {
return (T) this;
}
if (type == IRStackFrame.class) {
return (T) getTopStackFrame();
}
return super.getAdapter(type);
}
}