diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt index 806521b811e..cb8f469d814 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_IMPL_NAME_SUFFIX import org.jetbrains.kotlin.codegen.coroutines.reportSuspensionPointInsideMonitor +import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX import org.jetbrains.kotlin.config.isReleaseCoroutines import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.* @@ -39,17 +40,20 @@ internal fun generateStateMachine( methodVisitor: MethodVisitor, access: Int, signature: JvmMethodGenericSignature, - obtainContinuationClassBuilder: () -> ClassBuilder, - element: KtElement + obtainContinuationClassBuilder: () -> ClassBuilder ): MethodVisitor { val state = classCodegen.context.state val languageVersionSettings = state.languageVersionSettings assert(languageVersionSettings.isReleaseCoroutines()) { "Experimental coroutines are unsupported in JVM_IR backend" } + val element = if (irFunction.isSuspend) + irFunction.symbol.descriptor.psiElement ?: classCodegen.irClass.descriptor.psiElement + else + classCodegen.context.suspendLambdaToOriginalFunctionMap[classCodegen.irClass.attributeOwnerId]!!.symbol.descriptor.psiElement return CoroutineTransformerMethodVisitor( methodVisitor, access, signature.asmMethod.name, signature.asmMethod.descriptor, signature.genericsSignature, null, obtainClassBuilderForCoroutineState = obtainContinuationClassBuilder, - reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(element, state, it) }, - lineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0, + reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(element as KtElement, state, it) }, + lineNumber = element?.let { CodegenUtil.getLineNumberForElement(it, false) } ?: 0, sourceFile = classCodegen.irClass.file.name, languageVersionSettings = languageVersionSettings, shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, @@ -71,6 +75,26 @@ internal fun IrFunction.anyOfOverriddenFunctionsReturnsNonUnit(): Boolean { } == true } +internal fun IrFunction.suspendForInlineToOriginal(): IrSimpleFunction? { + if (origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE && + origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE + ) return null + return parentAsClass.declarations.find { + it is IrSimpleFunction && it.attributeOwnerId == (this as IrSimpleFunction).attributeOwnerId && + it.name.asString() + FOR_INLINE_SUFFIX == name.asString() + } as IrSimpleFunction? +} + +internal fun IrFunction.alwaysNeedsContinuation(): Boolean = + this is IrSimpleFunction && hasContinuation() && parentAsClass.declarations.any { + it is IrSimpleFunction && it.attributeOwnerId == attributeOwnerId && + it.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE + } + +internal fun IrFunction.continuationClass(): IrClass? = + (body as? IrBlockBody)?.statements?.find { it is IrClass && it.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS } + as IrClass? + internal fun IrFunction.continuationParameter(): IrValueParameter? = when { isInvokeSuspendOfLambda() || isInvokeSuspendForInlineOfLambda() -> dispatchReceiverParameter else -> valueParameters.singleOrNull { it.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index d0efd15ef8b..f474f26aef3 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -23,7 +23,6 @@ import org.jetbrains.kotlin.codegen.inline.* 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.SAFE_AS -import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq import org.jetbrains.kotlin.codegen.pseudoInsns.fixStackAndJump @@ -52,7 +51,6 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext import org.jetbrains.kotlin.types.model.TypeParameterMarker -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.keysToMap @@ -222,10 +220,8 @@ class ExpressionCodegen( private fun generateFakeContinuationConstructorIfNeeded() { if (irFunction.origin != FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE) return - val continuationClass = classCodegen.irClass.functions.find { - it.attributeOwnerId == (irFunction as? IrSimpleFunction)?.attributeOwnerId && - it.name.asString() == irFunction.name.asString().removeSuffix(FOR_INLINE_SUFFIX) - }?.body?.statements?.firstIsInstance() ?: error("could not find continuation for ${irFunction.render()}") + val continuationClass = irFunction.suspendForInlineToOriginal()?.continuationClass() + ?: error("could not find continuation for ${irFunction.render()}") val continuationType = typeMapper.mapClass(continuationClass) val continuationIndex = frameMap.getIndex(irFunction.continuationParameter()!!.symbol) with(mv) { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt index e546ad1e756..c3668f36bb7 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.load.java.JavaVisibilities import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.STRICTFP_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.SYNCHRONIZED_ANNOTATION_FQ_NAME @@ -43,10 +42,6 @@ open class FunctionCodegen( ) { private val context = classCodegen.context - private val continuationClassCodegen = lazy { - classCodegen.createLocalClassCodegen(irFunction.continuationClass(), irFunction).also { it.generate() } - } - fun generate(smapOverride: DefaultSourceMapper? = null): JvmMethodGenericSignature = try { doGenerate(smapOverride) @@ -56,8 +51,7 @@ open class FunctionCodegen( private fun doGenerate(smapOverride: DefaultSourceMapper?): JvmMethodGenericSignature { val signature = context.methodSignatureMapper.mapSignatureWithGeneric(irFunction) - - val flags = calculateMethodFlags(irFunction.isStatic) + val flags = irFunction.calculateMethodFlags() var methodVisitor = createMethod(flags, signature) if (context.state.generateParametersMetadata && flags.and(Opcodes.ACC_SYNTHETIC) == 0) { @@ -75,11 +69,7 @@ open class FunctionCodegen( TypeReference.newTypeReference(TypeReference.METHOD_RETURN).value, path, descr, visible ) } - }.genAnnotations( - irFunction, - signature.asmMethod.returnType, - irFunction.returnType - ) + }.genAnnotations(irFunction, signature.asmMethod.returnType, irFunction.returnType) // Not generating parameter annotations for default stubs fixes KT-7892, though // this certainly looks like a workaround for a javac bug. if (irFunction !is IrConstructor || !irFunction.parentAsClass.shouldNotGenerateConstructorParameterAnnotations()) { @@ -87,29 +77,18 @@ open class FunctionCodegen( } } + val continuationClassCodegen = lazy { + classCodegen.createLocalClassCodegen(irFunction.continuationClass()!!, irFunction).also { it.generate() } + } if (!context.state.classBuilderMode.generateBodies || flags.and(Opcodes.ACC_ABSTRACT) != 0 || irFunction.isExternal) { generateAnnotationDefaultValueIfNeeded(methodVisitor) } else { - val frameMap = createFrameMapWithReceivers() + val frameMap = irFunction.createFrameMapWithReceivers() if (irFunction.hasContinuation() || irFunction.isInvokeSuspendOfLambda()) { - if (irFunction is IrSimpleFunction && irFunction.parentAsClass.declarations.any { - it is IrSimpleFunction && it.attributeOwnerId == irFunction.attributeOwnerId && - it.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE - } - ) { - // Force generation of fake continuation for inliner. - continuationClassCodegen.value + methodVisitor = generateStateMachine(irFunction, classCodegen, methodVisitor, flags, signature) { + // This has to be done lazily to avoid generating the class if tail call optimization makes it redundant. + if (irFunction.isSuspend) continuationClassCodegen.value.visitor else classCodegen.visitor } - // This has to be done lazily to avoid generating the class if tail call optimization makes it redundant. - val getContinuation = { - if (irFunction.isSuspend) - continuationClassCodegen.value.visitor - else - classCodegen.visitor - } - methodVisitor = generateStateMachine( - irFunction, classCodegen, methodVisitor, flags, signature, getContinuation, psiElement() - ) } context.state.globalInlineContext.enterDeclaration(irFunction.suspendFunctionOriginal().descriptor) try { @@ -121,10 +100,9 @@ open class FunctionCodegen( methodVisitor.visitMaxs(-1, -1) } methodVisitor.visitEnd() - if (continuationClassCodegen.isInitialized()) { + if (continuationClassCodegen.isInitialized() || irFunction.alwaysNeedsContinuation()) { continuationClassCodegen.value.done() } - return signature } @@ -136,16 +114,6 @@ open class FunctionCodegen( private fun IrClass.shouldNotGenerateConstructorParameterAnnotations() = isAnonymousObject || origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS || origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA - private fun psiElement(): KtElement = - (if (irFunction.isSuspend) - irFunction.symbol.descriptor.psiElement ?: irFunction.parentAsClass.descriptor.psiElement - else - context.suspendLambdaToOriginalFunctionMap[irFunction.parentAsClass.attributeOwnerId]!!.symbol.descriptor.psiElement) - as KtElement - - private fun IrFunction.continuationClass(): IrClass = - (body as IrBlockBody).statements.first { it is IrClass && it.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS } as IrClass - private fun IrFunction.getVisibilityForDefaultArgumentStub(): Int = when (visibility) { Visibilities.PUBLIC -> Opcodes.ACC_PUBLIC @@ -153,59 +121,48 @@ open class FunctionCodegen( else -> throw IllegalStateException("Default argument stub should be either public or package private: ${ir2string(this)}") } - private fun calculateMethodFlags(isStatic: Boolean): Int { - if (irFunction.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) { - return irFunction.getVisibilityForDefaultArgumentStub() or Opcodes.ACC_SYNTHETIC.let { - if (irFunction is IrConstructor) it else it or Opcodes.ACC_STATIC + private fun IrFunction.calculateMethodFlags(): Int { + if (origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) { + return getVisibilityForDefaultArgumentStub() or Opcodes.ACC_SYNTHETIC.let { + if (this is IrConstructor) it else it or Opcodes.ACC_STATIC } } - val visibility = irFunction.getVisibilityAccessFlag() - val staticFlag = if (isStatic) Opcodes.ACC_STATIC else 0 - val varargFlag = if (irFunction.valueParameters.lastOrNull()?.varargElementType != null) Opcodes.ACC_VARARGS else 0 - val deprecation = irFunction.deprecationFlags - val bridgeFlag = if ( - irFunction.origin == IrDeclarationOrigin.BRIDGE || - irFunction.origin == IrDeclarationOrigin.BRIDGE_SPECIAL - ) Opcodes.ACC_BRIDGE else 0 - val modalityFlag = when ((irFunction as? IrSimpleFunction)?.modality) { + val isVararg = valueParameters.lastOrNull()?.varargElementType != null + val isBridge = origin == IrDeclarationOrigin.BRIDGE || origin == IrDeclarationOrigin.BRIDGE_SPECIAL + val modalityFlag = when ((this as? IrSimpleFunction)?.modality) { Modality.FINAL -> when { - irFunction.origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER -> 0 - classCodegen.irClass.isInterface && irFunction.body != null -> 0 - !classCodegen.irClass.isAnnotationClass || irFunction.isStatic -> Opcodes.ACC_FINAL - else -> Opcodes.ACC_ABSTRACT + origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER -> 0 + parentAsClass.isInterface && body != null -> 0 + parentAsClass.isAnnotationClass && !isStatic -> Opcodes.ACC_ABSTRACT + else -> Opcodes.ACC_FINAL } Modality.ABSTRACT -> Opcodes.ACC_ABSTRACT - else -> if (classCodegen.irClass.isJvmInterface && irFunction.body == null) Opcodes.ACC_ABSTRACT else 0 //TODO transform interface modality on lowering to DefaultImpls + // TODO transform interface modality on lowering to DefaultImpls + else -> if (parentAsClass.isJvmInterface && body == null) Opcodes.ACC_ABSTRACT else 0 } - val nativeFlag = if (irFunction.isExternal) Opcodes.ACC_NATIVE else 0 - val syntheticFlag = - if (irFunction.origin.isSynthetic || irFunction.hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) || - (irFunction.isSuspend && Visibilities.isPrivate(irFunction.visibility) && !irFunction.isInline) || - irFunction.isReifiable() - ) Opcodes.ACC_SYNTHETIC - else 0 - val strictFpFlag = if (irFunction.hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME)) Opcodes.ACC_STRICT else 0 - val synchronizedFlag = if (irFunction.hasAnnotation(SYNCHRONIZED_ANNOTATION_FQ_NAME)) Opcodes.ACC_SYNCHRONIZED else 0 + val isSynthetic = origin.isSynthetic || hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) || + (isSuspend && Visibilities.isPrivate(visibility) && !isInline) || isReifiable() + val isStrict = hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME) + val isSynchronized = hasAnnotation(SYNCHRONIZED_ANNOTATION_FQ_NAME) - return visibility or - modalityFlag or - staticFlag or - varargFlag or - deprecation or - nativeFlag or - bridgeFlag or - syntheticFlag or - strictFpFlag or - synchronizedFlag + return getVisibilityAccessFlag() or modalityFlag or deprecationFlags or + (if (isStatic) Opcodes.ACC_STATIC else 0) or + (if (isVararg) Opcodes.ACC_VARARGS else 0) or + (if (isExternal) Opcodes.ACC_NATIVE else 0) or + (if (isBridge) Opcodes.ACC_BRIDGE else 0) or + (if (isSynthetic) Opcodes.ACC_SYNTHETIC else 0) or + (if (isStrict) Opcodes.ACC_STRICT else 0) or + (if (isSynchronized) Opcodes.ACC_SYNCHRONIZED else 0) } protected open fun createMethod(flags: Int, signature: JvmMethodGenericSignature): MethodVisitor = classCodegen.visitor.newMethod( irFunction.OtherOrigin, flags, - signature.asmMethod.name, signature.asmMethod.descriptor, - if (flags.and(Opcodes.ACC_SYNTHETIC) != 0) null else signature.genericsSignature, + signature.asmMethod.name, + signature.asmMethod.descriptor, + signature.genericsSignature.takeIf { flags.and(Opcodes.ACC_SYNTHETIC) == 0 }, getThrownExceptions(irFunction)?.toTypedArray() ) @@ -223,10 +180,7 @@ open class FunctionCodegen( private fun generateAnnotationDefaultValueIfNeeded(methodVisitor: MethodVisitor) { getAnnotationDefaultValueExpression()?.let { defaultValueExpression -> - val annotationCodegen = object: AnnotationCodegen( - classCodegen, - context - ) { + val annotationCodegen = object : AnnotationCodegen(classCodegen, context) { override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor { return methodVisitor.visitAnnotationDefault() } @@ -249,26 +203,18 @@ open class FunctionCodegen( ?.expression } - private fun IrFrameMap.enterDispatchReceiver(parameter: IrValueParameter) { - val type = context.typeMapper.mapTypeAsDeclaration(parameter.type) - enter(parameter, type) - } - - private fun createFrameMapWithReceivers(): IrFrameMap { + private fun IrFunction.createFrameMapWithReceivers(): IrFrameMap { val frameMap = IrFrameMap() - - if (irFunction is IrConstructor) { - frameMap.enterDispatchReceiver(irFunction.constructedClass.thisReceiver!!) - } else if (irFunction.dispatchReceiverParameter != null) { - frameMap.enterDispatchReceiver(irFunction.dispatchReceiverParameter!!) + val receiver = if (this is IrConstructor) parentAsClass.thisReceiver else dispatchReceiverParameter + receiver?.let { + frameMap.enter(it, context.typeMapper.mapTypeAsDeclaration(it.type)) } - irFunction.extensionReceiverParameter?.let { + extensionReceiverParameter?.let { frameMap.enter(it, context.typeMapper.mapType(it)) } - for (parameter in irFunction.valueParameters) { + for (parameter in valueParameters) { frameMap.enter(parameter, context.typeMapper.mapType(parameter.type)) } - return frameMap } }