/** * This file Copyright (c) 2005-2008 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other free and open source software ("FOSS") code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.debug.internal.core.model; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; 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.IBreakpointManagerListener; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.ILineBreakpoint; import org.eclipse.debug.core.model.IMemoryBlock; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.debug.core.model.IThread; import org.eclipse.debug.core.model.IValue; import org.eclipse.debug.core.model.IVariable; import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; import org.osgi.framework.Constants; import com.aptana.ide.core.IdeLog; import com.aptana.ide.core.PathUtils; import com.aptana.ide.core.StringUtils; import com.aptana.ide.core.resources.IUniformResource; import com.aptana.ide.core.resources.IUniformResourceMarker; import com.aptana.ide.debug.core.DetailFormatter; import com.aptana.ide.debug.core.IDebugConstants; import com.aptana.ide.debug.core.IDetailFormattersChangeListener; import com.aptana.ide.debug.core.ILaunchConfigurationConstants; import com.aptana.ide.debug.core.JSDebugPlugin; import com.aptana.ide.debug.core.JSDetailFormattersManager; import com.aptana.ide.debug.core.model.IJSDebugTarget; import com.aptana.ide.debug.core.model.IJSExceptionBreakpoint; import com.aptana.ide.debug.core.model.IJSLineBreakpoint; import com.aptana.ide.debug.core.model.IJSScriptElement; import com.aptana.ide.debug.core.model.IJSWatchpoint; import com.aptana.ide.debug.core.model.JSDebugModel; import com.aptana.ide.debug.core.xhr.IXHRService; import com.aptana.ide.debug.internal.core.DbgSourceURLStreamHandler; import com.aptana.ide.debug.internal.core.IFileContentRetriever; import com.aptana.ide.debug.internal.core.LocalResourceMapper; import com.aptana.ide.debug.internal.core.Util; import com.aptana.ide.debug.internal.core.xhr.XHRService; /** * @author Max Stepanov */ public class JSDebugTarget extends JSDebugElement implements IJSDebugTarget, IBreakpointManagerListener, IDetailFormattersChangeListener, DebugConnection.IHandler { private static final String UPDATE = "update"; //$NON-NLS-1$ private static final String VERSION = "version"; //$NON-NLS-1$ private static final String JAVASCRIPT = "javascript"; //$NON-NLS-1$ private static final String JAVASCRIPT_SCHEME = "javascript:"; //$NON-NLS-1$ private static final String OPENED = "opened"; //$NON-NLS-1$ private static final String HTTP = "http"; //$NON-NLS-1$ private static final String FILE = "file"; //$NON-NLS-1$ private static final String EXCEPTION_0_1 = "exception*{0}*{1}"; //$NON-NLS-1$ private static final String BREAKPOINT_0_1_2_3 = "breakpoint*{0}*{1}*{2}{3}"; //$NON-NLS-1$ private static final String WATCHPOINT_0_1_2 = "watchpoint*{0}*{1}*{2}"; //$NON-NLS-1$ private static final String DETAILS_0 = "details*{0}"; //$NON-NLS-1$ private static final String SET_VALUE_0_1 = "setValue*{0}*{1}"; //$NON-NLS-1$ private static final String EVAL_0 = "eval[{0}]"; //$NON-NLS-1$ private static final String EVAL_0_1 = "eval*{0}*{1}"; //$NON-NLS-1$ private static final String RESULT = "result"; //$NON-NLS-1$ private static final String FRAME_0 = "frame[{0}]"; //$NON-NLS-1$ private static final String VARIABLES_0 = "variables*{0}"; //$NON-NLS-1$ private static final String OPEN_URL_0 = "openUrl*{0}"; //$NON-NLS-1$ private static final String OPTION_0_1 = "option*{0}*{1}"; //$NON-NLS-1$ private static final String ENABLE = "enable"; //$NON-NLS-1$ private static final String STEP_FILTERS = "stepFilters"; //$NON-NLS-1$ private static final String MONITOR_XHR = "monitorXHR"; //$NON-NLS-1$ private static final String DETAIL_FORMATTERS = "detailFormatters"; //$NON-NLS-1$ private static final String DISABLE = "disable"; //$NON-NLS-1$ private static final String CHANGE = "change"; //$NON-NLS-1$ private static final String REMOVE = "remove"; //$NON-NLS-1$ private static final String CREATE = "create"; //$NON-NLS-1$ private static final String RESOLVED = "resolved"; //$NON-NLS-1$ private static final String DESTROYED = "destroyed"; //$NON-NLS-1$ private static final String ARGS_SPLIT = "\\*"; //$NON-NLS-1$ private static final String SUBARGS_SPLIT = "\\|"; //$NON-NLS-1$ private static final String CREATED = "created"; //$NON-NLS-1$ private static final String TERMINATE = "terminate"; //$NON-NLS-1$ private static final String SUSPEND = "suspend"; //$NON-NLS-1$ private static final String ERROR = "error"; //$NON-NLS-1$ private static final String COMPLETED = "completed"; //$NON-NLS-1$ private static final String LOADED = "loaded"; //$NON-NLS-1$ private static final String SEND = "send"; //$NON-NLS-1$ private static final String HEADERS = "headers"; //$NON-NLS-1$ private static final String AUTH = "auth"; //$NON-NLS-1$ private static final String OPEN = "open"; //$NON-NLS-1$ private static final String START = "start"; //$NON-NLS-1$ private static final String LOAD = "load"; //$NON-NLS-1$ private static final String EXCEPTION = "exception"; //$NON-NLS-1$ private static final String ERR = "err"; //$NON-NLS-1$ private static final String TRACE = "trace"; //$NON-NLS-1$ private static final String SRC = "src"; //$NON-NLS-1$ private static final String BREAKPOINT = "breakpoint"; //$NON-NLS-1$ private static final String SCRIPTS = "scripts"; //$NON-NLS-1$ private static final String CLIENT = "client"; //$NON-NLS-1$ private static final String XHR = "xhr"; //$NON-NLS-1$ private static final String LOG = "log"; //$NON-NLS-1$ private static final String SUCCESS = "success"; //$NON-NLS-1$ private static final String GET_SOURCE_0 = "getSource*{0}"; //$NON-NLS-1$ private static final int PROTOCOL_VERSION_MIN = 0; private static final int PROTOCOL_VERSION_MAX = 1; /** * Step filter bit mask - indicates if step filters are enabled. */ private static final int STEP_FILTERS_ENABLED = 0x001; /** * Step filter bit mask - indicates if constructors are filtered. */ private static final int FILTER_CONSTRUCTORS = 0x002; /** * Mask used to flip individual bit masks via XOR */ private static final int XOR_MASK = 0xFFF; private static boolean checkUpdate = true; private DebugConnection connection; private int stepFilterMask = 0; private String[] stepFilters = null; private ILaunch launch; private String label; private IProcess process; private OutputStream out; private OutputStream err; private LocalResourceMapper resourceMapper; private JSDebugThread[] threads = new JSDebugThread[0]; private IFileContentRetriever fileContentRetriever; private XHRService xhrService; private Map<String, IJSScriptElement> topScriptElements = new HashMap<String, IJSScriptElement>(); private Map<Integer, IJSScriptElement> scripts = new HashMap<Integer, IJSScriptElement>(); private List<IBreakpoint> runToLineBreakpoints = new ArrayList<IBreakpoint>(); private Map<String, String> sourceResolveCache = new HashMap<String, String>(64); private String mainFile = null; private IBreakpoint skipOperationOnBreakpoint = null; private boolean ignoreBreakpointCreation = false; private boolean contentChanged = false; private Job updateContentJob = new Job("Debugger Content Update") { //$NON-NLS-1$ { setPriority(Job.INTERACTIVE); setSystem(true); } @Override protected IStatus run(IProgressMonitor monitor) { if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } if (connection == null || !connection.isConnected()) { return Status.OK_STATUS; } try { boolean changed = false; synchronized (this) { if (contentChanged) { changed = true; contentChanged = false; } } if (changed) { fireChangeEvent(DebugEvent.CONTENT); } return Status.OK_STATUS; } finally { schedule(1000); } } }; /** * JSDebugTarget * * @param launch * @param process * @param httpServer * @param resourceMapper * @param socket * @param debugMode * @throws CoreException */ public JSDebugTarget(ILaunch launch, IProcess process, HttpServerProcess httpServer, LocalResourceMapper resourceMapper, DebugConnection connection, boolean debugMode) throws CoreException { this(launch, null, process, httpServer, resourceMapper, connection, debugMode); } /** * JSDebugTarget * * @param launch * @param label * @param process * @param httpServer * @param resourceMapper * @param socket * @param debugMode * @throws CoreException */ public JSDebugTarget(ILaunch launch, String label, IProcess process, HttpServerProcess httpServer, LocalResourceMapper resourceMapper, DebugConnection connection, boolean debugMode) throws CoreException { super(null); this.launch = launch; this.label = label; this.process = process; this.resourceMapper = resourceMapper; this.connection = connection; try { if (debugMode) { launch.addDebugTarget(this); } else { /* TODO: do some refactoring here */ if (process instanceof JSDebugProcess) { ((JSDebugProcess) process).setDebugTarget(this); } if (httpServer != null) { httpServer.setDebugTarget(this); } } init(debugMode); } catch (CoreException e) { shutdown(); throw e; } catch (Exception e) { shutdown(); throwDebugException(e); } } /*package*/ DebugConnection getConnection() { return connection; } /** * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ public Object getAdapter(Class adapter) { if (adapter == IFileContentRetriever.class) { return getFileContentRetriever(); } if (adapter == IXHRService.class) { return xhrService; } return super.getAdapter(adapter); } /** * getFileContentRetriever * * @return IFileContentRetriever */ private IFileContentRetriever getFileContentRetriever() { if (fileContentRetriever == null) { fileContentRetriever = new IFileContentRetriever() { public InputStream getContents(URI uri) throws CoreException { String[] args = connection.sendCommandAndWait(StringUtils .format(GET_SOURCE_0, Util.encodeData(uri.toString()))); if (args != null && SUCCESS.equals(args[1])) { return new ByteArrayInputStream(Util.decodeData(args[2]).getBytes()); // TODO: encoding ? } return null; } }; } return fileContentRetriever; } /** * handleMessage * * @param message */ public void handleMessage(String message) { String[] args = message.split(ARGS_SPLIT); int j = 0; String action = args[j++]; if (LOG.equals(action)) { handleLog(args); return; } else if (XHR.equals(action)) { handleXHR(args); return; } else if (CLIENT.equals(action)) { handleClientAction(args); return; } else if (SCRIPTS.equals(action)) { handleScripts(args); return; } else if (BREAKPOINT.equals(action)) { action = args[j++]; /* find breakpoint(s) */ String sourceFile = resolveSourceFile(Util.decodeData(args[j++])); int lineNumber = -1; try { lineNumber = Integer.parseInt(args[j++]); } catch (NumberFormatException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } try { IBreakpoint breakpoint = findBreakpointAt(sourceFile, lineNumber); if (CREATE.equals(action) || CHANGE.equals(action)) { boolean enabled = "1".equals(args[j++]); //$NON-NLS-1$ int hitCount = -1; try { hitCount = Integer.parseInt(args[j++]); } catch (NumberFormatException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } String condition = Util.decodeData(args[j++]); boolean conditionOnTrue = "1".equals(args[j++]); //$NON-NLS-1$ if (breakpoint == null) { Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put(IBreakpoint.ENABLED, Boolean.valueOf(enabled)); if (hitCount != -1) { attributes.put(IDebugConstants.BREAKPOINT_HIT_COUNT, new Integer(hitCount)); } if (condition.length() != 0) { attributes.put(IDebugConstants.BREAKPOINT_CONDITION, condition); attributes.put(IDebugConstants.BREAKPOINT_CONDITION_ENABLED, Boolean.TRUE); attributes.put(IDebugConstants.BREAKPOINT_CONDITION_SUSPEND_ON_TRUE, Boolean .valueOf(conditionOnTrue)); } /* create breakpoint */ Object resource = findSourceResource(sourceFile); if (resource instanceof IResource) { /* XXX: temporary solution to prevent breakpoint changes be sent back to host */ try { ignoreBreakpointCreation = true; breakpoint = JSDebugModel.createLineBreakpoint((IResource) resource, lineNumber, attributes, true); } finally { ignoreBreakpointCreation = false; } } else if (resource instanceof IUniformResource) { try { ignoreBreakpointCreation = true; breakpoint = JSDebugModel.createLineBreakpoint((IUniformResource) resource, lineNumber, attributes, true); } finally { ignoreBreakpointCreation = false; } } } else if (CHANGE.equals(action)) { if (breakpoint.isEnabled() != enabled) { skipOperationOnBreakpoint = breakpoint; breakpoint.setEnabled(enabled); } if (breakpoint instanceof IJSLineBreakpoint) { IJSLineBreakpoint lineBreakpoint = (IJSLineBreakpoint) breakpoint; if (lineBreakpoint.getHitCount() != hitCount) { skipOperationOnBreakpoint = breakpoint; lineBreakpoint.setHitCount(hitCount); } if (!condition.equals(lineBreakpoint.getCondition())) { skipOperationOnBreakpoint = breakpoint; lineBreakpoint.setCondition(condition); lineBreakpoint.setConditionEnabled(condition.length() != 0); } if (lineBreakpoint.isConditionSuspendOnTrue() != conditionOnTrue) { skipOperationOnBreakpoint = breakpoint; lineBreakpoint.setConditionSuspendOnTrue(conditionOnTrue); } } } } else if (REMOVE.equals(action) && breakpoint != null) { skipOperationOnBreakpoint = breakpoint; breakpoint.delete(); } } catch (CoreException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } return; } else if (OPENED.equals(action)) { mainFile = resolveSourceFile(Util.decodeData(args[1])); DebugEvent event = new DebugEvent(this, DebugEvent.MODEL_SPECIFIC, IDebugConstants.DEBUG_EVENT_URL_OPENED); event.setData(mainFile); fireEvent(event); return; } if (threads.length > 0) { threads[0].handleMessage(args); } } /** * handleLog * * @param args */ private void handleLog(String[] args) { String log = args[1]; String text = Util.decodeData(args[2]); if (args.length >= 4) { StringBuffer sb = new StringBuffer(text); String type = args[3]; if (SRC.equals(type) && args.length >= 6) { String fileName = resolveSourceFile(Util.decodeData(args[4])); IFile file = PathUtils.findWorkspaceFile(fileName); if (file != null) { fileName = file.getFullPath().makeRelative().toString(); } sb.append(StringUtils.format(" ({0}:{1})", new String[] { fileName, args[5] })); //$NON-NLS-1$ } else if (TRACE.equals(type)) { sb.append('\n'); for (int i = 4; i < args.length; ++i) { String[] subargs = args[i].split(SUBARGS_SPLIT); if (subargs[0].length() == 0) { subargs[0] = StringUtils.format("[{0}]", //$NON-NLS-1$ i == args.length-1 ? Messages.JSDebugTarget_TopLevelScript : Messages.JSDebugTarget_EvalScript); } String fileName = resolveSourceFile(Util.decodeData(subargs[2])); IFile file = PathUtils.findWorkspaceFile(fileName); if (file != null) { fileName = file.getFullPath().makeRelative().toString(); } sb.append(StringUtils.format("\tat {0}({1}) ({2}:{3})\n", //$NON-NLS-1$ new String[] { Util.decodeData(subargs[0]), Util.decodeData(subargs[1]), fileName, subargs[3] })); } } text = sb.toString(); } if (!text.endsWith("\n")) { //$NON-NLS-1$ text += "\n"; //$NON-NLS-1$ } try { if (ERR.equals(log) || EXCEPTION.equals(log)) { if (err != null) { err.write(text.getBytes()); } } else /* out */{ if (out != null) { out.write(text.getBytes()); } } } catch (IOException e) { JSDebugPlugin.log(e); } } /** * handleXHR * * @param args */ private void handleXHR(String[] args) { int j = 1; String rid = args[j++]; String cmd = args[j++]; if (START.equals(cmd)) { String method = args[j++]; String url = Util.decodeData(args[j++]); String[][] headers = getXHRHeaders(Util.decodeData(args[j++])); String body = Util.decodeData(args[j++]); xhrService.openRequest(rid, method, url, false); xhrService.setRequestHeaders(rid, headers); xhrService.setRequestBody(rid, body); } else if (LOAD.equals(cmd)) { int statusCode = -1; try { statusCode = Integer.parseInt(args[j++]); } catch (NumberFormatException e) { } String statusText = Util.decodeData(args[j++]); String[][] headers = getXHRHeaders(Util.decodeData(args[j++])); String response = Util.decodeData(args[j++]); xhrService.setResponseStatus(rid, statusCode, statusText); xhrService.setResponseHeaders(rid, headers); xhrService.setResponseBody(rid, response); } /* XXX: obsoleted XHR commands below */ else if (OPEN.equals(cmd)) { String method = args[j++]; String url = Util.decodeData(args[j++]); String auth = args[j++]; xhrService.openRequest(rid, method, url, AUTH.equals(auth)); } else if (HEADERS.equals(cmd)) { String[][] headers = getXHRHeaders(Util.decodeData(args[j++])); xhrService.setRequestHeaders(rid, headers); } else if (SEND.equals(cmd)) { String body = Util.decodeData(args[j++]); xhrService.setRequestBody(rid, body); } else if (LOADED.equals(cmd)) { int statusCode = -1; try { statusCode = Integer.parseInt(args[j++]); } catch (NumberFormatException e) { } String statusText = Util.decodeData(args[j++]); String[][] headers = getXHRHeaders(Util.decodeData(args[j++])); xhrService.setResponseHeaders(rid, headers); xhrService.setResponseStatus(rid, statusCode, statusText); } else if (COMPLETED.equals(cmd)) { String response = Util.decodeData(args[j++]); xhrService.setResponseBody(rid, response); } else if (ERROR.equals(cmd)) { xhrService.setError(rid); } } /** * getXHRHeaders * * @param string * @return String[][] */ private static String[][] getXHRHeaders(String string) { String[] headers = string.split("\\n"); //$NON-NLS-1$ List<String[]> list = new ArrayList<String[]>(headers.length); for (int i = 0; i < headers.length; ++i) { String header = headers[i]; String value = StringUtils.EMPTY; int pos = header.indexOf(": "); //$NON-NLS-1$ if (pos != -1) { value = header.substring(pos + 2); header = header.substring(0, pos); } list.add(new String[] { header, value }); } return (String[][]) list.toArray(new String[list.size()][]); } /** * handleClientAction * * @param args */ private void handleClientAction(String[] args) { int j = 1; String action = args[j++]; if (SUSPEND.equals(action)) { try { if (canSuspend()) { suspend(); } } catch (DebugException ignore) { } } else if (TERMINATE.equals(action)) { try { if (canTerminate()) { terminate(); } } catch (DebugException ignore) { } } else if (OPEN.equals(action)) { String fileName = resolveSourceFile(Util.decodeData(args[j++])); if ( fileName != null ) { Object sourceElement = null; ISourceLocator locator = launch.getSourceLocator(); if ( locator instanceof ISourceLookupDirector ) { sourceElement = ((ISourceLookupDirector)locator).getSourceElement(fileName); } if ( sourceElement != null ) { JSDebugPlugin.getDefault().openInEditor(sourceElement); } } } } /** * handleScripts * * @param args */ private void handleScripts(String[] args) { String action = args[1]; if (CREATED.equals(action)) { for (int i = 2; i < args.length; ++i) { int j = 0; String[] subargs = args[i].split(SUBARGS_SPLIT); if (subargs.length < 5) { IdeLog.logInfo(JSDebugPlugin.getDefault(), StringUtils.format( "Missing fields in response: <{0}>", args[i])); //$NON-NLS-1$ continue; } int scriptTag = -1; try { scriptTag = Integer.parseInt(subargs[j++]); } catch (NumberFormatException e) { } String fileName = Util.decodeData(subargs[j++]); if (fileName.length() == 0 || "[Eval-script]".equals(fileName) //$NON-NLS-1$ || fileName.startsWith("javascript:")) { //$NON-NLS-1$ continue; } fileName = resolveSourceFile(fileName); String scriptName = Util.decodeData(subargs[j++]); int baseLine = -1; int lineExtent = -1; try { baseLine = Integer.parseInt(subargs[j++]); lineExtent = Integer.parseInt(subargs[j++]); } catch (NumberFormatException e) { } if (scriptName.length() == 0) { continue; // skip empty scripts } JSDebugScriptElement topScriptElement = (JSDebugScriptElement) topScriptElements.get(fileName); if (topScriptElement == null) { String name = fileName; IFile file = PathUtils.findWorkspaceFile(fileName); if (file != null) { name = file.getFullPath().toString(); } topScriptElement = new JSDebugTopScriptElement(this, name, fileName); topScriptElements.put(fileName, topScriptElement); } JSDebugScriptElement scriptElement = new JSDebugScriptElement(this, scriptName, baseLine, lineExtent); topScriptElement.insertElement(scriptElement); if (scriptTag > 0) { scripts.put(new Integer(scriptTag), scriptElement); } } synchronized (updateContentJob) { contentChanged = true; }; } else if (DESTROYED.equals(action)) { int j = 0; String[] subargs = args[2].split(SUBARGS_SPLIT); int scriptTag = -1; try { scriptTag = Integer.parseInt(subargs[j++]); } catch (NumberFormatException e) { } String fileName = resolveSourceFile(Util.decodeData(subargs[j++])); JSDebugScriptElement topScriptElement = (JSDebugScriptElement) topScriptElements.get(fileName); if (scriptTag > 0) { JSDebugScriptElement scriptElement = (JSDebugScriptElement) scripts.remove(new Integer(scriptTag)); if (topScriptElement != null && scriptElement != null) { topScriptElement.removeElement(scriptElement); } } } else if (RESOLVED.equals(action)) { for (int i = 2; i < args.length; ++i) { int j = 0; String[] subargs = args[i].split(SUBARGS_SPLIT); int scriptTag = -1; try { scriptTag = Integer.parseInt(subargs[j++]); } catch (NumberFormatException e) { } String scriptName = Util.decodeData(subargs[j++]); if (scriptTag < 0 || scriptName.length() == 0) { continue; } JSDebugScriptElement scriptElement = (JSDebugScriptElement) scripts.get(new Integer(scriptTag)); if (scriptElement != null) { scriptElement.setName(scriptName); scriptElement.fireChangeEvent(DebugEvent.STATE); } } } } /** * @see org.eclipse.debug.core.model.IDebugElement#getLaunch() */ public ILaunch getLaunch() { return launch; } /** * @see org.eclipse.debug.core.model.IDebugElement#getDebugTarget() */ public IDebugTarget getDebugTarget() { return this; } /** * @see org.eclipse.debug.core.model.IDebugTarget#getProcess() */ public IProcess getProcess() { return process; } /** * @see org.eclipse.debug.core.model.IDebugTarget#getThreads() */ public IThread[] getThreads() throws DebugException { return threads; } /** * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads() */ public boolean hasThreads() throws DebugException { return threads.length > 0; } /** * @see org.eclipse.debug.core.model.IDebugTarget#getName() */ public String getName() throws DebugException { return label != null ? label : Messages.JSDebugTarget_JSDebugger; } /** * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint) */ public boolean supportsBreakpoint(IBreakpoint breakpoint) { if (breakpoint.getModelIdentifier().equals(getModelIdentifier())) { return true; } return false; } /** * @see org.eclipse.debug.core.model.ITerminate#canTerminate() */ public boolean canTerminate() { return !isTerminated(); } /** * @see org.eclipse.debug.core.model.ITerminate#isTerminated() */ public boolean isTerminated() { return connection.isTerminated(); } /** * @see org.eclipse.debug.core.model.ITerminate#terminate() */ public void terminate() throws DebugException { if (isTerminated()) { return; } connection.sendCommand(TERMINATE); } /** * @see org.eclipse.debug.core.model.ISuspendResume#canResume() */ public boolean canResume() { return isSuspended(); } /** * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend() */ public boolean canSuspend() { return !isSuspended(); } /** * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended() */ public boolean isSuspended() { return threads.length > 0 ? threads[0].isSuspended() : false; } /** * @see org.eclipse.debug.core.model.ISuspendResume#resume() */ public void resume() throws DebugException { threads[0].resume(); } /** * @see org.eclipse.debug.core.model.ISuspendResume#suspend() */ public void suspend() throws DebugException { if (isDisconnected()) { return; } threads[0].suspend(); } /** * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint) */ public void breakpointAdded(IBreakpoint breakpoint) { if (supportsBreakpoint(breakpoint) && breakpoint instanceof IJSLineBreakpoint) { try { if (((IJSLineBreakpoint) breakpoint).isRunToLine()) { runToLineBreakpoints.add(breakpoint); } } catch (CoreException e) { } } handleBreakpoint(breakpoint, CREATE); } /** * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, * org.eclipse.core.resources.IMarkerDelta) */ public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) { if (supportsBreakpoint(breakpoint) && breakpoint instanceof IJSLineBreakpoint) { try { if (((IJSLineBreakpoint) breakpoint).isRunToLine()) { runToLineBreakpoints.remove(breakpoint); } } catch (CoreException e) { } } handleBreakpoint(breakpoint, REMOVE); } /** * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, * org.eclipse.core.resources.IMarkerDelta) */ public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) { /* TODO: check delta to use right operation (create/change) */ handleBreakpoint(breakpoint, CHANGE); } /** * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect() */ public boolean canDisconnect() { return !isDisconnected(); } /** * @see org.eclipse.debug.core.model.IDisconnect#disconnect() */ public void disconnect() throws DebugException { connection.sendCommandAndWait(DISABLE); stopDebug(); } /** * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected() */ public boolean isDisconnected() { return !connection.isConnected(); } /** * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval() */ public boolean supportsStorageRetrieval() { return false; } /** * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long) */ public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException { throwNotImplemented(); return null; } /** * @see com.aptana.ide.debug.core.model.IJSDebugTarget#isFilterConstructors() */ public boolean isFilterConstructors() { return (stepFilterMask & FILTER_CONSTRUCTORS) > 0; } /** * @see com.aptana.ide.debug.core.model.IJSDebugTarget#setFilterConstructors(boolean) */ public void setFilterConstructors(boolean filter) { if (filter) { stepFilterMask = stepFilterMask | FILTER_CONSTRUCTORS; } else { stepFilterMask = stepFilterMask & (FILTER_CONSTRUCTORS ^ XOR_MASK); } } /** * @see org.eclipse.debug.core.model.IStepFilters#isStepFiltersEnabled() */ public boolean isStepFiltersEnabled() { return (stepFilterMask & STEP_FILTERS_ENABLED) > 0; } /** * @see org.eclipse.debug.core.model.IStepFilters#setStepFiltersEnabled(boolean) */ public void setStepFiltersEnabled(boolean enabled) { if (enabled) { stepFilterMask = stepFilterMask | STEP_FILTERS_ENABLED; } else { stepFilterMask = stepFilterMask & (STEP_FILTERS_ENABLED ^ XOR_MASK); } try { setOption("stepFiltersEnabled", Boolean.toString(isStepFiltersEnabled())); //$NON-NLS-1$ } catch (DebugException e) { } } /** * @see org.eclipse.debug.core.model.IStepFilters#supportsStepFilters() */ public boolean supportsStepFilters() { return !isTerminated() && !isDisconnected(); } /** * @see com.aptana.ide.debug.core.model.IJSDebugTarget#getStepFilters() */ public String[] getStepFilters() { return stepFilters; } /** * @see com.aptana.ide.debug.core.model.IJSDebugTarget#setStepFilters(java.lang.String[]) */ public void setStepFilters(String[] list) { stepFilters = list; } /** * @see com.aptana.ide.debug.core.model.IJSDebugTarget#getAttribute(java.lang.String) */ public String getAttribute(String key) { return getLaunch().getAttribute(key); } /** * @see com.aptana.ide.debug.core.model.IJSDebugTarget#setAttribute(java.lang.String, java.lang.String) */ public void setAttribute(String key, String value) { getLaunch().setAttribute(key, value); try { handleAttribute(key); } catch (DebugException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } } /** * handleAttribute * * @param key * @throws DebugException */ private void handleAttribute(String key) throws DebugException { String value = getAttribute(key); boolean boolValue = Boolean.valueOf(value).booleanValue(); if (ILaunchConfigurationConstants.CONFIGURATION_SUSPEND_ON_FIRST_LINE.equals(key)) { setOption("suspendOnFirstLine", Boolean.toString(boolValue)); //$NON-NLS-1$ } else if (ILaunchConfigurationConstants.CONFIGURATION_SUSPEND_ON_EXCEPTIONS.equals(key)) { setOption("suspendOnExceptions", Boolean.toString(boolValue)); //$NON-NLS-1$ } else if (ILaunchConfigurationConstants.CONFIGURATION_SUSPEND_ON_ERRORS.equals(key)) { setOption("suspendOnErrors", Boolean.toString(boolValue)); //$NON-NLS-1$ } else if (ILaunchConfigurationConstants.CONFIGURATION_SUSPEND_ON_DEBUGGER_KEYWORDS.equals(key)) { setOption("suspendOnKeywords", Boolean.toString(boolValue)); //$NON-NLS-1$ } } /** * @see com.aptana.ide.debug.core.IDetailFormattersChangeListener#detailFormattersChanged() */ public void detailFormattersChanged() { try { handleDetailFormattersChange(); } catch (DebugException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } } /** * handleDetailFormattersChange * * @throws DebugException */ private void handleDetailFormattersChange() throws DebugException { Collection detailformatters = JSDetailFormattersManager.getDefault().getDetailFormatters(); StringBuffer sb = new StringBuffer(DETAIL_FORMATTERS); if (!detailformatters.isEmpty()) { for (Iterator i = detailformatters.iterator(); i.hasNext();) { DetailFormatter detailFormatter = (DetailFormatter) i.next(); if (!detailFormatter.isEnabled()) { continue; } sb.append(StringUtils.format("*{0}|{1}", //$NON-NLS-1$ new String[] { detailFormatter.getTypeName(), Util.encodeData(detailFormatter.getSnippet()) })); } } connection.sendCommandAndWait(sb.toString()); } /** * init * * @param debugMode * @throws CoreException */ private void init(boolean debugMode) throws CoreException { synchronized (this) { connection.start(this); updateContentJob.schedule(); /* check debugger/protocol version */ checkVersion(); } if (true /* TODO: monitor XHR option */) { xhrService = new XHRService(); setOption(MONITOR_XHR, Boolean.toString(true)); if (process instanceof JSDebugProcess) { ((JSDebugProcess) process).setXHRService(xhrService); } } if (debugMode) { fireCreationEvent(); JSDebugThread thread = new JSDebugThread(this); threads = new JSDebugThread[] { thread }; for (int i = 0; i < threads.length; ++i) { threads[i].fireCreationEvent(); } fireChangeEvent(DebugEvent.CONTENT); handleAttribute(ILaunchConfigurationConstants.CONFIGURATION_SUSPEND_ON_FIRST_LINE); handleAttribute(ILaunchConfigurationConstants.CONFIGURATION_SUSPEND_ON_EXCEPTIONS); handleAttribute(ILaunchConfigurationConstants.CONFIGURATION_SUSPEND_ON_ERRORS); handleAttribute(ILaunchConfigurationConstants.CONFIGURATION_SUSPEND_ON_DEBUGGER_KEYWORDS); setOption("bypassConstructors", Boolean.toString(isFilterConstructors())); //$NON-NLS-1$ setOption("stepFiltersEnabled", Boolean.toString(isStepFiltersEnabled())); //$NON-NLS-1$ if (stepFilters != null && stepFilters.length > 0) { StringBuffer sb = new StringBuffer(STEP_FILTERS); for (int i = 0; i < stepFilters.length; ++i) { sb.append(i != 0 ? '|' : '*').append(Util.encodeData(stepFilters[i])); } connection.sendCommandAndWait(sb.toString()); } handleDetailFormattersChange(); JSDetailFormattersManager.getDefault().addChangeListener(this); /* restore breakpoints */ IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints( getModelIdentifier()); for (int i = 0; i < breakpoints.length; ++i) { breakpointAdded(breakpoints[i]); } // Register listeners DebugPlugin.getDefault().getBreakpointManager().addBreakpointManagerListener(this); DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this); connection.sendCommandAndWait(ENABLE); } if (process instanceof JSDebugProcess) { out = ((JSDebugProcess) process).getOutputStream(); err = ((JSDebugProcess) process).getErrorStream(); } } /** * checkVersion * * @throws DebugException */ private void checkVersion() throws DebugException { int protoVersion = 0; String version = null; String[] args = connection.sendCommandAndWait(VERSION); if (args != null && args.length >= 3 && args[1].charAt(0) != '!') { IdeLog.logInfo(JSDebugPlugin.getDefault(), StringUtils.format("Extension version: {0}; protocol v{1}", //$NON-NLS-1$ new String[] { args[2], args[1] })); try { protoVersion = Integer.parseInt(args[1]); } catch (NumberFormatException e) { } version = args[2]; } if ((protoVersion < PROTOCOL_VERSION_MIN) || (protoVersion > PROTOCOL_VERSION_MAX)) { throwDebugException(StringUtils.format( "Incompatible debugger extension protocol version {0} for [{1},{2}]", //$NON-NLS-1$ new String[] { Integer.toString(protoVersion), Integer.toString(PROTOCOL_VERSION_MIN), Integer.toString(PROTOCOL_VERSION_MAX) })); } if (checkUpdate) { boolean update = false; if (version != null) { String pluginVersion = (String) Platform.getBundle(JSDebugPlugin.ID).getHeaders().get( Constants.BUNDLE_VERSION); int index = pluginVersion.lastIndexOf('.'); if (index != -1) { if (index >= version.length() || !pluginVersion.substring(0, index).equals(version.substring(0, index))) { update = true; } else if (!pluginVersion.substring(index + 1).equals(version.substring(index + 1))) { try { if (Integer.parseInt(pluginVersion.substring(index + 1)) > Integer.parseInt(version .substring(index + 1))) { update = true; } } catch (NumberFormatException e) { } } } if (update) { args = connection.sendCommandAndWait(UPDATE); if (args != null && args.length >= 2) { IdeLog.logInfo(JSDebugPlugin.getDefault(), StringUtils.format( "Extension update available: {0}", //$NON-NLS-1$ new String[] { args[1] })); } } } checkUpdate = false; } } /** * setOption * * @param option * @param value * @throws DebugException */ private synchronized void setOption(String option, String value) throws DebugException { if (connection.isConnected()) { connection.sendCommandAndWait(StringUtils.format(OPTION_0_1, new String[] { option, value })); } } /** * openURL * * @param url * @throws DebugException */ public void openURL(URL url) throws DebugException { if (connection.isConnected()) { try { DebugEvent event = new DebugEvent(this, DebugEvent.MODEL_SPECIFIC, IDebugConstants.DEBUG_EVENT_URL_OPEN); URL fileUrl = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath()); event.setData(resolveSourceFile(fileUrl.toExternalForm())); fireEvent(event); } catch (MalformedURLException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } mainFile = null; connection.sendCommandAndWait(StringUtils.format(OPEN_URL_0, Util.encodeData(url.toString()))); } } /** * stopDebug * * @throws DebugException */ private void stopDebug() throws DebugException { if (connection == null || !connection.isConnected()) { return; } connection.stop(); updateContentJob.cancel(); if (threads.length > 0) { for (int i = 0; i < threads.length; ++i) { threads[i].fireTerminateEvent(); } threads = new JSDebugThread[0]; topScriptElements.clear(); scripts.clear(); fireChangeEvent(DebugEvent.CONTENT); // Unregister listeners JSDetailFormattersManager.getDefault().removeChangeListener(this); DebugPlugin.getDefault().getBreakpointManager().removeBreakpointListener(this); DebugPlugin.getDefault().getBreakpointManager().removeBreakpointManagerListener(this); } } public void handleShutdown() { try { shutdown(); } catch (DebugException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } } /** * shutdown * * @throws DebugException */ private void shutdown() throws DebugException { try { stopDebug(); if (connection != null) { connection.dispose(); } } catch (IOException e) { throwDebugException(e); } finally { if (DebugPlugin.getDefault() != null) { fireTerminateEvent(); } } } /** * @see org.eclipse.debug.core.IBreakpointManagerListener#breakpointManagerEnablementChanged(boolean) */ public void breakpointManagerEnablementChanged(boolean enabled) { IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager() .getBreakpoints(getModelIdentifier()); for (int i = 0; i < breakpoints.length; i++) { IBreakpoint breakpoint = breakpoints[i]; if (enabled) { breakpointAdded(breakpoint); } else { breakpointRemoved(breakpoint, null); } } } /** * loadVariables * * @param qualifier * @return IVariable[] * @throws DebugException */ protected IVariable[] loadVariables(String qualifier) throws DebugException { if (!isSuspended()) { return new IVariable[0]; } List<IVariable> list = new ArrayList<IVariable>(); String[] args = connection.sendCommandAndWait(StringUtils.format(VARIABLES_0, Util.encodeData(qualifier))); if (args != null) { for (int i = 1; i < args.length; ++i) { int j = 0; String varData = args[i]; if (varData.length() == 0) { break; } if (varData.endsWith("|")) //$NON-NLS-1$ { varData += "| "; //$NON-NLS-1$ } String[] subargs = varData.split(SUBARGS_SPLIT); String name = Util.decodeData(subargs[j++]); String type = Util.decodeData(subargs[j++]); String flags = subargs[j++]; String stringValue = Util.decodeData(subargs[j++]); boolean complex = flags.indexOf('o') != -1; IValue ivalue; String q = StringUtils.format("{0}.{1}", //$NON-NLS-1$ new String[] { qualifier, name }); ivalue = new JSDebugValue(this, q, type, complex, stringValue); list.add(new JSDebugVariable(this, q, name, ivalue, convertVariableFlags(flags))); } } return (IVariable[]) list.toArray(new IVariable[list.size()]); } /** * evaluateExpression * * @param expression * @param context * @return Object * @throws DebugException */ protected Object evaluateExpression(String expression, IDebugElement context) throws DebugException { if (!isSuspended()) { return null; } String qualifier; Object result = null; // TODO: caching ? if (context instanceof JSDebugStackFrame) { qualifier = StringUtils.format(FRAME_0, ((JSDebugStackFrame) context).getFrameId()); } else if (context instanceof JSDebugVariable) { qualifier = ((JSDebugVariable) context).getQualifier(); } else { return result; } String[] args = connection.sendCommandAndWait(StringUtils.format(EVAL_0_1, new String[] { Util.encodeData(qualifier), Util.encodeData(expression) })); String status = args != null && args.length > 1 ? args[1] : null; if (RESULT.equals(status)) { String evalId = args[2]; String varData = args[3]; if (varData.endsWith("|")) //$NON-NLS-1$ { varData += "| "; //$NON-NLS-1$ } String[] subargs = varData.split(SUBARGS_SPLIT); int j = 0; String type = subargs[j++]; String flags = subargs[j++]; String stringValue = Util.decodeData(subargs[j++]); boolean complex = flags.indexOf('o') != -1; result = new JSDebugValue(this, StringUtils.format(EVAL_0, evalId), type, complex, stringValue); } else if (EXCEPTION.equals(status)) { result = new String[] { args[2] }; } return result; } /** * setValue * * @param variable * @param newValue * @return Object * @throws DebugException */ protected Object setValue(IVariable variable, IValue newValue) throws DebugException { if (!isSuspended()) { return null; } String qualifier; String vqualifier; Object result = null; if (variable instanceof JSDebugVariable) { qualifier = ((JSDebugVariable) variable).getQualifier(); } else { return result; } if (newValue instanceof JSDebugValue) { vqualifier = ((JSDebugValue) newValue).getQualifier(); } else { return result; } String[] args = connection.sendCommandAndWait(StringUtils.format(SET_VALUE_0_1, new String[] { Util.encodeData(qualifier), vqualifier })); if (args != null && args.length >= 3) { String status = args[1]; if (RESULT.equals(status)) { String[] subargs = args[2].split(SUBARGS_SPLIT); int j = 0; String type = subargs[j++]; String flags = subargs[j++]; String stringValue = Util.decodeData(subargs[j++]); boolean complex = flags.indexOf('o') != -1; result = new JSDebugValue(this, qualifier, type, complex, stringValue); } else if (EXCEPTION.equals(status)) { result = new String[] { args[2] }; } } return result; } /** * @see com.aptana.ide.debug.core.model.IJSDebugTarget#computeValueDetails(org.eclipse.debug.core.model.IValue) */ public String computeValueDetails(IValue value) throws DebugException { if (!isSuspended()) { return StringUtils.EMPTY; } String qualifier; String result = null; if (value instanceof JSDebugValue) { qualifier = ((JSDebugValue) value).getQualifier(); } else { return value.getValueString(); } String[] args = connection.sendCommandAndWait(StringUtils.format(DETAILS_0, Util.encodeData(qualifier))); if (args != null && args.length >= 3) { String status = args[1]; if (RESULT.equals(status)) { result = Util.decodeData(args[2]); } } if (result == null) { result = value.getValueString(); } if (result == null) { return StringUtils.EMPTY; } return result; } /** * findVariable * * @param variableName * @param context * @return IVariable * @throws DebugException */ protected IVariable findVariable(String variableName, IDebugElement context) throws DebugException { if (Util.checkVariable(variableName)) { Object result = evaluateExpression(variableName, context); if (result instanceof IValue) { return new JSDebugVariable(this, null/* TODO? */, variableName, (IValue) result); } } return null; } /** * handleBreakpoint * * @param breakpoint * @param operation */ private void handleBreakpoint(IBreakpoint breakpoint, String operation) { if (isDisconnected()) { return; } if (breakpoint.equals(skipOperationOnBreakpoint)) { skipOperationOnBreakpoint = null; return; } if (CREATE.equals(operation) && ignoreBreakpointCreation) { return; } if (supportsBreakpoint(breakpoint)) { if (breakpoint instanceof IJSLineBreakpoint) { handleLineBreakpoint((IJSLineBreakpoint) breakpoint, operation); } else if (breakpoint instanceof IJSExceptionBreakpoint) { handleExceptionBreakpoint((IJSExceptionBreakpoint) breakpoint, operation); } else if(breakpoint instanceof IJSWatchpoint) { handleWatchpoint((IJSWatchpoint) breakpoint, operation); } } } /** * handleLineBreakpoint * * @param breakpoint * @param operation */ private void handleLineBreakpoint(IJSLineBreakpoint breakpoint, String operation) { IMarker marker = breakpoint.getMarker(); URL url = null; String properties = StringUtils.EMPTY; try { URI uri = null; if (marker instanceof IUniformResourceMarker) { uri = ((IUniformResourceMarker) marker).getUniformResource().getURI(); } else { IResource resource = marker.getResource(); if (resource instanceof IWorkspaceRoot) { uri = URI.create((String) marker.getAttribute(IDebugConstants.BREAKPOINT_LOCATION)); } else { uri = resource.getLocation().makeAbsolute().toFile().toURI(); } } if (uri != null && resourceMapper != null) { uri = resourceMapper.resolveLocalURI(uri); } if (uri != null) { if ("dbgsource".equals(uri.getScheme())) //$NON-NLS-1$ { url = new URL(null, uri.toString(), DbgSourceURLStreamHandler.getDefault()); } else { url = uri.toURL(); } } } catch (MalformedURLException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } catch (CoreException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } int lineNumber = marker.getAttribute(IMarker.LINE_NUMBER, -1); if (lineNumber == -1) { return; } boolean remove = REMOVE.equals(operation); if (!remove) { boolean enabled = false; try { enabled = breakpoint.isEnabled(); } catch (CoreException ignore) { } int hitCount = marker.getAttribute(IDebugConstants.BREAKPOINT_HIT_COUNT, 0); boolean conditionEnabled = marker.getAttribute(IDebugConstants.BREAKPOINT_CONDITION_ENABLED, false); String condition = conditionEnabled ? marker.getAttribute(IDebugConstants.BREAKPOINT_CONDITION, StringUtils.EMPTY) : StringUtils.EMPTY; String suspendOnTrue = marker.getAttribute(IDebugConstants.BREAKPOINT_CONDITION_SUSPEND_ON_TRUE, true) ? "1" : "0"; //$NON-NLS-1$ //$NON-NLS-2$ properties = StringUtils.format("*{0}*{1}*{2}*{3}", //$NON-NLS-1$ new String[] { enabled ? "1" : "0", Integer.toString(hitCount), Util.encodeData(condition), suspendOnTrue }); //$NON-NLS-1$ //$NON-NLS-2$ } try { String[] args = connection.sendCommandAndWait(StringUtils.format(BREAKPOINT_0_1_2_3, new String[] { operation, Util.encodeData(url.toString()), Integer.toString(lineNumber), properties })); if (!remove && (args == null || args.length < 2 || !(operation + 'd').equals(args[1]))) { breakpoint.setEnabled(false); } } catch (CoreException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } } /** * handleExceptionBreakpoint * * @param breakpoint * @param operation */ private void handleExceptionBreakpoint(IJSExceptionBreakpoint breakpoint, String operation) { IMarker marker = breakpoint.getMarker(); String exceptionTypeName = marker.getAttribute(IDebugConstants.EXCEPTION_TYPE_NAME, StringUtils.EMPTY); if (exceptionTypeName == null || exceptionTypeName.length() == 0) { return; } boolean enabled = false; try { enabled = breakpoint.isEnabled(); } catch (CoreException ignore) { } if (!enabled) { operation = REMOVE; } enabled = !REMOVE.equals(operation); try { String[] args = connection.sendCommandAndWait(StringUtils.format(EXCEPTION_0_1, new String[] { operation, exceptionTypeName })); if (enabled && (args == null || !(operation + 'd').equals(args[1]))) { breakpoint.setEnabled(false); } } catch (CoreException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } } /** * handleWatchpoint * * @param watchpoint * @param operation */ private void handleWatchpoint(IJSWatchpoint watchpoint, String operation) { IMarker marker = watchpoint.getMarker(); String variableName = marker.getAttribute(IDebugConstants.WATCHPOINT_VARIABLE_ACCESSOR, StringUtils.EMPTY); boolean enabled = false; try { enabled = watchpoint.isEnabled(); } catch (CoreException ignore) { } if (!enabled) { if (CREATE.equals(operation)) { return; } operation = REMOVE; } enabled = !REMOVE.equals(operation); String kind = StringUtils.EMPTY; if (enabled) { try { if (watchpoint.isAccess()) { kind += 'r'; } if (watchpoint.isModification()) { kind += 'w'; } } catch (CoreException ignore) { } } try { String[] args = connection.sendCommandAndWait(StringUtils.format(WATCHPOINT_0_1_2, new String[] { operation, Util.encodeData(variableName), kind })); if (enabled && (args == null || args.length < 2 || !(operation + 'd').equals(args[1]))) { watchpoint.setEnabled(false); } } catch (CoreException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } } /** * findBreakpointAt * * @param filename * @param lineNumber * @return IBreakpoint */ protected IBreakpoint findBreakpointAt(String filename, int lineNumber) { IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager() .getBreakpoints(getModelIdentifier()); IBreakpoint breakpoint = findBreakpointIn(filename, lineNumber, breakpoints); if (breakpoint != null) { return breakpoint; } if (!runToLineBreakpoints.isEmpty()) { return findBreakpointIn(filename, lineNumber, (IBreakpoint[]) runToLineBreakpoints .toArray(new IBreakpoint[runToLineBreakpoints.size()])); } return null; } /** * findBreakpointIn * * @param filename * @param lineNumber * @param breakpoints * @return IBreakpoint */ protected IBreakpoint findBreakpointIn(String filename, int lineNumber, IBreakpoint[] breakpoints) { for (int i = 0; i < breakpoints.length; ++i) { IBreakpoint breakpoint = breakpoints[i]; if (getDebugTarget().supportsBreakpoint(breakpoint)) { if (breakpoint instanceof ILineBreakpoint) { try { IMarker marker = breakpoint.getMarker(); boolean fileMatched = false; if (marker instanceof IUniformResourceMarker) { URI breakpointURI = ((IUniformResourceMarker) marker).getUniformResource().getURI(); fileMatched = new URI(Util.fixupURI(filename)).equals(breakpointURI); } else if (marker.getResource() instanceof IWorkspaceRoot) { URI breakpointURI = URI.create((String) marker.getAttribute(IDebugConstants.BREAKPOINT_LOCATION)); fileMatched = new URI(Util.fixupURI(filename)).equals(breakpointURI); } else { IFile file = PathUtils.findWorkspaceFile(filename); if (file != null) { fileMatched = file.equals(marker.getResource()); } else { File breakpointFile = marker.getResource().getLocation().toFile(); fileMatched = new File(filename).equals(breakpointFile); } } if (fileMatched && ((ILineBreakpoint) breakpoint).getLineNumber() == lineNumber) { return breakpoint; } } catch (CoreException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } catch (URISyntaxException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } } } } return null; } /** * Returns absolute path for local files or leaves path(URL) unchanged * * @param sourceFile * @return String */ protected String resolveSourceFile(String sourceFile) { String resolved = (String) sourceResolveCache.get(sourceFile); if (resolved != null) { return resolved; } try { URI uri = new URI(sourceFile); String scheme = uri.getScheme(); if (FILE.equals(scheme)) { try { File osFile = new File(uri.getSchemeSpecificPart()); IPath canonicalPath = new Path(osFile.getCanonicalPath()); IFile file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(canonicalPath); if (file != null) { resolved = file.getLocation().toString(); } else { resolved = osFile.getAbsolutePath(); } } catch (IOException e) { } } else if (HTTP.equals(scheme) && resourceMapper != null) { File osFile = resourceMapper.resolveServerURL(uri.toURL()); if (osFile != null) { resolved = osFile.getAbsolutePath(); } } else if (JAVASCRIPT.equals(scheme)) { if (mainFile != null) { return mainFile; } } if (resolved != null) { sourceResolveCache.put(sourceFile, resolved); return resolved; } } catch (URISyntaxException e) { if ( sourceFile.startsWith(JAVASCRIPT_SCHEME) ) { if (mainFile != null) { return mainFile; } } IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } catch (MalformedURLException e) { IdeLog.logError(JSDebugPlugin.getDefault(), StringUtils.EMPTY, e); } return sourceFile; } /** * findSourceResource * * @param sourceFile * @return Object * @throws CoreException */ private Object findSourceResource(String sourceFile) throws CoreException { ISourceLocator locator = launch.getSourceLocator(); if (locator instanceof ISourceLookupDirector) { ISourceLookupDirector lookupDirector = (ISourceLookupDirector) locator; Object[] result = lookupDirector.findSourceElements(sourceFile); if (result != null && result.length > 0) { Object resource = result[0]; if (resource instanceof IResource) { return resource; } if (!(resource instanceof IUniformResource) && resource instanceof IAdaptable) { Object adopted = ((IAdaptable) resource).getAdapter(IUniformResource.class); if (adopted != null) { resource = adopted; } } return resource; } } return null; } /** * convertVariableFlags * * @param string * @return int */ private static int convertVariableFlags(String string) { int flags = 0; char[] chars = string.toCharArray(); for (int i = 0; i < chars.length; ++i) { switch (chars[i]) { case 'w': flags |= JSDebugVariable.FLAGS_MODIFIABLE; break; case 'c': flags |= JSDebugVariable.FLAGS_CONST; break; case 'l': flags |= JSDebugVariable.FLAGS_LOCAL; break; case 'a': flags |= JSDebugVariable.FLAGS_ARGUMENT; break; case 'e': flags |= JSDebugVariable.FLAGS_EXCEPTION; break; default: break; } } return flags; } /** * @see com.aptana.ide.debug.core.model.IJSDebugTarget#getTopScriptElements() */ public IJSScriptElement[] getTopScriptElements() { return (IJSScriptElement[]) topScriptElements.values().toArray(new IJSScriptElement[topScriptElements.size()]); } }