JVM IR: Turn static callable references into singletons

This commit is contained in:
Steven Schäfer
2020-03-05 11:35:51 +01:00
committed by Dmitry Petrov
parent ac5c255c20
commit bb5a639153
9 changed files with 196 additions and 10 deletions
@@ -2376,6 +2376,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/callableReference/function/specialCalls.kt");
}
@TestMetadata("staticFunctionReference.kt")
public void testStaticFunctionReference() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/function/staticFunctionReference.kt");
}
@TestMetadata("topLevelFromClass.kt")
public void testTopLevelFromClass() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/function/topLevelFromClass.kt");
@@ -4162,6 +4167,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/closures/simplestClosureAndBoxing.kt");
}
@TestMetadata("staticLambda.kt")
public void testStaticLambda() throws Exception {
runTest("compiler/testData/codegen/box/closures/staticLambda.kt");
}
@TestMetadata("subclosuresWithinInitializers.kt")
public void testSubclosuresWithinInitializers() throws Exception {
runTest("compiler/testData/codegen/box/closures/subclosuresWithinInitializers.kt");
@@ -301,6 +301,7 @@ private val jvmFilePhases =
returnableBlocksPhase then
localDeclarationsPhase then
jvmLocalClassExtractionPhase then
staticLambdaPhase then
jvmDefaultConstructorPhase then
@@ -0,0 +1,110 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal val staticLambdaPhase = makeIrFilePhase(
::StaticLambdaLowering,
name = "StaticLambdaPhase",
description = "Turn static callable references into singletons"
)
class StaticLambdaLowering(val backendContext: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() {
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
override fun visitClass(declaration: IrClass): IrStatement {
declaration.transformChildrenVoid()
if (declaration.isSyntheticSingleton) {
declaration.declarations += backendContext.declarationFactory.getFieldForObjectInstance(declaration).also { field ->
field.initializer = backendContext.createIrBuilder(field.symbol).run {
irExprBody(irCall(declaration.primaryConstructor!!))
}
}
}
return declaration
}
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
val constructor = expression.symbol.owner
if (!constructor.constructedClass.isSyntheticSingleton)
return super.visitConstructorCall(expression)
val instanceField = backendContext.declarationFactory.getFieldForObjectInstance(constructor.constructedClass)
return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type)
}
// Recognize callable references with no value or type arguments. The only type arguments in Kotlin stem from usages of
// reified type parameters, which we unfortunately don't record as parameters so we have to check the body of the class.
private val IrClass.isSyntheticSingleton: Boolean
get() = (origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL || origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL)
&& primaryConstructor!!.valueParameters.isEmpty()
&& !containsReifiedTypeParameters
// Check whether there is any usage of reified type parameters in the body of the given class. This method does not
// distinguish between reified type parameters declared in inline functions inside the class and those coming from the
// outside. This is sufficient, because we only apply this function to callable references where this does not matter.
private val IrClass.containsReifiedTypeParameters: Boolean
get() = containsReifiedTypeParametersCache.getOrPut(this) {
var containsReified = false
acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
if (!containsReified)
element.acceptChildrenVoid(this)
}
override fun visitMemberAccess(expression: IrMemberAccessExpression) {
for (i in 0 until expression.typeArgumentsCount) {
if (expression.getTypeArgument(i)?.isReified == true) {
containsReified = true
break
}
}
super.visitMemberAccess(expression)
}
override fun visitTypeOperator(expression: IrTypeOperatorCall) {
if (expression.typeOperand.isReified)
containsReified = true
super.visitTypeOperator(expression)
}
override fun visitClassReference(expression: IrClassReference) {
if (expression.classType.isReified)
containsReified = true
super.visitClassReference(expression)
}
private val IrType.isReified: Boolean
get() = classifierOrNull?.safeAs<IrTypeParameterSymbol>()?.owner?.isReified == true
})
return containsReified
}
private val containsReifiedTypeParametersCache = mutableMapOf<IrClass, Boolean>()
}
@@ -0,0 +1,22 @@
// TARGET_BACKEND: JVM
var capturedRef: ((Int) -> Int)? = null
fun ref(x: Int) = x
fun updateCapturedRef(): Boolean {
val r = ::ref
if (capturedRef == null) {
capturedRef = r
} else if (capturedRef !== r) {
return false
}
return true
}
fun box(): String {
updateCapturedRef()
if (!updateCapturedRef())
return "FAIL"
return "OK"
}
+20
View File
@@ -0,0 +1,20 @@
// TARGET_BACKEND: JVM
var capturedLambda: ((Int) -> Int)? = null
fun captureLambda(): Boolean {
val lambda = { x: Int -> x + 1 }
if (capturedLambda == null) {
capturedLambda = lambda
} else if (capturedLambda !== lambda) {
return false
}
return true
}
fun box(): String {
captureLambda()
if (!captureLambda())
return "FAIL"
return "OK"
}
@@ -3,19 +3,10 @@ public interface Base {
public abstract @org.jetbrains.annotations.Nullable method generic(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
public final class Override5Kt$box$1$invokeSuspend$$inlined$inlineMe$1$2 {
inner class Override5Kt$box$1$invokeSuspend$$inlined$inlineMe$1
inner class Override5Kt$box$1$invokeSuspend$$inlined$inlineMe$1$2
public method <init>(): void
public synthetic bridge @org.jetbrains.annotations.Nullable method invoke(): java.lang.Object
public final method invoke(): void
}
@kotlin.Metadata
public final class Override5Kt$box$1$invokeSuspend$$inlined$inlineMe$1 {
inner class Override5Kt$box$1$invokeSuspend$$inlined$inlineMe$1
inner class Override5Kt$box$1$invokeSuspend$$inlined$inlineMe$1$2
inner class Override5Kt$inlineMe$1$generic$2
public method <init>(): void
public @org.jetbrains.annotations.Nullable method generic(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
}
@@ -46,8 +37,10 @@ public final class Override5Kt$inlineMe$1$generic$1 {
@kotlin.Metadata
public final class Override5Kt$inlineMe$1$generic$2 {
public final static @org.jetbrains.annotations.NotNull field INSTANCE: Override5Kt$inlineMe$1$generic$2
inner class Override5Kt$inlineMe$1
inner class Override5Kt$inlineMe$1$generic$2
static method <clinit>(): void
public method <init>(): void
public synthetic bridge @org.jetbrains.annotations.Nullable method invoke(): java.lang.Object
public final method invoke(): void
@@ -2396,6 +2396,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/callableReference/function/specialCalls.kt");
}
@TestMetadata("staticFunctionReference.kt")
public void testStaticFunctionReference() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/function/staticFunctionReference.kt");
}
@TestMetadata("topLevelFromClass.kt")
public void testTopLevelFromClass() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/function/topLevelFromClass.kt");
@@ -4182,6 +4187,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/closures/simplestClosureAndBoxing.kt");
}
@TestMetadata("staticLambda.kt")
public void testStaticLambda() throws Exception {
runTest("compiler/testData/codegen/box/closures/staticLambda.kt");
}
@TestMetadata("subclosuresWithinInitializers.kt")
public void testSubclosuresWithinInitializers() throws Exception {
runTest("compiler/testData/codegen/box/closures/subclosuresWithinInitializers.kt");
@@ -2396,6 +2396,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/callableReference/function/specialCalls.kt");
}
@TestMetadata("staticFunctionReference.kt")
public void testStaticFunctionReference() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/function/staticFunctionReference.kt");
}
@TestMetadata("topLevelFromClass.kt")
public void testTopLevelFromClass() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/function/topLevelFromClass.kt");
@@ -4182,6 +4187,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/closures/simplestClosureAndBoxing.kt");
}
@TestMetadata("staticLambda.kt")
public void testStaticLambda() throws Exception {
runTest("compiler/testData/codegen/box/closures/staticLambda.kt");
}
@TestMetadata("subclosuresWithinInitializers.kt")
public void testSubclosuresWithinInitializers() throws Exception {
runTest("compiler/testData/codegen/box/closures/subclosuresWithinInitializers.kt");
@@ -2376,6 +2376,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/callableReference/function/specialCalls.kt");
}
@TestMetadata("staticFunctionReference.kt")
public void testStaticFunctionReference() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/function/staticFunctionReference.kt");
}
@TestMetadata("topLevelFromClass.kt")
public void testTopLevelFromClass() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/function/topLevelFromClass.kt");
@@ -4162,6 +4167,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/closures/simplestClosureAndBoxing.kt");
}
@TestMetadata("staticLambda.kt")
public void testStaticLambda() throws Exception {
runTest("compiler/testData/codegen/box/closures/staticLambda.kt");
}
@TestMetadata("subclosuresWithinInitializers.kt")
public void testSubclosuresWithinInitializers() throws Exception {
runTest("compiler/testData/codegen/box/closures/subclosuresWithinInitializers.kt");