1) Made getVariables and evalBlock methods in PHPDBGInterface synchronized (At least...
[phpeclipse.git] / net.sourceforge.phpeclipse.debug.core / src / net / sourceforge / phpdt / internal / debug / core / model / PHPDebugTarget.java
1 /**********************************************************************
2  Copyright (c) 2000, 2002 IBM Corp. and others.
3  All rights reserved. This program and the accompanying materials
4  are made available under the terms of the Common Public License v1.0
5  which accompanies this distribution, and is available at
6  http://www.eclipse.org/legal/cpl-v10.html
7
8  Contributors:
9  IBM Corporation - Initial implementation
10  Vicente Fernando - www.alfersoft.com.ar
11  **********************************************************************/
12 package net.sourceforge.phpdt.internal.debug.core.model;
13
14 import net.sourceforge.phpdt.internal.debug.core.PHPDBGProxy;
15 import net.sourceforge.phpdt.internal.debug.core.PHPDebugCorePlugin;
16 import net.sourceforge.phpdt.internal.debug.core.breakpoints.PHPLineBreakpoint;
17 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
18
19 import org.eclipse.core.resources.IMarkerDelta;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.debug.core.DebugEvent;
22 import org.eclipse.debug.core.DebugException;
23 import org.eclipse.debug.core.DebugPlugin;
24 import org.eclipse.debug.core.IBreakpointManager;
25 import org.eclipse.debug.core.IDebugEventSetListener;
26 import org.eclipse.debug.core.ILaunch;
27 import org.eclipse.debug.core.ILaunchListener;
28 import org.eclipse.debug.core.model.IBreakpoint;
29 import org.eclipse.debug.core.model.IDebugTarget;
30 import org.eclipse.debug.core.model.IMemoryBlock;
31 import org.eclipse.debug.core.model.IProcess;
32 import org.eclipse.debug.core.model.IStackFrame;
33 import org.eclipse.debug.core.model.IThread;
34 import org.eclipse.jface.resource.ImageDescriptor;
35 import org.eclipse.ui.model.IWorkbenchAdapter;
36
37 /**
38  * Debug target for PHP debug model.
39  */
40 public class PHPDebugTarget extends PHPDebugElement implements IPHPDebugTarget, ILaunchListener,
41                 IDebugEventSetListener {
42
43         private IProcess process;
44
45         private ILaunch launch;
46
47         private PHPThread[] threads = new PHPThread[0];
48
49         private PHPDBGProxy phpDBGProxy;
50
51         private class State {
52                 private boolean isTerminated = false;
53
54                 private boolean isSuspended = false;
55
56                 boolean isTerminated() {
57                         return isTerminated;
58                 }
59
60                 boolean isSuspended() {
61                         return isSuspended;
62                 }
63
64                 void setTerminated(boolean terminated) {
65                         this.isTerminated = terminated;
66                 }
67
68                 void setSuspended(boolean suspended) {
69                         if (isTerminated())
70                                 throw new IllegalStateException();
71                         this.isSuspended = suspended;
72                 }
73         }
74
75         private final State state = new State();
76
77         public PHPDebugTarget(ILaunch launch, IProcess process) {
78                 super (null);
79                 if (null == launch && null == process)
80                         throw new IllegalArgumentException();
81                 this.launch = launch;
82                 this.process = process;
83                 // TODO XXX remove breakpoint listener at termination to avoid live leak
84                 IBreakpointManager manager = DebugPlugin.getDefault()
85                                 .getBreakpointManager();
86                 manager.addBreakpointListener(this);
87                 DebugPlugin.getDefault().addDebugEventListener(this);
88                 initialize();
89         }
90
91         protected synchronized void initialize() {
92                 DebugEvent ev = new DebugEvent(this, DebugEvent.CREATE);
93                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
94         }
95
96         public void addThread(PHPThread phpThread) {
97                 int i;
98                 PHPThread[] updatedThreads = new PHPThread[threads.length + 1];
99
100                 for (i = 0; i < threads.length; i++) {
101                         updatedThreads[i] = threads[i];
102                 }
103                 updatedThreads[i] = phpThread;
104                 threads = updatedThreads;
105
106                 fireChangeEvent();
107                 fireThreadCreateEvent(phpThread);
108         }
109
110         private void fireChangeEvent() {
111                 DebugEvent ev = new DebugEvent(this, DebugEvent.CHANGE);
112                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
113         }
114
115         private void fireThreadCreateEvent(PHPThread phpThread) {
116                 DebugEvent ev = new DebugEvent(phpThread, DebugEvent.CREATE);
117                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { ev });
118         }
119
120         protected PHPThread getThreadById(int id) {
121                 for (int i = 0; i < threads.length; i++) {
122                         if (threads[i].getId() == id) {
123                                 return threads[i];
124                         }
125                 }
126                 return null;
127         }
128
129         public IThread[] getThreads() {
130                 return threads;
131         }
132
133         public boolean hasThreads() throws DebugException {
134                 return threads.length > 0;
135         }
136
137         public String getName() throws DebugException {
138                 return "PHP Debugger at localhost:" + getPHPDBGProxy().getPort();
139         }
140
141         public boolean supportsBreakpoint(IBreakpoint arg0) {
142                 if (arg0.getModelIdentifier().equals(PHPDebugCorePlugin.PLUGIN_ID)) {
143                         return true;
144                 }
145                 return false;
146         }
147
148         public String getModelIdentifier() {
149                 return PHPDebugCorePlugin.PLUGIN_ID;
150         }
151
152         public IStackFrame[] getStackFrames () throws DebugException {
153                 return (IStackFrame[]) this.phpDBGProxy.getDBGInterface ().getStackList ();
154         }
155
156         public IDebugTarget getDebugTarget() {
157                 return this;
158         }
159
160         public ILaunch getLaunch() {
161                 return launch;
162         }
163
164         public synchronized boolean canTerminate() {
165                 return !isTerminated();
166         }
167
168         public synchronized boolean isTerminated() {
169                 return state.isTerminated();
170         }
171
172         public synchronized void terminate() {
173                 // This method is synchronized to control a race condition between the
174                 // UI thread that terminates the debugging session, and the slave
175                 // thread that executes PHPLoop.run
176                 if (isTerminated())
177                         // Avoid terminating twice...
178                         return;
179                 state.setTerminated(true);
180                 phpDBGProxy.stop();
181                 this.threads = new PHPThread[0];
182                 fireChangeEvent();
183                 IBreakpointManager manager = DebugPlugin.getDefault()
184                                 .getBreakpointManager();
185                 manager.removeBreakpointListener(this);
186                 DebugPlugin.getDefault().removeDebugEventListener(this);
187         }
188
189         public synchronized boolean canResume() {
190                 if (isTerminated())
191                         return false;
192                 return isSuspended();
193         }
194
195         public synchronized boolean canSuspend() {
196                 if (isTerminated())
197                         return false;
198                 return !isSuspended();
199         }
200
201         public synchronized boolean isSuspended() {
202                 return state.isSuspended();
203         }
204
205         public synchronized void resume() throws DebugException {
206                 if (!isSuspended())
207                         return;
208                 state.setSuspended(false);
209                 this.getPHPDBGProxy().resume();
210                 IThread[] threads = getThreads();
211                 for (int i = 0; i < threads.length; ++i)
212                         threads[i].resume();
213         }
214
215         public synchronized void suspend() throws DebugException {
216                 if (isSuspended())
217                         return;
218                 this.getPHPDBGProxy().pause();
219                 state.setSuspended(true);
220                 IThread[] threads = getThreads();
221                 for (int i = 0; i < threads.length; ++i)
222                         threads[i].suspend();
223         }
224
225         public void breakpointAdded(IBreakpoint breakpoint) {
226                 this.getPHPDBGProxy().addBreakpoint(breakpoint);
227         }
228
229         public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta arg1) {
230                 this.getPHPDBGProxy().removeBreakpoint(breakpoint);
231         }
232
233         /**
234          * The method will be called when the user enables/disables
235          * breakpoints. In this case we add or remove the breakpoint.
236          * It's also called when leaving the breakpoint properties dialog
237          * (skip count and breakpoint condition) with the OK button.
238          *
239          * This method is also called whenever a source file has changed.
240          * In this case we terminate since the source will be out of sync with the debugger.
241          * TODO Is it correct to call this method when a sourcefile is modified?
242          *
243          */
244         public void breakpointChanged (IBreakpoint breakpoint, IMarkerDelta arg1) {
245                 PHPLineBreakpoint bp;
246                 bp = (PHPLineBreakpoint) breakpoint;
247
248                 try {
249                         if (breakpoint.isEnabled ()     &&                                                                      // Check if breakpoint state changed from disabled to enabled
250                                 !arg1.getAttribute ("org.eclipse.debug.core.enabled", false)) {
251                                 this.getPHPDBGProxy().addBreakpoint(breakpoint);
252                         }
253                         else if (!breakpoint.isEnabled () &&                                                    // Check if breakpoint state changed from enabled to disabled
254                             arg1.getAttribute ("org.eclipse.debug.core.enabled", true)) {
255                                 this.getPHPDBGProxy().removeBreakpoint(breakpoint);
256                         }
257                         else if (bp.getChangeID() != arg1.getAttribute ("net.sourceforge.phpeclipse.debug.changeID", 0)) {
258                                 if (breakpoint.isEnabled()) {                                                           // If the breakpoint is already enabled
259                                         this.getPHPDBGProxy().removeBreakpoint(breakpoint);             // we remove this breakpoint first
260                                         this.getPHPDBGProxy().addBreakpoint(breakpoint);                // and then we add again (else DBG would have two breakpoints!).
261                                 }
262                                 else {
263                                         this.getPHPDBGProxy().removeBreakpoint(breakpoint);
264                                 }
265                         }
266                         else {                                                                                                                  // All other cases will terminate the debugger
267                                 terminate ();
268                         }
269                 } catch (CoreException e) {
270                         // Do nothing
271                 }
272         }
273
274         public boolean canDisconnect() {
275                 return false;
276         }
277
278         public void disconnect() throws DebugException {
279         }
280
281         public boolean isDisconnected() {
282                 return false;
283         }
284
285         public boolean supportsStorageRetrieval() {
286                 return false;
287         }
288
289         public IMemoryBlock getMemoryBlock(long arg0, long arg1)
290                         throws DebugException {
291                 return null;
292         }
293
294         public Object getAdapter(Class arg0) {
295                 if (IWorkbenchAdapter.class.equals(arg0)) {
296                         return new IWorkbenchAdapter() {
297                                 public Object[] getChildren(Object o) {
298                                         Object[] children = null;
299                                         IThread[] threads = getThreads();
300                                         if (null != threads) {
301                                                 children = new Object[threads.length];
302                                                 for (int i = 0; i < threads.length; ++i)
303                                                         children[i] = threads[i];
304                                         }
305                                         return children;
306                                 }
307
308                                 public ImageDescriptor getImageDescriptor(Object object) {
309                                         return null;
310                                 }
311
312                                 public String getLabel(Object o) {
313                                         String label = "(Unable to look up name... check error log)";
314                                         try {
315                                                 label = getName();
316                                         } catch (DebugException x) {
317                                                 PHPeclipsePlugin.log(label, x);
318                                         }
319                                         return label;
320                                 }
321
322                                 public Object getParent(Object o) {
323                                         return PHPDebugTarget.this.getLaunch();
324                                 }
325                         };
326                 }
327                 else {
328                     if (arg0 == PHPDebugElement.class) {
329                         return this;
330                     }
331
332                     return super.getAdapter(arg0);
333                 }
334         }
335
336         public IProcess getProcess() {
337                 return process;
338         }
339
340         public void setProcess(IProcess process) {
341                 this.process = process;
342         }
343
344         public PHPDBGProxy getPHPDBGProxy() {
345                 return phpDBGProxy;
346         }
347
348         public void setPHPDBGProxy(PHPDBGProxy phpDBGProxy) {
349                 this.phpDBGProxy = phpDBGProxy;
350         }
351
352         /**
353          * @see ILaunchListener#launchRemoved(ILaunch)
354          */
355         public void launchRemoved(ILaunch launch) {
356                 if (!isTerminated()) {
357                         return;
358                 }
359                 if (launch.equals(getLaunch())) {
360                         // This target has been deregistered, but it hasn't successfully
361                         // terminated.
362                         // Update internal state to reflect that it is disconnected
363                         terminate();
364                 }
365         }
366
367         /**
368          * @see ILaunchListener#launchAdded(ILaunch)
369          */
370         public void launchAdded(ILaunch launch) {
371         }
372
373         /**
374          * @see ILaunchListener#launchChanged(ILaunch)
375          */
376         public void launchChanged(ILaunch launch) {
377         }
378
379         /**
380          * When a debug target or process terminates, terminate DBG Proxy.
381          *
382          * @see IDebugEventSetListener#handleDebugEvents(DebugEvent[])
383          */
384         public void handleDebugEvents(DebugEvent[] events) {
385                 for (int i = 0; i < events.length; i++) {
386                         DebugEvent event = events[i];
387                         if (event.getKind() == DebugEvent.TERMINATE) {
388                                 Object source = event.getSource();
389                                 if (source instanceof PHPDebugTarget
390                                                 || source instanceof IDebugTarget) {
391                                         getPHPDBGProxy().stop();
392                                 } else if (source instanceof IProcess) {
393                                         if (getDebugTarget().getProcess() == (IProcess) source) {
394                                                 getPHPDBGProxy().stop();
395                                         }
396                                 }
397                         } else if (event.getKind() == DebugEvent.SUSPEND) {
398                                 getPHPDBGProxy().pause();
399                         }
400                 }
401         }
402 }