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