blob: e7db635abe139c4c9d5fc99634ba9aeb9cb778e9 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.debugger.engine;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.*;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.debugger.settings.NodeRendererSettings;
import com.intellij.debugger.ui.breakpoints.Breakpoint;
import com.intellij.debugger.ui.impl.FrameVariablesTree;
import com.intellij.debugger.ui.impl.watch.*;
import com.intellij.debugger.ui.tree.render.ClassRenderer;
import com.intellij.debugger.ui.tree.render.DescriptorLabelListener;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.ColoredTextContainer;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.intellij.xdebugger.frame.*;
import com.intellij.xdebugger.frame.presentation.XValuePresentation;
import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
import com.intellij.xdebugger.settings.XDebuggerSettingsManager;
import com.sun.jdi.*;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.ExceptionEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author egor
*/
public class JavaStackFrame extends XStackFrame {
private static final Logger LOG = Logger.getInstance(JavaStackFrame.class);
private final DebugProcessImpl myDebugProcess;
private final XSourcePosition myXSourcePosition;
private final NodeManagerImpl myNodeManager;
private final StackFrameDescriptorImpl myDescriptor;
private static final JavaFramesListRenderer FRAME_RENDERER = new JavaFramesListRenderer();
private JavaDebuggerEvaluator myEvaluator = null;
public JavaStackFrame(@NotNull StackFrameProxyImpl stackFrameProxy,
@NotNull DebugProcessImpl debugProcess,
@NotNull MethodsTracker tracker,
@NotNull NodeManagerImpl nodeManager) {
myDebugProcess = debugProcess;
myNodeManager = nodeManager;
myDescriptor = new StackFrameDescriptorImpl(stackFrameProxy, tracker);
myDescriptor.setContext(null);
myDescriptor.updateRepresentation(null, DescriptorLabelListener.DUMMY_LISTENER);
myXSourcePosition = ApplicationManager.getApplication().runReadAction(new Computable<XSourcePosition>() {
@Override
public XSourcePosition compute() {
return myDescriptor.getSourcePosition() != null ? DebuggerUtilsEx.toXSourcePosition(myDescriptor.getSourcePosition()) : null;
}
});
}
public StackFrameDescriptorImpl getDescriptor() {
return myDescriptor;
}
@Nullable
@Override
public XDebuggerEvaluator getEvaluator() {
if (myEvaluator == null) {
myEvaluator = new JavaDebuggerEvaluator(myDebugProcess, this);
}
return myEvaluator;
}
@Nullable
@Override
public XSourcePosition getSourcePosition() {
return myXSourcePosition;
}
@Override
public void customizePresentation(@NotNull ColoredTextContainer component) {
StackFrameDescriptorImpl selectedDescriptor = null;
DebuggerSession session = myDebugProcess.getSession();
if (session != null) {
XDebugSession xSession = session.getXDebugSession();
if (xSession != null) {
XStackFrame frame = xSession.getCurrentStackFrame();
if (frame instanceof JavaStackFrame) {
selectedDescriptor = ((JavaStackFrame)frame).getDescriptor();
}
}
}
FRAME_RENDERER.customizePresentation(myDescriptor, component, selectedDescriptor);
}
@Override
public void computeChildren(@NotNull final XCompositeNode node) {
XStackFrame xFrame = getDescriptor().getXStackFrame();
if (xFrame != null) {
xFrame.computeChildren(node);
return;
}
myDebugProcess.getManagerThread().schedule(new DebuggerContextCommandImpl(myDebugProcess.getDebuggerContext()) {
@Override
public Priority getPriority() {
return Priority.NORMAL;
}
@Override
public void threadAction() {
XValueChildrenList children = new XValueChildrenList();
buildVariablesThreadAction(getFrameDebuggerContext(), children, node);
node.addChildren(children, true);
}
});
}
DebuggerContextImpl getFrameDebuggerContext() {
DebuggerManagerThreadImpl.assertIsManagerThread();
DebuggerContextImpl context = myDebugProcess.getDebuggerContext();
if (context.getFrameProxy() != getStackFrameProxy()) {
SuspendContextImpl threadSuspendContext = SuspendManagerUtil.getSuspendContextForThread(context.getSuspendContext(), getStackFrameProxy().threadProxy());
context = DebuggerContextImpl.createDebuggerContext(
myDebugProcess.mySession,
threadSuspendContext,
getStackFrameProxy().threadProxy(),
getStackFrameProxy());
context.setPositionCache(myDescriptor.getSourcePosition());
context.initCaches();
}
return context;
}
// copied from DebuggerTree
private void buildVariablesThreadAction(DebuggerContextImpl debuggerContext, XValueChildrenList children, XCompositeNode node) {
try {
final StackFrameDescriptorImpl stackDescriptor = myDescriptor;
final StackFrameProxyImpl frame = getStackFrameProxy();
final EvaluationContextImpl evaluationContext = debuggerContext.createEvaluationContext();
if (!debuggerContext.isEvaluationPossible()) {
node.setErrorMessage(MessageDescriptor.EVALUATION_NOT_POSSIBLE.getLabel());
//myChildren.add(myNodeManager.createNode(MessageDescriptor.EVALUATION_NOT_POSSIBLE, evaluationContext));
}
final Location location = frame.location();
LOG.assertTrue(location != null);
final ObjectReference thisObjectReference = frame.thisObject();
if (thisObjectReference != null) {
ValueDescriptorImpl thisDescriptor = myNodeManager.getThisDescriptor(stackDescriptor, thisObjectReference);
children.add(JavaValue.create(thisDescriptor, evaluationContext, myNodeManager));
}
else {
StaticDescriptorImpl staticDecriptor = myNodeManager.getStaticDescriptor(stackDescriptor, location.method().declaringType());
if (staticDecriptor.isExpandable()) {
children.addTopGroup(new JavaStaticGroup(staticDecriptor, evaluationContext, myNodeManager));
}
}
DebugProcessImpl debugProcess = debuggerContext.getDebugProcess();
if (debugProcess == null) {
return;
}
// add last method return value if any
final Pair<Method, Value> methodValuePair = debugProcess.getLastExecutedMethod();
if (methodValuePair != null) {
ValueDescriptorImpl returnValueDescriptor = myNodeManager.getMethodReturnValueDescriptor(stackDescriptor, methodValuePair.getFirst(), methodValuePair.getSecond());
children.add(JavaValue.create(returnValueDescriptor, evaluationContext, myNodeManager));
}
// add context exceptions
for (Pair<Breakpoint, Event> pair : DebuggerUtilsEx.getEventDescriptors(debuggerContext.getSuspendContext())) {
final Event debugEvent = pair.getSecond();
if (debugEvent instanceof ExceptionEvent) {
final ObjectReference exception = ((ExceptionEvent)debugEvent).exception();
if (exception != null) {
final ValueDescriptorImpl exceptionDescriptor = myNodeManager.getThrownExceptionObjectDescriptor(stackDescriptor, exception);
children.add(JavaValue.create(exceptionDescriptor, evaluationContext, myNodeManager));
}
}
}
final ClassRenderer classRenderer = NodeRendererSettings.getInstance().getClassRenderer();
if (classRenderer.SHOW_VAL_FIELDS_AS_LOCAL_VARIABLES) {
if (thisObjectReference != null && debugProcess.getVirtualMachineProxy().canGetSyntheticAttribute()) {
final ReferenceType thisRefType = thisObjectReference.referenceType();
if (thisRefType instanceof ClassType && thisRefType.equals(location.declaringType()) && thisRefType.name().contains("$")) { // makes sense for nested classes only
final ClassType clsType = (ClassType)thisRefType;
final VirtualMachineProxyImpl vm = debugProcess.getVirtualMachineProxy();
for (Field field : clsType.fields()) {
if ((!vm.canGetSyntheticAttribute() || field.isSynthetic()) && StringUtil
.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) {
final FieldDescriptorImpl fieldDescriptor = myNodeManager.getFieldDescriptor(stackDescriptor, thisObjectReference, field);
children.add(JavaValue.create(fieldDescriptor, evaluationContext, myNodeManager));
}
}
}
}
}
try {
buildVariables(debuggerContext, evaluationContext, children, node);
//if (classRenderer.SORT_ASCENDING) {
// Collections.sort(myChildren, NodeManagerImpl.getNodeComparator());
//}
}
catch (EvaluateException e) {
node.setErrorMessage(e.getMessage());
//myChildren.add(myNodeManager.createMessageNode(new MessageDescriptor(e.getMessage())));
}
}
catch (EvaluateException e) {
node.setErrorMessage(e.getMessage());
//myChildren.clear();
//myChildren.add(myNodeManager.createMessageNode(new MessageDescriptor(e.getMessage())));
}
catch (InvalidStackFrameException e) {
LOG.info(e);
//myChildren.clear();
//notifyCancelled();
}
catch (InternalException e) {
if (e.errorCode() == 35) {
node.setErrorMessage(DebuggerBundle.message("error.corrupt.debug.info", e.getMessage()));
//myChildren.add(
// myNodeManager.createMessageNode(new MessageDescriptor(DebuggerBundle.message("error.corrupt.debug.info", e.getMessage()))));
}
else {
throw e;
}
}
}
// copied from FrameVariablesTree
private void buildVariables(DebuggerContextImpl debuggerContext, EvaluationContextImpl evaluationContext, XValueChildrenList children, XCompositeNode node) throws EvaluateException {
boolean myAutoWatchMode = DebuggerSettings.getInstance().AUTO_VARIABLES_MODE;
if (evaluationContext == null) {
return;
}
final SourcePosition sourcePosition = debuggerContext.getSourcePosition();
if (sourcePosition == null) {
return;
}
try {
if (!XDebuggerSettingsManager.getInstance().getDataViewSettings().isAutoExpressions() && !myAutoWatchMode) {
// optimization
superBuildVariables(evaluationContext, children);
}
else {
final Map<String, LocalVariableProxyImpl> visibleVariables = FrameVariablesTree.getVisibleVariables(getStackFrameProxy());
final EvaluationContextImpl evalContext = evaluationContext;
final Pair<Set<String>, Set<TextWithImports>> usedVars =
ApplicationManager.getApplication().runReadAction(new Computable<Pair<Set<String>, Set<TextWithImports>>>() {
@Override
public Pair<Set<String>, Set<TextWithImports>> compute() {
return FrameVariablesTree.findReferencedVars(visibleVariables.keySet(), sourcePosition, evalContext);
}
});
// add locals
if (myAutoWatchMode) {
for (String var : usedVars.first) {
final LocalVariableDescriptorImpl descriptor = myNodeManager.getLocalVariableDescriptor(null, visibleVariables.get(var));
children.add(JavaValue.create(descriptor, evaluationContext, myNodeManager));
}
}
else {
superBuildVariables(evaluationContext, children);
}
// add expressions
final EvaluationContextImpl evalContextCopy = evaluationContext.createEvaluationContext(evaluationContext.getThisObject());
evalContextCopy.setAutoLoadClasses(false);
for (TextWithImports text : usedVars.second) {
WatchItemDescriptor descriptor = myNodeManager.getWatchItemDescriptor(null, text, null);
children.add(JavaValue.create(descriptor, evaluationContext, myNodeManager));
}
}
}
catch (EvaluateException e) {
if (e.getCause() instanceof AbsentInformationException) {
final StackFrameProxyImpl frame = getStackFrameProxy();
final Collection<Value> argValues = frame.getArgumentValues();
int index = 0;
for (Value argValue : argValues) {
children.add(createArgumentValue(index++, argValue, null, evaluationContext));
}
//node.setMessage(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE.getLabel(), XDebuggerUIConstants.INFORMATION_MESSAGE_ICON, SimpleTextAttributes.REGULAR_ATTRIBUTES, null);
children.add(new DummyMessageValueNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE.getLabel(), XDebuggerUIConstants.INFORMATION_MESSAGE_ICON));
//myChildren.add(myNodeManager.createMessageNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE));
// trying to collect values from variable slots
final List<DecompiledLocalVariable> decompiled = FrameVariablesTree.collectVariablesFromBytecode(frame, argValues.size());
if (!decompiled.isEmpty()) {
try {
final Map<DecompiledLocalVariable, Value> values = LocalVariablesUtil.fetchValues(frame.getStackFrame(), decompiled);
for (DecompiledLocalVariable var : decompiled) {
children.add(createArgumentValue(var.getSlot(), values.get(var), var.getName(), evaluationContext));
}
}
catch (Exception ex) {
LOG.info(ex);
}
}
}
else {
throw e;
}
}
}
static class DummyMessageValueNode extends XNamedValue {
private final String myMessage;
private final Icon myIcon;
public DummyMessageValueNode(String message, Icon icon) {
super("");
myMessage = message;
myIcon = icon;
}
@Override
public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) {
node.setPresentation(myIcon, new XValuePresentation() {
@NotNull
@Override
public String getSeparator() {
return "";
}
@Override
public void renderValue(@NotNull XValueTextRenderer renderer) {
renderer.renderValue(myMessage);
}
}, false);
}
}
private JavaValue createArgumentValue(int index, Value value, String name, EvaluationContextImpl evaluationContext) {
ArgumentValueDescriptorImpl descriptor = myNodeManager.getArgumentValueDescriptor(null, index, value, name);
// setContext is required to calculate correct name
descriptor.setContext(evaluationContext);
return JavaValue.create(null, descriptor, evaluationContext, myNodeManager, true);
}
protected void superBuildVariables(final EvaluationContextImpl evaluationContext, XValueChildrenList children) throws EvaluateException {
final StackFrameProxyImpl frame = getStackFrameProxy();
for (final LocalVariableProxyImpl local : frame.visibleVariables()) {
final LocalVariableDescriptorImpl descriptor = myNodeManager.getLocalVariableDescriptor(null, local);
children.add(JavaValue.create(descriptor, evaluationContext, myNodeManager));
}
}
public StackFrameProxyImpl getStackFrameProxy() {
return myDescriptor.getFrameProxy();
}
@Nullable
@Override
public Object getEqualityObject() {
return myDescriptor.getMethod();
}
}