diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt index f0d8c5568bd..90194479a45 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/fileClasses/JvmFileClassUtil.kt @@ -34,6 +34,9 @@ object JvmFileClassUtil { val JVM_MULTIFILE_CLASS: FqName = FqName("kotlin.jvm.JvmMultifileClass") val JVM_MULTIFILE_CLASS_SHORT = JVM_MULTIFILE_CLASS.shortName().asString() + private val JVM_PACKAGE_NAME: FqName = FqName("kotlin.jvm.JvmPackageName") + private val JVM_PACKAGE_NAME_SHORT = JVM_PACKAGE_NAME.shortName().asString() + private const val MULTIFILE_PART_NAME_DELIMITER = "__" fun getPartFqNameForDeserialized(descriptor: DeserializedMemberDescriptor): FqName { @@ -55,13 +58,14 @@ object JvmFileClassUtil { @JvmStatic fun getFileClassInfoNoResolve(file: KtFile): JvmFileClassInfo { val parsedAnnotations = parseJvmNameOnFileNoResolve(file) - val packageFqName = file.packageFqName + val packageFqName = parsedAnnotations?.jvmPackageName ?: file.packageFqName return when { parsedAnnotations != null -> { - val facadeClassFqName = packageFqName.child(Name.identifier(parsedAnnotations.jvmName)) + val simpleName = parsedAnnotations.jvmName ?: PackagePartClassUtils.getFilePartShortName(file.name) + val facadeClassFqName = packageFqName.child(Name.identifier(simpleName)) when { parsedAnnotations.isMultifileClass -> JvmMultifileClassPartInfo( - fileClassFqName = packageFqName.child(Name.identifier(manglePartName(parsedAnnotations.jvmName, file.name))), + fileClassFqName = packageFqName.child(Name.identifier(manglePartName(simpleName, file.name))), facadeClassFqName = facadeClassFqName ) else -> JvmSimpleFileClassInfo(facadeClassFqName, true) @@ -72,12 +76,17 @@ object JvmFileClassUtil { } private fun parseJvmNameOnFileNoResolve(file: KtFile): ParsedJvmFileClassAnnotations? { - val jvmName = findAnnotationEntryOnFileNoResolve(file, JVM_NAME_SHORT) ?: return null - val nameExpr = jvmName.valueArguments.firstOrNull()?.getArgumentExpression() ?: return null - val name = getLiteralStringFromRestrictedConstExpression(nameExpr) ?: return null - if (!Name.isValidIdentifier(name)) return null - val isMultifileClassPart = findAnnotationEntryOnFileNoResolve(file, JVM_MULTIFILE_CLASS_SHORT) != null - return ParsedJvmFileClassAnnotations(name, isMultifileClassPart) + val jvmNameAnnotation = findAnnotationEntryOnFileNoResolve(file, JVM_NAME_SHORT) + val jvmName = jvmNameAnnotation?.let(this::getLiteralStringFromAnnotation)?.takeIf(Name::isValidIdentifier) + + val jvmPackageNameAnnotation = findAnnotationEntryOnFileNoResolve(file, JVM_PACKAGE_NAME_SHORT) + val jvmPackageName = jvmPackageNameAnnotation?.let(this::getLiteralStringFromAnnotation)?.let(::FqName) + + if (jvmName == null && jvmPackageName == null) return null + + val isMultifileClass = findAnnotationEntryOnFileNoResolve(file, JVM_MULTIFILE_CLASS_SHORT) != null + + return ParsedJvmFileClassAnnotations(jvmName, jvmPackageName, isMultifileClass) } @JvmStatic @@ -86,14 +95,15 @@ object JvmFileClassUtil { it.calleeExpression?.constructorReferenceExpression?.getReferencedName() == shortName } - private fun getLiteralStringFromRestrictedConstExpression(argumentExpression: KtExpression?): String? { + private fun getLiteralStringFromAnnotation(annotation: KtAnnotationEntry): String? { + val argumentExpression = annotation.valueArguments.firstOrNull()?.getArgumentExpression() ?: return null val stringTemplate = argumentExpression as? KtStringTemplateExpression ?: return null val singleEntry = stringTemplate.entries.singleOrNull() as? KtLiteralStringTemplateEntry ?: return null return singleEntry.text } } -internal class ParsedJvmFileClassAnnotations(val jvmName: String, val isMultifileClass: Boolean) +internal class ParsedJvmFileClassAnnotations(val jvmName: String?, val jvmPackageName: FqName?, val isMultifileClass: Boolean) val KtFile.javaFileFacadeFqName: FqName get() { diff --git a/compiler/testData/codegen/box/jvmPackageName/simple.kt b/compiler/testData/codegen/box/jvmPackageName/simple.kt new file mode 100644 index 00000000000..01d9fac7917 --- /dev/null +++ b/compiler/testData/codegen/box/jvmPackageName/simple.kt @@ -0,0 +1,21 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// LANGUAGE_VERSION: 1.2 + +// FILE: foo.kt + +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@file:JvmPackageName("baz.foo.quux.bar") +package foo.bar + +fun f(): String = "O" + +val g: String? get() = "K" + +inline fun i(block: () -> T): T = block() + +// FILE: bar.kt + +import foo.bar.* + +fun box(): String = i { f() + g } diff --git a/compiler/testData/codegen/boxInline/jvmPackageName/simple.kt b/compiler/testData/codegen/boxInline/jvmPackageName/simple.kt new file mode 100644 index 00000000000..a68cc6dd26d --- /dev/null +++ b/compiler/testData/codegen/boxInline/jvmPackageName/simple.kt @@ -0,0 +1,21 @@ +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// LANGUAGE_VERSION: 1.2 + +// FILE: 1.kt + +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@file:JvmPackageName("baz.foo.quux.bar") +package foo.bar + +fun f(): String = "O" + +val g: String? get() = "K" + +inline fun i(block: () -> T): T = block() + +// FILE: 2.kt + +import foo.bar.* + +fun box(): String = i { f() + g } diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index a0916dfc998..7a8a67ae4ac 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -10401,6 +10401,21 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/jvmPackageName") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmPackageName extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInJvmPackageName() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/jvmPackageName"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/jvmPackageName/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/jvmStatic") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java index b510fa962ef..689a82569cd 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxInlineCodegenTestGenerated.java @@ -1572,6 +1572,21 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli } } + @TestMetadata("compiler/testData/codegen/boxInline/jvmPackageName") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmPackageName extends AbstractIrBlackBoxInlineCodegenTest { + public void testAllFilesPresentInJvmPackageName() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/jvmPackageName"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/jvmPackageName/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxInline/lambdaClassClash") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java index bbdd4282ca0..63206bdaf8d 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrCompileKotlinAgainstInlineKotlinTestGenerated.java @@ -1572,6 +1572,21 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC } } + @TestMetadata("compiler/testData/codegen/boxInline/jvmPackageName") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmPackageName extends AbstractIrCompileKotlinAgainstInlineKotlinTest { + public void testAllFilesPresentInJvmPackageName() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/jvmPackageName"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/jvmPackageName/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxInline/lambdaClassClash") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 74b9e236cc0..c10c5db8ab7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -10401,6 +10401,21 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/jvmPackageName") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmPackageName extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInJvmPackageName() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/jvmPackageName"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/jvmPackageName/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/jvmStatic") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java index c8da2d68c1c..4c05101df54 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java @@ -1572,6 +1572,21 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo } } + @TestMetadata("compiler/testData/codegen/boxInline/jvmPackageName") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmPackageName extends AbstractBlackBoxInlineCodegenTest { + public void testAllFilesPresentInJvmPackageName() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/jvmPackageName"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/jvmPackageName/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxInline/lambdaClassClash") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java index 936a348dbcd..445a59a4bba 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -1572,6 +1572,21 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi } } + @TestMetadata("compiler/testData/codegen/boxInline/jvmPackageName") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmPackageName extends AbstractCompileKotlinAgainstInlineKotlinTest { + public void testAllFilesPresentInJvmPackageName() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/jvmPackageName"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/jvmPackageName/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxInline/lambdaClassClash") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 4c6f6d88d16..def50092945 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -10401,6 +10401,21 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes } } + @TestMetadata("compiler/testData/codegen/box/jvmPackageName") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmPackageName extends AbstractLightAnalysisModeTest { + public void testAllFilesPresentInJvmPackageName() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/jvmPackageName"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/jvmPackageName/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/jvmStatic") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/runtime.jvm/src/kotlin/jvm/annotations/JvmPlatformAnnotations.kt b/core/runtime.jvm/src/kotlin/jvm/annotations/JvmPlatformAnnotations.kt index 28890bec5fc..e270da2ded6 100644 --- a/core/runtime.jvm/src/kotlin/jvm/annotations/JvmPlatformAnnotations.kt +++ b/core/runtime.jvm/src/kotlin/jvm/annotations/JvmPlatformAnnotations.kt @@ -62,6 +62,18 @@ public annotation class JvmName(val name: String) @MustBeDocumented public annotation class JvmMultifileClass +/** + * Changes the fully qualified name of the JVM package of the .class file generated from this file. + * This does not affect the way Kotlin clients will see the declarations in this file, but Java clients and other JVM language clients + * will see the class file as if it was declared in the specified package. + * If a file is annotated with this annotation, it can only have function, property and typealias declarations, but no classes. + */ +@Target(AnnotationTarget.FILE) +@Retention(AnnotationRetention.SOURCE) +@MustBeDocumented +@SinceKotlin("1.2") +internal annotation class JvmPackageName(val name: String) + @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FIELD) @Retention(AnnotationRetention.SOURCE) public annotation class JvmSynthetic diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 0cc7030b0c7..e7916d83290 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -11733,6 +11733,15 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/jvmPackageName") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmPackageName extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInJvmPackageName() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/jvmPackageName"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + } + @TestMetadata("compiler/testData/codegen/box/jvmStatic") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)