[K/JS] Prevent Char boxing on value or reference equals call
This commit is contained in:
+7
-17
@@ -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.JsCommonBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
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.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
|
||||||
@@ -227,25 +230,12 @@ class AutoboxingTransformer(context: JsCommonBackendContext) : AbstractValueUsag
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
return if (
|
return if (expression.origin == IrStatementOrigin.SYNTHETIC_NOT_AUTOBOXED_CHECK) {
|
||||||
expression.symbol != irBuiltIns.eqeqeqSymbol ||
|
|
||||||
!expression.allArgumentsHaveType(irBuiltIns.charType) &&
|
|
||||||
expression.origin != IrStatementOrigin.SYNTHETIC_NOT_AUTOBOXED_CHECK
|
|
||||||
) {
|
|
||||||
super.visitCall(expression)
|
|
||||||
} else {
|
|
||||||
expression.apply { transformChildrenVoid() }
|
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 =
|
private tailrec fun IrExpression.isGetUnit(irBuiltIns: IrBuiltIns): Boolean =
|
||||||
|
|||||||
+18
-4
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.backend.js.lower.calls
|
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.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.isEqualsInheritedFromAny
|
import org.jetbrains.kotlin.ir.backend.js.utils.isEqualsInheritedFromAny
|
||||||
@@ -28,7 +27,7 @@ class EqualityAndComparisonCallsTransformer(context: JsIrBackendContext) : Calls
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
symbolToTransformer.run {
|
symbolToTransformer.run {
|
||||||
add(irBuiltIns.eqeqeqSymbol, intrinsics.jsEqeqeq)
|
add(irBuiltIns.eqeqeqSymbol, ::transformEqeqeqOperator)
|
||||||
add(irBuiltIns.eqeqSymbol, ::transformEqeqOperator)
|
add(irBuiltIns.eqeqSymbol, ::transformEqeqOperator)
|
||||||
// ieee754equals can only be applied in between statically known Floats, Doubles, null or undefined
|
// ieee754equals can only be applied in between statically known Floats, Doubles, null or undefined
|
||||||
add(irBuiltIns.ieee754equalsFunByOperandType, ::chooseEqualityOperatorForPrimitiveTypes)
|
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 {
|
private fun transformEqeqOperator(call: IrFunctionAccessExpression): IrExpression {
|
||||||
val lhs = call.getValueArgument(0)!!
|
val lhs = call.getValueArgument(0)!!
|
||||||
val rhs = call.getValueArgument(1)!!
|
val rhs = call.getValueArgument(1)!!
|
||||||
@@ -186,7 +200,7 @@ class EqualityAndComparisonCallsTransformer(context: JsIrBackendContext) : Calls
|
|||||||
isBuiltin() && this != PrimitiveType.FLOATING_POINT_NUMBER
|
isBuiltin() && this != PrimitiveType.FLOATING_POINT_NUMBER
|
||||||
|
|
||||||
private fun IrType.isDefaultEqualsMethod() =
|
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() =
|
private fun IrExpression.isBoxIntrinsic() =
|
||||||
this is IrCall && symbol == icUtils.boxIntrinsic
|
this is IrCall && symbol == icUtils.boxIntrinsic
|
||||||
@@ -204,7 +218,7 @@ class EqualityAndComparisonCallsTransformer(context: JsIrBackendContext) : Calls
|
|||||||
call.putValueArgument(0, lhsUnboxed)
|
call.putValueArgument(0, lhsUnboxed)
|
||||||
call.putValueArgument(1, rhsUnboxed)
|
call.putValueArgument(1, rhsUnboxed)
|
||||||
|
|
||||||
if (lhsUnboxed.type.getLowestUnderlyingType().getPrimitiveType().canBeUsedWithJsEq()) {
|
if (lhsUnboxed.type.isChar() || lhsUnboxed.type.getLowestUnderlyingType().getPrimitiveType().canBeUsedWithJsEq()) {
|
||||||
return chooseEqualityOperatorForPrimitiveTypes(call)
|
return chooseEqualityOperatorForPrimitiveTypes(call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,8 +219,8 @@ public class DirectiveTestUtils {
|
|||||||
@Override
|
@Override
|
||||||
void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception {
|
void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception {
|
||||||
String functionName = arguments.getNamedArgument("function");
|
String functionName = arguments.getNamedArgument("function");
|
||||||
String countStr = arguments.getNamedArgument("count");
|
String countStr = arguments.findNamedArgument("count");
|
||||||
int expectedCount = Integer.valueOf(countStr);
|
String maxCountStr = arguments.findNamedArgument("max");
|
||||||
|
|
||||||
JsFunction function = AstSearchUtil.getFunction(ast, functionName);
|
JsFunction function = AstSearchUtil.getFunction(ast, functionName);
|
||||||
List<T> nodes = collectInstances(klass, function.getBody());
|
List<T> nodes = collectInstances(klass, function.getBody());
|
||||||
@@ -230,10 +230,24 @@ public class DirectiveTestUtils {
|
|||||||
actualCount += getActualCountFor(node, arguments);
|
actualCount += getActualCountFor(node, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
String message = "Function " + functionName + " contains " + actualCount +
|
if (countStr != null) {
|
||||||
" nodes of type " + klass.getName() +
|
int expectedCount = Integer.valueOf(countStr);
|
||||||
", but expected count is " + expectedCount;
|
|
||||||
assertEquals(message, expectedCount, actualCount);
|
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) {
|
protected int getActualCountFor(@NotNull T node, @NotNull ArgumentsHelper arguments) {
|
||||||
|
|||||||
+7
-1
@@ -1,15 +1,21 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 1372
|
// EXPECTED_REACHABLE_NODES: 1372
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
// CHECK_NEW_COUNT: function=box max=6
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|
||||||
assertEquals(true, 'A' == 'A')
|
assertEquals(true, 'A' == 'A')
|
||||||
|
assertEquals(false, 'A' != 'A')
|
||||||
assertEquals(false, 'A' == 'B')
|
assertEquals(false, 'A' == 'B')
|
||||||
|
assertEquals(true, 'A' != 'B')
|
||||||
assertEquals(false, ('A' as Any) == (65 as Any))
|
assertEquals(false, ('A' as Any) == (65 as Any))
|
||||||
|
assertEquals(true, ('A' as Any) != (65 as Any))
|
||||||
|
|
||||||
assertEquals(true, 'A' === 'A')
|
assertEquals(true, 'A' === 'A')
|
||||||
|
assertEquals(false, 'A' !== 'A')
|
||||||
assertEquals(false, 'A' === 'B')
|
assertEquals(false, 'A' === 'B')
|
||||||
|
assertEquals(true, 'A' !== 'B')
|
||||||
assertEquals(false, ('A' as Any) === (65 as Any))
|
assertEquals(false, ('A' as Any) === (65 as Any))
|
||||||
|
assertEquals(true, ('A' as Any) !== (65 as Any))
|
||||||
|
|
||||||
assertTrue(bar('Q'))
|
assertTrue(bar('Q'))
|
||||||
assertFalse(bar('W'))
|
assertFalse(bar('W'))
|
||||||
|
|||||||
Reference in New Issue
Block a user