Type-check reference to property with invisible setter to KProperty

#KT-12337 Fixed
This commit is contained in:
Alexander Udalov
2016-06-15 17:53:52 +03:00
parent c5a208f3eb
commit 6562a2db19
13 changed files with 145 additions and 19 deletions
@@ -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 ->
@@ -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);
}
@@ -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)) {
@@ -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
}
@@ -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"))
}
@@ -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())
}