Type-check reference to property with invisible setter to KProperty
#KT-12337 Fixed
This commit is contained in:
@@ -3090,7 +3090,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
PropertyReferenceCodegen codegen = new PropertyReferenceCodegen(
|
||||
state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION),
|
||||
element, classBuilder, target, dispatchReceiver, receiverAsmType
|
||||
element, classBuilder, variableDescriptor, target, dispatchReceiver, receiverAsmType
|
||||
);
|
||||
codegen.generate();
|
||||
|
||||
|
||||
@@ -140,14 +140,16 @@ public class JvmRuntimeTypes {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public KotlinType getSupertypeForPropertyReference(@NotNull VariableDescriptorWithAccessors descriptor, boolean isBound) {
|
||||
public KotlinType getSupertypeForPropertyReference(
|
||||
@NotNull VariableDescriptorWithAccessors descriptor, boolean isMutable, boolean isBound
|
||||
) {
|
||||
if (descriptor instanceof LocalVariableDescriptor) {
|
||||
return (descriptor.isVar() ? mutableLocalVariableReference : localVariableReference).getDefaultType();
|
||||
return (isMutable ? mutableLocalVariableReference : localVariableReference).getDefaultType();
|
||||
}
|
||||
|
||||
int arity = (descriptor.getExtensionReceiverParameter() != null ? 1 : 0) +
|
||||
(descriptor.getDispatchReceiverParameter() != null ? 1 : 0) -
|
||||
(isBound ? 1 : 0);
|
||||
return (descriptor.isVar() ? mutablePropertyReferences : propertyReferences).get(arity).getDefaultType();
|
||||
return (isMutable ? mutablePropertyReferences : propertyReferences).get(arity).getDefaultType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.method
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.context.ClassContext
|
||||
@@ -46,6 +47,7 @@ class PropertyReferenceCodegen(
|
||||
context: ClassContext,
|
||||
expression: KtElement,
|
||||
classBuilder: ClassBuilder,
|
||||
private val localVariableDescriptorForReference: VariableDescriptor,
|
||||
private val target: VariableDescriptor,
|
||||
dispatchReceiver: ReceiverValue?,
|
||||
private val receiverType: Type? // non-null for bound references
|
||||
@@ -177,7 +179,7 @@ class PropertyReferenceCodegen(
|
||||
value.put(OBJECT_TYPE, this)
|
||||
}
|
||||
|
||||
if (!target.isVar) return
|
||||
if (!ReflectionTypes.isKMutablePropertyType(localVariableDescriptorForReference.type)) return
|
||||
|
||||
val setterParameters = (getterParameters + arrayOf(OBJECT_TYPE))
|
||||
generateAccessor(method("set", Type.VOID_TYPE, *setterParameters)) { value ->
|
||||
|
||||
+8
-2
@@ -301,8 +301,13 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
callableDescriptor = bindingContext.get(VARIABLE, expression);
|
||||
if (callableDescriptor == null) return;
|
||||
|
||||
//noinspection ConstantConditions
|
||||
supertypes = Collections.singleton(
|
||||
runtimeTypes.getSupertypeForPropertyReference((PropertyDescriptor) target, receiverType != null)
|
||||
runtimeTypes.getSupertypeForPropertyReference(
|
||||
(PropertyDescriptor) target,
|
||||
ReflectionTypes.Companion.isKMutablePropertyType(callableDescriptor.getReturnType()),
|
||||
receiverType != null
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
@@ -380,7 +385,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
if (delegate != null && descriptor instanceof VariableDescriptorWithAccessors) {
|
||||
VariableDescriptorWithAccessors variableDescriptor = (VariableDescriptorWithAccessors) descriptor;
|
||||
String name = inventAnonymousClassName();
|
||||
KotlinType supertype = runtimeTypes.getSupertypeForPropertyReference(variableDescriptor, /* bound = */ false);
|
||||
KotlinType supertype =
|
||||
runtimeTypes.getSupertypeForPropertyReference(variableDescriptor, variableDescriptor.isVar(), /* bound = */ false);
|
||||
ClassDescriptor classDescriptor = recordClassForCallable(delegate, variableDescriptor, Collections.singleton(supertype), name);
|
||||
recordClosure(classDescriptor, name);
|
||||
}
|
||||
|
||||
+14
-6
@@ -180,7 +180,8 @@ private fun createReflectionTypeForCallableDescriptor(
|
||||
reflectionTypes: ReflectionTypes,
|
||||
trace: BindingTrace?,
|
||||
reportOn: KtExpression?,
|
||||
ignoreReceiver: Boolean
|
||||
ignoreReceiver: Boolean,
|
||||
scopeOwnerDescriptor: DeclarationDescriptor
|
||||
): KotlinType? {
|
||||
if (descriptor is CallableMemberDescriptor && isMemberExtension(descriptor)) {
|
||||
if (reportOn != null) {
|
||||
@@ -209,7 +210,12 @@ private fun createReflectionTypeForCallableDescriptor(
|
||||
return reflectionTypes.getKFunctionType(Annotations.EMPTY, receiverType, valueParametersTypes, returnType)
|
||||
}
|
||||
is PropertyDescriptor -> {
|
||||
reflectionTypes.getKPropertyType(Annotations.EMPTY, receiverType, descriptor.type, descriptor.isVar)
|
||||
val mutable = descriptor.isVar && run {
|
||||
val setter = descriptor.setter
|
||||
// TODO: support private-to-this
|
||||
setter == null || Visibilities.isVisible(null, setter, scopeOwnerDescriptor)
|
||||
}
|
||||
reflectionTypes.getKPropertyType(Annotations.EMPTY, receiverType, descriptor.type, mutable)
|
||||
}
|
||||
is VariableDescriptor -> {
|
||||
if (reportOn != null) {
|
||||
@@ -230,9 +236,10 @@ private fun isMemberExtension(descriptor: CallableMemberDescriptor): Boolean {
|
||||
fun getReflectionTypeForCandidateDescriptor(
|
||||
descriptor: CallableDescriptor,
|
||||
reflectionTypes: ReflectionTypes,
|
||||
ignoreReceiver: Boolean
|
||||
ignoreReceiver: Boolean,
|
||||
scopeOwnerDescriptor: DeclarationDescriptor
|
||||
): KotlinType? =
|
||||
createReflectionTypeForCallableDescriptor(descriptor, null, reflectionTypes, null, null, ignoreReceiver)
|
||||
createReflectionTypeForCallableDescriptor(descriptor, null, reflectionTypes, null, null, ignoreReceiver, scopeOwnerDescriptor)
|
||||
|
||||
fun createReflectionTypeForResolvedCallableReference(
|
||||
reference: KtCallableReferenceExpression,
|
||||
@@ -243,7 +250,7 @@ fun createReflectionTypeForResolvedCallableReference(
|
||||
reflectionTypes: ReflectionTypes
|
||||
): KotlinType? {
|
||||
val type = createReflectionTypeForCallableDescriptor(
|
||||
descriptor, lhsType, reflectionTypes, context.trace, reference.callableReference, ignoreReceiver
|
||||
descriptor, lhsType, reflectionTypes, context.trace, reference.callableReference, ignoreReceiver, context.scope.ownerDescriptor
|
||||
) ?: return null
|
||||
when (descriptor) {
|
||||
is FunctionDescriptor -> {
|
||||
@@ -272,7 +279,8 @@ fun getResolvedCallableReferenceShapeType(
|
||||
overloadResolutionResults.isSingleResult ->
|
||||
OverloadResolutionResultsUtil.getResultingCall(overloadResolutionResults, context.contextDependency)?.let { call ->
|
||||
createReflectionTypeForCallableDescriptor(
|
||||
call.resultingDescriptor, lhsType, reflectionTypes, context.trace, reference, reference.isEmptyLHS
|
||||
call.resultingDescriptor, lhsType, reflectionTypes, context.trace, reference, reference.isEmptyLHS,
|
||||
context.scope.ownerDescriptor
|
||||
)
|
||||
}
|
||||
expectedTypeUnknown /* && overload resolution was ambiguous */ ->
|
||||
|
||||
@@ -161,7 +161,8 @@ class CandidateResolver(
|
||||
val candidate = candidateCall.candidateDescriptor
|
||||
val candidateReflectionType = getReflectionTypeForCandidateDescriptor(
|
||||
candidate, reflectionTypes,
|
||||
call.callElement.parent.let { it is KtCallableReferenceExpression && it.isEmptyLHS }
|
||||
call.callElement.parent.let { it is KtCallableReferenceExpression && it.isEmptyLHS },
|
||||
scope.ownerDescriptor
|
||||
)
|
||||
if (candidateReflectionType != null) {
|
||||
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(candidateReflectionType, expectedType)) {
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import kotlin.reflect.KMutableProperty
|
||||
|
||||
class Bar(name: String) {
|
||||
var foo: String = name
|
||||
private set
|
||||
|
||||
fun test() {
|
||||
val p = Bar::foo
|
||||
if (p !is KMutableProperty<*>) throw AssertionError("Fail: p is not a KMutableProperty")
|
||||
p.set(this, "OK")
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val bar = Bar("Fail")
|
||||
bar.test()
|
||||
return bar.foo
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// See KT-12337 Reference to property with invisible setter should not be a KMutableProperty
|
||||
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.KMutableProperty
|
||||
|
||||
open class Bar(name: String) {
|
||||
var foo: String = name
|
||||
private set
|
||||
}
|
||||
|
||||
class Baz : Bar("") {
|
||||
fun ref() = Bar::foo
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val p1: KProperty1<Bar, String> = Bar::foo
|
||||
if (p1 is KMutableProperty<*>) return "Fail: p1 is a KMutableProperty"
|
||||
|
||||
val p2 = Baz().ref()
|
||||
if (p2 is KMutableProperty<*>) return "Fail: p2 is a KMutableProperty"
|
||||
|
||||
val p3 = Bar("")::foo
|
||||
if (p3 is KMutableProperty<*>) return "Fail: p3 is a KMutableProperty"
|
||||
|
||||
return p1.get(Bar("OK"))
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlin.reflect.*
|
||||
import kotlin.reflect.jvm.*
|
||||
|
||||
object Delegate {
|
||||
operator fun getValue(thiz: My, property: KProperty<*>): String {
|
||||
if (property !is KMutableProperty<*>) return "Fail: property is not a KMutableProperty"
|
||||
property as KMutableProperty1<My, String>
|
||||
|
||||
try {
|
||||
property.set(thiz, "")
|
||||
return "Fail: property.set should cause IllegalCallableAccessException"
|
||||
}
|
||||
catch (e: IllegalCallableAccessException) {
|
||||
// OK
|
||||
}
|
||||
|
||||
property.accessible = true
|
||||
property.set(thiz, "")
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
operator fun setValue(thiz: My, property: KProperty<*>, value: String) {
|
||||
}
|
||||
}
|
||||
|
||||
class My {
|
||||
var delegate: String by Delegate
|
||||
private set
|
||||
}
|
||||
|
||||
fun box() = My().delegate
|
||||
@@ -1956,6 +1956,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("privateSetterInsideClass.kt")
|
||||
public void testPrivateSetterInsideClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/property/privateSetterInsideClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("privateSetterOutsideClass.kt")
|
||||
public void testPrivateSetterOutsideClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/property/privateSetterOutsideClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleExtension.kt")
|
||||
public void testSimpleExtension() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/property/simpleExtension.kt");
|
||||
@@ -5116,6 +5128,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("privateSetterKPropertyIsNotMutable.kt")
|
||||
public void testPrivateSetterKPropertyIsNotMutable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/privateSetterKPropertyIsNotMutable.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("privateVar.kt")
|
||||
public void testPrivateVar() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/privateVar.kt");
|
||||
|
||||
@@ -216,6 +216,9 @@ public abstract class KotlinBuiltIns {
|
||||
|
||||
public final FqNameUnsafe kClass = reflect("KClass");
|
||||
public final FqNameUnsafe kCallable = reflect("KCallable");
|
||||
public final FqNameUnsafe kMutableProperty0 = reflect("KMutableProperty0");
|
||||
public final FqNameUnsafe kMutableProperty1 = reflect("KMutableProperty1");
|
||||
public final FqNameUnsafe kMutableProperty2 = reflect("KMutableProperty2");
|
||||
public final ClassId kProperty = ClassId.topLevel(reflect("KProperty").toSafe());
|
||||
|
||||
public final Map<FqNameUnsafe, PrimitiveType> fqNameToPrimitiveType;
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
@@ -120,12 +121,19 @@ class ReflectionTypes(module: ModuleDescriptor) {
|
||||
type.isFunctionTypeOrSubtype || isKCallableType(type)
|
||||
|
||||
private fun isKCallableType(type: KotlinType): Boolean =
|
||||
isExactKCallableType(type) ||
|
||||
hasFqName(type.constructor, KotlinBuiltIns.FQ_NAMES.kCallable) ||
|
||||
type.constructor.supertypes.any { isKCallableType(it) }
|
||||
|
||||
private fun isExactKCallableType(type: KotlinType): Boolean {
|
||||
val descriptor = type.constructor.declarationDescriptor
|
||||
return descriptor is ClassDescriptor && DescriptorUtils.getFqName(descriptor) == KotlinBuiltIns.FQ_NAMES.kCallable
|
||||
fun isKMutablePropertyType(type: KotlinType): Boolean {
|
||||
val typeConstructor = type.constructor
|
||||
return hasFqName(typeConstructor, KotlinBuiltIns.FQ_NAMES.kMutableProperty0) ||
|
||||
hasFqName(typeConstructor, KotlinBuiltIns.FQ_NAMES.kMutableProperty1) ||
|
||||
hasFqName(typeConstructor, KotlinBuiltIns.FQ_NAMES.kMutableProperty2)
|
||||
}
|
||||
|
||||
private fun hasFqName(typeConstructor: TypeConstructor, fqName: FqNameUnsafe): Boolean {
|
||||
val descriptor = typeConstructor.declarationDescriptor as? ClassDescriptor ?: return false
|
||||
return descriptor.name == fqName.shortName() && DescriptorUtils.getFqName(descriptor) == fqName
|
||||
}
|
||||
|
||||
fun createKPropertyStarType(module: ModuleDescriptor): KotlinType? {
|
||||
|
||||
@@ -249,7 +249,7 @@ private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factor
|
||||
fun CallableDescriptor.callableReferenceType(resolutionFacade: ResolutionFacade): FuzzyType? {
|
||||
if (!CallType.CALLABLE_REFERENCE.descriptorKindFilter.accepts(this)) return null // not supported by callable references
|
||||
return getReflectionTypeForCandidateDescriptor(
|
||||
this, resolutionFacade.getFrontendService(ReflectionTypes::class.java), false
|
||||
this, resolutionFacade.getFrontendService(ReflectionTypes::class.java), false, resolutionFacade.moduleDescriptor
|
||||
)?.toFuzzyType(emptyList())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user