Evaluate expression: move helper methods for extract function to separate file

This commit is contained in:
Natalia Ukhorskaya
2014-05-28 15:09:55 +04:00
parent 03d2e89c6b
commit 7671962284
2 changed files with 139 additions and 110 deletions
@@ -43,17 +43,9 @@ import org.jetbrains.eval4j.jdi.asJdiValue
import org.jetbrains.eval4j.jdi.makeInitialFrame
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils
import org.jetbrains.jet.lang.resolve.name.FqName
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.eval4j.jdi.asValue
import org.jetbrains.jet.plugin.refactoring.createTempCopy
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionData
import org.jetbrains.jet.plugin.refactoring.extractFunction.performAnalysis
import org.jetbrains.jet.plugin.refactoring.extractFunction.validate
import org.jetbrains.jet.plugin.refactoring.extractFunction.generateFunction
import org.jetbrains.jet.lang.psi.JetNamedFunction
import com.intellij.psi.PsiFile
import org.jetbrains.jet.codegen.ClassFileFactory
import org.jetbrains.jet.plugin.codeInsight.CodeInsightUtils
import org.jetbrains.jet.OutputFileCollection
import org.jetbrains.jet.plugin.caches.resolve.getAnalysisResults
import org.jetbrains.jet.lang.psi.JetCodeFragment
@@ -63,15 +55,11 @@ import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Status
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.jet.codegen.CompilationErrorHandler
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.ErrorMessage
import org.jetbrains.jet.lang.diagnostics.Severity
import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages
import com.sun.jdi.request.EventRequest
import com.sun.jdi.ObjectReference
import com.intellij.debugger.engine.SuspendContext
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionOptions
import org.jetbrains.jet.lang.diagnostics.DiagnosticHolder
object KotlinEvaluationBuilder: EvaluatorBuilder {
override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator {
@@ -271,38 +259,6 @@ private fun createFileForDebugger(codeFragment: JetCodeFragment,
return jetFile
}
fun addImportsToFile(newImportList: JetImportList?, tmpFile: JetFile) {
if (newImportList != null) {
val tmpFileImportList = tmpFile.getImportList()
val packageDirective = tmpFile.getPackageDirective()
if (tmpFileImportList == null) {
tmpFile.addAfter(JetPsiFactory.createNewLine(tmpFile.getProject()), packageDirective)
tmpFile.addAfter(newImportList, tmpFile.getPackageDirective())
}
else {
tmpFileImportList.replace(newImportList)
}
tmpFile.addAfter(JetPsiFactory.createNewLine(tmpFile.getProject()), packageDirective)
}
}
fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment, contextElement: PsiElement): JetExpression? {
val parent = contextElement.getParent()
if (parent == null) return null
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
val debugExpression = codeFragment.getContentElement()
if (debugExpression == null) return null
val newDebugExpression = parent.addBefore(debugExpression, contextElement)
if (newDebugExpression == null) return null
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
return newDebugExpression as JetExpression
}
fun checkForSyntacticErrors(file: JetFile) {
try {
AnalyzingUtils.checkForSyntacticErrors(file)
@@ -312,69 +268,3 @@ fun checkForSyntacticErrors(file: JetFile) {
}
}
private fun getFunctionForExtractedFragment(
codeFragment: JetCodeFragment,
breakpointFile: PsiFile,
breakpointLine: Int
): JetNamedFunction? {
fun getErrorMessageForExtractFunctionResult(analysisResult: AnalysisResult): String {
return analysisResult.messages.map {
errorMessage ->
val message = when(errorMessage) {
ErrorMessage.NO_EXPRESSION -> "Cannot perform an action without an expression"
ErrorMessage.NO_CONTAINER -> "Cannot perform an action at this breakpoint ${breakpointFile.getName()}:${breakpointLine}"
ErrorMessage.SUPER_CALL -> "Cannot perform an action for expression with super call"
ErrorMessage.DENOTABLE_TYPES -> "Cannot perform an action because following types are unavailable from debugger scope"
ErrorMessage.MULTIPLE_OUTPUT -> "Cannot perform an action because this code fragment changes more than one variable"
ErrorMessage.DECLARATIONS_OUT_OF_SCOPE,
ErrorMessage.OUTPUT_AND_EXIT_POINT,
ErrorMessage.MULTIPLE_EXIT_POINTS,
ErrorMessage.VARIABLES_ARE_USED_OUTSIDE -> "Cannot perform an action for this expression"
}
if (errorMessage.additionalInfo == null) message else "$message: ${errorMessage.additionalInfo?.makeString(", ")}"
}.makeString(", ")
}
return ApplicationManager.getApplication()?.runReadAction(object: Computable<JetNamedFunction> {
override fun compute(): JetNamedFunction? {
checkForSyntacticErrors(codeFragment)
val originalFile = breakpointFile as JetFile
val lineStart = CodeInsightUtils.getStartLineOffset(originalFile, breakpointLine)
if (lineStart == null) return null
val tmpFile = originalFile.createTempCopy { it }
tmpFile.skipVisibilityCheck = true
val elementAtOffset = tmpFile.findElementAt(lineStart)
if (elementAtOffset == null) return null
val contextElement: PsiElement = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
addImportsToFile(codeFragment.importsAsImportList(), tmpFile)
val newDebugExpression = addDebugExpressionBeforeContextElement(codeFragment, contextElement)
if (newDebugExpression == null) return null
val targetSibling = tmpFile.getDeclarations().firstOrNull()
if (targetSibling == null) return null
val analysisResult = ExtractionData(
tmpFile, Collections.singletonList(newDebugExpression), targetSibling, ExtractionOptions(false)
).performAnalysis()
if (analysisResult.status != Status.SUCCESS) {
throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult))
}
val validationResult = analysisResult.descriptor!!.validate()
if (!validationResult.conflicts.isEmpty()) {
throw EvaluateExceptionUtil.createEvaluateException("Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet()?.map { it.getText() }?.makeString(",")}")
}
return validationResult.descriptor.generateFunction(true)
}
})
}
@@ -0,0 +1,139 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.Computable
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.plugin.codeInsight.CodeInsightUtils
import org.jetbrains.jet.plugin.refactoring.createTempCopy
import org.jetbrains.jet.lang.psi.codeFragmentUtil.skipVisibilityCheck
import com.intellij.psi.PsiElement
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionData
import java.util.Collections
import org.jetbrains.jet.plugin.refactoring.extractFunction.performAnalysis
import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Status
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
fun getFunctionForExtractedFragment(
codeFragment: JetCodeFragment,
breakpointFile: PsiFile,
breakpointLine: Int
): JetNamedFunction? {
fun getErrorMessageForExtractFunctionResult(analysisResult: AnalysisResult): String {
return analysisResult.messages.map {
errorMessage ->
val message = when(errorMessage) {
ErrorMessage.NO_EXPRESSION -> "Cannot perform an action without an expression"
ErrorMessage.NO_CONTAINER -> "Cannot perform an action at this breakpoint ${breakpointFile.getName()}:${breakpointLine}"
ErrorMessage.SUPER_CALL -> "Cannot perform an action for expression with super call"
ErrorMessage.DENOTABLE_TYPES -> "Cannot perform an action because following types are unavailable from debugger scope"
ErrorMessage.MULTIPLE_OUTPUT -> "Cannot perform an action because this code fragment changes more than one variable"
ErrorMessage.DECLARATIONS_OUT_OF_SCOPE,
ErrorMessage.OUTPUT_AND_EXIT_POINT,
ErrorMessage.MULTIPLE_EXIT_POINTS,
ErrorMessage.VARIABLES_ARE_USED_OUTSIDE -> "Cannot perform an action for this expression"
}
if (errorMessage.additionalInfo == null) message else "$message: ${errorMessage.additionalInfo?.makeString(", ")}"
}.makeString(", ")
}
return ApplicationManager.getApplication()?.runReadAction(object: Computable<JetNamedFunction> {
override fun compute(): JetNamedFunction? {
checkForSyntacticErrors(codeFragment)
val originalFile = breakpointFile as JetFile
val lineStart = CodeInsightUtils.getStartLineOffset(originalFile, breakpointLine)
if (lineStart == null) return null
val tmpFile = originalFile.createTempCopy { it }
tmpFile.skipVisibilityCheck = true
val elementAtOffset = tmpFile.findElementAt(lineStart)
if (elementAtOffset == null) return null
val contextElement: PsiElement = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
addImportsToFile(codeFragment.importsAsImportList(), tmpFile)
val newDebugExpression = addDebugExpressionBeforeContextElement(codeFragment, contextElement)
if (newDebugExpression == null) return null
val targetSibling = tmpFile.getDeclarations().firstOrNull()
if (targetSibling == null) return null
val analysisResult = ExtractionData(
tmpFile, Collections.singletonList(newDebugExpression), targetSibling, ExtractionOptions(false)
).performAnalysis()
if (analysisResult.status != Status.SUCCESS) {
throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult))
}
val validationResult = analysisResult.descriptor!!.validate()
if (!validationResult.conflicts.isEmpty()) {
throw EvaluateExceptionUtil.createEvaluateException("Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet()?.map { it.getText() }?.makeString(",")}")
}
return validationResult.descriptor.generateFunction(true)
}
})
}
private fun addImportsToFile(newImportList: JetImportList?, tmpFile: JetFile) {
if (newImportList != null) {
val tmpFileImportList = tmpFile.getImportList()
val packageDirective = tmpFile.getPackageDirective()
if (tmpFileImportList == null) {
tmpFile.addAfter(JetPsiFactory.createNewLine(tmpFile.getProject()), packageDirective)
tmpFile.addAfter(newImportList, tmpFile.getPackageDirective())
}
else {
tmpFileImportList.replace(newImportList)
}
tmpFile.addAfter(JetPsiFactory.createNewLine(tmpFile.getProject()), packageDirective)
}
}
private fun addDebugExpressionBeforeContextElement(codeFragment: JetCodeFragment, contextElement: PsiElement): JetExpression? {
val parent = contextElement.getParent()
if (parent == null) return null
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
val debugExpression = codeFragment.getContentElement()
if (debugExpression == null) return null
val newDebugExpression = parent.addBefore(debugExpression, contextElement)
if (newDebugExpression == null) return null
parent.addBefore(JetPsiFactory.createNewLine(contextElement.getProject()), contextElement)
return newDebugExpression as JetExpression
}