Introduce CHECK_TAIL_CALL_OPTIMIZATION directive
This directive generates TailCallOptimizationChecker in package helpers. The check for tail call optimization is based on coroutine stack traces bug (feature?): when tail call optimization hits, the continuation object is not generated. Thus, there is no debug metadata for this suspend function. Consequently, the coroutines stack trace does not contain stack trace element for that function. This check is performed by TailCallOptimizationChecker. Since this is runtime check, unlike bytecode tests, it does not require test data adjustments on each codegen or inliner change. Since the check is based on debug metadata, which is JVM specific, there is not support for other backends yet.
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin
|
||||
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
|
||||
fun createTextForHelpers(isReleaseCoroutines: Boolean, checkStateMachine: Boolean): String {
|
||||
fun createTextForHelpers(isReleaseCoroutines: Boolean, checkStateMachine: Boolean, checkTailCallOptimization: Boolean): String {
|
||||
val coroutinesPackage =
|
||||
if (isReleaseCoroutines)
|
||||
DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME_RELEASE.asString()
|
||||
@@ -123,9 +123,45 @@ fun createTextForHelpers(isReleaseCoroutines: Boolean, checkStateMachine: Boolea
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
// TODO: Find a way to check for tail-call optimization on JS and Native
|
||||
val checkTailCallOptimizationString =
|
||||
"""
|
||||
class TailCallOptimizationCheckerClass {
|
||||
private val stackTrace = arrayListOf<StackTraceElement?>()
|
||||
|
||||
suspend fun saveStackTrace() = suspendCoroutineUninterceptedOrReturn<Unit> {
|
||||
saveStackTrace(it)
|
||||
}
|
||||
|
||||
fun saveStackTrace(c: Continuation<*>) {
|
||||
if (c !is CoroutineStackFrame) error("Continuation " + c + " is not subtype of CoroutineStackFrame")
|
||||
stackTrace.clear()
|
||||
var csf: CoroutineStackFrame? = c
|
||||
while (csf != null) {
|
||||
stackTrace.add(csf.getStackTraceElement())
|
||||
csf = csf.callerFrame
|
||||
}
|
||||
}
|
||||
|
||||
fun checkNoStateMachineIn(method: String) {
|
||||
stackTrace.find { it?.methodName == method }?.let { error("tail-call optimization miss: method at " + it + " has state-machine " +
|
||||
stackTrace.joinToString(separator = "\n")) }
|
||||
}
|
||||
|
||||
fun checkStateMachineIn(method: String) {
|
||||
stackTrace.find { it?.methodName == method } ?: error("tail-call optimization hit: method " + method + " has not state-machine " +
|
||||
stackTrace.joinToString(separator = "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
val TailCallOptimizationChecker = TailCallOptimizationCheckerClass()
|
||||
""".trimIndent()
|
||||
|
||||
return """
|
||||
|package helpers
|
||||
|import $coroutinesPackage.*
|
||||
|import $coroutinesPackage.intrinsics.*
|
||||
|${if (checkTailCallOptimization) "import $coroutinesPackage.jvm.internal.*" else ""}
|
||||
|
|
||||
|fun <T> handleResultContinuation(x: (T) -> Unit): Continuation<T> = object: Continuation<T> {
|
||||
| override val context = EmptyCoroutineContext
|
||||
@@ -149,5 +185,6 @@ fun createTextForHelpers(isReleaseCoroutines: Boolean, checkStateMachine: Boolea
|
||||
|}
|
||||
|
|
||||
|${if (checkStateMachine) checkStateMachineString else ""}
|
||||
|${if (checkTailCallOptimization) checkTailCallOptimizationString else ""}
|
||||
""".trimMargin()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -807,12 +807,15 @@ public class KotlinTestUtils {
|
||||
!isDirectiveDefined(expectedText, "!LANGUAGE: -ReleaseCoroutines");
|
||||
|
||||
boolean checkStateMachine = isDirectiveDefined(expectedText, "CHECK_STATE_MACHINE");
|
||||
boolean checkTailCallOptimization = isDirectiveDefined(expectedText, "CHECK_TAIL_CALL_OPTIMIZATION");
|
||||
|
||||
testFiles.add(factory.createFile(supportModule,
|
||||
"CoroutineUtil.kt",
|
||||
CoroutineTestUtilKt.createTextForHelpers(isReleaseCoroutines, checkStateMachine),
|
||||
directives
|
||||
));
|
||||
testFiles.add(
|
||||
factory.createFile(
|
||||
supportModule,
|
||||
"CoroutineUtil.kt",
|
||||
CoroutineTestUtilKt.createTextForHelpers(isReleaseCoroutines, checkStateMachine, checkTailCallOptimization),
|
||||
directives
|
||||
));
|
||||
}
|
||||
|
||||
return testFiles;
|
||||
|
||||
@@ -792,12 +792,15 @@ public class KotlinTestUtils {
|
||||
!isDirectiveDefined(expectedText, "!LANGUAGE: -ReleaseCoroutines");
|
||||
|
||||
boolean checkStateMachine = isDirectiveDefined(expectedText, "CHECK_STATE_MACHINE");
|
||||
boolean checkTailCallOptimization = isDirectiveDefined(expectedText, "CHECK_TAIL_CALL_OPTIMIZATION");
|
||||
|
||||
testFiles.add(factory.createFile(supportModule,
|
||||
"CoroutineUtil.kt",
|
||||
CoroutineTestUtilKt.createTextForHelpers(isReleaseCoroutines, checkStateMachine),
|
||||
directives
|
||||
));
|
||||
testFiles.add(
|
||||
factory.createFile(
|
||||
supportModule,
|
||||
"CoroutineUtil.kt",
|
||||
CoroutineTestUtilKt.createTextForHelpers(isReleaseCoroutines, checkStateMachine, checkTailCallOptimization),
|
||||
directives
|
||||
));
|
||||
}
|
||||
|
||||
return testFiles;
|
||||
|
||||
Reference in New Issue
Block a user