1 /* 2 * Copyright (c) 1998, 2008, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * 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 StackFrameImpl(VirtualMachine vm, ThreadReferenceImpl thread, long id, Location location)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 */ threadResumable(ThreadAction action)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 validateStackFrame()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 */ location()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 */ thread()97 public ThreadReference thread() { 98 validateStackFrame(); 99 return thread; 100 } 101 equals(Object obj)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 hashCode()114 public int hashCode() { 115 return (thread().hashCode() << 4) + ((int)id); 116 } 117 thisObject()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 */ createVisibleVariables()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 = 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 */ visibleVariables()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 */ visibleVariableByName(String name)192 public LocalVariable visibleVariableByName(String name) throws AbsentInformationException { 193 validateStackFrame(); 194 createVisibleVariables(); 195 return visibleVariables.get(name); 196 } 197 getValue(LocalVariable variable)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 getValues(List<? extends LocalVariable> variables)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 setValue(LocalVariable variableIntf, Value valueIntf)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 getArgumentValues()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 = 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 pop()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 toString()401 public String toString() { 402 return location.toString() + " in thread " + thread.toString(); 403 } 404 } 405