| /******************************************************************************* |
| * Copyright (c) 2011, 2012 Sierra Wireless 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 |
| * |
| * Contributors: |
| * Sierra Wireless - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ldt.debug.core.internal; |
| |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IMarkerDelta; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.debug.core.DebugEvent; |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.IDebugEventSetListener; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.model.IBreakpoint; |
| import org.eclipse.debug.core.model.IProcess; |
| import org.eclipse.debug.core.model.IThread; |
| import org.eclipse.dltk.dbgp.DbgpBaseCommands; |
| import org.eclipse.dltk.dbgp.DbgpRequest; |
| import org.eclipse.dltk.dbgp.exceptions.DbgpException; |
| import org.eclipse.dltk.debug.core.IDbgpService; |
| import org.eclipse.dltk.debug.core.model.IScriptThread; |
| import org.eclipse.dltk.internal.debug.core.model.ScriptDebugTarget; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NodeList; |
| |
| public class LuaDebugTarget extends ScriptDebugTarget { |
| private enum ChangeType { |
| ADD, UPDATE, REMOVE; |
| } |
| |
| private List<LuaCoroutine> coroutines = new ArrayList<LuaCoroutine>(); |
| |
| public LuaDebugTarget(String modelId, IDbgpService dbgpService, String sessionId, ILaunch launch, IProcess process) { |
| super(modelId, dbgpService, sessionId, launch, process); |
| DebugPlugin.getDefault().addDebugEventListener(new IDebugEventSetListener() { |
| |
| protected List<LuaCoroutine> parseCoroutineList(Element response) throws DbgpException, CoreException { |
| NodeList xmlNodes = response.getElementsByTagName("coroutine"); //$NON-NLS-1$ |
| // TODO recycle LuaCoroutine instances to avoid flickering (may also require to modify stack) |
| List<LuaCoroutine> coroList = new ArrayList<LuaCoroutine>(xmlNodes.getLength()); |
| for (int i = 0; i < xmlNodes.getLength(); i++) { |
| Element coro = (Element) xmlNodes.item(i); |
| if (coro.getAttribute("running").equals("0")) { //$NON-NLS-1$ //$NON-NLS-2$ |
| coroList.add(new LuaCoroutine(LuaDebugTarget.this, coro.getAttribute("id"), coro.getAttribute("name"))); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| return coroList; |
| } |
| |
| @Override |
| public void handleDebugEvents(DebugEvent[] events) { |
| for (DebugEvent event : events) { |
| if (event.getKind() == DebugEvent.SUSPEND) { |
| IScriptThread thread = (IScriptThread) event.getSource(); |
| DbgpRequest listRequest = DbgpBaseCommands.createRequest("coroutine_list"); //$NON-NLS-1$ |
| try { |
| Element response = thread.getDbgpSession().getCommunicator().communicate(listRequest); |
| LuaDebugTarget.this.coroutines = parseCoroutineList(response); |
| } catch (DbgpException e) { |
| Activator.logError(Messages.LuaDebugTarget_error_coro_list, e); |
| LuaDebugTarget.this.coroutines.clear(); |
| } catch (CoreException e) { |
| Activator.logError(Messages.LuaDebugTarget_error_coro_list, e); |
| LuaDebugTarget.this.coroutines.clear(); |
| } |
| } else if (event.getKind() == DebugEvent.TERMINATE) { |
| DebugPlugin.getDefault().removeDebugEventListener(this); |
| } |
| } |
| } |
| }); |
| |
| } |
| |
| /** |
| * Just a job performing operations on {@link IBreakpoint}. |
| */ |
| final class ChangeJob extends Job { |
| private ChangeType changeType; |
| private IBreakpoint bp; |
| private IMarkerDelta markerDelta; |
| |
| public ChangeJob(String name, IBreakpoint breakpoint, IMarkerDelta delta, ChangeType type) { |
| super(name); |
| bp = breakpoint; |
| markerDelta = delta; |
| changeType = type; |
| } |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| breakpoint(changeType, bp, markerDelta); |
| return Status.OK_STATUS; |
| } |
| |
| } |
| |
| /** |
| * @see org.eclipse.dltk.internal.debug.core.model.ScriptDebugTarget#runToLine(java.net.URI, int) |
| */ |
| // TODO BUG_ECLIPSE TOOLSLINUX-101 355616 |
| @Override |
| public void runToLine(URI uri, final int lineNumber) throws DebugException { |
| URI remoteUri = getPathMapper().map(uri); |
| super.runToLine(remoteUri, lineNumber); |
| } |
| |
| private void breakpoint(final ChangeType changeType, final IBreakpoint bp, final IMarkerDelta markerDelta) { |
| switch (changeType) { |
| case ADD: |
| super.breakpointAdded(bp); |
| break; |
| case UPDATE: |
| super.breakpointChanged(bp, markerDelta); |
| break; |
| case REMOVE: |
| super.breakpointRemoved(bp, markerDelta); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * Perform breakpoint update in a separate Job |
| * |
| * @see ScriptDebugTarget#breakpointChanged(IBreakpoint, IMarkerDelta) |
| */ |
| // TODO BUG_ECLIPSE 360003 |
| @Override |
| public void breakpointChanged(final IBreakpoint breakpoint, final IMarkerDelta delta) { |
| final ChangeJob job = new ChangeJob(Messages.LuaDebugTargetUpdate, breakpoint, delta, ChangeType.UPDATE); |
| job.setSystem(true); |
| job.schedule(); |
| } |
| |
| /** |
| * Perform breakpoint add in a separate Job |
| * |
| * @see ScriptDebugTarget#breakpointAdded(IBreakpoint) |
| */ |
| // TODO BUG_ECLIPSE 360003 |
| @Override |
| public void breakpointAdded(final IBreakpoint breakpoint) { |
| final ChangeJob job = new ChangeJob(Messages.LuaDebugTargetAdd, breakpoint, null, ChangeType.ADD); |
| job.setSystem(true); |
| job.schedule(); |
| } |
| |
| /** |
| * Perform breakpoint removal in a separate Job |
| * |
| * @see ScriptDebugTarget#breakpointRemoved(IBreakpoint, IMarkerDelta) |
| */ |
| // TODO BUG_ECLIPSE 360003 |
| @Override |
| public void breakpointRemoved(final IBreakpoint breakpoint, final IMarkerDelta delta) { |
| final ChangeJob job = new ChangeJob(Messages.LuaDebugTargetRemove, breakpoint, delta, ChangeType.REMOVE); |
| job.setSystem(true); |
| job.schedule(); |
| } |
| |
| /** |
| * Returns the "main" thread of the instance. This is the only thread which is really mapped to a debug socket, others are just coroutines. |
| * |
| * @return Main thread if any, null otherwise. |
| */ |
| public IScriptThread getMainThread() { |
| IThread[] threads = super.getThreads(); |
| return threads.length > 0 ? (IScriptThread) threads[0] : null; |
| } |
| |
| /** |
| * @see org.eclipse.dltk.internal.debug.core.model.ScriptDebugTarget#getThreads() |
| */ |
| @Override |
| public IThread[] getThreads() { |
| // TODO: is rebuild thread list each call is expansive ? |
| List<IThread> threads = new ArrayList<IThread>(); |
| threads.addAll(Arrays.asList(super.getThreads())); |
| // coroutines are shown only when main thread is suspended (while thread is running, it is pointless to show them) |
| if (threads.size() > 0 && threads.get(0).isSuspended()) { |
| threads.addAll(coroutines); |
| } |
| return threads.toArray(new IThread[threads.size()]); |
| } |
| } |