/*
* Copyright 2010-2015 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.js.backend.ast.JsExpression
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation
import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.operation.OperatorTable
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.js.translate.utils.getPrecisePrimitiveType
import org.jetbrains.kotlin.js.translate.utils.getPrimitiveNumericComparisonInfo
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
class BinaryOperationIntrinsics {
private data class IntrinsicKey(
val token: KtToken,
val function: FunctionDescriptor,
val leftType: KotlinType?,
val rightType: KotlinType?
)
private val intrinsicCache = mutableMapOf<IntrinsicKey, BinaryOperationIntrinsic?>()
fun getIntrinsic(expression: KtBinaryExpression, context: TranslationContext): BinaryOperationIntrinsic? {
val descriptor =
getCallableDescriptorForOperationExpression(context.bindingContext(), expression) as? FunctionDescriptor ?: return null
val (leftType, rightType) = binaryOperationTypes(expression, context)
val token = getOperationToken(expression)
return computeAndCache(IntrinsicKey(token, descriptor, leftType, rightType))
}
private val factories = listOf(CompareToBOIF, EqualsBOIF)
private fun computeAndCache(key: IntrinsicKey): BinaryOperationIntrinsic? {
if (key in intrinsicCache) return intrinsicCache[key]
val result = factories.firstNotNullResult { factory ->
if (factory.getSupportTokens().contains(key.token)) {
factory.getIntrinsic(key.function, key.leftType, key.rightType)
} else null
}
intrinsicCache[key] = result
return result
}
}
// Takes into account smart-casts (needed for IEEE 754 comparisons)
fun binaryOperationTypes(expression: KtBinaryExpression, context: TranslationContext): Pair<KotlinType?, KotlinType?> {
val info = context.getPrimitiveNumericComparisonInfo(expression)
if (info != null) {
return info.leftType to info.rightType
}
return expression.left?.let { context.getPrecisePrimitiveType(it) } to expression.right?.let { context.getPrecisePrimitiveType(it) }
}
interface BinaryOperationIntrinsicFactory {
fun getSupportTokens(): Set<KtToken>
fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic?
}
typealias OperatorSelector = (KtBinaryExpression) -> JsBinaryOperator
val defaultOperatorSelector: OperatorSelector = { OperatorTable.getBinaryOperator(getOperationToken(it)) }
// toLeft(L, R) OP toRight(L, R)
fun complexBinaryIntrinsic(
toLeft: (JsExpression, JsExpression, TranslationContext) -> JsExpression,
toRight: (JsExpression, JsExpression, TranslationContext) -> JsExpression,
operator: (KtBinaryExpression) -> JsBinaryOperator = defaultOperatorSelector
) = BinaryOperationIntrinsic { expression, left, right, context ->
JsBinaryOperation(operator(expression), toLeft(left, right, context), toRight(left, right, context))
}
// toLeft(L, C) OP toRight(R, C)
fun binaryIntrinsic(
toLeft: (JsExpression, TranslationContext) -> JsExpression = { l, _ -> l },
toRight: (JsExpression, TranslationContext) -> JsExpression = { r, _ -> r },
operator: OperatorSelector = defaultOperatorSelector
) = complexBinaryIntrinsic({ l, _, c -> toLeft(l, c) }, { _, r, c -> toRight(r, c) }, operator)
fun coerceTo(type: KotlinType): (JsExpression, TranslationContext) -> JsExpression =
{ e, c ->
TranslationUtils.coerce(c, e, type)
}