diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/LabelNormalizationMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/LabelNormalizationMethodTransformer.kt index 81a7bb8c945..d40624659c3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/LabelNormalizationMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/LabelNormalizationMethodTransformer.kt @@ -150,13 +150,13 @@ class LabelNormalizationMethodTransformer : MethodTransformer() { } } -private fun InsnList.replaceNodeGetNext(oldNode: AbstractInsnNode, newNode: AbstractInsnNode): AbstractInsnNode? { +fun InsnList.replaceNodeGetNext(oldNode: AbstractInsnNode, newNode: AbstractInsnNode): AbstractInsnNode? { insertBefore(oldNode, newNode) remove(oldNode) return newNode.next } -private fun InsnList.removeNodeGetNext(oldNode: AbstractInsnNode): AbstractInsnNode? { +fun InsnList.removeNodeGetNext(oldNode: AbstractInsnNode): AbstractInsnNode? { val next = oldNode.next remove(oldNode) return next diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.java index 633e0d47493..3f218fed8d3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil; import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer; +import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantCoercionToUnitTransformer; import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantNullCheckMethodTransformer; import org.jetbrains.kotlin.codegen.optimization.common.UtilKt; import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer; @@ -42,7 +43,8 @@ public class OptimizationMethodVisitor extends MethodVisitor { new RedundantNullCheckMethodTransformer(), new RedundantBoxingMethodTransformer(), new DeadCodeEliminationMethodTransformer(), - new RedundantGotoMethodTransformer() + new RedundantGotoMethodTransformer(), + new RedundantCoercionToUnitTransformer() }; private final MethodNode methodNode; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.kt index 81debf608bc..f835f26ec2d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.kt @@ -117,89 +117,86 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic protected open fun onMergeFail(value: BoxedBasicValue) {} protected open fun onMergeSuccess(v: BoxedBasicValue, w: BoxedBasicValue) {} - companion object { - private val UNBOXING_METHOD_NAMES = - ImmutableSet.of("booleanValue", "charValue", "byteValue", "shortValue", "intValue", "floatValue", "longValue", "doubleValue") +} - private val KCLASS_TO_JLCLASS = Type.getMethodDescriptor(AsmTypes.JAVA_CLASS_TYPE, AsmTypes.K_CLASS_TYPE) - private val JLCLASS_TO_KCLASS = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE) +private val UNBOXING_METHOD_NAMES = + ImmutableSet.of("booleanValue", "charValue", "byteValue", "shortValue", "intValue", "floatValue", "longValue", "doubleValue") - private fun isWrapperClassNameOrNumber(internalClassName: String) = - isWrapperClassName(internalClassName) || internalClassName == Type.getInternalName(Number::class.java) +private val KCLASS_TO_JLCLASS = Type.getMethodDescriptor(AsmTypes.JAVA_CLASS_TYPE, AsmTypes.K_CLASS_TYPE) +private val JLCLASS_TO_KCLASS = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE) - private fun isWrapperClassName(internalClassName: String) = - JvmPrimitiveType.isWrapperClassName(buildFqNameByInternal(internalClassName)) +fun AbstractInsnNode.isUnboxing() = + isPrimitiveUnboxing() || isJavaLangClassUnboxing() - private fun buildFqNameByInternal(internalClassName: String) = - FqName(Type.getObjectType(internalClassName).className) +fun AbstractInsnNode.isBoxing() = + isPrimitiveBoxing() || isJavaLangClassBoxing() - private fun AbstractInsnNode.isUnboxing() = - isPrimitiveUnboxing() || isJavaLangClassUnboxing() - - private inline fun AbstractInsnNode.isMethodInsnWith(opcode: Int, condition: MethodInsnNode.() -> Boolean): Boolean = - if (this.opcode == opcode && this is MethodInsnNode) - this.condition() - else - false - - private fun AbstractInsnNode.isPrimitiveUnboxing() = - isMethodInsnWith(Opcodes.INVOKEVIRTUAL) { - isWrapperClassNameOrNumber(owner) && isUnboxingMethodName(name) - } - - private fun AbstractInsnNode.isJavaLangClassUnboxing() = - isMethodInsnWith(Opcodes.INVOKESTATIC) { - owner == "kotlin/jvm/JvmClassMappingKt" && - name == "getJavaClass" && - desc == KCLASS_TO_JLCLASS - } - - private fun isUnboxingMethodName(name: String) = - UNBOXING_METHOD_NAMES.contains(name) - - private fun AbstractInsnNode.isBoxing() = - this.isPrimitiveBoxing() || this.isJavaLangClassBoxing() - - private fun AbstractInsnNode.isPrimitiveBoxing() = - isMethodInsnWith(Opcodes.INVOKESTATIC) { - isWrapperClassName(owner) && - name == "valueOf" && - isBoxingMethodDescriptor() - } - - private fun MethodInsnNode.isBoxingMethodDescriptor(): Boolean { - val ownerType = Type.getObjectType(owner) - return desc == Type.getMethodDescriptor(ownerType, AsmUtil.unboxType(ownerType)) +fun AbstractInsnNode.isPrimitiveUnboxing() = + isMethodInsnWith(Opcodes.INVOKEVIRTUAL) { + isWrapperClassNameOrNumber(owner) && isUnboxingMethodName(name) } - private fun AbstractInsnNode.isJavaLangClassBoxing() = - isMethodInsnWith(Opcodes.INVOKESTATIC) { - owner == AsmTypes.REFLECTION && - name == "getOrCreateKotlinClass" && - desc == JLCLASS_TO_KCLASS - } +private fun AbstractInsnNode.isJavaLangClassUnboxing() = + isMethodInsnWith(Opcodes.INVOKESTATIC) { + owner == "kotlin/jvm/JvmClassMappingKt" && + name == "getJavaClass" && + desc == KCLASS_TO_JLCLASS + } - private fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List) = - values[0] is ProgressionIteratorBasicValue && - isMethodInsnWith(INVOKEINTERFACE) { - name == "next" - } +inline fun AbstractInsnNode.isMethodInsnWith(opcode: Int, condition: MethodInsnNode.() -> Boolean): Boolean = + this.opcode == opcode && this is MethodInsnNode && this.condition() - private fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List) = - isMethodInsnWith(INVOKEINTERFACE) { - val firstArgType = values[0].type - firstArgType != null && - isProgressionClass(firstArgType.internalName) && - name == "iterator" - } +private fun isWrapperClassNameOrNumber(internalClassName: String) = + isWrapperClassName(internalClassName) || internalClassName == Type.getInternalName(Number::class.java) - private fun isProgressionClass(internalClassName: String) = - RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName)) +private fun isWrapperClassName(internalClassName: String) = + JvmPrimitiveType.isWrapperClassName(buildFqNameByInternal(internalClassName)) - private fun getValuesTypeOfProgressionClass(progressionClassInternalName: String) = - RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(buildFqNameByInternal(progressionClassInternalName))?.let { - type -> - type.typeName.asString() - } ?: error("type should be not null") - } + +private fun buildFqNameByInternal(internalClassName: String) = + FqName(Type.getObjectType(internalClassName).className) + +private fun isUnboxingMethodName(name: String) = + UNBOXING_METHOD_NAMES.contains(name) + +fun AbstractInsnNode.isPrimitiveBoxing() = + isMethodInsnWith(Opcodes.INVOKESTATIC) { + isWrapperClassName(owner) && + name == "valueOf" && + isBoxingMethodDescriptor() + } + +private fun MethodInsnNode.isBoxingMethodDescriptor(): Boolean { + val ownerType = Type.getObjectType(owner) + return desc == Type.getMethodDescriptor(ownerType, AsmUtil.unboxType(ownerType)) } + +private fun AbstractInsnNode.isJavaLangClassBoxing() = + isMethodInsnWith(Opcodes.INVOKESTATIC) { + owner == AsmTypes.REFLECTION && + name == "getOrCreateKotlinClass" && + desc == JLCLASS_TO_KCLASS + } + +private fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List) = + values[0] is ProgressionIteratorBasicValue && + isMethodInsnWith(Opcodes.INVOKEINTERFACE) { + name == "next" + } + +private fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List) = + isMethodInsnWith(Opcodes.INVOKEINTERFACE) { + val firstArgType = values[0].type + firstArgType != null && + isProgressionClass(firstArgType.internalName) && + name == "iterator" + } + +private fun isProgressionClass(internalClassName: String) = + RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName)) + +private fun getValuesTypeOfProgressionClass(progressionClassInternalName: String) = + RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(buildFqNameByInternal(progressionClassInternalName))?.let { + type -> + type.typeName.asString() + } ?: error("type should be not null") \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantCoercionToUnitTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantCoercionToUnitTransformer.kt new file mode 100644 index 00000000000..dd60bc6bbc2 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantCoercionToUnitTransformer.kt @@ -0,0 +1,228 @@ +/* + * 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. + */ + +package org.jetbrains.kotlin.codegen.optimization.boxing + +import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence +import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful +import org.jetbrains.kotlin.codegen.optimization.fixStack.peek +import org.jetbrains.kotlin.codegen.optimization.fixStack.top +import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext +import org.jetbrains.kotlin.codegen.optimization.replaceNodeGetNext +import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.tree.* +import org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer +import org.jetbrains.org.objectweb.asm.tree.analysis.Frame +import org.jetbrains.org.objectweb.asm.tree.analysis.SourceInterpreter +import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue +import org.jetbrains.org.objectweb.asm.util.Printer + +class RedundantCoercionToUnitTransformer : MethodTransformer() { + override fun transform(internalClassName: String, methodNode: MethodNode) { + Transformer(methodNode).transform() + } + + private class Transformer(val methodNode: MethodNode) { + private val insnList = methodNode.instructions + + private val frames: Array?> = Analyzer(SourceInterpreter()).analyze("fake", methodNode) + private val insns = insnList.toArray() + + private val dontTouchInsns = hashSetOf() + private val transformations = hashMapOf Unit>() + private val removableNops = hashSetOf() + + fun transform() { + computeDontTouchInsns() + computeTransformations() + transformations.values.forEach { it() } + postprocessNops() + } + + private fun computeDontTouchInsns() { + for (i in 0..insns.lastIndex) { + val frame = frames[i] ?: continue + val insn = insns[i] + + when (insn.opcode) { + Opcodes.DUP -> + dontTouchWordsOnTop(i, frame, 1) + Opcodes.DUP_X1 -> + dontTouchWordsOnTop(i, frame, 2) + Opcodes.DUP_X2 -> + dontTouchWordsOnTop(i, frame, 3) + Opcodes.DUP2 -> + dontTouchWordsOnTop(i, frame, 2) + Opcodes.DUP2_X1 -> + dontTouchWordsOnTop(i, frame, 3) + Opcodes.DUP2_X2 -> + dontTouchWordsOnTop(i, frame, 4) + Opcodes.SWAP -> + dontTouchWordsOnTop(i, frame, 2) + } + } + } + + private fun dontTouchWordsOnTop(at: Int, frame: Frame, expectedWords: Int) { + var words = 0 + var offset = 0 + while (words < expectedWords) { + val value = frame.peek(offset) ?: throwIllegalStackInsn(at) + offset++ + words += value.size + dontTouchInsns.addAll(value.insns) + } + if (words != expectedWords) throwIllegalStackInsn(at) + } + + private fun computeTransformations() { + transformations.clear() + + for (i in 0..insns.lastIndex) { + if (frames[i] == null) continue + val insn = insns[i] + + if (insn.opcode == Opcodes.POP) { + propagatePopBackwards(insn, 0) + } + } + } + + private fun propagatePopBackwards(insn: AbstractInsnNode, poppedValueSize: Int) { + if (transformations.containsKey(insn)) return + + when { + insn.opcode == Opcodes.POP -> { + val inputTop = getInputTop(insn) + val sources = inputTop.insns + if (sources.all { !isDontTouch(it) } && sources.any { isTransformablePopOperand(it) }) { + transformations[insn] = replaceWithNopTransformation(insn) + sources.forEach { propagatePopBackwards(it, inputTop.size) } + } + } + + insn.opcode == Opcodes.CHECKCAST -> { + val inputTop = getInputTop(insn) + val sources = inputTop.insns + val resultType = (insn as TypeInsnNode).desc + if (sources.all { !isDontTouch(it) } && sources.any { isTransformableCheckcastOperand(it, resultType) }) { + transformations[insn] = replaceWithNopTransformation(insn) + sources.forEach { propagatePopBackwards(it, inputTop.size) } + } + else { + transformations[insn] = insertPopAfterTransformation(insn, poppedValueSize) + } + } + + insn.isPrimitiveBoxing() -> { + val boxedValueSize = getInputTop(insn).size + transformations[insn] = replaceWithPopTransformation(insn, boxedValueSize) + } + + insn.isUnitOrNull() -> { + transformations[insn] = replaceWithNopTransformation(insn) + } + + else -> { + transformations[insn] = insertPopAfterTransformation(insn, poppedValueSize) + } + } + } + + private fun postprocessNops() { + var node: AbstractInsnNode? = insnList.first + var hasRemovableNops = false + while (node != null) { + node = node.next + val begin = node ?: break + while (node != null && node !is LabelNode) { + if (node in removableNops) { + hasRemovableNops = true + } + node = node.next + } + val end = node + if (hasRemovableNops) { + removeUnneededNopsInRange(begin, end) + } + hasRemovableNops = false + } + } + + private fun removeUnneededNopsInRange(begin: AbstractInsnNode, end: AbstractInsnNode?) { + var node: AbstractInsnNode? = begin + var keepNop = true + while (node != null && node != end) { + if (node in removableNops && !keepNop) { + node = insnList.removeNodeGetNext(node) + } + else { + if (node.isMeaningful) keepNop = false + node = node.next + } + } + } + + private fun replaceWithPopTransformation(insn: AbstractInsnNode, size: Int): () -> Unit = + { insnList.replaceNodeGetNext(insn, createPopInsn(size)) } + + private fun insertPopAfterTransformation(insn: AbstractInsnNode, size: Int) = + { insnList.insert(insn, createPopInsn(size)) } + + private fun replaceWithNopTransformation(insn: AbstractInsnNode): () -> Unit = + { insnList.replaceNodeGetNext(insn, createRemovableNopInsn()) } + + private fun createPopInsn(size: Int) = + when (size) { + 1 -> InsnNode(Opcodes.POP) + 2 -> InsnNode(Opcodes.POP2) + else -> throw AssertionError("Unexpected popped value size: $size") + } + + private fun createRemovableNopInsn() = + InsnNode(Opcodes.NOP).apply { removableNops.add(this) } + + private fun getInputTop(insn: AbstractInsnNode): SourceValue { + val i = insnList.indexOf(insn) + val frame = frames[i] ?: throw AssertionError("Unexpected dead instruction #$i") + return frame.top() ?: throw AssertionError("Instruction #$i has empty stack on input") + } + + private fun isTransformableCheckcastOperand(it: AbstractInsnNode, resultType: String) = + it.isPrimitiveBoxing() && (it as MethodInsnNode).owner == resultType + + private fun isTransformablePopOperand(insn: AbstractInsnNode) = + insn.opcode == Opcodes.CHECKCAST || insn.isPrimitiveBoxing() || insn.isUnitOrNull() + + private fun isDontTouch(insn: AbstractInsnNode) = + insn in dontTouchInsns + + private fun throwIllegalStackInsn(i: Int): Nothing = + throw AssertionError("#$i: illegal use of ${Printer.OPCODES[insns[i].opcode]}, input stack: ${formatInputStack(frames[i])}") + + private fun formatInputStack(frame: Frame?): String = + if (frame == null) + "unknown (dead code)" + else + (0..frame.stackSize - 1).map { frame.getStack(it).size }.joinToString(prefix = "[", postfix = "]") + } + +} + +fun AbstractInsnNode.isUnitOrNull() = + opcode == Opcodes.ACONST_NULL || + opcode == Opcodes.GETSTATIC && this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE" diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/StackTransformationUtils.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/StackTransformationUtils.kt index 527819463b2..efb94c7d95f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/StackTransformationUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/StackTransformationUtils.kt @@ -41,13 +41,11 @@ inline fun InsnList.forEachInlineMarker(block: (String, MethodInsnNode) -> Unit) } } -fun Frame.top(): V? { - val stackSize = stackSize - if (stackSize == 0) - return null - else - return getStack(stackSize - 1) -} +fun Frame.top(): V? = + peek(0) + +fun Frame.peek(offset: Int): V? = + if (stackSize >= offset) getStack(stackSize - offset - 1) else null fun MethodNode.updateMaxLocals(newMaxLocals: Int) { maxLocals = Math.max(maxLocals, newMaxLocals) diff --git a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/nopInlineFuns.kt b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/nopInlineFuns.kt new file mode 100644 index 00000000000..17d8063be40 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/nopInlineFuns.kt @@ -0,0 +1,26 @@ +fun test() { + val a = inlineFunInt { 1 } + val b = simpleFunInt { 1 } + val c = inlineFunVoid { val aa = 1 } + val d = simpleFunVoid { val aa = 1 } +} + +inline fun inlineFunInt(f: () -> Int): Int { + val a = 1 + return f() +} + +inline fun inlineFunVoid(f: () -> Unit): Unit { + val a = 1 + return f() +} + +fun simpleFunInt(f: () -> Int): Int { + return f() +} + +fun simpleFunVoid(f: () -> Unit): Unit { + return f() +} + +// 3 NOP diff --git a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/returnsUnit.kt b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/returnsUnit.kt new file mode 100644 index 00000000000..e87aab1b0cd --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/returnsUnit.kt @@ -0,0 +1,4 @@ +fun test() = Unit + +// 0 GETSTATIC +// 0 POP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeCall.kt b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeCall.kt new file mode 100644 index 00000000000..d6a51c3321f --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeCall.kt @@ -0,0 +1,10 @@ +interface A { + fun foo() +} + +fun test(x: A?) { + x?.foo() +} + +// 1 POP +// 0 ACONST_NULL diff --git a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeCallWithReturnValue.kt b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeCallWithReturnValue.kt new file mode 100644 index 00000000000..aede41c7b0f --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeCallWithReturnValue.kt @@ -0,0 +1,10 @@ +interface A { + fun foo(): Any +} + +fun test(x: A?) { + x?.foo() +} + +// 2 POP +// 0 ACONST_NULL \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeLet.kt b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeLet.kt new file mode 100644 index 00000000000..bf0c1cbb946 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeLet.kt @@ -0,0 +1,19 @@ +fun test(ss: List) { + val shortStrings = hashSetOf() + val longStrings = hashSetOf() + for (s in ss) { + s?.let { + if (s.length < 4) { + shortStrings.add(s) + } + else { + longStrings.add(s) + } + } + } +} + +// 2 POP +// 0 INVOKESTATIC java/lang/Boolean\.valueOf +// 0 CHECKCAST java/lang/Boolean +// 0 ACONST_NULL diff --git a/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/tryInlined.kt b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/tryInlined.kt new file mode 100644 index 00000000000..ce32924eb6e --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/tryInlined.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME + +import java.io.* + +fun test(r: Reader) { + val ss = hashSetOf() + r.useLines { it.forEach { ss.add(it) } } +} + +// 2 POP diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 73e38b9ed6c..452c7cf441e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -575,6 +575,51 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } } + @TestMetadata("compiler/testData/codegen/bytecodeText/coercionToUnitOptimization") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CoercionToUnitOptimization extends AbstractBytecodeTextTest { + public void testAllFilesPresentInCoercionToUnitOptimization() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/coercionToUnitOptimization"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("nopInlineFuns.kt") + public void testNopInlineFuns() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/nopInlineFuns.kt"); + doTest(fileName); + } + + @TestMetadata("returnsUnit.kt") + public void testReturnsUnit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/returnsUnit.kt"); + doTest(fileName); + } + + @TestMetadata("safeCall.kt") + public void testSafeCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeCall.kt"); + doTest(fileName); + } + + @TestMetadata("safeCallWithReturnValue.kt") + public void testSafeCallWithReturnValue() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeCallWithReturnValue.kt"); + doTest(fileName); + } + + @TestMetadata("safeLet.kt") + public void testSafeLet() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/safeLet.kt"); + doTest(fileName); + } + + @TestMetadata("tryInlined.kt") + public void testTryInlined() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coercionToUnitOptimization/tryInlined.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/bytecodeText/conditions") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)