Use our own primitive boxing methods in coroutines codegen

#KT-26490 Fixed
This commit is contained in:
Ilmir Usmanov
2018-09-12 15:35:07 +03:00
parent 273889d1a9
commit e9fc65c7ae
7 changed files with 105 additions and 1 deletions
@@ -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)
)
}
}
}
@@ -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)
@@ -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)