mirror of
https://github.com/corda/corda.git
synced 2025-01-03 11:44:16 +00:00
CORDA-1134 - Don't use private serializers for all CAPS properties (#2708)
Whilst it does currently work it only does so by going down an incorrect code path. A property THING that is public and has a getter should be fetched using that getter, not trip into the private property accessor.
This commit is contained in:
parent
ba925c0277
commit
9e9fab7e1a
@ -136,7 +136,16 @@ fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
|
|||||||
this.field = property
|
this.field = property
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clazz = clazz.superclass
|
||||||
|
} while (clazz != null)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Running as two loops rather than one as we need to ensure we have captured all of the properties
|
||||||
|
// before looking for interacting methods and need to cope with the class hierarchy introducing
|
||||||
|
// new properties / methods
|
||||||
|
//
|
||||||
|
clazz = this
|
||||||
|
do {
|
||||||
// Note: It is possible for a class to have multiple instances of a function where the types
|
// Note: It is possible for a class to have multiple instances of a function where the types
|
||||||
// differ. For example:
|
// differ. For example:
|
||||||
// interface I<out T> { val a: T }
|
// interface I<out T> { val a: T }
|
||||||
@ -148,14 +157,23 @@ fun Class<out Any?>.propertyDescriptors(): Map<String, PropertyDescriptor> {
|
|||||||
//
|
//
|
||||||
// In addition, only getters that take zero parameters and setters that take a single
|
// In addition, only getters that take zero parameters and setters that take a single
|
||||||
// parameter will be considered
|
// parameter will be considered
|
||||||
clazz.declaredMethods?.map { func ->
|
clazz!!.declaredMethods?.map { func ->
|
||||||
if (!Modifier.isPublic(func.modifiers)) return@map
|
if (!Modifier.isPublic(func.modifiers)) return@map
|
||||||
if (func.name == "getClass") return@map
|
if (func.name == "getClass") return@map
|
||||||
|
|
||||||
PropertyDescriptorsRegex.re.find(func.name)?.apply {
|
PropertyDescriptorsRegex.re.find(func.name)?.apply {
|
||||||
|
// matching means we have an func getX where the property could be x or X
|
||||||
|
// so having pre-loaded all of the properties we try to match to either case. If that
|
||||||
|
// fails the getter doesn't refer to a property directly, but may to a cosntructor
|
||||||
|
// parameter that shadows a property
|
||||||
|
val properties =
|
||||||
|
classProperties[groups[2]!!.value] ?:
|
||||||
|
classProperties[groups[2]!!.value.decapitalize()] ?:
|
||||||
// take into account those constructor properties that don't directly map to a named
|
// take into account those constructor properties that don't directly map to a named
|
||||||
// property which are, by default, already added to the map
|
// property which are, by default, already added to the map
|
||||||
classProperties.computeIfAbsent(groups[2]!!.value.decapitalize()) { PropertyDescriptor() }.apply {
|
classProperties.computeIfAbsent(groups[2]!!.value) { PropertyDescriptor() }
|
||||||
|
|
||||||
|
properties.apply {
|
||||||
when (groups[1]!!.value) {
|
when (groups[1]!!.value) {
|
||||||
"set" -> {
|
"set" -> {
|
||||||
if (func.parameterCount == 1) {
|
if (func.parameterCount == 1) {
|
||||||
@ -221,12 +239,16 @@ internal fun <T : Any> propertiesForSerializationFromConstructor(
|
|||||||
// it so just ignore it as it'll be supplied at runtime anyway on invocation
|
// it so just ignore it as it'll be supplied at runtime anyway on invocation
|
||||||
val name = param.value.name ?: return@forEach
|
val name = param.value.name ?: return@forEach
|
||||||
|
|
||||||
val propertyReader = if (name in classProperties) {
|
// We will already have disambiguated getA for property A or a but we still need to cope
|
||||||
if (classProperties[name]!!.getter != null) {
|
// with the case we don't know the case of A when the parameter doesn't match a property
|
||||||
// it's a publicly accessible property
|
// but has a getter
|
||||||
val matchingProperty = classProperties[name]!!
|
val matchingProperty = classProperties[name] ?: classProperties[name.capitalize()] ?:
|
||||||
|
throw NotSerializableException(
|
||||||
|
"Constructor parameter - \"$name\" - doesn't refer to a property of \"$clazz\"")
|
||||||
|
|
||||||
// Check that the method has a getter in java.
|
// If the property has a getter we'll use that to retrieve it's value from the instance, if it doesn't
|
||||||
|
// *for *know* we switch to a reflection based method
|
||||||
|
val propertyReader = if (matchingProperty.getter != null) {
|
||||||
val getter = matchingProperty.getter ?: throw NotSerializableException(
|
val getter = matchingProperty.getter ?: throw NotSerializableException(
|
||||||
"Property has no getter method for - \"$name\" - of \"$clazz\". If using Java and the parameter name"
|
"Property has no getter method for - \"$name\" - of \"$clazz\". If using Java and the parameter name"
|
||||||
+ "looks anonymous, check that you have the -parameters option specified in the "
|
+ "looks anonymous, check that you have the -parameters option specified in the "
|
||||||
@ -250,10 +272,6 @@ internal fun <T : Any> propertiesForSerializationFromConstructor(
|
|||||||
|
|
||||||
Pair(PrivatePropertyReader(field, type), field.genericType)
|
Pair(PrivatePropertyReader(field, type), field.genericType)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
throw NotSerializableException(
|
|
||||||
"Constructor parameter - \"$name\" - doesn't refer to a property of \"$clazz\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
this += PropertyAccessorConstructor(
|
this += PropertyAccessorConstructor(
|
||||||
param.index,
|
param.index,
|
||||||
|
@ -196,4 +196,32 @@ class PrivatePropertyTests {
|
|||||||
|
|
||||||
assertEquals(c1, c2)
|
assertEquals(c1, c2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Reproduces CORDA-1134
|
||||||
|
//
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@Test
|
||||||
|
fun allCapsProprtyNotPrivate() {
|
||||||
|
data class C (val CCC: String)
|
||||||
|
|
||||||
|
val output = SerializationOutput(factory).serializeAndReturnSchema(C("this is nice"))
|
||||||
|
|
||||||
|
val serializersByDescriptor = fields["serializersByDesc"]!!.get(factory) as ConcurrentHashMap<Any, AMQPSerializer<Any>>
|
||||||
|
|
||||||
|
val schemaDescriptor = output.schema.types.first().descriptor.name
|
||||||
|
serializersByDescriptor.filterKeys { (it as Symbol) == schemaDescriptor }.values.apply {
|
||||||
|
assertEquals(1, size)
|
||||||
|
|
||||||
|
assertTrue(this.first() is ObjectSerializer)
|
||||||
|
val propertySerializers = (this.first() as ObjectSerializer).propertySerializers.serializationOrder.map { it.getter }
|
||||||
|
|
||||||
|
// CCC is the only property to be serialised
|
||||||
|
assertEquals(1, propertySerializers.size)
|
||||||
|
|
||||||
|
// and despite being all caps it should still be a public getter
|
||||||
|
assertTrue(propertySerializers[0].propertyReader is PublicPropertyReader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user