|
1 /* |
|
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 |
|
26 package com.sun.tools.jdi; |
|
27 |
|
28 import com.sun.jdi.*; |
|
29 |
|
30 import java.util.List; |
|
31 import java.util.Map; |
|
32 import java.util.ArrayList; |
|
33 import java.util.Arrays; |
|
34 import java.util.HashMap; |
|
35 import java.util.Iterator; |
|
36 import java.util.Collections; |
|
37 |
|
38 public 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 } |