--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/tools/jdi/StackFrameImpl.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,404 @@
+/*
+ * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.jdi;
+
+import com.sun.jdi.*;
+
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Collections;
+
+public class StackFrameImpl extends MirrorImpl
+ implements StackFrame, ThreadListener
+{
+ /* Once false, frame should not be used.
+ * access synchronized on (vm.state())
+ */
+ private boolean isValid = true;
+
+ private final ThreadReferenceImpl thread;
+ private final long id;
+ private final Location location;
+ private Map<String, LocalVariable> visibleVariables = null;
+ private ObjectReference thisObject = null;
+
+ StackFrameImpl(VirtualMachine vm, ThreadReferenceImpl thread,
+ long id, Location location) {
+ super(vm);
+ this.thread = thread;
+ this.id = id;
+ this.location = location;
+ thread.addListener(this);
+ }
+
+ /*
+ * ThreadListener implementation
+ * Must be synchronized since we must protect against
+ * sending defunct (isValid == false) stack ids to the back-end.
+ */
+ public boolean threadResumable(ThreadAction action) {
+ synchronized (vm.state()) {
+ if (isValid) {
+ isValid = false;
+ return false; /* remove this stack frame as a listener */
+ } else {
+ throw new InternalException(
+ "Invalid stack frame thread listener");
+ }
+ }
+ }
+
+ void validateStackFrame() {
+ if (!isValid) {
+ throw new InvalidStackFrameException("Thread has been resumed");
+ }
+ }
+
+ /**
+ * Return the frame location.
+ * Need not be synchronized since it cannot be provably stale.
+ */
+ public Location location() {
+ validateStackFrame();
+ return location;
+ }
+
+ /**
+ * Return the thread holding the frame.
+ * Need not be synchronized since it cannot be provably stale.
+ */
+ public ThreadReference thread() {
+ validateStackFrame();
+ return thread;
+ }
+
+ public boolean equals(Object obj) {
+ if ((obj != null) && (obj instanceof StackFrameImpl)) {
+ StackFrameImpl other = (StackFrameImpl)obj;
+ return (id == other.id) &&
+ (thread().equals(other.thread())) &&
+ (location().equals(other.location())) &&
+ super.equals(obj);
+ } else {
+ return false;
+ }
+ }
+
+ public int hashCode() {
+ return (thread().hashCode() << 4) + ((int)id);
+ }
+
+ public ObjectReference thisObject() {
+ validateStackFrame();
+ MethodImpl currentMethod = (MethodImpl)location.method();
+ if (currentMethod.isStatic() || currentMethod.isNative()) {
+ return null;
+ } else {
+ if (thisObject == null) {
+ PacketStream ps;
+
+ /* protect against defunct frame id */
+ synchronized (vm.state()) {
+ validateStackFrame();
+ ps = JDWP.StackFrame.ThisObject.
+ enqueueCommand(vm, thread, id);
+ }
+
+ /* actually get it, now that order is guaranteed */
+ try {
+ thisObject = JDWP.StackFrame.ThisObject.
+ waitForReply(vm, ps).objectThis;
+ } catch (JDWPException exc) {
+ switch (exc.errorCode()) {
+ case JDWP.Error.INVALID_FRAMEID:
+ case JDWP.Error.THREAD_NOT_SUSPENDED:
+ case JDWP.Error.INVALID_THREAD:
+ throw new InvalidStackFrameException();
+ default:
+ throw exc.toJDIException();
+ }
+ }
+ }
+ }
+ return thisObject;
+ }
+
+ /**
+ * Build the visible variable map.
+ * Need not be synchronized since it cannot be provably stale.
+ */
+ private void createVisibleVariables() throws AbsentInformationException {
+ if (visibleVariables == null) {
+ List<LocalVariable> allVariables = location.method().variables();
+ Map<String, LocalVariable> map = new HashMap<String, LocalVariable>(allVariables.size());
+
+ for (LocalVariable variable : allVariables) {
+ String name = variable.name();
+ if (variable.isVisible(this)) {
+ LocalVariable existing = (LocalVariable)map.get(name);
+ if ((existing == null) ||
+ ((LocalVariableImpl)variable).hides(existing)) {
+ map.put(name, variable);
+ }
+ }
+ }
+ visibleVariables = map;
+ }
+ }
+
+ /**
+ * Return the list of visible variable in the frame.
+ * Need not be synchronized since it cannot be provably stale.
+ */
+ public List<LocalVariable> visibleVariables() throws AbsentInformationException {
+ validateStackFrame();
+ createVisibleVariables();
+ List<LocalVariable> mapAsList = new ArrayList<LocalVariable>(visibleVariables.values());
+ Collections.sort(mapAsList);
+ return mapAsList;
+ }
+
+ /**
+ * Return a particular variable in the frame.
+ * Need not be synchronized since it cannot be provably stale.
+ */
+ public LocalVariable visibleVariableByName(String name) throws AbsentInformationException {
+ validateStackFrame();
+ createVisibleVariables();
+ return visibleVariables.get(name);
+ }
+
+ public Value getValue(LocalVariable variable) {
+ List<LocalVariable> list = new ArrayList<LocalVariable>(1);
+ list.add(variable);
+ return getValues(list).get(variable);
+ }
+
+ public Map<LocalVariable, Value> getValues(List<? extends LocalVariable> variables) {
+ validateStackFrame();
+ validateMirrors(variables);
+
+ int count = variables.size();
+ JDWP.StackFrame.GetValues.SlotInfo[] slots =
+ new JDWP.StackFrame.GetValues.SlotInfo[count];
+
+ for (int i=0; i<count; ++i) {
+ LocalVariableImpl variable = (LocalVariableImpl)variables.get(i);
+ if (!variable.isVisible(this)) {
+ throw new IllegalArgumentException(variable.name() +
+ " is not valid at this frame location");
+ }
+ slots[i] = new JDWP.StackFrame.GetValues.SlotInfo(variable.slot(),
+ (byte)variable.signature().charAt(0));
+ }
+
+ PacketStream ps;
+
+ /* protect against defunct frame id */
+ synchronized (vm.state()) {
+ validateStackFrame();
+ ps = JDWP.StackFrame.GetValues.enqueueCommand(vm, thread, id, slots);
+ }
+
+ /* actually get it, now that order is guaranteed */
+ ValueImpl[] values;
+ try {
+ values = JDWP.StackFrame.GetValues.waitForReply(vm, ps).values;
+ } catch (JDWPException exc) {
+ switch (exc.errorCode()) {
+ case JDWP.Error.INVALID_FRAMEID:
+ case JDWP.Error.THREAD_NOT_SUSPENDED:
+ case JDWP.Error.INVALID_THREAD:
+ throw new InvalidStackFrameException();
+ default:
+ throw exc.toJDIException();
+ }
+ }
+
+ if (count != values.length) {
+ throw new InternalException(
+ "Wrong number of values returned from target VM");
+ }
+ Map<LocalVariable, Value> map = new HashMap<LocalVariable, Value>(count);
+ for (int i=0; i<count; ++i) {
+ LocalVariableImpl variable = (LocalVariableImpl)variables.get(i);
+ map.put(variable, values[i]);
+ }
+ return map;
+ }
+
+ public void setValue(LocalVariable variableIntf, Value valueIntf)
+ throws InvalidTypeException, ClassNotLoadedException {
+
+ validateStackFrame();
+ validateMirror(variableIntf);
+ validateMirrorOrNull(valueIntf);
+
+ LocalVariableImpl variable = (LocalVariableImpl)variableIntf;
+ ValueImpl value = (ValueImpl)valueIntf;
+
+ if (!variable.isVisible(this)) {
+ throw new IllegalArgumentException(variable.name() +
+ " is not valid at this frame location");
+ }
+
+ try {
+ // Validate and convert value if necessary
+ value = ValueImpl.prepareForAssignment(value, variable);
+
+ JDWP.StackFrame.SetValues.SlotInfo[] slotVals =
+ new JDWP.StackFrame.SetValues.SlotInfo[1];
+ slotVals[0] = new JDWP.StackFrame.SetValues.
+ SlotInfo(variable.slot(), value);
+
+ PacketStream ps;
+
+ /* protect against defunct frame id */
+ synchronized (vm.state()) {
+ validateStackFrame();
+ ps = JDWP.StackFrame.SetValues.
+ enqueueCommand(vm, thread, id, slotVals);
+ }
+
+ /* actually set it, now that order is guaranteed */
+ try {
+ JDWP.StackFrame.SetValues.waitForReply(vm, ps);
+ } catch (JDWPException exc) {
+ switch (exc.errorCode()) {
+ case JDWP.Error.INVALID_FRAMEID:
+ case JDWP.Error.THREAD_NOT_SUSPENDED:
+ case JDWP.Error.INVALID_THREAD:
+ throw new InvalidStackFrameException();
+ default:
+ throw exc.toJDIException();
+ }
+ }
+ } catch (ClassNotLoadedException e) {
+ /*
+ * Since we got this exception,
+ * the variable type must be a reference type. The value
+ * we're trying to set is null, but if the variable's
+ * class has not yet been loaded through the enclosing
+ * class loader, then setting to null is essentially a
+ * no-op, and we should allow it without an exception.
+ */
+ if (value != null) {
+ throw e;
+ }
+ }
+ }
+
+ public List<Value> getArgumentValues() {
+ validateStackFrame();
+ MethodImpl mmm = (MethodImpl)location.method();
+ List<String> argSigs = mmm.argumentSignatures();
+ int count = argSigs.size();
+ JDWP.StackFrame.GetValues.SlotInfo[] slots =
+ new JDWP.StackFrame.GetValues.SlotInfo[count];
+
+ int slot;
+ if (mmm.isStatic()) {
+ slot = 0;
+ } else {
+ slot = 1;
+ }
+ for (int ii = 0; ii < count; ++ii) {
+ char sigChar = (char)argSigs.get(ii).charAt(0);
+ slots[ii] = new JDWP.StackFrame.GetValues.SlotInfo(slot++,(byte)sigChar);
+ if (sigChar == 'J' || sigChar == 'D') {
+ slot++;
+ }
+ }
+
+ PacketStream ps;
+
+ /* protect against defunct frame id */
+ synchronized (vm.state()) {
+ validateStackFrame();
+ ps = JDWP.StackFrame.GetValues.enqueueCommand(vm, thread, id, slots);
+ }
+
+ ValueImpl[] values;
+ try {
+ values = JDWP.StackFrame.GetValues.waitForReply(vm, ps).values;
+ } catch (JDWPException exc) {
+ switch (exc.errorCode()) {
+ case JDWP.Error.INVALID_FRAMEID:
+ case JDWP.Error.THREAD_NOT_SUSPENDED:
+ case JDWP.Error.INVALID_THREAD:
+ throw new InvalidStackFrameException();
+ default:
+ throw exc.toJDIException();
+ }
+ }
+
+ if (count != values.length) {
+ throw new InternalException(
+ "Wrong number of values returned from target VM");
+ }
+ return Arrays.asList((Value[])values);
+ }
+
+ void pop() throws IncompatibleThreadStateException {
+ validateStackFrame();
+ // flush caches and disable caching until command completion
+ CommandSender sender =
+ new CommandSender() {
+ public PacketStream send() {
+ return JDWP.StackFrame.PopFrames.enqueueCommand(vm,
+ thread, id);
+ }
+ };
+ try {
+ PacketStream stream = thread.sendResumingCommand(sender);
+ JDWP.StackFrame.PopFrames.waitForReply(vm, stream);
+ } catch (JDWPException exc) {
+ switch (exc.errorCode()) {
+ case JDWP.Error.THREAD_NOT_SUSPENDED:
+ throw new IncompatibleThreadStateException(
+ "Thread not current or suspended");
+ case JDWP.Error.INVALID_THREAD: /* zombie */
+ throw new IncompatibleThreadStateException("zombie");
+ case JDWP.Error.NO_MORE_FRAMES:
+ throw new InvalidStackFrameException(
+ "No more frames on the stack");
+ default:
+ throw exc.toJDIException();
+ }
+ }
+
+ // enable caching - suspended again
+ vm.state().freeze();
+ }
+
+ public String toString() {
+ return location.toString() + " in thread " + thread.toString();
+ }
+}