JVM: surround captured lambdas with conditional suspension markers

This commit is contained in:
pyos
2020-04-03 14:03:39 +02:00
committed by Ilmir Usmanov
parent c650c9570f
commit 829343cf6f
10 changed files with 190 additions and 180 deletions
@@ -2633,15 +2633,14 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
if (maybeSuspensionPoint) {
addSuspendMarker(v, suspensionPointKind, true);
addSuspendMarker(v, true, suspensionPointKind == SuspensionPointKind.NOT_INLINE);
}
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
if (maybeSuspensionPoint) {
addReturnsUnitMarkerIfNecessary(v, resolvedCall);
addSuspendMarker(v, suspensionPointKind, false);
addSuspendMarker(v, false, suspensionPointKind == SuspensionPointKind.NOT_INLINE);
addInlineMarker(v, false);
}
@@ -144,8 +144,7 @@ class AnonymousObjectTransformer(
inliningContext,
classBuilder,
methodsToTransform,
superClassName,
allCapturedParamBuilder.listCaptured()
superClassName
)
loop@ for (next in methodsToTransform) {
val deferringVisitor =
@@ -526,7 +525,7 @@ class AnonymousObjectTransformer(
alreadyAddedParam?.newFieldName ?: getNewFieldName(desc.fieldName, false),
alreadyAddedParam != null
)
if (info is ExpressionLambda && info.isCapturedSuspend(desc, inliningContext)) {
if (info is ExpressionLambda && info.isCapturedSuspend(desc)) {
recapturedParamInfo.functionalArgument = NonInlineableArgumentForInlineableParameterCalledInSuspend
}
val composed = StackValue.field(
@@ -65,7 +65,10 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu
for (info in capturedVars) {
val field = remapper.findField(FieldInsnNode(0, info.containingLambdaName, info.fieldName, ""))
?: error("Captured field not found: " + info.containingLambdaName + "." + info.fieldName)
builder.addCapturedParam(field, info.fieldName)
val recapturedParamInfo = builder.addCapturedParam(field, info.fieldName)
if (this is ExpressionLambda && isCapturedSuspend(info)) {
recapturedParamInfo.functionalArgument = NonInlineableArgumentForInlineableParameterCalledInSuspend
}
}
return builder.buildParameters()
@@ -226,7 +229,7 @@ abstract class ExpressionLambda(isCrossInline: Boolean) : LambdaInfo(isCrossInli
}
abstract fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor
abstract fun isCapturedSuspend(desc: CapturedParamDesc, inliningContext: InliningContext): Boolean
abstract fun isCapturedSuspend(desc: CapturedParamDesc): Boolean
}
class PsiExpressionLambda(
@@ -341,6 +344,6 @@ class PsiExpressionLambda(
)
}
override fun isCapturedSuspend(desc: CapturedParamDesc, inliningContext: InliningContext): Boolean =
isCapturedSuspendLambda(closure, desc.fieldName, inliningContext.state.bindingContext)
override fun isCapturedSuspend(desc: CapturedParamDesc): Boolean =
isCapturedSuspendLambda(closure, desc.fieldName, typeMapper.bindingContext)
}
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationM
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.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
@@ -216,10 +215,8 @@ 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
markNoinlineLambdaIfSuspend(mv, info)
super.visitMethodInsn(opcode, owner, name, desc, itf)
return
}
@@ -363,8 +360,6 @@ 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)
}
@@ -385,21 +380,8 @@ class MethodInliner(
node.accept(lambdaInliner)
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
surroundInvokesWithSuspendMarkersIfNeeded(resultNode)
return resultNode
}
private fun isDefaultLambdaWithReification(lambdaInfo: LambdaInfo) =
@@ -901,6 +883,8 @@ class MethodInliner(
getFunctionalArgumentIfExists((insnNode as VarInsnNode).`var`)
insnNode is FieldInsnNode && insnNode.name.startsWith(CAPTURED_FIELD_FOLD_PREFIX) ->
findCapturedField(insnNode, nodeRemapper).functionalArgument
insnNode is FieldInsnNode && insnNode.isSuspendLambdaCapturedByOuterObjectOrLambda(inliningContext) ->
NonInlineableArgumentForInlineableParameterCalledInSuspend
else ->
null
}
@@ -6,36 +6,28 @@
package org.jetbrains.kotlin.codegen.inline.coroutines
import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.codegen.AsmUtil.CAPTURED_THIS_FIELD
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
import org.jetbrains.kotlin.codegen.coroutines.*
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.codegen.optimization.fixStack.FixStackMethodTransformer
import org.jetbrains.kotlin.config.isReleaseCoroutines
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.org.objectweb.asm.*
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"
const val FOR_INLINE_SUFFIX = "\$\$forInline"
class CoroutineTransformer(
private val inliningContext: InliningContext,
private val classBuilder: ClassBuilder,
private val methods: List<MethodNode>,
private val superClassName: String,
private val capturedParams: List<CapturedParamInfo>
private val superClassName: String
) {
private val state = inliningContext.state
// If we inline into inline function, we should generate both method with state-machine for Java interop and method without
@@ -89,21 +81,18 @@ class CoroutineTransformer(
)
) {
val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline
val stateMachineBuilder = surroundNoinlineCallsWithMarkers(
node,
CoroutineTransformerMethodVisitor(
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
obtainClassBuilderForCoroutineState = { classBuilder },
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
// TODO: this linenumbers might not be correct and since they are used only for step-over, check them.
lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
languageVersionSettings = state.languageVersionSettings,
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = classBuilder.thisName,
isForNamedFunction = false,
disableTailCallOptimizationForFunctionReturningUnit = false
)
val stateMachineBuilder = CoroutineTransformerMethodVisitor(
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
obtainClassBuilderForCoroutineState = { classBuilder },
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
// TODO: this linenumbers might not be correct and since they are used only for step-over, check them.
lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
languageVersionSettings = state.languageVersionSettings,
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = classBuilder.thisName,
isForNamedFunction = false,
disableTailCallOptimizationForFunctionReturningUnit = false
)
if (generateForInline)
@@ -126,23 +115,20 @@ class CoroutineTransformer(
// If the node already has state-machine, it is safer to generate state-machine.
val disableTailCallOptimization = methods.find { it.name == name && it.desc == node.desc }?.let { isStateMachine(it) } ?: false
val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline
val stateMachineBuilder = surroundNoinlineCallsWithMarkers(
node,
CoroutineTransformerMethodVisitor(
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
languageVersionSettings = state.languageVersionSettings,
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = classBuilder.thisName,
isForNamedFunction = true,
needDispatchReceiver = true,
internalNameForDispatchReceiver = classBuilder.thisName,
disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization,
putContinuationParameterToLvt = !state.isIrBackend
)
val stateMachineBuilder = CoroutineTransformerMethodVisitor(
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
languageVersionSettings = state.languageVersionSettings,
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = classBuilder.thisName,
isForNamedFunction = true,
needDispatchReceiver = true,
internalNameForDispatchReceiver = classBuilder.thisName,
disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization,
putContinuationParameterToLvt = !state.isIrBackend
)
if (generateForInline)
@@ -152,35 +138,6 @@ class CoroutineTransformer(
}
}
private fun surroundNoinlineCallsWithMarkers(node: MethodNode, delegate: MethodVisitor): MethodVisitor =
SurroundSuspendLambdaCallsWithSuspendMarkersMethodVisitor(
delegate, node.access, node.name, node.desc, classBuilder.thisName, this::fieldIsCapturedSuspendLambda
)
private fun fieldIsCapturedSuspendLambda(field: FieldInsnNode): Boolean =
capturedParams.find { it.newFieldName == field.name }?.let { it.functionalArgument?.isSuspendLambda() == true }
?: isSuspendLambdaCapturedByOuterObjectOrLambda(field)
// We cannot find the lambda in captured parameters: it came from object outside of the our reach:
// this can happen when the lambda capture by non-transformed closure:
// inline fun inlineMe(crossinline c: suspend() -> Unit) = suspend { c() }
// inline fun inlineMe2(crossinline c: suspend() -> Unit) = suspend { inlineMe { c() }() }
// Suppose, we inline inlineMe into inlineMe2: the only knowledge we have about inlineMe$1 is captured receiver (this$0)
// Thus, transformed lambda from inlineMe, inlineMe3$$inlined$inlineMe2$1 contains the following bytecode
// ALOAD 0
// GETFIELD inlineMe2$1$invokeSuspend$$inlined$inlineMe$1.this$0 : LScratchKt$inlineMe2$1;
// GETFIELD inlineMe2$1.$c : Lkotlin/jvm/functions/Function1;
// Since inlineMe2's lambda is outside of reach of the inliner, find crossinline parameter from compilation context:
private fun isSuspendLambdaCapturedByOuterObjectOrLambda(field: FieldInsnNode): Boolean {
val functionDescriptor = inliningContext.root.sourceCompilerForInline.compilationContextFunctionDescriptor
val classDescriptor = functionDescriptor.findContainingClassOrLambda() ?: return false
return isCapturedSuspendLambda(classDescriptor, field.name, inliningContext.state.bindingContext)
}
private tailrec fun DeclarationDescriptor.findContainingClassOrLambda(): ClassDescriptor? =
if (containingDeclaration is ClassDescriptor) containingDeclaration as ClassDescriptor
else containingDeclaration?.findContainingClassOrLambda()
private fun createNewMethodFrom(node: MethodNode, name: String): MethodVisitor {
return classBuilder.newMethod(
JvmDeclarationOrigin.NO_ORIGIN, node.access, name, node.desc, node.signature, ArrayUtil.toStringArray(node.exceptions)
@@ -219,77 +176,71 @@ class CoroutineTransformer(
}
}
class SurroundSuspendLambdaCallsWithSuspendMarkersMethodVisitor(
delegate: MethodVisitor, access: Int, name: String, desc: String,
private val thisName: String,
private val isCapturedSuspendLambda: (FieldInsnNode) -> Boolean
) : TransformationMethodVisitor(delegate, access, name, desc, null, null) {
override fun performTransformations(methodNode: MethodNode) {
fun AbstractInsnNode.index() = methodNode.instructions.indexOf(this)
private const val NOINLINE_CALL_MARKER = "\$\$\$\$\$NOINLINE_CALL_MARKER\$\$\$\$\$"
FixStackMethodTransformer().transform(thisName, methodNode)
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()] ?: continue
val receiver = findReceiverOfInvoke(frame, insn).takeIf { it?.isSuspendLambda(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.isSuspendLambda(invoke: MethodInsnNode): Boolean {
if (opcode != Opcodes.GETFIELD) return false
this as FieldInsnNode
if (desc != "L${invoke.owner};") return false
var current: FieldInsnNode? = this
// Unroll the battery of
// GETFIELD <outer1>.this$0 L<outer2>;
// GETFIELD <outer2>.this$0 L<outer3>;
// ...
// GETFIELD <outerN>.$action Lkotlin/jvm/functions/FunctionM;
while (current != null) {
if (current.owner == thisName) break
if (current.previous?.opcode != Opcodes.GETFIELD || current.previous.cast<FieldInsnNode>().name != CAPTURED_THIS_FIELD) return false
current = current.previous as FieldInsnNode
}
return isCapturedSuspendLambda(this)
fun markNoinlineLambdaIfSuspend(mv: MethodVisitor, info: FunctionalArgument?) {
when (info) {
NonInlineableArgumentForInlineableSuspendParameter ->
mv.visitMethodInsn(Opcodes.INVOKESTATIC, NOINLINE_CALL_MARKER, "always", "()V", false)
NonInlineableArgumentForInlineableParameterCalledInSuspend ->
mv.visitMethodInsn(Opcodes.INVOKESTATIC, NOINLINE_CALL_MARKER, "conditional", "()V", false)
}
}
private fun FunctionalArgument.isSuspendLambda(): Boolean =
this is NonInlineableArgumentForInlineableParameterCalledInSuspend || (this is ExpressionLambda && isSuspend)
private fun Frame<SourceValue>.getSource(offset: Int): AbstractInsnNode? =
getStack(stackSize - offset - 1)?.insns?.singleOrNull()
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 {
fun surroundInvokesWithSuspendMarkersIfNeeded(node: MethodNode) {
val markers = node.instructions.asSequence().filter {
it.opcode == Opcodes.INVOKESTATIC && (it as MethodInsnNode).owner == NOINLINE_CALL_MARKER
}.toList()
if (markers.isEmpty()) return
val sourceFrames = MethodTransformer.analyze("fake", node, SourceInterpreter())
val loads = markers.map { marker ->
val arity = (marker.next as MethodInsnNode).owner.removePrefix(NUMBERED_FUNCTION_PREFIX).toInt()
var receiver = sourceFrames[node.instructions.indexOf(marker) + 1].getSource(arity)
// Navigate the ALOAD+GETFIELD+... chain to the first instruction. We need to insert a stack
// spilling marker before it starts.
while (receiver?.opcode == Opcodes.GETFIELD) {
receiver = sourceFrames[node.instructions.indexOf(receiver)].getSource(0)
}
receiver
}
for ((marker, load) in markers.zip(loads)) {
val conditional = (marker as MethodInsnNode).name == "conditional"
val invoke = marker.next as MethodInsnNode
node.instructions.remove(marker)
if (load == null) {
continue // dead code, doesn't matter
}
node.instructions.insertBefore(load, withInstructionAdapter {
addInlineMarker(this, isStartNotEnd = true)
})
methodNode.instructions.insertBefore(invoke, withInstructionAdapter {
addSuspendMarker(this, isStartNotEnd = true)
node.instructions.insertBefore(invoke, withInstructionAdapter {
addSuspendMarker(this, isStartNotEnd = true, inlinable = conditional)
})
methodNode.instructions.insert(invoke, withInstructionAdapter {
addSuspendMarker(this, isStartNotEnd = false)
node.instructions.insert(invoke, withInstructionAdapter {
addSuspendMarker(this, isStartNotEnd = false, inlinable = conditional)
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 }
// We cannot find the lambda in captured parameters: it came from object outside of the our reach:
// this can happen when the lambda capture by non-transformed closure:
// inline fun inlineMe(crossinline c: suspend() -> Unit) = suspend { c() }
// inline fun inlineMe2(crossinline c: suspend() -> Unit) = suspend { inlineMe { c() }() }
// Suppose, we inline inlineMe into inlineMe2: the only knowledge we have about inlineMe$1 is captured receiver (this$0)
// Thus, transformed lambda from inlineMe, inlineMe3$$inlined$inlineMe2$1 contains the following bytecode
// ALOAD 0
// GETFIELD inlineMe2$1$invokeSuspend$$inlined$inlineMe$1.this$0 : LScratchKt$inlineMe2$1;
// GETFIELD inlineMe2$1.$c : Lkotlin/jvm/functions/Function1;
// Since inlineMe2's lambda is outside of reach of the inliner, find crossinline parameter from compilation context:
fun FieldInsnNode.isSuspendLambdaCapturedByOuterObjectOrLambda(inliningContext: InliningContext): Boolean {
var container: DeclarationDescriptor = inliningContext.root.sourceCompilerForInline.compilationContextFunctionDescriptor
while (container !is ClassDescriptor) {
container = container.containingDeclaration ?: return false
}
return isCapturedSuspendLambda(container, name, inliningContext.state.bindingContext)
}
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.codegen.context.CodegenContext
import org.jetbrains.kotlin.codegen.context.CodegenContextUtil
import org.jetbrains.kotlin.codegen.context.InlineLambdaContext
import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.coroutines.SuspensionPointKind
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
@@ -432,18 +431,14 @@ fun addReturnsUnitMarker(v: InstructionAdapter) {
v.emitInlineMarker(INLINE_MARKER_RETURNS_UNIT)
}
fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
v.emitInlineMarker(if (isStartNotEnd) INLINE_MARKER_BEFORE_SUSPEND_ID else INLINE_MARKER_AFTER_SUSPEND_ID)
}
fun addInlineSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
v.emitInlineMarker(if (isStartNotEnd) INLINE_MARKER_BEFORE_INLINE_SUSPEND_ID else INLINE_MARKER_AFTER_INLINE_SUSPEND_ID)
}
fun addSuspendMarker(v: InstructionAdapter, kind: SuspensionPointKind, isStartNotEnd: Boolean) = when (kind) {
SuspensionPointKind.NEVER -> Unit
SuspensionPointKind.NOT_INLINE -> addInlineSuspendMarker(v, isStartNotEnd)
SuspensionPointKind.ALWAYS -> addSuspendMarker(v, isStartNotEnd)
fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean, inlinable: Boolean = false) {
val marker = when {
inlinable && isStartNotEnd -> INLINE_MARKER_BEFORE_INLINE_SUSPEND_ID
inlinable -> INLINE_MARKER_AFTER_INLINE_SUSPEND_ID
isStartNotEnd -> INLINE_MARKER_BEFORE_SUSPEND_ID
else -> INLINE_MARKER_AFTER_SUSPEND_ID
}
v.emitInlineMarker(marker)
}
fun addFakeContinuationConstructorCallMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
@@ -377,6 +377,10 @@ class ExpressionCodegen(
val asmType = if (expression is IrConstructorCall) typeMapper.mapTypeAsDeclaration(expression.type) else expression.asmType
val isSuspensionPoint = expression.isSuspensionPoint()
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
addInlineMarker(mv, isStartNotEnd = true)
}
when {
expression is IrConstructorCall -> {
closureReifiedMarkers[expression.symbol.owner.parentAsClass]?.let {
@@ -404,8 +408,6 @@ class ExpressionCodegen(
}
expression.symbol.descriptor is ConstructorDescriptor ->
throw AssertionError("IrCall with ConstructorDescriptor: ${expression.javaClass.simpleName}")
isSuspensionPoint != SuspensionPointKind.NEVER ->
addInlineMarker(mv, isStartNotEnd = true)
}
expression.dispatchReceiver?.let { receiver ->
@@ -430,7 +432,7 @@ class ExpressionCodegen(
expression.markLineNumber(true)
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
addSuspendMarker(mv, isSuspensionPoint, isStartNotEnd = true)
addSuspendMarker(mv, isStartNotEnd = true, isSuspensionPoint == SuspensionPointKind.NOT_INLINE)
}
if (irFunction.isInvokeSuspendOfContinuation()) {
@@ -443,7 +445,7 @@ class ExpressionCodegen(
}
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
addSuspendMarker(mv, isSuspensionPoint, isStartNotEnd = false)
addSuspendMarker(mv, isStartNotEnd = false, isSuspensionPoint == SuspensionPointKind.NOT_INLINE)
addInlineMarker(mv, isStartNotEnd = false)
}
@@ -250,7 +250,7 @@ class IrExpressionLambdaImpl(
override fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor = function.descriptor
override fun isCapturedSuspend(desc: CapturedParamDesc, inliningContext: InliningContext): Boolean =
override fun isCapturedSuspend(desc: CapturedParamDesc): Boolean =
capturedParameters[desc]?.let { it.isInlineParameter() && it.type.isSuspendFunctionTypeOrSubtype() } == true
}
@@ -68,11 +68,33 @@ public final class TcoContinuationKt$foo$$inlined$flow$1 {
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
}
@kotlin.Metadata
public final class TcoContinuationKt$foo$$inlined$map$1$2$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field L$2: java.lang.Object
field L$3: java.lang.Object
field L$4: java.lang.Object
field L$5: java.lang.Object
field L$6: java.lang.Object
field L$7: java.lang.Object
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
synthetic final field this$0: TcoContinuationKt$foo$$inlined$map$1$2
inner class TcoContinuationKt$foo$$inlined$map$1$2
inner class TcoContinuationKt$foo$$inlined$map$1$2$1
public method <init>(p0: TcoContinuationKt$foo$$inlined$map$1$2, p1: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
public final class TcoContinuationKt$foo$$inlined$map$1$2 {
synthetic final field $this_flow$inlined: FlowCollector
synthetic final field this$0: TcoContinuationKt$foo$$inlined$map$1
inner class TcoContinuationKt$foo$$inlined$map$1$2
inner class TcoContinuationKt$foo$$inlined$map$1$2$1
public method <init>(p0: FlowCollector, p1: TcoContinuationKt$foo$$inlined$map$1): void
public @org.jetbrains.annotations.Nullable method emit(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
}
@@ -100,6 +122,14 @@ public final class TcoContinuationKt$map$$inlined$transform$1$1 {
@kotlin.Metadata
public final class TcoContinuationKt$map$$inlined$transform$1$2$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field L$2: java.lang.Object
field L$3: java.lang.Object
field L$4: java.lang.Object
field L$5: java.lang.Object
field L$6: java.lang.Object
field L$7: java.lang.Object
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$1$2
@@ -148,6 +178,14 @@ public final class TcoContinuationKt$map$$inlined$transform$2$1 {
@kotlin.Metadata
public final class TcoContinuationKt$map$$inlined$transform$2$2$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field L$2: java.lang.Object
field L$3: java.lang.Object
field L$4: java.lang.Object
field L$5: java.lang.Object
field L$6: java.lang.Object
field L$7: java.lang.Object
synthetic field data: java.lang.Object
synthetic field exception: java.lang.Throwable
synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$2$2
@@ -64,11 +64,32 @@ public final class TcoContinuationKt$foo$$inlined$flow$1 {
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
public final class TcoContinuationKt$foo$$inlined$map$1$2$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field L$2: java.lang.Object
field L$3: java.lang.Object
field L$4: java.lang.Object
field L$5: java.lang.Object
field L$6: java.lang.Object
field L$7: java.lang.Object
field label: int
synthetic field result: java.lang.Object
synthetic final field this$0: TcoContinuationKt$foo$$inlined$map$1$2
inner class TcoContinuationKt$foo$$inlined$map$1$2
inner class TcoContinuationKt$foo$$inlined$map$1$2$1
public method <init>(p0: TcoContinuationKt$foo$$inlined$map$1$2, p1: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class TcoContinuationKt$foo$$inlined$map$1$2 {
synthetic final field $this_flow$inlined: FlowCollector
synthetic final field this$0: TcoContinuationKt$foo$$inlined$map$1
inner class TcoContinuationKt$foo$$inlined$map$1$2
inner class TcoContinuationKt$foo$$inlined$map$1$2$1
public method <init>(p0: FlowCollector, p1: TcoContinuationKt$foo$$inlined$map$1): void
public @org.jetbrains.annotations.Nullable method emit(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@@ -93,7 +114,16 @@ public final class TcoContinuationKt$map$$inlined$transform$1$1 {
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
public final class TcoContinuationKt$map$$inlined$transform$1$2$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field L$2: java.lang.Object
field L$3: java.lang.Object
field L$4: java.lang.Object
field L$5: java.lang.Object
field L$6: java.lang.Object
field L$7: java.lang.Object
field label: int
synthetic field result: java.lang.Object
synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$1$2
@@ -137,7 +167,16 @@ public final class TcoContinuationKt$map$$inlined$transform$2$1 {
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
public final class TcoContinuationKt$map$$inlined$transform$2$2$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field L$2: java.lang.Object
field L$3: java.lang.Object
field L$4: java.lang.Object
field L$5: java.lang.Object
field L$6: java.lang.Object
field L$7: java.lang.Object
field label: int
synthetic field result: java.lang.Object
synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$2$2