Desugar basic binary operators (+, -, *, /, %, ..).

Fold String.plus calls to IrStringConcatenationExpression.
This commit is contained in:
Dmitry Petrov
2016-08-17 14:10:08 +03:00
committed by Dmitry Petrov
parent 287d804131
commit 1b018a6ead
14 changed files with 197 additions and 11 deletions
@@ -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)
}
}
@@ -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!!
@@ -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
}
}
@@ -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")
@@ -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
View File
@@ -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
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}