Evaluate Expression: fix function call from base class on object with subtype
This commit is contained in:
+1
-27
@@ -25,20 +25,11 @@ import com.intellij.debugger.SourcePosition
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import java.util.ArrayList
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
|
||||
import org.jetbrains.jet.plugin.caches.resolve.JavaResolveExtension
|
||||
import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaClassImpl
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName
|
||||
import org.jetbrains.jet.codegen.AsmUtil
|
||||
import org.apache.log4j.Logger
|
||||
import org.jetbrains.jet.plugin.refactoring.runReadAction
|
||||
|
||||
class KotlinEvaluateExpressionCache(val project: Project) {
|
||||
|
||||
@@ -86,29 +77,12 @@ class KotlinEvaluateExpressionCache(val project: Project) {
|
||||
val value = context.findLocalVariable(name, asmType = null, checkType = false, failIfNotFound = false)
|
||||
if (value == null) return@all false
|
||||
|
||||
val thisDescriptor = value.asmType.getClassDescriptor()
|
||||
val thisDescriptor = value.asmType.getClassDescriptor(project)
|
||||
val superClassDescriptor = jetType.getConstructor().getDeclarationDescriptor() as? ClassDescriptor
|
||||
return@all thisDescriptor != null && superClassDescriptor != null && DescriptorUtils.isSubclass(thisDescriptor, superClassDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Type.getClassDescriptor(): ClassDescriptor? {
|
||||
if (AsmUtil.isPrimitive(this)) return null
|
||||
|
||||
val jvmName = JvmClassName.byInternalName(getInternalName()).getFqNameForClassNameWithoutDollars()
|
||||
|
||||
val platformClasses = JavaToKotlinClassMap.getInstance().mapPlatformClass(jvmName)
|
||||
if (platformClasses.notEmpty) return platformClasses.first()
|
||||
|
||||
return runReadAction {
|
||||
val classes = JavaPsiFacade.getInstance(project).findClasses(jvmName.asString(), GlobalSearchScope.allScope(project))
|
||||
if (classes.isEmpty()) null else {
|
||||
val clazz = classes.first()
|
||||
JavaResolveExtension.getResolver(project, clazz).resolveClass(JavaClassImpl(clazz))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class CompiledDataDescriptor(val bytecodes: ByteArray, val sourcePosition: SourcePosition, val funName: String, val parameters: ParametersDescriptor)
|
||||
|
||||
class ParametersDescriptor : Iterable<Pair<String, JetType>> {
|
||||
|
||||
@@ -61,6 +61,15 @@ import org.jetbrains.jet.codegen.AsmUtil
|
||||
import com.sun.jdi.InvalidStackFrameException
|
||||
import org.jetbrains.jet.plugin.refactoring.runReadAction
|
||||
import org.jetbrains.jet.lang.psi.analysisContext
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
|
||||
import org.jetbrains.jet.lang.resolve.java.JvmClassName
|
||||
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.jet.plugin.caches.resolve.JavaResolveExtension
|
||||
import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaClassImpl
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||
|
||||
private val RECEIVER_NAME = "\$receiver"
|
||||
private val THIS_NAME = "this"
|
||||
@@ -295,8 +304,20 @@ private fun SuspendContext.getInvokePolicy(): Int {
|
||||
}
|
||||
|
||||
fun EvaluationContextImpl.findLocalVariable(name: String, asmType: Type?, checkType: Boolean, failIfNotFound: Boolean): Value? {
|
||||
val project = getDebugProcess().getProject()
|
||||
val frame = getFrameProxy()?.getStackFrame()
|
||||
if (frame == null) return null
|
||||
|
||||
fun isValueOfCorrectType(value: Value, asmType: Type?, shouldCheckType: Boolean): Boolean {
|
||||
if (!shouldCheckType || asmType == null || value.asmType == asmType) return true
|
||||
if (project == null) return false
|
||||
|
||||
val thisDesc = value.asmType.getClassDescriptor(project)
|
||||
val expDesc = asmType.getClassDescriptor(project)
|
||||
return thisDesc != null && expDesc != null && DescriptorUtils.isSubclass(thisDesc, expDesc)
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
when (name) {
|
||||
THIS_NAME -> {
|
||||
@@ -365,6 +386,23 @@ fun EvaluationContextImpl.findLocalVariable(name: String, asmType: Type?, checkT
|
||||
}
|
||||
}
|
||||
|
||||
fun Type.getClassDescriptor(project: Project): ClassDescriptor? {
|
||||
if (AsmUtil.isPrimitive(this)) return null
|
||||
|
||||
val jvmName = JvmClassName.byInternalName(getInternalName()).getFqNameForClassNameWithoutDollars()
|
||||
|
||||
val platformClasses = JavaToKotlinClassMap.getInstance().mapPlatformClass(jvmName)
|
||||
if (platformClasses.notEmpty) return platformClasses.first()
|
||||
|
||||
return runReadAction {
|
||||
val classes = JavaPsiFacade.getInstance(project).findClasses(jvmName.asString(), GlobalSearchScope.allScope(project))
|
||||
if (classes.isEmpty()) null else {
|
||||
val clazz = classes.first()
|
||||
JavaResolveExtension.getResolver(project, clazz).resolveClass(JavaClassImpl(clazz))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCapturedFieldName(name: String) = when (name) {
|
||||
RECEIVER_NAME -> AsmUtil.CAPTURED_RECEIVER_FIELD
|
||||
THIS_NAME -> AsmUtil.CAPTURED_THIS_FIELD
|
||||
@@ -373,4 +411,3 @@ private fun getCapturedFieldName(name: String) = when (name) {
|
||||
else -> "$$name"
|
||||
}
|
||||
|
||||
private fun isValueOfCorrectType(value: Value, asmType: Type?, shouldCheckType: Boolean) = !shouldCheckType || asmType == null || value.asmType == asmType
|
||||
@@ -0,0 +1,8 @@
|
||||
LineBreakpoint created at funFromSuperClass.kt:12
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! funFromSuperClass.FunFromSuperClassPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
funFromSuperClass.kt:11
|
||||
Compile bytecode for foo()
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,20 @@
|
||||
package funFromSuperClass
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
Derived().test()
|
||||
}
|
||||
|
||||
open class Base {
|
||||
fun foo() = 1
|
||||
|
||||
fun test() {
|
||||
//Breakpoint!
|
||||
val a = 1
|
||||
}
|
||||
}
|
||||
|
||||
class Derived: Base()
|
||||
|
||||
|
||||
// EXPRESSION: foo()
|
||||
// RESULT: 1: I
|
||||
+6
@@ -120,6 +120,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
doSingleBreakpointTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("funFromSuperClass.kt")
|
||||
public void testFunFromSuperClass() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/funFromSuperClass.kt");
|
||||
doSingleBreakpointTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("imports.kt")
|
||||
public void testImports() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/imports.kt");
|
||||
|
||||
Reference in New Issue
Block a user