blob: 53ee292190bdc515eea4ba958bf6c89eb004be6c [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2016, 2018 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.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugException;
import org.eclipse.statet.jcommons.collections.ImCollections;
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.lang.ObjectUtils;
import org.eclipse.statet.jcommons.lang.ObjectUtils.ToStringBuilder;
import org.eclipse.statet.ecommons.debug.core.eval.IEvaluationListener;
import org.eclipse.statet.ecommons.ts.core.SystemRunnable;
import org.eclipse.statet.ecommons.ts.core.Tool;
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.RJTmp;
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.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.eclient.core.RToolService;
import org.eclipse.statet.rj.services.FunctionCall;
import org.eclipse.statet.rj.services.RService;
@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 IProgressMonitor monitor) throws CoreException {
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 @NonNull String id;
{ final FunctionCall call= r.createFunctionCall(RJTmp.CREATE_ID);
call.addChar(RJTmp.PREFIX_PAR, "dbg_watch"); //$NON-NLS-1$
id= RDataUtils.checkSingleCharValue(call.evalData(monitor));
}
try {
evalExpression(id, r, monitor);
}
finally {
if (this.state != FINISHING_OK) {
thread.getExpressionManager().cleanEvalResult(id, r, monitor);
}
}
}
catch (final CoreException | UnexpectedRDataException e) {
this.state= FINISHING_ERROR;
final ToStringBuilder sb= new ObjectUtils.ToStringBuilder(
"An error occurred when evaluating watch expression." );
sb.addProp("expression", this.task.getRExpression()); //$NON-NLS-1$
sb.addProp("frame", this.task.getStackFrame()); //$NON-NLS-1$
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
sb.toString(), e ));
}
}
private void evalExpression(final String id, final AbstractRDbgController r,
final IProgressMonitor monitor) throws UnexpectedRDataException, CoreException, DebugException {
final RMainThread thread= this.task.getThread();
final String valueName= id + ".value"; //$NON-NLS-1$
final RElementName valueElementName= RElementName.create(ImCollections.newList(
RJTmp.PKG_NAME, RJTmp.ENV_NAME,
RElementName.create(RElementName.MAIN_DEFAULT, valueName) ));
// System.out.println(this.task.getRExpression() + " in " + this.task.getStackFrame());
CombinedRElement element;
final RReference ref;
try {
element= r.evalCombinedStruct(
this.task.getRExpression(),
new RReferenceImpl(this.task.getStackFrame().getHandle(), RObject.TYPE_ENVIRONMENT, null),
0, RService.DEPTH_REFERENCE, valueElementName, monitor );
ref= RDataUtils.checkRReference(element);
}
catch (final CoreException e) {
synchronized (this) {
this.state= FINISHING_ERROR;
final IStatus status= e.getStatus();
if (status.getSeverity() == IStatus.CANCEL) {
evalCompleted(new REvaluationResult(this.task.getRExpression(), thread,
IStatus.CANCEL, Messages.Expression_Evaluate_Cancelled_message ));
}
else {
evalCompleted(new REvaluationResult(this.task.getRExpression(), thread,
IStatus.ERROR, status.getMessage() ));
}
return;
}
}
{ final FunctionCall call= r.createFunctionCall(RJTmp.SET);
call.addChar(RJTmp.NAME_PAR, valueName);
call.add(RJTmp.VALUE_PAR, ref);
call.evalVoid(monitor);
}
if (ref.getReferencedRObjectType() == RObject.TYPE_ENVIRONMENT) {
thread.resolveReference((CombinedRElement) ref, this.stamp, monitor);
}
else {
element= r.getWorkspaceData().resolve(ref, RWorkspace.RESOLVE_UPTODATE, 0, monitor);
if (element == null) {
element= r.evalCombinedStruct(valueElementName, 0, RService.DEPTH_INFINITE,
monitor );
if (element == null) {
throw new UnexpectedRDataException("null");
}
}
}
final RElementVariable variable= new RElementVariable(element, thread, this.stamp, null);
variable.getValue(monitor);
this.state= FINISHING_OK;
evalCompleted(new REvaluationResult(this.task.getRExpression(), thread,
variable, id ));
}
private void evalCompleted(final REvaluationResult evalResult) {
this.task.getThread().getExpressionManager().setEvalResult(this.task.getKey(), evalResult);
this.listener.evaluationFinished(evalResult);
}
}