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;
18 import net.sourceforge.phpdt.internal.debug.core.PHPDebugCorePlugin;
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;
33 public class PHPStackFrame extends PHPDebugElement implements IStackFrame, Comparable {
35 private PHPThread thread; // The thread to which this stackframe belongs
36 private String file; // The file name???
37 private int lineNumber;
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
45 private boolean fAvailable; // Needed when updating the stackframe list, shows whether the stackframe
46 // is within the list which was received from dbg
58 public PHPStackFrame(PHPThread thread, String file, int line, int index,
59 String desc, int modno, int scope_id) {
62 this.lineNumber = line;
66 this.description = desc;
68 this.scope_id = scope_id;
69 this.varList = new Vector();
70 this.fUpToDate = false;
80 // public PHPStackFrame(PHPThread thread, String file, int line, int index) {
83 // this.lineNumber = line;
84 // this.index = index;
86 // this.thread = thread;
87 // this.fUpToDate = false;
94 public int getScopeID() {
101 public void setScopeID(int scope_id) {
102 this.scope_id = scope_id;
109 public IThread getThread() {
116 public void setThread(PHPThread thread) {
117 this.thread = thread;
123 // private void setUpToDate(boolean upToDate) {
124 // fUpToDate = upToDate;
130 // private boolean isUpToDate() {
137 public void setAvailable(boolean available) {
138 fAvailable = available;
144 public boolean isAvailable() {
149 * @see IAdaptable#getAdapter(Class)
151 public Object getAdapter(Class adapter) {
152 if (adapter == PHPStackFrame.class) {
156 return super.getAdapter(adapter);
162 private void resetHasChangedInfo(Vector varList) {
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
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
177 } catch (DebugException e) { // That's, because of the hasVariables method
180 var.setValueChanged(false); // Reset the 'has changed' flag
185 * Go up the tree of PHPVariables
186 * look whether the PHPValue is a reference to a parent PHPValue
188 * TODO Check where this recursion can come from.
189 * Whether this back reference is legal or a bug.
191 * Typically $GLOBALS contains $GLOBALS
196 * <li> false if the PHPValue is not a child of itself
197 * <li> true if the PHPValue is
200 private boolean hasRecursion(PHPVariable var) {
201 PHPVariable parentVar;
204 val = (PHPValue) var.getValue(); // Get the PHPValue from the current PHPVariable
206 while (var != null) { // As long as we have PHPVariable
207 parentVar = var.getParent(); // Get the parent PHPVariable
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
219 return false; // No recursion found
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:
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.
238 * @param varListOld The 'static' list of variables which are to be updated.
239 * @param varListNew The new list of (current) variables from DBG.
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
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.
253 for (n = 0; n < varListNew.size(); n++) { // For every variable in 'DBG list'
254 varNew = (PHPVariable) varListNew.get(n); // Get the DBG variable
256 for (o = 0; o < varListOld.size(); o++) { // For every variable in static list
257 varOld = (PHPVariable) varListOld.get(o); // Get the static variable
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
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());
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
278 } catch (DebugException e) { // That's, because of the hasVariables method
281 break; // Found the variable,
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
290 // Look for the variables we can remove from the 'static' list
292 for (o = 0; o < varListOld.size(); o++) { // For every variable in 'static' list
293 varOld = (PHPVariable) varListOld.get(o); // Get the static variable
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
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
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
311 * This function returns the array of PHPVariables for this stackframe
312 * The PHPVariables should not change (newly build up) between two steps
314 * A PHPVariable with the same name but with different object ID is
315 * handled as a new variable.
317 * @return The array of PHPVariables for this stackframe.
319 public IVariable[] getVariables() throws DebugException {
321 resetHasChangedInfo(varList);
322 updateVariableList(varList, this.getPHPDBGProxy().readVariables(this));
324 Collections.sort(varList, new PHPVariableComparator());
327 return (PHPVariable[]) varList.toArray(new PHPVariable[varList.size()]);
333 private PHPVariable findVariable(Vector varList, String varname) {
334 PHPVariable variable;
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
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);
351 if (variable.getName().equals(varname)) {
354 } catch (DebugException e) { // That's, because of the hasVariables method
362 * This method is called from the UI (e.g. from PHPDebugHover
363 * to find the variable the mouse is pointing to)
365 * @param s The variable name we are looking for.
368 public IVariable findVariable(String s) throws DebugException {
373 return (findVariable(varList, s)); // Prefix the variable name with $
379 public boolean hasVariables() throws DebugException {
384 return (varList.size() > 0);
387 public int getLineNumber() {
391 public void setLineNumber(int line) {
395 public int getCharStart() throws DebugException {
400 public int getCharEnd() throws DebugException {
405 public String getName() {
406 StringBuffer name = new StringBuffer();
408 if (!this.getDescription().equals("")) {
409 name.append(this.getDescription());
411 name.append(this.getFileName());
414 name.append(" [line ");
415 name.append(this.getLineNumber());
418 return name.toString();
421 public String getFileName() {
425 public void setDescription(String desc) {
426 this.description = desc;
429 public String getDescription() {
430 return this.description;
433 public IRegisterGroup[] getRegisterGroups() throws DebugException {
437 public boolean hasRegisterGroups() throws DebugException {
441 public String getModelIdentifier() {
442 return this.getThread().getModelIdentifier();
445 public IDebugTarget getDebugTarget() {
446 return this.getThread().getDebugTarget();
449 public ILaunch getLaunch() {
450 return this.getDebugTarget().getLaunch();
453 public boolean canStepInto() {
457 public boolean canStepOver() {
461 public boolean canStepReturn() {
465 public boolean isStepping() {
472 public void stepInto() throws DebugException {
475 thread.prepareForResume(DebugEvent.STEP_INTO); // Don't know why, but this is necessary
476 this.getPHPDBGProxy().readStepIntoEnd(PHPStackFrame.this);
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???)
485 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_INTO);
486 //DebugPlugin.getDefault().fireDebugEventSet (new DebugEvent[] { ev });
492 public void stepOver() throws DebugException {
495 thread.prepareForResume(DebugEvent.STEP_OVER);
496 this.getPHPDBGProxy().readStepOverEnd(PHPStackFrame.this);
498 // See comment within the previous stepInto method.
500 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_OVER);
501 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
507 public void stepReturn() throws DebugException {
510 thread.prepareForResume(DebugEvent.STEP_RETURN);
511 this.getPHPDBGProxy().readStepReturnEnd(PHPStackFrame.this);
513 //ev = new DebugEvent (this.getThread (), DebugEvent.RESUME, DebugEvent.STEP_RETURN);
514 //DebugPlugin.getDefault ().fireDebugEventSet (new DebugEvent[] { ev });
517 public boolean canResume() {
518 return this.getThread().canResume();
521 public boolean canSuspend() {
522 return this.getThread().canSuspend();
525 public boolean isSuspended() {
526 return this.getThread().isSuspended();
529 public void resume() throws DebugException {
532 this.getThread().resume();
535 public void suspend() throws DebugException {
538 public boolean canTerminate() {
539 return this.getThread().canTerminate();
542 public boolean isTerminated() {
543 return this.getThread().isTerminated();
546 public void terminate() throws DebugException {
547 getPHPDBGProxy().stop();
550 public int getIndex() {
554 public void setIndex(int index) {
558 public PHPDBGProxy getPHPDBGProxy() {
559 PHPDebugTarget DebugTarget;
561 DebugTarget = (PHPDebugTarget) thread.getDebugTarget();
563 return DebugTarget.getPHPDBGProxy();
566 public void setFile(String file) {
569 final String COMPILED_EVAL = "eval()'d code";
570 final String COMPILED_LAMBDA = "runtime-created function";
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();
579 // assume COMPILED_STRING_DESCRIPTION_FORMAT
580 // "filename(linenumber) : string"
583 switch (file.charAt(i)) {
588 this.file = file.substring(0, i);
590 lineNumber = Integer.parseInt(file.substring(i + 1, j));
591 } catch (NumberFormatException e) {
592 PHPDebugCorePlugin.log(e);
600 public int getModNo() {
605 * This function is needed when sorting the stackframes by their index numbers.
607 * @param obj The stackframe which this one is compared to.
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.
615 public int compareTo(Object obj) {
616 if (!(obj instanceof PHPStackFrame)) {
617 throw new IllegalArgumentException("A PHPStackFrame can only be compared with another PHPStackFrame");
619 int frameIndex = ((PHPStackFrame) obj).getIndex();
620 if (index < frameIndex) {
622 } else if (index > frameIndex) {