diff --git a/compiler/testData/codegen/box/reflection/multifileClasses/callFunctionsInMultifileClass.kt b/compiler/testData/codegen/box/reflection/multifileClasses/callFunctionsInMultifileClass.kt new file mode 100644 index 00000000000..6f8ec65e68c --- /dev/null +++ b/compiler/testData/codegen/box/reflection/multifileClasses/callFunctionsInMultifileClass.kt @@ -0,0 +1,32 @@ +// WITH_REFLECT +// FILE: Test1.kt + +@file:kotlin.jvm.JvmName("Test") +@file:kotlin.jvm.JvmMultifileClass +package test + +import kotlin.test.assertEquals + +fun getX() = 1 + +fun box(): String { + assertEquals("getX", ::getX.name) + assertEquals("getY", ::getY.name) + assertEquals("getZ", ::getZ.name) + + assertEquals(1, ::getX.call()) + assertEquals(239, ::getY.call()) + assertEquals(42, ::getZ.callBy(emptyMap())) + + return "OK" +} + +// FILE: Test2.kt + +@file:kotlin.jvm.JvmName("Test") +@file:kotlin.jvm.JvmMultifileClass +package test + +fun getY() = 239 + +fun getZ(value: Int = 42) = value diff --git a/compiler/testData/codegen/box/reflection/multifileClasses/callPropertiesInMultifileClass.kt b/compiler/testData/codegen/box/reflection/multifileClasses/callPropertiesInMultifileClass.kt new file mode 100644 index 00000000000..dc4040474fa --- /dev/null +++ b/compiler/testData/codegen/box/reflection/multifileClasses/callPropertiesInMultifileClass.kt @@ -0,0 +1,42 @@ +// KT-11447 Multifile declaration causes IAE: Method can not access a member of class +// WITH_REFLECT +// FILE: Test1.kt + +@file:kotlin.jvm.JvmName("Test") +@file:kotlin.jvm.JvmMultifileClass +package test + +import kotlin.test.assertEquals + +var x = 1 + +fun box(): String { + assertEquals("x", ::x.name) + assertEquals("y", ::y.name) + assertEquals("MAGIC_NUMBER", ::MAGIC_NUMBER.name) + + assertEquals(1, ::x.call()) + assertEquals(1, ::x.getter.call()) + + assertEquals(239, ::y.call()) + assertEquals(239, ::y.getter.call()) + + assertEquals(42, ::MAGIC_NUMBER.call()) + assertEquals(42, ::MAGIC_NUMBER.getter.call()) + + assertEquals(Unit, ::x.setter.call(2)) + assertEquals(2, ::x.call()) + assertEquals(2, ::x.getter.call()) + + return "OK" +} + +// FILE: Test2.kt + +@file:kotlin.jvm.JvmName("Test") +@file:kotlin.jvm.JvmMultifileClass +package test + +val y = 239 + +const val MAGIC_NUMBER = 42 diff --git a/compiler/testData/codegen/box/reflection/multifileClasses/javaFieldForVarAndConstVal.kt b/compiler/testData/codegen/box/reflection/multifileClasses/javaFieldForVarAndConstVal.kt new file mode 100644 index 00000000000..870043e54a1 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/multifileClasses/javaFieldForVarAndConstVal.kt @@ -0,0 +1,52 @@ +// WITH_REFLECT +// FULL_JDK +// FILE: 1.kt + +@file:kotlin.jvm.JvmName("Test") +@file:kotlin.jvm.JvmMultifileClass +package test + +import kotlin.reflect.jvm.* +import kotlin.test.assertEquals + +fun testX() { + val field = ::x.javaField ?: throw AssertionError("No java field for ${::x.name}") + + try { + field.get(null) + throw AssertionError("Fail: field.get should fail because the field is private") + } + catch (e: IllegalAccessException) { + // OK + } + + field.setAccessible(true) + assertEquals("I am x", field.get(null)) + field.set(null, "OK") +} + +fun testY() { + val field = ::y.javaField ?: throw AssertionError("No java field for ${::y.name}") + + assertEquals("I am const y", field.get(null)) + + // Accessible = false should have no effect because the field is public + field.setAccessible(false) + + assertEquals("I am const y", field.get(null)) +} + +fun box(): String { + testX() + testY() + return x +} + +// FILE: 2.kt + +@file:kotlin.jvm.JvmName("Test") +@file:kotlin.jvm.JvmMultifileClass +package test + +var x = "I am x" +const val y = "I am const y" diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 66cef1aec1b..943459ffab3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -11485,6 +11485,33 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/reflection/multifileClasses") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class MultifileClasses extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInMultifileClasses() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/multifileClasses"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("callFunctionsInMultifileClass.kt") + public void testCallFunctionsInMultifileClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/multifileClasses/callFunctionsInMultifileClass.kt"); + doTest(fileName); + } + + @TestMetadata("callPropertiesInMultifileClass.kt") + public void testCallPropertiesInMultifileClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/multifileClasses/callPropertiesInMultifileClass.kt"); + doTest(fileName); + } + + @TestMetadata("javaFieldForVarAndConstVal.kt") + public void testJavaFieldForVarAndConstVal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/reflection/multifileClasses/javaFieldForVarAndConstVal.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/reflection/noReflectAtRuntime") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt index 84a4be1d113..9c201791551 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt @@ -39,6 +39,9 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain val moduleData: RuntimeModuleData get() = moduleData_() + protected open val methodOwner: Class<*> + get() = jClass + abstract val constructorDescriptors: Collection abstract fun getProperties(name: Name): Collection @@ -175,9 +178,7 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain fun findMethodBySignature(name: String, desc: String, declared: Boolean): Method? { if (name == "") return null - // Method for a top level function should be the one from the package facade. - // This is likely to change after the package part reform. - return jClass.tryGetMethod(name, loadParameterTypes(desc), declared) + return methodOwner.tryGetMethod(name, loadParameterTypes(desc), declared) } fun findDefaultMethod(name: String, desc: String, isMember: Boolean, declared: Boolean): Method? { @@ -189,7 +190,7 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain } addParametersAndMasks(parameterTypes, desc, false) - return jClass.tryGetMethod(name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, parameterTypes, declared) + return methodOwner.tryGetMethod(name + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, parameterTypes, declared) } fun findConstructorBySignature(desc: String, declared: Boolean): Constructor<*>? { diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt index 32e16e4e691..2837b5f5f6d 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt @@ -38,6 +38,19 @@ internal class KPackageImpl(override val jClass: Class<*>, val moduleName: Strin } } + override val methodOwner: Class<*> by lazy(LazyThreadSafetyMode.PUBLICATION) { + val facadeName = ReflectKotlinClass.create(jClass)?.classHeader?.multifileClassName + // We need to check isNotEmpty because this is the value read from the annotation which cannot be null. + // The default value for 'xs' is empty string, as declared in kotlin.Metadata + // TODO: do not read ReflectKotlinClass multiple times, obtain facade name from descriptor + if (facadeName != null && facadeName.isNotEmpty()) { + jClass.classLoader.loadClass(facadeName.replace('/', '.')) + } + else { + jClass + } + } + internal val scope: MemberScope get() = descriptor().memberScope override val members: Collection>