From 74e68950f93054d0c8251ff27bcca66435b49ab9 Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Mon, 20 Apr 2015 15:52:09 +0300 Subject: [PATCH] Evaluate expression: support inline functions from source code --- .../evaluate/KotlinEvaluationBuilder.kt | 58 +++++++++++++++---- .../debugger/tinyApp/outs/inlineFunction.out | 8 +++ .../singleBreakpoint/inlineFunction.kt | 11 ++++ .../tinyApp/src/forTests/inlineFunction2.kt | 3 + ...KotlinEvaluateExpressionTestGenerated.java | 6 ++ 5 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 idea/testData/debugger/tinyApp/outs/inlineFunction.out create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/inlineFunction.kt create mode 100644 idea/testData/debugger/tinyApp/src/forTests/inlineFunction2.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt index af1d1de9c6b..fb620fd2b2d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt @@ -47,16 +47,22 @@ import org.jetbrains.kotlin.backend.common.output.OutputFileCollection import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.codegen.state.Progress import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages import org.jetbrains.kotlin.idea.JetLanguage import org.jetbrains.kotlin.idea.caches.resolve.JavaResolveExtension +import org.jetbrains.kotlin.idea.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache.CompiledDataDescriptor import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache.ParametersDescriptor import org.jetbrains.kotlin.idea.debugger.evaluate.compilingEvaluator.loadClasses import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractionResult +import org.jetbrains.kotlin.idea.util.DebuggerUtils import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.attachment.attachmentByPsiFile import org.jetbrains.kotlin.idea.util.attachment.mergeAttachments @@ -77,6 +83,7 @@ import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.Opcodes.ASM5 import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.tree.MethodNode +import java.util.* private val RECEIVER_NAME = "\$receiver" private val THIS_NAME = "this" @@ -159,7 +166,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment, companion object { private fun extractAndCompile(codeFragment: JetCodeFragment, sourcePosition: SourcePosition, context: EvaluationContextImpl): CompiledDataDescriptor { - codeFragment.checkForErrors() + codeFragment.checkForErrors(false) val extractionResult = getFunctionForExtractedFragment(codeFragment, sourcePosition.getFile(), sourcePosition.getLine()) if (extractionResult == null) { @@ -274,19 +281,36 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment, return parameterNames.zip(parameterTypes).map { this.findLocalVariable(it.first, it.second, checkType = false, failIfNotFound = true)!! } } - private fun createClassFileFactory(codeFragment: JetCodeFragment, extractedFunction: JetNamedFunction, context: EvaluationContextImpl): ClassFileFactory { + private fun createClassFileFactory( + codeFragment: JetCodeFragment, + extractedFunction: JetNamedFunction, + context: EvaluationContextImpl + ): ClassFileFactory { return runReadAction { - val file = createFileForDebugger(codeFragment, extractedFunction) + val jetFile = createFileForDebugger(codeFragment, extractedFunction) - val (bindingContext, moduleDescriptor) = file.checkForErrors() + val (bindingContext, moduleDescriptor, files) = jetFile.checkForErrors(true) + + val generateClassFilter = object : GenerationState.GenerateClassFilter { + override fun shouldGeneratePackagePart(file: JetFile) = file == jetFile + override fun shouldAnnotateClass(classOrObject: JetClassOrObject) = true + override fun shouldGenerateClass(classOrObject: JetClassOrObject) = classOrObject.getContainingJetFile() == jetFile + override fun shouldGenerateScript(script: JetScript) = false + } val state = GenerationState( - file.getProject(), + jetFile.getProject(), ClassBuilderFactories.BINARIES, + Progress.DEAF, moduleDescriptor, bindingContext, - listOf(file) - ) + files, + true, true, + generateClassFilter, + false, false, + null, null, + DiagnosticSink.DO_NOTHING, + null) extractedFunction.getReceiverTypeReference()?.let { state.recordAnonymousType(it, THIS_NAME, context) @@ -336,8 +360,8 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment, throw EvaluateExceptionUtil.createEvaluateException(e) } - private fun JetFile.checkForErrors() = - runReadAction { + private fun JetFile.checkForErrors(analyzeInlineFunctions: Boolean): ExtendedAnalysisResult { + return runReadAction { try { AnalyzingUtils.checkForSyntacticErrors(this) } @@ -345,7 +369,8 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment, throw EvaluateExceptionUtil.createEvaluateException(e.getMessage()) } - val analysisResult = this.analyzeFullyAndGetResult(createFlexibleTypesFile()) + val resolutionFacade = KotlinCacheService.getInstance(getProject()).getResolutionFacade(listOf(this, createFlexibleTypesFile())) + val analysisResult = resolutionFacade.analyzeFullyAndGetResult(Collections.singletonList(this)) if (analysisResult.isError()) { throw EvaluateExceptionUtil.createEvaluateException(analysisResult.error) } @@ -355,8 +380,17 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment, throw EvaluateExceptionUtil.createEvaluateException(DefaultErrorMessages.render(it)) } - analysisResult + if (analyzeInlineFunctions) { + val (newBindingContext, files) = DebuggerUtils.analyzeInlinedFunctions(resolutionFacade, bindingContext, this, false) + ExtendedAnalysisResult(newBindingContext, analysisResult.moduleDescriptor, files) + } + else { + ExtendedAnalysisResult(bindingContext, analysisResult.moduleDescriptor, Collections.singletonList(this)) + } } + } + + private data class ExtendedAnalysisResult(val bindingContext: BindingContext, val moduleDescriptor: ModuleDescriptor, val files: List) } } @@ -496,7 +530,7 @@ fun EvaluationContextImpl.findLocalVariable(name: String, asmType: Type?, checkT } } - fun Value.isSharedVar(): Boolean { + fun Value.isSharedVar(): Boolean { return this.asmType.getSort() == Type.OBJECT && this.asmType.getInternalName().startsWith(AsmTypes.REF_TYPE_PREFIX) } diff --git a/idea/testData/debugger/tinyApp/outs/inlineFunction.out b/idea/testData/debugger/tinyApp/outs/inlineFunction.out new file mode 100644 index 00000000000..350128bee2c --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/inlineFunction.out @@ -0,0 +1,8 @@ +LineBreakpoint created at inlineFunction.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 !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! inlineFunction.InlineFunctionPackage +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +inlineFunction.kt:7 +Compile bytecode for myFun { 1 } +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/inlineFunction.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/inlineFunction.kt new file mode 100644 index 00000000000..3e15b696803 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/inlineFunction.kt @@ -0,0 +1,11 @@ +package inlineFunction + +import inlineFunctionOtherPackage.* + +fun main(args: Array) { + //Breakpoint! + val a = 1 +} + +// EXPRESSION: myFun { 1 } +// RESULT: 1: I \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/forTests/inlineFunction2.kt b/idea/testData/debugger/tinyApp/src/forTests/inlineFunction2.kt new file mode 100644 index 00000000000..657d0ddcd9f --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/forTests/inlineFunction2.kt @@ -0,0 +1,3 @@ +package inlineFunctionOtherPackage + +inline fun myFun(f: () -> Int): Int = f() \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java index 1bf1e014a3b..2e0fbd714c7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -139,6 +139,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat doSingleBreakpointTest(fileName); } + @TestMetadata("inlineFunction.kt") + public void testInlineFunction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/inlineFunction.kt"); + doSingleBreakpointTest(fileName); + } + @TestMetadata("innerClass.kt") public void testInnerClass() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/innerClass.kt");