Support new scheme of compilation of OptionalExpectation annotations

Instead of generating these annotation classes as package-private on
JVM, serialize their metadata to the .kotlin_module file, and load it
when compiling dependent multiplatform modules.

The problem with generating them as package-private was that
kotlin-stdlib for JVM would end up declaring symbols from other
platforms, which would include some annotations from package
kotlin.native. But using that package is discouraged by some tools
because it has a Java keyword in its name. In particular, jlink refused
to work with such artifact altogether (KT-21266).

 #KT-38652 Fixed
This commit is contained in:
Alexander Udalov
2019-12-17 17:43:42 +01:00
committed by Alexander Udalov
parent 63e355d979
commit 012ffa2993
42 changed files with 1469 additions and 145 deletions
@@ -3,10 +3,15 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package kotlinx.metadata.jvm
import kotlinx.metadata.InconsistentKotlinMetadataException
import kotlinx.metadata.KmAnnotation
import kotlinx.metadata.KmClass
import kotlinx.metadata.KmClassVisitor
import kotlinx.metadata.impl.accept
import org.jetbrains.kotlin.metadata.jvm.JvmModuleProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping
@@ -62,6 +67,17 @@ class KotlinModuleMetadata(@Suppress("CanBeParameter", "MemberVisibilityCanBePri
*/
}
override fun visitOptionalAnnotationClass(): KmClassVisitor? {
/*
return object : ClassWriter(TODO() /* use StringTableImpl here */) {
override fun visitEnd() {
b.addOptionalAnnotationClass(t)
}
}
*/
return null
}
/**
* Returns the metadata of the module file that was written with this writer.
*
@@ -80,13 +96,19 @@ class KotlinModuleMetadata(@Suppress("CanBeParameter", "MemberVisibilityCanBePri
fun accept(v: KmModuleVisitor) {
for ((fqName, parts) in data.packageFqName2Parts) {
val (fileFacades, multiFileClassParts) = parts.parts.partition { parts.getMultifileFacadeName(it) == null }
v.visitPackageParts(fqName, fileFacades, multiFileClassParts.associate { it to parts.getMultifileFacadeName(it)!! })
v.visitPackageParts(fqName, fileFacades, multiFileClassParts.associateWith { parts.getMultifileFacadeName(it)!! })
}
for (annotation in data.moduleData.annotations) {
v.visitAnnotation(KmAnnotation(annotation, emptyMap()))
}
for (classProto in data.moduleData.optionalAnnotations) {
v.visitOptionalAnnotationClass()?.let {
classProto.accept(it, data.moduleData.nameResolver)
}
}
v.visitEnd()
}
@@ -147,6 +169,17 @@ abstract class KmModuleVisitor(private val delegate: KmModuleVisitor? = null) {
delegate?.visitAnnotation(annotation)
}
/**
* Visits an `@OptionalExpectation`-annotated annotation class declared in this module.
* Such classes are not materialized to bytecode on JVM, but the Kotlin compiler stores their metadata in the module file on JVM,
* and loads it during compilation of dependent modules, in order to avoid reporting "unresolved reference" errors on usages.
*
* Multiplatform projects are an experimental feature of Kotlin, and their behavior and/or binary format
* may change in a subsequent release.
*/
open fun visitOptionalAnnotationClass(): KmClassVisitor? =
delegate?.visitOptionalAnnotationClass()
/**
* Visits the end of the module.
*/
@@ -171,6 +204,16 @@ class KmModule : KmModuleVisitor() {
*/
val annotations: MutableList<KmAnnotation> = ArrayList(0)
/**
* `@OptionalExpectation`-annotated annotation classes declared in this module.
* Such classes are not materialized to bytecode on JVM, but the Kotlin compiler stores their metadata in the module file on JVM,
* and loads it during compilation of dependent modules, in order to avoid reporting "unresolved reference" errors on usages.
*
* Multiplatform projects are an experimental feature of Kotlin, and their behavior and/or binary format
* may change in a subsequent release.
*/
val optionalAnnotationClasses: MutableList<KmClass> = ArrayList(0)
override fun visitPackageParts(fqName: String, fileFacades: List<String>, multiFileClassParts: Map<String, String>) {
packageParts[fqName] = KmPackageParts(fileFacades.toMutableList(), multiFileClassParts.toMutableMap())
}
@@ -179,6 +222,9 @@ class KmModule : KmModuleVisitor() {
annotations.add(annotation)
}
override fun visitOptionalAnnotationClass(): KmClass =
KmClass().also(optionalAnnotationClasses::add)
/**
* Populates the given visitor with data in this module.
*
@@ -189,6 +235,7 @@ class KmModule : KmModuleVisitor() {
visitor.visitPackageParts(fqName, parts.fileFacades, parts.multiFileClassParts)
}
annotations.forEach(visitor::visitAnnotation)
optionalAnnotationClasses.forEach { visitor.visitOptionalAnnotationClass()?.let(it::accept) }
}
}