Debugger: Extract Smart step visitor
This commit is contained in:
+26
-222
@@ -1,251 +1,55 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
*
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
* 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.kotlin.idea.debugger.stepping
|
package org.jetbrains.kotlin.idea.debugger.stepping.smartStepInto
|
||||||
|
|
||||||
import com.intellij.debugger.SourcePosition
|
import com.intellij.debugger.SourcePosition
|
||||||
import com.intellij.debugger.actions.JvmSmartStepIntoHandler
|
import com.intellij.debugger.actions.JvmSmartStepIntoHandler
|
||||||
import com.intellij.debugger.actions.MethodSmartStepTarget
|
|
||||||
import com.intellij.debugger.actions.SmartStepTarget
|
import com.intellij.debugger.actions.SmartStepTarget
|
||||||
import com.intellij.debugger.engine.MethodFilter
|
import com.intellij.debugger.engine.MethodFilter
|
||||||
import com.intellij.psi.PsiDocumentManager
|
import com.intellij.psi.PsiDocumentManager
|
||||||
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.idea.core.util.CodeInsightUtils.getTopmostElementAtOffset
|
||||||
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
|
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinBasicStepMethodFilter
|
||||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinLambdaMethodFilter
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinLambdaSmartStepTarget
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinMethodSmartStepTarget
|
||||||
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.DescriptorToSourceUtilsIde
|
|
||||||
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
|
||||||
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.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
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
|
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
|
||||||
|
|
||||||
class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
|
||||||
|
|
||||||
override fun isAvailable(position: SourcePosition?) = position?.file is KtFile
|
override fun isAvailable(position: SourcePosition?) = position?.file is KtFile
|
||||||
|
|
||||||
override fun findSmartStepTargets(position: SourcePosition): List<SmartStepTarget> {
|
override fun findSmartStepTargets(position: SourcePosition): List<SmartStepTarget> {
|
||||||
val file = position.file
|
val file = position.file
|
||||||
|
val element = position.elementAt ?: return emptyList()
|
||||||
|
val ktElement = getTopmostElementAtOffset(element, element.textRange.startOffset) as? KtElement
|
||||||
|
val elementTextRange = ktElement?.textRange ?: return emptyList()
|
||||||
|
val document = PsiDocumentManager.getInstance(file.project).getDocument(file) ?: return emptyList()
|
||||||
|
val lines = Range(document.getLineNumber(elementTextRange.startOffset), document.getLineNumber(elementTextRange.endOffset))
|
||||||
|
|
||||||
val elementAtOffset = position.elementAt ?: return emptyList()
|
val consumer = OrderedSet<SmartStepTarget>()
|
||||||
|
val visitor = SmartStepTargetVisitor(ktElement, lines, consumer)
|
||||||
|
ktElement.accept(visitor, null)
|
||||||
|
|
||||||
val element = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, elementAtOffset.textRange.startOffset) as? KtElement
|
return consumer
|
||||||
?: return emptyList()
|
|
||||||
|
|
||||||
val elementTextRange = element.textRange ?: return emptyList()
|
|
||||||
|
|
||||||
val doc = PsiDocumentManager.getInstance(file.project).getDocument(file) ?: return emptyList()
|
|
||||||
|
|
||||||
val lines = Range(doc.getLineNumber(elementTextRange.startOffset), doc.getLineNumber(elementTextRange.endOffset))
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
val bindingContext = element.analyzeWithAllCompilerChecks().bindingContext
|
|
||||||
val result = OrderedSet<SmartStepTarget>()
|
|
||||||
|
|
||||||
// TODO support class initializers, local functions, delegated properties with specified type, setter for properties
|
|
||||||
element.accept(object : KtTreeVisitorVoid() {
|
|
||||||
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
|
|
||||||
recordFunctionLiteral(lambdaExpression.functionLiteral)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitNamedFunction(function: KtNamedFunction) {
|
|
||||||
if (!recordFunctionLiteral(function)) {
|
|
||||||
super.visitNamedFunction(function)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun recordFunctionLiteral(function: KtFunction): Boolean {
|
|
||||||
val context = function.analyze()
|
|
||||||
val resolvedCall = function.getParentCall(context).getResolvedCall(context)
|
|
||||||
if (resolvedCall != null) {
|
|
||||||
val arguments = resolvedCall.valueArguments
|
|
||||||
for ((param, argument) in arguments) {
|
|
||||||
if (argument.arguments.any { getArgumentExpression(it) == function }) {
|
|
||||||
val resultingDescriptor = resolvedCall.resultingDescriptor
|
|
||||||
val label = KotlinLambdaSmartStepTarget.calcLabel(resultingDescriptor, param.name)
|
|
||||||
result.add(
|
|
||||||
KotlinLambdaSmartStepTarget(
|
|
||||||
label, function, lines, InlineUtil.isInline(resultingDescriptor), param.type.isSuspendFunctionType
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getArgumentExpression(it: ValueArgument) =
|
|
||||||
(it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
|
|
||||||
|
|
||||||
override fun visitObjectLiteralExpression(expression: KtObjectLiteralExpression) {
|
|
||||||
// skip calls in object declarations
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitIfExpression(expression: KtIfExpression) {
|
|
||||||
expression.condition?.accept(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitWhileExpression(expression: KtWhileExpression) {
|
|
||||||
expression.condition?.accept(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitDoWhileExpression(expression: KtDoWhileExpression) {
|
|
||||||
expression.condition?.accept(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitForExpression(expression: KtForExpression) {
|
|
||||||
expression.loopRange?.accept(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitWhenExpression(expression: KtWhenExpression) {
|
|
||||||
expression.subjectExpression?.accept(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitArrayAccessExpression(expression: KtArrayAccessExpression) {
|
|
||||||
recordFunction(expression)
|
|
||||||
super.visitArrayAccessExpression(expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitUnaryExpression(expression: KtUnaryExpression) {
|
|
||||||
recordFunction(expression.operationReference)
|
|
||||||
super.visitUnaryExpression(expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitBinaryExpression(expression: KtBinaryExpression) {
|
|
||||||
recordFunction(expression.operationReference)
|
|
||||||
super.visitBinaryExpression(expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitCallExpression(expression: KtCallExpression) {
|
|
||||||
val calleeExpression = expression.calleeExpression
|
|
||||||
if (calleeExpression != null) {
|
|
||||||
recordFunction(calleeExpression)
|
|
||||||
}
|
|
||||||
super.visitCallExpression(expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun recordFunction(expression: KtExpression) {
|
|
||||||
val resolvedCall = expression.getResolvedCall(bindingContext) ?: return
|
|
||||||
|
|
||||||
val descriptor = resolvedCall.resultingDescriptor
|
|
||||||
if (descriptor !is FunctionDescriptor || isIntrinsic(descriptor)) return
|
|
||||||
|
|
||||||
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor)
|
|
||||||
if (descriptor.isFromJava) {
|
|
||||||
if (declaration is PsiMethod) {
|
|
||||||
result.add(MethodSmartStepTarget(declaration, null, expression, 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)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createMethodFilter(stepTarget: SmartStepTarget?): MethodFilter? {
|
override fun createMethodFilter(stepTarget: SmartStepTarget?): MethodFilter? {
|
||||||
return when (stepTarget) {
|
return when (stepTarget) {
|
||||||
is KotlinMethodSmartStepTarget ->
|
is KotlinMethodSmartStepTarget -> {
|
||||||
KotlinBasicStepMethodFilter(
|
val declarationPtr = stepTarget.declaration?.createSmartPointer()
|
||||||
stepTarget.declaration?.createSmartPointer(),
|
val lines = stepTarget.callingExpressionLines ?: return null
|
||||||
stepTarget.isInvoke,
|
KotlinBasicStepMethodFilter(declarationPtr, stepTarget.isInvoke, stepTarget.targetMethodName, lines)
|
||||||
stepTarget.targetMethodName,
|
}
|
||||||
stepTarget.callingExpressionLines!!
|
is KotlinLambdaSmartStepTarget -> {
|
||||||
)
|
val lines = stepTarget.callingExpressionLines ?: return null
|
||||||
is KotlinLambdaSmartStepTarget ->
|
KotlinLambdaMethodFilter(stepTarget.getLambda(), lines, stepTarget.isInline, stepTarget.isSuspend)
|
||||||
KotlinLambdaMethodFilter(
|
}
|
||||||
stepTarget.getLambda(), stepTarget.callingExpressionLines!!, stepTarget.isInline, stepTarget.isSuspend
|
|
||||||
)
|
|
||||||
else -> super.createMethodFilter(stepTarget)
|
else -> super.createMethodFilter(stepTarget)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val methods = IntrinsicMethods(JvmTarget.JVM_1_6)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+208
@@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.debugger.stepping.smartStepInto
|
||||||
|
|
||||||
|
import com.intellij.debugger.actions.MethodSmartStepTarget
|
||||||
|
import com.intellij.debugger.actions.SmartStepTarget
|
||||||
|
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.*
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinLambdaSmartStepTarget
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinMethodSmartStepTarget
|
||||||
|
import org.jetbrains.kotlin.idea.project.platform
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.load.java.isFromJava
|
||||||
|
import org.jetbrains.kotlin.platform.jvm.JdkPlatform
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
|
|
||||||
|
// TODO support class initializers, local functions, delegated properties with specified type, setter for properties
|
||||||
|
class SmartStepTargetVisitor(
|
||||||
|
private val element: KtElement,
|
||||||
|
private val lines: Range<Int>,
|
||||||
|
private val consumer: OrderedSet<SmartStepTarget>
|
||||||
|
) : KtTreeVisitorVoid() {
|
||||||
|
private fun append(target: SmartStepTarget) {
|
||||||
|
consumer += target
|
||||||
|
}
|
||||||
|
|
||||||
|
private val intrinsicMethods = run {
|
||||||
|
val jvmTarget = element.platform.firstIsInstanceOrNull<JdkPlatform>()?.targetVersion ?: JvmTarget.DEFAULT
|
||||||
|
IntrinsicMethods(jvmTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
|
||||||
|
recordFunction(lambdaExpression.functionLiteral)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitNamedFunction(function: KtNamedFunction) {
|
||||||
|
if (!recordFunction(function)) {
|
||||||
|
super.visitNamedFunction(function)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recordFunction(function: KtFunction): Boolean {
|
||||||
|
val context = function.analyze()
|
||||||
|
val resolvedCall = function.getParentCall(context).getResolvedCall(context) ?: return false
|
||||||
|
val arguments = resolvedCall.valueArguments
|
||||||
|
|
||||||
|
for ((param, argument) in arguments) {
|
||||||
|
if (argument.arguments.any { getArgumentExpression(it) == function }) {
|
||||||
|
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||||
|
val label = KotlinLambdaSmartStepTarget.calcLabel(resultingDescriptor, param.name)
|
||||||
|
val isInline = InlineUtil.isInline(resultingDescriptor)
|
||||||
|
val isSuspend = param.type.isSuspendFunctionType
|
||||||
|
val target = KotlinLambdaSmartStepTarget(label, function, lines, isInline, isSuspend)
|
||||||
|
append(target)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getArgumentExpression(it: ValueArgument): KtExpression? {
|
||||||
|
return (it.getArgumentExpression() as? KtLambdaExpression)?.functionLiteral ?: it.getArgumentExpression()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitObjectLiteralExpression(expression: KtObjectLiteralExpression) {
|
||||||
|
// Skip calls in object declarations
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitIfExpression(expression: KtIfExpression) {
|
||||||
|
expression.condition?.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitWhileExpression(expression: KtWhileExpression) {
|
||||||
|
expression.condition?.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitDoWhileExpression(expression: KtDoWhileExpression) {
|
||||||
|
expression.condition?.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitForExpression(expression: KtForExpression) {
|
||||||
|
expression.loopRange?.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitWhenExpression(expression: KtWhenExpression) {
|
||||||
|
expression.subjectExpression?.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitArrayAccessExpression(expression: KtArrayAccessExpression) {
|
||||||
|
recordFunctionCall(expression)
|
||||||
|
super.visitArrayAccessExpression(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitUnaryExpression(expression: KtUnaryExpression) {
|
||||||
|
recordFunctionCall(expression.operationReference)
|
||||||
|
super.visitUnaryExpression(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitBinaryExpression(expression: KtBinaryExpression) {
|
||||||
|
recordFunctionCall(expression.operationReference)
|
||||||
|
super.visitBinaryExpression(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCallExpression(expression: KtCallExpression) {
|
||||||
|
val calleeExpression = expression.calleeExpression
|
||||||
|
if (calleeExpression != null) {
|
||||||
|
recordFunctionCall(calleeExpression)
|
||||||
|
}
|
||||||
|
super.visitCallExpression(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||||
|
recordGetter(expression)
|
||||||
|
super.visitSimpleNameExpression(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recordGetter(expression: KtSimpleNameExpression) {
|
||||||
|
val bindingContext = expression.analyze()
|
||||||
|
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(element.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)
|
||||||
|
append(KotlinMethodSmartStepTarget(delegatedPropertyGetterDescriptor, ktDeclaration, label, expression, lines))
|
||||||
|
} else {
|
||||||
|
if (ktDeclaration is KtPropertyAccessor && ktDeclaration.hasBody()) {
|
||||||
|
val label = KotlinMethodSmartStepTarget.calcLabel(getterDescriptor)
|
||||||
|
append(KotlinMethodSmartStepTarget(getterDescriptor, ktDeclaration, label, expression, lines))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recordFunctionCall(expression: KtExpression) {
|
||||||
|
val resolvedCall = expression.resolveToCall() ?: return
|
||||||
|
|
||||||
|
val descriptor = resolvedCall.resultingDescriptor
|
||||||
|
if (descriptor !is FunctionDescriptor || isIntrinsic(descriptor)) return
|
||||||
|
|
||||||
|
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(element.project, descriptor)
|
||||||
|
if (descriptor.isFromJava) {
|
||||||
|
if (declaration is PsiMethod) {
|
||||||
|
append(MethodSmartStepTarget(declaration, null, expression, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
append(KotlinMethodSmartStepTarget(descriptor, declaration, label, expression, lines))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isIntrinsic(descriptor: CallableMemberDescriptor): Boolean {
|
||||||
|
return intrinsicMethods.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
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.idea.debugger.test
|
|||||||
|
|
||||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||||
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinSmartStepIntoHandler
|
import org.jetbrains.kotlin.idea.debugger.stepping.smartStepInto.KotlinSmartStepIntoHandler
|
||||||
import org.jetbrains.kotlin.idea.debugger.test.mock.MockSourcePosition
|
import org.jetbrains.kotlin.idea.debugger.test.mock.MockSourcePosition
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||||
|
|||||||
+1
@@ -14,6 +14,7 @@ import com.intellij.debugger.impl.PositionUtil
|
|||||||
import com.intellij.execution.process.ProcessOutputTypes
|
import com.intellij.execution.process.ProcessOutputTypes
|
||||||
import com.sun.jdi.request.StepRequest
|
import com.sun.jdi.request.StepRequest
|
||||||
import org.jetbrains.kotlin.idea.debugger.stepping.*
|
import org.jetbrains.kotlin.idea.debugger.stepping.*
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.stepping.smartStepInto.KotlinSmartStepIntoHandler
|
||||||
import org.jetbrains.kotlin.idea.debugger.test.util.SteppingInstruction
|
import org.jetbrains.kotlin.idea.debugger.test.util.SteppingInstruction
|
||||||
import org.jetbrains.kotlin.idea.debugger.test.util.SteppingInstructionKind
|
import org.jetbrains.kotlin.idea.debugger.test.util.SteppingInstructionKind
|
||||||
import org.jetbrains.kotlin.idea.debugger.test.util.renderSourcePosition
|
import org.jetbrains.kotlin.idea.debugger.test.util.renderSourcePosition
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
<applicationService serviceImplementation="org.jetbrains.kotlin.idea.debugger.ToggleKotlinVariablesState"/>
|
<applicationService serviceImplementation="org.jetbrains.kotlin.idea.debugger.ToggleKotlinVariablesState"/>
|
||||||
|
|
||||||
<debugger.asyncStackTraceProvider implementation="org.jetbrains.kotlin.idea.debugger.KotlinCoroutinesAsyncStackTraceProvider"/>
|
<debugger.asyncStackTraceProvider implementation="org.jetbrains.kotlin.idea.debugger.KotlinCoroutinesAsyncStackTraceProvider"/>
|
||||||
<debugger.jvmSmartStepIntoHandler implementation="org.jetbrains.kotlin.idea.debugger.stepping.KotlinSmartStepIntoHandler"/>
|
<debugger.jvmSmartStepIntoHandler implementation="org.jetbrains.kotlin.idea.debugger.stepping.smartStepInto.KotlinSmartStepIntoHandler"/>
|
||||||
<debugger.positionManagerFactory implementation="org.jetbrains.kotlin.idea.debugger.KotlinPositionManagerFactory"/>
|
<debugger.positionManagerFactory implementation="org.jetbrains.kotlin.idea.debugger.KotlinPositionManagerFactory"/>
|
||||||
<debugger.codeFragmentFactory implementation="org.jetbrains.kotlin.idea.debugger.evaluate.KotlinCodeFragmentFactory"/>
|
<debugger.codeFragmentFactory implementation="org.jetbrains.kotlin.idea.debugger.evaluate.KotlinCodeFragmentFactory"/>
|
||||||
<debuggerEditorTextProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.debugger.KotlinEditorTextProvider"/>
|
<debuggerEditorTextProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.debugger.KotlinEditorTextProvider"/>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
<applicationService serviceImplementation="org.jetbrains.kotlin.idea.debugger.ToggleKotlinVariablesState"/>
|
<applicationService serviceImplementation="org.jetbrains.kotlin.idea.debugger.ToggleKotlinVariablesState"/>
|
||||||
|
|
||||||
<debugger.asyncStackTraceProvider implementation="org.jetbrains.kotlin.idea.debugger.KotlinCoroutinesAsyncStackTraceProvider"/>
|
<debugger.asyncStackTraceProvider implementation="org.jetbrains.kotlin.idea.debugger.KotlinCoroutinesAsyncStackTraceProvider"/>
|
||||||
<debugger.jvmSmartStepIntoHandler implementation="org.jetbrains.kotlin.idea.debugger.stepping.KotlinSmartStepIntoHandler"/>
|
<debugger.jvmSmartStepIntoHandler implementation="org.jetbrains.kotlin.idea.debugger.stepping.smartStepInto.KotlinSmartStepIntoHandler"/>
|
||||||
<debugger.positionManagerFactory implementation="org.jetbrains.kotlin.idea.debugger.KotlinPositionManagerFactory"/>
|
<debugger.positionManagerFactory implementation="org.jetbrains.kotlin.idea.debugger.KotlinPositionManagerFactory"/>
|
||||||
<debugger.codeFragmentFactory implementation="org.jetbrains.kotlin.idea.debugger.evaluate.KotlinCodeFragmentFactory"/>
|
<debugger.codeFragmentFactory implementation="org.jetbrains.kotlin.idea.debugger.evaluate.KotlinCodeFragmentFactory"/>
|
||||||
<debuggerEditorTextProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.debugger.KotlinEditorTextProvider"/>
|
<debuggerEditorTextProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.debugger.KotlinEditorTextProvider"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user