Do not generate $$forInline suffix for enclosing method

in OUTERCLASS field.

The inliner generates two versions of suspend functions/lambdas in
inline functions: with state-machine and without. The former is used
to call the function from Java or via reflection and have ordinary
name, while the latter is used by inliner and have $$forInline suffix.
The inliner throws the state-machine version away, duplicates
$$forInline version and then call state-machine generator.

If these suspend functions/lambdas are not going to be inlined,
$$forInline version is not generated. However, all objects, which are
used in these suspend functions/lambdas, have $$forInline version
written to OUTERCLASS field. This leads to errors by proguard.
Since they are used in both state-machine version and for-inline ones,
we can simply remove $$forInline suffix from OUTERCLASS field and this
fixes the issue.

 #KT-31242 Fixed
This commit is contained in:
Ilmir Usmanov
2019-04-30 21:23:38 +03:00
parent dd6bc100ed
commit effbcdaf70
8 changed files with 126 additions and 32 deletions
@@ -10,7 +10,9 @@ import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.coroutines.DEBUG_METADATA_ANNOTATION_ASM_TYPE
import org.jetbrains.kotlin.codegen.coroutines.isCapturedSuspendLambda
import org.jetbrains.kotlin.codegen.coroutines.isCoroutineSuperClass
import org.jetbrains.kotlin.codegen.coroutines.isResumeImplMethodName
import org.jetbrains.kotlin.codegen.inline.coroutines.CoroutineTransformer
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
import org.jetbrains.kotlin.codegen.serialization.JvmCodegenStringTable
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
@@ -244,7 +246,14 @@ class AnonymousObjectTransformer(
private fun writeOuterInfo(visitor: ClassVisitor) {
val info = inliningContext.callSiteInfo
visitor.visitOuterClass(info.ownerClassName, info.functionName, info.functionDesc)
// Since $$forInline functions are not generated if retransformation is the last one (i.e. call site is not inline)
// link to the function in OUTERCLASS field becomes invalid. However, since $$forInline function always has no-inline
// companion without the suffix, use it.
if (info.isSuspend && info.isInlineOrInsideInline) {
visitor.visitOuterClass(info.ownerClassName, info.functionName?.removeSuffix(FOR_INLINE_SUFFIX), info.functionDesc)
} else {
visitor.visitOuterClass(info.ownerClassName, info.functionName, info.functionDesc)
}
}
private fun inlineMethodAndUpdateGlobalResult(
@@ -288,7 +297,8 @@ class AnonymousObjectTransformer(
transformationInfo.oldClassName,
sourceNode.name,
if (isConstructor) transformationInfo.newConstructorDescriptor else sourceNode.desc,
inliningContext.callSiteInfo.isInlineOrInsideInline
inliningContext.callSiteInfo.isInlineOrInsideInline,
isSuspendFunctionOrLambda(sourceNode)
), null
)
@@ -298,6 +308,12 @@ class AnonymousObjectTransformer(
return result
}
private fun isSuspendFunctionOrLambda(sourceNode: MethodNode): Boolean =
(sourceNode.desc.endsWith(";Lkotlin/coroutines/Continuation;)Ljava/lang/Object;") ||
sourceNode.desc.endsWith(";Lkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object;")) &&
(CoroutineTransformer.findFakeContinuationConstructorClassName(sourceNode) != null ||
languageVersionSettings.isResumeImplMethodName(sourceNode.name.removeSuffix(FOR_INLINE_SUFFIX)))
private fun generateConstructorAndFields(
classBuilder: ClassBuilder,
allCapturedBuilder: ParametersBuilder,
@@ -1,19 +1,14 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen.inline
class InlineCallSiteInfo(val ownerClassName: String, val functionName: String?, val functionDesc: String?, val isInlineOrInsideInline: Boolean)
class InlineCallSiteInfo(
val ownerClassName: String,
val functionName: String?,
val functionDesc: String?,
val isInlineOrInsideInline: Boolean,
val isSuspend: Boolean
)
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -122,7 +122,11 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
val signature = codegen.state.typeMapper.mapSignatureSkipGeneric(context.functionDescriptor, context.contextKind)
return InlineCallSiteInfo(
parentCodegen.className, signature.asmMethod.name, signature.asmMethod.descriptor, compilationContextFunctionDescriptor.isInlineOrInsideInline()
parentCodegen.className,
signature.asmMethod.name,
signature.asmMethod.descriptor,
compilationContextFunctionDescriptor.isInlineOrInsideInline(),
compilationContextFunctionDescriptor.isSuspend
)
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm.codegen
@@ -65,7 +54,7 @@ class IrSourceCompilerForInline(
get() = OwnerKind.getMemberOwnerKind(callElement.descriptor.containingDeclaration)
override val inlineCallSiteInfo: InlineCallSiteInfo
get() = InlineCallSiteInfo("TODO", null, null, false)
get() = InlineCallSiteInfo("TODO", null, null, false, false)
override val lazySourceMapper: DefaultSourceMapper
get() = codegen.classCodegen.getOrCreateSourceMapper()
@@ -0,0 +1,60 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FILE: flow.kt
// COMMON_COROUTINES_TEST
// FULL_JDK
// WITH_RUNTIME
// WITH_COROUTINES
package flow
interface FlowCollector<T> {
suspend fun emit(value: T)
}
interface Flow<T> {
suspend fun collect(collector: FlowCollector<T>)
}
public inline fun <T> flow(crossinline block: suspend FlowCollector<T>.() -> Unit) = object : Flow<T> {
override suspend fun collect(collector: FlowCollector<T>) = collector.block()
}
suspend inline fun <T> Flow<T>.collect(crossinline action: suspend (T) -> Unit): Unit =
collect(object : FlowCollector<T> {
override suspend fun emit(value: T) = action(value)
})
public inline fun <T, R> Flow<T>.transform(crossinline transformer: suspend FlowCollector<R>.(value: T) -> Unit): Flow<R> {
return flow {
collect { value ->
transformer(value)
}
}
}
public inline fun <T, R> Flow<T>.map(crossinline transformer: suspend (value: T) -> R): Flow<R> = transform { value -> emit(transformer(value)) }
inline fun decorate() = suspend {
flow<Int> {
emit(1)
}.map { it + 1 }
.collect {
}
}
// FILE: box.kt
// COMMON_COROUTINES_TEST
import flow.*
fun box() : String {
decorate()
val enclosingMethod = try {
Class.forName("flow.FlowKt\$decorate\$1\$invokeSuspend\$\$inlined\$map\$1\$1").enclosingMethod
} catch (ignore: ClassNotFoundException) {
Class.forName("flow.FlowKt\$decorate\$1\$doResume\$\$inlined\$map\$1\$1").enclosingMethod
}
if ("$enclosingMethod".contains("\$\$forInline")) return "$enclosingMethod"
return "OK"
}
@@ -3430,6 +3430,16 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
}
@TestMetadata("enclodingMethod.kt")
public void testEnclodingMethod_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/enclodingMethod.kt", "kotlin.coroutines.experimental");
}
@TestMetadata("enclodingMethod.kt")
public void testEnclodingMethod_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/enclodingMethod.kt", "kotlin.coroutines");
}
@TestMetadata("fileNameInMetadata.kt")
public void testFileNameInMetadata() throws Exception {
runTest("compiler/testData/codegen/boxInline/suspend/fileNameInMetadata.kt");
@@ -3430,6 +3430,16 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
}
@TestMetadata("enclodingMethod.kt")
public void testEnclodingMethod_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/enclodingMethod.kt", "kotlin.coroutines.experimental");
}
@TestMetadata("enclodingMethod.kt")
public void testEnclodingMethod_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/enclodingMethod.kt", "kotlin.coroutines");
}
@TestMetadata("fileNameInMetadata.kt")
public void testFileNameInMetadata() throws Exception {
runTest("compiler/testData/codegen/boxInline/suspend/fileNameInMetadata.kt");
@@ -3430,6 +3430,16 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/crossinlineSuspendLambdaInsideCrossinlineSuspendLambda.kt", "kotlin.coroutines");
}
@TestMetadata("enclodingMethod.kt")
public void testEnclodingMethod_1_2() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/enclodingMethod.kt", "kotlin.coroutines.experimental");
}
@TestMetadata("enclodingMethod.kt")
public void testEnclodingMethod_1_3() throws Exception {
runTestWithPackageReplacement("compiler/testData/codegen/boxInline/suspend/enclodingMethod.kt", "kotlin.coroutines");
}
@TestMetadata("fileNameInMetadata.kt")
public void testFileNameInMetadata() throws Exception {
runTest("compiler/testData/codegen/boxInline/suspend/fileNameInMetadata.kt");