JVM_IR JavaSamConversionEqualsHashCode

This commit is contained in:
Dmitry Petrov
2022-03-02 16:15:17 +03:00
committed by Space
parent 870cdfd226
commit fdb01d96f0
15 changed files with 204 additions and 7 deletions
@@ -44774,6 +44774,34 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/sam/equality/simpleLambdas.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode")
@TestDataPath("$PROJECT_ROOT")
public class JavaSamWithEqualsHashCode {
@Test
public void testAllFilesPresentInJavaSamWithEqualsHashCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("classImplementingFunctionInterface.kt")
public void testClassImplementingFunctionInterface() throws Exception {
runTest("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode/classImplementingFunctionInterface.kt");
}
@Test
@TestMetadata("functionReference.kt")
public void testFunctionReference() throws Exception {
runTest("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode/functionReference.kt");
}
@Test
@TestMetadata("functionalExpression.kt")
public void testFunctionalExpression() throws Exception {
runTest("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode/functionalExpression.kt");
}
}
}
@Nested
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmSymbols
import org.jetbrains.kotlin.backend.jvm.ir.*
import org.jetbrains.kotlin.backend.jvm.lower.indy.*
import org.jetbrains.kotlin.config.JvmClosureGenerationScheme
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
@@ -75,6 +76,9 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
private val shouldGenerateIndyLambdas =
context.state.lambdasScheme == JvmClosureGenerationScheme.INDY
private val isJavaSamConversionWithEqualsHashCode =
context.state.languageVersionSettings.supportsFeature(LanguageFeature.JavaSamConversionEqualsHashCode)
override fun visitBlock(expression: IrBlock): IrExpression {
if (!expression.origin.isLambda)
return super.visitBlock(expression)
@@ -444,7 +448,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
private fun canGenerateIndySamConversionOnFunctionalExpression(samSuperType: IrType, expression: IrExpression): Boolean {
val samClass = samSuperType.classOrNull
?: throw AssertionError("Class type expected: ${samSuperType.render()}")
if (!samClass.owner.isFromJava())
if (!samClass.owner.isFromJava() || isJavaSamConversionWithEqualsHashCode)
return false
if (expression is IrBlock && expression.origin == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE)
return false
@@ -595,7 +599,8 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
private val isKotlinFunInterface = samInterface != null && !samInterface.isFromJava()
private val needToGenerateSamEqualsHashCodeMethods =
isKotlinFunInterface && (isAdaptedReference || !isLambda)
(isKotlinFunInterface || isJavaSamConversionWithEqualsHashCode) &&
(isAdaptedReference || !isLambda)
private val superType =
samSuperType
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope
import org.jetbrains.kotlin.backend.jvm.ir.rawType
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
@@ -34,6 +35,10 @@ internal val singleAbstractMethodPhase = makeIrFilePhase(
)
private class JvmSingleAbstractMethodLowering(context: JvmBackendContext) : SingleAbstractMethodLowering(context) {
private val isJavaSamConversionWithEqualsHashCode =
context.state.languageVersionSettings.supportsFeature(LanguageFeature.JavaSamConversionEqualsHashCode)
override val inInlineFunctionScope: Boolean
get() = allScopes.any { it.irElement.safeAs<IrDeclaration>()?.isInPublicInlineScope == true }
@@ -56,5 +61,6 @@ private class JvmSingleAbstractMethodLowering(context: JvmBackendContext) : Sing
private val IrType.isKotlinFunInterface: Boolean
get() = getClass()?.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
override val IrType.needEqualsHashCodeMethods get() = isKotlinFunInterface
override val IrType.needEqualsHashCodeMethods
get() = isKotlinFunInterface || isJavaSamConversionWithEqualsHashCode
}
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.backend.jvm.ir.getSingleAbstractMethod
import org.jetbrains.kotlin.backend.jvm.ir.isCompiledToJvmDefault
import org.jetbrains.kotlin.backend.jvm.lower.findInterfaceImplementation
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.declarations.buildClass
@@ -80,6 +81,10 @@ internal class LambdaMetafactoryArgumentsBuilder(
private val context: JvmBackendContext,
private val crossinlineLambdas: Set<IrSimpleFunction>
) {
private val isJavaSamConversionWithEqualsHashCode =
context.state.languageVersionSettings.supportsFeature(LanguageFeature.JavaSamConversionEqualsHashCode)
/**
* @see java.lang.invoke.LambdaMetafactory
*/
@@ -99,12 +104,10 @@ 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.isLambda && !samClass.isFromJava()) {
if (!reference.origin.isLambda && (!samClass.isFromJava() || isJavaSamConversionWithEqualsHashCode)) {
semanticsHazard = true
}
// Don't use JDK LambdaMetafactory for serializable lambdas
// TODO implement support for serializable lambdas with LambdaMetafactory (requires additional code for deserialization)
if (samClass.isInheritedFromSerializable()) {
shouldBeSerializable = true
}
@@ -133,7 +136,7 @@ internal class LambdaMetafactoryArgumentsBuilder(
functionHazard = true
}
// Can't use invokedynamic if the referenced function has to be inlined for correct semantics
// Can't use invokedynamic if the referenced function has to be inlined for correct semantics.
// Also in some cases like `private inline fun` we'd need accessors, which `SyntheticAccessorLowering`
// won't generate under the assumption that the inline function will be inlined. Plus if the function
// is in a different module we should probably copy it anyway (and regenerate all objects in it).
@@ -0,0 +1,25 @@
// !LANGUAGE: +JavaSamConversionEqualsHashCode
// TARGET_BACKEND: JVM_IR
// FULL_JDK
class F(val v: String) : () -> Unit {
override fun invoke() {}
override fun equals(other: Any?): Boolean =
other is F && other.v == v
override fun hashCode(): Int =
v.hashCode()
}
fun box(): String {
val r1 = Runnable(F("abc"))
val r2 = Runnable(F("abc"))
if (r1 != r2)
return "r1 != r2"
if (r1.hashCode() != r2.hashCode())
return "r1.hashCode() != r2.hashCode()"
return "OK"
}
@@ -0,0 +1,17 @@
// !LANGUAGE: +JavaSamConversionEqualsHashCode
// TARGET_BACKEND: JVM_IR
// FULL_JDK
fun foo() {}
fun box(): String {
val r1 = Runnable(::foo)
val r2 = Runnable(::foo)
if (r1 != r2)
return "r1 != r2"
if (r1.hashCode() != r2.hashCode())
return "r1.hashCode() != r2.hashCode()"
return "OK"
}
@@ -0,0 +1,16 @@
// !LANGUAGE: +JavaSamConversionEqualsHashCode
// TARGET_BACKEND: JVM_IR
// FULL_JDK
fun box(): String {
val lambda: () -> Unit = { }
val r1 = Runnable(lambda)
val r2 = Runnable(lambda)
if (r1 != r2)
return "r1 != r2"
if (r1.hashCode() != r2.hashCode())
return "r1.hashCode() != r2.hashCode()"
return "OK"
}
@@ -44252,6 +44252,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/sam/equality/simpleLambdas.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode")
@TestDataPath("$PROJECT_ROOT")
public class JavaSamWithEqualsHashCode {
@Test
public void testAllFilesPresentInJavaSamWithEqualsHashCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
}
}
@Nested
@@ -44774,6 +44774,34 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/sam/equality/simpleLambdas.kt");
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode")
@TestDataPath("$PROJECT_ROOT")
public class JavaSamWithEqualsHashCode {
@Test
public void testAllFilesPresentInJavaSamWithEqualsHashCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("classImplementingFunctionInterface.kt")
public void testClassImplementingFunctionInterface() throws Exception {
runTest("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode/classImplementingFunctionInterface.kt");
}
@Test
@TestMetadata("functionReference.kt")
public void testFunctionReference() throws Exception {
runTest("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode/functionReference.kt");
}
@Test
@TestMetadata("functionalExpression.kt")
public void testFunctionalExpression() throws Exception {
runTest("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode/functionalExpression.kt");
}
}
}
@Nested
@@ -35755,6 +35755,19 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/sam/equality/simpleLambdas.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class JavaSamWithEqualsHashCode extends AbstractLightAnalysisModeTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInJavaSamWithEqualsHashCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
}
}
@TestMetadata("compiler/testData/codegen/box/sameFileInSourceAndDependencies")
@@ -290,6 +290,7 @@ enum class LanguageFeature(
ContextReceivers(sinceVersion = null, defaultState = State.DISABLED, kind = UNSTABLE_FEATURE),
GenericInlineClassParameter(sinceVersion = null, defaultState = State.ENABLED_WITH_WARNING, kind = UNSTABLE_FEATURE),
ValueClasses(sinceVersion = null, defaultState = State.DISABLED, kind = UNSTABLE_FEATURE),
JavaSamConversionEqualsHashCode(sinceVersion = null, defaultState = State.DISABLED, kind = UNSTABLE_FEATURE),
;
val presentableName: String
@@ -32290,6 +32290,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode")
@TestDataPath("$PROJECT_ROOT")
public class JavaSamWithEqualsHashCode {
@Test
public void testAllFilesPresentInJavaSamWithEqualsHashCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
}
}
@Nested
@@ -32392,6 +32392,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode")
@TestDataPath("$PROJECT_ROOT")
public class JavaSamWithEqualsHashCode {
@Test
public void testAllFilesPresentInJavaSamWithEqualsHashCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
}
}
@Nested
@@ -26982,6 +26982,19 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/equality"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
}
@TestMetadata("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class JavaSamWithEqualsHashCode extends AbstractIrCodegenBoxWasmTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
}
public void testAllFilesPresentInJavaSamWithEqualsHashCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
}
}
@TestMetadata("compiler/testData/codegen/box/sameFileInSourceAndDependencies")
@@ -35331,6 +35331,18 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/equality"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true);
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode")
@TestDataPath("$PROJECT_ROOT")
@Tag("codegen")
@UseExtTestCaseGroupProvider()
public class JavaSamWithEqualsHashCode {
@Test
public void testAllFilesPresentInJavaSamWithEqualsHashCode() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/sam/javaSamWithEqualsHashCode"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true);
}
}
}
@Nested