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); 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") @TestMetadata("equalsHashCodeToString.kt")
public void testEqualsHashCodeToString() throws Exception { public void testEqualsHashCodeToString() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/equalsHashCodeToString.kt"); String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/call/equalsHashCodeToString.kt");
@@ -102,8 +102,16 @@ internal sealed class FunctionCaller(
abstract class FieldSetter( abstract class FieldSetter(
field: ReflectField, field: ReflectField,
private val notNull: Boolean,
private val requiresInstance: Boolean private val requiresInstance: Boolean
) : FieldAccessor(field, if (requiresInstance) 2 else 1) { ) : 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? { override fun call(args: Array<*>): Any? {
checkArguments(args) checkArguments(args)
return field.set(if (requiresInstance) args.first() else null, args.last()) 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<*>) { override fun checkArguments(args: Array<*>) {
super.checkArguments(args) super.checkArguments(args)
checkObjectInstance(args.firstOrNull()) checkObjectInstance(args.firstOrNull())
@@ -18,6 +18,7 @@ package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.DescriptorFactory import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.types.TypeUtils
import java.lang.reflect.Field import java.lang.reflect.Field
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import kotlin.reflect.KMutableProperty import kotlin.reflect.KMutableProperty
@@ -81,16 +82,19 @@ private fun KPropertyImpl.Accessor<*>.computeCallerForAccessor(isGetter: Boolean
fun isPlatformStaticProperty() = fun isPlatformStaticProperty() =
property.descriptor.annotations.findAnnotation(PLATFORM_STATIC) != null property.descriptor.annotations.findAnnotation(PLATFORM_STATIC) != null
fun isNotNullProperty() =
!TypeUtils.isNullableType(property.descriptor.type)
fun computeFieldCaller(field: Field): FunctionCaller.FieldAccessor = when { fun computeFieldCaller(field: Field): FunctionCaller.FieldAccessor = when {
!Modifier.isStatic(field.modifiers) -> !Modifier.isStatic(field.modifiers) ->
if (isGetter) FunctionCaller.InstanceFieldGetter(field) if (isGetter) FunctionCaller.InstanceFieldGetter(field)
else FunctionCaller.InstanceFieldSetter(field) else FunctionCaller.InstanceFieldSetter(field, isNotNullProperty())
isPlatformStaticProperty() -> isPlatformStaticProperty() ->
if (isGetter) FunctionCaller.PlatformStaticInObjectFieldGetter(field) if (isGetter) FunctionCaller.PlatformStaticInObjectFieldGetter(field)
else FunctionCaller.PlatformStaticInObjectFieldSetter(field) else FunctionCaller.PlatformStaticInObjectFieldSetter(field, isNotNullProperty())
else -> else ->
if (isGetter) FunctionCaller.StaticFieldGetter(field) if (isGetter) FunctionCaller.StaticFieldGetter(field)
else FunctionCaller.StaticFieldSetter(field) else FunctionCaller.StaticFieldSetter(field, isNotNullProperty())
} }
val jvmSignature = RuntimeTypeMapper.mapPropertySignature(property.descriptor) val jvmSignature = RuntimeTypeMapper.mapPropertySignature(property.descriptor)