1
+ package org.jetbrains.kotlinx.jupyter
2
+
3
+ import org.jetbrains.kotlinx.jupyter.api.VariableState
4
+ import org.jetbrains.kotlinx.jupyter.compiler.util.SerializedVariablesState
5
+ import kotlin.reflect.KClass
6
+ import kotlin.reflect.KProperty
7
+ import kotlin.reflect.KProperty1
8
+ import kotlin.reflect.full.declaredMemberProperties
9
+ import kotlin.reflect.full.isSubclassOf
10
+ import kotlin.reflect.jvm.isAccessible
11
+ import kotlin.reflect.jvm.jvmErasure
12
+
13
+ typealias FieldDescriptor = Map <String , SerializedVariablesState ?>
14
+ typealias MutableFieldDescriptor = MutableMap <String , SerializedVariablesState ?>
15
+ typealias PropertiesData = Collection <KProperty1 <out Any , * >>
16
+
17
+ data class ProcessedSerializedVarsState (
18
+ val serializedVariablesState : SerializedVariablesState ,
19
+ val propertiesData : PropertiesData ?
20
+ )
21
+
22
+ data class ProcessedDescriptorsState (
23
+ // perhaps, better tp make SerializedVariablesState -> PropertiesData?
24
+ val processedSerializedVarsState : MutableMap <SerializedVariablesState , PropertiesData ?> = mutableMapOf(),
25
+ val instancesPerState : MutableMap <SerializedVariablesState , Any ?> = mutableMapOf()
26
+ )
27
+
28
+ class VariablesSerializer (private val serializationStep : Int = 2 , private val serializationLimit : Int = 10000 ) {
29
+
30
+ private val seenObjectsPerCell: MutableMap <Int , MutableMap <Any , SerializedVariablesState >> = mutableMapOf ()
31
+
32
+ var currentSerializeCount: Int = 0
33
+
34
+ /* *
35
+ * Stores info computed descriptors in a cell
36
+ */
37
+ private val computedDescriptorsPerCell: MutableMap <Int , ProcessedDescriptorsState > = mutableMapOf ()
38
+
39
+
40
+ fun serializeVariables (cellId : Int , variablesState : Map <String , VariableState >): Map <String , SerializedVariablesState > {
41
+ return variablesState.mapValues { serializeVariableState(cellId, it.key, it.value) }
42
+ }
43
+
44
+ fun doIncrementalSerialization (cellId : Int , propertyName : String , serializedVariablesState : SerializedVariablesState ): SerializedVariablesState {
45
+ val cellDescriptors = computedDescriptorsPerCell[cellId] ? : return serializedVariablesState
46
+ return updateVariableState(propertyName, cellDescriptors, serializedVariablesState)
47
+ }
48
+
49
+ /* *
50
+ * @param evaluatedDescriptorsState - origin variable state to get value from
51
+ * @param serializedVariablesState - current state of recursive state to go further
52
+ */
53
+ private fun updateVariableState (propertyName : String , evaluatedDescriptorsState : ProcessedDescriptorsState ,
54
+ serializedVariablesState : SerializedVariablesState ) : SerializedVariablesState {
55
+ val value = evaluatedDescriptorsState.instancesPerState[serializedVariablesState]
56
+ val propertiesData = evaluatedDescriptorsState.processedSerializedVarsState[serializedVariablesState] ? : return serializedVariablesState
57
+ val property = propertiesData.firstOrNull {
58
+ it.name == propertyName
59
+ } ? : return serializedVariablesState
60
+
61
+ return serializeVariableState(propertyName, property, value)
62
+ }
63
+
64
+
65
+ fun serializeVariableState (cellId : Int , name : String , variableState : VariableState ): SerializedVariablesState {
66
+ return serializeVariableState(cellId, name, variableState.property, variableState.value)
67
+ }
68
+
69
+ fun serializeVariableState (cellId : Int , name : String , property : KProperty <* >, value : Any? ): SerializedVariablesState {
70
+ val processedData = createSerializeVariableState(name, property, value)
71
+ val serializedVersion = processedData.serializedVariablesState
72
+
73
+ if (seenObjectsPerCell.containsKey(cellId)) {
74
+ seenObjectsPerCell[cellId]!! .clear()
75
+ }
76
+ seenObjectsPerCell.putIfAbsent(cellId, mutableMapOf ())
77
+ // always override?
78
+ computedDescriptorsPerCell[cellId] = ProcessedDescriptorsState ()
79
+ val currentCellDescriptors = computedDescriptorsPerCell[cellId]
80
+ currentCellDescriptors!! .processedSerializedVarsState[serializedVersion] = processedData.propertiesData
81
+
82
+ if (value != null ) {
83
+ seenObjectsPerCell[cellId]!! [value] = serializedVersion
84
+ }
85
+ if (serializedVersion.isContainer) {
86
+ iterateThroughContainerMembers(cellId, value, serializedVersion.fieldDescriptor, processedData.propertiesData)
87
+ }
88
+ return processedData.serializedVariablesState
89
+ }
90
+
91
+
92
+ private fun iterateThroughContainerMembers (cellId : Int , callInstance : Any? , descriptor : MutableFieldDescriptor , properties : PropertiesData ? , currentDepth : Int = 0): Unit {
93
+ if (properties == null || callInstance == null || currentDepth > serializationStep) return
94
+
95
+ val serializedIteration = mutableMapOf<String , ProcessedSerializedVarsState >()
96
+ val callInstances = mutableMapOf<String , Any ?>()
97
+
98
+ seenObjectsPerCell.putIfAbsent(cellId, mutableMapOf ())
99
+ val seenObjectsPerCell = seenObjectsPerCell[cellId]
100
+ val currentCellDescriptors = computedDescriptorsPerCell[cellId]!!
101
+ val instancesPerState = currentCellDescriptors.instancesPerState
102
+
103
+ for (it in properties) {
104
+ if (currentSerializeCount > serializationLimit) {
105
+ break
106
+ }
107
+ it as KProperty1 <Any , * >
108
+ val name = it.name
109
+ val wasAccessible = it.isAccessible
110
+ it.isAccessible = true
111
+ val value = it.get(callInstance)
112
+
113
+ if (! seenObjectsPerCell!! .containsKey(value)) {
114
+ serializedIteration[name] = createSerializeVariableState(name, it, value)
115
+ descriptor[name] = serializedIteration[name]!! .serializedVariablesState
116
+ }
117
+ instancesPerState[descriptor[name]!! ] = value
118
+
119
+ if (value != null && ! seenObjectsPerCell.containsKey(value)) {
120
+ if (descriptor[name] != null ) {
121
+ seenObjectsPerCell[value] = descriptor[name]!!
122
+ }
123
+ }
124
+ it.isAccessible = wasAccessible
125
+ currentSerializeCount++
126
+ }
127
+
128
+ serializedIteration.forEach {
129
+ val serializedVariablesState = it.value.serializedVariablesState
130
+ val name = it.key
131
+ if (serializedVariablesState.isContainer) {
132
+ iterateThroughContainerMembers(cellId, callInstances[name], serializedVariablesState.fieldDescriptor,
133
+ it.value.propertiesData, currentDepth + 1 )
134
+ }
135
+ }
136
+ }
137
+
138
+
139
+ }
140
+ // TODO: place code bellow to the VariablesSerializer once it's good
141
+ /* *
142
+ * Map of seen objects.
143
+ * Key: hash code of actual value
144
+ * Value: this value
145
+ */
146
+ val seenObjects: MutableMap <Any , SerializedVariablesState > = mutableMapOf ()
147
+ var currentSerializeCount: Int = 0
148
+ val computedDescriptorsPerCell: Map <Int , ProcessedDescriptorsState > = mutableMapOf ()
149
+
150
+
151
+ fun serializeVariableState (name : String , property : KProperty <* >, value : Any? ): SerializedVariablesState {
152
+ val processedData = createSerializeVariableState(name, property, value)
153
+ val serializedVersion = processedData.serializedVariablesState
154
+ if (value != null ) {
155
+ seenObjects[value] = serializedVersion
156
+ }
157
+ if (serializedVersion.isContainer) {
158
+ iterateThroughContainerMembers(value, serializedVersion.fieldDescriptor, processedData.propertiesData)
159
+ }
160
+ return processedData.serializedVariablesState
161
+ }
162
+
163
+ fun serializeVariableState (name : String , variableState : VariableState ): SerializedVariablesState {
164
+ return serializeVariableState(name, variableState.property, variableState.value)
165
+ }
166
+
167
+ // maybe let it be global
168
+ fun createSerializeVariableState (name : String , property : KProperty <* >, value : Any? ): ProcessedSerializedVarsState {
169
+ val returnType = property.returnType
170
+ val classifier = returnType.classifier as KClass <* >
171
+ val membersProperties = if (value != null ) value::class .declaredMemberProperties else null
172
+ val isContainer = if (membersProperties != null ) membersProperties.size > 1 else false
173
+ val serializedVariablesState = SerializedVariablesState (name, classifier.simpleName.toString(),
174
+ getProperString(value), isContainer)
175
+
176
+ return ProcessedSerializedVarsState (serializedVariablesState, membersProperties)
177
+ }
178
+
179
+ fun createSerializeVariableState (name : String , variableState : VariableState ): ProcessedSerializedVarsState {
180
+ val returnType = variableState.property.returnType
181
+ val classifier = returnType.classifier as KClass <* >
182
+ val property = variableState.property
183
+ val javaField = property.returnType.jvmErasure
184
+
185
+ val membersProperties = if (variableState.value != null ) variableState.value!! ::class .declaredMemberProperties else null
186
+ val isContainer = if (membersProperties != null ) membersProperties.size > 1 else false
187
+ val serializedVariablesState = SerializedVariablesState (name, classifier.simpleName.toString(), variableState.stringValue, isContainer)
188
+
189
+ return ProcessedSerializedVarsState (serializedVariablesState, membersProperties)
190
+ }
191
+
192
+ fun iterateThroughContainerMembers (callInstance : Any? , descriptor : MutableFieldDescriptor , properties : PropertiesData ? , currentDepth : Int = 0): Unit {
193
+ if (properties == null || callInstance == null || currentDepth > 2 ) return
194
+
195
+ val serializedIteration = mutableMapOf<String , ProcessedSerializedVarsState >()
196
+ val callInstances = mutableMapOf<String , Any ?>()
197
+
198
+ for (it in properties) {
199
+ if (currentSerializeCount > 1000 ) {
200
+ break
201
+ }
202
+ it as KProperty1 <Any , * >
203
+ val name = it.name
204
+ val wasAccessible = it.isAccessible
205
+ it.isAccessible = true
206
+ val value = it.get(callInstance)
207
+
208
+ if (! seenObjects.containsKey(value)) {
209
+ serializedIteration[name] = createSerializeVariableState(name, it, value)
210
+ descriptor[name] = serializedIteration[name]?.serializedVariablesState
211
+ }
212
+
213
+ if (value != null && ! seenObjects.containsKey(value)) {
214
+ if (descriptor[name] != null ) {
215
+ seenObjects[value] = descriptor[name]!!
216
+ }
217
+ }
218
+ it.isAccessible = wasAccessible
219
+ currentSerializeCount++
220
+ }
221
+
222
+ serializedIteration.forEach {
223
+ val serializedVariablesState = it.value.serializedVariablesState
224
+ val name = it.key
225
+ if (serializedVariablesState.isContainer) {
226
+ iterateThroughContainerMembers(callInstances[name], serializedVariablesState.fieldDescriptor,
227
+ it.value.propertiesData, currentDepth + 1 )
228
+ }
229
+ }
230
+ }
231
+
232
+
233
+ fun getProperString (value : Any? ) : String {
234
+ value ? : return " null"
235
+
236
+ val kClass = value::class
237
+ val isFromJavaArray = kClass.java.isArray
238
+ if (isFromJavaArray || kClass.isSubclassOf(Array ::class )) {
239
+ value as Array <* >
240
+ return value.toString()
241
+ }
242
+ val isCollection = kClass.isSubclassOf(Collection ::class )
243
+ if (isCollection) {
244
+ value as Collection <* >
245
+ return buildString {
246
+ value.forEach {
247
+ append(it.toString(), " , " )
248
+ }
249
+ }
250
+ }
251
+ val isMap = kClass.isSubclassOf(Map ::class )
252
+ if (isMap) {
253
+ value as Map <Any , Any ?>
254
+ return buildString {
255
+ value.forEach {
256
+ append(it.key, ' =' , it.value, " \n " )
257
+ }
258
+ }
259
+ }
260
+ return value.toString()
261
+ }
0 commit comments