Debugger: initial support for member extension functions in evaluate expression
This commit is contained in:
@@ -389,9 +389,6 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
val valueParameters = extractedFunction.valueParameters
|
||||
var paramIndex = 0
|
||||
for (param in parameters) {
|
||||
|
||||
if (param.callText.contains(THIS_NAME)) continue
|
||||
|
||||
val valueParameter = valueParameters[paramIndex++]
|
||||
|
||||
val paramRef = valueParameter.typeReference
|
||||
|
||||
+13
-3
@@ -38,15 +38,14 @@ import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiRange
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import java.util.*
|
||||
|
||||
interface Parameter {
|
||||
@@ -95,6 +94,17 @@ class RenameReplacement(override val parameter: Parameter): ParameterReplacement
|
||||
}
|
||||
}
|
||||
|
||||
class WrapInWithReplacement(override val parameter: Parameter): ParameterReplacement {
|
||||
override fun invoke(descriptor: ExtractableCodeDescriptor, e: KtElement): KtElement {
|
||||
val call = e.parents.firstIsInstance<KtCallExpression>().getQualifiedExpressionForSelectorOrThis()
|
||||
val replacingExpression = KtPsiFactory(e).createExpressionByPattern("with($0) { $1 }", parameter.name, call.text)
|
||||
val replace = call.replace(replacingExpression)
|
||||
return (replace as KtCallExpression).functionLiteralArguments.first().getFunctionLiteral().bodyExpression!!.statements.first()
|
||||
}
|
||||
|
||||
override fun copy(parameter: Parameter) = WrapInWithReplacement(parameter)
|
||||
}
|
||||
|
||||
class AddPrefixReplacement(override val parameter: Parameter): ParameterReplacement {
|
||||
override fun copy(parameter: Parameter) = AddPrefixReplacement(parameter)
|
||||
|
||||
|
||||
+1
-1
@@ -695,7 +695,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
||||
val adjustedParameters = paramsInfo.parameters.filterTo(HashSet<Parameter>()) { it.refCount > 0 }
|
||||
|
||||
val receiverCandidates = adjustedParameters.filterTo(HashSet<Parameter>()) { it.receiverCandidate }
|
||||
val receiverParameter = if (receiverCandidates.size == 1) receiverCandidates.first() else null
|
||||
val receiverParameter = if (receiverCandidates.size == 1 && !options.canWrapInWith) receiverCandidates.first() else null
|
||||
receiverParameter?.let { adjustedParameters.remove(it) }
|
||||
|
||||
return AnalysisResult(
|
||||
|
||||
+15
-1
@@ -84,6 +84,11 @@ internal fun ExtractionData.inferParametersInfo(
|
||||
extractReceiver(receiverToExtract, ref, project, originalElements,
|
||||
originalDescriptor, info, options, targetScope, refInfo,
|
||||
originalDeclaration, originalRef, extractedDescriptorToParameter, resolvedCall, pseudocode, codeFragmentText, bindingContext, false)
|
||||
if (options.canWrapInWith && resolvedCall != null && isMemberExtensionFunction(resolvedCall, ref)) {
|
||||
extractReceiver(resolvedCall.dispatchReceiver, ref, project, originalElements,
|
||||
originalDescriptor, info, options, targetScope, refInfo,
|
||||
originalDeclaration, originalRef, extractedDescriptorToParameter, resolvedCall, pseudocode, codeFragmentText, bindingContext, true)
|
||||
}
|
||||
}
|
||||
|
||||
val varNameValidator = NewDeclarationNameValidator(
|
||||
@@ -159,7 +164,7 @@ private fun extractReceiver(
|
||||
} as? ClassifierDescriptor
|
||||
}
|
||||
|
||||
if (referencedClassifierDescriptor != null) {
|
||||
if (referencedClassifierDescriptor != null && !(isMemberExtensionFunction && options.canWrapInWith)) {
|
||||
if (!referencedClassifierDescriptor.defaultType.processTypeIfExtractable(
|
||||
info.typeParameters, info.nonDenotableTypes, options, targetScope, referencedClassifierDescriptor is TypeParameterDescriptor
|
||||
)) return
|
||||
@@ -253,6 +258,7 @@ private fun extractReceiver(
|
||||
|
||||
info.replacementMap.putValue(refInfo.offsetInBody,
|
||||
when {
|
||||
isMemberExtensionFunction -> WrapInWithReplacement(parameter)
|
||||
hasThisReceiver && extractThis -> AddPrefixReplacement(parameter)
|
||||
else -> RenameReplacement(parameter)
|
||||
})
|
||||
@@ -297,4 +303,12 @@ private fun suggestParameterType(
|
||||
receiverToExtract.exists() -> receiverToExtract.type
|
||||
else -> null
|
||||
} ?: builtIns.defaultParameterType
|
||||
}
|
||||
|
||||
private fun isMemberExtensionFunction(resolvedCall: ResolvedCall<*>, ref: KtSimpleNameExpression): Boolean {
|
||||
// TODO temporary hack because we couldn't correctly extract member extension function with two explicit receivers
|
||||
if (ref.parent !is KtCallExpression || ref.parent.parent !is KtQualifiedExpression) return false
|
||||
|
||||
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||
return resultingDescriptor is FunctionDescriptor && resolvedCall.extensionReceiver != ReceiverValue.NO_RECEIVER && resolvedCall.dispatchReceiver != ReceiverValue.NO_RECEIVER
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
LineBreakpoint created at extensionMemberFunction.kt:13
|
||||
LineBreakpoint created at extensionMemberFunction.kt:19
|
||||
LineBreakpoint created at extensionMemberFunction.kt:25
|
||||
LineBreakpoint created at extensionMemberFunction.kt:31
|
||||
LineBreakpoint created at extensionMemberFunction.kt:41
|
||||
LineBreakpoint created at extensionMemberFunction.kt:46
|
||||
LineBreakpoint created at extensionMemberFunction.kt:51
|
||||
LineBreakpoint created at extensionMemberFunction.kt:56
|
||||
LineBreakpoint created at extensionMemberFunction.kt:70
|
||||
LineBreakpoint created at extensionMemberFunction.kt:76
|
||||
LineBreakpoint created at extensionMemberFunction.kt:82
|
||||
LineBreakpoint created at extensionMemberFunction.kt:88
|
||||
LineBreakpoint created at extensionMemberFunction.kt:98
|
||||
LineBreakpoint created at extensionMemberFunction.kt:103
|
||||
LineBreakpoint created at extensionMemberFunction.kt:108
|
||||
LineBreakpoint created at extensionMemberFunction.kt:113
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! extensionMemberFunction.ExtensionMemberFunctionKt
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
extensionMemberFunction.kt:13
|
||||
Compile bytecode for extClass.testPublic()
|
||||
extensionMemberFunction.kt:19
|
||||
extensionMemberFunction.kt:25
|
||||
Compile bytecode for extClass.testPrivate()
|
||||
extensionMemberFunction.kt:31
|
||||
extensionMemberFunction.kt:41
|
||||
extensionMemberFunction.kt:46
|
||||
extensionMemberFunction.kt:51
|
||||
extensionMemberFunction.kt:56
|
||||
extensionMemberFunction.kt:70
|
||||
Compile bytecode for extClass.testCompPublic()
|
||||
extensionMemberFunction.kt:76
|
||||
extensionMemberFunction.kt:82
|
||||
Compile bytecode for extClass.testCompPrivate()
|
||||
extensionMemberFunction.kt:88
|
||||
extensionMemberFunction.kt:98
|
||||
extensionMemberFunction.kt:103
|
||||
extensionMemberFunction.kt:108
|
||||
extensionMemberFunction.kt:113
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package extensionMemberFunction
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
MemberClass().testMember(ExtClass())
|
||||
MemberClass.testCompanion(ExtClass())
|
||||
}
|
||||
|
||||
class MemberClass {
|
||||
fun testMember(extClass: ExtClass) {
|
||||
// EXPRESSION: extClass.testPublic()
|
||||
// RESULT: 1: I
|
||||
//Breakpoint!
|
||||
extClass.testPublic()
|
||||
|
||||
with(extClass) {
|
||||
// EXPRESSION: testPublic()
|
||||
// RESULT: Following declarations are unavailable in debug scope: testPublic
|
||||
//Breakpoint!
|
||||
testPublic()
|
||||
}
|
||||
|
||||
// EXPRESSION: extClass.testPrivate()
|
||||
// RESULT: 1: I
|
||||
//Breakpoint!
|
||||
extClass.testPrivate()
|
||||
|
||||
with(extClass) {
|
||||
// EXPRESSION: testPrivate()
|
||||
// RESULT: Following declarations are unavailable in debug scope: testPrivate
|
||||
//Breakpoint!
|
||||
testPrivate()
|
||||
}
|
||||
|
||||
extClass.testExtMember()
|
||||
}
|
||||
|
||||
fun ExtClass.testExtMember() {
|
||||
// EXPRESSION: testPublic()
|
||||
// RESULT: Following declarations are unavailable in debug scope: testPublic
|
||||
//Breakpoint!
|
||||
testPublic()
|
||||
|
||||
// EXPRESSION: this.testPublic()
|
||||
// RESULT: A receiver of type extensionMemberFunction.ExtClass is required
|
||||
//Breakpoint!
|
||||
this.testPublic()
|
||||
|
||||
// EXPRESSION: testPrivate()
|
||||
// RESULT: Following declarations are unavailable in debug scope: testPrivate
|
||||
//Breakpoint!
|
||||
testPrivate()
|
||||
|
||||
// EXPRESSION: this.testPrivate()
|
||||
// RESULT: A receiver of type extensionMemberFunction.ExtClass is required
|
||||
//Breakpoint!
|
||||
this.testPrivate()
|
||||
}
|
||||
|
||||
public fun ExtClass.testPublic() = a
|
||||
private fun ExtClass.testPrivate() = a
|
||||
|
||||
companion object {
|
||||
public fun ExtClass.testCompPublic() = a
|
||||
private fun ExtClass.testCompPrivate() = a
|
||||
|
||||
fun testCompanion(extClass: ExtClass) {
|
||||
// EXPRESSION: extClass.testCompPublic()
|
||||
// RESULT: 1: I
|
||||
//Breakpoint!
|
||||
extClass.testCompPublic()
|
||||
|
||||
with(extClass) {
|
||||
// EXPRESSION: testCompPublic()
|
||||
// RESULT: Following declarations are unavailable in debug scope: testCompPublic
|
||||
//Breakpoint!
|
||||
testCompPublic()
|
||||
}
|
||||
|
||||
// EXPRESSION: extClass.testCompPrivate()
|
||||
// RESULT: 1: I
|
||||
//Breakpoint!
|
||||
extClass.testCompPrivate()
|
||||
|
||||
with(extClass) {
|
||||
// EXPRESSION: testCompPrivate()
|
||||
// RESULT: Following declarations are unavailable in debug scope: testCompPrivate
|
||||
//Breakpoint!
|
||||
testCompPrivate()
|
||||
}
|
||||
|
||||
extClass.testExtCompanion()
|
||||
}
|
||||
|
||||
fun ExtClass.testExtCompanion() {
|
||||
// EXPRESSION: testCompPublic()
|
||||
// RESULT: Following declarations are unavailable in debug scope: testCompPublic
|
||||
//Breakpoint!
|
||||
testCompPublic()
|
||||
|
||||
// EXPRESSION: this.testCompPublic()
|
||||
// RESULT: A receiver of type extensionMemberFunction.ExtClass is required
|
||||
//Breakpoint!
|
||||
this.testCompPublic()
|
||||
|
||||
// EXPRESSION: testCompPrivate()
|
||||
// RESULT: Following declarations are unavailable in debug scope: testCompPrivate
|
||||
//Breakpoint!
|
||||
testCompPrivate()
|
||||
|
||||
// EXPRESSION: this.testCompPrivate()
|
||||
// RESULT: A receiver of type extensionMemberFunction.ExtClass is required
|
||||
//Breakpoint!
|
||||
this.testCompPrivate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExtClass {
|
||||
val a = 1
|
||||
}
|
||||
+6
@@ -676,6 +676,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
doMultipleBreakpointsTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extensionMemberFunction.kt")
|
||||
public void testExtensionMemberFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/extensionMemberFunction.kt");
|
||||
doMultipleBreakpointsTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("funFromOuterClassInLamdba.kt")
|
||||
public void testFunFromOuterClassInLamdba() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/funFromOuterClassInLamdba.kt");
|
||||
|
||||
Reference in New Issue
Block a user