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
|
||||
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
// IGNORE_BACKEND: JVM
|
||||
// WITH_COROUTINES
|
||||
// WITH_RUNTIME
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
+18
-2
@@ -23,13 +23,29 @@ final class InlineWithStateMachineKt$mainSuspend$1 {
|
||||
synthetic final method setLabel(p0: int): void
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class InlineWithStateMachineKt$suspendHere$1 {
|
||||
field L$0: java.lang.Object
|
||||
field L$1: java.lang.Object
|
||||
synthetic field data: java.lang.Object
|
||||
synthetic field exception: java.lang.Throwable
|
||||
inner class InlineWithStateMachineKt$suspendHere$1
|
||||
public method <init>(p0: COROUTINES_PACKAGE.Continuation): void
|
||||
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
|
||||
synthetic final method getLabel(): int
|
||||
synthetic final method setLabel(p0: int): void
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class InlineWithStateMachineKt {
|
||||
inner class InlineWithStateMachineKt$box$1
|
||||
inner class InlineWithStateMachineKt$mainSuspend$1
|
||||
inner class InlineWithStateMachineKt$suspendHere$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 mainSuspend(@org.jetbrains.annotations.NotNull p0: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
private final static method suspendHere(p0: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
private final static method suspendThere(p0: java.lang.String, p1: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
private final static @org.jetbrains.annotations.Nullable method suspendHere$$forInline(@org.jetbrains.annotations.NotNull p0: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
private final static @org.jetbrains.annotations.Nullable method suspendThere$$forInline(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
}
|
||||
|
||||
Vendored
+2
-1
@@ -31,5 +31,6 @@ public final class InlineWithoutStateMachineKt {
|
||||
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void
|
||||
public final static @org.jetbrains.annotations.Nullable method complexSuspend(@org.jetbrains.annotations.NotNull p0: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
private final static method suspendThere(p0: java.lang.String, p1: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
private final static @org.jetbrains.annotations.Nullable method suspendThere$$forInline(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: COROUTINES_PACKAGE.Continuation): java.lang.Object
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// FILE: inlined.kt
|
||||
// WITH_RUNTIME
|
||||
// NO_CHECK_LAMBDA_INLINING
|
||||
|
||||
import kotlin.coroutines.experimental.*
|
||||
import kotlin.coroutines.experimental.intrinsics.*
|
||||
|
||||
var result = "FAIL"
|
||||
var i = 0
|
||||
var finished = false
|
||||
|
||||
var proceed: () -> Unit = {}
|
||||
|
||||
suspend fun suspendHere() = suspendCoroutine<Unit> {
|
||||
i++
|
||||
proceed = { it.resume(Unit) }
|
||||
}
|
||||
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
@kotlin.internal.InlineOnly
|
||||
inline suspend fun inlineMe() {
|
||||
suspendHere()
|
||||
suspendHere()
|
||||
suspendHere()
|
||||
suspendHere()
|
||||
suspendHere()
|
||||
}
|
||||
|
||||
// FILE: inlineSite.kt
|
||||
|
||||
import kotlin.coroutines.experimental.*
|
||||
import kotlin.coroutines.experimental.intrinsics.*
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
val continuation = object: Continuation<Unit> {
|
||||
override val context: CoroutineContext
|
||||
get() = EmptyCoroutineContext
|
||||
|
||||
override fun resume(value: Unit) {
|
||||
proceed = {
|
||||
result = "OK"
|
||||
finished = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun resumeWithException(exception: Throwable) {
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
c.startCoroutine(continuation)
|
||||
}
|
||||
|
||||
suspend fun inlineSite() {
|
||||
inlineMe()
|
||||
inlineMe()
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
inlineSite()
|
||||
}
|
||||
for (counter in 0 until 10) {
|
||||
if (i != counter + 1) return "Expected ${counter + 1}, got $i"
|
||||
proceed()
|
||||
}
|
||||
if (i != 10) return "FAIL $i"
|
||||
if (finished) return "resume on root continuation is called"
|
||||
proceed()
|
||||
if (!finished) return "resume on root continuation is not called"
|
||||
return result
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// FILE: inlined.kt
|
||||
// WITH_RUNTIME
|
||||
// NO_CHECK_LAMBDA_INLINING
|
||||
|
||||
import kotlin.coroutines.experimental.*
|
||||
import kotlin.coroutines.experimental.intrinsics.*
|
||||
|
||||
var result = "FAIL"
|
||||
var i = 0
|
||||
var finished = false
|
||||
|
||||
var proceed: () -> Unit = {}
|
||||
|
||||
suspend fun suspendHere() = suspendCoroutine<Unit> {
|
||||
i++
|
||||
proceed = { it.resume(Unit) }
|
||||
}
|
||||
|
||||
inline suspend fun inlineMe() {
|
||||
suspendHere()
|
||||
suspendHere()
|
||||
suspendHere()
|
||||
suspendHere()
|
||||
suspendHere()
|
||||
}
|
||||
|
||||
// FILE: inlineSite.kt
|
||||
|
||||
import kotlin.coroutines.experimental.*
|
||||
import kotlin.coroutines.experimental.intrinsics.*
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
val continuation = object: Continuation<Unit> {
|
||||
override val context: CoroutineContext
|
||||
get() = EmptyCoroutineContext
|
||||
|
||||
override fun resume(value: Unit) {
|
||||
proceed = {
|
||||
result = "OK"
|
||||
finished = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun resumeWithException(exception: Throwable) {
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
c.startCoroutine(continuation)
|
||||
}
|
||||
|
||||
suspend fun inlineSite() {
|
||||
inlineMe()
|
||||
inlineMe()
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
inlineSite()
|
||||
}
|
||||
for (counter in 0 until 10) {
|
||||
if (i != counter + 1) return "Expected ${counter + 1}, got $i"
|
||||
proceed()
|
||||
}
|
||||
if (i != 10) return "FAIL $i"
|
||||
if (finished) return "resume on root continuation is called"
|
||||
proceed()
|
||||
if (!finished) return "resume on root continuation is not called"
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
suspend inline fun simple() {}
|
||||
|
||||
suspend inline fun <T> generic() {}
|
||||
|
||||
suspend inline fun <T, reified U> genericWithReified() {}
|
||||
|
||||
suspend inline fun Unit.simple() {}
|
||||
|
||||
suspend inline fun <T> T.generic() {}
|
||||
|
||||
suspend inline fun <T, reified U> T.genericWithReified() {}
|
||||
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
@kotlin.internal.InlineOnly
|
||||
suspend inline fun shouldNotHaveSuffix() {}
|
||||
|
||||
suspend inline fun acceptsCrossinline(crossinline c: () -> Unit) {}
|
||||
|
||||
class Foo {
|
||||
suspend inline fun simple() {}
|
||||
|
||||
suspend inline fun <T> generic() {}
|
||||
|
||||
suspend inline fun <T, reified U> genericWithReified() {}
|
||||
|
||||
suspend inline fun Unit.simple() {}
|
||||
|
||||
suspend inline fun <T> T.generic() {}
|
||||
|
||||
suspend inline fun <T, reified U> T.genericWithReified() {}
|
||||
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
@kotlin.internal.InlineOnly
|
||||
suspend inline fun shouldNotHaveSuffix() {}
|
||||
|
||||
suspend inline fun acceptsCrossinline(crossinline c: () -> Unit) {}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
@kotlin.Metadata
|
||||
public final class Foo {
|
||||
public method <init>(): void
|
||||
private final method acceptsCrossinline(p0: kotlin.jvm.functions.Function0, p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final @org.jetbrains.annotations.Nullable method generic$$forInline(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final @org.jetbrains.annotations.Nullable method generic$$forInline(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
public final @org.jetbrains.annotations.Nullable method generic(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
public final @org.jetbrains.annotations.Nullable method generic(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final method genericWithReified(p0: java.lang.Object, p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final method genericWithReified(p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final @kotlin.internal.InlineOnly method shouldNotHaveSuffix(p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final @org.jetbrains.annotations.Nullable method simple$$forInline(@org.jetbrains.annotations.NotNull p0: kotlin.Unit, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final @org.jetbrains.annotations.Nullable method simple$$forInline(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
public final @org.jetbrains.annotations.Nullable method simple(@org.jetbrains.annotations.NotNull p0: kotlin.Unit, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
public final @org.jetbrains.annotations.Nullable method simple(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class SimpleNamedKt {
|
||||
private final static method acceptsCrossinline(p0: kotlin.jvm.functions.Function0, p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final static @org.jetbrains.annotations.Nullable method generic$$forInline(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final static @org.jetbrains.annotations.Nullable method generic$$forInline(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
public final static @org.jetbrains.annotations.Nullable method generic(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
public final static @org.jetbrains.annotations.Nullable method generic(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final static method genericWithReified(p0: java.lang.Object, p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final static method genericWithReified(p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final static @kotlin.internal.InlineOnly method shouldNotHaveSuffix(p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final static @org.jetbrains.annotations.Nullable method simple$$forInline(@org.jetbrains.annotations.NotNull p0: kotlin.Unit, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
private final static @org.jetbrains.annotations.Nullable method simple$$forInline(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
public final static @org.jetbrains.annotations.Nullable method simple(@org.jetbrains.annotations.NotNull p0: kotlin.Unit, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
public final static @org.jetbrains.annotations.Nullable method simple(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object
|
||||
}
|
||||
+1
@@ -107,6 +107,7 @@ abstract class AbstractLightAnalysisModeTest : CodegenTestCase() {
|
||||
|
||||
override fun shouldWriteMethod(access: Int, name: String, desc: String) = when {
|
||||
name == "<clinit>" -> false
|
||||
name.contains("\$\$forInline") -> false
|
||||
AsmTypes.DEFAULT_CONSTRUCTOR_MARKER.descriptor in desc -> false
|
||||
name.startsWith("access$") && (access and ACC_STATIC != 0) && (access and ACC_SYNTHETIC != 0) -> false
|
||||
else -> true
|
||||
|
||||
@@ -128,6 +128,9 @@ object InlineTestUtil {
|
||||
if (skipMethodsOfThisClass) {
|
||||
return null
|
||||
}
|
||||
if (name == "doResume" && desc == "(Ljava/lang/Object;Ljava/lang/Throwable;)Ljava/lang/Object;") {
|
||||
return null
|
||||
}
|
||||
|
||||
return object : MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions) {
|
||||
override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
|
||||
|
||||
Generated
+2
-14
@@ -7059,25 +7059,13 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
@TestMetadata("inlineMultiModuleOverride.kt")
|
||||
public void testInlineMultiModuleOverride_1_2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt");
|
||||
try {
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines.experimental");
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines.experimental");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineMultiModuleOverride.kt")
|
||||
public void testInlineMultiModuleOverride_1_3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt");
|
||||
try {
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines");
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineMultiModuleWithController.kt")
|
||||
|
||||
+23
@@ -3422,6 +3422,29 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class InlineUsedAsNoinline extends AbstractIrBlackBoxInlineCodegenTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInInlineUsedAsNoinline() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineOnly.kt")
|
||||
public void testInlineOnly() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/inlineOnly.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleNamed.kt")
|
||||
public void testSimpleNamed() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/simpleNamed.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/suspend/receiver")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+23
@@ -3422,6 +3422,29 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class InlineUsedAsNoinline extends AbstractIrCompileKotlinAgainstInlineKotlinTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInInlineUsedAsNoinline() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineOnly.kt")
|
||||
public void testInlineOnly() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/inlineOnly.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleNamed.kt")
|
||||
public void testSimpleNamed() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/simpleNamed.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/suspend/receiver")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+2
-14
@@ -7059,25 +7059,13 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
@TestMetadata("inlineMultiModuleOverride.kt")
|
||||
public void testInlineMultiModuleOverride_1_2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt");
|
||||
try {
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines.experimental");
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines.experimental");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineMultiModuleOverride.kt")
|
||||
public void testInlineMultiModuleOverride_1_3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt");
|
||||
try {
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines");
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineMultiModuleWithController.kt")
|
||||
|
||||
+23
@@ -3422,6 +3422,29 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class InlineUsedAsNoinline extends AbstractBlackBoxInlineCodegenTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInInlineUsedAsNoinline() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineOnly.kt")
|
||||
public void testInlineOnly() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/inlineOnly.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleNamed.kt")
|
||||
public void testSimpleNamed() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/simpleNamed.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/suspend/receiver")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+18
@@ -253,6 +253,24 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeListing/inline")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Inline extends AbstractBytecodeListingTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInInline() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/inline"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleNamed.kt")
|
||||
public void testSimpleNamed() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeListing/inline/simpleNamed.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeListing/inlineClasses")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Generated
+23
@@ -3422,6 +3422,29 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class InlineUsedAsNoinline extends AbstractCompileKotlinAgainstInlineKotlinTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInInlineUsedAsNoinline() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineOnly.kt")
|
||||
public void testInlineOnly() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/inlineOnly.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleNamed.kt")
|
||||
public void testSimpleNamed() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/simpleNamed.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/suspend/receiver")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+12
-24
@@ -7036,30 +7036,6 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class MultiModule extends AbstractLightAnalysisModeTest {
|
||||
@TestMetadata("inlineMultiModuleOverride.kt")
|
||||
public void ignoreInlineMultiModuleOverride_1_2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt");
|
||||
try {
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines.experimental");
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineMultiModuleOverride.kt")
|
||||
public void ignoreInlineMultiModuleOverride_1_3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt");
|
||||
try {
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines");
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||
}
|
||||
@@ -7080,6 +7056,18 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineMultiModuleOverride.kt")
|
||||
public void testInlineMultiModuleOverride_1_2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt");
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines.experimental");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineMultiModuleOverride.kt")
|
||||
public void testInlineMultiModuleOverride_1_3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt");
|
||||
doTestWithCoroutinesPackageReplacement(fileName, "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineMultiModuleWithController.kt")
|
||||
public void testInlineMultiModuleWithController_1_2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleWithController.kt");
|
||||
|
||||
@@ -73,7 +73,7 @@ fun MemberDescriptor.isInlineOnlyOrReifiable(): Boolean =
|
||||
this is CallableMemberDescriptor && (isReifiable() || DescriptorUtils.getDirectMember(this).isReifiable() || isInlineOnly())
|
||||
|
||||
fun MemberDescriptor.isEffectivelyInlineOnly(): Boolean =
|
||||
isInlineOnlyOrReifiable() || safeAs<FunctionDescriptor>()?.let { it.isSuspend && it.isInline } == true
|
||||
isInlineOnlyOrReifiable() || (this is FunctionDescriptor && this.isSuspend && this.valueParameters.any { it.isCrossinline })
|
||||
|
||||
fun MemberDescriptor.isInlineOnly(): Boolean {
|
||||
if (this !is FunctionDescriptor ||
|
||||
|
||||
Reference in New Issue
Block a user