Debugger: replace one read action with lock for extract variable and for analyze file
This commit is contained in:
@@ -46,7 +46,6 @@ import org.jetbrains.jet.lang.resolve.name.FqName
|
|||||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
|
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
|
||||||
import org.jetbrains.jet.plugin.debugger.KotlinEditorTextProvider
|
import org.jetbrains.jet.plugin.debugger.KotlinEditorTextProvider
|
||||||
import org.jetbrains.jet.lang.psi.JetPsiFactory
|
import org.jetbrains.jet.lang.psi.JetPsiFactory
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
|
||||||
import org.jetbrains.eval4j.jdi.asValue
|
import org.jetbrains.eval4j.jdi.asValue
|
||||||
import org.jetbrains.jet.plugin.refactoring.createTempCopy
|
import org.jetbrains.jet.plugin.refactoring.createTempCopy
|
||||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionData
|
import org.jetbrains.jet.plugin.refactoring.extractFunction.ExtractionData
|
||||||
@@ -56,11 +55,11 @@ import org.jetbrains.jet.plugin.util.MaybeValue
|
|||||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.validate
|
import org.jetbrains.jet.plugin.refactoring.extractFunction.validate
|
||||||
import org.jetbrains.jet.plugin.refactoring.checkConflictsInteractively
|
import org.jetbrains.jet.plugin.refactoring.checkConflictsInteractively
|
||||||
import org.jetbrains.jet.plugin.refactoring.extractFunction.generateFunction
|
import org.jetbrains.jet.plugin.refactoring.extractFunction.generateFunction
|
||||||
import org.jetbrains.jet.lang.psi.JetElement
|
|
||||||
import com.intellij.util.text.CharArrayUtil
|
import com.intellij.util.text.CharArrayUtil
|
||||||
import org.jetbrains.jet.lang.psi.JetNamedFunction
|
import org.jetbrains.jet.lang.psi.JetNamedFunction
|
||||||
import com.intellij.psi.PsiDocumentManager
|
import com.intellij.psi.PsiDocumentManager
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.jet.codegen.ClassFileFactory
|
||||||
import org.jetbrains.jet.plugin.codeInsight.CodeInsightUtils
|
import org.jetbrains.jet.plugin.codeInsight.CodeInsightUtils
|
||||||
import org.jetbrains.jet.OutputFileCollection
|
import org.jetbrains.jet.OutputFileCollection
|
||||||
import org.jetbrains.jet.lang.psi.JetExpressionCodeFragment
|
import org.jetbrains.jet.lang.psi.JetExpressionCodeFragment
|
||||||
@@ -90,80 +89,49 @@ class KotlinEvaluator(val codeFragment: PsiElement,
|
|||||||
val sourcePosition: SourcePosition
|
val sourcePosition: SourcePosition
|
||||||
) : Evaluator {
|
) : Evaluator {
|
||||||
override fun evaluate(context: EvaluationContextImpl): Any? {
|
override fun evaluate(context: EvaluationContextImpl): Any? {
|
||||||
return ApplicationManager.getApplication()?.runReadAction(object: Computable<Any> {
|
val extractedFunction = getFunctionForExtractedFragment(codeFragment, sourcePosition.getFile(), sourcePosition.getLine())
|
||||||
override fun compute(): Any? {
|
if (extractedFunction == null) {
|
||||||
if (codeFragment !is JetExpressionCodeFragment) {
|
exception("This code fragment cannot be extracted to function")
|
||||||
throw AssertionError("KotlinEvaluator should be invoke only for KotlinCodeFragment")
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val extractedFunction = getFunctionForExtractedFragment(codeFragment, sourcePosition.getFile(), sourcePosition.getLine())
|
val classFileFactory = createClassFileFactory(extractedFunction)
|
||||||
if (extractedFunction == null) {
|
|
||||||
exception("This code fragment cannot be extracted to function")
|
|
||||||
}
|
|
||||||
|
|
||||||
val file = createFileForDebugger(codeFragment, extractedFunction)
|
// KT-4509
|
||||||
|
val outputFiles = (classFileFactory : OutputFileCollection).asList().filter { it.relativePath != "$packageInternalName.class" }
|
||||||
|
if (outputFiles.size() != 1) exception("More than one class file found. Note that lambdas, classes and objects are unsupported yet.\n${outputFiles.makeString("\n")}")
|
||||||
|
|
||||||
val analyzeExhaust = file.getAnalysisResults()
|
val virtualMachine = context.getDebugProcess().getVirtualMachineProxy().getVirtualMachine()
|
||||||
val bindingContext = analyzeExhaust.getBindingContext()
|
|
||||||
try {
|
|
||||||
analyzeExhaust.throwIfError()
|
|
||||||
AnalyzingUtils.throwExceptionOnErrors(bindingContext)
|
|
||||||
}
|
|
||||||
catch (e: IllegalStateException) {
|
|
||||||
exception(e.getMessage() ?: "Exception from kotlin compiler")
|
|
||||||
}
|
|
||||||
|
|
||||||
val state = GenerationState(
|
var resultValue: Value? = null
|
||||||
file.getProject(),
|
ClassReader(outputFiles.first().asByteArray()).accept(object : ClassVisitor(ASM5) {
|
||||||
ClassBuilderFactories.BINARIES,
|
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
||||||
bindingContext,
|
if (name == extractedFunction.getName()) {
|
||||||
Collections.singletonList(file),
|
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
|
||||||
true)
|
override fun visitEnd() {
|
||||||
|
val value = interpreterLoop(
|
||||||
|
this,
|
||||||
|
makeInitialFrame(this, context.getArgumentsByNames(extractedFunction.getParameterNamesForDebugger())),
|
||||||
|
JDIEval(virtualMachine,
|
||||||
|
context.getClassLoader()!!,
|
||||||
|
context.getSuspendContext().getThread()?.getThreadReference()!!)
|
||||||
|
)
|
||||||
|
|
||||||
KotlinCodegenFacade.compileCorrectFiles(state) {
|
resultValue = when (value) {
|
||||||
e, msg ->
|
is ValueReturned -> value.result
|
||||||
exception("$msg\n${e?.getMessage() ?: ""}")
|
is ExceptionThrown -> exception(value.exception.toString())
|
||||||
}
|
is AbnormalTermination -> exception(value.message)
|
||||||
|
else -> throw IllegalStateException("Unknown result value produced by eval4j")
|
||||||
// KT-4509
|
|
||||||
val outputFiles = (state.getFactory() : OutputFileCollection).asList().filter { it.relativePath != "$packageInternalName.class" }
|
|
||||||
if (outputFiles.size() != 1) exception("More than one class file found. Note that lambdas, classes and objects are unsupported yet.\n${outputFiles.makeString("\n")}")
|
|
||||||
|
|
||||||
var resultValue: Value? = null
|
|
||||||
|
|
||||||
val virtualMachine = context.getDebugProcess().getVirtualMachineProxy().getVirtualMachine()
|
|
||||||
|
|
||||||
ClassReader(outputFiles.first().asByteArray()).accept(object : ClassVisitor(ASM5) {
|
|
||||||
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
|
||||||
if (name == extractedFunction.getName()) {
|
|
||||||
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
|
|
||||||
override fun visitEnd() {
|
|
||||||
val value = interpreterLoop(
|
|
||||||
this,
|
|
||||||
makeInitialFrame(this, context.getArgumentsByNames(extractedFunction.getParameterNamesForDebugger())),
|
|
||||||
JDIEval(virtualMachine,
|
|
||||||
context.getClassLoader()!!,
|
|
||||||
context.getSuspendContext().getThread()?.getThreadReference()!!)
|
|
||||||
)
|
|
||||||
|
|
||||||
resultValue = when (value) {
|
|
||||||
is ValueReturned -> value.result
|
|
||||||
is ExceptionThrown -> exception(value.exception.toString())
|
|
||||||
is AbnormalTermination -> exception(value.message)
|
|
||||||
else -> throw IllegalStateException("Unknown result value produced by eval4j")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visitMethod(access, name, desc, signature, exceptions)
|
|
||||||
}
|
}
|
||||||
}, 0)
|
}
|
||||||
|
return super.visitMethod(access, name, desc, signature, exceptions)
|
||||||
if (resultValue == null) exception("Cannot evaluate expression")
|
|
||||||
|
|
||||||
return resultValue!!.asJdiValue(virtualMachine, resultValue!!.asmType)
|
|
||||||
}
|
}
|
||||||
})
|
}, 0)
|
||||||
|
|
||||||
|
if (resultValue == null) exception("Cannot evaluate expression")
|
||||||
|
|
||||||
|
return resultValue!!.asJdiValue(virtualMachine, resultValue!!.asmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getModifier(): Modifier? {
|
override fun getModifier(): Modifier? {
|
||||||
@@ -203,6 +171,39 @@ class KotlinEvaluator(val codeFragment: PsiElement,
|
|||||||
return Collections.emptyList()
|
return Collections.emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createClassFileFactory(extractedFunction: JetNamedFunction): ClassFileFactory {
|
||||||
|
return ApplicationManager.getApplication()?.runReadAction(object: Computable<ClassFileFactory> {
|
||||||
|
override fun compute(): ClassFileFactory? {
|
||||||
|
val file = createFileForDebugger(codeFragment as JetExpressionCodeFragment, extractedFunction)
|
||||||
|
|
||||||
|
val analyzeExhaust = file.getAnalysisResults()
|
||||||
|
val bindingContext = analyzeExhaust.getBindingContext()
|
||||||
|
try {
|
||||||
|
analyzeExhaust.throwIfError()
|
||||||
|
AnalyzingUtils.throwExceptionOnErrors(bindingContext)
|
||||||
|
}
|
||||||
|
catch (e: IllegalStateException) {
|
||||||
|
exception(e.getMessage() ?: "Exception from kotlin compiler")
|
||||||
|
}
|
||||||
|
|
||||||
|
val state = GenerationState(
|
||||||
|
file.getProject(),
|
||||||
|
ClassBuilderFactories.BINARIES,
|
||||||
|
bindingContext,
|
||||||
|
Collections.singletonList(file),
|
||||||
|
true)
|
||||||
|
|
||||||
|
KotlinCodegenFacade.compileCorrectFiles(state) {
|
||||||
|
e, msg ->
|
||||||
|
exception("$msg\n${e?.getMessage() ?: ""}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.getFactory()
|
||||||
|
}
|
||||||
|
})!!
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private fun exception(msg: String) = throw EvaluateExceptionUtil.createEvaluateException(msg)
|
private fun exception(msg: String) = throw EvaluateExceptionUtil.createEvaluateException(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,43 +238,47 @@ private fun getFunctionForExtractedFragment(
|
|||||||
breakpointFile: PsiFile,
|
breakpointFile: PsiFile,
|
||||||
breakpointLine: Int
|
breakpointLine: Int
|
||||||
): JetNamedFunction? {
|
): JetNamedFunction? {
|
||||||
val project = codeFragment.getProject()
|
return ApplicationManager.getApplication()?.runReadAction(object: Computable<JetNamedFunction> {
|
||||||
|
override fun compute(): JetNamedFunction? {
|
||||||
|
val project = codeFragment.getProject()
|
||||||
|
|
||||||
val originalFile = breakpointFile as JetFile
|
val originalFile = breakpointFile as JetFile
|
||||||
|
|
||||||
val lineStart = CodeInsightUtils.getStartLineOffset(originalFile, breakpointLine)
|
val lineStart = CodeInsightUtils.getStartLineOffset(originalFile, breakpointLine)
|
||||||
if (lineStart == null) return null
|
if (lineStart == null) return null
|
||||||
|
|
||||||
val tmpFile = originalFile.createTempCopy { it }
|
val tmpFile = originalFile.createTempCopy { it }
|
||||||
val elementAtOffset = tmpFile.findElementAt(lineStart)
|
val elementAtOffset = tmpFile.findElementAt(lineStart)
|
||||||
if (elementAtOffset == null) return null
|
if (elementAtOffset == null) return null
|
||||||
|
|
||||||
val element: PsiElement = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
|
val element: PsiElement = CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
|
||||||
|
|
||||||
val debugExpression = JetPsiFactory.createExpression(project, codeFragment.getText())
|
val debugExpression = JetPsiFactory.createExpression(project, codeFragment.getText())
|
||||||
|
|
||||||
val parent = element.getParent()
|
val parent = element.getParent()
|
||||||
if (parent == null) return null
|
if (parent == null) return null
|
||||||
|
|
||||||
parent.addBefore(JetPsiFactory.createNewLine(project), element)
|
parent.addBefore(JetPsiFactory.createNewLine(project), element)
|
||||||
val newDebugExpression = parent.addBefore(debugExpression, element)
|
val newDebugExpression = parent.addBefore(debugExpression, element)
|
||||||
if (newDebugExpression == null) return null
|
if (newDebugExpression == null) return null
|
||||||
|
|
||||||
parent.addBefore(JetPsiFactory.createNewLine(project), element)
|
parent.addBefore(JetPsiFactory.createNewLine(project), element)
|
||||||
|
|
||||||
val nextSibling = tmpFile.getDeclarations().firstOrNull()
|
val nextSibling = tmpFile.getDeclarations().firstOrNull()
|
||||||
if (nextSibling == null) return null
|
if (nextSibling == null) return null
|
||||||
|
|
||||||
val analysisResult = ExtractionData(tmpFile, Collections.singletonList(newDebugExpression), nextSibling).performAnalysis()
|
val analysisResult = ExtractionData(tmpFile, Collections.singletonList(newDebugExpression), nextSibling).performAnalysis()
|
||||||
if (analysisResult is MaybeError) {
|
if (analysisResult is MaybeError) {
|
||||||
throw EvaluateExceptionUtil.createEvaluateException(analysisResult.error)
|
throw EvaluateExceptionUtil.createEvaluateException(analysisResult.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
val validationResult = (analysisResult as MaybeValue).value.validate()
|
val validationResult = (analysisResult as MaybeValue).value.validate()
|
||||||
if (!validationResult.conflicts.isEmpty()) {
|
if (!validationResult.conflicts.isEmpty()) {
|
||||||
throw EvaluateExceptionUtil.createEvaluateException("Some declarations are unavailable")
|
throw EvaluateExceptionUtil.createEvaluateException("Some declarations are unavailable")
|
||||||
}
|
}
|
||||||
|
|
||||||
return validationResult.descriptor.generateFunction(true)
|
return validationResult.descriptor.generateFunction(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user