@@ -2,9 +2,11 @@ package org.jetbrains.kotlinx.jupyter
2
2
3
3
import org.jetbrains.kotlinx.jupyter.api.VariableState
4
4
import org.jetbrains.kotlinx.jupyter.compiler.util.SerializedVariablesState
5
+ import java.lang.reflect.Field
5
6
import kotlin.reflect.KClass
6
7
import kotlin.reflect.KProperty
7
8
import kotlin.reflect.KProperty1
9
+ import kotlin.reflect.KTypeParameter
8
10
import kotlin.reflect.full.declaredMemberProperties
9
11
import kotlin.reflect.full.isSubclassOf
10
12
import kotlin.reflect.jvm.isAccessible
@@ -13,14 +15,16 @@ typealias FieldDescriptor = Map<String, SerializedVariablesState?>
13
15
typealias MutableFieldDescriptor = MutableMap <String , SerializedVariablesState ?>
14
16
typealias PropertiesData = Collection <KProperty1 <out Any , * >>
15
17
16
- data class ProcessedSerializedVarsState (
18
+ class ProcessedSerializedVarsState (
17
19
val serializedVariablesState : SerializedVariablesState ,
18
- val propertiesData : PropertiesData ?
20
+ val propertiesData : PropertiesData ? ,
21
+ val jvmOnlyFields : Array <Field >? = null
19
22
)
20
23
21
24
data class ProcessedDescriptorsState (
22
- // perhaps, better tp make SerializedVariablesState -> PropertiesData?
23
- val processedSerializedVarsState : MutableMap <SerializedVariablesState , PropertiesData ?> = mutableMapOf(),
25
+ val processedSerializedVarsToKProperties : MutableMap <SerializedVariablesState , PropertiesData ?> = mutableMapOf(),
26
+ // do we need this? Probably, not
27
+ // val processedSerializedVarsToJvmFields: MutableMap<SerializedVariablesState, Array<Field>?> = mutableMapOf(),
24
28
val instancesPerState : MutableMap <SerializedVariablesState , Any ?> = mutableMapOf()
25
29
)
26
30
@@ -41,8 +45,11 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
41
45
*/
42
46
private val computedDescriptorsPerCell: MutableMap <Int , ProcessedDescriptorsState > = mutableMapOf ()
43
47
48
+ private val isSerializationActive: Boolean = System .getProperty(serializationEnvProperty)?.toBooleanStrictOrNull() ? : true
44
49
45
50
fun serializeVariables (cellId : Int , variablesState : Map <String , VariableState >): Map <String , SerializedVariablesState > {
51
+ if (! isSerializationActive) return emptyMap()
52
+
46
53
if (seenObjectsPerCell.containsKey(cellId)) {
47
54
seenObjectsPerCell[cellId]!! .clear()
48
55
}
@@ -54,6 +61,8 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
54
61
}
55
62
56
63
fun doIncrementalSerialization (cellId : Int , propertyName : String , serializedVariablesState : SerializedVariablesState ): SerializedVariablesState {
64
+ if (! isSerializationActive) return serializedVariablesState
65
+
57
66
val cellDescriptors = computedDescriptorsPerCell[cellId] ? : return serializedVariablesState
58
67
return updateVariableState(cellId, propertyName, cellDescriptors, serializedVariablesState)
59
68
}
@@ -62,12 +71,16 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
62
71
* @param evaluatedDescriptorsState - origin variable state to get value from
63
72
* @param serializedVariablesState - current state of recursive state to go further
64
73
*/
65
- private fun updateVariableState (cellId : Int , propertyName : String , evaluatedDescriptorsState : ProcessedDescriptorsState ,
66
- serializedVariablesState : SerializedVariablesState ): SerializedVariablesState {
74
+ private fun updateVariableState (
75
+ cellId : Int ,
76
+ propertyName : String ,
77
+ evaluatedDescriptorsState : ProcessedDescriptorsState ,
78
+ serializedVariablesState : SerializedVariablesState
79
+ ): SerializedVariablesState {
67
80
val value = evaluatedDescriptorsState.instancesPerState[serializedVariablesState]
68
- val propertiesData = evaluatedDescriptorsState.processedSerializedVarsState [serializedVariablesState]
69
- if (propertiesData == null && value != null && value::class .java.isArray) {
70
- return serializeVariableState(cellId, propertyName, null , value, false )
81
+ val propertiesData = evaluatedDescriptorsState.processedSerializedVarsToKProperties [serializedVariablesState]
82
+ if (propertiesData == null && value != null && ( value::class .java.isArray || value:: class .java.isMemberClass) ) {
83
+ return serializeVariableState(cellId, propertyName, propertiesData , value, false )
71
84
}
72
85
val property = propertiesData?.firstOrNull {
73
86
it.name == propertyName
@@ -76,14 +89,26 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
76
89
return serializeVariableState(cellId, propertyName, property, value, false )
77
90
}
78
91
79
-
80
92
fun serializeVariableState (cellId : Int , name : String? , variableState : VariableState ? , isOverride : Boolean = true): SerializedVariablesState {
81
- if (variableState == null || name == null ) return SerializedVariablesState ()
93
+ if (! isSerializationActive || variableState == null || name == null ) return SerializedVariablesState ()
82
94
return serializeVariableState(cellId, name, variableState.property, variableState.value, isOverride)
83
95
}
84
96
85
- fun serializeVariableState (cellId : Int , name : String , property : KProperty <* >? , value : Any? , isOverride : Boolean = true): SerializedVariablesState {
86
- val processedData = createSerializeVariableState(name, property, value)
97
+ private fun serializeVariableState (cellId : Int , name : String , property : Field ? , value : Any? , isOverride : Boolean = true): SerializedVariablesState {
98
+ val processedData = createSerializeVariableState(name, getSimpleTypeNameFrom(property, value), value)
99
+ return doActualSerialization(cellId, processedData, value, isOverride)
100
+ }
101
+
102
+ private fun serializeVariableState (cellId : Int , name : String , property : KProperty <* >, value : Any? , isOverride : Boolean = true): SerializedVariablesState {
103
+ val processedData = createSerializeVariableState(name, getSimpleTypeNameFrom(property, value), value)
104
+ return doActualSerialization(cellId, processedData, value, isOverride)
105
+ }
106
+
107
+ private fun doActualSerialization (cellId : Int , processedData : ProcessedSerializedVarsState , value : Any? , isOverride : Boolean = true): SerializedVariablesState {
108
+ fun isCanBeComputed (fieldDescriptors : MutableMap <String , SerializedVariablesState ?>): Boolean {
109
+ return (fieldDescriptors.isEmpty() || (fieldDescriptors.isNotEmpty() && fieldDescriptors.entries.first().value?.fieldDescriptor!! .isEmpty()))
110
+ }
111
+
87
112
val serializedVersion = processedData.serializedVariablesState
88
113
89
114
seenObjectsPerCell.putIfAbsent(cellId, mutableMapOf ())
@@ -92,26 +117,40 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
92
117
computedDescriptorsPerCell[cellId] = ProcessedDescriptorsState ()
93
118
}
94
119
val currentCellDescriptors = computedDescriptorsPerCell[cellId]
95
- currentCellDescriptors!! .processedSerializedVarsState[serializedVersion] = processedData.propertiesData
120
+ currentCellDescriptors!! .processedSerializedVarsToKProperties[serializedVersion] = processedData.propertiesData
121
+ // currentCellDescriptors.processedSerializedVarsToJvmFields[serializedVersion] = processedData.jvmOnlyFields
96
122
97
123
if (value != null ) {
98
- seenObjectsPerCell[cellId]!! [ value] = serializedVersion
124
+ seenObjectsPerCell[cellId]!! .putIfAbsent( value, serializedVersion)
99
125
}
100
126
if (serializedVersion.isContainer) {
101
- iterateThroughContainerMembers(cellId, value, serializedVersion.fieldDescriptor, currentCellDescriptors.processedSerializedVarsState[serializedVersion])
127
+ // check for seen
128
+ if (seenObjectsPerCell[cellId]!! .containsKey(value)) {
129
+ val previouslySerializedState = seenObjectsPerCell[cellId]!! [value] ? : return processedData.serializedVariablesState
130
+ serializedVersion.fieldDescriptor + = previouslySerializedState.fieldDescriptor
131
+ if (isCanBeComputed(serializedVersion.fieldDescriptor)) {
132
+ iterateThroughContainerMembers(cellId, value, serializedVersion.fieldDescriptor, currentCellDescriptors.processedSerializedVarsToKProperties[serializedVersion])
133
+ }
134
+ } else {
135
+ // add jvm descriptors
136
+ processedData.jvmOnlyFields?.forEach {
137
+ serializedVersion.fieldDescriptor[it.name] = serializeVariableState(cellId, it.name, it, value)
138
+ }
139
+ iterateThroughContainerMembers(cellId, value, serializedVersion.fieldDescriptor, currentCellDescriptors.processedSerializedVarsToKProperties[serializedVersion])
140
+ }
102
141
}
103
142
return processedData.serializedVariablesState
104
143
}
105
144
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) {
108
146
if (properties == null || callInstance == null || currentDepth >= serializationStep) return
109
147
110
148
val serializedIteration = mutableMapOf<String , ProcessedSerializedVarsState >()
111
149
112
150
seenObjectsPerCell.putIfAbsent(cellId, mutableMapOf ())
113
151
val seenObjectsPerCell = seenObjectsPerCell[cellId]
114
152
val currentCellDescriptors = computedDescriptorsPerCell[cellId]!!
153
+ // ok, it's a copy on the left for some reason
115
154
val instancesPerState = currentCellDescriptors.instancesPerState
116
155
117
156
for (it in properties) {
@@ -123,7 +162,7 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
123
162
val value = tryGetValueFromProperty(it, callInstance)
124
163
125
164
if (! seenObjectsPerCell!! .containsKey(value)) {
126
- serializedIteration[name] = createSerializeVariableState(name, it , value)
165
+ serializedIteration[name] = createSerializeVariableState(name, getSimpleTypeNameFrom(it, value) , value)
127
166
descriptor[name] = serializedIteration[name]!! .serializedVariablesState
128
167
}
129
168
if (descriptor[name] != null ) {
@@ -143,6 +182,26 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
143
182
}
144
183
145
184
val isArrayType = checkCreateForPossibleArray(callInstance, descriptor, serializedIteration)
185
+ computedDescriptorsPerCell[cellId]!! .instancesPerState + = instancesPerState
186
+
187
+ // check for seen
188
+ // for now it's O(c*n)
189
+ if (serializedIteration.isEmpty()) {
190
+ val processedVars = computedDescriptorsPerCell[cellId]!! .processedSerializedVarsToKProperties
191
+ descriptor.forEach { (_, state) ->
192
+ if (processedVars.containsKey(state)) {
193
+ processedVars.entries.firstOrNull {
194
+ val itValue = it.key
195
+ if (itValue.value == state?.value && itValue.type == state?.value) {
196
+ state?.fieldDescriptor?.put(itValue.type, itValue)
197
+ true
198
+ } else {
199
+ false
200
+ }
201
+ }
202
+ }
203
+ }
204
+ }
146
205
147
206
serializedIteration.forEach {
148
207
val serializedVariablesState = it.value.serializedVariablesState
@@ -155,61 +214,108 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
155
214
isArrayType -> {
156
215
callInstance
157
216
}
158
- else -> { null }
217
+ else -> {
218
+ null
219
+ }
159
220
}
160
221
if (isArrayType) {
161
222
if (callInstance is List <* >) {
162
223
callInstance.forEach { arrayElem ->
163
- iterateThroughContainerMembers(cellId, arrayElem, serializedVariablesState.fieldDescriptor,
164
- it.value.propertiesData, currentDepth + 1 )
224
+ iterateThroughContainerMembers(
225
+ cellId,
226
+ arrayElem,
227
+ serializedVariablesState.fieldDescriptor,
228
+ it.value.propertiesData,
229
+ currentDepth + 1
230
+ )
165
231
}
166
232
} else {
167
233
callInstance as Array <* >
168
234
callInstance.forEach { arrayElem ->
169
- iterateThroughContainerMembers(cellId, arrayElem, serializedVariablesState.fieldDescriptor,
170
- it.value.propertiesData, currentDepth + 1 )
235
+ iterateThroughContainerMembers(
236
+ cellId,
237
+ arrayElem,
238
+ serializedVariablesState.fieldDescriptor,
239
+ it.value.propertiesData,
240
+ currentDepth + 1
241
+ )
171
242
}
172
243
}
173
244
174
245
return @forEach
175
246
}
176
- iterateThroughContainerMembers(cellId, neededCallInstance, serializedVariablesState.fieldDescriptor,
177
- it.value.propertiesData, currentDepth + 1 )
247
+
248
+ // update state with JVMFields
249
+ it.value.jvmOnlyFields?.forEach { field ->
250
+ serializedVariablesState.fieldDescriptor[field.name] = serializeVariableState(cellId, field.name, field, neededCallInstance)
251
+ val properInstance = serializedVariablesState.fieldDescriptor[field.name]
252
+ instancesPerState[properInstance!! ] = neededCallInstance
253
+ seenObjectsPerCell?.set(neededCallInstance!! , serializedVariablesState)
254
+ }
255
+ computedDescriptorsPerCell[cellId]!! .instancesPerState + = instancesPerState
256
+ // computedDescriptorsPerCell[cellId]!!.processedSerializedVarsToJvmFields[serializedVariablesState] = it.value.jvmOnlyFields
257
+ iterateThroughContainerMembers(
258
+ cellId,
259
+ neededCallInstance,
260
+ serializedVariablesState.fieldDescriptor,
261
+ it.value.propertiesData,
262
+ currentDepth + 1
263
+ )
178
264
}
179
265
}
180
266
}
181
267
268
+ private fun getSimpleTypeNameFrom (property : Field ? , value : Any? ): String? {
269
+ return if (property != null ) {
270
+ val returnType = property.type
271
+ returnType.simpleName
272
+ } else {
273
+ value?.toString()
274
+ }
275
+ }
182
276
183
- private fun createSerializeVariableState ( name : String , property : KProperty <* >? , value : Any? ): ProcessedSerializedVarsState {
184
- val simpleName = if (property != null ) {
277
+ private fun getSimpleTypeNameFrom ( property : KProperty <* >? , value : Any? ): String? {
278
+ return if (property != null ) {
185
279
val returnType = property.returnType
186
- val classifier = returnType.classifier as KClass <* >
187
- classifier.simpleName
280
+ val classifier = returnType.classifier
281
+ if (classifier is KTypeParameter ) {
282
+ classifier.name
283
+ } else {
284
+ (classifier as KClass <* >).simpleName
285
+ }
188
286
} else {
189
287
value?.toString()
190
288
}
289
+ }
191
290
291
+ private fun createSerializeVariableState (name : String , simpleTypeName : String? , value : Any? ): ProcessedSerializedVarsState {
192
292
// make it exception-safe
193
293
val membersProperties = try {
194
294
if (value != null ) value::class .declaredMemberProperties else null
195
295
} catch (e: Throwable ) {
196
296
null
197
297
}
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) {
298
+ val javaClass = value?.javaClass
299
+ val jvmFields = if (javaClass != null && javaClass.isMemberClass) {
300
+ javaClass.declaredFields
301
+ } else { null }
302
+
303
+ val isContainer = if (membersProperties != null ) (
304
+ membersProperties.isNotEmpty() || value!! ::class .java.isArray || (javaClass != null && javaClass.isMemberClass)
305
+ ) else false
306
+ val type = if (value != null && value::class .java.isArray) {
200
307
" Array"
201
308
} else if (isContainer && value is List <* >) {
202
309
" SingletonList"
203
310
} else {
204
- simpleName .toString()
311
+ simpleTypeName .toString()
205
312
}
206
313
207
- val serializedVariablesState = SerializedVariablesState (name, type, getProperString(value), isContainer)
314
+ val serializedVariablesState = SerializedVariablesState (type, getProperString(value), isContainer)
208
315
209
- return ProcessedSerializedVarsState (serializedVariablesState, membersProperties)
316
+ return ProcessedSerializedVarsState (serializedVariablesState, membersProperties, jvmFields )
210
317
}
211
318
212
-
213
319
private fun tryGetValueFromProperty (property : KProperty1 <Any , * >, callInstance : Any ): Any? {
214
320
// some fields may be optimized out like array size. Thus, calling it.isAccessible would return error
215
321
val canAccess = try {
@@ -244,12 +350,13 @@ class VariablesSerializer(private val serializationStep: Int = 2, private val se
244
350
}
245
351
}
246
352
353
+ companion object {
354
+ const val serializationEnvProperty = " jupyter.serialization.enabled"
355
+ }
247
356
}
248
357
249
- // TODO: maybe think of considering the depth?
250
358
fun getProperString (value : Any? ): String {
251
-
252
- fun print (builder : StringBuilder , containerSize : Int , index : Int , value : Any? ): Unit {
359
+ fun print (builder : StringBuilder , containerSize : Int , index : Int , value : Any? ) {
253
360
if (index != containerSize - 1 ) {
254
361
builder.append(value, " , " )
255
362
} else {
@@ -296,4 +403,4 @@ fun getProperString(value: Any?): String {
296
403
}
297
404
298
405
return value.toString()
299
- }
406
+ }
0 commit comments