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 DebuggerManagerThreadImpl.assertIsManagerThread();
90 if (!super.isValid()) {
94 if (myStackFrame != null) {
95 myStackFrame.location(); //extra check if jdi frame is valid
98 } catch (InvalidStackFrameException e) {
104 protected void clearCaches() {
105 DebuggerManagerThreadImpl.assertIsManagerThread();
106 if (LOG.isDebugEnabled()) {
107 LOG.debug("caches cleared " + super.toString());
112 myThisReference = null;
113 myClassLoader = null;
118 * Use with caution. Better access stackframe data through the Proxy's methods
122 public StackFrame getStackFrame() throws EvaluateException {
123 DebuggerManagerThreadImpl.assertIsManagerThread();
127 if (myStackFrame == null) {
129 final ThreadReference threadRef = myThreadProxy.getThreadReference();
130 myStackFrame = threadRef.frame(getFrameIndex());
132 catch (IndexOutOfBoundsException e) {
133 throw new EvaluateException(e.getMessage(), e);
135 catch (ObjectCollectedException ignored) {
136 throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.thread.collected"));
138 catch (IncompatibleThreadStateException e) {
139 throw EvaluateExceptionUtil.createEvaluateException(e);
147 public int getFrameIndex() throws EvaluateException {
148 DebuggerManagerThreadImpl.assertIsManagerThread();
150 if(myFrameIndex == -1) {
151 int count = myThreadProxy.frameCount();
153 if(myFrameFromBottomIndex > count) {
154 throw EvaluateExceptionUtil.createEvaluateException(new IncompatibleThreadStateException());
157 myFrameIndex = count - myFrameFromBottomIndex;
162 // public boolean isProxiedFrameValid() {
163 // if (myStackFrame != null) {
165 // myStackFrame.thread();
168 // catch (InvalidStackFrameException e) {
175 public VirtualMachineProxyImpl getVirtualMachine() {
176 return (VirtualMachineProxyImpl) myTimer;
180 public Location location() throws EvaluateException {
181 InvalidStackFrameException error = null;
182 for (int attempt = 0; attempt < 2; attempt++) {
184 return getStackFrame().location();
186 catch (InvalidStackFrameException e) {
191 throw new EvaluateException(error.getMessage(), error);
195 public ThreadReferenceProxyImpl threadProxy() {
196 return myThreadProxy;
199 public @NonNls String toString() {
201 return "StackFrameProxyImpl: " + getStackFrame().toString();
203 catch (EvaluateException e) {
204 return "StackFrameProxyImpl: " + e.getMessage() + "; frameFromBottom = " + myFrameFromBottomIndex + " threadName = " + threadProxy().name();
209 public ObjectReference thisObject() throws EvaluateException {
210 DebuggerManagerThreadImpl.assertIsManagerThread();
213 for (int attempt = 0; attempt < 2; attempt++) {
215 if(myThisReference == null) {
216 myThisReference = getStackFrame().thisObject();
220 catch (InvalidStackFrameException ignored) {
225 catch (InternalException e) {
226 // suppress some internal errors caused by bugs in specific JDI implementations
227 if(e.errorCode() != 23) {
228 throw EvaluateExceptionUtil.createEvaluateException(e);
231 return myThisReference;
234 public List<LocalVariableProxyImpl> visibleVariables() throws EvaluateException {
235 DebuggerManagerThreadImpl.assertIsManagerThread();
236 InvalidStackFrameException error = null;
237 for (int attempt = 0; attempt < 2; attempt++) {
239 final List<LocalVariable> list = getStackFrame().visibleVariables();
240 final List<LocalVariableProxyImpl> locals = new ArrayList<LocalVariableProxyImpl>(list.size());
241 for (LocalVariable localVariable : list) {
242 LOG.assertTrue(localVariable != null);
243 locals.add(new LocalVariableProxyImpl(this, localVariable));
247 catch (InvalidStackFrameException e) {
251 catch (AbsentInformationException e) {
252 throw EvaluateExceptionUtil.createEvaluateException(e);
255 throw new EvaluateException(error.getMessage(), error);
259 public LocalVariableProxyImpl visibleVariableByName(String name) throws EvaluateException {
260 DebuggerManagerThreadImpl.assertIsManagerThread();
261 final LocalVariable variable = visibleVariableByNameInt(name);
262 return variable != null ? new LocalVariableProxyImpl(this, variable) : null;
266 public Value visibleValueByName(@NotNull String name) throws EvaluateException {
267 LocalVariable variable = visibleVariableByNameInt(name);
268 return variable != null ? getValue(new LocalVariableProxyImpl(this, variable)) : null;
271 protected LocalVariable visibleVariableByNameInt(String name) throws EvaluateException {
272 DebuggerManagerThreadImpl.assertIsManagerThread();
273 InvalidStackFrameException error = null;
274 for (int attempt = 0; attempt < 2; attempt++) {
277 return getStackFrame().visibleVariableByName(name);
279 catch (InvalidStackFrameException e) {
284 catch (InvalidStackFrameException e) {
285 throw EvaluateExceptionUtil.createEvaluateException(e);
287 catch (AbsentInformationException e) {
288 throw EvaluateExceptionUtil.createEvaluateException(e);
291 throw new EvaluateException(error.getMessage(), error);
294 public Value getValue(LocalVariableProxyImpl localVariable) throws EvaluateException {
295 DebuggerManagerThreadImpl.assertIsManagerThread();
296 InvalidStackFrameException error = null;
297 for (int attempt = 0; attempt < 2; attempt++) {
299 return getAllValues().get(localVariable.getVariable());
301 catch (InvalidStackFrameException e) {
306 throw new EvaluateException(error.getMessage(), error);
309 public List<Value> getArgumentValues() throws EvaluateException {
310 DebuggerManagerThreadImpl.assertIsManagerThread();
311 InvalidStackFrameException error = null;
312 for (int attempt = 0; attempt < 2; attempt++) {
314 final StackFrame stackFrame = getStackFrame();
315 return stackFrame != null? stackFrame.getArgumentValues() : Collections.<Value>emptyList();
317 catch (InternalException e) {
318 // From Oracle's forums:
319 // This could be a JPDA bug. Unexpected JDWP Error: 32 means that an 'opaque' frame was detected at the lower JPDA levels,
320 // typically a native frame.
321 if (e.errorCode() == 32 /*opaque frame JDI bug*/ ) {
322 return Collections.emptyList();
328 catch (InvalidStackFrameException e) {
333 throw new EvaluateException(error.getMessage(), error);
336 private Map<LocalVariable, Value> getAllValues() throws EvaluateException{
337 DebuggerManagerThreadImpl.assertIsManagerThread();
339 if (myAllValues == null) {
341 StackFrame stackFrame = getStackFrame();
342 myAllValues = new THashMap<LocalVariable, Value>(stackFrame.getValues(stackFrame.visibleVariables()));
344 catch (InconsistentDebugInfoException ignored) {
346 throw EvaluateExceptionUtil.INCONSISTEND_DEBUG_INFO;
348 catch (AbsentInformationException e) {
349 throw EvaluateExceptionUtil.createEvaluateException(e);
355 public void setValue(LocalVariableProxyImpl localVariable, Value value) throws EvaluateException, ClassNotLoadedException, InvalidTypeException {
356 DebuggerManagerThreadImpl.assertIsManagerThread();
357 InvalidStackFrameException error = null;
358 for (int attempt = 0; attempt < 2; attempt++) {
360 final LocalVariable variable = localVariable.getVariable();
361 final StackFrame stackFrame = getStackFrame();
362 stackFrame.setValue(variable, (value instanceof ObjectReference)? ((ObjectReference)value) : value);
363 if (myAllValues != null) {
364 // update cached data if any
365 // re-read the value just set from the stackframe to be 100% sure
366 myAllValues.put(variable, stackFrame.getValue(variable));
370 catch (InvalidStackFrameException e) {
375 throw new EvaluateException(error.getMessage(), error);
378 public int hashCode() {
379 return 31 * myThreadProxy.hashCode() + myFrameFromBottomIndex;
383 public boolean equals(final Object obj) {
384 if (!(obj instanceof StackFrameProxyImpl)) {
387 StackFrameProxyImpl frameProxy = (StackFrameProxyImpl)obj;
388 if(frameProxy == this)return true;
390 return (myFrameFromBottomIndex == frameProxy.myFrameFromBottomIndex) &&
391 (myThreadProxy.equals(frameProxy.myThreadProxy));
394 public boolean isLocalVariableVisible(LocalVariableProxyImpl var) throws EvaluateException {
396 return var.getVariable().isVisible(getStackFrame());
398 catch (IllegalArgumentException ignored) {
399 // can be thrown if frame's method is different than variable's method
405 public ClassLoaderReference getClassLoader() throws EvaluateException {
406 if(myClassLoader == null) {
407 myClassLoader = location().declaringType().classLoader();
409 return myClassLoader;
412 public boolean isBottom() {
413 return myFrameFromBottomIndex == 1;
416 public int getIndexFromBottom() {
417 return myFrameFromBottomIndex;