CORDA-2318 resolve nested type params (#4405)

* Test nested generics case

* Recursively resolve type parameters

* Comment and ensure type variables are resolved

* Only upper-bounding needs to be done recursively
This commit is contained in:
Dominic Fox 2018-12-13 10:11:43 +00:00 committed by GitHub
parent 8610f22cd9
commit 858301abae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 18 deletions

View File

@ -191,23 +191,6 @@ sealed class TypeIdentifier {
}
}
private class ReconstitutedParameterizedType(
private val _rawType: Type,
private val _ownerType: Type?,
private val _actualTypeArguments: Array<Type>) : ParameterizedType {
override fun getRawType(): Type = _rawType
override fun getOwnerType(): Type? = _ownerType
override fun getActualTypeArguments(): Array<Type> = _actualTypeArguments
override fun toString(): String = TypeIdentifier.forGenericType(this).prettyPrint(false)
override fun equals(other: Any?): Boolean =
other is ParameterizedType &&
other.rawType == rawType &&
other.ownerType == ownerType &&
Arrays.equals(other.actualTypeArguments, actualTypeArguments)
override fun hashCode(): Int =
Arrays.hashCode(actualTypeArguments) xor Objects.hashCode(ownerType) xor Objects.hashCode(rawType)
}
/**
* A parameterised class such as Map<String, String> for which we have resolved type parameter values.
*
@ -235,8 +218,12 @@ sealed class TypeIdentifier {
}
}
/**
* Take all type parameters to their upper bounds, recursively resolving type variables against the provided context.
*/
internal fun Type.resolveAgainst(context: Type): Type = when (this) {
is WildcardType -> this.upperBound
is ReconstitutedParameterizedType -> this
is ParameterizedType,
is TypeVariable<*> -> TypeToken.of(context).resolveType(this).type.upperBound
else -> this
@ -252,5 +239,28 @@ private val Type.upperBound: Type
this.upperBounds.isEmpty() || this.upperBounds.size > 1 -> this
else -> this.upperBounds[0]
}
// Ignore types that we have created ourselves
is ReconstitutedParameterizedType -> this
is ParameterizedType -> ReconstitutedParameterizedType(
rawType,
ownerType,
actualTypeArguments.map { it.upperBound }.toTypedArray())
else -> this
}
}
private class ReconstitutedParameterizedType(
private val _rawType: Type,
private val _ownerType: Type?,
private val _actualTypeArguments: Array<Type>) : ParameterizedType {
override fun getRawType(): Type = _rawType
override fun getOwnerType(): Type? = _ownerType
override fun getActualTypeArguments(): Array<Type> = _actualTypeArguments
override fun toString(): String = TypeIdentifier.forGenericType(this).prettyPrint(false)
override fun equals(other: Any?): Boolean =
other is ParameterizedType &&
other.rawType == rawType &&
other.ownerType == ownerType &&
Arrays.equals(other.actualTypeArguments, actualTypeArguments)
override fun hashCode(): Int =
Arrays.hashCode(actualTypeArguments) xor Objects.hashCode(ownerType) xor Objects.hashCode(rawType)
}

View File

@ -1,15 +1,28 @@
package net.corda.serialization.internal.amqp
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.serialization.ConstructorForDeserialization
import net.corda.core.serialization.SerializableCalculatedProperty
import net.corda.serialization.internal.amqp.custom.PublicKeySerializer
import net.corda.serialization.internal.amqp.testutils.deserialize
import net.corda.serialization.internal.amqp.testutils.serialize
import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
import java.math.BigInteger
import kotlin.test.assertEquals
class RoundTripTests {
@Test
fun mutableBecomesImmutable() {
data class C(val l: MutableList<String>)
@ -120,4 +133,34 @@ class RoundTripTests {
val deserialized = DeserializationInput(factory).deserialize(bytes) as I
assertThat(deserialized.squared).isEqualTo(2)
}
data class MembershipState<out T: Any>(val metadata: T): ContractState {
override val participants: List<AbstractParty>
get() = emptyList()
}
data class OnMembershipChanged(val changedMembership : StateAndRef<MembershipState<Any>>)
@Test
fun canSerializeClassesWithUntypedProperties() {
val data = MembershipState<Any>(mapOf("foo" to "bar"))
val party = Party(
CordaX500Name(organisation = "Test Corp", locality = "Madrid", country = "ES"),
entropyToKeyPair(BigInteger.valueOf(83)).public)
val transactionState = TransactionState(
data,
"foo",
party
)
val ref = StateRef(SecureHash.zeroHash, 0)
val instance = OnMembershipChanged(StateAndRef(
transactionState,
ref
))
val factory = testDefaultFactoryNoEvolution().apply { register(PublicKeySerializer) }
val bytes = SerializationOutput(factory).serialize(instance)
val deserialized = DeserializationInput(factory).deserialize(bytes)
assertEquals(mapOf("foo" to "bar"), deserialized.changedMembership.state.data.metadata)
}
}