Use Java reflection for determining if a type is a Kotlin singleton. (#2446)

Kotlin's own reflection has a habit of throwing weird errors and does not work for private objects - thus the unit test which tests this feature has actually never worked properly, but somehow works by accident. An attempt to upgrade to the latest Kotlin revealed the issue so it must have always been unstable.
This commit is contained in:
Mike Hearn 2018-02-02 17:17:23 +01:00 committed by GitHub
parent 82ece34ac8
commit 38ccd0572c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -6,14 +6,15 @@ import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.serializers.FieldSerializer
import com.esotericsoftware.kryo.util.DefaultClassResolver
import com.esotericsoftware.kryo.util.Util
import net.corda.nodeapi.internal.AttachmentsClassLoader
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationContext
import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.AttachmentsClassLoader
import net.corda.nodeapi.internal.serialization.amqp.hasAnnotationInHierarchy
import net.corda.nodeapi.internal.serialization.kryo.ThrowableSerializer
import java.io.PrintWriter
import java.lang.reflect.Modifier
import java.lang.reflect.Modifier.isAbstract
import java.nio.charset.StandardCharsets
import java.nio.file.Files
@ -74,10 +75,21 @@ class CordaClassResolver(serializationContext: SerializationContext) : DefaultCl
override fun registerImplicit(type: Class<*>): Registration {
val targetType = typeForSerializationOf(type)
// Is this a Kotlin object? We use our own reflection here rather than .kotlin.objectInstance because Kotlin
// reflection won't work for private objects, and can throw exceptions in other circumstances as well.
val objectInstance = try {
targetType.kotlin.objectInstance
targetType.declaredFields.singleOrNull {
it.name == "INSTANCE" &&
it.type == type &&
Modifier.isStatic(it.modifiers) &&
Modifier.isFinal(it.modifiers) &&
Modifier.isPublic(it.modifiers)
}?.let {
it.isAccessible = true
type.cast(it.get(null)!!)
}
} catch (t: Throwable) {
null // objectInstance will throw if the type is something like a lambda
null
}
// We have to set reference to true, since the flag influences how String fields are treated and we want it to be consistent.