JVM IR: Turn static callable references into singletons
This commit is contained in:
committed by
Dmitry Petrov
parent
ac5c255c20
commit
bb5a639153
Generated
+10
@@ -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
|
||||
|
||||
|
||||
+110
@@ -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>()
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
@@ -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
-10
@@ -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
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user