Skip to content

Commit 922f348

Browse files
Fixed bug with usageMap with var redefinition; some tests added
1 parent cef6207 commit 922f348

File tree

6 files changed

+173
-10
lines changed

6 files changed

+173
-10
lines changed

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

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ interface Notebook {
1414
*/
1515
val variablesMap: MutableMap<String, VariableState>
1616

17+
/**
18+
* Stores useful variables in a cell.
19+
* Useful <==> declarations + modifying references
20+
*/
21+
var usageMap: Map<Int, Set<String>>
22+
1723
/**
1824
* Mapping allowing to get cell by execution number
1925
*/

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

+2
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ class NotebookImpl(
106106

107107
override val variablesMap = mutableMapOf<String, VariableState>()
108108

109+
override var usageMap = mapOf<Int, Set<String>>()
110+
109111
override fun getCell(id: Int): CodeCellImpl {
110112
return cells[id] ?: throw ArrayIndexOutOfBoundsException(
111113
"There is no cell with number '$id'"

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

+1
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ class ReplForJupyterImpl(
381381
} ?: emptyList()
382382

383383
notebook.updateVarsState(varsMap)
384+
notebook.usageMap = usageMap
384385
printVars()
385386
// printUsage(jupyterId, usageMap[jupyterId - 1]!!)
386387

src/main/kotlin/org/jetbrains/kotlinx/jupyter/repl/impl/InternalEvaluatorImpl.kt

+14-8
Original file line numberDiff line numberDiff line change
@@ -190,16 +190,22 @@ internal class InternalEvaluatorImpl(
190190
} else {
191191
val stringVal = casted.get(cellKlassInstance).toString()
192192
val state = VariableState(casted, cellKlassInstance, stringVal)
193+
194+
// redeclaration of any type
195+
if (containingMap.containsKey(casted.name)) {
196+
val oldCellId = containingMap[casted.name]
197+
if (oldCellId != cellId) {
198+
usageMap[oldCellId]?.remove(casted.name)
199+
}
200+
}
201+
// it was val, now it's var
202+
if (it is KMutableProperty1 && variablesCache.containsKey(casted.name)) {
203+
variablesCache.remove(casted.name)
204+
}
205+
containingMap[casted.name] = cellId
206+
usageMap[cellId]?.add(casted.name)
193207
// invariant with changes: cache --> varsMap, other way is not allowed
194208
if (it !is KMutableProperty1) {
195-
// it is a replacement
196-
if (variablesCache.containsKey(casted.name)) {
197-
val oldCellID = containingMap[casted.name]
198-
usageMap[oldCellID]?.remove(casted.name)
199-
}
200-
containingMap[casted.name] = cellId
201-
usageMap[cellId]?.add(casted.name)
202-
203209
variablesCache[casted.name] = state
204210
variablesMap[casted.name] = state
205211
return@forEach

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

+149-2
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ class ReplTests : AbstractSingleReplTest() {
443443
}
444444

445445
@Test
446-
fun testStateConsistency() {
446+
fun testVarsStateConsistency() {
447447
assertTrue(repl.notebook.variablesMap.isEmpty())
448448
val res = eval(
449449
"""
@@ -471,7 +471,7 @@ class ReplTests : AbstractSingleReplTest() {
471471
}
472472

473473
@Test
474-
fun testEmptyState() {
474+
fun testVarsEmptyState() {
475475
val res = eval("3+2")
476476
val state = repl.notebook.variablesMap
477477
val strState = mutableMapOf<String, String>()
@@ -504,4 +504,151 @@ class ReplTests : AbstractSingleReplTest() {
504504
assertEquals("abc", varsState["y"]!!.stringValue)
505505
assertEquals("1", varsState["z"]!!.stringValue)
506506
}
507+
508+
@Test
509+
fun testVarsCaptureSeparateCells() {
510+
eval(
511+
"""
512+
val x = 1
513+
val y = "abc"
514+
val z = x
515+
""".trimIndent()
516+
)
517+
val varsState = repl.notebook.variablesMap
518+
assertTrue(varsState.isNotEmpty())
519+
eval(
520+
"""
521+
val x = "abc"
522+
var y = 123
523+
val z = x
524+
""".trimIndent(),
525+
jupyterId = 1
526+
)
527+
assertTrue(varsState.isNotEmpty())
528+
assertEquals(3, varsState.size)
529+
assertEquals("abc", varsState["x"]!!.stringValue)
530+
assertEquals("123", varsState["y"]!!.stringValue)
531+
assertEquals("abc", varsState["z"]!!.stringValue)
532+
533+
eval(
534+
"""
535+
val x = 1024
536+
y += 123
537+
""".trimIndent(),
538+
jupyterId = 2
539+
)
540+
541+
assertTrue(varsState.isNotEmpty())
542+
assertEquals(3, varsState.size)
543+
assertEquals("1024", varsState["x"]!!.stringValue)
544+
assertEquals("${123 * 2}", varsState["y"]!!.stringValue)
545+
assertEquals("abc", varsState["z"]!!.stringValue)
546+
}
547+
548+
@Test
549+
fun testVarsUsageConsistency() {
550+
eval("3+2")
551+
val state = repl.notebook.usageMap
552+
assertTrue(state.values.size == 1)
553+
assertTrue(state.values.first().isEmpty())
554+
val setOfNextCell = setOf<String>()
555+
assertEquals(state.values.first(), setOfNextCell)
556+
}
557+
558+
@Test
559+
fun testVarsDefsUsage() {
560+
eval(
561+
"""
562+
val x = 1
563+
val z = "abcd"
564+
var f = 47
565+
""".trimIndent()
566+
)
567+
val state = repl.notebook.usageMap
568+
assertTrue(state.isNotEmpty())
569+
assertTrue(state.values.first().isNotEmpty())
570+
val setOfCell = setOf("z", "f", "x")
571+
assertTrue(state.containsValue(setOfCell))
572+
}
573+
574+
@Test
575+
fun testVarsDefNRefUsage() {
576+
eval(
577+
"""
578+
val x = "abcd"
579+
var f = 47
580+
""".trimIndent()
581+
)
582+
val state = repl.notebook.usageMap
583+
assertTrue(state.isNotEmpty())
584+
eval(
585+
"""
586+
val z = 1
587+
f += f
588+
""".trimIndent()
589+
)
590+
assertTrue(state.isNotEmpty())
591+
592+
val setOfCell = setOf("z", "f", "x")
593+
assertTrue(state.containsValue(setOfCell))
594+
}
595+
596+
@Test
597+
fun testSeparateDefsUsage() {
598+
eval(
599+
"""
600+
val x = "abcd"
601+
var f = 47
602+
""".trimIndent(),
603+
jupyterId = 1
604+
)
605+
val state = repl.notebook.usageMap
606+
assertTrue(state[0]!!.contains("x"))
607+
608+
eval(
609+
"""
610+
val x = 341
611+
var f = "abcd"
612+
""".trimIndent(),
613+
jupyterId = 2
614+
)
615+
assertTrue(state.isNotEmpty())
616+
assertTrue(state[0]!!.isEmpty())
617+
assertTrue(state[1]!!.contains("x"))
618+
619+
val setOfPrevCell = setOf<String>()
620+
val setOfNextCell = setOf("x", "f")
621+
assertEquals(state[0], setOfPrevCell)
622+
assertEquals(state[1], setOfNextCell)
623+
}
624+
625+
@Test
626+
fun testSeparateCellsUsage() {
627+
eval(
628+
"""
629+
val x = "abcd"
630+
var f = 47
631+
""".trimIndent(),
632+
jupyterId = 1
633+
)
634+
val state = repl.notebook.usageMap
635+
assertTrue(state[0]!!.contains("x"))
636+
637+
eval(
638+
"""
639+
val x = 341
640+
f += x
641+
""".trimIndent(),
642+
jupyterId = 2
643+
)
644+
assertTrue(state.isNotEmpty())
645+
assertTrue(state[0]!!.isNotEmpty())
646+
assertFalse(state[0]!!.contains("x"))
647+
assertTrue(state[1]!!.contains("x"))
648+
649+
val setOfPrevCell = setOf("f")
650+
val setOfNextCell = setOf("x", "f")
651+
assertEquals(state[0], setOfPrevCell)
652+
assertEquals(state[1], setOfNextCell)
653+
}
507654
}

src/test/kotlin/org/jetbrains/kotlinx/jupyter/test/testUtil.kt

+1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ object NotebookMock : Notebook {
168168
override val cellsList: Collection<CodeCell>
169169
get() = emptyList()
170170
override val variablesMap = mutableMapOf<String, VariableState>()
171+
override var usageMap = mapOf<Int, Set<String>>()
171172

172173
override fun getCell(id: Int): CodeCellImpl {
173174
return cells[id] ?: throw ArrayIndexOutOfBoundsException(

0 commit comments

Comments
 (0)