From 7e30b9e7f5686bb1d58dac535c657724b63a440f Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Tue, 29 Jan 2019 16:20:33 +0300 Subject: [PATCH] New J2K: Add support of binary expressions conversion to ImplicitCastsConversion --- .../conversions/ImplicitCastsConversion.kt | 77 ++++++++++++++++--- .../org/jetbrains/kotlin/j2k/expressions.kt | 58 ++++++++++---- .../org/jetbrains/kotlin/j2k/tree/types.kt | 16 ++++ 3 files changed, 129 insertions(+), 22 deletions(-) diff --git a/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/ImplicitCastsConversion.kt b/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/ImplicitCastsConversion.kt index a56e97000bf..a4bd8b0ad23 100644 --- a/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/ImplicitCastsConversion.kt +++ b/j2k/newSrc/org/jetbrains/kotlin/j2k/conversions/ImplicitCastsConversion.kt @@ -5,13 +5,11 @@ package org.jetbrains.kotlin.j2k.conversions -import org.jetbrains.kotlin.j2k.ConversionContext +import org.jetbrains.kotlin.j2k.* import org.jetbrains.kotlin.j2k.ast.Nullability -import org.jetbrains.kotlin.j2k.bangedBangedExpr -import org.jetbrains.kotlin.j2k.copyTreeAndDetach -import org.jetbrains.kotlin.j2k.fixLiteral import org.jetbrains.kotlin.j2k.tree.* import org.jetbrains.kotlin.j2k.tree.impl.* +import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType class ImplicitCastsConversion(private val context: ConversionContext) : RecursiveApplicableConversionBase() { override fun applyToElement(element: JKTreeElement): JKTreeElement { @@ -19,11 +17,71 @@ class ImplicitCastsConversion(private val context: ConversionContext) : Recursiv is JKVariable -> convertVariable(element) is JKForInStatement -> convertForInStatement(element) is JKMethodCallExpression -> convertMethodCallExpression(element) + is JKBinaryExpression -> return recurse(convertBinaryExpression(element)) is JKKtAssignmentStatement -> convertAssignmentStatement(element) } return recurse(element) } + + private fun convertBinaryExpression(binaryExpression: JKBinaryExpression): JKExpression { + fun JKBinaryExpression.addBangBang(): JKBinaryExpression { + fun JKType.isAcceptable() = asPrimitiveType() != null + + if (operator.isEquals()) return this + + val leftType = left.type(context.symbolProvider) ?: return this + val rightType = right.type(context.symbolProvider) ?: return this + return if (leftType.isAcceptable() + && rightType.isAcceptable() + && (leftType.isNullable() || rightType.isNullable()) + && (operator.isArithmetic() || operator.isLessOrGreater()) + ) { + JKBinaryExpressionImpl( + ::left.detached().bangedBangedExpr(context.symbolProvider), + ::right.detached().bangedBangedExpr(context.symbolProvider), + operator + ) + } else this + } + + fun JKBinaryExpression.convertComparationExpression(): JKBinaryExpression { + if (!operator.isComparationOperator()) return this + val leftType = left.type(context.symbolProvider)?.asPrimitiveType() ?: return this + val rightType = right.type(context.symbolProvider)?.asPrimitiveType() ?: return this + + val leftOperandCastedCasted by lazy { + JKBinaryExpressionImpl( + ::left.detached().let { it.castTo(rightType, strict = true) ?: it }, + ::right.detached(), + operator + ) + } + + val rightOperandCastedCasted by lazy { + JKBinaryExpressionImpl( + ::left.detached(), + ::right.detached().let { it.castTo(leftType, strict = true) ?: it }, + operator + + ) + } + println() + + return when { + leftType.jvmPrimitiveType == rightType.jvmPrimitiveType -> this + leftType.jvmPrimitiveType == JvmPrimitiveType.CHAR -> leftOperandCastedCasted + rightType.jvmPrimitiveType == JvmPrimitiveType.CHAR -> rightOperandCastedCasted + operator.isEquals() -> + if (rightType isStrongerThan leftType) leftOperandCastedCasted + else rightOperandCastedCasted + else -> this + } + } + + return binaryExpression.convertComparationExpression().addBangBang() + } + private fun convertVariable(variable: JKVariable) { if (variable.initializer is JKStubExpression) return variable.initializer.castTo(variable.type.type)?.also { @@ -78,11 +136,11 @@ class ImplicitCastsConversion(private val context: ConversionContext) : Recursiv return null } - private fun JKExpression.castToAsPrimitiveTypes(toType: JKType): JKExpression? { + private fun JKExpression.castToAsPrimitiveTypes(toType: JKType, strict: Boolean): JKExpression? { if (this is JKPrefixExpression && (operator.token.text == "+" || operator.token.text == "-") ) { - val casted = expression.castToAsPrimitiveTypes(toType) ?: return null + val casted = expression.castToAsPrimitiveTypes(toType, strict) ?: return null return JKPrefixExpressionImpl(casted, operator) } val expressionTypeAsPrimitive = type(context.symbolProvider)?.asPrimitiveType() ?: return null @@ -90,7 +148,8 @@ class ImplicitCastsConversion(private val context: ConversionContext) : Recursiv if (toTypeAsPrimitive == expressionTypeAsPrimitive) return null if (this is JKLiteralExpression) { - if (expressionTypeAsPrimitive == JKJavaPrimitiveTypeImpl.INT + if (!strict + && expressionTypeAsPrimitive == JKJavaPrimitiveTypeImpl.INT && (toTypeAsPrimitive == JKJavaPrimitiveTypeImpl.LONG || toTypeAsPrimitive == JKJavaPrimitiveTypeImpl.SHORT || toTypeAsPrimitive == JKJavaPrimitiveTypeImpl.BYTE) @@ -118,10 +177,10 @@ class ImplicitCastsConversion(private val context: ConversionContext) : Recursiv } - private fun JKExpression.castTo(toType: JKType): JKExpression? { + private fun JKExpression.castTo(toType: JKType, strict: Boolean = false): JKExpression? { val expressionType = type(context.symbolProvider) if (expressionType == toType) return null - castToAsPrimitiveTypes(toType)?.also { return it } + castToAsPrimitiveTypes(toType, strict)?.also { return it } return addBangBang(toType) } } \ No newline at end of file diff --git a/j2k/newSrc/org/jetbrains/kotlin/j2k/expressions.kt b/j2k/newSrc/org/jetbrains/kotlin/j2k/expressions.kt index 398505df778..a6a21374d89 100644 --- a/j2k/newSrc/org/jetbrains/kotlin/j2k/expressions.kt +++ b/j2k/newSrc/org/jetbrains/kotlin/j2k/expressions.kt @@ -110,18 +110,48 @@ private fun JKKtOperatorToken.unaryExpressionMethodType( } } -private val booleanOperators = TokenSet.create( - KtTokens.LT, - KtTokens.GT, - KtTokens.LTEQ, - KtTokens.GTEQ, - KtTokens.EQEQEQ, - KtTokens.EXCLEQEQEQ, - KtTokens.EQEQ, - KtTokens.EXCLEQ, - KtTokens.ANDAND, - KtTokens.OROR -) +fun JKOperator.isComparationOperator() = + (token as? JKKtSingleValueOperatorToken)?.psiToken in comparationOperators + +fun JKOperator.isEquals() = + (token as? JKKtSingleValueOperatorToken)?.psiToken in equalsOperators + +fun JKOperator.isArithmetic() = + (token as? JKKtSingleValueOperatorToken)?.psiToken in arithmeticOperators + +fun JKOperator.isLessOrGreater() = + (token as? JKKtSingleValueOperatorToken)?.psiToken in lessGreaterOperators + +private val equalsOperators = + TokenSet.create( + KtTokens.EQEQEQ, + KtTokens.EXCLEQEQEQ, + KtTokens.EQEQ, + KtTokens.EXCLEQ + ) + +private val lessGreaterOperators = + TokenSet.create( + KtTokens.LT, + KtTokens.GT, + KtTokens.LTEQ, + KtTokens.GTEQ + ) + +private val comparationOperators = + TokenSet.orSet( + lessGreaterOperators, + equalsOperators + ) + +private val booleanOperators = + TokenSet.orSet( + comparationOperators, + TokenSet.create( + KtTokens.ANDAND, + KtTokens.OROR + ) + ) private val arithmeticOperators = TokenSet.create( KtTokens.MUL, @@ -429,7 +459,9 @@ private fun convertCharLiteral(text: String): JKKtLiteralExpression { private fun convertIntegerLiteral(element: JKLiteralExpression): JKKtLiteralExpression { var text = element.literal if (element.type == JKLiteralExpression.LiteralType.LONG) { - text = text.replace("l", "L") + text = text.replace("l", "L").let { + if (!it.endsWith("L")) it + "L" else it + } } fun isHexLiteral(text: String) = text.startsWith("0x") || text.startsWith("0X") diff --git a/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/types.kt b/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/types.kt index 3d392f1c179..a075c319641 100644 --- a/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/types.kt +++ b/j2k/newSrc/org/jetbrains/kotlin/j2k/tree/types.kt @@ -155,6 +155,22 @@ fun JKType.toKtType(symbolProvider: JKSymbolProvider): KotlinType? = // else -> TODO(this::class.java.toString()) } +infix fun JKJavaPrimitiveType.isStrongerThan(other: JKJavaPrimitiveType) = + jvmPrimitivePrioritypriority.getValue(this.jvmPrimitiveType.primitiveType) > + jvmPrimitivePrioritypriority.getValue(other.jvmPrimitiveType.primitiveType) + +private val jvmPrimitivePrioritypriority = + mapOf( + PrimitiveType.BOOLEAN to -1, + PrimitiveType.CHAR to 0, + PrimitiveType.BYTE to 1, + PrimitiveType.SHORT to 2, + PrimitiveType.INT to 3, + PrimitiveType.LONG to 4, + PrimitiveType.FLOAT to 5, + PrimitiveType.DOUBLE to 6 + ) + fun JKClassSymbol.toKtType(): KotlinType? { val classDescriptor = when (this) {