Generate separate methods for inline and noinline uses of inline suspend functions
Previously, inline suspend functions were effectively inline only, but ordinary inline functions can be used as noinline. To fix the issue, I generate two functions: one for inline with suffix $$forInline and without state machine; and the other one without any suffix and state machine for direct calls. This change does not affect effectively inline only suspend functions, i.e. functions with reified generics, annotated with @InlineOnly annotation and functions with crossinline parameters. #KT-20219: Fixed
This commit is contained in:
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||
import org.jetbrains.kotlin.codegen.context.*;
|
||||
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
|
||||
import org.jetbrains.kotlin.codegen.coroutines.SuspendFunctionGenerationStrategy;
|
||||
import org.jetbrains.kotlin.codegen.coroutines.SuspendInlineFunctionGenerationStrategy;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.config.JvmDefaultMode;
|
||||
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUtilKt;
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
|
||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature;
|
||||
@@ -126,16 +128,28 @@ public class FunctionCodegen {
|
||||
|
||||
if (owner.getContextKind() != OwnerKind.DEFAULT_IMPLS || function.hasBody()) {
|
||||
FunctionGenerationStrategy strategy;
|
||||
if (functionDescriptor.isSuspend() && !functionDescriptor.isInline()) {
|
||||
strategy = new SuspendFunctionGenerationStrategy(
|
||||
state,
|
||||
CoroutineCodegenUtilKt.<FunctionDescriptor>unwrapInitialDescriptorForSuspendFunction(functionDescriptor),
|
||||
function,
|
||||
v.getThisName(),
|
||||
state.getConstructorCallNormalizationMode()
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (functionDescriptor.isSuspend()) {
|
||||
if (AnnotationUtilKt.isEffectivelyInlineOnly(functionDescriptor)) {
|
||||
strategy = new FunctionGenerationStrategy.FunctionDefault(state, function);
|
||||
} else if (!functionDescriptor.isInline()) {
|
||||
strategy = new SuspendFunctionGenerationStrategy(
|
||||
state,
|
||||
CoroutineCodegenUtilKt.<FunctionDescriptor>unwrapInitialDescriptorForSuspendFunction(functionDescriptor),
|
||||
function,
|
||||
v.getThisName(),
|
||||
state.getConstructorCallNormalizationMode()
|
||||
);
|
||||
} else {
|
||||
strategy = new SuspendInlineFunctionGenerationStrategy(
|
||||
state,
|
||||
CoroutineCodegenUtilKt.<FunctionDescriptor>unwrapInitialDescriptorForSuspendFunction(functionDescriptor),
|
||||
function,
|
||||
v.getThisName(),
|
||||
state.getConstructorCallNormalizationMode(),
|
||||
this
|
||||
);
|
||||
}
|
||||
} else {
|
||||
strategy = new FunctionGenerationStrategy.FunctionDefault(state, function);
|
||||
}
|
||||
|
||||
@@ -202,12 +216,12 @@ public class FunctionCodegen {
|
||||
|
||||
MethodVisitor mv =
|
||||
strategy.wrapMethodVisitor(
|
||||
v.newMethod(origin,
|
||||
flags,
|
||||
asmMethod.getName(),
|
||||
asmMethod.getDescriptor(),
|
||||
jvmSignature.getGenericsSignature(),
|
||||
getThrownExceptions(functionDescriptor, typeMapper)
|
||||
newMethod(origin,
|
||||
flags,
|
||||
asmMethod.getName(),
|
||||
asmMethod.getDescriptor(),
|
||||
jvmSignature.getGenericsSignature(),
|
||||
getThrownExceptions(functionDescriptor, typeMapper)
|
||||
),
|
||||
flags, asmMethod.getName(),
|
||||
asmMethod.getDescriptor()
|
||||
@@ -254,6 +268,18 @@ public class FunctionCodegen {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MethodVisitor newMethod(
|
||||
@NotNull JvmDeclarationOrigin origin,
|
||||
int access,
|
||||
@NotNull String name,
|
||||
@NotNull String desc,
|
||||
@Nullable String signature,
|
||||
@Nullable String[] exceptions
|
||||
) {
|
||||
return v.newMethod(origin, access, name, desc, signature, exceptions);
|
||||
}
|
||||
|
||||
private static boolean shouldDelegateMethodBodyToInlineClass(
|
||||
@NotNull JvmDeclarationOrigin origin,
|
||||
@NotNull FunctionDescriptor functionDescriptor,
|
||||
|
||||
+3
-3
@@ -28,10 +28,10 @@ import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class SuspendFunctionGenerationStrategy(
|
||||
open class SuspendFunctionGenerationStrategy(
|
||||
state: GenerationState,
|
||||
private val originalSuspendDescriptor: FunctionDescriptor,
|
||||
private val declaration: KtFunction,
|
||||
protected val originalSuspendDescriptor: FunctionDescriptor,
|
||||
protected val declaration: KtFunction,
|
||||
private val containingClassInternalName: String,
|
||||
private val constructorCallNormalizationMode: JVMConstructorCallNormalizationMode
|
||||
) : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.FunctionCodegen
|
||||
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy
|
||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
// For named suspend function we generate two methods:
|
||||
// 1) to use as noinline function, which have state machine
|
||||
// 2) to use from inliner: private one without state machine
|
||||
class SuspendInlineFunctionGenerationStrategy(
|
||||
state: GenerationState,
|
||||
originalSuspendDescriptor: FunctionDescriptor,
|
||||
declaration: KtFunction,
|
||||
containingClassInternalName: String,
|
||||
constructorCallNormalizationMode: JVMConstructorCallNormalizationMode,
|
||||
private val codegen: FunctionCodegen
|
||||
) : SuspendFunctionGenerationStrategy(
|
||||
state,
|
||||
originalSuspendDescriptor,
|
||||
declaration,
|
||||
containingClassInternalName,
|
||||
constructorCallNormalizationMode
|
||||
) {
|
||||
private val defaultStrategy = FunctionGenerationStrategy.FunctionDefault(state, declaration)
|
||||
|
||||
override fun wrapMethodVisitor(mv: MethodVisitor, access: Int, name: String, desc: String): MethodVisitor {
|
||||
if (access and Opcodes.ACC_ABSTRACT != 0) return mv
|
||||
|
||||
return MethodNodeCopyingMethodVisitor(
|
||||
super.wrapMethodVisitor(mv, access, name, desc),
|
||||
access,
|
||||
name,
|
||||
desc = desc,
|
||||
signature = null,
|
||||
exceptions = null,
|
||||
codegen = codegen,
|
||||
declaration = declaration,
|
||||
originalSuspendDescriptor = originalSuspendDescriptor,
|
||||
isReleaseCoroutines = state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||
)
|
||||
}
|
||||
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
super.doGenerateBody(codegen, signature)
|
||||
defaultStrategy.doGenerateBody(codegen, signature)
|
||||
}
|
||||
|
||||
private class MethodNodeCopyingMethodVisitor(
|
||||
delegate: MethodVisitor,
|
||||
private val access: Int,
|
||||
private val name: String,
|
||||
private val desc: String,
|
||||
private val signature: String?,
|
||||
private val exceptions: Array<out String>?,
|
||||
private val codegen: FunctionCodegen,
|
||||
private val declaration: KtFunction,
|
||||
private val originalSuspendDescriptor: FunctionDescriptor,
|
||||
private val isReleaseCoroutines: Boolean
|
||||
) : TransformationMethodVisitor(
|
||||
delegate,
|
||||
calculateAccessForInline(access),
|
||||
"$name\$\$forInline",
|
||||
desc,
|
||||
signature,
|
||||
exceptions
|
||||
) {
|
||||
override fun performTransformations(methodNode: MethodNode) {
|
||||
val newMethodNode = codegen.newMethod(
|
||||
OtherOrigin(declaration, getOrCreateJvmSuspendFunctionView(originalSuspendDescriptor, isReleaseCoroutines)),
|
||||
calculateAccessForInline(access), "$name\$\$forInline", desc, signature, exceptions
|
||||
)
|
||||
methodNode.instructions.resetLabels()
|
||||
methodNode.accept(newMethodNode)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun calculateAccessForInline(access: Int): Int {
|
||||
var accessForInline = access
|
||||
if (accessForInline and Opcodes.ACC_PUBLIC != 0) {
|
||||
accessForInline = accessForInline xor Opcodes.ACC_PUBLIC
|
||||
}
|
||||
if (accessForInline and Opcodes.ACC_PROTECTED != 0) {
|
||||
accessForInline = accessForInline xor Opcodes.ACC_PROTECTED
|
||||
}
|
||||
return accessForInline or Opcodes.ACC_PRIVATE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -503,7 +503,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
val asmMethod = if (callDefault)
|
||||
state.typeMapper.mapDefaultMethod(functionDescriptor, sourceCompilerForInline.contextKind)
|
||||
else
|
||||
jvmSignature.asmMethod
|
||||
mangleSuspendInlineFunctionAsmMethodIfNeeded(functionDescriptor, jvmSignature.asmMethod)
|
||||
|
||||
val methodId = MethodId(DescriptorUtils.getFqNameSafe(functionDescriptor.containingDeclaration), asmMethod)
|
||||
val directMember = getDirectMemberAndCallableFromObject(functionDescriptor)
|
||||
@@ -511,16 +511,26 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
return sourceCompilerForInline.doCreateMethodNodeFromSource(functionDescriptor, jvmSignature, callDefault, asmMethod)
|
||||
}
|
||||
|
||||
val resultInCache = state.inlineCache.methodNodeById.getOrPut(
|
||||
methodId
|
||||
) {
|
||||
doCreateMethodNodeFromCompiled(directMember, state, asmMethod)
|
||||
?: throw IllegalStateException("Couldn't obtain compiled function body for " + functionDescriptor)
|
||||
val resultInCache = state.inlineCache.methodNodeById.getOrPut(methodId) {
|
||||
val result = doCreateMethodNodeFromCompiled(directMember, state, asmMethod)
|
||||
?: if (functionDescriptor.isSuspend)
|
||||
doCreateMethodNodeFromCompiled(directMember, state, jvmSignature.asmMethod)
|
||||
else
|
||||
null
|
||||
result ?: throw IllegalStateException("Couldn't obtain compiled function body for $functionDescriptor")
|
||||
}
|
||||
|
||||
return resultInCache.copyWithNewNode(cloneMethodNode(resultInCache.node))
|
||||
}
|
||||
|
||||
// For suspend inline functions we generate two methods:
|
||||
// 1) normal one: with state machine to call directly
|
||||
// 2) for inliner: with mangled name and without state machine
|
||||
private fun mangleSuspendInlineFunctionAsmMethodIfNeeded(functionDescriptor: FunctionDescriptor, asmMethod: Method): Method {
|
||||
if (!functionDescriptor.isSuspend) return asmMethod
|
||||
return Method("${asmMethod.name}\$\$forInline", asmMethod.descriptor)
|
||||
}
|
||||
|
||||
private fun getDirectMemberAndCallableFromObject(functionDescriptor: FunctionDescriptor): CallableMemberDescriptor {
|
||||
val directMember = JvmCodegenUtil.getDirectMember(functionDescriptor)
|
||||
return (directMember as? ImportedFromObjectCallableDescriptor<*>)?.callableFromObject ?: directMember
|
||||
|
||||
Reference in New Issue
Block a user