diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/EditSourceAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/EditSourceAction.java index 778df906c36d..0223b4d2289a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/EditSourceAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/EditSourceAction.java @@ -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() { 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); } }); } diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ToggleFieldBreakpointAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/ToggleFieldBreakpointAction.java index a7e00ee0dd72..9c807a1b9f1b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/ToggleFieldBreakpointAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/ToggleFieldBreakpointAction.java @@ -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)); } }); } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DefaultSourcePositionProvider.java b/java/debugger/impl/src/com/intellij/debugger/engine/DefaultSourcePositionProvider.java new file mode 100644 index 000000000000..2abb48abc3f8 --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DefaultSourcePositionProvider.java @@ -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 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()); + } +} \ No newline at end of file diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java index 44025e1767f5..50cf45f8e259 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java @@ -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)); } } }); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SourcePositionProvider.java b/java/debugger/impl/src/com/intellij/debugger/engine/SourcePositionProvider.java new file mode 100644 index 000000000000..da58aca48d0e --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/engine/SourcePositionProvider.java @@ -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 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 + ); +} diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java index 5051750dec60..da88267615d1 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/FieldDescriptorImpl.java @@ -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 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); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java index f936d79a6725..cf4c8864d8f2 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/LocalVariableDescriptorImpl.java @@ -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; } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index ae23b4d1e929..8dfe8ee5d074 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -119,6 +119,9 @@ + +