Debugger: Implement error reporting for evaluator
This commit is contained in:
+9
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger
|
||||
|
||||
import com.intellij.debugger.engine.DebugProcessImpl
|
||||
import com.intellij.debugger.DebuggerContext
|
||||
import com.intellij.debugger.engine.JavaStackFrame
|
||||
import com.intellij.debugger.engine.JavaValue
|
||||
@@ -50,6 +51,7 @@ import org.jetbrains.kotlin.codegen.inline.INLINE_FUN_VAR_SUFFIX
|
||||
import org.jetbrains.kotlin.codegen.inline.isFakeLocalVariableForInline
|
||||
import org.jetbrains.org.objectweb.asm.Type as AsmType
|
||||
import org.jetbrains.kotlin.utils.getSafe
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerEvaluator
|
||||
import java.lang.reflect.Modifier
|
||||
import java.util.*
|
||||
|
||||
@@ -60,6 +62,13 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe
|
||||
|
||||
private val kotlinVariableViewService = ToggleKotlinVariablesState.getService()
|
||||
|
||||
private val kotlinEvaluator by lazy {
|
||||
val debugProcess = descriptor.debugProcess as DebugProcessImpl // Cast as in JavaStackFrame
|
||||
KotlinDebuggerEvaluator(debugProcess, this@KotlinStackFrame)
|
||||
}
|
||||
|
||||
override fun getEvaluator() = kotlinEvaluator
|
||||
|
||||
override fun superBuildVariables(evaluationContext: EvaluationContextImpl, children: XValueChildrenList) {
|
||||
if (!kotlinVariableViewService.kotlinVariableView) {
|
||||
return super.superBuildVariables(evaluationContext, children)
|
||||
|
||||
+10
-2
@@ -68,7 +68,7 @@ class KotlinCodeFragmentFactory : CodeFragmentFactory() {
|
||||
}
|
||||
|
||||
val codeFragment = constructor(project, "fragment.kt", item.text, initImports(item.imports), contextElement)
|
||||
supplyDebugLabels(codeFragment, context)
|
||||
supplyDebugInformation(item, codeFragment, context)
|
||||
|
||||
codeFragment.putCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR, { expression: KtExpression ->
|
||||
val debuggerContext = DebuggerManagerEx.getInstanceEx(project).context
|
||||
@@ -139,10 +139,16 @@ class KotlinCodeFragmentFactory : CodeFragmentFactory() {
|
||||
return codeFragment
|
||||
}
|
||||
|
||||
private fun supplyDebugLabels(codeFragment: KtCodeFragment, context: PsiElement?) {
|
||||
private fun supplyDebugInformation(item: TextWithImports, codeFragment: KtCodeFragment, context: PsiElement?) {
|
||||
val project = codeFragment.project
|
||||
val debugProcess = getDebugProcess(project, context) ?: return
|
||||
|
||||
DebugLabelPropertyDescriptorProvider(codeFragment, debugProcess).supplyDebugLabels()
|
||||
|
||||
val evaluator = debugProcess.session.xDebugSession?.currentStackFrame?.evaluator
|
||||
if (evaluator is KotlinDebuggerEvaluator) {
|
||||
codeFragment.putUserData(EVALUATION_TYPE, evaluator.getType(item))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDebugProcess(project: Project, context: PsiElement?): DebugProcessImpl? {
|
||||
@@ -295,6 +301,8 @@ class KotlinCodeFragmentFactory : CodeFragmentFactory() {
|
||||
@get:TestOnly
|
||||
val DEBUG_CONTEXT_FOR_TESTS: Key<DebuggerContextImpl> = Key.create("DEBUG_CONTEXT_FOR_TESTS")
|
||||
|
||||
val EVALUATION_TYPE: Key<KotlinDebuggerEvaluator.EvaluationType> = Key.create("DEBUG_EVALUATION_TYPE")
|
||||
|
||||
const val FAKE_JAVA_CONTEXT_FUNCTION_NAME = "_java_locals_debug_fun_"
|
||||
const val FAKE_JAVA_THIS_NAME = "\$this\$_java_locals_debug_fun_"
|
||||
|
||||
|
||||
+86
-39
@@ -22,6 +22,7 @@ import com.intellij.debugger.engine.evaluation.EvaluateException
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import com.intellij.debugger.engine.evaluation.expression.*
|
||||
import com.intellij.lang.java.JavaLanguage
|
||||
import com.intellij.openapi.diagnostic.Attachment
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
@@ -42,6 +43,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.core.util.attachmentByPsiFile
|
||||
import org.jetbrains.kotlin.idea.core.util.mergeAttachments
|
||||
import org.jetbrains.kotlin.idea.core.util.runInReadActionWithWriteActionPriorityWithPCE
|
||||
@@ -65,6 +67,8 @@ import org.jetbrains.kotlin.resolve.isInlineClassType
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.EvaluationStatus.EvaluationContextLanguage
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.compilingEvaluator.ClassLoadingResult
|
||||
import java.util.*
|
||||
|
||||
internal val LOG = Logger.getInstance(KotlinEvaluator::class.java)
|
||||
@@ -93,35 +97,68 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
return context.debugProcess.virtualMachineProxy.mirrorOfVoid()
|
||||
}
|
||||
|
||||
val status = EvaluationStatus()
|
||||
|
||||
val evaluationType = codeFragment.getUserData(KotlinCodeFragmentFactory.EVALUATION_TYPE)
|
||||
if (evaluationType != null) {
|
||||
status.evaluationType(evaluationType)
|
||||
}
|
||||
|
||||
val language = when (codeFragment.language) {
|
||||
KotlinLanguage.INSTANCE -> EvaluationContextLanguage.Kotlin
|
||||
JavaLanguage.INSTANCE -> EvaluationContextLanguage.Java
|
||||
else -> EvaluationContextLanguage.Other
|
||||
}
|
||||
|
||||
status.contextLanguage(language)
|
||||
|
||||
try {
|
||||
return evaluateWithStatus(context, status)
|
||||
} finally {
|
||||
status.send()
|
||||
}
|
||||
}
|
||||
|
||||
private fun evaluateWithStatus(context: EvaluationContextImpl, status: EvaluationStatus): Any? {
|
||||
if (DumbService.getInstance(codeFragment.project).isDumb) {
|
||||
evaluationException("Code fragment evaluation is not available in the dumb mode")
|
||||
}
|
||||
|
||||
val frameProxy = context.frameProxy
|
||||
?: evaluationException("Cannot evaluate a code fragment: frame proxy is not available")
|
||||
val frameProxy = context.frameProxy ?: run {
|
||||
status.error(EvaluationError.NoFrameProxy)
|
||||
evaluationException("Cannot evaluate a code fragment: frame proxy is not available")
|
||||
}
|
||||
|
||||
val operatingThread = context.suspendContext.thread
|
||||
?: evaluationException("Cannot evaluate a code fragment: thread is not available")
|
||||
val operatingThread = context.suspendContext.thread ?: run {
|
||||
status.error(EvaluationError.ThreadNotAvailable)
|
||||
evaluationException("Cannot evaluate a code fragment: thread is not available")
|
||||
}
|
||||
|
||||
if (!operatingThread.isSuspended) {
|
||||
status.error(EvaluationError.ThreadNotSuspended)
|
||||
evaluationException("Evaluation is available only for the suspended threads")
|
||||
}
|
||||
|
||||
try {
|
||||
val executionContext = ExecutionContext(context, frameProxy)
|
||||
return evaluateSafe(executionContext)
|
||||
return evaluateSafe(executionContext, status)
|
||||
} catch (e: EvaluateException) {
|
||||
status.error(EvaluationError.EvaluateException)
|
||||
throw e
|
||||
} catch (e: ProcessCanceledException) {
|
||||
status.error(EvaluationError.ProcessCancelledException)
|
||||
evaluationException(e)
|
||||
} catch (e: Eval4JInterpretingException) {
|
||||
status.error(EvaluationError.InterpretingException)
|
||||
evaluationException(e.cause)
|
||||
} catch (e: Exception) {
|
||||
val isSpecialException = isSpecialException(e)
|
||||
if (isSpecialException) {
|
||||
status.error(EvaluationError.SpecialException)
|
||||
evaluationException(e)
|
||||
}
|
||||
|
||||
status.error(EvaluationError.GenericException)
|
||||
reportError(codeFragment, sourcePosition, e.message ?: "An exception occurred", e)
|
||||
|
||||
val cause = if (e.message != null) ": ${e.message}" else ""
|
||||
@@ -129,38 +166,44 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
}
|
||||
}
|
||||
|
||||
private fun evaluateSafe(context: ExecutionContext): Any? {
|
||||
fun compilerFactory(): CompiledDataDescriptor = compileCodeFragment(context)
|
||||
private fun evaluateSafe(context: ExecutionContext, status: EvaluationStatus): Any? {
|
||||
fun compilerFactory(): CompiledDataDescriptor = compileCodeFragment(context, status)
|
||||
|
||||
val (compiledData, isCompiledDataFromCache) = compileCodeFragmentCacheAware(codeFragment, sourcePosition, ::compilerFactory)
|
||||
val classLoaderRef = loadClassesSafely(context, compiledData.classes)
|
||||
val (compiledData, _) = compileCodeFragmentCacheAware(codeFragment, sourcePosition, ::compilerFactory, force = false)
|
||||
|
||||
val classLoadingResult = loadClassesSafely(context, compiledData.classes)
|
||||
val classLoaderRef = (classLoadingResult as? ClassLoadingResult.Success)?.classLoader
|
||||
|
||||
if (classLoadingResult is ClassLoadingResult.Failure) {
|
||||
status.classLoadingFailed()
|
||||
}
|
||||
|
||||
val result = if (classLoaderRef != null) {
|
||||
evaluateWithCompilation(context, compiledData, classLoaderRef)
|
||||
?: evaluateWithEval4J(context, compiledData, classLoaderRef)
|
||||
try {
|
||||
status.usedEvaluator(EvaluationStatus.EvaluatorType.Bytecode)
|
||||
return evaluateWithCompilation(context, compiledData, classLoaderRef)
|
||||
} catch (e: Throwable) {
|
||||
status.compilingEvaluatorFailed()
|
||||
LOG.warn("Compiling evaluator failed", e)
|
||||
|
||||
status.usedEvaluator(EvaluationStatus.EvaluatorType.Eval4j)
|
||||
evaluateWithEval4J(context, compiledData, classLoaderRef)
|
||||
}
|
||||
} else {
|
||||
status.usedEvaluator(EvaluationStatus.EvaluatorType.Eval4j)
|
||||
evaluateWithEval4J(context, compiledData, classLoaderRef)
|
||||
}
|
||||
|
||||
// If bytecode was taken from cache and exception was thrown - recompile bytecode and run eval4j again
|
||||
if (isCompiledDataFromCache && result is ExceptionThrown && result.kind == ExceptionThrown.ExceptionKind.BROKEN_CODE) {
|
||||
val (recompiledData, _) = compileCodeFragmentCacheAware(codeFragment, sourcePosition, ::compilerFactory, force = true)
|
||||
return evaluateWithEval4J(context, recompiledData, classLoaderRef).toJdiValue(context)
|
||||
}
|
||||
|
||||
return when (result) {
|
||||
is InterpreterResult -> result.toJdiValue(context)
|
||||
else -> result
|
||||
}
|
||||
return result.toJdiValue(context)
|
||||
}
|
||||
|
||||
private fun compileCodeFragment(context: ExecutionContext): CompiledDataDescriptor {
|
||||
private fun compileCodeFragment(context: ExecutionContext, status: EvaluationStatus): CompiledDataDescriptor {
|
||||
val debugProcess = context.debugProcess
|
||||
var analysisResult = checkForErrors(codeFragment, debugProcess)
|
||||
var analysisResult = analyze(codeFragment, status, debugProcess)
|
||||
|
||||
if (codeFragment.wrapToStringIfNeeded(analysisResult.bindingContext)) {
|
||||
// Repeat analysis with toString() added
|
||||
analysisResult = checkForErrors(codeFragment, debugProcess)
|
||||
analysisResult = analyze(codeFragment, status, debugProcess)
|
||||
}
|
||||
|
||||
val (bindingContext) = runReadAction {
|
||||
@@ -172,8 +215,13 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
|
||||
val moduleDescriptor = analysisResult.moduleDescriptor
|
||||
|
||||
val result = CodeFragmentCompiler(context).compile(codeFragment, bindingContext, moduleDescriptor)
|
||||
return createCompiledDataDescriptor(result, sourcePosition)
|
||||
try {
|
||||
val result = CodeFragmentCompiler(context).compile(codeFragment, bindingContext, moduleDescriptor)
|
||||
return createCompiledDataDescriptor(result, sourcePosition)
|
||||
} catch (e: Throwable) {
|
||||
status.error(EvaluationError.BackendException)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtCodeFragment.wrapToStringIfNeeded(bindingContext: BindingContext): Boolean {
|
||||
@@ -205,7 +253,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
val files: List<KtFile>
|
||||
)
|
||||
|
||||
private fun checkForErrors(codeFragment: KtCodeFragment, debugProcess: DebugProcessImpl): ErrorCheckingResult {
|
||||
private fun analyze(codeFragment: KtCodeFragment, status: EvaluationStatus, debugProcess: DebugProcessImpl): ErrorCheckingResult {
|
||||
return runInReadActionWithWriteActionPriorityWithPCE {
|
||||
try {
|
||||
AnalyzingUtils.checkForSyntacticErrors(codeFragment)
|
||||
@@ -221,6 +269,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
val analysisResult = resolutionFacade.analyzeWithAllCompilerChecks(filesToAnalyze)
|
||||
|
||||
if (analysisResult.isError()) {
|
||||
status.error(EvaluationError.FrontendException)
|
||||
evaluationException(analysisResult.error)
|
||||
}
|
||||
|
||||
@@ -229,7 +278,10 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
bindingContext.diagnostics
|
||||
.filter { it.factory !in IGNORED_DIAGNOSTICS }
|
||||
.firstOrNull { it.severity == Severity.ERROR && it.psiElement.containingFile == codeFragment }
|
||||
?.let { evaluationException(DefaultErrorMessages.render(it)) }
|
||||
?.let {
|
||||
status.error(EvaluationError.ErrorsInCode)
|
||||
evaluationException(DefaultErrorMessages.render(it))
|
||||
}
|
||||
|
||||
ErrorCheckingResult(bindingContext, analysisResult.moduleDescriptor, Collections.singletonList(codeFragment))
|
||||
}
|
||||
@@ -240,17 +292,12 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
|
||||
compiledData: CompiledDataDescriptor,
|
||||
classLoader: ClassLoaderReference
|
||||
): Value? {
|
||||
return try {
|
||||
runEvaluation(context, compiledData, classLoader) { args ->
|
||||
val mainClassType = context.findClass(GENERATED_CLASS_NAME, classLoader) as? ClassType
|
||||
?: error("Can not find class \"$GENERATED_CLASS_NAME\"")
|
||||
val mainMethod = mainClassType.methods().single { it.name() == GENERATED_FUNCTION_NAME }
|
||||
val returnValue = context.invokeMethod(mainClassType, mainMethod, args)
|
||||
EvaluatorValueConverter(context).unref(returnValue)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
LOG.error("Unable to evaluate the expression with compilation", e)
|
||||
return null
|
||||
return runEvaluation(context, compiledData, classLoader) { args ->
|
||||
val mainClassType = context.findClass(GENERATED_CLASS_NAME, classLoader) as? ClassType
|
||||
?: error("Can not find class \"$GENERATED_CLASS_NAME\"")
|
||||
val mainMethod = mainClassType.methods().single { it.name() == GENERATED_FUNCTION_NAME }
|
||||
val returnValue = context.invokeMethod(mainClassType, mainMethod, args)
|
||||
EvaluatorValueConverter(context).unref(returnValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-7
@@ -23,21 +23,32 @@ import org.jetbrains.kotlin.idea.debugger.evaluate.LOG
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassLoadingAdapter
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad
|
||||
|
||||
fun loadClassesSafely(context: ExecutionContext, classes: Collection<ClassToLoad>): ClassLoaderReference? {
|
||||
sealed class ClassLoadingResult {
|
||||
class Success(val classLoader: ClassLoaderReference) : ClassLoadingResult()
|
||||
class Failure(val error: Throwable) : ClassLoadingResult()
|
||||
object NotNeeded : ClassLoadingResult()
|
||||
}
|
||||
|
||||
fun loadClassesSafely(context: ExecutionContext, classes: Collection<ClassToLoad>): ClassLoadingResult {
|
||||
if (classes.isEmpty()) {
|
||||
return ClassLoadingResult.NotNeeded
|
||||
}
|
||||
|
||||
return try {
|
||||
loadClasses(context, classes)
|
||||
val cl = loadClasses(context, classes)
|
||||
if (cl != null) {
|
||||
ClassLoadingResult.Success(cl)
|
||||
} else {
|
||||
ClassLoadingResult.NotNeeded
|
||||
}
|
||||
} catch (e: EvaluateException) {
|
||||
throw e
|
||||
} catch (e: Throwable) {
|
||||
LOG.debug("Failed to evaluate expression", e)
|
||||
null
|
||||
ClassLoadingResult.Failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadClasses(context: ExecutionContext, classes: Collection<ClassToLoad>): ClassLoaderReference? {
|
||||
if (classes.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return ClassLoadingAdapter.loadClasses(context, classes)
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.evaluate
|
||||
|
||||
import org.jetbrains.kotlin.idea.statistics.FUSEventGroups
|
||||
import org.jetbrains.kotlin.idea.statistics.KotlinFUSLogger
|
||||
|
||||
class EvaluationStatus {
|
||||
private var error: EvaluationError? = null
|
||||
private var values = mutableMapOf<String, String>()
|
||||
private var evaluationTypeSet: Boolean = false
|
||||
|
||||
fun error(kind: EvaluationError) {
|
||||
if (error == null) {
|
||||
error = kind
|
||||
}
|
||||
}
|
||||
|
||||
fun flag(name: String, value: Boolean) {
|
||||
values[name] = value.toString()
|
||||
}
|
||||
|
||||
fun <T : Enum<T>> value(name: String, value: T) {
|
||||
values[name] = value.name
|
||||
}
|
||||
|
||||
fun evaluationType(type: KotlinDebuggerEvaluator.EvaluationType) {
|
||||
evaluationTypeSet = true
|
||||
value("evaluationType", type)
|
||||
}
|
||||
|
||||
fun send() {
|
||||
if (!evaluationTypeSet) {
|
||||
return
|
||||
}
|
||||
|
||||
val statusName = error?.name ?: "Success"
|
||||
KotlinFUSLogger.log(FUSEventGroups.DebugEval, statusName, values)
|
||||
}
|
||||
|
||||
enum class EvaluatorType {
|
||||
Bytecode, Eval4j
|
||||
}
|
||||
|
||||
enum class EvaluationContextLanguage {
|
||||
Java, Kotlin, Other
|
||||
}
|
||||
}
|
||||
|
||||
enum class EvaluationError {
|
||||
NoFrameProxy,
|
||||
ThreadNotAvailable,
|
||||
ThreadNotSuspended,
|
||||
|
||||
ProcessCancelledException,
|
||||
InterpretingException,
|
||||
EvaluateException,
|
||||
SpecialException,
|
||||
GenericException,
|
||||
|
||||
FrontendException,
|
||||
BackendException,
|
||||
ErrorsInCode
|
||||
}
|
||||
|
||||
fun EvaluationStatus.classLoadingFailed() {
|
||||
flag("classLoadingFailed", true)
|
||||
}
|
||||
|
||||
fun EvaluationStatus.compilingEvaluatorFailed() {
|
||||
flag("compilingEvaluatorFailed", true)
|
||||
}
|
||||
|
||||
fun EvaluationStatus.usedEvaluator(evaluator: EvaluationStatus.EvaluatorType) {
|
||||
value("evaluator", evaluator)
|
||||
}
|
||||
|
||||
fun EvaluationStatus.contextLanguage(language: EvaluationStatus.EvaluationContextLanguage) {
|
||||
value("contextLanguage", language)
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.evaluate
|
||||
|
||||
import com.intellij.debugger.engine.DebugProcessImpl
|
||||
import com.intellij.debugger.engine.JavaDebuggerEvaluator
|
||||
import com.intellij.debugger.engine.JavaStackFrame
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImports
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.xdebugger.XExpression
|
||||
import com.intellij.xdebugger.XSourcePosition
|
||||
import com.intellij.xdebugger.frame.XValue
|
||||
import com.intellij.xdebugger.impl.evaluate.quick.XValueHint
|
||||
import com.intellij.xdebugger.impl.ui.tree.nodes.EvaluatingExpressionRootNode
|
||||
import com.intellij.xdebugger.impl.ui.tree.nodes.WatchNodeImpl
|
||||
import java.util.*
|
||||
|
||||
private data class Key(val text: String, val imports: String) {
|
||||
constructor(expr: XExpression) : this(expr.expression, StringUtil.notNullize(expr.customInfo))
|
||||
constructor(text: TextWithImports) : this(text.text, text.imports)
|
||||
}
|
||||
|
||||
class KotlinDebuggerEvaluator(
|
||||
debugProcess: DebugProcessImpl?,
|
||||
stackFrame: JavaStackFrame?
|
||||
) : JavaDebuggerEvaluator(debugProcess, stackFrame) {
|
||||
private val types = HashMap<Key, EvaluationType>()
|
||||
|
||||
fun getType(text: TextWithImports): EvaluationType {
|
||||
return types[Key(text)] ?: EvaluationType.UNKNOWN
|
||||
}
|
||||
|
||||
override fun evaluate(expression: XExpression, callback: XEvaluationCallback, expressionPosition: XSourcePosition?) {
|
||||
val key = Key(expression)
|
||||
val type = getType(callback)
|
||||
if (type != null) {
|
||||
types[key] = type
|
||||
}
|
||||
|
||||
val wrappedCallback = object : XEvaluationCallback {
|
||||
override fun errorOccurred(errorMessage: String) {
|
||||
types.remove(key)
|
||||
callback.errorOccurred(errorMessage)
|
||||
}
|
||||
|
||||
override fun evaluated(result: XValue) {
|
||||
types.remove(key)
|
||||
callback.evaluated(result)
|
||||
}
|
||||
}
|
||||
|
||||
super.evaluate(expression, wrappedCallback, expressionPosition)
|
||||
}
|
||||
|
||||
private fun getType(callback: XEvaluationCallback): EvaluationType? {
|
||||
val name = callback.javaClass.name
|
||||
for (value in EvaluationType.values()) {
|
||||
val clazz = value.clazz ?: continue
|
||||
if (name.startsWith(clazz.name)) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
enum class EvaluationType(val clazz: Class<*>?) {
|
||||
WATCH(WatchNodeImpl::class.java),
|
||||
WINDOW(EvaluatingExpressionRootNode::class.java),
|
||||
POPUP(XValueHint::class.java),
|
||||
UNKNOWN(null);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user