blob: d0b36d9b31e9ae8996e14df1b6179e4ab41a175f [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 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.breakpoints;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.IBreakpointManagerListener;
import org.eclipse.debug.core.IBreakpointsListener;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.ISynchronizable;
import org.eclipse.osgi.util.NLS;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.Immutable;
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.StatusException;
import org.eclipse.statet.jcommons.text.core.TextRegion;
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.runtime.core.util.StatusUtils;
import org.eclipse.statet.ecommons.text.IMarkerPositionResolver;
import org.eclipse.statet.internal.r.debug.core.RDebugCorePlugin;
import org.eclipse.statet.ltk.core.LTK;
import org.eclipse.statet.ltk.model.core.elements.IModelElement;
import org.eclipse.statet.ltk.model.core.elements.ISourceUnit;
import org.eclipse.statet.nico.core.runtime.Queue;
import org.eclipse.statet.nico.core.runtime.ToolStatus;
import org.eclipse.statet.r.console.core.RDbg;
import org.eclipse.statet.r.console.core.RProcessREnvironment;
import org.eclipse.statet.r.core.RProject;
import org.eclipse.statet.r.core.RProjects;
import org.eclipse.statet.r.core.model.IRLangSourceElement;
import org.eclipse.statet.r.core.model.IRModelInfo;
import org.eclipse.statet.r.core.model.IRModelManager;
import org.eclipse.statet.r.core.model.IRSourceUnit;
import org.eclipse.statet.r.core.model.IRWorkspaceSourceUnit;
import org.eclipse.statet.r.core.model.RModel;
import org.eclipse.statet.r.debug.core.IRDebugTarget;
import org.eclipse.statet.r.debug.core.RDebugModel;
import org.eclipse.statet.r.debug.core.breakpoints.IRBreakpoint;
import org.eclipse.statet.r.debug.core.breakpoints.IRBreakpoint.ITargetData;
import org.eclipse.statet.r.debug.core.breakpoints.IRExceptionBreakpoint;
import org.eclipse.statet.r.debug.core.breakpoints.IRLineBreakpoint;
import org.eclipse.statet.r.debug.core.breakpoints.IRMethodBreakpoint;
import org.eclipse.statet.r.debug.core.breakpoints.RLineBreakpointValidator;
import org.eclipse.statet.r.debug.core.breakpoints.RLineBreakpointValidator.ModelPosition;
import org.eclipse.statet.r.nico.AbstractRDbgController;
import org.eclipse.statet.r.nico.AbstractRDbgController.IRControllerTracepointAdapter;
import org.eclipse.statet.r.nico.IRModelSrcref;
import org.eclipse.statet.r.nico.IRSrcref;
import org.eclipse.statet.r.nico.RSrcref;
import org.eclipse.statet.rj.data.REnvironment;
import org.eclipse.statet.rj.server.dbg.DbgEnablement;
import org.eclipse.statet.rj.server.dbg.ElementTracepointInstallationRequest;
import org.eclipse.statet.rj.server.dbg.ElementTracepoints;
import org.eclipse.statet.rj.server.dbg.FlagTracepointInstallationRequest;
import org.eclipse.statet.rj.server.dbg.SrcfileData;
import org.eclipse.statet.rj.server.dbg.Srcref;
import org.eclipse.statet.rj.server.dbg.Tracepoint;
import org.eclipse.statet.rj.server.dbg.TracepointEvent;
import org.eclipse.statet.rj.server.dbg.TracepointPosition;
import org.eclipse.statet.rj.server.dbg.TracepointState;
import org.eclipse.statet.rj.server.dbg.TracepointStatesUpdate;
@NonNullByDefault
public class RControllerBreakpointAdapter implements IRControllerTracepointAdapter,
IBreakpointManagerListener, IBreakpointsListener {
private static class Position extends TracepointPosition {
private final IRLineBreakpoint breakpoint;
private @Nullable String label;
public Position(final int type, final long id, final int[] exprIndex,
final IRLineBreakpoint breakpoint) {
super(type, id, exprIndex);
this.breakpoint= breakpoint;
}
public IRLineBreakpoint getBreakpoint() {
return this.breakpoint;
}
void setLabel(final @Nullable String label) {
this.label= label;
}
public @Nullable String getElementLabel() {
return this.label;
}
}
private static class Element extends ElementTracepoints {
private final IResource resource;
public Element(final SrcfileData fileInfo, final IResource resource,
final String elementId, final int @Nullable [] elementSrcref) {
super(fileInfo, elementId, elementSrcref);
this.resource= resource;
}
@Override
public List<Position> getPositions() {
return (List<Position>) super.getPositions();
}
public IResource getResource() {
return this.resource;
}
}
private static class UpdateData {
private final IResource resource;
private final String elementId;
public UpdateData(final IResource resource, final String elementId) {
this.resource= resource;
this.elementId= elementId;
}
}
private static class ElementInstallData {
private final IResource resource;
private final String elementId;
private final @Nullable String elementLabel;
public ElementInstallData(final IResource resource, final String elementId,
final @Nullable String elementLabel) {
this.resource= resource;
this.elementId= elementId;
this.elementLabel= elementLabel;
}
public ElementInstallData(final IResource resource, final String elementId,
final Position position) {
this.resource= resource;
this.elementId= elementId;
this.elementLabel= position.getElementLabel();
}
@Override
public int hashCode() {
return this.resource.hashCode() ^ Objects.hashCode(this.elementId);
}
@Override
public boolean equals(final @Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ElementInstallData) {
final ElementInstallData other= (ElementInstallData) obj;
return (this.resource.equals(other.resource)
&& Objects.equals(this.elementId, other.elementId)
&& Objects.equals(this.elementLabel, other.elementLabel) );
}
return false;
}
}
private static class BreakpointTargetData implements IRBreakpoint.ITargetData, Immutable {
private final @Nullable ElementInstallData latest;
private final @Nullable ElementInstallData installed;
public BreakpointTargetData(final @Nullable ElementInstallData latest,
final @Nullable ElementInstallData installed) {
this.latest= latest;
this.installed= installed;
}
@Override
public boolean isInstalled() {
return (this.installed != null);
}
@Override
public <T> @Nullable T getAdapter(final Class<T> adapterType) {
return null;
}
}
private static final IRBreakpoint.ITargetData INSTALLED_DATA= new IRBreakpoint.ITargetData() {
@Override
public boolean isInstalled() {
return true;
}
@Override
public <T> @Nullable T getAdapter(final Class<T> adapter) {
return null;
}
};
private static final IRBreakpoint.ITargetData NOT_INSTALLED_DATA= new IRBreakpoint.ITargetData() {
@Override
public boolean isInstalled() {
return false;
}
@Override
public <T> @Nullable T getAdapter(final Class<T> adapter) {
return null;
}
};
private final IRDebugTarget debugTarget;
private final AbstractRDbgController controller;
private IBreakpointManager breakpointManager;
private boolean initialized;
private final List<IRLineBreakpoint> positionUpdatesBreakpoints= new ArrayList<>();
private final List<UpdateData> positionUpdatesElements= new ArrayList<>();
private final AtomicInteger positionModCounter= new AtomicInteger();
private final Object positionUpdatesLock= this.positionUpdatesBreakpoints;
private final Object targetUpdateLock= new Object();
private final Object flagUpdateLock= new Object();
private boolean flagUpdateCheck;
private @Nullable ITargetData exceptionBreakpointData;
private final List<IRBreakpoint> stateUpdatesBreakpoints= new ArrayList<>();
private final Object stateUpdatesLock= this.stateUpdatesBreakpoints;
private final Map<IResource, List<TracepointState>> stateUpdatesMap= new HashMap<>();
private final List<IResource> currentRequests= new ArrayList<>();
private final SystemRunnable updateRunnable= new SystemRunnable() {
private List<String> knownPackages= new ArrayList<>();
@Override
public String getTypeId() {
return "r/dbg/breakpoint.update";
}
@Override
public String getLabel() {
return "Update Breakpoints";
}
@Override
public boolean canRunIn(final Tool tool) {
return (tool == RControllerBreakpointAdapter.this.controller.getTool());
}
@Override
public boolean changed(final int event, final Tool tool) {
switch (event) {
case REMOVING_FROM:
case MOVING_FROM:
return false;
}
return true;
}
@Override
public void run(final ToolService service, final ProgressMonitor m) throws StatusException {
boolean checkInstalled= (RControllerBreakpointAdapter.this.controller.getHotTasksState() <= 1);
synchronized (RControllerBreakpointAdapter.this.updateRunnable) {
RControllerBreakpointAdapter.this.updateRunnableScheduled= false;
}
{ // init
if (!RControllerBreakpointAdapter.this.initialized) {
m.setWorkRemaining(2);
final ProgressMonitor mInit= m.newSubMonitor(1).setWorkRemaining(4);
synchronized (RControllerBreakpointAdapter.this.flagUpdateLock) {
RControllerBreakpointAdapter.this.flagUpdateCheck= false;
}
final IBreakpoint[] breakpoints= RControllerBreakpointAdapter
.this.breakpointManager.getBreakpoints(RDebugModel.IDENTIFIER);
mInit.addWorked(1);
try {
boolean exceptionAvailable= false;
synchronized (RControllerBreakpointAdapter.this.stateUpdatesLock) {
final ProgressMonitor m0= mInit.newSubMonitor(1);
for (int i= 0; i < breakpoints.length; i++) {
m0.setWorkRemaining(breakpoints.length - i);
if (breakpoints[i] instanceof IRBreakpoint) {
final IRBreakpoint breakpoint= (IRBreakpoint) breakpoints[i];
scheduleStateUpdate((IRBreakpoint) breakpoints[i]);
final String breakpointType= breakpoint.getBreakpointType();
if (breakpointType == RDebugModel.R_EXCEPTION_BREAKPOINT_TYPE_ID) {
exceptionAvailable= true;
}
m0.addWorked(1);
}
}
}
{ final ProgressMonitor m0= mInit.newSubMonitor(1);
final FlagTracepointInstallationRequest request= createFlagRequest(
(exceptionAvailable) ? Boolean.TRUE : null );
if (request != null) {
installFlagTracepoints(request, m0);
}
}
synchronized (RControllerBreakpointAdapter.this.positionUpdatesLock) {
for (int i= 0; i < breakpoints.length; i++) {
if (breakpoints[i] instanceof IRLineBreakpoint) {
schedulePositionUpdate((IRLineBreakpoint) breakpoints[i]);
}
}
mInit.addWorked(1);
}
{ final ProgressMonitor m0= mInit.newSubMonitor(1).setWorkRemaining(2);
final List<Element> positions= getPendingElementPositions(m0.newSubMonitor(1));
installElementTracepoints(
new ElementTracepointInstallationRequest(positions, true),
m0.setWorkRemaining(1) );
}
}
finally {
RControllerBreakpointAdapter.this.initialized= true;
checkInstalled= false;
checkUpdates();
}
}
m.setWorkRemaining(6);
// regular
List<String> newPackages= null;
final List<? extends RProcessREnvironment> environments= RControllerBreakpointAdapter
.this.controller.getWorkspaceData().getRSearchEnvironments();
if (environments != null) {
final List<String> packages= new ArrayList<>(environments.size() - 1);
for (final RProcessREnvironment environment : environments) {
if (environment.getSpecialType() == REnvironment.ENVTYPE_PACKAGE) {
final String pkgName= environment.getElementName().getSegmentName();
packages.add(pkgName);
if (this.knownPackages != null && !this.knownPackages.contains(pkgName)) {
if (newPackages == null) {
newPackages= new ArrayList<>(4);
}
newPackages.add(pkgName);
}
}
}
if (this.knownPackages == null) {
newPackages= packages;
}
this.knownPackages= packages;
}
if (newPackages != null || checkInstalled) {
final IBreakpoint[] breakpoints= RControllerBreakpointAdapter
.this.breakpointManager.getBreakpoints(RDebugModel.IDENTIFIER);
Map<String, RProject> rProjects= null;
boolean exceptionAvailable= false;
for (int i= 0; i < breakpoints.length; i++) {
if (breakpoints[i] instanceof IRBreakpoint) {
final IRBreakpoint breakpoint= (IRBreakpoint) breakpoints[i];
final IMarker marker= breakpoint.getMarker();
if (marker == null) {
continue;
}
final String breakpointType= breakpoint.getBreakpointType();
if (breakpointType == RDebugModel.R_EXCEPTION_BREAKPOINT_TYPE_ID) {
exceptionAvailable= true;
}
else if (breakpointType == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID
|| breakpointType == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID) {
final IRLineBreakpoint lineBreakpoint= (IRLineBreakpoint) breakpoint;
if (checkInstalled) {
final ITargetData targetData= lineBreakpoint.getTargetData(RControllerBreakpointAdapter.this.debugTarget);
if (targetData != null && targetData.isInstalled()) {
schedulePositionUpdate(lineBreakpoint);
continue;
}
}
final IResource resource= marker.getResource();
if (RControllerBreakpointAdapter.this.currentRequests.contains(resource)) {
schedulePositionUpdate(lineBreakpoint);
continue;
}
if (newPackages != null) {
final IProject project= resource.getProject();
if (rProjects == null) {
rProjects= new HashMap<>();
}
RProject rProject= rProjects.get(project.getName());
if (rProject == null) {
rProject= RProjects.getRProject(project);
if (rProject == null) {
continue; // ?
}
rProjects.put(project.getName(), rProject);
}
final String pkgName= rProject.getPkgName();
if (newPackages.contains(pkgName)) {
schedulePositionUpdate(lineBreakpoint);
continue;
}
}
}
}
}
{ final ProgressMonitor m0= m.newSubMonitor(1);
final FlagTracepointInstallationRequest request= createFlagRequest(
(((RControllerBreakpointAdapter.this.exceptionBreakpointData != null) ? RControllerBreakpointAdapter.this.exceptionBreakpointData.isInstalled() : false)
!= exceptionAvailable ) ?
Boolean.valueOf(exceptionAvailable) : null );
if (request != null) {
installFlagTracepoints(request, m0);
}
}
}
while (true) {
final List<Element> positions= getPendingElementPositions(m.newSubMonitor(2));
if (!positions.isEmpty()) {
m.setWorkRemaining(2);
installElementTracepoints(
new ElementTracepointInstallationRequest(positions, false),
m.newSubMonitor(1) );
}
else {
break;
}
}
}
}
};
private boolean updateRunnableScheduled;
public RControllerBreakpointAdapter(final IRDebugTarget target,
final AbstractRDbgController controller) {
this.debugTarget= target;
this.controller= controller;
this.breakpointManager= DebugPlugin.getDefault().getBreakpointManager();
this.breakpointManager.addBreakpointManagerListener(this);
this.breakpointManager.addBreakpointListener(this);
breakpointManagerEnablementChanged(this.breakpointManager.isEnabled());
}
public void init() {
final Queue queue= this.controller.getTool().getQueue();
queue.addHot(this.updateRunnable);
queue.addOnIdle(this.updateRunnable, 5500);
}
public boolean supportsBreakpoint(final IRBreakpoint breakpoint) {
final String breakpointType= breakpoint.getBreakpointType();
switch (breakpointType) {
case RDebugModel.R_LINE_BREAKPOINT_TYPE_ID:
case RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID:
case RDebugModel.R_EXCEPTION_BREAKPOINT_TYPE_ID:
return true;
default:
return false;
}
}
private @Nullable IRBreakpoint getRBreakpoint(final TracepointEvent event) {
if (event.getFilePath() == null) {
return null;
}
try {
final IWorkspace workspace= ResourcesPlugin.getWorkspace();
final IMarker marker;
if (event.getType() == Tracepoint.TYPE_EB) {
final IResource resource= workspace.getRoot();
final IMarker[] markers= resource.findMarkers(RExceptionBreakpoint.R_EXCEPTION_BREAKPOINT_MARKER_TYPE, false, IResource.DEPTH_ZERO);
marker= (markers.length > 0) ? markers[0] : null;
}
else {
final IResource resource= workspace.getRoot().getFile(new Path(event.getFilePath()));
marker= resource.getMarker(event.getId());
}
final IBreakpoint b= this.breakpointManager.getBreakpoint(marker);
return (b instanceof IRBreakpoint) ? (IRBreakpoint) b : null;
}
catch (final CoreException e) {
return null;
}
}
@Override
public void handle(final TracepointEvent event) {
switch (event.getKind()) {
case TracepointEvent.KIND_INSTALLED:
case TracepointEvent.KIND_UNINSTALLED:
if (event.getType() == Tracepoint.TYPE_EB) {
updateFlagInstallData(event);
}
else {
updateElementInstallData(event);
}
}
}
/** Call in R thread */
@Override
public boolean matchScriptBreakpoint(final IRModelSrcref srcref,
final ProgressMonitor m) {
try {
if (srcref instanceof IAdaptable) {
final IMarker marker= ((IAdaptable) srcref).getAdapter(IMarker.class);
final ISourceUnit su= srcref.getFile();
if (marker != null && su instanceof IRWorkspaceSourceUnit
&& marker.getResource() == su.getResource()) {
return doMatchScriptBreakpoint(srcref,
(IRWorkspaceSourceUnit) su, marker,
m );
}
}
return false;
}
catch (final Exception e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when looking for script breakpoints.", e));
return false;
}
}
private boolean doMatchScriptBreakpoint(final IRModelSrcref srcref,
final IRWorkspaceSourceUnit rSourceUnit, final IMarker marker,
final ProgressMonitor m) throws CoreException {
final List<IRLineBreakpoint> breakpoints= RDebugModel.getLineBreakpoints(
(IFile) rSourceUnit.getResource() );
if (breakpoints.isEmpty()) {
return false;
}
final IMarkerPositionResolver resolver= rSourceUnit.getMarkerPositionResolver();
synchronized ((resolver != null && resolver.getDocument() instanceof ISynchronizable) ? ((ISynchronizable) resolver.getDocument()).getLockObject() : new Object()) {
final int lineNumber= getLineNumber(marker, resolver);
if (lineNumber < 0) {
return false;
}
for (final IRLineBreakpoint breakpoint : breakpoints) {
try {
if (isScriptBreakpoint(breakpoint)
&& ((resolver != null) ? resolver.getLine(breakpoint.getMarker()) : breakpoint.getLineNumber()) == lineNumber ) {
return breakpoint.isEnabled();
}
}
catch (final CoreException e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when checking breakpoints.", e));
}
}
return false;
}
}
private @Nullable FlagTracepointInstallationRequest createFlagRequest(final @Nullable Boolean exception) {
int count= 0;
if (exception != null) {
count++;
}
if (count == 0) {
return null;
}
final byte[] types= new byte[count];
final int[] flags= new int[count];
int i= 0;
if (exception != null) {
types[i]= Tracepoint.TYPE_EB;
flags[i]= (exception.booleanValue()) ? TracepointState.FLAG_ENABLED : 0;
i++;
}
return new FlagTracepointInstallationRequest(types, flags);
}
/** Call in R thread */
private void installFlagTracepoints(final FlagTracepointInstallationRequest request,
final ProgressMonitor m) {
try {
this.controller.exec(request, m);
}
catch (final Exception e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when updating breakpoints in R." , e ));
}
finally {
checkUpdates();
}
}
/** Call in R thread */
@Override
public @Nullable ElementTracepointInstallationRequest getElementTracepoints(final SrcfileData srcfile,
final IRModelSrcref srcref,
final ProgressMonitor m) {
try {
final ISourceUnit su= srcref.getFile();
if (su instanceof IRWorkspaceSourceUnit) {
return doGetElementTracepoints(srcfile, srcref, (IRWorkspaceSourceUnit) su, m);
}
return null;
}
catch (final Exception e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when looking for script list.", e));
return null;
}
}
private @Nullable ElementTracepointInstallationRequest doGetElementTracepoints(final SrcfileData srcfile,
final IRModelSrcref srcref,
final IRWorkspaceSourceUnit rSourceUnit,
final ProgressMonitor m) throws CoreException, BadLocationException {
if (rSourceUnit.getResource().getType() != IResource.FILE
|| !rSourceUnit.getResource().exists()) {
return null;
}
List<? extends IRLangSourceElement> elements= srcref.getElements();
if (elements.isEmpty()) {
return null;
}
final int modCounter= this.positionModCounter.get();
final List<IRLineBreakpoint> breakpoints= RDebugModel.getLineBreakpoints(
(IFile) rSourceUnit.getResource() );
if (breakpoints.isEmpty()) {
return null;
}
final IProgressMonitor eMonitor= StatusUtils.convert(m);
rSourceUnit.connect(eMonitor);
try {
final AbstractDocument document= rSourceUnit.getDocument(eMonitor);
synchronized ((document instanceof ISynchronizable) ? ((ISynchronizable) document).getLockObject() : new Object()) {
final IRModelInfo modelInfo= (IRModelInfo) rSourceUnit.getModelInfo(
RModel.R_TYPE_ID, IRModelManager.MODEL_FILE, eMonitor );
if (elements.get(0).getSourceParent() != modelInfo.getSourceElement()) {
final List<? extends IRLangSourceElement> orgElements= elements;
elements= modelInfo.getSourceElement().getSourceChildren(new IModelElement.Filter() {
@Override
public boolean include(final IModelElement element) {
return orgElements.contains(element);
// return map.containsKey(element.getId());
}
});
}
if (elements.isEmpty()) {
return null;
}
final IMarkerPositionResolver resolver= rSourceUnit.getMarkerPositionResolver();
final int[] lines= new int[elements.size()*2];
for (int i= 0, j= 0; i < elements.size(); i++, j+=2) {
final TextRegion region= elements.get(i).getSourceRange();
lines[j]= document.getLineOfOffset(region.getStartOffset()) + 1;
lines[j+1]= document.getLineOfOffset(region.getEndOffset()) + 1;
}
HashMap<String, @Nullable Element> map= null;
List<String> cleanup= null;
for (final IRLineBreakpoint breakpoint : breakpoints) {
try {
if (isElementBreakpoint(breakpoint)) {
final IMarker marker= breakpoint.getMarker();
final int breakpointLineNumber= (resolver != null) ?
resolver.getLine(breakpoint.getMarker()) :
breakpoint.getLineNumber();
for (int j= 0; j < lines.length; j+=2) {
if (lines[j] <= breakpointLineNumber && lines[j+1] >= breakpointLineNumber) {
final RLineBreakpointValidator validator= (resolver != null) ?
new RLineBreakpointValidator(rSourceUnit,
breakpoint.getBreakpointType(),
resolver.getPosition(marker).getOffset(),
eMonitor ) :
new RLineBreakpointValidator(rSourceUnit,
breakpoint, eMonitor );
final String elementId;
if (validator.getType() == breakpoint.getBreakpointType()
&& (elementId= validator.computeElementId()) != null
&& elements.contains(validator.getBaseElement()) ) {
if (map == null) {
map= new HashMap<>(elements.size());
}
final BreakpointTargetData breakpointData= (BreakpointTargetData) breakpoint.getTargetData(this.debugTarget);
if (breakpointData != null && breakpointData.latest != null
&& !elementId.equals(breakpointData.latest.elementId) ) {
if (cleanup == null) {
cleanup= new ArrayList<>();
}
if (!cleanup.contains(breakpointData.latest.elementId)) {
cleanup.add(breakpointData.latest.elementId);
}
}
addBreakpoint(map, srcfile, rSourceUnit.getResource(),
elementId, breakpoint, validator, modCounter );
}
break;
}
}
}
}
catch (final CoreException e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when checking breakpoint.", e));
}
}
if (cleanup != null) {
cleanup.removeAll(map.keySet());
if (!cleanup.isEmpty()) {
synchronized (this.positionUpdatesLock) {
for (int i= 0; i < cleanup.size(); i++) {
this.positionUpdatesElements.add(
new UpdateData(rSourceUnit.getResource(), cleanup.get(i)));
}
}
}
}
if (map != null) {
final ArrayList<Element> list= new ArrayList<>(map.size());
addElements(list, map, false);
if (!list.isEmpty()) {
return new ElementTracepointInstallationRequest(list, false);
}
}
}
}
finally {
rSourceUnit.disconnect(eMonitor);
}
return null;
}
@Override
public @Nullable ElementTracepointInstallationRequest prepareFileElementTracepoints(final SrcfileData srcfile,
final IRSourceUnit su,
final ProgressMonitor m) {
try {
if (su instanceof IRWorkspaceSourceUnit) {
final ElementTracepointInstallationRequest request= doPrepareFileElementTracepoints(
srcfile, (IRWorkspaceSourceUnit) su, m );
if (request != null) {
installElementTracepoints(request, m);
for (final ElementTracepoints positions : request.getRequests()) {
if (positions instanceof Element) {
this.currentRequests.add(((Element) positions).getResource());
}
}
return request;
}
}
return null;
}
catch (final Exception e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when looking for line breakpoints.", e));
return null;
}
}
@Override
public void finishFileElementTracepoints(final SrcfileData srcfile, final IRSourceUnit su,
final ElementTracepointInstallationRequest request,
final ProgressMonitor m) {
for (final ElementTracepoints positions : request.getRequests()) {
if (positions instanceof Element) {
this.currentRequests.remove(((Element) positions).getResource());
}
}
ElementTracepointInstallationRequest installRequest= request;
if (srcfile != null) {
try {
installRequest= doPrepareFileElementTracepoints(srcfile, (IRWorkspaceSourceUnit) su,
m );
}
catch (final CoreException e) {}
}
if (installRequest != null) {
installElementTracepoints(request, m);
}
}
private @Nullable ElementTracepointInstallationRequest doPrepareFileElementTracepoints(
final SrcfileData srcfile, final IRWorkspaceSourceUnit rSourceUnit,
final ProgressMonitor m) throws CoreException {
if (rSourceUnit.getResource().getType() != IResource.FILE
|| !rSourceUnit.getResource().exists()) {
return null;
}
final int modeCounter= this.positionModCounter.get();
final List<IRLineBreakpoint> breakpoints= RDebugModel.getLineBreakpoints(
(IFile) rSourceUnit.getResource() );
if (breakpoints.isEmpty()) {
return null;
}
final IProgressMonitor eMonitor= StatusUtils.convert(m);
Map<String, @Nullable Element> map= null;
for (final IRLineBreakpoint breakpoint : breakpoints) {
try {
final String breakpointType= breakpoint.getBreakpointType();
if (breakpointType == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID
|| breakpointType == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID) {
rSourceUnit.getDocument(eMonitor).get();
final RLineBreakpointValidator validator= new RLineBreakpointValidator(
rSourceUnit, breakpoint, eMonitor );
final String elementId;
if (validator.getType() == breakpoint.getBreakpointType()
&& (elementId= validator.computeElementId()) != null ) {
// || (((elementId= validator.computeElementId()) != null) ?
// !elementId.equals(breakpoint.getElementId()) :
// null != breakpoint.getElementId() )) {
if (map == null) {
map= new HashMap<>(breakpoints.size());
}
addBreakpoint(map, srcfile, rSourceUnit.getResource(), elementId,
breakpoint, validator, modeCounter );
}
}
}
catch (final CoreException e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when checking breakpoint.", e));
}
}
if (map != null) {
final ArrayList<Element> list= new ArrayList<>(map.size());
addElements(list, map, false);
if (!list.isEmpty()) {
return new ElementTracepointInstallationRequest(list, false);
}
}
return null;
}
@Override
public void installElementTracepoints(final ElementTracepointInstallationRequest request,
final ProgressMonitor m) {
try {
synchronized (RControllerBreakpointAdapter.this.stateUpdatesMap) {
final List<TracepointState> states= getPendingTracepointStates();
RControllerBreakpointAdapter.this.controller.exec(
new TracepointStatesUpdate(states, request.getReset()),
m );
}
this.controller.exec(request, m);
}
catch (final Exception e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when updating breakpoints in R." , e ));
}
finally {
checkUpdates();
}
}
/** Call in R thread */
private List<Element> getPendingElementPositions(final ProgressMonitor m) {
final IRBreakpoint[] breakpointsToUpdate;
final UpdateData[] elementsToUpdate;
synchronized (this.positionUpdatesLock) {
if (this.positionUpdatesBreakpoints.isEmpty() && this.positionUpdatesElements.isEmpty()) {
return ImCollections.emptyList();
}
breakpointsToUpdate= this.positionUpdatesBreakpoints.toArray(new @NonNull IRBreakpoint[this.positionUpdatesBreakpoints.size()]);
this.positionUpdatesBreakpoints.clear();
elementsToUpdate= this.positionUpdatesElements.toArray(new @NonNull UpdateData[this.positionUpdatesElements.size()]);
this.positionUpdatesElements.clear();
}
final Map<IResource, Map<String, @Nullable Element>> resourceMap= new HashMap<>();
// by resources
for (int i= 0; i < breakpointsToUpdate.length; i++) {
final IRBreakpoint rBreakpoint= breakpointsToUpdate[i];
final String breakpointType= rBreakpoint.getBreakpointType();
if (breakpointType == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID
|| breakpointType == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID) {
try {
final BreakpointTargetData breakpointData= (BreakpointTargetData) rBreakpoint.getTargetData(this.debugTarget);
if (breakpointData != null && breakpointData.latest != null) {
Map<String, @Nullable Element> map= resourceMap.get(breakpointData.latest.resource);
if (map == null) {
map= new HashMap<>();
resourceMap.put(breakpointData.latest.resource, map);
}
map.put(breakpointData.latest.elementId, null);
}
if (rBreakpoint.isRegistered()) {
final IResource resource= rBreakpoint.getMarker().getResource();
if (!resourceMap.containsKey(resource)) {
resourceMap.put(resource, new HashMap<String, @Nullable Element>());
}
}
else if (breakpointData != null) {
rBreakpoint.unregisterTarget(this.debugTarget);
}
}
catch (final CoreException e) {
logPrepareError(e, rBreakpoint);
}
}
}
for (int i= 0; i < elementsToUpdate.length; i++) {
final UpdateData updateData= elementsToUpdate[i];
Map<String, @Nullable Element> map= resourceMap.get(updateData.resource);
if (map == null) {
map= new HashMap<>();
resourceMap.put(updateData.resource, map);
}
map.put(updateData.elementId, null);
}
final IProgressMonitor eMonitor= StatusUtils.convert(m);
int n= 0;
for (final Entry<IResource, Map<String, @Nullable Element>> resourceEntry : resourceMap.entrySet()) {
final IResource resource= resourceEntry.getKey();
final Map<String, @Nullable Element> map= resourceEntry.getValue();
try {
final SrcfileData srcfile= RDbg.createRJSrcfileData(resource);
if (resource.exists() && resource.getType() == IResource.FILE) {
final ISourceUnit su= LTK.getSourceUnitManager().getSourceUnit(
LTK.PERSISTENCE_CONTEXT, resource, null, true, eMonitor );
if (su != null) {
try {
if (su instanceof IRWorkspaceSourceUnit) {
doGetPendingElementPositions(srcfile, (IRWorkspaceSourceUnit) su,
breakpointsToUpdate, map, eMonitor);
continue;
}
}
finally {
su.disconnect(eMonitor);
}
}
}
for (final String elementId : map.keySet()) {
addClear(map, srcfile, resource, elementId);
}
}
catch (final CoreException e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, -1,
NLS.bind("An error occurred when preparing update of R line breakpoints in ''{0}''.",
resource.getFullPath().toString() ), e ));
}
n += map.size();
}
final List<Element> list= new ArrayList<>(n);
for (final Entry<IResource, Map<String, @Nullable Element>> resourceEntry : resourceMap.entrySet()) {
addElements(list, resourceEntry.getValue(), true);
}
return list;
}
private void doGetPendingElementPositions(final SrcfileData srcfile, final IRWorkspaceSourceUnit rSourceUnit,
final IRBreakpoint[] breakpointsToUpdate, final Map<String, @Nullable Element> map,
final IProgressMonitor eMonitor) throws CoreException {
final int modCounter= this.positionModCounter.get();
final List<IRLineBreakpoint> breakpoints= RDebugModel.getLineBreakpoints(
(IFile) rSourceUnit.getResource() );
for (final IRLineBreakpoint lineBreakpoint : breakpoints) {
if (contains(breakpointsToUpdate, lineBreakpoint)) {
try {
if (lineBreakpoint.isEnabled()) {
final RLineBreakpointValidator validator= new RLineBreakpointValidator(
rSourceUnit,
lineBreakpoint.getBreakpointType(), lineBreakpoint.getCharStart(),
eMonitor );
final String elementId;
if (validator.getType() == lineBreakpoint.getBreakpointType()
&& (elementId= validator.computeElementId()) != null ) {
addBreakpoint(map, srcfile, rSourceUnit.getResource(), elementId,
lineBreakpoint, validator, modCounter );
}
}
}
catch (final CoreException e) {
logPrepareError(e, lineBreakpoint);
}
}
}
for (final IRLineBreakpoint lineBreakpoint : breakpoints) {
if (!contains(breakpointsToUpdate, lineBreakpoint)) {
try {
if (lineBreakpoint.isEnabled()) {
final RLineBreakpointValidator validator= new RLineBreakpointValidator(
rSourceUnit,
lineBreakpoint.getBreakpointType(), lineBreakpoint.getCharStart(),
eMonitor );
final String elementId;
if (validator.getType() == lineBreakpoint.getBreakpointType()
&& (elementId= validator.computeElementId()) != null
&& map.containsKey(elementId) ) {
addBreakpoint(map, srcfile, rSourceUnit.getResource(), elementId,
lineBreakpoint, validator, modCounter );
}
}
}
catch (final CoreException e) {
logPrepareError(e, lineBreakpoint);
}
}
}
for (final Entry<String, @Nullable Element> elementEntry : map.entrySet()) {
if (elementEntry.getValue() == null) {
addClear(map, srcfile, rSourceUnit.getResource(), elementEntry.getKey());
}
}
}
private void logPrepareError(final CoreException e, final IRBreakpoint breakpoint) {
if (breakpoint instanceof IRLineBreakpoint) {
String fileName= null;
final IMarker marker= breakpoint.getMarker();
if (marker != null) {
final IResource resource= marker.getResource();
if (resource != null) {
fileName= resource.getFullPath().toString();
}
}
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, -1,
NLS.bind("An error occurred when preparing update of an R line breakpoint in ''{0}''.",
(fileName != null) ? fileName : "<missing>" ), e ));
}
else {
String exceptionId= null;
try {
exceptionId= ((IRExceptionBreakpoint) breakpoint).getExceptionId();
}
catch (final CoreException ignore) {}
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, -1,
NLS.bind("An error occurred when preparing update of an R error breakpoint ''{0}''.",
(exceptionId != null) ? exceptionId : "<missing>" ), e ));
}
}
// /** Call in R thread */
// private void report(final ElementTracepointInstallationRequest request,
// final TracepointInstallationReport report) {
// if (request == null) {
// throw new NullPointerException();
// }
// final List<? extends ElementTracepoints> elements= request.getRequests();
// final int l= elements.size();
// int[] results= null;
// if (report != null && report.getInstallationResults().length == l) {
// results= report.getInstallationResults();
// }
//
// if (results == null) {
// return; // ?
// }
//
// ArrayList<Element> cleanup= null;
// List<IRLineBreakpoint> updated= null;
//
// for (int i= 0; i < l; i++) {
// if (results[i] == TracepointInstallationReport.FOUND_UNCHANGED
// || !(elements.get(i) instanceof Element)) {
// continue;
// }
//
// final Element current= (Element) elements.get(i);
// final boolean installed= (results[i] == TracepointInstallationReport.FOUND_SET);
// for (final TracepointPosition position : current.getPositions()) {
// if (!(position instanceof Position)) {
// continue;
// }
// final IRLineBreakpoint breakpoint= ((Position) position).getBreakpoint();
// try {
// if (breakpoint == null || !breakpoint.isRegistered()) {
// continue;
// }
// final IMarker marker= breakpoint.getMarker();
// if (marker == null || marker.getId() != position.getId()) {
// continue;
// }
// final ElementTargetData newData= new ElementTargetData((installed) ? current : null);
// final ElementTargetData oldData= (ElementTargetData) breakpoint.registerTarget(this.debugTarget, newData);
//// if (oldData != null && oldData.isInstalled()
//// && oldData.getElementId().equals(current.getElementId())) {
//// if (cleanup == null) {
//// cleanup= new ArrayList<>(l-i);
//// }
//// if (!contains(cleanup, oldData.installed)) {
//// cleanup.add(oldData.installed);
//// }
//// }
// if (updated == null) {
// updated= new ArrayList<>(l - i);
// }
// updated.add(breakpoint);
// }
// catch (final CoreException e) {} // only isRegistered
// }
// }
//
// if (cleanup != null) {
// for (int i= 0; i < cleanup.size(); i++) {
// final Element current= cleanup.get(i);
// for (final TracepointPosition position : current.getPositions()) {
// if (!(position instanceof Position)) {
// continue;
// }
// final IRBreakpoint breakpoint= ((Position) position).getBreakpoint();
// try {
// if (breakpoint == null || !breakpoint.isRegistered()) {
// continue;
// }
// final ElementTargetData data= (ElementTargetData) breakpoint.getTargetData(this.debugTarget);
// if (data != null && data.isInstalled()) {
// breakpoint.registerTarget(this.debugTarget, new ElementTargetData(null));
// }
// }
// catch (final CoreException e) {} // only isRegistered
// }
// }
// }
//
// if (updated != null) {
// synchronized (this.stateUpdatesLock) {
// for (int i= 0; i < updated.size(); i++) {
// scheduleStateUpdate(updated.get(i));
// }
// }
// }
// }
private void updateFlagInstallData(final TracepointEvent event) {
final IRBreakpoint.ITargetData newData;
switch (event.getKind()) {
case TracepointEvent.KIND_INSTALLED:
newData= INSTALLED_DATA;
break;
case TracepointEvent.KIND_UNINSTALLED:
newData= NOT_INSTALLED_DATA;
break;
default:
return;
}
synchronized (this.flagUpdateLock) {
this.exceptionBreakpointData= newData;
}
final IRBreakpoint breakpoint= getRBreakpoint(event);
if (breakpoint == null) {
return;
}
breakpoint.registerTarget(this.debugTarget, newData);
}
private void updateElementInstallData(final TracepointEvent event) {
final IRBreakpoint breakpoint= getRBreakpoint(event);
if (breakpoint == null) {
return;
}
final IMarker marker= breakpoint.getMarker();
if (marker == null) {
return;
}
final ElementInstallData installData;
switch (event.getKind()) {
case TracepointEvent.KIND_INSTALLED:
installData= new ElementInstallData(marker.getResource(),
event.getElementId(), event.getLabel() );
break;
case TracepointEvent.KIND_UNINSTALLED:
installData= null;
break;
default:
return;
}
synchronized (this.targetUpdateLock) {
final BreakpointTargetData oldData= (BreakpointTargetData) breakpoint.getTargetData(this.debugTarget);
breakpoint.registerTarget(this.debugTarget,
new BreakpointTargetData(
(oldData != null) ? oldData.latest : null,
installData ));
}
}
private void addBreakpoint(final Map<String, Element> map,
final SrcfileData srcfile, final IResource resource, final String elementId,
final IRLineBreakpoint breakpoint, final RLineBreakpointValidator validator,
final int modCounter) throws CoreException {
synchronized (this.positionUpdatesLock) {
if (this.positionModCounter.get() == modCounter) {
this.positionUpdatesBreakpoints.remove(breakpoint);
}
}
Element elementPositions= map.get(elementId);
if (elementPositions == null) {
final IRSrcref elementSrcref= validator.computeElementSrcref();
elementPositions= new Element(srcfile, resource, elementId,
(elementSrcref != null) ? RDbg.createRJSrcref(elementSrcref) : null );
map.put(elementId, elementPositions);
}
final IMarker marker= breakpoint.getMarker();
if (marker == null) {
return;
}
int[] rExpressionIndex= validator.computeRExpressionIndex();
if (rExpressionIndex == null) {
rExpressionIndex= new int[0];
}
final int type;
if (breakpoint.getBreakpointType() == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID) {
if (breakpoint.getElementType() == IRLineBreakpoint.R_TOPLEVEL_COMMAND_ELEMENT_TYPE) {
type= Tracepoint.TYPE_TB;
}
else {
type= Tracepoint.TYPE_LB;
}
}
else if (breakpoint.getBreakpointType() == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID) {
type= Tracepoint.TYPE_FB;
}
else {
return;
}
final Position position= new Position(type, marker.getId(), rExpressionIndex,
breakpoint );
if (!elementPositions.getPositions().contains(position)) {
{ final int[] elementSrcref= elementPositions.getElementSrcref();
if (elementSrcref != null) {
final RSrcref[] rExpressionSrcrefs= validator.computeRExpressionSrcrefs();
if (rExpressionSrcrefs != null) {
final int [] @Nullable [] srcrefs= position.getSrcrefs();
for (int i= 0; i < rExpressionSrcrefs.length; i++) {
if (rExpressionSrcrefs[i] != null) {
srcrefs[i]= Srcref.substract(
RDbg.createRJSrcref(rExpressionSrcrefs[i]),
elementSrcref );
}
}
}
}
}
{ String label= validator.computeSubLabel();
if (label == null) {
label= validator.computeElementLabel();
}
position.setLabel(label);
}
elementPositions.getPositions().add(position);
}
final ElementInstallData latestData= new ElementInstallData(marker.getResource(),
elementId, position );
synchronized (this.targetUpdateLock) {
final BreakpointTargetData oldData= (BreakpointTargetData) breakpoint.getTargetData(this.debugTarget);
if (oldData != null && Objects.equals(latestData, oldData.latest)) {
return;
}
breakpoint.registerTarget(this.debugTarget,
new BreakpointTargetData(
latestData,
(oldData != null) ? oldData.installed : null ));
}
synchronized (this.stateUpdatesLock) {
scheduleStateUpdate(breakpoint);
}
}
private void addClear(final Map<String, Element> map,
final SrcfileData srcfile, final IResource resource, final String elementId)
throws CoreException {
Element elementPositions= map.get(elementId);
if (elementPositions != null) {
return;
}
elementPositions= new Element(srcfile, resource, elementId, null);
map.put(elementId, elementPositions);
}
private void addElements(final List<Element> list, final Map<String, @Nullable Element> map,
final boolean delete) {
final Collection<@Nullable Element> values= map.values();
for (final Element elementPositions : values) {
if (elementPositions == null) {
continue;
}
if (elementPositions.getPositions().size() > 0) {
Collections.sort(elementPositions.getPositions());
}
else if (!delete) {
continue;
}
list.add(elementPositions);
}
}
@Override
public void breakpointManagerEnablementChanged(final boolean enabled) {
try {
this.controller.exec(new DbgEnablement(enabled));
}
catch (final StatusException e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when updating breakpoint enablement in the R engine.",
e ));
}
}
@Override
public void breakpointsAdded(final IBreakpoint[] breakpoints) {
boolean check= false;
try {
for (int i= 0; i < breakpoints.length; i++) {
if (breakpoints[i] instanceof IRBreakpoint) {
final IRBreakpoint rBreakpoint= (IRBreakpoint) breakpoints[i];
try {
check= true;
final String breakpointType= rBreakpoint.getBreakpointType();
if (breakpointType == RDebugModel.R_EXCEPTION_BREAKPOINT_TYPE_ID) {
final IRBreakpoint.ITargetData data;
synchronized (this.flagUpdateLock) {
data= this.exceptionBreakpointData;
if (data != INSTALLED_DATA) {
scheduleExceptionUpdate();
}
}
if (data == INSTALLED_DATA) {
rBreakpoint.registerTarget(this.debugTarget, data);
}
}
else if (breakpointType == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID
|| breakpointType == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID) {
synchronized (this.positionUpdatesLock) {
this.positionModCounter.incrementAndGet();
schedulePositionUpdate((IRLineBreakpoint) rBreakpoint);
}
}
}
catch (final Exception e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when handling creation of an R breakpoint.", e ));
}
}
}
}
finally {
if (check) {
checkUpdates();
}
}
}
@Override
public void breakpointsRemoved(final IBreakpoint[] breakpoints, final @Nullable IMarkerDelta[] deltas) {
boolean check= false;
try {
for (int i= 0; i < breakpoints.length; i++) {
if (breakpoints[i] instanceof IRBreakpoint) {
final IRBreakpoint rBreakpoint= (IRBreakpoint) breakpoints[i];
try {
check= true;
final String breakpointType= rBreakpoint.getBreakpointType();
if (breakpointType == RDebugModel.R_EXCEPTION_BREAKPOINT_TYPE_ID) {
final IRBreakpoint.ITargetData data;
synchronized (this.flagUpdateLock) {
data= this.exceptionBreakpointData;
if (data == INSTALLED_DATA) {
scheduleExceptionUpdate();
}
}
}
else if (breakpointType == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID
|| breakpointType == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID) {
final IMarkerDelta delta= deltas[i];
final IMarker marker= rBreakpoint.getMarker();
IResource resource= null;
if (delta != null) {
resource= delta.getResource();
deactivateBreakpoint(resource, delta.getId());
}
else if (marker != null) {
resource= marker.getResource();
deactivateBreakpoint(resource, marker.getId());
}
String elementId= null;
if (delta != null) {
elementId= delta.getAttribute(RLineBreakpoint.ELEMENT_ID_MARKER_ATTR,
null );
}
else if (marker != null) {
elementId= marker.getAttribute(RLineBreakpoint.ELEMENT_ID_MARKER_ATTR,
null );
}
synchronized (this.positionUpdatesLock) {
this.positionModCounter.incrementAndGet();
schedulePositionUpdate((IRLineBreakpoint) rBreakpoint);
if (elementId != null) {
this.positionUpdatesElements.add(new UpdateData(resource, elementId));
}
}
}
}
catch (final Exception e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when handling deletion of an R breakpoint.", e ));
}
}
}
}
finally {
if (check) {
checkUpdates();
}
}
}
@Override
public void breakpointsChanged(final IBreakpoint[] breakpoints, final @Nullable IMarkerDelta[] deltas) {
boolean check= false;
try {
for (int i= 0; i < breakpoints.length; i++) {
if (breakpoints[i] instanceof IRBreakpoint) {
final IRBreakpoint rBreakpoint= (IRBreakpoint) breakpoints[i];
try {
final IMarkerDelta delta= deltas[i];
if (delta == null) {
continue;
}
check= true;
final String breakpointType= rBreakpoint.getBreakpointType();
final IMarker marker= rBreakpoint.getMarker();
if (!marker.getResource().equals(delta.getResource())
|| marker.getId() != delta.getId()) {
deactivateBreakpoint(delta.getResource(), delta.getId());
}
synchronized (this.stateUpdatesLock) {
scheduleStateUpdate(rBreakpoint);
}
if (breakpointType == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID
|| breakpointType == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID) {
// see Bug 545767
synchronized (this.positionUpdatesLock) {
this.positionModCounter.incrementAndGet();
schedulePositionUpdate((IRLineBreakpoint) rBreakpoint);
}
}
}
catch (final Exception e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when handling changes of an R breakpoint.", e ));
}
}
}
}
finally {
if (check) {
checkUpdates();
}
}
}
protected void deactivateBreakpoint(final @Nullable IResource resource, final long id) {
if (resource == null) {
return;
}
synchronized (this.stateUpdatesMap) {
List<TracepointState> list= this.stateUpdatesMap.get(resource);
if (list == null) {
list= new ArrayList<>(8);
this.stateUpdatesMap.put(resource, list);
}
String filePath;
if (list.size() > 0) {
for (int i= 0; i < list.size(); i++) {
final TracepointState state= list.get(i);
if (state.getId() == id) {
if (state.getType() == TracepointState.TYPE_DELETED) {
return;
}
list.remove(i);
}
}
filePath= list.get(0).getFilePath();
}
else {
filePath= resource.getFullPath().toPortableString();
}
list.add(new TracepointState(TracepointState.TYPE_DELETED, filePath, id));
}
}
private List<TracepointState> getPendingTracepointStates() {
// requires lock of stateUpdatesMap
final ImList<IRBreakpoint> breakpoints;
synchronized (this.stateUpdatesLock) {
if (this.stateUpdatesBreakpoints.isEmpty() && this.stateUpdatesMap.isEmpty()) {
return ImCollections.emptyList();
}
breakpoints= ImCollections.toList(this.stateUpdatesBreakpoints);
this.stateUpdatesBreakpoints.clear();
}
ITER_BREAKPOINTS: for (final IRBreakpoint breakpoint : breakpoints) {
try {
final IMarker marker= breakpoint.getMarker();
if (marker == null || !marker.exists()) {
continue ITER_BREAKPOINTS;
}
final TracepointState newState;
if (breakpoint instanceof IRExceptionBreakpoint) {
newState= createState((IRExceptionBreakpoint) breakpoint, marker);
}
else if (breakpoint instanceof IRLineBreakpoint) {
newState= createState((IRLineBreakpoint) breakpoint, marker);
}
else {
newState= null;
}
if (newState == null) {
continue ITER_BREAKPOINTS;
}
List<TracepointState> list= this.stateUpdatesMap.get(marker.getResource());
if (list == null) {
list= new ArrayList<>(8);
this.stateUpdatesMap.put(marker.getResource(), list);
}
for (int i= 0; i < list.size(); i++) {
final TracepointState state= list.get(i);
if (state.getId() == newState.getId()
&& state.getType() == Tracepoint.TYPE_DELETED) {
continue ITER_BREAKPOINTS;
}
}
list.add(newState);
}
catch (final CoreException e) {
logPrepareError(e, breakpoint);
}
}
final List<TracepointState> list= new ArrayList<>();
for (final Iterator<List<TracepointState>> iter= this.stateUpdatesMap.values().iterator();
iter.hasNext(); ) {
final TracepointState[] states;
final List<TracepointState> statesList= iter.next();
states= statesList.toArray(new @NonNull TracepointState[statesList.size()]);
Arrays.sort(states);
boolean delete= true;
for (int i= 0; i < states.length; i++) {
list.add(states[i]);
if (states[i].getType() != Tracepoint.TYPE_DELETED) {
delete= false;
}
}
if (delete) {
iter.remove();
}
else {
statesList.clear();
}
}
return list;
}
private void schedulePositionUpdate(final IRLineBreakpoint lineBreakpoint) {
if (!contains(this.positionUpdatesBreakpoints, lineBreakpoint)) {
this.positionUpdatesBreakpoints.add(lineBreakpoint);
}
}
private void scheduleExceptionUpdate() {
this.flagUpdateCheck= true;
}
private void scheduleStateUpdate(final IRBreakpoint lineBreakpoint) {
if (!contains(this.stateUpdatesBreakpoints, lineBreakpoint)) {
this.stateUpdatesBreakpoints.add(lineBreakpoint);
}
}
private void checkUpdates() {
if (!this.initialized) {
return;
}
synchronized (this.stateUpdatesMap) {
try {
final List<TracepointState> states= getPendingTracepointStates();
if (!states.isEmpty() && this.controller.getStatus() != ToolStatus.TERMINATED) {
this.controller.exec(new TracepointStatesUpdate(states, false));
}
}
catch (final StatusException e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when updating breakpoint states in the R engine.",
e ));
}
}
boolean scheduleInstall= false;
synchronized (this.positionUpdatesLock) {
scheduleInstall|= (!this.positionUpdatesBreakpoints.isEmpty());
}
synchronized (this.flagUpdateLock) {
scheduleInstall|= (this.flagUpdateCheck);
}
if (scheduleInstall) {
synchronized (this.updateRunnable) {
if (!this.updateRunnableScheduled) {
this.updateRunnableScheduled= true;
this.controller.getTool().getQueue().addHot(this.updateRunnable);
}
}
}
}
private boolean contains(final List<?> list, final Object object) {
for (int i= 0; i < list.size(); i++) {
if (list.get(i) == object) {
return true;
}
}
return false;
}
private boolean contains(final Object[] array, final Object object) {
for (int i= 0; i < array.length; i++) {
if (array[i] == object) {
return true;
}
}
return false;
}
private int getLineNumber(final IMarker marker, final @Nullable IMarkerPositionResolver resolver) {
return (resolver != null) ?
resolver.getLine(marker) :
marker.getAttribute(IMarker.LINE_NUMBER, -1);
}
private boolean isScriptBreakpoint(final IRLineBreakpoint breakpoint) throws CoreException {
return (breakpoint.getBreakpointType() == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID
&& breakpoint.getElementType() == IRLineBreakpoint.R_TOPLEVEL_COMMAND_ELEMENT_TYPE );
}
private boolean isElementBreakpoint(final IRLineBreakpoint breakpoint) throws CoreException {
final int elementType;
return ((breakpoint.getBreakpointType() == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID
|| breakpoint.getBreakpointType() == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID )
&& ((elementType= breakpoint.getElementType()) == IRLineBreakpoint.R_COMMON_FUNCTION_ELEMENT_TYPE
|| elementType == IRLineBreakpoint.R_S4_METHOD_ELEMENT_TYPE ));
}
private @Nullable TracepointState createState(final IRLineBreakpoint breakpoint,
final IMarker marker) throws CoreException {
final int type;
int flags= breakpoint.isEnabled() ? TracepointState.FLAG_ENABLED : 0;
if (breakpoint.getBreakpointType() == RDebugModel.R_LINE_BREAKPOINT_TYPE_ID) {
if (breakpoint.getElementType() == IRLineBreakpoint.R_TOPLEVEL_COMMAND_ELEMENT_TYPE) {
type= Tracepoint.TYPE_TB;
}
else {
type= Tracepoint.TYPE_LB;
}
}
else if (breakpoint.getBreakpointType() == RDebugModel.R_METHOD_BREAKPOINT_TYPE_ID) {
type= Tracepoint.TYPE_FB;
final IRMethodBreakpoint methodBreakpoint= (IRMethodBreakpoint) breakpoint;
if (methodBreakpoint.isEntry()) {
flags |= TracepointState.FLAG_MB_ENTRY;
}
if (methodBreakpoint.isExit()) {
flags |= TracepointState.FLAG_MB_EXIT;
}
}
else {
return null;
}
final String expr= (breakpoint.isConditionEnabled()) ?
breakpoint.getConditionExpr() : null;
final BreakpointTargetData breakpointData= (BreakpointTargetData) breakpoint.getTargetData(this.debugTarget);
final ModelPosition modelPosition= RLineBreakpointValidator.getModelPosition(breakpoint);
String elementId= null;
String elementLabel= null;
int[] index= null;
if (breakpointData != null) {
if (breakpointData.installed != null) {
elementId= breakpointData.installed.elementId;
elementLabel= breakpointData.installed.elementLabel;
}
if (breakpointData.latest != null
&& elementId == null) {
elementId= breakpointData.latest.elementId;
elementLabel= breakpointData.latest.elementLabel;
}
}
if (modelPosition != null) {
if (elementId != null) {
if (elementId.equals(modelPosition.getElementId())) {
index= modelPosition.getRExpressionIndex();
}
}
else {
elementId= modelPosition.getElementId();
index= modelPosition.getRExpressionIndex();
}
}
if (elementLabel == null) {
elementLabel= breakpoint.getSubLabel();
if (elementLabel == null) {
elementLabel= breakpoint.getElementLabel();
}
}
if (elementId == null) {
return null;
}
if (index == null) {
index= new int[] { -1 };
}
return new TracepointState(type,
marker.getResource().getFullPath().toPortableString(), marker.getId(),
elementId, index, elementLabel, flags, expr);
}
private @Nullable TracepointState createState(final IRExceptionBreakpoint breakpoint,
final IMarker marker) throws CoreException {
final int type;
final int flags= breakpoint.isEnabled() ? TracepointState.FLAG_ENABLED : 0;
if (breakpoint.getBreakpointType() == RDebugModel.R_EXCEPTION_BREAKPOINT_TYPE_ID) {
type= Tracepoint.TYPE_EB;
}
else {
return null;
}
final String expr= null;
final String exceptionId= breakpoint.getExceptionId();
final String elementLabel= null;
return new TracepointState(type, TracepointState.EB_FILEPATH, marker.getId(),
exceptionId, elementLabel, flags, expr);
}
@Override
public @Nullable Object toEclipseData(final TracepointEvent event) {
try {
final IRBreakpoint breakpoint= getRBreakpoint(event);
String label= event.getLabel();
if (event.getType() == Tracepoint.TYPE_EB) {
if (label == null || label.equals("*")) {
label= "error";
}
}
else if (label == null && breakpoint instanceof IRLineBreakpoint) {
final IRLineBreakpoint lineBreakpoint= (IRLineBreakpoint) breakpoint;
final BreakpointTargetData breakpointData= (BreakpointTargetData) breakpoint.getTargetData(this.debugTarget);
if (breakpointData != null && breakpointData.installed != null) {
label= breakpointData.installed.elementLabel;
}
if (label == null) {
try {
label= lineBreakpoint.getSubLabel();
if (label == null) {
label= lineBreakpoint.getElementLabel();
}
}
catch (final CoreException e) {}
}
}
switch (event.getType()) {
case Tracepoint.TYPE_FB:
return new TracepointEventMethodBreakpointStatus(event, label, breakpoint);
case Tracepoint.TYPE_EB:
return new TracepointEventExceptionBreakpointStatus(event, label, breakpoint);
default:
return new TracepointEventBreakpointStatus(event, label, breakpoint);
}
}
catch (final Exception e) {
RDebugCorePlugin.log(new Status(IStatus.ERROR, RDebugCorePlugin.BUNDLE_ID, 0,
"An error occurred when creating breakpoint status.", e ));
return null;
}
}
/** Call in R thread */
public void dispose() {
if (this.breakpointManager != null) {
if (DebugPlugin.getDefault() != null) {
this.breakpointManager.removeBreakpointManagerListener(this);
this.breakpointManager.removeBreakpointListener(this);
final IBreakpoint[] breakpoints= this.breakpointManager.getBreakpoints(
RDebugModel.IDENTIFIER );
for (int i= 0; i < breakpoints.length; i++) {
if (breakpoints[i] instanceof IRBreakpoint) {
final IRBreakpoint breakpoint= (IRBreakpoint) breakpoints[i];
breakpoint.unregisterTarget(this.debugTarget);
}
}
}
this.breakpointManager= null;
}
synchronized (this.stateUpdatesLock) {
this.stateUpdatesBreakpoints.clear();
}
synchronized (this.stateUpdatesMap) {
this.stateUpdatesMap.clear();
}
synchronized (this.positionUpdatesLock) {
this.positionUpdatesBreakpoints.clear();
this.positionUpdatesElements.clear();
}
}
}