Add EfficientIterables.md documentation for Kotlin interop. Optimize ObjInt operations by integrating value caching and updating number operations. Introduce high-performance enumerate method for ObjList, ObjSet, ObjRange, and custom iterables. Update compiler and loop handling for improved enumeration logic.

This commit is contained in:
sergeych committed 2025-12-23 08:33:34 +01:00
1 parent 5f3a54d08f
commit 3f235878c0
12 files changed
+220 -77

No files matched your search

@@ -2040,7 +2040,7 @@ class Compiler(
var breakCaught = false
if (size > 0) {
var current = runCatching { sourceObj.getAt(forContext, ObjInt(0)) }
var current = runCatching { sourceObj.getAt(forContext, ObjInt.of(0)) }
.getOrElse {
throw ScriptError(
tOp.pos,
@@ -2065,7 +2065,7 @@ class Compiler(
throw lbe
}
if (++index >= size) break
current = sourceObj.getAt(forContext, ObjInt(index.toLong()))
current = sourceObj.getAt(forContext, ObjInt.of(index.toLong()))
}
}
if (!breakCaught && elseStatement != null) {
@@ -2087,7 +2087,7 @@ class Compiler(
var result: Obj = ObjVoid
if (catchBreak) {
for (i in start..<end) {
loopVar.value = ObjInt(i)
loopVar.value = ObjInt.of(i)
try {
result = body.execute(forScope)
} catch (lbe: LoopBreakContinueException) {
@@ -2100,7 +2100,7 @@ class Compiler(
}
} else {
for (i in start..<end) {
loopVar.value = ObjInt(i)
loopVar.value = ObjInt.of(i)
result = body.execute(forScope)
}
}
@@ -2112,38 +2112,33 @@ class Compiler(
body: Statement, elseStatement: Statement?, label: String?,
catchBreak: Boolean
): Obj {
val iterObj = sourceObj.invokeInstanceMethod(forScope, "iterator")
var result: Obj = ObjVoid
var completedNaturally = false
try {
while (iterObj.invokeInstanceMethod(forScope, "hasNext").toBool()) {
if (catchBreak)
try {
loopVar.value = iterObj.invokeInstanceMethod(forScope, "next")
result = body.execute(forScope)
} catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) {
if (lbe.doContinue) continue
// premature finish, will trigger cancel in finally
return lbe.result
}
throw lbe
}
else {
loopVar.value = iterObj.invokeInstanceMethod(forScope, "next")
var breakCaught = false
sourceObj.enumerate(forScope) { item ->
loopVar.value = item
if (catchBreak) {
try {
result = body.execute(forScope)
true
} catch (lbe: LoopBreakContinueException) {
if (lbe.label == label || lbe.label == null) {
if (lbe.doContinue) true
else {
result = lbe.result
breakCaught = true
false
}
} else
throw lbe
}
}
completedNaturally = true
return elseStatement?.execute(forScope) ?: result
} finally {
if (!completedNaturally) {
// Best-effort cancellation on premature termination
runCatching {
iterObj.invokeInstanceMethod(forScope, "cancelIteration") { ObjVoid }
}
} else {
result = body.execute(forScope)
true
}
}
return if (!breakCaught && elseStatement != null) {
elseStatement.execute(forScope)
} else result
}
@Suppress("UNUSED_VARIABLE")
@@ -127,6 +127,40 @@ open class Obj {
return invokeInstanceMethod(scope, "contains", other).toBool()
}
/**
* Call [callback] for each element of this obj considering it provides [Iterator]
* methods `hasNext` and `next`.
*
* IF callback returns false, iteration is stopped.
*/
open suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
val iterator = invokeInstanceMethod(scope, "iterator")
val hasNext = iterator.getInstanceMethod(scope, "hasNext")
val next = iterator.getInstanceMethod(scope, "next")
var closeIt = false
try {
while (hasNext.invoke(scope, iterator).toBool()) {
val nextValue = next.invoke(scope, iterator)
val shouldContinue = try {
callback(nextValue)
} catch (e: Exception) {
// iteration aborted due to exception in callback
closeIt = true
throw e
}
if (!shouldContinue) {
closeIt = true
break
}
}
} finally {
if (closeIt) {
// Best-effort cancel on premature termination
iterator.invokeInstanceMethod(scope, "cancelIteration") { ObjVoid }
}
}
}
/**
* Default toString implementation:
*
@@ -444,8 +478,8 @@ open class Obj {
is Obj -> obj
is Double -> ObjReal(obj)
is Float -> ObjReal(obj.toDouble())
is Int -> ObjInt(obj.toLong())
is Long -> ObjInt(obj)
is Int -> ObjInt.of(obj.toLong())
is Long -> ObjInt.of(obj)
is String -> ObjString(obj)
is CharSequence -> ObjString(obj.toString())
is Boolean -> ObjBool(obj)
@@ -45,11 +45,11 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu
}
override suspend fun incrementAndGet(scope: Scope): Obj {
return ObjInt(value + 1)
return of(value + 1)
}
override suspend fun decrementAndGet(scope: Scope): Obj {
return ObjInt(value - 1)
return of(value - 1)
}
override suspend fun compareTo(scope: Scope, other: Obj): Int {
@@ -63,29 +63,29 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu
override suspend fun plus(scope: Scope, other: Obj): Obj =
if (other is ObjInt)
ObjInt(this.value + other.value)
of(this.value + other.value)
else
ObjReal(this.doubleValue + other.toDouble())
override suspend fun minus(scope: Scope, other: Obj): Obj =
if (other is ObjInt)
ObjInt(this.value - other.value)
of(this.value - other.value)
else
ObjReal(this.doubleValue - other.toDouble())
override suspend fun mul(scope: Scope, other: Obj): Obj =
if (other is ObjInt) {
ObjInt(this.value * other.value)
of(this.value * other.value)
} else ObjReal(this.value * other.toDouble())
override suspend fun div(scope: Scope, other: Obj): Obj =
if (other is ObjInt)
ObjInt(this.value / other.value)
of(this.value / other.value)
else ObjReal(this.value / other.toDouble())
override suspend fun mod(scope: Scope, other: Obj): Obj =
if (other is ObjInt)
ObjInt(this.value % other.value)
of(this.value % other.value)
else ObjReal(this.value.toDouble() % other.toDouble())
/**
@@ -158,8 +158,15 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu
}
companion object {
val Zero = ObjInt(0, true)
val One = ObjInt(1, true)
private val cache = Array(256) { ObjInt((it - 128).toLong(), true) }
fun of(value: Long): ObjInt {
return if (value in -128L..127L) cache[(value + 128).toInt()]
else ObjInt(value)
}
val Zero = of(0)
val One = of(1)
val type = object : ObjClass("Int") {
override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj =
when (lynonType) {
@@ -178,4 +185,5 @@ class ObjInt(val value: Long, override val isConst: Boolean = false) : Obj(), Nu
}
}
fun Int.toObj() = ObjInt(this.toLong())
fun Int.toObj() = ObjInt.of(this.toLong())
fun Long.toObj() = ObjInt.of(this)
@@ -76,36 +76,3 @@ fun Obj.toFlow(scope: Scope): Flow<Obj> = flow {
}
}
/**
* Call [callback] for each element of this obj considering it provides [Iterator]
* methods `hasNext` and `next`.
*
* IF callback returns false, iteration is stopped.
*/
suspend fun Obj.enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
val iterator = invokeInstanceMethod(scope, "iterator")
val hasNext = iterator.getInstanceMethod(scope, "hasNext")
val next = iterator.getInstanceMethod(scope, "next")
var closeIt = false
try {
while (hasNext.invoke(scope, iterator).toBool()) {
val nextValue = next.invoke(scope, iterator)
val shouldContinue = try {
callback(nextValue)
} catch (e: Exception) {
// iteration aborted due to exception in callback
closeIt = true
throw e
}
if (!shouldContinue) {
closeIt = true
break
}
}
} finally {
if (closeIt) {
// Best-effort cancel on premature termination
iterator.invokeInstanceMethod(scope, "cancelIteration") { ObjVoid }
}
}
}
@@ -140,6 +140,12 @@ class ObjList(val list: MutableList<Obj> = mutableListOf()) : Obj() {
return list.contains(other)
}
override suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
for (item in list) {
if (!callback(item)) break
}
}
override val objClass: ObjClass
get() = type
@@ -114,6 +114,36 @@ class ObjRange(val start: Obj?, val end: Obj?, val isEndInclusive: Boolean) : Ob
start is ObjChar && end is ObjChar
}
override suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
if (isIntRange) {
val s = (start as ObjInt).value
val e = (end as ObjInt).value
if (isEndInclusive) {
for (i in s..e) {
if (!callback(ObjInt.of(i))) break
}
} else {
for (i in s..<e) {
if (!callback(ObjInt.of(i))) break
}
}
} else if (isCharRange) {
val s = (start as ObjChar).value
val e = (end as ObjChar).value
if (isEndInclusive) {
for (c in s..e) {
if (!callback(ObjChar(c))) break
}
} else {
for (c in s..<e) {
if (!callback(ObjChar(c))) break
}
}
} else {
super.enumerate(scope, callback)
}
}
override suspend fun compareTo(scope: Scope, other: Obj): Int {
return (other as? ObjRange)?.let {
if( start == other.start && end == other.end ) 0 else -1
@@ -34,6 +34,12 @@ class ObjSet(val set: MutableSet<Obj> = mutableSetOf()) : Obj() {
return set.contains(other)
}
override suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
for (item in set) {
if (!callback(item)) break
}
}
override suspend fun plus(scope: Scope, other: Obj): Obj {
return ObjSet(
if (other is ObjSet)