Skip to content

Commit 0981a80

Browse files
Consider anonymous classes; still need a bit of to do
1 parent 2b43c34 commit 0981a80

File tree

3 files changed

+142
-40
lines changed

3 files changed

+142
-40
lines changed

jupyter-lib/shared-compiler/src/main/kotlin/org/jetbrains/kotlinx/jupyter/compiler/util/serializedCompiledScript.kt

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ data class SerializedVariablesState(
3030
val fieldDescriptor: MutableMap<String, SerializedVariablesState?> = mutableMapOf()
3131
}
3232

33-
3433
@Serializable
3534
class EvaluatedSnippetMetadata(
3635
val newClasspath: Classpath = emptyList(),

src/main/kotlin/org/jetbrains/kotlinx/jupyter/serializationUtils.kt

+112-34
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package org.jetbrains.kotlinx.jupyter
22

33
import org.jetbrains.kotlinx.jupyter.api.VariableState
44
import org.jetbrains.kotlinx.jupyter.compiler.util.SerializedVariablesState
5+
import java.lang.reflect.Field
56
import kotlin.reflect.KClass
67
import kotlin.reflect.KProperty
78
import kotlin.reflect.KProperty1
9+
import kotlin.reflect.KTypeParameter
810
import kotlin.reflect.full.declaredMemberProperties
911
import kotlin.reflect.full.isSubclassOf
1012
import kotlin.reflect.jvm.isAccessible
@@ -15,8 +17,33 @@ typealias PropertiesData = Collection<KProperty1<out Any, *>>
1517

1618
data class ProcessedSerializedVarsState(
1719
val serializedVariablesState: SerializedVariablesState,
18-
val propertiesData: PropertiesData?
19-
)
20+
val propertiesData: PropertiesData?,
21+
val jvmOnlyFields: Array<Field>? = null
22+
) {
23+
// autogenerated
24+
override fun equals(other: Any?): Boolean {
25+
if (this === other) return true
26+
if (javaClass != other?.javaClass) return false
27+
28+
other as ProcessedSerializedVarsState
29+
30+
if (serializedVariablesState != other.serializedVariablesState) return false
31+
if (propertiesData != other.propertiesData) return false
32+
if (jvmOnlyFields != null) {
33+
if (other.jvmOnlyFields == null) return false
34+
if (!jvmOnlyFields.contentEquals(other.jvmOnlyFields)) return false
35+
} else if (other.jvmOnlyFields != null) return false
36+
37+
return true
38+
}
39+
40+
override fun hashCode(): Int {
41+
var result = serializedVariablesState.hashCode()
42+
result = 31 * result + (propertiesData?.hashCode() ?: 0)
43+
result = 31 * result + (jvmOnlyFields?.contentHashCode() ?: 0)
44+
return result
45+
}
46+
}
2047

