Skip to content

Commit 5617daf

Browse files
Fix recursion detection
1 parent 86e5a01 commit 5617daf

File tree

2 files changed

+55
-9
lines changed
  • jupyter-lib/api/src/main/kotlin/org/jetbrains/kotlinx/jupyter/api
  • src/test/kotlin/org/jetbrains/kotlinx/jupyter/test/repl

2 files changed

+55
-9
lines changed

jupyter-lib/api/src/main/kotlin/org/jetbrains/kotlinx/jupyter/api/VariableState.kt

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ data class VariableStateImpl(
3939
}
4040
}
4141
override var isRecursive: Boolean = false
42+
private var isLargeForString: Boolean = false
4243

4344
private val valCache = VariableStateCache<Result<Any?>> (
4445
{
@@ -73,17 +74,35 @@ data class VariableStateImpl(
7374
val res = action(this)
7475
isAccessible = wasAccessible
7576
return res
76-
private val customDelegate = DependentLazyDelegate {
77-
fun getRecursiveObjectName(): String {
78-
val kClassName = cachedValue.getOrNull()!!::class.simpleName
79-
return "$kClassName: recursive structure"
8077
}
81-
if (cachedValue.getOrNull() == null) {
82-
return@DependentLazyDelegate null
83-
}
84-
handleIfRecursiveStructure()
8578
}
86-
}
79+
private val customDelegate = DependentLazyDelegate {
80+
fun getRecursiveObjectName(): String {
81+
val kClassName = cachedValue.getOrNull()!!::class.simpleName
82+
return "$kClassName: recursive structure"
83+
}
84+
if (cachedValue.getOrNull() == null) {
85+
return@DependentLazyDelegate null
86+
}
87+
handleIfRecursiveStructure()
88+
try {
89+
cachedValue.getOrNull().toString()
90+
isRecursive = false
91+
isLargeForString = false
92+
} catch (e: VirtualMachineError) {
93+
when (e) {
94+
is StackOverflowError -> {
95+
isRecursive = true
96+
}
97+
is OutOfMemoryError -> {
98+
isLargeForString = true
99+
}
100+
else -> {
101+
return@DependentLazyDelegate null
102+
}
103+
}
104+
}
105+
}
87106

88107
private class VariableStateCache<T>(
89108
val equalityChecker: (T, T) -> Boolean = { x, y -> x == y },

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,33 @@ class ReplVarsTest : AbstractSingleReplTest() {
761761
assertEquals(2, newData.fieldDescriptor.size)
762762
}
763763

764+
@Test
765+
fun testProperBiRecursionHandling() {
766+
eval(
767+
"""
768+
val l = mutableListOf<Any>()
769+
l.add(listOf(l))
770+
771+
val m = mutableMapOf<Int, Any>(1 to l)
772+
773+
val z = setOf(1, 2, 4)
774+
""".trimIndent(),
775+
jupyterId = 1
776+
)
777+
var state = repl.notebook.variablesState
778+
assertEquals("ArrayList: recursive structure", state["l"]!!.stringValue)
779+
assertEquals("LinkedHashMap: recursive structure", state["m"]!!.stringValue)
780+
eval(
781+
"""
782+
val m = mutableMapOf<Int, Any>(1 to "abc")
783+
""".trimIndent(),
784+
jupyterId = 2
785+
)
786+
state = repl.notebook.variablesState
787+
assertEquals("ArrayList: recursive structure", state["l"]!!.stringValue)
788+
assertNotEquals("LinkedHashMap: recursive structure", state["m"]!!.stringValue)
789+
}
790+
764791
@Test
765792
fun testUnchangedVars() {
766793
eval(

0 commit comments

Comments
 (0)