bump version to 1.0.7-SNAPSHOT; fix potential infinite loops in Scope traversal

This commit is contained in:
Sergey Chernov 2025-12-09 07:33:05 +01:00
parent 55caa65f97
commit c0fab3d60e
3 changed files with 61 additions and 14 deletions

View File

@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "net.sergeych" group = "net.sergeych"
version = "1.0.6-SNAPSHOT" version = "1.0.7-SNAPSHOT"
// Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below // Removed legacy buildscript classpath declarations; plugins are applied via the plugins DSL below

View File

@ -1351,15 +1351,21 @@ class FastLocalVarRef(
if (owner.frameId != cachedOwnerFrameId) return false if (owner.frameId != cachedOwnerFrameId) return false
// Ensure owner is an ancestor (or same) of current // Ensure owner is an ancestor (or same) of current
var s: Scope? = current var s: Scope? = current
var guard = 0
while (s != null) { while (s != null) {
if (s === owner) return true if (s === owner) return true
s = s.parent val next = s.parent
// Defensive: break on self-parent or pathological cycles
if (next === s) return false
s = next
if (++guard > 4096) return false
} }
return false return false
} }
private fun resolveSlotInAncestry(scope: Scope): Int { private fun resolveSlotInAncestry(scope: Scope): Int {
var s: Scope? = scope var s: Scope? = scope
var guard = 0
while (s != null) { while (s != null) {
val idx = s.getSlotIndexOf(name) val idx = s.getSlotIndexOf(name)
if (idx != null) { if (idx != null) {
@ -1368,7 +1374,10 @@ class FastLocalVarRef(
cachedSlot = idx cachedSlot = idx
return idx return idx
} }
s = s.parent val next = s.parent
if (next === s) return -1
s = next
if (++guard > 4096) return -1
} }
return -1 return -1
} }
@ -1387,16 +1396,26 @@ class FastLocalVarRef(
// Try per-frame local binding maps in the ancestry first (locals declared in frames) // Try per-frame local binding maps in the ancestry first (locals declared in frames)
run { run {
var s: Scope? = scope var s: Scope? = scope
var guard = 0
while (s != null) { while (s != null) {
s.localBindings[name]?.let { return it } s.localBindings[name]?.let { return it }
s = s.parent val next = s.parent
if (next === s) break
s = next
if (++guard > 4096) break
} }
} }
// Try to find a direct local binding in the current ancestry (without invoking name resolution that may prefer fields) // Try to find a direct local binding in the current ancestry (without invoking name resolution that may prefer fields)
run {
var s: Scope? = scope var s: Scope? = scope
var guard = 0
while (s != null) { while (s != null) {
s.objects[name]?.let { return it } s.objects[name]?.let { return it }
s = s.parent val next = s.parent
if (next === s) break
s = next
if (++guard > 4096) break
}
} }
// Fallback to standard name lookup (locals or closure chain) if the slot owner changed across suspension // Fallback to standard name lookup (locals or closure chain) if the slot owner changed across suspension
scope[name]?.let { return it } scope[name]?.let { return it }
@ -1413,16 +1432,26 @@ class FastLocalVarRef(
// Try per-frame local binding maps in the ancestry first // Try per-frame local binding maps in the ancestry first
run { run {
var s: Scope? = scope var s: Scope? = scope
var guard = 0
while (s != null) { while (s != null) {
s.localBindings[name]?.let { return it.value } s.localBindings[name]?.let { return it.value }
s = s.parent val next = s.parent
if (next === s) break
s = next
if (++guard > 4096) break
} }
} }
// Try to find a direct local binding in the current ancestry first // Try to find a direct local binding in the current ancestry first
run {
var s: Scope? = scope var s: Scope? = scope
var guard = 0
while (s != null) { while (s != null) {
s.objects[name]?.let { return it.value } s.objects[name]?.let { return it.value }
s = s.parent val next = s.parent
if (next === s) break
s = next
if (++guard > 4096) break
}
} }
// Fallback to standard name lookup (locals or closure chain) // Fallback to standard name lookup (locals or closure chain)
scope[name]?.let { return it.value } scope[name]?.let { return it.value }
@ -1443,6 +1472,7 @@ class FastLocalVarRef(
// Try per-frame local binding maps in the ancestry first // Try per-frame local binding maps in the ancestry first
run { run {
var s: Scope? = scope var s: Scope? = scope
var guard = 0
while (s != null) { while (s != null) {
val rec = s.localBindings[name] val rec = s.localBindings[name]
if (rec != null) { if (rec != null) {
@ -1450,7 +1480,10 @@ class FastLocalVarRef(
rec.value = newValue rec.value = newValue
return return
} }
s = s.parent val next = s.parent
if (next === s) break
s = next
if (++guard > 4096) break
} }
} }
// Fallback to standard name lookup // Fallback to standard name lookup

View File

@ -4056,4 +4056,18 @@ class ScriptTest {
""".trimIndent() """.trimIndent()
) )
} }
@Test
fun testHangOnPrintlnInMethods() = runTest {
eval("""
class T(someList) {
fun f() {
val x = [...someList]
println(x)
}
}
T([1,2]).f()
""")
}
} }