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.DEBUG_METADATA_ANNOTATION_ASM_TYPE
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.isCapturedSuspendLambda
|
import org.jetbrains.kotlin.codegen.coroutines.isCapturedSuspendLambda
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.isCoroutineSuperClass
|
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.CoroutineTransformer
|
||||||
|
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
|
||||||
import org.jetbrains.kotlin.codegen.serialization.JvmCodegenStringTable
|
import org.jetbrains.kotlin.codegen.serialization.JvmCodegenStringTable
|
||||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||||
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
|
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
|
||||||
@@ -244,7 +246,14 @@ class AnonymousObjectTransformer(
|
|||||||
|
|
||||||
private fun writeOuterInfo(visitor: ClassVisitor) {
|
private fun writeOuterInfo(visitor: ClassVisitor) {
|
||||||
val info = inliningContext.callSiteInfo
|
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(
|
private fun inlineMethodAndUpdateGlobalResult(
|
||||||
@@ -288,7 +297,8 @@ class AnonymousObjectTransformer(
|
|||||||
transformationInfo.oldClassName,
|
transformationInfo.oldClassName,
|
||||||
sourceNode.name,
|
sourceNode.name,
|
||||||
if (isConstructor) transformationInfo.newConstructorDescriptor else sourceNode.desc,
|
if (isConstructor) transformationInfo.newConstructorDescriptor else sourceNode.desc,
|
||||||
inliningContext.callSiteInfo.isInlineOrInsideInline
|
inliningContext.callSiteInfo.isInlineOrInsideInline,
|
||||||
|
isSuspendFunctionOrLambda(sourceNode)
|
||||||
), null
|
), null
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -298,6 +308,12 @@ class AnonymousObjectTransformer(
|
|||||||
return result
|
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(
|
private fun generateConstructorAndFields(
|
||||||
classBuilder: ClassBuilder,
|
classBuilder: ClassBuilder,
|
||||||
allCapturedBuilder: ParametersBuilder,
|
allCapturedBuilder: ParametersBuilder,
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2016 JetBrains s.r.o.
|
* 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.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.inline
|
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.
|
* 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)
|
val signature = codegen.state.typeMapper.mapSignatureSkipGeneric(context.functionDescriptor, context.contextKind)
|
||||||
return InlineCallSiteInfo(
|
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.
|
* 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.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||||
@@ -65,7 +54,7 @@ class IrSourceCompilerForInline(
|
|||||||
get() = OwnerKind.getMemberOwnerKind(callElement.descriptor.containingDeclaration)
|
get() = OwnerKind.getMemberOwnerKind(callElement.descriptor.containingDeclaration)
|
||||||
|
|
||||||
override val inlineCallSiteInfo: InlineCallSiteInfo
|
override val inlineCallSiteInfo: InlineCallSiteInfo
|
||||||
get() = InlineCallSiteInfo("TODO", null, null, false)
|
get() = InlineCallSiteInfo("TODO", null, null, false, false)
|
||||||
|
|
||||||
override val lazySourceMapper: DefaultSourceMapper
|
override val lazySourceMapper: DefaultSourceMapper
|
||||||
get() = codegen.classCodegen.getOrCreateSourceMapper()
|
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");
|
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")
|
@TestMetadata("fileNameInMetadata.kt")
|
||||||
public void testFileNameInMetadata() throws Exception {
|
public void testFileNameInMetadata() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxInline/suspend/fileNameInMetadata.kt");
|
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");
|
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")
|
@TestMetadata("fileNameInMetadata.kt")
|
||||||
public void testFileNameInMetadata() throws Exception {
|
public void testFileNameInMetadata() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxInline/suspend/fileNameInMetadata.kt");
|
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");
|
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")
|
@TestMetadata("fileNameInMetadata.kt")
|
||||||
public void testFileNameInMetadata() throws Exception {
|
public void testFileNameInMetadata() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxInline/suspend/fileNameInMetadata.kt");
|
runTest("compiler/testData/codegen/boxInline/suspend/fileNameInMetadata.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user