Desugar basic binary operators (+, -, *, /, %, ..).
Fold String.plus calls to IrStringConcatenationExpression.
This commit is contained in:
committed by
Dmitry Petrov
parent
287d804131
commit
1b018a6ead
@@ -22,12 +22,14 @@ import org.jetbrains.kotlin.ir.declarations.IrModule
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi2ir.generators.IrGeneratorContext
|
||||
import org.jetbrains.kotlin.psi2ir.generators.IrModuleGenerator
|
||||
import org.jetbrains.kotlin.psi2ir.transformations.collapseDesugaredBlocks
|
||||
import org.jetbrains.kotlin.psi2ir.transformations.foldStringConcatenation
|
||||
import org.jetbrains.kotlin.psi2ir.transformations.inlineDesugaredBlocks
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
|
||||
class Psi2IrTranslator(val configuration: Configuration = Configuration()) {
|
||||
class Configuration(
|
||||
val shouldCollapseDesugaredBlocks: Boolean = true
|
||||
val shouldInlineDesugaredBlocks: Boolean = true,
|
||||
val shouldFoldStringConcatenation: Boolean = true
|
||||
)
|
||||
|
||||
fun generateModule(moduleDescriptor: ModuleDescriptor, ktFiles: List<KtFile>, bindingContext: BindingContext): IrModule {
|
||||
@@ -38,6 +40,7 @@ class Psi2IrTranslator(val configuration: Configuration = Configuration()) {
|
||||
}
|
||||
|
||||
private fun postprocess(irElement: IrElement) {
|
||||
if (configuration.shouldCollapseDesugaredBlocks) collapseDesugaredBlocks(irElement)
|
||||
if (configuration.shouldInlineDesugaredBlocks) inlineDesugaredBlocks(irElement)
|
||||
if (configuration.shouldFoldStringConcatenation) foldStringConcatenation(irElement)
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -40,7 +40,9 @@ val KT_OPERATOR_TO_IR_OPERATOR = hashMapOf(
|
||||
KtTokens.PLUS to IrOperator.PLUS,
|
||||
KtTokens.MINUS to IrOperator.MINUS,
|
||||
KtTokens.MUL to IrOperator.MUL,
|
||||
KtTokens.DIV to IrOperator.DIV
|
||||
KtTokens.DIV to IrOperator.DIV,
|
||||
KtTokens.PERC to IrOperator.PERC,
|
||||
KtTokens.RANGE to IrOperator.RANGE
|
||||
)
|
||||
|
||||
val AUGMENTED_ASSIGNMENTS = KtTokens.AUGMENTED_ASSIGNMENTS
|
||||
@@ -55,10 +57,17 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
|
||||
return when (ktOperator) {
|
||||
KtTokens.EQ -> generateAssignment(expression)
|
||||
in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, ktOperator)
|
||||
in BINARY_OPERATORS_WITH_CALLS -> generateBinaryOperatorWithConventionalCall(expression, ktOperator)
|
||||
else -> createDummyExpression(expression, ktOperator.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateBinaryOperatorWithConventionalCall(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression {
|
||||
val irOperator = getIrOperator(ktOperator)
|
||||
val operatorCall = getResolvedCall(expression)!!
|
||||
return IrCallGenerator(irStatementGenerator).generateCall(expression, operatorCall, irOperator)
|
||||
}
|
||||
|
||||
private fun generateAugmentedAssignment(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression {
|
||||
val ktLeft = expression.left!!
|
||||
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.psi2ir.transformations
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.detach
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCallExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStringConcatenationExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStringConcatenationExpressionImpl
|
||||
import org.jetbrains.kotlin.ir.replaceWith
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import java.util.*
|
||||
|
||||
fun foldStringConcatenation(element: IrElement) {
|
||||
element.accept(FoldStringConcatenation(), null)
|
||||
}
|
||||
|
||||
class FoldStringConcatenation : IrElementVisitor<Unit, Nothing?> {
|
||||
override fun visitElement(element: IrElement, data: Nothing?) {
|
||||
element.acceptChildren(this, data)
|
||||
}
|
||||
|
||||
override fun visitCallExpression(expression: IrCallExpression, data: Nothing?) {
|
||||
if (!isStringPlus(expression.descriptor)) {
|
||||
visitElement(expression, data)
|
||||
return
|
||||
}
|
||||
|
||||
val arguments = ArrayList<IrExpression>()
|
||||
collectStringConcatenationArguments(expression, arguments)
|
||||
val irStringConcatenation = IrStringConcatenationExpressionImpl(expression.startOffset, expression.endOffset, expression.type)
|
||||
arguments.forEach { irStringConcatenation.addArgument(it) }
|
||||
|
||||
expression.replaceWith(irStringConcatenation)
|
||||
}
|
||||
|
||||
private fun collectStringConcatenationArguments(expression: IrExpression, arguments: ArrayList<IrExpression>) {
|
||||
when {
|
||||
expression is IrCallExpression && isStringPlus(expression.descriptor)-> {
|
||||
collectStringConcatenationArguments(expression.dispatchReceiver!!, arguments)
|
||||
collectStringConcatenationArguments(expression.getArgument(0)!!, arguments)
|
||||
}
|
||||
expression is IrStringConcatenationExpression -> {
|
||||
arguments.addAll(expression.arguments)
|
||||
expression.arguments.forEach { it.detach() }
|
||||
}
|
||||
else -> {
|
||||
arguments.add(expression)
|
||||
expression.detach()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isStringPlus(descriptor: CallableDescriptor): Boolean {
|
||||
if (descriptor.name != OperatorNameConventions.PLUS) return false
|
||||
val dispatchReceiverType = descriptor.dispatchReceiverParameter?.type ?: return false
|
||||
if (!KotlinBuiltIns.isString(dispatchReceiverType)) return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -23,11 +23,11 @@ import org.jetbrains.kotlin.ir.expressions.IrBlockExpressionImpl
|
||||
import org.jetbrains.kotlin.ir.replaceWith
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
|
||||
fun collapseDesugaredBlocks(element: IrElement) {
|
||||
element.accept(CollapseDesugaredBlocks(), null)
|
||||
fun inlineDesugaredBlocks(element: IrElement) {
|
||||
element.accept(InlineDesugaredBlocks(), null)
|
||||
}
|
||||
|
||||
class CollapseDesugaredBlocks : IrElementVisitor<Unit, Nothing?> {
|
||||
class InlineDesugaredBlocks : IrElementVisitor<Unit, Nothing?> {
|
||||
override fun visitElement(element: IrElement, data: Nothing?) {
|
||||
element.acceptChildren(this, data)
|
||||
}
|
||||
@@ -48,7 +48,7 @@ sealed class IrOperator(val debugName: String) {
|
||||
object MINUS : IrOperator("MINUS")
|
||||
object MUL : IrOperator("MUL")
|
||||
object DIV : IrOperator("DIV")
|
||||
object MOD : IrOperator("MOD")
|
||||
object PERC : IrOperator("PERC")
|
||||
|
||||
object EQ : IrOperator("EQ")
|
||||
object PLUSEQ : IrOperator("PLUSEQ")
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ class IrStringConcatenationExpressionImpl(
|
||||
}
|
||||
|
||||
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
|
||||
visitor.visitStringTemplate(this, data)
|
||||
visitor.visitStringConcatenation(this, data)
|
||||
|
||||
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
|
||||
arguments.forEach { it.accept(visitor, data) }
|
||||
|
||||
@@ -99,6 +99,9 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
|
||||
"SET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name}" +
|
||||
"type=${expression.renderType()}"
|
||||
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenationExpression, data: Nothing?): String =
|
||||
"STRING_CONCATENATION type=${expression.renderType()}"
|
||||
|
||||
override fun visitTypeOperatorExpression(expression: IrTypeOperatorExpression, data: Nothing?): String {
|
||||
return "TYPE_OP operator=${expression.operator} typeOperand=${expression.typeOperand.render()}"
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ interface IrElementVisitor<out R, in D> {
|
||||
fun <T> visitLiteral(expression: IrLiteralExpression<T>, data: D): R = visitExpression(expression, data)
|
||||
fun visitReturnExpression(expression: IrReturnExpression, data: D): R = visitExpression(expression, data)
|
||||
fun visitBlockExpression(expression: IrBlockExpression, data: D): R = visitExpression(expression, data)
|
||||
fun visitStringTemplate(expression: IrStringConcatenationExpression, data: D) = visitExpression(expression, data)
|
||||
fun visitStringConcatenation(expression: IrStringConcatenationExpression, data: D) = visitExpression(expression, data)
|
||||
fun visitThisExpression(expression: IrThisExpression, data: D) = visitExpression(expression, data)
|
||||
|
||||
fun visitDeclarationReference(expression: IrDeclarationReference, data: D) = visitExpression(expression, data)
|
||||
|
||||
+3
-1
@@ -8,7 +8,9 @@ IrFile /assignments.kt
|
||||
SET_VAR x type=<no-type>
|
||||
LITERAL Int type=kotlin.Int value='1'
|
||||
SET_VAR x type=<no-type>
|
||||
DUMMY PLUS type=kotlin.Int
|
||||
CALL .plus type=kotlin.Int operator=PLUS
|
||||
$this: GET_VAR x type=kotlin.Int
|
||||
other: LITERAL Int type=kotlin.Int value='1'
|
||||
IrFunction public fun test2(/*0*/ r: Ref): kotlin.Unit
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=false
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
fun test1(a: Int, b: Int) = a + b
|
||||
fun test2(a: Int, b: Int) = a - b
|
||||
fun test3(a: Int, b: Int) = a * b
|
||||
fun test4(a: Int, b: Int) = a / b
|
||||
fun test5(a: Int, b: Int) = a % b
|
||||
fun test6(a: Int, b: Int) = a .. b
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
IrFile /simpleOperators.kt
|
||||
IrFunction public fun test1(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Int
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
CALL .plus type=kotlin.Int operator=PLUS
|
||||
$this: GET_VAR a type=kotlin.Int
|
||||
other: GET_VAR b type=kotlin.Int
|
||||
IrFunction public fun test2(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Int
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
CALL .minus type=kotlin.Int operator=MINUS
|
||||
$this: GET_VAR a type=kotlin.Int
|
||||
other: GET_VAR b type=kotlin.Int
|
||||
IrFunction public fun test3(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Int
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
CALL .times type=kotlin.Int operator=MUL
|
||||
$this: GET_VAR a type=kotlin.Int
|
||||
other: GET_VAR b type=kotlin.Int
|
||||
IrFunction public fun test4(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Int
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
CALL .div type=kotlin.Int operator=DIV
|
||||
$this: GET_VAR a type=kotlin.Int
|
||||
other: GET_VAR b type=kotlin.Int
|
||||
IrFunction public fun test5(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Int
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
CALL .mod type=kotlin.Int operator=PERC
|
||||
$this: GET_VAR a type=kotlin.Int
|
||||
other: GET_VAR b type=kotlin.Int
|
||||
IrFunction public fun test6(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.ranges.IntRange
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
CALL .rangeTo type=kotlin.ranges.IntRange operator=RANGE
|
||||
$this: GET_VAR a type=kotlin.Int
|
||||
other: GET_VAR b type=kotlin.Int
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun test1(a: String, b: Any) = a + b
|
||||
fun test2(a: String, b: Int) = a + "+" + b
|
||||
fun test3(a: String, b: Int) = (a + "+") + (b + 1) + a
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
IrFile /stringPlus.kt
|
||||
IrFunction public fun test1(/*0*/ a: kotlin.String, /*1*/ b: kotlin.Any): kotlin.String
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
STRING_CONCATENATION type=kotlin.String
|
||||
GET_VAR a type=kotlin.String
|
||||
GET_VAR b type=kotlin.Any
|
||||
IrFunction public fun test2(/*0*/ a: kotlin.String, /*1*/ b: kotlin.Int): kotlin.String
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
STRING_CONCATENATION type=kotlin.String
|
||||
GET_VAR a type=kotlin.String
|
||||
LITERAL String type=kotlin.String value='+'
|
||||
GET_VAR b type=kotlin.Int
|
||||
IrFunction public fun test3(/*0*/ a: kotlin.String, /*1*/ b: kotlin.Int): kotlin.String
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
STRING_CONCATENATION type=kotlin.String
|
||||
GET_VAR a type=kotlin.String
|
||||
LITERAL String type=kotlin.String value='+'
|
||||
CALL .plus type=kotlin.Int operator=PLUS
|
||||
$this: GET_VAR b type=kotlin.Int
|
||||
other: LITERAL Int type=kotlin.Int value='1'
|
||||
GET_VAR a type=kotlin.String
|
||||
@@ -113,6 +113,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleOperators.kt")
|
||||
public void testSimpleOperators() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/simpleOperators.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCasts.kt")
|
||||
public void testSmartCasts() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/smartCasts.kt");
|
||||
@@ -130,4 +136,10 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/smoke.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("stringPlus.kt")
|
||||
public void testStringPlus() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/stringPlus.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user