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 &&
|
||||
|
||||
Reference in New Issue
Block a user