@@ -2,36 +2,82 @@ package org.utbot.instrumentation.util
22
33import com.esotericsoftware.kryo.kryo5.Kryo
44import com.esotericsoftware.kryo.kryo5.KryoException
5+ import com.esotericsoftware.kryo.kryo5.Serializer
56import com.esotericsoftware.kryo.kryo5.io.Input
6- import com.esotericsoftware.kryo.kryo5.serializers.JavaSerializer
7- import org.utbot.framework.plugin.api.util.utContext
7+ import com.esotericsoftware.kryo.kryo5.io.Output
8+ import com.jetbrains.rd.util.getLogger
9+ import com.jetbrains.rd.util.warn
10+ import org.utbot.framework.plugin.api.ClassId
11+ import org.utbot.framework.plugin.api.util.id
12+ import org.utbot.framework.plugin.api.util.jClass
13+ import java.io.ByteArrayInputStream
14+ import java.io.ByteArrayOutputStream
815import java.io.InputStream
916import java.io.ObjectInputStream
17+ import java.io.ObjectOutputStream
1018import java.io.ObjectStreamClass
19+ import java.lang.RuntimeException
1120
12- /* *
13- * This ad-hoc solution for ClassNotFoundException
14- */
15- class JavaSerializerWrapper : JavaSerializer () {
16- override fun read (kryo : Kryo , input : Input ? , type : Class <* >? ): Any? {
17- return try {
18- val graphContext = kryo.graphContext
19- var objectStream = graphContext.get<Any >(this ) as ObjectInputStream ?
20- if (objectStream == null ) {
21- objectStream = IgnoringUidWrappingObjectInputStream (input, kryo)
22- graphContext.put(this , objectStream)
21+ class ThrowableSerializer : Serializer <Throwable >() {
22+ companion object {
23+ private val loggedUnserializableExceptionClassIds = mutableSetOf<ClassId >()
24+ private val logger = getLogger<ThrowableSerializer >()
25+ }
26+
27+ private class ThrowableModel (
28+ val classId : ClassId ,
29+ val message : String? ,
30+ val stackTrace : Array <StackTraceElement >,
31+ val cause : ThrowableModel ? ,
32+ val serializedException : ByteArray ,
33+ )
34+
35+ override fun write (kryo : Kryo , output : Output , throwable : Throwable ? ) {
36+ fun Throwable.toModel (): ThrowableModel = ThrowableModel (
37+ classId = this ::class .java.id,
38+ message = message,
39+ stackTrace = stackTrace,
40+ cause = cause?.toModel(),
41+ serializedException = ByteArrayOutputStream ().use { byteOutputStream ->
42+ val objectOutputStream = ObjectOutputStream (byteOutputStream)
43+ objectOutputStream.writeObject(this )
44+ objectOutputStream.flush()
45+ byteOutputStream.toByteArray()
46+ }
47+ )
48+ kryo.writeObject(output, throwable?.toModel())
49+ }
50+
51+ override fun read (kryo : Kryo , input : Input , type : Class <out Throwable >): Throwable ? {
52+ fun ThrowableModel.toThrowable (): Throwable = try {
53+ ByteArrayInputStream (this .serializedException).use { byteInputStream ->
54+ val objectInputStream = IgnoringUidWrappingObjectInputStream (byteInputStream, kryo.classLoader)
55+ objectInputStream.readObject() as Throwable
56+ }
57+ } catch (e: Throwable ) {
58+ if (loggedUnserializableExceptionClassIds.add(this .classId)) {
59+ logger.warn { " Failed to deserialize ${this .classId} from bytes, cause: $e " }
60+ logger.warn { " Falling back to constructing throwable instance from ThrowableModel" }
61+ }
62+
63+ val cause = cause?.toThrowable()
64+ when {
65+ RuntimeException ::class .java.isAssignableFrom(classId.jClass) -> RuntimeException (message, cause)
66+ Error ::class .java.isAssignableFrom(classId.jClass) -> Error (message, cause)
67+ else -> Exception (message, cause)
68+ }.also {
69+ it.stackTrace = stackTrace
2370 }
24- objectStream.readObject()
25- } catch (ex: java.lang.Exception ) {
26- throw KryoException (" Error during Java deserialization." , ex)
2771 }
72+
73+ return kryo.readObject(input, ThrowableModel ::class .java)?.toThrowable()
2874 }
2975}
3076
31- class IgnoringUidWrappingObjectInputStream (iss : InputStream ? , private val kryo : Kryo ) : ObjectInputStream(iss) {
77+ class IgnoringUidWrappingObjectInputStream (iss : InputStream ? , private val classLoader : ClassLoader ) : ObjectInputStream(iss) {
3278 override fun resolveClass (type : ObjectStreamClass ): Class <* >? {
3379 return try {
34- Class .forName(type.name, false , kryo. classLoader)
80+ Class .forName(type.name, false , classLoader)
3581 } catch (ex: ClassNotFoundException ) {
3682 try {
3783 return Kryo ::class .java.classLoader.loadClass(type.name)
@@ -52,7 +98,7 @@ class IgnoringUidWrappingObjectInputStream(iss : InputStream?, private val kryo:
5298
5399 // the class in the local JVM that this descriptor represents.
54100 val localClass: Class <* > = try {
55- kryo. classLoader.loadClass(resultClassDescriptor.name)
101+ classLoader.loadClass(resultClassDescriptor.name)
56102 } catch (e: ClassNotFoundException ) {
57103 return resultClassDescriptor
58104 }
0 commit comments