diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt index b6eff9aaab4..55de84c67f4 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt @@ -14,7 +14,10 @@ import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget -import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrValueParameter import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol @@ -227,25 +230,12 @@ class AutoboxingTransformer(context: JsCommonBackendContext) : AbstractValueUsag } override fun visitCall(expression: IrCall): IrExpression { - return if ( - expression.symbol != irBuiltIns.eqeqeqSymbol || - !expression.allArgumentsHaveType(irBuiltIns.charType) && - expression.origin != IrStatementOrigin.SYNTHETIC_NOT_AUTOBOXED_CHECK - ) { - super.visitCall(expression) - } else { + return if (expression.origin == IrStatementOrigin.SYNTHETIC_NOT_AUTOBOXED_CHECK) { expression.apply { transformChildrenVoid() } + } else { + super.visitCall(expression) } } - - private fun IrCall.allArgumentsHaveType(type: IrType): Boolean { - for (i in 0 until valueArgumentsCount) { - if (getValueArgument(i)?.type != type) { - return false - } - } - return true - } } private tailrec fun IrExpression.isGetUnit(irBuiltIns: IrBuiltIns): Boolean = diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/EqualityAndComparisonCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/EqualityAndComparisonCallsTransformer.kt index 0a4cb3d0865..e51a6ece8cf 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/EqualityAndComparisonCallsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/EqualityAndComparisonCallsTransformer.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.ir.backend.js.lower.calls -import org.jetbrains.kotlin.descriptors.InlineClassRepresentation import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.utils.isEqualsInheritedFromAny @@ -28,7 +27,7 @@ class EqualityAndComparisonCallsTransformer(context: JsIrBackendContext) : Calls init { symbolToTransformer.run { - add(irBuiltIns.eqeqeqSymbol, intrinsics.jsEqeqeq) + add(irBuiltIns.eqeqeqSymbol, ::transformEqeqeqOperator) add(irBuiltIns.eqeqSymbol, ::transformEqeqOperator) // ieee754equals can only be applied in between statically known Floats, Doubles, null or undefined add(irBuiltIns.ieee754equalsFunByOperandType, ::chooseEqualityOperatorForPrimitiveTypes) @@ -74,6 +73,21 @@ class EqualityAndComparisonCallsTransformer(context: JsIrBackendContext) : Calls } } + private fun transformEqeqeqOperator(call: IrFunctionAccessExpression): IrExpression { + val lhs = call.getValueArgument(0)!! + val rhs = call.getValueArgument(1)!! + + return if (lhs.isCharBoxing() && rhs.isCharBoxing()) { + optimizeInlineClassEquality(call, lhs, rhs) + } else { + irCall(call, intrinsics.jsEqeqeq) + } + } + + private fun IrExpression.isCharBoxing(): Boolean { + return this is IrCall && symbol == intrinsics.jsBoxIntrinsic && getValueArgument(0)!!.type.isChar() + } + private fun transformEqeqOperator(call: IrFunctionAccessExpression): IrExpression { val lhs = call.getValueArgument(0)!! val rhs = call.getValueArgument(1)!! @@ -186,7 +200,7 @@ class EqualityAndComparisonCallsTransformer(context: JsIrBackendContext) : Calls isBuiltin() && this != PrimitiveType.FLOATING_POINT_NUMBER private fun IrType.isDefaultEqualsMethod() = - findEqualsMethod()?.origin === IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER + isChar() || findEqualsMethod()?.origin === IrDeclarationOrigin.GENERATED_SINGLE_FIELD_VALUE_CLASS_MEMBER private fun IrExpression.isBoxIntrinsic() = this is IrCall && symbol == icUtils.boxIntrinsic @@ -204,7 +218,7 @@ class EqualityAndComparisonCallsTransformer(context: JsIrBackendContext) : Calls call.putValueArgument(0, lhsUnboxed) call.putValueArgument(1, rhsUnboxed) - if (lhsUnboxed.type.getLowestUnderlyingType().getPrimitiveType().canBeUsedWithJsEq()) { + if (lhsUnboxed.type.isChar() || lhsUnboxed.type.getLowestUnderlyingType().getPrimitiveType().canBeUsedWithJsEq()) { return chooseEqualityOperatorForPrimitiveTypes(call) } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/testOld/utils/DirectiveTestUtils.java b/js/js.tests/test/org/jetbrains/kotlin/js/testOld/utils/DirectiveTestUtils.java index 73f87551adc..ca606b50353 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/testOld/utils/DirectiveTestUtils.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/testOld/utils/DirectiveTestUtils.java @@ -219,8 +219,8 @@ public class DirectiveTestUtils { @Override void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception { String functionName = arguments.getNamedArgument("function"); - String countStr = arguments.getNamedArgument("count"); - int expectedCount = Integer.valueOf(countStr); + String countStr = arguments.findNamedArgument("count"); + String maxCountStr = arguments.findNamedArgument("max"); JsFunction function = AstSearchUtil.getFunction(ast, functionName); List nodes = collectInstances(klass, function.getBody()); @@ -230,10 +230,24 @@ public class DirectiveTestUtils { actualCount += getActualCountFor(node, arguments); } - String message = "Function " + functionName + " contains " + actualCount + - " nodes of type " + klass.getName() + - ", but expected count is " + expectedCount; - assertEquals(message, expectedCount, actualCount); + if (countStr != null) { + int expectedCount = Integer.valueOf(countStr); + + String message = "Function " + functionName + " contains " + actualCount + + " nodes of type " + klass.getName() + + ", but expected count is " + expectedCount; + assertEquals(message, expectedCount, actualCount); + } else if (maxCountStr != null) { + int expectedCount = Integer.valueOf(maxCountStr); + + String message = "Function " + functionName + " contains " + actualCount + + " nodes of type " + klass.getName() + + ", but expected max is " + expectedCount; + assertTrue(message, expectedCount >= actualCount); + + } else { + throw new IllegalArgumentException("'max' or 'count' argument should be provided"); + } } protected int getActualCountFor(@NotNull T node, @NotNull ArgumentsHelper arguments) { diff --git a/js/js.translator/testData/box/char/charEquals.kt b/js/js.translator/testData/box/char/charEquals.kt index 3dc4ac6f016..a4c68ea35cb 100644 --- a/js/js.translator/testData/box/char/charEquals.kt +++ b/js/js.translator/testData/box/char/charEquals.kt @@ -1,15 +1,21 @@ // EXPECTED_REACHABLE_NODES: 1372 package foo +// CHECK_NEW_COUNT: function=box max=6 fun box(): String { - assertEquals(true, 'A' == 'A') + assertEquals(false, 'A' != 'A') assertEquals(false, 'A' == 'B') + assertEquals(true, 'A' != 'B') assertEquals(false, ('A' as Any) == (65 as Any)) + assertEquals(true, ('A' as Any) != (65 as Any)) assertEquals(true, 'A' === 'A') + assertEquals(false, 'A' !== 'A') assertEquals(false, 'A' === 'B') + assertEquals(true, 'A' !== 'B') assertEquals(false, ('A' as Any) === (65 as Any)) + assertEquals(true, ('A' as Any) !== (65 as Any)) assertTrue(bar('Q')) assertFalse(bar('W'))