Optimize constant conditions
Using basic constant propagation (only integer constants, no arithmetic calculations), rewrite conditional jump instructions with constant arguments. This covers problem description in KT-17007. Note that it also works transparently with inline functions. Partial evaluation is required to cover more "advanced" cases. As a side effect, this also covers KT-3098: rewrite IF_ICMP<cmp_op>(x, 0) to IF<cmp0_op>(x).
This commit is contained in:
+187
@@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.common.insnText
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||||
|
|
||||||
|
class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
|
||||||
|
private val deadCodeElimination = DeadCodeEliminationMethodTransformer()
|
||||||
|
|
||||||
|
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||||
|
do {
|
||||||
|
val changes = ConstantConditionsOptimization(internalClassName, methodNode).run()
|
||||||
|
if (changes) deadCodeElimination.transform(internalClassName, methodNode)
|
||||||
|
} while (changes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConstantConditionsOptimization(val internalClassName: String, val methodNode: MethodNode) {
|
||||||
|
fun run(): Boolean {
|
||||||
|
val actions = collectRewriteActions()
|
||||||
|
actions.forEach { it() }
|
||||||
|
return actions.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectRewriteActions(): List<() -> Unit> =
|
||||||
|
arrayListOf<() -> Unit>().also { actions ->
|
||||||
|
val frames = analyze(internalClassName, methodNode, ConstantPropagationInterpreter())
|
||||||
|
val insns = methodNode.instructions.toArray()
|
||||||
|
for (i in frames.indices) {
|
||||||
|
val frame = frames[i] ?: continue
|
||||||
|
val insn = insns[i] as? JumpInsnNode ?: continue
|
||||||
|
when (insn.opcode) {
|
||||||
|
in Opcodes.IFEQ .. Opcodes.IFLE ->
|
||||||
|
tryRewriteComparisonWithZero(insn, frame, actions)
|
||||||
|
in Opcodes.IF_ICMPEQ .. Opcodes.IF_ICMPLE ->
|
||||||
|
tryRewriteBinaryComparison(insn, frame, actions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryRewriteComparisonWithZero(insn: JumpInsnNode, frame: Frame<BasicValue>, actions: ArrayList<() -> Unit>) {
|
||||||
|
val top = frame.top()!!.safeAs<IConstValue>() ?: return
|
||||||
|
|
||||||
|
val constCondition = when (insn.opcode) {
|
||||||
|
Opcodes.IFEQ -> top.value == 0
|
||||||
|
Opcodes.IFNE -> top.value != 0
|
||||||
|
Opcodes.IFGE -> top.value >= 0
|
||||||
|
Opcodes.IFGT -> top.value > 0
|
||||||
|
Opcodes.IFLE -> top.value <= 0
|
||||||
|
Opcodes.IFLT -> top.value < 0
|
||||||
|
else -> throw AssertionError("Unexpected instruction: ${insn.insnText}")
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.add {
|
||||||
|
methodNode.instructions.run {
|
||||||
|
insertBefore(insn, InsnNode(Opcodes.POP))
|
||||||
|
if (constCondition)
|
||||||
|
set(insn, JumpInsnNode(Opcodes.GOTO, insn.label))
|
||||||
|
else
|
||||||
|
remove(insn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryRewriteBinaryComparison(insn: JumpInsnNode, frame: Frame<BasicValue>, actions: ArrayList<() -> Unit>) {
|
||||||
|
val arg1 = frame.peek(1)!!
|
||||||
|
val arg2 = frame.peek(0)!!
|
||||||
|
|
||||||
|
if (arg1 is IConstValue && arg2 is IConstValue) {
|
||||||
|
rewriteBinaryComparisonOfConsts(insn, arg1.value, arg2.value, actions)
|
||||||
|
}
|
||||||
|
else if (arg2 is IConstValue && arg2.value == 0) {
|
||||||
|
rewriteBinaryComparisonWith0(insn, actions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rewriteBinaryComparisonOfConsts(insn: JumpInsnNode, value1: Int, value2: Int, actions: ArrayList<() -> Unit>) {
|
||||||
|
val constCondition = when (insn.opcode) {
|
||||||
|
Opcodes.IF_ICMPEQ -> value1 == value2
|
||||||
|
Opcodes.IF_ICMPNE -> value1 != value2
|
||||||
|
Opcodes.IF_ICMPLE -> value1 <= value2
|
||||||
|
Opcodes.IF_ICMPLT -> value1 < value2
|
||||||
|
Opcodes.IF_ICMPGE -> value1 >= value2
|
||||||
|
Opcodes.IF_ICMPGT -> value1 > value2
|
||||||
|
else -> throw AssertionError("Unexpected instruction: ${insn.insnText}")
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.add {
|
||||||
|
methodNode.instructions.run {
|
||||||
|
insertBefore(insn, InsnNode(Opcodes.POP))
|
||||||
|
insertBefore(insn, InsnNode(Opcodes.POP))
|
||||||
|
if (constCondition)
|
||||||
|
set(insn, JumpInsnNode(Opcodes.GOTO, insn.label))
|
||||||
|
else
|
||||||
|
remove(insn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rewriteBinaryComparisonWith0(insn: JumpInsnNode, actions: ArrayList<() -> Unit>) {
|
||||||
|
actions.add {
|
||||||
|
methodNode.instructions.run {
|
||||||
|
insertBefore(insn, InsnNode(Opcodes.POP))
|
||||||
|
val cmpWith0Opcode = when (insn.opcode) {
|
||||||
|
Opcodes.IF_ICMPEQ -> Opcodes.IFEQ
|
||||||
|
Opcodes.IF_ICMPNE -> Opcodes.IFNE
|
||||||
|
Opcodes.IF_ICMPLE -> Opcodes.IFLE
|
||||||
|
Opcodes.IF_ICMPLT -> Opcodes.IFLT
|
||||||
|
Opcodes.IF_ICMPGE -> Opcodes.IFGE
|
||||||
|
Opcodes.IF_ICMPGT -> Opcodes.IFGT
|
||||||
|
else -> throw AssertionError("Unexpected instruction: ${insn.insnText}")
|
||||||
|
}
|
||||||
|
set(insn, JumpInsnNode(cmpWith0Opcode, insn.label))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class IConstValue private constructor(val value: Int) : StrictBasicValue(Type.INT_TYPE) {
|
||||||
|
override fun equals(other: Any?): Boolean =
|
||||||
|
other === this ||
|
||||||
|
other is IConstValue && other.value == this.value
|
||||||
|
|
||||||
|
override fun hashCode(): Int = value
|
||||||
|
|
||||||
|
override fun toString(): String = "IConst($value)"
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val ICONST_CACHE = Array(7) { IConstValue(it - 1) }
|
||||||
|
|
||||||
|
fun of(value: Int) =
|
||||||
|
if (value in -1 .. 5)
|
||||||
|
ICONST_CACHE[value + 1]
|
||||||
|
else
|
||||||
|
IConstValue(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConstantPropagationInterpreter : OptimizationBasicInterpreter() {
|
||||||
|
override fun newOperation(insn: AbstractInsnNode): BasicValue =
|
||||||
|
when (insn.opcode) {
|
||||||
|
in Opcodes.ICONST_M1 .. Opcodes.ICONST_5 ->
|
||||||
|
IConstValue.of(insn.opcode - Opcodes.ICONST_0)
|
||||||
|
Opcodes.BIPUSH, Opcodes.SIPUSH ->
|
||||||
|
IConstValue.of(insn.cast<IntInsnNode>().operand)
|
||||||
|
Opcodes.LDC -> {
|
||||||
|
val operand = insn.cast<LdcInsnNode>().cst
|
||||||
|
if (operand is Int)
|
||||||
|
IConstValue.of(operand)
|
||||||
|
else
|
||||||
|
super.newOperation(insn)
|
||||||
|
}
|
||||||
|
else -> super.newOperation(insn)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
||||||
|
if (v is IConstValue && w is IConstValue && v == w)
|
||||||
|
v
|
||||||
|
else
|
||||||
|
super.merge(v, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -52,6 +52,7 @@ class OptimizationMethodVisitor(
|
|||||||
CapturedVarsOptimizationMethodTransformer(),
|
CapturedVarsOptimizationMethodTransformer(),
|
||||||
RedundantNullCheckMethodTransformer(),
|
RedundantNullCheckMethodTransformer(),
|
||||||
RedundantCheckCastEliminationMethodTransformer(),
|
RedundantCheckCastEliminationMethodTransformer(),
|
||||||
|
ConstantConditionEliminationMethodTransformer(),
|
||||||
RedundantBoxingMethodTransformer(),
|
RedundantBoxingMethodTransformer(),
|
||||||
RedundantCoercionToUnitTransformer(),
|
RedundantCoercionToUnitTransformer(),
|
||||||
DeadCodeEliminationMethodTransformer(),
|
DeadCodeEliminationMethodTransformer(),
|
||||||
|
|||||||
+24
-6
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.optimization.boxing
|
package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.common.isLoadOperation
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||||
import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
|
import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
|
||||||
@@ -63,7 +64,7 @@ class RedundantCoercionToUnitTransformer : MethodTransformer() {
|
|||||||
private val frames by lazy { analyzeMethodBody() }
|
private val frames by lazy { analyzeMethodBody() }
|
||||||
|
|
||||||
fun transform() {
|
fun transform() {
|
||||||
if (!insns.any { it.isUnitInstanceOrNull() }) return
|
if (!insns.any { it.isPurePush() }) return
|
||||||
|
|
||||||
computeTransformations()
|
computeTransformations()
|
||||||
for ((insn, transformation) in transformations.entries) {
|
for ((insn, transformation) in transformations.entries) {
|
||||||
@@ -87,7 +88,7 @@ class RedundantCoercionToUnitTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun unaryOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue {
|
override fun unaryOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue {
|
||||||
if (insn.opcode != Opcodes.CHECKCAST) {
|
if (insn.opcode != Opcodes.CHECKCAST && !insn.isPrimitiveTypeConversion()) {
|
||||||
value.insns.markAsDontTouch()
|
value.insns.markAsDontTouch()
|
||||||
}
|
}
|
||||||
return super.unaryOperation(insn, value)
|
return super.unaryOperation(insn, value)
|
||||||
@@ -158,10 +159,22 @@ class RedundantCoercionToUnitTransformer : MethodTransformer() {
|
|||||||
transformations[insn] = replaceWithPopTransformation(boxedValueSize)
|
transformations[insn] = replaceWithPopTransformation(boxedValueSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
insn.isUnitInstanceOrNull() -> {
|
insn.isPurePush() -> {
|
||||||
transformations[insn] = replaceWithNopTransformation()
|
transformations[insn] = replaceWithNopTransformation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insn.isPrimitiveTypeConversion() -> {
|
||||||
|
val inputTop = getInputTop(insn)
|
||||||
|
val sources = inputTop.insns
|
||||||
|
if (sources.all { !isDontTouch(it) }) {
|
||||||
|
transformations[insn] = replaceWithNopTransformation()
|
||||||
|
sources.forEach { propagatePopBackwards(it, inputTop.size) }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
transformations[insn] = replaceWithPopTransformation(poppedValueSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
transformations[insn] = insertPopAfterTransformation(poppedValueSize)
|
transformations[insn] = insertPopAfterTransformation(poppedValueSize)
|
||||||
}
|
}
|
||||||
@@ -232,7 +245,7 @@ class RedundantCoercionToUnitTransformer : MethodTransformer() {
|
|||||||
it.isPrimitiveBoxing() && (it as MethodInsnNode).owner == resultType
|
it.isPrimitiveBoxing() && (it as MethodInsnNode).owner == resultType
|
||||||
|
|
||||||
private fun isTransformablePopOperand(insn: AbstractInsnNode) =
|
private fun isTransformablePopOperand(insn: AbstractInsnNode) =
|
||||||
insn.opcode == Opcodes.CHECKCAST || insn.isPrimitiveBoxing() || insn.isUnitInstanceOrNull()
|
insn.opcode == Opcodes.CHECKCAST || insn.isPrimitiveBoxing() || insn.isPurePush()
|
||||||
|
|
||||||
private fun isDontTouch(insn: AbstractInsnNode) =
|
private fun isDontTouch(insn: AbstractInsnNode) =
|
||||||
dontTouchInsnIndices[insnList.indexOf(insn)]
|
dontTouchInsnIndices[insnList.indexOf(insn)]
|
||||||
@@ -240,9 +253,14 @@ class RedundantCoercionToUnitTransformer : MethodTransformer() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun AbstractInsnNode.isUnitInstanceOrNull() =
|
fun AbstractInsnNode.isPurePush() =
|
||||||
opcode == Opcodes.ACONST_NULL || isUnitInstance()
|
isLoadOperation() ||
|
||||||
|
opcode in Opcodes.ACONST_NULL .. Opcodes.LDC + 2 ||
|
||||||
|
isUnitInstance()
|
||||||
|
|
||||||
fun AbstractInsnNode.isUnitInstance() =
|
fun AbstractInsnNode.isUnitInstance() =
|
||||||
opcode == Opcodes.GETSTATIC &&
|
opcode == Opcodes.GETSTATIC &&
|
||||||
this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
|
this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
|
||||||
|
|
||||||
|
fun AbstractInsnNode.isPrimitiveTypeConversion() =
|
||||||
|
opcode in Opcodes.I2L .. Opcodes.I2S
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// FILE: util.kt
|
||||||
|
|
||||||
|
inline fun ieq(x: Int, y: Int) = x == y
|
||||||
|
inline fun ine(x: Int, y: Int) = x != y
|
||||||
|
inline fun ilt(x: Int, y: Int) = x < y
|
||||||
|
inline fun ile(x: Int, y: Int) = x <= y
|
||||||
|
inline fun igt(x: Int, y: Int) = x > y
|
||||||
|
inline fun ige(x: Int, y: Int) = x >= y
|
||||||
|
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun testeq(x: Int) = ieq(x, 0)
|
||||||
|
fun testne(x: Int) = ine(x, 0)
|
||||||
|
fun testlt(x: Int) = ilt(x, 0)
|
||||||
|
fun testle(x: Int) = ile(x, 0)
|
||||||
|
fun testgt(x: Int) = igt(x, 0)
|
||||||
|
fun testge(x: Int) = ige(x, 0)
|
||||||
|
|
||||||
|
// @TestKt.class:
|
||||||
|
// 0 IF_ICMPEQ
|
||||||
|
// 0 IF_ICMPNE
|
||||||
|
// 0 IF_ICMPGE
|
||||||
|
// 0 IF_ICMPGT
|
||||||
|
// 0 IF_ICMPLE
|
||||||
|
// 0 IF_ICMPLT
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// FILE: util.kt
|
||||||
|
const val FLAG = true
|
||||||
|
const val OTHER_FLAG = false
|
||||||
|
|
||||||
|
fun doStuff1() {}
|
||||||
|
fun doStuff2() {}
|
||||||
|
fun doStuff3() {}
|
||||||
|
|
||||||
|
inline fun doStuff1IfTrue(flag: Boolean) {
|
||||||
|
if (flag) doStuff1()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun doStuff2IfFalse(flag: Boolean) {
|
||||||
|
if (!flag) doStuff2()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun doStuff3IfComplex(flag: Boolean) {
|
||||||
|
if (flag && !OTHER_FLAG) doStuff3()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: test.kt
|
||||||
|
fun test() {
|
||||||
|
doStuff1IfTrue(FLAG)
|
||||||
|
doStuff2IfFalse(FLAG)
|
||||||
|
doStuff3IfComplex(FLAG)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TestKt.class:
|
||||||
|
// 0 FLAG
|
||||||
|
// 1 INVOKESTATIC UtilKt.doStuff1
|
||||||
|
// 0 INVOKESTATIC UtilKt.doStuff2
|
||||||
|
// 1 INVOKESTATIC UtilKt.doStuff3
|
||||||
|
// 0 ILOAD 0
|
||||||
|
// 0 IFEQ
|
||||||
|
// 0 IFNE
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// FILE: util.kt
|
||||||
|
const val MAGIC = 42
|
||||||
|
|
||||||
|
fun doStuffEq() {}
|
||||||
|
fun doStuffNotEq() {}
|
||||||
|
fun doStuffLt() {}
|
||||||
|
fun doStuffLe() {}
|
||||||
|
fun doStuffGt() {}
|
||||||
|
fun doStuffGe() {}
|
||||||
|
|
||||||
|
inline fun doStuffIfEq(i: Int) {
|
||||||
|
if (i == MAGIC) doStuffEq()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun doStuffIfNotEq(i: Int) {
|
||||||
|
if (i != MAGIC) doStuffNotEq()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun doStuffIfLt(i: Int) {
|
||||||
|
if (i < MAGIC) doStuffLt()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun doStuffIfLe(i: Int) {
|
||||||
|
if (i <= MAGIC) doStuffLe()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun doStuffIfGt(i: Int) {
|
||||||
|
if (i > MAGIC) doStuffGt()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun doStuffIfGe(i: Int) {
|
||||||
|
if (i >= MAGIC) doStuffGe()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: test.kt
|
||||||
|
fun test() {
|
||||||
|
doStuffIfEq(100)
|
||||||
|
doStuffIfNotEq(100)
|
||||||
|
doStuffIfLt(100)
|
||||||
|
doStuffIfLe(100)
|
||||||
|
doStuffIfGt(100)
|
||||||
|
doStuffIfGe(100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TestKt.class:
|
||||||
|
// 0 INVOKESTATIC UtilKt.doStuffEq
|
||||||
|
// 1 INVOKESTATIC UtilKt.doStuffNotEq
|
||||||
|
// 0 INVOKESTATIC UtilKt.doStuffLt
|
||||||
|
// 0 INVOKESTATIC UtilKt.doStuffLe
|
||||||
|
// 1 INVOKESTATIC UtilKt.doStuffGt
|
||||||
|
// 1 INVOKESTATIC UtilKt.doStuffGe
|
||||||
|
// 0 ILOAD 0
|
||||||
|
// 0 IFEQ
|
||||||
|
// 0 IFNE
|
||||||
|
// 0 IFLT
|
||||||
|
// 0 IFLE
|
||||||
|
// 0 IFGE
|
||||||
|
// 0 IFGT
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fun testLt(x: String, y: String) = x < y
|
||||||
|
fun testLe(x: String, y: String) = x <= y
|
||||||
|
fun testGt(x: String, y: String) = x > y
|
||||||
|
fun testGe(x: String, y: String) = x >= y
|
||||||
|
|
||||||
|
// 0 IF_ICMPGE
|
||||||
|
// 0 IF_ICMPGT
|
||||||
|
// 0 IF_ICMPLE
|
||||||
|
// 0 IF_ICMPLT
|
||||||
@@ -885,6 +885,39 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/bytecodeText/constantConditions")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ConstantConditions extends AbstractBytecodeTextTest {
|
||||||
|
public void testAllFilesPresentInConstantConditions() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/constantConditions"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("cmpIntWith0.kt")
|
||||||
|
public void testCmpIntWith0() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constantConditions/cmpIntWith0.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("constantFlag.kt")
|
||||||
|
public void testConstantFlag() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constantConditions/constantFlag.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("constantInt.kt")
|
||||||
|
public void testConstantInt() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constantConditions/constantInt.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt3098.kt")
|
||||||
|
public void testKt3098() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/constantConditions/kt3098.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/bytecodeText/constants")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/constants")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user