| /*=============================================================================# |
| # Copyright (c) 2016, 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.eval; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.IExpressionManager; |
| |
| import org.eclipse.statet.jcommons.collections.CopyOnWriteList; |
| 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.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.eval.REvalExpressionTask.Key; |
| import org.eclipse.statet.internal.r.debug.core.model.RDebugTarget; |
| import org.eclipse.statet.internal.r.debug.core.model.RMainThread; |
| import org.eclipse.statet.internal.r.debug.core.model.RStackFrame; |
| import org.eclipse.statet.r.console.core.RProcess; |
| import org.eclipse.statet.r.core.tool.TmpUtils; |
| import org.eclipse.statet.r.debug.core.IRStackFrame; |
| import org.eclipse.statet.r.nico.AbstractRDbgController; |
| |
| |
| @NonNullByDefault |
| public class ExpressionManager { |
| |
| |
| private class CleanRunnable implements SystemRunnable { |
| |
| |
| private boolean scheduled; |
| |
| |
| public CleanRunnable() { |
| } |
| |
| |
| @Override |
| public String getTypeId() { |
| return "r/dbg/exprs/clean"; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public String getLabel() { |
| return Messages.Expression_Clean_task; |
| } |
| |
| @Override |
| public boolean canRunIn(final Tool tool) { |
| return (tool == ExpressionManager.this.thread.getTool()); |
| } |
| |
| @Override |
| public boolean changed(final int event, final Tool process) { |
| switch (event) { |
| case REMOVING_FROM: |
| case MOVING_FROM: |
| return false; |
| case BEING_ABANDONED: |
| // case FINISHING_: // handled in #loadContext |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| @Override |
| public void run(final ToolService service, final ProgressMonitor m) throws StatusException { |
| synchronized (this) { |
| this.scheduled= false; |
| } |
| cleanEvalResults((AbstractRDbgController) service, m); |
| } |
| |
| } |
| |
| |
| private final Map<REvalExpressionTask.Key, @Nullable REvaluationResult> evalResults= new HashMap<>(); |
| private final List<@NonNull REvaluationResult> oldEvalResults= new ArrayList<>(); |
| |
| private final CopyOnWriteList<@NonNull REvaluationExpression> expressions= new CopyOnWriteList<>(); |
| |
| private final RMainThread thread; |
| |
| private final CleanRunnable cleanRunnable= new CleanRunnable(); |
| |
| |
| public ExpressionManager(final RMainThread thread) { |
| this.thread= thread; |
| } |
| |
| |
| public void clearCache(final int stamp, final @Nullable ProgressMonitor m) { |
| synchronized (this) { |
| if (!this.evalResults.isEmpty()) { |
| for (final Iterator<Entry<Key, @Nullable REvaluationResult>> iter= this.evalResults.entrySet().iterator(); iter.hasNext();) { |
| final Entry<Key, @Nullable REvaluationResult> entry= iter.next(); |
| final REvaluationResult result= entry.getValue(); |
| if (result != null) { |
| this.oldEvalResults.add(result); |
| entry.setValue(null); |
| } |
| else { |
| iter.remove(); |
| } |
| } |
| } |
| } |
| |
| int cleanCounter= 0; |
| for (final REvaluationResult result : this.oldEvalResults) { |
| if (result.isLocked()) { |
| if (stamp != 0) { |
| result.reset(stamp, m); |
| } |
| } |
| else { |
| cleanCounter++; |
| } |
| } |
| if (cleanCounter > 0) { |
| scheduleClean(); |
| } |
| } |
| |
| public void evaluate(final String expressionText, final IRStackFrame stackFrame, |
| final boolean forceReevaluate, final IEvaluationListener listener) { |
| if (!checkExpression(expressionText, listener)) { |
| return; |
| } |
| |
| final REvalExpressionTask task= new REvalExpressionTask(expressionText, |
| (@NonNull RStackFrame) stackFrame ); |
| REvaluationResult result; |
| final REvaluationResult prev; |
| synchronized (this) { |
| if (forceReevaluate) { |
| result= null; |
| this.evalResults.put(task.getKey(), null); |
| } |
| else { |
| result= this.evalResults.get(task.getKey()); |
| if (result != null) { |
| result.lock(); |
| } |
| } |
| } |
| |
| if (result == null) { |
| final int stamp= this.thread.checkStackFrame(stackFrame); |
| if (stamp != 0) { |
| final RProcess tool= this.thread.getTool(); |
| final EvalExpressionRunnable runnable= new EvalExpressionRunnable(task, stamp, |
| listener ); |
| synchronized (runnable) { |
| if (tool.getQueue().add(runnable).getSeverity() == Status.OK) { |
| // async |
| return; |
| } |
| } |
| } |
| result= new REvaluationResult(expressionText, this.thread); |
| } |
| |
| listener.evaluationFinished(result); |
| } |
| |
| private boolean checkExpression(final String rExpression, final IEvaluationListener listener) { |
| final RDebugTarget debugTarget= this.thread.getDebugTarget(); |
| final ExpressionValidator expressionValidator= debugTarget.getExpressionValidator(); |
| final String errorMessage; |
| synchronized (expressionValidator) { |
| errorMessage= expressionValidator.checkExpression(rExpression); |
| } |
| if (errorMessage != null) { |
| listener.evaluationFinished(new REvaluationResult(rExpression, this.thread, |
| IStatus.ERROR, errorMessage )); |
| return false; |
| } |
| return true; |
| } |
| |
| |
| public void setEvalResult(final REvalExpressionTask.Key evalKey, |
| final REvaluationResult result) { |
| final REvaluationResult prev; |
| synchronized (this) { |
| prev= this.evalResults.put(evalKey, result); |
| } |
| if (prev != null) { |
| prev.free(); |
| } |
| } |
| |
| public @Nullable REvaluationResult getEvalResult(final REvalExpressionTask.Key evalKey) { |
| synchronized (this) { |
| final REvaluationResult result= this.evalResults.get(evalKey); |
| if (result != null) { |
| result.lock(); |
| } |
| return result; |
| } |
| } |
| |
| |
| public void scheduleClean() { |
| synchronized (this.cleanRunnable) { |
| if (this.cleanRunnable.scheduled) { |
| return; |
| } |
| this.thread.getTool().getQueue().addHot(this.cleanRunnable); |
| this.cleanRunnable.scheduled= true; |
| } |
| } |
| |
| private void cleanEvalResults( |
| final AbstractRDbgController r, final ProgressMonitor m) { |
| for (final Iterator<REvaluationResult> iter= this.oldEvalResults.iterator(); iter.hasNext(); ) { |
| final REvaluationResult result= iter.next(); |
| if (result.isLocked()) { |
| continue; |
| } |
| iter.remove(); |
| final TmpUtils.Item tmpId= result.getTmpItem(); |
| if (tmpId != null) { |
| tmpId.disposeChecked(m); |
| } |
| } |
| } |
| |
| |
| public void register(final REvaluationExpression expression) { |
| this.expressions.add(expression); |
| } |
| |
| public void unregister(final REvaluationExpression expression) { |
| this.expressions.remove(expression); |
| } |
| |
| public void updateExpressions(final List<@NonNull DebugEvent> eventCollection) { |
| final ImList<@NonNull REvaluationExpression> list= this.expressions.toList(); |
| |
| for (final REvaluationExpression expression : list) { |
| eventCollection.add(new DebugEvent(expression, DebugEvent.CHANGE, DebugEvent.CONTENT)); |
| } |
| } |
| |
| public void cleanExpressions(final List<@NonNull DebugEvent> eventCollection) { |
| final ImList<@NonNull REvaluationExpression> list= this.expressions.clearToList(); |
| final IExpressionManager manager= DebugPlugin.getDefault().getExpressionManager(); |
| |
| for (final REvaluationExpression expression : list) { |
| manager.removeExpression(expression); |
| } |
| } |
| |
| } |