Evaluate Expression: fix function call from base class on object with subtype

This commit is contained in:
Natalia Ukhorskaya
2014-09-19 13:58:56 +04:00
parent b5f738db07
commit 2b388ed308
5 changed files with 73 additions and 28 deletions
@@ -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
@@ -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");