001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.debugger.jpda.models;
043:
044: import com.sun.jdi.AbsentInformationException;
045: import com.sun.jdi.IncompatibleThreadStateException;
046: import com.sun.jdi.InvalidStackFrameException;
047: import com.sun.jdi.InternalException;
048: import com.sun.jdi.NativeMethodException;
049: import com.sun.jdi.ObjectReference;
050: import com.sun.jdi.PrimitiveValue;
051: import com.sun.jdi.StackFrame;
052: import com.sun.jdi.ThreadReference;
053: import com.sun.jdi.VMDisconnectedException;
054: import com.sun.jdi.Value;
055:
056: import com.sun.jdi.request.EventRequest;
057: import java.lang.reflect.InvocationTargetException;
058: import java.util.ArrayList;
059: import java.util.Collections;
060: import java.util.List;
061:
062: import org.netbeans.api.debugger.Session;
063: import org.netbeans.api.debugger.jpda.CallStackFrame;
064: import org.netbeans.api.debugger.jpda.JPDAThread;
065: import org.netbeans.api.debugger.jpda.LocalVariable;
066: import org.netbeans.api.debugger.jpda.This;
067: import org.netbeans.modules.debugger.jpda.EditorContextBridge;
068: import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
069: import org.netbeans.modules.debugger.jpda.util.Executor;
070: import org.netbeans.spi.debugger.jpda.EditorContext.MethodArgument;
071: import org.netbeans.spi.debugger.jpda.EditorContext.Operation;
072: import org.openide.ErrorManager;
073:
074: /**
075: * Class representating one line of callstack.
076: */
077: public class CallStackFrameImpl implements CallStackFrame {
078:
079: private static final boolean IS_JDK_16 = !System.getProperty(
080: "java.version").startsWith("1.5"); // NOI18N
081: static final boolean IS_JDK_160_02 = IS_JDK_16
082: && !System.getProperty("java.version").equals("1.6.0")
083: && !System.getProperty("java.version").equals("1.6.0_01");
084:
085: private StackFrame sf;
086: private int depth;
087: private JPDADebuggerImpl debugger;
088: //private AST ast;
089: private Operation currentOperation;
090: private boolean valid;
091:
092: public CallStackFrameImpl(StackFrame sf, int depth,
093: JPDADebuggerImpl debugger) {
094: this .sf = sf;
095: this .depth = depth;
096: this .debugger = debugger;
097: this .valid = true; // suppose we're valid when we're new
098: }
099:
100: // public interface ........................................................
101:
102: /**
103: * Returns line number of this frame in this callstack.
104: *
105: * @return Returns line number of this frame in this callstack.
106: */
107: public synchronized int getLineNumber(String struts) {
108: if (!valid)
109: return 0;
110: try {
111: return getStackFrame().location().lineNumber(struts);
112: } catch (InvalidStackFrameException isfex) {
113: // this stack frame is not available or information in it is not available
114: valid = false;
115: return 0;
116: } catch (VMDisconnectedException ex) {
117: return 0;
118: }
119: }
120:
121: public synchronized Operation getCurrentOperation(String struts) {
122: return currentOperation;
123: }
124:
125: public synchronized void setCurrentOperation(Operation operation) {
126: this .currentOperation = operation;
127: }
128:
129: /**
130: * Returns method name of this frame in this callstack.
131: *
132: * @return Returns method name of this frame in this callstack.
133: */
134: public synchronized String getMethodName() {
135: if (!valid)
136: return "";
137: try {
138: return getStackFrame().location().method().name();
139: } catch (InvalidStackFrameException ex) {
140: // this stack frame is not available or information in it is not available
141: valid = false;
142: return "";
143: } catch (VMDisconnectedException ex) {
144: return "";
145: }
146: }
147:
148: /**
149: * Returns class name of this frame in this callstack.
150: *
151: * @return class name of this frame in this callstack
152: */
153: public synchronized String getClassName() {
154: if (!valid)
155: return "";
156: try {
157: return getStackFrame().location().declaringType().name();
158: } catch (InvalidStackFrameException ex) {
159: // this stack frame is not available or information in it is not available
160: valid = false;
161: return "";
162: } catch (VMDisconnectedException ex) {
163: return "";
164: }
165: }
166:
167: /**
168: * Returns name of default stratumn.
169: *
170: * @return name of default stratumn
171: */
172: public synchronized String getDefaultStratum() {
173: if (!valid)
174: return "";
175: try {
176: return getStackFrame().location().declaringType()
177: .defaultStratum();
178: } catch (InvalidStackFrameException ex) {
179: // this stack frame is not available or information in it is not available
180: valid = false;
181: return "";
182: } catch (VMDisconnectedException ex) {
183: return "";
184: }
185: }
186:
187: /**
188: * Returns name of default stratumn.
189: *
190: * @return name of default stratumn
191: */
192: public synchronized List<String> getAvailableStrata() {
193: if (!valid)
194: return Collections.emptyList();
195: try {
196: return getStackFrame().location().declaringType()
197: .availableStrata();
198: } catch (InvalidStackFrameException ex) {
199: // this stack frame is not available or information in it is not available
200: valid = false;
201: return Collections.emptyList();
202: } catch (VMDisconnectedException ex) {
203: return Collections.emptyList();
204: }
205: }
206:
207: /**
208: * Returns name of file of this frame.
209: *
210: * @return name of file of this frame
211: * @throws NoInformationException if informations about source are not included or some other error
212: * occurres.
213: */
214: public synchronized String getSourceName(String stratum)
215: throws AbsentInformationException {
216: if (!valid)
217: return "";
218: try {
219: return getStackFrame().location().sourceName(stratum);
220: } catch (InvalidStackFrameException ex) {
221: // this stack frame is not available or information in it is not available
222: valid = false;
223: return "";
224: } catch (VMDisconnectedException ex) {
225: return "";
226: }
227: }
228:
229: /**
230: * Returns source path of file this frame is stopped in or null.
231: *
232: * @return source path of file this frame is stopped in or null
233: */
234: public synchronized String getSourcePath(String stratum)
235: throws AbsentInformationException {
236: if (!valid)
237: return "";
238: try {
239: return getStackFrame().location().sourcePath(stratum);
240: } catch (InvalidStackFrameException ex) {
241: // this stack frame is not available or information in it is not available
242: valid = false;
243: return "";
244: } catch (VMDisconnectedException ex) {
245: return "";
246: }
247: }
248:
249: /**
250: * Returns local variables.
251: *
252: * @return local variables
253: */
254: public org.netbeans.api.debugger.jpda.LocalVariable[] getLocalVariables()
255: throws AbsentInformationException {
256: try {
257: String className = getStackFrame().location()
258: .declaringType().name();
259: List l = getStackFrame().visibleVariables();
260: int n = l.size();
261: LocalVariable[] locals = new LocalVariable[n];
262: for (int i = 0; i < n; i++) {
263: com.sun.jdi.LocalVariable lv = (com.sun.jdi.LocalVariable) l
264: .get(i);
265: Value v = getStackFrame().getValue(lv);
266: LocalVariable local = (LocalVariable) debugger
267: .getLocalVariable(lv, v);
268: if (local instanceof Local) {
269: Local localImpl = (Local) local;
270: localImpl.setFrame(this );
271: localImpl.setInnerValue(v);
272: localImpl.setClassName(className);
273: } else {
274: ObjectLocalVariable localImpl = (ObjectLocalVariable) local;
275: localImpl.setFrame(this );
276: localImpl.setInnerValue(v);
277: localImpl.setClassName(className);
278: }
279: locals[i] = local;
280: }
281: return locals;
282: } catch (NativeMethodException ex) {
283: throw new AbsentInformationException("native method");
284: } catch (InvalidStackFrameException ex) {
285: throw new AbsentInformationException("thread is running");
286: } catch (VMDisconnectedException ex) {
287: return new LocalVariable[0];
288: }
289: }
290:
291: /**
292: * Returns local variable.
293: * @param name The name of the variable
294: * @return local variable
295: */
296: org.netbeans.api.debugger.jpda.LocalVariable getLocalVariable(
297: String name) throws AbsentInformationException {
298: try {
299: String className = getStackFrame().location()
300: .declaringType().name();
301: com.sun.jdi.LocalVariable lv = getStackFrame()
302: .visibleVariableByName(name);
303: Value v = getStackFrame().getValue(lv);
304: LocalVariable local = (LocalVariable) debugger
305: .getLocalVariable(lv, v);
306: if (local instanceof Local) {
307: Local localImpl = (Local) local;
308: localImpl.setFrame(this );
309: localImpl.setInnerValue(v);
310: localImpl.setClassName(className);
311: } else {
312: ObjectLocalVariable localImpl = (ObjectLocalVariable) local;
313: localImpl.setFrame(this );
314: localImpl.setInnerValue(v);
315: localImpl.setClassName(className);
316: }
317: return local;
318: } catch (NativeMethodException ex) {
319: throw new AbsentInformationException("native method");
320: } catch (InvalidStackFrameException ex) {
321: throw new AbsentInformationException("thread is running");
322: } catch (VMDisconnectedException ex) {
323: return null;
324: }
325: }
326:
327: public LocalVariable[] getMethodArguments() {
328: StackFrame sf = getStackFrame();
329: String url = debugger.getEngineContext().getURL(sf,
330: getDefaultStratum());
331: List<Value> argValues = getArgumentValues(sf);
332: if (argValues == null)
333: return null;
334: MethodArgument[] argumentNames = EditorContextBridge
335: .getContext().getArguments(url,
336: sf.location().lineNumber());
337: if (argumentNames == null)
338: return null;
339: LocalVariable[] arguments = new LocalVariable[argumentNames.length];
340: for (int i = 0; i < arguments.length; i++) {
341: com.sun.jdi.Value value = argValues.get(i);
342: if (value instanceof ObjectReference) {
343: arguments[i] = new ArgumentObjectVariable(debugger,
344: (ObjectReference) value, argumentNames[i]
345: .getName(), argumentNames[i].getType());
346: } else {
347: arguments[i] = new ArgumentVariable(debugger,
348: (PrimitiveValue) value, argumentNames[i]
349: .getName(), argumentNames[i].getType());
350: }
351: }
352: return arguments;
353: }
354:
355: List<LocalVariable> findOperationArguments(Operation operation) {
356: if (!IS_JDK_160_02)
357: return null; // Can evaluate methods after pop since JDK 1.6.0_02
358: JPDADebuggerImpl debuggerImpl = (JPDADebuggerImpl) debugger;
359: JPDAThreadImpl thread = (JPDAThreadImpl) getThread();
360: synchronized (debuggerImpl.LOCK) {
361: synchronized (thread) {
362: ThreadReference tr = thread.getThreadReference();
363: com.sun.jdi.VirtualMachine vm = tr.virtualMachine();
364: com.sun.jdi.request.StepRequest step = vm
365: .eventRequestManager()
366: .createStepRequest(
367: tr,
368: com.sun.jdi.request.StepRequest.STEP_MIN,
369: com.sun.jdi.request.StepRequest.STEP_INTO);
370: step.addCountFilter(1);
371: step
372: .setSuspendPolicy(com.sun.jdi.request.StepRequest.SUSPEND_EVENT_THREAD);
373: step.enable();
374: step.putProperty("silent", Boolean.TRUE);
375: final Boolean[] stepDone = new Boolean[] { null };
376: debugger.getOperator().register(step, new Executor() {
377: public boolean exec(com.sun.jdi.event.Event event) {
378: synchronized (stepDone) {
379: stepDone[0] = true;
380: stepDone.notify();
381: }
382: return false;
383: }
384:
385: public void removed(EventRequest eventRequest) {
386: synchronized (stepDone) {
387: stepDone[0] = false;
388: stepDone.notify();
389: }
390: }
391: });
392: tr.resume();
393: synchronized (stepDone) {
394: if (stepDone[0] == null) {
395: try {
396: stepDone.wait();
397: } catch (InterruptedException iex) {
398: }
399: }
400: if (Boolean.FALSE.equals(stepDone[0])) {
401: return null; // Step was canceled
402: }
403: }
404: StackFrame sf = null;
405: List<com.sun.jdi.Value> arguments = null;
406: try {
407: sf = tr.frames(0, 1).get(0);
408: arguments = getArgumentValues(sf);
409: } catch (IncompatibleThreadStateException itsex) {
410: ErrorManager.getDefault().notify(itsex);
411: return null;
412: } finally {
413: vm.eventRequestManager().deleteEventRequest(step);
414: debugger.getOperator().unregister(step);
415: try {
416: if (sf != null) {
417: tr.popFrames(sf);
418: }
419: } catch (IncompatibleThreadStateException itsex) {
420: ErrorManager.getDefault().notify(itsex);
421: return null;
422: } catch (NativeMethodException nmex) {
423: return null;
424: } catch (InternalException iex) {
425: if (iex.errorCode() == 32) {
426: return null;
427: } else {
428: throw iex;
429: }
430: }
431: }
432: if (arguments != null) {
433: MethodArgument[] argumentNames;
434: try {
435: Session session = debugger.getSession();
436: argumentNames = EditorContextBridge
437: .getContext()
438: .getArguments(
439: debuggerImpl
440: .getEngineContext()
441: .getURL(
442: tr.frames(0, 1)
443: .get(0),
444: session
445: .getCurrentLanguage()),
446: operation);
447: } catch (IncompatibleThreadStateException itsex) {
448: ErrorManager.getDefault().notify(itsex);
449: return null;
450: }
451: if (argumentNames != null) {
452: List<LocalVariable> argumentList = new ArrayList<LocalVariable>(
453: arguments.size());
454: for (int i = 0; i < arguments.size(); i++) {
455: com.sun.jdi.Value value = arguments.get(i);
456: if (value instanceof ObjectReference) {
457: argumentList
458: .add(new ArgumentObjectVariable(
459: debuggerImpl,
460: (ObjectReference) value,
461: argumentNames[i]
462: .getName(),
463: //argumentNames[i].getType()));
464: value.type().name()));
465: } else {
466: argumentList.add(new ArgumentVariable(
467: debuggerImpl,
468: (PrimitiveValue) value,
469: argumentNames[i].getName(),
470: //argumentNames[i].getType()));
471: (value != null) ? value.type()
472: .name() : null));
473: }
474: }
475: return argumentList;
476: }
477: }
478: }
479: }
480: return null;
481: }
482:
483: private static List<com.sun.jdi.Value> getArgumentValues(
484: StackFrame sf) {
485: if (!IS_JDK_16)
486: return null;
487: try {
488: if (sf.location().method().isNative()) {
489: throw new NativeMethodException(sf.location().method()
490: .name());
491: }
492: java.lang.reflect.Method getArgumentValuesMethod = sf
493: .getClass().getMethod("getArgumentValues"); // NOI18N
494: Object values = getArgumentValuesMethod.invoke(sf,
495: new java.lang.Object[] {});
496: return (List<com.sun.jdi.Value>) values;
497: } catch (IllegalAccessException ex) {
498: org.openide.ErrorManager.getDefault().notify(ex);
499: } catch (IllegalArgumentException ex) {
500: org.openide.ErrorManager.getDefault().notify(ex);
501: } catch (InvocationTargetException ex) {
502: org.openide.ErrorManager.getDefault().notify(ex);
503: } catch (java.lang.NoSuchMethodException ex) {
504: org.openide.ErrorManager.getDefault().notify(ex);
505: } catch (java.lang.SecurityException ex) {
506: org.openide.ErrorManager.getDefault().notify(ex);
507: }
508: return java.util.Collections.emptyList();
509: }
510:
511: /**
512: * Returns object reference this frame is associated with or null (
513: * frame is in static method).
514: *
515: * @return object reference this frame is associated with or null
516: */
517: public synchronized This getThisVariable() {
518: if (!valid)
519: return null;
520: ObjectReference this R;
521: try {
522: this R = getStackFrame().this Object();
523: } catch (InvalidStackFrameException ex) {
524: valid = false;
525: return null;
526: }
527: if (this R == null)
528: return null;
529: return new ThisVariable(debugger, this R, "");
530: }
531:
532: /**
533: * Sets this frame current.
534: *
535: * @see org.netbeans.api.debugger.jpda.JPDADebugger#getCurrentCallStackFrame
536: */
537: public void makeCurrent() {
538: debugger.setCurrentCallStackFrame(this );
539: }
540:
541: /**
542: * Returns <code>true</code> if the method in this frame is obsoleted.
543: *
544: * @return <code>true</code> if the method in this frame is obsoleted
545: * @throws InvalidStackFrameException when this stack frame becomes invalid
546: */
547: public synchronized boolean isObsolete() {
548: return getStackFrame().location().method().isObsolete();
549: }
550:
551: public boolean canPop() {
552: if (!debugger.canPopFrames())
553: return false;
554: ThreadReference t = getStackFrame().thread();
555: try {
556: if (t.frameCount() <= 1) { // Nowhere to pop
557: return false;
558: }
559: List topFrames = t.frames(0, 2);
560: if (((StackFrame) topFrames.get(0)).location().method()
561: .isNative()
562: || ((StackFrame) topFrames.get(1)).location()
563: .method().isNative()) {
564: // Have native methods on the stack - can not pop
565: return false;
566: }
567: } catch (IncompatibleThreadStateException itsex) {
568: return false;
569: }
570: // Looks like we should be able to pop...
571: return true;
572: }
573:
574: /**
575: * Pop stack frames. All frames up to and including the frame
576: * are popped off the stack. The frame previous to the parameter
577: * frame will become the current frame.
578: *
579: * @throws InvalidStackFrameException when this stack frame becomes invalid
580: */
581: public void popFrame() {
582: debugger.popFrames(sf.thread(), getStackFrame());
583: }
584:
585: /**
586: * Returns thread.
587: *
588: * @return thread
589: * @throws InvalidStackFrameException when this stack frame becomes invalid
590: */
591: public JPDAThread getThread() {
592: return debugger.getThread(sf.thread());
593: }
594:
595: // other methods............................................................
596:
597: /**
598: * Get the JDI stack frame.
599: * @throws IllegalStateException when the associated thread is not suspended.
600: */
601: public StackFrame getStackFrame() {
602: return sf;
603: }
604:
605: /**
606: * Get the depth of this stack frame in the thread stack.
607: */
608: public int getFrameDepth() {
609: return depth;
610: }
611:
612: public boolean equals(Object o) {
613: try {
614: return (o instanceof CallStackFrameImpl)
615: && (sf.equals(((CallStackFrameImpl) o).sf));
616: } catch (InvalidStackFrameException isfex) {
617: return sf == ((CallStackFrameImpl) o).sf;
618: }
619: }
620:
621: private Integer hashCode;
622:
623: public synchronized int hashCode() {
624: if (hashCode == null) {
625: try {
626: hashCode = new Integer(sf.hashCode());
627: } catch (InvalidStackFrameException isfex) {
628: valid = false;
629: hashCode = new Integer(super.hashCode());
630: }
631: }
632: return hashCode.intValue();
633: }
634: }
|