blob: 5cfd2cbc9011a6611765ac037fe0c255038aac16 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.tools.jdi;
27
28import com.sun.jdi.*;
29
30import java.util.List;
31import java.util.Map;
32import java.util.ArrayList;
33import java.util.Arrays;
34import java.util.HashMap;
35import java.util.Iterator;
36import java.util.Collections;
37
38public class StackFrameImpl extends MirrorImpl
39 implements StackFrame, ThreadListener
40{
41 /* Once false, frame should not be used.
42 * access synchronized on (vm.state())
43 */
44 private boolean isValid = true;
45
46 private final ThreadReferenceImpl thread;
47 private final long id;
48 private final Location location;
49 private Map<String, LocalVariable> visibleVariables = null;
50 private ObjectReference thisObject = null;
51
52 StackFrameImpl(VirtualMachine vm, ThreadReferenceImpl thread,
53 long id, Location location) {
54 super(vm);
55 this.thread = thread;
56 this.id = id;
57 this.location = location;
58 thread.addListener(this);
59 }
60
61 /*
62 * ThreadListener implementation
63 * Must be synchronized since we must protect against
64 * sending defunct (isValid == false) stack ids to the back-end.
65 */
66 public boolean threadResumable(ThreadAction action) {
67 synchronized (vm.state()) {
68 if (isValid) {
69 isValid = false;
70 return false; /* remove this stack frame as a listener */
71 } else {
72 throw new InternalException(
73 "Invalid stack frame thread listener");
74 }
75 }
76 }
77
78 void validateStackFrame() {
79 if (!isValid) {
80 throw new InvalidStackFrameException("Thread has been resumed");
81 }
82 }
83
84 /**
85 * Return the frame location.
86 * Need not be synchronized since it cannot be provably stale.
87 */
88 public Location location() {
89 validateStackFrame();
90 return location;
91 }
92
93 /**
94 * Return the thread holding the frame.
95 * Need not be synchronized since it cannot be provably stale.
96 */
97 public ThreadReference thread() {
98 validateStackFrame();
99 return thread;
100 }
101
102 public boolean equals(Object obj) {
103 if ((obj != null) && (obj instanceof StackFrameImpl)) {
104 StackFrameImpl other = (StackFrameImpl)obj;
105 return (id == other.id) &&
106 (thread().equals(other.thread())) &&
107 (location().equals(other.location())) &&
108 super.equals(obj);
109 } else {
110 return false;
111 }
112 }
113
114 public int hashCode() {
115 return (thread().hashCode() << 4) + ((int)id);
116 }
117
118 public ObjectReference thisObject() {
119 validateStackFrame();
120 MethodImpl currentMethod = (MethodImpl)location.method();
121 if (currentMethod.isStatic() || currentMethod.isNative()) {
122 return null;
123 } else {
124 if (thisObject == null) {
125 PacketStream ps;
126
127 /* protect against defunct frame id */
128 synchronized (vm.state()) {
129 validateStackFrame();
130 ps = JDWP.StackFrame.ThisObject.
131 enqueueCommand(vm, thread, id);
132 }
133
134 /* actually get it, now that order is guaranteed */
135 try {
136 thisObject = JDWP.StackFrame.ThisObject.
137 waitForReply(vm, ps).objectThis;
138 } catch (JDWPException exc) {
139 switch (exc.errorCode()) {
140 case JDWP.Error.INVALID_FRAMEID:
141 case JDWP.Error.THREAD_NOT_SUSPENDED:
142 case JDWP.Error.INVALID_THREAD:
143 throw new InvalidStackFrameException();
144 default:
145 throw exc.toJDIException();
146 }
147 }
148 }
149 }
150 return thisObject;
151 }
152
153 /**
154 * Build the visible variable map.
155 * Need not be synchronized since it cannot be provably stale.
156 */
157 private void createVisibleVariables() throws AbsentInformationException {
158 if (visibleVariables == null) {
159 List<LocalVariable> allVariables = location.method().variables();
160 Map<String, LocalVariable> map = new HashMap<String, LocalVariable>(allVariables.size());
161
162 for (LocalVariable variable : allVariables) {
163 String name = variable.name();
164 if (variable.isVisible(this)) {
165 LocalVariable existing = (LocalVariable)map.get(name);
166 if ((existing == null) ||
167 ((LocalVariableImpl)variable).hides(existing)) {
168 map.put(name, variable);
169 }
170 }
171 }
172 visibleVariables = map;
173 }
174 }
175
176 /**
177 * Return the list of visible variable in the frame.
178 * Need not be synchronized since it cannot be provably stale.
179 */
180 public List<LocalVariable> visibleVariables() throws AbsentInformationException {
181 validateStackFrame();
182 createVisibleVariables();
183 List<LocalVariable> mapAsList = new ArrayList<LocalVariable>(visibleVariables.values());
184 Collections.sort(mapAsList);
185 return mapAsList;
186 }
187
188 /**
189 * Return a particular variable in the frame.
190 * Need not be synchronized since it cannot be provably stale.
191 */
192 public LocalVariable visibleVariableByName(String name) throws AbsentInformationException {
193 validateStackFrame();
194 createVisibleVariables();
195 return visibleVariables.get(name);
196 }
197
198 public Value getValue(LocalVariable variable) {
199 List<LocalVariable> list = new ArrayList<LocalVariable>(1);
200 list.add(variable);
201 return getValues(list).get(variable);
202 }
203
204 public Map<LocalVariable, Value> getValues(List<? extends LocalVariable> variables) {
205 validateStackFrame();
206 validateMirrors(variables);
207
208 int count = variables.size();
209 JDWP.StackFrame.GetValues.SlotInfo[] slots =
210 new JDWP.StackFrame.GetValues.SlotInfo[count];
211
212 for (int i=0; i<count; ++i) {
213 LocalVariableImpl variable = (LocalVariableImpl)variables.get(i);
214 if (!variable.isVisible(this)) {
215 throw new IllegalArgumentException(variable.name() +
216 " is not valid at this frame location");
217 }
218 slots[i] = new JDWP.StackFrame.GetValues.SlotInfo(variable.slot(),
219 (byte)variable.signature().charAt(0));
220 }
221
222 PacketStream ps;
223
224 /* protect against defunct frame id */
225 synchronized (vm.state()) {
226 validateStackFrame();
227 ps = JDWP.StackFrame.GetValues.enqueueCommand(vm, thread, id, slots);
228 }
229
230 /* actually get it, now that order is guaranteed */
231 ValueImpl[] values;
232 try {
233 values = JDWP.StackFrame.GetValues.waitForReply(vm, ps).values;
234 } catch (JDWPException exc) {
235 switch (exc.errorCode()) {
236 case JDWP.Error.INVALID_FRAMEID:
237 case JDWP.Error.THREAD_NOT_SUSPENDED:
238 case JDWP.Error.INVALID_THREAD:
239 throw new InvalidStackFrameException();
240 default:
241 throw exc.toJDIException();
242 }
243 }
244
245 if (count != values.length) {
246 throw new InternalException(
247 "Wrong number of values returned from target VM");
248 }
249 Map<LocalVariable, Value> map = new HashMap<LocalVariable, Value>(count);
250 for (int i=0; i<count; ++i) {
251 LocalVariableImpl variable = (LocalVariableImpl)variables.get(i);
252 map.put(variable, values[i]);
253 }
254 return map;
255 }
256
257 public void setValue(LocalVariable variableIntf, Value valueIntf)
258 throws InvalidTypeException, ClassNotLoadedException {
259
260 validateStackFrame();
261 validateMirror(variableIntf);
262 validateMirrorOrNull(valueIntf);
263
264 LocalVariableImpl variable = (LocalVariableImpl)variableIntf;
265 ValueImpl value = (ValueImpl)valueIntf;
266
267 if (!variable.isVisible(this)) {
268 throw new IllegalArgumentException(variable.name() +
269 " is not valid at this frame location");
270 }
271
272 try {
273 // Validate and convert value if necessary
274 value = ValueImpl.prepareForAssignment(value, variable);
275
276 JDWP.StackFrame.SetValues.SlotInfo[] slotVals =
277 new JDWP.StackFrame.SetValues.SlotInfo[1];
278 slotVals[0] = new JDWP.StackFrame.SetValues.
279 SlotInfo(variable.slot(), value);
280
281 PacketStream ps;
282
283 /* protect against defunct frame id */
284 synchronized (vm.state()) {
285 validateStackFrame();
286 ps = JDWP.StackFrame.SetValues.
287 enqueueCommand(vm, thread, id, slotVals);
288 }
289
290 /* actually set it, now that order is guaranteed */
291 try {
292 JDWP.StackFrame.SetValues.waitForReply(vm, ps);
293 } catch (JDWPException exc) {
294 switch (exc.errorCode()) {
295 case JDWP.Error.INVALID_FRAMEID:
296 case JDWP.Error.THREAD_NOT_SUSPENDED:
297 case JDWP.Error.INVALID_THREAD:
298 throw new InvalidStackFrameException();
299 default:
300 throw exc.toJDIException();
301 }
302 }
303 } catch (ClassNotLoadedException e) {
304 /*
305 * Since we got this exception,
306 * the variable type must be a reference type. The value
307 * we're trying to set is null, but if the variable's
308 * class has not yet been loaded through the enclosing
309 * class loader, then setting to null is essentially a
310 * no-op, and we should allow it without an exception.
311 */
312 if (value != null) {
313 throw e;
314 }
315 }
316 }
317
318 public List<Value> getArgumentValues() {
319 validateStackFrame();
320 MethodImpl mmm = (MethodImpl)location.method();
321 List<String> argSigs = mmm.argumentSignatures();
322 int count = argSigs.size();
323 JDWP.StackFrame.GetValues.SlotInfo[] slots =
324 new JDWP.StackFrame.GetValues.SlotInfo[count];
325
326 int slot;
327 if (mmm.isStatic()) {
328 slot = 0;
329 } else {
330 slot = 1;
331 }
332 for (int ii = 0; ii < count; ++ii) {
333 char sigChar = (char)argSigs.get(ii).charAt(0);
334 slots[ii] = new JDWP.StackFrame.GetValues.SlotInfo(slot++,(byte)sigChar);
335 if (sigChar == 'J' || sigChar == 'D') {
336 slot++;
337 }
338 }
339
340 PacketStream ps;
341
342 /* protect against defunct frame id */
343 synchronized (vm.state()) {
344 validateStackFrame();
345 ps = JDWP.StackFrame.GetValues.enqueueCommand(vm, thread, id, slots);
346 }
347
348 ValueImpl[] values;
349 try {
350 values = JDWP.StackFrame.GetValues.waitForReply(vm, ps).values;
351 } catch (JDWPException exc) {
352 switch (exc.errorCode()) {
353 case JDWP.Error.INVALID_FRAMEID:
354 case JDWP.Error.THREAD_NOT_SUSPENDED:
355 case JDWP.Error.INVALID_THREAD:
356 throw new InvalidStackFrameException();
357 default:
358 throw exc.toJDIException();
359 }
360 }
361
362 if (count != values.length) {
363 throw new InternalException(
364 "Wrong number of values returned from target VM");
365 }
366 return Arrays.asList((Value[])values);
367 }
368
369 void pop() throws IncompatibleThreadStateException {
370 validateStackFrame();
371 // flush caches and disable caching until command completion
372 CommandSender sender =
373 new CommandSender() {
374 public PacketStream send() {
375 return JDWP.StackFrame.PopFrames.enqueueCommand(vm,
376 thread, id);
377 }
378 };
379 try {
380 PacketStream stream = thread.sendResumingCommand(sender);
381 JDWP.StackFrame.PopFrames.waitForReply(vm, stream);
382 } catch (JDWPException exc) {
383 switch (exc.errorCode()) {
384 case JDWP.Error.THREAD_NOT_SUSPENDED:
385 throw new IncompatibleThreadStateException(
386 "Thread not current or suspended");
387 case JDWP.Error.INVALID_THREAD: /* zombie */
388 throw new IncompatibleThreadStateException("zombie");
389 case JDWP.Error.NO_MORE_FRAMES:
390 throw new InvalidStackFrameException(
391 "No more frames on the stack");
392 default:
393 throw exc.toJDIException();
394 }
395 }
396
397 // enable caching - suspended again
398 vm.state().freeze();
399 }
400
401 public String toString() {
402 return location.toString() + " in thread " + thread.toString();
403 }
404}