mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
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:
parent
8610f22cd9
commit
858301abae
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user