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
This commit is contained in:
@@ -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 {
|
||||
|
||||
+2
-1
@@ -134,7 +134,8 @@ class AnonymousObjectTransformer(
|
||||
inliningContext,
|
||||
classBuilder,
|
||||
methodsToTransform,
|
||||
superClassName
|
||||
superClassName,
|
||||
additionalFakeParams
|
||||
)
|
||||
for (next in methodsToTransform) {
|
||||
val deferringVisitor =
|
||||
|
||||
@@ -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<out T : BaseExpressionCodegen>(
|
||||
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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) =
|
||||
|
||||
+128
-37
@@ -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<MethodNode>,
|
||||
private val superClassName: String
|
||||
private val superClassName: String,
|
||||
private val capturedParams: List<CapturedParamInfo>
|
||||
) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SurroundSuspendLambdaCallsWithSuspendMarkersMethodVisitor(
|
||||
delegate: MethodVisitor,
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
val thisName: String,
|
||||
val capturedParams: List<CapturedParamInfo>
|
||||
) : 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<Pair<AbstractInsnNode, AbstractInsnNode>>()
|
||||
|
||||
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<Pair<AbstractInsnNode, AbstractInsnNode>>
|
||||
) {
|
||||
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<SourceValue>, 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<MethodInsnNode>().let { it.owner == NOINLINE_CALL_MARKER && it.name == NOINLINE_CALL_MARKER }
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
+10
@@ -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");
|
||||
|
||||
Generated
+10
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
Generated
+5
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user