Added
Link Here
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 2010, 2011 IBM Corporation and others. |
3 |
* All rights reserved. This program and the accompanying materials |
4 |
* are made available under the terms of the Eclipse Public License v1.0 |
5 |
* which accompanies this distribution, and is available at |
6 |
* http://www.eclipse.org/legal/epl-v10.html |
7 |
* |
8 |
* Contributors: |
9 |
* IBM Corporation - initial API and implementation |
10 |
*******************************************************************************/ |
11 |
package org.eclipse.wst.jsdt.debug.internal.crossfire.jsdi; |
12 |
|
13 |
import java.util.ArrayList; |
14 |
import java.util.Arrays; |
15 |
import java.util.Collections; |
16 |
import java.util.HashMap; |
17 |
import java.util.Iterator; |
18 |
import java.util.List; |
19 |
import java.util.Map; |
20 |
import java.util.Map.Entry; |
21 |
import java.util.Vector; |
22 |
|
23 |
import org.eclipse.core.resources.IMarkerDelta; |
24 |
import org.eclipse.core.resources.IResource; |
25 |
import org.eclipse.core.runtime.CoreException; |
26 |
import org.eclipse.core.runtime.Path; |
27 |
import org.eclipse.core.runtime.QualifiedName; |
28 |
import org.eclipse.debug.core.DebugPlugin; |
29 |
import org.eclipse.debug.core.IBreakpointListener; |
30 |
import org.eclipse.debug.core.IBreakpointManager; |
31 |
import org.eclipse.debug.core.model.IBreakpoint; |
32 |
import org.eclipse.wst.jsdt.core.JavaScriptCore; |
33 |
import org.eclipse.wst.jsdt.debug.core.breakpoints.IJavaScriptLineBreakpoint; |
34 |
import org.eclipse.wst.jsdt.debug.core.jsdi.BooleanValue; |
35 |
import org.eclipse.wst.jsdt.debug.core.jsdi.NullValue; |
36 |
import org.eclipse.wst.jsdt.debug.core.jsdi.NumberValue; |
37 |
import org.eclipse.wst.jsdt.debug.core.jsdi.StringValue; |
38 |
import org.eclipse.wst.jsdt.debug.core.jsdi.UndefinedValue; |
39 |
import org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine; |
40 |
import org.eclipse.wst.jsdt.debug.core.jsdi.event.EventQueue; |
41 |
import org.eclipse.wst.jsdt.debug.core.jsdi.request.EventRequestManager; |
42 |
import org.eclipse.wst.jsdt.debug.core.model.JavaScriptDebugModel; |
43 |
import org.eclipse.wst.jsdt.debug.internal.core.JavaScriptDebugPlugin; |
44 |
import org.eclipse.wst.jsdt.debug.internal.core.breakpoints.JavaScriptLineBreakpoint; |
45 |
import org.eclipse.wst.jsdt.debug.internal.crossfire.Constants; |
46 |
import org.eclipse.wst.jsdt.debug.internal.crossfire.CrossFirePlugin; |
47 |
import org.eclipse.wst.jsdt.debug.internal.crossfire.Tracing; |
48 |
import org.eclipse.wst.jsdt.debug.internal.crossfire.event.CFEventQueue; |
49 |
import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.Attributes; |
50 |
import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.CFEventPacket; |
51 |
import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.CFRequestPacket; |
52 |
import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.CFResponsePacket; |
53 |
import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.Commands; |
54 |
import org.eclipse.wst.jsdt.debug.internal.crossfire.transport.JSON; |
55 |
import org.eclipse.wst.jsdt.debug.transport.DebugSession; |
56 |
import org.eclipse.wst.jsdt.debug.transport.exception.DisconnectedException; |
57 |
import org.eclipse.wst.jsdt.debug.transport.exception.TimeoutException; |
58 |
|
59 |
/** |
60 |
* Default CrossFire implementation of {@link VirtualMachine} |
61 |
* |
62 |
* @since 1.0 |
63 |
*/ |
64 |
public class CFVirtualMachine extends CFMirror implements VirtualMachine, IBreakpointListener { |
65 |
|
66 |
private final NullValue nullvalue = new CFNullValue(this); |
67 |
private final UndefinedValue undefinedvalue = new CFUndefinedValue(this); |
68 |
|
69 |
private final DebugSession session; |
70 |
private final CFEventRequestManager ermanager = new CFEventRequestManager(this); |
71 |
private final CFEventQueue queue = new CFEventQueue(this, ermanager); |
72 |
private boolean disconnected = false; |
73 |
|
74 |
private Map threads = null; |
75 |
private Map scripts = null; |
76 |
private Map breakpointHandles = new HashMap(); |
77 |
|
78 |
/** |
79 |
* Constructor |
80 |
* |
81 |
* @param session |
82 |
*/ |
83 |
public CFVirtualMachine(DebugSession session) { |
84 |
super(); |
85 |
this.session = session; |
86 |
initializeBreakpoints(); |
87 |
} |
88 |
|
89 |
/** |
90 |
* Synchronizes the set of breakpoints between client and server |
91 |
*/ |
92 |
void initializeBreakpoints() { |
93 |
IBreakpointManager manager = DebugPlugin.getDefault().getBreakpointManager(); |
94 |
manager.addBreakpointListener(this); |
95 |
IBreakpoint[] managerBreakpoints = manager.getBreakpoints(JavaScriptDebugModel.MODEL_ID); |
96 |
Vector allBps = new Vector(); |
97 |
for (int i = 0; i < managerBreakpoints.length; i++) { |
98 |
IBreakpoint current = managerBreakpoints[i]; |
99 |
if (current instanceof JavaScriptLineBreakpoint) { |
100 |
try { |
101 |
JavaScriptLineBreakpoint breakpoint = (JavaScriptLineBreakpoint)current; |
102 |
|
103 |
IResource resource = breakpoint.getMarker().getResource(); |
104 |
QualifiedName qName = new QualifiedName(JavaScriptCore.PLUGIN_ID, "scriptURL"); //$NON-NLS-1$ |
105 |
String url = resource.getPersistentProperty(qName); |
106 |
if (url == null) { |
107 |
String path = breakpoint.getScriptPath(); |
108 |
url = JavaScriptDebugPlugin.getExternalScriptPath(new Path(path)); |
109 |
} |
110 |
|
111 |
if (url != null) { |
112 |
Map location = new HashMap(); |
113 |
location.put(Attributes.LINE, new Integer(breakpoint.getLineNumber())); |
114 |
location.put(Attributes.URL, url); |
115 |
Map attributes = new HashMap(); |
116 |
if (breakpoint.isConditionEnabled()) { |
117 |
String condition = breakpoint.getCondition(); |
118 |
if (condition != null) { |
119 |
attributes.put(Attributes.CONDITION, condition); |
120 |
} |
121 |
} |
122 |
int hitCount = breakpoint.getHitCount(); |
123 |
if (hitCount != -1) { |
124 |
attributes.put(Attributes.HIT_COUNT, new Integer(hitCount)); |
125 |
} |
126 |
Map bpMap = new HashMap(); |
127 |
bpMap.put(Attributes.TYPE, Attributes.LINE); |
128 |
bpMap.put(Attributes.LOCATION, location); |
129 |
bpMap.put(Attributes.ATTRIBUTES, attributes); |
130 |
allBps.add(bpMap); |
131 |
} |
132 |
} catch (CoreException e) { |
133 |
CrossFirePlugin.log(e); |
134 |
} |
135 |
} |
136 |
} |
137 |
if (allBps.size() > 0) { |
138 |
CFRequestPacket request = new CFRequestPacket(Commands.SET_BREAKPOINTS, null); |
139 |
request.setArgument(Attributes.BREAKPOINTS, Arrays.asList(allBps.toArray())); |
140 |
CFResponsePacket response = ((CFVirtualMachine)virtualMachine()).sendRequest(request); |
141 |
if (!response.isSuccess()) { |
142 |
if(TRACE) { |
143 |
Tracing.writeString("VM [failed setbreakpoints request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
144 |
} |
145 |
} |
146 |
} |
147 |
|
148 |
CFRequestPacket request = new CFRequestPacket(Commands.GET_BREAKPOINTS, null); |
149 |
CFResponsePacket response = sendRequest(request); |
150 |
if(response.isSuccess()) { |
151 |
List list = (List) response.getBody().get(Attributes.BREAKPOINTS); |
152 |
Map bp = null; |
153 |
for (Iterator i = list.iterator(); i.hasNext();) { |
154 |
bp = (Map) i.next(); |
155 |
addBreakpoint(bp); |
156 |
} |
157 |
} |
158 |
else if(TRACE) { |
159 |
Tracing.writeString("VM [failed getbreakpoints request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
160 |
} |
161 |
} |
162 |
|
163 |
/** |
164 |
* Sends the <code>getbreakpoint</code> request for the given breakpoint handle |
165 |
* @param handle |
166 |
* @return the {@link RemoteBreakpoint} representing the request or <code>null</code> if the breakpoint could not be found |
167 |
*/ |
168 |
public RemoteBreakpoint getBreakpoint(Number handle) { |
169 |
CFRequestPacket request = new CFRequestPacket(Commands.GET_BREAKPOINTS, null); |
170 |
request.setArgument(Attributes.HANDLES, Arrays.asList(new Number[] {handle})); |
171 |
CFResponsePacket response = sendRequest(request); |
172 |
if(response.isSuccess()) { |
173 |
List list = (List)response.getBody().get(Attributes.BREAKPOINTS); |
174 |
if (list != null && list.size() > 0) { |
175 |
addBreakpoint((Map)list.get(0)); |
176 |
} |
177 |
} |
178 |
else if(TRACE) { |
179 |
Tracing.writeString("VM [failed getbreakpoint request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
180 |
} |
181 |
return (RemoteBreakpoint) breakpointHandles.get(handle); |
182 |
} |
183 |
|
184 |
/** |
185 |
* Sends the <code>changebreakpoint</code> request for the given breakpoint handle to change the given map of attributes |
186 |
* @param handle |
187 |
* @param attributes |
188 |
* @return the changed {@link RemoteBreakpoint} object or <code>null</code> if the request failed |
189 |
*/ |
190 |
public RemoteBreakpoint changeBreakpoint(Number handle, Map attributes) { |
191 |
CFRequestPacket request = new CFRequestPacket(Commands.CHANGE_BREAKPOINTS, null); |
192 |
request.setArgument(Attributes.HANDLES, Arrays.asList(new Number[] {handle})); |
193 |
request.setArgument(Attributes.ATTRIBUTES, attributes); |
194 |
CFResponsePacket response = sendRequest(request); |
195 |
if(response.isSuccess()) { |
196 |
updateBreakpoint(response.getBody()); |
197 |
} |
198 |
else if(TRACE) { |
199 |
Tracing.writeString("VM [failed getbreakpoint request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
200 |
} |
201 |
return (RemoteBreakpoint) breakpointHandles.get(handle); |
202 |
} |
203 |
|
204 |
/** |
205 |
* Called via reflection to determine if the VM supports suspend on script loads |
206 |
* @return <code>true</code> if this VM can suspend when a script loads <code>false</code> otherwise |
207 |
*/ |
208 |
public boolean supportsSuspendOnScriptLoads() { |
209 |
return false; |
210 |
} |
211 |
|
212 |
/** |
213 |
* Add the breakpoint described by the given JSON to the handles list |
214 |
* @param json |
215 |
*/ |
216 |
public void addBreakpoint(Map json) { |
217 |
if(json != null) { |
218 |
Number handle = (Number) json.get(Attributes.HANDLE); |
219 |
if(handle != null) { |
220 |
RemoteBreakpoint bp = (RemoteBreakpoint) breakpointHandles.get(handle); |
221 |
if(bp == null) { |
222 |
bp = new RemoteBreakpoint(this, |
223 |
handle, |
224 |
(Map) json.get(Attributes.LOCATION), |
225 |
(Map) json.get(Attributes.ATTRIBUTES), |
226 |
(String)json.get(Attributes.TYPE)); |
227 |
} |
228 |
} |
229 |
} |
230 |
} |
231 |
|
232 |
/** |
233 |
* Locates the breakpoint for the handle given in the map and updates its attributes |
234 |
* |
235 |
* @param json the JSON map, cannot be <code>null</code> |
236 |
*/ |
237 |
public void updateBreakpoint(Map json) { |
238 |
if(json != null) { |
239 |
Number handle = (Number) json.get(Attributes.HANDLE); |
240 |
if(handle != null) { |
241 |
RemoteBreakpoint bp = (RemoteBreakpoint) breakpointHandles.get(handle); |
242 |
if(bp != null) { |
243 |
bp.setEnabled(RemoteBreakpoint.getEnabled(json)); |
244 |
bp.setCondition(RemoteBreakpoint.getCondition(json)); |
245 |
} |
246 |
} |
247 |
} |
248 |
} |
249 |
|
250 |
/** |
251 |
* Adds or removes the breakpoint from the cache based on the <code>isset</code> attribute |
252 |
* |
253 |
* @param json the JSON map, cannot be <code>null</code> |
254 |
*/ |
255 |
public void toggleBreakpoint(Map json) { |
256 |
if(json != null) { |
257 |
Boolean isset = (Boolean)json.get(Attributes.SET); |
258 |
if(isset != null && isset.booleanValue()) { |
259 |
updateBreakpoint(json); |
260 |
} |
261 |
else { |
262 |
Number handle = (Number) json.get(Attributes.HANDLE); |
263 |
breakpointHandles.remove(handle); |
264 |
} |
265 |
} |
266 |
} |
267 |
|
268 |
/** |
269 |
* @return the 'readiness' of the VM - i.e. is it in a state to process requests, etc |
270 |
*/ |
271 |
boolean ready() { |
272 |
return !disconnected; |
273 |
} |
274 |
|
275 |
/** |
276 |
* Sends an <code>createcontext</code> request for the given URL and returns the status of the request. |
277 |
* |
278 |
* @param url the URL to open / update in the remote target, <code>null</code> is not accepted |
279 |
* @return <code>true</code> if the request was successful, <code>false</code> otherwise |
280 |
*/ |
281 |
boolean createContext(String url) { |
282 |
if(url != null && ready()) { |
283 |
CFRequestPacket request = new CFRequestPacket(Commands.CREATE_CONTEXT, null); |
284 |
request.getArguments().put(Attributes.URL, url); |
285 |
CFResponsePacket response = sendRequest(request); |
286 |
if(response.isSuccess()) { |
287 |
return true; |
288 |
} |
289 |
else if(TRACE) { |
290 |
Tracing.writeString("VM [failed createcontext request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
291 |
} |
292 |
} |
293 |
return false; |
294 |
} |
295 |
|
296 |
/** |
297 |
* Sends the frame request |
298 |
* @param contextid |
299 |
* @param index |
300 |
* @param includescopes |
301 |
* @return |
302 |
*/ |
303 |
CFStackFrame getFrame(String contextid, int index, boolean includescopes) { |
304 |
if(index > -1) { |
305 |
CFThreadReference thread = findThread(contextid); |
306 |
if(thread != null) { |
307 |
CFRequestPacket request = new CFRequestPacket(Commands.FRAME, thread.id()); |
308 |
request.setArgument(Attributes.INDEX, new Integer(index)); |
309 |
request.setArgument(Attributes.INCLUDE_SCOPES, new Boolean(includescopes)); |
310 |
CFResponsePacket response = sendRequest(request); |
311 |
if(response.isSuccess()) { |
312 |
return new CFStackFrame(this, thread, response.getBody()); |
313 |
} |
314 |
else if(TRACE) { |
315 |
Tracing.writeString("VM [failed frame request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
316 |
} |
317 |
} |
318 |
} |
319 |
return null; |
320 |
|
321 |
} |
322 |
|
323 |
/** |
324 |
* Sends a request to enable the tool with the given name in the remote Crossfire server |
325 |
* |
326 |
* @param tools the array of tool names to enable, <code>null</code> is not allowed |
327 |
* @return <code>true</code> if the server reports the tool became enabled, <code>false</code> otherwise |
328 |
*/ |
329 |
boolean enableTools(String[] tools) { |
330 |
if(tools != null && tools.length > 0 && ready()) { |
331 |
CFRequestPacket request = new CFRequestPacket(Commands.ENABLE_TOOLS, null); |
332 |
request.getArguments().put(Attributes.TOOLS, Arrays.asList(tools)); |
333 |
CFResponsePacket response = sendRequest(request); |
334 |
if(response.isSuccess()) { |
335 |
//TODO handle the tool being enabled |
336 |
return true; |
337 |
} |
338 |
else if(TRACE) { |
339 |
Tracing.writeString("VM [failed enabletool request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
340 |
} |
341 |
} |
342 |
return false; |
343 |
} |
344 |
|
345 |
/** |
346 |
* Sends a request to disable the tool with the given name in the remote Crossfire server |
347 |
* |
348 |
* @param tools the array of tool names to disable, <code>null</code> is not allowed |
349 |
* @return <code>true</code> if the server reports the tool became disabled, <code>false</code> otherwise |
350 |
*/ |
351 |
boolean disableTools(String[] tools) { |
352 |
if(tools != null && tools.length > 0 && ready()) { |
353 |
CFRequestPacket request = new CFRequestPacket(Commands.DISABLE_TOOLS, null); |
354 |
request.getArguments().put(Attributes.TOOLS, Arrays.asList(tools)); |
355 |
CFResponsePacket response = sendRequest(request); |
356 |
if(response.isSuccess()) { |
357 |
//TODO handle the tool being enabled |
358 |
return true; |
359 |
} |
360 |
else if(TRACE) { |
361 |
Tracing.writeString("VM [failed disabletool request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
362 |
} |
363 |
} |
364 |
return false; |
365 |
} |
366 |
|
367 |
/** |
368 |
* Returns the complete listing of tools from Crossfire regardless of their enabled state. |
369 |
* |
370 |
* @return the listing of tools or an empty list, never <code>null</code> |
371 |
*/ |
372 |
List allTools() { |
373 |
if(ready()) { |
374 |
CFRequestPacket request = new CFRequestPacket(Commands.GET_TOOLS, null); |
375 |
CFResponsePacket response = sendRequest(request); |
376 |
if(response.isSuccess()) { |
377 |
//TODO do we want to make these first-class objects in our model so we can track state, etc for tools? |
378 |
List tools = (List) response.getBody().get(Attributes.TOOLS); |
379 |
if(tools != null) { |
380 |
return tools; |
381 |
} |
382 |
} |
383 |
else if(TRACE) { |
384 |
Tracing.writeString("VM [failed alltools request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
385 |
} |
386 |
} |
387 |
return Collections.EMPTY_LIST; |
388 |
} |
389 |
|
390 |
/* (non-Javadoc) |
391 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#resume() |
392 |
*/ |
393 |
public void resume() { |
394 |
if(ready()) { |
395 |
if(threads != null) { |
396 |
Entry entry = null; |
397 |
for (Iterator iter = threads.entrySet().iterator(); iter.hasNext();) { |
398 |
entry = (Entry) iter.next(); |
399 |
CFThreadReference thread = (CFThreadReference) entry.getValue(); |
400 |
if(thread.isSuspended()) { |
401 |
CFRequestPacket request = new CFRequestPacket(Commands.CONTINUE, thread.id()); |
402 |
CFResponsePacket response = sendRequest(request); |
403 |
if(response.isSuccess()) { |
404 |
if(thread.isSuspended()) { |
405 |
thread.markSuspended(false); |
406 |
} |
407 |
} |
408 |
else if(TRACE) { |
409 |
Tracing.writeString("VM [failed continue request][context: "+thread.id()+"]: "+JSON.serialize(request)); //$NON-NLS-1$ //$NON-NLS-2$ |
410 |
} |
411 |
} |
412 |
} |
413 |
} |
414 |
} |
415 |
} |
416 |
|
417 |
/* (non-Javadoc) |
418 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#suspend() |
419 |
*/ |
420 |
public void suspend() { |
421 |
if(ready()) { |
422 |
if(threads != null) { |
423 |
Entry entry = null; |
424 |
for (Iterator iter = threads.entrySet().iterator(); iter.hasNext();) { |
425 |
entry = (Entry) iter.next(); |
426 |
CFThreadReference thread = (CFThreadReference) entry.getValue(); |
427 |
if(thread.isRunning()) { |
428 |
CFRequestPacket request = new CFRequestPacket(Commands.SUSPEND, thread.id()); |
429 |
CFResponsePacket response = sendRequest(request); |
430 |
if(response.isSuccess()) { |
431 |
if(!thread.isSuspended()) { |
432 |
thread.markSuspended(true); |
433 |
} |
434 |
} |
435 |
else if(TRACE) { |
436 |
Tracing.writeString("VM [failed suspend request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
437 |
} |
438 |
} |
439 |
} |
440 |
} |
441 |
} |
442 |
} |
443 |
|
444 |
/* (non-Javadoc) |
445 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#terminate() |
446 |
*/ |
447 |
public void terminate() { |
448 |
if(ready()) { |
449 |
disconnectVM(); |
450 |
} |
451 |
} |
452 |
|
453 |
/* (non-Javadoc) |
454 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#name() |
455 |
*/ |
456 |
public String name() { |
457 |
return Messages.vm_name; |
458 |
} |
459 |
|
460 |
/* (non-Javadoc) |
461 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#description() |
462 |
*/ |
463 |
public String description() { |
464 |
return Messages.crossfire_vm; |
465 |
} |
466 |
|
467 |
/* (non-Javadoc) |
468 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#version() |
469 |
*/ |
470 |
public synchronized String version() { |
471 |
if(ready()) { |
472 |
CFRequestPacket request = new CFRequestPacket(Commands.VERSION, null); |
473 |
CFResponsePacket response = sendRequest(request); |
474 |
if(response.isSuccess()) { |
475 |
Map json = response.getBody(); |
476 |
return (String) json.get(Commands.VERSION); |
477 |
} |
478 |
if(TRACE) { |
479 |
Tracing.writeString("VM [failed version request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
480 |
} |
481 |
} |
482 |
return Constants.UNKNOWN; |
483 |
} |
484 |
|
485 |
/* (non-Javadoc) |
486 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#allThreads() |
487 |
*/ |
488 |
public synchronized List allThreads() { |
489 |
if(threads == null) { |
490 |
threads = new HashMap(); |
491 |
CFRequestPacket request = new CFRequestPacket(Commands.LISTCONTEXTS, null); |
492 |
CFResponsePacket response = sendRequest(request); |
493 |
if(response.isSuccess()) { |
494 |
List contexts = (List) response.getBody().get(Attributes.CONTEXTS); |
495 |
for (Iterator iter = contexts.iterator(); iter.hasNext();) { |
496 |
Map json = (Map) iter.next(); |
497 |
CFThreadReference thread = new CFThreadReference(this, json); |
498 |
threads.put(thread.id(), thread); |
499 |
} |
500 |
} |
501 |
else if(TRACE) { |
502 |
Tracing.writeString("VM [failed allthreads request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
503 |
} |
504 |
} |
505 |
return new ArrayList(threads.values()); |
506 |
} |
507 |
|
508 |
/** |
509 |
* Adds a thread to the listing |
510 |
* |
511 |
* @param id |
512 |
* @param url |
513 |
* @return the new thread |
514 |
*/ |
515 |
public CFThreadReference addThread(String id, String url) { |
516 |
if(threads == null) { |
517 |
allThreads(); |
518 |
} |
519 |
CFThreadReference thread = new CFThreadReference(this, id, url); |
520 |
threads.put(thread.id(), thread); |
521 |
return thread; |
522 |
} |
523 |
|
524 |
/** |
525 |
* Removes the thread with the given id |
526 |
* |
527 |
* @param id the id of the thread to remove |
528 |
*/ |
529 |
public void removeThread(String id) { |
530 |
if(threads != null) { |
531 |
Object obj = threads.remove(id); |
532 |
if(TRACE && obj == null) { |
533 |
Tracing.writeString("VM [failed to remove thread]: "+id); //$NON-NLS-1$ |
534 |
} |
535 |
} |
536 |
} |
537 |
|
538 |
/** |
539 |
* Returns the thread with the given id or <code>null</code> |
540 |
* |
541 |
* @param id |
542 |
* @return the thread or <code>null</code> |
543 |
*/ |
544 |
public synchronized CFThreadReference findThread(String id) { |
545 |
if(threads == null) { |
546 |
allThreads(); |
547 |
} |
548 |
CFThreadReference thread = (CFThreadReference) threads.get(id); |
549 |
if(TRACE && thread == null) { |
550 |
Tracing.writeString("VM [failed to find thread]: "+id); //$NON-NLS-1$ |
551 |
} |
552 |
return thread; |
553 |
} |
554 |
|
555 |
/* (non-Javadoc) |
556 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#allScripts() |
557 |
*/ |
558 |
public synchronized List allScripts() { |
559 |
if(scripts == null) { |
560 |
scripts = new HashMap(); |
561 |
List threads = allThreads(); |
562 |
for (Iterator iter = threads.iterator(); iter.hasNext();) { |
563 |
CFThreadReference thread = (CFThreadReference) iter.next(); |
564 |
CFRequestPacket request = new CFRequestPacket(Commands.SCRIPTS, thread.id()); |
565 |
request.setArgument(Attributes.INCLUDE_SOURCE, Boolean.FALSE); |
566 |
CFResponsePacket response = sendRequest(request); |
567 |
if(response.isSuccess()) { |
568 |
List scriptz = (List) response.getBody().get(Attributes.SCRIPTS); |
569 |
for (Iterator iter2 = scriptz.iterator(); iter2.hasNext();) { |
570 |
Map smap = (Map) iter2.next(); |
571 |
if(smap != null) { |
572 |
CFScriptReference script = new CFScriptReference(this, thread.id(), smap); |
573 |
scripts.put(script.url(), script); |
574 |
} |
575 |
} |
576 |
} |
577 |
else if(TRACE) { |
578 |
Tracing.writeString("VM [failed scripts request]: "+JSON.serialize(request)); //$NON-NLS-1$ |
579 |
} |
580 |
} |
581 |
if(scripts.size() < 1) { |
582 |
scripts = null; |
583 |
return Collections.EMPTY_LIST; |
584 |
} |
585 |
} |
586 |
return new ArrayList(scripts.values()); |
587 |
} |
588 |
|
589 |
/** |
590 |
* Returns the script with the given url or <code>null</code> |
591 |
* |
592 |
* @param url |
593 |
* @return the thread or <code>null</code> |
594 |
*/ |
595 |
public synchronized CFScriptReference findScript(String url) { |
596 |
if(scripts == null) { |
597 |
allScripts(); |
598 |
} |
599 |
CFScriptReference script = null; |
600 |
if(scripts != null) { |
601 |
//the scripts collection can be null after a call to allScripts() |
602 |
//when the remote target had no scripts loaded. In this case |
603 |
//we do not keep the initialized collection so that any successive |
604 |
//calls the this method or allScripts will cause the remote target |
605 |
//to be asked for all of its scripts |
606 |
script = (CFScriptReference) scripts.get(url); |
607 |
} |
608 |
//if we find we have a script id that is not cached, we should try a lookup + add in the vm |
609 |
if(script == null) { |
610 |
if(TRACE) { |
611 |
Tracing.writeString("VM [failed to find script]: "+url); //$NON-NLS-1$ |
612 |
} |
613 |
} |
614 |
return script; |
615 |
} |
616 |
|
617 |
/** |
618 |
* Adds the given script to the listing |
619 |
* |
620 |
* @param context_id |
621 |
* @param json |
622 |
* |
623 |
* @return the new script |
624 |
*/ |
625 |
public CFScriptReference addScript(String context_id, Map json) { |
626 |
if(scripts == null) { |
627 |
allScripts(); |
628 |
} |
629 |
CFScriptReference script = new CFScriptReference(this, context_id, json); |
630 |
scripts.put(script.url(), script); |
631 |
return script; |
632 |
} |
633 |
|
634 |
/** |
635 |
* Removes all {@link CFScriptReference}s from the cache when the associated context is destroyed |
636 |
* |
637 |
* @param contextid |
638 |
*/ |
639 |
public void removeScriptsForContext(String contextid) { |
640 |
if(scripts != null) { |
641 |
Entry e = null; |
642 |
for(Iterator i = scripts.entrySet().iterator(); i.hasNext();) { |
643 |
e = (Entry) i.next(); |
644 |
if(contextid.equals(((CFScriptReference)e.getValue()).context())) { |
645 |
i.remove(); |
646 |
} |
647 |
} |
648 |
} |
649 |
} |
650 |
|
651 |
/** |
652 |
* Removes the script with the given url from the listing |
653 |
* |
654 |
* @param url the script to remove |
655 |
*/ |
656 |
public void removeScript(String url) { |
657 |
if(scripts != null) { |
658 |
Object obj = scripts.remove(url); |
659 |
if(TRACE && obj == null) { |
660 |
Tracing.writeString("VM [failed to remove script]: "+url); //$NON-NLS-1$ |
661 |
} |
662 |
} |
663 |
} |
664 |
|
665 |
/* (non-Javadoc) |
666 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#dispose() |
667 |
*/ |
668 |
public synchronized void dispose() { |
669 |
try { |
670 |
if(TRACE) { |
671 |
Tracing.writeString("VM [disposing]"); //$NON-NLS-1$ |
672 |
} |
673 |
queue.dispose(); |
674 |
ermanager.dispose(); |
675 |
} |
676 |
finally { |
677 |
//fall-back in case the VM has been disposed but not disconnected |
678 |
disconnectVM(); |
679 |
} |
680 |
} |
681 |
|
682 |
/* (non-Javadoc) |
683 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#mirrorOfUndefined() |
684 |
*/ |
685 |
public UndefinedValue mirrorOfUndefined() { |
686 |
return undefinedvalue; |
687 |
} |
688 |
|
689 |
/* (non-Javadoc) |
690 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#mirrorOfNull() |
691 |
*/ |
692 |
public NullValue mirrorOfNull() { |
693 |
return nullvalue; |
694 |
} |
695 |
|
696 |
/* (non-Javadoc) |
697 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#mirrorOf(boolean) |
698 |
*/ |
699 |
public BooleanValue mirrorOf(boolean bool) { |
700 |
return new CFBooleanValue(this, bool); |
701 |
} |
702 |
|
703 |
/* (non-Javadoc) |
704 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#mirrorOf(java.lang.Number) |
705 |
*/ |
706 |
public NumberValue mirrorOf(Number number) { |
707 |
return new CFNumberValue(this, number); |
708 |
} |
709 |
|
710 |
/* (non-Javadoc) |
711 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#mirrorOf(java.lang.String) |
712 |
*/ |
713 |
public StringValue mirrorOf(String string) { |
714 |
return new CFStringValue(this, string); |
715 |
} |
716 |
|
717 |
/* (non-Javadoc) |
718 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#eventRequestManager() |
719 |
*/ |
720 |
public synchronized EventRequestManager eventRequestManager() { |
721 |
return ermanager; |
722 |
} |
723 |
|
724 |
/* (non-Javadoc) |
725 |
* @see org.eclipse.wst.jsdt.debug.core.jsdi.VirtualMachine#eventQueue() |
726 |
*/ |
727 |
public synchronized EventQueue eventQueue() { |
728 |
return queue; |
729 |
} |
730 |
|
731 |
/** |
732 |
* Receives an {@link CFEventPacket} from the underlying {@link DebugSession}, |
733 |
* waiting for the {@link VirtualMachine#DEFAULT_TIMEOUT}. |
734 |
* |
735 |
* @return the next {@link CFEventPacket} never <code>null</code> |
736 |
* @throws TimeoutException |
737 |
* @throws DisconnectedException |
738 |
*/ |
739 |
public CFEventPacket receiveEvent() throws TimeoutException, DisconnectedException { |
740 |
return (CFEventPacket) session.receive(CFEventPacket.EVENT, DEFAULT_TIMEOUT); |
741 |
} |
742 |
|
743 |
/** |
744 |
* Receives an {@link CFEventPacket} from the underlying {@link DebugSession}, |
745 |
* waiting for the {@link VirtualMachine#DEFAULT_TIMEOUT}. |
746 |
* @param timeout |
747 |
* @return the next {@link CFEventPacket} never <code>null</code> |
748 |
* @throws TimeoutException |
749 |
* @throws DisconnectedException |
750 |
*/ |
751 |
public CFEventPacket receiveEvent(int timeout) throws TimeoutException, DisconnectedException { |
752 |
return (CFEventPacket) session.receive(CFEventPacket.EVENT, timeout); |
753 |
} |
754 |
|
755 |
/** |
756 |
* Sends a request to the underlying {@link DebugSession}, waiting |
757 |
* for the {@link VirtualMachine#DEFAULT_TIMEOUT}. |
758 |
* |
759 |
* @param request |
760 |
* @return the {@link CFResponsePacket} for the request |
761 |
*/ |
762 |
public CFResponsePacket sendRequest(CFRequestPacket request) { |
763 |
try { |
764 |
session.send(request); |
765 |
return (CFResponsePacket) session.receiveResponse(request.getSequence(), 3000); |
766 |
} |
767 |
catch(DisconnectedException de) { |
768 |
disconnectVM(); |
769 |
handleException(de.getMessage(), (de.getCause() == null ? de : de.getCause())); |
770 |
} |
771 |
catch(TimeoutException te) { |
772 |
CrossFirePlugin.log(te); |
773 |
} |
774 |
return CFResponsePacket.FAILED; |
775 |
} |
776 |
|
777 |
/** |
778 |
* disconnects the VM |
779 |
*/ |
780 |
public synchronized void disconnectVM() { |
781 |
if (disconnected) { |
782 |
// no-op it is already disconnected |
783 |
if(TRACE) { |
784 |
Tracing.writeString("VM [already disconnected]"); //$NON-NLS-1$ |
785 |
} |
786 |
return; |
787 |
} |
788 |
if(TRACE) { |
789 |
Tracing.writeString("VM [disconnecting]"); //$NON-NLS-1$ |
790 |
} |
791 |
try { |
792 |
if(threads != null) { |
793 |
threads.clear(); |
794 |
} |
795 |
if(scripts != null) { |
796 |
scripts.clear(); |
797 |
} |
798 |
this.queue.dispose(); |
799 |
this.ermanager.dispose(); |
800 |
this.session.dispose(); |
801 |
} finally { |
802 |
disconnected = true; |
803 |
DebugPlugin.getDefault().getBreakpointManager().removeBreakpointListener(this); |
804 |
} |
805 |
} |
806 |
|
807 |
/* (non-Javadoc) |
808 |
* @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint) |
809 |
*/ |
810 |
public void breakpointAdded(IBreakpoint breakpoint) { |
811 |
if (JavaScriptDebugModel.MODEL_ID.equals(breakpoint.getModelIdentifier())) { |
812 |
if (breakpoint instanceof IJavaScriptLineBreakpoint) { |
813 |
//TODO check handle map send request as needed |
814 |
} |
815 |
} |
816 |
} |
817 |
|
818 |
/* (non-Javadoc) |
819 |
* @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) |
820 |
*/ |
821 |
public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) { |
822 |
if (JavaScriptDebugModel.MODEL_ID.equals(breakpoint.getModelIdentifier())) { |
823 |
if (breakpoint instanceof IJavaScriptLineBreakpoint) { |
824 |
//TODO check handle map send request as needed |
825 |
} |
826 |
} |
827 |
} |
828 |
|
829 |
/* (non-Javadoc) |
830 |
* @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) |
831 |
*/ |
832 |
public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) { |
833 |
if (JavaScriptDebugModel.MODEL_ID.equals(breakpoint.getModelIdentifier())) { |
834 |
if (breakpoint instanceof IJavaScriptLineBreakpoint) { |
835 |
//TODO check handle map send request as needed |
836 |
} |
837 |
} |
838 |
} |
839 |
} |