mirror of
https://github.com/corda/corda.git
synced 2025-06-01 15:10:54 +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.
|
* 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) {
|
internal fun Type.resolveAgainst(context: Type): Type = when (this) {
|
||||||
is WildcardType -> this.upperBound
|
is WildcardType -> this.upperBound
|
||||||
|
is ReconstitutedParameterizedType -> this
|
||||||
is ParameterizedType,
|
is ParameterizedType,
|
||||||
is TypeVariable<*> -> TypeToken.of(context).resolveType(this).type.upperBound
|
is TypeVariable<*> -> TypeToken.of(context).resolveType(this).type.upperBound
|
||||||
else -> this
|
else -> this
|
||||||
@ -252,5 +239,28 @@ private val Type.upperBound: Type
|
|||||||
this.upperBounds.isEmpty() || this.upperBounds.size > 1 -> this
|
this.upperBounds.isEmpty() || this.upperBounds.size > 1 -> this
|
||||||
else -> this.upperBounds[0]
|
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
|
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
|
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.ConstructorForDeserialization
|
||||||
import net.corda.core.serialization.SerializableCalculatedProperty
|
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.deserialize
|
||||||
import net.corda.serialization.internal.amqp.testutils.serialize
|
import net.corda.serialization.internal.amqp.testutils.serialize
|
||||||
import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution
|
import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.math.BigInteger
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class RoundTripTests {
|
class RoundTripTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun mutableBecomesImmutable() {
|
fun mutableBecomesImmutable() {
|
||||||
data class C(val l: MutableList<String>)
|
data class C(val l: MutableList<String>)
|
||||||
@ -120,4 +133,34 @@ class RoundTripTests {
|
|||||||
val deserialized = DeserializationInput(factory).deserialize(bytes) as I
|
val deserialized = DeserializationInput(factory).deserialize(bytes) as I
|
||||||
assertThat(deserialized.squared).isEqualTo(2)
|
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…
x
Reference in New Issue
Block a user