94 lines
2.8 KiB
Kotlin
94 lines
2.8 KiB
Kotlin
/*
|
|
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* JVM stress tests for scope frame pooling (deep nesting and recursion).
|
|
*/
|
|
|
|
import kotlinx.coroutines.runBlocking
|
|
import net.sergeych.lyng.PerfFlags
|
|
import net.sergeych.lyng.Scope
|
|
import net.sergeych.lyng.obj.ObjInt
|
|
import kotlin.test.Test
|
|
import kotlin.test.assertEquals
|
|
|
|
class DeepPoolingStressJvmTest {
|
|
@Test
|
|
fun deepNestedCalls_noLeak_and_correct_with_and_without_pooling() = runBlocking {
|
|
val depth = 200
|
|
val script = """
|
|
fun f0(x) { x + 1 }
|
|
fun f1(x) { f0(x) + 1 }
|
|
fun f2(x) { f1(x) + 1 }
|
|
fun f3(x) { f2(x) + 1 }
|
|
fun f4(x) { f3(x) + 1 }
|
|
fun f5(x) { f4(x) + 1 }
|
|
|
|
fun chain(x, d) {
|
|
var i = 0
|
|
var s = x
|
|
while (i < d) {
|
|
// 5 nested calls per iteration
|
|
s = f5(s)
|
|
i = i + 1
|
|
}
|
|
s
|
|
}
|
|
chain(0, $depth)
|
|
""".trimIndent()
|
|
|
|
// Pool OFF
|
|
PerfFlags.SCOPE_POOL = false
|
|
val scope1 = Scope()
|
|
val r1 = (scope1.eval(script) as ObjInt).value
|
|
// Pool ON
|
|
PerfFlags.SCOPE_POOL = true
|
|
val scope2 = Scope()
|
|
val r2 = (scope2.eval(script) as ObjInt).value
|
|
// Each loop adds 6 (f0..f5 adds 6)
|
|
val expected = 6L * depth
|
|
assertEquals(expected, r1)
|
|
assertEquals(expected, r2)
|
|
// Reset
|
|
PerfFlags.SCOPE_POOL = false
|
|
}
|
|
|
|
@Test
|
|
fun recursion_factorial_correct_with_and_without_pooling() = runBlocking {
|
|
val n = 10
|
|
val script = """
|
|
fun fact(x) {
|
|
if (x <= 1) 1 else x * fact(x - 1)
|
|
}
|
|
fact($n)
|
|
""".trimIndent()
|
|
// OFF
|
|
PerfFlags.SCOPE_POOL = false
|
|
val scope1 = Scope()
|
|
val r1 = (scope1.eval(script) as ObjInt).value
|
|
// ON
|
|
PerfFlags.SCOPE_POOL = true
|
|
val scope2 = Scope()
|
|
val r2 = (scope2.eval(script) as ObjInt).value
|
|
// 10! = 3628800
|
|
val expected = 3628800L
|
|
assertEquals(expected, r1)
|
|
assertEquals(expected, r2)
|
|
PerfFlags.SCOPE_POOL = false
|
|
}
|
|
}
|