/******************************************************************************* * Copyright (c) 2005, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.dltk.internal.debug.core.model; import java.net.URI; import java.util.IdentityHashMap; import java.util.Map; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IBreakpointListener; import org.eclipse.debug.core.IBreakpointManager; import org.eclipse.debug.core.IBreakpointManagerListener; import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.dltk.compiler.util.Util; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.dbgp.IDbgpSession; import org.eclipse.dltk.dbgp.IDbgpSpawnpoint; import org.eclipse.dltk.dbgp.breakpoints.DbgpBreakpointConfig; import org.eclipse.dltk.dbgp.commands.IDbgpBreakpointCommands; import org.eclipse.dltk.dbgp.commands.IDbgpCoreCommands; import org.eclipse.dltk.dbgp.commands.IDbgpSpawnpointCommands; import org.eclipse.dltk.dbgp.exceptions.DbgpException; import org.eclipse.dltk.debug.core.DLTKDebugPlugin; import org.eclipse.dltk.debug.core.DebugOption; import org.eclipse.dltk.debug.core.IDLTKDebugToolkit; import org.eclipse.dltk.debug.core.ScriptDebugManager; import org.eclipse.dltk.debug.core.model.IScriptBreakpoint; import org.eclipse.dltk.debug.core.model.IScriptBreakpointPathMapper; import org.eclipse.dltk.debug.core.model.IScriptDebugTarget; import org.eclipse.dltk.debug.core.model.IScriptExceptionBreakpoint; import org.eclipse.dltk.debug.core.model.IScriptLineBreakpoint; import org.eclipse.dltk.debug.core.model.IScriptMethodEntryBreakpoint; import org.eclipse.dltk.debug.core.model.IScriptSpawnpoint; import org.eclipse.dltk.debug.core.model.IScriptWatchpoint; import org.eclipse.osgi.util.NLS; public class ScriptBreakpointManager implements IBreakpointListener, IBreakpointManagerListener { final IScriptBreakpointPathMapper bpPathMapper; private static final IDbgpSession[] NO_SESSIONS = new IDbgpSession[0]; private IDbgpSession[] sessions; // Utility methods protected static IBreakpointManager getBreakpointManager() { return DebugPlugin.getDefault().getBreakpointManager(); } protected static DbgpBreakpointConfig createBreakpointConfig( IScriptBreakpoint breakpoint) throws CoreException { // Enabled boolean enabled = breakpoint.isEnabled() && getBreakpointManager().isEnabled(); DbgpBreakpointConfig config = new DbgpBreakpointConfig(enabled); // Hit value config.setHitValue(breakpoint.getHitValue()); // Hit condition config.setHitCondition(breakpoint.getHitCondition()); // Expression if (breakpoint.getExpressionState()) { config.setExpression(breakpoint.getExpression()); } if (breakpoint instanceof IScriptLineBreakpoint && !(breakpoint instanceof IScriptMethodEntryBreakpoint)) { IScriptLineBreakpoint lineBreakpoint = (IScriptLineBreakpoint) breakpoint; config.setLineNo(lineBreakpoint.getLineNumber()); } return config; } protected static String makeWatchpointExpression( IScriptWatchpoint watchpoint) throws CoreException { final IDLTKDebugToolkit debugToolkit = ScriptDebugManager.getInstance() .getDebugToolkitByDebugModel(watchpoint.getModelIdentifier()); if (debugToolkit.isAccessWatchpointSupported()) { return watchpoint.getFieldName() + (watchpoint.isAccess() ? '1' : '0') + (watchpoint.isModification() ? '1' : '0'); } else { return watchpoint.getFieldName(); } } // Adding, removing, updating protected void addBreakpoint(final IDbgpSession session, IScriptBreakpoint breakpoint) throws CoreException, DbgpException { final IDbgpCoreCommands commands = session.getCoreCommands(); DbgpBreakpointConfig config = createBreakpointConfig(breakpoint); String id = null; URI bpUri = null; // map the outgoing uri if we're a line breakpoint if (breakpoint instanceof IScriptLineBreakpoint) { IScriptLineBreakpoint bp = (IScriptLineBreakpoint) breakpoint; bpUri = bpPathMapper.map(bp.getResourceURI()); } // Type specific if (breakpoint instanceof IScriptWatchpoint) { IScriptWatchpoint watchpoint = (IScriptWatchpoint) breakpoint; config.setExpression(makeWatchpointExpression(watchpoint)); id = commands.setWatchBreakpoint(bpUri, watchpoint.getLineNumber(), config); } else if (breakpoint instanceof IScriptMethodEntryBreakpoint) { IScriptMethodEntryBreakpoint entryBreakpoint = (IScriptMethodEntryBreakpoint) breakpoint; if (entryBreakpoint.breakOnExit()) { final String exitId = commands.setReturnBreakpoint(bpUri, entryBreakpoint.getMethodName(), config); entryBreakpoint.setExitBreakpointId(exitId); } if (entryBreakpoint.breakOnEntry()) { final String entryId = commands.setCallBreakpoint(bpUri, entryBreakpoint.getMethodName(), config); entryBreakpoint.setEntryBreakpointId(entryId); } } else if (breakpoint instanceof IScriptLineBreakpoint) { IScriptLineBreakpoint lineBreakpoint = (IScriptLineBreakpoint) breakpoint; if (ScriptBreakpointUtils.isConditional(lineBreakpoint)) { id = commands.setConditionalBreakpoint(bpUri, lineBreakpoint.getLineNumber(), config); } else { id = commands.setLineBreakpoint(bpUri, lineBreakpoint.getLineNumber(), config); } } else if (breakpoint instanceof IScriptExceptionBreakpoint) { IScriptExceptionBreakpoint lineBreakpoint = (IScriptExceptionBreakpoint) breakpoint; id = commands.setExceptionBreakpoint(lineBreakpoint.getTypeName(), config); } // Identifier breakpoint.setId(session, id); } private void addSpawnpoint(final IDbgpSession session, IScriptSpawnpoint spawnpoint) throws DbgpException, CoreException { final IDbgpSpawnpointCommands commands = (IDbgpSpawnpointCommands) session .get(IDbgpSpawnpointCommands.class); final IDbgpSpawnpoint p = commands.setSpawnpoint( bpPathMapper.map(spawnpoint.getResourceURI()), spawnpoint.getLineNumber(), spawnpoint.isEnabled()); if (p != null) { spawnpoint.setId(session, p.getId()); } } protected void changeBreakpoint(final IDbgpSession session, IScriptBreakpoint breakpoint) throws DbgpException, CoreException { final IDbgpBreakpointCommands commands = session.getCoreCommands(); URI bpUri = null; // map the outgoing uri if we're a line breakpoint if (breakpoint instanceof IScriptLineBreakpoint) { IScriptLineBreakpoint bp = (IScriptLineBreakpoint) breakpoint; bpUri = bpPathMapper.map(bp.getResourceURI()); } if (breakpoint instanceof IScriptMethodEntryBreakpoint) { DbgpBreakpointConfig config = createBreakpointConfig(breakpoint); IScriptMethodEntryBreakpoint entryBreakpoint = (IScriptMethodEntryBreakpoint) breakpoint; String entryId = entryBreakpoint.getEntryBreakpointId(); if (entryBreakpoint.breakOnEntry()) { if (entryId == null) { // Create entry breakpoint entryId = commands.setCallBreakpoint(bpUri, entryBreakpoint.getMethodName(), config); entryBreakpoint.setEntryBreakpointId(entryId); } else { // Update entry breakpoint commands.updateBreakpoint(entryId, config); } } else { if (entryId != null) { // Remove existing entry breakpoint commands.removeBreakpoint(entryId); entryBreakpoint.setEntryBreakpointId(null); } } String exitId = entryBreakpoint.getExitBreakpointId(); if (entryBreakpoint.breakOnExit()) { if (exitId == null) { // Create exit breakpoint exitId = commands.setReturnBreakpoint(bpUri, entryBreakpoint.getMethodName(), config); entryBreakpoint.setExitBreakpointId(exitId); } else { // Update exit breakpoint commands.updateBreakpoint(exitId, config); } } else { if (exitId != null) { // Remove exit breakpoint commands.removeBreakpoint(exitId); entryBreakpoint.setExitBreakpointId(null); } } } else { // All other breakpoints final String id = breakpoint.getId(session); if (id != null) { final DbgpBreakpointConfig config = createBreakpointConfig(breakpoint); if (breakpoint instanceof IScriptWatchpoint) { config.setExpression(makeWatchpointExpression((IScriptWatchpoint) breakpoint)); } commands.updateBreakpoint(id, config); } } } protected static void removeBreakpoint(IDbgpSession session, IScriptBreakpoint breakpoint) throws DbgpException, CoreException { final IDbgpBreakpointCommands commands = session.getCoreCommands(); final String id = breakpoint.removeId(session); if (id != null) { commands.removeBreakpoint(id); } if (breakpoint instanceof IScriptMethodEntryBreakpoint) { IScriptMethodEntryBreakpoint entryBreakpoint = (IScriptMethodEntryBreakpoint) breakpoint; final String entryId = entryBreakpoint.getEntryBreakpointId(); if (entryId != null) { commands.removeBreakpoint(entryId); } final String exitId = entryBreakpoint.getExitBreakpointId(); if (exitId != null) { commands.removeBreakpoint(exitId); } } } private static final int NO_CHANGES = 0; private static final int MINOR_CHANGE = 1; private static final int MAJOR_CHANGE = 2; private int hasBreakpointChanges(IMarkerDelta delta, IScriptBreakpoint breakpoint) { final String[] attrs = breakpoint.getUpdatableAttributes(); try { final IMarker marker = delta.getMarker(); for (int i = 0; i < attrs.length; ++i) { final String attr = attrs[i]; final Object oldValue = delta.getAttribute(attr); final Object newValue = marker.getAttribute(attr); if (oldValue == null) { if (newValue != null) { return classifyBreakpointChange(delta, breakpoint, attr); } continue; } if (newValue == null) { return classifyBreakpointChange(delta, breakpoint, attr); } if (!oldValue.equals(newValue)) { return classifyBreakpointChange(delta, breakpoint, attr); } } } catch (CoreException e) { DLTKDebugPlugin.log(e); } return NO_CHANGES; } private static int hasSpawnpointChanges(IMarkerDelta delta, IScriptBreakpoint breakpoint) { final String[] attrs = breakpoint.getUpdatableAttributes(); try { final IMarker marker = delta.getMarker(); for (int i = 0; i < attrs.length; ++i) { final String attr = attrs[i]; if (IBreakpoint.ENABLED.equals(attr) || IMarker.LINE_NUMBER.equals(attr)) { final Object oldValue = delta.getAttribute(attr); final Object newValue = marker.getAttribute(attr); if (oldValue == null) { if (newValue != null) { return IMarker.LINE_NUMBER.equals(attr) ? MAJOR_CHANGE : MINOR_CHANGE; } continue; } if (newValue == null) { return IMarker.LINE_NUMBER.equals(attr) ? MAJOR_CHANGE : MINOR_CHANGE; } if (!oldValue.equals(newValue)) { return IMarker.LINE_NUMBER.equals(attr) ? MAJOR_CHANGE : MINOR_CHANGE; } } } } catch (CoreException e) { DLTKDebugPlugin.log(e); } return NO_CHANGES; } private int classifyBreakpointChange(IMarkerDelta delta, IScriptBreakpoint breakpoint, String attr) throws CoreException { final boolean conditional = ScriptBreakpointUtils .isConditional(breakpoint); if (conditional && AbstractScriptBreakpoint.EXPRESSION.equals(attr)) { return MAJOR_CHANGE; } final boolean oldExprState = delta.getAttribute( AbstractScriptBreakpoint.EXPRESSION_STATE, false); final String oldExpr = delta.getAttribute( AbstractScriptBreakpoint.EXPRESSION, null); if (ScriptBreakpointUtils.isConditional(oldExprState, oldExpr) != conditional) { return MAJOR_CHANGE; } if (IMarker.LINE_NUMBER.equals(attr) && !target.getOptions().get( DebugOption.DBGP_BREAKPOINT_UPDATE_LINE_NUMBER)) { return MAJOR_CHANGE; } return MINOR_CHANGE; } // DebugTarget private final IScriptDebugTarget target; private void changeSpawnpoint(final IDbgpSession session, IScriptSpawnpoint spawnpoint) throws DbgpException, CoreException { final IDbgpSpawnpointCommands commands = (IDbgpSpawnpointCommands) session .get(IDbgpSpawnpointCommands.class); if (commands != null) { final String id = spawnpoint.getId(session); if (id != null) { commands.updateSpawnpoint(id, spawnpoint.isEnabled()); } } } protected void removeSpawnpoint(final IDbgpSession session, IScriptSpawnpoint spawnpoint) throws DbgpException, CoreException { final IDbgpSpawnpointCommands commands = (IDbgpSpawnpointCommands) session .get(IDbgpSpawnpointCommands.class); if (commands != null) { final String id = spawnpoint.getId(session); if (id != null) { commands.removeSpawnpoint(id); spawnpoint.setId(session, null); } } } public ScriptBreakpointManager(IScriptDebugTarget target, IScriptBreakpointPathMapper pathMapper) { this.target = target; this.bpPathMapper = pathMapper; this.sessions = NO_SESSIONS; } public boolean supportsBreakpoint(IBreakpoint breakpoint) { if (breakpoint instanceof IScriptBreakpoint) { return StrUtils.equals(breakpoint.getModelIdentifier(), target.getModelIdentifier()); } return false; } private void threadAccepted() { IBreakpointManager manager = getBreakpointManager(); manager.addBreakpointListener(target); manager.addBreakpointManagerListener(this); } public void threadTerminated() { IBreakpointManager manager = getBreakpointManager(); manager.removeBreakpointListener(target); manager.removeBreakpointManagerListener(this); if (bpPathMapper instanceof IScriptBreakpointPathMapperExtension) { ((IScriptBreakpointPathMapperExtension) bpPathMapper).clearCache(); } } synchronized IDbgpSession[] getSessions() { return sessions; } private synchronized boolean addSession(IDbgpSession session) { for (int i = 0; i < sessions.length; ++i) { if (session.equals(sessions[i])) { return false; } } final IDbgpSession[] temp = new IDbgpSession[sessions.length + 1]; System.arraycopy(sessions, 0, temp, 0, sessions.length); temp[sessions.length] = session; sessions = temp; return true; } synchronized boolean removeSession(IDbgpSession session) { for (int i = 0; i < sessions.length; ++i) { if (session.equals(sessions[i])) { if (sessions.length == 1) { sessions = NO_SESSIONS; } else { final IDbgpSession[] temp = new IDbgpSession[sessions.length - 1]; if (i > 0) { System.arraycopy(sessions, 0, temp, 0, i); } ++i; if (i < sessions.length) { System.arraycopy(sessions, i, temp, i - 1, sessions.length - i); } sessions = temp; } return true; } } return false; } public void initializeSession(IDbgpSession session, IProgressMonitor monitor) { if (!addSession(session)) { return; } IBreakpoint[] breakpoints = getBreakpointManager().getBreakpoints( target.getModelIdentifier()); monitor.beginTask(Util.EMPTY_STRING, breakpoints.length); for (int i = 0; i < breakpoints.length; i++) { try { final IBreakpoint breakpoint = breakpoints[i]; if (breakpoint instanceof IScriptSpawnpoint) { addSpawnpoint(session, (IScriptSpawnpoint) breakpoint); } else { addBreakpoint(session, (IScriptBreakpoint) breakpoint); } } catch (Exception e) { DLTKDebugPlugin.logWarning( NLS.bind(Messages.ErrorSetupDeferredBreakpoints, e.getMessage()), e); if (DLTKCore.DEBUG) { e.printStackTrace(); } } monitor.worked(1); } threadAccepted(); monitor.done(); } private static class TemporaryBreakpoint implements IDebugEventSetListener { final ScriptBreakpointManager manager; final Map<IDbgpSession, String> ids = new IdentityHashMap<IDbgpSession, String>( 1); /** * @param manager * @param uri * @param line */ public TemporaryBreakpoint(ScriptBreakpointManager manager, URI uri, int line) { this.manager = manager; final IDbgpSession[] sessions = manager.getSessions(); for (int i = 0; i < sessions.length; ++i) { DbgpBreakpointConfig config = new DbgpBreakpointConfig(true); try { final String id = sessions[i].getCoreCommands() .setLineBreakpoint(uri, line, config); if (id != null) { ids.put(sessions[i], id); } } catch (DbgpException e) { DLTKDebugPlugin.log(e); } } } public void handleDebugEvents(DebugEvent[] events) { for (int i = 0; i < events.length; ++i) { DebugEvent event = events[i]; if (event.getKind() == DebugEvent.SUSPEND) { removeBreakpoint(); DebugPlugin.getDefault().removeDebugEventListener(this); break; } } } private void removeBreakpoint() { try { final IDbgpSession[] sessions = manager.getSessions(); for (int i = 0; i < sessions.length; ++i) { final IDbgpSession session = sessions[i]; final String id = (String) ids.remove(session); if (id != null) { session.getCoreCommands().removeBreakpoint(id); } } } catch (DbgpException e) { DLTKDebugPlugin.log(e); } } } public void setBreakpointUntilFirstSuspend(URI uri, int line) { uri = bpPathMapper.map(uri); final TemporaryBreakpoint temp = new TemporaryBreakpoint(this, uri, line); if (!temp.ids.isEmpty()) { DebugPlugin.getDefault().addDebugEventListener(temp); } } // IBreakpointListener public void breakpointAdded(IBreakpoint breakpoint) { if (!supportsBreakpoint(breakpoint)) { return; } try { final IDbgpSession[] sessions = getSessions(); for (int i = 0; i < sessions.length; ++i) { if (breakpoint instanceof IScriptSpawnpoint) { addSpawnpoint(sessions[i], (IScriptSpawnpoint) breakpoint); } else { addBreakpoint(sessions[i], (IScriptBreakpoint) breakpoint); } } } catch (Exception e) { DLTKDebugPlugin.log(e); } } /** * @see IBreakpointListener#breakpointChanged(IBreakpoint, IMarkerDelta) * @param breakpoint * @param delta * if delta is <code>null</code> then there was a call to * BreakPointManager.fireBreakpointChanged(IBreakpoint * breakpoint), so see it as a major change. */ public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) { if (!supportsBreakpoint(breakpoint)) { return; } try { if (breakpoint instanceof IScriptSpawnpoint) { final int changes = delta != null ? hasSpawnpointChanges(delta, (IScriptSpawnpoint) breakpoint) : MAJOR_CHANGE; if (changes != NO_CHANGES) { final IDbgpSession[] sessions = getSessions(); if (changes == MAJOR_CHANGE) { for (int i = 0; i < sessions.length; ++i) { removeSpawnpoint(sessions[i], (IScriptSpawnpoint) breakpoint); addSpawnpoint(sessions[i], (IScriptSpawnpoint) breakpoint); } } else { for (int i = 0; i < sessions.length; ++i) { changeSpawnpoint(sessions[i], (IScriptSpawnpoint) breakpoint); } } } } else { final IScriptBreakpoint sbp = (IScriptBreakpoint) breakpoint; final int changes = delta != null ? hasBreakpointChanges(delta, sbp) : MAJOR_CHANGE; if (changes != NO_CHANGES) { final IDbgpSession[] sessions = getSessions(); if (changes == MAJOR_CHANGE) { for (int i = 0; i < sessions.length; ++i) { removeBreakpoint(sessions[i], sbp); addBreakpoint(sessions[i], sbp); } } else { for (int i = 0; i < sessions.length; ++i) { changeBreakpoint(sessions[i], sbp); } } } } } catch (Exception e) { DLTKDebugPlugin.log(e); } } public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) { if (!supportsBreakpoint(breakpoint)) { return; } try { final IDbgpSession[] sessions = getSessions(); if (breakpoint instanceof IScriptSpawnpoint) { for (int i = 0; i < sessions.length; ++i) { removeSpawnpoint(sessions[i], (IScriptSpawnpoint) breakpoint); } } else { for (int i = 0; i < sessions.length; ++i) { removeBreakpoint(sessions[i], (IScriptBreakpoint) breakpoint); } } } catch (Exception e) { DLTKDebugPlugin.log(e); } } // IBreakpointManagerListener public void breakpointManagerEnablementChanged(boolean enabled) { IBreakpoint[] breakpoints = getBreakpointManager().getBreakpoints( target.getModelIdentifier()); final IDbgpSession[] sessions = getSessions(); for (int i = 0; i < breakpoints.length; ++i) { try { final IBreakpoint breakpoint = breakpoints[i]; if (breakpoint instanceof IScriptSpawnpoint) { for (int j = 0; j < sessions.length; ++j) { changeSpawnpoint(sessions[j], (IScriptSpawnpoint) breakpoint); } } else if (breakpoint instanceof IScriptBreakpoint) { for (int j = 0; j < sessions.length; ++j) { changeBreakpoint(sessions[j], (IScriptBreakpoint) breakpoint); } } } catch (Exception e) { DLTKDebugPlugin.log(e); } } } }