Do not transform state-machine when inlining
Sometimes, state-machine, generated in inline functions with crossinline parameter, is transformed, since all usages should be renamed. However, this is wrong: in this case, we will have state-machine inside state-machine. This fix addresses the issue. #KT-25893 Fixed
This commit is contained in:
+17
-127
@@ -9,8 +9,8 @@ import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.coroutines.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.coroutines.isCoroutineSuperClass
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.CoroutineTransformer
|
||||
import org.jetbrains.kotlin.codegen.serialization.JvmCodegenStringTable
|
||||
import org.jetbrains.kotlin.codegen.writeKotlinMetadata
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
@@ -21,10 +21,8 @@ import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
|
||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
@@ -52,6 +50,7 @@ class AnonymousObjectTransformer(
|
||||
val classBuilder = createRemappingClassBuilderViaFactory(inliningContext)
|
||||
val methodsToTransform = ArrayList<MethodNode>()
|
||||
val metadataReader = ReadKotlinClassHeaderAnnotationVisitor()
|
||||
lateinit var superClassName: String
|
||||
|
||||
createClassReader().accept(object : ClassVisitor(API, classBuilder.visitor) {
|
||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
|
||||
@@ -59,6 +58,7 @@ class AnonymousObjectTransformer(
|
||||
if (languageVersionSettings.isCoroutineSuperClass(superName)) {
|
||||
inliningContext.isContinuation = true
|
||||
}
|
||||
superClassName = superName
|
||||
}
|
||||
|
||||
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
|
||||
@@ -133,44 +133,23 @@ class AnonymousObjectTransformer(
|
||||
constructor!!, allCapturedParamBuilder, constructorParamBuilder,transformationInfo, parentRemapper
|
||||
)
|
||||
|
||||
val crossinlineSuspendElement = capturedCrossinlineSuspendElement()
|
||||
val capturesCrossinlineSuspend = (!inliningContext.isInliningLambda || inliningContext.isContinuation) &&
|
||||
crossinlineSuspendElement != null
|
||||
|
||||
val deferringMethods = ArrayList<DeferredMethodVisitor>()
|
||||
|
||||
generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, parentRemapper, additionalFakeParams)
|
||||
|
||||
val isLambdaAlreadyGeneratedAndNotGoingToBeInlined = transformationInfo.oldClassName.contains("\$\$special\$\$inlined")
|
||||
|
||||
val hasLambdasToInline =
|
||||
((parentRemapper is RegeneratedLambdaFieldRemapper) && parentRemapper.recapturedLambdas.isNotEmpty()) || transformationInfo.capturedLambdasToInline.isNotEmpty()
|
||||
|
||||
val coroutineTransformer = CoroutineTransformer(
|
||||
inliningContext,
|
||||
classBuilder,
|
||||
sourceInfo,
|
||||
methodsToTransform,
|
||||
superClassName
|
||||
)
|
||||
for (next in methodsToTransform) {
|
||||
// Generate state machine for
|
||||
// 1) doResume method of suspend lambda
|
||||
// 2) Suspend named function
|
||||
// Iff it captures crossinline suspend lambda
|
||||
val generateStateMachineForLambda =
|
||||
languageVersionSettings.isResumeImplMethodName(next.name) && capturesCrossinlineSuspend && inliningContext.isContinuation &&
|
||||
!isLambdaAlreadyGeneratedAndNotGoingToBeInlined && hasLambdasToInline
|
||||
val continuationClassName = findFakeContinuationConstructorClassName(next)
|
||||
val generateStateMachineForNamedFunction =
|
||||
capturesCrossinlineSuspend && !inliningContext.isContinuation && continuationClassName != null
|
||||
|
||||
val deferringVisitor =
|
||||
if (crossinlineSuspendElement != null) {
|
||||
when {
|
||||
generateStateMachineForLambda -> newStateMachineForLambda(classBuilder, next, crossinlineSuspendElement)
|
||||
generateStateMachineForNamedFunction -> newStateMachineForNamedFunction(
|
||||
classBuilder,
|
||||
next,
|
||||
continuationClassName!!,
|
||||
crossinlineSuspendElement
|
||||
)
|
||||
else -> newMethod(classBuilder, next)
|
||||
}
|
||||
} else newMethod(classBuilder, next)
|
||||
when {
|
||||
coroutineTransformer.shouldTransform(next) -> coroutineTransformer.newMethod(next)
|
||||
else -> newMethod(classBuilder, next)
|
||||
}
|
||||
val funResult = inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false)
|
||||
|
||||
val returnType = Type.getReturnType(next.desc)
|
||||
@@ -185,29 +164,9 @@ class AnonymousObjectTransformer(
|
||||
}
|
||||
|
||||
deferringMethods.forEach { method ->
|
||||
replaceFakeContinuationsWithRealOnes(
|
||||
method.intermediate,
|
||||
if (!inliningContext.isContinuation)
|
||||
getLastParameterIndex(method.intermediate.desc, method.intermediate.access)
|
||||
else 0
|
||||
)
|
||||
coroutineTransformer.replaceFakesWithReals(method.intermediate)
|
||||
removeFinallyMarkers(method.intermediate)
|
||||
method.visitEnd()
|
||||
|
||||
// During regeneration of named suspend functions, which capture crossinline suspend lambda, we need to spill the variables
|
||||
// into continuation object.
|
||||
// In order to do this, we reuse class builder, which regenerates continuation object.
|
||||
if (capturesCrossinlineSuspend &&
|
||||
!inliningContext.isContinuation &&
|
||||
inliningContext is RegeneratedClassContext
|
||||
) {
|
||||
val continuationClassName = findFakeContinuationConstructorClassName(method.intermediate)
|
||||
if (continuationClassName != null) {
|
||||
inliningContext.continuationBuilders
|
||||
.remove(continuationClassName)
|
||||
?.let(ClassBuilder::done)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SourceMapper.flushToClassBuilder(sourceMapper, classBuilder)
|
||||
@@ -226,8 +185,7 @@ class AnonymousObjectTransformer(
|
||||
writeOuterInfo(visitor)
|
||||
|
||||
if (continuationClassName == transformationInfo.oldClassName) {
|
||||
assert(inliningContext.parent?.parent is RegeneratedClassContext)
|
||||
(inliningContext.parent?.parent as RegeneratedClassContext).continuationBuilders[continuationClassName] = classBuilder
|
||||
coroutineTransformer.registerClassBuilder(continuationClassName)
|
||||
} else {
|
||||
classBuilder.done()
|
||||
}
|
||||
@@ -235,10 +193,6 @@ class AnonymousObjectTransformer(
|
||||
return transformationResult
|
||||
}
|
||||
|
||||
private fun capturedCrossinlineSuspendElement(): KtExpression? = inliningContext.expressionMap.values.find { lambda ->
|
||||
lambda is PsiExpressionLambda && lambda.isCrossInline && lambda.invokeMethodDescriptor.isSuspend
|
||||
}?.cast<PsiExpressionLambda>()?.functionWithBodyOrCallableReference
|
||||
|
||||
private fun writeTransformedMetadata(header: KotlinClassHeader, classBuilder: ClassBuilder) {
|
||||
writeKotlinMetadata(classBuilder, state, header.kind, header.extraInt) action@ { av ->
|
||||
val (newProto, newStringTable) = transformMetadata(header) ?: run {
|
||||
@@ -447,63 +401,6 @@ class AnonymousObjectTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
private fun newStateMachineForLambda(builder: ClassBuilder, original: MethodNode, element: KtExpression): DeferredMethodVisitor {
|
||||
return DeferredMethodVisitor(
|
||||
MethodNode(
|
||||
original.access, original.name, original.desc, original.signature,
|
||||
ArrayUtil.toStringArray(original.exceptions)
|
||||
)
|
||||
) {
|
||||
CoroutineTransformerMethodVisitor(
|
||||
builder.newMethod(
|
||||
NO_ORIGIN, original.access, original.name, original.desc, original.signature,
|
||||
ArrayUtil.toStringArray(original.exceptions)
|
||||
), original.access, original.name, original.desc, null, null,
|
||||
obtainClassBuilderForCoroutineState = { builder },
|
||||
element = element,
|
||||
diagnostics = state.diagnostics,
|
||||
languageVersionSettings = languageVersionSettings,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = builder.thisName,
|
||||
isForNamedFunction = false,
|
||||
sourceFile = sourceInfo ?: "",
|
||||
isCrossinlineLambda = inliningContext.isContinuation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun newStateMachineForNamedFunction(
|
||||
builder: ClassBuilder,
|
||||
original: MethodNode,
|
||||
continuationClassName: String,
|
||||
element: KtExpression
|
||||
): DeferredMethodVisitor {
|
||||
assert(inliningContext is RegeneratedClassContext)
|
||||
return DeferredMethodVisitor(
|
||||
MethodNode(
|
||||
original.access, original.name, original.desc, original.signature,
|
||||
ArrayUtil.toStringArray(original.exceptions)
|
||||
)
|
||||
) {
|
||||
CoroutineTransformerMethodVisitor(
|
||||
builder.newMethod(
|
||||
NO_ORIGIN, original.access, original.name, original.desc, original.signature,
|
||||
ArrayUtil.toStringArray(original.exceptions)
|
||||
), original.access, original.name, original.desc, null, null,
|
||||
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
|
||||
element = element,
|
||||
diagnostics = state.diagnostics,
|
||||
languageVersionSettings = languageVersionSettings,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = builder.thisName,
|
||||
isForNamedFunction = true,
|
||||
needDispatchReceiver = true,
|
||||
internalNameForDispatchReceiver = builder.thisName,
|
||||
sourceFile = sourceInfo ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractParametersMappingAndPatchConstructor(
|
||||
constructor: MethodNode,
|
||||
capturedParamBuilder: ParametersBuilder,
|
||||
@@ -649,10 +546,3 @@ class AnonymousObjectTransformer(
|
||||
private fun isFirstDeclSiteLambdaFieldRemapper(parentRemapper: FieldRemapper): Boolean =
|
||||
parentRemapper !is RegeneratedLambdaFieldRemapper && parentRemapper !is InlinedLambdaRemapper
|
||||
}
|
||||
|
||||
internal fun findFakeContinuationConstructorClassName(node: MethodNode): String? {
|
||||
val marker = node.instructions.asSequence().firstOrNull(::isBeforeFakeContinuationConstructorCallMarker) ?: return null
|
||||
val new = marker.next
|
||||
assert(new?.opcode == Opcodes.NEW)
|
||||
return (new as TypeInsnNode).desc
|
||||
}
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* 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.
|
||||
* Copyright 2010-2018 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
class RootInliningContext(
|
||||
expressionMap: Map<Int, LambdaInfo>,
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.coroutines.continuationAsmType
|
||||
import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
|
||||
import org.jetbrains.kotlin.codegen.inline.FieldRemapper.Companion.foldName
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.CoroutineTransformer
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer
|
||||
@@ -182,7 +183,7 @@ class MethodInliner(
|
||||
val transformer = transformationInfo!!.createTransformer(
|
||||
childInliningContext,
|
||||
isSameModule,
|
||||
findFakeContinuationConstructorClassName(node)
|
||||
CoroutineTransformer.findFakeContinuationConstructorClassName(node)
|
||||
)
|
||||
|
||||
val transformResult = transformer.doTransform(nodeRemapper)
|
||||
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline.coroutines
|
||||
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.coroutines.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
||||
|
||||
class CoroutineTransformer(
|
||||
private val inliningContext: InliningContext,
|
||||
private val classBuilder: ClassBuilder,
|
||||
private val sourceFile: String?,
|
||||
private val methods: List<MethodNode>,
|
||||
private val superClassName: String
|
||||
) {
|
||||
private val state = inliningContext.state
|
||||
|
||||
fun shouldTransform(node: MethodNode): Boolean {
|
||||
if (isContinuationNotLambda()) return false
|
||||
val crossinlineSuspend = crossinlineSuspend() ?: return false
|
||||
if (inliningContext.isInliningLambda && !inliningContext.isContinuation) return false
|
||||
return when {
|
||||
isSuspendFunction(node) -> true
|
||||
isSuspendLambda(node) -> {
|
||||
if (isStateMachine(node)) return false
|
||||
val functionDescriptor =
|
||||
crossinlineSuspend.invokeMethodDescriptor.containingDeclaration as? FunctionDescriptor ?: return true
|
||||
!functionDescriptor.isInline
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun isContinuationNotLambda(): Boolean = inliningContext.isContinuation &&
|
||||
if (state.languageVersionSettings.isReleaseCoroutines()) superClassName.endsWith("ContinuationImpl")
|
||||
else methods.any { it.name == "getLabel" }
|
||||
|
||||
private fun crossinlineSuspend(): PsiExpressionLambda? = inliningContext.expressionMap.values.find {
|
||||
it is PsiExpressionLambda && it.isCrossInline && it.invokeMethodDescriptor.isSuspend
|
||||
}?.cast()
|
||||
|
||||
private fun isStateMachine(node: MethodNode): Boolean =
|
||||
node.instructions.asSequence().any { it.opcode == Opcodes.INVOKESTATIC && (it as MethodInsnNode).name == "getCOROUTINE_SUSPENDED" }
|
||||
|
||||
private fun isSuspendLambda(node: MethodNode) = isResumeImpl(node)
|
||||
|
||||
fun newMethod(node: MethodNode): DeferredMethodVisitor {
|
||||
val element = crossinlineSuspend()?.functionWithBodyOrCallableReference.sure {
|
||||
"crossinline lambda should have element"
|
||||
}
|
||||
return when {
|
||||
isResumeImpl(node) -> {
|
||||
assert(!isStateMachine(node)) {
|
||||
"Inlining/transforming state-machine"
|
||||
}
|
||||
newStateMachineForLambda(node, element)
|
||||
}
|
||||
isSuspendFunction(node) -> newStateMachineForNamedFunction(node, element)
|
||||
else -> error("no need to generate state maching for ${node.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun isResumeImpl(node: MethodNode): Boolean =
|
||||
state.languageVersionSettings.isResumeImplMethodName(node.name) &&
|
||||
inliningContext.isContinuation
|
||||
|
||||
private fun isSuspendFunction(node: MethodNode): Boolean = findFakeContinuationConstructorClassName(node) != null
|
||||
|
||||
private fun newStateMachineForLambda(node: MethodNode, element: KtElement): DeferredMethodVisitor {
|
||||
return DeferredMethodVisitor(
|
||||
MethodNode(
|
||||
node.access, node.name, node.desc, node.signature,
|
||||
ArrayUtil.toStringArray(node.exceptions)
|
||||
)
|
||||
) {
|
||||
CoroutineTransformerMethodVisitor(
|
||||
classBuilder.newMethod(
|
||||
JvmDeclarationOrigin.NO_ORIGIN,
|
||||
node.access,
|
||||
node.name,
|
||||
node.desc,
|
||||
node.signature,
|
||||
ArrayUtil.toStringArray(node.exceptions)
|
||||
), node.access, node.name, node.desc, null, null,
|
||||
obtainClassBuilderForCoroutineState = { classBuilder },
|
||||
element = element,
|
||||
diagnostics = state.diagnostics,
|
||||
languageVersionSettings = state.languageVersionSettings,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
isForNamedFunction = false,
|
||||
sourceFile = sourceFile ?: "",
|
||||
isCrossinlineLambda = inliningContext.isContinuation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun newStateMachineForNamedFunction(node: MethodNode, element: KtElement): DeferredMethodVisitor {
|
||||
val continuationClassName = findFakeContinuationConstructorClassName(node)
|
||||
assert(inliningContext is RegeneratedClassContext)
|
||||
return DeferredMethodVisitor(
|
||||
MethodNode(
|
||||
node.access, node.name, node.desc, node.signature,
|
||||
ArrayUtil.toStringArray(node.exceptions)
|
||||
)
|
||||
) {
|
||||
CoroutineTransformerMethodVisitor(
|
||||
classBuilder.newMethod(
|
||||
JvmDeclarationOrigin.NO_ORIGIN, node.access, node.name, node.desc, node.signature,
|
||||
ArrayUtil.toStringArray(node.exceptions)
|
||||
), node.access, node.name, node.desc, null, null,
|
||||
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
|
||||
element = element,
|
||||
diagnostics = state.diagnostics,
|
||||
languageVersionSettings = state.languageVersionSettings,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
isForNamedFunction = true,
|
||||
needDispatchReceiver = true,
|
||||
internalNameForDispatchReceiver = classBuilder.thisName,
|
||||
sourceFile = sourceFile ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun replaceFakesWithReals(node: MethodNode) {
|
||||
findFakeContinuationConstructorClassName(node)?.let(::unregisterClassBuilder)?.let(ClassBuilder::done)
|
||||
replaceFakeContinuationsWithRealOnes(
|
||||
node, if (!inliningContext.isContinuation) getLastParameterIndex(node.desc, node.access) else 0
|
||||
)
|
||||
}
|
||||
|
||||
fun registerClassBuilder(continuationClassName: String) {
|
||||
val context = inliningContext.parent?.parent as? RegeneratedClassContext ?: error("incorrect context")
|
||||
context.continuationBuilders[continuationClassName] = classBuilder
|
||||
}
|
||||
|
||||
fun unregisterClassBuilder(continuationClassName: String) =
|
||||
(inliningContext as RegeneratedClassContext).continuationBuilders.remove(continuationClassName)
|
||||
|
||||
companion object {
|
||||
fun findFakeContinuationConstructorClassName(node: MethodNode): String? {
|
||||
val marker = node.instructions.asSequence().firstOrNull(::isBeforeFakeContinuationConstructorCallMarker) ?: return null
|
||||
val new = marker.next
|
||||
assert(new?.opcode == Opcodes.NEW)
|
||||
return (new as TypeInsnNode).desc
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// WITH_RUNTIME
|
||||
// LANGUAGE_VERSION: 1.2
|
||||
// IGNORE_BACKEND: JVM
|
||||
// WITH_COROUTINES
|
||||
import helpers.*
|
||||
import kotlin.coroutines.experimental.*
|
||||
import kotlin.coroutines.experimental.intrinsics.*
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
interface SuspendRunnable {
|
||||
suspend fun run(): String
|
||||
}
|
||||
|
||||
inline fun inlineMe(crossinline c: suspend () -> String) = object : SuspendRunnable {
|
||||
override suspend fun run() = c()
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var res = "FAIL"
|
||||
builder {
|
||||
res = inlineMe { "OK" }.run()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Test for continuation of 'run' transformation.
|
||||
// Since continuation is not suspend lambda, it should not have state-machine.
|
||||
// @CrossinlineSuspendContinuation_1_2Kt$box$1$doResume$$inlined$inlineMe$1$1.class:
|
||||
// 0 TABLESWITCH
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// WITH_RUNTIME
|
||||
// LANGUAGE_VERSION: 1.3
|
||||
// WITH_COROUTINES
|
||||
import helpers.*
|
||||
import kotlin.coroutines.*
|
||||
import kotlin.coroutines.intrinsics.*
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
interface SuspendRunnable {
|
||||
suspend fun run(): String
|
||||
}
|
||||
|
||||
inline fun inlineMe(crossinline c: suspend () -> String) = object : SuspendRunnable {
|
||||
override suspend fun run() = c()
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var res = "FAIL"
|
||||
builder {
|
||||
res = inlineMe { "OK" }.run()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Test for continuation of 'run' transformation.
|
||||
// Since continuation is not suspend lambda, it should not have state-machine
|
||||
// @CrossinlineSuspendContinuation_1_3Kt$box$1$invokeSuspend$$inlined$inlineMe$1$1.class:
|
||||
// 0 TABLESWITCH
|
||||
@@ -0,0 +1,13 @@
|
||||
class Handler(val func: suspend (Any) -> Unit)
|
||||
|
||||
inline fun createHandler(crossinline handler: suspend (Any) -> Unit): Handler {
|
||||
return Handler({ handler.invoke(it) })
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
createHandler({
|
||||
if (it !is String) {}
|
||||
})
|
||||
}
|
||||
|
||||
// 2 TABLESWITCH
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
fun <T> builder(c: suspend () -> T): T = TODO()
|
||||
|
||||
class Test {
|
||||
fun doWork() {
|
||||
builder {
|
||||
execute {
|
||||
getData { getSomeString() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun execute(crossinline action: suspend () -> Unit) {
|
||||
builder { action() }
|
||||
}
|
||||
|
||||
private suspend fun <T> getData(dataProvider: suspend () -> T): T = builder { dataProvider() }
|
||||
|
||||
private suspend fun getSomeString(): String {
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
// 4 TABLESWITCH
|
||||
@@ -1090,6 +1090,16 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/coroutines"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("crossinlineSuspendContinuation_1_2.kt")
|
||||
public void testCrossinlineSuspendContinuation_1_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/coroutines/crossinlineSuspendContinuation_1_2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("crossinlineSuspendContinuation_1_3.kt")
|
||||
public void testCrossinlineSuspendContinuation_1_3() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/coroutines/crossinlineSuspendContinuation_1_3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("doNotReassignContinuation.kt")
|
||||
public void testDoNotReassignContinuation() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/coroutines/doNotReassignContinuation.kt");
|
||||
@@ -1297,6 +1307,29 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInVarStore.kt", "kotlin.coroutines");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/coroutines/stateMachine")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class StateMachine extends AbstractBytecodeTextTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInStateMachine() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/coroutines/stateMachine"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("kt25893.kt")
|
||||
public void testKt25893() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/coroutines/stateMachine/kt25893.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withTypeParameter.kt")
|
||||
public void testWithTypeParameter() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/coroutines/stateMachine/withTypeParameter.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination")
|
||||
|
||||
Reference in New Issue
Block a user