2148
data class ProcessedDescriptorsState(
2249
// perhaps, better tp make SerializedVariablesState -> PropertiesData?
@@ -41,7 +68,6 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
4168
*/
4269
private val computedDescriptorsPerCell: MutableMap<Int, ProcessedDescriptorsState> = mutableMapOf()
4370

44-
4571
fun serializeVariables(cellId: Int, variablesState: Map<String, VariableState>): Map<String, SerializedVariablesState> {
4672
if (seenObjectsPerCell.containsKey(cellId)) {
4773
seenObjectsPerCell[cellId]!!.clear()
@@ -62,12 +88,17 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
6288
* @param evaluatedDescriptorsState - origin variable state to get value from
6389
* @param serializedVariablesState - current state of recursive state to go further
6490
*/
65-
private fun updateVariableState(cellId: Int, propertyName: String, evaluatedDescriptorsState: ProcessedDescriptorsState,
66-
serializedVariablesState: SerializedVariablesState): SerializedVariablesState {
91+
private fun updateVariableState(
92+
cellId: Int,
93+
propertyName: String,
94+
evaluatedDescriptorsState: ProcessedDescriptorsState,
95+
serializedVariablesState: SerializedVariablesState
96+
): SerializedVariablesState {
97+
// TODO: consider anonymous class as well and fix bug with state
6798
val value = evaluatedDescriptorsState.instancesPerState[serializedVariablesState]
6899
val propertiesData = evaluatedDescriptorsState.processedSerializedVarsState[serializedVariablesState]
69-
if (propertiesData == null && value != null && value::class.java.isArray) {
70-
return serializeVariableState(cellId, propertyName, null, value, false)
100+
if (propertiesData == null && value != null && (value::class.java.isArray || value::class.java.isMemberClass)) {
101+
return serializeVariableState(cellId, propertyName, propertiesData, value, false)
71102
}
72103
val property = propertiesData?.firstOrNull {
73104
it.name == propertyName
@@ -76,14 +107,22 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
76107
return serializeVariableState(cellId, propertyName, property, value, false)
77108
}
78109

79-
80110
fun serializeVariableState(cellId: Int, name: String?, variableState: VariableState?, isOverride: Boolean = true): SerializedVariablesState {
81111
if (variableState == null || name == null) return SerializedVariablesState()
82112
return serializeVariableState(cellId, name, variableState.property, variableState.value, isOverride)
83113
}
84114

85-
fun serializeVariableState(cellId: Int, name: String, property: KProperty<*>?, value: Any?, isOverride: Boolean = true): SerializedVariablesState {
86-
val processedData = createSerializeVariableState(name, property, value)
115+
fun serializeVariableState(cellId: Int, name: String, property: Field?, value: Any?, isOverride: Boolean = true): SerializedVariablesState {
116+
val processedData = createSerializeVariableState(name, getSimpleTypeNameFrom(property, value), value)
117+
return doActualSerialization(cellId, processedData, value, isOverride)
118+
}
119+
120+
fun serializeVariableState(cellId: Int, name: String, property: KProperty<*>, value: Any?, isOverride: Boolean = true): SerializedVariablesState {
121+
val processedData = createSerializeVariableState(name, getSimpleTypeNameFrom(property, value), value)
122+
return doActualSerialization(cellId, processedData, value, isOverride)
123+
}
124+
125+
private fun doActualSerialization(cellId: Int, processedData: ProcessedSerializedVarsState, value: Any?, isOverride: Boolean = true): SerializedVariablesState {
87126
val serializedVersion = processedData.serializedVariablesState
88127

89128
seenObjectsPerCell.putIfAbsent(cellId, mutableMapOf())
@@ -103,8 +142,7 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
103142
return processedData.serializedVariablesState
104143
}
105144

106-
107-
private fun iterateThroughContainerMembers(cellId: Int, callInstance: Any?, descriptor: MutableFieldDescriptor, properties: PropertiesData?, currentDepth: Int = 0): Unit {
145+
private fun iterateThroughContainerMembers(cellId: Int, callInstance: Any?, descriptor: MutableFieldDescriptor, properties: PropertiesData?, currentDepth: Int = 0) {
108146
if (properties == null || callInstance == null || currentDepth >= serializationStep) return
109147

110148
val serializedIteration = mutableMapOf<String, ProcessedSerializedVarsState>()
@@ -123,7 +161,7 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
123161
val value = tryGetValueFromProperty(it, callInstance)
124162

125163
if (!seenObjectsPerCell!!.containsKey(value)) {
126-
serializedIteration[name] = createSerializeVariableState(name, it, value)
164+
serializedIteration[name] = createSerializeVariableState(name, getSimpleTypeNameFrom(it, value), value)
127165
descriptor[name] = serializedIteration[name]!!.serializedVariablesState
128166
}
129167
if (descriptor[name] != null) {
@@ -155,61 +193,104 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
155193
isArrayType -> {
156194
callInstance
157195
}
158-
else -> { null }
196+
else -> {
197+
null
198+
}
159199
}
160200
if (isArrayType) {
161201
if (callInstance is List<*>) {
162202
callInstance.forEach { arrayElem ->
163-
iterateThroughContainerMembers(cellId, arrayElem, serializedVariablesState.fieldDescriptor,
164-
it.value.propertiesData, currentDepth + 1)
203+
iterateThroughContainerMembers(
204+
cellId,
205+
arrayElem,
206+
serializedVariablesState.fieldDescriptor,
207+
it.value.propertiesData,
208+
currentDepth + 1
209+
)
165210
}
166211
} else {
167212
callInstance as Array<*>
168213
callInstance.forEach { arrayElem ->
169-
iterateThroughContainerMembers(cellId, arrayElem, serializedVariablesState.fieldDescriptor,
170-
it.value.propertiesData, currentDepth + 1)
214+
iterateThroughContainerMembers(
215+
cellId,
216+
arrayElem,
217+
serializedVariablesState.fieldDescriptor,
218+
it.value.propertiesData,
219+
currentDepth + 1
220+
)
171221
}
172222
}
173223

174224
return@forEach
175225
}
176-
iterateThroughContainerMembers(cellId, neededCallInstance, serializedVariablesState.fieldDescriptor,
177-
it.value.propertiesData, currentDepth + 1)
226+
227+
// update state with JVMFields
228+
it.value.jvmOnlyFields?.forEach { field ->
229+
serializedVariablesState.fieldDescriptor[field.name] = serializeVariableState(cellId, field.name, field, neededCallInstance)
230+
instancesPerState[serializedVariablesState] = neededCallInstance
231+
}
232+
iterateThroughContainerMembers(
233+
cellId,
234+
neededCallInstance,
235+
serializedVariablesState.fieldDescriptor,
236+
it.value.propertiesData,
237+
currentDepth + 1
238+
)
178239
}
179240
}
180241
}
181242

243+
private fun getSimpleTypeNameFrom(property: Field?, value: Any?): String? {
244+
return if (property != null) {
245+
val returnType = property.type
246+
returnType.simpleName
247+
} else {
248+
value?.toString()
249+
}
250+
}
182251

183-
private fun createSerializeVariableState(name: String, property: KProperty<*>?, value: Any?): ProcessedSerializedVarsState {
184-
val simpleName = if (property != null) {
252+
private fun getSimpleTypeNameFrom(property: KProperty<*>?, value: Any?): String? {
253+
return if (property != null) {
185254
val returnType = property.returnType
186-
val classifier = returnType.classifier as KClass<*>
187-
classifier.simpleName
255+
val classifier = returnType.classifier
256+
if (classifier is KTypeParameter) {
257+
classifier.name
258+
} else {
259+
(classifier as KClass<*>).simpleName
260+
}
188261
} else {
189262
value?.toString()
190263
}
264+
}
191265

266+
private fun createSerializeVariableState(name: String, simpleTypeName: String?, value: Any?): ProcessedSerializedVarsState {
192267
// make it exception-safe
193268
val membersProperties = try {
194269
if (value != null) value::class.declaredMemberProperties else null
195270
} catch (e: Throwable) {
196271
null
197272
}
198-
val isContainer = if (membersProperties != null) (membersProperties.size > 1 || value!!::class.java.isArray) else false
199-
val type = if (value!= null && value::class.java.isArray) {
273+
val javaClass = value?.javaClass
274+
val jvmFields = if (javaClass != null && javaClass.isMemberClass) {
275+
javaClass.declaredFields
276+
} else { null }
277+
278+
val isContainer = if (membersProperties != null) (
279+
membersProperties.isNotEmpty() || value!!::class.java.isArray || (javaClass != null && javaClass.isMemberClass)
280+
) else false
281+
val type = if (value != null && value::class.java.isArray) {
200282
"Array"
201283
} else if (isContainer && value is List<*>) {
202284
"SingletonList"
203285
} else {
204-
simpleName.toString()
286+
simpleTypeName.toString()
205287
}
206288

207289
val serializedVariablesState = SerializedVariablesState(name, type, getProperString(value), isContainer)
208290

209-
return ProcessedSerializedVarsState(serializedVariablesState, membersProperties)
291+
return ProcessedSerializedVarsState(serializedVariablesState, membersProperties, jvmFields)
210292
}
211293

212-
213294
private fun tryGetValueFromProperty(property: KProperty1<Any, *>, callInstance: Any): Any? {
214295
// some fields may be optimized out like array size. Thus, calling it.isAccessible would return error
215296
val canAccess = try {
@@ -243,13 +324,10 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
243324
false
244325
}
245326
}
246-
247327
}
248328

249-
// TODO: maybe think of considering the depth?
250329
fun getProperString(value: Any?): String {
251-
252-
fun print(builder: StringBuilder, containerSize:Int, index: Int, value: Any?): Unit {
330+
fun print(builder: StringBuilder, containerSize: Int, index: Int, value: Any?) {
253331
if (index != containerSize - 1) {
254332
builder.append(value, ", ")
255333
} else {
@@ -296,4 +374,4 @@ fun getProperString(value: Any?): String {
296374
}
297375

298376
return value.toString()
299-
}
377+
}

src/test/kotlin/org/jetbrains/kotlinx/jupyter/test/repl/ReplTests.kt

+30-5
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,6 @@ class ReplVarsTest : AbstractSingleReplTest() {
776776
}
777777
}
778778

779-
780779
class ReplVarsSerializationTest : AbstractSingleReplTest() {
781780
override val repl = makeSimpleRepl()
782781

@@ -805,7 +804,7 @@ class ReplVarsSerializationTest : AbstractSingleReplTest() {
805804
val actualContainer = listDescriptors.entries.first().value!!
806805
assertEquals(2, actualContainer.fieldDescriptor.size)
807806
assertTrue(actualContainer.isContainer)
808-
assertEquals(listOf(1,2,3,4).toString().substring(1, actualContainer.value!!.length + 1), actualContainer.value)
807+
assertEquals(listOf(1, 2, 3, 4).toString().substring(1, actualContainer.value!!.length + 1), actualContainer.value)
809808
}
810809

811810
@Test
@@ -842,6 +841,34 @@ class ReplVarsSerializationTest : AbstractSingleReplTest() {
842841
}
843842
}
844843

844+
@Test
845+
fun cyclicReferenceTest() {
846+
val res = eval(
847+
"""
848+
class C {
849+
inner class Inner;
850+
val i = Inner()
851+
val counter = 0
852+
}
853+
val c = C()
854+
""".trimIndent(),
855+
jupyterId = 1
856+
)
857+
val varsData = res.metadata.evaluatedVariablesState
858+
assertEquals(1, varsData.size)
859+
assertTrue(varsData.containsKey("c"))
860+
861+
val serializedState = varsData["c"]!!
862+
assertTrue(serializedState.isContainer)
863+
val descriptor = serializedState.fieldDescriptor
864+
assertEquals(2, descriptor.size)
865+
assertEquals("0", descriptor["counter"]!!.value)
866+
867+
val serializer = repl.variablesSerializer
868+
869+
val newData = serializer.doIncrementalSerialization(0, descriptor["i"]!!.name, descriptor["i"]!!)
870+
val a = 1
871+
}
845872

846873
@Test
847874
fun incrementalUpdateTest() {
@@ -882,7 +909,5 @@ class ReplVarsSerializationTest : AbstractSingleReplTest() {
882909
assertTrue(state.isContainer)
883910
assertEquals("${values++}", state.value)
884911
}
885-
886912
}
887-
888-
}
913+
}

0 commit comments

Comments
 (0)