IDEA-132151 Debugger: add ability to create custom LocalVariableDescriptor

This commit is contained in:
Natalia Ukhorskaya
2014-12-10 20:44:06 +03:00
committed by Egor.Ushakov
parent 614a249125
commit 39ec065308
8 changed files with 242 additions and 144 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* 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.
@@ -21,6 +21,7 @@ import com.intellij.debugger.engine.evaluation.expression.Modifier;
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.engine.SourcePositionProvider;
import com.intellij.debugger.ui.impl.watch.*;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
@@ -77,13 +78,7 @@ public class EditSourceAction extends DebuggerAction{
final NodeDescriptorImpl nodeDescriptor1 = nodeDescriptor;
return ApplicationManager.getApplication().runReadAction(new Computable<SourcePosition>() {
public SourcePosition compute() {
if (nodeDescriptor1 instanceof FieldDescriptorImpl && debuggerSession != null) {
return ((FieldDescriptorImpl)nodeDescriptor1).getSourcePosition(project, context);
}
if (nodeDescriptor1 instanceof LocalVariableDescriptorImpl && debuggerSession != null) {
return ((LocalVariableDescriptorImpl)nodeDescriptor1).getSourcePosition(project, context);
}
return null;
return SourcePositionProvider.getSourcePosition(nodeDescriptor1, project, context);
}
});
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* 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.
@@ -25,6 +25,7 @@ import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
import com.intellij.debugger.engine.requests.RequestManagerImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.engine.SourcePositionProvider;
import com.intellij.debugger.ui.breakpoints.Breakpoint;
import com.intellij.debugger.ui.breakpoints.BreakpointManager;
import com.intellij.debugger.ui.breakpoints.FieldBreakpoint;
@@ -161,8 +162,7 @@ public class ToggleFieldBreakpointAction extends AnAction {
public void threadAction() {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final FieldDescriptorImpl descriptor = (FieldDescriptorImpl)selectedNode.getDescriptor();
positionRef.set(descriptor.getSourcePosition(project, debuggerContext));
positionRef.set(SourcePositionProvider.getSourcePosition(selectedNode.getDescriptor(), project, debuggerContext));
}
});
}
@@ -0,0 +1,162 @@
/*
* 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.SourcePosition;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerContextUtil;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl;
import com.intellij.debugger.ui.tree.FieldDescriptor;
import com.intellij.debugger.ui.tree.LocalVariableDescriptor;
import com.intellij.debugger.ui.tree.NodeDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.sun.jdi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class DefaultSourcePositionProvider extends SourcePositionProvider {
private static final DefaultSourcePositionProvider ourInstance = new DefaultSourcePositionProvider();
private DefaultSourcePositionProvider() {
}
public static SourcePositionProvider getInstance() {
return ourInstance;
}
@Nullable
@Override
protected SourcePosition computeSourcePosition(@NotNull NodeDescriptor descriptor,
@NotNull Project project,
@NotNull DebuggerContextImpl context,
boolean nearest) {
StackFrameProxyImpl frame = context.getFrameProxy();
if (frame == null) {
return null;
}
if (descriptor instanceof FieldDescriptor) {
return getSourcePositionForField((FieldDescriptor)descriptor, project, context, nearest);
}
else if (descriptor instanceof LocalVariableDescriptor) {
return getSourcePositionForLocalVariable((LocalVariableDescriptor)descriptor, project, context, nearest);
}
return null;
}
@Nullable
protected SourcePosition getSourcePositionForField(@NotNull FieldDescriptor descriptor,
@NotNull Project project,
@NotNull DebuggerContextImpl context,
boolean nearest) {
final ReferenceType type = descriptor.getField().declaringType();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
final String fieldName = descriptor.getField().name();
if (fieldName.startsWith(FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) {
// this field actually mirrors a local variable in the outer class
String varName = fieldName.substring(fieldName.lastIndexOf('$') + 1);
PsiElement element = PositionUtil.getContextElement(context);
if (element == null) {
return null;
}
PsiClass aClass = PsiTreeUtil.getParentOfType(element, PsiClass.class, false);
if (aClass == null) {
return null;
}
PsiElement navigationElement = aClass.getNavigationElement();
if (!(navigationElement instanceof PsiClass)) {
return null;
}
aClass = (PsiClass)navigationElement;
PsiVariable psiVariable = facade.getResolveHelper().resolveReferencedVariable(varName, aClass);
if (psiVariable == null) {
return null;
}
if (nearest) {
return DebuggerContextUtil.findNearest(context, psiVariable, aClass.getContainingFile());
}
return SourcePosition.createFromOffset(psiVariable.getContainingFile(), psiVariable.getTextOffset());
}
else {
final DebuggerSession session = context.getDebuggerSession();
final GlobalSearchScope scope = session != null? session.getSearchScope() : GlobalSearchScope.allScope(project);
PsiClass aClass = facade.findClass(type.name().replace('$', '.'), scope);
if (aClass == null) {
// trying to search, assuming declaring class is an anonymous class
final DebugProcessImpl debugProcess = context.getDebugProcess();
if (debugProcess != null) {
try {
final List<Location> locations = type.allLineLocations();
if (!locations.isEmpty()) {
// important: use the last location to be sure the position will be within the anonymous class
final Location lastLocation = locations.get(locations.size() - 1);
final SourcePosition position = debugProcess.getPositionManager().getSourcePosition(lastLocation);
aClass = JVMNameUtil.getClassAt(position);
}
}
catch (AbsentInformationException ignored) {
}
catch (ClassNotPreparedException ignored) {
}
}
}
if (aClass != null) {
PsiField field = aClass.findFieldByName(fieldName, false);
if (field != null) {
PsiElement element = field.getNavigationElement();
if (nearest) {
return DebuggerContextUtil.findNearest(context, element, aClass.getContainingFile());
}
return SourcePosition.createFromOffset(element.getContainingFile(), element.getTextOffset());
}
}
return null;
}
}
@Nullable
protected SourcePosition getSourcePositionForLocalVariable(@NotNull LocalVariableDescriptor descriptor,
@NotNull Project project,
@NotNull DebuggerContextImpl context,
boolean nearest) {
PsiElement place = PositionUtil.getContextElement(context);
if (place == null) {
return null;
}
PsiVariable psiVariable = JavaPsiFacade.getInstance(project).getResolveHelper().resolveReferencedVariable(descriptor.getName(), place);
if (psiVariable == null) {
return null;
}
PsiFile containingFile = psiVariable.getContainingFile();
if(containingFile == null) return null;
if (nearest) {
return DebuggerContextUtil.findNearest(context, psiVariable, containingFile);
}
return SourcePosition.createFromOffset(containingFile, psiVariable.getTextOffset());
}
}
@@ -404,18 +404,9 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV
@Override
public void run() {
final boolean nearest = navigatable instanceof XNearestSourcePosition;
if (myValueDescriptor instanceof FieldDescriptorImpl) {
SourcePosition position = ((FieldDescriptorImpl)myValueDescriptor).getSourcePosition(getProject(), getDebuggerContext(), nearest);
if (position != null) {
navigatable.setSourcePosition(DebuggerUtilsEx.toXSourcePosition(position));
}
}
if (myValueDescriptor instanceof LocalVariableDescriptorImpl) {
SourcePosition position =
((LocalVariableDescriptorImpl)myValueDescriptor).getSourcePosition(getProject(), getDebuggerContext(), nearest);
if (position != null) {
navigatable.setSourcePosition(DebuggerUtilsEx.toXSourcePosition(position));
}
SourcePosition position = SourcePositionProvider.getSourcePosition(myValueDescriptor, getProject(), getDebuggerContext(), nearest);
if (position != null) {
navigatable.setSourcePosition(DebuggerUtilsEx.toXSourcePosition(position));
}
}
});
@@ -0,0 +1,58 @@
/*
* 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.SourcePosition;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.ui.tree.NodeDescriptor;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class SourcePositionProvider {
public static ExtensionPointName<SourcePositionProvider> EP_NAME = ExtensionPointName.create("com.intellij.debugger.sourcePositionProvider");
@Nullable
public static SourcePosition getSourcePosition(@NotNull NodeDescriptor descriptor,
@NotNull Project project,
@NotNull DebuggerContextImpl context
) {
return getSourcePosition(descriptor, project, context, false);
}
@Nullable
public static SourcePosition getSourcePosition(@NotNull NodeDescriptor descriptor,
@NotNull Project project,
@NotNull DebuggerContextImpl context,
boolean nearest
) {
for (SourcePositionProvider provider : EP_NAME.getExtensions()) {
SourcePosition sourcePosition = provider.computeSourcePosition(descriptor, project, context, nearest);
if (sourcePosition != null) {
return sourcePosition;
}
}
return DefaultSourcePositionProvider.getInstance().computeSourcePosition(descriptor, project, context, nearest);
}
@Nullable
protected abstract SourcePosition computeSourcePosition(@NotNull NodeDescriptor descriptor,
@NotNull Project project,
@NotNull DebuggerContextImpl context,
boolean nearest
);
}
@@ -17,17 +17,11 @@ package com.intellij.debugger.ui.impl.watch;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.DebuggerContext;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.JVMNameUtil;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerContextUtil;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.settings.NodeRendererSettings;
import com.intellij.debugger.ui.tree.FieldDescriptor;
@@ -35,16 +29,16 @@ import com.intellij.debugger.ui.tree.NodeDescriptor;
import com.intellij.debugger.ui.tree.render.ClassRenderer;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiExpression;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.StringBuilderSpinAllocator;
import com.sun.jdi.*;
import com.sun.jdi.Field;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class FieldDescriptorImpl extends ValueDescriptorImpl implements FieldDescriptor{
public static final String OUTER_LOCAL_VAR_FIELD_PREFIX = "val$";
@@ -71,80 +65,6 @@ public class FieldDescriptorImpl extends ValueDescriptorImpl implements FieldDes
return myObject;
}
@SuppressWarnings({"HardCodedStringLiteral"})
@Nullable
public SourcePosition getSourcePosition(final Project project, final DebuggerContextImpl context) {
return getSourcePosition(project, context, false);
}
@SuppressWarnings({"HardCodedStringLiteral"})
@Nullable
public SourcePosition getSourcePosition(final Project project, final DebuggerContextImpl context, boolean nearest) {
if (context.getFrameProxy() == null) {
return null;
}
final ReferenceType type = myField.declaringType();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
final String fieldName = myField.name();
if (fieldName.startsWith(OUTER_LOCAL_VAR_FIELD_PREFIX)) {
// this field actually mirrors a local variable in the outer class
String varName = fieldName.substring(fieldName.lastIndexOf('$') + 1);
PsiElement element = PositionUtil.getContextElement(context);
if (element == null) {
return null;
}
PsiClass aClass = PsiTreeUtil.getParentOfType(element, PsiClass.class, false);
if (aClass == null) {
return null;
}
aClass = (PsiClass) aClass.getNavigationElement();
PsiVariable psiVariable = facade.getResolveHelper().resolveReferencedVariable(varName, aClass);
if (psiVariable == null) {
return null;
}
if (nearest) {
return DebuggerContextUtil.findNearest(context, psiVariable, aClass.getContainingFile());
}
return SourcePosition.createFromOffset(psiVariable.getContainingFile(), psiVariable.getTextOffset());
}
else {
final DebuggerSession session = context.getDebuggerSession();
final GlobalSearchScope scope = session != null? session.getSearchScope() : GlobalSearchScope.allScope(myProject);
PsiClass aClass = facade.findClass(type.name().replace('$', '.'), scope);
if (aClass == null) {
// trying to search, assuming declaring class is an anonymous class
final DebugProcessImpl debugProcess = context.getDebugProcess();
if (debugProcess != null) {
try {
final List<Location> locations = type.allLineLocations();
if (!locations.isEmpty()) {
// important: use the last location to be sure the position will be within the anonymous class
final Location lastLocation = locations.get(locations.size() - 1);
final SourcePosition position = debugProcess.getPositionManager().getSourcePosition(lastLocation);
aClass = JVMNameUtil.getClassAt(position);
}
}
catch (AbsentInformationException ignored) {
}
catch (ClassNotPreparedException ignored) {
}
}
}
if (aClass != null) {
PsiField field = aClass.findFieldByName(fieldName, false);
if (field != null) {
PsiElement element = field.getNavigationElement();
if (nearest) {
return DebuggerContextUtil.findNearest(context, element, aClass.getContainingFile());
}
return SourcePosition.createFromOffset(element.getContainingFile(), element.getTextOffset());
}
}
return null;
}
}
@Override
public void setAncestor(NodeDescriptor oldDescriptor) {
super.setAncestor(oldDescriptor);
@@ -17,12 +17,9 @@ package com.intellij.debugger.ui.impl.watch;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.DebuggerContext;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerContextUtil;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.debugger.jdi.LocalVariableProxyImpl;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
@@ -31,12 +28,13 @@ import com.intellij.debugger.ui.tree.LocalVariableDescriptor;
import com.intellij.debugger.ui.tree.NodeDescriptor;
import com.intellij.debugger.ui.tree.render.ClassRenderer;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiExpression;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.StringBuilderSpinAllocator;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements LocalVariableDescriptor {
private final StackFrameProxyImpl myFrameProxy;
@@ -60,35 +58,6 @@ public class LocalVariableDescriptorImpl extends ValueDescriptorImpl implements
return myLocalVariable;
}
@Nullable
public SourcePosition getSourcePosition(final Project project, final DebuggerContextImpl context) {
return getSourcePosition(project, context, false);
}
@Nullable
public SourcePosition getSourcePosition(final Project project, final DebuggerContextImpl context, boolean nearest) {
StackFrameProxyImpl frame = context.getFrameProxy();
if (frame == null) return null;
PsiElement place = PositionUtil.getContextElement(context);
if (place == null) {
return null;
}
PsiVariable psiVariable = JavaPsiFacade.getInstance(project).getResolveHelper().resolveReferencedVariable(getName(), place);
if (psiVariable == null) {
return null;
}
PsiFile containingFile = psiVariable.getContainingFile();
if(containingFile == null) return null;
if (nearest) {
return DebuggerContextUtil.findNearest(context, psiVariable, containingFile);
}
return SourcePosition.createFromOffset(containingFile, psiVariable.getTextOffset());
}
public boolean isNewLocal() {
return myIsNewLocal;
}
+3
View File
@@ -119,6 +119,9 @@
<extensionPoint name="debugger.frameExtraVarsProvider"
interface="com.intellij.debugger.engine.FrameExtraVariablesProvider"/>
<extensionPoint name="debugger.sourcePositionProvider"
interface="com.intellij.debugger.engine.SourcePositionProvider"/>
<extensionPoint name="debugger.simpleGetterProvider"
interface="com.intellij.debugger.engine.SimpleGetterProvider"/>