2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * @author Eugene Zhuravlev
20 package com.intellij.debugger.jdi;
22 import com.intellij.debugger.DebuggerBundle;
23 import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
24 import com.intellij.debugger.engine.evaluation.EvaluateException;
25 import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
26 import com.intellij.debugger.engine.jdi.StackFrameProxy;
27 import com.intellij.openapi.diagnostic.Logger;
29 import gnu.trove.THashMap;
30 import org.jetbrains.annotations.NonNls;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.List;
39 public class StackFrameProxyImpl extends JdiProxy implements StackFrameProxy {
40 private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.jdi.StackFrameProxyImpl");
41 private final ThreadReferenceProxyImpl myThreadProxy;
42 private final int myFrameFromBottomIndex; // 1-based
45 private int myFrameIndex = -1;
46 private StackFrame myStackFrame;
47 private ObjectReference myThisReference;
48 private ClassLoaderReference myClassLoader;
49 private Boolean myIsObsolete = null;
50 private Map<LocalVariable, Value> myAllValues;
52 public StackFrameProxyImpl(ThreadReferenceProxyImpl threadProxy, @NotNull StackFrame frame, int fromBottomIndex /* 1-based */) {
53 super(threadProxy.getVirtualMachine());
54 myThreadProxy = threadProxy;
55 myFrameFromBottomIndex = fromBottomIndex;
59 public boolean isObsolete() throws EvaluateException {
60 DebuggerManagerThreadImpl.assertIsManagerThread();
62 if (myIsObsolete != null) {
63 return myIsObsolete.booleanValue();
65 InvalidStackFrameException error = null;
66 for (int attempt = 0; attempt < 2; attempt++) {
68 boolean isObsolete = (getVirtualMachine().canRedefineClasses() && location().method().isObsolete());
69 myIsObsolete = isObsolete? Boolean.TRUE : Boolean.FALSE;
72 catch (InvalidStackFrameException e) {
76 catch (InternalException e) {
77 if (e.errorCode() == 23 /*INVALID_METHODID according to JDI sources*/) {
78 myIsObsolete = Boolean.TRUE;
84 throw new EvaluateException(error.getMessage(), error);
88 public boolean isValid() {
89 if (!super.isValid()) {
93 if (myStackFrame != null) {
94 myStackFrame.location(); //extra check if jdi frame is valid
97 } catch (InvalidStackFrameException e) {
103 protected void clearCaches() {
104 DebuggerManagerThreadImpl.assertIsManagerThread();
105 if (LOG.isDebugEnabled()) {
106 LOG.debug("caches cleared " + super.toString());
111 myThisReference = null;
112 myClassLoader = null;
117 * Use with caution. Better access stackframe data through the Proxy's methods
121 public StackFrame getStackFrame() throws EvaluateException {
122 DebuggerManagerThreadImpl.assertIsManagerThread();
126 if (myStackFrame == null) {
128 final ThreadReference threadRef = myThreadProxy.getThreadReference();
129 myStackFrame = threadRef.frame(getFrameIndex());
131 catch (IndexOutOfBoundsException e) {
132 throw new EvaluateException(e.getMessage(), e);
134 catch (ObjectCollectedException ignored) {
135 throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.thread.collected"));
137 catch (IncompatibleThreadStateException e) {
138 throw EvaluateExceptionUtil.createEvaluateException(e);
146 public int getFrameIndex() throws EvaluateException {
147 DebuggerManagerThreadImpl.assertIsManagerThread();
149 if(myFrameIndex == -1) {
150 int count = myThreadProxy.frameCount();
152 if(myFrameFromBottomIndex > count) {
153 throw EvaluateExceptionUtil.createEvaluateException(new IncompatibleThreadStateException());
156 myFrameIndex = count - myFrameFromBottomIndex;
161 // public boolean isProxiedFrameValid() {
162 // if (myStackFrame != null) {
164 // myStackFrame.thread();
167 // catch (InvalidStackFrameException e) {
174 public VirtualMachineProxyImpl getVirtualMachine() {
175 return (VirtualMachineProxyImpl) myTimer;
179 public Location location() throws EvaluateException {
180 InvalidStackFrameException error = null;
181 for (int attempt = 0; attempt < 2; attempt++) {
183 return getStackFrame().location();
185 catch (InvalidStackFrameException e) {
190 throw new EvaluateException(error.getMessage(), error);
194 public ThreadReferenceProxyImpl threadProxy() {
195 return myThreadProxy;
198 public @NonNls String toString() {
200 return "StackFrameProxyImpl: " + getStackFrame().toString();
202 catch (EvaluateException e) {
203 return "StackFrameProxyImpl: " + e.getMessage() + "; frameFromBottom = " + myFrameFromBottomIndex + " threadName = " + threadProxy().name();
208 public ObjectReference thisObject() throws EvaluateException {
209 DebuggerManagerThreadImpl.assertIsManagerThread();
212 for (int attempt = 0; attempt < 2; attempt++) {
214 if(myThisReference == null) {
215 myThisReference = getStackFrame().thisObject();
219 catch (InvalidStackFrameException ignored) {
224 catch (InternalException e) {
225 // suppress some internal errors caused by bugs in specific JDI implementations
226 if(e.errorCode() != 23) {
227 throw EvaluateExceptionUtil.createEvaluateException(e);
230 return myThisReference;
233 public List<LocalVariableProxyImpl> visibleVariables() throws EvaluateException {
234 DebuggerManagerThreadImpl.assertIsManagerThread();
235 InvalidStackFrameException error = null;
236 for (int attempt = 0; attempt < 2; attempt++) {
238 final List<LocalVariable> list = getStackFrame().visibleVariables();
239 final List<LocalVariableProxyImpl> locals = new ArrayList<LocalVariableProxyImpl>(list.size());
240 for (LocalVariable localVariable : list) {
241 LOG.assertTrue(localVariable != null);
242 locals.add(new LocalVariableProxyImpl(this, localVariable));
246 catch (InvalidStackFrameException e) {
250 catch (AbsentInformationException e) {
251 throw EvaluateExceptionUtil.createEvaluateException(e);
254 throw new EvaluateException(error.getMessage(), error);
258 public LocalVariableProxyImpl visibleVariableByName(String name) throws EvaluateException {
259 DebuggerManagerThreadImpl.assertIsManagerThread();
260 final LocalVariable variable = visibleVariableByNameInt(name);
261 return variable != null ? new LocalVariableProxyImpl(this, variable) : null;
265 public Value visibleValueByName(@NotNull String name) throws EvaluateException {
266 LocalVariable variable = visibleVariableByNameInt(name);
267 return variable != null ? getValue(new LocalVariableProxyImpl(this, variable)) : null;
270 protected LocalVariable visibleVariableByNameInt(String name) throws EvaluateException {
271 DebuggerManagerThreadImpl.assertIsManagerThread();
272 InvalidStackFrameException error = null;
273 for (int attempt = 0; attempt < 2; attempt++) {
276 return getStackFrame().visibleVariableByName(name);
278 catch (InvalidStackFrameException e) {
283 catch (InvalidStackFrameException e) {
284 throw EvaluateExceptionUtil.createEvaluateException(e);
286 catch (AbsentInformationException e) {
287 throw EvaluateExceptionUtil.createEvaluateException(e);
290 throw new EvaluateException(error.getMessage(), error);
293 public Value getValue(LocalVariableProxyImpl localVariable) throws EvaluateException {
294 DebuggerManagerThreadImpl.assertIsManagerThread();
295 InvalidStackFrameException error = null;
296 for (int attempt = 0; attempt < 2; attempt++) {
298 return getAllValues().get(localVariable.getVariable());
300 catch (InvalidStackFrameException e) {
305 throw new EvaluateException(error.getMessage(), error);
308 public List<Value> getArgumentValues() throws EvaluateException {
309 DebuggerManagerThreadImpl.assertIsManagerThread();
310 InvalidStackFrameException error = null;
311 for (int attempt = 0; attempt < 2; attempt++) {
313 final StackFrame stackFrame = getStackFrame();
314 return stackFrame != null? stackFrame.getArgumentValues() : Collections.<Value>emptyList();
316 catch (InternalException e) {
317 // From Oracle's forums:
318 // This could be a JPDA bug. Unexpected JDWP Error: 32 means that an 'opaque' frame was detected at the lower JPDA levels,
319 // typically a native frame.
320 if (e.errorCode() == 32 /*opaque frame JDI bug*/ ) {
321 return Collections.emptyList();
327 catch (InvalidStackFrameException e) {
332 throw new EvaluateException(error.getMessage(), error);
335 private Map<LocalVariable, Value> getAllValues() throws EvaluateException{
336 DebuggerManagerThreadImpl.assertIsManagerThread();
338 if (myAllValues == null) {
340 StackFrame stackFrame = getStackFrame();
341 myAllValues = new THashMap<LocalVariable, Value>(stackFrame.getValues(stackFrame.visibleVariables()));
343 catch (InconsistentDebugInfoException ignored) {
345 throw EvaluateExceptionUtil.INCONSISTEND_DEBUG_INFO;
347 catch (AbsentInformationException e) {
348 throw EvaluateExceptionUtil.createEvaluateException(e);
354 public void setValue(LocalVariableProxyImpl localVariable, Value value) throws EvaluateException, ClassNotLoadedException, InvalidTypeException {
355 DebuggerManagerThreadImpl.assertIsManagerThread();
356 InvalidStackFrameException error = null;
357 for (int attempt = 0; attempt < 2; attempt++) {
359 final LocalVariable variable = localVariable.getVariable();
360 final StackFrame stackFrame = getStackFrame();
361 stackFrame.setValue(variable, (value instanceof ObjectReference)? ((ObjectReference)value) : value);
362 if (myAllValues != null) {
363 // update cached data if any
364 // re-read the value just set from the stackframe to be 100% sure
365 myAllValues.put(variable, stackFrame.getValue(variable));
369 catch (InvalidStackFrameException e) {
374 throw new EvaluateException(error.getMessage(), error);
377 public int hashCode() {
378 return 31 * myThreadProxy.hashCode() + myFrameFromBottomIndex;
382 public boolean equals(final Object obj) {
383 if (!(obj instanceof StackFrameProxyImpl)) {
386 StackFrameProxyImpl frameProxy = (StackFrameProxyImpl)obj;
387 if(frameProxy == this)return true;
389 return (myFrameFromBottomIndex == frameProxy.myFrameFromBottomIndex) &&
390 (myThreadProxy.equals(frameProxy.myThreadProxy));
393 public boolean isLocalVariableVisible(LocalVariableProxyImpl var) throws EvaluateException {
395 return var.getVariable().isVisible(getStackFrame());
397 catch (IllegalArgumentException ignored) {
398 // can be thrown if frame's method is different than variable's method
404 public ClassLoaderReference getClassLoader() throws EvaluateException {
405 if(myClassLoader == null) {
406 myClassLoader = location().declaringType().classLoader();
408 return myClassLoader;
411 public boolean isBottom() {
412 return myFrameFromBottomIndex == 1;
415 public int getIndexFromBottom() {
416 return myFrameFromBottomIndex;