diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 79bf6fe288a..a444dc035f9 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -372,7 +372,7 @@ - + diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/KotlinSourcePositionProvider.kt b/idea/src/org/jetbrains/jet/plugin/debugger/KotlinSourcePositionProvider.kt new file mode 100644 index 00000000000..7ad64d72b66 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/debugger/KotlinSourcePositionProvider.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2010-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 org.jetbrains.jet.plugin.debugger + +import com.intellij.debugger.engine.SourcePositionProvider +import com.intellij.debugger.ui.tree.NodeDescriptor +import com.intellij.openapi.project.Project +import com.intellij.debugger.impl.DebuggerContextImpl +import com.intellij.debugger.SourcePosition +import org.jetbrains.jet.codegen.AsmUtil +import org.jetbrains.jet.lang.psi.JetClassOrObject +import com.intellij.debugger.impl.DebuggerContextUtil +import com.sun.jdi.ReferenceType +import com.intellij.psi.PsiElement +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.jet.lang.resolve.java.JvmClassName +import com.intellij.psi.JavaPsiFacade +import org.jetbrains.jet.lang.psi.psiUtil.getStrictParentOfType +import com.sun.jdi.AbsentInformationException +import com.sun.jdi.ClassNotPreparedException +import com.intellij.debugger.ui.tree.FieldDescriptor + +public class KotlinSourcePositionProvider: SourcePositionProvider() { + override fun computeSourcePosition(descriptor: NodeDescriptor, project: Project, context: DebuggerContextImpl, nearest: Boolean): SourcePosition? { + if (context.getFrameProxy() == null) return null + + if (descriptor is FieldDescriptor) { + return computeSourcePosition(descriptor, project, context, nearest) + } + + return null + } + + fun computeSourcePosition(descriptor: FieldDescriptor, project: Project, context: DebuggerContextImpl, nearest: Boolean): SourcePosition? { + val fieldName = descriptor.getField().name() + if (fieldName == AsmUtil.CAPTURED_THIS_FIELD || fieldName == AsmUtil.CAPTURED_RECEIVER_FIELD) { + return null + } + + val type = descriptor.getField().declaringType() + val myClass = findClassByType(project, type, context)?.getNavigationElement() + if (myClass !is JetClassOrObject) { + return null + } + + val field = myClass.getDeclarations().firstOrNull { fieldName == it.getName() } + if (field == null) return null + + if (nearest) { + return DebuggerContextUtil.findNearest(context, field, myClass.getContainingFile()) + } + return SourcePosition.createFromOffset(field.getContainingFile(), field.getTextOffset()) + } + + private fun findClassByType(project: Project, type: ReferenceType, context: DebuggerContextImpl): PsiElement? { + val session = context.getDebuggerSession() + val scope = if (session != null) session.getSearchScope() else GlobalSearchScope.allScope(project) + val className = JvmClassName.byInternalName(type.name()).getFqNameForClassNameWithoutDollars().asString() + + val myClass = JavaPsiFacade.getInstance(project).findClass(className, scope) + if (myClass != null) return myClass + + val position = getLastSourcePosition(type, context) + if (position != null) { + val element = position.getElementAt() + if (element != null) { + return element.getStrictParentOfType() + } + } + return null + } + + private fun getLastSourcePosition(type: ReferenceType, context: DebuggerContextImpl): SourcePosition? { + val debugProcess = context.getDebugProcess() + if (debugProcess != null) { + try { + val locations = type.allLineLocations() + if (!locations.isEmpty()) { + val lastLocation = locations.get(locations.size() - 1) + return debugProcess.getPositionManager().getSourcePosition(lastLocation) + } + } + catch (ignored: AbsentInformationException) { + } + catch (ignored: ClassNotPreparedException) { + } + } + return null + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/render/KotlinClassWithDelegatedPropertyRenderer.kt b/idea/src/org/jetbrains/jet/plugin/debugger/render/KotlinClassWithDelegatedPropertyRenderer.kt index 981142f8c57..03324a8bb56 100644 --- a/idea/src/org/jetbrains/jet/plugin/debugger/render/KotlinClassWithDelegatedPropertyRenderer.kt +++ b/idea/src/org/jetbrains/jet/plugin/debugger/render/KotlinClassWithDelegatedPropertyRenderer.kt @@ -16,6 +16,7 @@ package org.jetbrains.jet.plugin.debugger.render +import com.intellij.debugger.ui.tree.render.ClassRenderer import com.sun.jdi.Type as JdiType import com.sun.jdi.Value import com.intellij.debugger.ui.tree.render.ChildrenBuilder @@ -36,9 +37,8 @@ import com.sun.jdi.InvocationException import com.sun.jdi.Method import org.jetbrains.jet.codegen.PropertyCodegen import org.jetbrains.jet.lang.resolve.name.Name -import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl -public class KotlinClassWithDelegatedPropertyRenderer : KotlinObjectRenderer() { +public class KotlinClassWithDelegatedPropertyRenderer : ClassRenderer() { override fun isApplicable(jdiType: Type?): Boolean { if (!super.isApplicable(jdiType)) return false @@ -68,7 +68,7 @@ public class KotlinClassWithDelegatedPropertyRenderer : KotlinObjectRenderer() { continue } - val fieldDescriptor = createFieldDescriptor(builder.getParentDescriptor() as ValueDescriptorImpl, nodeDescriptorFactory, value, field, context) + val fieldDescriptor = nodeDescriptorFactory.getFieldDescriptor(builder.getParentDescriptor(), value, field) children.add(nodeManager.createNode(fieldDescriptor, context)) if (field.name().endsWith(JvmAbi.DELEGATED_PROPERTY_NAME_SUFFIX)) { diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/render/KotlinObjectRenderer.kt b/idea/src/org/jetbrains/jet/plugin/debugger/render/KotlinObjectRenderer.kt deleted file mode 100644 index 0ef1c91790f..00000000000 --- a/idea/src/org/jetbrains/jet/plugin/debugger/render/KotlinObjectRenderer.kt +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2010-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 org.jetbrains.jet.plugin.debugger.render - -import com.intellij.debugger.ui.tree.render.ClassRenderer -import com.sun.jdi.Type -import com.intellij.debugger.engine.evaluation.EvaluationContext -import com.sun.jdi.ObjectReference -import com.sun.jdi.Field -import com.sun.jdi.ClassType -import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl -import com.intellij.debugger.ui.tree.NodeDescriptorFactory -import com.intellij.debugger.ui.tree.FieldDescriptor -import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl -import com.intellij.openapi.project.Project -import com.intellij.debugger.impl.DebuggerContextImpl -import org.jetbrains.jet.lang.resolve.java.JvmAbi -import com.intellij.debugger.ui.tree.ValueDescriptor -import com.intellij.debugger.ui.tree.render.DescriptorLabelListener -import com.intellij.debugger.settings.NodeRendererSettings -import com.intellij.psi.PsiClass -import com.intellij.psi.JavaPsiFacade -import com.intellij.debugger.SourcePosition -import com.intellij.debugger.impl.DebuggerContextUtil -import com.intellij.psi.search.GlobalSearchScope -import com.sun.jdi.AbsentInformationException -import com.sun.jdi.ClassNotPreparedException -import com.intellij.psi.PsiElement -import org.jetbrains.jet.lang.psi.JetClass -import org.jetbrains.jet.lang.resolve.java.JvmClassName -import com.sun.jdi.ReferenceType -import org.jetbrains.jet.codegen.AsmUtil -import org.jetbrains.jet.lang.psi.JetClassOrObject -import org.jetbrains.jet.lang.psi.psiUtil.getStrictParentOfType - -public open class KotlinObjectRenderer : ClassRenderer() { - - override fun isApplicable(jdiType: Type?): Boolean { - if (!super.isApplicable(jdiType)) return false - - var isCustomRendererApplicable = false - NodeRendererSettings.getInstance().getCustomRenderers().iterateRenderers { - isCustomRendererApplicable = it.isApplicable(jdiType) - !isCustomRendererApplicable - } - - if (isCustomRendererApplicable) return false - - return jdiType.isKotlinClass() - } - - override fun createFieldDescriptor( - parentDescriptor: ValueDescriptorImpl?, - nodeDescriptorFactory: NodeDescriptorFactory?, - objRef: ObjectReference?, - field: Field?, - evaluationContext: EvaluationContext? - ): FieldDescriptor { - if (field?.declaringType().isKotlinClass()) { - return KotlinObjectFieldDescriptor(evaluationContext?.getProject(), objRef, field) - } - return super.createFieldDescriptor(parentDescriptor, nodeDescriptorFactory, objRef, field, evaluationContext) - } - - override fun calcLabel( - descriptor: ValueDescriptor?, - evaluationContext: EvaluationContext?, - labelListener: DescriptorLabelListener? - ): String? { - val toStringRenderer = NodeRendererSettings.getInstance().getToStringRenderer() - if (toStringRenderer.isApplicable(descriptor?.getValue()?.type())) { - return toStringRenderer.calcLabel(descriptor, evaluationContext, labelListener) - } - return super.calcLabel(descriptor, evaluationContext, labelListener) - } -} - -public class KotlinObjectFieldDescriptor( - project: Project?, - objRef: ObjectReference?, - field: Field? -) : FieldDescriptorImpl(project, objRef, field) { - override fun getSourcePosition(project: Project?, context: DebuggerContextImpl?, nearest: Boolean): SourcePosition? { - if (context == null || context.getFrameProxy() == null) return null - - val fieldName = getField().name() - if (fieldName == AsmUtil.CAPTURED_THIS_FIELD || fieldName == AsmUtil.CAPTURED_RECEIVER_FIELD) { - return null - } - - val type = getField().declaringType() - val myClass = findClassByType(type, context)?.getNavigationElement() - if (myClass !is JetClassOrObject) { - return null - } - - val field = myClass.getDeclarations().firstOrNull { fieldName == it.getName() } - if (field == null) return null - - if (nearest) { - return DebuggerContextUtil.findNearest(context, field, myClass.getContainingFile()) - } - return SourcePosition.createFromOffset(field.getContainingFile(), field.getTextOffset()) - } - - private fun findClassByType(type: ReferenceType, context: DebuggerContextImpl): PsiElement? { - val session = context.getDebuggerSession() - val scope = if (session != null) session.getSearchScope() else GlobalSearchScope.allScope(myProject) - val className = JvmClassName.byInternalName(type.name()).getFqNameForClassNameWithoutDollars().asString() - - val myClass = JavaPsiFacade.getInstance(myProject).findClass(className, scope) - if (myClass != null) return myClass - - val position = getLastSourcePosition(type, context) - if (position != null) { - val element = position.getElementAt() - if (element != null) { - return element.getStrictParentOfType() - } - } - return null - } - - - private fun getLastSourcePosition(type: ReferenceType, context: DebuggerContextImpl): SourcePosition? { - val debugProcess = context.getDebugProcess() - if (debugProcess != null) { - try { - val locations = type.allLineLocations() - if (!locations.isEmpty()) { - val lastLocation = locations.get(locations.size() - 1) - return debugProcess.getPositionManager().getSourcePosition(lastLocation) - } - } - catch (ignored: AbsentInformationException) { - } - catch (ignored: ClassNotPreparedException) { - } - } - return null - } -} - -private fun Type?.isKotlinClass(): Boolean { - return this is ClassType && this.allInterfaces().any { it.name() == JvmAbi.K_OBJECT.asString() } -} - diff --git a/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClass.out b/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClass.out index cd9b3d49fab..b0e66be3177 100644 --- a/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClass.out +++ b/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClass.out @@ -30,10 +30,10 @@ class MyDelegateThrowsException { static = static = delegatedPropertyInClass.DelegatedPropertyInClassPackage$@packagePartHASH local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} local = a: delegatedPropertyInClass.A = {delegatedPropertyInClass.A@uniqueID} - field = prop$delegate: delegatedPropertyInClass.MyDelegate = {delegatedPropertyInClass.MyDelegate@uniqueID} (sp = null) - field = prop: int = 1 - field = propEx$delegate: delegatedPropertyInClass.MyDelegateThrowsException = {delegatedPropertyInClass.MyDelegateThrowsException@uniqueID} (sp = null) - field = propEx: java.lang.IllegalStateException = {java.lang.IllegalStateException@uniqueID}java.lang.IllegalStateException + field = prop$delegate: delegatedPropertyInClass.MyDelegate = {delegatedPropertyInClass.MyDelegate@uniqueID} (sp = delegatedPropertyInClass.kt, 12) + field = prop: int = 1 (sp = delegatedPropertyInClass.kt, 12) + field = propEx$delegate: delegatedPropertyInClass.MyDelegateThrowsException = {delegatedPropertyInClass.MyDelegateThrowsException@uniqueID} (sp = delegatedPropertyInClass.kt, 13) + field = propEx: java.lang.IllegalStateException = {java.lang.IllegalStateException@uniqueID}java.lang.IllegalStateException (sp = delegatedPropertyInClass.kt, 13) field = detailMessage: java.lang.String = null (sp = Throwable.!EXT!) field = cause: java.lang.Throwable = {java.lang.IllegalStateException@uniqueID}java.lang.IllegalStateException (sp = Throwable.!EXT!) field = stackTrace: java.lang.StackTraceElement[] = null (sp = Throwable.!EXT!) diff --git a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt index 862a8a32cb1..8c3e9039d6a 100644 --- a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt @@ -62,6 +62,7 @@ import com.intellij.debugger.settings.NodeRendererSettings import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl import com.intellij.debugger.impl.DebuggerContextImpl import com.intellij.debugger.SourcePosition +import com.intellij.debugger.engine.SourcePositionProvider public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestBase() { private val logger = Logger.getLogger(javaClass())!! @@ -252,9 +253,8 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestB is LocalVariableDescriptor -> logDescriptor(descriptor, "$curIndent local = $label\n") is StaticDescriptor -> logDescriptor(descriptor, "$curIndent static = $label\n") is ThisDescriptorImpl -> logDescriptor(descriptor, "$curIndent this = $label\n") - is FieldDescriptorImpl -> logDescriptor(descriptor, "$curIndent field = $label" - + " (sp = ${render(descriptor.getSourcePosition(myProject, debuggerContext))})\n") - is FieldDescriptor -> logDescriptor(descriptor, "$curIndent field = $label\n") + is FieldDescriptor -> logDescriptor(descriptor, "$curIndent field = $label" + + " (sp = ${render(SourcePositionProvider.getSourcePosition(descriptor, myProject, debuggerContext))})\n") else -> logDescriptor(descriptor, "$curIndent unknown = $label\n") }