K2 JVM IR: fix access to Java field shadowed by Kotlin property
#KT-56386 Fixed Co-authored-by: Mikhail Glukhikh <Mikhail.Glukhikh@jetbrains.com>
This commit is contained in:
+12
-6
@@ -28737,6 +28737,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaInvisibleFieldAndKotlinPropertyReference.kt");
|
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
|
@Test
|
||||||
@TestMetadata("javaProtectedFieldAndKotlinInvisibleProperty.kt")
|
@TestMetadata("javaProtectedFieldAndKotlinInvisibleProperty.kt")
|
||||||
public void testJavaProtectedFieldAndKotlinInvisibleProperty() throws Exception {
|
public void testJavaProtectedFieldAndKotlinInvisibleProperty() throws Exception {
|
||||||
@@ -28749,12 +28761,6 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyReference.kt");
|
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
|
@Test
|
||||||
@TestMetadata("javaProtectedFieldAndKotlinPropertyReference.kt")
|
@TestMetadata("javaProtectedFieldAndKotlinPropertyReference.kt")
|
||||||
public void testJavaProtectedFieldAndKotlinPropertyReference() throws Exception {
|
public void testJavaProtectedFieldAndKotlinPropertyReference() throws Exception {
|
||||||
|
|||||||
+12
@@ -253,6 +253,18 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt");
|
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
|
@Test
|
||||||
@TestMetadata("javaStatics.kt")
|
@TestMetadata("javaStatics.kt")
|
||||||
public void testJavaStatics() throws Exception {
|
public void testJavaStatics() throws Exception {
|
||||||
|
|||||||
+12
-6
@@ -28737,6 +28737,18 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
|||||||
runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaInvisibleFieldAndKotlinPropertyReference.kt");
|
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
|
@Test
|
||||||
@TestMetadata("javaProtectedFieldAndKotlinInvisibleProperty.kt")
|
@TestMetadata("javaProtectedFieldAndKotlinInvisibleProperty.kt")
|
||||||
public void testJavaProtectedFieldAndKotlinInvisibleProperty() throws Exception {
|
public void testJavaProtectedFieldAndKotlinInvisibleProperty() throws Exception {
|
||||||
@@ -28749,12 +28761,6 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
|||||||
runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyReference.kt");
|
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
|
@Test
|
||||||
@TestMetadata("javaProtectedFieldAndKotlinPropertyReference.kt")
|
@TestMetadata("javaProtectedFieldAndKotlinPropertyReference.kt")
|
||||||
public void testJavaProtectedFieldAndKotlinPropertyReference() throws Exception {
|
public void testJavaProtectedFieldAndKotlinPropertyReference() throws Exception {
|
||||||
|
|||||||
+12
@@ -253,6 +253,18 @@ public class FirPsiBytecodeTextTestGenerated extends AbstractFirPsiBytecodeTextT
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt");
|
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
|
@Test
|
||||||
@TestMetadata("javaStatics.kt")
|
@TestMetadata("javaStatics.kt")
|
||||||
public void testJavaStatics() throws Exception {
|
public void testJavaStatics() throws Exception {
|
||||||
|
|||||||
@@ -376,6 +376,7 @@ private val jvmFilePhases = listOf(
|
|||||||
replaceKFunctionInvokeWithFunctionInvokePhase,
|
replaceKFunctionInvokeWithFunctionInvokePhase,
|
||||||
kotlinNothingValueExceptionPhase,
|
kotlinNothingValueExceptionPhase,
|
||||||
makePropertyDelegateMethodsStaticPhase,
|
makePropertyDelegateMethodsStaticPhase,
|
||||||
|
addSuperQualifierToJavaFieldAccessPhase,
|
||||||
|
|
||||||
renameFieldsPhase,
|
renameFieldsPhase,
|
||||||
fakeInliningLocalVariablesLowering,
|
fakeInliningLocalVariablesLowering,
|
||||||
|
|||||||
+98
@@ -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)
|
||||||
|
}
|
||||||
+4
@@ -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
|
// 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
|
// 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.
|
// 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 =
|
internal fun IrClass.getReceiverField(context: JvmBackendContext): IrField =
|
||||||
context.irFactory.buildField {
|
context.irFactory.buildField {
|
||||||
name = Name.identifier("receiver")
|
name = Name.identifier("receiver")
|
||||||
|
|||||||
+1
-3
@@ -1,7 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND_K1: JVM_IR
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// FIR_STATUS: accesses companion property backing field statically and fails (does not work in K1/JVM too)
|
|
||||||
|
|
||||||
// FILE: Base.java
|
// FILE: Base.java
|
||||||
public class Base {
|
public class Base {
|
||||||
|
|||||||
Vendored
-2
@@ -1,7 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND_K1: JVM_IR
|
// IGNORE_BACKEND_K1: JVM_IR
|
||||||
// IGNORE_BACKEND_K2: JVM_IR
|
|
||||||
// tried to access field Derived.a from class DerivedKt
|
|
||||||
|
|
||||||
// FILE: BaseJava.java
|
// FILE: BaseJava.java
|
||||||
public class BaseJava {
|
public class BaseJava {
|
||||||
|
|||||||
-2
@@ -1,7 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND_K1: JVM_IR
|
// IGNORE_BACKEND_K1: JVM_IR
|
||||||
// IGNORE_BACKEND_K2: JVM_IR
|
|
||||||
// tried to access field Derived.a from class DerivedKt
|
|
||||||
|
|
||||||
// FILE: BaseJava.java
|
// FILE: BaseJava.java
|
||||||
public class BaseJava {
|
public class BaseJava {
|
||||||
|
|||||||
Vendored
-2
@@ -1,7 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND_K1: 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"
|
// Field VS property: case "reference"
|
||||||
|
|
||||||
// FILE: BaseJava.java
|
// FILE: BaseJava.java
|
||||||
|
|||||||
Vendored
-2
@@ -1,7 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND_K1: JVM_IR
|
// IGNORE_BACKEND_K1: JVM_IR
|
||||||
// IGNORE_BACKEND_K2: JVM_IR
|
|
||||||
// tried to access field My.b from class Some
|
|
||||||
|
|
||||||
// FILE: Jaba.java
|
// FILE: Jaba.java
|
||||||
|
|
||||||
|
|||||||
-2
@@ -1,7 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND_K1: JVM_IR
|
// IGNORE_BACKEND_K1: JVM_IR
|
||||||
// IGNORE_BACKEND_K2: JVM_IR
|
|
||||||
// tried to access field My.a from class TestKt
|
|
||||||
|
|
||||||
// FILE: Jaba.java
|
// FILE: Jaba.java
|
||||||
|
|
||||||
|
|||||||
Vendored
-2
@@ -1,7 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND_K1: JVM_IR
|
// IGNORE_BACKEND_K1: JVM_IR
|
||||||
// IGNORE_BACKEND_K2: JVM_IR
|
|
||||||
// tried to access field My.b from class TestKt
|
|
||||||
|
|
||||||
// FILE: Jaba.java
|
// FILE: Jaba.java
|
||||||
|
|
||||||
|
|||||||
Vendored
+3
-2
@@ -1,7 +1,6 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND_K1: JVM_IR
|
// IGNORE_BACKEND_K1: JVM_IR
|
||||||
// IGNORE_BACKEND_K2: JVM_IR
|
// CHECK_BYTECODE_TEXT
|
||||||
// tried to access field B.f from class TestKt
|
|
||||||
|
|
||||||
// FILE: A.java
|
// FILE: A.java
|
||||||
|
|
||||||
@@ -24,3 +23,5 @@ public class C extends B {}
|
|||||||
fun box(): String {
|
fun box(): String {
|
||||||
return C().f
|
return C().f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1 GETFIELD A.f
|
||||||
|
|||||||
+3
-2
@@ -1,7 +1,6 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND_K1: JVM_IR
|
// IGNORE_BACKEND_K1: JVM_IR
|
||||||
// IGNORE_BACKEND_K2: JVM_IR
|
// CHECK_BYTECODE_TEXT
|
||||||
// tried to access field base.B.f from class TestKt
|
|
||||||
|
|
||||||
// FILE: Y.java
|
// FILE: Y.java
|
||||||
|
|
||||||
@@ -36,3 +35,5 @@ public class C extends B {}
|
|||||||
fun box(): String {
|
fun box(): String {
|
||||||
return C().f
|
return C().f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1 GETFIELD base/A.f
|
||||||
|
|||||||
Vendored
+37
@@ -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
|
||||||
+36
@@ -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
|
||||||
+6
-3
@@ -1,7 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND_K1: 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
|
// Field VS property: case "reference", protected field, invisible property
|
||||||
|
|
||||||
// FILE: BaseJava.java
|
// FILE: BaseJava.java
|
||||||
@@ -9,7 +7,7 @@
|
|||||||
package base;
|
package base;
|
||||||
|
|
||||||
public class BaseJava {
|
public class BaseJava {
|
||||||
protected String a = "OK";
|
protected String a = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// FILE: Derived.kt
|
// FILE: Derived.kt
|
||||||
@@ -24,9 +22,14 @@ open class Intermediate : BaseJava() {
|
|||||||
|
|
||||||
class Derived : Intermediate() {
|
class Derived : Intermediate() {
|
||||||
fun foo() = a
|
fun foo() = a
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
a = "OK"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val d = Derived()
|
val d = Derived()
|
||||||
|
d.bar()
|
||||||
return d.foo()
|
return d.foo()
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-5
@@ -1,7 +1,5 @@
|
|||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND_K1: 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
|
// Field VS property: case "reference", protected field in the same package, invisible property
|
||||||
|
|
||||||
// FILE: BaseJava.java
|
// FILE: BaseJava.java
|
||||||
@@ -9,23 +7,31 @@
|
|||||||
package base;
|
package base;
|
||||||
|
|
||||||
public class BaseJava {
|
public class BaseJava {
|
||||||
protected String a = "OK";
|
protected String a = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// FILE: Derived.kt
|
// FILE: Derived.kt
|
||||||
|
|
||||||
package base
|
package derived
|
||||||
// Note: this test should report an error when we are in different package
|
|
||||||
|
import base.BaseJava
|
||||||
|
|
||||||
open class Intermediate : BaseJava() {
|
open class Intermediate : BaseJava() {
|
||||||
private val a = "FAIL"
|
private val a = "FAIL"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove suppress after dropping the relevant diagnostic
|
||||||
|
@Suppress("JAVA_SHADOWED_PROTECTED_FIELD_REFERENCE")
|
||||||
class Derived : Intermediate() {
|
class Derived : Intermediate() {
|
||||||
fun foo() = this::a.get()
|
fun foo() = this::a.get()
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
Derived::a.set(this, "OK")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val d = Derived()
|
val d = Derived()
|
||||||
|
d.bar()
|
||||||
return d.foo()
|
return d.foo()
|
||||||
}
|
}
|
||||||
|
|||||||
-37
@@ -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()
|
|
||||||
}
|
|
||||||
+9
-4
@@ -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
|
// FILE: Java1.java
|
||||||
public class Java1 {
|
public class Java1 {
|
||||||
public int f;
|
public int f;
|
||||||
@@ -16,8 +20,9 @@ open class Kotlin2 : Java2() {
|
|||||||
|
|
||||||
fun test1(j: Kotlin2) = j.f
|
fun test1(j: Kotlin2) = j.f
|
||||||
|
|
||||||
// @Kotlin2.class:
|
// K2 generates access to Java1.f in both cases. The main motivation for this is to fix cases like KT-49507.
|
||||||
// 1 GETFIELD Java2.f : I
|
// Java1 in this case is the most specific Java superclass of Kotlin2 which has no Kotlin superclasses in its hierarchy.
|
||||||
|
|
||||||
// @TestKt.class:
|
// 2 GETFIELD Java1.f : I
|
||||||
// 1 GETFIELD Kotlin2.f : I
|
// 0 GETFIELD Java2.f : I
|
||||||
|
// 0 GETFIELD Kotlin2.f : I
|
||||||
|
|||||||
@@ -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
|
// FILE: JFieldOwner.java
|
||||||
|
|
||||||
public class JFieldOwner {
|
public class JFieldOwner {
|
||||||
@@ -30,8 +34,9 @@ fun test(b : Boolean) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TestKt.class:
|
// K2 generates access to Java1.f in both cases. The main motivation for this is to fix cases like KT-49507.
|
||||||
// 1 GETFIELD JFieldOwner.f : I
|
|
||||||
// 1 PUTFIELD JFieldOwner.f : I
|
// 2 GETFIELD JFieldOwner.f : I
|
||||||
// 1 GETFIELD Mid.f : I
|
// 2 PUTFIELD JFieldOwner.f : I
|
||||||
// 1 PUTFIELD Mid.f : I
|
// 0 GETFIELD Mid.f : I
|
||||||
|
// 0 PUTFIELD Mid.f : I
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
+6
-6
@@ -230,15 +230,15 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("javaFields.kt")
|
@TestMetadata("javaFieldsWithIntersectionTypes_k1.kt")
|
||||||
public void testJavaFields() throws Exception {
|
public void testJavaFieldsWithIntersectionTypes_k1() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/javaFields.kt");
|
runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes_k1.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("javaFieldsWithIntersectionTypes.kt")
|
@TestMetadata("javaFields_k1.kt")
|
||||||
public void testJavaFieldsWithIntersectionTypes() throws Exception {
|
public void testJavaFields_k1() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt");
|
runTest("compiler/testData/codegen/bytecodeText/javaFields_k1.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+12
-6
@@ -28737,6 +28737,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaInvisibleFieldAndKotlinPropertyReference.kt");
|
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
|
@Test
|
||||||
@TestMetadata("javaProtectedFieldAndKotlinInvisibleProperty.kt")
|
@TestMetadata("javaProtectedFieldAndKotlinInvisibleProperty.kt")
|
||||||
public void testJavaProtectedFieldAndKotlinInvisibleProperty() throws Exception {
|
public void testJavaProtectedFieldAndKotlinInvisibleProperty() throws Exception {
|
||||||
@@ -28749,12 +28761,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/javaFieldAndKotlinProperty/javaProtectedFieldAndKotlinInvisiblePropertyReference.kt");
|
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
|
@Test
|
||||||
@TestMetadata("javaProtectedFieldAndKotlinPropertyReference.kt")
|
@TestMetadata("javaProtectedFieldAndKotlinPropertyReference.kt")
|
||||||
public void testJavaProtectedFieldAndKotlinPropertyReference() throws Exception {
|
public void testJavaProtectedFieldAndKotlinPropertyReference() throws Exception {
|
||||||
|
|||||||
+12
@@ -253,6 +253,18 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/javaFieldsWithIntersectionTypes.kt");
|
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
|
@Test
|
||||||
@TestMetadata("javaStatics.kt")
|
@TestMetadata("javaStatics.kt")
|
||||||
public void testJavaStatics() throws Exception {
|
public void testJavaStatics() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user