blob: 4cdc9a47f7e5a1978939ab7474b7959951407fd3 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2020 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.model;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.core.model.IStepFilters;
import org.eclipse.debug.core.model.IThread;
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.NullProgressMonitor;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.internal.r.debug.core.RDebugCorePlugin;
import org.eclipse.statet.internal.r.debug.core.breakpoints.RControllerBreakpointAdapter;
import org.eclipse.statet.internal.r.debug.core.eval.ExpressionValidator;
import org.eclipse.statet.nico.core.NicoCore;
import org.eclipse.statet.nico.core.runtime.IRemoteEngineController;
import org.eclipse.statet.nico.core.runtime.ToolController.IToolStatusListener;
import org.eclipse.statet.nico.core.runtime.ToolStatus;
import org.eclipse.statet.r.console.core.RProcess;
import org.eclipse.statet.r.core.data.RValueFormatter;
import org.eclipse.statet.r.core.data.RValueValidator;
import org.eclipse.statet.r.debug.core.IRDebugTarget;
import org.eclipse.statet.r.debug.core.breakpoints.IRBreakpoint;
import org.eclipse.statet.r.nico.AbstractRDbgController;
import org.eclipse.statet.rj.server.dbg.DbgFilterState;
import org.eclipse.statet.rj.server.dbg.DbgRequest;
@NonNullByDefault
public class RDebugTarget extends RDebugElement implements IRDebugTarget, IStepFilters,
IToolStatusListener {
protected final RProcess process;
private final AbstractRDbgController controller;
private final RControllerBreakpointAdapter breakpointAdapter;
protected final List<IThread> threads= new ArrayList<>(1);
private @Nullable RMainThread mainThread;
protected boolean stepFiltersEnabled;
private @Nullable RValueValidator valueValidator;
private @Nullable RValueFormatter valueFormatter;
private @Nullable ExpressionValidator expressionValidator;
public RDebugTarget(final AbstractRDbgController controller) {
super(null);
this.controller= controller;
this.process= controller.getTool();
this.breakpointAdapter= new RControllerBreakpointAdapter(this, this.controller);
init();
this.controller.initDebug(this.breakpointAdapter);
}
@Override
public final RDebugTarget getDebugTarget() {
return this;
}
@Override
public ILaunch getLaunch() {
return this.process.getLaunch();
}
@Override
public RProcess getProcess() {
return this.process;
}
@Override
public String getName() throws DebugException {
return "R Engine"; //$NON-NLS-1$
}
protected void init() {
getLaunch().addDebugTarget(this);
fireCreationEvent();
this.controller.addToolStatusListener(this);
this.breakpointAdapter.init();
initState();
}
@Override
public void controllerStatusChanged(final ToolStatus oldStatus,
final ToolStatus newStatus, final List<DebugEvent> eventCollection) {
if (newStatus == ToolStatus.TERMINATED) {
this.breakpointAdapter.dispose();
eventCollection.add(new DebugEvent(this, DebugEvent.TERMINATE));
synchronized (this.threads) {
if (this.mainThread != null) {
this.mainThread.setTerminated();
this.mainThread= null;
}
this.threads.clear();
}
}
}
private void initState() {
synchronized (this.threads) {
final RMainThread thread= this.mainThread= new RMainThread(this, this.controller, "R Main Thread");
this.threads.add(thread);
thread.fireCreationEvent();
}
}
@Override
public boolean hasThreads() throws DebugException {
return !this.threads.isEmpty();
}
@Override
public @NonNull IThread[] getThreads() throws DebugException {
synchronized (this.threads) {
return this.threads.toArray(new IThread[this.threads.size()]);
}
}
@Override
public boolean isTerminated() {
return this.process.isTerminated() && !isDisconnected();
}
@Override
public boolean canTerminate() {
return this.process.canTerminate();
}
@Override
public void terminate() throws DebugException {
this.process.terminate();
}
@Override
public boolean isDisconnected() {
return (this.process.isProvidingFeatureSet(IRemoteEngineController.FEATURE_SET_ID)
&& ((IRemoteEngineController) this.controller).isDisconnected());
}
@Override
public boolean canDisconnect() {
return (this.process.isProvidingFeatureSet(IRemoteEngineController.FEATURE_SET_ID)
&& !this.process.isTerminated() );
}
@Override
public void disconnect() throws DebugException {
if (canDisconnect()) {
try {
((IRemoteEngineController) this.controller).disconnect(new NullProgressMonitor());
}
catch (final StatusException e) {
throw new DebugException(new Status(IStatus.ERROR, NicoCore.BUNDLE_ID,
DebugException.TARGET_REQUEST_FAILED, e.getMessage(), e ));
}
}
}
protected void exec(final DbgRequest request) throws DebugException {
try {
this.controller.exec(request);
}
catch (final StatusException e) {
throw new DebugException(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID,
DebugException.REQUEST_FAILED,
"An error occurred when executing debug request in the R engine.",
e ));
}
}
@Override
public boolean isSuspended() {
final RMainThread mainThread= this.mainThread;
return (mainThread != null && mainThread.isSuspended());
}
@Override
public boolean canSuspend() {
final RMainThread mainThread= this.mainThread;
return (mainThread != null && mainThread.canSuspend());
}
@Override
public void suspend() throws DebugException {
final RMainThread mainThread= this.mainThread;
if (mainThread != null) {
mainThread.suspend();
}
}
@Override
public boolean canResume() {
if (isSuspended()) {
return true;
}
synchronized (this.threads) {
for (int i= 0; i < this.threads.size(); i++) {
if (this.threads.get(i).canResume()) {
return true;
}
}
}
return false;
}
@Override
public void resume() throws DebugException {
if (canResume()) {
exec(new DbgRequest.Resume());
}
}
@Override
public boolean supportsBreakpoint(final IBreakpoint breakpoint) {
if (breakpoint instanceof IRBreakpoint) {
return this.breakpointAdapter.supportsBreakpoint((IRBreakpoint) breakpoint);
}
return false;
}
@Override
public void breakpointAdded(final IBreakpoint breakpoint) {
}
@Override
public void breakpointRemoved(final IBreakpoint breakpoint, final IMarkerDelta delta) {
}
@Override
public void breakpointChanged(final IBreakpoint breakpoint, final IMarkerDelta delta) {
}
@Override
public boolean isStepFiltersEnabled() {
return this.stepFiltersEnabled;
}
@Override
public boolean supportsStepFilters() {
return true;
}
@Override
public void setStepFiltersEnabled(final boolean enabled) {
if (this.process.isTerminated()) {
return;
}
this.stepFiltersEnabled= enabled;
try {
this.controller.exec(new DbgFilterState(enabled));
}
catch (final StatusException e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when updating step filters in the R engine.",
e ));
}
}
@Override
public boolean supportsStorageRetrieval() {
return false;
}
@Override
public IMemoryBlock getMemoryBlock(final long startAddress, final long length) throws DebugException {
return null;
}
public synchronized RValueValidator getValueValidator() {
if (this.valueValidator == null) {
this.valueValidator= new RValueValidator();
}
return this.valueValidator;
}
public synchronized RValueFormatter getValueFormatter() {
if (this.valueFormatter == null) {
this.valueFormatter= new RValueFormatter();
}
return this.valueFormatter;
}
public synchronized ExpressionValidator getExpressionValidator() {
if (this.expressionValidator == null) {
this.expressionValidator= new ExpressionValidator();
}
return this.expressionValidator;
}
}