Debugger: Do not require a SourcePosition to exist

This commit is contained in:
Yan Zhulanow
2019-03-25 22:14:38 +03:00
parent 81a7f73db5
commit 2c5e8dab23
3 changed files with 32 additions and 52 deletions
@@ -95,10 +95,14 @@ class KotlinDebuggerCaches(project: Project) {
fun compileCodeFragmentCacheAware( fun compileCodeFragmentCacheAware(
codeFragment: KtCodeFragment, codeFragment: KtCodeFragment,
sourcePosition: SourcePosition, sourcePosition: SourcePosition?,
compileCode: () -> CompiledDataDescriptor, compileCode: () -> CompiledDataDescriptor,
force: Boolean = false force: Boolean = false
): Pair<CompiledDataDescriptor, Boolean> { ): Pair<CompiledDataDescriptor, Boolean> {
if (sourcePosition == null) {
return Pair(compileCode(), false)
}
val evaluateExpressionCache = getInstance(codeFragment.project) val evaluateExpressionCache = getInstance(codeFragment.project)
val text = "${codeFragment.importsToString()}\n${codeFragment.text}" val text = "${codeFragment.importsToString()}\n${codeFragment.text}"
@@ -18,25 +18,20 @@ package org.jetbrains.kotlin.idea.debugger.evaluate
import com.intellij.debugger.SourcePosition import com.intellij.debugger.SourcePosition
import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.SuspendContext
import com.intellij.debugger.engine.evaluation.EvaluateException import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.engine.evaluation.expression.* import com.intellij.debugger.engine.evaluation.expression.*
import com.intellij.diagnostic.LogMessageEx
import com.intellij.openapi.diagnostic.Attachment import com.intellij.openapi.diagnostic.Attachment
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.DumbService
import com.intellij.psi.JavaPsiFacade import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.testFramework.runInEdtAndWait import com.intellij.testFramework.runInEdtAndWait
import com.intellij.util.ExceptionUtil
import com.sun.jdi.* import com.sun.jdi.*
import com.sun.jdi.Value import com.sun.jdi.Value
import com.sun.jdi.request.EventRequest
import org.jetbrains.eval4j.* import org.jetbrains.eval4j.*
import org.jetbrains.eval4j.Value as Eval4JValue import org.jetbrains.eval4j.Value as Eval4JValue
import org.jetbrains.eval4j.jdi.JDIEval import org.jetbrains.eval4j.jdi.JDIEval
@@ -85,36 +80,15 @@ internal const val GENERATED_CLASS_NAME = "Generated_for_debugger_class"
object KotlinEvaluatorBuilder : EvaluatorBuilder { object KotlinEvaluatorBuilder : EvaluatorBuilder {
override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator { override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator {
if (codeFragment !is KtCodeFragment || position == null) { if (codeFragment !is KtCodeFragment) {
return EvaluatorBuilderImpl.getInstance().build(codeFragment, position) return EvaluatorBuilderImpl.getInstance().build(codeFragment, position)
} }
if (position.line < 0 && position.file !is KtFile) { val context = codeFragment.context ?: evaluationException("Cannot evaluate an expression without a context")
evaluationException("Couldn't evaluate Kotlin expression at $position") val file = context.containingFile
}
val file = position.file if (file !is KtFile) {
if (file is KtFile && position.line >= 0) { reportError(codeFragment, position, "Unknown context${codeFragment.context?.javaClass}")
val document = PsiDocumentManager.getInstance(file.project).getDocument(file)
if (document == null || document.lineCount < position.line) {
evaluationException(
"Couldn't evaluate Kotlin expression: breakpoint is placed outside the file. " +
"It may happen when you've changed source file after starting a debug process."
)
}
}
if (codeFragment.context !is KtElement) {
val attachments = arrayOf(
attachmentByPsiFile(position.file),
attachmentByPsiFile(codeFragment),
Attachment("breakpoint.info", "line: ${position.line}")
)
LOG.error(
"Trying to evaluate ${codeFragment::class.java} with context ${codeFragment.context?.javaClass}",
mergeAttachments(*attachments)
)
evaluationException("Couldn't evaluate Kotlin expression in this context") evaluationException("Couldn't evaluate Kotlin expression in this context")
} }
@@ -122,7 +96,7 @@ object KotlinEvaluatorBuilder : EvaluatorBuilder {
} }
} }
class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: SourcePosition) : Evaluator { class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: SourcePosition?) : Evaluator {
override fun evaluate(context: EvaluationContextImpl): Any? { override fun evaluate(context: EvaluationContextImpl): Any? {
if (codeFragment.text.isEmpty()) { if (codeFragment.text.isEmpty()) {
return context.debugProcess.virtualMachineProxy.mirrorOfVoid() return context.debugProcess.virtualMachineProxy.mirrorOfVoid()
@@ -157,25 +131,10 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
evaluationException(e) evaluationException(e)
} }
val text = runReadAction { codeFragment.context?.text ?: "null" } reportError(codeFragment, sourcePosition, e.message ?: "An exception occurred", e)
val attachments = arrayOf(
attachmentByPsiFile(sourcePosition.file),
attachmentByPsiFile(codeFragment),
Attachment("breakpoint.info", "line: ${runReadAction { sourcePosition.line }}"),
Attachment("context.info", text)
)
LOG.error(
@Suppress("DEPRECATION")
LogMessageEx.createEvent(
"Couldn't evaluate expression",
ExceptionUtil.getThrowableText(e),
mergeAttachments(*attachments)
)
)
val cause = if (e.message != null) ": ${e.message}" else "" val cause = if (e.message != null) ": ${e.message}" else ""
evaluationException("An exception occurs during Evaluate Expression Action $cause") evaluationException("Cannot evaluate the expression: $cause")
} }
} }
@@ -466,5 +425,22 @@ private fun isSpecialException(th: Throwable): Boolean {
} }
} }
private fun reportError(codeFragment: KtCodeFragment, position: SourcePosition?, message: String, throwable: Throwable? = null) {
val contextFile = codeFragment.context?.containingFile
val attachments = arrayOf(
attachmentByPsiFile(contextFile),
attachmentByPsiFile(codeFragment),
Attachment("breakpoint.info", "Position: " + position?.run { "${file.name}:$line" }),
Attachment("context.info", runReadAction { codeFragment.context?.text ?: "null" })
)
LOG.error(
"Cannot evaluate a code fragment of type " + codeFragment::class.java + ": " + message.decapitalize(),
throwable,
mergeAttachments(*attachments)
)
}
private fun evaluationException(msg: String): Nothing = throw EvaluateExceptionUtil.createEvaluateException(msg) private fun evaluationException(msg: String): Nothing = throw EvaluateExceptionUtil.createEvaluateException(msg)
private fun evaluationException(e: Throwable): Nothing = throw EvaluateExceptionUtil.createEvaluateException(e) private fun evaluationException(e: Throwable): Nothing = throw EvaluateExceptionUtil.createEvaluateException(e)
@@ -13,10 +13,10 @@ data class CompiledDataDescriptor(
val parameters: List<CodeFragmentParameter.Dumb>, val parameters: List<CodeFragmentParameter.Dumb>,
val crossingBounds: Set<CodeFragmentParameter.Dumb>, val crossingBounds: Set<CodeFragmentParameter.Dumb>,
val mainMethodSignature: CodeFragmentCompiler.MethodSignature, val mainMethodSignature: CodeFragmentCompiler.MethodSignature,
val sourcePosition: SourcePosition val sourcePosition: SourcePosition?
) { ) {
companion object { companion object {
fun from(result: CodeFragmentCompiler.CompilationResult, sourcePosition: SourcePosition): CompiledDataDescriptor { fun from(result: CodeFragmentCompiler.CompilationResult, sourcePosition: SourcePosition?): CompiledDataDescriptor {
val localFunctionSuffixes = result.localFunctionSuffixes val localFunctionSuffixes = result.localFunctionSuffixes
val dumbParameters = ArrayList<CodeFragmentParameter.Dumb>(result.parameterInfo.parameters.size) val dumbParameters = ArrayList<CodeFragmentParameter.Dumb>(result.parameterInfo.parameters.size)