Skip to content

Commit fe3c4b1

Browse files
committed
[instrumentation-serialization]
attempt #1, stucked on cyclic references and random primitive serialization bugs
1 parent 53b1558 commit fe3c4b1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1318
-552
lines changed

build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ buildscript {
1616
dependencies {
1717
classpath group: 'org.jetbrains.kotlin', name: 'kotlin-gradle-plugin', version: kotlin_version
1818
classpath group: 'org.jetbrains.kotlin', name: 'kotlin-allopen', version: kotlin_version
19+
classpath group: 'org.jetbrains.kotlin', name: 'kotlin-serialization', version: kotlin_version
1920
}
2021
}
2122

gradle.properties

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ javacpp_version=1.5.3
4242
jsoup_version=1.7.2
4343
djl_api_version=0.17.0
4444
pytorch_native_version=1.9.1
45-
# soot also depends on asm, so there could be two different versions
45+
# soot also depends on asm, so there could be two different versions
46+
org.gradle.jvmargs=-Xmx4096m

gradle/include/jvm-project.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
apply plugin: 'java'
22
apply plugin: 'kotlin'
3+
apply plugin: 'kotlinx-serialization'
34

45
dependencies {
56
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: coroutines_version
67
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-collections-immutable-jvm', version: collections_version
78
implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlin_version
89
implementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version
910

11+
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-serialization-json', version: '1.0.1'
12+
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-serialization-core', version: '1.0.1'
13+
1014
testImplementation("org.junit.jupiter:junit-jupiter:$junit5_version"){
1115
force = true
1216
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.utbot.common
2+
3+
import kotlinx.serialization.KSerializer
4+
import kotlinx.serialization.descriptors.SerialDescriptor
5+
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
6+
import kotlinx.serialization.descriptors.element
7+
import kotlinx.serialization.encoding.*
8+
9+
object IntRangeSerializer: KSerializer<IntRange> {
10+
override val descriptor: SerialDescriptor
11+
get() = buildClassSerialDescriptor("asd") {
12+
element<Int>("first")
13+
element<Int>("last")
14+
}
15+
16+
override fun deserialize(decoder: Decoder): IntRange {
17+
return decoder.decodeStructure(descriptor) {
18+
var first = -1
19+
var last = -1
20+
21+
while (true) {
22+
when(val index = decodeElementIndex(descriptor)) {
23+
0 -> first = decodeIntElement(descriptor, 0)
24+
1 -> last = decodeIntElement(descriptor, 1)
25+
CompositeDecoder.DECODE_DONE -> break
26+
else -> error("Unexpected index: $index")
27+
}
28+
}
29+
30+
IntRange(first, last)
31+
}
32+
}
33+
34+
override fun serialize(encoder: Encoder, value: IntRange) {
35+
encoder.encodeStructure(descriptor) {
36+
encodeIntElement(descriptor, 0, value.first)
37+
encodeIntElement(descriptor, 1, value.last)
38+
}
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
package org.utbot.framework
2+
3+
import kotlinx.serialization.ExperimentalSerializationApi
4+
import kotlinx.serialization.InternalSerializationApi
5+
import kotlinx.serialization.KSerializer
6+
import kotlinx.serialization.descriptors.*
7+
import kotlinx.serialization.encoding.*
8+
import kotlinx.serialization.serializer
9+
import org.utbot.common.IntRangeSerializer
10+
import org.utbot.framework.plugin.api.*
11+
import org.utbot.framework.plugin.api.util.*
12+
13+
private val classIdSerializer = serializer<ClassId>()
14+
15+
object UtContextThrowableSerializer : KSerializer<Throwable> {
16+
override val descriptor: SerialDescriptor
17+
get() = TODO("Not yet implemented")
18+
19+
override fun deserialize(decoder: Decoder): Throwable {
20+
TODO("Not yet implemented")
21+
}
22+
23+
override fun serialize(encoder: Encoder, value: Throwable) {
24+
TODO("Not yet implemented")
25+
}
26+
27+
}
28+
29+
object UtContextClassSerializer : KSerializer<Class<*>> {
30+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("utClass", PrimitiveKind.STRING)
31+
32+
override fun deserialize(decoder: Decoder): Class<*> {
33+
val name = decoder.decodeString()
34+
35+
return utContext.classLoader.loadClass(name)
36+
}
37+
38+
override fun serialize(encoder: Encoder, value: Class<*>) {
39+
encoder.encodeString(value.name)
40+
}
41+
}
42+
43+
object UtContextNullableClassSerializer : KSerializer<Class<*>?> {
44+
override fun deserialize(decoder: Decoder): Class<*>? {
45+
val decoded = decoder.decodeString()
46+
47+
return if (decoded == "1") null else utContext.classLoader.loadClass(decoded)
48+
}
49+
50+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("utClassNullable", PrimitiveKind.STRING)
51+
52+
override fun serialize(encoder: Encoder, value: Class<*>?) {
53+
encoder.encodeString(value?.name ?: "1") // 1 because classes cannot be named with numbers
54+
}
55+
}
56+
57+
private val surrogateBuiltinClassIdSerializer = serializer<SurrogateBuiltinClassId>()
58+
59+
object BuiltinClassIdSerializer : KSerializer<BuiltinClassId> {
60+
@InternalSerializationApi
61+
@ExperimentalSerializationApi
62+
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("BuiltinClassId") {
63+
element<SurrogateBuiltinClassId>("surrogate")
64+
element("outerClass", UtContextNullableClassSerializer.descriptor)
65+
}
66+
67+
@InternalSerializationApi
68+
@ExperimentalSerializationApi
69+
override fun deserialize(decoder: Decoder): BuiltinClassId {
70+
return decoder.decodeStructure(descriptor) {
71+
var surrogate: SurrogateBuiltinClassId? = null
72+
var outerClass: Class<*>? = null
73+
74+
while (true) {
75+
val index = decodeElementIndex(descriptor)
76+
77+
when (index) {
78+
0 -> surrogate = decodeSerializableElement(descriptor, 0, surrogateBuiltinClassIdSerializer)
79+
1 -> outerClass = decodeSerializableElement(descriptor, 1, UtContextNullableClassSerializer)
80+
CompositeDecoder.DECODE_DONE -> break
81+
else -> error("unknown element $index")
82+
}
83+
}
84+
85+
return BuiltinClassId(
86+
surrogate!!.name,
87+
surrogate.canonicalName,
88+
surrogate.simpleName,
89+
surrogate.simpleNameWithEnclosings,
90+
surrogate.isPublic,
91+
surrogate.isProtected,
92+
surrogate.isPrivate,
93+
surrogate.isFinal,
94+
surrogate.isStatic,
95+
surrogate.isAbstract,
96+
surrogate.isAnonymous,
97+
surrogate.isLocal,
98+
surrogate.isInner,
99+
surrogate.isNested,
100+
surrogate.isSynthetic,
101+
surrogate.allMethods,
102+
surrogate.allConstructors,
103+
surrogate.outerClass
104+
)
105+
}
106+
}
107+
108+
@InternalSerializationApi
109+
@ExperimentalSerializationApi
110+
override fun serialize(encoder: Encoder, value: BuiltinClassId) {
111+
encoder.encodeStructure(descriptor) {
112+
encodeSerializableElement(
113+
descriptor,
114+
0,
115+
surrogateBuiltinClassIdSerializer,
116+
value as SurrogateBuiltinClassId
117+
)
118+
encodeSerializableElement(descriptor, 1, UtContextNullableClassSerializer, value.outerClass)
119+
}
120+
}
121+
}
122+
123+
private fun <T : Enum<T>> safeValueOf(enumType: Class<T>, type: String): T {
124+
return java.lang.Enum.valueOf(enumType, type)
125+
}
126+
127+
object UtEnumConstantModelSerializer : KSerializer<UtEnumConstantModel> {
128+
@InternalSerializationApi
129+
@ExperimentalSerializationApi
130+
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UtEnumConstantModel") {
131+
element("classId", classIdSerializer.descriptor)
132+
element<String>("enum")
133+
}
134+
135+
@InternalSerializationApi
136+
@ExperimentalSerializationApi
137+
override fun deserialize(decoder: Decoder): UtEnumConstantModel {
138+
return decoder.decodeStructure(descriptor) {
139+
var classId: ClassId? = null
140+
var enum: String? = null
141+
142+
while (true) {
143+
when (val index = decodeElementIndex(IntRangeSerializer.descriptor)) {
144+
0 -> classId = decodeSerializableElement(descriptor, 0, classIdSerializer)
145+
1 -> enum = decodeStringElement(descriptor, 1)
146+
CompositeDecoder.DECODE_DONE -> break
147+
else -> error("Unexpected token $index")
148+
}
149+
}
150+
151+
152+
@Suppress("UNCHECKED_CAST")
153+
// suppose that UtEnumConstantModel.classId is underlying enum class
154+
UtEnumConstantModel(classId!!, safeValueOf(classId.jClass as Class<out Enum<*>>, enum!!))
155+
}
156+
}
157+
158+
@InternalSerializationApi
159+
@ExperimentalSerializationApi
160+
override fun serialize(encoder: Encoder, value: UtEnumConstantModel) {
161+
encoder.encodeStructure(descriptor) {
162+
encodeSerializableElement(descriptor, 0, classIdSerializer, value.classId)
163+
encodeStringElement(descriptor, 1, value.value.name)
164+
}
165+
}
166+
167+
}
168+
169+
private fun String.parseToPrimitiveTypeJvmName(primitiveType: Char): Any {
170+
return when (primitiveType) {
171+
'V' -> Unit
172+
'Z' -> toBoolean()
173+
'B' -> toByte()
174+
'C' -> single()
175+
'S' -> toShort()
176+
'I' -> toInt()
177+
'J' -> toLong()
178+
'F' -> toFloat()
179+
'D' -> toDouble()
180+
else -> error("Primitive expected here, but got: $primitiveType")
181+
}
182+
}
183+
184+
object UtPrimitiveModelSerializer : KSerializer<UtPrimitiveModel> {
185+
// todo kononov: possible speed up - char+string instead to remove string concatenation, it is just premilinary version
186+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UtPrimitiveModel", PrimitiveKind.STRING)
187+
188+
override fun deserialize(decoder: Decoder): UtPrimitiveModel {
189+
val decodedString = decoder.decodeString()
190+
191+
return UtPrimitiveModel(decodedString.substring(1).parseToPrimitiveTypeJvmName(decodedString.first()))
192+
}
193+
194+
override fun serialize(encoder: Encoder, value: UtPrimitiveModel) {
195+
// because name `value` comes from KSerializer interface,
196+
// so that name collision is kept to confront calling this function with named parameters
197+
@Suppress("UnnecessaryVariable")
198+
val primitiveModel = value
199+
200+
if (!primitiveModel.classId.isPrimitive || primitiveModelValueToClassId(primitiveModel.value) != primitiveModel.classId) {
201+
throw IllegalStateException("PrimitiveModel invariant corrupted: has ${primitiveModel.classId} for ${primitiveModel.value}")
202+
}
203+
204+
val jvmTypeName = primitiveModel.classId.primitiveTypeJvmNameOrNull()!!
205+
val valueString = primitiveModel.value.toString()
206+
207+
encoder.encodeString(jvmTypeName + valueString)
208+
}
209+
}
210+
211+
object UtClassRefModelSerializer : KSerializer<UtClassRefModel> {
212+
@InternalSerializationApi
213+
@ExperimentalSerializationApi
214+
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UtClassRefModel") {
215+
element("classId", classIdSerializer.descriptor)
216+
}
217+
218+
override fun deserialize(decoder: Decoder): UtClassRefModel {
219+
val classId = decoder.decodeSerializableValue(classIdSerializer)
220+
221+
// if serialization succeeded - expecting deserialization be succeeded
222+
return UtClassRefModel(classId, classId.jClass)
223+
}
224+
225+
override fun serialize(encoder: Encoder, value: UtClassRefModel) {
226+
// because name `value` comes from KSerializer interface,
227+
// so that name collision is kept to confront calling this function with named parameters
228+
@Suppress("UnnecessaryVariable")
229+
val classRefModel = value
230+
val underlyingClassId = classRefModel.value.id
231+
232+
if (underlyingClassId != classRefModel.classId) {
233+
throw IllegalStateException("classRefModel invariant corrupted: has ${classRefModel.classId} for $underlyingClassId")
234+
}
235+
236+
encoder.encodeSerializableValue(classIdSerializer, classRefModel.classId)
237+
}
238+
}

0 commit comments

Comments
 (0)