1 19 20 package org.netbeans.modules.debugger.jpda.models; 21 22 import com.sun.jdi.AbsentInformationException; 23 import com.sun.jdi.ArrayReference; 24 import com.sun.jdi.InternalException; 25 import com.sun.jdi.InvalidStackFrameException; 26 import com.sun.jdi.LocalVariable; 27 import com.sun.jdi.NativeMethodException; 28 import com.sun.jdi.ObjectReference; 29 import com.sun.jdi.ReferenceType; 30 import com.sun.jdi.StackFrame; 31 import com.sun.jdi.VMDisconnectedException; 32 import com.sun.jdi.Value; 33 import java.beans.Customizer ; 34 35 import java.beans.PropertyChangeEvent ; 36 import java.beans.PropertyChangeListener ; 37 import java.lang.ref.WeakReference ; 38 import java.util.ArrayList ; 39 import java.util.List ; 40 import java.util.Map ; 41 import java.util.Vector ; 42 import java.util.WeakHashMap ; 43 import org.netbeans.api.debugger.jpda.JPDAClassType; 44 import org.netbeans.spi.debugger.ContextProvider; 45 import org.netbeans.api.debugger.jpda.JPDADebugger; 46 import org.netbeans.api.debugger.jpda.Variable; 47 import org.netbeans.spi.debugger.jpda.EditorContext.Operation; 48 import org.netbeans.spi.viewmodel.ModelEvent; 49 import org.netbeans.spi.viewmodel.TreeModel; 50 import org.netbeans.spi.viewmodel.ModelListener; 51 import org.netbeans.spi.viewmodel.UnknownTypeException; 52 53 import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl; 54 55 import org.openide.util.RequestProcessor; 56 57 58 61 public class LocalsTreeModel implements TreeModel, PropertyChangeListener { 62 63 64 private static boolean verbose = 65 (System.getProperty ("netbeans.debugger.viewrefresh") != null) && 66 (System.getProperty ("netbeans.debugger.viewrefresh").indexOf ('l') >= 0); 67 68 69 private static final int ARRAY_CHILDREN_NESTED_LENGTH = 100; 70 71 72 private JPDADebuggerImpl debugger; 73 private Listener listener; 74 private List <ModelListener> listeners = new ArrayList <ModelListener>(); 75 private Map <Value, ArrayChildrenNode> cachedArrayChildren = new WeakHashMap <Value, ArrayChildrenNode>(); 77 78 79 public LocalsTreeModel (ContextProvider lookupProvider) { 80 debugger = (JPDADebuggerImpl) lookupProvider. 81 lookupFirst (null, JPDADebugger.class); 82 } 83 84 public Object getRoot () { 85 return ROOT; 86 } 87 88 public void propertyChange(PropertyChangeEvent evt) { 89 fireTableValueChangedChanged(evt.getSource(), null); 90 } 91 92 public Object [] getChildren (Object o, int from, int to) 93 throws UnknownTypeException { 94 Object [] ch = getChildrenImpl(o, from, to); 95 for (int i = 0; i < ch.length; i++) { 96 if (ch[i] instanceof Customizer ) { 97 ((Customizer ) ch[i]).addPropertyChangeListener(this); 98 } 99 } 100 return ch; 101 } 102 103 public Object [] getChildrenImpl (Object o, int from, int to) 104 throws UnknownTypeException { 105 try { 106 if (o.equals (ROOT)) { 107 Object [] os = getLocalVariables (from, to); 108 return os; 109 } else 110 if (o instanceof AbstractVariable) { AbstractVariable abstractVariable = (AbstractVariable) o; 112 boolean isArray = 113 abstractVariable.getInnerValue () instanceof ArrayReference; 114 if (isArray) { 115 to = abstractVariable.getFieldsCount (); 116 } 118 if (isArray && (to - from) > ARRAY_CHILDREN_NESTED_LENGTH) { 119 ArrayChildrenNode achn = 120 cachedArrayChildren.get(abstractVariable.getInnerValue ()); 121 if (achn == null) { 122 achn = new ArrayChildrenNode(abstractVariable); 123 cachedArrayChildren.put(abstractVariable.getInnerValue (), achn); 124 } else { 125 achn.update(abstractVariable); 126 } 127 return achn.getChildren(); 128 } else { 129 return abstractVariable.getFields (from, Math.min(to, abstractVariable.getFieldsCount())); 130 } 131 } else 132 if (o instanceof ArrayChildrenNode) { 133 return ((ArrayChildrenNode) o).getChildren(); 134 } else 135 if (o instanceof JPDAClassType) { 136 JPDAClassType clazz = (JPDAClassType) o; 137 List staticFields = clazz.staticFields(); 138 Object [] fields = new Object [1 + staticFields.size()]; 139 fields[0] = clazz.classObject(); 140 System.arraycopy(staticFields.toArray(), 0, fields, 1, staticFields.size()); 141 return fields; 142 } else 143 if ("lastOperations" == o) { 144 CallStackFrameImpl frame = (CallStackFrameImpl) debugger. 145 getCurrentCallStackFrame (); 146 if (frame == null) { 147 return new Object [] {}; 148 } 149 List <Operation> operations = frame.getThread().getLastOperations(); 150 List <Variable> lastOperationValues = new ArrayList <Variable>(operations.size()); 151 for (int i = 0; i < operations.size(); i++) { 152 Variable ret = operations.get(i).getReturnValue(); 153 if (ret != null) { 154 lastOperationValues.add(ret); 155 } 156 } 157 return lastOperationValues.toArray(); 158 } else 159 throw new UnknownTypeException (o); 160 } catch (VMDisconnectedException ex) { 161 return new Object [0]; 162 } 163 } 164 165 174 public int getChildrenCount (Object node) throws UnknownTypeException { 175 try { 176 if (node.equals (ROOT)) { 177 return Integer.MAX_VALUE; 179 220 } else 221 if (node instanceof AbstractVariable) { AbstractVariable abstractVariable = (AbstractVariable) node; 223 if (abstractVariable.getInnerValue () instanceof 224 ArrayReference 225 ) { 226 return Integer.MAX_VALUE; 228 } 230 return Integer.MAX_VALUE; 232 } else 234 if (node instanceof ArrayChildrenNode) { 235 return Integer.MAX_VALUE; 237 } else 239 if (node instanceof JPDAClassType) { 240 return Integer.MAX_VALUE; 242 } else 245 if (node instanceof JPDAClassType) { 246 JPDAClassType clazz = (JPDAClassType) node; 247 return 1 + clazz.staticFields().size(); 248 } else 249 if ("lastOperations" == node) { CallStackFrameImpl frame = (CallStackFrameImpl) debugger. 251 getCurrentCallStackFrame (); 252 if (frame == null) { 253 return 0; 254 } 255 List <Operation> operations = frame.getThread().getLastOperations(); 256 return operations.size(); 257 } else 258 throw new UnknownTypeException (node); 259 } catch (VMDisconnectedException ex) { 260 } 261 return 0; 262 } 263 264 public boolean isLeaf (Object o) throws UnknownTypeException { 265 if (o.equals (ROOT)) 266 return false; 267 if (o instanceof AbstractVariable) 268 return !(((AbstractVariable) o).getInnerValue () instanceof ObjectReference); 269 if (o.toString().startsWith("SubArray")) { 270 return false; 271 } 272 if (o.equals ("NoInfo")) return true; 274 if (o instanceof JPDAClassType) return false; 275 if (o == "lastOperations") return false; 276 throw new UnknownTypeException (o); 277 } 278 279 280 public void addModelListener (ModelListener l) { 281 synchronized (listeners) { 282 listeners.add (l); 283 if (listener == null) 284 listener = new Listener (this, debugger); 285 } 286 } 287 288 public void removeModelListener (ModelListener l) { 289 synchronized (listeners) { 290 listeners.remove (l); 291 if (listeners.size () == 0) { 292 listener.destroy (); 293 listener = null; 294 } 295 } 296 } 297 298 void fireTreeChanged () { 299 List <ModelListener> ls; 300 synchronized (listeners) { 301 ls = new ArrayList <ModelListener>(listeners); 302 } 303 int i, k = ls.size (); 304 for (i = 0; i < k; i++) 305 ls.get(i).modelChanged ( 306 new ModelEvent.TreeChanged (this) 307 ); 308 } 309 310 private void fireTableValueChangedChanged (Object node, String propertyName) { 311 List <ModelListener> ls; 312 synchronized (listeners) { 313 ls = new ArrayList <ModelListener>(listeners); 314 } 315 int i, k = ls.size (); 316 for (i = 0; i < k; i++) 317 ls.get(i).modelChanged ( 318 new ModelEvent.TableValueChanged (this, node, propertyName) 319 ); 320 } 321 322 323 325 private Object [] getLocalVariables ( 326 int from, 327 int to 328 ) { 329 synchronized (debugger.LOCK) { 330 CallStackFrameImpl callStackFrame = (CallStackFrameImpl) debugger. 331 getCurrentCallStackFrame (); 332 if (callStackFrame == null) 333 return new String [] {"No current thread"}; 334 StackFrame stackFrame = callStackFrame.getStackFrame (); 335 if (stackFrame == null) 336 return new String [] {"No current thread"}; 337 try { 338 ObjectReference thisR = stackFrame.thisObject (); 339 List <Operation> operations = callStackFrame.getThread().getLastOperations(); 340 ReturnVariableImpl returnVariable; 341 boolean haveLastOperations; 342 if (operations != null && operations.size() > 0 && operations.get(0).getReturnValue() != null) { 343 haveLastOperations = true; 344 returnVariable = null; 345 } else { 346 returnVariable = ((JPDAThreadImpl) callStackFrame.getThread()).getReturnVariable(); 347 haveLastOperations = false; 348 } 349 int retValShift = (haveLastOperations || returnVariable != null) ? 1 : 0; 350 if (thisR == null) { 351 ReferenceType classType = stackFrame.location().declaringType(); 352 Object [] avs = null; 353 try { 354 avs = getLocalVariables ( 355 callStackFrame, 356 stackFrame, 357 Math.max (from - retValShift - 1, 0), 358 Math.max (to - retValShift - 1, 0) 359 ); 360 } catch (AbsentInformationException ex) { 361 avs = new String [] {"NoInfo"}; 362 } 363 Object [] result = new Object [avs.length + retValShift + 1]; 364 if (from < 1 && retValShift > 0) { 365 if (returnVariable != null) { 366 result[0] = returnVariable; 367 } else { 368 result[0] = "lastOperations"; } 370 } 371 if (from < 1 + retValShift) { 372 result[retValShift] = debugger.getClassType(classType); 374 } 375 System.arraycopy (avs, 0, result, 1 + retValShift, avs.length); 376 return result; 377 } else { 378 Object [] avs = null; 379 try { 380 avs = getLocalVariables ( 381 callStackFrame, 382 stackFrame, 383 Math.max (from - retValShift - 1, 0), 384 Math.max (to - retValShift - 1, 0) 385 ); 386 } catch (AbsentInformationException ex) { 387 avs = new Object [] {"NoInfo"}; 388 } 389 Object [] result = new Object [avs.length + retValShift + 1]; 390 if (from < 1 && retValShift > 0) { 391 if (returnVariable != null) { 392 result[0] = returnVariable; 393 } else { 394 result[0] = "lastOperations"; } 396 } 397 if (from < 1 + retValShift) { 398 result[retValShift] = new ThisVariable (debugger, thisR, ""); 399 } 400 System.arraycopy (avs, 0, result, 1 + retValShift, avs.length); 401 return result; 402 } 403 } catch (InternalException ex) { 404 return new String [] {ex.getMessage ()}; 405 } 406 } } 408 409 org.netbeans.api.debugger.jpda.LocalVariable[] getLocalVariables ( 410 final CallStackFrameImpl callStackFrame, 411 final StackFrame stackFrame, 412 int from, 413 int to 414 ) throws AbsentInformationException { 415 org.netbeans.api.debugger.jpda.LocalVariable[] locals = callStackFrame.getLocalVariables(); 416 int n = locals.length; 417 to = Math.min(n, to); 418 from = Math.min(n, from); 419 if (from != 0 || to != n) { 420 org.netbeans.api.debugger.jpda.LocalVariable[] subLocals = new org.netbeans.api.debugger.jpda.LocalVariable[to - from]; 421 for (int i = from; i < to; i++) { 422 subLocals[i - from] = locals[i]; 423 } 424 locals = subLocals; 425 } 426 return locals; 427 448 } 449 475 476 public Variable getVariable (Value v) { 477 if (v instanceof ObjectReference) 478 return new AbstractVariable ( 479 debugger, 480 (ObjectReference) v, 481 null 482 ); 483 return new AbstractVariable (debugger, v, null); 484 } 485 486 487 JPDADebuggerImpl getDebugger () { 488 return debugger; 489 } 490 491 492 494 private static class Listener implements PropertyChangeListener { 495 496 private JPDADebugger debugger; 497 private WeakReference <LocalsTreeModel> model; 498 499 public Listener ( 500 LocalsTreeModel tm, 501 JPDADebugger debugger 502 ) { 503 this.debugger = debugger; 504 model = new WeakReference <LocalsTreeModel>(tm); 505 debugger.addPropertyChangeListener (this); 506 } 507 508 void destroy () { 509 debugger.removePropertyChangeListener (this); 510 if (task != null) { 511 task.cancel (); 513 if (verbose) 514 System.out.println("LTM cancel old task " + task); 515 task = null; 516 } 517 } 518 519 private LocalsTreeModel getModel () { 520 LocalsTreeModel tm = model.get (); 521 if (tm == null) { 522 destroy (); 523 } 524 return tm; 525 } 526 527 private RequestProcessor.Task task; 530 531 public void propertyChange (PropertyChangeEvent e) { 532 if ( ( (e.getPropertyName () == 533 JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME) || 534 (e.getPropertyName () == JPDADebugger.PROP_STATE) 536 ) && (debugger.getState () == JPDADebugger.STATE_STOPPED) 537 ) { 538 final LocalsTreeModel ltm = getModel (); 541 if (ltm == null) return; 542 if (task != null) { 543 task.cancel (); 545 if (verbose) 546 System.out.println("LTM cancel old task " + task); 547 task = null; 548 } 549 task = RequestProcessor.getDefault ().post (new Runnable () { 550 public void run () { 551 if (debugger.getState () != JPDADebugger.STATE_STOPPED) { 552 if (verbose) 553 System.out.println("LTM cancel started task " + task); 554 return; 555 } 556 if (verbose) 557 System.out.println("LTM do task " + task); 558 ltm.fireTreeChanged (); 559 } 560 }, 500); 561 if (verbose) 562 System.out.println("LTM create task " + task); 563 } else 564 if ( (e.getPropertyName () == JPDADebugger.PROP_STATE) && 565 (debugger.getState () != JPDADebugger.STATE_STOPPED) && 566 (task != null) 567 ) { 568 task.cancel (); 571 if (verbose) 572 System.out.println("LTM cancel task " + task); 573 task = null; 574 } 575 } 576 } 577 578 582 private static final class ArrayChildrenNode { 583 584 private AbstractVariable var; 585 private int from = 0; 586 private int length; 587 private int maxIndexLog; 588 589 public ArrayChildrenNode(AbstractVariable var) { 590 this(var, 0, var.getFieldsCount(), -1); 591 } 592 593 private ArrayChildrenNode(AbstractVariable var, int from, int length, 594 int maxIndex) { 595 this.var = var; 596 this.from = from; 597 this.length = length; 598 if (maxIndex < 0) { 599 maxIndex = from + length - 1; 600 } 601 this.maxIndexLog = ArrayFieldVariable.log10(maxIndex); 602 } 603 604 private static int pow(int a, int b) { 605 if (b == 0) return 1; 606 int p = a; 607 for (int i = 1; i < b; i++) { 608 p *= a; 609 } 610 return p; 611 } 612 613 public Object [] getChildren() { 614 if (length > ARRAY_CHILDREN_NESTED_LENGTH) { 615 int depth = (int) Math.ceil(Math.log(length)/Math.log(ARRAY_CHILDREN_NESTED_LENGTH) - 1); 616 int n = pow(ARRAY_CHILDREN_NESTED_LENGTH, depth); 617 int numCh = (int) Math.ceil(length/((double) n)); 618 619 Object [] ch = new Object [numCh]; 621 for (int i = 0; i < numCh; i++) { 622 int chLength = n; 623 if (i == (numCh - 1)) { 624 chLength = length % n; 625 if (chLength == 0) chLength = n; 626 } 627 ch[i] = new ArrayChildrenNode(var, from + i*n, chLength, from + length - 1); 628 } 629 return ch; 630 } else { 631 return var.getFields(from, from + length); 632 } 633 } 634 635 public void update(AbstractVariable var) { 636 this.var = var; 637 } 638 639 640 public boolean equals(Object obj) { 641 if (!(obj instanceof ArrayChildrenNode)) return false; 642 ArrayChildrenNode achn = (ArrayChildrenNode) obj; 643 return achn.var.equals(this.var) && 644 achn.from == this.from && 645 achn.length == this.length; 646 } 647 648 public int hashCode() { 649 return var.hashCode() + from + length; 650 } 651 652 public String toString() { 653 int num0 = maxIndexLog - ArrayFieldVariable.log10(from); 654 String froms; 655 if (num0 > 0) { 656 froms = ArrayFieldVariable.zeros(2*num0) + from; } else { 658 froms = Integer.toString(from); 659 } 660 int last = from + length - 1; 661 num0 = maxIndexLog - ArrayFieldVariable.log10(last); 662 String lasts; 663 if (num0 > 0) { 664 lasts = ArrayFieldVariable.zeros(2*num0) + last; } else { 666 lasts = Integer.toString(last); 667 } 668 return "SubArray"+froms+"-"+lasts; } 670 671 } 672 } 673 | Popular Tags |