Evaluate expression: support EE on method with expression body, on properties
#KT-5485 Fixed
This commit is contained in:
+57
-11
@@ -16,9 +16,7 @@
|
||||
|
||||
package org.jetbrains.jet.plugin.debugger.evaluate
|
||||
|
||||
import org.jetbrains.jet.lang.psi.JetCodeFragment
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.jet.lang.psi.JetNamedFunction
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.ErrorMessage
|
||||
import org.jetbrains.jet.lang.psi.JetFile
|
||||
@@ -33,11 +31,13 @@ import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Statu
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.validate
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.generateFunction
|
||||
import org.jetbrains.jet.lang.psi.JetImportList
|
||||
import org.jetbrains.jet.lang.psi.JetPsiFactory
|
||||
import org.jetbrains.jet.lang.psi.JetExpression
|
||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionOptions
|
||||
import org.jetbrains.jet.plugin.refactoring.runReadAction
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.PsiModificationTrackerImpl
|
||||
import org.jetbrains.jet.lang.psi.*
|
||||
import org.jetbrains.jet.plugin.intentions.InsertExplicitTypeArguments
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
|
||||
fun getFunctionForExtractedFragment(
|
||||
codeFragment: JetCodeFragment,
|
||||
@@ -122,19 +122,65 @@ private fun addImportsToFile(newImportList: JetImportList?, tmpFile: JetFile) {
|
||||
}
|
||||
|
||||
private fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment, contextElement: PsiElement): JetExpression? {
|
||||
val parent = contextElement.getParent()
|
||||
if (parent == null) return null
|
||||
|
||||
val psiFactory = JetPsiFactory(codeFragment)
|
||||
parent.addBefore(psiFactory.createNewLine(), contextElement)
|
||||
|
||||
val elementBefore = when {
|
||||
contextElement is JetProperty && !contextElement.isLocal() -> {
|
||||
wrapInRunFun(contextElement.getDelegateExpressionOrInitializer()!!)
|
||||
}
|
||||
contextElement is JetDeclarationWithBody && !contextElement.hasBlockBody()-> {
|
||||
wrapInRunFun(contextElement.getBodyExpression()!!)
|
||||
}
|
||||
contextElement is JetDeclarationWithBody && contextElement.hasBlockBody()-> {
|
||||
val block = contextElement.getBodyExpression() as JetBlockExpression
|
||||
block.getStatements().first ?: block.getLastChild()
|
||||
}
|
||||
contextElement is JetWhenEntry -> {
|
||||
val entryExpression = contextElement.getExpression()
|
||||
if (entryExpression is JetBlockExpression) {
|
||||
entryExpression.getStatements().first ?: entryExpression.getLastChild()
|
||||
}
|
||||
else {
|
||||
wrapInRunFun(entryExpression!!)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
contextElement
|
||||
}
|
||||
}
|
||||
|
||||
val parent = elementBefore?.getParent()
|
||||
if (parent == null || elementBefore == null) return null
|
||||
|
||||
parent.addBefore(psiFactory.createNewLine(), elementBefore)
|
||||
|
||||
val debugExpression = codeFragment.getContentElement()
|
||||
if (debugExpression == null) return null
|
||||
|
||||
val newDebugExpression = parent.addBefore(debugExpression, contextElement)
|
||||
val newDebugExpression = parent.addBefore(debugExpression, elementBefore)
|
||||
if (newDebugExpression == null) return null
|
||||
|
||||
parent.addBefore(psiFactory.createNewLine(), contextElement)
|
||||
parent.addBefore(psiFactory.createNewLine(), elementBefore)
|
||||
|
||||
return newDebugExpression as JetExpression
|
||||
}
|
||||
|
||||
private fun createRunFunction(body: JetExpression): JetCallExpression {
|
||||
val callExpression = JetPsiFactory(body).createExpression("run { \n${body.getText()} \n}") as JetCallExpression
|
||||
val typeArguments = InsertExplicitTypeArguments.createTypeArguments(callExpression)
|
||||
if (typeArguments?.getArguments()?.isNotEmpty() ?: false) {
|
||||
val calleeExpression = callExpression.getCalleeExpression()
|
||||
callExpression.addAfter(typeArguments!!, calleeExpression)
|
||||
}
|
||||
return callExpression
|
||||
}
|
||||
|
||||
private fun wrapInRunFun(expression: JetExpression): PsiElement? {
|
||||
val newBody = createRunFunction(expression)
|
||||
val replacedBody = (expression.replace(newBody) as JetCallExpression)
|
||||
|
||||
// Increment modification tracker to clear ResolveCache after changes in function body
|
||||
(PsiManager.getInstance(expression.getProject()).getModificationTracker() as PsiModificationTrackerImpl).incCounter()
|
||||
|
||||
return replacedBody.getFunctionLiteralArguments().first().getFunctionLiteral().getBodyExpression()?.getFirstChild()
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.types.ErrorUtils
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer
|
||||
import org.jetbrains.jet.plugin.codeInsight.ShortenReferences
|
||||
import org.jetbrains.jet.lang.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.jet.lang.psi.JetTypeArgumentList
|
||||
|
||||
public class InsertExplicitTypeArguments : JetSelfTargetingIntention<JetCallExpression>(
|
||||
"insert.explicit.type.arguments", javaClass()) {
|
||||
@@ -48,23 +49,32 @@ public class InsertExplicitTypeArguments : JetSelfTargetingIntention<JetCallExpr
|
||||
}
|
||||
|
||||
override fun applyTo(element: JetCallExpression, editor: Editor) {
|
||||
val context = AnalyzerFacadeWithCache.getContextForElement(element)
|
||||
val resolvedCall = element.getResolvedCall(context)
|
||||
if (resolvedCall == null) return
|
||||
|
||||
val args = resolvedCall.getTypeArguments()
|
||||
val types = resolvedCall.getCandidateDescriptor().getTypeParameters()
|
||||
|
||||
val psiFactory = JetPsiFactory(element)
|
||||
val typeArgs = types.map {
|
||||
assert(args[it] != null, "there is a null in the type arguments to transform")
|
||||
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(args[it]!!)
|
||||
}.joinToString(", ", "<", ">")
|
||||
val argumentList = createTypeArguments(element)
|
||||
if (argumentList == null) return
|
||||
|
||||
val callee = element.getCalleeExpression()
|
||||
if (callee == null) return
|
||||
|
||||
element.addAfter(psiFactory.createTypeArguments(typeArgs), callee)
|
||||
element.addAfter(argumentList, callee)
|
||||
ShortenReferences.process(element.getTypeArgumentList()!!)
|
||||
}
|
||||
|
||||
class object {
|
||||
fun createTypeArguments(element: JetCallExpression): JetTypeArgumentList? {
|
||||
val context = AnalyzerFacadeWithCache.getContextForElement(element)
|
||||
val resolvedCall = element.getResolvedCall(context)
|
||||
if (resolvedCall == null) return null
|
||||
|
||||
val args = resolvedCall.getTypeArguments()
|
||||
val types = resolvedCall.getCandidateDescriptor().getTypeParameters()
|
||||
|
||||
val psiFactory = JetPsiFactory(element)
|
||||
val typeArgs = types.map {
|
||||
assert(args[it] != null, "there is a null in the type arguments to transform")
|
||||
DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(args[it]!!)
|
||||
}.joinToString(", ", "<", ">")
|
||||
|
||||
return psiFactory.createTypeArguments(typeArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
LineBreakpoint created at whenEntry.kt:10
|
||||
LineBreakpoint created at whenEntry.kt:18
|
||||
LineBreakpoint created at whenEntry.kt:26
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! whenEntry.WhenEntryPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
whenEntry.kt:9
|
||||
Compile bytecode for a
|
||||
whenEntry.kt:17
|
||||
Compile bytecode for a
|
||||
whenEntry.kt:25
|
||||
Compile bytecode for a
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,21 @@
|
||||
LineBreakpoint created at withoutBodyFunctions.kt:9
|
||||
LineBreakpoint created at withoutBodyFunctions.kt:15
|
||||
LineBreakpoint created at withoutBodyFunctions.kt:21
|
||||
LineBreakpoint created at withoutBodyFunctions.kt:27
|
||||
LineBreakpoint created at withoutBodyFunctions.kt:32
|
||||
LineBreakpoint created at withoutBodyFunctions.kt:36
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! withoutBodyFunctions.WithoutBodyFunctionsPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
withoutBodyFunctions.kt:8
|
||||
Compile bytecode for 1 + 1
|
||||
withoutBodyFunctions.kt:14
|
||||
Compile bytecode for 1 + 2
|
||||
withoutBodyFunctions.kt:20
|
||||
Compile bytecode for i
|
||||
withoutBodyFunctions.kt:26
|
||||
Compile bytecode for i
|
||||
withoutBodyFunctions.kt:31
|
||||
Compile bytecode for i
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,21 @@
|
||||
LineBreakpoint created at withoutBodyProperties.kt:8
|
||||
LineBreakpoint created at withoutBodyProperties.kt:13
|
||||
LineBreakpoint created at withoutBodyProperties.kt:18
|
||||
LineBreakpoint created at withoutBodyProperties.kt:29
|
||||
LineBreakpoint created at withoutBodyProperties.kt:36
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! withoutBodyProperties.WithoutBodyPropertiesPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
withoutBodyProperties.kt:7
|
||||
Compile bytecode for 1 + 1
|
||||
withoutBodyProperties.kt:12
|
||||
Compile bytecode for 1 + 2
|
||||
withoutBodyProperties.kt:17
|
||||
Compile bytecode for 1 + 3
|
||||
withoutBodyProperties.kt:28
|
||||
Compile bytecode for i
|
||||
withoutBodyProperties.kt:28
|
||||
withoutBodyProperties.kt:35
|
||||
Compile bytecode for 1 + 4
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,8 @@
|
||||
LineBreakpoint created at withoutBodyTypeParameters.kt:8
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!RT_JAR! withoutBodyTypeParameters.WithoutBodyTypeParametersPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
withoutBodyTypeParameters.kt:7
|
||||
Compile bytecode for i
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,31 @@
|
||||
package whenEntry
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val a = 1
|
||||
|
||||
// EXPRESSION: a
|
||||
// RESULT: 1: I
|
||||
val b = when {
|
||||
//Breakpoint!
|
||||
a == 1 -> 1 + 1
|
||||
else -> 1 + 2
|
||||
}
|
||||
|
||||
// EXPRESSION: a
|
||||
// RESULT: 1: I
|
||||
val c = when {
|
||||
//Breakpoint!
|
||||
a == 1 -> { 1 + 1 }
|
||||
else -> 1 + 2
|
||||
}
|
||||
|
||||
// EXPRESSION: a
|
||||
// RESULT: 1: I
|
||||
val d = when {
|
||||
//Breakpoint!
|
||||
a == 1 -> {
|
||||
1 + 1
|
||||
}
|
||||
else -> 1 + 2
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package withoutBodyFunctions
|
||||
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
// EXPRESSION: 1 + 1
|
||||
// RESULT: 2: I
|
||||
val aGet: Int
|
||||
//Breakpoint!
|
||||
get() = 1
|
||||
|
||||
// EXPRESSION: 1 + 2
|
||||
// RESULT: 3: I
|
||||
val aGet2: Int
|
||||
//Breakpoint!
|
||||
get() { return 1 }
|
||||
|
||||
fun fooWithBody(i: Int): Int {
|
||||
// EXPRESSION: i
|
||||
// RESULT: 2: I
|
||||
//Breakpoint!
|
||||
return i
|
||||
}
|
||||
|
||||
// EXPRESSION: i
|
||||
// RESULT: 2: I
|
||||
//Breakpoint!
|
||||
fun foo(i: Int) = i
|
||||
|
||||
// EXPRESSION: i
|
||||
// RESULT: 2: I
|
||||
//Breakpoint!
|
||||
fun fooOneLine(i: Int): Int { return 1 }
|
||||
|
||||
// Cannot stop at this breakpoint - empty body
|
||||
//Breakpoint!
|
||||
fun fooEmpty(i: Int) {}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
aGet
|
||||
aGet2
|
||||
|
||||
fooWithBody(2)
|
||||
foo(2)
|
||||
fooOneLine(2)
|
||||
fooEmpty(2)
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package withoutBodyProperties
|
||||
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
// EXPRESSION: 1 + 1
|
||||
// RESULT: 2: I
|
||||
//Breakpoint!
|
||||
val a = 1
|
||||
|
||||
// EXPRESSION: 1 + 2
|
||||
// RESULT: 3: I
|
||||
//Breakpoint!
|
||||
var aDelegate: Int by Delegates.notNull()
|
||||
|
||||
// EXPRESSION: 1 + 3
|
||||
// RESULT: 4: I
|
||||
//Breakpoint!
|
||||
val aLambda = { 1 + 1 }
|
||||
|
||||
class A {
|
||||
{
|
||||
// EXPRESSION: i
|
||||
// RESULT: 1: I
|
||||
|
||||
// EXPRESSION: i
|
||||
// RESULT: 2: I
|
||||
for (i in 1..2) {
|
||||
//Breakpoint!
|
||||
val a = 1
|
||||
}
|
||||
}
|
||||
|
||||
// EXPRESSION: 1 + 4
|
||||
// RESULT: 5: I
|
||||
//Breakpoint!
|
||||
val prop = 1
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
a
|
||||
aDelegate = 1
|
||||
aLambda
|
||||
|
||||
A().prop
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package withoutBodyTypeParameters
|
||||
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
// EXPRESSION: i
|
||||
// RESULT: instance of java.lang.Integer(id=ID): Ljava/lang/Integer;
|
||||
//Breakpoint!
|
||||
fun foo<T>(i: T) = i
|
||||
|
||||
fun run(i: () -> Int) = 1
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
foo(2)
|
||||
}
|
||||
+20
@@ -161,6 +161,26 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
doMultipleBreakpointsTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/exceptions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("whenEntry.kt")
|
||||
public void testWhenEntry() throws Exception {
|
||||
doMultipleBreakpointsTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/whenEntry.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withoutBodyFunctions.kt")
|
||||
public void testWithoutBodyFunctions() throws Exception {
|
||||
doMultipleBreakpointsTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/withoutBodyFunctions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withoutBodyProperties.kt")
|
||||
public void testWithoutBodyProperties() throws Exception {
|
||||
doMultipleBreakpointsTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/withoutBodyProperties.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withoutBodyTypeParameters.kt")
|
||||
public void testWithoutBodyTypeParameters() throws Exception {
|
||||
doMultipleBreakpointsTest("idea/testData/debugger/tinyApp/src/evaluate/multipleBreakpoints/withoutBodyTypeParameters.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/debugger/tinyApp/src/evaluate/frame")
|
||||
|
||||
Reference in New Issue
Block a user