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:
+18
-2
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+3
-14
@@ -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"
|
||||
}
|
||||
+10
@@ -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");
|
||||
|
||||
Generated
+10
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user