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