@@ -10,6 +10,7 @@ import java.lang.reflect.Field
1010import kotlin.contracts.ExperimentalContracts
1111import kotlin.contracts.contract
1212import kotlin.math.abs
13+ import kotlin.random.Random
1314import kotlin.reflect.KClass
1415import kotlin.reflect.KProperty
1516import kotlin.reflect.KProperty1
@@ -59,7 +60,8 @@ data class ProcessedDescriptorsState(
5960)
6061
6162data class RuntimeObjectWrapper (
62- val objectInstance : Any?
63+ val objectInstance : Any? ,
64+ val isRecursive : Boolean = false
6365) {
6466 override fun equals (other : Any? ): Boolean {
6567 if (other == null ) return objectInstance == null
@@ -69,11 +71,24 @@ data class RuntimeObjectWrapper(
6971 }
7072
7173 override fun hashCode (): Int {
72- return objectInstance?.hashCode() ? : 0
74+ return if (isRecursive) Random .hashCode() else objectInstance?.hashCode() ? : 0
7375 }
7476}
7577
76- fun Any?.toObjectWrapper (): RuntimeObjectWrapper = RuntimeObjectWrapper (this )
78+ fun Any?.toObjectWrapper (isRecursive : Boolean = false): RuntimeObjectWrapper = RuntimeObjectWrapper (this , isRecursive)
79+
80+
81+ fun Any?.getToStringValue (isRecursive : Boolean = false): String {
82+ return if (isRecursive) {
83+ " ${this !! ::class .simpleName} : recursive structure"
84+ } else {
85+ try {
86+ this ?.toString() ? : " null"
87+ } catch (e: StackOverflowError ) {
88+ " ${this !! ::class .simpleName} : recursive structure"
89+ }
90+ }
91+ }
7792
7893/* *
7994 * Provides contract for using threshold-based removal heuristic.
@@ -125,7 +140,8 @@ class VariablesSerializer(
125140 " Map" ,
126141 " Set" ,
127142 " Collection" ,
128- " LinkedValues"
143+ " LinkedValues" ,
144+ " LinkedEntrySet"
129145 )
130146
131147 fun isStandardType (type : String ): Boolean = containersTypes.contains(type)
@@ -382,25 +398,27 @@ class VariablesSerializer(
382398 it.name == propertyName
383399 } ? : return serializedVariablesState
384400
385- return serializeVariableState(cellId, propertyName, property, value, false )
401+ return serializeVariableState(cellId, propertyName, property, value, isRecursive = false , false )
386402 }
387403
388404 private fun serializeVariableState (cellId : Int , name : String? , variableState : VariableState ? , isOverride : Boolean = true): SerializedVariablesState {
389405 if (! isSerializationActive || variableState == null || name == null ) return SerializedVariablesState ()
390- return serializeVariableState(cellId, name, variableState.property, variableState.value, isOverride)
406+ // force recursive check
407+ variableState.stringValue
408+ return serializeVariableState(cellId, name, variableState.property, variableState.value.getOrNull(), variableState.isRecursive, isOverride)
391409 }
392410
393- private fun serializeVariableState (cellId : Int , name : String , property : Field ? , value : Any? , isOverride : Boolean = true): SerializedVariablesState {
411+ private fun serializeVariableState (cellId : Int , name : String , property : Field ? , value : Any? , isRecursive : Boolean , isOverride : Boolean = true): SerializedVariablesState {
394412 val processedData = createSerializeVariableState(name, getSimpleTypeNameFrom(property, value), value)
395- return doActualSerialization(cellId, processedData, value.toObjectWrapper() , isOverride)
413+ return doActualSerialization(cellId, processedData, value.toObjectWrapper(isRecursive), isRecursive , isOverride)
396414 }
397415
398- private fun serializeVariableState (cellId : Int , name : String , property : KProperty <* >, value : Any? , isOverride : Boolean = true): SerializedVariablesState {
416+ private fun serializeVariableState (cellId : Int , name : String , property : KProperty <* >, value : Any? , isRecursive : Boolean , isOverride : Boolean = true): SerializedVariablesState {
399417 val processedData = createSerializeVariableState(name, getSimpleTypeNameFrom(property, value), value)
400- return doActualSerialization(cellId, processedData, value.toObjectWrapper() , isOverride)
418+ return doActualSerialization(cellId, processedData, value.toObjectWrapper(isRecursive), isRecursive , isOverride)
401419 }
402420
403- private fun doActualSerialization (cellId : Int , processedData : ProcessedSerializedVarsState , value : RuntimeObjectWrapper , isOverride : Boolean = true): SerializedVariablesState {
421+ private fun doActualSerialization (cellId : Int , processedData : ProcessedSerializedVarsState , value : RuntimeObjectWrapper , isRecursive : Boolean , isOverride : Boolean = true): SerializedVariablesState {
404422 val serializedVersion = processedData.serializedVariablesState
405423
406424 seenObjectsPerCell.putIfAbsent(cellId, mutableMapOf ())
@@ -428,9 +446,13 @@ class VariablesSerializer(
428446 }
429447 val type = processedData.propertiesType
430448 if (type == PropertiesType .KOTLIN ) {
431- iterateThroughContainerMembers(cellId, value.objectInstance, serializedVersion.fieldDescriptor, kProperties = currentCellDescriptors.processedSerializedVarsToKTProperties[serializedVersion])
449+ val kProperties = currentCellDescriptors.processedSerializedVarsToKTProperties[serializedVersion]
450+ if (kProperties?.size == 1 && kProperties.first().name == " size" ) {
451+ serializedVersion.fieldDescriptor.addDescriptor(value.objectInstance, " data" )
452+ }
453+ iterateThroughContainerMembers(cellId, value.objectInstance, serializedVersion.fieldDescriptor, isRecursive = isRecursive, kProperties = currentCellDescriptors.processedSerializedVarsToKTProperties[serializedVersion])
432454 } else {
433- iterateThroughContainerMembers(cellId, value.objectInstance, serializedVersion.fieldDescriptor, currentCellDescriptors.processedSerializedVarsToJavaProperties[serializedVersion])
455+ iterateThroughContainerMembers(cellId, value.objectInstance, serializedVersion.fieldDescriptor, isRecursive = isRecursive, currentCellDescriptors.processedSerializedVarsToJavaProperties[serializedVersion])
434456 }
435457 }
436458
@@ -441,18 +463,19 @@ class VariablesSerializer(
441463 cellId : Int ,
442464 callInstance : Any? ,
443465 descriptor : MutableFieldDescriptor ,
466+ isRecursive : Boolean = false,
444467 properties : PropertiesData ? = null,
445468 kProperties : KPropertiesData ? = null,
446469 currentDepth : Int = 0
447470 ) {
448471 fun iterateAndStoreValues (callInstance : Any , descriptorsState : MutableMap <String , SerializedVariablesState ?>) {
449472 if (callInstance is Collection <* >) {
450473 callInstance.forEach {
451- descriptorsState.addDescriptor(it)
474+ descriptorsState.addDescriptor(it, name = it.getToStringValue() )
452475 }
453476 } else if (callInstance is Array <* >) {
454477 callInstance.forEach {
455- descriptorsState.addDescriptor(it)
478+ descriptorsState.addDescriptor(it, name = it.getToStringValue() )
456479 }
457480 }
458481 }
@@ -472,15 +495,15 @@ class VariablesSerializer(
472495 if (currentSerializeCount > serializationLimit) {
473496 break
474497 }
475- iterateThrough(it, seenObjectsPerCell, serializedIteration, descriptor, instancesPerState, callInstance)
498+ iterateThrough(it, seenObjectsPerCell, serializedIteration, descriptor, instancesPerState, callInstance, isRecursive )
476499 currentSerializeCount++
477500 }
478501 } else if (kProperties != null ) {
479502 for (it in kProperties) {
480503 if (currentSerializeCount > serializationLimit) {
481504 break
482505 }
483- iterateThrough(it, seenObjectsPerCell, serializedIteration, descriptor, instancesPerState, callInstance)
506+ iterateThrough(it, seenObjectsPerCell, serializedIteration, descriptor, instancesPerState, callInstance, isRecursive )
484507 currentSerializeCount++
485508 }
486509 }
@@ -506,6 +529,10 @@ class VariablesSerializer(
506529 }
507530 }
508531
532+ // if (isRecursive) {
533+ // return
534+ // }
535+
509536 serializedIteration.forEach {
510537 val serializedVariablesState = it.value.serializedVariablesState
511538 val name = it.key
@@ -520,14 +547,15 @@ class VariablesSerializer(
520547 else -> {
521548 null
522549 }
523- }.toObjectWrapper()
550+ }.toObjectWrapper(isRecursive )
524551
525552 computedDescriptorsPerCell[cellId]!! .instancesPerState + = instancesPerState
526553 iterateThroughContainerMembers(
527554 cellId,
528555 neededCallInstance.objectInstance,
529556 serializedVariablesState.fieldDescriptor,
530- it.value.propertiesData,
557+ isRecursive = isRecursive,
558+ properties = it.value.propertiesData,
531559 currentDepth = currentDepth + 1
532560 )
533561 }
@@ -545,17 +573,18 @@ class VariablesSerializer(
545573 serializedIteration : MutableMap <String , ProcessedSerializedVarsState >,
546574 descriptor : MutableFieldDescriptor ,
547575 instancesPerState : MutableMap <SerializedVariablesState , Any ?>,
548- callInstance : Any
576+ callInstance : Any ,
577+ isRecursive : Boolean = false
549578 ) {
550579 contract {
551580 returns() implies (elem is Field || elem is KProperty1 <* , * >)
552581 }
553582
554583 val name = if (elem is Field ) elem.name else (elem as KProperty1 <Any , * >).name
555- val value = if (elem is Field ) tryGetValueFromProperty(elem, callInstance).toObjectWrapper()
584+ val value = if (elem is Field ) tryGetValueFromProperty(elem, callInstance).toObjectWrapper(isRecursive )
556585 else {
557586 elem as KProperty1 <Any , * >
558- tryGetValueFromProperty(elem, callInstance).toObjectWrapper()
587+ tryGetValueFromProperty(elem, callInstance).toObjectWrapper(isRecursive )
559588 }
560589
561590 val simpleType = if (elem is Field ) getSimpleTypeNameFrom(elem, value.objectInstance) ? : " null"
@@ -564,6 +593,7 @@ class VariablesSerializer(
564593 getSimpleTypeNameFrom(elem, value.objectInstance) ? : " null"
565594 }
566595 serializedIteration[name] = if (standardContainersUtilizer.isStandardType(simpleType)) {
596+ // todo might add isRecursive
567597 standardContainersUtilizer.serializeContainer(simpleType, value.objectInstance, true )
568598 } else {
569599 createSerializeVariableState(name, simpleType, value)
@@ -612,7 +642,7 @@ class VariablesSerializer(
612642 if (value != null ) {
613643 value::class .simpleName
614644 } else {
615- value?.toString ()
645+ value?.getToStringValue ()
616646 }
617647 }
618648 }
@@ -627,7 +657,7 @@ class VariablesSerializer(
627657 (classifier as KClass <* >).simpleName
628658 }
629659 } else {
630- value?.toString ()
660+ value?.getToStringValue ()
631661 }
632662 }
633663
@@ -742,7 +772,8 @@ fun getProperString(value: Any?): String {
742772
743773 val kClass = value::class
744774 val isFromJavaArray = kClass.java.isArray
745- try {
775+
776+ return try {
746777 if (isFromJavaArray || kClass.isArray()) {
747778 value as Array <* >
748779 return buildString {
@@ -780,11 +811,14 @@ fun getProperString(value: Any?): String {
780811 }
781812 }
782813 }
783- } catch (e: Throwable ) {
784814 value.toString()
815+ } catch (e: Throwable ) {
816+ if (e is StackOverflowError ) {
817+ " ${value::class .simpleName} : recursive structure"
818+ } else {
819+ value.toString()
820+ }
785821 }
786-
787- return value.toString()
788822}
789823
790824fun KClass <* >.isArray (): Boolean = this .isSubclassOf(Array ::class )
0 commit comments