JVM_IR: Generate more line numbers for intrinsic comparisons.

Otherwise, if complex expressions such as when expressions are
used in combination with the intrinsics we get incorrect stepping
behavior.

^KT-64341 Fixed
This commit is contained in:
Mads Ager
2023-12-13 12:43:05 +01:00
committed by Space Cloud
parent a441a82357
commit e41a1247e2
23 changed files with 723 additions and 54 deletions
@@ -601,6 +601,48 @@ public class FirLightTreeSteppingTestGenerated extends AbstractFirLightTreeStepp
runTest("compiler/testData/debug/stepping/whenExpr.kt"); runTest("compiler/testData/debug/stepping/whenExpr.kt");
} }
@Test
@TestMetadata("whenInConjunction.kt")
public void testWhenInConjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInConjunction.kt");
}
@Test
@TestMetadata("whenInDisjunction.kt")
public void testWhenInDisjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInDisjunction.kt");
}
@Test
@TestMetadata("whenInGeneralObjectComparison.kt")
public void testWhenInGeneralObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInGeneralObjectComparison.kt");
}
@Test
@TestMetadata("whenInNegation.kt")
public void testWhenInNegation() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNegation.kt");
}
@Test
@TestMetadata("whenInNullComparison.kt")
public void testWhenInNullComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNullComparison.kt");
}
@Test
@TestMetadata("whenInPrimitiveToObjectComparison.kt")
public void testWhenInPrimitiveToObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInPrimitiveToObjectComparison.kt");
}
@Test
@TestMetadata("whenInZeroComparison.kt")
public void testWhenInZeroComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInZeroComparison.kt");
}
@Test @Test
@TestMetadata("whenIsChecks.kt") @TestMetadata("whenIsChecks.kt")
public void testWhenIsChecks() throws Exception { public void testWhenIsChecks() throws Exception {
@@ -601,6 +601,48 @@ public class FirPsiSteppingTestGenerated extends AbstractFirPsiSteppingTest {
runTest("compiler/testData/debug/stepping/whenExpr.kt"); runTest("compiler/testData/debug/stepping/whenExpr.kt");
} }
@Test
@TestMetadata("whenInConjunction.kt")
public void testWhenInConjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInConjunction.kt");
}
@Test
@TestMetadata("whenInDisjunction.kt")
public void testWhenInDisjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInDisjunction.kt");
}
@Test
@TestMetadata("whenInGeneralObjectComparison.kt")
public void testWhenInGeneralObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInGeneralObjectComparison.kt");
}
@Test
@TestMetadata("whenInNegation.kt")
public void testWhenInNegation() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNegation.kt");
}
@Test
@TestMetadata("whenInNullComparison.kt")
public void testWhenInNullComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNullComparison.kt");
}
@Test
@TestMetadata("whenInPrimitiveToObjectComparison.kt")
public void testWhenInPrimitiveToObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInPrimitiveToObjectComparison.kt");
}
@Test
@TestMetadata("whenInZeroComparison.kt")
public void testWhenInZeroComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInZeroComparison.kt");
}
@Test @Test
@TestMetadata("whenIsChecks.kt") @TestMetadata("whenIsChecks.kt")
public void testWhenIsChecks() throws Exception { public void testWhenIsChecks() throws Exception {
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.declarations.isSingleFieldValueClass import org.jetbrains.kotlin.ir.declarations.isSingleFieldValueClass
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.isTypeParameter import org.jetbrains.kotlin.ir.util.isTypeParameter
@@ -110,6 +111,12 @@ abstract class BooleanValue(codegen: ExpressionCodegen) :
StackValue.coerce(Type.BOOLEAN_TYPE, target, mv) StackValue.coerce(Type.BOOLEAN_TYPE, target, mv)
} }
} }
fun markLineNumber(expression: IrExpression) {
with(codegen) {
expression.markLineNumber(startOffset = true)
}
}
} }
class BooleanConstant(codegen: ExpressionCodegen, val value: Boolean) : BooleanValue(codegen) { class BooleanConstant(codegen: ExpressionCodegen, val value: Boolean) : BooleanValue(codegen) {
@@ -12,25 +12,41 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
object AndAnd : IntrinsicMethod() { object AndAnd : IntrinsicMethod() {
private class BooleanConjunction(val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) : private class BooleanConjunction(
BooleanValue(codegen) { val left: IrExpression,
val right: IrExpression,
codegen: ExpressionCodegen,
val data: BlockInfo
) : BooleanValue(codegen) {
override fun jumpIfFalse(target: Label) { override fun jumpIfFalse(target: Label) {
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(target) val leftValue = left.accept(codegen, data).coerceToBoolean()
arg1.accept(codegen, data).coerceToBoolean().jumpIfFalse(target) markLineNumber(left)
leftValue.jumpIfFalse(target)
val rightValue = right.accept(codegen, data).coerceToBoolean()
markLineNumber(right)
rightValue.jumpIfFalse(target)
} }
override fun jumpIfTrue(target: Label) { override fun jumpIfTrue(target: Label) {
val stayLabel = Label() val stayLabel = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(stayLabel) val leftValue = left.accept(codegen, data).coerceToBoolean()
arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target) markLineNumber(left)
leftValue.jumpIfFalse(stayLabel)
val rightValue = right.accept(codegen, data).coerceToBoolean()
markLineNumber(right)
rightValue.jumpIfTrue(target)
mv.visitLabel(stayLabel) mv.visitLabel(stayLabel)
} }
override fun discard() { override fun discard() {
val end = Label() val end = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(end) val leftValue = left.accept(codegen, data).coerceToBoolean()
arg1.accept(codegen, data).discard() markLineNumber(left)
leftValue.jumpIfFalse(end)
val rightValue = right.accept(codegen, data)
markLineNumber(right)
rightValue.discard()
mv.visitLabel(end) mv.visitLabel(end)
} }
} }
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.codegen.BranchedValue
import org.jetbrains.kotlin.codegen.NumberCompare import org.jetbrains.kotlin.codegen.NumberCompare
import org.jetbrains.kotlin.codegen.ObjectCompare import org.jetbrains.kotlin.codegen.ObjectCompare
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
@@ -67,27 +68,37 @@ object CompareTo : IntrinsicMethod() {
} }
} }
class IntegerZeroComparison(val a: MaterialValue) : BooleanValue(a.codegen) { class IntegerZeroComparison(val nonZeroExpression: IrExpression, val a: MaterialValue) : BooleanValue(a.codegen) {
override fun jumpIfFalse(target: Label) { override fun jumpIfFalse(target: Label) {
markLineNumber(nonZeroExpression)
mv.visitJumpInsn(Opcodes.IFNE, target) mv.visitJumpInsn(Opcodes.IFNE, target)
} }
override fun jumpIfTrue(target: Label) { override fun jumpIfTrue(target: Label) {
markLineNumber(nonZeroExpression)
mv.visitJumpInsn(Opcodes.IFEQ, target) mv.visitJumpInsn(Opcodes.IFEQ, target)
} }
override fun discard() { override fun discard() {
markLineNumber(nonZeroExpression)
a.discard() a.discard()
} }
} }
class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: MaterialValue) : BooleanValue(a.codegen) { class BooleanComparison(
val expression: IrFunctionAccessExpression,
val op: IElementType,
val a: MaterialValue,
val b: MaterialValue
) : BooleanValue(a.codegen) {
override fun jumpIfFalse(target: Label) { override fun jumpIfFalse(target: Label) {
// TODO 1. get rid of the dependency; 2. take `b.type` into account. // TODO 1. get rid of the dependency; 2. take `b.type` into account.
val opcode = if (a.type.sort == Type.OBJECT) val opcode = if (a.type.sort == Type.OBJECT)
ObjectCompare.getObjectCompareOpcode(op) ObjectCompare.getObjectCompareOpcode(op)
else else
NumberCompare.patchOpcode(NumberCompare.getNumberCompareOpcode(op), mv, op, a.type) NumberCompare.patchOpcode(NumberCompare.getNumberCompareOpcode(op), mv, op, a.type)
markLineNumber(expression)
mv.visitJumpInsn(opcode, target) mv.visitJumpInsn(opcode, target)
} }
@@ -96,17 +107,23 @@ class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: Mater
BranchedValue.negatedOperations[ObjectCompare.getObjectCompareOpcode(op)]!! BranchedValue.negatedOperations[ObjectCompare.getObjectCompareOpcode(op)]!!
else else
NumberCompare.patchOpcode(BranchedValue.negatedOperations[NumberCompare.getNumberCompareOpcode(op)]!!, mv, op, a.type) NumberCompare.patchOpcode(BranchedValue.negatedOperations[NumberCompare.getNumberCompareOpcode(op)]!!, mv, op, a.type)
markLineNumber(expression)
mv.visitJumpInsn(opcode, target) mv.visitJumpInsn(opcode, target)
} }
override fun discard() { override fun discard() {
markLineNumber(expression)
b.discard() b.discard()
a.discard() a.discard()
} }
} }
class NonIEEE754FloatComparison(
class NonIEEE754FloatComparison(op: IElementType, private val a: MaterialValue, private val b: MaterialValue) : BooleanValue(a.codegen) { private val expression: IrFunctionAccessExpression,
op: IElementType,
private val a: MaterialValue,
private val b: MaterialValue
) : BooleanValue(a.codegen) {
private val numberCompareOpcode = NumberCompare.getNumberCompareOpcode(op) private val numberCompareOpcode = NumberCompare.getNumberCompareOpcode(op)
private fun invokeStaticComparison(type: Type) { private fun invokeStaticComparison(type: Type) {
@@ -118,22 +135,26 @@ class NonIEEE754FloatComparison(op: IElementType, private val a: MaterialValue,
} }
override fun jumpIfFalse(target: Label) { override fun jumpIfFalse(target: Label) {
markLineNumber(expression)
invokeStaticComparison(a.type) invokeStaticComparison(a.type)
mv.visitJumpInsn(numberCompareOpcode, target) mv.visitJumpInsn(numberCompareOpcode, target)
} }
override fun jumpIfTrue(target: Label) { override fun jumpIfTrue(target: Label) {
markLineNumber(expression)
invokeStaticComparison(a.type) invokeStaticComparison(a.type)
mv.visitJumpInsn(BranchedValue.negatedOperations[numberCompareOpcode]!!, target) mv.visitJumpInsn(BranchedValue.negatedOperations[numberCompareOpcode]!!, target)
} }
override fun discard() { override fun discard() {
markLineNumber(expression)
b.discard() b.discard()
a.discard() a.discard()
} }
} }
class PrimitiveToObjectComparison( class PrimitiveToObjectComparison(
private val expression: IrFunctionAccessExpression,
private val op: IElementType, private val op: IElementType,
private val leftIsPrimitive: Boolean, private val leftIsPrimitive: Boolean,
private val left: MaterialValue, private val left: MaterialValue,
@@ -159,26 +180,31 @@ class PrimitiveToObjectComparison(
mv.mark(compareLabel) mv.mark(compareLabel)
// Type checking OK, can unbox and compare: // Type checking OK, can unbox and compare:
return if (leftIsPrimitive) { return if (leftIsPrimitive) {
BooleanComparison(op, left, right.materializedAt(left.type, right.irType)) BooleanComparison(expression, op, left, right.materializedAt(left.type, right.irType))
} else { } else {
val leftUnboxed = left.materializedAt(right.type, left.irType) val leftUnboxed = left.materializedAt(right.type, left.irType)
mv.load(tmp, right.type) mv.load(tmp, right.type)
codegen.frameMap.leaveTemp(right.type) codegen.frameMap.leaveTemp(right.type)
BooleanComparison(op, leftUnboxed, right) BooleanComparison(expression, op, leftUnboxed, right)
} }
} }
override fun jumpIfFalse(target: Label) { override fun jumpIfFalse(target: Label) {
checkTypeAndCompare(target).jumpIfFalse(target) markLineNumber(expression)
val comparison = checkTypeAndCompare(target)
comparison.jumpIfFalse(target)
} }
override fun jumpIfTrue(target: Label) { override fun jumpIfTrue(target: Label) {
markLineNumber(expression)
val wrongType = Label() val wrongType = Label()
checkTypeAndCompare(wrongType).jumpIfTrue(target) val comparison = checkTypeAndCompare(wrongType)
comparison.jumpIfTrue(target)
mv.mark(wrongType) mv.mark(wrongType)
} }
override fun discard() { override fun discard() {
markLineNumber(expression)
right.discard() right.discard()
left.discard() left.discard()
} }
@@ -200,9 +226,9 @@ class PrimitiveComparison(
&& (left.isSmartcastFromHigherThanNullable(codegen.context) || right.isSmartcastFromHigherThanNullable(codegen.context)) && (left.isSmartcastFromHigherThanNullable(codegen.context) || right.isSmartcastFromHigherThanNullable(codegen.context))
return if (useNonIEEE754Comparison) { return if (useNonIEEE754Comparison) {
NonIEEE754FloatComparison(operatorToken, a, b) NonIEEE754FloatComparison(expression, operatorToken, a, b)
} else { } else {
BooleanComparison(operatorToken, a, b) BooleanComparison(expression, operatorToken, a, b)
} }
} }
} }
@@ -57,10 +57,21 @@ class ExplicitEquals : IntrinsicMethod() {
class Equals(val operator: IElementType) : IntrinsicMethod() { class Equals(val operator: IElementType) : IntrinsicMethod() {
private class BooleanNullCheck(val value: PromisedValue) : BooleanValue(value.codegen) { private class BooleanNullCheck(val expression: IrFunctionAccessExpression, val value: PromisedValue) : BooleanValue(value.codegen) {
override fun jumpIfFalse(target: Label) = value.materialize().also { mv.ifnonnull(target) } override fun jumpIfFalse(target: Label) {
override fun jumpIfTrue(target: Label) = value.materialize().also { mv.ifnull(target) } value.materialize()
markLineNumber(expression)
mv.ifnonnull(target)
}
override fun jumpIfTrue(target: Label) {
value.materialize()
markLineNumber(expression)
mv.ifnull(target)
}
override fun discard() { override fun discard() {
markLineNumber(expression)
value.discard() value.discard()
} }
} }
@@ -71,7 +82,7 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
val irValue = if (a.isNullConst()) b else a val irValue = if (a.isNullConst()) b else a
val value = irValue.accept(codegen, data) val value = irValue.accept(codegen, data)
return if (!isPrimitive(value.type) && (irValue.type.classOrNull?.owner?.isSingleFieldValueClass != true || irValue.type.isNullable())) return if (!isPrimitive(value.type) && (irValue.type.classOrNull?.owner?.isSingleFieldValueClass != true || irValue.type.isNullable()))
BooleanNullCheck(value) BooleanNullCheck(expression, value)
else { else {
value.discard() value.discard()
BooleanConstant(codegen, false) BooleanConstant(codegen, false)
@@ -80,7 +91,7 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
val leftType = codegen.typeMapper.mapTypeAsDeclaration(a.type) val leftType = codegen.typeMapper.mapTypeAsDeclaration(a.type)
if (expression.origin == IrStatementOrigin.EQEQEQ || expression.origin == IrStatementOrigin.EXCLEQEQ) { if (expression.origin == IrStatementOrigin.EQEQEQ || expression.origin == IrStatementOrigin.EXCLEQEQ) {
return referenceEquals(a, b, leftType, codegen, data) return referenceEquals(expression, a, b, leftType, codegen, data)
} }
val rightType = codegen.typeMapper.mapTypeAsDeclaration(b.type) val rightType = codegen.typeMapper.mapTypeAsDeclaration(b.type)
@@ -93,7 +104,7 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
) { ) {
val aValue = a.accept(codegen, data).materializedAt(leftType, a.type) val aValue = a.accept(codegen, data).materializedAt(leftType, a.type)
val bValue = b.accept(codegen, data).materializedAt(rightType, b.type) val bValue = b.accept(codegen, data).materializedAt(rightType, b.type)
return PrimitiveToObjectComparison(operator, AsmUtil.isIntOrLongPrimitive(leftType), aValue, bValue) return PrimitiveToObjectComparison(expression, operator, AsmUtil.isIntOrLongPrimitive(leftType), aValue, bValue)
} }
if (isPrimitive(leftType) && leftType == rightType) { if (isPrimitive(leftType) && leftType == rightType) {
@@ -101,26 +112,29 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
return if (leftType == Type.FLOAT_TYPE || leftType == Type.DOUBLE_TYPE) { return if (leftType == Type.FLOAT_TYPE || leftType == Type.DOUBLE_TYPE) {
val aValue = a.accept(codegen, data).materializedAt(leftType, a.type) val aValue = a.accept(codegen, data).materializedAt(leftType, a.type)
val bValue = b.accept(codegen, data).materializedAt(rightType, b.type) val bValue = b.accept(codegen, data).materializedAt(rightType, b.type)
return NonIEEE754FloatComparison(operator, aValue, bValue) return NonIEEE754FloatComparison(expression, operator, aValue, bValue)
} else { } else {
referenceEquals(a, b, leftType, codegen, data) referenceEquals(expression, a, b, leftType, codegen, data)
} }
} }
// We can use reference equality for enums, otherwise we fall back to boxed equality. // We can use reference equality for enums, otherwise we fall back to boxed equality.
return when { return when {
a.isEnumValue || b.isEnumValue -> a.isEnumValue || b.isEnumValue ->
referenceEquals(a, b, leftType, codegen, data) referenceEquals(expression, a, b, leftType, codegen, data)
a.isClassValue && b.isClassValue -> { a.isClassValue && b.isClassValue -> {
val leftValue = codegen.generateClassLiteralReference(a, wrapIntoKClass = false, wrapPrimitives = true, data = data) val leftValue = codegen.generateClassLiteralReference(a, wrapIntoKClass = false, wrapPrimitives = true, data = data)
val rightValue = codegen.generateClassLiteralReference(b, wrapIntoKClass = false, wrapPrimitives = true, data = data) val rightValue = codegen.generateClassLiteralReference(b, wrapIntoKClass = false, wrapPrimitives = true, data = data)
BooleanComparison(operator, leftValue, rightValue) BooleanComparison(expression, operator, leftValue, rightValue)
} }
else -> { else -> {
a.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType) a.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
b.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType) b.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
with(codegen) {
expression.markLineNumber(startOffset = true)
}
genAreEqualCall(codegen.mv) genAreEqualCall(codegen.mv)
MaterialValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType) MaterialValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType)
} }
@@ -128,6 +142,7 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
} }
private fun referenceEquals( private fun referenceEquals(
expression: IrFunctionAccessExpression,
left: IrExpression, left: IrExpression,
right: IrExpression, right: IrExpression,
leftType: Type, leftType: Type,
@@ -137,11 +152,11 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
val operandType = if (!isPrimitive(leftType)) AsmTypes.OBJECT_TYPE else leftType val operandType = if (!isPrimitive(leftType)) AsmTypes.OBJECT_TYPE else leftType
return if (operandType == Type.INT_TYPE && (left.isIntegerConst(0) || right.isIntegerConst(0))) { return if (operandType == Type.INT_TYPE && (left.isIntegerConst(0) || right.isIntegerConst(0))) {
val nonZero = if (left.isIntegerConst(0)) right else left val nonZero = if (left.isIntegerConst(0)) right else left
IntegerZeroComparison(nonZero.accept(codegen, data).materializedAt(operandType, nonZero.type)) IntegerZeroComparison(expression, nonZero.accept(codegen, data).materializedAt(operandType, nonZero.type))
} else { } else {
val leftValue = left.accept(codegen, data).materializedAt(operandType, left.type) val leftValue = left.accept(codegen, data).materializedAt(operandType, left.type)
val rightValue = right.accept(codegen, data).materializedAt(operandType, right.type) val rightValue = right.accept(codegen, data).materializedAt(operandType, right.type)
BooleanComparison(operator, leftValue, rightValue) BooleanComparison(expression, operator, leftValue, rightValue)
} }
} }
@@ -22,16 +22,26 @@ import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
import org.jetbrains.kotlin.backend.jvm.codegen.coerceToBoolean import org.jetbrains.kotlin.backend.jvm.codegen.coerceToBoolean
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
import kotlin.math.exp
object Not : IntrinsicMethod() { object Not : IntrinsicMethod() {
class BooleanNegation(val value: BooleanValue) : BooleanValue(value.codegen) { class BooleanNegation(val expression: IrFunctionAccessExpression, val value: BooleanValue) : BooleanValue(value.codegen) {
override fun jumpIfFalse(target: Label) = value.jumpIfTrue(target) override fun jumpIfFalse(target: Label) {
override fun jumpIfTrue(target: Label) = value.jumpIfFalse(target) markLineNumber(expression)
value.jumpIfTrue(target)
}
override fun jumpIfTrue(target: Label) {
markLineNumber(expression)
value.jumpIfFalse(target)
}
override fun discard() { override fun discard() {
markLineNumber(expression)
value.discard() value.discard()
} }
} }
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) =
BooleanNegation(expression.dispatchReceiver!!.accept(codegen, data).coerceToBoolean()) BooleanNegation(expression, expression.dispatchReceiver!!.accept(codegen, data).coerceToBoolean())
} }
@@ -14,25 +14,37 @@ import org.jetbrains.org.objectweb.asm.Label
object OrOr : IntrinsicMethod() { object OrOr : IntrinsicMethod() {
private class BooleanDisjunction( private class BooleanDisjunction(
val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo val left: IrExpression, val right: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo
) : BooleanValue(codegen) { ) : BooleanValue(codegen) {
override fun jumpIfFalse(target: Label) { override fun jumpIfFalse(target: Label) {
val stayLabel = Label() val stayLabel = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(stayLabel) val leftValue = left.accept(codegen, data).coerceToBoolean()
arg1.accept(codegen, data).coerceToBoolean().jumpIfFalse(target) markLineNumber(left)
leftValue.jumpIfTrue(stayLabel)
val rightValue = right.accept(codegen, data).coerceToBoolean()
markLineNumber(right)
rightValue.jumpIfFalse(target)
mv.visitLabel(stayLabel) mv.visitLabel(stayLabel)
} }
override fun jumpIfTrue(target: Label) { override fun jumpIfTrue(target: Label) {
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(target) val leftValue = left.accept(codegen, data).coerceToBoolean()
arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target) markLineNumber(left)
leftValue.jumpIfTrue(target)
val rightValue = right.accept(codegen, data).coerceToBoolean()
markLineNumber(right)
rightValue.jumpIfTrue(target)
} }
override fun discard() { override fun discard() {
val end = Label() val end = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(end) val leftValue = left.accept(codegen, data).coerceToBoolean()
arg1.accept(codegen, data).discard() markLineNumber(left)
leftValue.jumpIfTrue(end)
val rightValue = right.accept(codegen, data)
markLineNumber(right)
rightValue.discard()
mv.visitLabel(end) mv.visitLabel(end)
} }
} }
+1 -1
View File
@@ -20,7 +20,7 @@ inline fun getB(): Int {
// test.kt:15 box // test.kt:15 box
// test.kt:7 box // test.kt:7 box
// test.kt:12 getA // test.kt:12 getA
// test.kt:7 box // test.kt:6 box
// test.kt:9 box // test.kt:9 box
// test.kt:15 box // test.kt:15 box
// test.kt:9 box // test.kt:9 box
+43
View File
@@ -0,0 +1,43 @@
// IGNORE_BACKEND: WASM
// FILE: test.kt
fun foo(n: Number) {
if (n.toInt() > 1 && when (n) {
is Float -> true
else -> false
}) {
}
if (when (n) {
is Float -> true
else -> false
} && n.toInt() > 1) {
}
}
fun box() {
foo(2.0f)
}
// EXPECTATIONS JVM_IR
// test.kt:18 box
// test.kt:5 foo
// test.kt:6 foo
// test.kt:5 foo
// test.kt:10 foo
// test.kt:11 foo
// test.kt:10 foo
// test.kt:13 foo
// test.kt:15 foo
// test.kt:19 box
// EXPECTATIONS JS_IR
// test.kt:18 box
// test.kt:5 foo
// test.kt:6 foo
// test.kt:6 foo
// test.kt:11 foo
// test.kt:11 foo
// test.kt:10 foo
// test.kt:13 foo
// test.kt:15 foo
// test.kt:19 box
+43
View File
@@ -0,0 +1,43 @@
// IGNORE_BACKEND: WASM
// FILE: test.kt
fun foo(n: Number) {
if (n.toInt() < 1 || when (n) {
is Float -> false
else -> true
}) {
}
if (when (n) {
is Float -> false
else -> true
} || n.toInt() > 1) {
}
}
fun box() {
foo(2.0f)
}
// EXPECTATIONS JVM_IR
// test.kt:18 box
// test.kt:5 foo
// test.kt:6 foo
// test.kt:5 foo
// test.kt:10 foo
// test.kt:11 foo
// test.kt:10 foo
// test.kt:13 foo
// test.kt:15 foo
// test.kt:19 box
// EXPECTATIONS JS_IR
// test.kt:18 box
// test.kt:5 foo
// test.kt:6 foo
// test.kt:6 foo
// test.kt:11 foo
// test.kt:11 foo
// test.kt:10 foo
// test.kt:13 foo
// test.kt:15 foo
// test.kt:19 box
@@ -0,0 +1,65 @@
// IGNORE_BACKEND: WASM
// FILE: test.kt
fun foo(n: Any, other: Any) {
if (other == when (n) {
is Int -> n
else -> 1.0f
}) {
}
if (when (n) {
is Int -> n
else -> 1.0f
} == other) {
}
if (other != when (n) {
is Int -> n
else -> 1.0f
}) {
}
if (when (n) {
is Int -> n
else -> 1.0f
} != other) {
}
}
fun box() {
foo(2, Any())
}
// EXPECTATIONS JVM_IR
// test.kt:28 box
// test.kt:5 foo
// test.kt:6 foo
// test.kt:5 foo
// test.kt:10 foo
// test.kt:11 foo
// test.kt:13 foo
// test.kt:10 foo
// test.kt:15 foo
// test.kt:16 foo
// test.kt:15 foo
// test.kt:20 foo
// test.kt:21 foo
// test.kt:23 foo
// test.kt:20 foo
// test.kt:25 foo
// test.kt:29 box
// EXPECTATIONS JS_IR
// test.kt:28 box
// test.kt:6 foo
// test.kt:6 foo
// test.kt:5 foo
// test.kt:11 foo
// test.kt:11 foo
// test.kt:10 foo
// test.kt:16 foo
// test.kt:16 foo
// test.kt:15 foo
// test.kt:21 foo
// test.kt:21 foo
// test.kt:20 foo
// test.kt:25 foo
// test.kt:29 box
+29
View File
@@ -0,0 +1,29 @@
// IGNORE_BACKEND: WASM
// FILE: test.kt
fun foo(n: Number) {
if (!when (n) {
is Float -> false
else -> true
}) {
}
}
fun box() {
foo(2.0f)
}
// EXPECTATIONS JVM_IR
// test.kt:13 box
// test.kt:5 foo
// test.kt:6 foo
// test.kt:5 foo
// test.kt:10 foo
// test.kt:14 box
// EXPECTATIONS JS_IR
// test.kt:13 box
// test.kt:6 foo
// test.kt:6 foo
// test.kt:10 foo
// test.kt:14 box
@@ -0,0 +1,39 @@
// IGNORE_BACKEND: WASM
// FILE: test.kt
fun foo(n: Number) {
if (when (n) {
is Float -> null
else -> 32
} == null) {
}
if (when (n) {
is Float -> null
else -> 32
} != null) {
}
}
fun box() {
foo(2.0f)
}
// EXPECTATIONS JVM_IR
// test.kt:18 box
// test.kt:5 foo
// test.kt:6 foo
// test.kt:5 foo
// test.kt:10 foo
// test.kt:11 foo
// test.kt:10 foo
// test.kt:15 foo
// test.kt:19 box
// EXPECTATIONS JS_IR
// test.kt:18 box
// test.kt:6 foo
// test.kt:6 foo
// test.kt:11 foo
// test.kt:11 foo
// test.kt:15 foo
// test.kt:19 box
@@ -0,0 +1,41 @@
// IGNORE_BACKEND: WASM
// FILE: test.kt
fun foo(n: Any) {
if (1 == when (n) {
is Int -> n
else -> 1.0f
}) {
}
if (1 != when (n) {
is Int -> n
else -> 1.0f
}) {
}
}
fun box() {
foo(2)
}
// EXPECTATIONS JVM_IR
// test.kt:18 box
// test.kt:5 foo
// test.kt:6 foo
// test.kt:5 foo
// test.kt:10 foo
// test.kt:11 foo
// test.kt:10 foo
// test.kt:15 foo
// test.kt:19 box
// EXPECTATIONS JS_IR
// test.kt:18 box
// test.kt:6 foo
// test.kt:6 foo
// test.kt:5 foo
// test.kt:11 foo
// test.kt:11 foo
// test.kt:10 foo
// test.kt:15 foo
// test.kt:19 box
@@ -0,0 +1,29 @@
// IGNORE_BACKEND: WASM
// FILE: test.kt
fun foo(n: Number) {
if (when (n) {
is Float -> 1
else -> 0
} == 0) {
}
}
fun box() {
foo(2.0f)
}
// EXPECTATIONS JVM_IR
// test.kt:13 box
// test.kt:5 foo
// test.kt:6 foo
// test.kt:5 foo
// test.kt:10 foo
// test.kt:14 box
// EXPECTATIONS JS_IR
// test.kt:13 box
// test.kt:6 foo
// test.kt:6 foo
// test.kt:10 foo
// test.kt:14 box
@@ -601,6 +601,48 @@ public class IrSteppingWithBytecodeInlinerTestGenerated extends AbstractIrSteppi
runTest("compiler/testData/debug/stepping/whenExpr.kt"); runTest("compiler/testData/debug/stepping/whenExpr.kt");
} }
@Test
@TestMetadata("whenInConjunction.kt")
public void testWhenInConjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInConjunction.kt");
}
@Test
@TestMetadata("whenInDisjunction.kt")
public void testWhenInDisjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInDisjunction.kt");
}
@Test
@TestMetadata("whenInGeneralObjectComparison.kt")
public void testWhenInGeneralObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInGeneralObjectComparison.kt");
}
@Test
@TestMetadata("whenInNegation.kt")
public void testWhenInNegation() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNegation.kt");
}
@Test
@TestMetadata("whenInNullComparison.kt")
public void testWhenInNullComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNullComparison.kt");
}
@Test
@TestMetadata("whenInPrimitiveToObjectComparison.kt")
public void testWhenInPrimitiveToObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInPrimitiveToObjectComparison.kt");
}
@Test
@TestMetadata("whenInZeroComparison.kt")
public void testWhenInZeroComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInZeroComparison.kt");
}
@Test @Test
@TestMetadata("whenIsChecks.kt") @TestMetadata("whenIsChecks.kt")
public void testWhenIsChecks() throws Exception { public void testWhenIsChecks() throws Exception {
@@ -601,6 +601,48 @@ public class IrSteppingWithIrInlinerTestGenerated extends AbstractIrSteppingWith
runTest("compiler/testData/debug/stepping/whenExpr.kt"); runTest("compiler/testData/debug/stepping/whenExpr.kt");
} }
@Test
@TestMetadata("whenInConjunction.kt")
public void testWhenInConjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInConjunction.kt");
}
@Test
@TestMetadata("whenInDisjunction.kt")
public void testWhenInDisjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInDisjunction.kt");
}
@Test
@TestMetadata("whenInGeneralObjectComparison.kt")
public void testWhenInGeneralObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInGeneralObjectComparison.kt");
}
@Test
@TestMetadata("whenInNegation.kt")
public void testWhenInNegation() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNegation.kt");
}
@Test
@TestMetadata("whenInNullComparison.kt")
public void testWhenInNullComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNullComparison.kt");
}
@Test
@TestMetadata("whenInPrimitiveToObjectComparison.kt")
public void testWhenInPrimitiveToObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInPrimitiveToObjectComparison.kt");
}
@Test
@TestMetadata("whenInZeroComparison.kt")
public void testWhenInZeroComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInZeroComparison.kt");
}
@Test @Test
@TestMetadata("whenIsChecks.kt") @TestMetadata("whenIsChecks.kt")
public void testWhenIsChecks() throws Exception { public void testWhenIsChecks() throws Exception {
@@ -595,6 +595,48 @@ public class IrJsSteppingTestGenerated extends AbstractIrJsSteppingTest {
runTest("compiler/testData/debug/stepping/whenExpr.kt"); runTest("compiler/testData/debug/stepping/whenExpr.kt");
} }
@Test
@TestMetadata("whenInConjunction.kt")
public void testWhenInConjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInConjunction.kt");
}
@Test
@TestMetadata("whenInDisjunction.kt")
public void testWhenInDisjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInDisjunction.kt");
}
@Test
@TestMetadata("whenInGeneralObjectComparison.kt")
public void testWhenInGeneralObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInGeneralObjectComparison.kt");
}
@Test
@TestMetadata("whenInNegation.kt")
public void testWhenInNegation() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNegation.kt");
}
@Test
@TestMetadata("whenInNullComparison.kt")
public void testWhenInNullComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNullComparison.kt");
}
@Test
@TestMetadata("whenInPrimitiveToObjectComparison.kt")
public void testWhenInPrimitiveToObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInPrimitiveToObjectComparison.kt");
}
@Test
@TestMetadata("whenInZeroComparison.kt")
public void testWhenInZeroComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInZeroComparison.kt");
}
@Test @Test
@TestMetadata("whenIsChecks.kt") @TestMetadata("whenIsChecks.kt")
public void testWhenIsChecks() throws Exception { public void testWhenIsChecks() throws Exception {
@@ -643,16 +643,16 @@ public final class OptionalUser : java/lang/Object {
LDC () LDC ()
LDC () LDC ()
INVOKESPECIAL (User, <init>, (Ljava/lang/String;Ljava/lang/String;)V) INVOKESPECIAL (User, <init>, (Ljava/lang/String;Ljava/lang/String;)V)
LABEL (L4)
LINENUMBER (9)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, areEqual, (Ljava/lang/Object;Ljava/lang/Object;)Z) INVOKESTATIC (kotlin/jvm/internal/Intrinsics, areEqual, (Ljava/lang/Object;Ljava/lang/Object;)Z)
IFNE (L4) IFNE (L5)
ICONST_1 ICONST_1
GOTO (L2) GOTO (L2)
LABEL (L4) LABEL (L5)
ICONST_0 ICONST_0
LABEL (L2) LABEL (L2)
IFEQ (L5) IFEQ (L6)
LABEL (L6)
LINENUMBER (9)
ALOAD (1) ALOAD (1)
ALOAD (2) ALOAD (2)
ICONST_0 ICONST_0
@@ -661,7 +661,7 @@ public final class OptionalUser : java/lang/Object {
ALOAD (0) ALOAD (0)
GETFIELD (OptionalUser, user, LUser;) GETFIELD (OptionalUser, user, LUser;)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V)
LABEL (L5) LABEL (L6)
RETURN RETURN
LABEL (L7) LABEL (L7)
} }
@@ -681,16 +681,16 @@ public final class OptionalUser : java/lang/Object {
LDC () LDC ()
LDC () LDC ()
INVOKESPECIAL (User, <init>, (Ljava/lang/String;Ljava/lang/String;)V) INVOKESPECIAL (User, <init>, (Ljava/lang/String;Ljava/lang/String;)V)
LABEL (L4)
LINENUMBER (9)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, areEqual, (Ljava/lang/Object;Ljava/lang/Object;)Z) INVOKESTATIC (kotlin/jvm/internal/Intrinsics, areEqual, (Ljava/lang/Object;Ljava/lang/Object;)Z)
IFNE (L4) IFNE (L5)
ICONST_1 ICONST_1
GOTO (L2) GOTO (L2)
LABEL (L4) LABEL (L5)
ICONST_0 ICONST_0
LABEL (L2) LABEL (L2)
IFEQ (L5) IFEQ (L6)
LABEL (L6)
LINENUMBER (9)
ALOAD (1) ALOAD (1)
ALOAD (2) ALOAD (2)
ICONST_0 ICONST_0
@@ -699,7 +699,7 @@ public final class OptionalUser : java/lang/Object {
ALOAD (0) ALOAD (0)
GETFIELD (OptionalUser, user, LUser;) GETFIELD (OptionalUser, user, LUser;)
INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V) INVOKEINTERFACE (kotlinx/serialization/encoding/CompositeEncoder, encodeSerializableElement, (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V)
LABEL (L5) LABEL (L6)
RETURN RETURN
LABEL (L7) LABEL (L7)
} }
@@ -595,6 +595,48 @@ public class FirWasmSteppingTestGenerated extends AbstractFirWasmSteppingTest {
runTest("compiler/testData/debug/stepping/whenExpr.kt"); runTest("compiler/testData/debug/stepping/whenExpr.kt");
} }
@Test
@TestMetadata("whenInConjunction.kt")
public void testWhenInConjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInConjunction.kt");
}
@Test
@TestMetadata("whenInDisjunction.kt")
public void testWhenInDisjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInDisjunction.kt");
}
@Test
@TestMetadata("whenInGeneralObjectComparison.kt")
public void testWhenInGeneralObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInGeneralObjectComparison.kt");
}
@Test
@TestMetadata("whenInNegation.kt")
public void testWhenInNegation() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNegation.kt");
}
@Test
@TestMetadata("whenInNullComparison.kt")
public void testWhenInNullComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNullComparison.kt");
}
@Test
@TestMetadata("whenInPrimitiveToObjectComparison.kt")
public void testWhenInPrimitiveToObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInPrimitiveToObjectComparison.kt");
}
@Test
@TestMetadata("whenInZeroComparison.kt")
public void testWhenInZeroComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInZeroComparison.kt");
}
@Test @Test
@TestMetadata("whenIsChecks.kt") @TestMetadata("whenIsChecks.kt")
public void testWhenIsChecks() throws Exception { public void testWhenIsChecks() throws Exception {
@@ -595,6 +595,48 @@ public class K1WasmSteppingTestGenerated extends AbstractK1WasmSteppingTest {
runTest("compiler/testData/debug/stepping/whenExpr.kt"); runTest("compiler/testData/debug/stepping/whenExpr.kt");
} }
@Test
@TestMetadata("whenInConjunction.kt")
public void testWhenInConjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInConjunction.kt");
}
@Test
@TestMetadata("whenInDisjunction.kt")
public void testWhenInDisjunction() throws Exception {
runTest("compiler/testData/debug/stepping/whenInDisjunction.kt");
}
@Test
@TestMetadata("whenInGeneralObjectComparison.kt")
public void testWhenInGeneralObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInGeneralObjectComparison.kt");
}
@Test
@TestMetadata("whenInNegation.kt")
public void testWhenInNegation() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNegation.kt");
}
@Test
@TestMetadata("whenInNullComparison.kt")
public void testWhenInNullComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInNullComparison.kt");
}
@Test
@TestMetadata("whenInPrimitiveToObjectComparison.kt")
public void testWhenInPrimitiveToObjectComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInPrimitiveToObjectComparison.kt");
}
@Test
@TestMetadata("whenInZeroComparison.kt")
public void testWhenInZeroComparison() throws Exception {
runTest("compiler/testData/debug/stepping/whenInZeroComparison.kt");
}
@Test @Test
@TestMetadata("whenIsChecks.kt") @TestMetadata("whenIsChecks.kt")
public void testWhenIsChecks() throws Exception { public void testWhenIsChecks() throws Exception {