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 / PHPStackFrame.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 java.util.Arrays;
15 import java.util.Vector;
16
17 import net.sourceforge.phpdt.internal.debug.core.PHPDBGProxy;
18
19 import org.eclipse.debug.core.DebugEvent;
20 import org.eclipse.debug.core.DebugException;
21 import org.eclipse.debug.core.DebugPlugin;
22 import org.eclipse.debug.core.ILaunch;
23 import org.eclipse.debug.core.model.IDebugTarget;
24 import org.eclipse.debug.core.model.IRegisterGroup;
25 import org.eclipse.debug.core.model.IStackFrame;
26 import org.eclipse.debug.core.model.IThread;
27 import org.eclipse.debug.core.model.IVariable;
28
29 /**
30  *
31  * TODO Remove the variables array and use only the varList vector
32  *      Have also to change hasVariables
33  */
34 public class PHPStackFrame extends PHPDebugElement implements IStackFrame, Comparable{
35
36         private PHPThread               thread;                         // The thread to which this stackframe belongs
37         private String                  file;               // The file name???
38         private int                     lineNumber;         //
39         private int                     index;              //
40         private int                     modno;              //
41         private PHPVariable[]   variables;          // The array of variables TODO: better introduce a vector?
42         private Vector          varList  = new Vector ();
43         private String                  description;        // The source file name with the full path on target/remote system
44         private boolean         fUpToDate;          // Indicates whether the variable list within this stackframe is
45                                                                                                 // up-to-date
46         private boolean         fAvailable;         // Needed when updating the stackframe list, shows whether the stackframe
47                                                                                                 // is within the list which was received from dbg
48
49         /**
50          *
51          * @param thread
52          * @param file
53          * @param line
54          * @param index
55          * @param desc
56          * @param modno
57          */
58         public PHPStackFrame (PHPThread thread, String file, int line, int index, String desc, int modno) {
59                 super (null);
60
61                 this.lineNumber  = line;
62                 this.index           = index;
63                 this.file            = file;
64                 this.thread      = thread;
65                 this.description = desc;
66                 this.modno               = modno;
67                 this.fUpToDate   = false;
68         }
69
70         /**
71          *
72          * @param thread
73          * @param file
74          * @param line
75          * @param index
76          */
77         public PHPStackFrame (PHPThread thread, String file, int line, int index) {
78                 super (null);
79
80                 this.lineNumber = line;
81                 this.index              = index;
82                 this.file               = file;
83                 this.thread     = thread;
84                 this.fUpToDate  = false;
85         }
86
87         /**
88          *
89          */
90         public IThread getThread () {
91                 return thread;
92         }
93
94         /**
95          * @param thread
96          */
97         public void setThread (PHPThread thread) {
98                 this.thread = thread;
99         }
100
101         /**
102          *
103          */
104         private void setUpToDate (boolean upToDate) {
105                 fUpToDate = upToDate;
106         }
107
108         /**
109          *
110          */
111         private boolean isUpToDate () {
112                 return fUpToDate;
113         }
114
115         /**
116          *
117          */
118         public void setAvailable (boolean available) {
119                 fAvailable = available;
120         }
121
122         /**
123          *
124          */
125         public boolean isAvailable () {
126                 return fAvailable;
127         }
128
129
130         /**
131          * @see IAdaptable#getAdapter(Class)
132          */
133         public Object getAdapter(Class adapter) {
134                 if (adapter == PHPStackFrame.class) {
135                         return this;
136                 }
137
138                 return super.getAdapter(adapter);
139         }
140
141         /**
142          *
143          *
144          */
145         private void resetHasChangedInfo (Vector varList) {
146                 int             n;
147                 PHPVariable var;
148                 PHPValue    val;
149
150                 for (n = 0; n < varList.size (); n++) {                  // For every variable in 'DBG list'
151                         var = (PHPVariable) varList.get (n);                    // Get the variable
152                         val = (PHPValue) var.getValue ();                       // Get the variable's value
153
154                         try {
155                                 if (val.hasVariables ()) {                              // Do we have other variables within the value
156                                         if (!hasRecursion (var)) {                          // Is this variable (value) branch recursive?
157                                                 resetHasChangedInfo (val.getChildVariables ()); //  No, go into branch
158                                         }
159                                 }
160                         }
161                         catch (DebugException e) {                                                  // That's, because of the hasVariables method
162                         }
163
164                         var.setValueChanged (false);                            // Reset the 'has changed' flag
165                 }
166         }
167
168         /**
169          * Go up the tree of PHPVariables
170          * look whether the PHPValue is a reference to a parent PHPValue
171          *
172          * TODO Check where this recursion can come from.
173          * Whether this back reference is legal or a bug.
174          *
175          * @param var
176          * @return
177          * <ul>
178          * <li> false if the PHPValue is not a child of itself
179          * <li> true if the PHPValue is
180          * </ul>
181          */
182         private boolean hasRecursion (PHPVariable var) {
183                 PHPVariable parentVar;
184                 PHPValue    val;
185
186                 val = (PHPValue) var.getValue ();                                                       // Get the PHPValue from the current PHPVariable
187
188                 while (var != null) {                                                                           // As long as we have PHPVariable
189                         parentVar = var.getParent ();                                                   // Get the parent PHPVariable
190
191                         if (parentVar != null) {                                // Is there a parent?
192                                 if (parentVar.getValue ().equals (val)) {                       // Get the PHPValue for the parent PHPVariable and check
193                                                                                                                                         // whether it is the same
194                                         return true;                                                                    // Return, if we have recursion
195                                 }
196                         }
197
198                         var = parentVar;
199                 }
200
201                 return false;                                                                                           // No recursion found
202         }
203
204         /**
205          * This method updates the 'static' variables list.
206          * It does a replication between the 'static' list (the variable list which
207          * is a member of this DBG interface object) and the DBG variable list
208          * (the list of variables which is received from PHP via DBG with the current suspend)
209          * Replication is done in the following way:
210          * <ul>
211          * <li> It looks for new variables within the DBG variables list and
212          *      adds them to the 'static' list.
213          * <li> It looks for changed variables copies the current value to the variable within
214          *              the 'static list' and mark these variables as 'hasChanged' (which uses the UI
215          *              for showing the variable with a different color).
216          * <li> It looks for variables within the 'static' list, and removes them
217          *              from the 'static' list in case the do not appear within the DBG list.
218          * </ul>
219          *
220          * @param varListOld The 'static' list of variables which are to be updated.
221          * @param varListNew The new list of (current) variables from DBG.
222          */
223         private void updateVariableList (Vector varListOld, Vector varListNew)
224         {
225                 PHPVariable     varOld;                                                                                 // The variable from the 'static' list
226                 PHPVariable varNew;                                                                                     // The variable from the DBG list
227                 PHPValue    valOld;                                                                                     // The value of the current variable from 'static' list
228                 PHPValue    valNew;                                                                                     // The value of the current variable from DBG list
229                 int         n;                                                                                          // Index for the DBG list
230                 int         o;                                                                                          // Index for the static list
231
232                 // Add the variables (and childs) to the static list if they are new
233                 //  and update the values of variables which are already existend within
234                 //  the 'static' list.
235
236                 for (n = 0; n < varListNew.size (); n++) {                  // For every variable in 'DBG list'
237                         varNew = (PHPVariable) varListNew.get (n);                              // Get the DBG variable
238
239                         for (o = 0; o < varListOld.size (); o++) {                              // For every variable in static list
240                                 varOld = (PHPVariable) varListOld.get (o);                      // Get the static variable
241
242                                 if (varNew.getName ().equals (varOld.getName ())) { // Did we found the variable within the 'static' list?
243                                         valOld = (PHPValue) varOld.getValue ();         // Get the value from 'static'
244                                         valNew = (PHPValue) varNew.getValue ();         // Get the value from DBG
245
246                                         try {
247                                                 if (valOld.hasVariables () ||                           // If the 'static' value has child variables
248                                                         valNew.hasVariables ()) {                               //  or if the DBG value has child variables
249                                                         if (!hasRecursion (varOld) &&
250                                                                 !hasRecursion (varNew)) {                       // Both branches should not have a recursion
251                                                                 updateVariableList (valOld.getChildVariables (),        // Update the variable list for the child variables
252                                                                                                         valNew.getChildVariables ());
253                                                         }
254                                                 }
255                                                 else if (!valOld.getValueString ().equals (valNew.getValueString ())) { // Has the value changed?
256                                                         valOld.setValueString (valNew.getValueString ());                   // Yes, set the 'static' value (variable) to the new value
257                                                         varOld.setValueChanged (true);                                                                          // and set the 'has changed' flag, so that the variable view
258                                                                                                                                                                                                 // could show the user the changed status with a different
259                                                                                                                                                                                                 // color
260                                                 }
261                                                 //else {
262                                                 //      varOld.setValueChanged (false);                                     // Reset the 'has changed' flag
263                                                 //}
264                                         }
265                                         catch (DebugException e) {                                                  // That's, because of the hasVariables method
266                                         }
267
268                                         break;                                          // Found the variable,
269                                 }
270                         }
271
272                         if (o == varListOld.size ()) {                          // Did we found the variable within the static list?
273                                 varListOld.add (varNew);                                                        //  No, then add the DBG variable to the static list
274                         }
275                 }
276
277                 // Look for the variables we can remove from the 'static' list
278
279                 for (o = 0; o < varListOld.size (); o++) {                                              // For every variable in 'static' list
280                         varOld = (PHPVariable) varListOld.get (o);                                                      // Get the static variable
281
282                         for (n = 0; n < varListNew.size (); n++) {                                                      // For all variables in 'DBG' list
283                                 varNew = (PHPVariable) varListNew.get (n);                                              // Get the variable from the 'DBG' list
284
285                                 if (varNew.getName ().equals (varOld.getName ())) {                     // Did we found the 'static' list variable within the 'DBG' list?
286                                         break;                                                                                                          // Yes we found the variable, then leave the loop
287                                 }
288                         }
289
290                         if (n == varListNew.size ()) {                                          // Did not find the 'static' list variable within the 'DBG' list?
291                                  varListOld.remove (o);                                         // then remove the 'static' list variable from list
292                                  o -= 1;                                                        // Adjust the 'static' list index
293                         }
294                 }
295         }
296
297
298         /**
299          *
300          * This function returns the array of PHPVariables for this stackframe
301          * The PHPVariables should not change (newly build up) between two steps
302          * (or breaks).
303          * A PHPVariable with the same name but with different object ID is
304          * handled as a new variable.
305          *
306          * TODO Remove the intermediate storage array
307          *
308          * @return The array of PHPVariables for this stackframe.
309          */
310         public IVariable[] getVariables() throws DebugException {
311                 if (!isUpToDate ()) {
312                         resetHasChangedInfo (varList);
313                         updateVariableList (varList, this.getPHPDBGProxy ().readVariables (this));
314                         setUpToDate (true);
315
316                         variables = (PHPVariable[]) varList.toArray (new PHPVariable[varList.size ()]);
317                         Arrays.sort(variables, new PHPVariableComparator());
318                 }
319
320                 return variables;                                           // Give the array back to user interface
321         }
322
323         /**
324          *
325          */
326         private PHPVariable findVariable (Vector varList, String varname) {
327                 PHPVariable     variable;
328                 PHPValue        value;
329                 int                     i;
330
331                 for (i = 0; i < varList.size (); i++) {                                         // For all variables
332                         variable = (PHPVariable) varList.get (i);                               // Get the variable
333                         value    = (PHPValue) variable.getValue ();                             // Get the value of the variable
334
335                         try {
336                                 if (value.hasVariables ()) {                                            // Does the variable/value have children
337                                         if (!hasRecursion (variable)) {                                 // Don't follow recursive variable/values
338                                                 variable = findVariable (value.getChildVariables (), varname);
339
340                                                 if (variable != null) {
341                                                         return variable;
342                                                 }
343                                         }
344                                 }
345                                 else if ((variable.getName ()).equals (varname)) {      //
346                                         return variable;                                                                //
347                                 }
348                         }
349                         catch (DebugException e) {                                                              // That's, because of the hasVariables method
350                         }
351                 }
352
353                 return null;
354         }
355
356         /**
357          * This method is called from the UI (e.g. from PHPDebugHover
358          * to find the variable the mouse is pointing to)
359          *
360          * @param s The variable name we are looking for.
361          * @return
362          */
363         public IVariable findVariable (String s) throws DebugException {
364                 if (!isUpToDate ()) {
365                         resetHasChangedInfo (varList);
366                         updateVariableList (varList, this.getPHPDBGProxy ().readVariables (this));
367                         setUpToDate (true);
368
369                         variables = (PHPVariable[]) varList.toArray (new PHPVariable[varList.size ()]);
370                 }
371
372                 return (findVariable (varList, s));                                                     // Prefix the variable name with $
373         }
374
375         /**
376          *
377          */
378         public boolean hasVariables () throws DebugException {
379                 return true;
380                 // return (varList.size () > 0);
381         }
382
383         public int getLineNumber() {
384                 return lineNumber;
385         }
386
387         public void setLineNumber(int line) {
388                 lineNumber = line;
389         }
390
391         public int getCharStart() throws DebugException {
392                 // not supported
393                 return -1;
394         }
395
396         public int getCharEnd() throws DebugException {
397                 // not supported
398                 return -1;
399         }
400
401         public String getName() {
402                 StringBuffer name = new StringBuffer();
403
404                 if (!this.getDescription().equals ("")) {
405                         name.append (this.getDescription ());
406                 }
407                 else {
408                         name.append (this.getFileName ());
409                 }
410
411                 name.append (" [line ");
412                 name.append (this.getLineNumber ());
413                 name.append ("]");
414
415                 return name.toString();
416         }
417
418         public String getFileName() {
419                 return file;
420         }
421
422         public void setDescription(String desc) {
423                 this.description= desc;
424         }
425
426         public String getDescription() {
427                 return this.description;
428         }
429
430         public IRegisterGroup[] getRegisterGroups() throws DebugException {
431                 return null;
432         }
433
434         public boolean hasRegisterGroups() throws DebugException {
435                 return false;
436         }
437
438         public String getModelIdentifier() {
439                 return this.getThread().getModelIdentifier();
440         }
441
442         public IDebugTarget getDebugTarget() {
443                 return this.getThread().getDebugTarget();
444         }
445
446         public ILaunch getLaunch() {
447                 return this.getDebugTarget().getLaunch();
448         }
449
450         public boolean canStepInto() {
451                 return canResume();
452         }
453
454         public boolean canStepOver() {
455                 return canResume();
456         }
457
458         public boolean canStepReturn() {
459                 return canResume();
460         }
461
462         public boolean isStepping() {
463                 return false;
464         }
465
466         /**
467          *
468          */
469         public void stepInto () throws DebugException {
470                 DebugEvent      ev;
471
472                 setUpToDate (false);
473
474                 thread.prepareForResume (DebugEvent.STEP_INTO);             // Don't know why, but this is necessary
475                 this.getPHPDBGProxy ().readStepIntoEnd (PHPStackFrame.this);
476                 
477         // Commented out sending the RESUME event because it was already sent by prepareForResume.
478         // The second RESUME event leads only to a little flickering within the variables view.
479         // It is also not clear why this event was necessary in eclipse < 3.2
480         // Also sending a SUSPEND event here leads to a total rebuild of the variables view.
481         // (eclipse 3.2 has a build in timeout of 500 ms which leads to a auto suspend, with
482         // no flickering... but why???)
483         // 
484                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
485                 //DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
486         }
487
488         /**
489          *
490          */
491         public void stepOver () throws DebugException {
492                 DebugEvent      ev;
493
494                 setUpToDate (false);
495
496                 thread.prepareForResume (DebugEvent.STEP_OVER);
497                 this.getPHPDBGProxy ().readStepOverEnd (PHPStackFrame.this) ;
498
499         // See comment within the previous stepInto method.
500         // 
501                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
502                 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
503         }
504
505         /**
506          *
507          */
508         public void stepReturn () throws DebugException {
509                 DebugEvent      ev;
510
511                 setUpToDate (false);
512
513                 thread.prepareForResume (DebugEvent.STEP_RETURN);
514                 this.getPHPDBGProxy ().readStepReturnEnd (PHPStackFrame.this) ;
515
516                 ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
517                 DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
518         }
519
520
521         public boolean canResume() {
522                 return this.getThread().canResume();
523         }
524
525         public boolean canSuspend() {
526                 return this.getThread().canSuspend();
527         }
528
529         public boolean isSuspended() {
530                 return this.getThread().isSuspended();
531         }
532
533         public void resume() throws DebugException {
534                 setUpToDate (false);
535                 this.getThread().resume();
536         }
537
538         public void suspend() throws DebugException {
539         }
540
541         public boolean canTerminate() {
542                 return this.getThread().canTerminate();
543         }
544
545         public boolean isTerminated() {
546                 return this.getThread().isTerminated();
547         }
548
549         public void terminate() throws DebugException {
550                 getPHPDBGProxy().stop();
551         }
552
553         public int getIndex() {
554                 return index;
555         }
556
557         public void setIndex (int index) {
558                 this.index = index;
559         }
560
561         public PHPDBGProxy getPHPDBGProxy() {
562                 PHPDebugTarget DebugTarget;
563
564                 DebugTarget = (PHPDebugTarget) thread.getDebugTarget ();
565
566                 return DebugTarget.getPHPDBGProxy ();
567         }
568
569         public void setFile(String file) {
570                 this.file = file;
571         }
572
573         public int getModNo() {
574                 return modno;
575         }
576
577         /**
578          * This function is needed when sorting the stackframes by their index numbers.
579          *
580          * @param obj The stackframe which this one is compared to.
581          * @return
582          * <ul>
583          * <li> -1 if the index of this stackframe is less.
584          * <li> 0 if the index of both stackfream is equal (should no happen).
585          * <li> 1 if the index of this stackfram is greater.
586          * </ul>
587          */
588         public int compareTo (Object obj)
589         {
590                 if (index < ((PHPStackFrame) obj).getIndex ()) {
591                         return -1;
592                 }
593                 else if (index > ((PHPStackFrame) obj).getIndex ()) {
594                         return 1;
595                 }
596
597                 return 0;
598         }
599 }