BinaryOperationIntrinsic as typealias
This commit is contained in:
committed by
Anton Bannykh
parent
2352dc3d1c
commit
757649d6da
+4
-9
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-40
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+64
-64
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
-90
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-4
@@ -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]
|
||||||
|
|||||||
+13
-13
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user