Refactoring: Incapsulate method calls into ExecutionContext

This commit is contained in:
Yan Zhulanow
2019-03-07 17:31:09 +03:00
parent 5d156c2edb
commit 6d90e850ff
11 changed files with 179 additions and 165 deletions
@@ -6,49 +6,83 @@
package org.jetbrains.kotlin.idea.debugger.evaluate package org.jetbrains.kotlin.idea.debugger.evaluate
import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.SuspendContextImpl
import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.jdi.VirtualMachineProxyImpl
import com.intellij.openapi.project.Project
import com.sun.jdi.* import com.sun.jdi.*
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
class ExecutionContext(val evaluationContext: EvaluationContextImpl, val thread: ThreadReference, val invokePolicy: Int) { class ExecutionContext(val evaluationContext: EvaluationContextImpl, val frameProxy: StackFrameProxyImpl) {
val vm: VirtualMachine val vm: VirtualMachineProxyImpl
get() = thread.virtualMachine() get() = evaluationContext.debugProcess.virtualMachineProxy
val classLoader: ClassLoaderReference?
get() = evaluationContext.classLoader
val suspendContext: SuspendContextImpl
get() = evaluationContext.suspendContext
val debugProcess: DebugProcessImpl val debugProcess: DebugProcessImpl
get() = evaluationContext.debugProcess get() = evaluationContext.debugProcess
fun loadClassType(asmType: Type, classLoader: ClassLoaderReference? = null): ReferenceType? { val project: Project
if (asmType.sort == Type.ARRAY) { get() = evaluationContext.project
return loadClassType(asmType.elementType, classLoader)
}
if (asmType.sort != Type.OBJECT) { val invokePolicy = evaluationContext.suspendContext.getInvokePolicy()
return null
}
val vm = thread.virtualMachine() @Throws(EvaluateException::class)
val className = asmType.className fun invokeMethod(obj: ObjectReference, method: Method, args: List<Value?>): Value? {
return debugProcess.invokeInstanceMethod(evaluationContext, obj, method, args, invokePolicy)
val classClass = vm.classesByName(Class::class.java.name).firstIsInstanceOrNull<ClassType>() ?: return null
val method: Method?
val args: List<Value?>
if (classLoader != null) {
method = classClass.methodsByName("forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;").firstOrNull()
args = listOf(vm.mirrorOf(className), vm.mirrorOf(true), classLoader)
} else {
method = classClass.methodsByName("forName", "(Ljava/lang/String;)Ljava/lang/Class;").firstOrNull()
args = listOf(vm.mirrorOf(className))
}
if (method == null) {
return null
}
val result = evaluationContext.debugProcess.invokeMethod(evaluationContext, classClass, method, args)
return (result as? ClassObjectReference)?.reflectedType()
} }
fun invokeMethod(type: ClassType, method: Method, args: List<Value?>): Value? {
return debugProcess.invokeMethod(evaluationContext, type, method, args)
}
@Throws(EvaluateException::class)
fun newInstance(type: ClassType, constructor: Method, args: List<Value?>): ObjectReference {
return debugProcess.newInstance(evaluationContext, type, constructor, args)
}
@Throws(EvaluateException::class)
fun newInstance(arrayType: ArrayType, dimension: Int): ArrayReference {
return debugProcess.newInstance(arrayType, dimension)
}
@Throws(
InvocationException::class,
ClassNotLoadedException::class,
IncompatibleThreadStateException::class,
InvalidTypeException::class,
EvaluateException::class
)
fun loadClass(asmType: Type, classLoader: ClassLoaderReference? = null): ReferenceType? {
return loadClass(asmType.className, classLoader)
}
@Throws(
InvocationException::class,
ClassNotLoadedException::class,
IncompatibleThreadStateException::class,
InvalidTypeException::class,
EvaluateException::class
)
fun loadClass(name: String, classLoader: ClassLoaderReference? = null): ReferenceType? {
return debugProcess.loadClass(evaluationContext, name, classLoader)
}
@Throws(EvaluateException::class)
fun findClass(name: String, classLoader: ClassLoaderReference? = null): ReferenceType? {
return debugProcess.findClass(evaluationContext, name, classLoader)
}
fun keepReference(reference: ObjectReference) {
// Not available in older IDEA versions
@Suppress("DEPRECATION")
DebuggerUtilsEx.keep(reference, evaluationContext)
}
} }
@@ -132,8 +132,19 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
evaluationException("Code fragment evaluation is not available in the dumb mode") 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 operatingThread = context.suspendContext.thread
?: evaluationException("Cannot evaluate a code fragment: thread is not available")
if (!operatingThread.isSuspended) {
evaluationException("Evaluation is available only for the suspended threads")
}
try { try {
return evaluateSafe(context) val executionContext = ExecutionContext(context, frameProxy)
return evaluateSafe(executionContext)
} catch (e: EvaluateException) { } catch (e: EvaluateException) {
throw e throw e
} catch (e: ProcessCanceledException) { } catch (e: ProcessCanceledException) {
@@ -168,37 +179,33 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
} }
} }
private fun evaluateSafe(context: EvaluationContextImpl): Any? { private fun evaluateSafe(context: ExecutionContext): Any? {
fun compilerFactory(): CompiledDataDescriptor = compileCodeFragment(context) fun compilerFactory(): CompiledDataDescriptor = compileCodeFragment(context)
val (compiledData, isCompiledDataFromCache) = compileCodeFragmentCacheAware(codeFragment, sourcePosition, ::compilerFactory) val (compiledData, isCompiledDataFromCache) = compileCodeFragmentCacheAware(codeFragment, sourcePosition, ::compilerFactory)
val classLoaderRef = loadClassesSafely(context, compiledData.classes) val classLoaderRef = loadClassesSafely(context, compiledData.classes)
val thread = context.suspendContext.thread?.threadReference ?: error("Can not find a thread to run evaluation on")
val invokePolicy = context.suspendContext.getInvokePolicy()
val executionContext = ExecutionContext(context, thread, invokePolicy)
val result = if (classLoaderRef != null) { val result = if (classLoaderRef != null) {
evaluateWithCompilation(executionContext, compiledData, classLoaderRef) evaluateWithCompilation(context, compiledData, classLoaderRef)
?: evaluateWithEval4J(executionContext, compiledData, classLoaderRef) ?: evaluateWithEval4J(context, compiledData, classLoaderRef)
} else { } else {
evaluateWithEval4J(executionContext, compiledData, classLoaderRef) evaluateWithEval4J(context, compiledData, classLoaderRef)
} }
// If bytecode was taken from cache and exception was thrown - recompile bytecode and run eval4j again // 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) { if (isCompiledDataFromCache && result is ExceptionThrown && result.kind == ExceptionThrown.ExceptionKind.BROKEN_CODE) {
val (recompiledData, _) = compileCodeFragmentCacheAware(codeFragment, sourcePosition, ::compilerFactory, force = true) val (recompiledData, _) = compileCodeFragmentCacheAware(codeFragment, sourcePosition, ::compilerFactory, force = true)
return evaluateWithEval4J(executionContext, recompiledData, classLoaderRef).toJdiValue(executionContext) return evaluateWithEval4J(context, recompiledData, classLoaderRef).toJdiValue(context)
} }
return when (result) { return when (result) {
is InterpreterResult -> result.toJdiValue(executionContext) is InterpreterResult -> result.toJdiValue(context)
else -> result else -> result
} }
} }
private fun compileCodeFragment(evaluationContext: EvaluationContextImpl): CompiledDataDescriptor { private fun compileCodeFragment(context: ExecutionContext): CompiledDataDescriptor {
val debugProcess = evaluationContext.debugProcess val debugProcess = context.debugProcess
var analysisResult = checkForErrors(codeFragment, debugProcess) var analysisResult = checkForErrors(codeFragment, debugProcess)
if (codeFragment.wrapToStringIfNeeded(analysisResult.bindingContext)) { if (codeFragment.wrapToStringIfNeeded(analysisResult.bindingContext)) {
@@ -215,7 +222,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
val moduleDescriptor = analysisResult.moduleDescriptor val moduleDescriptor = analysisResult.moduleDescriptor
val result = CodeFragmentCompiler(evaluationContext).compile(codeFragment, bindingContext, moduleDescriptor) val result = CodeFragmentCompiler(context).compile(codeFragment, bindingContext, moduleDescriptor)
return CompiledDataDescriptor.from(result, sourcePosition) return CompiledDataDescriptor.from(result, sourcePosition)
} }
@@ -285,10 +292,10 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
): Value? { ): Value? {
return try { return try {
runEvaluation(context, compiledData, classLoader) { args -> runEvaluation(context, compiledData, classLoader) { args ->
val mainClassType = context.loadClassType(Type.getObjectType(GENERATED_CLASS_NAME), classLoader) as? ClassType val mainClassType = context.loadClass(Type.getObjectType(GENERATED_CLASS_NAME), classLoader) as? ClassType
?: error("Can not find class \"$GENERATED_CLASS_NAME\"") ?: error("Can not find class \"$GENERATED_CLASS_NAME\"")
val mainMethod = mainClassType.methods().single { it.name() == GENERATED_FUNCTION_NAME } val mainMethod = mainClassType.methods().single { it.name() == GENERATED_FUNCTION_NAME }
val returnValue = context.debugProcess.invokeMethod(context.evaluationContext, mainClassType, mainMethod, args) val returnValue = context.invokeMethod(mainClassType, mainMethod, args)
EvaluatorValueConverter(context).unref(returnValue) EvaluatorValueConverter(context).unref(returnValue)
} }
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -307,7 +314,11 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
val mainMethod = mainClassAsmNode.methods.first { it.name == GENERATED_FUNCTION_NAME } val mainMethod = mainClassAsmNode.methods.first { it.name == GENERATED_FUNCTION_NAME }
return runEvaluation(context, compiledData, classLoader ?: context.evaluationContext.classLoader) { args -> return runEvaluation(context, compiledData, classLoader ?: context.evaluationContext.classLoader) { args ->
val eval = JDIEval(context.vm, classLoader, context.thread, context.invokePolicy) val vm = context.vm.virtualMachine
val thread = context.suspendContext.thread?.threadReference?.takeIf { it.isAtBreakpoint }
?: error("Can not find a thread to run evaluation on")
val eval = JDIEval(vm, classLoader, thread, context.invokePolicy)
interpreterLoop(mainMethod, makeInitialFrame(mainMethod, args.map { it.asValue() }), eval) interpreterLoop(mainMethod, makeInitialFrame(mainMethod, args.map { it.asValue() }), eval)
} }
} }
@@ -321,24 +332,24 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
// Preload additional classes // Preload additional classes
compiledData.classes compiledData.classes
.filter { !it.isMainClass } .filter { !it.isMainClass }
.forEach { context.loadClassType(Type.getObjectType(it.className), classLoader) } .forEach { context.loadClass(Type.getObjectType(it.className), classLoader) }
return context.vm.executeWithBreakpointsDisabled { return context.vm.virtualMachine.executeWithBreakpointsDisabled {
for (parameterType in compiledData.mainMethodSignature.parameterTypes) { for (parameterType in compiledData.mainMethodSignature.parameterTypes) {
context.loadClassType(parameterType, classLoader) context.loadClass(parameterType, classLoader)
} }
val args = context.calculateMainMethodCallArguments(compiledData) val args = calculateMainMethodCallArguments(context, compiledData)
block(args) block(args)
} }
} }
private fun ExecutionContext.calculateMainMethodCallArguments(compiledData: CompiledDataDescriptor): List<Value?> { private fun calculateMainMethodCallArguments(context: ExecutionContext, compiledData: CompiledDataDescriptor): List<Value?> {
val asmValueParameters = compiledData.mainMethodSignature.parameterTypes val asmValueParameters = compiledData.mainMethodSignature.parameterTypes
val valueParameters = compiledData.parameters val valueParameters = compiledData.parameters
require(asmValueParameters.size == valueParameters.size) require(asmValueParameters.size == valueParameters.size)
val args = valueParameters.zip(asmValueParameters) val args = valueParameters.zip(asmValueParameters)
val variableFinder = VariableFinder.instance(this) ?: error("Frame map is not available") val variableFinder = VariableFinder(context)
return args.map { (parameter, asmType) -> return args.map { (parameter, asmType) ->
val result = variableFinder.find(parameter, asmType) val result = variableFinder.find(parameter, asmType)
@@ -347,7 +358,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
val name = parameter.debugString val name = parameter.debugString
fun isInsideDefaultInterfaceMethod(): Boolean { fun isInsideDefaultInterfaceMethod(): Boolean {
val method = evaluationContext.frameProxy?.safeLocation()?.safeMethod() ?: return false val method = context.frameProxy.safeLocation()?.safeMethod() ?: return false
val desc = method.signature() val desc = method.signature()
return method.name().endsWith("\$default") && DEFAULT_METHOD_MARKERS.any { desc.contains("I${it.descriptor})") } return method.name().endsWith("\$default") && DEFAULT_METHOD_MARKERS.any { desc.contains("I${it.descriptor})") }
} }
@@ -359,7 +370,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
} else if (parameter.kind == CodeFragmentParameter.Kind.ORDINARY && isInsideDefaultInterfaceMethod()) { } else if (parameter.kind == CodeFragmentParameter.Kind.ORDINARY && isInsideDefaultInterfaceMethod()) {
evaluationException("Parameter evaluation is not supported for '\$default' methods") evaluationException("Parameter evaluation is not supported for '\$default' methods")
} else { } else {
throw VariableFinder.variableNotFound(evaluationContext, buildString { throw VariableFinder.variableNotFound(context, buildString {
append("Cannot find local variable: name = '").append(name).append("', type = ").append(asmType.className) append("Cannot find local variable: name = '").append(name).append("', type = ").append(asmType.className)
}) })
} }
@@ -376,7 +387,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
private val DEFAULT_METHOD_MARKERS = listOf(AsmTypes.OBJECT_TYPE, AsmTypes.DEFAULT_CONSTRUCTOR_MARKER) private val DEFAULT_METHOD_MARKERS = listOf(AsmTypes.OBJECT_TYPE, AsmTypes.DEFAULT_CONSTRUCTOR_MARKER)
private fun InterpreterResult.toJdiValue(context: ExecutionContext): com.sun.jdi.Value? { private fun InterpreterResult.toJdiValue(context: ExecutionContext): Value? {
val jdiValue = when (this) { val jdiValue = when (this) {
is ValueReturned -> result is ValueReturned -> result
is ExceptionThrown -> { is ExceptionThrown -> {
@@ -394,7 +405,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour
} }
val sharedVar = if ((jdiValue is AbstractValue<*>)) getValueIfSharedVar(jdiValue, context) else null val sharedVar = if ((jdiValue is AbstractValue<*>)) getValueIfSharedVar(jdiValue, context) else null
return sharedVar?.value ?: jdiValue.asJdiValue(context.vm, jdiValue.asmType) return sharedVar?.value ?: jdiValue.asJdiValue(context.vm.virtualMachine, jdiValue.asmType)
} }
private fun getValueIfSharedVar(value: Eval4JValue, context: ExecutionContext): VariableFinder.Result? { private fun getValueIfSharedVar(value: Eval4JValue, context: ExecutionContext): VariableFinder.Result? {
@@ -16,31 +16,26 @@
package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.evaluation.EvaluationContext
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.sun.jdi.* import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
abstract class AbstractAndroidClassLoadingAdapter : ClassLoadingAdapter { abstract class AbstractAndroidClassLoadingAdapter : ClassLoadingAdapter {
protected fun dex(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): ByteArray? { protected fun dex(context: ExecutionContext, classes: Collection<ClassToLoad>): ByteArray? {
return AndroidDexer.getInstances(context.project).single().dex(classes) return AndroidDexer.getInstances(context.project).single().dex(classes)
} }
protected fun wrapToByteBuffer(bytes: ArrayReference, context: EvaluationContext, process: DebugProcessImpl): ObjectReference { protected fun wrapToByteBuffer(bytes: ArrayReference, context: ExecutionContext): ObjectReference {
val byteBufferClass = process.findClass(context, "java.nio.ByteBuffer", context.classLoader) as ClassType val classLoader = context.classLoader
val byteBufferClass = context.findClass("java.nio.ByteBuffer", classLoader) as ClassType
val wrapMethod = byteBufferClass.concreteMethodByName("wrap", "([B)Ljava/nio/ByteBuffer;") val wrapMethod = byteBufferClass.concreteMethodByName("wrap", "([B)Ljava/nio/ByteBuffer;")
?: error("'wrap' method not found") ?: error("'wrap' method not found")
return process.invokeMethod(context, byteBufferClass, wrapMethod, listOf(bytes)) as ObjectReference return context.invokeMethod(byteBufferClass, wrapMethod, listOf(bytes)) as ObjectReference
} }
protected fun DebugProcessImpl.tryLoadClass( protected fun tryLoadClass(context: ExecutionContext, fqName: String, classLoader: ClassLoaderReference?): ReferenceType? {
context: EvaluationContextImpl,
fqName: String,
classLoader: ClassLoaderReference?
): ReferenceType? {
return try { return try {
loadClass(context, fqName, classLoader) context.loadClass(fqName, classLoader)
} catch (e: Throwable) { } catch (e: Throwable) {
null null
} }
@@ -18,44 +18,39 @@ package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading
import com.intellij.debugger.engine.JVMNameUtil import com.intellij.debugger.engine.JVMNameUtil
import com.intellij.debugger.engine.evaluation.EvaluateException import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.sun.jdi.* import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
import org.jetbrains.kotlin.idea.debugger.isDexDebug import org.jetbrains.kotlin.idea.debugger.isDexDebug
class AndroidOClassLoadingAdapter : AbstractAndroidClassLoadingAdapter() { class AndroidOClassLoadingAdapter : AbstractAndroidClassLoadingAdapter() {
override fun isApplicable(context: EvaluationContextImpl, info: ClassLoadingAdapter.Companion.ClassInfoForEvaluator) = with(info) { override fun isApplicable(context: ExecutionContext, info: ClassLoadingAdapter.Companion.ClassInfoForEvaluator) = with(info) {
isCompilingEvaluatorPreferred && context.debugProcess.isDexDebug() isCompilingEvaluatorPreferred && context.debugProcess.isDexDebug()
} }
private fun resolveClassLoaderClass(context: EvaluationContextImpl): ClassType? { private fun resolveClassLoaderClass(context: ExecutionContext): ClassType? {
return try { return try {
context.debugProcess.tryLoadClass( val classLoader = context.classLoader
context, "dalvik.system.InMemoryDexClassLoader", context.classLoader tryLoadClass(context, "dalvik.system.InMemoryDexClassLoader", classLoader) as? ClassType
) as? ClassType
} catch (e: EvaluateException) { } catch (e: EvaluateException) {
null null
} }
} }
override fun loadClasses(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderReference { override fun loadClasses(context: ExecutionContext, classes: Collection<ClassToLoad>): ClassLoaderReference {
val process = context.debugProcess
val inMemoryClassLoaderClass = resolveClassLoaderClass(context) ?: error("InMemoryDexClassLoader class not found") val inMemoryClassLoaderClass = resolveClassLoaderClass(context) ?: error("InMemoryDexClassLoader class not found")
val constructorMethod = inMemoryClassLoaderClass.concreteMethodByName( val constructorMethod = inMemoryClassLoaderClass.concreteMethodByName(
JVMNameUtil.CONSTRUCTOR_NAME, "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V" JVMNameUtil.CONSTRUCTOR_NAME, "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"
) ?: error("Constructor method not found") ) ?: error("Constructor method not found")
val dexBytes = dex(context, classes) ?: error("Can't dex classes") val dexBytes = dex(context, classes) ?: error("Can't dex classes")
val dexBytesMirror = mirrorOfByteArray(dexBytes, context, process) val dexBytesMirror = mirrorOfByteArray(dexBytes, context)
val dexByteBuffer = wrapToByteBuffer(dexBytesMirror, context, process) val dexByteBuffer = wrapToByteBuffer(dexBytesMirror, context)
val newClassLoader = process.newInstance( val classLoader = context.classLoader
context, inMemoryClassLoaderClass, constructorMethod, val args = listOf(dexByteBuffer, classLoader)
listOf(dexByteBuffer, context.classLoader) val newClassLoader = context.newInstance(inMemoryClassLoaderClass, constructorMethod, args) as ClassLoaderReference
) context.keepReference(newClassLoader)
DebuggerUtilsEx.keep(newClassLoader, context) return newClassLoader
return newClassLoader as ClassLoaderReference
} }
} }
@@ -16,13 +16,11 @@
package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.sun.jdi.ArrayReference import com.sun.jdi.ArrayReference
import com.sun.jdi.ArrayType import com.sun.jdi.ArrayType
import com.sun.jdi.ClassLoaderReference import com.sun.jdi.ClassLoaderReference
import com.sun.jdi.Value import com.sun.jdi.Value
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
import org.jetbrains.kotlin.idea.debugger.evaluate.GENERATED_FUNCTION_NAME import org.jetbrains.kotlin.idea.debugger.evaluate.GENERATED_FUNCTION_NAME
import org.jetbrains.org.objectweb.asm.ClassReader import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
@@ -38,7 +36,7 @@ interface ClassLoadingAdapter {
OrdinaryClassLoadingAdapter() OrdinaryClassLoadingAdapter()
) )
fun loadClasses(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderReference? { fun loadClasses(context: ExecutionContext, classes: Collection<ClassToLoad>): ClassLoaderReference? {
val mainClass = classes.firstOrNull { it.isMainClass } ?: return null val mainClass = classes.firstOrNull { it.isMainClass } ?: return null
var info = ClassInfoForEvaluator(containsAdditionalClasses = classes.size > 1) var info = ClassInfoForEvaluator(containsAdditionalClasses = classes.size > 1)
@@ -92,18 +90,19 @@ interface ClassLoadingAdapter {
} }
} }
fun isApplicable(context: EvaluationContextImpl, info: ClassInfoForEvaluator): Boolean fun isApplicable(context: ExecutionContext, info: ClassInfoForEvaluator): Boolean
fun loadClasses(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderReference fun loadClasses(context: ExecutionContext, classes: Collection<ClassToLoad>): ClassLoaderReference
fun mirrorOfByteArray(bytes: ByteArray, context: EvaluationContextImpl, process: DebugProcessImpl): ArrayReference { fun mirrorOfByteArray(bytes: ByteArray, context: ExecutionContext): ArrayReference {
val arrayClass = process.findClass(context, "byte[]", context.classLoader) as ArrayType val classLoader = context.classLoader
val reference = process.newInstance(arrayClass, bytes.size) val arrayClass = context.findClass("byte[]", classLoader) as ArrayType
DebuggerUtilsEx.keep(reference, context) val reference = context.newInstance(arrayClass, bytes.size)
context.keepReference(reference)
val mirrors = ArrayList<Value>(bytes.size) val mirrors = ArrayList<Value>(bytes.size)
for (byte in bytes) { for (byte in bytes) {
mirrors += process.virtualMachineProxy.mirrorOf(byte) mirrors += context.vm.mirrorOf(byte)
} }
var loaded = 0 var loaded = 0
@@ -16,15 +16,12 @@
package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.evaluation.EvaluateException import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.impl.ClassLoadingUtils import com.intellij.debugger.impl.ClassLoadingUtils
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.intellij.debugger.ui.impl.watch.CompilingEvaluatorImpl
import com.intellij.openapi.projectRoots.JavaSdkVersion import com.intellij.openapi.projectRoots.JavaSdkVersion
import com.sun.jdi.ClassLoaderReference import com.sun.jdi.ClassLoaderReference
import com.sun.jdi.ClassType import com.sun.jdi.ClassType
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
import org.jetbrains.kotlin.idea.debugger.isDexDebug import org.jetbrains.kotlin.idea.debugger.isDexDebug
import org.jetbrains.org.objectweb.asm.ClassReader import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.ClassVisitor import org.jetbrains.org.objectweb.asm.ClassVisitor
@@ -63,28 +60,28 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
return classWriter.toByteArray() return classWriter.toByteArray()
} }
fun useMagicAccessor(evaluationContext: EvaluationContextImpl): Boolean { fun useMagicAccessor(context: ExecutionContext): Boolean {
val rawVersion = evaluationContext.debugProcess.virtualMachineProxy.version()?.substringBefore('_') ?: return false val rawVersion = context.vm.version()?.substringBefore('_') ?: return false
val javaVersion = JavaSdkVersion.fromVersionString(rawVersion) ?: return false val javaVersion = JavaSdkVersion.fromVersionString(rawVersion) ?: return false
return !javaVersion.isAtLeast(JavaSdkVersion.JDK_1_9) return !javaVersion.isAtLeast(JavaSdkVersion.JDK_1_9)
} }
} }
override fun isApplicable(context: EvaluationContextImpl, info: ClassLoadingAdapter.Companion.ClassInfoForEvaluator) = with(info) { override fun isApplicable(context: ExecutionContext, info: ClassLoadingAdapter.Companion.ClassInfoForEvaluator): Boolean {
isCompilingEvaluatorPreferred && context.classLoader != null && !context.debugProcess.isDexDebug() return info.isCompilingEvaluatorPreferred && context.classLoader != null && !context.debugProcess.isDexDebug()
} }
override fun loadClasses(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderReference { override fun loadClasses(context: ExecutionContext, classes: Collection<ClassToLoad>): ClassLoaderReference {
val process = context.debugProcess val process = context.debugProcess
val classLoader = try { val classLoader = try {
ClassLoadingUtils.getClassLoader(context, process) ClassLoadingUtils.getClassLoader(context.evaluationContext, process)
} catch (e: Exception) { } catch (e: Exception) {
throw EvaluateException("Error creating evaluation class loader: $e", e) throw EvaluateException("Error creating evaluation class loader: $e", e)
} }
try { try {
defineClasses(classes, context, process, classLoader) defineClasses(classes, context, classLoader)
} catch (e: Exception) { } catch (e: Exception) {
throw EvaluateException("Error during classes definition $e", e) throw EvaluateException("Error during classes definition $e", e)
} }
@@ -94,8 +91,7 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
private fun defineClasses( private fun defineClasses(
classes: Collection<ClassToLoad>, classes: Collection<ClassToLoad>,
context: EvaluationContextImpl, context: ExecutionContext,
process: DebugProcessImpl,
classLoader: ClassLoaderReference classLoader: ClassLoaderReference
) { ) {
val classesToLoad = if (classes.size == 1) { val classesToLoad = if (classes.size == 1) {
@@ -110,31 +106,24 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
for ((className, _, bytes) in classesToLoad) { for ((className, _, bytes) in classesToLoad) {
val patchedBytes = if (useMagicAccessor(context)) changeSuperToMagicAccessor(bytes) else bytes val patchedBytes = if (useMagicAccessor(context)) changeSuperToMagicAccessor(bytes) else bytes
defineClass(className, patchedBytes, context, process, classLoader) defineClass(className, patchedBytes, context, classLoader)
} }
} }
private fun defineClass( private fun defineClass(
name: String, name: String,
bytes: ByteArray, bytes: ByteArray,
context: EvaluationContextImpl, context: ExecutionContext,
process: DebugProcessImpl,
classLoader: ClassLoaderReference classLoader: ClassLoaderReference
) { ) {
try { try {
val vm = process.virtualMachineProxy val vm = context.vm
val classLoaderType = classLoader.referenceType() as ClassType val classLoaderType = classLoader.referenceType() as ClassType
val defineMethod = classLoaderType.concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;") val defineMethod = classLoaderType.concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;")
val nameObj = vm.mirrorOf(name) val nameObj = vm.mirrorOf(name)
// Still actual for older platform versions val args = listOf(nameObj, mirrorOfByteArray(bytes, context), vm.mirrorOf(0), vm.mirrorOf(bytes.size))
@Suppress("DEPRECATION") context.invokeMethod(classLoader, defineMethod, args)
DebuggerUtilsEx.keep(nameObj, context)
process.invokeMethod(
context, classLoader, defineMethod,
listOf(nameObj, mirrorOfByteArray(bytes, context, process), vm.mirrorOf(0), vm.mirrorOf(bytes.size))
)
} catch (e: Exception) { } catch (e: Exception) {
throw EvaluateException("Error during class $name definition: $e", e) throw EvaluateException("Error during class $name definition: $e", e)
} }
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.idea.debugger.evaluate.compilation package org.jetbrains.kotlin.idea.debugger.evaluate.compilation
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import org.jetbrains.kotlin.backend.common.output.OutputFile import org.jetbrains.kotlin.backend.common.output.OutputFile
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
@@ -43,7 +42,7 @@ import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.utils.Printer import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
class CodeFragmentCompiler(val evaluationContext: EvaluationContextImpl) { class CodeFragmentCompiler(private val executionContext: ExecutionContext) {
data class CompilationResult( data class CompilationResult(
val classes: List<ClassToLoad>, val classes: List<ClassToLoad>,
val parameterInfo: CodeFragmentParameterInfo, val parameterInfo: CodeFragmentParameterInfo,
@@ -78,7 +77,7 @@ class CodeFragmentCompiler(val evaluationContext: EvaluationContextImpl) {
bindingContext, listOf(codeFragment), compilerConfiguration bindingContext, listOf(codeFragment), compilerConfiguration
).build() ).build()
val parameterInfo = CodeFragmentParameterAnalyzer(evaluationContext, codeFragment, bindingContext).analyze() val parameterInfo = CodeFragmentParameterAnalyzer(executionContext, codeFragment, bindingContext).analyze()
val (classDescriptor, methodDescriptor) = createDescriptorsForCodeFragment( val (classDescriptor, methodDescriptor) = createDescriptorsForCodeFragment(
codeFragment, Name.identifier(GENERATED_CLASS_NAME), Name.identifier(GENERATED_FUNCTION_NAME), codeFragment, Name.identifier(GENERATED_CLASS_NAME), Name.identifier(GENERATED_FUNCTION_NAME),
parameterInfo, returnType, moduleDescriptorWrapper.packageFragmentForEvaluator parameterInfo, returnType, moduleDescriptorWrapper.packageFragmentForEvaluator
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.idea.debugger.evaluate.compilation package org.jetbrains.kotlin.idea.debugger.evaluate.compilation
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.CodeFragmentCodegenInfo import org.jetbrains.kotlin.codegen.CodeFragmentCodegenInfo
@@ -15,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.idea.debugger.evaluate.DebuggerFieldPropertyDescriptor import org.jetbrains.kotlin.idea.debugger.evaluate.DebuggerFieldPropertyDescriptor
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
import org.jetbrains.kotlin.idea.debugger.evaluate.compilation.CodeFragmentParameter.* import org.jetbrains.kotlin.idea.debugger.evaluate.compilation.CodeFragmentParameter.*
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinCodeFragmentFactory.Companion.FAKE_JAVA_CONTEXT_FUNCTION_NAME import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinCodeFragmentFactory.Companion.FAKE_JAVA_CONTEXT_FUNCTION_NAME
import org.jetbrains.kotlin.idea.debugger.safeLocation import org.jetbrains.kotlin.idea.debugger.safeLocation
@@ -57,8 +57,8 @@ interface CodeFragmentParameter {
} }
class CodeFragmentParameterInfo( class CodeFragmentParameterInfo(
val parameters: List<CodeFragmentParameter.Smart>, val parameters: List<Smart>,
val crossingBounds: Set<CodeFragmentParameter.Dumb> val crossingBounds: Set<Dumb>
) )
/* /*
@@ -66,7 +66,7 @@ class CodeFragmentParameterInfo(
It handles both directly mentioned names such as local variables or parameters and implicit values (dispatch/extension receivers). It handles both directly mentioned names such as local variables or parameters and implicit values (dispatch/extension receivers).
*/ */
class CodeFragmentParameterAnalyzer( class CodeFragmentParameterAnalyzer(
private val evaluationContext: EvaluationContextImpl, private val context: ExecutionContext,
private val codeFragment: KtCodeFragment, private val codeFragment: KtCodeFragment,
private val bindingContext: BindingContext private val bindingContext: BindingContext
) { ) {
@@ -76,7 +76,7 @@ class CodeFragmentParameterAnalyzer(
private val onceUsedChecker = OnceUsedChecker(CodeFragmentParameterAnalyzer::class.java) private val onceUsedChecker = OnceUsedChecker(CodeFragmentParameterAnalyzer::class.java)
private val containingPrimaryConstructor: ConstructorDescriptor? by lazy { private val containingPrimaryConstructor: ConstructorDescriptor? by lazy {
evaluationContext.frameProxy?.safeLocation()?.safeMethod()?.takeIf { it.isConstructor } ?: return@lazy null context.frameProxy.safeLocation()?.safeMethod()?.takeIf { it.isConstructor } ?: return@lazy null
val constructor = codeFragment.context?.getParentOfType<KtPrimaryConstructor>(false) ?: return@lazy null val constructor = codeFragment.context?.getParentOfType<KtPrimaryConstructor>(false) ?: return@lazy null
bindingContext[BindingContext.CONSTRUCTOR, constructor] bindingContext[BindingContext.CONSTRUCTOR, constructor]
} }
@@ -17,15 +17,15 @@
package org.jetbrains.kotlin.idea.debugger.evaluate.compilingEvaluator package org.jetbrains.kotlin.idea.debugger.evaluate.compilingEvaluator
import com.intellij.debugger.engine.evaluation.EvaluateException import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.sun.jdi.ClassLoaderReference import com.sun.jdi.ClassLoaderReference
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
import org.jetbrains.kotlin.idea.debugger.evaluate.LOG 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.ClassLoadingAdapter
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad
fun loadClassesSafely(evaluationContext: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderReference? { fun loadClassesSafely(context: ExecutionContext, classes: Collection<ClassToLoad>): ClassLoaderReference? {
return try { return try {
loadClasses(evaluationContext, classes) loadClasses(context, classes)
} catch (e: EvaluateException) { } catch (e: EvaluateException) {
throw e throw e
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -34,10 +34,10 @@ fun loadClassesSafely(evaluationContext: EvaluationContextImpl, classes: Collect
} }
} }
fun loadClasses(evaluationContext: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderReference? { fun loadClasses(context: ExecutionContext, classes: Collection<ClassToLoad>): ClassLoaderReference? {
if (classes.isEmpty()) { if (classes.isEmpty()) {
return null return null
} }
return ClassLoadingAdapter.loadClasses(evaluationContext, classes) return ClassLoadingAdapter.loadClasses(context, classes)
} }
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.idea.debugger.evaluate.variables package org.jetbrains.kotlin.idea.debugger.evaluate.variables
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.sun.jdi.* import com.sun.jdi.*
import org.jetbrains.kotlin.fileClasses.internalNameWithoutInnerClasses import org.jetbrains.kotlin.fileClasses.internalNameWithoutInnerClasses
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
@@ -18,7 +17,7 @@ import com.sun.jdi.Type as JdiType
import kotlin.jvm.internal.Ref import kotlin.jvm.internal.Ref
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
class EvaluatorValueConverter(private val executionContext: ExecutionContext) { class EvaluatorValueConverter(private val context: ExecutionContext) {
private companion object { private companion object {
private val UNBOXING_METHOD_NAMES = mapOf( private val UNBOXING_METHOD_NAMES = mapOf(
"java/lang/Boolean" to "booleanValue", "java/lang/Boolean" to "booleanValue",
@@ -84,7 +83,7 @@ class EvaluatorValueConverter(private val executionContext: ExecutionContext) {
private fun coerceBoxing(value: Value?, type: AsmType): Result? { private fun coerceBoxing(value: Value?, type: AsmType): Result? {
when { when {
value == null -> return Result(value) value == null -> return Result(value)
type == AsmType.VOID_TYPE -> return Result(executionContext.vm.mirrorOfVoid()) type == AsmType.VOID_TYPE -> return Result(context.vm.mirrorOfVoid())
type.isBoxedType -> { type.isBoxedType -> {
if (value.asmType().isBoxedType) { if (value.asmType().isBoxedType) {
return Result(value) return Result(value)
@@ -131,14 +130,13 @@ class EvaluatorValueConverter(private val executionContext: ExecutionContext) {
val unboxedType = value.asmType() val unboxedType = value.asmType()
val boxedType = box(unboxedType) val boxedType = box(unboxedType)
val boxedTypeClass = (executionContext.loadClassType(boxedType) as ClassType?) val boxedTypeClass = (context.loadClass(boxedType) as ClassType?)
?: error("Class $boxedType is not loaded") ?: error("Class $boxedType is not loaded")
val methodDesc = AsmType.getMethodDescriptor(boxedType, unboxedType) val methodDesc = AsmType.getMethodDescriptor(boxedType, unboxedType)
val valueOfMethod = boxedTypeClass.methodsByName("valueOf", methodDesc).first() val valueOfMethod = boxedTypeClass.methodsByName("valueOf", methodDesc).first()
val debugProcess = executionContext.evaluationContext.debugProcess return context.invokeMethod(boxedTypeClass, valueOfMethod, listOf(value))
return debugProcess.invokeMethod(executionContext.evaluationContext, boxedTypeClass, valueOfMethod, listOf(value))
} }
private fun unbox(value: Value?): Value? { private fun unbox(value: Value?): Value? {
@@ -153,7 +151,7 @@ class EvaluatorValueConverter(private val executionContext: ExecutionContext) {
val unboxingMethodName = UNBOXING_METHOD_NAMES.getValue(boxedType.internalName) val unboxingMethodName = UNBOXING_METHOD_NAMES.getValue(boxedType.internalName)
val methodDesc = AsmType.getMethodDescriptor(unboxedType) val methodDesc = AsmType.getMethodDescriptor(unboxedType)
val valueMethod = boxedTypeClass.methodsByName(unboxingMethodName, methodDesc).first() val valueMethod = boxedTypeClass.methodsByName(unboxingMethodName, methodDesc).first()
return executionContext.debugProcess.invokeMethod(executionContext.evaluationContext, value, valueMethod, emptyList()) return context.invokeMethod(value, valueMethod, emptyList())
} }
private fun ref(value: Value?): Value? { private fun ref(value: Value?): Value? {
@@ -163,10 +161,8 @@ class EvaluatorValueConverter(private val executionContext: ExecutionContext) {
fun wrapRef(value: Value?, refTypeClass: ClassType): Value? { fun wrapRef(value: Value?, refTypeClass: ClassType): Value? {
val constructor = refTypeClass.methods().single { it.isConstructor } val constructor = refTypeClass.methods().single { it.isConstructor }
val ref = refTypeClass.newInstance(executionContext.thread, constructor, emptyList(), executionContext.invokePolicy) val ref = context.newInstance(refTypeClass, constructor, emptyList())
context.keepReference(ref)
@Suppress("DEPRECATION")
DebuggerUtilsEx.keep(ref, executionContext.evaluationContext)
val elementField = refTypeClass.fieldByName("element") ?: error("'element' field not found") val elementField = refTypeClass.fieldByName("element") ?: error("'element' field not found")
ref.setValue(elementField, value) ref.setValue(elementField, value)
@@ -177,13 +173,13 @@ class EvaluatorValueConverter(private val executionContext: ExecutionContext) {
val primitiveType = value.asmType() val primitiveType = value.asmType()
val refType = PRIMITIVE_TO_REF.getValue(primitiveType) val refType = PRIMITIVE_TO_REF.getValue(primitiveType)
val refTypeClass = (executionContext.loadClassType(refType) as ClassType?) val refTypeClass = (context.loadClass(refType) as ClassType?)
?: error("Class $refType is not loaded") ?: error("Class $refType is not loaded")
return wrapRef(value, refTypeClass) return wrapRef(value, refTypeClass)
} else { } else {
val refType = AsmType.getType(Ref.ObjectRef::class.java) val refType = AsmType.getType(Ref.ObjectRef::class.java)
val refTypeClass = (executionContext.loadClassType(refType) as ClassType?) val refTypeClass = (context.loadClass(refType) as ClassType?)
?: error("Class $refType is not loaded") ?: error("Class $refType is not loaded")
return wrapRef(value, refTypeClass) return wrapRef(value, refTypeClass)
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.idea.debugger.evaluate.variables package org.jetbrains.kotlin.idea.debugger.evaluate.variables
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.jdi.LocalVariableProxyImpl import com.intellij.debugger.jdi.LocalVariableProxyImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.openapi.diagnostic.Attachment import com.intellij.openapi.diagnostic.Attachment
@@ -33,7 +32,9 @@ import kotlin.coroutines.Continuation
import org.jetbrains.org.objectweb.asm.Type as AsmType import org.jetbrains.org.objectweb.asm.Type as AsmType
import com.sun.jdi.Type as JdiType import com.sun.jdi.Type as JdiType
class VariableFinder private constructor(private val context: ExecutionContext, private val frameProxy: StackFrameProxyImpl) { class VariableFinder(private val context: ExecutionContext) {
private val frameProxy = context.frameProxy
companion object { companion object {
private const val USE_UNSAFE_FALLBACK = true private const val USE_UNSAFE_FALLBACK = true
@@ -44,14 +45,9 @@ class VariableFinder private constructor(private val context: ExecutionContext,
"kotlin.coroutines.jvm.internal.RestrictedSuspendLambda" "kotlin.coroutines.jvm.internal.RestrictedSuspendLambda"
) )
fun instance(context: ExecutionContext): VariableFinder? { fun variableNotFound(context: ExecutionContext, message: String): Exception {
val frameProxy = context.evaluationContext.frameProxy ?: return null
return VariableFinder(context, frameProxy)
}
fun variableNotFound(context: EvaluationContextImpl, message: String): Exception {
val frameProxy = context.frameProxy val frameProxy = context.frameProxy
val location = frameProxy?.safeLocation() val location = frameProxy.safeLocation()
val scope = context.debugProcess.searchScope val scope = context.debugProcess.searchScope
val locationText = location?.run { "Location: ${sourceName()}:${lineNumber()}" } ?: "No location available" val locationText = location?.run { "Location: ${sourceName()}:${lineNumber()}" } ?: "No location available"
@@ -297,7 +293,7 @@ class VariableFinder private constructor(private val context: ExecutionContext,
} }
private fun findDebugLabel(name: String): Result? { private fun findDebugLabel(name: String): Result? {
val markupMap = DebugLabelPropertyDescriptorProvider.getMarkupMap(context.evaluationContext.debugProcess) val markupMap = DebugLabelPropertyDescriptorProvider.getMarkupMap(context.debugProcess)
for ((value, markup) in markupMap) { for ((value, markup) in markupMap) {
if (markup.text == name) { if (markup.text == name) {
@@ -408,7 +404,7 @@ class VariableFinder private constructor(private val context: ExecutionContext,
.methodsByName("getContext", "()Lkotlin/coroutines/CoroutineContext;").firstOrNull() .methodsByName("getContext", "()Lkotlin/coroutines/CoroutineContext;").firstOrNull()
?: return null ?: return null
return context.debugProcess.invokeMethod(context.evaluationContext, continuation, getContextMethod, emptyList()) as? ObjectReference return context.invokeMethod(continuation, getContextMethod, emptyList()) as? ObjectReference
} }
private fun findCapturedVariableInReceiver(variables: List<LocalVariableProxyImpl>, kind: VariableKind): Result? { private fun findCapturedVariableInReceiver(variables: List<LocalVariableProxyImpl>, kind: VariableKind): Result? {
@@ -482,7 +478,7 @@ class VariableFinder private constructor(private val context: ExecutionContext,
.methodsByName("getValue", "()Ljava/lang/Object;").firstOrNull() .methodsByName("getValue", "()Ljava/lang/Object;").firstOrNull()
?: return rawValue ?: return rawValue
return context.debugProcess.invokeMethod(context.evaluationContext, delegateValue, getValueMethod, emptyList()) return context.invokeMethod(delegateValue, getValueMethod, emptyList())
} }
private fun isCapturedReceiverFieldName(name: String): Boolean { private fun isCapturedReceiverFieldName(name: String): Boolean {