Support Evaluate Expression for renamed local variables in inline function

#KT-10179 Fixed
This commit is contained in:
Natalia Ukhorskaya
2015-11-30 15:22:41 +03:00
parent cd5e406876
commit dc60c025c3
9 changed files with 300 additions and 1 deletions
@@ -25,6 +25,14 @@ import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtFunction
import java.util.*
public fun isInsideInlineFunctionBody(visibleVariables: List<LocalVariable>): Boolean {
return visibleVariables.any { it.name().startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) }
}
public fun numberOfInlinedFunctions(visibleVariables: List<LocalVariable>): Int {
return visibleVariables.count { it.name().startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) }
}
fun isInsideInlineArgument(inlineArgument: KtFunction, location: Location, debugProcess: DebugProcessImpl): Boolean {
val visibleVariables = location.visibleVariables(debugProcess)
val lambdaOrdinalIndex = runReadAction { lambdaOrdinalIndex(inlineArgument) }
@@ -27,6 +27,8 @@ import org.jetbrains.eval4j.jdi.asValue
import org.jetbrains.eval4j.obj
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
import org.jetbrains.kotlin.idea.debugger.isInsideInlineFunctionBody
import org.jetbrains.kotlin.idea.debugger.numberOfInlinedFunctions
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
@@ -48,6 +50,14 @@ class FrameVisitor(context: EvaluationContextImpl) {
}
}
else -> {
if (isInsideInlineFunctionBody(frame.visibleVariables())) {
val number = numberOfInlinedFunctions(frame.visibleVariables())
val inlineFunVar = findLocalVariableForInlineArgument(name, number, asmType, true)
if (inlineFunVar != null) {
return inlineFunVar
}
}
val localVariable = if (isFunctionType(asmType))
findLocalVariableForLocalFun(name, asmType, checkType)
else
@@ -77,7 +87,15 @@ class FrameVisitor(context: EvaluationContextImpl) {
}
private fun findThis(asmType: Type?): Value? {
val thisObject = frame!!.thisObject()
if (isInsideInlineFunctionBody(frame!!.visibleVariables())) {
val number = numberOfInlinedFunctions(frame.visibleVariables())
val inlineFunVar = findLocalVariableForInlineArgument("this_", number, asmType, true)
if (inlineFunVar != null) {
return inlineFunVar
}
}
val thisObject = frame.thisObject()
if (thisObject != null) {
val eval4jValue = thisObject.asValue()
if (isValueOfCorrectType(eval4jValue, asmType, true)) return eval4jValue
@@ -99,6 +117,10 @@ class FrameVisitor(context: EvaluationContextImpl) {
return findLocalVariable(name + "$", asmType, checkType)
}
private fun findLocalVariableForInlineArgument(name: String, number: Int, asmType: Type?, checkType: Boolean): Value? {
return findLocalVariable(name + InlineCodegenUtil.INLINE_FUN_VAR_SUFFIX.repeat(number), asmType, checkType)
}
private fun isFunctionType(type: Type?): Boolean {
return type?.sort == Type.OBJECT &&
type!!.internalName.startsWith(InlineCodegenUtil.NUMBERED_FUNCTION_PREFIX)
@@ -0,0 +1,39 @@
LineBreakpoint created at frameInlineArgument.kt:7
!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! frameInlineArgument.FrameInlineArgumentKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
frameInlineArgument.kt:7
Compile bytecode for element
package frameInlineArgument
fun main(args: Array<String>) {
val element = 1
A().inlineFun {
//Breakpoint!
element
}
}
class A {
inline fun inlineFun(s: () -> Unit) {
val element = 1.0
s()
}
val prop = 1
}
// PRINT_FRAME
// EXPRESSION: element
// RESULT: 1: I
frame = main():7, FrameInlineArgumentKt {frameInlineArgument}
local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = frameInlineArgument.kt, 3)
local = element: int = 1 (sp = frameInlineArgument.kt, 4)
local = this_$iv: frameInlineArgument.A = {frameInlineArgument.A@uniqueID} (sp = null)
field = prop: int = 1 (sp = frameInlineArgument.kt, 17)
local = element$iv: double = 1.0 (sp = frameInlineArgument.kt, 4)
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
+43
View File
@@ -0,0 +1,43 @@
LineBreakpoint created at frameInlineFun.kt:14
!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! frameInlineFun.FrameInlineFunKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
frameInlineFun.kt:14
Compile bytecode for element
Compile bytecode for this.prop
package frameInlineFun
fun main(args: Array<String>) {
val element = 1
A().inlineFun {
element
}
}
class A {
inline fun inlineFun(s: (Int) -> Unit) {
val element = 1.0
//Breakpoint!
s(1)
}
val prop = 1
}
// PRINT_FRAME
// EXPRESSION: element
// RESULT: 1.0: D
// EXPRESSION: this.prop
// RESULT: 1: I
frame = main():14, FrameInlineFunKt {frameInlineFun}
local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = null)
local = element: int = 1 (sp = frameInlineFun.kt, 12)
local = this_$iv: frameInlineFun.A = {frameInlineFun.A@uniqueID} (sp = null)
field = prop: int = 1 (sp = frameInlineFun.kt, 17)
local = element$iv: double = 1.0 (sp = frameInlineFun.kt, 12)
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,71 @@
LineBreakpoint created at frameInlineFunCallInsideInlineFun.kt:20
!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! frameInlineFunCallInsideInlineFun.FrameInlineFunCallInsideInlineFunKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
frameInlineFunCallInsideInlineFun.kt:20
frameInlineFunCallInsideInlineFun.kt:5
frameInlineFunCallInsideInlineFun.kt:7
Compile bytecode for element
Compile bytecode for this.prop
package frameInlineFunCallInsideInlineFun
class A {
inline fun inlineFun(s: (Int) -> Unit) {
val element = 1.0
//TODO breakpoint here doesn't work (not only in tests)
s(1)
}
val prop = 1
}
class B {
inline fun foo(s: (Int) -> Unit) {
val element = 2
val a = A()
// STEP_INTO: 1
// STEP_OVER: 1
//Breakpoint!
a.inlineFun {
val e = element
}
s(1)
}
}
class C {
fun bar() {
val element = 1f
B().foo {
val e = element
}
}
}
fun main(args: Array<String>) {
C().bar()
}
// PRINT_FRAME
// EXPRESSION: element
// RESULT: 1.0: D
// EXPRESSION: this.prop
// RESULT: 1: I
frame = bar():7, C {frameInlineFunCallInsideInlineFun}
this = this = {frameInlineFunCallInsideInlineFun.C@uniqueID}
- Class has no fields
local = element: float = 1.0 (sp = frameInlineFunCallInsideInlineFun.kt, 5)
local = this_$iv: frameInlineFunCallInsideInlineFun.B = {frameInlineFunCallInsideInlineFun.B@uniqueID} (sp = null)
- Class has no fields
local = element$iv: int = 2 (sp = frameInlineFunCallInsideInlineFun.kt, 5)
local = a$iv: frameInlineFunCallInsideInlineFun.A = {frameInlineFunCallInsideInlineFun.A@uniqueID} (sp = null)
field = prop: int = 1 (sp = frameInlineFunCallInsideInlineFun.kt, 10)
local = this_$iv$iv: frameInlineFunCallInsideInlineFun.A = {frameInlineFunCallInsideInlineFun.A@uniqueID} (sp = null)
field = prop: int = 1 (sp = frameInlineFunCallInsideInlineFun.kt, 10)
local = element$iv$iv: double = 1.0 (sp = frameInlineFunCallInsideInlineFun.kt, 5)
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,24 @@
package frameInlineArgument
fun main(args: Array<String>) {
val element = 1
A().inlineFun {
//Breakpoint!
element
}
}
class A {
inline fun inlineFun(s: () -> Unit) {
val element = 1.0
s()
}
val prop = 1
}
// PRINT_FRAME
// EXPRESSION: element
// RESULT: 1: I
@@ -0,0 +1,27 @@
package frameInlineFun
fun main(args: Array<String>) {
val element = 1
A().inlineFun {
element
}
}
class A {
inline fun inlineFun(s: (Int) -> Unit) {
val element = 1.0
//Breakpoint!
s(1)
}
val prop = 1
}
// PRINT_FRAME
// EXPRESSION: element
// RESULT: 1.0: D
// EXPRESSION: this.prop
// RESULT: 1: I
@@ -0,0 +1,47 @@
package frameInlineFunCallInsideInlineFun
class A {
inline fun inlineFun(s: (Int) -> Unit) {
val element = 1.0
//TODO breakpoint here doesn't work (not only in tests)
s(1)
}
val prop = 1
}
class B {
inline fun foo(s: (Int) -> Unit) {
val element = 2
val a = A()
// STEP_INTO: 1
// STEP_OVER: 1
//Breakpoint!
a.inlineFun {
val e = element
}
s(1)
}
}
class C {
fun bar() {
val element = 1f
B().foo {
val e = element
}
}
}
fun main(args: Array<String>) {
C().bar()
}
// PRINT_FRAME
// EXPRESSION: element
// RESULT: 1.0: D
// EXPRESSION: this.prop
// RESULT: 1: I
@@ -489,6 +489,24 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
doSingleBreakpointTest(fileName);
}
@TestMetadata("frameInlineArgument.kt")
public void testFrameInlineArgument() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/frameInlineArgument.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("frameInlineFun.kt")
public void testFrameInlineFun() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/frameInlineFun.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("frameInlineFunCallInsideInlineFun.kt")
public void testFrameInlineFunCallInsideInlineFun() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/frameInlineFunCallInsideInlineFun.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("frameInnerClass.kt")
public void testFrameInnerClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/frameInnerClass.kt");