From 6d90e850ff93f155c07720c2febf3ba2c2dd7228 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Thu, 7 Mar 2019 17:31:09 +0300 Subject: [PATCH] Refactoring: Incapsulate method calls into ExecutionContext --- .../debugger/evaluate/ExecutionContext.kt | 100 ++++++++++++------ .../evaluate/KotlinEvaluationBuilder.kt | 65 +++++++----- .../AbstractAndroidClassLoadingAdapter.kt | 21 ++-- .../AndroidOClassLoadingAdapter.kt | 31 +++--- .../classLoading/ClassLoadingAdapter.kt | 21 ++-- .../OrdinaryClassLoadingAdapter.kt | 39 +++---- .../compilation/CodeFragmentCompiler.kt | 5 +- .../CodeFragmentParameterAnalyzer.kt | 10 +- .../debugger/evaluate/compilingEvaluator.kt | 10 +- .../variables/EvaluatorValueConverter.kt | 22 ++-- .../evaluate/variables/VariableFinder.kt | 20 ++-- 11 files changed, 179 insertions(+), 165 deletions(-) diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/ExecutionContext.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/ExecutionContext.kt index 800a65ba9e1..ace91128273 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/ExecutionContext.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/ExecutionContext.kt @@ -6,49 +6,83 @@ package org.jetbrains.kotlin.idea.debugger.evaluate 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.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 org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.org.objectweb.asm.Type -class ExecutionContext(val evaluationContext: EvaluationContextImpl, val thread: ThreadReference, val invokePolicy: Int) { - val vm: VirtualMachine - get() = thread.virtualMachine() +class ExecutionContext(val evaluationContext: EvaluationContextImpl, val frameProxy: StackFrameProxyImpl) { + val vm: VirtualMachineProxyImpl + get() = evaluationContext.debugProcess.virtualMachineProxy + + val classLoader: ClassLoaderReference? + get() = evaluationContext.classLoader + + val suspendContext: SuspendContextImpl + get() = evaluationContext.suspendContext val debugProcess: DebugProcessImpl get() = evaluationContext.debugProcess - fun loadClassType(asmType: Type, classLoader: ClassLoaderReference? = null): ReferenceType? { - if (asmType.sort == Type.ARRAY) { - return loadClassType(asmType.elementType, classLoader) - } + val project: Project + get() = evaluationContext.project - if (asmType.sort != Type.OBJECT) { - return null - } + val invokePolicy = evaluationContext.suspendContext.getInvokePolicy() - val vm = thread.virtualMachine() - val className = asmType.className - - val classClass = vm.classesByName(Class::class.java.name).firstIsInstanceOrNull() ?: return null - - val method: Method? - val args: List - - 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() + @Throws(EvaluateException::class) + fun invokeMethod(obj: ObjectReference, method: Method, args: List): Value? { + return debugProcess.invokeInstanceMethod(evaluationContext, obj, method, args, invokePolicy) } + fun invokeMethod(type: ClassType, method: Method, args: List): Value? { + return debugProcess.invokeMethod(evaluationContext, type, method, args) + } + + @Throws(EvaluateException::class) + fun newInstance(type: ClassType, constructor: Method, args: List): 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) + } } \ No newline at end of file diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt index 5b367f9ebf7..cbd7ad0b933 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluationBuilder.kt @@ -132,8 +132,19 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour 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 { - return evaluateSafe(context) + val executionContext = ExecutionContext(context, frameProxy) + return evaluateSafe(executionContext) } catch (e: EvaluateException) { throw e } 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) val (compiledData, isCompiledDataFromCache) = compileCodeFragmentCacheAware(codeFragment, sourcePosition, ::compilerFactory) 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) { - evaluateWithCompilation(executionContext, compiledData, classLoaderRef) - ?: evaluateWithEval4J(executionContext, compiledData, classLoaderRef) + evaluateWithCompilation(context, compiledData, classLoaderRef) + ?: evaluateWithEval4J(context, compiledData, classLoaderRef) } 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 (isCompiledDataFromCache && result is ExceptionThrown && result.kind == ExceptionThrown.ExceptionKind.BROKEN_CODE) { 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) { - is InterpreterResult -> result.toJdiValue(executionContext) + is InterpreterResult -> result.toJdiValue(context) else -> result } } - private fun compileCodeFragment(evaluationContext: EvaluationContextImpl): CompiledDataDescriptor { - val debugProcess = evaluationContext.debugProcess + private fun compileCodeFragment(context: ExecutionContext): CompiledDataDescriptor { + val debugProcess = context.debugProcess var analysisResult = checkForErrors(codeFragment, debugProcess) if (codeFragment.wrapToStringIfNeeded(analysisResult.bindingContext)) { @@ -215,7 +222,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour 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) } @@ -285,10 +292,10 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour ): Value? { return try { 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\"") 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) } } 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 } 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) } } @@ -321,24 +332,24 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour // Preload additional classes compiledData.classes .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) { - context.loadClassType(parameterType, classLoader) + context.loadClass(parameterType, classLoader) } - val args = context.calculateMainMethodCallArguments(compiledData) + val args = calculateMainMethodCallArguments(context, compiledData) block(args) } } - private fun ExecutionContext.calculateMainMethodCallArguments(compiledData: CompiledDataDescriptor): List { + private fun calculateMainMethodCallArguments(context: ExecutionContext, compiledData: CompiledDataDescriptor): List { val asmValueParameters = compiledData.mainMethodSignature.parameterTypes val valueParameters = compiledData.parameters require(asmValueParameters.size == valueParameters.size) 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) -> val result = variableFinder.find(parameter, asmType) @@ -347,7 +358,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: Sour val name = parameter.debugString fun isInsideDefaultInterfaceMethod(): Boolean { - val method = evaluationContext.frameProxy?.safeLocation()?.safeMethod() ?: return false + val method = context.frameProxy.safeLocation()?.safeMethod() ?: return false val desc = method.signature() 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()) { evaluationException("Parameter evaluation is not supported for '\$default' methods") } else { - throw VariableFinder.variableNotFound(evaluationContext, buildString { + throw VariableFinder.variableNotFound(context, buildString { 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 fun InterpreterResult.toJdiValue(context: ExecutionContext): com.sun.jdi.Value? { + private fun InterpreterResult.toJdiValue(context: ExecutionContext): Value? { val jdiValue = when (this) { is ValueReturned -> result 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 - 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? { diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/AbstractAndroidClassLoadingAdapter.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/AbstractAndroidClassLoadingAdapter.kt index 9c68f9683de..ece5774fc3e 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/AbstractAndroidClassLoadingAdapter.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/AbstractAndroidClassLoadingAdapter.kt @@ -16,31 +16,26 @@ 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 org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext abstract class AbstractAndroidClassLoadingAdapter : ClassLoadingAdapter { - protected fun dex(context: EvaluationContextImpl, classes: Collection): ByteArray? { + protected fun dex(context: ExecutionContext, classes: Collection): ByteArray? { return AndroidDexer.getInstances(context.project).single().dex(classes) } - protected fun wrapToByteBuffer(bytes: ArrayReference, context: EvaluationContext, process: DebugProcessImpl): ObjectReference { - val byteBufferClass = process.findClass(context, "java.nio.ByteBuffer", context.classLoader) as ClassType + protected fun wrapToByteBuffer(bytes: ArrayReference, context: ExecutionContext): ObjectReference { + val classLoader = context.classLoader + val byteBufferClass = context.findClass("java.nio.ByteBuffer", classLoader) as ClassType val wrapMethod = byteBufferClass.concreteMethodByName("wrap", "([B)Ljava/nio/ByteBuffer;") ?: 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( - context: EvaluationContextImpl, - fqName: String, - classLoader: ClassLoaderReference? - ): ReferenceType? { + protected fun tryLoadClass(context: ExecutionContext, fqName: String, classLoader: ClassLoaderReference?): ReferenceType? { return try { - loadClass(context, fqName, classLoader) + context.loadClass(fqName, classLoader) } catch (e: Throwable) { null } diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/AndroidOClassLoadingAdapter.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/AndroidOClassLoadingAdapter.kt index 2014fc8907e..1754fd881d6 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/AndroidOClassLoadingAdapter.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/AndroidOClassLoadingAdapter.kt @@ -18,44 +18,39 @@ package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading import com.intellij.debugger.engine.JVMNameUtil 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 org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext import org.jetbrains.kotlin.idea.debugger.isDexDebug 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() } - private fun resolveClassLoaderClass(context: EvaluationContextImpl): ClassType? { + private fun resolveClassLoaderClass(context: ExecutionContext): ClassType? { return try { - context.debugProcess.tryLoadClass( - context, "dalvik.system.InMemoryDexClassLoader", context.classLoader - ) as? ClassType + val classLoader = context.classLoader + tryLoadClass(context, "dalvik.system.InMemoryDexClassLoader", classLoader) as? ClassType } catch (e: EvaluateException) { null } } - override fun loadClasses(context: EvaluationContextImpl, classes: Collection): ClassLoaderReference { - val process = context.debugProcess + override fun loadClasses(context: ExecutionContext, classes: Collection): ClassLoaderReference { val inMemoryClassLoaderClass = resolveClassLoaderClass(context) ?: error("InMemoryDexClassLoader class not found") val constructorMethod = inMemoryClassLoaderClass.concreteMethodByName( JVMNameUtil.CONSTRUCTOR_NAME, "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V" ) ?: error("Constructor method not found") val dexBytes = dex(context, classes) ?: error("Can't dex classes") - val dexBytesMirror = mirrorOfByteArray(dexBytes, context, process) - val dexByteBuffer = wrapToByteBuffer(dexBytesMirror, context, process) + val dexBytesMirror = mirrorOfByteArray(dexBytes, context) + val dexByteBuffer = wrapToByteBuffer(dexBytesMirror, context) - val newClassLoader = process.newInstance( - context, inMemoryClassLoaderClass, constructorMethod, - listOf(dexByteBuffer, context.classLoader) - ) + val classLoader = context.classLoader + val args = listOf(dexByteBuffer, classLoader) + val newClassLoader = context.newInstance(inMemoryClassLoaderClass, constructorMethod, args) as ClassLoaderReference + context.keepReference(newClassLoader) - DebuggerUtilsEx.keep(newClassLoader, context) - - return newClassLoader as ClassLoaderReference + return newClassLoader } } \ No newline at end of file diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/ClassLoadingAdapter.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/ClassLoadingAdapter.kt index c4b6d450bf7..cd1fe9e1c00 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/ClassLoadingAdapter.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/ClassLoadingAdapter.kt @@ -16,13 +16,11 @@ 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.ArrayType import com.sun.jdi.ClassLoaderReference 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.org.objectweb.asm.ClassReader import org.jetbrains.org.objectweb.asm.Label @@ -38,7 +36,7 @@ interface ClassLoadingAdapter { OrdinaryClassLoadingAdapter() ) - fun loadClasses(context: EvaluationContextImpl, classes: Collection): ClassLoaderReference? { + fun loadClasses(context: ExecutionContext, classes: Collection): ClassLoaderReference? { val mainClass = classes.firstOrNull { it.isMainClass } ?: return null 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): ClassLoaderReference + fun loadClasses(context: ExecutionContext, classes: Collection): ClassLoaderReference - fun mirrorOfByteArray(bytes: ByteArray, context: EvaluationContextImpl, process: DebugProcessImpl): ArrayReference { - val arrayClass = process.findClass(context, "byte[]", context.classLoader) as ArrayType - val reference = process.newInstance(arrayClass, bytes.size) - DebuggerUtilsEx.keep(reference, context) + fun mirrorOfByteArray(bytes: ByteArray, context: ExecutionContext): ArrayReference { + val classLoader = context.classLoader + val arrayClass = context.findClass("byte[]", classLoader) as ArrayType + val reference = context.newInstance(arrayClass, bytes.size) + context.keepReference(reference) val mirrors = ArrayList(bytes.size) for (byte in bytes) { - mirrors += process.virtualMachineProxy.mirrorOf(byte) + mirrors += context.vm.mirrorOf(byte) } var loaded = 0 diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/OrdinaryClassLoadingAdapter.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/OrdinaryClassLoadingAdapter.kt index 4a1c8f053dd..66f6f1b7d59 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/OrdinaryClassLoadingAdapter.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/classLoading/OrdinaryClassLoadingAdapter.kt @@ -16,15 +16,12 @@ 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.EvaluationContextImpl 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.sun.jdi.ClassLoaderReference import com.sun.jdi.ClassType +import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext import org.jetbrains.kotlin.idea.debugger.isDexDebug import org.jetbrains.org.objectweb.asm.ClassReader import org.jetbrains.org.objectweb.asm.ClassVisitor @@ -63,28 +60,28 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter { return classWriter.toByteArray() } - fun useMagicAccessor(evaluationContext: EvaluationContextImpl): Boolean { - val rawVersion = evaluationContext.debugProcess.virtualMachineProxy.version()?.substringBefore('_') ?: return false + fun useMagicAccessor(context: ExecutionContext): Boolean { + val rawVersion = context.vm.version()?.substringBefore('_') ?: return false val javaVersion = JavaSdkVersion.fromVersionString(rawVersion) ?: return false return !javaVersion.isAtLeast(JavaSdkVersion.JDK_1_9) } } - override fun isApplicable(context: EvaluationContextImpl, info: ClassLoadingAdapter.Companion.ClassInfoForEvaluator) = with(info) { - isCompilingEvaluatorPreferred && context.classLoader != null && !context.debugProcess.isDexDebug() + override fun isApplicable(context: ExecutionContext, info: ClassLoadingAdapter.Companion.ClassInfoForEvaluator): Boolean { + return info.isCompilingEvaluatorPreferred && context.classLoader != null && !context.debugProcess.isDexDebug() } - override fun loadClasses(context: EvaluationContextImpl, classes: Collection): ClassLoaderReference { + override fun loadClasses(context: ExecutionContext, classes: Collection): ClassLoaderReference { val process = context.debugProcess val classLoader = try { - ClassLoadingUtils.getClassLoader(context, process) + ClassLoadingUtils.getClassLoader(context.evaluationContext, process) } catch (e: Exception) { throw EvaluateException("Error creating evaluation class loader: $e", e) } try { - defineClasses(classes, context, process, classLoader) + defineClasses(classes, context, classLoader) } catch (e: Exception) { throw EvaluateException("Error during classes definition $e", e) } @@ -94,8 +91,7 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter { private fun defineClasses( classes: Collection, - context: EvaluationContextImpl, - process: DebugProcessImpl, + context: ExecutionContext, classLoader: ClassLoaderReference ) { val classesToLoad = if (classes.size == 1) { @@ -110,31 +106,24 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter { for ((className, _, bytes) in classesToLoad) { val patchedBytes = if (useMagicAccessor(context)) changeSuperToMagicAccessor(bytes) else bytes - defineClass(className, patchedBytes, context, process, classLoader) + defineClass(className, patchedBytes, context, classLoader) } } private fun defineClass( name: String, bytes: ByteArray, - context: EvaluationContextImpl, - process: DebugProcessImpl, + context: ExecutionContext, classLoader: ClassLoaderReference ) { try { - val vm = process.virtualMachineProxy + val vm = context.vm val classLoaderType = classLoader.referenceType() as ClassType val defineMethod = classLoaderType.concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;") val nameObj = vm.mirrorOf(name) - // Still actual for older platform versions - @Suppress("DEPRECATION") - DebuggerUtilsEx.keep(nameObj, context) - - process.invokeMethod( - context, classLoader, defineMethod, - listOf(nameObj, mirrorOfByteArray(bytes, context, process), vm.mirrorOf(0), vm.mirrorOf(bytes.size)) - ) + val args = listOf(nameObj, mirrorOfByteArray(bytes, context), vm.mirrorOf(0), vm.mirrorOf(bytes.size)) + context.invokeMethod(classLoader, defineMethod, args) } catch (e: Exception) { throw EvaluateException("Error during class $name definition: $e", e) } diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentCompiler.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentCompiler.kt index 91807b21967..9f66067da1b 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentCompiler.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentCompiler.kt @@ -5,7 +5,6 @@ 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.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.codegen.* @@ -43,7 +42,7 @@ import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.utils.Printer import org.jetbrains.org.objectweb.asm.Type -class CodeFragmentCompiler(val evaluationContext: EvaluationContextImpl) { +class CodeFragmentCompiler(private val executionContext: ExecutionContext) { data class CompilationResult( val classes: List, val parameterInfo: CodeFragmentParameterInfo, @@ -78,7 +77,7 @@ class CodeFragmentCompiler(val evaluationContext: EvaluationContextImpl) { bindingContext, listOf(codeFragment), compilerConfiguration ).build() - val parameterInfo = CodeFragmentParameterAnalyzer(evaluationContext, codeFragment, bindingContext).analyze() + val parameterInfo = CodeFragmentParameterAnalyzer(executionContext, codeFragment, bindingContext).analyze() val (classDescriptor, methodDescriptor) = createDescriptorsForCodeFragment( codeFragment, Name.identifier(GENERATED_CLASS_NAME), Name.identifier(GENERATED_FUNCTION_NAME), parameterInfo, returnType, moduleDescriptorWrapper.packageFragmentForEvaluator diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt index 3e1e85ffedb..abdd491303b 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilation/CodeFragmentParameterAnalyzer.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.idea.debugger.evaluate.compilation import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil -import com.intellij.debugger.engine.evaluation.EvaluationContextImpl import com.intellij.psi.PsiElement import org.jetbrains.kotlin.codegen.AsmUtil 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.SyntheticFieldDescriptor 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.KotlinCodeFragmentFactory.Companion.FAKE_JAVA_CONTEXT_FUNCTION_NAME import org.jetbrains.kotlin.idea.debugger.safeLocation @@ -57,8 +57,8 @@ interface CodeFragmentParameter { } class CodeFragmentParameterInfo( - val parameters: List, - val crossingBounds: Set + val parameters: List, + val crossingBounds: Set ) /* @@ -66,7 +66,7 @@ class CodeFragmentParameterInfo( It handles both directly mentioned names such as local variables or parameters and implicit values (dispatch/extension receivers). */ class CodeFragmentParameterAnalyzer( - private val evaluationContext: EvaluationContextImpl, + private val context: ExecutionContext, private val codeFragment: KtCodeFragment, private val bindingContext: BindingContext ) { @@ -76,7 +76,7 @@ class CodeFragmentParameterAnalyzer( private val onceUsedChecker = OnceUsedChecker(CodeFragmentParameterAnalyzer::class.java) 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(false) ?: return@lazy null bindingContext[BindingContext.CONSTRUCTOR, constructor] } diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilingEvaluator.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilingEvaluator.kt index e2fd8e3027d..59148d2d32c 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilingEvaluator.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/compilingEvaluator.kt @@ -17,15 +17,15 @@ package org.jetbrains.kotlin.idea.debugger.evaluate.compilingEvaluator import com.intellij.debugger.engine.evaluation.EvaluateException -import com.intellij.debugger.engine.evaluation.EvaluationContextImpl 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.classLoading.ClassLoadingAdapter import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad -fun loadClassesSafely(evaluationContext: EvaluationContextImpl, classes: Collection): ClassLoaderReference? { +fun loadClassesSafely(context: ExecutionContext, classes: Collection): ClassLoaderReference? { return try { - loadClasses(evaluationContext, classes) + loadClasses(context, classes) } catch (e: EvaluateException) { throw e } catch (e: Throwable) { @@ -34,10 +34,10 @@ fun loadClassesSafely(evaluationContext: EvaluationContextImpl, classes: Collect } } -fun loadClasses(evaluationContext: EvaluationContextImpl, classes: Collection): ClassLoaderReference? { +fun loadClasses(context: ExecutionContext, classes: Collection): ClassLoaderReference? { if (classes.isEmpty()) { return null } - return ClassLoadingAdapter.loadClasses(evaluationContext, classes) + return ClassLoadingAdapter.loadClasses(context, classes) } \ No newline at end of file diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/EvaluatorValueConverter.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/EvaluatorValueConverter.kt index f30c9d960ba..f3bf2a587df 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/EvaluatorValueConverter.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/EvaluatorValueConverter.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.idea.debugger.evaluate.variables -import com.intellij.debugger.impl.DebuggerUtilsEx import com.sun.jdi.* import org.jetbrains.kotlin.fileClasses.internalNameWithoutInnerClasses import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext @@ -18,7 +17,7 @@ import com.sun.jdi.Type as JdiType import kotlin.jvm.internal.Ref @Suppress("SpellCheckingInspection") -class EvaluatorValueConverter(private val executionContext: ExecutionContext) { +class EvaluatorValueConverter(private val context: ExecutionContext) { private companion object { private val UNBOXING_METHOD_NAMES = mapOf( "java/lang/Boolean" to "booleanValue", @@ -84,7 +83,7 @@ class EvaluatorValueConverter(private val executionContext: ExecutionContext) { private fun coerceBoxing(value: Value?, type: AsmType): Result? { when { 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 -> { if (value.asmType().isBoxedType) { return Result(value) @@ -131,14 +130,13 @@ class EvaluatorValueConverter(private val executionContext: ExecutionContext) { val unboxedType = value.asmType() val boxedType = box(unboxedType) - val boxedTypeClass = (executionContext.loadClassType(boxedType) as ClassType?) + val boxedTypeClass = (context.loadClass(boxedType) as ClassType?) ?: error("Class $boxedType is not loaded") val methodDesc = AsmType.getMethodDescriptor(boxedType, unboxedType) val valueOfMethod = boxedTypeClass.methodsByName("valueOf", methodDesc).first() - val debugProcess = executionContext.evaluationContext.debugProcess - return debugProcess.invokeMethod(executionContext.evaluationContext, boxedTypeClass, valueOfMethod, listOf(value)) + return context.invokeMethod(boxedTypeClass, valueOfMethod, listOf(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 methodDesc = AsmType.getMethodDescriptor(unboxedType) 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? { @@ -163,10 +161,8 @@ class EvaluatorValueConverter(private val executionContext: ExecutionContext) { fun wrapRef(value: Value?, refTypeClass: ClassType): Value? { val constructor = refTypeClass.methods().single { it.isConstructor } - val ref = refTypeClass.newInstance(executionContext.thread, constructor, emptyList(), executionContext.invokePolicy) - - @Suppress("DEPRECATION") - DebuggerUtilsEx.keep(ref, executionContext.evaluationContext) + val ref = context.newInstance(refTypeClass, constructor, emptyList()) + context.keepReference(ref) val elementField = refTypeClass.fieldByName("element") ?: error("'element' field not found") ref.setValue(elementField, value) @@ -177,13 +173,13 @@ class EvaluatorValueConverter(private val executionContext: ExecutionContext) { val primitiveType = value.asmType() 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") return wrapRef(value, refTypeClass) } else { 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") return wrapRef(value, refTypeClass) diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/VariableFinder.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/VariableFinder.kt index 2a2ff1457e6..795c48b1229 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/VariableFinder.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/evaluate/variables/VariableFinder.kt @@ -6,7 +6,6 @@ package org.jetbrains.kotlin.idea.debugger.evaluate.variables 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.StackFrameProxyImpl import com.intellij.openapi.diagnostic.Attachment @@ -33,7 +32,9 @@ import kotlin.coroutines.Continuation import org.jetbrains.org.objectweb.asm.Type as AsmType 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 { private const val USE_UNSAFE_FALLBACK = true @@ -44,14 +45,9 @@ class VariableFinder private constructor(private val context: ExecutionContext, "kotlin.coroutines.jvm.internal.RestrictedSuspendLambda" ) - fun instance(context: ExecutionContext): VariableFinder? { - val frameProxy = context.evaluationContext.frameProxy ?: return null - return VariableFinder(context, frameProxy) - } - - fun variableNotFound(context: EvaluationContextImpl, message: String): Exception { + fun variableNotFound(context: ExecutionContext, message: String): Exception { val frameProxy = context.frameProxy - val location = frameProxy?.safeLocation() + val location = frameProxy.safeLocation() val scope = context.debugProcess.searchScope 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? { - val markupMap = DebugLabelPropertyDescriptorProvider.getMarkupMap(context.evaluationContext.debugProcess) + val markupMap = DebugLabelPropertyDescriptorProvider.getMarkupMap(context.debugProcess) for ((value, markup) in markupMap) { if (markup.text == name) { @@ -408,7 +404,7 @@ class VariableFinder private constructor(private val context: ExecutionContext, .methodsByName("getContext", "()Lkotlin/coroutines/CoroutineContext;").firstOrNull() ?: 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, kind: VariableKind): Result? { @@ -482,7 +478,7 @@ class VariableFinder private constructor(private val context: ExecutionContext, .methodsByName("getValue", "()Ljava/lang/Object;").firstOrNull() ?: return rawValue - return context.debugProcess.invokeMethod(context.evaluationContext, delegateValue, getValueMethod, emptyList()) + return context.invokeMethod(delegateValue, getValueMethod, emptyList()) } private fun isCapturedReceiverFieldName(name: String): Boolean {