JVM_IR: Support crossinline suspend lambdas

The main idea is the following: since we need to generate
(fake)continuations before inlining, we move IrClasses of suspend
lambdas and continuation classes of named functions into the functions.
Thus, it allows the codegen to generate them prior to inlining and
the inliner will happily transform them for us.
Because of that, lowerings which transform call-site function are likely
to change reference to lowered suspend lambdas or functions.
Hence, do not rely on references to lowered suspend lambdas or
functions, instead, rely on attributes.

Do not generate continuation for inline suspend lambdas.
Previously, inline suspend lambdas were treated like suspend functions,
thus we generated continuations for them. Now we just do not treat them
as suspend functions or lambdas during AddContinuationLowering.
We should add continuation parameter to them, however.

Do not generate secondary constructor for suspend lambdas, otherwise,
the inliner is unable to transform them (it requires only one
constructor to be present).

Generate continuation classes for suspend functions as first statement
inside the function.
This enables suspend functions in local object inside inline functions.
Since we already have attributes inside suspend named functions, we
just reuse them to generate continuation class names. This allows us
to close the gap between code generated by old back-end and the new
one.

If a suspend named function captures crossinline lambda, we should
generate a template for inliner: a copy of the function without
state-machine and a continuation constructor call. The call is needed
so the inliner transforms the continuation as well.

