Support JvmPackageName with JvmMultifileClass

This is an internal feature of our standard library needed to compile
new API for unsigned types
This commit is contained in:
Alexander Udalov
2019-02-28 19:36:40 +01:00
parent ea21cda4df
commit 59fda8d7ce
26 changed files with 1164 additions and 210 deletions
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.psi.KtTypeAlias
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.MemberComparator
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClassPart
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
@@ -49,7 +50,6 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
@@ -66,7 +66,7 @@ class MultifileClassCodegenImpl(
) : MultifileClassCodegen {
private val facadeClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(facadeFqName)
private val packageFragment = getOnlyPackageFragment(facadeFqName.parent(), files, state.module)
private val packageFragment = getOnlyPackageFragment(files, state.module)
private val compiledPackageFragment = getCompiledPackageFragment(facadeFqName, state)
@@ -329,6 +329,20 @@ class MultifileClassCodegenImpl(
arv.visit(null, internalName)
}
arv.visitEnd()
val kotlinPackageFqName =
packageFragment?.fqName ?: compiledPackageFragment?.fqName
?: error("Either source package or compiled package should not be null: $facadeClassType ($files)")
if (files.any { it.packageFqName != kotlinPackageFqName })
throw UnsupportedOperationException(
"Multi-file parts of a facade with JvmPackageName should all lie in the same Kotlin package:\n " +
files.joinToString("\n ") { file -> "$file: package ${file.packageFqName}" }
)
if (kotlinPackageFqName != JvmClassName.byInternalName(facadeClassType.internalName).packageFqName) {
av.visit(JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME, kotlinPackageFqName.asString())
}
}
}
@@ -350,21 +364,10 @@ class MultifileClassCodegenImpl(
private val J_L_OBJECT = AsmTypes.OBJECT_TYPE.internalName
private const val FACADE_CLASS_ATTRIBUTES = Opcodes.ACC_PUBLIC or Opcodes.ACC_FINAL or Opcodes.ACC_SUPER
private fun getOnlyPackageFragment(
packageFqName: FqName,
files: Collection<KtFile>,
moduleDescriptor: ModuleDescriptor
): PackageFragmentDescriptor? {
val fragments = SmartList<PackageFragmentDescriptor>()
for (file in files) {
val fragment = moduleDescriptor.findPackageFragmentForFile(file)
private fun getOnlyPackageFragment(files: Collection<KtFile>, moduleDescriptor: ModuleDescriptor): PackageFragmentDescriptor? {
val fragments = files.mapTo(linkedSetOf()) { file ->
moduleDescriptor.findPackageFragmentForFile(file)
?: throw AssertionError("package fragment is null for " + file + "\n" + file.text)
assert(packageFqName == fragment.fqName) { "expected package fq name: " + packageFqName + ", actual: " + fragment.fqName }
if (!fragments.contains(fragment)) {
fragments.add(fragment)
}
}
if (fragments.size > 1) {
throw IllegalStateException("More than one package fragment, files: $files | fragments: $fragments")
@@ -22,9 +22,11 @@ import org.jetbrains.kotlin.codegen.context.MultifileClassPartContext
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.org.objectweb.asm.Opcodes
@@ -97,6 +99,11 @@ class MultifileClassPartCodegen(
writeKotlinMetadata(v, state, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART, extraFlags) { av ->
AsmUtil.writeAnnotationData(av, serializer, packageProto)
av.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, facadeClassType.internalName)
val kotlinPackageFqName = element.packageFqName
if (kotlinPackageFqName != JvmClassName.byInternalName(partType.internalName).packageFqName) {
av.visit(METADATA_PACKAGE_NAME_FIELD_NAME, kotlinPackageFqName.asString())
}
}
}
@@ -28,7 +28,10 @@ import org.jetbrains.kotlin.resolve.AdditionalAnnotationChecker
import org.jetbrains.kotlin.resolve.AnnotationChecker
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention
import org.jetbrains.kotlin.resolve.descriptorUtil.isRepeatableAnnotation
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
object RepeatableAnnotationChecker: AdditionalAnnotationChecker {
@@ -81,12 +84,8 @@ object FileClassAnnotationsChecker: AdditionalAnnotationChecker {
if (classDescriptor.getAnnotationRetention() != KotlinRetention.SOURCE) {
trace.report(ErrorsJvm.ANNOTATION_IS_NOT_APPLICABLE_TO_MULTIFILE_CLASSES.on(entry, classFqName))
}
if (classFqName == JvmFileClassUtil.JVM_PACKAGE_NAME) {
trace.report(ErrorsJvm.JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_MULTIFILE_CLASSES.on(entry))
}
}
}
else {
} else {
for ((entry, classDescriptor) in fileAnnotationsToCheck) {
if (classDescriptor.fqNameSafe != JvmFileClassUtil.JVM_PACKAGE_NAME) continue
@@ -97,13 +96,11 @@ object FileClassAnnotationsChecker: AdditionalAnnotationChecker {
val value = (stringTemplateEntries.singleOrNull() as? KtLiteralStringTemplateEntry)?.text
if (value == null) {
trace.report(ErrorsJvm.JVM_PACKAGE_NAME_CANNOT_BE_EMPTY.on(entry))
}
else if (!isValidJavaFqName(value)) {
} else if (!isValidJavaFqName(value)) {
trace.report(ErrorsJvm.JVM_PACKAGE_NAME_MUST_BE_VALID_NAME.on(entry))
}
else if (entry.containingKtFile.declarations.any {
it !is KtFunction && it !is KtProperty && it !is KtTypeAlias
}) {
} else if (entry.containingKtFile.declarations.any {
it !is KtFunction && it !is KtProperty && it !is KtTypeAlias
}) {
trace.report(ErrorsJvm.JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES.on(entry))
}
}
@@ -67,7 +67,6 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(NON_SOURCE_REPEATED_ANNOTATION, "Repeatable annotations with non-SOURCE retention are not yet supported");
MAP.put(ANNOTATION_IS_NOT_APPLICABLE_TO_MULTIFILE_CLASSES, "Annotation ''@{0}'' is not applicable to the multi-file classes", TO_STRING);
MAP.put(JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_MULTIFILE_CLASSES, "''@JvmPackageName'' annotation is not supported in multi-file classes");
MAP.put(JVM_PACKAGE_NAME_CANNOT_BE_EMPTY, "''@JvmPackageName'' annotation value cannot be empty");
MAP.put(JVM_PACKAGE_NAME_MUST_BE_VALID_NAME, "''@JvmPackageName'' annotation value must be a valid dot-qualified name of a package");
MAP.put(JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES, "''@JvmPackageName'' annotation is not supported for files with class declarations");
@@ -68,7 +68,6 @@ public interface ErrorsJvm {
DiagnosticFactory0<KtAnnotationEntry> NON_SOURCE_REPEATED_ANNOTATION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<KtAnnotationEntry, FqName> ANNOTATION_IS_NOT_APPLICABLE_TO_MULTIFILE_CLASSES = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_MULTIFILE_CLASSES = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> JVM_PACKAGE_NAME_CANNOT_BE_EMPTY = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> JVM_PACKAGE_NAME_MUST_BE_VALID_NAME = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtAnnotationEntry> JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES = DiagnosticFactory0.create(ERROR);
@@ -0,0 +1,41 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: A1.kt
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@file:JvmPackageName("baz.foo.quux.bar")
@file:JvmName("Facade")
@file:JvmMultifileClass
package foo.bar
val g: S? get() = f().substring(0, 0) + "K"
// FILE: A2.kt
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@file:JvmPackageName("baz.foo.quux.bar")
@file:JvmName("Facade")
@file:JvmMultifileClass
package foo.bar
inline fun <T> i(block: () -> T): T = block()
// FILE: A3.kt
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@file:JvmPackageName("baz.foo.quux.bar")
@file:JvmName("Facade")
@file:JvmMultifileClass
package foo.bar
typealias S = String
fun f(): String = "O"
// FILE: B.kt
import foo.bar.*
fun box(): S = i { f() + g }
@@ -0,0 +1,25 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: A.kt
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@file:JvmPackageName("baz.foo.quux.bar")
@file:JvmName("Facade")
@file:JvmMultifileClass
package foo.bar
typealias S = String
fun f(): String = "O"
val g: S? get() = f().substring(0, 0) + "K"
inline fun <T> i(block: () -> T): T = block()
// FILE: B.kt
import foo.bar.*
fun box(): S = i { f() + g }
@@ -1,11 +1,5 @@
// !DIAGNOSTICS: -INVISIBLE_MEMBER -INVISIBLE_REFERENCE
// FILE: a.kt
<!JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_MULTIFILE_CLASSES!>@file:JvmPackageName("a")<!>
@file:JvmMultifileClass
package a
fun a() {}
// FILE: b.kt
<!JVM_PACKAGE_NAME_CANNOT_BE_EMPTY!>@file:JvmPackageName("")<!>
package b
@@ -1,9 +1,5 @@
package
package a {
public fun a(): kotlin.Unit
}
package b {
public fun b(): kotlin.Unit
}
@@ -0,0 +1,7 @@
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@file:JvmPackageName("j1")
@file:JvmName("K1")
@file:JvmMultifileClass
package k1
fun f1() {}
@@ -0,0 +1,7 @@
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@file:JvmPackageName("j1")
@file:JvmName("K1")
@file:JvmMultifileClass
package k1
val v2 = Unit
@@ -0,0 +1,7 @@
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@file:JvmPackageName("j1")
@file:JvmName("K1")
@file:JvmMultifileClass
package k1
typealias T3 = List<String>
@@ -0,0 +1,7 @@
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@file:JvmPackageName("j2")
@file:JvmName("K2")
@file:JvmMultifileClass
package k2
fun f1() {}
@@ -0,0 +1,7 @@
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@file:JvmPackageName("j3")
@file:JvmName("K2")
@file:JvmMultifileClass
package k2
val v2 = Unit
@@ -0,0 +1,5 @@
@file:JvmName("K2")
@file:JvmMultifileClass
package k2
typealias T3 = List<String>
@@ -0,0 +1,8 @@
k1
j1/K1__K1_AKt (j1/K1)
j1/K1__K1_BKt (j1/K1)
j1/K1__K1_CKt (j1/K1)
k2
k2/K2__K2_CKt (k2/K2)
j2/K2__K2_AKt (j2/K2)
j3/K2__K2_BKt (j3/K2)
@@ -14655,6 +14655,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/jvmPackageName/metadataField.kt");
}
@TestMetadata("multifileClass.kt")
public void testMultifileClass() throws Exception {
runTest("compiler/testData/codegen/box/jvmPackageName/multifileClass.kt");
}
@TestMetadata("rootPackage.kt")
public void testRootPackage() throws Exception {
runTest("compiler/testData/codegen/box/jvmPackageName/rootPackage.kt");
@@ -188,6 +188,11 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
runTest("compiler/testData/compileKotlinAgainstKotlin/jvmPackageNameInRootPackage.kt");
}
@TestMetadata("jvmPackageNameMultifileClass.kt")
public void testJvmPackageNameMultifileClass() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/jvmPackageNameMultifileClass.kt");
}
@TestMetadata("jvmPackageNameWithJvmName.kt")
public void testJvmPackageNameWithJvmName() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/jvmPackageNameWithJvmName.kt");
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.load.kotlin.loadModuleMapping
@@ -31,28 +30,28 @@ import java.io.File
class JvmModuleProtoBufTest : KtUsefulTestCase() {
private fun doTest(
relativeDirectory: String,
compileWith: LanguageVersion = LanguageVersion.LATEST_STABLE,
loadWith: LanguageVersion = LanguageVersion.LATEST_STABLE,
extraOptions: List<String> = emptyList()
relativeDirectory: String,
compileWith: LanguageVersion = LanguageVersion.LATEST_STABLE,
loadWith: LanguageVersion = LanguageVersion.LATEST_STABLE,
extraOptions: List<String> = emptyList()
) {
val directory = KotlinTestUtils.getTestDataPathBase() + relativeDirectory
val tmpdir = KotlinTestUtils.tmpDir(this::class.simpleName)
val moduleName = "main"
CompilerTestUtil.executeCompilerAssertSuccessful(K2JVMCompiler(), listOf(
CompilerTestUtil.executeCompilerAssertSuccessful(
K2JVMCompiler(), listOf(
directory,
"-d", tmpdir.path,
"-module-name", moduleName,
"-language-version", compileWith.versionString
) + extraOptions)
) + extraOptions
)
val mapping = ModuleMapping.loadModuleMapping(
File(tmpdir, "META-INF/$moduleName.${ModuleMapping.MAPPING_FILE_EXT}").readBytes(), "test",
CompilerDeserializationConfiguration(
LanguageVersionSettingsImpl(loadWith, ApiVersion.createByLanguageVersion(loadWith))
),
::error
File(tmpdir, "META-INF/$moduleName.${ModuleMapping.MAPPING_FILE_EXT}").readBytes(), "test",
CompilerDeserializationConfiguration(LanguageVersionSettingsImpl(loadWith, ApiVersion.createByLanguageVersion(loadWith))),
::error
)
val result = buildString {
for (annotationClassId in mapping.moduleData.annotations) {
@@ -82,18 +81,19 @@ class JvmModuleProtoBufTest : KtUsefulTestCase() {
}
fun testJvmPackageName() {
doTest("/moduleProtoBuf/jvmPackageName",
compileWith = LanguageVersion.KOTLIN_1_2, loadWith = LanguageVersion.KOTLIN_1_2)
doTest("/moduleProtoBuf/jvmPackageName")
}
fun testJvmPackageNameManyParts() {
doTest("/moduleProtoBuf/jvmPackageNameManyParts",
compileWith = LanguageVersion.KOTLIN_1_2, loadWith = LanguageVersion.KOTLIN_1_2)
doTest("/moduleProtoBuf/jvmPackageNameManyParts")
}
fun testJvmPackageNameLanguageVersion11() {
doTest("/moduleProtoBuf/jvmPackageNameLanguageVersion11",
compileWith = LanguageVersion.KOTLIN_1_2, loadWith = LanguageVersion.KOTLIN_1_1)
doTest("/moduleProtoBuf/jvmPackageNameLanguageVersion11", loadWith = LanguageVersion.KOTLIN_1_1)
}
fun testJvmPackageNameMultifileClass() {
doTest("/moduleProtoBuf/jvmPackageNameMultifileClass")
}
fun testExperimental() {
@@ -14655,6 +14655,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/jvmPackageName/metadataField.kt");
}
@TestMetadata("multifileClass.kt")
public void testMultifileClass() throws Exception {
runTest("compiler/testData/codegen/box/jvmPackageName/multifileClass.kt");
}
@TestMetadata("rootPackage.kt")
public void testRootPackage() throws Exception {
runTest("compiler/testData/codegen/box/jvmPackageName/rootPackage.kt");
@@ -14660,6 +14660,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/jvmPackageName/metadataField.kt");
}
@TestMetadata("multifileClass.kt")
public void testMultifileClass() throws Exception {
runTest("compiler/testData/codegen/box/jvmPackageName/multifileClass.kt");
}
@TestMetadata("rootPackage.kt")
public void testRootPackage() throws Exception {
runTest("compiler/testData/codegen/box/jvmPackageName/rootPackage.kt");