diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt index ecbbc732ab4..fffe5ad393e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt @@ -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() 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) { @@ -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() 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()?.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 -} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InliningContext.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InliningContext.kt index 0f095b87c2c..6ef45f69db1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InliningContext.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InliningContext.kt @@ -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, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt index ca541f42ba4..0825dfb4ea4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -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) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt new file mode 100644 index 00000000000..361f3329bbe --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt @@ -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, + 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 + } + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/coroutines/crossinlineSuspendContinuation_1_2.kt b/compiler/testData/codegen/bytecodeText/coroutines/crossinlineSuspendContinuation_1_2.kt new file mode 100644 index 00000000000..7258fc876f2 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/crossinlineSuspendContinuation_1_2.kt @@ -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 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/coroutines/crossinlineSuspendContinuation_1_3.kt b/compiler/testData/codegen/bytecodeText/coroutines/crossinlineSuspendContinuation_1_3.kt new file mode 100644 index 00000000000..c3c555ca259 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/crossinlineSuspendContinuation_1_3.kt @@ -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 \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/coroutines/stateMachine/kt25893.kt b/compiler/testData/codegen/bytecodeText/coroutines/stateMachine/kt25893.kt new file mode 100644 index 00000000000..e8b245b2843 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/stateMachine/kt25893.kt @@ -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) { + createHandler({ + if (it !is String) {} + }) +} + +// 2 TABLESWITCH \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/coroutines/stateMachine/withTypeParameter.kt b/compiler/testData/codegen/bytecodeText/coroutines/stateMachine/withTypeParameter.kt new file mode 100644 index 00000000000..9e2beafdbe8 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/stateMachine/withTypeParameter.kt @@ -0,0 +1,23 @@ +fun 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 getData(dataProvider: suspend () -> T): T = builder { dataProvider() } + + private suspend fun getSomeString(): String { + return "OK" + } +} + +// 4 TABLESWITCH \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 6c870322205..ed5d1fdb363 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -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")