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.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.IrGeneratorContext
|
import org.jetbrains.kotlin.psi2ir.generators.IrGeneratorContext
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.IrModuleGenerator
|
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
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
|
||||||
class Psi2IrTranslator(val configuration: Configuration = Configuration()) {
|
class Psi2IrTranslator(val configuration: Configuration = Configuration()) {
|
||||||
class 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 {
|
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) {
|
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.PLUS to IrOperator.PLUS,
|
||||||
KtTokens.MINUS to IrOperator.MINUS,
|
KtTokens.MINUS to IrOperator.MINUS,
|
||||||
KtTokens.MUL to IrOperator.MUL,
|
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
|
val AUGMENTED_ASSIGNMENTS = KtTokens.AUGMENTED_ASSIGNMENTS
|
||||||
@@ -55,10 +57,17 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
|
|||||||
return when (ktOperator) {
|
return when (ktOperator) {
|
||||||
KtTokens.EQ -> generateAssignment(expression)
|
KtTokens.EQ -> generateAssignment(expression)
|
||||||
in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, ktOperator)
|
in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, ktOperator)
|
||||||
|
in BINARY_OPERATORS_WITH_CALLS -> generateBinaryOperatorWithConventionalCall(expression, ktOperator)
|
||||||
else -> createDummyExpression(expression, ktOperator.toString())
|
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 {
|
private fun generateAugmentedAssignment(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression {
|
||||||
val ktLeft = expression.left!!
|
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.replaceWith
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
|
||||||
fun collapseDesugaredBlocks(element: IrElement) {
|
fun inlineDesugaredBlocks(element: IrElement) {
|
||||||
element.accept(CollapseDesugaredBlocks(), null)
|
element.accept(InlineDesugaredBlocks(), null)
|
||||||
}
|
}
|
||||||
|
|
||||||
class CollapseDesugaredBlocks : IrElementVisitor<Unit, Nothing?> {
|
class InlineDesugaredBlocks : IrElementVisitor<Unit, Nothing?> {
|
||||||
override fun visitElement(element: IrElement, data: Nothing?) {
|
override fun visitElement(element: IrElement, data: Nothing?) {
|
||||||
element.acceptChildren(this, data)
|
element.acceptChildren(this, data)
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ sealed class IrOperator(val debugName: String) {
|
|||||||
object MINUS : IrOperator("MINUS")
|
object MINUS : IrOperator("MINUS")
|
||||||
object MUL : IrOperator("MUL")
|
object MUL : IrOperator("MUL")
|
||||||
object DIV : IrOperator("DIV")
|
object DIV : IrOperator("DIV")
|
||||||
object MOD : IrOperator("MOD")
|
object PERC : IrOperator("PERC")
|
||||||
|
|
||||||
object EQ : IrOperator("EQ")
|
object EQ : IrOperator("EQ")
|
||||||
object PLUSEQ : IrOperator("PLUSEQ")
|
object PLUSEQ : IrOperator("PLUSEQ")
|
||||||
|
|||||||
+1
-1
@@ -55,7 +55,7 @@ class IrStringConcatenationExpressionImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
|
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) {
|
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
|
||||||
arguments.forEach { it.accept(visitor, data) }
|
arguments.forEach { it.accept(visitor, data) }
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
|
|||||||
"SET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name}" +
|
"SET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name}" +
|
||||||
"type=${expression.renderType()}"
|
"type=${expression.renderType()}"
|
||||||
|
|
||||||
|
override fun visitStringConcatenation(expression: IrStringConcatenationExpression, data: Nothing?): String =
|
||||||
|
"STRING_CONCATENATION type=${expression.renderType()}"
|
||||||
|
|
||||||
override fun visitTypeOperatorExpression(expression: IrTypeOperatorExpression, data: Nothing?): String {
|
override fun visitTypeOperatorExpression(expression: IrTypeOperatorExpression, data: Nothing?): String {
|
||||||
return "TYPE_OP operator=${expression.operator} typeOperand=${expression.typeOperand.render()}"
|
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 <T> visitLiteral(expression: IrLiteralExpression<T>, data: D): R = visitExpression(expression, data)
|
||||||
fun visitReturnExpression(expression: IrReturnExpression, 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 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 visitThisExpression(expression: IrThisExpression, data: D) = visitExpression(expression, data)
|
||||||
|
|
||||||
fun visitDeclarationReference(expression: IrDeclarationReference, 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>
|
SET_VAR x type=<no-type>
|
||||||
LITERAL Int type=kotlin.Int value='1'
|
LITERAL Int type=kotlin.Int value='1'
|
||||||
SET_VAR x type=<no-type>
|
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
|
IrFunction public fun test2(/*0*/ r: Ref): kotlin.Unit
|
||||||
IrExpressionBody
|
IrExpressionBody
|
||||||
BLOCK type=<no-type> hasResult=false isDesugared=false
|
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);
|
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")
|
@TestMetadata("smartCasts.kt")
|
||||||
public void testSmartCasts() throws Exception {
|
public void testSmartCasts() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/smartCasts.kt");
|
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");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/smoke.kt");
|
||||||
doTest(fileName);
|
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