blob: 493b7911c2adb2fe7b5a7d90380fbacfed4ac003 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2016, 2021 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.eval;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugException;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.lang.ObjectUtils;
import org.eclipse.statet.jcommons.lang.ObjectUtils.ToStringBuilder;
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.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.model.RElementVariable;
import org.eclipse.statet.internal.r.debug.core.model.RMainThread;
import org.eclipse.statet.r.console.core.RWorkspace;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.core.model.RElementName;
import org.eclipse.statet.r.core.tool.AbstractStatetRRunnable;
import org.eclipse.statet.r.core.tool.TmpUtils;
import org.eclipse.statet.r.nico.AbstractRDbgController;
import org.eclipse.statet.rj.data.RDataUtils;
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.RReferenceImpl;
import org.eclipse.statet.rj.services.RService;
import org.eclipse.statet.rj.ts.core.RToolService;
@NonNullByDefault
public class EvalExpressionRunnable extends AbstractStatetRRunnable implements SystemRunnable {
private final REvalExpressionTask task;
private final int stamp;
private final IEvaluationListener listener;
private int state;
public EvalExpressionRunnable(final REvalExpressionTask task, final int stamp,
final IEvaluationListener listener) {
super("r/dbg/watch", Messages.Expression_Evaluate_task);
this.task= task;
this.stamp= stamp;
this.listener= listener;
}
@Override
public boolean canRunIn(final Tool tool) {
return (tool == this.task.getThread().getTool());
}
@Override
public boolean changed(final int event, final Tool tool) {
switch (event) {
case REMOVING_FROM:
case MOVING_FROM:
return false;
case BEING_ABANDONED:
case FINISHING_OK:
case FINISHING_ERROR:
case FINISHING_CANCEL:
synchronized (this) {
if (this.state == 0) {
this.state= event;
this.listener.evaluationFinished(new REvaluationResult(
this.task.getRExpression(), this.task.getThread()) );
}
}
break;
default:
break;
}
return true;
}
@Override
protected void run(final RToolService service, final ProgressMonitor m) throws StatusException {
try {
final AbstractRDbgController r= (AbstractRDbgController) service;
if (this.stamp != r.getChangeStamp()) {
return;
}
final RMainThread thread= this.task.getThread();
final @Nullable REvaluationResult result= thread.getExpressionManager()
.getEvalResult(this.task.getKey());
if (result != null) {
this.state= FINISHING_OK;
this.listener.evaluationFinished(result);
return;
}
final TmpUtils.Item tmpItem= TmpUtils.newItem("dbg_watch", r, m);
try {
evalExpression(tmpItem, r, m);
}
finally {
if (this.state != FINISHING_OK) {
tmpItem.disposeChecked(m);
}
}
}
catch (final CoreException | UnexpectedRDataException e) {
this.state= FINISHING_ERROR;
final ToStringBuilder sb= new ObjectUtils.ToStringBuilder(
"An error occurred when evaluating the watch expression." );
sb.addProp("expression", this.task.getRExpression()); //$NON-NLS-1$
sb.addProp("frame", this.task.getStackFrame()); //$NON-NLS-1$
RDebugCorePlugin.logError(sb.toString(), e);
}
}
private void evalExpression(final TmpUtils.Item tmpItem, final AbstractRDbgController r,
final ProgressMonitor m) throws UnexpectedRDataException, StatusException, DebugException {
final RMainThread thread= this.task.getThread();
final String valueName= tmpItem.createSub("value"); //$NON-NLS-1$
final RElementName valueElementName= RElementName.create(RElementName.MAIN_DEFAULT,
valueName );
final RElementName fqElementName= TmpUtils.createFQElementName(valueElementName);
// System.out.println(this.task.getRExpression() + " in " + this.task.getStackFrame());
CombinedRElement element;
try {
element= r.evalCombinedStruct(this.task.getRExpression(),
new RReferenceImpl(this.task.getStackFrame().getHandle(),
RObject.TYPE_ENVIRONMENT, RObject.CLASSNAME_ENVIRONMENT ),
0, RService.DEPTH_REFERENCE, fqElementName, m );
}
catch (final StatusException e) {
synchronized (this) {
this.state= FINISHING_ERROR;
final Status status= e.getStatus();
if (status.getSeverity() == Status.CANCEL) {
evalCompleted(new REvaluationResult(this.task.getRExpression(), thread,
Status.CANCEL, Messages.Expression_Evaluate_Cancelled_message ));
}
else {
evalCompleted(new REvaluationResult(this.task.getRExpression(), thread,
Status.ERROR, status.getMessage() ));
}
return;
}
}
final RReference ref= RDataUtils.checkRReference(element);
tmpItem.set(valueName, ref, m);
if (ref.getReferencedRObjectType() == RObject.TYPE_ENVIRONMENT) {
thread.resolveReference((CombinedRElement) ref, this.stamp, m);
}
else {
element= r.getWorkspaceData().resolve(ref, RWorkspace.RESOLVE_UPTODATE, 0, m);
if (element == null) {
element= r.findCombinedStruct(valueElementName, TmpUtils.ENV_FQ_ELEMENT_NAME, false,
0, RService.DEPTH_INFINITE, m );
if (element == null) {
throw new UnexpectedRDataException("null"); //$NON-NLS-1$
}
}
}
final RElementVariable variable= new RElementVariable(element, thread, this.stamp, null);
variable.getValue(m);
this.state= FINISHING_OK;
evalCompleted(new REvaluationResult(this.task.getRExpression(), thread,
variable, tmpItem ));
}
private void evalCompleted(final REvaluationResult evalResult) {
this.task.getThread().getExpressionManager().setEvalResult(this.task.getKey(), evalResult);
this.listener.evaluationFinished(evalResult);
}
}