From 2dee5060225354406f3068fe738d6c3a4fa620e4 Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Tue, 2 Apr 2019 21:17:34 +0300 Subject: [PATCH] Generate suspend markers for inline and crossinline parameters if they are not inlined, but directly called. Previously, all inline and crossinline lambda calls were treated by codegen as if they are always going to be inlined. However, this is not always the case. Note, that we cannot generate these markers during codegen, since we can inline code with no suspension points, but the whole inlined code will become one giant suspension point. This, of course, breaks tail-call optimization and, hence, slows down cold streams. Because of that, we generate these markers, when we are sure, that they are not going to be inlined. The only place, in which we know that, is the inliner. During inlining of the inline function, we check, whether the parameter is inline or crossinline and whether it is not an inline lambda. If these checks pass, we generate the markers. Noinline parameters are already covered by the codegen. #KT-30706 Fixed #KT-26925 Fixed #KT-26418 Fixed --- .../jetbrains/kotlin/codegen/CallGenerator.kt | 5 +- .../inline/AnonymousObjectTransformer.kt | 3 +- .../kotlin/codegen/inline/InlineCodegen.kt | 23 ++- .../kotlin/codegen/inline/LambdaInfo.kt | 3 +- .../kotlin/codegen/inline/MethodInliner.kt | 29 ++- .../inline/coroutines/CoroutineTransformer.kt | 165 ++++++++++++++---- .../suspend/stateMachine/passLambda.kt | 150 ++++++++++++++++ .../BlackBoxInlineCodegenTestGenerated.java | 10 ++ ...otlinAgainstInlineKotlinTestGenerated.java | 10 ++ .../IrBlackBoxInlineCodegenTestGenerated.java | 10 ++ .../IrInlineSuspendTestsGenerated.java | 5 + .../InlineSuspendTestsGenerated.java | 10 ++ 12 files changed, 368 insertions(+), 55 deletions(-) create mode 100644 compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt index fba6b675eb5..2a135aef8a6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CallGenerator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-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. */ @@ -19,7 +19,8 @@ enum class ValueKind { METHOD_HANDLE_IN_DEFAULT, CAPTURED, DEFAULT_LAMBDA_CAPTURED_PARAMETER, - NON_INLINEABLE_CALLED_IN_SUSPEND + NON_INLINEABLE_ARGUMENT_FOR_INLINE_PARAMETER_CALLED_IN_SUSPEND, + NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER } interface CallGenerator { 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 cf351f6078c..356a0a5ecc3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt @@ -134,7 +134,8 @@ class AnonymousObjectTransformer( inliningContext, classBuilder, methodsToTransform, - superClassName + superClassName, + additionalFakeParams ) for (next in methodsToTransform) { val deferringVisitor = diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index 7a68d3837f2..300f23d0908 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.codegen.inline import com.intellij.psi.PsiElement import com.intellij.util.ArrayUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive @@ -409,8 +410,11 @@ abstract class InlineCodegen( info.remapValue = remappedValue } else { info = invocationParamBuilder.addNextValueParameter(jvmType, false, remappedValue, parameterIndex) - if (kind == ValueKind.NON_INLINEABLE_CALLED_IN_SUSPEND) { - info.functionalArgument = CrossinlineLambdaInSuspendContextAsNoInline + info.functionalArgument = when (kind) { + ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_PARAMETER_CALLED_IN_SUSPEND -> + NonInlineableArgumentForInlineableParameterCalledInSuspend(kotlinType?.isSuspendFunctionTypeOrSubtype == true) + ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER -> NonInlineableArgumentForInlineableSuspendParameter + else -> null } } @@ -804,15 +808,18 @@ class PsiInlineCodegen( } } else { val value = codegen.gen(argumentExpression) - putValueIfNeeded( - parameterType, - value, - if (isCallSiteIsSuspend(valueParameterDescriptor)) ValueKind.NON_INLINEABLE_CALLED_IN_SUSPEND else ValueKind.GENERAL, - parameterIndex - ) + val kind = when { + isCallSiteIsSuspend(valueParameterDescriptor) -> ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_PARAMETER_CALLED_IN_SUSPEND + isInlineSuspendParameter(valueParameterDescriptor) -> ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER + else -> ValueKind.GENERAL + } + putValueIfNeeded(parameterType, value, kind, parameterIndex) } } + private fun isInlineSuspendParameter(descriptor: ValueParameterDescriptor): Boolean = + functionDescriptor.isInline && !descriptor.isNoinline && descriptor.type.isSuspendFunctionTypeOrSubtype + private fun isCallSiteIsSuspend(descriptor: ValueParameterDescriptor): Boolean = state.bindingContext[CodegenBinding.CALL_SITE_IS_SUSPEND_FOR_CROSSINLINE_LAMBDA, descriptor] == true diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt index 48dc1cb66d2..91cd3b1be69 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt @@ -79,7 +79,8 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu } } -object CrossinlineLambdaInSuspendContextAsNoInline : FunctionalArgument +class NonInlineableArgumentForInlineableParameterCalledInSuspend(val isSuspend: Boolean) : FunctionalArgument +object NonInlineableArgumentForInlineableSuspendParameter : FunctionalArgument class DefaultLambda( override val lambdaClassType: Type, 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 29eec28e001..79e8f40fcaa 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -9,16 +9,14 @@ import org.jetbrains.kotlin.codegen.* 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.inline.coroutines.* import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer -import org.jetbrains.kotlin.codegen.optimization.common.ControlFlowGraph -import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence -import org.jetbrains.kotlin.codegen.optimization.common.asSequence -import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful +import org.jetbrains.kotlin.codegen.optimization.common.* import org.jetbrains.kotlin.codegen.optimization.fixStack.peek import org.jetbrains.kotlin.codegen.optimization.fixStack.top +import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor @@ -220,6 +218,9 @@ class MethodInliner( val info = invokeCall.functionalArgument if (info !is LambdaInfo) { + if (info == NonInlineableArgumentForInlineableSuspendParameter) { + super.visitMethodInsn(Opcodes.INVOKESTATIC, NOINLINE_CALL_MARKER, NOINLINE_CALL_MARKER, "()V", false) + } //noninlinable lambda super.visitMethodInsn(opcode, owner, name, desc, itf) return @@ -366,6 +367,8 @@ class MethodInliner( ReifiedTypeInliner.isNeedClassReificationMarker(MethodInsnNode(opcode, owner, name, desc, false)) ) { //we shouldn't process here content of inlining lambda it should be reified at external level except default lambdas + } else if (owner == NOINLINE_CALL_MARKER && name == NOINLINE_CALL_MARKER) { + // do not generate multiple markers on single invoke } else { super.visitMethodInsn(opcode, owner, name, desc, itf) } @@ -386,7 +389,21 @@ class MethodInliner( node.accept(lambdaInliner) - return resultNode + return surroundInvokesWithSuspendMarkersIfNeeded(resultNode) + } + + private fun surroundInvokesWithSuspendMarkersIfNeeded(node: MethodNode): MethodNode { + val markers = node.instructions.asSequence().filter { it.isNoinlineCallMarker() }.toList() + if (markers.isEmpty()) return node + val invokes = markers.map { it.next as MethodInsnNode } + node.instructions.removeAll(markers) + + val sourceFrames = MethodTransformer.analyze(inlineCallSiteInfo.ownerClassName, node, SourceInterpreter()) + val toSurround = invokes.mapNotNull { insn -> + findReceiverOfInvoke(sourceFrames[node.instructions.indexOf(insn)], insn)?.let { insn to it } + } + surroundInvokesWithSuspendMarkers(node, toSurround) + return node } private fun isDefaultLambdaWithReification(lambdaInfo: LambdaInfo) = 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 index ccb1f4503eb..ab5d5932a2c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt @@ -7,27 +7,34 @@ package org.jetbrains.kotlin.codegen.inline.coroutines import com.intellij.util.ArrayUtil import org.jetbrains.kotlin.codegen.ClassBuilder -import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor +import org.jetbrains.kotlin.codegen.TransformationMethodVisitor +import org.jetbrains.kotlin.codegen.coroutines.* import org.jetbrains.kotlin.codegen.coroutines.getLastParameterIndex -import org.jetbrains.kotlin.codegen.coroutines.isResumeImplMethodName import org.jetbrains.kotlin.codegen.coroutines.replaceFakeContinuationsWithRealOnes import org.jetbrains.kotlin.codegen.inline.* import org.jetbrains.kotlin.codegen.optimization.common.asSequence +import org.jetbrains.kotlin.codegen.optimization.common.findPreviousOrNull +import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer 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.org.objectweb.asm.MethodVisitor import org.jetbrains.org.objectweb.asm.Opcodes -import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode -import org.jetbrains.org.objectweb.asm.tree.MethodNode -import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode +import org.jetbrains.org.objectweb.asm.tree.* +import org.jetbrains.org.objectweb.asm.tree.analysis.Frame +import org.jetbrains.org.objectweb.asm.tree.analysis.SourceInterpreter +import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue + +const val NOINLINE_CALL_MARKER = "NOINLINE_CALL_MARKER" class CoroutineTransformer( private val inliningContext: InliningContext, private val classBuilder: ClassBuilder, private val methods: List, - private val superClassName: String + private val superClassName: String, + private val capturedParams: List ) { private val state = inliningContext.state @@ -94,22 +101,18 @@ class CoroutineTransformer( 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 + surroundNoinlineCallsWithMarkersIfNeeded( + node, + CoroutineTransformerMethodVisitor( + createNewMethodFrom(node), 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 + ) ) } } @@ -123,24 +126,48 @@ class CoroutineTransformer( 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 + surroundNoinlineCallsWithMarkersIfNeeded( + node, + CoroutineTransformerMethodVisitor( + createNewMethodFrom(node), 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 + ) ) } } + private fun surroundNoinlineCallsWithMarkersIfNeeded(node: MethodNode, delegate: MethodVisitor): MethodVisitor = + if (capturedParams.any { (it.functionalArgument as? NonInlineableArgumentForInlineableParameterCalledInSuspend)?.isSuspend == true }) + SurroundSuspendLambdaCallsWithSuspendMarkersMethodVisitor( + delegate, + node.access, + node.name, + node.desc, + classBuilder.thisName, + capturedParams + ) + else + delegate + + private fun createNewMethodFrom(node: MethodNode): MethodVisitor { + return classBuilder.newMethod( + JvmDeclarationOrigin.NO_ORIGIN, + node.access, + node.name, + node.desc, + node.signature, + ArrayUtil.toStringArray(node.exceptions) + ) + } + fun replaceFakesWithReals(node: MethodNode) { findFakeContinuationConstructorClassName(node)?.let(::unregisterClassBuilder)?.let(ClassBuilder::done) replaceFakeContinuationsWithRealOnes( @@ -164,4 +191,68 @@ class CoroutineTransformer( return (new as TypeInsnNode).desc } } -} \ No newline at end of file +} + +private class SurroundSuspendLambdaCallsWithSuspendMarkersMethodVisitor( + delegate: MethodVisitor, + access: Int, + name: String, + desc: String, + val thisName: String, + val capturedParams: List +) : TransformationMethodVisitor(delegate, access, name, desc, null, null) { + override fun performTransformations(methodNode: MethodNode) { + fun AbstractInsnNode.index() = methodNode.instructions.indexOf(this) + + val sourceFrames = MethodTransformer.analyze(thisName, methodNode, SourceInterpreter()) + + val noinlineInvokes = arrayListOf>() + + for (insn in methodNode.instructions.asSequence()) { + if (insn.opcode != Opcodes.INVOKEINTERFACE) continue + insn as MethodInsnNode + if (!isInvokeOnLambda(insn.owner, insn.name)) continue + val frame = sourceFrames[insn.index()] + val receiver = findReceiverOfInvoke(frame, insn).takeIf { it?.isNoinlineSuspendLambda(insn) == true } as? FieldInsnNode ?: continue + val aload = receiver.findPreviousOrNull { it.opcode != Opcodes.GETFIELD } ?: error("GETFIELD cannot be the first instruction") + assert(aload.opcode == Opcodes.ALOAD) { "Before GETFIELD there shall be ALOAD" } + noinlineInvokes.add(insn to aload) + } + + surroundInvokesWithSuspendMarkers(methodNode, noinlineInvokes) + } + + private fun AbstractInsnNode.isNoinlineSuspendLambda(invoke: MethodInsnNode): Boolean { + if (opcode != Opcodes.GETFIELD) return false + this as FieldInsnNode + if (owner != thisName || desc != "L${invoke.owner};") return false + val functionalArgument = capturedParams.find { it.newFieldName == name }?.functionalArgument ?: return false + return functionalArgument is NonInlineableArgumentForInlineableParameterCalledInSuspend && functionalArgument.isSuspend + } +} + +fun surroundInvokesWithSuspendMarkers( + methodNode: MethodNode, + noinlineInvokes: List> +) { + for ((invoke, aload) in noinlineInvokes) { + // Generate inline markers for stack transformation. It is required for local variables spilling. + methodNode.instructions.insertBefore(aload, withInstructionAdapter { + addInlineMarker(this, isStartNotEnd = true) + }) + methodNode.instructions.insertBefore(invoke, withInstructionAdapter { + addSuspendMarker(this, isStartNotEnd = true) + }) + methodNode.instructions.insert(invoke, withInstructionAdapter { + addSuspendMarker(this, isStartNotEnd = false) + addInlineMarker(this, isStartNotEnd = false) + }) + } +} + +// TODO: What to do if suddenly there are not exactly one receiver? +fun findReceiverOfInvoke(frame: Frame, insn: MethodInsnNode): AbstractInsnNode? = + frame.getStack(frame.stackSize - insn.owner.removePrefix(NUMBERED_FUNCTION_PREFIX).toInt() - 1)?.insns?.singleOrNull() + +fun AbstractInsnNode.isNoinlineCallMarker(): Boolean = + opcode == Opcodes.INVOKESTATIC && cast().let { it.owner == NOINLINE_CALL_MARKER && it.name == NOINLINE_CALL_MARKER } diff --git a/compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt b/compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt new file mode 100644 index 00000000000..116fcd3c71e --- /dev/null +++ b/compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt @@ -0,0 +1,150 @@ +// IGNORE_BACKEND: JVM_IR +// COMMON_COROUTINES_TEST +// WITH_RUNTIME +// WITH_COROUTINES +// NO_CHECK_LAMBDA_INLINING +// CHECK_STATE_MACHINE + +// FILE: inline.kt + +import helpers.* + +interface SuspendRunnable { + suspend fun run() +} + +inline fun inlineMe1(crossinline c: suspend () -> Unit) = + object : SuspendRunnable { + override suspend fun run() { + c() + c() + } + } + +inline fun inlineMe2(crossinline c: suspend () -> Unit) = suspend { + c() + c() +} + +inline suspend fun inlineMe3(crossinline c: suspend () -> Unit) { + c() + c() +} + +inline suspend fun inlineMe4(c: suspend () -> Unit) { + c() + c() +} + +inline fun inlineMe5(noinline c: suspend () -> Unit) = + object : SuspendRunnable { + override suspend fun run() { + c() + c() + } + } + +inline fun inlineMe6(noinline c: suspend () -> Unit) = suspend { + c() + c() +} + +inline suspend fun inlineMe7(noinline c: suspend () -> Unit) { + c() + c() +} + +inline fun inlineMe11(crossinline c: suspend () -> Unit) = inlineMe1(c) +inline fun inlineMe12(crossinline c: suspend () -> Unit) = inlineMe2(c) +inline suspend fun inlineMe13(crossinline c: suspend () -> Unit) = inlineMe3(c) +inline suspend fun inlineMe14(crossinline c: suspend () -> Unit) = inlineMe4(c) + +// FILE: box.kt +// COMMON_COROUTINES_TEST + +import helpers.* +import COROUTINES_PACKAGE.* + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(CheckStateMachineContinuation) +} + +fun box(): String { + val lambda = suspend { + StateMachineChecker.suspendHere() + StateMachineChecker.suspendHere() + } + + builder { + val r = inlineMe1(lambda) + r.run() + } + StateMachineChecker.check(numberOfSuspensions = 4) + StateMachineChecker.reset() + builder { + inlineMe2(lambda)() + } + StateMachineChecker.check(numberOfSuspensions = 4) + StateMachineChecker.reset() + builder { + inlineMe3(lambda) + } + StateMachineChecker.check(numberOfSuspensions = 4) + StateMachineChecker.reset() + builder { + inlineMe4(lambda) + } + StateMachineChecker.check(numberOfSuspensions = 4) + StateMachineChecker.reset() + builder { + val r = inlineMe5(lambda) + r.run() + } + StateMachineChecker.check(numberOfSuspensions = 4) + StateMachineChecker.reset() + builder { + inlineMe6(lambda)() + } + StateMachineChecker.check(numberOfSuspensions = 4) + StateMachineChecker.reset() + builder { + inlineMe7(lambda) + } + StateMachineChecker.check(numberOfSuspensions = 4) + + StateMachineChecker.reset() + builder { + val r = inlineMe11 { + StateMachineChecker.suspendHere() + StateMachineChecker.suspendHere() + } + r.run() + } + StateMachineChecker.check(numberOfSuspensions = 4) + StateMachineChecker.reset() + builder { + inlineMe12 { + StateMachineChecker.suspendHere() + StateMachineChecker.suspendHere() + }() + } + StateMachineChecker.check(numberOfSuspensions = 4) + StateMachineChecker.reset() + builder { + inlineMe13 { + StateMachineChecker.suspendHere() + StateMachineChecker.suspendHere() + } + } + StateMachineChecker.check(numberOfSuspensions = 4) + StateMachineChecker.reset() + builder { + inlineMe14 { + StateMachineChecker.suspendHere() + StateMachineChecker.suspendHere() + } + } + StateMachineChecker.check(numberOfSuspensions = 4) + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java index d0299257d43..a89e06c4cf7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java @@ -3936,6 +3936,16 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/oneInlineTwoCaptures.kt", "kotlin.coroutines"); } + @TestMetadata("passLambda.kt") + public void testPassLambda_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("passLambda.kt") + public void testPassLambda_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt", "kotlin.coroutines"); + } + @TestMetadata("passParameterLambda.kt") public void testPassParameterLambda_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passParameterLambda.kt", "kotlin.coroutines.experimental"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java index c77c2a7d6dc..951b62ca464 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -3936,6 +3936,16 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/oneInlineTwoCaptures.kt", "kotlin.coroutines"); } + @TestMetadata("passLambda.kt") + public void testPassLambda_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("passLambda.kt") + public void testPassLambda_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt", "kotlin.coroutines"); + } + @TestMetadata("passParameterLambda.kt") public void testPassParameterLambda_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passParameterLambda.kt", "kotlin.coroutines.experimental"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java index b76a203c7b6..21690c243a8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java @@ -3936,6 +3936,16 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/oneInlineTwoCaptures.kt", "kotlin.coroutines"); } + @TestMetadata("passLambda.kt") + public void testPassLambda_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("passLambda.kt") + public void testPassLambda_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt", "kotlin.coroutines"); + } + @TestMetadata("passParameterLambda.kt") public void testPassParameterLambda_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passParameterLambda.kt", "kotlin.coroutines.experimental"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrInlineSuspendTestsGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrInlineSuspendTestsGenerated.java index e41382dd0f8..901df93fd3e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrInlineSuspendTestsGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrInlineSuspendTestsGenerated.java @@ -339,6 +339,11 @@ public class IrInlineSuspendTestsGenerated extends AbstractIrInlineSuspendTests runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/oneInlineTwoCaptures.kt", "kotlin.coroutines"); } + @TestMetadata("passLambda.kt") + public void testPassLambda_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt", "kotlin.coroutines"); + } + @TestMetadata("passParameterLambda.kt") public void testPassParameterLambda_1_3() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passParameterLambda.kt", "kotlin.coroutines"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineSuspendTestsGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineSuspendTestsGenerated.java index 928b4c463f9..523b0bda0c6 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineSuspendTestsGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineSuspendTestsGenerated.java @@ -549,6 +549,16 @@ public class InlineSuspendTestsGenerated extends AbstractInlineSuspendTests { runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/oneInlineTwoCaptures.kt", "kotlin.coroutines"); } + @TestMetadata("passLambda.kt") + public void testPassLambda_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("passLambda.kt") + public void testPassLambda_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passLambda.kt", "kotlin.coroutines"); + } + @TestMetadata("passParameterLambda.kt") public void testPassParameterLambda_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/stateMachine/passParameterLambda.kt", "kotlin.coroutines.experimental");