| /*=============================================================================# |
| # 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); |
| } |
| |
| } |