JVM: surround captured lambdas with conditional suspension markers
This commit is contained in:
@@ -2633,15 +2633,14 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (maybeSuspensionPoint) {
|
if (maybeSuspensionPoint) {
|
||||||
addSuspendMarker(v, suspensionPointKind, true);
|
addSuspendMarker(v, true, suspensionPointKind == SuspensionPointKind.NOT_INLINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
|
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
|
||||||
|
|
||||||
if (maybeSuspensionPoint) {
|
if (maybeSuspensionPoint) {
|
||||||
addReturnsUnitMarkerIfNecessary(v, resolvedCall);
|
addReturnsUnitMarkerIfNecessary(v, resolvedCall);
|
||||||
|
addSuspendMarker(v, false, suspensionPointKind == SuspensionPointKind.NOT_INLINE);
|
||||||
addSuspendMarker(v, suspensionPointKind, false);
|
|
||||||
addInlineMarker(v, false);
|
addInlineMarker(v, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -144,8 +144,7 @@ class AnonymousObjectTransformer(
|
|||||||
inliningContext,
|
inliningContext,
|
||||||
classBuilder,
|
classBuilder,
|
||||||
methodsToTransform,
|
methodsToTransform,
|
||||||
superClassName,
|
superClassName
|
||||||
allCapturedParamBuilder.listCaptured()
|
|
||||||
)
|
)
|
||||||
loop@ for (next in methodsToTransform) {
|
loop@ for (next in methodsToTransform) {
|
||||||
val deferringVisitor =
|
val deferringVisitor =
|
||||||
@@ -526,7 +525,7 @@ class AnonymousObjectTransformer(
|
|||||||
alreadyAddedParam?.newFieldName ?: getNewFieldName(desc.fieldName, false),
|
alreadyAddedParam?.newFieldName ?: getNewFieldName(desc.fieldName, false),
|
||||||
alreadyAddedParam != null
|
alreadyAddedParam != null
|
||||||
)
|
)
|
||||||
if (info is ExpressionLambda && info.isCapturedSuspend(desc, inliningContext)) {
|
if (info is ExpressionLambda && info.isCapturedSuspend(desc)) {
|
||||||
recapturedParamInfo.functionalArgument = NonInlineableArgumentForInlineableParameterCalledInSuspend
|
recapturedParamInfo.functionalArgument = NonInlineableArgumentForInlineableParameterCalledInSuspend
|
||||||
}
|
}
|
||||||
val composed = StackValue.field(
|
val composed = StackValue.field(
|
||||||
|
|||||||
@@ -65,7 +65,10 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu
|
|||||||
for (info in capturedVars) {
|
for (info in capturedVars) {
|
||||||
val field = remapper.findField(FieldInsnNode(0, info.containingLambdaName, info.fieldName, ""))
|
val field = remapper.findField(FieldInsnNode(0, info.containingLambdaName, info.fieldName, ""))
|
||||||
?: error("Captured field not found: " + 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()
|
return builder.buildParameters()
|
||||||
@@ -226,7 +229,7 @@ abstract class ExpressionLambda(isCrossInline: Boolean) : LambdaInfo(isCrossInli
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor
|
abstract fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor
|
||||||
abstract fun isCapturedSuspend(desc: CapturedParamDesc, inliningContext: InliningContext): Boolean
|
abstract fun isCapturedSuspend(desc: CapturedParamDesc): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class PsiExpressionLambda(
|
class PsiExpressionLambda(
|
||||||
@@ -341,6 +344,6 @@ class PsiExpressionLambda(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isCapturedSuspend(desc: CapturedParamDesc, inliningContext: InliningContext): Boolean =
|
override fun isCapturedSuspend(desc: CapturedParamDesc): Boolean =
|
||||||
isCapturedSuspendLambda(closure, desc.fieldName, inliningContext.state.bindingContext)
|
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.common.*
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
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.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||||
@@ -216,10 +215,8 @@ class MethodInliner(
|
|||||||
val info = invokeCall.functionalArgument
|
val info = invokeCall.functionalArgument
|
||||||
|
|
||||||
if (info !is LambdaInfo) {
|
if (info !is LambdaInfo) {
|
||||||
if (info == NonInlineableArgumentForInlineableSuspendParameter) {
|
|
||||||
super.visitMethodInsn(Opcodes.INVOKESTATIC, NOINLINE_CALL_MARKER, NOINLINE_CALL_MARKER, "()V", false)
|
|
||||||
}
|
|
||||||
//noninlinable lambda
|
//noninlinable lambda
|
||||||
|
markNoinlineLambdaIfSuspend(mv, info)
|
||||||
super.visitMethodInsn(opcode, owner, name, desc, itf)
|
super.visitMethodInsn(opcode, owner, name, desc, itf)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -363,8 +360,6 @@ class MethodInliner(
|
|||||||
ReifiedTypeInliner.isNeedClassReificationMarker(MethodInsnNode(opcode, owner, name, desc, false))
|
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
|
//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 {
|
} else {
|
||||||
super.visitMethodInsn(opcode, owner, name, desc, itf)
|
super.visitMethodInsn(opcode, owner, name, desc, itf)
|
||||||
}
|
}
|
||||||
@@ -385,21 +380,8 @@ class MethodInliner(
|
|||||||
|
|
||||||
node.accept(lambdaInliner)
|
node.accept(lambdaInliner)
|
||||||
|
|
||||||
return surroundInvokesWithSuspendMarkersIfNeeded(resultNode)
|
surroundInvokesWithSuspendMarkersIfNeeded(resultNode)
|
||||||
}
|
return 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) =
|
private fun isDefaultLambdaWithReification(lambdaInfo: LambdaInfo) =
|
||||||
@@ -901,6 +883,8 @@ class MethodInliner(
|
|||||||
getFunctionalArgumentIfExists((insnNode as VarInsnNode).`var`)
|
getFunctionalArgumentIfExists((insnNode as VarInsnNode).`var`)
|
||||||
insnNode is FieldInsnNode && insnNode.name.startsWith(CAPTURED_FIELD_FOLD_PREFIX) ->
|
insnNode is FieldInsnNode && insnNode.name.startsWith(CAPTURED_FIELD_FOLD_PREFIX) ->
|
||||||
findCapturedField(insnNode, nodeRemapper).functionalArgument
|
findCapturedField(insnNode, nodeRemapper).functionalArgument
|
||||||
|
insnNode is FieldInsnNode && insnNode.isSuspendLambdaCapturedByOuterObjectOrLambda(inliningContext) ->
|
||||||
|
NonInlineableArgumentForInlineableParameterCalledInSuspend
|
||||||
else ->
|
else ->
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
+82
-131
@@ -6,36 +6,28 @@
|
|||||||
package org.jetbrains.kotlin.codegen.inline.coroutines
|
package org.jetbrains.kotlin.codegen.inline.coroutines
|
||||||
|
|
||||||
import com.intellij.util.ArrayUtil
|
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.ClassBuilder
|
||||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.*
|
import org.jetbrains.kotlin.codegen.coroutines.*
|
||||||
import org.jetbrains.kotlin.codegen.inline.*
|
import org.jetbrains.kotlin.codegen.inline.*
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
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.transformer.MethodTransformer
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
|
|
||||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
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.*
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
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.SourceInterpreter
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
|
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
|
||||||
|
|
||||||
const val NOINLINE_CALL_MARKER = "NOINLINE_CALL_MARKER"
|
|
||||||
|
|
||||||
const val FOR_INLINE_SUFFIX = "\$\$forInline"
|
const val FOR_INLINE_SUFFIX = "\$\$forInline"
|
||||||
|
|
||||||
class CoroutineTransformer(
|
class CoroutineTransformer(
|
||||||
private val inliningContext: InliningContext,
|
private val inliningContext: InliningContext,
|
||||||
private val classBuilder: ClassBuilder,
|
private val classBuilder: ClassBuilder,
|
||||||
private val methods: List<MethodNode>,
|
private val methods: List<MethodNode>,
|
||||||
private val superClassName: String,
|
private val superClassName: String
|
||||||
private val capturedParams: List<CapturedParamInfo>
|
|
||||||
) {
|
) {
|
||||||
private val state = inliningContext.state
|
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
|
// 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 sourceCompilerForInline = inliningContext.root.sourceCompilerForInline
|
||||||
val stateMachineBuilder = surroundNoinlineCallsWithMarkers(
|
val stateMachineBuilder = CoroutineTransformerMethodVisitor(
|
||||||
node,
|
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
|
||||||
CoroutineTransformerMethodVisitor(
|
obtainClassBuilderForCoroutineState = { classBuilder },
|
||||||
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
|
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
|
||||||
obtainClassBuilderForCoroutineState = { classBuilder },
|
// TODO: this linenumbers might not be correct and since they are used only for step-over, check them.
|
||||||
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
|
lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
|
||||||
// TODO: this linenumbers might not be correct and since they are used only for step-over, check them.
|
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
|
||||||
lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
|
languageVersionSettings = state.languageVersionSettings,
|
||||||
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
|
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||||
languageVersionSettings = state.languageVersionSettings,
|
containingClassInternalName = classBuilder.thisName,
|
||||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
isForNamedFunction = false,
|
||||||
containingClassInternalName = classBuilder.thisName,
|
disableTailCallOptimizationForFunctionReturningUnit = false
|
||||||
isForNamedFunction = false,
|
|
||||||
disableTailCallOptimizationForFunctionReturningUnit = false
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (generateForInline)
|
if (generateForInline)
|
||||||
@@ -126,23 +115,20 @@ class CoroutineTransformer(
|
|||||||
// If the node already has state-machine, it is safer to generate state-machine.
|
// 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 disableTailCallOptimization = methods.find { it.name == name && it.desc == node.desc }?.let { isStateMachine(it) } ?: false
|
||||||
val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline
|
val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline
|
||||||
val stateMachineBuilder = surroundNoinlineCallsWithMarkers(
|
val stateMachineBuilder = CoroutineTransformerMethodVisitor(
|
||||||
node,
|
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
|
||||||
CoroutineTransformerMethodVisitor(
|
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
|
||||||
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
|
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
|
||||||
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
|
lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
|
||||||
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
|
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
|
||||||
lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
|
languageVersionSettings = state.languageVersionSettings,
|
||||||
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
|
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||||
languageVersionSettings = state.languageVersionSettings,
|
containingClassInternalName = classBuilder.thisName,
|
||||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
isForNamedFunction = true,
|
||||||
containingClassInternalName = classBuilder.thisName,
|
needDispatchReceiver = true,
|
||||||
isForNamedFunction = true,
|
internalNameForDispatchReceiver = classBuilder.thisName,
|
||||||
needDispatchReceiver = true,
|
disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization,
|
||||||
internalNameForDispatchReceiver = classBuilder.thisName,
|
putContinuationParameterToLvt = !state.isIrBackend
|
||||||
disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization,
|
|
||||||
putContinuationParameterToLvt = !state.isIrBackend
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (generateForInline)
|
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 {
|
private fun createNewMethodFrom(node: MethodNode, name: String): MethodVisitor {
|
||||||
return classBuilder.newMethod(
|
return classBuilder.newMethod(
|
||||||
JvmDeclarationOrigin.NO_ORIGIN, node.access, name, node.desc, node.signature, ArrayUtil.toStringArray(node.exceptions)
|
JvmDeclarationOrigin.NO_ORIGIN, node.access, name, node.desc, node.signature, ArrayUtil.toStringArray(node.exceptions)
|
||||||
@@ -219,77 +176,71 @@ class CoroutineTransformer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SurroundSuspendLambdaCallsWithSuspendMarkersMethodVisitor(
|
private const val NOINLINE_CALL_MARKER = "\$\$\$\$\$NOINLINE_CALL_MARKER\$\$\$\$\$"
|
||||||
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)
|
|
||||||
|
|
||||||
FixStackMethodTransformer().transform(thisName, methodNode)
|
fun markNoinlineLambdaIfSuspend(mv: MethodVisitor, info: FunctionalArgument?) {
|
||||||
val sourceFrames = MethodTransformer.analyze(thisName, methodNode, SourceInterpreter())
|
when (info) {
|
||||||
|
NonInlineableArgumentForInlineableSuspendParameter ->
|
||||||
val noinlineInvokes = arrayListOf<Pair<AbstractInsnNode, AbstractInsnNode>>()
|
mv.visitMethodInsn(Opcodes.INVOKESTATIC, NOINLINE_CALL_MARKER, "always", "()V", false)
|
||||||
|
NonInlineableArgumentForInlineableParameterCalledInSuspend ->
|
||||||
for (insn in methodNode.instructions.asSequence()) {
|
mv.visitMethodInsn(Opcodes.INVOKESTATIC, NOINLINE_CALL_MARKER, "conditional", "()V", false)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FunctionalArgument.isSuspendLambda(): Boolean =
|
private fun Frame<SourceValue>.getSource(offset: Int): AbstractInsnNode? =
|
||||||
this is NonInlineableArgumentForInlineableParameterCalledInSuspend || (this is ExpressionLambda && isSuspend)
|
getStack(stackSize - offset - 1)?.insns?.singleOrNull()
|
||||||
|
|
||||||
fun surroundInvokesWithSuspendMarkers(
|
fun surroundInvokesWithSuspendMarkersIfNeeded(node: MethodNode) {
|
||||||
methodNode: MethodNode,
|
val markers = node.instructions.asSequence().filter {
|
||||||
noinlineInvokes: List<Pair<AbstractInsnNode, AbstractInsnNode>>
|
it.opcode == Opcodes.INVOKESTATIC && (it as MethodInsnNode).owner == NOINLINE_CALL_MARKER
|
||||||
) {
|
}.toList()
|
||||||
for ((invoke, aload) in noinlineInvokes) {
|
if (markers.isEmpty()) return
|
||||||
// Generate inline markers for stack transformation. It is required for local variables spilling.
|
|
||||||
methodNode.instructions.insertBefore(aload, withInstructionAdapter {
|
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)
|
addInlineMarker(this, isStartNotEnd = true)
|
||||||
})
|
})
|
||||||
methodNode.instructions.insertBefore(invoke, withInstructionAdapter {
|
node.instructions.insertBefore(invoke, withInstructionAdapter {
|
||||||
addSuspendMarker(this, isStartNotEnd = true)
|
addSuspendMarker(this, isStartNotEnd = true, inlinable = conditional)
|
||||||
})
|
})
|
||||||
methodNode.instructions.insert(invoke, withInstructionAdapter {
|
node.instructions.insert(invoke, withInstructionAdapter {
|
||||||
addSuspendMarker(this, isStartNotEnd = false)
|
addSuspendMarker(this, isStartNotEnd = false, inlinable = conditional)
|
||||||
addInlineMarker(this, isStartNotEnd = false)
|
addInlineMarker(this, isStartNotEnd = false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: What to do if suddenly there are not exactly one receiver?
|
// We cannot find the lambda in captured parameters: it came from object outside of the our reach:
|
||||||
fun findReceiverOfInvoke(frame: Frame<SourceValue>, insn: MethodInsnNode): AbstractInsnNode? =
|
// this can happen when the lambda capture by non-transformed closure:
|
||||||
frame.getStack(frame.stackSize - insn.owner.removePrefix(NUMBERED_FUNCTION_PREFIX).toInt() - 1)?.insns?.singleOrNull()
|
// inline fun inlineMe(crossinline c: suspend() -> Unit) = suspend { c() }
|
||||||
|
// inline fun inlineMe2(crossinline c: suspend() -> Unit) = suspend { inlineMe { c() }() }
|
||||||
fun AbstractInsnNode.isNoinlineCallMarker(): Boolean =
|
// Suppose, we inline inlineMe into inlineMe2: the only knowledge we have about inlineMe$1 is captured receiver (this$0)
|
||||||
opcode == Opcodes.INVOKESTATIC && cast<MethodInsnNode>().let { it.owner == NOINLINE_CALL_MARKER && it.name == NOINLINE_CALL_MARKER }
|
// 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.CodegenContextUtil
|
||||||
import org.jetbrains.kotlin.codegen.context.InlineLambdaContext
|
import org.jetbrains.kotlin.codegen.context.InlineLambdaContext
|
||||||
import org.jetbrains.kotlin.codegen.context.MethodContext
|
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.coroutines.unwrapInitialDescriptorForSuspendFunction
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||||
@@ -432,18 +431,14 @@ fun addReturnsUnitMarker(v: InstructionAdapter) {
|
|||||||
v.emitInlineMarker(INLINE_MARKER_RETURNS_UNIT)
|
v.emitInlineMarker(INLINE_MARKER_RETURNS_UNIT)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
|
fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean, inlinable: Boolean = false) {
|
||||||
v.emitInlineMarker(if (isStartNotEnd) INLINE_MARKER_BEFORE_SUSPEND_ID else INLINE_MARKER_AFTER_SUSPEND_ID)
|
val marker = when {
|
||||||
}
|
inlinable && isStartNotEnd -> INLINE_MARKER_BEFORE_INLINE_SUSPEND_ID
|
||||||
|
inlinable -> INLINE_MARKER_AFTER_INLINE_SUSPEND_ID
|
||||||
fun addInlineSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
|
isStartNotEnd -> INLINE_MARKER_BEFORE_SUSPEND_ID
|
||||||
v.emitInlineMarker(if (isStartNotEnd) INLINE_MARKER_BEFORE_INLINE_SUSPEND_ID else INLINE_MARKER_AFTER_INLINE_SUSPEND_ID)
|
else -> INLINE_MARKER_AFTER_SUSPEND_ID
|
||||||
}
|
}
|
||||||
|
v.emitInlineMarker(marker)
|
||||||
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 addFakeContinuationConstructorCallMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
|
fun addFakeContinuationConstructorCallMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
|
||||||
|
|||||||
+6
-4
@@ -377,6 +377,10 @@ class ExpressionCodegen(
|
|||||||
val asmType = if (expression is IrConstructorCall) typeMapper.mapTypeAsDeclaration(expression.type) else expression.asmType
|
val asmType = if (expression is IrConstructorCall) typeMapper.mapTypeAsDeclaration(expression.type) else expression.asmType
|
||||||
val isSuspensionPoint = expression.isSuspensionPoint()
|
val isSuspensionPoint = expression.isSuspensionPoint()
|
||||||
|
|
||||||
|
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
|
||||||
|
addInlineMarker(mv, isStartNotEnd = true)
|
||||||
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
expression is IrConstructorCall -> {
|
expression is IrConstructorCall -> {
|
||||||
closureReifiedMarkers[expression.symbol.owner.parentAsClass]?.let {
|
closureReifiedMarkers[expression.symbol.owner.parentAsClass]?.let {
|
||||||
@@ -404,8 +408,6 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
expression.symbol.descriptor is ConstructorDescriptor ->
|
expression.symbol.descriptor is ConstructorDescriptor ->
|
||||||
throw AssertionError("IrCall with ConstructorDescriptor: ${expression.javaClass.simpleName}")
|
throw AssertionError("IrCall with ConstructorDescriptor: ${expression.javaClass.simpleName}")
|
||||||
isSuspensionPoint != SuspensionPointKind.NEVER ->
|
|
||||||
addInlineMarker(mv, isStartNotEnd = true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expression.dispatchReceiver?.let { receiver ->
|
expression.dispatchReceiver?.let { receiver ->
|
||||||
@@ -430,7 +432,7 @@ class ExpressionCodegen(
|
|||||||
expression.markLineNumber(true)
|
expression.markLineNumber(true)
|
||||||
|
|
||||||
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
|
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
|
||||||
addSuspendMarker(mv, isSuspensionPoint, isStartNotEnd = true)
|
addSuspendMarker(mv, isStartNotEnd = true, isSuspensionPoint == SuspensionPointKind.NOT_INLINE)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (irFunction.isInvokeSuspendOfContinuation()) {
|
if (irFunction.isInvokeSuspendOfContinuation()) {
|
||||||
@@ -443,7 +445,7 @@ class ExpressionCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
|
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
|
||||||
addSuspendMarker(mv, isSuspensionPoint, isStartNotEnd = false)
|
addSuspendMarker(mv, isStartNotEnd = false, isSuspensionPoint == SuspensionPointKind.NOT_INLINE)
|
||||||
addInlineMarker(mv, isStartNotEnd = false)
|
addInlineMarker(mv, isStartNotEnd = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -250,7 +250,7 @@ class IrExpressionLambdaImpl(
|
|||||||
|
|
||||||
override fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor = function.descriptor
|
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
|
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
|
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
|
@kotlin.Metadata
|
||||||
public final class TcoContinuationKt$foo$$inlined$map$1$2 {
|
public final class TcoContinuationKt$foo$$inlined$map$1$2 {
|
||||||
synthetic final field $this_flow$inlined: FlowCollector
|
synthetic final field $this_flow$inlined: FlowCollector
|
||||||
synthetic final field this$0: TcoContinuationKt$foo$$inlined$map$1
|
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
|
||||||
|
inner class TcoContinuationKt$foo$$inlined$map$1$2$1
|
||||||
public method <init>(p0: FlowCollector, p1: TcoContinuationKt$foo$$inlined$map$1): void
|
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
|
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
|
@kotlin.Metadata
|
||||||
public final class TcoContinuationKt$map$$inlined$transform$1$2$1 {
|
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 data: java.lang.Object
|
||||||
synthetic field exception: java.lang.Throwable
|
synthetic field exception: java.lang.Throwable
|
||||||
synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$1$2
|
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
|
@kotlin.Metadata
|
||||||
public final class TcoContinuationKt$map$$inlined$transform$2$2$1 {
|
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 data: java.lang.Object
|
||||||
synthetic field exception: java.lang.Throwable
|
synthetic field exception: java.lang.Throwable
|
||||||
synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$2$2
|
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
|
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
|
@kotlin.Metadata
|
||||||
public final class TcoContinuationKt$foo$$inlined$map$1$2 {
|
public final class TcoContinuationKt$foo$$inlined$map$1$2 {
|
||||||
synthetic final field $this_flow$inlined: FlowCollector
|
synthetic final field $this_flow$inlined: FlowCollector
|
||||||
synthetic final field this$0: TcoContinuationKt$foo$$inlined$map$1
|
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
|
||||||
|
inner class TcoContinuationKt$foo$$inlined$map$1$2$1
|
||||||
public method <init>(p0: FlowCollector, p1: TcoContinuationKt$foo$$inlined$map$1): void
|
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
|
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.Metadata
|
||||||
|
@kotlin.coroutines.jvm.internal.DebugMetadata
|
||||||
public final class TcoContinuationKt$map$$inlined$transform$1$2$1 {
|
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
|
field label: int
|
||||||
synthetic field result: java.lang.Object
|
synthetic field result: java.lang.Object
|
||||||
synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$1$2
|
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.Metadata
|
||||||
|
@kotlin.coroutines.jvm.internal.DebugMetadata
|
||||||
public final class TcoContinuationKt$map$$inlined$transform$2$2$1 {
|
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
|
field label: int
|
||||||
synthetic field result: java.lang.Object
|
synthetic field result: java.lang.Object
|
||||||
synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$2$2
|
synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$2$2
|
||||||
|
|||||||
Reference in New Issue
Block a user