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
 
   9         IBM Corporation - Initial implementation
 
  10         Vicente Fernando - www.alfersoft.com.ar
 
  11 **********************************************************************/
 
  12 package net.sourceforge.phpdt.internal.debug.core.model;
 
  14 import java.util.Collections;
 
  15 import java.util.Vector;
 
  17 import net.sourceforge.phpdt.internal.debug.core.PHPDBGProxy;
 
  19 import org.eclipse.core.runtime.IAdaptable;
 
  20 import org.eclipse.debug.core.DebugEvent;
 
  21 import org.eclipse.debug.core.DebugException;
 
  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;
 
  32 public class PHPStackFrame extends PHPDebugElement implements IStackFrame, Comparable {
 
  34         private PHPThread               thread;                                 // The thread to which this stackframe belongs
 
  35         private String          file;                                   // The file name???
 
  36         private int                     lineNumber;
 
  39         private int                     scope_id;                               // scope id
 
  40         private Vector          varList;                                // Variables list
 
  41         private String          description;                    // The source file name with the full path on target/remote system
 
  42         private boolean                 fUpToDate;                              // Indicates whether the variable list within this stackframe is
 
  44         private boolean                 fAvailable;                     // Needed when updating the stackframe list, shows whether the stackframe
 
  45                                                                                                         // is within the list which was received from dbg
 
  57         public PHPStackFrame(PHPThread thread, String file, int line, int index,
 
  58                         String desc, int modno, int scope_id) {
 
  61                 this.lineNumber = line;
 
  65                 this.description = desc;
 
  67                 this.scope_id = scope_id;
 
  68                 this.varList = new Vector();
 
  69                 this.fUpToDate = false;
 
  79 //      public PHPStackFrame(PHPThread thread, String file, int line, int index) {
 
  82 //              this.lineNumber = line;
 
  83 //              this.index = index;
 
  85 //              this.thread = thread;
 
  86 //              this.fUpToDate = false;
 
  93         public int getScopeID() {
 
 100         public void setScopeID(int scope_id) {
 
 101                 this.scope_id = scope_id;
 
 108         public IThread getThread() {
 
 115         public void setThread(PHPThread thread) {
 
 116                 this.thread = thread;
 
 122 //      private void setUpToDate(boolean upToDate) {
 
 123 //              fUpToDate = upToDate;
 
 129 //      private boolean isUpToDate() {
 
 136         public void setAvailable(boolean available) {
 
 137                 fAvailable = available;
 
 143         public boolean isAvailable() {
 
 148          * @see IAdaptable#getAdapter(Class)
 
 150         public Object getAdapter(Class adapter) {
 
 151                 if (adapter == PHPStackFrame.class) {
 
 155                 return super.getAdapter(adapter);
 
 161         private void resetHasChangedInfo(Vector varList) {
 
 166                 for (n = 0; n < varList.size(); n++) {                                          // For every variable in 'DBG list'
 
 167                         var = (PHPVariable) varList.get(n);                                     // Get the variable
 
 168                         val = (PHPValue) var.getValue();                                                // Get the variable's value
 
 171                                 if (val.hasVariables()) {                                                       // Do we have other variables within the value
 
 172                                         if (!hasRecursion(var)) {                                               // Is this variable (value) branch recursive?
 
 173                                                 resetHasChangedInfo(val.getChildVariables()); //  No, go into branch
 
 176                         } catch (DebugException e) {                                                    // That's, because of the hasVariables method
 
 179                         var.setValueChanged(false);                                                     // Reset the 'has changed' flag
 
 184          * Go up the tree of PHPVariables
 
 185          * look whether the PHPValue is a reference to a parent PHPValue
 
 187          * TODO Check where this recursion can come from.
 
 188          * Whether this back reference is legal or a bug.
 
 190          * Typically $GLOBALS contains $GLOBALS
 
 195          * <li> false if the PHPValue is not a child of itself
 
 196          * <li> true if the PHPValue is
 
 199         private boolean hasRecursion(PHPVariable var) {
 
 200                 PHPVariable parentVar;
 
 203                 val = (PHPValue) var.getValue();                                                        // Get the PHPValue from the current PHPVariable
 
 205                 while (var != null) {                                                                           // As long as we have PHPVariable
 
 206                         parentVar = var.getParent();                                                    // Get the parent PHPVariable
 
 208                         if (parentVar != null) {                                                                // Is there a parent?
 
 209                                 if (parentVar.getValue().equals(val)) {                         // Get the PHPValue for the parent PHPVariable and check
 
 210                                                                                                                                         // whether it is the same
 
 211                                         return true;                                                                    // Return, if we have recursion
 
 218                 return false;                                                                                           // No recursion found
 
 222          * This method updates the 'static' variables list.
 
 223          * It does a replication between the 'static' list (the variable list which
 
 224          * is a member of this DBG interface object) and the DBG variable list
 
 225          * (the list of variables which is received from PHP via DBG with the current suspend)
 
 226          * Replication is done in the following way:
 
 228          * <li> It looks for new variables within the DBG variables list and
 
 229          *      adds them to the 'static' list.
 
 230          * <li> It looks for changed variables copies the current value to the variable within
 
 231          *              the 'static list' and mark these variables as 'hasChanged' (which uses the UI
 
 232          *              for showing the variable with a different color).
 
 233          * <li> It looks for variables within the 'static' list, and removes them
 
 234          *              from the 'static' list in case the do not appear within the DBG list.
 
 237          * @param varListOld The 'static' list of variables which are to be updated.
 
 238          * @param varListNew The new list of (current) variables from DBG.
 
 240         private void updateVariableList(Vector varListOld, Vector varListNew) {
 
 241                 PHPVariable varOld;                                                                             // The variable from the 'static' list
 
 242                 PHPVariable varNew;                                                                             // The variable from the DBG list
 
 243                 PHPValue        valOld;                                                                                 // The value of the current variable from 'static' list
 
 244                 PHPValue        valNew;                                                                                 // The value of the current variable from DBG list
 
 245                 int             n;                                                                                              // Index for the DBG list
 
 246                 int             o;                                                                                              // Index for the static list
 
 248                 // Add the variables (and childs) to the static list if they are new
 
 249                 //  and update the values of variables which are already existend within
 
 250                 //  the 'static' list.
 
 252                 for (n = 0; n < varListNew.size(); n++) {                                       // For every variable in 'DBG list'
 
 253                         varNew = (PHPVariable) varListNew.get(n);                               // Get the DBG variable
 
 255                         for (o = 0; o < varListOld.size(); o++) {                               // For every variable in static list
 
 256                                 varOld = (PHPVariable) varListOld.get(o);                       // Get the static variable
 
 258                                 if (varNew.getName().equals(varOld.getName())) {        // Did we found the variable within the 'static' list?
 
 259                                         valOld = (PHPValue) varOld.getValue();                  // Get the value from 'static'
 
 260                                         valNew = (PHPValue) varNew.getValue();                  // Get the value from DBG
 
 263                                                 if (valOld.hasVariables() ||                            // If the 'static' value has child variables
 
 264                                                                 valNew.hasVariables()) {                        //  or if the DBG value has child variables
 
 265                                                         if (!hasRecursion(varOld) && !hasRecursion(varNew)) { // Both branches should not have a recursion
 
 266                                                                 updateVariableList(valOld.getChildVariables(), // Update the variable list for the child variables
 
 267                                                                                 valNew.getChildVariables());
 
 270                                                 if (!valOld.getValueString().equals(
 
 271                                                                 valNew.getValueString())) {             // Has the value changed?
 
 272                                                         valOld.setValueString(valNew.getValueString()); // Yes, set the 'static' value (variable) to the new value
 
 273                                                         varOld.setValueChanged(true);                   // and set the 'has changed' flag, so that the variable view
 
 274                                                                                                                                         // could show the user the changed status with a different
 
 277                                         } catch (DebugException e) {                                    // That's, because of the hasVariables method
 
 280                                         break;                                                                                  // Found the variable,
 
 284                         if (o == varListOld.size()) {                                                   // Did we found the variable within the static list?
 
 285                                 varListOld.add(varNew);                                                         //  No, then add the DBG variable to the static list
 
 289                 // Look for the variables we can remove from the 'static' list
 
 291                 for (o = 0; o < varListOld.size(); o++) {                                       // For every variable in 'static' list
 
 292                         varOld = (PHPVariable) varListOld.get(o);                               // Get the static variable
 
 294                         for (n = 0; n < varListNew.size(); n++) {                               // For all variables in 'DBG' list
 
 295                                 varNew = (PHPVariable) varListNew.get(n);                       // Get the variable from the 'DBG' list
 
 297                                 if (varNew.getName().equals(varOld.getName())) {        // Did we found the 'static' list variable within the 'DBG' list?
 
 298                                         break;                                                                                  // Yes we found the variable, then leave the loop
 
 302                         if (n == varListNew.size()) {                                                   // Did not find the 'static' list variable within the 'DBG' list?
 
 303                                 varListOld.remove(o--);                                                         // then remove the 'static' list variable from list
 
 310          * This function returns the array of PHPVariables for this stackframe
 
 311          * The PHPVariables should not change (newly build up) between two steps
 
 313          * A PHPVariable with the same name but with different object ID is
 
 314          * handled as a new variable.
 
 316          * @return The array of PHPVariables for this stackframe.
 
 318         public IVariable[] getVariables() throws DebugException {
 
 320                         resetHasChangedInfo(varList);
 
 321                         updateVariableList(varList, this.getPHPDBGProxy().readVariables(this));
 
 323                         Collections.sort(varList, new PHPVariableComparator());
 
 326                 return (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
 
 332         private PHPVariable findVariable(Vector varList, String varname) {
 
 333                 PHPVariable variable;
 
 337                 for (i = 0; i < varList.size(); i++) {                                          // For all variables
 
 338                         variable = (PHPVariable) varList.get(i);                                // Get the variable
 
 339                         value = (PHPValue) variable.getValue();                                 // Get the value of the variable
 
 342                                 if (value.hasVariables()) {                                             // Does the variable/value have children
 
 343                                         if (!hasRecursion(variable)) {                                  // Don't follow recursive variable/values
 
 344                                                 PHPVariable var = findVariable(value.getChildVariables(), varname);
 
 350                                 if (variable.getName().equals(varname)) {
 
 353                         } catch (DebugException e) {                                                    // That's, because of the hasVariables method
 
 361          * This method is called from the UI (e.g. from PHPDebugHover
 
 362          * to find the variable the mouse is pointing to)
 
 364          * @param s The variable name we are looking for.
 
 367         public IVariable findVariable(String s) throws DebugException {
 
 372                 return (findVariable(varList, s));                                                      // Prefix the variable name with $
 
 378         public boolean hasVariables() throws DebugException {
 
 383                 return (varList.size() > 0);
 
 386         public int getLineNumber() {
 
 390         public void setLineNumber(int line) {
 
 394         public int getCharStart() throws DebugException {
 
 399         public int getCharEnd() throws DebugException {
 
 404         public String getName() {
 
 405                 StringBuffer name = new StringBuffer();
 
 407                 if (!this.getDescription().equals("")) {
 
 408                         name.append(this.getDescription());
 
 410                         name.append(this.getFileName());
 
 413                 name.append(" [line ");
 
 414                 name.append(this.getLineNumber());
 
 417                 return name.toString();
 
 420         public String getFileName() {
 
 424         public void setDescription(String desc) {
 
 425                 this.description = desc;
 
 428         public String getDescription() {
 
 429                 return this.description;
 
 432         public IRegisterGroup[] getRegisterGroups() throws DebugException {
 
 436         public boolean hasRegisterGroups() throws DebugException {
 
 440         public String getModelIdentifier() {
 
 441                 return this.getThread().getModelIdentifier();
 
 444         public IDebugTarget getDebugTarget() {
 
 445                 return this.getThread().getDebugTarget();
 
 448         public ILaunch getLaunch() {
 
 449                 return this.getDebugTarget().getLaunch();
 
 452         public boolean canStepInto() {
 
 456         public boolean canStepOver() {
 
 460         public boolean canStepReturn() {
 
 464         public boolean isStepping() {
 
 471         public void stepInto() throws DebugException {
 
 474                 thread.prepareForResume(DebugEvent.STEP_INTO);                          // Don't know why, but this is necessary
 
 475                 this.getPHPDBGProxy().readStepIntoEnd(PHPStackFrame.this);
 
 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???)
 
 484                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
 
 485                 //DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
 
 491         public void stepOver() throws DebugException {
 
 494                 thread.prepareForResume(DebugEvent.STEP_OVER);
 
 495                 this.getPHPDBGProxy().readStepOverEnd(PHPStackFrame.this);
 
 497         // See comment within the previous stepInto method.
 
 499                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
 
 500                 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
 
 506         public void stepReturn() throws DebugException {
 
 509                 thread.prepareForResume(DebugEvent.STEP_RETURN);
 
 510                 this.getPHPDBGProxy().readStepReturnEnd(PHPStackFrame.this);
 
 512                 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
 
 513                 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
 
 516         public boolean canResume() {
 
 517                 return this.getThread().canResume();
 
 520         public boolean canSuspend() {
 
 521                 return this.getThread().canSuspend();
 
 524         public boolean isSuspended() {
 
 525                 return this.getThread().isSuspended();
 
 528         public void resume() throws DebugException {
 
 531                 this.getThread().resume();
 
 534         public void suspend() throws DebugException {
 
 537         public boolean canTerminate() {
 
 538                 return this.getThread().canTerminate();
 
 541         public boolean isTerminated() {
 
 542                 return this.getThread().isTerminated();
 
 545         public void terminate() throws DebugException {
 
 546                 getPHPDBGProxy().stop();
 
 549         public int getIndex() {
 
 553         public void setIndex(int index) {
 
 557         public PHPDBGProxy getPHPDBGProxy() {
 
 558                 PHPDebugTarget DebugTarget;
 
 560                 DebugTarget = (PHPDebugTarget) thread.getDebugTarget();
 
 562                 return DebugTarget.getPHPDBGProxy();
 
 565         public void setFile(String file) {
 
 569         public int getModNo() {
 
 574          * This function is needed when sorting the stackframes by their index numbers.
 
 576          * @param obj The stackframe which this one is compared to.
 
 579          * <li> -1 if the index of this stackframe is less.
 
 580          * <li> 0 if the index of both stackfream is equal (should no happen).
 
 581          * <li> 1 if the index of this stackfram is greater.
 
 584         public int compareTo(Object obj) {
 
 585                 //if (index < ((PHPStackFrame) obj).getIndex()) {
 
 587                 //} else if (index > ((PHPStackFrame) obj).getIndex()) {
 
 592                 return Integer.signum(index - ((PHPStackFrame) obj).getIndex());