Optimize coercion to Unit.
POP operations are backward-propagated. Operation can't be transformed if its result is moved within stack (by DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, or SWAP). Unnecessary operations are replaced with NOPs (for debugger). Unnecessary NOPs are removed. KT-9922 Suboptimal generation for simple safe call with unused return value KT-11116 Optimize methods returning Unit
This commit is contained in:
+2
-2
@@ -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
|
||||
|
||||
+3
-1
@@ -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;
|
||||
|
||||
+71
-74
@@ -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<BasicValue>) =
|
||||
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<BasicValue>) =
|
||||
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<BasicValue>) =
|
||||
values[0] is ProgressionIteratorBasicValue &&
|
||||
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
||||
name == "next"
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List<BasicValue>) =
|
||||
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")
|
||||
+228
@@ -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<Frame<SourceValue>?> = Analyzer<SourceValue>(SourceInterpreter()).analyze("fake", methodNode)
|
||||
private val insns = insnList.toArray()
|
||||
|
||||
private val dontTouchInsns = hashSetOf<AbstractInsnNode>()
|
||||
private val transformations = hashMapOf<AbstractInsnNode, () -> Unit>()
|
||||
private val removableNops = hashSetOf<InsnNode>()
|
||||
|
||||
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<SourceValue>, 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<SourceValue>?): 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"
|
||||
+5
-7
@@ -41,13 +41,11 @@ inline fun InsnList.forEachInlineMarker(block: (String, MethodInsnNode) -> Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun <V : Value> Frame<V>.top(): V? {
|
||||
val stackSize = stackSize
|
||||
if (stackSize == 0)
|
||||
return null
|
||||
else
|
||||
return getStack(stackSize - 1)
|
||||
}
|
||||
fun <V : Value> Frame<V>.top(): V? =
|
||||
peek(0)
|
||||
|
||||
fun <V : Value> Frame<V>.peek(offset: Int): V? =
|
||||
if (stackSize >= offset) getStack(stackSize - offset - 1) else null
|
||||
|
||||
fun MethodNode.updateMaxLocals(newMaxLocals: Int) {
|
||||
maxLocals = Math.max(maxLocals, newMaxLocals)
|
||||
|
||||
+26
@@ -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
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
fun test() = Unit
|
||||
|
||||
// 0 GETSTATIC
|
||||
// 0 POP
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
interface A {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
fun test(x: A?) {
|
||||
x?.foo()
|
||||
}
|
||||
|
||||
// 1 POP
|
||||
// 0 ACONST_NULL
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
interface A {
|
||||
fun foo(): Any
|
||||
}
|
||||
|
||||
fun test(x: A?) {
|
||||
x?.foo()
|
||||
}
|
||||
|
||||
// 2 POP
|
||||
// 0 ACONST_NULL
|
||||
@@ -0,0 +1,19 @@
|
||||
fun test(ss: List<String?>) {
|
||||
val shortStrings = hashSetOf<String>()
|
||||
val longStrings = hashSetOf<String>()
|
||||
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
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
import java.io.*
|
||||
|
||||
fun test(r: Reader) {
|
||||
val ss = hashSetOf<String>()
|
||||
r.useLines { it.forEach { ss.add(it) } }
|
||||
}
|
||||
|
||||
// 2 POP
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user