JVM_IR KT-45998 fix protected companion object member accessors

Also, make sure it works with indy-based SAM conversions.
This commit is contained in:
Dmitry Petrov
2021-04-16 16:53:16 +03:00
committed by TeamCityServer
parent b1fb0ba9e1
commit 9a4a39e680
10 changed files with 164 additions and 19 deletions
@@ -20958,6 +20958,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
public void testPrivateTopLevelFun() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/withAccessor/privateTopLevelFun.kt");
}
@Test
@TestMetadata("protectedSuperclassCompanionObjectMember.kt")
public void testProtectedSuperclassCompanionObjectMember() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/withAccessor/protectedSuperclassCompanionObjectMember.kt");
}
}
}
@@ -39772,6 +39778,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/syntheticAccessors/protectedFromLambda.kt");
}
@Test
@TestMetadata("protectedSuperclassCompanionObjectMember.kt")
public void testProtectedSuperclassCompanionObjectMember() throws Exception {
runTest("compiler/testData/codegen/box/syntheticAccessors/protectedSuperclassCompanionObjectMember.kt");
}
@Test
@TestMetadata("superCallFromMultipleSubclasses.kt")
public void testSuperCallFromMultipleSubclasses() throws Exception {
@@ -179,7 +179,20 @@ class JvmCachedDeclarations(
modality = if (isInterface) Modality.OPEN else target.modality
// Since we already mangle the name above we need to reset internal visibilities to public in order
// to avoid mangling the same name twice.
visibility = if (target.visibility == DescriptorVisibilities.INTERNAL) DescriptorVisibilities.PUBLIC else target.visibility
visibility = when (target.visibility) {
DescriptorVisibilities.INTERNAL ->
DescriptorVisibilities.PUBLIC
DescriptorVisibilities.PROTECTED -> {
// Required to properly create accessors to protected static companion object member
// when this member is referenced in subclass.
if (isStatic)
JavaDescriptorVisibilities.PROTECTED_STATIC_VISIBILITY
else
DescriptorVisibilities.PROTECTED
}
else ->
target.visibility
}
isSuspend = target.isSuspend
}.apply proxy@{
parent = this@makeProxy
@@ -5,35 +5,27 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.codegen.isEffectivelyInlineOnly
import org.jetbrains.kotlin.backend.jvm.codegen.isInlineFunctionCall
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
internal val jvmStaticInObjectPhase = makeIrModulePhase(
@@ -145,10 +137,37 @@ private class CompanionObjectJvmStaticTransformer(val context: JvmBackendContext
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
val callee = expression.symbol.owner
if (callee.isJvmStaticInCompanion() && callee.visibility == DescriptorVisibilities.PROTECTED && !callee.isInlineFunctionCall(context)) {
val (staticProxy, _) = context.cachedDeclarations.getStaticAndCompanionDeclaration(callee)
return expression.makeStatic(context, staticProxy)
return when {
shouldReplaceWithStaticCall(callee) -> {
val (staticProxy, _) = context.cachedDeclarations.getStaticAndCompanionDeclaration(callee)
expression.makeStatic(context, staticProxy)
}
callee.symbol == context.ir.symbols.indyLambdaMetafactoryIntrinsic -> {
val implFunRef = expression.getValueArgument(1) as? IrFunctionReference
?: throw AssertionError("'implMethodReference' is expected to be 'IrFunctionReference': ${expression.dump()}")
val implFun = implFunRef.symbol.owner
if (implFunRef.dispatchReceiver != null && implFun is IrSimpleFunction && shouldReplaceWithStaticCall(implFun)) {
val (staticProxy, _) = context.cachedDeclarations.getStaticAndCompanionDeclaration(implFun)
expression.putValueArgument(
1,
IrFunctionReferenceImpl(
implFunRef.startOffset, implFunRef.endOffset, implFunRef.type,
staticProxy.symbol,
staticProxy.typeParameters.size,
staticProxy.valueParameters.size,
implFunRef.reflectionTarget, implFunRef.origin
)
)
}
expression
}
else ->
expression
}
return expression
}
private fun shouldReplaceWithStaticCall(callee: IrSimpleFunction) =
callee.isJvmStaticInCompanion() &&
callee.visibility == DescriptorVisibilities.PROTECTED &&
!callee.isInlineFunctionCall(context)
}
@@ -332,7 +332,9 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
classes.flatMap { it.declarations.filter(IrDeclaration::isAnonymousObject).filterIsInstance<IrClass>() }
val candidates = objectsInScope + companions + classes
candidates.lastOrNull { parent is IrClass && it.isSubclassOf(parent) } ?: classes.last()
} else parent
} else {
parent
}
private fun IrConstructor.makeConstructorAccessor(
originForConstructorAccessor: IrDeclarationOrigin =
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.overrides.buildFakeOverrideMember
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
@@ -56,7 +55,7 @@ internal class LambdaMetafactoryArgumentsBuilder(
// Can't use JDK LambdaMetafactory for function references by default (because of 'equals').
// TODO special mode that would generate indy everywhere?
if (reference.origin != IrStatementOrigin.LAMBDA && !samClass.isFromJava())
if (!reference.origin.isLambda && !samClass.isFromJava())
return null
val samMethod = samClass.getSingleAbstractMethod()
@@ -83,7 +82,7 @@ internal class LambdaMetafactoryArgumentsBuilder(
// JDK LambdaMetafactory doesn't copy annotations from implementation method to an instance method in a
// corresponding synthetic class, which doesn't look like a binary compatible change.
// TODO relaxed mode?
if (implFun.annotations.isNotEmpty())
if (reference.origin.isLambda && implFun.annotations.isNotEmpty())
return null
// Don't use JDK LambdaMetafactory for big arity lambdas.
@@ -0,0 +1,36 @@
// TARGET_BACKEND: JVM
// IGNORE_LIGHT_ANALYSIS
// IGNORE_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
// WITH_RUNTIME
// FULL_JDK
// CHECK_BYTECODE_TEXT
// 1 java/lang/invoke/LambdaMetafactory
// FILE: test.kt
import c2.*
fun box(): String =
C2().supplier().get()
// FILE: C1.kt
package c1
open class C1 {
companion object {
@JvmStatic
protected fun test(): String = "OK"
}
}
// FILE: C2.kt
package c2
import c1.*
import java.util.function.Supplier
class C2 : C1() {
fun supplier() = Supplier<String>(::test)
}
@@ -0,0 +1,30 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// IGNORE_LIGHT_ANALYSIS
// IGNORE_BACKEND: JVM
// FILE: test.kt
import c2.*
fun box(): String =
C2().b()()
// FILE: C1.kt
package c1
open class C1 {
companion object {
@JvmStatic
protected fun test(string: String): String =
string
}
}
// FILE: C2.kt
package c2
import c1.*
class C2 : C1() {
fun b() = { test("OK") }
}
@@ -20934,6 +20934,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
public void testPrivateTopLevelFun() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/withAccessor/privateTopLevelFun.kt");
}
@Test
@TestMetadata("protectedSuperclassCompanionObjectMember.kt")
public void testProtectedSuperclassCompanionObjectMember() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/withAccessor/protectedSuperclassCompanionObjectMember.kt");
}
}
}
@@ -39748,6 +39754,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/syntheticAccessors/protectedFromLambda.kt");
}
@Test
@TestMetadata("protectedSuperclassCompanionObjectMember.kt")
public void testProtectedSuperclassCompanionObjectMember() throws Exception {
runTest("compiler/testData/codegen/box/syntheticAccessors/protectedSuperclassCompanionObjectMember.kt");
}
@Test
@TestMetadata("superCallFromMultipleSubclasses.kt")
public void testSuperCallFromMultipleSubclasses() throws Exception {
@@ -20958,6 +20958,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
public void testPrivateTopLevelFun() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/withAccessor/privateTopLevelFun.kt");
}
@Test
@TestMetadata("protectedSuperclassCompanionObjectMember.kt")
public void testProtectedSuperclassCompanionObjectMember() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/withAccessor/protectedSuperclassCompanionObjectMember.kt");
}
}
}
@@ -39772,6 +39778,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/syntheticAccessors/protectedFromLambda.kt");
}
@Test
@TestMetadata("protectedSuperclassCompanionObjectMember.kt")
public void testProtectedSuperclassCompanionObjectMember() throws Exception {
runTest("compiler/testData/codegen/box/syntheticAccessors/protectedSuperclassCompanionObjectMember.kt");
}
@Test
@TestMetadata("superCallFromMultipleSubclasses.kt")
public void testSuperCallFromMultipleSubclasses() throws Exception {
@@ -17482,6 +17482,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class WithAccessor extends AbstractLightAnalysisModeTest {
@TestMetadata("protectedSuperclassCompanionObjectMember.kt")
public void ignoreProtectedSuperclassCompanionObjectMember() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/functionRefToJavaInterface/withAccessor/protectedSuperclassCompanionObjectMember.kt");
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
@@ -31765,6 +31770,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class SyntheticAccessors extends AbstractLightAnalysisModeTest {
@TestMetadata("protectedSuperclassCompanionObjectMember.kt")
public void ignoreProtectedSuperclassCompanionObjectMember() throws Exception {
runTest("compiler/testData/codegen/box/syntheticAccessors/protectedSuperclassCompanionObjectMember.kt");
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}