jdk/src/share/classes/com/sun/tools/jdi/StackFrameImpl.java
changeset 2 90ce3da70b43
child 51 6fe31bc95bbc
--- /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();
+    }
+}