From d73d3c46e2848279af6b3667124bfa00d9087f38 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 9 Feb 2023 00:57:53 +0100 Subject: [PATCH] K2 JVM IR: fix access to Java field shadowed by Kotlin property #KT-56386 Fixed Co-authored-by: Mikhail Glukhikh --- .../FirBlackBoxCodegenTestGenerated.java | 18 ++-- .../codegen/FirBytecodeTextTestGenerated.java | 12 +++ .../FirPsiBlackBoxCodegenTestGenerated.java | 18 ++-- .../FirPsiBytecodeTextTestGenerated.java | 12 +++ .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 1 + ...SuperQualifierToJavaFieldAccessLowering.kt | 98 +++++++++++++++++++ .../jvm/lower/FunctionReferenceLowering.kt | 4 + .../javaFieldAndCompanionProperty.kt | 4 +- .../javaFieldAndKotlinInvisibleProperty.kt | 2 - ...KotlinInvisiblePropertyViaTypeParameter.kt | 2 - .../javaFieldAndKotlinPropertyReference.kt | 2 - .../javaFieldAndKotlinPropertySuperAccess.kt | 2 - ...eldAndKotlinPropertyWithComplexReceiver.kt | 2 - ...javaFieldAndKotlinPropertyWithSmartcast.kt | 2 - .../javaFieldKotlinPropertyJavaField.kt | 5 +- ...vaFieldKotlinPropertyJavaPackagePrivate.kt | 5 +- .../javaPackagePrivateClassAndPublicField.kt | 37 +++++++ ...gePrivateClassExposedViaProtectedStatic.kt | 36 +++++++ ...rotectedFieldAndKotlinInvisibleProperty.kt | 9 +- ...ieldAndKotlinInvisiblePropertyReference.kt | 16 ++- ...tedFieldAndKotlinInvisiblePropertyWrite.kt | 37 ------- .../codegen/bytecodeText/javaFields.kt | 13 ++- .../javaFieldsWithIntersectionTypes.kt | 15 ++- .../javaFieldsWithIntersectionTypes_k1.kt | 40 ++++++++ .../codegen/bytecodeText/javaFields_k1.kt | 26 +++++ .../codegen/BytecodeTextTestGenerated.java | 12 +-- .../IrBlackBoxCodegenTestGenerated.java | 18 ++-- .../codegen/IrBytecodeTextTestGenerated.java | 12 +++ 28 files changed, 363 insertions(+), 97 deletions(-) create mode 100644 compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddSuperQualifierToJavaFieldAccessLowering.kt create mode 100644 compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassAndPublicField.kt create mode 100644 compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassExposedViaProtectedStatic.kt delete mode 100644 compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyWrite.kt create mode 100644 compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes_k1.kt create mode 100644 compiler/testData/codegen/bytecodeText/javaFields_k1.kt diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index c779e7a5c1f..4511ea283ad 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -28737,6 +28737,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaInvisibleFieldAndKotlinPropertyReference.kt"); } + @Test + @TestMetadata("javaPackagePrivateClassAndPublicField.kt") + public void testJavaPackagePrivateClassAndPublicField() throws Exception { + runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassAndPublicField.kt"); + } + + @Test + @TestMetadata("javaPackagePrivateClassExposedViaProtectedStatic.kt") + public void testJavaPackagePrivateClassExposedViaProtectedStatic() throws Exception { + runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassExposedViaProtectedStatic.kt"); + } + @Test @TestMetadata("javaProtectedFieldAndKotlinInvisibleProperty.kt") public void testJavaProtectedFieldAndKotlinInvisibleProperty() throws Exception { @@ -28749,12 +28761,6 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyReference.kt"); } - @Test - @TestMetadata("javaProtectedFieldAndKotlinInvisiblePropertyWrite.kt") - public void testJavaProtectedFieldAndKotlinInvisiblePropertyWrite() throws Exception { - runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyWrite.kt"); - } - @Test @TestMetadata("javaProtectedFieldAndKotlinPropertyReference.kt") public void testJavaProtectedFieldAndKotlinPropertyReference() throws Exception { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java index c8cdbe442eb..8ef82969235 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBytecodeTextTestGenerated.java @@ -253,6 +253,18 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt"); } + @Test + @TestMetadata("javaFieldsWithIntersectionTypes_k1.kt") + public void testJavaFieldsWithIntersectionTypes_k1() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes_k1.kt"); + } + + @Test + @TestMetadata("javaFields_k1.kt") + public void testJavaFields_k1() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/javaFields_k1.kt"); + } + @Test @TestMetadata("javaStatics.kt") public void testJavaStatics() throws Exception { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java index 6e2ba5701b7..0bd3bcdf66d 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java @@ -28737,6 +28737,18 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaInvisibleFieldAndKotlinPropertyReference.kt"); } + @Test + @TestMetadata("javaPackagePrivateClassAndPublicField.kt") + public void testJavaPackagePrivateClassAndPublicField() throws Exception { + runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassAndPublicField.kt"); + } + + @Test + @TestMetadata("javaPackagePrivateClassExposedViaProtectedStatic.kt") + public void testJavaPackagePrivateClassExposedViaProtectedStatic() throws Exception { + runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassExposedViaProtectedStatic.kt"); + } + @Test @TestMetadata("javaProtectedFieldAndKotlinInvisibleProperty.kt") public void testJavaProtectedFieldAndKotlinInvisibleProperty() throws Exception { @@ -28749,12 +28761,6 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyReference.kt"); } - @Test - @TestMetadata("javaProtectedFieldAndKotlinInvisiblePropertyWrite.kt") - public void testJavaProtectedFieldAndKotlinInvisiblePropertyWrite() throws Exception { - runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyWrite.kt"); - } - @Test @TestMetadata("javaProtectedFieldAndKotlinPropertyReference.kt") public void testJavaProtectedFieldAndKotlinPropertyReference() throws Exception { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBytecodeTextTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBytecodeTextTestGenerated.java index 72ceb7a22fd..fb3a20e18a5 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBytecodeTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBytecodeTextTestGenerated.java @@ -253,6 +253,18 @@ public class FirPsiBytecodeTextTestGenerated extends AbstractFirPsiBytecodeTextT runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt"); } + @Test + @TestMetadata("javaFieldsWithIntersectionTypes_k1.kt") + public void testJavaFieldsWithIntersectionTypes_k1() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes_k1.kt"); + } + + @Test + @TestMetadata("javaFields_k1.kt") + public void testJavaFields_k1() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/javaFields_k1.kt"); + } + @Test @TestMetadata("javaStatics.kt") public void testJavaStatics() throws Exception { diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index dda5c522a91..670fe6a9018 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -376,6 +376,7 @@ private val jvmFilePhases = listOf( replaceKFunctionInvokeWithFunctionInvokePhase, kotlinNothingValueExceptionPhase, makePropertyDelegateMethodsStaticPhase, + addSuperQualifierToJavaFieldAccessPhase, renameFieldsPhase, fakeInliningLocalVariablesLowering, diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddSuperQualifierToJavaFieldAccessLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddSuperQualifierToJavaFieldAccessLowering.kt new file mode 100644 index 00000000000..0d74ef23574 --- /dev/null +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddSuperQualifierToJavaFieldAccessLowering.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2010-2023 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.phaser.makeIrFilePhase +import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrFieldAccessExpression +import org.jetbrains.kotlin.ir.types.getClass +import org.jetbrains.kotlin.ir.util.isClass +import org.jetbrains.kotlin.ir.util.isEnumClass +import org.jetbrains.kotlin.ir.util.isFromJava +import org.jetbrains.kotlin.ir.util.parentClassOrNull +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid + +internal val addSuperQualifierToJavaFieldAccessPhase = makeIrFilePhase( + { context -> + if (context.state.configuration.getBoolean(CommonConfigurationKeys.USE_FIR)) { + AddSuperQualifierToJavaFieldAccessLowering + } else { + FileLoweringPass.Empty + } + }, + name = "AddSuperQualifierToJavaFieldAccess", + description = "Make `\$delegate` methods for optimized delegated properties static", + // Property references need to be lowered to classes with field access inside. We can't perform this phase on unlowered property + // references as IrPropertyReference doesn't have superQualifierSymbol. + prerequisite = setOf(propertyReferencePhase), +) + +// This lowering changes field accesses so that codegen will correctly generate access to a public field in a Java superclass of a Kotlin +// class, even if that field is "shadowed" by a property in the Kotlin class, with a _private_ field with the same name. +// See KT-49507 and KT-48954 as good examples for cases we try to handle here. +private object AddSuperQualifierToJavaFieldAccessLowering : IrElementVisitorVoid, FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.accept(this, null) + } + + override fun visitElement(element: IrElement) { + element.acceptChildren(this, null) + } + + override fun visitFieldAccess(expression: IrFieldAccessExpression) { + val dispatchReceiver = expression.receiver + if (dispatchReceiver != null) { + expression.superQualifierSymbol = superQualifierSymbolForField(dispatchReceiver, expression.symbol.owner)?.symbol + } + super.visitFieldAccess(expression) + } + + // Note that we're abusing superQualifierSymbol to make codegen generate the correct field owner in the bytecode. + // However, currently there seems to be no better way to support it. + private fun superQualifierSymbolForField(dispatchReceiver: IrExpression, field: IrField): IrClass? { + if (field.correspondingPropertySymbol != null) return null + val originalContainingClass = field.parentClassOrNull ?: return null + val dispatchReceiverRepresentativeClass = dispatchReceiver.type.erasedUpperBound + // Find first Java super class to avoid possible visibility exposure & separate compilation problems + return getJavaFieldContainingClassSymbol(dispatchReceiverRepresentativeClass, originalContainingClass) + } + + // Note: dispatchReceiverRepresentativeClassifierSymbol here is the use-site receiver class, + // and originalContainingClass is the class which contains Java field we are trying to access + // ! Interfaces are out of our interests here ! + // This function returns a class symbol which: + // - is the most derived Java class in hierarchy which has no Kotlin base classes (including transitive ones) + // E.g. K2 <: J3 <: K1 <: J2 <: J1 ==> J2 is chosen + // We shouldn't allow base Kotlin classes to avoid possible clashes with invisible properties inside + private fun getJavaFieldContainingClassSymbol(dispatchReceiverRepresentativeClass: IrClass, originalContainingClass: IrClass): IrClass = + findMostSpecificJavaClassWithoutKotlinSuperclasses( + dispatchReceiverRepresentativeClass, originalContainingClass + )?.resultingClass ?: originalContainingClass + + private fun findMostSpecificJavaClassWithoutKotlinSuperclasses(current: IrClass, top: IrClass): SearchResult? { + if (current == top) return SearchResult(top, false) + + val superClass = current.superTypes.firstNotNullOfOrNull { supertype -> + supertype.getClass()?.takeIf { it.isClass || it.isEnumClass } + } ?: return null + + val result = findMostSpecificJavaClassWithoutKotlinSuperclasses(superClass, top) ?: return null + return if (current.isFromJava()) { + if (result.hasKotlinSuperclasses) result else SearchResult(current, false) + } else { + SearchResult(result.resultingClass, true) + } + } + + private class SearchResult(val resultingClass: IrClass, val hasKotlinSuperclasses: Boolean) +} diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt index 7ddd46191bd..f8da171d199 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt @@ -1013,6 +1013,10 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) // in the anonymous class for this callable reference. Technically it's incorrect because this field is not declared here. It would // be more correct to create a fake override but that seems like more work for no clear benefit. Codegen will generate the correct // field access anyway, even if the field is not present in this parent. + // Note that it is necessary to generate an access to the field whose parent is this anonymous class, and NOT some supertype like + // k.j.i.CallableReference, or k.j.i.FunctionReferenceImpl, because then AddSuperQualifierToJavaFieldAccess lowering would add + // superQualifierSymbol, which would break inlining of bound function references, since inliner will not understand how to transform + // this getfield instruction in the bytecode. internal fun IrClass.getReceiverField(context: JvmBackendContext): IrField = context.irFactory.buildField { name = Name.identifier("receiver") diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndCompanionProperty.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndCompanionProperty.kt index 198cde701d0..3766f3e9b69 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndCompanionProperty.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndCompanionProperty.kt @@ -1,7 +1,5 @@ // TARGET_BACKEND: JVM_IR -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_FIR: JVM_IR -// FIR_STATUS: accesses companion property backing field statically and fails (does not work in K1/JVM too) +// IGNORE_BACKEND_K1: JVM_IR // FILE: Base.java public class Base { diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinInvisibleProperty.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinInvisibleProperty.kt index d17336f801d..e145070ef45 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinInvisibleProperty.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinInvisibleProperty.kt @@ -1,7 +1,5 @@ // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// tried to access field Derived.a from class DerivedKt // FILE: BaseJava.java public class BaseJava { diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinInvisiblePropertyViaTypeParameter.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinInvisiblePropertyViaTypeParameter.kt index 95e7868512f..a29b974c6bd 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinInvisiblePropertyViaTypeParameter.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinInvisiblePropertyViaTypeParameter.kt @@ -1,7 +1,5 @@ // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// tried to access field Derived.a from class DerivedKt // FILE: BaseJava.java public class BaseJava { diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyReference.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyReference.kt index dda60526777..d2d58fdcf47 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyReference.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyReference.kt @@ -1,7 +1,5 @@ // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// FIR status: fails because of incorrect receiver in bytecode // Field VS property: case "reference" // FILE: BaseJava.java diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertySuperAccess.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertySuperAccess.kt index 9706e718c24..448982210e5 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertySuperAccess.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertySuperAccess.kt @@ -1,7 +1,5 @@ // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// tried to access field My.b from class Some // FILE: Jaba.java diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyWithComplexReceiver.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyWithComplexReceiver.kt index 1bf3a2c8caa..a5417cd86c4 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyWithComplexReceiver.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyWithComplexReceiver.kt @@ -1,7 +1,5 @@ // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// tried to access field My.a from class TestKt // FILE: Jaba.java diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyWithSmartcast.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyWithSmartcast.kt index f4ea77586dc..9fd12ce7e02 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyWithSmartcast.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldAndKotlinPropertyWithSmartcast.kt @@ -1,7 +1,5 @@ // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// tried to access field My.b from class TestKt // FILE: Jaba.java diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldKotlinPropertyJavaField.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldKotlinPropertyJavaField.kt index 4bd706caa14..acdc5ef0af2 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldKotlinPropertyJavaField.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldKotlinPropertyJavaField.kt @@ -1,7 +1,6 @@ // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// tried to access field B.f from class TestKt +// CHECK_BYTECODE_TEXT // FILE: A.java @@ -24,3 +23,5 @@ public class C extends B {} fun box(): String { return C().f } + +// 1 GETFIELD A.f diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldKotlinPropertyJavaPackagePrivate.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldKotlinPropertyJavaPackagePrivate.kt index a1fd63f5737..ee4ec0472df 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldKotlinPropertyJavaPackagePrivate.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaFieldKotlinPropertyJavaPackagePrivate.kt @@ -1,7 +1,6 @@ // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// tried to access field base.B.f from class TestKt +// CHECK_BYTECODE_TEXT // FILE: Y.java @@ -36,3 +35,5 @@ public class C extends B {} fun box(): String { return C().f } + +// 1 GETFIELD base/A.f diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassAndPublicField.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassAndPublicField.kt new file mode 100644 index 00000000000..115dce63d12 --- /dev/null +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassAndPublicField.kt @@ -0,0 +1,37 @@ +// TARGET_BACKEND: JVM_IR +// IGNORE_BACKEND_K1: JVM_IR +// CHECK_BYTECODE_TEXT +// FILE: a/VeryBase.java + +package a; + +class VeryBase { + public String foo = "OK"; +} + +// FILE: a/Base.java + +package a; + +public class Base extends VeryBase { +} + +// FILE: b/Intermediate.java + +package b; + +class Intermediate extends a.Base { +} + +// FILE: box.kt + +package b + +private class Final : Intermediate() { + private val foo = "FAIL" +} + +fun box(): String = + Final().foo + +// 1 GETFIELD b/Intermediate.foo diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassExposedViaProtectedStatic.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassExposedViaProtectedStatic.kt new file mode 100644 index 00000000000..c9205848f52 --- /dev/null +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassExposedViaProtectedStatic.kt @@ -0,0 +1,36 @@ +// TARGET_BACKEND: JVM_IR +// IGNORE_BACKEND_K1: JVM_IR +// CHECK_BYTECODE_TEXT +// FILE: p/PackagePrivateJavaClass.java + +package p; + +class PackagePrivateJavaClass { + public String foo = "OK"; +} + +// FILE: p/JavaWrapper.java + +package p; + +public class JavaWrapper { + protected static class JavaDerived extends PackagePrivateJavaClass {} +} + +// FILE: test.kt + +import p.JavaWrapper + +class KotlinWrapper : JavaWrapper() { + protected class KotlinDerived : JavaDerived() { + private val foo = "FAIL" + } + + fun bar() = KotlinDerived().foo +} + +fun box(): String { + return KotlinWrapper().bar() +} + +// 1 GETFIELD p/JavaWrapper\$JavaDerived.foo diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisibleProperty.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisibleProperty.kt index cb09445955a..f4b071bcbf2 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisibleProperty.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisibleProperty.kt @@ -1,7 +1,5 @@ // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// tried to access field derived.Intermediate.a from class derived.Derived // Field VS property: case "reference", protected field, invisible property // FILE: BaseJava.java @@ -9,7 +7,7 @@ package base; public class BaseJava { - protected String a = "OK"; + protected String a = ""; } // FILE: Derived.kt @@ -24,9 +22,14 @@ open class Intermediate : BaseJava() { class Derived : Intermediate() { fun foo() = a + + fun bar() { + a = "OK" + } } fun box(): String { val d = Derived() + d.bar() return d.foo() } diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyReference.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyReference.kt index bdde63b42ec..7d157b884c0 100644 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyReference.kt +++ b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyReference.kt @@ -1,7 +1,5 @@ // TARGET_BACKEND: JVM_IR // IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// tried to access field base.Intermediate.a from class base.Derived$foo$1 // Field VS property: case "reference", protected field in the same package, invisible property // FILE: BaseJava.java @@ -9,23 +7,31 @@ package base; public class BaseJava { - protected String a = "OK"; + protected String a = ""; } // FILE: Derived.kt -package base -// Note: this test should report an error when we are in different package +package derived + +import base.BaseJava open class Intermediate : BaseJava() { private val a = "FAIL" } +// TODO: remove suppress after dropping the relevant diagnostic +@Suppress("JAVA_SHADOWED_PROTECTED_FIELD_REFERENCE") class Derived : Intermediate() { fun foo() = this::a.get() + + fun bar() { + Derived::a.set(this, "OK") + } } fun box(): String { val d = Derived() + d.bar() return d.foo() } diff --git a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyWrite.kt b/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyWrite.kt deleted file mode 100644 index 14e6ef94629..00000000000 --- a/compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyWrite.kt +++ /dev/null @@ -1,37 +0,0 @@ -// TARGET_BACKEND: JVM_IR -// IGNORE_BACKEND_K1: JVM_IR -// IGNORE_BACKEND_K2: JVM_IR -// tried to access field derived.Intermediate.a from class derived.Derived -// Field VS property: case "reference", protected field, invisible property - -// FILE: BaseJava.java - -package base; - -public class BaseJava { - protected String a = ""; -} - -// FILE: Derived.kt - -package derived - -import base.BaseJava - -open class Intermediate : BaseJava() { - private val a = "FAIL" -} - -class Derived : Intermediate() { - fun foo() = a - - fun bar() { - a = "OK" - } -} - -fun box(): String { - val d = Derived() - d.bar() - return d.foo() -} diff --git a/compiler/testData/codegen/bytecodeText/javaFields.kt b/compiler/testData/codegen/bytecodeText/javaFields.kt index a3afb7bb6ff..5fa1d50a247 100644 --- a/compiler/testData/codegen/bytecodeText/javaFields.kt +++ b/compiler/testData/codegen/bytecodeText/javaFields.kt @@ -1,3 +1,7 @@ +// TARGET_BACKEND: JVM_IR +// IGNORE_BACKEND_K1: JVM_IR +// ^ See javaFields_k1.kt for a copy of this test for K1. + // FILE: Java1.java public class Java1 { public int f; @@ -16,8 +20,9 @@ open class Kotlin2 : Java2() { fun test1(j: Kotlin2) = j.f -// @Kotlin2.class: -// 1 GETFIELD Java2.f : I +// K2 generates access to Java1.f in both cases. The main motivation for this is to fix cases like KT-49507. +// Java1 in this case is the most specific Java superclass of Kotlin2 which has no Kotlin superclasses in its hierarchy. -// @TestKt.class: -// 1 GETFIELD Kotlin2.f : I \ No newline at end of file +// 2 GETFIELD Java1.f : I +// 0 GETFIELD Java2.f : I +// 0 GETFIELD Kotlin2.f : I diff --git a/compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt b/compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt index 93bcb38f0f4..b8f26bd04f0 100644 --- a/compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt +++ b/compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt @@ -1,3 +1,7 @@ +// TARGET_BACKEND: JVM_IR +// IGNORE_BACKEND_K1: JVM_IR +// ^ See javaFieldsWithIntersectionTypes_k1.kt for a copy of this test for K1. + // FILE: JFieldOwner.java public class JFieldOwner { @@ -30,8 +34,9 @@ fun test(b : Boolean) { } -// @TestKt.class: -// 1 GETFIELD JFieldOwner.f : I -// 1 PUTFIELD JFieldOwner.f : I -// 1 GETFIELD Mid.f : I -// 1 PUTFIELD Mid.f : I +// K2 generates access to Java1.f in both cases. The main motivation for this is to fix cases like KT-49507. + +// 2 GETFIELD JFieldOwner.f : I +// 2 PUTFIELD JFieldOwner.f : I +// 0 GETFIELD Mid.f : I +// 0 PUTFIELD Mid.f : I diff --git a/compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes_k1.kt b/compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes_k1.kt new file mode 100644 index 00000000000..1a47c96c305 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes_k1.kt @@ -0,0 +1,40 @@ +// IGNORE_BACKEND_K2: JVM_IR +// ^ See javaFieldsWithIntersectionTypes.kt for a copy of this test for K2. + +// FILE: JFieldOwner.java + +public class JFieldOwner { + public int f; +} + +// FILE: test.kt + +interface IFoo + +class Derived1 : JFieldOwner(), IFoo +class Derived2 : JFieldOwner(), IFoo + +open class Mid : JFieldOwner() +class DerivedThroughMid1 : Mid(), IFoo +class DerivedThroughMid2 : Mid(), IFoo + +fun test(b : Boolean) { + val d1 = Derived1() + val d2 = Derived2() + val k = if (b) d1 else d2 + k.f = 42 + k.f + + val md1 = DerivedThroughMid1() + val md2 = DerivedThroughMid2() + val mk = if (b) md1 else md2 + mk.f = 44 + mk.f + +} + +// @TestKt.class: +// 1 GETFIELD JFieldOwner.f : I +// 1 PUTFIELD JFieldOwner.f : I +// 1 GETFIELD Mid.f : I +// 1 PUTFIELD Mid.f : I diff --git a/compiler/testData/codegen/bytecodeText/javaFields_k1.kt b/compiler/testData/codegen/bytecodeText/javaFields_k1.kt new file mode 100644 index 00000000000..12cae3c63ed --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/javaFields_k1.kt @@ -0,0 +1,26 @@ +// IGNORE_BACKEND_K2: JVM_IR +// ^ See javaFields.kt for a copy of this test for K2. + +// FILE: Java1.java +public class Java1 { + public int f; +} + +// FILE: Java2.java +public class Java2 extends Kotlin1 { +} + +// FILE: test.kt +open class Kotlin1 : Java1() + +open class Kotlin2 : Java2() { + fun getF() = super.f +} + +fun test1(j: Kotlin2) = j.f + +// @Kotlin2.class: +// 1 GETFIELD Java2.f : I + +// @TestKt.class: +// 1 GETFIELD Kotlin2.f : I diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java index a5bd58db061..504125d10bb 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BytecodeTextTestGenerated.java @@ -230,15 +230,15 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { } @Test - @TestMetadata("javaFields.kt") - public void testJavaFields() throws Exception { - runTest("compiler/testData/codegen/bytecodeText/javaFields.kt"); + @TestMetadata("javaFieldsWithIntersectionTypes_k1.kt") + public void testJavaFieldsWithIntersectionTypes_k1() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes_k1.kt"); } @Test - @TestMetadata("javaFieldsWithIntersectionTypes.kt") - public void testJavaFieldsWithIntersectionTypes() throws Exception { - runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt"); + @TestMetadata("javaFields_k1.kt") + public void testJavaFields_k1() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/javaFields_k1.kt"); } @Test diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index e6aa1f113df..3801c387292 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -28737,6 +28737,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaInvisibleFieldAndKotlinPropertyReference.kt"); } + @Test + @TestMetadata("javaPackagePrivateClassAndPublicField.kt") + public void testJavaPackagePrivateClassAndPublicField() throws Exception { + runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassAndPublicField.kt"); + } + + @Test + @TestMetadata("javaPackagePrivateClassExposedViaProtectedStatic.kt") + public void testJavaPackagePrivateClassExposedViaProtectedStatic() throws Exception { + runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaPackagePrivateClassExposedViaProtectedStatic.kt"); + } + @Test @TestMetadata("javaProtectedFieldAndKotlinInvisibleProperty.kt") public void testJavaProtectedFieldAndKotlinInvisibleProperty() throws Exception { @@ -28749,12 +28761,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyReference.kt"); } - @Test - @TestMetadata("javaProtectedFieldAndKotlinInvisiblePropertyWrite.kt") - public void testJavaProtectedFieldAndKotlinInvisiblePropertyWrite() throws Exception { - runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyWrite.kt"); - } - @Test @TestMetadata("javaProtectedFieldAndKotlinPropertyReference.kt") public void testJavaProtectedFieldAndKotlinPropertyReference() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java index 82261908407..5d7878c2d74 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBytecodeTextTestGenerated.java @@ -253,6 +253,18 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt"); } + @Test + @TestMetadata("javaFieldsWithIntersectionTypes_k1.kt") + public void testJavaFieldsWithIntersectionTypes_k1() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes_k1.kt"); + } + + @Test + @TestMetadata("javaFields_k1.kt") + public void testJavaFields_k1() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/javaFields_k1.kt"); + } + @Test @TestMetadata("javaStatics.kt") public void testJavaStatics() throws Exception {