|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
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 |
|
{ |
|
|
|
|
|
*/ |
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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"); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Location location() { |
|
validateStackFrame(); |
|
return location; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
|
|
synchronized (vm.state()) { |
|
validateStackFrame(); |
|
ps = JDWP.StackFrame.ThisObject. |
|
enqueueCommand(vm, thread, id); |
|
} |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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 = map.get(name); |
|
if ((existing == null) || |
|
((LocalVariableImpl)variable).hides(existing)) { |
|
map.put(name, variable); |
|
} |
|
} |
|
} |
|
visibleVariables = map; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public List<LocalVariable> visibleVariables() throws AbsentInformationException { |
|
validateStackFrame(); |
|
createVisibleVariables(); |
|
List<LocalVariable> mapAsList = new ArrayList<LocalVariable>(visibleVariables.values()); |
|
Collections.sort(mapAsList); |
|
return mapAsList; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
|
|
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"); |
|
} |
|
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 { |
|
|
|
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; |
|
|
|
|
|
synchronized (vm.state()) { |
|
validateStackFrame(); |
|
ps = JDWP.StackFrame.SetValues. |
|
enqueueCommand(vm, thread, id, slotVals); |
|
} |
|
|
|
|
|
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) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 = argSigs.get(ii).charAt(0); |
|
slots[ii] = new JDWP.StackFrame.GetValues.SlotInfo(slot++,(byte)sigChar); |
|
if (sigChar == 'J' || sigChar == 'D') { |
|
slot++; |
|
} |
|
} |
|
|
|
PacketStream ps; |
|
|
|
|
|
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(); |
|
|
|
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: |
|
throw new IncompatibleThreadStateException("zombie"); |
|
case JDWP.Error.NO_MORE_FRAMES: |
|
throw new InvalidStackFrameException( |
|
"No more frames on the stack"); |
|
default: |
|
throw exc.toJDIException(); |
|
} |
|
} |
|
|
|
|
|
vm.state().freeze(); |
|
} |
|
|
|
public String toString() { |
|
return location.toString() + " in thread " + thread.toString(); |
|
} |
|
} |