Fix leak in debugger (KT-24903)

#KT-24903 Fixed
This commit is contained in:
Nikolay Krasko
2018-06-13 16:59:23 +03:00
parent 64f14ad89b
commit dc8a3ab534
4 changed files with 123 additions and 102 deletions
@@ -18,17 +18,14 @@ package org.jetbrains.kotlin.idea.debugger.stepping
import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.NamedMethodFilter import com.intellij.debugger.engine.NamedMethodFilter
import com.intellij.psi.SmartPsiElementPointer
import com.intellij.util.Range import com.intellij.util.Range
import com.intellij.util.SofterReference
import com.sun.jdi.Location import com.sun.jdi.Location
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.codegen.SamCodegenUtil import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.*
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.getDirectlyOverriddenDeclarations import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
@@ -36,32 +33,26 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
class KotlinBasicStepMethodFilter( class KotlinBasicStepMethodFilter(
targetDescriptor: CallableMemberDescriptor, private val declarationPtr: SmartPsiElementPointer<KtDeclaration>?,
private val isInvoke: Boolean,
private val targetMethodName: String,
private val myCallingExpressionLines: Range<Int> private val myCallingExpressionLines: Range<Int>
) : NamedMethodFilter { ) : NamedMethodFilter {
private val myTargetMethodName: String = when (targetDescriptor) { init {
is ClassDescriptor, is ConstructorDescriptor -> "<init>" assert(declarationPtr != null || isInvoke)
is PropertyAccessorDescriptor -> JvmAbi.getterName(targetDescriptor.correspondingProperty.name.asString())
else -> targetDescriptor.name.asString()
} }
private val _targetDescriptor = SofterReference(
(targetDescriptor as? FunctionDescriptor)?.let { SamCodegenUtil.getOriginalIfSamAdapter(it) } ?: targetDescriptor
)
override fun getCallingExpressionLines() = myCallingExpressionLines override fun getCallingExpressionLines() = myCallingExpressionLines
override fun getMethodName() = myTargetMethodName override fun getMethodName() = targetMethodName
override fun locationMatches(process: DebugProcessImpl, location: Location): Boolean { override fun locationMatches(process: DebugProcessImpl, location: Location): Boolean {
val targetDescriptor = _targetDescriptor.get() ?: return true
val method = location.method() val method = location.method()
if (myTargetMethodName != method.name()) return false if (targetMethodName != method.name()) return false
val positionManager = process.positionManager val positionManager = process.positionManager
val currentDescriptor = runReadAction { val (currentDescriptor, currentDeclaration) = runReadAction {
val elementAt = positionManager.getSourcePosition(location)?.elementAt val elementAt = positionManager.getSourcePosition(location)?.elementAt
val declaration = elementAt?.getParentOfTypesAndPredicate(false, KtDeclaration::class.java) { val declaration = elementAt?.getParentOfTypesAndPredicate(false, KtDeclaration::class.java) {
@@ -69,44 +60,36 @@ class KotlinBasicStepMethodFilter(
} }
if (declaration is KtClass && method.name() == "<init>") { if (declaration is KtClass && method.name() == "<init>") {
declaration.resolveToDescriptorIfAny()?.unsubstitutedPrimaryConstructor declaration.resolveToDescriptorIfAny()?.unsubstitutedPrimaryConstructor to declaration
} else { } else {
declaration?.resolveToDescriptorIfAny() declaration?.resolveToDescriptorIfAny() to declaration
} }
} ?: return false // TODO: Check that we can always find a descriptor (libraries with sources, libraries without sources) }
if (currentDescriptor == null || currentDeclaration == null) {
return false
}
@Suppress("FoldInitializerAndIfToElvis") @Suppress("FoldInitializerAndIfToElvis")
if (currentDescriptor !is CallableMemberDescriptor) return false if (currentDescriptor !is CallableMemberDescriptor) return false
if (currentDescriptor.kind != DECLARATION) return false if (currentDescriptor.kind != DECLARATION) return false
if (targetDescriptor is FunctionInvokeDescriptor) { if (isInvoke) {
// There can be only one 'invoke' target at the moment so consider position as expected. // There can be only one 'invoke' target at the moment so consider position as expected.
// Descriptors can be not-equal, say when parameter has type `(T) -> T` and lambda is `Int.() -> Int`. // Descriptors can be not-equal, say when parameter has type `(T) -> T` and lambda is `Int.() -> Int`.
return true return true
} }
if (compareDescriptors(currentDescriptor, targetDescriptor)) return true val declaration = declarationPtr?.element
?: return true // Element is lost. But we know that name is matches, so stop.
// We should stop if current descriptor overrides the target one or some base descriptor of target if (currentDeclaration.isEquivalentTo(declaration)) {
// (if target descriptor is delegation or fake override)
val baseDescriptors = when (targetDescriptor.kind) {
DELEGATION, FAKE_OVERRIDE ->
targetDescriptor.getDirectlyOverriddenDeclarations()
DECLARATION, SYNTHESIZED ->
listOf(targetDescriptor)
}
if (baseDescriptors.any { baseOfTarget -> compareDescriptors(baseOfTarget, currentDescriptor) }) {
return true return true
} }
return DescriptorUtils.getAllOverriddenDescriptors(currentDescriptor).any { baseOfCurrent -> return DescriptorUtils.getAllOverriddenDescriptors(currentDescriptor).any { baseOfCurrent ->
baseDescriptors.any { baseOfTarget -> compareDescriptors(baseOfCurrent, baseOfTarget) } val currentBaseDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(currentDeclaration.project, baseOfCurrent)
declaration.isEquivalentTo(currentBaseDeclaration)
} }
} }
}
private fun compareDescriptors(d1: DeclarationDescriptor, d2: DeclarationDescriptor): Boolean {
return d1 == d2 || d1.original == d2.original
} }
@@ -4,23 +4,39 @@ import com.intellij.debugger.actions.SmartStepTarget
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.util.Range import com.intellij.util.Range
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import javax.swing.Icon import javax.swing.Icon
class KotlinMethodSmartStepTarget( class KotlinMethodSmartStepTarget(
val descriptor: CallableMemberDescriptor, descriptor: CallableMemberDescriptor,
val declaration: KtDeclaration?,
label: String, label: String,
highlightElement: PsiElement, highlightElement: PsiElement,
lines: Range<Int> lines: Range<Int>
) : SmartStepTarget(label, highlightElement, false, lines) { ) : SmartStepTarget(label, highlightElement, false, lines) {
val isInvoke = descriptor is FunctionInvokeDescriptor
init {
assert(declaration != null || isInvoke)
}
private val isExtension = descriptor.isExtension
val targetMethodName: String = when (descriptor) {
is ClassDescriptor, is ConstructorDescriptor -> "<init>"
is PropertyAccessorDescriptor -> JvmAbi.getterName(descriptor.correspondingProperty.name.asString())
else -> descriptor.name.asString()
}
override fun getIcon(): Icon? { override fun getIcon(): Icon? {
return when { return when {
descriptor.isExtension -> KotlinIcons.EXTENSION_FUNCTION isExtension -> KotlinIcons.EXTENSION_FUNCTION
else -> KotlinIcons.FUNCTION else -> KotlinIcons.FUNCTION
} }
} }
@@ -44,19 +60,19 @@ class KotlinMethodSmartStepTarget(
if (other == null || other !is KotlinMethodSmartStepTarget) return false if (other == null || other !is KotlinMethodSmartStepTarget) return false
if (descriptor is FunctionInvokeDescriptor && other.descriptor is FunctionInvokeDescriptor) { if (isInvoke && other.isInvoke) {
// Don't allow to choose several invoke targets in smart step into as we can't distinguish them reliably during debug // Don't allow to choose several invoke targets in smart step into as we can't distinguish them reliably during debug
return true return true
} }
return descriptor == other.descriptor return declaration === other.declaration
} }
override fun hashCode(): Int { override fun hashCode(): Int {
if (descriptor is FunctionInvokeDescriptor) { if (isInvoke) {
// Predefined value to make all FunctionInvokeDescriptor targets equal // Predefined value to make all FunctionInvokeDescriptor targets equal
return 42 return 42
} }
return descriptor.hashCode() return declaration!!.hashCode()
} }
} }
@@ -26,13 +26,11 @@ import com.intellij.psi.PsiMethod
import com.intellij.util.Range import com.intellij.util.Range
import com.intellij.util.containers.OrderedSet import com.intellij.util.containers.OrderedSet
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.builtins.isSuspendFunctionType import org.jetbrains.kotlin.builtins.isSuspendFunctionType
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
@@ -40,6 +38,7 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.load.java.isFromJava import org.jetbrains.kotlin.load.java.isFromJava
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -150,62 +149,69 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
} }
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
val resolvedCall = expression.getResolvedCall(bindingContext) recordGetter(expression)
if (resolvedCall != null) { super.visitSimpleNameExpression(expression)
val propertyDescriptor = resolvedCall.resultingDescriptor }
if (propertyDescriptor is PropertyDescriptor) {
val getterDescriptor = propertyDescriptor.getter private fun recordGetter(expression: KtSimpleNameExpression) {
if (getterDescriptor != null && !getterDescriptor.isDefault) { val resolvedCall = expression.getResolvedCall(bindingContext) ?: return
val delegatedResolvedCall = bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor] val propertyDescriptor = resolvedCall.resultingDescriptor as? PropertyDescriptor ?: return
if (delegatedResolvedCall == null) {
val getter = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, getterDescriptor) val getterDescriptor = propertyDescriptor.getter
if (getter is KtPropertyAccessor && getter.hasBody()) { if (getterDescriptor == null || getterDescriptor.isDefault) return
val label = KotlinMethodSmartStepTarget.calcLabel(getterDescriptor)
result.add(KotlinMethodSmartStepTarget(getterDescriptor, label, expression, lines)) val ktDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, getterDescriptor) as? KtDeclaration ?: return
}
} else { val delegatedResolvedCall = bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor]
val delegatedPropertyGetterDescriptor = delegatedResolvedCall.resultingDescriptor if (delegatedResolvedCall != null) {
val label = val delegatedPropertyGetterDescriptor = delegatedResolvedCall.resultingDescriptor
"${propertyDescriptor.name}." + KotlinMethodSmartStepTarget.calcLabel(delegatedPropertyGetterDescriptor) val label = "${propertyDescriptor.name}." + KotlinMethodSmartStepTarget.calcLabel(delegatedPropertyGetterDescriptor)
result.add(KotlinMethodSmartStepTarget(delegatedPropertyGetterDescriptor, label, expression, lines)) result.add(KotlinMethodSmartStepTarget(delegatedPropertyGetterDescriptor, ktDeclaration, label, expression, lines))
} } else {
} if (ktDeclaration is KtPropertyAccessor && ktDeclaration.hasBody()) {
val label = KotlinMethodSmartStepTarget.calcLabel(getterDescriptor)
result.add(KotlinMethodSmartStepTarget(getterDescriptor, ktDeclaration, label, expression, lines))
} }
} }
super.visitSimpleNameExpression(expression)
} }
private fun recordFunction(expression: KtExpression) { private fun recordFunction(expression: KtExpression) {
val resolvedCall = expression.getResolvedCall(bindingContext) ?: return val resolvedCall = expression.getResolvedCall(bindingContext) ?: return
val descriptor = resolvedCall.resultingDescriptor val descriptor = resolvedCall.resultingDescriptor
if (descriptor is FunctionDescriptor && !isIntrinsic(descriptor)) { if (descriptor !is FunctionDescriptor || isIntrinsic(descriptor)) return
if (descriptor.isFromJava) {
(DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor) as? PsiMethod)?.let {
result.add(MethodSmartStepTarget(it, null, expression, false, lines))
}
} else {
if (descriptor is ConstructorDescriptor && descriptor.isPrimary) {
val psiElement = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor)
if (psiElement is KtClass && psiElement.getAnonymousInitializers().isEmpty()) {
// There is no constructor or init block, so do not show it in smart step into
return
}
}
val callLabel = KotlinMethodSmartStepTarget.calcLabel(descriptor) val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor)
val label = when (descriptor) { if (descriptor.isFromJava) {
is FunctionInvokeDescriptor -> { (declaration as? PsiMethod)?.let {
when (expression) { result.add(MethodSmartStepTarget(it, null, declaration, false, lines))
is KtSimpleNameExpression -> "${runReadAction { expression.text }}.$callLabel"
else -> callLabel
}
}
else -> callLabel
}
result.add(KotlinMethodSmartStepTarget(descriptor, label, expression, lines))
} }
} else {
if (declaration == null && !isInvokeInBuiltinFunction(descriptor)) {
return
}
if (declaration !is KtDeclaration?) return
if (descriptor is ConstructorDescriptor && descriptor.isPrimary) {
if (declaration is KtClass && declaration.getAnonymousInitializers().isEmpty()) {
// There is no constructor or init block, so do not show it in smart step into
return
}
}
val callLabel = KotlinMethodSmartStepTarget.calcLabel(descriptor)
val label = when (descriptor) {
is FunctionInvokeDescriptor -> {
when (expression) {
is KtSimpleNameExpression -> "${runReadAction { expression.text }}.$callLabel"
else -> callLabel
}
}
else -> callLabel
}
result.add(KotlinMethodSmartStepTarget(descriptor, declaration, label, expression, lines))
} }
} }
}, null) }, null)
@@ -216,7 +222,12 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
override fun createMethodFilter(stepTarget: SmartStepTarget?): MethodFilter? { override fun createMethodFilter(stepTarget: SmartStepTarget?): MethodFilter? {
return when (stepTarget) { return when (stepTarget) {
is KotlinMethodSmartStepTarget -> is KotlinMethodSmartStepTarget ->
KotlinBasicStepMethodFilter(stepTarget.descriptor, stepTarget.callingExpressionLines!!) KotlinBasicStepMethodFilter(
stepTarget.declaration?.createSmartPointer(),
stepTarget.isInvoke,
stepTarget.targetMethodName,
stepTarget.callingExpressionLines!!
)
is KotlinLambdaSmartStepTarget -> is KotlinLambdaSmartStepTarget ->
KotlinLambdaMethodFilter( KotlinLambdaMethodFilter(
stepTarget.getLambda(), stepTarget.callingExpressionLines!!, stepTarget.isInline, stepTarget.isSuspend stepTarget.getLambda(), stepTarget.callingExpressionLines!!, stepTarget.isInline, stepTarget.isSuspend
@@ -231,4 +242,10 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
private fun isIntrinsic(descriptor: CallableMemberDescriptor): Boolean { private fun isIntrinsic(descriptor: CallableMemberDescriptor): Boolean {
return methods.getIntrinsic(descriptor) != null return methods.getIntrinsic(descriptor) != null
} }
private fun isInvokeInBuiltinFunction(descriptor: DeclarationDescriptor): Boolean {
if (descriptor !is FunctionInvokeDescriptor) return false
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return false
return classDescriptor.defaultType.isBuiltinFunctionalType
}
} }
@@ -59,6 +59,7 @@ import org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinLineBreakpointType
import org.jetbrains.kotlin.idea.debugger.stepping.* import org.jetbrains.kotlin.idea.debugger.stepping.*
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
@@ -246,7 +247,11 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
KotlinLambdaMethodFilter( KotlinLambdaMethodFilter(
stepTarget.getLambda(), stepTarget.getCallingExpressionLines()!!, stepTarget.isInline, stepTarget.isSuspend) stepTarget.getLambda(), stepTarget.getCallingExpressionLines()!!, stepTarget.isInline, stepTarget.isSuspend)
is KotlinMethodSmartStepTarget -> is KotlinMethodSmartStepTarget ->
KotlinBasicStepMethodFilter(stepTarget.descriptor, stepTarget.getCallingExpressionLines()!!) KotlinBasicStepMethodFilter(
stepTarget.declaration?.createSmartPointer(),
stepTarget.isInvoke,
stepTarget.targetMethodName,
stepTarget.getCallingExpressionLines()!!)
is MethodSmartStepTarget -> BasicStepMethodFilter(stepTarget.method, stepTarget.getCallingExpressionLines()) is MethodSmartStepTarget -> BasicStepMethodFilter(stepTarget.method, stepTarget.getCallingExpressionLines())
else -> null else -> null
} }