Add function name to fake local variable for inline argument

This commit is contained in:
Natalia Ukhorskaya
2016-03-10 15:13:59 +03:00
parent a830d80dac
commit e2d2d696c0
6 changed files with 80 additions and 12 deletions
+4 -1
View File
@@ -12,4 +12,7 @@
- Support conversion for annotation constructor calls
- Place comments from the middle of the call to the end
- Drop line breaks between operator arguments (except '+', "-", "&&" and "||")
- Add non-null assertions on call site for non-null parameters
- Add non-null assertions on call site for non-null parameters
### IDE
- Debugger can distinguish nested inline arguments
@@ -42,6 +42,8 @@ import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.SpecialBuiltinMembers;
import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeKt;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtElement;
import org.jetbrains.kotlin.psi.KtFunction;
import org.jetbrains.kotlin.psi.KtNamedFunction;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
@@ -51,6 +53,7 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.CallResolverUtilKt;
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.KClassValue;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
@@ -394,8 +397,18 @@ public class FunctionCodegen {
int indexOfLambdaOrdinal = name.lastIndexOf("$");
if (indexOfLambdaOrdinal > 0) {
int lambdaOrdinal = Integer.parseInt(name.substring(indexOfLambdaOrdinal + 1));
KtElement functionArgument = parentCodegen.element;
String functionName = "unknown";
if (functionArgument instanceof KtFunction) {
ValueParameterDescriptor inlineArgumentDescriptor =
InlineUtil.getInlineArgumentDescriptor((KtFunction) functionArgument, parentCodegen.bindingContext);
if (inlineArgumentDescriptor != null) {
functionName = inlineArgumentDescriptor.getContainingDeclaration().getName().asString();
}
}
mv.visitLocalVariable(
JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + lambdaOrdinal,
JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + lambdaOrdinal + "$" + functionName,
Type.INT_TYPE.getDescriptor(), null,
methodBegin, methodEnd,
lambdaFakeIndex);
@@ -24,6 +24,8 @@ import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import java.util.*
fun isInsideInlineFunctionBody(visibleVariables: List<LocalVariable>): Boolean {
@@ -36,32 +38,49 @@ fun numberOfInlinedFunctions(visibleVariables: List<LocalVariable>): Int {
fun isInsideInlineArgument(inlineArgument: KtFunction, location: Location, debugProcess: DebugProcessImpl): Boolean {
val visibleVariables = location.visibleVariables(debugProcess)
val lambdaOrdinalIndex = runReadAction { lambdaOrdinalIndex(inlineArgument) }
val context = KotlinDebuggerCaches.getOrCreateTypeMapper(inlineArgument).bindingContext
val lambdaOrdinal = runReadAction { lambdaOrdinalByArgument(inlineArgument, context) }
val markerLocalVariables = visibleVariables.filter { it.name().startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT) }
return markerLocalVariables.firstOrNull { lambdaOrdinal(it.name()) == lambdaOrdinalIndex } != null
val functionName = runReadAction { functionNameByArgument(inlineArgument, context) }
return markerLocalVariables.firstOrNull {
lambdaOrdinalByLocalVariable(it.name()) == lambdaOrdinal && functionNameByLocalVariable(it.name()) == functionName
} != null
}
private fun lambdaOrdinalIndex(elementAt: KtFunction): Int {
val typeMapper = KotlinDebuggerCaches.getOrCreateTypeMapper(elementAt)
val type = CodegenBinding.asmTypeForAnonymousClass(typeMapper.bindingContext, elementAt)
private fun lambdaOrdinalByArgument(elementAt: KtFunction, context: BindingContext): Int {
val type = CodegenBinding.asmTypeForAnonymousClass(context, elementAt)
return type.className.substringAfterLast("$").toInt()
}
private fun functionNameByArgument(elementAt: KtFunction, context: BindingContext): String {
val inlineArgumentDescriptor = InlineUtil.getInlineArgumentDescriptor(elementAt, context)
return inlineArgumentDescriptor?.containingDeclaration?.name?.asString() ?: "unknown"
}
private fun Location.visibleVariables(debugProcess: DebugProcessImpl): List<LocalVariable> {
val stackFrame = MockStackFrame(this, debugProcess.virtualMachineProxy.virtualMachine)
return stackFrame.visibleVariables()
}
private fun lambdaOrdinal(name: String): Int {
return try {
return name.substringAfter(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT).toInt()
private fun lambdaOrdinalByLocalVariable(name: String): Int {
try {
val nameWithoutPrefix = name.removePrefix(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)
return Integer.parseInt(nameWithoutPrefix.substringBefore("$", nameWithoutPrefix))
}
catch(e: NumberFormatException) {
0
return 0
}
}
private fun functionNameByLocalVariable(name: String): String {
val nameWithoutPrefix = name.removePrefix(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)
return nameWithoutPrefix.substringAfterLast("$", "unknown")
}
private class MockStackFrame(private val location: Location, private val vm: VirtualMachine) : StackFrame {
private var visibleVariables: Map<String, LocalVariable>? = null
@@ -0,0 +1,8 @@
LineBreakpoint created at nestedInlineArguments.kt:13 lambdaOrdinal = 0
!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! nestedInlineArguments.NestedInlineArgumentsKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
nestedInlineArguments.kt:13
Compile bytecode for it.i
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,19 @@
package nestedInlineArguments
fun main(args: Array<String>) {
val list1 = listOf("a")
val list2 = listOf(Bar())
list1.map { list2.foo(1) }
}
inline fun <reified T: Bar> List<T>.foo(key: Int): T? {
// EXPRESSION: it.i
// RESULT: 1: I
//Breakpoint! (lambdaOrdinal = 0)
return this.firstOrNull { it.i == key }
}
open class Foo
open class Bar {
val i = 1
}
@@ -229,6 +229,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
doSingleBreakpointTest(fileName);
}
@TestMetadata("nestedInlineArguments.kt")
public void testNestedInlineArguments() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/nestedInlineArguments.kt");
doSingleBreakpointTest(fileName);
}
@TestMetadata("onClassHeader.kt")
public void testOnClassHeader() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/onClassHeader.kt");