| /*=============================================================================# |
| # Copyright (c) 2010, 2018 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.IRegion; |
| 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.ecommons.text.IMarkerPositionResolver; |
| import org.eclipse.statet.ecommons.ts.core.SystemRunnable; |
| import org.eclipse.statet.ecommons.ts.core.Tool; |
| import org.eclipse.statet.ecommons.ts.core.ToolService; |
| |
| 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 IProgressMonitor monitor) throws CoreException { |
| boolean checkInstalled= (RControllerBreakpointAdapter.this.controller.getHotTasksState() <= 1); |
| |
| synchronized (RControllerBreakpointAdapter.this.updateRunnable) { |
| RControllerBreakpointAdapter.this.updateRunnableScheduled= false; |
| } |
| |
| { // init |
| if (!RControllerBreakpointAdapter.this.initialized) { |
| synchronized (RControllerBreakpointAdapter.this.flagUpdateLock) { |
| RControllerBreakpointAdapter.this.flagUpdateCheck= false; |
| } |
| final IBreakpoint[] breakpoints= RControllerBreakpointAdapter |
| .this.breakpointManager.getBreakpoints(RDebugModel.IDENTIFIER); |
| try { |
| boolean exceptionAvailable= false; |
| synchronized (RControllerBreakpointAdapter.this.stateUpdatesLock) { |
| for (int i= 0; i < 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; |
| } |
| } |
| } |
| } |
| { final FlagTracepointInstallationRequest request= createFlagRequest( |
| (exceptionAvailable) ? Boolean.TRUE : null ); |
| if (request != null) { |
| installFlagTracepoints(request, monitor); |
| } |
| } |
| synchronized (RControllerBreakpointAdapter.this.positionUpdatesLock) { |
| for (int i= 0; i < breakpoints.length; i++) { |
| if (breakpoints[i] instanceof IRLineBreakpoint) { |
| schedulePositionUpdate((IRLineBreakpoint) breakpoints[i]); |
| } |
| } |
| } |
| { final List<Element> positions= getPendingElementPositions(monitor); |
| installElementTracepoints( |
| new ElementTracepointInstallationRequest(positions, true), |
| monitor ); |
| } |
| } |
| finally { |
| RControllerBreakpointAdapter.this.initialized= true; |
| checkInstalled= false; |
| checkUpdates(); |
| } |
| } |
| |
| // 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 FlagTracepointInstallationRequest request= createFlagRequest( |
| (((RControllerBreakpointAdapter.this.exceptionBreakpointData != null) ? RControllerBreakpointAdapter.this.exceptionBreakpointData.isInstalled() : false) |
| != exceptionAvailable ) ? |
| Boolean.valueOf(exceptionAvailable) : null ); |
| if (request != null) { |
| installFlagTracepoints(request, monitor); |
| } |
| } |
| } |
| |
| while (true) { |
| final List<Element> positions= getPendingElementPositions(monitor); |
| if (!positions.isEmpty()) { |
| installElementTracepoints( |
| new ElementTracepointInstallationRequest(positions, false), |
| monitor ); |
| } |
| 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 IProgressMonitor monitor) { |
| 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, |
| monitor ); |
| } |
| } |
| 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 IProgressMonitor monitor) 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 IProgressMonitor monitor) { |
| try { |
| this.controller.exec(request, monitor); |
| } |
| 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 IProgressMonitor monitor) { |
| try { |
| final ISourceUnit su= srcref.getFile(); |
| if (su instanceof IRWorkspaceSourceUnit) { |
| return doGetElementTracepoints(srcfile, srcref, |
| (IRWorkspaceSourceUnit) su, |
| monitor ); |
| } |
| 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 IProgressMonitor monitor) 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; |
| } |
| |
| rSourceUnit.connect(monitor); |
| try { |
| final AbstractDocument document= rSourceUnit.getDocument(monitor); |
| synchronized ((document instanceof ISynchronizable) ? ((ISynchronizable) document).getLockObject() : new Object()) { |
| final IRModelInfo modelInfo= (IRModelInfo) rSourceUnit.getModelInfo( |
| RModel.R_TYPE_ID, IRModelManager.MODEL_FILE, monitor ); |
| 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 IRegion region= elements.get(i).getSourceRange(); |
| lines[j]= document.getLineOfOffset(region.getOffset()) + 1; |
| lines[j+1]= document.getLineOfOffset(region.getOffset()+region.getLength()) + 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(), |
| monitor ) : |
| new RLineBreakpointValidator(rSourceUnit, |
| breakpoint, monitor ); |
| 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(monitor); |
| } |
| return null; |
| } |
| |
| @Override |
| public @Nullable ElementTracepointInstallationRequest prepareFileElementTracepoints(final SrcfileData srcfile, |
| final IRSourceUnit su, |
| final IProgressMonitor monitor) { |
| try { |
| if (su instanceof IRWorkspaceSourceUnit) { |
| final ElementTracepointInstallationRequest request= doPrepareFileElementTracepoints( |
| srcfile, (IRWorkspaceSourceUnit) su, monitor); |
| if (request != null) { |
| installElementTracepoints(request, monitor); |
| |
| 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 IProgressMonitor monitor) { |
| 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, |
| monitor ); |
| } |
| catch (final CoreException e) {} |
| } |
| if (installRequest != null) { |
| installElementTracepoints(request, monitor); |
| } |
| } |
| |
| private @Nullable ElementTracepointInstallationRequest doPrepareFileElementTracepoints( |
| final SrcfileData srcfile, final IRWorkspaceSourceUnit rSourceUnit, |
| final IProgressMonitor monitor) 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; |
| } |
| 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(monitor).get(); |
| final RLineBreakpointValidator validator= new RLineBreakpointValidator( |
| rSourceUnit, breakpoint, monitor ); |
| 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 IProgressMonitor monitor) { |
| try { |
| synchronized (RControllerBreakpointAdapter.this.stateUpdatesMap) { |
| final List<TracepointState> states= getPendingTracepointStates(); |
| RControllerBreakpointAdapter.this.controller.exec( |
| new TracepointStatesUpdate(states, request.getReset()), |
| monitor ); |
| } |
| |
| this.controller.exec(request, monitor); |
| } |
| 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 IProgressMonitor monitor) { |
| 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); |
| } |
| |
| 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, monitor ); |
| if (su != null) { |
| try { |
| if (su instanceof IRWorkspaceSourceUnit) { |
| doGetPendingElementPositions(srcfile, (IRWorkspaceSourceUnit) su, |
| breakpointsToUpdate, map, monitor); |
| continue; |
| } |
| } |
| finally { |
| su.disconnect(monitor); |
| } |
| } |
| } |
| |
| 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 monitor) 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(), |
| monitor ); |
| 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(), |
| monitor ); |
| 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 CoreException 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 IMarker marker= rBreakpoint.getMarker(); |
| if (!marker.getResource().equals(delta.getResource()) |
| || marker.getId() != delta.getId()) { |
| deactivateBreakpoint(delta.getResource(), delta.getId()); |
| } |
| |
| synchronized (this.stateUpdatesLock) { |
| scheduleStateUpdate(rBreakpoint); |
| } |
| |
| // synchronized (fPendingPositionUpdatesLock) { |
| // schedulePositionUpdate(lineBreakpoint); |
| // } |
| } |
| 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 CoreException 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(); |
| } |
| } |
| |
| } |