Fix leak in debugger (KT-24903)
#KT-24903 Fixed
This commit is contained in:
+25
-42
@@ -18,17 +18,14 @@ package org.jetbrains.kotlin.idea.debugger.stepping
|
||||
|
||||
import com.intellij.debugger.engine.DebugProcessImpl
|
||||
import com.intellij.debugger.engine.NamedMethodFilter
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import com.intellij.util.Range
|
||||
import com.intellij.util.SofterReference
|
||||
import com.sun.jdi.Location
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||
import org.jetbrains.kotlin.codegen.SamCodegenUtil
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.*
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION
|
||||
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.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
@@ -36,32 +33,26 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
|
||||
class KotlinBasicStepMethodFilter(
|
||||
targetDescriptor: CallableMemberDescriptor,
|
||||
private val declarationPtr: SmartPsiElementPointer<KtDeclaration>?,
|
||||
private val isInvoke: Boolean,
|
||||
private val targetMethodName: String,
|
||||
private val myCallingExpressionLines: Range<Int>
|
||||
) : NamedMethodFilter {
|
||||
private val myTargetMethodName: String = when (targetDescriptor) {
|
||||
is ClassDescriptor, is ConstructorDescriptor -> "<init>"
|
||||
is PropertyAccessorDescriptor -> JvmAbi.getterName(targetDescriptor.correspondingProperty.name.asString())
|
||||
else -> targetDescriptor.name.asString()
|
||||
init {
|
||||
assert(declarationPtr != null || isInvoke)
|
||||
}
|
||||
|
||||
private val _targetDescriptor = SofterReference(
|
||||
(targetDescriptor as? FunctionDescriptor)?.let { SamCodegenUtil.getOriginalIfSamAdapter(it) } ?: targetDescriptor
|
||||
)
|
||||
|
||||
override fun getCallingExpressionLines() = myCallingExpressionLines
|
||||
|
||||
override fun getMethodName() = myTargetMethodName
|
||||
override fun getMethodName() = targetMethodName
|
||||
|
||||
override fun locationMatches(process: DebugProcessImpl, location: Location): Boolean {
|
||||
val targetDescriptor = _targetDescriptor.get() ?: return true
|
||||
|
||||
val method = location.method()
|
||||
if (myTargetMethodName != method.name()) return false
|
||||
if (targetMethodName != method.name()) return false
|
||||
|
||||
val positionManager = process.positionManager
|
||||
|
||||
val currentDescriptor = runReadAction {
|
||||
val (currentDescriptor, currentDeclaration) = runReadAction {
|
||||
val elementAt = positionManager.getSourcePosition(location)?.elementAt
|
||||
|
||||
val declaration = elementAt?.getParentOfTypesAndPredicate(false, KtDeclaration::class.java) {
|
||||
@@ -69,44 +60,36 @@ class KotlinBasicStepMethodFilter(
|
||||
}
|
||||
|
||||
if (declaration is KtClass && method.name() == "<init>") {
|
||||
declaration.resolveToDescriptorIfAny()?.unsubstitutedPrimaryConstructor
|
||||
declaration.resolveToDescriptorIfAny()?.unsubstitutedPrimaryConstructor to declaration
|
||||
} 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")
|
||||
if (currentDescriptor !is CallableMemberDescriptor) 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.
|
||||
// Descriptors can be not-equal, say when parameter has type `(T) -> T` and lambda is `Int.() -> Int`.
|
||||
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 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) }) {
|
||||
if (currentDeclaration.isEquivalentTo(declaration)) {
|
||||
return true
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+24
-8
@@ -4,23 +4,39 @@ import com.intellij.debugger.actions.SmartStepTarget
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.Range
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
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.resolve.descriptorUtil.isExtension
|
||||
import javax.swing.Icon
|
||||
|
||||
class KotlinMethodSmartStepTarget(
|
||||
val descriptor: CallableMemberDescriptor,
|
||||
descriptor: CallableMemberDescriptor,
|
||||
val declaration: KtDeclaration?,
|
||||
label: String,
|
||||
highlightElement: PsiElement,
|
||||
lines: Range<Int>
|
||||
) : 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? {
|
||||
return when {
|
||||
descriptor.isExtension -> KotlinIcons.EXTENSION_FUNCTION
|
||||
isExtension -> KotlinIcons.EXTENSION_FUNCTION
|
||||
else -> KotlinIcons.FUNCTION
|
||||
}
|
||||
}
|
||||
@@ -44,19 +60,19 @@ class KotlinMethodSmartStepTarget(
|
||||
|
||||
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
|
||||
return true
|
||||
}
|
||||
|
||||
return descriptor == other.descriptor
|
||||
return declaration === other.declaration
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
if (descriptor is FunctionInvokeDescriptor) {
|
||||
if (isInvoke) {
|
||||
// Predefined value to make all FunctionInvokeDescriptor targets equal
|
||||
return 42
|
||||
}
|
||||
return descriptor.hashCode()
|
||||
return declaration!!.hashCode()
|
||||
}
|
||||
}
|
||||
+68
-51
@@ -26,13 +26,11 @@ import com.intellij.psi.PsiMethod
|
||||
import com.intellij.util.Range
|
||||
import com.intellij.util.containers.OrderedSet
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
|
||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
|
||||
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.load.java.isFromJava
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
@@ -150,62 +149,69 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||
}
|
||||
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
val resolvedCall = expression.getResolvedCall(bindingContext)
|
||||
if (resolvedCall != null) {
|
||||
val propertyDescriptor = resolvedCall.resultingDescriptor
|
||||
if (propertyDescriptor is PropertyDescriptor) {
|
||||
val getterDescriptor = propertyDescriptor.getter
|
||||
if (getterDescriptor != null && !getterDescriptor.isDefault) {
|
||||
val delegatedResolvedCall = bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor]
|
||||
if (delegatedResolvedCall == null) {
|
||||
val getter = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, getterDescriptor)
|
||||
if (getter is KtPropertyAccessor && getter.hasBody()) {
|
||||
val label = KotlinMethodSmartStepTarget.calcLabel(getterDescriptor)
|
||||
result.add(KotlinMethodSmartStepTarget(getterDescriptor, label, expression, lines))
|
||||
}
|
||||
} else {
|
||||
val delegatedPropertyGetterDescriptor = delegatedResolvedCall.resultingDescriptor
|
||||
val label =
|
||||
"${propertyDescriptor.name}." + KotlinMethodSmartStepTarget.calcLabel(delegatedPropertyGetterDescriptor)
|
||||
result.add(KotlinMethodSmartStepTarget(delegatedPropertyGetterDescriptor, label, expression, lines))
|
||||
}
|
||||
}
|
||||
recordGetter(expression)
|
||||
super.visitSimpleNameExpression(expression)
|
||||
}
|
||||
|
||||
private fun recordGetter(expression: KtSimpleNameExpression) {
|
||||
val resolvedCall = expression.getResolvedCall(bindingContext) ?: return
|
||||
val propertyDescriptor = resolvedCall.resultingDescriptor as? PropertyDescriptor ?: return
|
||||
|
||||
val getterDescriptor = propertyDescriptor.getter
|
||||
if (getterDescriptor == null || getterDescriptor.isDefault) return
|
||||
|
||||
val ktDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, getterDescriptor) as? KtDeclaration ?: return
|
||||
|
||||
val delegatedResolvedCall = bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor]
|
||||
if (delegatedResolvedCall != null) {
|
||||
val delegatedPropertyGetterDescriptor = delegatedResolvedCall.resultingDescriptor
|
||||
val label = "${propertyDescriptor.name}." + KotlinMethodSmartStepTarget.calcLabel(delegatedPropertyGetterDescriptor)
|
||||
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) {
|
||||
val resolvedCall = expression.getResolvedCall(bindingContext) ?: return
|
||||
|
||||
val descriptor = resolvedCall.resultingDescriptor
|
||||
if (descriptor is FunctionDescriptor && !isIntrinsic(descriptor)) {
|
||||
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
|
||||
}
|
||||
}
|
||||
if (descriptor !is FunctionDescriptor || isIntrinsic(descriptor)) 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, label, expression, lines))
|
||||
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor)
|
||||
if (descriptor.isFromJava) {
|
||||
(declaration as? PsiMethod)?.let {
|
||||
result.add(MethodSmartStepTarget(it, null, declaration, false, 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)
|
||||
@@ -216,7 +222,12 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||
override fun createMethodFilter(stepTarget: SmartStepTarget?): MethodFilter? {
|
||||
return when (stepTarget) {
|
||||
is KotlinMethodSmartStepTarget ->
|
||||
KotlinBasicStepMethodFilter(stepTarget.descriptor, stepTarget.callingExpressionLines!!)
|
||||
KotlinBasicStepMethodFilter(
|
||||
stepTarget.declaration?.createSmartPointer(),
|
||||
stepTarget.isInvoke,
|
||||
stepTarget.targetMethodName,
|
||||
stepTarget.callingExpressionLines!!
|
||||
)
|
||||
is KotlinLambdaSmartStepTarget ->
|
||||
KotlinLambdaMethodFilter(
|
||||
stepTarget.getLambda(), stepTarget.callingExpressionLines!!, stepTarget.isInline, stepTarget.isSuspend
|
||||
@@ -231,4 +242,10 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||
private fun isIntrinsic(descriptor: CallableMemberDescriptor): Boolean {
|
||||
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.util.application.runReadAction
|
||||
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.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
|
||||
@@ -246,7 +247,11 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
KotlinLambdaMethodFilter(
|
||||
stepTarget.getLambda(), stepTarget.getCallingExpressionLines()!!, stepTarget.isInline, stepTarget.isSuspend)
|
||||
is KotlinMethodSmartStepTarget ->
|
||||
KotlinBasicStepMethodFilter(stepTarget.descriptor, stepTarget.getCallingExpressionLines()!!)
|
||||
KotlinBasicStepMethodFilter(
|
||||
stepTarget.declaration?.createSmartPointer(),
|
||||
stepTarget.isInvoke,
|
||||
stepTarget.targetMethodName,
|
||||
stepTarget.getCallingExpressionLines()!!)
|
||||
is MethodSmartStepTarget -> BasicStepMethodFilter(stepTarget.method, stepTarget.getCallingExpressionLines())
|
||||
else -> null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user