JVM_IR: further refine synthetic accessor generation
References to protected members from crossinline lambdas in the same package do not need accessors.
This commit is contained in:
+16
-28
@@ -12,16 +12,11 @@ import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
|
||||
internal open class IrInlineReferenceLocator(private val context: JvmBackendContext) : IrElementVisitorVoidWithContext() {
|
||||
val inlineReferences = mutableSetOf<IrCallableReference>()
|
||||
|
||||
// For crossinline lambdas, the call site is null as it's probably in a separate class somewhere.
|
||||
// All other lambdas are guaranteed to be inlined into the scope they are declared in.
|
||||
val lambdaToCallSite = mutableMapOf<IrFunction, IrDeclaration?>()
|
||||
|
||||
override fun visitElement(element: IrElement) = element.acceptChildrenVoid(this)
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression) {
|
||||
@@ -35,37 +30,30 @@ internal open class IrInlineReferenceLocator(private val context: JvmBackendCont
|
||||
if (!isInlineIrExpression(valueArgument))
|
||||
continue
|
||||
|
||||
if (valueArgument is IrPropertyReference) {
|
||||
handleInlineFunctionCallableReferenceParam(valueArgument)
|
||||
continue
|
||||
}
|
||||
|
||||
val reference = when (valueArgument) {
|
||||
is IrFunctionReference -> valueArgument
|
||||
is IrBlock -> valueArgument.statements.filterIsInstance<IrFunctionReference>().singleOrNull()
|
||||
else -> null
|
||||
} ?: continue
|
||||
|
||||
handleInlineFunctionCallableReferenceParam(reference)
|
||||
if (valueArgument is IrBlock && valueArgument.origin.isLambda) {
|
||||
val declaration = if (parameter.isCrossinline) null else currentScope!!.irElement as IrDeclaration
|
||||
handleInlineFunctionLambdaParam(reference, function, declaration)
|
||||
val reference = valueArgument.statements.last() as IrFunctionReference
|
||||
visitInlineLambda(reference, function, parameter, currentScope!!.irElement as IrDeclaration)
|
||||
} else if (valueArgument is IrCallableReference) {
|
||||
visitInlineReference(valueArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitFunctionAccess(expression)
|
||||
}
|
||||
|
||||
open fun handleInlineFunctionCallableReferenceParam(valueArgument: IrCallableReference) {
|
||||
inlineReferences.add(valueArgument)
|
||||
}
|
||||
open fun visitInlineReference(argument: IrCallableReference) {}
|
||||
|
||||
open fun handleInlineFunctionLambdaParam(lambdaReference: IrFunctionReference, callee: IrFunction, callSite: IrDeclaration?) {
|
||||
lambdaToCallSite[lambdaReference.symbol.owner] = callSite
|
||||
}
|
||||
open fun visitInlineLambda(argument: IrFunctionReference, callee: IrFunction, parameter: IrValueParameter, scope: IrDeclaration) =
|
||||
visitInlineReference(argument)
|
||||
|
||||
companion object {
|
||||
fun scan(context: JvmBackendContext, element: IrElement) =
|
||||
IrInlineReferenceLocator(context).apply { element.accept(this, null) }
|
||||
fun scan(context: JvmBackendContext, element: IrElement): Set<IrCallableReference> =
|
||||
mutableSetOf<IrCallableReference>().apply {
|
||||
element.accept(object : IrInlineReferenceLocator(context) {
|
||||
override fun visitInlineReference(argument: IrCallableReference) {
|
||||
add(argument)
|
||||
}
|
||||
}, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -641,17 +641,18 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
private fun markSuspendLambdas(irElement: IrElement): Pair<List<SuspendLambdaInfo>, List<IrFunction>> {
|
||||
val suspendLambdas = arrayListOf<SuspendLambdaInfo>()
|
||||
val capturesCrossinline = mutableSetOf<IrCallableReference>()
|
||||
val visitor = object : IrInlineReferenceLocator(context) {
|
||||
val inlineReferences = mutableSetOf<IrCallableReference>()
|
||||
irElement.acceptChildrenVoid(object : IrInlineReferenceLocator(context) {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun handleInlineFunctionCallableReferenceParam(valueArgument: IrCallableReference) {
|
||||
super.handleInlineFunctionCallableReferenceParam(valueArgument)
|
||||
val getValue = (valueArgument as? IrGetValue) ?: return
|
||||
override fun visitInlineReference(argument: IrCallableReference) {
|
||||
inlineReferences.add(argument)
|
||||
val getValue = (argument as? IrGetValue) ?: return
|
||||
val parameter = getValue.symbol.owner as? IrValueParameter ?: return
|
||||
if (parameter.isCrossinline) {
|
||||
capturesCrossinline += valueArgument
|
||||
capturesCrossinline += argument
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,9 +677,8 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
irElement.acceptChildrenVoid(visitor)
|
||||
return suspendLambdas to visitor.inlineReferences.map { it.symbol.owner as IrFunction }
|
||||
})
|
||||
return suspendLambdas to inlineReferences.map { it.symbol.owner as IrFunction }
|
||||
}
|
||||
|
||||
private class SuspendLambdaInfo(
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
|
||||
private fun IrFunctionReference.isSuspendCallableReference(): Boolean = isSuspend && origin == null
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
ignoredFunctionReferences.addAll(IrInlineReferenceLocator.scan(context, irFile).inlineReferences)
|
||||
ignoredFunctionReferences.addAll(IrInlineReferenceLocator.scan(context, irFile))
|
||||
irFile.transformChildrenVoid(this)
|
||||
}
|
||||
|
||||
|
||||
+4
-13
@@ -14,12 +14,8 @@ import org.jetbrains.kotlin.ir.builders.createTmpVariable
|
||||
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
||||
import org.jetbrains.kotlin.ir.builders.irInt
|
||||
import org.jetbrains.kotlin.ir.builders.irReturn
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCallableReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
@@ -44,16 +40,11 @@ internal class FakeInliningLocalVariablesLowering(val context: JvmBackendContext
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleInlineFunctionCallableReferenceParam(valueArgument: IrCallableReference) {
|
||||
// Do not record inline function callable reference parameters. They will not be used.
|
||||
}
|
||||
|
||||
override fun handleInlineFunctionLambdaParam(lambdaReference: IrFunctionReference, callee: IrFunction, callSite: IrDeclaration?) {
|
||||
// Do not record lambda parameters. Instead deal with them now.
|
||||
val lambda = lambdaReference.symbol.owner
|
||||
override fun visitInlineLambda(argument: IrFunctionReference, callee: IrFunction, parameter: IrValueParameter, scope: IrDeclaration) {
|
||||
val lambda = argument.symbol.owner
|
||||
if (lambda.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) {
|
||||
val argumentToFunctionName = context.methodSignatureMapper.mapFunctionName(callee)
|
||||
val lambdaReferenceName = context.getLocalClassType(lambdaReference)!!.internalName.substringAfterLast("/")
|
||||
val lambdaReferenceName = context.getLocalClassType(argument)!!.internalName.substringAfterLast("/")
|
||||
val localName = "${JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT}-$argumentToFunctionName-$lambdaReferenceName"
|
||||
lambda.addFakeLocalVariable(localName)
|
||||
}
|
||||
|
||||
+1
-3
@@ -50,9 +50,7 @@ internal class InlineCallableReferenceToLambdaPhase(val context: JvmBackendConte
|
||||
private var inlinableReferences = mutableSetOf<IrCallableReference>()
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
IrInlineReferenceLocator.scan(context, irFile).let {
|
||||
inlinableReferences.addAll(it.inlineReferences)
|
||||
}
|
||||
inlinableReferences.addAll(IrInlineReferenceLocator.scan(context, irFile))
|
||||
irFile.transformChildrenVoid(this)
|
||||
}
|
||||
|
||||
|
||||
+17
-7
@@ -41,11 +41,18 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {
|
||||
data class LambdaCallSite(val scope: IrDeclaration, val crossinline: Boolean)
|
||||
|
||||
private val pendingAccessorsToAdd = mutableListOf<IrFunction>()
|
||||
private val inlineLambdaToCallSite = mutableMapOf<IrFunction, IrDeclaration?>()
|
||||
private val inlineLambdaToCallSite = mutableMapOf<IrFunction, LambdaCallSite>()
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
inlineLambdaToCallSite.putAll(IrInlineReferenceLocator.scan(context, irFile).lambdaToCallSite)
|
||||
irFile.accept(object : IrInlineReferenceLocator(context) {
|
||||
override fun visitInlineLambda(argument: IrFunctionReference, callee: IrFunction, parameter: IrValueParameter, scope: IrDeclaration) {
|
||||
inlineLambdaToCallSite[argument.symbol.owner] = LambdaCallSite(scope, parameter.isCrossinline)
|
||||
}
|
||||
}, null)
|
||||
|
||||
irFile.transformChildrenVoid(this)
|
||||
|
||||
for (accessor in pendingAccessorsToAdd) {
|
||||
@@ -509,16 +516,18 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
|
||||
this == JavaVisibilities.PROTECTED_AND_PACKAGE ||
|
||||
this == JavaVisibilities.PROTECTED_STATIC_VISIBILITY
|
||||
|
||||
private fun IrDeclaration.getAccessContext(withSuper: Boolean): IrDeclarationContainer? = when {
|
||||
private fun IrDeclaration.getAccessContext(allowSamePackage: Boolean): IrDeclarationContainer? = when {
|
||||
this is IrDeclarationContainer -> this
|
||||
// For inline lambdas, we can navigate to the only call site directly.
|
||||
this in inlineLambdaToCallSite -> inlineLambdaToCallSite[this]?.getAccessContext(withSuper)
|
||||
// For inline lambdas, we can navigate to the only call site directly. Crossinline lambdas might be inlined
|
||||
// into other classes in the same package, so private/super require accessors anyway.
|
||||
this in inlineLambdaToCallSite ->
|
||||
inlineLambdaToCallSite[this]!!.takeIf { allowSamePackage || !it.crossinline }?.scope?.getAccessContext(allowSamePackage)
|
||||
// Accesses from inline functions can actually be anywhere; even private inline functions can be
|
||||
// inlined into a different class, e.g. a callable reference. For protected inline functions
|
||||
// calling methods on `super` we also need an accessor to satisfy INVOKESPECIAL constraints.
|
||||
// TODO scan nested classes for calls to private inline functions?
|
||||
this is IrFunction && isInline -> null
|
||||
else -> (parent as? IrDeclaration)?.getAccessContext(withSuper)
|
||||
else -> (parent as? IrDeclaration)?.getAccessContext(allowSamePackage)
|
||||
}
|
||||
|
||||
private fun IrSymbol.isAccessible(withSuper: Boolean, thisObjReference: IrClassSymbol?): Boolean {
|
||||
@@ -547,7 +556,8 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
|
||||
|
||||
// If local variables are accessible by Kotlin rules, they also are by Java rules.
|
||||
val symbolDeclarationContainer = (declaration.parent as? IrDeclarationContainer) as? IrElement ?: return true
|
||||
val contextDeclarationContainer = (currentScope!!.irElement as IrDeclaration).getAccessContext(withSuper) ?: return false
|
||||
val contextDeclarationContainer =
|
||||
(currentScope!!.irElement as IrDeclaration).getAccessContext(declaration.visibility.isProtected && !withSuper) ?: return false
|
||||
|
||||
val samePackage = declaration.getPackageFragment()?.fqName == contextDeclarationContainer.getPackageFragment()?.fqName
|
||||
val fromSubclassOfReceiversClass = contextDeclarationContainer is IrClass && symbolDeclarationContainer is IrClass &&
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// FILE: protectedPack/J.java
|
||||
|
||||
package protectedPack;
|
||||
|
||||
public class J {
|
||||
protected String foo = "OK";
|
||||
}
|
||||
|
||||
// FILE: 1.kt
|
||||
|
||||
package protectedPack
|
||||
|
||||
inline fun foo(crossinline bar: () -> String) = object {
|
||||
fun baz() = bar()
|
||||
}.baz()
|
||||
|
||||
fun box(): String {
|
||||
return foo { J().foo!! }
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// !LANGUAGE: -ProhibitProtectedCallFromInline
|
||||
// IGNORE_BACKEND: JVM
|
||||
// IGNORE_BACKEND_MULTI_MODULE: JVM
|
||||
// FILE: 1.kt
|
||||
|
||||
package test
|
||||
|
||||
inline fun runCrossinline(crossinline f: () -> String) = f()
|
||||
|
||||
open class Base {
|
||||
protected open val FOO = "O"
|
||||
|
||||
protected open fun test() = "K"
|
||||
}
|
||||
|
||||
open class P : Base() {
|
||||
inline fun protectedProp(crossinline f: (String) -> String): String =
|
||||
runCrossinline { f(FOO) }
|
||||
|
||||
inline fun protectedFun(crossinline f: (String) -> String): String =
|
||||
runCrossinline { f(test()) }
|
||||
}
|
||||
|
||||
// FILE: 2.kt
|
||||
|
||||
import test.*
|
||||
|
||||
fun box() : String {
|
||||
val p = P()
|
||||
return p.protectedProp { it } + p.protectedFun { it }
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package a
|
||||
|
||||
open class A {
|
||||
protected fun protectedFun(): String = "OK"
|
||||
}
|
||||
|
||||
inline fun foo(crossinline bar: () -> String) = object {
|
||||
fun baz() = bar()
|
||||
}.baz()
|
||||
|
||||
class BSamePackage: A() {
|
||||
fun test(): String = foo {
|
||||
protectedFun()
|
||||
}
|
||||
}
|
||||
|
||||
// 0 INVOKESTATIC a/BSamePackage.access
|
||||
+5
@@ -1147,6 +1147,11 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxAga
|
||||
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedPropertyInPackage.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("protectedPropertyInPackageFromCrossinline.kt")
|
||||
public void testProtectedPropertyInPackageFromCrossinline() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedPropertyInPackageFromCrossinline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("protectedStaticClass.kt")
|
||||
public void testProtectedStaticClass() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedStaticClass.kt");
|
||||
|
||||
+5
@@ -4320,6 +4320,11 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/privateInCrossInline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("protectedMembersFromSuper.kt")
|
||||
public void testProtectedMembersFromSuper() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/protectedMembersFromSuper.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("superCall.kt")
|
||||
public void testSuperCall() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/superCall.kt");
|
||||
|
||||
@@ -254,6 +254,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
runTest("compiler/testData/codegen/bytecodeText/maxStackAfterOptimizations.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noAccessorForProtectedInSamePackageCrossinline.kt")
|
||||
public void testNoAccessorForProtectedInSamePackageCrossinline() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/noAccessorForProtectedInSamePackageCrossinline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noFlagAnnotations.kt")
|
||||
public void testNoFlagAnnotations() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/noFlagAnnotations.kt");
|
||||
|
||||
Generated
+5
@@ -4320,6 +4320,11 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/privateInCrossInline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("protectedMembersFromSuper.kt")
|
||||
public void testProtectedMembersFromSuper() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/protectedMembersFromSuper.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("superCall.kt")
|
||||
public void testSuperCall() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/superCall.kt");
|
||||
|
||||
Generated
+5
@@ -1148,6 +1148,11 @@ public class IrBlackBoxAgainstJavaCodegenTestGenerated extends AbstractIrBlackBo
|
||||
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedPropertyInPackage.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("protectedPropertyInPackageFromCrossinline.kt")
|
||||
public void testProtectedPropertyInPackageFromCrossinline() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedPropertyInPackageFromCrossinline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("protectedStaticClass.kt")
|
||||
public void testProtectedStaticClass() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxAgainstJava/visibility/protectedAndPackage/protectedStaticClass.kt");
|
||||
|
||||
+5
@@ -4070,6 +4070,11 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/privateInCrossInline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("protectedMembersFromSuper.kt")
|
||||
public void testProtectedMembersFromSuper() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/protectedMembersFromSuper.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("superCall.kt")
|
||||
public void testSuperCall() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/superCall.kt");
|
||||
|
||||
+5
@@ -254,6 +254,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
||||
runTest("compiler/testData/codegen/bytecodeText/maxStackAfterOptimizations.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noAccessorForProtectedInSamePackageCrossinline.kt")
|
||||
public void testNoAccessorForProtectedInSamePackageCrossinline() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/noAccessorForProtectedInSamePackageCrossinline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noFlagAnnotations.kt")
|
||||
public void testNoFlagAnnotations() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/noFlagAnnotations.kt");
|
||||
|
||||
Generated
+5
@@ -4070,6 +4070,11 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/privateInCrossInline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("protectedMembersFromSuper.kt")
|
||||
public void testProtectedMembersFromSuper() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/protectedMembersFromSuper.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("superCall.kt")
|
||||
public void testSuperCall() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/syntheticAccessors/withinInlineLambda/superCall.kt");
|
||||
|
||||
Reference in New Issue
Block a user