diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 0040217e165..13077885a60 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -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.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.unwrapInitialDescriptorForSuspendFunction(functionDescriptor), + function, + v.getThisName(), + state.getConstructorCallNormalizationMode() + ); + } else { + strategy = new SuspendInlineFunctionGenerationStrategy( + state, + CoroutineCodegenUtilKt.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, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt index 8541f5ab727..dc218ee3563 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendFunctionGenerationStrategy.kt @@ -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) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendInlineFunctionGenerationStrategy.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendInlineFunctionGenerationStrategy.kt new file mode 100644 index 00000000000..65438994899 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SuspendInlineFunctionGenerationStrategy.kt @@ -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?, + 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 + } + } +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index d55bbafafe3..303cbd159f5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -503,7 +503,7 @@ abstract class InlineCodegen( 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( 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 diff --git a/compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt b/compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt index bfb4943eeed..fd7e43f325c 100644 --- a/compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt +++ b/compiler/testData/codegen/box/coroutines/multiModule/inlineMultiModuleOverride.kt @@ -1,6 +1,5 @@ // IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: NATIVE -// IGNORE_BACKEND: JVM // WITH_COROUTINES // WITH_RUNTIME // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.txt index 10bf75e84b6..dd1c2e0b921 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.txt @@ -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 (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 } diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt index 09ef8dcb272..99505265e8f 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt @@ -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 } diff --git a/compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/inlineOnly.kt b/compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/inlineOnly.kt new file mode 100644 index 00000000000..960e8234336 --- /dev/null +++ b/compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/inlineOnly.kt @@ -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 { + 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 { + 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 +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/simpleNamed.kt b/compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/simpleNamed.kt new file mode 100644 index 00000000000..9d5976cee2c --- /dev/null +++ b/compiler/testData/codegen/boxInline/suspend/inlineUsedAsNoinline/simpleNamed.kt @@ -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 { + 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 { + 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 +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/inline/simpleNamed.kt b/compiler/testData/codegen/bytecodeListing/inline/simpleNamed.kt new file mode 100644 index 00000000000..d67bd824b90 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/inline/simpleNamed.kt @@ -0,0 +1,39 @@ +// WITH_RUNTIME + +suspend inline fun simple() {} + +suspend inline fun generic() {} + +suspend inline fun genericWithReified() {} + +suspend inline fun Unit.simple() {} + +suspend inline fun T.generic() {} + +suspend inline fun 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 generic() {} + + suspend inline fun genericWithReified() {} + + suspend inline fun Unit.simple() {} + + suspend inline fun T.generic() {} + + suspend inline fun T.genericWithReified() {} + + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.InlineOnly + suspend inline fun shouldNotHaveSuffix() {} + + suspend inline fun acceptsCrossinline(crossinline c: () -> Unit) {} +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/inline/simpleNamed.txt b/compiler/testData/codegen/bytecodeListing/inline/simpleNamed.txt new file mode 100644 index 00000000000..94d4812e130 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/inline/simpleNamed.txt @@ -0,0 +1,32 @@ +@kotlin.Metadata +public final class Foo { + public method (): 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 +} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLightAnalysisModeTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLightAnalysisModeTest.kt index 9c4dd8ec939..825211b4dda 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLightAnalysisModeTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractLightAnalysisModeTest.kt @@ -107,6 +107,7 @@ abstract class AbstractLightAnalysisModeTest : CodegenTestCase() { override fun shouldWriteMethod(access: Int, name: String, desc: String) = when { name == "" -> 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 diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/InlineTestUtil.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/InlineTestUtil.kt index d4713326204..0719a40cd8c 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/InlineTestUtil.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/InlineTestUtil.kt @@ -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) { diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index f2dd2def78f..b35ba67e2fa 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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") diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java index f5a5b57ea72..efb08cf742b 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java @@ -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) diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java index bbc017d6c05..c90645949f8 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java @@ -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) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 47edebd3d66..3b175a6277f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java index dba9d993c50..92739631958 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java @@ -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) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 0579db9eb86..5d0144ed8c1 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -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) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java index ec6e5831a7f..d93d5f2374e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -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) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index db052ceec7b..411df2d12bf 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -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"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt index 223613b5a3e..b9daccd2e8e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/annotationUtil.kt @@ -73,7 +73,7 @@ fun MemberDescriptor.isInlineOnlyOrReifiable(): Boolean = this is CallableMemberDescriptor && (isReifiable() || DescriptorUtils.getDirectMember(this).isReifiable() || isInlineOnly()) fun MemberDescriptor.isEffectivelyInlineOnly(): Boolean = - isInlineOnlyOrReifiable() || safeAs()?.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 ||