From 9a3a7fbcca35f860babdd2e66f3e6af173c3497b Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Thu, 7 Jun 2018 15:26:54 +0300 Subject: [PATCH] Debugger: Dispose ExtractionData/ExtractionResult when necessary This fixes memory leak due to extensive use of "Evaluate expression" --- .../evaluate/KotlinEvaluationBuilder.kt | 8 +++-- .../extractFunctionForDebuggerUtil.kt | 30 +++++++++++-------- .../extractionEngine/ExtractionData.kt | 7 +++-- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt index 236b833b707..e7279dd5b3c 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt @@ -26,6 +26,7 @@ import com.intellij.diagnostic.LogMessageEx import com.intellij.openapi.diagnostic.Attachment import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.CharsetToolkit import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiDocumentManager @@ -214,8 +215,11 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour val extractionResult = getFunctionForExtractedFragment(codeFragment, sourcePosition.file, sourcePosition.line) ?: throw IllegalStateException("Code fragment cannot be extracted to function: ${codeFragment.text}") - val parametersDescriptor = extractionResult.getParametersForDebugger(codeFragment) - val extractedFunction = extractionResult.declaration as KtNamedFunction + val (parametersDescriptor, extractedFunction) = try { + extractionResult.getParametersForDebugger(codeFragment) to extractionResult.declaration as KtNamedFunction + } finally { + Disposer.dispose(extractionResult) + } if (LOG.isDebugEnabled) { LOG.debug("Extracted function:\n" + runReadAction { extractedFunction.text }) diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/extractFunctionForDebuggerUtil.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/extractFunctionForDebuggerUtil.kt index 3b9ae8b46c7..b2628bb096f 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/extractFunctionForDebuggerUtil.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/extractFunctionForDebuggerUtil.kt @@ -20,6 +20,7 @@ import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil import com.intellij.diagnostic.LogMessageEx import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.Attachment +import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Key import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile @@ -97,20 +98,25 @@ fun getFunctionForExtractedFragment( allowSpecialClassNames = true, captureLocalFunctions = true, canWrapInWith = true) - val analysisResult = ExtractionData(tmpFile, newDebugExpressions.toRange(), targetSibling, null, options).performAnalysis() - if (analysisResult.status != Status.SUCCESS) { - throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult, tmpFile)) - } + val extractionData = ExtractionData(tmpFile, newDebugExpressions.toRange(), targetSibling, null, options) + try { + val analysisResult = extractionData.performAnalysis() + if (analysisResult.status != Status.SUCCESS) { + throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult, tmpFile)) + } - val validationResult = analysisResult.descriptor!!.validate() - if (!validationResult.conflicts.isEmpty) { - throw EvaluateExceptionUtil.createEvaluateException("Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet().joinToString(",") { it.text }}") - } + val validationResult = analysisResult.descriptor!!.validate() + if (!validationResult.conflicts.isEmpty) { + throw EvaluateExceptionUtil.createEvaluateException("Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet().joinToString(",") { it.text }}") + } - val generatorOptions = ExtractionGeneratorOptions(inTempFile = true, - dummyName = GENERATED_FUNCTION_NAME, - allowExpressionBody = false) - return ExtractionGeneratorConfiguration(validationResult.descriptor, generatorOptions).generateDeclaration() + val generatorOptions = ExtractionGeneratorOptions(inTempFile = true, + dummyName = GENERATED_FUNCTION_NAME, + allowExpressionBody = false) + return ExtractionGeneratorConfiguration(validationResult.descriptor, generatorOptions).generateDeclaration() + } finally { + Disposer.dispose(extractionData) + } } return runReadAction { generateFunction() } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt index f6a4a0fd1d7..227b7a29697 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionData.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.ExtractableSubstringInfo import org.jetbrains.kotlin.idea.refactoring.introduce.extractableSubstringInfo import org.jetbrains.kotlin.idea.refactoring.introduce.substringContextOrThis import org.jetbrains.kotlin.idea.resolve.frontendService +import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.util.psi.patternMatching.KotlinPsiRange import org.jetbrains.kotlin.psi.* @@ -270,6 +271,8 @@ data class ExtractionData( } fun unmarkReferencesInside(root: PsiElement) { - if (!root.isValid) return - root.forEachDescendantOfType { it.resolveResult = null } + runReadAction { + if (!root.isValid) return@runReadAction + root.forEachDescendantOfType { it.resolveResult = null } + } } \ No newline at end of file