Refactor CoroutineTransformerMethodVisitor, so it no longer depends on
PSI.
This commit is contained in:
Ilmir Usmanov
2019-10-22 20:02:34 +03:00
parent 3b37f6bd32
commit a1448ebb37
55 changed files with 680 additions and 366 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.codegen.coroutines package org.jetbrains.kotlin.codegen.coroutines
import com.intellij.util.ArrayUtil import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure import org.jetbrains.kotlin.codegen.binding.CalculatedClosure
@@ -17,6 +18,7 @@ import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.inline.coroutines.SurroundSuspendLambdaCallsWithSuspendMarkersMethodVisitor import org.jetbrains.kotlin.codegen.inline.coroutines.SurroundSuspendLambdaCallsWithSuspendMarkersMethodVisitor
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.isReleaseCoroutines import org.jetbrains.kotlin.config.isReleaseCoroutines
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
@@ -32,6 +34,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
@@ -470,8 +473,9 @@ class CoroutineCodegenForLambda private constructor(
val stateMachineBuilder = CoroutineTransformerMethodVisitor( val stateMachineBuilder = CoroutineTransformerMethodVisitor(
mv, access, name, desc, null, null, mv, access, name, desc, null, null,
obtainClassBuilderForCoroutineState = { v }, obtainClassBuilderForCoroutineState = { v },
element = element, reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(element, state, it) },
diagnostics = state.diagnostics, lineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0,
sourceFile = element.containingKtFile.name,
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization, shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = v.thisName, containingClassInternalName = v.thisName,
isForNamedFunction = false, isForNamedFunction = false,
@@ -768,3 +772,7 @@ private object FailingFunctionGenerationStrategy : FunctionGenerationStrategy()
error("This functions must not be called") error("This functions must not be called")
} }
} }
fun reportSuspensionPointInsideMonitor(element: KtElement, state: GenerationState, stackTraceElement: String) {
state.diagnostics.report(ErrorsJvm.SUSPENSION_POINT_INSIDE_MONITOR.on(element, stackTraceElement))
}
@@ -60,13 +60,13 @@ class CoroutineTransformerMethodVisitor(
private val isForNamedFunction: Boolean, private val isForNamedFunction: Boolean,
private val shouldPreserveClassInitialization: Boolean, private val shouldPreserveClassInitialization: Boolean,
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
// These two are needed to report diagnostics about suspension points inside critical section
private val element: KtElement,
private val diagnostics: DiagnosticSink,
// Since tail-call optimization of functions with Unit return type relies on ability of call-site to recognize them, // Since tail-call optimization of functions with Unit return type relies on ability of call-site to recognize them,
// in order to ignore return value and push Unit, when we cannot ensure this ability, for example, when the function overrides function, // in order to ignore return value and push Unit, when we cannot ensure this ability, for example, when the function overrides function,
// returning Any, we need to disable tail-call optimization for these functions. // returning Any, we need to disable tail-call optimization for these functions.
private val disableTailCallOptimizationForFunctionReturningUnit: Boolean, private val disableTailCallOptimizationForFunctionReturningUnit: Boolean,
private val reportSuspensionPointInsideMonitor: (String) -> Unit,
private val lineNumber: Int,
private val sourceFile: String,
// It's only matters for named functions, may differ from '!isStatic(access)' in case of DefaultImpls // It's only matters for named functions, may differ from '!isStatic(access)' in case of DefaultImpls
private val needDispatchReceiver: Boolean = false, private val needDispatchReceiver: Boolean = false,
// May differ from containingClassInternalName in case of DefaultImpls // May differ from containingClassInternalName in case of DefaultImpls
@@ -76,8 +76,6 @@ class CoroutineTransformerMethodVisitor(
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) { ) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
private val classBuilderForCoroutineState: ClassBuilder by lazy(obtainClassBuilderForCoroutineState) private val classBuilderForCoroutineState: ClassBuilder by lazy(obtainClassBuilderForCoroutineState)
private val lineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0
private val sourceFile = element.containingKtFile.name
private var continuationIndex = if (isForNamedFunction) -1 else 0 private var continuationIndex = if (isForNamedFunction) -1 else 0
private var dataIndex = if (isForNamedFunction) -1 else 1 private var dataIndex = if (isForNamedFunction) -1 else 1
@@ -315,7 +313,7 @@ class CoroutineTransformerMethodVisitor(
sourceFile, sourceFile,
findSuspensionPointLineNumber(suspensionPoint)?.line ?: -1 findSuspensionPointLineNumber(suspensionPoint)?.line ?: -1
) )
diagnostics.report(ErrorsJvm.SUSPENSION_POINT_INSIDE_MONITOR.on(element, "$stackTraceElement")) reportSuspensionPointInsideMonitor("$stackTraceElement")
return return
} }
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.codegen.coroutines package org.jetbrains.kotlin.codegen.coroutines
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.inline.addFakeContinuationConstructorCallMarker import org.jetbrains.kotlin.codegen.inline.addFakeContinuationConstructorCallMarker
@@ -96,8 +97,9 @@ open class SuspendFunctionGenerationStrategy(
return CoroutineTransformerMethodVisitor( return CoroutineTransformerMethodVisitor(
mv, access, name, desc, null, null, containingClassInternalName, this::classBuilderForCoroutineState, mv, access, name, desc, null, null, containingClassInternalName, this::classBuilderForCoroutineState,
isForNamedFunction = true, isForNamedFunction = true,
element = declaration, reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(declaration, state, it) },
diagnostics = state.diagnostics, lineNumber = CodegenUtil.getLineNumberForElement(declaration, false) ?: 0,
sourceFile = declaration.containingKtFile.name,
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization, shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
needDispatchReceiver = originalSuspendDescriptor.dispatchReceiverParameter != null, needDispatchReceiver = originalSuspendDescriptor.dispatchReceiverParameter != null,
internalNameForDispatchReceiver = containingClassInternalNameOrNull(), internalNameForDispatchReceiver = containingClassInternalNameOrNull(),
@@ -302,7 +302,8 @@ class AnonymousObjectTransformer(
sourceNode.name, sourceNode.name,
if (isConstructor) transformationInfo.newConstructorDescriptor else sourceNode.desc, if (isConstructor) transformationInfo.newConstructorDescriptor else sourceNode.desc,
inliningContext.callSiteInfo.isInlineOrInsideInline, inliningContext.callSiteInfo.isInlineOrInsideInline,
isSuspendFunctionOrLambda(sourceNode) isSuspendFunctionOrLambda(sourceNode),
inliningContext.root.sourceCompilerForInline.inlineCallSiteInfo.lineNumber
), null ), null
) )
@@ -10,5 +10,6 @@ class InlineCallSiteInfo(
val functionName: String?, val functionName: String?,
val functionDesc: String?, val functionDesc: String?,
val isInlineOrInsideInline: Boolean, val isInlineOrInsideInline: Boolean,
val isSuspend: Boolean val isSuspend: Boolean,
val lineNumber: Int
) )
@@ -157,6 +157,7 @@ class MethodInliner(
), AsmTypeRemapper(remapper, result) ), AsmTypeRemapper(remapper, result)
) )
val fakeContinuationName = CoroutineTransformer.findFakeContinuationConstructorClassName(node)
val markerShift = calcMarkerShift(parameters, node) val markerShift = calcMarkerShift(parameters, node)
val lambdaInliner = object : InlineAdapter(remappingMethodAdapter, parameters.argsSizeOnStack, sourceMapper) { val lambdaInliner = object : InlineAdapter(remappingMethodAdapter, parameters.argsSizeOnStack, sourceMapper) {
private var transformationInfo: TransformationInfo? = null private var transformationInfo: TransformationInfo? = null
@@ -176,9 +177,7 @@ class MethodInliner(
inlineCallSiteInfo inlineCallSiteInfo
) )
val transformer = transformationInfo!!.createTransformer( val transformer = transformationInfo!!.createTransformer(
childInliningContext, childInliningContext, isSameModule, fakeContinuationName
isSameModule,
CoroutineTransformer.findFakeContinuationConstructorClassName(node)
) )
val transformResult = transformer.doTransform(nodeRemapper) val transformResult = transformer.doTransform(nodeRemapper)
@@ -75,6 +75,8 @@ interface SourceCompilerForInline {
val compilationContextFunctionDescriptor: FunctionDescriptor val compilationContextFunctionDescriptor: FunctionDescriptor
fun getContextLabels(): Set<String> fun getContextLabels(): Set<String>
fun reportSuspensionPointInsideMonitor(stackTraceElement: String)
} }
@@ -120,7 +122,8 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
signature.asmMethod.name, signature.asmMethod.name,
signature.asmMethod.descriptor, signature.asmMethod.descriptor,
compilationContextFunctionDescriptor.isInlineOrInsideInline(), compilationContextFunctionDescriptor.isInlineOrInsideInline(),
compilationContextFunctionDescriptor.isSuspend compilationContextFunctionDescriptor.isSuspend,
CodegenUtil.getLineNumberForElement(callElement, false) ?: 0
) )
} }
@@ -365,6 +368,10 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
) )
} }
override fun reportSuspensionPointInsideMonitor(stackTraceElement: String) {
org.jetbrains.kotlin.codegen.coroutines.reportSuspensionPointInsideMonitor(callElement, state, stackTraceElement)
}
companion object { companion object {
fun getContext( fun getContext(
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
@@ -6,6 +6,7 @@
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.backend.common.CodegenUtil
import org.jetbrains.kotlin.codegen.AsmUtil.CAPTURED_THIS_FIELD 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.TransformationMethodVisitor
@@ -66,29 +67,20 @@ class CoroutineTransformer(
if (state.languageVersionSettings.isReleaseCoroutines()) superClassName.endsWith("ContinuationImpl") if (state.languageVersionSettings.isReleaseCoroutines()) superClassName.endsWith("ContinuationImpl")
else methods.any { it.name == "getLabel" } else methods.any { it.name == "getLabel" }
private fun crossinlineLambda(): PsiExpressionLambda? = inliningContext.expressionMap.values.find {
it is PsiExpressionLambda && it.isCrossInline
}?.cast()
private fun isStateMachine(node: MethodNode): Boolean = private fun isStateMachine(node: MethodNode): Boolean =
node.instructions.asSequence().any { insn -> insn is LdcInsnNode && insn.cst == ILLEGAL_STATE_ERROR_MESSAGE } node.instructions.asSequence().any { insn -> insn is LdcInsnNode && insn.cst == ILLEGAL_STATE_ERROR_MESSAGE }
private fun isSuspendLambda(node: MethodNode) = isResumeImpl(node) private fun isSuspendLambda(node: MethodNode) = isResumeImpl(node)
fun newMethod(node: MethodNode): DeferredMethodVisitor { fun newMethod(node: MethodNode): DeferredMethodVisitor {
// Find ANY element to report error about suspension point in monitor on.
val element = crossinlineLambda()?.functionWithBodyOrCallableReference
?: inliningContext.root.sourceCompilerForInline.callElement as? KtElement
?: error("crossinline lambda should have element")
return when { return when {
isResumeImpl(node) -> { isResumeImpl(node) -> {
assert(!isStateMachine(node)) { assert(!isStateMachine(node)) {
"Inlining/transforming state-machine" "Inlining/transforming state-machine"
} }
newStateMachineForLambda(node, element) newStateMachineForLambda(node)
} }
isSuspendFunctionWithFakeConstructorCall(node) -> newStateMachineForNamedFunction(node, element) isSuspendFunctionWithFakeConstructorCall(node) -> newStateMachineForNamedFunction(node)
else -> error("no need to generate state maching for ${node.name}") else -> error("no need to generate state maching for ${node.name}")
} }
} }
@@ -99,7 +91,7 @@ class CoroutineTransformer(
private fun isSuspendFunctionWithFakeConstructorCall(node: MethodNode): Boolean = findFakeContinuationConstructorClassName(node) != null private fun isSuspendFunctionWithFakeConstructorCall(node: MethodNode): Boolean = findFakeContinuationConstructorClassName(node) != null
private fun newStateMachineForLambda(node: MethodNode, element: KtElement): DeferredMethodVisitor { private fun newStateMachineForLambda(node: MethodNode): DeferredMethodVisitor {
val name = node.name.removeSuffix(FOR_INLINE_SUFFIX) val name = node.name.removeSuffix(FOR_INLINE_SUFFIX)
return DeferredMethodVisitor( return DeferredMethodVisitor(
MethodNode( MethodNode(
@@ -107,13 +99,16 @@ class CoroutineTransformer(
ArrayUtil.toStringArray(node.exceptions) ArrayUtil.toStringArray(node.exceptions)
) )
) { ) {
val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline
val stateMachineBuilder = surroundNoinlineCallsWithMarkers( val stateMachineBuilder = surroundNoinlineCallsWithMarkers(
node, node,
CoroutineTransformerMethodVisitor( CoroutineTransformerMethodVisitor(
createNewMethodFrom(node, name), node.access, name, node.desc, null, null, createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
obtainClassBuilderForCoroutineState = { classBuilder }, obtainClassBuilderForCoroutineState = { classBuilder },
element = element, reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
diagnostics = state.diagnostics, // 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, languageVersionSettings = state.languageVersionSettings,
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = classBuilder.thisName, containingClassInternalName = classBuilder.thisName,
@@ -137,7 +132,7 @@ class CoroutineTransformer(
} }
} }
private fun newStateMachineForNamedFunction(node: MethodNode, element: KtElement): DeferredMethodVisitor { private fun newStateMachineForNamedFunction(node: MethodNode): DeferredMethodVisitor {
val name = node.name.removeSuffix(FOR_INLINE_SUFFIX) val name = node.name.removeSuffix(FOR_INLINE_SUFFIX)
val continuationClassName = findFakeContinuationConstructorClassName(node) val continuationClassName = findFakeContinuationConstructorClassName(node)
assert(inliningContext is RegeneratedClassContext) assert(inliningContext is RegeneratedClassContext)
@@ -149,20 +144,23 @@ 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 stateMachineBuilder = surroundNoinlineCallsWithMarkers( val stateMachineBuilder = surroundNoinlineCallsWithMarkers(
node, node,
CoroutineTransformerMethodVisitor( CoroutineTransformerMethodVisitor(
createNewMethodFrom(node, name), node.access, name, node.desc, null, null, createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! }, obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
element = element, reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
diagnostics = state.diagnostics, lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
languageVersionSettings = state.languageVersionSettings, languageVersionSettings = state.languageVersionSettings,
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = classBuilder.thisName, containingClassInternalName = classBuilder.thisName,
isForNamedFunction = true, isForNamedFunction = true,
needDispatchReceiver = true, needDispatchReceiver = true,
internalNameForDispatchReceiver = classBuilder.thisName, internalNameForDispatchReceiver = classBuilder.thisName,
disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization,
putContinuationParameterToLvt = !state.isIrBackend
) )
) )
@@ -233,7 +231,7 @@ class CoroutineTransformer(
fun oldContinuationFrom(method: MethodNode): String? = fun oldContinuationFrom(method: MethodNode): String? =
methods.find { it.name == method.name + FOR_INLINE_SUFFIX && it.desc == method.desc } methods.find { it.name == method.name + FOR_INLINE_SUFFIX && it.desc == method.desc }
?.let { findFakeContinuationConstructorClassName(it) } ?.let { findFakeContinuationConstructorClassName(it) }
companion object { companion object {
fun findFakeContinuationConstructorClassName(node: MethodNode): String? { fun findFakeContinuationConstructorClassName(node: MethodNode): String? {
@@ -431,7 +431,7 @@ fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
v.emitInlineMarker(if (isStartNotEnd) INLINE_MARKER_BEFORE_SUSPEND_ID else INLINE_MARKER_AFTER_SUSPEND_ID) v.emitInlineMarker(if (isStartNotEnd) INLINE_MARKER_BEFORE_SUSPEND_ID else INLINE_MARKER_AFTER_SUSPEND_ID)
} }
internal fun addFakeContinuationConstructorCallMarker(v: InstructionAdapter, isStartNotEnd: Boolean) { fun addFakeContinuationConstructorCallMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
v.emitInlineMarker(if (isStartNotEnd) INLINE_MARKER_BEFORE_FAKE_CONTINUATION_CONSTRUCTOR_CALL else INLINE_MARKER_AFTER_FAKE_CONTINUATION_CONSTRUCTOR_CALL) v.emitInlineMarker(if (isStartNotEnd) INLINE_MARKER_BEFORE_FAKE_CONTINUATION_CONSTRUCTOR_CALL else INLINE_MARKER_AFTER_FAKE_CONTINUATION_CONSTRUCTOR_CALL)
} }
@@ -83,6 +83,7 @@ abstract class IrElementTransformerVoidWithContext : IrElementTransformerVoid()
protected val currentScope get() = scopeStack.peek() protected val currentScope get() = scopeStack.peek()
protected val parentScope get() = if (scopeStack.size < 2) null else scopeStack[scopeStack.size - 2] protected val parentScope get() = if (scopeStack.size < 2) null else scopeStack[scopeStack.size - 2]
protected val allScopes get() = scopeStack protected val allScopes get() = scopeStack
protected val currentDeclarationParent get() = allScopes.last { it.irElement is IrDeclarationParent }.irElement as IrDeclarationParent
fun printScopeStack() { fun printScopeStack() {
scopeStack.forEach { println(it.scope.scopeOwner) } scopeStack.forEach { println(it.scope.scopeOwner) }
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.common.ir
import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DumpIrTreeWithDescriptorsVisitor import org.jetbrains.kotlin.backend.common.DumpIrTreeWithDescriptorsVisitor
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
import org.jetbrains.kotlin.backend.common.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
@@ -566,9 +567,17 @@ fun copyBodyToStatic(oldFunction: IrFunction, staticFunction: IrFunction) {
val mapping: Map<IrValueParameter, IrValueParameter> = val mapping: Map<IrValueParameter, IrValueParameter> =
(listOfNotNull(oldFunction.dispatchReceiverParameter, oldFunction.extensionReceiverParameter) + oldFunction.valueParameters) (listOfNotNull(oldFunction.dispatchReceiverParameter, oldFunction.extensionReceiverParameter) + oldFunction.valueParameters)
.zip(staticFunction.valueParameters).toMap() .zip(staticFunction.valueParameters).toMap()
staticFunction.body = oldFunction.body copyBodyWithParametersMapping(staticFunction, oldFunction, mapping)
}
fun copyBodyWithParametersMapping(
newFunction: IrFunction,
oldFunction: IrFunction,
mapping: Map<IrValueParameter, IrValueParameter>
) {
newFunction.body = oldFunction.body?.deepCopyWithSymbols(oldFunction)
?.transform( ?.transform(
object: IrElementTransformerVoid() { object : IrElementTransformerVoid() {
// Remap return targets to the static method so they do not appear to be // Remap return targets to the static method so they do not appear to be
// non-local returns. // non-local returns.
override fun visitReturn(expression: IrReturn): IrExpression { override fun visitReturn(expression: IrReturn): IrExpression {
@@ -578,8 +587,9 @@ fun copyBodyToStatic(oldFunction: IrFunction, staticFunction: IrFunction) {
expression.startOffset, expression.startOffset,
expression.endOffset, expression.endOffset,
expression.type, expression.type,
staticFunction.symbol, newFunction.symbol,
expression.value) expression.value
)
} else expression } else expression
} }
@@ -589,8 +599,9 @@ fun copyBodyToStatic(oldFunction: IrFunction, staticFunction: IrFunction) {
IrGetValueImpl(expression.startOffset, expression.endOffset, it.type, it.symbol, expression.origin) IrGetValueImpl(expression.startOffset, expression.endOffset, it.type, it.symbol, expression.origin)
} ?: expression } ?: expression
}, null) }, null
?.patchDeclarationParents(staticFunction) )
?.patchDeclarationParents(newFunction)
} }
val IrSymbol.isSuspend: Boolean val IrSymbol.isSuspend: Boolean
@@ -87,6 +87,9 @@ class LocalDeclarationsLowering(
object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE : object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE :
IrDeclarationOriginImpl("FIELD_FOR_CAPTURED_VALUE", isSynthetic = true) IrDeclarationOriginImpl("FIELD_FOR_CAPTURED_VALUE", isSynthetic = true)
object DECLARATION_ORIGIN_FIELD_FOR_CROSSINLINE_CAPTURED_VALUE :
IrDeclarationOriginImpl("FIELD_FOR_CROSSINLINE_CAPTURED_VALUE", isSynthetic = true)
private object STATEMENT_ORIGIN_INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE : private object STATEMENT_ORIGIN_INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE :
IrStatementOriginImpl("INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE") IrStatementOriginImpl("INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE")
@@ -655,14 +658,15 @@ class LocalDeclarationsLowering(
name: Name, name: Name,
visibility: Visibility, visibility: Visibility,
parent: IrClass, parent: IrClass,
fieldType: IrType fieldType: IrType,
isCrossinline: Boolean
): IrField { ): IrField {
val descriptor = WrappedFieldDescriptor() val descriptor = WrappedFieldDescriptor()
val symbol = IrFieldSymbolImpl(descriptor) val symbol = IrFieldSymbolImpl(descriptor)
return IrFieldImpl( return IrFieldImpl(
startOffset, startOffset,
endOffset, endOffset,
DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE, if (isCrossinline) DECLARATION_ORIGIN_FIELD_FOR_CROSSINLINE_CAPTURED_VALUE else DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE,
symbol, symbol,
name, name,
fieldType, fieldType,
@@ -682,16 +686,18 @@ class LocalDeclarationsLowering(
val generatedNames = mutableSetOf<Name>() val generatedNames = mutableSetOf<Name>()
localClassContext.closure.capturedValues.forEach { capturedValue -> localClassContext.closure.capturedValues.forEach { capturedValue ->
val owner = capturedValue.owner
val irField = createFieldForCapturedValue( val irField = createFieldForCapturedValue(
classDeclaration.startOffset, classDeclaration.startOffset,
classDeclaration.endOffset, classDeclaration.endOffset,
suggestNameForCapturedValue(capturedValue.owner, generatedNames), suggestNameForCapturedValue(owner, generatedNames),
Visibilities.PRIVATE, Visibilities.PRIVATE,
classDeclaration, classDeclaration,
capturedValue.owner.type owner.type,
owner is IrValueParameter && owner.isCrossinline
) )
localClassContext.capturedValueToField[capturedValue.owner] = irField localClassContext.capturedValueToField[owner] = irField
} }
} }
@@ -176,13 +176,15 @@ fun IrDeclarationContainer.addFunction(
returnType: IrType, returnType: IrType,
modality: Modality = Modality.FINAL, modality: Modality = Modality.FINAL,
isStatic: Boolean = false, isStatic: Boolean = false,
isSuspend: Boolean = false isSuspend: Boolean = false,
origin: IrDeclarationOrigin = IrDeclarationOrigin.DEFINED
): IrSimpleFunction = ): IrSimpleFunction =
addFunction { addFunction {
this.name = Name.identifier(name) this.name = Name.identifier(name)
this.returnType = returnType this.returnType = returnType
this.modality = modality this.modality = modality
this.isSuspend = isSuspend this.isSuspend = isSuspend
this.origin = origin
}.apply { }.apply {
if (!isStatic) { if (!isStatic) {
dispatchReceiverParameter = parentAsClass.thisReceiver!!.copyTo(this) dispatchReceiverParameter = parentAsClass.thisReceiver!!.copyTo(this)
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.jetbrains.kotlin.backend.jvm package org.jetbrains.kotlin.backend.jvm
@@ -51,4 +40,9 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object POLYMORPHIC_SIGNATURE_INSTANTIATION : IrDeclarationOriginImpl("POLYMORPHIC_SIGNATURE_INSTANTIATION", isSynthetic = true) object POLYMORPHIC_SIGNATURE_INSTANTIATION : IrDeclarationOriginImpl("POLYMORPHIC_SIGNATURE_INSTANTIATION", isSynthetic = true)
object ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER : IrDeclarationOriginImpl("ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER", isSynthetic = true) object ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER : IrDeclarationOriginImpl("ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER", isSynthetic = true)
object OBJECT_SUPER_CONSTRUCTOR_PARAMETER : IrDeclarationOriginImpl("OBJECT_SUPER_CONSTURCTOR_PARAMETER", isSynthetic = true) object OBJECT_SUPER_CONSTRUCTOR_PARAMETER : IrDeclarationOriginImpl("OBJECT_SUPER_CONSTURCTOR_PARAMETER", isSynthetic = true)
object CONTINUATION_CLASS : IrDeclarationOriginImpl("CONTINUATION_CLASS")
object SUSPEND_LAMBDA : IrDeclarationOriginImpl("SUSPEND_LAMBDA")
object FOR_INLINE_STATE_MACHINE_TEMPLATE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE")
object FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE_CROSSINLINE")
object FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE_CROSSINLINE_VIEW")
} }
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.ir.builders.irNull
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
@@ -97,9 +98,8 @@ class JvmBackendContext(
override val internalPackageFqn = FqName("kotlin.jvm") override val internalPackageFqn = FqName("kotlin.jvm")
val suspendFunctionContinuations = mutableMapOf<IrFunction, IrClass>() val suspendLambdaToOriginalFunctionMap = mutableMapOf<IrFunctionReference, IrFunction>()
val suspendLambdaToOriginalFunctionMap = mutableMapOf<IrClass, IrFunction>() val continuationClassBuilders = mutableMapOf<IrSimpleFunction, ClassBuilder>()
val continuationClassBuilders = mutableMapOf<IrClass, ClassBuilder>()
val suspendFunctionOriginalToView = mutableMapOf<IrFunction, IrFunction>() val suspendFunctionOriginalToView = mutableMapOf<IrFunction, IrFunction>()
val suspendFunctionViewToOriginal = mutableMapOf<IrFunction, IrFunction>() val suspendFunctionViewToOriginal = mutableMapOf<IrFunction, IrFunction>()
val fakeContinuation: IrExpression = createFakeContinuation(this) val fakeContinuation: IrExpression = createFakeContinuation(this)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.backend.jvm.codegen package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField
import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField
@@ -126,12 +127,6 @@ open class ClassCodegen protected constructor(
} else null } else null
} }
// Suspend function state-machine builder requires half-built continuation class
val continuationCodegens = nestedClasses.filter { it.irClass in context.suspendFunctionContinuations.values }
for (continuationCodegen in continuationCodegens) {
continuationCodegen.generate()
}
val fileEntry = context.psiSourceManager.getFileEntry(irClass.fileParent) val fileEntry = context.psiSourceManager.getFileEntry(irClass.fileParent)
if (fileEntry != null) { if (fileEntry != null) {
/* TODO: Temporary workaround: ClassBuilder needs a pathless name. */ /* TODO: Temporary workaround: ClassBuilder needs a pathless name. */
@@ -153,14 +148,14 @@ open class ClassCodegen protected constructor(
// Generate nested classes at the end, to ensure that codegen for companion object will have the necessary JVM signatures in its // Generate nested classes at the end, to ensure that codegen for companion object will have the necessary JVM signatures in its
// trace for properties moved to the outer class // trace for properties moved to the outer class
for (codegen in (nestedClasses - continuationCodegens)) { for (codegen in nestedClasses) {
codegen.generate() codegen.generate()
} }
generateKotlinMetadataAnnotation() generateKotlinMetadataAnnotation()
if (irClass in context.suspendFunctionContinuations.values) { if (irClass.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS) {
context.continuationClassBuilders[irClass] = visitor context.continuationClassBuilders[irClass.attributeOwnerId as IrSimpleFunction] = visitor
} else { } else {
done() done()
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.jvm.codegen package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.common.ir.isSuspend import org.jetbrains.kotlin.backend.common.ir.isSuspend
@@ -14,6 +15,8 @@ import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor
import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME
import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME
import org.jetbrains.kotlin.codegen.coroutines.reportSuspensionPointInsideMonitor
import org.jetbrains.kotlin.codegen.inline.addFakeContinuationConstructorCallMarker
import org.jetbrains.kotlin.config.isReleaseCoroutines import org.jetbrains.kotlin.config.isReleaseCoroutines
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
@@ -37,11 +40,14 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import org.jetbrains.org.objectweb.asm.MethodVisitor import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
internal fun generateStateMachineForNamedFunction( internal fun generateStateMachineForNamedFunction(
irFunction: IrFunction, irFunction: IrFunction,
@@ -49,21 +55,19 @@ internal fun generateStateMachineForNamedFunction(
methodVisitor: MethodVisitor, methodVisitor: MethodVisitor,
access: Int, access: Int,
signature: JvmMethodGenericSignature, signature: JvmMethodGenericSignature,
continuationClassBuilder: ClassBuilder?, obtainContinuationClassBuilder: () -> ClassBuilder,
element: KtElement element: KtElement
): MethodVisitor { ): MethodVisitor {
assert(irFunction.isSuspend) assert(irFunction.isSuspend)
assert(continuationClassBuilder != null) {
"Class builder for continuation is null"
}
val state = classCodegen.state val state = classCodegen.state
val languageVersionSettings = state.languageVersionSettings val languageVersionSettings = state.languageVersionSettings
assert(languageVersionSettings.isReleaseCoroutines()) { "Experimental coroutines are unsupported in JVM_IR backend" } assert(languageVersionSettings.isReleaseCoroutines()) { "Experimental coroutines are unsupported in JVM_IR backend" }
return CoroutineTransformerMethodVisitor( return CoroutineTransformerMethodVisitor(
methodVisitor, access, signature.asmMethod.name, signature.asmMethod.descriptor, signature.genericsSignature, null, methodVisitor, access, signature.asmMethod.name, signature.asmMethod.descriptor, null, null,
obtainClassBuilderForCoroutineState = { continuationClassBuilder!! }, obtainClassBuilderForCoroutineState = obtainContinuationClassBuilder,
element = element, reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(element, state, it) },
diagnostics = state.diagnostics, lineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0,
sourceFile = classCodegen.irClass.file.name,
languageVersionSettings = languageVersionSettings, languageVersionSettings = languageVersionSettings,
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = classCodegen.visitor.thisName, containingClassInternalName = classCodegen.visitor.thisName,
@@ -92,8 +96,9 @@ internal fun generateStateMachineForLambda(
return CoroutineTransformerMethodVisitor( return CoroutineTransformerMethodVisitor(
methodVisitor, access, signature.asmMethod.name, signature.asmMethod.descriptor, signature.genericsSignature, null, methodVisitor, access, signature.asmMethod.name, signature.asmMethod.descriptor, signature.genericsSignature, null,
obtainClassBuilderForCoroutineState = { classCodegen.visitor }, obtainClassBuilderForCoroutineState = { classCodegen.visitor },
element = element, reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(element, state, it) },
diagnostics = state.diagnostics, lineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0,
sourceFile = classCodegen.irClass.file.name,
languageVersionSettings = languageVersionSettings, languageVersionSettings = languageVersionSettings,
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
containingClassInternalName = classCodegen.visitor.thisName, containingClassInternalName = classCodegen.visitor.thisName,
@@ -102,17 +107,18 @@ internal fun generateStateMachineForLambda(
) )
} }
internal fun IrFunction.isInvokeSuspendOfLambda(context: JvmBackendContext): Boolean = internal fun IrFunction.isInvokeSuspendOfLambda(): Boolean =
name.asString() == INVOKE_SUSPEND_METHOD_NAME && parent in context.suspendLambdaToOriginalFunctionMap name.asString() == INVOKE_SUSPEND_METHOD_NAME && parentAsClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA
internal fun IrFunction.isInvokeOfSuspendLambda(context: JvmBackendContext): Boolean = internal fun IrFunction.isInvokeSuspendForInlineOfLambda(): Boolean =
name.asString() == "invoke" && parent in context.suspendLambdaToOriginalFunctionMap origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE
&& parentAsClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA
internal fun IrFunction.isInvokeSuspendOfContinuation(context: JvmBackendContext): Boolean = internal fun IrFunction.isInvokeSuspendOfContinuation(): Boolean =
name.asString() == INVOKE_SUSPEND_METHOD_NAME && parentAsClass in context.suspendFunctionContinuations.values name.asString() == INVOKE_SUSPEND_METHOD_NAME && parentAsClass.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS
internal fun IrFunction.isInvokeOfSuspendCallableReference(): Boolean = isSuspend && name.asString() == "invoke" && internal fun IrFunction.isInvokeOfSuspendCallableReference(): Boolean = isSuspend && name.asString() == "invoke" &&
(parent as? IrClass)?.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL parentAsClass.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
internal fun IrFunction.isKnownToBeTailCall(): Boolean = internal fun IrFunction.isKnownToBeTailCall(): Boolean =
when (origin) { when (origin) {
@@ -126,13 +132,15 @@ internal fun IrFunction.isKnownToBeTailCall(): Boolean =
else -> isInvokeOfSuspendCallableReference() else -> isInvokeOfSuspendCallableReference()
} }
internal fun IrFunction.shouldNotContainSuspendMarkers(context: JvmBackendContext): Boolean = internal fun IrFunction.shouldNotContainSuspendMarkers(): Boolean =
isInvokeSuspendOfContinuation(context) || isKnownToBeTailCall() isInvokeSuspendOfContinuation() || isKnownToBeTailCall()
// Transform `suspend fun foo(params): RetType` into `fun foo(params, $completion: Continuation<RetType>): Any?` // Transform `suspend fun foo(params): RetType` into `fun foo(params, $completion: Continuation<RetType>): Any?`
// the result is called 'view', just to be consistent with old backend. // the result is called 'view', just to be consistent with old backend.
internal fun IrFunction.getOrCreateSuspendFunctionViewIfNeeded(context: JvmBackendContext): IrFunction { internal fun IrFunction.getOrCreateSuspendFunctionViewIfNeeded(context: JvmBackendContext): IrFunction {
if (!isSuspend || origin == JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW) return this if (!isSuspend || origin == JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW ||
origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW
) return this
context.suspendFunctionOriginalToView[this]?.let { return it } context.suspendFunctionOriginalToView[this]?.let { return it }
return suspendFunctionView(context) return suspendFunctionView(context)
} }
@@ -152,7 +160,12 @@ private fun IrFunction.suspendFunctionView(context: JvmBackendContext): IrFuncti
else else
WrappedSimpleFunctionDescriptor(sourceElement = originalDescriptor.source) WrappedSimpleFunctionDescriptor(sourceElement = originalDescriptor.source)
return IrFunctionImpl( return IrFunctionImpl(
startOffset, endOffset, JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW, IrSimpleFunctionSymbolImpl(descriptor), startOffset, endOffset,
if (origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE)
JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW
else
JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW,
IrSimpleFunctionSymbolImpl(descriptor),
name, visibility, modality, context.irBuiltIns.anyNType, name, visibility, modality, context.irBuiltIns.anyNType,
isInline = isInline, isExternal = isExternal, isTailrec = isTailrec, isSuspend = isSuspend, isExpect = isExpect, isInline = isInline, isExternal = isExternal, isTailrec = isTailrec, isSuspend = isSuspend, isExpect = isExpect,
isFakeOverride = false, isOperator = false isFakeOverride = false, isOperator = false
@@ -196,6 +209,7 @@ private fun IrFunction.suspendFunctionView(context: JvmBackendContext): IrFuncti
return super.visitCall(expression.createSuspendFunctionCallViewIfNeeded(context, it, callerIsInlineLambda = false)) return super.visitCall(expression.createSuspendFunctionCallViewIfNeeded(context, it, callerIsInlineLambda = false))
} }
}) })
it.copyAttributes(this)
} }
} }
@@ -216,7 +230,8 @@ internal fun IrCall.createSuspendFunctionCallViewIfNeeded(
} }
val continuationParameter = val continuationParameter =
when { when {
caller.isInvokeSuspendOfLambda(context) || caller.isInvokeSuspendOfContinuation(context) -> caller.isInvokeSuspendOfLambda() || caller.isInvokeSuspendOfContinuation() ||
caller.isInvokeSuspendForInlineOfLambda() ->
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.dispatchReceiverParameter!!.symbol) IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.dispatchReceiverParameter!!.symbol)
callerIsInlineLambda -> context.fakeContinuation callerIsInlineLambda -> context.fakeContinuation
else -> IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.valueParameters.last().symbol) else -> IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.valueParameters.last().symbol)
@@ -231,3 +246,31 @@ internal fun createFakeContinuation(context: JvmBackendContext): IrExpression =
context.ir.symbols.continuationClass.createType(true, listOf(makeTypeProjection(context.irBuiltIns.anyNType, Variance.INVARIANT))), context.ir.symbols.continuationClass.createType(true, listOf(makeTypeProjection(context.irBuiltIns.anyNType, Variance.INVARIANT))),
"FAKE_CONTINUATION" "FAKE_CONTINUATION"
) )
internal fun generateFakeContinuationConstructorCall(
v: InstructionAdapter,
containingClassBuilder: ClassBuilder,
classBuilder: ClassBuilder,
irFunction: IrFunction
) {
val continuationType = Type.getObjectType(classBuilder.thisName)
// TODO: This is different in case of DefaultImpls
val thisNameType = Type.getObjectType(containingClassBuilder.thisName.replace(".", "/"))
val continuationIndex = listOfNotNull(irFunction.dispatchReceiverParameter, irFunction.extensionReceiverParameter).size +
irFunction.valueParameters.size - 1
with(v) {
addFakeContinuationConstructorCallMarker(this, true)
anew(continuationType)
dup()
if (irFunction.dispatchReceiverParameter != null) {
load(0, AsmTypes.OBJECT_TYPE)
load(continuationIndex, Type.getObjectType("kotlin/coroutines/Continuation"))
invokespecial(continuationType.internalName, "<init>", "(${thisNameType}Lkotlin/coroutines/Continuation;)V", false)
} else {
load(continuationIndex, Type.getObjectType("kotlin/coroutines/Continuation"))
invokespecial(continuationType.internalName, "<init>", "(Lkotlin/coroutines/Continuation;)V", false)
}
pop()
addFakeContinuationConstructorCallMarker(this, false)
}
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.codegen.inline.*
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.putNeedClassReificationMarker import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.putNeedClassReificationMarker
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.AS import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.AS
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.SAFE_AS import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind.SAFE_AS
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
import org.jetbrains.kotlin.codegen.pseudoInsns.fixStackAndJump import org.jetbrains.kotlin.codegen.pseudoInsns.fixStackAndJump
@@ -48,9 +49,11 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.model.TypeParameterMarker import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.keysToMap import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.kotlin.utils.sure
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -193,6 +196,7 @@ class ExpressionCodegen(
val info = BlockInfo() val info = BlockInfo()
val body = irFunction.body!! val body = irFunction.body!!
generateNonNullAssertions() generateNonNullAssertions()
generateFakeContinuationConstructorIfNeeded()
val result = body.accept(this, info) val result = body.accept(this, info)
// If this function has an expression body, return the result of that expression. // If this function has an expression body, return the result of that expression.
// Otherwise, if it does not end in a return statement, it must be void-returning, // Otherwise, if it does not end in a return statement, it must be void-returning,
@@ -213,6 +217,19 @@ class ExpressionCodegen(
writeParameterInLocalVariableTable(startLabel, endLabel) writeParameterInLocalVariableTable(startLabel, endLabel)
} }
private fun generateFakeContinuationConstructorIfNeeded() {
if (irFunction.origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW) return
val continuationClass = classCodegen.irClass.functions.find {
it.name.asString() == irFunction.name.asString().removeSuffix(FOR_INLINE_SUFFIX)
}?.body?.statements?.get(0) ?: error("could not find continuation for ${irFunction.render()}")
generateFakeContinuationConstructorCall(
mv,
classCodegen.visitor,
context.continuationClassBuilders[(continuationClass as IrClass).attributeOwnerId]!!,
irFunction
)
}
private fun generateNonNullAssertions() { private fun generateNonNullAssertions() {
if (state.isParamAssertionsDisabled) if (state.isParamAssertionsDisabled)
return return
@@ -229,14 +246,17 @@ class ExpressionCodegen(
irFunction.origin == IrDeclarationOrigin.BRIDGE_SPECIAL || irFunction.origin == IrDeclarationOrigin.BRIDGE_SPECIAL ||
irFunction.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE || irFunction.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE ||
irFunction.origin == JvmLoweredDeclarationOrigin.JVM_STATIC_WRAPPER || irFunction.origin == JvmLoweredDeclarationOrigin.JVM_STATIC_WRAPPER ||
irFunction.origin == JvmLoweredDeclarationOrigin.MULTIFILE_BRIDGE irFunction.origin == JvmLoweredDeclarationOrigin.MULTIFILE_BRIDGE ||
irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS ||
irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA
if (notCallableFromJava) if (notCallableFromJava)
return return
// Do not generate non-null checks for suspend function views. When resumed the arguments // Do not generate non-null checks for suspend function views. When resumed the arguments
// will be null and the actual values are taken from the continuation. // will be null and the actual values are taken from the continuation.
val isSuspendFunctionView = irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW val isSuspendFunctionView = irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_FUNCTION_VIEW ||
irFunction.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE_VIEW
if (isSuspendFunctionView) if (isSuspendFunctionView)
return return
@@ -381,7 +401,7 @@ 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}")
callee.isSuspend && !irFunction.shouldNotContainSuspendMarkers(classCodegen.context) -> callee.isSuspend && !irFunction.shouldNotContainSuspendMarkers() ->
addInlineMarker(mv, isStartNotEnd = true) addInlineMarker(mv, isStartNotEnd = true)
} }
@@ -407,13 +427,13 @@ class ExpressionCodegen(
expression.markLineNumber(true) expression.markLineNumber(true)
// Do not generate redundant markers in continuation class. // Do not generate redundant markers in continuation class.
if (callee.isSuspend && !irFunction.shouldNotContainSuspendMarkers(classCodegen.context)) { if (callee.isSuspend && !irFunction.shouldNotContainSuspendMarkers()) {
addSuspendMarker(mv, isStartNotEnd = true) addSuspendMarker(mv, isStartNotEnd = true)
} }
callGenerator.genCall(callable, this, expression) callGenerator.genCall(callable, this, expression)
if (callee.isSuspend && !irFunction.shouldNotContainSuspendMarkers(classCodegen.context)) { if (callee.isSuspend && !irFunction.shouldNotContainSuspendMarkers()) {
addSuspendMarker(mv, isStartNotEnd = false) addSuspendMarker(mv, isStartNotEnd = false)
addInlineMarker(mv, isStartNotEnd = false) addInlineMarker(mv, isStartNotEnd = false)
} }
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.mangleNameIfNeeded import org.jetbrains.kotlin.codegen.mangleNameIfNeeded
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_IMPL_NAME_SUFFIX
import org.jetbrains.kotlin.codegen.visitAnnotableParameterCount import org.jetbrains.kotlin.codegen.visitAnnotableParameterCount
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
@@ -28,6 +30,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.sure
import org.jetbrains.org.objectweb.asm.MethodVisitor import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -77,34 +80,56 @@ open class FunctionCodegen(
generateAnnotationDefaultValueIfNeeded(methodVisitor) generateAnnotationDefaultValueIfNeeded(methodVisitor)
} else { } else {
val frameMap = createFrameMapWithReceivers() val frameMap = createFrameMapWithReceivers()
val irClass = context.suspendFunctionContinuations[irFunction]
val element = (irFunction.symbol.descriptor.psiElement
?: context.suspendLambdaToOriginalFunctionMap[irFunction.parent]?.symbol?.descriptor?.psiElement) as? KtElement
val continuationClassBuilder = context.continuationClassBuilders[irClass]
methodVisitor = when { methodVisitor = when {
irFunction.isSuspend && irFunction.hasContinuation() -> {
// We do not generate continuation and state-machine for synthetic accessors, bridges, and delegated members,
// in a sense, they are tail-call
!irFunction.isKnownToBeTailCall() &&
// TODO: We should generate two versions of inline suspend function: one with state-machine and one without
!irFunction.isInline ->
generateStateMachineForNamedFunction( generateStateMachineForNamedFunction(
irFunction, classCodegen, methodVisitor, flags, signature, continuationClassBuilder, element!! irFunction, classCodegen, methodVisitor,
access = flags,
signature = signature,
obtainContinuationClassBuilder = {
context.continuationClassBuilders[continuationClass().attributeOwnerId]!!
},
element = psiElement()
) )
irFunction.isInvokeSuspendOfLambda(context) -> generateStateMachineForLambda( }
classCodegen, methodVisitor, flags, signature, element!! irFunction.isInvokeSuspendOfLambda() -> generateStateMachineForLambda(
classCodegen, methodVisitor, flags, signature, psiElement()
) )
else -> methodVisitor else -> methodVisitor
} }
ExpressionCodegen(functionView, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, inlinedInto).generate() ExpressionCodegen(functionView, signature, frameMap, InstructionAdapter(methodVisitor), classCodegen, inlinedInto).generate()
methodVisitor.visitMaxs(-1, -1) methodVisitor.visitMaxs(-1, -1)
continuationClassBuilder?.done() if (irFunction.hasContinuation()) {
context.continuationClassBuilders[continuationClass().attributeOwnerId].sure {
"Could not find continuation class builder for ${continuationClass().render()}"
}.done()
}
} }
methodVisitor.visitEnd() methodVisitor.visitEnd()
return signature return signature
} }
private fun psiElement(): KtElement =
if (irFunction.isSuspend) irFunction.symbol.descriptor.psiElement as KtElement
else context.suspendLambdaToOriginalFunctionMap[irFunction.parentAsClass.attributeOwnerId]!!.symbol.descriptor.psiElement as KtElement
private fun IrFunction.hasContinuation(): Boolean = isSuspend &&
// We do not generate continuation and state-machine for synthetic accessors, bridges, and delegated members,
// in a sense, they are tail-call
!isKnownToBeTailCall() &&
// TODO: We should generate two versions of inline suspend function: one with state-machine and one without
!isInline &&
// This is suspend lambda parameter of inline function
origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA &&
// This is just a template for inliner
origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE &&
// Continuations are generated for suspendImpls
parentAsClass.functions.none { it.name.asString() == name.asString() + SUSPEND_IMPL_NAME_SUFFIX }
private fun continuationClass(): IrClass =
irFunction.body!!.statements[0] as IrClass
private fun calculateMethodFlags(isStatic: Boolean): Int { private fun calculateMethodFlags(isStatic: Boolean): Int {
if (irFunction.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) { if (irFunction.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) {
return Opcodes.ACC_PUBLIC or Opcodes.ACC_SYNTHETIC.let { return Opcodes.ACC_PUBLIC or Opcodes.ACC_SYNTHETIC.let {
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.backend.common.ir.ir2string import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.codegen.BaseExpressionCodegen import org.jetbrains.kotlin.codegen.BaseExpressionCodegen
import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.ClassBuilder
@@ -16,16 +17,24 @@ import org.jetbrains.kotlin.codegen.inline.*
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.commons.Method
@@ -61,7 +70,8 @@ class IrSourceCompilerForInline(
root.signature.asmMethod.descriptor, root.signature.asmMethod.descriptor,
//compilationContextFunctionDescriptor.isInlineOrInsideInline() //compilationContextFunctionDescriptor.isInlineOrInsideInline()
false, false,
compilationContextFunctionDescriptor.isSuspend compilationContextFunctionDescriptor.isSuspend,
findElement()?.let { CodegenUtil.getLineNumberForElement(it, false) } ?: 0
) )
} }
@@ -127,13 +137,20 @@ class IrSourceCompilerForInline(
override fun getContextLabels(): Set<String> { override fun getContextLabels(): Set<String> {
val name = codegen.irFunction.name.asString() val name = codegen.irFunction.name.asString()
if (name == INVOKE_SUSPEND_METHOD_NAME) { if (name == INVOKE_SUSPEND_METHOD_NAME) {
codegen.context.suspendLambdaToOriginalFunctionMap[codegen.irFunction.parent]?.let { codegen.context.suspendLambdaToOriginalFunctionMap[codegen.irFunction.parentAsClass.attributeOwnerId]?.let {
return setOf(it.name.asString()) return setOf(it.name.asString())
} }
} }
return setOf(name) return setOf(name)
} }
// TODO: Find a way to avoid using PSI here
override fun reportSuspensionPointInsideMonitor(stackTraceElement: String) {
org.jetbrains.kotlin.codegen.coroutines.reportSuspensionPointInsideMonitor(findElement()!!, state, stackTraceElement)
}
private fun findElement() = (callElement.symbol.descriptor.original as? DeclarationDescriptorWithSource)?.source?.getPsi() as? KtElement
internal val isPrimaryCopy: Boolean internal val isPrimaryCopy: Boolean
get() = codegen.classCodegen !is FakeClassCodegen get() = codegen.classCodegen !is FakeClassCodegen
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isSuspendFunction import org.jetbrains.kotlin.ir.util.isSuspendFunction
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.load.kotlin.computeExpandedTypeForInlineClass import org.jetbrains.kotlin.load.kotlin.computeExpandedTypeForInlineClass
import org.jetbrains.kotlin.load.kotlin.mapBuiltInType import org.jetbrains.kotlin.load.kotlin.mapBuiltInType
@@ -5,12 +5,14 @@
package org.jetbrains.kotlin.backend.jvm.codegen package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
@@ -90,7 +92,8 @@ fun PromisedValue.coerce(target: Type, irTarget: IrType): PromisedValue {
// Boxing and unboxing kotlin.Result leads to CCE in generated code // Boxing and unboxing kotlin.Result leads to CCE in generated code
val doNotCoerceKotlinResultInContinuation = val doNotCoerceKotlinResultInContinuation =
(codegen.irFunction.isInvokeSuspendOfContinuation(codegen.context) || codegen.irFunction.isInvokeOfSuspendLambda(codegen.context)) (codegen.irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS ||
codegen.irFunction.parentAsClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA)
&& (irType.isKotlinResult() || irTarget.isKotlinResult()) && (irType.isKotlinResult() || irTarget.isKotlinResult())
// Coerce inline classes // Coerce inline classes
@@ -5,19 +5,16 @@
package org.jetbrains.kotlin.backend.jvm.lower package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.ir.* import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.backend.common.lower.LocalDeclarationsLowering
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.parents
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrBlock import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrBlock
import org.jetbrains.kotlin.backend.jvm.codegen.isInvokeOfSuspendCallableReference
import org.jetbrains.kotlin.codegen.coroutines.* import org.jetbrains.kotlin.codegen.coroutines.*
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
@@ -43,7 +40,6 @@ import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
internal val addContinuationPhase = makeIrFilePhase( internal val addContinuationPhase = makeIrFilePhase(
::AddContinuationLowering, ::AddContinuationLowering,
@@ -51,50 +47,53 @@ internal val addContinuationPhase = makeIrFilePhase(
"Add continuation classes to suspend functions and transform suspend lambdas into continuations" "Add continuation classes to suspend functions and transform suspend lambdas into continuations"
) )
private object CONTINUATION_CLASS : IrDeclarationOriginImpl("CONTINUATION_CLASS")
private class AddContinuationLowering(private val context: JvmBackendContext) : FileLoweringPass { private class AddContinuationLowering(private val context: JvmBackendContext) : FileLoweringPass {
private val functionsToAdd = mutableMapOf<IrClass, MutableSet<IrFunction>>()
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
val suspendLambdas = markSuspendLambdas(irFile) val (suspendLambdas, inlineLambdas) = markSuspendLambdas(irFile)
val suspendFunctions = markSuspendFunctions(irFile, suspendLambdas.map { it.function }.toSet()) transformSuspendFunctions(irFile, (suspendLambdas.map { it.function } + inlineLambdas).toSet())
for (lambda in suspendLambdas) { for ((clazz, functions) in functionsToAdd) {
generateContinuationClassForLambda(lambda) for (function in functions) {
clazz.declarations.add(function)
}
} }
transformReferencesToSuspendLambdas(irFile, suspendLambdas) transformReferencesToSuspendLambdas(irFile, suspendLambdas)
transformSuspendCalls(irFile)
for (suspendFunction in suspendFunctions) {
generateContinuationClassForNamedFunction(suspendFunction)
}
} }
private fun transformReferencesToSuspendLambdas(irFile: IrFile, suspendLambdas: List<SuspendLambdaInfo>) { private fun transformReferencesToSuspendLambdas(irFile: IrFile, suspendLambdas: List<SuspendLambdaInfo>) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() { for (lambda in suspendLambdas) {
(lambda.function.parent as IrDeclarationContainer).declarations.remove(lambda.function)
}
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
expression.transformChildrenVoid(this)
if (!expression.isSuspend) if (!expression.isSuspend)
return expression return expression
val constructor = suspendLambdas.singleOrNull { it.function == expression.symbol.owner }?.constructor val info = suspendLambdas.singleOrNull { it.function == expression.symbol.owner } ?: return expression
?: return expression return context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset).run {
val expressionArguments = expression.getArguments().map { it.second } val expressionArguments = expression.getArguments().map { it.second }
assert(constructor.valueParameters.size == expressionArguments.size) { irBlock {
"Inconsistency between callable reference to suspend lambda and the corresponding continuation" +generateContinuationClassForLambda(info, currentDeclarationParent)
} val constructor = info.constructor
val irBuilder = context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset) assert(constructor.valueParameters.size == expressionArguments.size + 1) {
irBuilder.run { "Inconsistency between callable reference to suspend lambda and the corresponding continuation"
return irCall(constructor.symbol).apply { }
expressionArguments.forEachIndexed { index, argument -> +irCall(constructor.symbol).apply {
putValueArgument(index, argument) expressionArguments.forEachIndexed { index, argument ->
putValueArgument(index, argument)
}
// Pass null as completion parameter
putValueArgument(expressionArguments.size, irNull())
} }
} }
} }.also { it.transformChildrenVoid(this) }
} }
}) })
} }
private fun generateContinuationClassForLambda(info: SuspendLambdaInfo) { private fun generateContinuationClassForLambda(info: SuspendLambdaInfo, parent: IrDeclarationParent): IrClass {
val suspendLambda = context.ir.symbols.suspendLambdaClass.owner val suspendLambda = context.ir.symbols.suspendLambdaClass.owner
suspendLambda.createContinuationClassFor(info.function).apply { return suspendLambda.createContinuationClassFor(parent, JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA).apply {
copyAttributes(info.reference) copyAttributes(info.reference)
val functionNClass = context.ir.symbols.getJvmFunctionClass(info.arity + 1) val functionNClass = context.ir.symbols.getJvmFunctionClass(info.arity + 1)
superTypes.add( superTypes.add(
@@ -120,11 +119,14 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
.mapNotNull { (i, field) -> if (info.reference.getValueArgument(i) == null) field else null } .mapNotNull { (i, field) -> if (info.reference.getValueArgument(i) == null) field else null }
val parametersWithArguments = parametersFields - parametersWithoutArguments val parametersWithArguments = parametersFields - parametersWithoutArguments
val constructor = addPrimaryConstructorForLambda(info.arity, info.reference, parametersFields) val constructor = addPrimaryConstructorForLambda(info.arity, info.reference, parametersFields)
val secondaryConstructor = addSecondaryConstructorForLambda(constructor)
val invokeToOverride = functionNClass.functions.single { val invokeToOverride = functionNClass.functions.single {
it.owner.valueParameters.size == info.arity + 1 && it.owner.name.asString() == "invoke" it.owner.valueParameters.size == info.arity + 1 && it.owner.name.asString() == "invoke"
} }
val invokeSuspend = addInvokeSuspendForLambda(info.function, parametersFields, receiverField) val invokeSuspend = addInvokeSuspendForLambda(info.function, parametersFields, receiverField)
if (info.capturesCrossinline) {
addInvokeSuspendForInlineForLambda(invokeSuspend, info.function, parametersFields, receiverField)
}
info.function.parentAsClass.declarations.remove(info.function)
if (info.arity <= 1) { if (info.arity <= 1) {
val singleParameterField = receiverField ?: parametersWithoutArguments.singleOrNull() val singleParameterField = receiverField ?: parametersWithoutArguments.singleOrNull()
val create = addCreate(constructor, suspendLambda, info, parametersWithArguments, singleParameterField) val create = addCreate(constructor, suspendLambda, info, parametersWithArguments, singleParameterField)
@@ -139,9 +141,9 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
) )
} }
context.suspendLambdaToOriginalFunctionMap[this] = info.function context.suspendLambdaToOriginalFunctionMap[attributeOwnerId as IrFunctionReference] = info.function
info.constructor = secondaryConstructor info.constructor = constructor
} }
} }
@@ -154,46 +156,73 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
it.owner.name.asString() == INVOKE_SUSPEND_METHOD_NAME && it.owner.valueParameters.size == 1 && it.owner.name.asString() == INVOKE_SUSPEND_METHOD_NAME && it.owner.valueParameters.size == 1 &&
it.owner.valueParameters[0].type.isKotlinResult() it.owner.valueParameters[0].type.isKotlinResult()
}.owner }.owner
return addFunctionOverride(superMethod) return addFunctionOverride(superMethod).also { it.copySuspendLambdaBodyFrom(irFunction, receiverField, fields) }
.also { function -> }
function.copyTypeParametersFrom(irFunction)
function.body = irFunction.body?.deepCopyWithSymbols(function)
function.body?.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
if (expression.symbol.owner == irFunction.extensionReceiverParameter) {
assert(receiverField != null)
return IrGetFieldImpl(
expression.startOffset,
expression.endOffset,
receiverField!!.symbol,
receiverField.type
).also {
it.receiver =
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.dispatchReceiverParameter!!.symbol)
}
} else if (expression.symbol.owner == irFunction.dispatchReceiverParameter) {
return IrGetValueImpl(expression.startOffset, expression.endOffset, function.dispatchReceiverParameter!!.symbol)
}
val field = fields.find { it.name == expression.symbol.owner.name } ?: return expression
return IrGetFieldImpl(expression.startOffset, expression.endOffset, field.symbol, field.type).also {
it.receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.dispatchReceiverParameter!!.symbol)
}
}
// If the suspend lambda body contains declarations of other classes (for other lambdas), private fun IrClass.addInvokeSuspendForInlineForLambda(
// do not rewrite those. In particular, that could lead to rewriting of returns in nested invokeSuspend: IrFunction,
// lambdas to unintended non-local returns. irFunction: IrFunction,
override fun visitClass(declaration: IrClass): IrStatement { fields: List<IrField>,
return declaration receiverField: IrField?
} ): IrFunction {
return addFunction(
INVOKE_SUSPEND_METHOD_NAME + FOR_INLINE_SUFFIX,
context.irBuiltIns.anyNType,
Modality.FINAL,
origin = JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE
).apply {
invokeSuspend.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
}.also { it.copySuspendLambdaBodyFrom(irFunction, receiverField, fields) }
}
override fun visitReturn(expression: IrReturn): IrExpression { private fun IrSimpleFunction.copySuspendLambdaBodyFrom(
val ret = super.visitReturn(expression) as IrReturn irFunction: IrFunction,
return IrReturnImpl(ret.startOffset, ret.endOffset, ret.type, function.symbol, ret.value) receiverField: IrField?,
fields: List<IrField>
) {
copyTypeParametersFrom(irFunction)
body = irFunction.body?.deepCopyWithSymbols(this)
body?.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
if (expression.symbol.owner == irFunction.extensionReceiverParameter) {
assert(receiverField != null)
return IrGetFieldImpl(
expression.startOffset,
expression.endOffset,
receiverField!!.symbol,
receiverField.type
).also {
it.receiver =
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchReceiverParameter!!.symbol)
} }
}) } else if (expression.symbol.owner == irFunction.dispatchReceiverParameter) {
(irFunction.parent as IrDeclarationContainer).declarations.remove(irFunction) return IrGetValueImpl(expression.startOffset, expression.endOffset, dispatchReceiverParameter!!.symbol)
}
val field = fields.find { it.name == expression.symbol.owner.name } ?: return expression
return IrGetFieldImpl(expression.startOffset, expression.endOffset, field.symbol, field.type).also {
it.receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchReceiverParameter!!.symbol)
}
} }
// If the suspend lambda body contains declarations of other classes (for other lambdas),
// do not rewrite those. In particular, that could lead to rewriting of returns in nested
// lambdas to unintended non-local returns.
override fun visitClass(declaration: IrClass): IrStatement {
return declaration
}
override fun visitReturn(expression: IrReturn): IrExpression {
val ret = super.visitReturn(expression) as IrReturn
return IrReturnImpl(ret.startOffset, ret.endOffset, ret.type, symbol, ret.value)
}
})
}
private fun IrFunction.copyBodyFrom(oldFunction: IrFunction) {
val mapping: Map<IrValueParameter, IrValueParameter> =
(listOfNotNull(oldFunction.dispatchReceiverParameter, oldFunction.extensionReceiverParameter) + oldFunction.valueParameters)
.zip(listOfNotNull(dispatchReceiverParameter, extensionReceiverParameter) + valueParameters).toMap()
copyBodyWithParametersMapping(this, oldFunction, mapping)
} }
private fun IrDeclarationContainer.addFunctionOverride( private fun IrDeclarationContainer.addFunctionOverride(
@@ -311,17 +340,15 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
} }
} }
private fun IrClass.createContinuationClassFor(irFunction: IrFunction): IrClass = buildClass { private fun IrClass.createContinuationClassFor(parent: IrDeclarationParent, newOrigin: IrDeclarationOrigin): IrClass = buildClass {
name = "${irFunction.name}\$Continuation".synthesizedName name = Name.special("<Continuation>")
origin = CONTINUATION_CLASS origin = newOrigin
visibility = JavaVisibilities.PACKAGE_VISIBILITY visibility = JavaVisibilities.PACKAGE_VISIBILITY
}.also { irClass -> }.also { irClass ->
irClass.createImplicitParameterDeclarationWithWrappedDescriptor() irClass.createImplicitParameterDeclarationWithWrappedDescriptor()
irClass.superTypes.add(defaultType) irClass.superTypes.add(defaultType)
val parent = irFunction.parents.firstIsInstance<IrDeclarationContainer>()
irClass.parent = parent irClass.parent = parent
parent.declarations.add(irClass)
} }
// Primary constructor accepts parameters equal to function reference arguments + continuation and sets the fields. // Primary constructor accepts parameters equal to function reference arguments + continuation and sets the fields.
@@ -354,51 +381,37 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
} }
} }
// Secondary constructor accepts parameters equals to arguments of function reference and is used for callable references
// TODO: get rid of it and use primary constructor only
private fun IrClass.addSecondaryConstructorForLambda(primary: IrConstructor): IrConstructor =
addConstructor {
isPrimary = false
returnType = defaultType
}.also { constructor ->
constructor.valueParameters.addAll(primary.valueParameters.dropLast(1).map { it.copyTo(constructor) })
constructor.body = context.createIrBuilder(constructor.symbol).irBlockBody {
+irDelegatingConstructorCall(primary).also {
for ((index, param) in constructor.valueParameters.withIndex()) {
it.putValueArgument(index, irGet(param))
}
it.putValueArgument(constructor.valueParameters.size, irNull())
}
}
}
private fun IrFunction.addCompletionValueParameter(): IrValueParameter = private fun IrFunction.addCompletionValueParameter(): IrValueParameter =
addValueParameter(SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME, continuationType()) addValueParameter(SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME, continuationType())
private fun IrFunction.continuationType(): IrType = private fun IrFunction.continuationType(): IrType =
context.ir.symbols.continuationClass.typeWith(returnType).makeNullable() context.ir.symbols.continuationClass.typeWith(returnType).makeNullable()
private fun generateContinuationClassForNamedFunction(irFunction: IrFunction) { private fun generateContinuationClassForNamedFunction(
context.ir.symbols.continuationImplClass.owner.createContinuationClassFor(irFunction).apply { irFunction: IrFunction,
val resultField = addField( dispatchReceiverParameter: IrValueParameter?,
context.state.languageVersionSettings.dataFieldName(), attributeContainer: IrAttributeContainer
context.irBuiltIns.anyType, ): IrClass {
JavaVisibilities.PACKAGE_VISIBILITY return context.ir.symbols.continuationImplClass.owner
) .createContinuationClassFor(irFunction, JvmLoweredDeclarationOrigin.CONTINUATION_CLASS)
val capturedThisField = irFunction.dispatchReceiverParameter?.let { addField("this\$0", it.type) } .apply {
val labelField = addField(COROUTINE_LABEL_FIELD_NAME, context.irBuiltIns.intType, JavaVisibilities.PACKAGE_VISIBILITY) val resultField = addField(
addConstructorForNamedFunction(capturedThisField) context.state.languageVersionSettings.dataFieldName(),
var function = irFunction context.irBuiltIns.anyType,
if (function is IrSimpleFunction && function.isOverridable && function.body != null) { JavaVisibilities.PACKAGE_VISIBILITY
// Create static method for the suspend state machine method so that reentering the method )
// does not lead to virtual dispatch to the wrong method. val capturedThisField = dispatchReceiverParameter?.let { addField("this\$0", it.type) }
context.suspendFunctionContinuations[function] = this val labelField = addField(COROUTINE_LABEL_FIELD_NAME, context.irBuiltIns.intType, JavaVisibilities.PACKAGE_VISIBILITY)
function = createStaticSuspendImpl(function) addConstructorForNamedFunction(capturedThisField)
addInvokeSuspendForNamedFunction(
irFunction,
resultField,
labelField,
capturedThisField,
irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_IMPL_STATIC_FUNCTION
)
copyAttributes(attributeContainer)
} }
addInvokeSuspendForNamedFunction(function, resultField, labelField, capturedThisField, function != irFunction)
context.suspendFunctionContinuations[function] = this
}
} }
private fun IrClass.addConstructorForNamedFunction(capturedThisField: IrField?): IrConstructor = addConstructor { private fun IrClass.addConstructorForNamedFunction(capturedThisField: IrField?): IrConstructor = addConstructor {
@@ -433,7 +446,8 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
capturedThisField: IrField?, capturedThisField: IrField?,
isStaticSuspendImpl: Boolean isStaticSuspendImpl: Boolean
) { ) {
val invokeSuspend = context.ir.symbols.continuationImplClass.owner.functions.single { it.name == Name.identifier(INVOKE_SUSPEND_METHOD_NAME) } val invokeSuspend = context.ir.symbols.continuationImplClass.owner.functions
.single { it.name == Name.identifier(INVOKE_SUSPEND_METHOD_NAME) }
addFunctionOverride(invokeSuspend).also { function -> addFunctionOverride(invokeSuspend).also { function ->
function.body = context.createIrBuilder(function.symbol).irBlockBody { function.body = context.createIrBuilder(function.symbol).irBlockBody {
+irSetField(irGet(function.dispatchReceiverParameter!!), resultField, irGet(function.valueParameters[0])) +irSetField(irGet(function.dispatchReceiverParameter!!), resultField, irGet(function.valueParameters[0]))
@@ -495,7 +509,6 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
copyMetadata = false copyMetadata = false
) )
copyBodyToStatic(irFunction, static) copyBodyToStatic(irFunction, static)
(irFunction.parent as IrClass).declarations.add(static)
// Rewrite the body of the original suspend method to forward to the new static method. // Rewrite the body of the original suspend method to forward to the new static method.
irFunction.body = context.createIrBuilder(irFunction.symbol).irBlockBody { irFunction.body = context.createIrBuilder(irFunction.symbol).irBlockBody {
+irReturn(irCall(static).also { +irReturn(irCall(static).also {
@@ -521,30 +534,78 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
} }
// TODO: Generate two copies of inline suspend functions // TODO: Generate two copies of inline suspend functions
private fun markSuspendFunctions(irFile: IrFile, suspendLambdas: Set<IrFunction>): Set<IrFunction> { private fun transformSuspendFunctions(irFile: IrFile, suspendAndInlineLambdas: Set<IrFunction>) {
val result = hashSetOf<IrFunction>() irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
private val functionsStack = arrayListOf<IrFunction>()
private val suspendFunctionsCapturingCrossinline = mutableSetOf<IrFunction>()
irFile.acceptChildrenVoid(object : IrElementVisitorVoid { override fun visitFunction(declaration: IrFunction): IrStatement {
override fun visitElement(element: IrElement) { functionsStack.push(declaration)
element.acceptChildrenVoid(this) val function = super.visitFunction(declaration) as IrFunction
functionsStack.pop()
if (skip(function)) return function
function as IrSimpleFunction
if (function in suspendFunctionsCapturingCrossinline) {
val newFunction = buildFun {
name = Name.identifier(function.name.asString() + FOR_INLINE_SUFFIX)
returnType = function.returnType
modality = function.modality
isSuspend = function.isSuspend
origin = JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
}.apply {
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyTo(this)
extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(this)
function.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
copyBodyFrom(function)
copyAttributes(function)
}
functionsToAdd[function.parentAsClass] = mutableSetOf(newFunction)
}
val newFunction = if (function.isOverridable) {
// Create static method for the suspend state machine method so that reentering the method
// does not lead to virtual dispatch to the wrong method.
functionsToAdd.getOrPut(function.parentAsClass) { mutableSetOf() }.add(function)
createStaticSuspendImpl(function)
} else function
newFunction.body = context.createIrBuilder(newFunction.symbol).irBlockBody {
+generateContinuationClassForNamedFunction(
newFunction,
function.dispatchReceiverParameter,
declaration as IrAttributeContainer
)
for (statement in newFunction.body!!.statements) {
+statement
}
}
return newFunction
} }
override fun visitFunction(declaration: IrFunction) { private fun skip(function: IrFunction) =
super.visitFunction(declaration) !function.isSuspend || function in suspendAndInlineLambdas || function.isInline || function.body == null ||
if (declaration.isSuspend && declaration !in suspendLambdas && !declaration.isInline && function.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE ||
!declaration.isInvokeOfSuspendCallableReference() function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
function.parentAsClass.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL
override fun visitFieldAccess(expression: IrFieldAccessExpression): IrExpression {
val result = super.visitFieldAccess(expression)
val function = functionsStack.peek() ?: return result
if (function.isSuspend &&
expression.symbol.owner.origin == LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CROSSINLINE_CAPTURED_VALUE
) { ) {
result.add(declaration) suspendFunctionsCapturingCrossinline += function
} }
return result
} }
}) })
return result
} }
private fun markSuspendLambdas(irElement: IrElement): List<SuspendLambdaInfo> { private fun markSuspendLambdas(irElement: IrElement): Pair<List<SuspendLambdaInfo>, List<IrFunction>> {
val suspendLambdas = arrayListOf<SuspendLambdaInfo>() val suspendLambdas = arrayListOf<SuspendLambdaInfo>()
val inlineLambdas = mutableSetOf<IrFunctionReference>() val inlineLambdas = mutableSetOf<IrFunctionReference>()
val capturesCrossinline = mutableSetOf<IrFunctionReference>()
irElement.acceptChildrenVoid(object : IrElementVisitorVoid { irElement.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) { override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this) element.acceptChildrenVoid(this)
@@ -552,6 +613,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
override fun visitCall(expression: IrCall) { override fun visitCall(expression: IrCall) {
val owner = expression.symbol.owner val owner = expression.symbol.owner
// Collect inline lambdas
if (owner.isInline) { if (owner.isInline) {
for (i in 0 until expression.valueArgumentsCount) { for (i in 0 until expression.valueArgumentsCount) {
if (owner.valueParameters[i].isNoinline) continue if (owner.valueParameters[i].isNoinline) continue
@@ -565,6 +627,20 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
} }
} }
} }
// collect lambdas, which capture crossinline
for (i in 0 until expression.valueArgumentsCount) {
val valueArgument = expression.getValueArgument(i) as? IrBlock ?: continue
if (valueArgument.origin != IrStatementOrigin.LAMBDA) continue
val reference = valueArgument.statements.last() as? IrFunctionReference ?: continue
for (j in 0 until reference.valueArgumentsCount) {
val getValue = (reference.getValueArgument(j) as? IrGetValue) ?: continue
val parameter = getValue.symbol.owner as? IrValueParameter ?: continue
if (parameter.isCrossinline) {
capturesCrossinline += reference
break
}
}
}
expression.acceptChildrenVoid(this) expression.acceptChildrenVoid(this)
} }
@@ -575,32 +651,21 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
suspendLambdas += SuspendLambdaInfo( suspendLambdas += SuspendLambdaInfo(
expression.symbol.owner, expression.symbol.owner,
(expression.type as IrSimpleType).arguments.size - 1, (expression.type as IrSimpleType).arguments.size - 1,
expression expression,
expression in capturesCrossinline
) )
} }
} }
}) })
return suspendLambdas return suspendLambdas to inlineLambdas.map { it.symbol.owner }
} }
private fun transformSuspendCalls(irFile: IrFile) { private class SuspendLambdaInfo(
irFile.transformChildrenVoid(object : IrElementTransformerVoid() { val function: IrFunction,
private val functionStack = mutableListOf<IrFunction>() val arity: Int,
override fun visitElement(element: IrElement): IrElement { val reference: IrFunctionReference,
element.transformChildrenVoid(this) val capturesCrossinline: Boolean
return element ) {
}
override fun visitFunction(declaration: IrFunction): IrStatement {
functionStack.push(declaration)
val res = super.visitFunction(declaration)
functionStack.pop()
return res
}
})
}
private class SuspendLambdaInfo(val function: IrFunction, val arity: Int, val reference: IrFunctionReference) {
lateinit var constructor: IrConstructor lateinit var constructor: IrConstructor
} }
} }
@@ -414,7 +414,4 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
} }
} }
} }
private val currentDeclarationParent
get() = allScopes.last { it.irElement is IrDeclarationParent }.irElement as IrDeclarationParent
} }
@@ -56,10 +56,6 @@ class FunctionGenerator(declarationGenerator: DeclarationGenerator) : Declaratio
?.let { ?.let {
declareSimpleFunctionInner(it, ktElement, IrDeclarationOrigin.FAKE_OVERRIDE).buildWithScope { irFunction -> declareSimpleFunctionInner(it, ktElement, IrDeclarationOrigin.FAKE_OVERRIDE).buildWithScope { irFunction ->
generateFunctionParameterDeclarationsAndReturnType(irFunction, ktElement, null) generateFunctionParameterDeclarationsAndReturnType(irFunction, ktElement, null)
val overridenSymbol = irFunction.overriddenSymbols.first()
if (overridenSymbol.isBound) {
irFunction.copyAttributes(overridenSymbol.owner)
}
} }
} }
@@ -8,10 +8,7 @@ package org.jetbrains.kotlin.ir.declarations.lazy
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.declarations.IrAttributeContainer import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
@@ -1,18 +1,10 @@
@kotlin.Metadata
final class LambdaWithLongReceiverKt$$call$Continuation {
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
public method <init>(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.coroutines.jvm.internal.DebugMetadata @kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata @kotlin.Metadata
final class LambdaWithLongReceiverKt$box$1$1 { final class LambdaWithLongReceiverKt$box$1$1 {
private field $p: long private field $p: long
private field label: int private field label: int
inner class LambdaWithLongReceiverKt$box$1
inner class LambdaWithLongReceiverKt$box$1$1 inner class LambdaWithLongReceiverKt$box$1$1
public method <init>(): void
public method <init>(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void public method <init>(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object public final method invoke(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object
@@ -26,17 +18,24 @@ final class LambdaWithLongReceiverKt$box$1 {
field L$0: java.lang.Object field L$0: java.lang.Object
private field label: int private field label: int
inner class LambdaWithLongReceiverKt$box$1 inner class LambdaWithLongReceiverKt$box$1
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef): void inner class LambdaWithLongReceiverKt$box$1$1
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef, @org.jetbrains.annotations.Nullable p1: kotlin.coroutines.Continuation): void public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef, @org.jetbrains.annotations.Nullable p1: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
} }
@kotlin.Metadata
final class LambdaWithLongReceiverKt$call$1 {
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
public method <init>(@org.jetbrains.annotations.Nullable p0: 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 LambdaWithLongReceiverKt { public final class LambdaWithLongReceiverKt {
inner class LambdaWithLongReceiverKt$box$1 inner class LambdaWithLongReceiverKt$box$1
inner class LambdaWithLongReceiverKt$box$1$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.Nullable method call(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object public final static @org.jetbrains.annotations.Nullable method call(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
@@ -1,11 +1,3 @@
@kotlin.Metadata
final class LambdaWithMultipleParametersKt$$call$Continuation {
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
public method <init>(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.coroutines.jvm.internal.DebugMetadata @kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata @kotlin.Metadata
final class LambdaWithMultipleParametersKt$box$1$1 { final class LambdaWithMultipleParametersKt$box$1$1 {
@@ -16,8 +8,8 @@ final class LambdaWithMultipleParametersKt$box$1$1 {
private field e: long private field e: long
private field f: long private field f: long
private field label: int private field label: int
inner class LambdaWithMultipleParametersKt$box$1
inner class LambdaWithMultipleParametersKt$box$1$1 inner class LambdaWithMultipleParametersKt$box$1$1
public method <init>(): void
public method <init>(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void public method <init>(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void
public final method invoke(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object, p4: java.lang.Object, p5: java.lang.Object, p6: java.lang.Object): java.lang.Object public final method invoke(p0: java.lang.Object, p1: java.lang.Object, p2: java.lang.Object, p3: java.lang.Object, p4: java.lang.Object, p5: java.lang.Object, p6: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
@@ -30,17 +22,24 @@ final class LambdaWithMultipleParametersKt$box$1 {
field L$0: java.lang.Object field L$0: java.lang.Object
private field label: int private field label: int
inner class LambdaWithMultipleParametersKt$box$1 inner class LambdaWithMultipleParametersKt$box$1
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef): void inner class LambdaWithMultipleParametersKt$box$1$1
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef, @org.jetbrains.annotations.Nullable p1: kotlin.coroutines.Continuation): void public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef, @org.jetbrains.annotations.Nullable p1: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
} }
@kotlin.Metadata
final class LambdaWithMultipleParametersKt$call$1 {
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
public method <init>(@org.jetbrains.annotations.Nullable p0: 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 LambdaWithMultipleParametersKt { public final class LambdaWithMultipleParametersKt {
inner class LambdaWithMultipleParametersKt$box$1 inner class LambdaWithMultipleParametersKt$box$1
inner class LambdaWithMultipleParametersKt$box$1$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.Nullable method call(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function7, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object public final static @org.jetbrains.annotations.Nullable method call(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function7, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
@@ -1,5 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
// WITH_REFLECT // WITH_REFLECT
@@ -1,5 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// WITH_COROUTINES // WITH_COROUTINES
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,24 +1,3 @@
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
final class InlineWithoutStateMachineKt$$complexSuspend$Continuation {
field L$0: java.lang.Object
field L$1: java.lang.Object
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
public method <init>(@org.jetbrains.annotations.Nullable p0: 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.coroutines.jvm.internal.DebugMetadata
final class InlineWithoutStateMachineKt$$suspendHere$Continuation {
field L$0: java.lang.Object
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
public method <init>(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.coroutines.jvm.internal.DebugMetadata @kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata @kotlin.Metadata
final class InlineWithoutStateMachineKt$box$1 { final class InlineWithoutStateMachineKt$box$1 {
@@ -27,13 +6,33 @@ final class InlineWithoutStateMachineKt$box$1 {
field L$1: java.lang.Object field L$1: java.lang.Object
private field label: int private field label: int
inner class InlineWithoutStateMachineKt$box$1 inner class InlineWithoutStateMachineKt$box$1
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef): void
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef, @org.jetbrains.annotations.Nullable p1: kotlin.coroutines.Continuation): void public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef, @org.jetbrains.annotations.Nullable p1: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
} }
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
final class InlineWithoutStateMachineKt$complexSuspend$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
public method <init>(@org.jetbrains.annotations.Nullable p0: 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.coroutines.jvm.internal.DebugMetadata
final class InlineWithoutStateMachineKt$suspendHere$1 {
field L$0: java.lang.Object
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
public method <init>(@org.jetbrains.annotations.Nullable p0: 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 InlineWithoutStateMachineKt { public final class InlineWithoutStateMachineKt {
inner class InlineWithoutStateMachineKt$box$1 inner class InlineWithoutStateMachineKt$box$1
@@ -1,5 +1,4 @@
// IGNORE_BACKEND_FIR: JVM_IR // IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -0,0 +1,170 @@
@kotlin.Metadata
public interface flow/Flow {
public abstract @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: flow.FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
public interface flow/FlowCollector {
public abstract @org.jetbrains.annotations.Nullable method emit(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.coroutines.jvm.internal.DebugMetadata
@kotlin.Metadata
final class flow/InnerObjectRetransformationKt$box$1 {
private field label: int
inner class flow/InnerObjectRetransformationKt$box$1
public method <init>(@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
public final method invoke(p0: java.lang.Object): java.lang.Object
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class flow/InnerObjectRetransformationKt$check$$inlined$collect$1 {
inner class flow/InnerObjectRetransformationKt$check$$inlined$collect$1
public method <init>(): void
public @org.jetbrains.annotations.Nullable method emit(@org.jetbrains.annotations.NotNull p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
final class flow/InnerObjectRetransformationKt$check$$inlined$flow$1$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field L$2: java.lang.Object
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
synthetic final field this$0: flow.InnerObjectRetransformationKt$check$$inlined$flow$1
public method <init>(p0: flow.InnerObjectRetransformationKt$check$$inlined$flow$1, 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 flow/InnerObjectRetransformationKt$check$$inlined$flow$1 {
inner class flow/InnerObjectRetransformationKt$check$$inlined$flow$1
public method <init>(): void
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: flow.FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
final class flow/InnerObjectRetransformationKt$check$$inlined$flowWith$1$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field L$2: java.lang.Object
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
synthetic final field this$0: flow.InnerObjectRetransformationKt$check$$inlined$flowWith$1
public method <init>(p0: flow.InnerObjectRetransformationKt$check$$inlined$flowWith$1, 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 flow/InnerObjectRetransformationKt$check$$inlined$flowWith$1 {
synthetic final field $this$flowWith$inlined: flow.Flow
inner class flow/InnerObjectRetransformationKt$check$$inlined$flowWith$1
public method <init>(p0: flow.Flow): void
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: flow.FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
final class flow/InnerObjectRetransformationKt$check$1 {
field L$0: java.lang.Object
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
public method <init>(@org.jetbrains.annotations.Nullable p0: 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
final class flow/InnerObjectRetransformationKt$collect$2$emit$1 {
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
private field this$0: flow.InnerObjectRetransformationKt$collect$2
public method <init>(@org.jetbrains.annotations.NotNull p0: flow.InnerObjectRetransformationKt$collect$2, @org.jetbrains.annotations.Nullable 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 flow/InnerObjectRetransformationKt$collect$2 {
private synthetic final field $action: kotlin.jvm.functions.Function2
inner class flow/InnerObjectRetransformationKt$collect$2
public method <init>(p0: kotlin.jvm.functions.Function2): void
public @org.jetbrains.annotations.Nullable method emit$$forInline(@org.jetbrains.annotations.NotNull p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
public @org.jetbrains.annotations.Nullable method emit(@org.jetbrains.annotations.NotNull p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
final class flow/InnerObjectRetransformationKt$flow$1$collect$1 {
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
private field this$0: flow.InnerObjectRetransformationKt$flow$1
public method <init>(@org.jetbrains.annotations.NotNull p0: flow.InnerObjectRetransformationKt$flow$1, @org.jetbrains.annotations.Nullable 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 flow/InnerObjectRetransformationKt$flow$1 {
private synthetic final field $block: kotlin.jvm.functions.Function2
inner class flow/InnerObjectRetransformationKt$flow$1
public method <init>(p0: kotlin.jvm.functions.Function2): void
public @org.jetbrains.annotations.Nullable method collect$$forInline(@org.jetbrains.annotations.NotNull p0: flow.FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: flow.FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
final class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$1$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field L$2: java.lang.Object
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
synthetic final field this$0: flow.InnerObjectRetransformationKt$flowWith$$inlined$flow$1
public method <init>(p0: flow.InnerObjectRetransformationKt$flowWith$$inlined$flow$1, 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 flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$1 {
synthetic final field $builderBlock$inlined: kotlin.jvm.functions.Function2
synthetic final field $this$flowWith$inlined: flow.Flow
inner class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$1
public method <init>(p0: kotlin.jvm.functions.Function2, p1: flow.Flow): void
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: flow.FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
final class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$2$1 {
field L$0: java.lang.Object
field L$1: java.lang.Object
field L$2: java.lang.Object
field label: int
@org.jetbrains.annotations.NotNull field result: java.lang.Object
synthetic final field this$0: flow.InnerObjectRetransformationKt$flowWith$$inlined$flow$2
public method <init>(p0: flow.InnerObjectRetransformationKt$flowWith$$inlined$flow$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 flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$2 {
synthetic final field $builderBlock$inlined: kotlin.jvm.functions.Function2
synthetic final field $this$flowWith$inlined: flow.Flow
inner class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$2
public method <init>(p0: kotlin.jvm.functions.Function2, p1: flow.Flow): void
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: flow.FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
public final class flow/InnerObjectRetransformationKt {
inner class flow/InnerObjectRetransformationKt$box$1
inner class flow/InnerObjectRetransformationKt$collect$2
inner class flow/InnerObjectRetransformationKt$flow$1
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
public final static @org.jetbrains.annotations.Nullable method check(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
private final static @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: flow.Flow, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function2, @org.jetbrains.annotations.NotNull p2: kotlin.coroutines.Continuation): java.lang.Object
public final static @org.jetbrains.annotations.NotNull method flow(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2): flow.Flow
public final static @org.jetbrains.annotations.NotNull method flowWith(@org.jetbrains.annotations.NotNull p0: flow.Flow, @org.jetbrains.annotations.NotNull p1: kotlin.jvm.functions.Function2): flow.Flow
}
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: test.kt // FILE: test.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// FILE: flow.kt // FILE: flow.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: test.kt // FILE: test.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: test.kt // FILE: test.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: test.kt // FILE: test.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: test.kt // FILE: test.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: test.kt // FILE: test.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: test.kt // FILE: test.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: test.kt // FILE: test.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: test.kt // FILE: test.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: inlined.kt // FILE: inlined.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: inlined.kt // FILE: inlined.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: inlined.kt // FILE: inlined.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: inlined.kt // FILE: inlined.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: inlined.kt // FILE: inlined.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,5 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: inlined.kt // FILE: inlined.kt
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// WITH_RUNTIME // WITH_RUNTIME
@@ -4,9 +4,9 @@ compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/so
compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:14:13: error: a suspension point at SourceKt$test$2.invokeSuspend(source.kt:17) is inside a critical section compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:14:13: error: a suspension point at SourceKt$test$2.invokeSuspend(source.kt:17) is inside a critical section
builder { builder {
^ ^
compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:24:25: error: a suspension point at SourceKt$test$3$invokeSuspend$$inlined$withCrossinline$2.invokeSuspend(source.kt:62) is inside a critical section compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:24:9: error: a suspension point at SourceKt$test$3$invokeSuspend$$inlined$withCrossinline$2.invokeSuspend(source.kt:62) is inside a critical section
withCrossinline { withCrossinline {
^ ^
compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:50:34: error: a suspension point at SourceKt$withCrossinline$c$1.invokeSuspend(source.kt:52) is inside a critical section compiler/testData/compileKotlinAgainstCustomBinaries/suspensionPointInMonitor/source.kt:50:34: error: a suspension point at SourceKt$withCrossinline$c$1.invokeSuspend(source.kt:52) is inside a critical section
val c : suspend () -> Unit = { val c : suspend () -> Unit = {
^ ^