Refactoring: Incapsulate method calls into ExecutionContext
This commit is contained in:
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+38
-27
@@ -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? {
|
||||||
|
|||||||
+8
-13
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-18
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+10
-11
@@ -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
|
||||||
|
|||||||
+14
-25
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -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
|
||||||
|
|||||||
+5
-5
@@ -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)
|
||||||
}
|
}
|
||||||
+9
-13
@@ -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)
|
||||||
|
|||||||
+8
-12
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user