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:
Alexander Udalov
2015-08-04 21:09:09 +03:00
parent 3091f2af3b
commit 49b689f883
4 changed files with 67 additions and 6 deletions
@@ -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"
}
@@ -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)