Evaluate: Use '<name>_field' syntax for field value evaluation (KT-14075)

This commit is contained in:
Yan Zhulanow
2018-11-13 16:09:45 +09:00
parent c2f33c0dfd
commit ada71ce3cd
16 changed files with 523 additions and 9 deletions
@@ -1881,7 +1881,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
@Nullable
private ClassDescriptor getSuperCallTarget(@NotNull Call call) {
public ClassDescriptor getSuperCallTarget(@NotNull Call call) {
KtSuperExpression superExpression = CallResolverUtilKt.getSuperCallExpression(call);
return superExpression == null ? null : getSuperCallLabelTarget(context, superExpression);
}
@@ -16,25 +16,46 @@
package org.jetbrains.kotlin.synthetic
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.java.components.SamConversionResolver
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.scopes.SyntheticScope
import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
import org.jetbrains.kotlin.storage.StorageManager
class JavaSyntheticScopes(
private val project: Project,
private val moduleDescriptor: ModuleDescriptor,
storageManager: StorageManager,
lookupTracker: LookupTracker,
languageVersionSettings: LanguageVersionSettings,
samConventionResolver: SamConversionResolver,
deprecationResolver: DeprecationResolver
): SyntheticScopes {
override val scopes = listOf(
JavaSyntheticPropertiesScope(storageManager, lookupTracker),
override val scopes = run {
val javaSyntheticPropertiesScope = JavaSyntheticPropertiesScope(storageManager, lookupTracker)
val scopesFromExtensions = SyntheticScopeProviderExtension
.getInstances(project)
.flatMap { it.getScopes(moduleDescriptor, javaSyntheticPropertiesScope) }
listOf(
javaSyntheticPropertiesScope,
SamAdapterFunctionsScope(
storageManager, languageVersionSettings, samConventionResolver, deprecationResolver,
lookupTracker
storageManager, languageVersionSettings, samConventionResolver, deprecationResolver,
lookupTracker
)
)
) + scopesFromExtensions
}
}
interface SyntheticScopeProviderExtension {
companion object : ProjectExtensionDescriptor<SyntheticScopeProviderExtension>(
"org.jetbrains.kotlin.syntheticScopeProviderExtension", SyntheticScopeProviderExtension::class.java)
fun getScopes(moduleDescriptor: ModuleDescriptor, javaSyntheticPropertiesScope: JavaSyntheticPropertiesScope): List<SyntheticScope>
}
@@ -39,9 +39,9 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
class LazyJavaClassDescriptor(
outerContext: LazyJavaResolverContext,
val outerContext: LazyJavaResolverContext,
containingDeclaration: DeclarationDescriptor,
private val jClass: JavaClass,
val jClass: JavaClass,
private val additionalSupertypeClassDescriptor: ClassDescriptor? = null
) : ClassDescriptorBase(outerContext.storageManager, containingDeclaration, jClass.name,
outerContext.components.sourceElementFactory.source(jClass),
@@ -79,7 +79,7 @@ internal val ClassDescriptor.internalName: String
return computeInternalName(this, isIrBackend = false)
}
internal val ClassId.internalName: String
val ClassId.internalName: String
get() {
return JvmClassName.byClassId(JavaToKotlinClassMap.mapKotlinToJava(asSingleFqName().toUnsafe()) ?: this).internalName
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.evaluate
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.completion.CompletionInformationProvider
class DebuggerFieldCompletionInformationProvider : CompletionInformationProvider {
override fun getContainerAndReceiverInformation(descriptor: DeclarationDescriptor) =
(descriptor as? DebuggerFieldPropertyDescriptor)?.description?.let { " $it" }
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.evaluate
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.synthetic.JavaSyntheticPropertiesScope
class DebuggerFieldExpressionCodegenExtension : ExpressionCodegenExtension {
override fun applyProperty(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
val propertyDescriptor = resolvedCall.resultingDescriptor as? PropertyDescriptor ?: return null
if (propertyDescriptor is DebuggerFieldPropertyDescriptor) {
return StackValue.StackValueWithSimpleReceiver.field(
c.typeMapper.mapType(propertyDescriptor.type),
propertyDescriptor.ownerType(c.codegen.state),
propertyDescriptor.fieldName,
false,
receiver
)
}
if (propertyDescriptor is JavaPropertyDescriptor) {
val containingClass = propertyDescriptor.containingDeclaration as? JavaClassDescriptor
if (containingClass != null) {
val correspondingGetter = JavaSyntheticPropertiesScope(LockBasedStorageManager.NO_LOCKS, LookupTracker.DO_NOTHING)
.getSyntheticExtensionProperties(listOf(containingClass.defaultType))
.firstOrNull { it.name == propertyDescriptor.name }
if (correspondingGetter != null) {
return c.codegen.intermediateValueForProperty(
correspondingGetter, false, false,
c.codegen.getSuperCallTarget(resolvedCall.call),
false, receiver, resolvedCall, false
)
}
}
}
return null
}
}
@@ -0,0 +1,193 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.evaluate
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.load.java.components.JavaSourceElementFactoryImpl
import org.jetbrains.kotlin.load.java.components.TypeUsage
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
import org.jetbrains.kotlin.load.java.structure.classId
import org.jetbrains.kotlin.load.kotlin.internalName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.SyntheticScope
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.synthetic.JavaSyntheticPropertiesScope
import org.jetbrains.kotlin.synthetic.SyntheticScopeProviderExtension
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
import org.jetbrains.org.objectweb.asm.Type
class DebuggerFieldSyntheticScopeProvider : SyntheticScopeProviderExtension {
override fun getScopes(
moduleDescriptor: ModuleDescriptor,
javaSyntheticPropertiesScope: JavaSyntheticPropertiesScope
): List<SyntheticScope> {
return listOf<SyntheticScope>(DebuggerFieldSyntheticScope(javaSyntheticPropertiesScope))
}
}
private class DebuggerFieldSyntheticScope(val javaSyntheticPropertiesScope: JavaSyntheticPropertiesScope) : SyntheticScope.Default() {
private val javaSourceElementFactory = JavaSourceElementFactoryImpl()
override fun getSyntheticExtensionProperties(
receiverTypes: Collection<KotlinType>,
name: Name,
location: LookupLocation
): Collection<PropertyDescriptor> {
if (!isInEvaluator(location)) {
return emptyList()
}
return getSyntheticExtensionProperties(receiverTypes).filter { it.name == name }
}
private fun isInEvaluator(location: LookupLocation): Boolean {
val element = (location as? KotlinLookupLocation)?.element ?: return false
val containingFile = element.containingFile?.takeIf { it.isValid } as? KtFile ?: return false
return containingFile.suppressDiagnosticsInDebugMode
}
override fun getSyntheticExtensionProperties(receiverTypes: Collection<KotlinType>): Collection<PropertyDescriptor> {
val result = mutableListOf<PropertyDescriptor>()
for (type in receiverTypes) {
val clazz = type.constructor.declarationDescriptor as? ClassDescriptor ?: continue
result += getSyntheticPropertiesForClass(clazz)
}
return result
}
private fun getSyntheticPropertiesForClass(clazz: ClassDescriptor): Collection<PropertyDescriptor> {
val collected = mutableMapOf<Name, PropertyDescriptor>()
val syntheticPropertyNames = javaSyntheticPropertiesScope
.getSyntheticExtensionProperties(listOf(clazz.defaultType))
.mapTo(mutableSetOf()) { it.name }
collectPropertiesWithParent(clazz, syntheticPropertyNames, collected)
return collected.values
}
private tailrec fun collectPropertiesWithParent(
clazz: ClassDescriptor,
syntheticNames: Set<Name>,
consumer: MutableMap<Name, PropertyDescriptor>
) {
when (clazz) {
is LazyJavaClassDescriptor -> collectJavaProperties(clazz, syntheticNames, consumer)
is JavaClassDescriptor -> error("Unsupported Java class type")
else -> collectKotlinProperties(clazz, consumer)
}
val superClass = clazz.getSuperClassNotAny()
if (superClass != null) {
collectPropertiesWithParent(superClass, syntheticNames, consumer)
}
}
private fun collectKotlinProperties(clazz: ClassDescriptor, consumer: MutableMap<Name, PropertyDescriptor>) {
for (descriptor in clazz.unsubstitutedMemberScope.getDescriptorsFiltered(DescriptorKindFilter.VARIABLES)) {
val propertyDescriptor = descriptor as? PropertyDescriptor ?: continue
val name = propertyDescriptor.name
if (propertyDescriptor.backingField == null || name in consumer) continue
val type = propertyDescriptor.type
val sourceElement = propertyDescriptor.source
consumer[name] = createSyntheticPropertyDescriptor(clazz, type, name.asString(), "Backing field", sourceElement) { state ->
state.typeMapper.mapType(clazz.defaultType)
}
}
}
private fun collectJavaProperties(
clazz: LazyJavaClassDescriptor,
syntheticNames: Set<Name>,
consumer: MutableMap<Name, PropertyDescriptor>
) {
val javaClass = clazz.jClass
for (field in javaClass.fields) {
val fieldName = field.name
if (field.isEnumEntry || field.isStatic || fieldName in consumer || fieldName !in syntheticNames) continue
val ownerClassName = javaClass.classId?.internalName ?: continue
val typeResolver = clazz.outerContext.typeResolver
val type = typeResolver.transformJavaType(field.type, TypeUsage.COMMON.toAttributes()).replaceArgumentsWithStarProjections()
val sourceElement = javaSourceElementFactory.source(field)
consumer[fieldName] = createSyntheticPropertyDescriptor(clazz, type, fieldName.asString(), "Java field", sourceElement) {
Type.getObjectType(ownerClassName)
}
}
}
private fun createSyntheticPropertyDescriptor(
clazz: ClassDescriptor,
type: KotlinType,
fieldName: String,
description: String,
getterSource: SourceElement,
ownerType: (GenerationState) -> Type
): PropertyDescriptor {
val propertyDescriptor = DebuggerFieldPropertyDescriptor(clazz, fieldName, description, ownerType)
val extensionReceiverParameter = DescriptorFactory.createExtensionReceiverParameterForCallable(
propertyDescriptor,
clazz.defaultType.replaceArgumentsWithStarProjections(),
Annotations.EMPTY
)
propertyDescriptor.setType(type, emptyList(), null, extensionReceiverParameter)
val getter = PropertyGetterDescriptorImpl(
propertyDescriptor, Annotations.EMPTY, Modality.FINAL,
Visibilities.PUBLIC, false, false, false,
CallableMemberDescriptor.Kind.SYNTHESIZED,
null, getterSource
)
propertyDescriptor.initialize(getter, null)
return propertyDescriptor
}
}
internal class DebuggerFieldPropertyDescriptor(
containingDeclaration: DeclarationDescriptor,
val fieldName: String,
val description: String,
val ownerType: (GenerationState) -> Type
) : PropertyDescriptorImpl(
containingDeclaration,
null,
Annotations.EMPTY,
Modality.FINAL,
Visibilities.PUBLIC,
/*isVar = */true,
Name.identifier(fieldName + "_field"),
CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE,
/*lateInit = */false,
/*isConst = */false,
/*isExpect = */false,
/*isActual = */false,
/*isExternal = */false,
/*isDelegated = */false
)
@@ -50,6 +50,10 @@
<extensionPoint qualifiedName="org.jetbrains.kotlin.scriptDefinitionsProvider"
interface="kotlin.script.experimental.intellij.ScriptDefinitionsProvider"
area="IDEA_PROJECT"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.syntheticScopeProviderExtension"
interface="org.jetbrains.kotlin.synthetic.SyntheticScopeProviderExtension"
area="IDEA_PROJECT"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
@@ -42,6 +42,10 @@
<extensionPoint qualifiedName="org.jetbrains.kotlin.scriptDefinitionsProvider"
interface="kotlin.script.experimental.intellij.ScriptDefinitionsProvider"
area="IDEA_PROJECT"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.syntheticScopeProviderExtension"
interface="org.jetbrains.kotlin.synthetic.SyntheticScopeProviderExtension"
area="IDEA_PROJECT"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
@@ -42,6 +42,10 @@
<extensionPoint qualifiedName="org.jetbrains.kotlin.scriptDefinitionsProvider"
interface="kotlin.script.experimental.intellij.ScriptDefinitionsProvider"
area="IDEA_PROJECT"/>
<extensionPoint qualifiedName="org.jetbrains.kotlin.syntheticScopeProviderExtension"
interface="org.jetbrains.kotlin.synthetic.SyntheticScopeProviderExtension"
area="IDEA_PROJECT"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
+4
View File
@@ -179,5 +179,9 @@
<idePlatformKindTooling implementation="org.jetbrains.kotlin.idea.core.platform.impl.JvmIdePlatformKindTooling"/>
<idePlatformKindTooling implementation="org.jetbrains.kotlin.idea.core.platform.impl.JsIdePlatformKindTooling"/>
<syntheticScopeProviderExtension implementation="org.jetbrains.kotlin.idea.debugger.evaluate.DebuggerFieldSyntheticScopeProvider"/>
<expressionCodegenExtension implementation="org.jetbrains.kotlin.idea.debugger.evaluate.DebuggerFieldExpressionCodegenExtension"/>
<completionInformationProvider implementation="org.jetbrains.kotlin.idea.debugger.evaluate.DebuggerFieldCompletionInformationProvider" />
</extensions>
</idea-plugin>
+4
View File
@@ -167,5 +167,9 @@
<idePlatformKindTooling implementation="org.jetbrains.kotlin.idea.core.platform.impl.JvmIdePlatformKindTooling"/>
<idePlatformKindTooling implementation="org.jetbrains.kotlin.idea.core.platform.impl.JsIdePlatformKindTooling"/>
<syntheticScopeProviderExtension implementation="org.jetbrains.kotlin.idea.debugger.evaluate.DebuggerFieldSyntheticScopeProvider"/>
<expressionCodegenExtension implementation="org.jetbrains.kotlin.idea.debugger.evaluate.DebuggerFieldExpressionCodegenExtension"/>
<completionInformationProvider implementation="org.jetbrains.kotlin.idea.debugger.evaluate.DebuggerFieldCompletionInformationProvider" />
</extensions>
</idea-plugin>
@@ -0,0 +1,105 @@
package fieldGetters
import forTests.FieldsGetters
import forTests.FieldsGetters.*
fun main(args: Array<String>) {
//Breakpoint!
val a = 5
}
class K1 {
val a: Int = 0
get() = field + 1
}
class K2 {
@JvmField
val a: Int = 0
}
// EXPRESSION: K1().a
// RESULT: 1: I
// EXPRESSION: K1().a_field
// RESULT: 0: I
// EXPRESSION: K2().a
// RESULT: 0: I
// EXPRESSION: K2().a_field
// RESULT: 0: I
// EXPRESSION: PublicField().foo
// RESULT: "a": Ljava/lang/String;
// EXPRESSION: PublicField().foo_field
// RESULT: Unresolved reference: foo_field
// EXPRESSION: PackagePrivateField().foo
// RESULT: "b": Ljava/lang/String;
// EXPRESSION: PackagePrivateField().foo_field
// RESULT: Unresolved reference: foo_field
// EXPRESSION: ProtectedField().foo
// RESULT: "c": Ljava/lang/String;
// EXPRESSION: ProtectedField().foo_field
// RESULT: Unresolved reference: foo_field
// EXPRESSION: PrivateField().foo
// RESULT: "d": Ljava/lang/String;
// EXPRESSION: PrivateField().foo_field
// RESULT: Unresolved reference: foo_field
// EXPRESSION: PublicFieldGetter().foo
// RESULT: "b": Ljava/lang/String;
// EXPRESSION: PublicFieldGetter().foo_field
// RESULT: "a": Ljava/lang/String;
// EXPRESSION: PrivateFieldPublicGetter().foo
// RESULT: "d": Ljava/lang/String;
// EXPRESSION: PrivateFieldPublicGetter().foo_field
// RESULT: "c": Ljava/lang/String;
// EXPRESSION: PrivateFieldPrivateGetter().foo
// RESULT: "f": Ljava/lang/String;
// EXPRESSION: PrivateFieldPrivateGetter().foo_field
// RESULT: "e": Ljava/lang/String;
// EXPRESSION: PublicGetter1().foo
// RESULT: "g": Ljava/lang/String;
// EXPRESSION: PublicGetter1().foo_field
// RESULT: "a": Ljava/lang/String;
// EXPRESSION: PublicGetter2().foo
// RESULT: "h": Ljava/lang/String;
// EXPRESSION: PublicGetter2().foo_field
// RESULT: "b": Ljava/lang/String;
// EXPRESSION: PrivateGetter1().foo
// RESULT: "d": Ljava/lang/String;
// EXPRESSION: PrivateGetter1().foo_field
// RESULT: Unresolved reference: foo_field
// EXPRESSION: PublicGetterOnly().foo
// RESULT: "a": Ljava/lang/String;
// EXPRESSION: PublicGetterOnly().foo_field
// RESULT: Unresolved reference: foo_field
// EXPRESSION: PublicFieldAndGetterInParent().foo
// RESULT: "a": Ljava/lang/String;
// EXPRESSION: PublicFieldAndGetterInParent().foo_field
// RESULT: "b": Ljava/lang/String;
@@ -0,0 +1,29 @@
LineBreakpoint created at fieldGetters.kt:8
Run Java
Connected to the target VM
fieldGetters.kt:8
Compile bytecode for K1().a
Compile bytecode for K1().a_field
Compile bytecode for K2().a
Compile bytecode for K2().a_field
Compile bytecode for PublicField().foo
Compile bytecode for PackagePrivateField().foo
Compile bytecode for ProtectedField().foo
Compile bytecode for PrivateField().foo
Compile bytecode for PublicFieldGetter().foo
Compile bytecode for PublicFieldGetter().foo_field
Compile bytecode for PrivateFieldPublicGetter().foo
Compile bytecode for PrivateFieldPublicGetter().foo_field
Compile bytecode for PrivateFieldPrivateGetter().foo
Compile bytecode for PrivateFieldPrivateGetter().foo_field
Compile bytecode for PublicGetter1().foo
Compile bytecode for PublicGetter1().foo_field
Compile bytecode for PublicGetter2().foo
Compile bytecode for PublicGetter2().foo_field
Compile bytecode for PrivateGetter1().foo
Compile bytecode for PublicGetterOnly().foo
Compile bytecode for PublicFieldAndGetterInParent().foo
Compile bytecode for PublicFieldAndGetterInParent().foo_field
Disconnected from the target VM
Process finished with exit code 0
@@ -0,0 +1,76 @@
package forTests;
public class FieldsGetters {
public static class PublicField {
public String foo = "a";
}
public static class PackagePrivateField {
String foo = "b";
}
public static class ProtectedField {
protected String foo = "c";
}
public static class PrivateField {
private String foo = "d";
}
public static class PublicFieldGetter {
public final String foo = "a";
public String getFoo() {
return "b";
}
}
public static class PrivateFieldPublicGetter {
private final String foo = "c";
public String getFoo() {
return "d";
}
}
public static class PrivateFieldPrivateGetter {
private final String foo = "e";
public String getFoo() {
return "f";
}
}
public static class PublicGetter1 extends PublicField {
public String getFoo() {
return "g";
}
}
public static class PublicGetter2 extends PackagePrivateField {
public String getFoo() {
return "h";
}
}
public static class PrivateGetter1 extends PrivateField {
private String getFoo() {
return "g";
}
}
public static class PublicGetterOnly {
public String getFoo() {
return "a";
}
}
public static class PublicFieldAndGetterInParent extends PublicGetterOnly {
public String foo = "b";
}
public abstract class AbstractGetter {
public String foo = "c";
public abstract String getFoo();
}
}
@@ -136,6 +136,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/extractVariablesFromCall.kt");
}
@TestMetadata("fieldGetters.kt")
public void testFieldGetters() throws Exception {
runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/fieldGetters.kt");
}
@TestMetadata("fileWithError.kt")
public void testFileWithError() throws Exception {
runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/fileWithError.kt");