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.Arrays;
15 import java.util.Collections;
16 import java.util.Vector;
18 import net.sourceforge.phpdt.internal.debug.core.PHPDBGProxy;
19 import net.sourceforge.phpdt.internal.debug.core.PHPDebugCorePlugin;
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;
34 public class PHPStackFrame extends PHPDebugElement implements IStackFrame, Comparable {
36 private PHPThread thread; // The thread to which this stackframe belongs
37 private String file; // The file name???
38 private int lineNumber;
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
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;
61 public PHPStackFrame (PHPThread thread, String file, int line, int index,
62 String desc, int modno, int scope_id) {
65 this.lineNumber = line;
69 this.description = desc;
71 this.scope_id = scope_id;
72 this.varList = new Vector();
73 this.fUpToDate = false;
83 // public PHPStackFrame(PHPThread thread, String file, int line, int index) {
86 // this.lineNumber = line;
87 // this.index = index;
89 // this.thread = thread;
90 // this.fUpToDate = false;
97 public int getScopeID() {
104 public void setScopeID(int scope_id) {
105 this.scope_id = scope_id;
112 public IThread getThread() {
119 public void setThread(PHPThread thread) {
120 this.thread = thread;
126 // private void setUpToDate(boolean upToDate) {
127 // fUpToDate = upToDate;
133 // private boolean isUpToDate() {
140 public void setAvailable(boolean available) {
141 fAvailable = available;
147 public boolean isAvailable() {
152 * @see IAdaptable#getAdapter(Class)
154 public Object getAdapter(Class adapter) {
155 if (adapter == PHPStackFrame.class) {
159 return super.getAdapter(adapter);
165 private void resetHasChangedInfo(Vector varList) {
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
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
180 } catch (DebugException e) { // That's, because of the hasVariables method
183 var.setValueChanged(false); // Reset the 'has changed' flag
188 * Go up the tree of PHPVariables
189 * look whether the PHPValue is a reference to a parent PHPValue
191 * TODO Check where this recursion can come from.
192 * Whether this back reference is legal or a bug.
194 * Typically $GLOBALS contains $GLOBALS
199 * <li> false if the PHPValue is not a child of itself
200 * <li> true if the PHPValue is
203 private boolean hasRecursion (PHPVariable var) {
204 PHPVariable parentVar;
207 val = (PHPValue) var.getValue (); // Get the PHPValue from the current PHPVariable
209 while (var != null) { // As long as we have PHPVariable
210 parentVar = var.getParent (); // Get the parent PHPVariable
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
222 return false; // No recursion found
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:
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.
241 * @param varListOld The 'static' list of variables which are to be updated.
242 * @param varListNew The new list of (current) variables from DBG.
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
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.
256 for (n = 0; n < varListNew.size(); n++) { // For every variable in 'DBG list'
257 varNew = (PHPVariable) varListNew.get(n); // Get the DBG variable
259 for (o = 0; o < varListOld.size(); o++) { // For every variable in static list
260 varOld = (PHPVariable) varListOld.get(o); // Get the static variable
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
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
272 valOld.getChildVariables (), // Update the variable list for the child variables
273 valNew.getChildVariables ());
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
284 } catch (DebugException e) { // That's, because of the hasVariables method
287 break; // Found the variable,
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
296 // Look for the variables we can remove from the 'static' list
298 for (o = 0; o < varListOld.size(); o++) { // For every variable in 'static' list
299 varOld = (PHPVariable) varListOld.get(o); // Get the static variable
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
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
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
317 * This function returns the array of PHPVariables for this stackframe
318 * The PHPVariables should not change (newly build up) between two steps
320 * A PHPVariable with the same name but with different object ID is
321 * handled as a new variable.
323 * @return The array of PHPVariables for this stackframe.
325 public IVariable[] getVariables() throws DebugException {
327 resetHasChangedInfo (varList);
328 updateVariableList (varList, this.getPHPDBGProxy ().readVariables (this));
331 variables = (PHPVariable[]) varList.toArray (new PHPVariable[varList.size ()]);
332 Arrays.sort (variables, new PHPVariableComparator ());
334 Collections.sort (varList, new PHPVariableComparator ());
338 // return (PHPVariable[]) varList.toArray (new PHPVariable[varList.size ()]);
340 return variables; // Give the array back to user interface
346 private PHPVariable findVariable(Vector varList, String varname) {
347 PHPVariable variable;
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
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);
366 if (variable.getName().equals (varname)) {
369 } catch (DebugException e) { // That's, because of the hasVariables method
377 * This method is called from the UI (e.g. from PHPDebugHover
378 * to find the variable the mouse is pointing to)
380 * @param s The variable name we are looking for.
383 public IVariable findVariable(String s) throws DebugException {
388 return (findVariable(varList, s)); // Prefix the variable name with $
394 public boolean hasVariables() throws DebugException {
399 return (varList.size() > 0);
402 public int getLineNumber() {
406 public void setLineNumber(int line) {
410 public int getCharStart() throws DebugException {
415 public int getCharEnd() throws DebugException {
420 public String getName() {
421 StringBuffer name = new StringBuffer();
423 if (!this.getDescription().equals("")) {
424 name.append (this.getDescription());
426 name.append (this.getFileName());
429 name.append (" [line ");
430 name.append (this.getLineNumber());
434 System.out.println ("PHPStackFrame getName: " + name.toString ());
437 return name.toString();
440 public String getFileName() {
444 public void setDescription(String desc) {
445 this.description = desc;
448 public String getDescription() {
449 return this.description;
452 public IRegisterGroup[] getRegisterGroups() throws DebugException {
456 public boolean hasRegisterGroups() throws DebugException {
460 public String getModelIdentifier() {
461 return this.getThread().getModelIdentifier();
464 public IDebugTarget getDebugTarget() {
465 return this.getThread().getDebugTarget();
468 public ILaunch getLaunch() {
469 return this.getDebugTarget().getLaunch();
472 public boolean canStepInto() {
476 public boolean canStepOver() {
480 public boolean canStepReturn() {
484 public boolean isStepping() {
491 public void stepInto() throws DebugException {
494 thread.prepareForResume(DebugEvent.STEP_INTO); // Don't know why, but this is necessary
495 this.getPHPDBGProxy().readStepIntoEnd(PHPStackFrame.this);
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???)
504 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
505 //DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
511 public void stepOver() throws DebugException {
514 thread.prepareForResume(DebugEvent.STEP_OVER);
515 this.getPHPDBGProxy().readStepOverEnd(PHPStackFrame.this);
517 // See comment within the previous stepInto method.
519 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
520 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
526 public void stepReturn() throws DebugException {
529 thread.prepareForResume(DebugEvent.STEP_RETURN);
530 this.getPHPDBGProxy().readStepReturnEnd(PHPStackFrame.this);
532 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
533 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
536 public boolean canResume() {
537 return this.getThread().canResume();
540 public boolean canSuspend() {
541 return this.getThread().canSuspend();
544 public boolean isSuspended() {
545 return this.getThread().isSuspended();
548 public void resume() throws DebugException {
551 this.getThread().resume();
554 public void suspend() throws DebugException {
557 public boolean canTerminate() {
558 return this.getThread().canTerminate();
561 public boolean isTerminated() {
562 return this.getThread().isTerminated();
565 public void terminate() throws DebugException {
566 getPHPDBGProxy().stop();
569 public int getIndex() {
573 public void setIndex(int index) {
577 public PHPDBGProxy getPHPDBGProxy() {
578 PHPDebugTarget DebugTarget;
580 DebugTarget = (PHPDebugTarget) thread.getDebugTarget();
582 return DebugTarget.getPHPDBGProxy();
585 public void setFile(String file) {
588 final String COMPILED_EVAL = "eval()'d code";
589 final String COMPILED_LAMBDA = "runtime-created function";
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();
598 // assume COMPILED_STRING_DESCRIPTION_FORMAT
599 // "filename(linenumber) : string"
602 switch (file.charAt(i)) {
607 this.file = file.substring(0, i);
609 lineNumber = Integer.parseInt(file.substring(i + 1, j));
610 } catch (NumberFormatException e) {
611 PHPDebugCorePlugin.log(e);
619 public int getModNo() {
624 * This function is needed when sorting the stackframes by their index numbers.
626 * @param obj The stackframe which this one is compared to.
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.
634 public int compareTo (Object obj) {
635 if (!(obj instanceof PHPStackFrame)) {
636 throw new IllegalArgumentException ("A PHPStackFrame can only be compared with another PHPStackFrame");
639 int frameIndex = ((PHPStackFrame) obj).getIndex();
641 if (index < frameIndex) {
643 } else if (index > frameIndex) {