diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/call/disallowNullValueForNotNullField.kt b/compiler/testData/codegen/boxWithStdlib/reflection/call/disallowNullValueForNotNullField.kt new file mode 100644 index 00000000000..13cdc93d5f0 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/call/disallowNullValueForNotNullField.kt @@ -0,0 +1,43 @@ +import kotlin.reflect.* +import kotlin.reflect.jvm.* +import kotlin.platform.platformStatic as static + +class A { + private var foo: String = "" +} + +object O { + private static var bar: String = "" +} + +class CounterTest(t: T) { + private var baz: String? = "" + private var generic: T = t +} + +fun box(): String { + val p = A::class.memberProperties.single() as KMutableProperty1 + p.isAccessible = true + try { + p.setter.call(A(), null) + return "Fail: exception should have been thrown" + } catch (e: IllegalArgumentException) {} + + + val o = O::class.memberProperties.single() as KMutableProperty1 + o.isAccessible = true + try { + o.setter.call(O, null) + return "Fail: exception should have been thrown" + } catch (e: IllegalArgumentException) {} + + + val c = CounterTest::class.memberProperties.single { it.name == "baz" } as KMutableProperty1, String?> + c.isAccessible = true + c.setter.call(CounterTest(""), null) // Should not fail, because CounterTest::baz is nullable + val d = CounterTest::class.memberProperties.single { it.name == "generic" } as KMutableProperty1, String?> + d.isAccessible = true + d.setter.call(CounterTest(""), null) // Also should not fail, because we can't be sure about nullability of 'generic' + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index b6d8907cd32..2c035469e77 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -2844,6 +2844,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("disallowNullValueForNotNullField.kt") + public void testDisallowNullValueForNotNullField() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/disallowNullValueForNotNullField.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("equalsHashCodeToString.kt") public void testEqualsHashCodeToString() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/equalsHashCodeToString.kt"); diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt index 2a1058d6d34..ebf0cdddf86 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/FunctionCaller.kt @@ -102,8 +102,16 @@ internal sealed class FunctionCaller( abstract class FieldSetter( field: ReflectField, + private val notNull: Boolean, private val requiresInstance: Boolean ) : FieldAccessor(field, if (requiresInstance) 2 else 1) { + override fun checkArguments(args: Array<*>) { + super.checkArguments(args) + if (notNull && args.last() == null) { + throw IllegalArgumentException("null is not allowed as a value for this property.") + } + } + override fun call(args: Array<*>): Any? { checkArguments(args) return field.set(if (requiresInstance) args.first() else null, args.last()) @@ -121,11 +129,11 @@ internal sealed class FunctionCaller( } } - class StaticFieldSetter(field: ReflectField) : FieldSetter(field, requiresInstance = false) + class StaticFieldSetter(field: ReflectField, notNull: Boolean) : FieldSetter(field, notNull, requiresInstance = false) - class InstanceFieldSetter(field: ReflectField) : FieldSetter(field, requiresInstance = true) + class InstanceFieldSetter(field: ReflectField, notNull: Boolean) : FieldSetter(field, notNull, requiresInstance = true) - class PlatformStaticInObjectFieldSetter(field: ReflectField) : FieldSetter(field, requiresInstance = true) { + class PlatformStaticInObjectFieldSetter(field: ReflectField, notNull: Boolean) : FieldSetter(field, notNull, requiresInstance = true) { override fun checkArguments(args: Array<*>) { super.checkArguments(args) checkObjectInstance(args.firstOrNull()) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt index 0bf6dd83151..b8e5657961b 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt @@ -18,6 +18,7 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.resolve.DescriptorFactory +import org.jetbrains.kotlin.types.TypeUtils import java.lang.reflect.Field import java.lang.reflect.Modifier import kotlin.reflect.KMutableProperty @@ -81,16 +82,19 @@ private fun KPropertyImpl.Accessor<*>.computeCallerForAccessor(isGetter: Boolean fun isPlatformStaticProperty() = property.descriptor.annotations.findAnnotation(PLATFORM_STATIC) != null + fun isNotNullProperty() = + !TypeUtils.isNullableType(property.descriptor.type) + fun computeFieldCaller(field: Field): FunctionCaller.FieldAccessor = when { !Modifier.isStatic(field.modifiers) -> if (isGetter) FunctionCaller.InstanceFieldGetter(field) - else FunctionCaller.InstanceFieldSetter(field) + else FunctionCaller.InstanceFieldSetter(field, isNotNullProperty()) isPlatformStaticProperty() -> if (isGetter) FunctionCaller.PlatformStaticInObjectFieldGetter(field) - else FunctionCaller.PlatformStaticInObjectFieldSetter(field) + else FunctionCaller.PlatformStaticInObjectFieldSetter(field, isNotNullProperty()) else -> if (isGetter) FunctionCaller.StaticFieldGetter(field) - else FunctionCaller.StaticFieldSetter(field) + else FunctionCaller.StaticFieldSetter(field, isNotNullProperty()) } val jvmSignature = RuntimeTypeMapper.mapPropertySignature(property.descriptor)