Use our own primitive boxing methods in coroutines codegen
#KT-26490 Fixed
This commit is contained in:
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.optimization.boxing.isPrimitiveBoxing
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.topLevelClassInternalName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
private val BOXING_CLASS_INTERNAL_NAME =
|
||||
RELEASE_COROUTINES_VERSION_SETTINGS.coroutinesJvmInternalPackageFqName().child(Name.identifier("Boxing")).topLevelClassInternalName()
|
||||
|
||||
object ChangeBoxingMethodTransformer : MethodTransformer() {
|
||||
private val wrapperToInternalBoxing: Map<String, String>
|
||||
|
||||
init {
|
||||
val map = hashMapOf<String, String>()
|
||||
for (primitiveType in JvmPrimitiveType.values()) {
|
||||
val name = primitiveType.wrapperFqName.topLevelClassInternalName()
|
||||
map[name] = "box${primitiveType.javaKeywordName.capitalize()}"
|
||||
}
|
||||
wrapperToInternalBoxing = map
|
||||
}
|
||||
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
for (boxing in methodNode.instructions.asSequence().filter { it.isPrimitiveBoxing() }) {
|
||||
assert(boxing.opcode == Opcodes.INVOKESTATIC) {
|
||||
"boxing shall be INVOKESTATIC wrapper.valueOf"
|
||||
}
|
||||
boxing as MethodInsnNode
|
||||
val methodName = wrapperToInternalBoxing[boxing.owner].sure {
|
||||
"expected primitive wrapper, but got ${boxing.owner}"
|
||||
}
|
||||
methodNode.instructions.set(
|
||||
boxing,
|
||||
MethodInsnNode(boxing.opcode, BOXING_CLASS_INTERNAL_NAME, methodName, boxing.desc, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -83,6 +83,9 @@ class CoroutineTransformerMethodVisitor(
|
||||
|
||||
FixStackMethodTransformer().transform(containingClassInternalName, methodNode)
|
||||
RedundantLocalsEliminationMethodTransformer(languageVersionSettings).transform(containingClassInternalName, methodNode)
|
||||
if (languageVersionSettings.isReleaseCoroutines()) {
|
||||
ChangeBoxingMethodTransformer.transform(containingClassInternalName, methodNode)
|
||||
}
|
||||
updateMaxStack(methodNode)
|
||||
|
||||
val suspensionPoints = collectSuspensionPoints(methodNode)
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ const val DO_RESUME_METHOD_NAME = "doResume"
|
||||
const val INVOKE_SUSPEND_METHOD_NAME = "invokeSuspend"
|
||||
const val EXCEPTION_FIELD_NAME = "exception"
|
||||
|
||||
private val RELEASE_COROUTINES_VERSION_SETTINGS = LanguageVersionSettingsImpl(LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3)
|
||||
val RELEASE_COROUTINES_VERSION_SETTINGS = LanguageVersionSettingsImpl(LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3)
|
||||
|
||||
fun LanguageVersionSettings.isResumeImplMethodName(name: String) =
|
||||
if (isReleaseCoroutines())
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// !LANGUAGE: +ReleaseCoroutines
|
||||
|
||||
inline fun inlineMe(crossinline c: suspend () -> Int): suspend () -> Int {
|
||||
val i: suspend () -> Int = { c() + c() }
|
||||
return i
|
||||
}
|
||||
|
||||
// 1 valueOf
|
||||
// 0 boxInt
|
||||
@@ -0,0 +1,9 @@
|
||||
// !LANGUAGE: +ReleaseCoroutines
|
||||
|
||||
inline suspend fun inlineMe() = 1000
|
||||
|
||||
// inlineMe$$forInline : valueOf
|
||||
// inlineMe : boxInt
|
||||
|
||||
// 1 valueOf
|
||||
// 1 boxInt
|
||||
@@ -0,0 +1,6 @@
|
||||
// !LANGUAGE: +ReleaseCoroutines
|
||||
|
||||
suspend fun produce(): Int = 1000
|
||||
|
||||
// 0 valueOf
|
||||
// 1 boxInt
|
||||
@@ -397,6 +397,34 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/boxing")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Boxing extends AbstractBytecodeTextTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInBoxing() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/boxing"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("crossinlineSuspend.kt")
|
||||
public void testCrossinlineSuspend() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/boxing/crossinlineSuspend.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineSuspend.kt")
|
||||
public void testInlineSuspend() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/boxing/inlineSuspend.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("suspend.kt")
|
||||
public void testSuspend() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/boxing/suspend.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/boxingOptimization")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user