| /******************************************************************************* |
| * Copyright (c) 2010, 2011 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 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.debug.internal.crossfire.jsdi; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| 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.Vector; |
| |
| import org.eclipse.core.resources.IMarkerDelta; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.QualifiedName; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.IBreakpointListener; |
| import org.eclipse.debug.core.IBreakpointManager; |
| import org.eclipse.debug.core.model.IBreakpoint; |
| import org.eclipse.wst.jsdt.core.JavaScriptCore; |
| import org.eclipse.wst.jsdt.debug.core.breakpoints.IJavaScriptLineBreakpoint; |
| import org.eclipse.wst.jsdt.debug.core.jsdi.BooleanValue; |
| import org.eclipse.wst.jsdt.debug.core.jsdi.NullValue; |
| import org.eclipse.wst.jsdt.debug.core.jsdi.NumberValue; |
| import org.eclipse.wst.jsdt.debug.core.jsdi.StringValue; |
| import org.eclipse.wst.jsdt.debug.core.jsdi.UndefinedValue; |
| import org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine; |
| import org.eclipse.wst.jsdt.debug.core.jsdi.event.EventQueue; |
| import org.eclipse.wst.jsdt.debug.core.jsdi.request.EventRequestManager; |
| import org.eclipse.wst.jsdt.debug.core.model.JavaScriptDebugModel; |
| import org.eclipse.wst.jsdt.debug.internal.core.JavaScriptDebugPlugin; |
| import org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptLineBreakpoint; |
| import org.eclipse.wst.jsdt.debug.internal.crossfire.Constants; |
| import org.eclipse.wst.jsdt.debug.internal.crossfire.CrossFirePlugin; |
| import org.eclipse.wst.jsdt.debug.internal.crossfire.Tracing; |
| import org.eclipse.wst.jsdt.debug.internal.crossfire.event.CFEventQueue; |
| import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.Attributes; |
| import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.CFEventPacket; |
| import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.CFRequestPacket; |
| import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.CFResponsePacket; |
| import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.Commands; |
| import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.JSON; |
| import org.eclipse.wst.jsdt.debug.transport.DebugSession; |
| import org.eclipse.wst.jsdt.debug.transport.exception.DisconnectedException; |
| import org.eclipse.wst.jsdt.debug.transport.exception.TimeoutException; |
| |
| /** |
| * Default CrossFire implementation of {@link VirtualMachine} |
| * |
| * @since 1.0 |
| */ |
| public class CFVirtualMachine extends CFMirror implements VirtualMachine, IBreakpointListener { |
| |
| private final NullValue nullvalue = new CFNullValue(this); |
| private final UndefinedValue undefinedvalue = new CFUndefinedValue(this); |
| |
| private final DebugSession session; |
| private final CFEventRequestManager ermanager = new CFEventRequestManager(this); |
| private final CFEventQueue queue = new CFEventQueue(this, ermanager); |
| private boolean disconnected = false; |
| |
| private Map threads = null; |
| private Map scripts = null; |
| private Map breakpointHandles = new HashMap(); |
| |
| /** |
| * Constructor |
| * |
| * @param session |
| */ |
| public CFVirtualMachine(DebugSession session) { |
| super(); |
| this.session = session; |
| initializeBreakpoints(); |
| } |
| |
| /** |
| * Synchronizes the set of breakpoints between client and server |
| */ |
| void initializeBreakpoints() { |
| IBreakpointManager manager = DebugPlugin.getDefault().getBreakpointManager(); |
| manager.addBreakpointListener(this); |
| IBreakpoint[] managerBreakpoints = manager.getBreakpoints(JavaScriptDebugModel.MODEL_ID); |
| Vector allBps = new Vector(); |
| for (int i = 0; i < managerBreakpoints.length; i++) { |
| IBreakpoint current = managerBreakpoints[i]; |
| if (current instanceof JavaScriptLineBreakpoint) { |
| try { |
| JavaScriptLineBreakpoint breakpoint = (JavaScriptLineBreakpoint)current; |
| |
| IResource resource = breakpoint.getMarker().getResource(); |
| QualifiedName qName = new QualifiedName(JavaScriptCore.PLUGIN_ID, "scriptURL"); //$NON-NLS-1$ |
| String url = resource.getPersistentProperty(qName); |
| if (url == null) { |
| String path = breakpoint.getScriptPath(); |
| url = JavaScriptDebugPlugin.getExternalScriptPath(new Path(path)); |
| } |
| |
| if (url != null) { |
| Map location = new HashMap(); |
| location.put(Attributes.LINE, new Integer(breakpoint.getLineNumber())); |
| location.put(Attributes.URL, url); |
| Map attributes = new HashMap(); |
| if (breakpoint.isConditionEnabled()) { |
| String condition = breakpoint.getCondition(); |
| if (condition != null) { |
| attributes.put(Attributes.CONDITION, condition); |
| } |
| } |
| int hitCount = breakpoint.getHitCount(); |
| if (hitCount != -1) { |
| attributes.put(Attributes.HIT_COUNT, new Integer(hitCount)); |
| } |
| Map bpMap = new HashMap(); |
| bpMap.put(Attributes.TYPE, Attributes.LINE); |
| bpMap.put(Attributes.LOCATION, location); |
| bpMap.put(Attributes.ATTRIBUTES, attributes); |
| allBps.add(bpMap); |
| } |
| } catch (CoreException e) { |
| CrossFirePlugin.log(e); |
| } |
| } |
| } |
| if (allBps.size() > 0) { |
| CFRequestPacket request = new CFRequestPacket(Commands.SET_BREAKPOINTS, null); |
| request.setArgument(Attributes.BREAKPOINTS, Arrays.asList(allBps.toArray())); |
| CFResponsePacket response = ((CFVirtualMachine)virtualMachine()).sendRequest(request); |
| if (!response.isSuccess()) { |
| if(TRACE) { |
| Tracing.writeString("VM [failed setbreakpoints request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| CFRequestPacket request = new CFRequestPacket(Commands.GET_BREAKPOINTS, null); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| List list = (List) response.getBody().get(Attributes.BREAKPOINTS); |
| Map bp = null; |
| for (Iterator i = list.iterator(); i.hasNext();) { |
| bp = (Map) i.next(); |
| addBreakpoint(bp); |
| } |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed getbreakpoints request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Sends the <code>getbreakpoint</code> request for the given breakpoint handle |
| * @param handle |
| * @return the {@link RemoteBreakpoint} representing the request or <code>null</code> if the breakpoint could not be found |
| */ |
| public RemoteBreakpoint getBreakpoint(Number handle) { |
| CFRequestPacket request = new CFRequestPacket(Commands.GET_BREAKPOINTS, null); |
| request.setArgument(Attributes.HANDLES, Arrays.asList(new Number[] {handle})); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| List list = (List)response.getBody().get(Attributes.BREAKPOINTS); |
| if (list != null && list.size() > 0) { |
| addBreakpoint((Map)list.get(0)); |
| } |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed getbreakpoint request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| return (RemoteBreakpoint) breakpointHandles.get(handle); |
| } |
| |
| /** |
| * Sends the <code>changebreakpoint</code> request for the given breakpoint handle to change the given map of attributes |
| * @param handle |
| * @param attributes |
| * @return the changed {@link RemoteBreakpoint} object or <code>null</code> if the request failed |
| */ |
| public RemoteBreakpoint changeBreakpoint(Number handle, Map attributes) { |
| CFRequestPacket request = new CFRequestPacket(Commands.CHANGE_BREAKPOINTS, null); |
| request.setArgument(Attributes.HANDLES, Arrays.asList(new Number[] {handle})); |
| request.setArgument(Attributes.ATTRIBUTES, attributes); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| updateBreakpoint(response.getBody()); |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed getbreakpoint request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| return (RemoteBreakpoint) breakpointHandles.get(handle); |
| } |
| |
| /** |
| * Called via reflection to determine if the VM supports suspend on script loads |
| * @return <code>true</code> if this VM can suspend when a script loads <code>false</code> otherwise |
| */ |
| public boolean supportsSuspendOnScriptLoads() { |
| return false; |
| } |
| |
| /** |
| * Add the breakpoint described by the given JSON to the handles list |
| * @param json |
| */ |
| public void addBreakpoint(Map json) { |
| if(json != null) { |
| Number handle = (Number) json.get(Attributes.HANDLE); |
| if(handle != null) { |
| RemoteBreakpoint bp = (RemoteBreakpoint) breakpointHandles.get(handle); |
| if(bp == null) { |
| bp = new RemoteBreakpoint(this, |
| handle, |
| (Map) json.get(Attributes.LOCATION), |
| (Map) json.get(Attributes.ATTRIBUTES), |
| (String)json.get(Attributes.TYPE)); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Locates the breakpoint for the handle given in the map and updates its attributes |
| * |
| * @param json the JSON map, cannot be <code>null</code> |
| */ |
| public void updateBreakpoint(Map json) { |
| if(json != null) { |
| Number handle = (Number) json.get(Attributes.HANDLE); |
| if(handle != null) { |
| RemoteBreakpoint bp = (RemoteBreakpoint) breakpointHandles.get(handle); |
| if(bp != null) { |
| bp.setEnabled(RemoteBreakpoint.getEnabled(json)); |
| bp.setCondition(RemoteBreakpoint.getCondition(json)); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Adds or removes the breakpoint from the cache based on the <code>isset</code> attribute |
| * |
| * @param json the JSON map, cannot be <code>null</code> |
| */ |
| public void toggleBreakpoint(Map json) { |
| if(json != null) { |
| Boolean isset = (Boolean)json.get(Attributes.SET); |
| if(isset != null && isset.booleanValue()) { |
| updateBreakpoint(json); |
| } |
| else { |
| Number handle = (Number) json.get(Attributes.HANDLE); |
| breakpointHandles.remove(handle); |
| } |
| } |
| } |
| |
| /** |
| * @return the 'readiness' of the VM - i.e. is it in a state to process requests, etc |
| */ |
| boolean ready() { |
| return !disconnected; |
| } |
| |
| /** |
| * Sends an <code>createcontext</code> request for the given URL and returns the status of the request. |
| * |
| * @param url the URL to open / update in the remote target, <code>null</code> is not accepted |
| * @return <code>true</code> if the request was successful, <code>false</code> otherwise |
| */ |
| boolean createContext(String url) { |
| if(url != null && ready()) { |
| CFRequestPacket request = new CFRequestPacket(Commands.CREATE_CONTEXT, null); |
| request.getArguments().put(Attributes.URL, url); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| return true; |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed createcontext request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Sends the frame request |
| * @param contextid |
| * @param index |
| * @param includescopes |
| * @return |
| */ |
| CFStackFrame getFrame(String contextid, int index, boolean includescopes) { |
| if(index > -1) { |
| CFThreadReference thread = findThread(contextid); |
| if(thread != null) { |
| CFRequestPacket request = new CFRequestPacket(Commands.FRAME, thread.id()); |
| request.setArgument(Attributes.INDEX, new Integer(index)); |
| request.setArgument(Attributes.INCLUDE_SCOPES, new Boolean(includescopes)); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| return new CFStackFrame(this, thread, response.getBody()); |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed frame request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| } |
| return null; |
| |
| } |
| |
| /** |
| * Sends a request to enable the tool with the given name in the remote Crossfire server |
| * |
| * @param tools the array of tool names to enable, <code>null</code> is not allowed |
| * @return <code>true</code> if the server reports the tool became enabled, <code>false</code> otherwise |
| */ |
| boolean enableTools(String[] tools) { |
| if(tools != null && tools.length > 0 && ready()) { |
| CFRequestPacket request = new CFRequestPacket(Commands.ENABLE_TOOLS, null); |
| request.getArguments().put(Attributes.TOOLS, Arrays.asList(tools)); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| //TODO handle the tool being enabled |
| return true; |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed enabletool request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Sends a request to disable the tool with the given name in the remote Crossfire server |
| * |
| * @param tools the array of tool names to disable, <code>null</code> is not allowed |
| * @return <code>true</code> if the server reports the tool became disabled, <code>false</code> otherwise |
| */ |
| boolean disableTools(String[] tools) { |
| if(tools != null && tools.length > 0 && ready()) { |
| CFRequestPacket request = new CFRequestPacket(Commands.DISABLE_TOOLS, null); |
| request.getArguments().put(Attributes.TOOLS, Arrays.asList(tools)); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| //TODO handle the tool being enabled |
| return true; |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed disabletool request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns the complete listing of tools from Crossfire regardless of their enabled state. |
| * |
| * @return the listing of tools or an empty list, never <code>null</code> |
| */ |
| List allTools() { |
| if(ready()) { |
| CFRequestPacket request = new CFRequestPacket(Commands.GET_TOOLS, null); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| //TODO do we want to make these first-class objects in our model so we can track state, etc for tools? |
| List tools = (List) response.getBody().get(Attributes.TOOLS); |
| if(tools != null) { |
| return tools; |
| } |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed alltools request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| return Collections.EMPTY_LIST; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#resume() |
| */ |
| public void resume() { |
| if(ready()) { |
| if(threads != null) { |
| Entry entry = null; |
| for (Iterator iter = threads.entrySet().iterator(); iter.hasNext();) { |
| entry = (Entry) iter.next(); |
| CFThreadReference thread = (CFThreadReference) entry.getValue(); |
| if(thread.isSuspended()) { |
| CFRequestPacket request = new CFRequestPacket(Commands.CONTINUE, thread.id()); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| if(thread.isSuspended()) { |
| thread.markSuspended(false); |
| } |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed continue request][context: "+thread.id()+"]: "+JSON.serialize(request)); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#suspend() |
| */ |
| public void suspend() { |
| if(ready()) { |
| if(threads != null) { |
| Entry entry = null; |
| for (Iterator iter = threads.entrySet().iterator(); iter.hasNext();) { |
| entry = (Entry) iter.next(); |
| CFThreadReference thread = (CFThreadReference) entry.getValue(); |
| if(thread.isRunning()) { |
| CFRequestPacket request = new CFRequestPacket(Commands.SUSPEND, thread.id()); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| if(!thread.isSuspended()) { |
| thread.markSuspended(true); |
| } |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed suspend request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#terminate() |
| */ |
| public void terminate() { |
| if(ready()) { |
| disconnectVM(); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#name() |
| */ |
| public String name() { |
| return Messages.vm_name; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#description() |
| */ |
| public String description() { |
| return Messages.crossfire_vm; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#version() |
| */ |
| public synchronized String version() { |
| if(ready()) { |
| CFRequestPacket request = new CFRequestPacket(Commands.VERSION, null); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| Map json = response.getBody(); |
| return (String) json.get(Commands.VERSION); |
| } |
| if(TRACE) { |
| Tracing.writeString("VM [failed version request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| return Constants.UNKNOWN; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#allThreads() |
| */ |
| public synchronized List allThreads() { |
| if(threads == null) { |
| threads = new HashMap(); |
| CFRequestPacket request = new CFRequestPacket(Commands.LISTCONTEXTS, null); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| List contexts = (List) response.getBody().get(Attributes.CONTEXTS); |
| for (Iterator iter = contexts.iterator(); iter.hasNext();) { |
| Map json = (Map) iter.next(); |
| CFThreadReference thread = new CFThreadReference(this, json); |
| threads.put(thread.id(), thread); |
| } |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed allthreads request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| return new ArrayList(threads.values()); |
| } |
| |
| /** |
| * Adds a thread to the listing |
| * |
| * @param id |
| * @param url |
| * @return the new thread |
| */ |
| public CFThreadReference addThread(String id, String url) { |
| if(threads == null) { |
| allThreads(); |
| } |
| CFThreadReference thread = new CFThreadReference(this, id, url); |
| threads.put(thread.id(), thread); |
| return thread; |
| } |
| |
| /** |
| * Removes the thread with the given id |
| * |
| * @param id the id of the thread to remove |
| */ |
| public void removeThread(String id) { |
| if(threads != null) { |
| Object obj = threads.remove(id); |
| if(TRACE && obj == null) { |
| Tracing.writeString("VM [failed to remove thread]: "+id); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /** |
| * Returns the thread with the given id or <code>null</code> |
| * |
| * @param id |
| * @return the thread or <code>null</code> |
| */ |
| public synchronized CFThreadReference findThread(String id) { |
| if(threads == null) { |
| allThreads(); |
| } |
| CFThreadReference thread = (CFThreadReference) threads.get(id); |
| if(TRACE && thread == null) { |
| Tracing.writeString("VM [failed to find thread]: "+id); //$NON-NLS-1$ |
| } |
| return thread; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#allScripts() |
| */ |
| public synchronized List allScripts() { |
| if(scripts == null) { |
| scripts = new HashMap(); |
| List threads = allThreads(); |
| for (Iterator iter = threads.iterator(); iter.hasNext();) { |
| CFThreadReference thread = (CFThreadReference) iter.next(); |
| CFRequestPacket request = new CFRequestPacket(Commands.SCRIPTS, thread.id()); |
| request.setArgument(Attributes.INCLUDE_SOURCE, Boolean.FALSE); |
| CFResponsePacket response = sendRequest(request); |
| if(response.isSuccess()) { |
| List scriptz = (List) response.getBody().get(Attributes.SCRIPTS); |
| for (Iterator iter2 = scriptz.iterator(); iter2.hasNext();) { |
| Map smap = (Map) iter2.next(); |
| if(smap != null) { |
| CFScriptReference script = new CFScriptReference(this, thread.id(), smap); |
| scripts.put(script.url(), script); |
| } |
| } |
| } |
| else if(TRACE) { |
| Tracing.writeString("VM [failed scripts request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
| } |
| } |
| if(scripts.size() < 1) { |
| scripts = null; |
| return Collections.EMPTY_LIST; |
| } |
| } |
| return new ArrayList(scripts.values()); |
| } |
| |
| /** |
| * Returns the script with the given url or <code>null</code> |
| * |
| * @param url |
| * @return the thread or <code>null</code> |
| */ |
| public synchronized CFScriptReference findScript(String url) { |
| if(scripts == null) { |
| allScripts(); |
| } |
| CFScriptReference script = null; |
| if(scripts != null) { |
| //the scripts collection can be null after a call to allScripts() |
| //when the remote target had no scripts loaded. In this case |
| //we do not keep the initialized collection so that any successive |
| //calls the this method or allScripts will cause the remote target |
| //to be asked for all of its scripts |
| script = (CFScriptReference) scripts.get(url); |
| } |
| //if we find we have a script id that is not cached, we should try a lookup + add in the vm |
| if(script == null) { |
| if(TRACE) { |
| Tracing.writeString("VM [failed to find script]: "+url); //$NON-NLS-1$ |
| } |
| } |
| return script; |
| } |
| |
| /** |
| * Adds the given script to the listing |
| * |
| * @param context_id |
| * @param json |
| * |
| * @return the new script |
| */ |
| public CFScriptReference addScript(String context_id, Map json) { |
| if(scripts == null) { |
| allScripts(); |
| } |
| CFScriptReference script = new CFScriptReference(this, context_id, json); |
| scripts.put(script.url(), script); |
| return script; |
| } |
| |
| /** |
| * Removes all {@link CFScriptReference}s from the cache when the associated context is destroyed |
| * |
| * @param contextid |
| */ |
| public void removeScriptsForContext(String contextid) { |
| if(scripts != null) { |
| Entry e = null; |
| for(Iterator i = scripts.entrySet().iterator(); i.hasNext();) { |
| e = (Entry) i.next(); |
| if(contextid.equals(((CFScriptReference)e.getValue()).context())) { |
| i.remove(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Removes the script with the given url from the listing |
| * |
| * @param url the script to remove |
| */ |
| public void removeScript(String url) { |
| if(scripts != null) { |
| Object obj = scripts.remove(url); |
| if(TRACE && obj == null) { |
| Tracing.writeString("VM [failed to remove script]: "+url); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#dispose() |
| */ |
| public synchronized void dispose() { |
| try { |
| if(TRACE) { |
| Tracing.writeString("VM [disposing]"); //$NON-NLS-1$ |
| } |
| queue.dispose(); |
| ermanager.dispose(); |
| } |
| finally { |
| //fall-back in case the VM has been disposed but not disconnected |
| disconnectVM(); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#mirrorOfUndefined() |
| */ |
| public UndefinedValue mirrorOfUndefined() { |
| return undefinedvalue; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#mirrorOfNull() |
| */ |
| public NullValue mirrorOfNull() { |
| return nullvalue; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#mirrorOf(boolean) |
| */ |
| public BooleanValue mirrorOf(boolean bool) { |
| return new CFBooleanValue(this, bool); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#mirrorOf(java.lang.Number) |
| */ |
| public NumberValue mirrorOf(Number number) { |
| return new CFNumberValue(this, number); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#mirrorOf(java.lang.String) |
| */ |
| public StringValue mirrorOf(String string) { |
| return new CFStringValue(this, string); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#eventRequestManager() |
| */ |
| public synchronized EventRequestManager eventRequestManager() { |
| return ermanager; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#eventQueue() |
| */ |
| public synchronized EventQueue eventQueue() { |
| return queue; |
| } |
| |
| /** |
| * Receives an {@link CFEventPacket} from the underlying {@link DebugSession}, |
| * waiting for the {@link VirtualMachine#DEFAULT_TIMEOUT}. |
| * |
| * @return the next {@link CFEventPacket} never <code>null</code> |
| * @throws TimeoutException |
| * @throws DisconnectedException |
| */ |
| public CFEventPacket receiveEvent() throws TimeoutException, DisconnectedException { |
| return (CFEventPacket) session.receive(CFEventPacket.EVENT, DEFAULT_TIMEOUT); |
| } |
| |
| /** |
| * Receives an {@link CFEventPacket} from the underlying {@link DebugSession}, |
| * waiting for the {@link VirtualMachine#DEFAULT_TIMEOUT}. |
| * @param timeout |
| * @return the next {@link CFEventPacket} never <code>null</code> |
| * @throws TimeoutException |
| * @throws DisconnectedException |
| */ |
| public CFEventPacket receiveEvent(int timeout) throws TimeoutException, DisconnectedException { |
| return (CFEventPacket) session.receive(CFEventPacket.EVENT, timeout); |
| } |
| |
| /** |
| * Sends a request to the underlying {@link DebugSession}, waiting |
| * for the {@link VirtualMachine#DEFAULT_TIMEOUT}. |
| * |
| * @param request |
| * @return the {@link CFResponsePacket} for the request |
| */ |
| public CFResponsePacket sendRequest(CFRequestPacket request) { |
| try { |
| session.send(request); |
| return (CFResponsePacket) session.receiveResponse(request.getSequence(), 3000); |
| } |
| catch(DisconnectedException de) { |
| disconnectVM(); |
| handleException(de.getMessage(), (de.getCause() == null ? de : de.getCause())); |
| } |
| catch(TimeoutException te) { |
| CrossFirePlugin.log(te); |
| } |
| return CFResponsePacket.FAILED; |
| } |
| |
| /** |
| * disconnects the VM |
| */ |
| public synchronized void disconnectVM() { |
| if (disconnected) { |
| // no-op it is already disconnected |
| if(TRACE) { |
| Tracing.writeString("VM [already disconnected]"); //$NON-NLS-1$ |
| } |
| return; |
| } |
| if(TRACE) { |
| Tracing.writeString("VM [disconnecting]"); //$NON-NLS-1$ |
| } |
| try { |
| if(threads != null) { |
| threads.clear(); |
| } |
| if(scripts != null) { |
| scripts.clear(); |
| } |
| this.queue.dispose(); |
| this.ermanager.dispose(); |
| this.session.dispose(); |
| } finally { |
| disconnected = true; |
| DebugPlugin.getDefault().getBreakpointManager().removeBreakpointListener(this); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint) |
| */ |
| public void breakpointAdded(IBreakpoint breakpoint) { |
| if (JavaScriptDebugModel.MODEL_ID.equals(breakpoint.getModelIdentifier())) { |
| if (breakpoint instanceof IJavaScriptLineBreakpoint) { |
| //TODO check handle map send request as needed |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @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 (JavaScriptDebugModel.MODEL_ID.equals(breakpoint.getModelIdentifier())) { |
| if (breakpoint instanceof IJavaScriptLineBreakpoint) { |
| //TODO check handle map send request as needed |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @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) { |
| if (JavaScriptDebugModel.MODEL_ID.equals(breakpoint.getModelIdentifier())) { |
| if (breakpoint instanceof IJavaScriptLineBreakpoint) { |
| //TODO check handle map send request as needed |
| } |
| } |
| } |
| } |