Do not allow to assign null to a non-null property via reflection
This was already working for accessor-based properties because the assertions were already generated into corresponding accessors, but for fields we must manually check the value against null
This commit is contained in:
Vendored
+43
@@ -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: T) {
|
||||
private var baz: String? = ""
|
||||
private var generic: T = t
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val p = A::class.memberProperties.single() as KMutableProperty1<A, String?>
|
||||
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, String?>
|
||||
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<CounterTest<*>, 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<CounterTest<*>, 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"
|
||||
}
|
||||
+6
@@ -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");
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user