BinaryOperationIntrinsic as typealias

This commit is contained in:
Anton Bannykh
2018-03-01 17:58:28 +03:00
committed by Anton Bannykh
parent 2352dc3d1c
commit 757649d6da
7 changed files with 138 additions and 220 deletions
@@ -30,19 +30,14 @@ import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
object AssignmentBOIF : BinaryOperationIntrinsicFactory { object AssignmentBOIF : BinaryOperationIntrinsicFactory {
private object CharAssignmentIntrinsic : BinaryOperationIntrinsic {
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val operator = OperatorTable.getBinaryOperator(PsiUtils.getOperationToken(expression))
return JsBinaryOperation(operator, left, TranslationUtils.charToBoxedChar(context, right))
}
}
override fun getSupportTokens() = ImmutableSet.of(KtTokens.EQ) override fun getSupportTokens() = ImmutableSet.of(KtTokens.EQ)
override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? { override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? {
if (leftType != null && !KotlinBuiltIns.isCharOrNullableChar(leftType) && rightType != null && KotlinBuiltIns.isCharOrNullableChar(rightType)) { if (leftType != null && !KotlinBuiltIns.isCharOrNullableChar(leftType) && rightType != null && KotlinBuiltIns.isCharOrNullableChar(rightType)) {
return CharAssignmentIntrinsic return { expression, left, right, context ->
val operator = OperatorTable.getBinaryOperator(PsiUtils.getOperationToken(expression))
JsBinaryOperation(operator, left, TranslationUtils.charToBoxedChar(context, right))
}
} }
return null return null
} }
@@ -17,66 +17,75 @@
package org.jetbrains.kotlin.js.translate.intrinsic.operation package org.jetbrains.kotlin.js.translate.intrinsic.operation
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation
import org.jetbrains.kotlin.js.backend.ast.JsExpression import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsIntLiteral import org.jetbrains.kotlin.js.backend.ast.JsIntLiteral
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern
import org.jetbrains.kotlin.js.translate.operation.OperatorTable import org.jetbrains.kotlin.js.translate.operation.OperatorTable
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken
import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.utils.identity
object CompareToBOIF : BinaryOperationIntrinsicFactory { object CompareToBOIF : BinaryOperationIntrinsicFactory {
private object CompareToIntrinsic : BinaryOperationIntrinsic {
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
return JsBinaryOperation(operator, left, right)
}
}
private object CompareToCharIntrinsic : BinaryOperationIntrinsic {
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
return JsBinaryOperation(operator, left, JsAstUtils.charToInt(right))
}
}
private object CompareCharToPrimitiveIntrinsic : BinaryOperationIntrinsic {
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
return JsBinaryOperation(operator, JsAstUtils.charToInt(left), right)
}
}
private object CompareToFunctionIntrinsic : BinaryOperationIntrinsic{
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
val compareTo = JsAstUtils.compareTo(left, right)
return JsBinaryOperation(operator, compareTo, JsIntLiteral(0))
}
}
override fun getSupportTokens(): Set<KtSingleValueToken> = OperatorConventions.COMPARISON_OPERATIONS override fun getSupportTokens(): Set<KtSingleValueToken> = OperatorConventions.COMPARISON_OPERATIONS
// toLeft(L, R) OP toRight(L, R)
private fun intrinsic(
toLeft: (JsExpression, JsExpression) -> JsExpression,
toRight: (JsExpression, JsExpression) -> JsExpression
): BinaryOperationIntrinsic = { expression, left, right, _ ->
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
JsBinaryOperation(operator, toLeft(left, right), toRight(left, right))
}
// toLeft(L) OP toRight(R)
private fun primitiveIntrinsic(
toLeft: (JsExpression) -> JsExpression,
toRight: (JsExpression) -> JsExpression
): BinaryOperationIntrinsic = intrinsic({ l, _ -> toLeft(l) }, { _, r -> toRight(r) })
// toLeft(L).compareTo(toRight(R)) OP 0
private fun compareToIntrinsic(
toLeft: (JsExpression) -> JsExpression,
toRight: (JsExpression) -> JsExpression
): BinaryOperationIntrinsic = intrinsic({ l, r -> compareForObject(toLeft(l), toRight(r)) }, { _, _ -> JsIntLiteral(0) })
private fun unboxCharIfNeeded(type: KotlinType): (JsExpression) -> JsExpression = { e ->
if (KotlinBuiltIns.isCharOrNullableChar(type)) {
charToInt(e)
} else e
}
// TODO Couldn't Long be converted to Number in all cases except Long.compareTo(Long)?
private val patterns = listOf(
pattern("Double|Float.compareTo(Long)") to primitiveIntrinsic(identity(), ::longToNumber),
pattern("Long.compareTo(Float|Double)") to primitiveIntrinsic(::longToNumber, identity()),
pattern("Int|Short|Byte.compareTo(Long)") to compareToIntrinsic(::longFromInt, identity()),
pattern("Long.compareTo(Int|Short|Byte)") to compareToIntrinsic(identity(), ::longFromInt),
pattern("Char.compareTo(Long)") to compareToIntrinsic({ longFromInt(charToInt(it)) }, identity()),
pattern("Long.compareTo(Char)") to compareToIntrinsic(identity(), { longFromInt(JsAstUtils.charToInt(it)) }),
pattern("Long.compareTo(Long)") to compareToIntrinsic(identity(), identity())
)
override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? { override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? {
if (descriptor.isDynamic()) return CompareToIntrinsic if (descriptor.isDynamic()) return primitiveIntrinsic(identity(), identity())
if (leftType == null || rightType == null) return null if (leftType == null || rightType == null || !KotlinBuiltIns.isBuiltIn(descriptor)) return null
if (!KotlinBuiltIns.isBuiltIn(descriptor)) return null patterns.forEach { (p, i) -> if (p.test(descriptor)) return i }
// Types may be nullable if properIeeeComparisons are switched off, e.g. fun foo(a: Double?) = a != null && a < 0.0 // Types may be nullable if properIeeeComparisons are switched off, e.g. fun foo(a: Double?) = a != null && a < 0.0
return when { return if (isPrimitiveTypeOrNullablePrimitiveType(leftType) && isPrimitiveTypeOrNullablePrimitiveType(rightType)) {
KotlinBuiltIns.isCharOrNullableChar(rightType) -> CompareToCharIntrinsic primitiveIntrinsic(unboxCharIfNeeded(leftType), unboxCharIfNeeded(rightType))
KotlinBuiltIns.isCharOrNullableChar(leftType) -> CompareCharToPrimitiveIntrinsic } else {
KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(leftType) && intrinsic({ l, r -> compareTo(l, r) }, { _, _ -> JsIntLiteral(0) })
KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(rightType) -> CompareToIntrinsic
else -> CompareToFunctionIntrinsic
} }
} }
} }
@@ -40,88 +40,88 @@ import org.jetbrains.kotlin.types.typeUtil.makeNullable
import java.util.* import java.util.*
object EqualsBOIF : BinaryOperationIntrinsicFactory { object EqualsBOIF : BinaryOperationIntrinsicFactory {
private object EqualsIntrinsic : BinaryOperationIntrinsic {
private val JS_NUMBER_PRIMITIVES = private val JS_NUMBER_PRIMITIVES =
EnumSet.of(PrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.INT, PrimitiveType.DOUBLE, PrimitiveType.FLOAT) EnumSet.of(PrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.INT, PrimitiveType.DOUBLE, PrimitiveType.FLOAT)
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression { private fun equalsIntrinsic(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val isNegated = expression.isNegated() val isNegated = expression.isNegated()
val anyType = context.currentModule.builtIns.anyType val anyType = context.currentModule.builtIns.anyType
if (right is JsNullLiteral || left is JsNullLiteral) { if (right is JsNullLiteral || left is JsNullLiteral) {
val (subject, ktSubject) = if (right is JsNullLiteral) Pair(left, expression.left!!) else Pair(right, expression.right!!) val (subject, ktSubject) = if (right is JsNullLiteral) Pair(left, expression.left!!) else Pair(right, expression.right!!)
val type = context.bindingContext().getType(ktSubject) ?: anyType val type = context.bindingContext().getType(ktSubject) ?: anyType
val coercedSubject = TranslationUtils.coerce(context, subject, type.makeNullable()) val coercedSubject = TranslationUtils.coerce(context, subject, type.makeNullable())
return TranslationUtils.nullCheck(coercedSubject, isNegated) return TranslationUtils.nullCheck(coercedSubject, isNegated)
}
val (leftKotlinType, rightKotlinType) = binaryOperationTypes(expression, context)
val leftType = leftKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) }
val rightType = rightKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) }
if (leftType != null && rightType != null && (
leftType in JS_NUMBER_PRIMITIVES && rightType in JS_NUMBER_PRIMITIVES ||
leftType in JS_NUMBER_PRIMITIVES && rightType == PrimitiveType.LONG ||
leftType == PrimitiveType.LONG && rightType in JS_NUMBER_PRIMITIVES ||
leftType == PrimitiveType.BOOLEAN && rightType == PrimitiveType.BOOLEAN ||
leftType == PrimitiveType.CHAR && rightType == PrimitiveType.CHAR
)) {
val useEq = leftType == PrimitiveType.LONG || rightType == PrimitiveType.LONG
val operator = when {
useEq && isNegated -> JsBinaryOperator.NEQ
useEq && !isNegated -> JsBinaryOperator.EQ
!useEq && isNegated -> JsBinaryOperator.REF_NEQ
else /* !useEq && !isNegated */ -> JsBinaryOperator.REF_EQ
}
val coercedLeft = TranslationUtils.coerce(context, left, leftKotlinType)
val coercedRight = TranslationUtils.coerce(context, right, rightKotlinType)
return JsBinaryOperation(operator, coercedLeft, coercedRight)
}
val resolvedCall = expression.getResolvedCall(context.bindingContext())
val appliedToDynamic =
resolvedCall != null &&
with(resolvedCall.dispatchReceiver) {
if (this != null) type.isDynamic() else false
}
if (appliedToDynamic) {
return JsBinaryOperation(if (isNegated) JsBinaryOperator.NEQ else JsBinaryOperator.EQ, left, right)
}
val coercedLeft = TranslationUtils.coerce(context, left, anyType)
val coercedRight = TranslationUtils.coerce(context, right, anyType)
val result = TopLevelFIF.KOTLIN_EQUALS.apply(coercedLeft, listOf(coercedRight), context)
return if (isNegated) JsAstUtils.not(result) else result
} }
}
object EnumEqualsIntrinsic : BinaryOperationIntrinsic { val (leftKotlinType, rightKotlinType) = binaryOperationTypes(expression, context)
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsBinaryOperation {
val operator = if (expression.isNegated()) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ val leftType = leftKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) }
return JsBinaryOperation(operator, left, right) val rightType = rightKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) }
if (leftType != null && rightType != null) {
} }
if (leftType != null && rightType != null && (
leftType in JS_NUMBER_PRIMITIVES && rightType in JS_NUMBER_PRIMITIVES ||
leftType in JS_NUMBER_PRIMITIVES && rightType == PrimitiveType.LONG ||
leftType == PrimitiveType.LONG && rightType in JS_NUMBER_PRIMITIVES ||
leftType == PrimitiveType.BOOLEAN && rightType == PrimitiveType.BOOLEAN ||
leftType == PrimitiveType.CHAR && rightType == PrimitiveType.CHAR
)) {
val useEq = leftType == PrimitiveType.LONG || rightType == PrimitiveType.LONG
val operator = when {
useEq && isNegated -> JsBinaryOperator.NEQ
useEq && !isNegated -> JsBinaryOperator.EQ
!useEq && isNegated -> JsBinaryOperator.REF_NEQ
else /* !useEq && !isNegated */ -> JsBinaryOperator.REF_EQ
}
val coercedLeft = TranslationUtils.coerce(context, left, leftKotlinType)
val coercedRight = TranslationUtils.coerce(context, right, rightKotlinType)
return JsBinaryOperation(operator, coercedLeft, coercedRight)
}
val resolvedCall = expression.getResolvedCall(context.bindingContext())
val appliedToDynamic =
resolvedCall != null &&
with(resolvedCall.dispatchReceiver) {
if (this != null) type.isDynamic() else false
}
if (appliedToDynamic) {
return JsBinaryOperation(if (isNegated) JsBinaryOperator.NEQ else JsBinaryOperator.EQ, left, right)
}
val coercedLeft = TranslationUtils.coerce(context, left, anyType)
val coercedRight = TranslationUtils.coerce(context, right, anyType)
val result = TopLevelFIF.KOTLIN_EQUALS.apply(coercedLeft, listOf(coercedRight), context)
return if (isNegated) JsAstUtils.not(result) else result
} }
override fun getSupportTokens() = OperatorConventions.EQUALS_OPERATIONS!! override fun getSupportTokens() = OperatorConventions.EQUALS_OPERATIONS!!
override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? = override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? =
when { when {
isEnumIntrinsicApplicable(descriptor, leftType, rightType) -> EnumEqualsIntrinsic isEnumEqualsIntrinsicApplicable(descriptor, leftType, rightType) -> { expression, left, right, _ ->
val operator = if (expression.isNegated()) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ
JsBinaryOperation(operator, left, right)
}
KotlinBuiltIns.isBuiltIn(descriptor) || KotlinBuiltIns.isBuiltIn(descriptor) ||
TopLevelFIF.EQUALS_IN_ANY.test(descriptor) -> EqualsIntrinsic TopLevelFIF.EQUALS_IN_ANY.test(descriptor) -> ::equalsIntrinsic
else -> null else -> null
} }
private fun isEnumIntrinsicApplicable(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): Boolean { private fun isEnumEqualsIntrinsicApplicable(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): Boolean {
return DescriptorUtils.isEnumClass(descriptor.containingDeclaration) && leftType != null && rightType != null && return DescriptorUtils.isEnumClass(descriptor.containingDeclaration) && leftType != null && rightType != null &&
!TypeUtils.isNullableType(leftType) && !TypeUtils.isNullableType(rightType) !TypeUtils.isNullableType(leftType) && !TypeUtils.isNullableType(rightType)
} }
private fun KtBinaryExpression.isNegated() = getOperationToken(this) == KtTokens.EXCLEQ private fun KtBinaryExpression.isNegated() = getOperationToken(this) == KtTokens.EXCLEQ
} }
@@ -1,90 +0,0 @@
/*
* 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.js.translate.intrinsic.operation
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation
import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsIntLiteral
import org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.operation.OperatorTable
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.utils.identity as ID
object LongCompareToBOIF : BinaryOperationIntrinsicFactory {
val FLOATING_POINT_COMPARE_TO_LONG_PATTERN = pattern("Double|Float.compareTo(Long)")
val LONG_COMPARE_TO_FLOATING_POINT_PATTERN = pattern("Long.compareTo(Float|Double)")
val INTEGER_COMPARE_TO_LONG_PATTERN = pattern("Int|Short|Byte.compareTo(Long)")
val CHAR_COMPARE_TO_LONG_PATTERN = pattern("Char.compareTo(Long)")
val LONG_COMPARE_TO_INTEGER_PATTERN = pattern("Long.compareTo(Int|Short|Byte)")
val LONG_COMPARE_TO_CHAR_PATTERN = pattern("Long.compareTo(Char)")
val LONG_COMPARE_TO_LONG_PATTERN = pattern("Long.compareTo(Long)")
private object FLOATING_POINT_COMPARE_TO_LONG : BinaryOperationIntrinsic {
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
return JsBinaryOperation(operator, left, invokeMethod(right, Namer.LONG_TO_NUMBER))
}
}
private object LONG_COMPARE_TO_FLOATING_POINT : BinaryOperationIntrinsic {
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
return JsBinaryOperation(operator, invokeMethod(left, Namer.LONG_TO_NUMBER), right)
}
}
private class CompareToBinaryIntrinsic(val toLeft: (JsExpression) -> JsExpression, val toRight: (JsExpression) -> JsExpression) : BinaryOperationIntrinsic {
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
val compareInvocation = compareForObject(toLeft(left), toRight(right))
return JsBinaryOperation(operator, compareInvocation, JsIntLiteral(0))
}
}
private val INTEGER_COMPARE_TO_LONG = CompareToBinaryIntrinsic(::longFromInt, ID())
private val CHAR_COMPARE_TO_LONG = CompareToBinaryIntrinsic( { longFromInt(charToInt(it)) }, ID())
private val LONG_COMPARE_TO_INTEGER = CompareToBinaryIntrinsic(ID(), ::longFromInt)
private val LONG_COMPARE_TO_CHAR = CompareToBinaryIntrinsic( ID(), { longFromInt(charToInt(it)) })
private val LONG_COMPARE_TO_LONG = CompareToBinaryIntrinsic( ID(), ID() )
override fun getSupportTokens() = OperatorConventions.COMPARISON_OPERATIONS
override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? {
if (KotlinBuiltIns.isBuiltIn(descriptor)) {
return when {
FLOATING_POINT_COMPARE_TO_LONG_PATTERN.test(descriptor) -> FLOATING_POINT_COMPARE_TO_LONG
LONG_COMPARE_TO_FLOATING_POINT_PATTERN.test(descriptor) -> LONG_COMPARE_TO_FLOATING_POINT
INTEGER_COMPARE_TO_LONG_PATTERN.test(descriptor) -> INTEGER_COMPARE_TO_LONG
CHAR_COMPARE_TO_LONG_PATTERN.test(descriptor) -> CHAR_COMPARE_TO_LONG
LONG_COMPARE_TO_INTEGER_PATTERN.test(descriptor) -> LONG_COMPARE_TO_INTEGER
LONG_COMPARE_TO_CHAR_PATTERN.test(descriptor) -> LONG_COMPARE_TO_CHAR
LONG_COMPARE_TO_LONG_PATTERN.test(descriptor) -> LONG_COMPARE_TO_LONG
else -> null
}
}
return null
}
}
@@ -28,9 +28,8 @@ import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
interface BinaryOperationIntrinsic { typealias BinaryOperationIntrinsic
fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression = (expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext) -> JsExpression
}
class BinaryOperationIntrinsics { class BinaryOperationIntrinsics {
@@ -53,7 +52,7 @@ class BinaryOperationIntrinsics {
return computeAndCache(IntrinsicKey(token, descriptor, leftType, rightType)) return computeAndCache(IntrinsicKey(token, descriptor, leftType, rightType))
} }
private val factories = listOf(LongCompareToBOIF, EqualsBOIF, CompareToBOIF, AssignmentBOIF) private val factories = listOf(CompareToBOIF, EqualsBOIF, AssignmentBOIF)
private fun computeAndCache(key: IntrinsicKey): BinaryOperationIntrinsic? { private fun computeAndCache(key: IntrinsicKey): BinaryOperationIntrinsic? {
if (key in intrinsicCache) return intrinsicCache[key] if (key in intrinsicCache) return intrinsicCache[key]
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.js.translate.operation; package org.jetbrains.kotlin.js.translate.operation;
import kotlin.jvm.functions.Function4;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.CallableDescriptor;
@@ -27,7 +28,6 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF; import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF;
import org.jetbrains.kotlin.js.translate.intrinsic.operation.BinaryOperationIntrinsic;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.lexer.KtToken; import org.jetbrains.kotlin.lexer.KtToken;
@@ -100,21 +100,21 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression translate() { private JsExpression translate() {
BinaryOperationIntrinsic intrinsic = getIntrinsicForExpression(); JsExpression e = tryApplyIntrinsic();
if (intrinsic != null) { if (e != null) {
return applyIntrinsic(intrinsic); return e;
} }
if (operationToken == KtTokens.ELVIS) { if (operationToken == KtTokens.ELVIS) {
return translateElvis(); return translateElvis();
} }
if (isAssignmentOperator(operationToken)) { if (isAssignmentOperator(operationToken)) {
return AssignmentTranslator.translate(expression, context()); return AssignmentTranslator.translate(this.expression, context());
} }
if (isNotOverloadable()) { if (isNotOverloadable()) {
return translateAsUnOverloadableBinaryOperation(); return translateAsUnOverloadableBinaryOperation();
} }
if (isCompareToCall(operationToken, operationDescriptor)) { if (isCompareToCall(operationToken, operationDescriptor)) {
return CompareToTranslator.translate(expression, context()); return CompareToTranslator.translate(this.expression, context());
} }
if (isEquals()) { if (isEquals()) {
return translateEquals(); return translateEquals();
@@ -159,25 +159,25 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
} }
@Nullable @Nullable
private BinaryOperationIntrinsic getIntrinsicForExpression() { private JsExpression tryApplyIntrinsic() {
return context().intrinsics().getBinaryOperationIntrinsic(expression, context()); Function4<KtBinaryExpression, JsExpression, JsExpression, TranslationContext, JsExpression> intrinsic =
} context().intrinsics().getBinaryOperationIntrinsic(expression, context());
if (intrinsic == null) return null;
@NotNull
private JsExpression applyIntrinsic(@NotNull BinaryOperationIntrinsic intrinsic) {
JsExpression leftExpression = Translation.translateAsExpression(leftKtExpression, context()); JsExpression leftExpression = Translation.translateAsExpression(leftKtExpression, context());
JsBlock rightBlock = new JsBlock(); JsBlock rightBlock = new JsBlock();
JsExpression rightExpression = Translation.translateAsExpression(rightKtExpression, context(), rightBlock); JsExpression rightExpression = Translation.translateAsExpression(rightKtExpression, context(), rightBlock);
if (rightBlock.isEmpty()) { if (rightBlock.isEmpty()) {
return intrinsic.apply(expression, leftExpression, rightExpression, context()); return intrinsic.invoke(expression, leftExpression, rightExpression, context());
} }
leftExpression = context().cacheExpressionIfNeeded(leftExpression); leftExpression = context().cacheExpressionIfNeeded(leftExpression);
context().addStatementsToCurrentBlockFrom(rightBlock); context().addStatementsToCurrentBlockFrom(rightBlock);
return intrinsic.apply(expression, leftExpression, rightExpression, context()); return intrinsic.invoke(expression, leftExpression, rightExpression, context());
} }
private boolean isNotOverloadable() { private boolean isNotOverloadable() {
@@ -212,6 +212,11 @@ public final class JsAstUtils {
return invokeMethod(Namer.kotlinLong(), Namer.LONG_FROM_NUMBER, expression); return invokeMethod(Namer.kotlinLong(), Namer.LONG_FROM_NUMBER, expression);
} }
@NotNull
public static JsExpression longToNumber(@NotNull JsExpression expression) {
return invokeMethod(expression, Namer.LONG_TO_NUMBER);
}
@NotNull @NotNull
public static JsExpression compareForObject(@NotNull JsExpression left, @NotNull JsExpression right) { public static JsExpression compareForObject(@NotNull JsExpression left, @NotNull JsExpression right) {
return invokeMethod(left, Namer.COMPARE_TO_METHOD_NAME, right); return invokeMethod(left, Namer.COMPARE_TO_METHOD_NAME, right);