| /*=============================================================================# |
| # Copyright (c) 2010, 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.r.nico; |
| |
| import java.net.URI; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.osgi.util.NLS; |
| |
| 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.StatusException; |
| import org.eclipse.statet.jcommons.ts.core.SystemRunnable; |
| import org.eclipse.statet.jcommons.ts.core.Tool; |
| import org.eclipse.statet.jcommons.ts.core.ToolRunnable; |
| import org.eclipse.statet.jcommons.ts.core.ToolService; |
| |
| import org.eclipse.statet.ecommons.io.FileUtil; |
| |
| import org.eclipse.statet.internal.r.console.core.RConsoleCorePlugin; |
| import org.eclipse.statet.ltk.core.Ltk; |
| import org.eclipse.statet.ltk.model.core.element.SourceUnit; |
| import org.eclipse.statet.ltk.model.core.element.WorkspaceSourceUnit; |
| import org.eclipse.statet.nico.core.runtime.Prompt; |
| import org.eclipse.statet.nico.core.runtime.SubmitType; |
| import org.eclipse.statet.nico.core.runtime.ToolStatus; |
| import org.eclipse.statet.nico.core.runtime.ToolStreamProxy; |
| import org.eclipse.statet.r.console.core.AbstractRController; |
| import org.eclipse.statet.r.console.core.ContinuePrompt; |
| import org.eclipse.statet.r.console.core.IRDataAdapter; |
| import org.eclipse.statet.r.console.core.RDbg; |
| import org.eclipse.statet.r.console.core.RProcess; |
| import org.eclipse.statet.r.core.RUtil; |
| import org.eclipse.statet.r.core.model.RSourceUnit; |
| import org.eclipse.statet.r.core.model.RWorkspaceSourceUnit; |
| import org.eclipse.statet.r.core.tool.IRConsoleService; |
| 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.server.dbg.CallStack; |
| import org.eclipse.statet.rj.server.dbg.CtrlReport; |
| import org.eclipse.statet.rj.server.dbg.DbgEnablement; |
| import org.eclipse.statet.rj.server.dbg.DbgFilterState; |
| import org.eclipse.statet.rj.server.dbg.DbgRequest; |
| import org.eclipse.statet.rj.server.dbg.ElementTracepointInstallationRequest; |
| import org.eclipse.statet.rj.server.dbg.Frame; |
| import org.eclipse.statet.rj.server.dbg.FrameContext; |
| import org.eclipse.statet.rj.server.dbg.FrameRef; |
| import org.eclipse.statet.rj.server.dbg.SetDebugReport; |
| import org.eclipse.statet.rj.server.dbg.SetDebugRequest; |
| import org.eclipse.statet.rj.server.dbg.SrcfileData; |
| import org.eclipse.statet.rj.server.dbg.TracepointEvent; |
| import org.eclipse.statet.rj.server.dbg.TracepointInstallationRequest; |
| import org.eclipse.statet.rj.server.dbg.TracepointStatesUpdate; |
| import org.eclipse.statet.rj.services.RVersion; |
| |
| |
| /** |
| * For implementations supporting debug features |
| */ |
| public abstract class AbstractRDbgController extends AbstractRController |
| implements IRDataAdapter, ICombinedRDataAdapter { |
| |
| |
| @NonNullByDefault |
| public interface IRControllerTracepointAdapter { |
| |
| |
| void handle(final TracepointEvent event); |
| |
| boolean matchScriptBreakpoint(IRModelSrcref srcref, |
| final ProgressMonitor m ); |
| |
| @Nullable ElementTracepointInstallationRequest getElementTracepoints( |
| SrcfileData srcfile, IRModelSrcref su, |
| final ProgressMonitor m ); |
| |
| @Nullable ElementTracepointInstallationRequest prepareFileElementTracepoints( |
| SrcfileData srcfile, RSourceUnit su, |
| final ProgressMonitor m ); |
| |
| void finishFileElementTracepoints(SrcfileData srcfileData, RSourceUnit su, |
| ElementTracepointInstallationRequest request, |
| final ProgressMonitor m ); |
| |
| void installElementTracepoints(ElementTracepointInstallationRequest request, |
| final ProgressMonitor m ); |
| |
| @Nullable Object toEclipseData(TracepointEvent hit); |
| |
| |
| } |
| |
| |
| private static final int TOPLEVELBROWSER_ENABLE_COMMANDS= 1; |
| private static final int TOPLEVELBROWSER_CHECK_SUSPENDED= 3; |
| private static final int TOPLEVELBROWSER_CHECK_SUBMIT= 4; |
| |
| |
| protected static final RReference TOPLEVEL_ENV_FRAME= new RReferenceImpl(0, RObject.TYPE_ENVIRONMENT, null); |
| |
| |
| private IRControllerTracepointAdapter breakpointAdapter; |
| |
| private CallStack callStack; |
| private int callStackStamp; |
| |
| private RReference globalEnv; |
| |
| private boolean suspendScheduled; |
| private final ToolRunnable suspendRunnable= new ControllerSystemRunnable(SUSPEND_TYPE_ID, |
| "Suspend") { |
| |
| @Override |
| public boolean changed(final int event, final Tool tool) { |
| switch (event) { |
| case MOVING_FROM: |
| return false; |
| case REMOVING_FROM: |
| case BEING_ABANDONED: |
| case FINISHING_OK: |
| case FINISHING_ERROR: |
| case FINISHING_CANCEL: |
| synchronized (AbstractRDbgController.this.suspendRunnable) { |
| AbstractRDbgController.this.suspendScheduled= false; |
| } |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| @Override |
| public void run(final ToolService service, final ProgressMonitor m) throws StatusException { |
| if (getStatusL() == ToolStatus.STARTED_SUSPENDED |
| || (getHotTasksState() <= 1 && (AbstractRDbgController.this.fCurrentPrompt.meta & META_PROMPT_SUSPENDED) != 0) ) { |
| return; |
| } |
| // if (!canSuspend(monitor)) { |
| // scheduleControllerRunnable(this); |
| // return; |
| // } |
| if (AbstractRDbgController.this.topLevelBrowserAction == 0) { |
| AbstractRDbgController.this.topLevelBrowserAction= TOPLEVELBROWSER_CHECK_SUBMIT; |
| } |
| doRequestSuspend(m); |
| } |
| |
| }; |
| |
| private boolean topLevelBrowserEnabled; |
| private int topLevelBrowserAction; |
| private final SystemRunnable fTopLevelBrowserRunnable= new ControllerSystemRunnable( |
| "r/debug", "Debugging") { //$NON-NLS-1$ |
| @Override |
| public void run(final ToolService service, final ProgressMonitor m) throws StatusException { |
| if (getCurrentLevelL() == 0) { |
| if ((AbstractRDbgController.this.fCurrentPrompt.meta & META_PROMPT_SUSPENDED) != 0) { |
| setSuspended(getBrowserLevel(AbstractRDbgController.this.fCurrentPrompt.text), 0, null); |
| } |
| else if ((AbstractRDbgController.this.fCurrentPrompt.meta & META_PROMPT_DEFAULT) != 0) { |
| switch (AbstractRDbgController.this.topLevelBrowserAction) { |
| case TOPLEVELBROWSER_ENABLE_COMMANDS: |
| initTopLevelBrowser(m); |
| break; |
| case TOPLEVELBROWSER_CHECK_SUSPENDED: |
| if (getQueue().getCurrentSize() > 0) { |
| AbstractRDbgController.this.topLevelBrowserAction= TOPLEVELBROWSER_CHECK_SUBMIT; |
| break; |
| } |
| setDebugBrowser(TOPLEVEL_ENV_FRAME, false, false, m); |
| //$FALL-THROUGH$ |
| default: |
| removePostControllerRunnable(AbstractRDbgController.this.fTopLevelBrowserRunnable); |
| } |
| } |
| } |
| } |
| |
| }; |
| |
| // private int fStepFilterAction; |
| // private final IToolRunnable fStepFilterRunnable= new IToolRunnable() { |
| // |
| // public String getTypeId() { |
| // return "r/debug/"; |
| // } |
| // |
| // public SubmitType getSubmitType() { |
| // return SubmitType.OTHER; |
| // } |
| // |
| // public String getLabel() { |
| // return "Step Filter"; |
| // } |
| // |
| // public boolean changed(final int event, final ToolProcess process) { |
| // return true; |
| // } |
| // |
| // public void run(final IToolRunnableControllerAdapter adapter, |
| // final ProgressMonitor m) throws CoreException { |
| // if (DebugPlugin.isUseStepFilters() |
| // && (fCurrentPrompt.meta & META_PROMPT_SUSPENDED) != 0) { |
| // if (fStepFilterAction == STEPFILTER_STEP1) { |
| // fStepFilterAction= 0; |
| // debugStepOver(true); |
| // } |
| // } |
| // } |
| // |
| // }; |
| |
| private String lastSrcfile; |
| private String lastSrcfilePath; |
| |
| private TracepointEvent breakpointHit; |
| |
| |
| /** |
| * |
| * @param process the R process the controller belongs to |
| * @param connectionInfo the initialization data |
| * @param enableDebug if debug features should be enabled |
| */ |
| public AbstractRDbgController(final RProcess process, |
| final Map<String, Object> connectionInfo) { |
| super(process, connectionInfo); |
| } |
| |
| |
| public void initDebug(final IRControllerTracepointAdapter breakpointAdapter) { |
| if (breakpointAdapter == null) { |
| throw new NullPointerException("breakpointAdapter"); |
| } |
| setDebugEnabled(true); |
| this.breakpointAdapter= breakpointAdapter; |
| |
| class LoadCallstackRunnable extends ControllerSystemRunnable implements SystemRunnable { |
| |
| public LoadCallstackRunnable() { |
| super("r/callstack", "Load Callstack"); //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void run(final ToolService service, |
| final ProgressMonitor m) throws StatusException { |
| getCallStack(m); |
| } |
| |
| } |
| addSuspendUpdateRunnable(new LoadCallstackRunnable()); |
| addToolStatusListener(new IToolStatusListener() { |
| @Override |
| public void controllerStatusChanged(final ToolStatus oldStatus, final ToolStatus newStatus, |
| final List<DebugEvent> eventCollection) { |
| switch (newStatus) { |
| case STARTED_IDLING: |
| case STARTED_PROCESSING: |
| case TERMINATED: |
| AbstractRDbgController.this.callStack= null; |
| } |
| } |
| }); |
| } |
| |
| protected final void setCurrentPromptL(final String text, final boolean addToHistory) { |
| final TracepointEvent hit= this.breakpointHit; |
| this.breakpointHit= null; |
| if (this.defaultPromptText.equals(text)) { |
| if (isDebugEnabled() && getRequestedLevelL() != 0) { |
| setSuspended(0, 0, null); |
| } |
| if (addToHistory) { |
| setCurrentPromptL(this.fDefaultPrompt); |
| return; |
| } |
| setCurrentPromptL(new Prompt(this.defaultPromptText, META_HISTORY_DONTADD | META_PROMPT_DEFAULT)); |
| return; |
| } |
| else if (this.continuePromptText.equals(text)) { |
| setCurrentPromptL(new ContinuePrompt( |
| this.fCurrentPrompt, this.fCurrentInput+this.fLineSeparator, this.continuePromptText, |
| addToHistory ? 0 : META_HISTORY_DONTADD)); |
| return; |
| } |
| else if (text != null) { |
| if (isDebugEnabled() && text.startsWith("Browse[") && text.endsWith("]> ")) { //$NON-NLS-1$ //$NON-NLS-2$ |
| this.callStack= null; |
| setSuspended(getBrowserLevel(text), |
| (hit != null) ? DebugEvent.BREAKPOINT : 0, |
| (hit != null) ? this.breakpointAdapter.toEclipseData(hit) : null ); |
| setCurrentPromptL(new Prompt(text, addToHistory ? (META_PROMPT_SUSPENDED) : |
| (META_PROMPT_SUSPENDED | META_HISTORY_DONTADD))); |
| return; |
| } |
| setCurrentPromptL(new Prompt(text, addToHistory ? 0 : META_HISTORY_DONTADD)); |
| return; |
| } |
| else { // TODO log warning / exception? |
| setCurrentPromptL(new Prompt("", addToHistory ? 0 : META_HISTORY_DONTADD)); //$NON-NLS-1$ |
| return; |
| } |
| } |
| |
| private int getBrowserLevel(final String prompt) { |
| return Integer.parseInt(prompt.substring(7, prompt.indexOf(']'))); |
| } |
| |
| @Override |
| protected boolean runConsoleCommandInSuspend(final String input) { |
| final ToolStreamProxy streams= getStreams(); |
| |
| if ((getPrompt().meta & META_PROMPT_SUSPENDED) != 0) { |
| final String trimmed= input.trim(); |
| if (trimmed.isEmpty()) { |
| streams.getOutputStreamMonitor().append(this.fLineSeparator, SubmitType.TOOLS, 0); |
| // revert counter? |
| return false; |
| } |
| else if (trimmed.length() == 1) { |
| try { |
| switch(trimmed.charAt(0)) { |
| case 'Q': |
| debugCancel(); |
| return false; |
| case 'c': |
| if (exec(new DbgRequest.Resume())) { |
| return false; |
| } |
| break; |
| case 'n': |
| if (exec(new DbgRequest.StepOver())) { |
| return false; |
| } |
| break; |
| case 's': |
| if (exec(new DbgRequest.StepInto())) { |
| return false; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| catch (final StatusException e) { |
| RConsoleCorePlugin.log(new Status(IStatus.INFO, RConsoleCorePlugin.BUNDLE_ID, 0, |
| "An error occurred when executing debug request in the R engine.", e )); |
| |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| |
| public void debugSuspend() { |
| synchronized (this.suspendRunnable) { |
| if (this.suspendScheduled) { |
| return; |
| } |
| this.suspendScheduled= true; |
| } |
| getQueue().addHot(this.suspendRunnable); |
| } |
| |
| public boolean exec(final DbgRequest request) throws StatusException { |
| |
| class DbgRequestResumeRunnable<R extends DbgRequest> extends SuspendResumeRunnable { |
| |
| |
| public DbgRequestResumeRunnable(final String id, final String label) { |
| super(id, label, RDbg.getResumeEventDetail(request.getOp())); |
| } |
| |
| |
| protected DbgRequest check(final R request, final ProgressMonitor m) { |
| return request; |
| } |
| |
| @Override |
| public void run(final ToolService adapter, |
| final ProgressMonitor m) throws StatusException { |
| if ((getPrompt().meta & META_PROMPT_SUSPENDED) == 0) { |
| return; |
| } |
| final DbgRequest checkedRequest= check((R) request, m); |
| if (checkedRequest == null) { |
| return; |
| } |
| final CtrlReport report= AbstractRDbgController.this.doExec(checkedRequest, m); |
| if (!report.isEngineSuspended()) { |
| super.run(adapter, m); |
| submitToConsole(getResumeRCommand(report.getOp()), null, m); |
| setDetail(RDbg.getResumeEventDetail(report.getOp())); |
| } |
| } |
| |
| @Override |
| protected void doExec(final ProgressMonitor m) throws StatusException { |
| briefChanged(IRConsoleService.AUTO_CHANGE); |
| } |
| |
| protected String getResumeRCommand(final byte op) { |
| switch (op) { |
| case DbgRequest.RESUME: |
| return "c"; //$NON-NLS-1$ |
| case DbgRequest.STEP_INTO: |
| return "s"; //$NON-NLS-1$ |
| case DbgRequest.STEP_OVER: |
| return "n"; //$NON-NLS-1$ |
| case DbgRequest.STEP_RETURN: |
| default: |
| return "c"; //$NON-NLS-1$ |
| } |
| } |
| |
| } |
| |
| switch (request.getOp()) { |
| case DbgRequest.RESUME: |
| scheduleSuspendExitRunnable(new DbgRequestResumeRunnable<DbgRequest.Resume>( |
| RESUME_TYPE_ID, "Resume" ) { |
| |
| @Override |
| protected void doExec(final ProgressMonitor m) throws StatusException { |
| super.doExec(m); |
| AbstractRDbgController.this.topLevelBrowserEnabled= false; |
| } |
| |
| }); |
| return true; |
| case DbgRequest.STEP_OVER: |
| scheduleSuspendExitRunnable(new DbgRequestResumeRunnable<DbgRequest.StepOver>( |
| STEP_OVER_TYPE_ID, "Step Over" )); |
| return true; |
| case DbgRequest.STEP_INTO: |
| if ((getPlatform().getRVersion().compareTo(new RVersion(3, 1, 0)) < 0)) { |
| return false; |
| } |
| scheduleSuspendExitRunnable(new DbgRequestResumeRunnable<DbgRequest.StepInto>( |
| STEP_INTO_TYPE_ID, "Step Into" )); |
| return true; |
| case DbgRequest.STEP_RETURN: |
| scheduleSuspendExitRunnable(new DbgRequestResumeRunnable<DbgRequest.StepReturn>( |
| STEP_RETURN_TYPE_ID, "Step Return" ) { |
| |
| private Frame targetFrame; |
| |
| @Override |
| protected DbgRequest check(final DbgRequest.StepReturn request, final ProgressMonitor m) { |
| final CallStack callStack= getCallStack(m); |
| if (request.getTarget() instanceof FrameRef.ByPosition) { |
| final int targetPosition= ((FrameRef.ByPosition) request.getTarget()).getPosition(); |
| final int n= callStack.getFrames().size(); |
| if (targetPosition >= 0 && targetPosition < n - 1) { |
| this.targetFrame= callStack.getFrames().get(targetPosition); |
| } |
| } |
| else if (request.getTarget() instanceof FrameRef.ByHandle) { |
| final long targetHandle= ((FrameRef.ByHandle) request.getTarget()).getHandle(); |
| this.targetFrame= callStack.findFrame(targetHandle); |
| } |
| return (this.targetFrame != null) ? |
| new DbgRequest.StepReturn(new FrameRef.ByHandle(this.targetFrame.getHandle())) : |
| null; |
| } |
| |
| @Override |
| protected void doExec(final ProgressMonitor m) throws StatusException { |
| super.doExec(m); |
| // if (targetPosition == 0) { |
| // fTopLevelBrowserAction= TOPLEVELBROWSER_ENABLE_COMMANDS; |
| // } |
| } |
| |
| }); |
| return true; |
| default: |
| throw new UnsupportedOperationException(request.toString()); |
| } |
| } |
| |
| public void debugStepInto(final int position, final String fRefCode) throws StatusException { |
| scheduleSuspendExitRunnable(new SuspendResumeRunnable(STEP_INTO_TYPE_ID, |
| "Step Into", DebugEvent.STEP_OVER) { |
| @Override |
| protected boolean canExec(final ProgressMonitor m) throws StatusException { |
| if ((getPrompt().meta & META_PROMPT_SUSPENDED) != 0) { |
| final CallStack stack= getCallStack(m); |
| if (stack != null) { |
| final int n= stack.getFrames().size(); |
| if (n == 0 || position > n) { |
| return false; |
| } |
| final int pos= (position >= 0) ? position : stack.getFrames().size() - 1; |
| try { |
| final SetDebugReport report= AbstractRDbgController.this.doExec( |
| new SetDebugRequest(pos, fRefCode, true, true), m); |
| return (report != null); |
| } |
| catch (final Exception e) { |
| RConsoleCorePlugin.log(new Status(IStatus.INFO, RConsoleCorePlugin.BUNDLE_ID, 0, |
| "A problem occurred when stepping into the specified function call: " + |
| "Could not prepare debug for '"+fRefCode+"'.", e )); |
| return false; |
| } |
| } |
| } |
| return false; |
| } |
| @Override |
| protected void doExec(final ProgressMonitor m) throws StatusException { |
| briefChanged(IRConsoleService.AUTO_CHANGE); |
| submitToConsole("c", "c", m); //$NON-NLS-1$ //$NON-NLS-2$ |
| return; |
| } |
| }); |
| } |
| |
| public void debugCancel() throws StatusException { |
| scheduleSuspendExitRunnable(new SuspendResumeRunnable(RESUME_TYPE_ID, |
| "Cancel", DebugEvent.CLIENT_REQUEST) { |
| @Override |
| protected boolean canExec(final ProgressMonitor m) throws StatusException { |
| return ((getPrompt().meta & META_PROMPT_SUSPENDED) != 0); |
| } |
| @Override |
| protected void doExec(final ProgressMonitor m) throws StatusException { |
| briefChanged(IRConsoleService.AUTO_CHANGE); |
| AbstractRDbgController.this.topLevelBrowserEnabled= false; |
| AbstractRDbgController.this.topLevelBrowserAction= TOPLEVELBROWSER_CHECK_SUBMIT; |
| submitToConsole("Q", "Q", m); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| }); |
| } |
| |
| @Override |
| protected QuitRunnable createQuitRunnable() { |
| return new QuitRunnable() { |
| @Override |
| protected void doExec(final ProgressMonitor m) throws StatusException { |
| if ((getPrompt().meta & META_PROMPT_SUSPENDED) != 0) { |
| briefChanged(IRConsoleService.AUTO_CHANGE); |
| AbstractRDbgController.this.topLevelBrowserEnabled= false; |
| AbstractRDbgController.this.topLevelBrowserAction= TOPLEVELBROWSER_CHECK_SUBMIT; |
| submitToConsole("Q", "Q", m); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| // getQueue().add(createQuitRunnable()); |
| } |
| }; |
| } |
| |
| @Override |
| protected void doQuitL(final ProgressMonitor m) throws StatusException { |
| if ((getPrompt().meta & META_PROMPT_SUSPENDED) == 0) { |
| super.doQuitL(m); |
| } |
| } |
| |
| @Override |
| protected void runSuspendedLoopL(final int o) { |
| if (this.topLevelBrowserAction == TOPLEVELBROWSER_CHECK_SUBMIT) { |
| this.topLevelBrowserAction= 0; |
| } |
| removePostControllerRunnable(this.fTopLevelBrowserRunnable); |
| |
| super.runSuspendedLoopL(o); |
| |
| if (getCurrentLevelL() == 0) { |
| if (this.topLevelBrowserAction == 0) { |
| this.topLevelBrowserAction= TOPLEVELBROWSER_CHECK_SUBMIT; |
| } |
| addPostControllerRunnable(this.fTopLevelBrowserRunnable); |
| } |
| } |
| |
| |
| public CallStack getCallStack(final ProgressMonitor m) { |
| if (this.callStack == null || this.callStackStamp != getChangeStamp()) { |
| this.callStackStamp= getChangeStamp(); |
| try { |
| this.callStack= doEvalCallStack(m); |
| } |
| catch (final Exception e) { |
| this.callStack= null; |
| RConsoleCorePlugin.log(new Status(IStatus.ERROR, RConsoleCorePlugin.BUNDLE_ID, 0, |
| "An error occurred when loading the R call stack.", e )); |
| } |
| } |
| return this.callStack; |
| } |
| |
| public FrameContext evalFrameContext(final int position, |
| final ProgressMonitor m) throws StatusException { |
| try { |
| return doEvalFrameContext(position, m); |
| } |
| catch (final Exception e) { |
| throw new StatusException(new ErrorStatus(RConsoleCorePlugin.BUNDLE_ID, |
| NLS.bind("An error occurred when loading detail of R stack frame {0}.", position), |
| e )); |
| } |
| } |
| |
| public abstract void exec(DbgEnablement request) throws StatusException; |
| |
| public abstract void exec(DbgFilterState request) throws StatusException; |
| |
| public abstract void exec(TracepointStatesUpdate request) throws StatusException; |
| |
| public abstract void exec(TracepointStatesUpdate request, |
| final ProgressMonitor m) throws StatusException; |
| |
| public abstract void exec(TracepointInstallationRequest request, |
| final ProgressMonitor m) throws StatusException; |
| |
| @Override |
| public Set<Long> getLazyEnvironments(final ProgressMonitor m) { |
| Set<Long> list= super.getLazyEnvironments(m); |
| if (isSuspendedL()) { |
| final CallStack stack= getCallStack(m); |
| if (stack != null) { |
| final List<? extends Frame> frames= stack.getFrames(); |
| if (list == null) { |
| list= new HashSet<>(frames.size()); |
| } |
| for (int i= 0; i < frames.size(); i++) { |
| final long handle= frames.get(i).getHandle(); |
| if (handle != 0) { |
| list.add(Long.valueOf(handle)); |
| } |
| } |
| } |
| } |
| return list; |
| } |
| |
| protected CallStack doEvalCallStack(final ProgressMonitor m) throws StatusException { |
| return null; |
| } |
| |
| protected FrameContext doEvalFrameContext(final int position, |
| final ProgressMonitor m) throws Exception { |
| return null; |
| } |
| |
| |
| public void initTopLevelBrowser(final ProgressMonitor m) throws StatusException { |
| if ((this.fCurrentPrompt.meta & META_PROMPT_DEFAULT) == 0) { |
| return; |
| } |
| if (this.topLevelBrowserAction != TOPLEVELBROWSER_CHECK_SUSPENDED) { |
| this.topLevelBrowserAction= TOPLEVELBROWSER_CHECK_SUBMIT; |
| } |
| setDebugBrowser(TOPLEVEL_ENV_FRAME, true, false, m); |
| // fStepFilterAction= STEPFILTER_STEP1; |
| // submitCommandToConsole(new String[] { "browser(skipCalls= 3L)" }, null, monitor); |
| } |
| |
| private void checkInit(final ProgressMonitor m) throws StatusException { |
| if (this.globalEnv == null) { |
| try { |
| this.globalEnv= RDataUtils.checkRReference( |
| evalData(".GlobalEnv", null, 0, DEPTH_REFERENCE, m) ); //$NON-NLS-1$ |
| } |
| catch (final UnexpectedRDataException e) { |
| throw new StatusException(new ErrorStatus(RConsoleCorePlugin.BUNDLE_ID, |
| "Init debug data failed.", |
| e )); |
| } |
| } |
| |
| } |
| |
| protected boolean setDebugBrowser(final RReference environment, final boolean enable, |
| final boolean temp, |
| final ProgressMonitor m) throws StatusException { |
| checkInit(m); |
| if (environment == TOPLEVEL_ENV_FRAME || environment.getHandle() == this.globalEnv.getHandle()) { |
| this.topLevelBrowserEnabled= enable; |
| if (enable) { |
| if (this.topLevelBrowserAction == 0) { |
| this.topLevelBrowserAction= TOPLEVELBROWSER_CHECK_SUBMIT; |
| } |
| } |
| else { |
| if (this.topLevelBrowserAction != TOPLEVELBROWSER_ENABLE_COMMANDS) { |
| this.topLevelBrowserAction= 0; |
| } |
| } |
| } |
| try { |
| final SetDebugReport report= doExec( |
| new SetDebugRequest(environment.getHandle(), enable, temp), m ); |
| return (report != null && report.isChanged()); |
| } |
| catch (final StatusException e) { |
| throw e; |
| } |
| catch (final Exception e) { |
| throw new StatusException(new ErrorStatus(RConsoleCorePlugin.BUNDLE_ID, |
| "An error occurred when changing the R debug browser state.", |
| e )); |
| } |
| } |
| |
| |
| protected SetDebugReport doExec(final SetDebugRequest request, |
| final ProgressMonitor m) throws StatusException { |
| return null; |
| } |
| |
| protected boolean canSuspend(final ProgressMonitor m) { |
| return true; |
| } |
| |
| protected void doRequestSuspend(final ProgressMonitor m) throws StatusException { |
| } |
| |
| protected void handleTracepointEvents(final List<? extends TracepointEvent> events) { |
| for (final TracepointEvent event : events) { |
| try { |
| if (event.getKind() == TracepointEvent.KIND_ABOUT_TO_HIT) { |
| this.breakpointHit= event; |
| } |
| else { |
| this.breakpointAdapter.handle(event); |
| } |
| } |
| catch (final Exception e) { |
| |
| } |
| } |
| } |
| |
| protected CtrlReport doExec(final DbgRequest request, |
| final ProgressMonitor m) throws StatusException { |
| return null; |
| } |
| |
| |
| @Override |
| public boolean acceptNewConsoleCommand() { |
| return ((this.fCurrentPrompt.meta & (META_PROMPT_DEFAULT | META_PROMPT_SUSPENDED)) != 0); |
| } |
| |
| @Override |
| public void submitToConsole(final String input, |
| final ProgressMonitor m) throws StatusException { |
| if (input.indexOf('\n') >= 0) { |
| // TODO progress |
| final String[] lines= RUtil.LINE_SEPARATOR_PATTERN.split(input); |
| for (int i= 0; i < lines.length; i++) { |
| if (this.topLevelBrowserAction != 0 && getCurrentLevelL() == 0 && (this.fCurrentPrompt.meta & META_PROMPT_DEFAULT) != 0) { |
| this.topLevelBrowserAction= 0; |
| setDebugBrowser(TOPLEVEL_ENV_FRAME, false, false, m); |
| } |
| super.submitToConsole(lines[i], m); |
| } |
| return; |
| } |
| |
| if (this.topLevelBrowserAction != 0 && getCurrentLevelL() == 0 && (this.fCurrentPrompt.meta & META_PROMPT_DEFAULT) != 0) { |
| this.topLevelBrowserAction= 0; |
| setDebugBrowser(TOPLEVEL_ENV_FRAME, false, false, m); |
| } |
| super.submitToConsole(input, m); |
| } |
| |
| @Override |
| public void submitCommandToConsole(final String[] lines, final IRSrcref srcref, |
| final ProgressMonitor m) throws StatusException { |
| if (isDebugEnabled() && getCurrentLevelL() == 0 |
| && (this.fCurrentPrompt.meta & META_PROMPT_DEFAULT) != 0) { |
| if (this.topLevelBrowserAction == TOPLEVELBROWSER_ENABLE_COMMANDS) { |
| initTopLevelBrowser(m); |
| } |
| else if (!this.topLevelBrowserEnabled && srcref instanceof IRModelSrcref |
| && this.breakpointAdapter.matchScriptBreakpoint((IRModelSrcref) srcref, m) ) { |
| initTopLevelBrowser(m); |
| } |
| } |
| |
| final SrcfileData srcfile= getSrcfile(srcref, m); |
| |
| doSubmitCommandL(lines, srcfile, srcref, m); |
| |
| if (isDebugEnabled() && srcfile != null && srcref instanceof IRModelSrcref) { |
| final ElementTracepointInstallationRequest breakpointsRequest= this.breakpointAdapter |
| .getElementTracepoints(srcfile, (IRModelSrcref) srcref, m ); |
| if (breakpointsRequest != null) { |
| this.breakpointAdapter.installElementTracepoints(breakpointsRequest, m); |
| } |
| } |
| } |
| |
| protected void doSubmitCommandL(final String[] lines, final SrcfileData srcfile, |
| final IRSrcref srcref, |
| final ProgressMonitor m) throws StatusException { |
| super.submitCommandToConsole(lines, srcref, m); |
| } |
| |
| @Override |
| public void submitFileCommandToConsole(final String[] lines, final SourceUnit su, |
| final ProgressMonitor m) throws StatusException { |
| if (su == null) { |
| super.submitFileCommandToConsole(lines, null, m); |
| return; |
| } |
| SrcfileData srcfile= getSrcfile(su, m); |
| final ElementTracepointInstallationRequest breakpointsRequest = |
| (isDebugEnabled() && su instanceof RWorkspaceSourceUnit) ? |
| this.breakpointAdapter.prepareFileElementTracepoints(srcfile, (RSourceUnit) su, m ) : |
| null; |
| try { |
| doSubmitFileCommandToConsole(lines, srcfile, su, m); |
| } |
| finally { |
| if (breakpointsRequest != null) { |
| if (srcfile.getTimestamp() != getTimestamp((RWorkspaceSourceUnit) su, m)) { |
| srcfile= null; |
| } |
| this.breakpointAdapter.finishFileElementTracepoints(srcfile, (RSourceUnit) su, |
| breakpointsRequest, m ); |
| } |
| } |
| } |
| |
| public void doSubmitFileCommandToConsole(final String[] lines, |
| final SrcfileData srcfile, final SourceUnit su, |
| final ProgressMonitor m) throws StatusException { |
| super.submitFileCommandToConsole(lines, su, m); |
| } |
| |
| protected SrcfileData getSrcfile(final IRSrcref srcref, |
| final ProgressMonitor m) throws StatusException { |
| if (srcref instanceof IRModelSrcref) { |
| return getSrcfile(((IRModelSrcref) srcref).getFile(), m); |
| } |
| return null; |
| } |
| |
| private long getTimestamp(final WorkspaceSourceUnit su, final ProgressMonitor m) { |
| return (su.getWorkingContext() == Ltk.PERSISTENCE_CONTEXT) ? |
| su.getResource().getLocalTimeStamp() : |
| RDbg.getTimestamp(su, m); |
| } |
| |
| protected SrcfileData getSrcfile(final SourceUnit su, |
| final ProgressMonitor m) throws StatusException { |
| String fileName= null; |
| if (su != null && su.getResource() != null) { |
| URI uri= null; |
| final FileUtil fileUtil= FileUtil.getFileUtil(su.getResource()); |
| if (fileUtil != null) { |
| uri= fileUtil.getURI(); |
| } |
| if (uri != null) { |
| fileName= uri.toString(); |
| try { |
| final IFileStore store= EFS.getStore(uri); |
| if (store != null) { |
| fileName= getWorkspaceData().toToolPath(store); |
| } |
| } |
| catch (final CoreException | StatusException e) { |
| } |
| } |
| if (fileName == null) { |
| return null; |
| } |
| if (su instanceof WorkspaceSourceUnit) { |
| final WorkspaceSourceUnit wsu= (WorkspaceSourceUnit) su; |
| final IPath path= wsu.getResource().getFullPath(); |
| |
| prepareSrcfile(fileName, path, m); |
| |
| return new SrcfileData( |
| (this.lastSrcfile == fileName) ? this.lastSrcfilePath : path.toPortableString(), |
| fileName, getTimestamp(wsu, m) ); |
| } |
| else { |
| return new SrcfileData(null, fileName, RDbg.getTimestamp(su, m)); |
| } |
| } |
| return null; |
| } |
| |
| private void prepareSrcfile(final String srcfile, final IPath path, final ProgressMonitor m) { |
| try { |
| if (srcfile == null || path == null) { |
| return; |
| } |
| final String statetPath= path.toPortableString(); |
| if (!srcfile.equals(this.lastSrcfile) || !statetPath.equals(this.lastSrcfilePath) ) { |
| doPrepareSrcfile(srcfile, statetPath, m); |
| } |
| this.lastSrcfile= srcfile; |
| this.lastSrcfilePath= statetPath; |
| } |
| catch (final Exception e) { |
| this.lastSrcfile= null; |
| this.lastSrcfilePath= null; |
| RConsoleCorePlugin.log(new Status(IStatus.ERROR, RConsoleCorePlugin.BUNDLE_ID, 0, |
| "An error occurred when preparing srcfile information in R." , e )); |
| } |
| } |
| |
| protected void doPrepareSrcfile(final String srcfile, final String statetPath, |
| final ProgressMonitor m) throws Exception { |
| } |
| |
| } |