Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update kotlin-metadata-jvm #255

Merged
merged 4 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ androidGradlePlugin = "7.2.2"
[libraries]

## region Application libraries
kotlinx-metadata = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version = "0.6.2" }
kotlinx-metadata = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version = "2.0.0" }

javaDiffUtils = { module = "io.github.java-diff-utils:java-diff-utils", version.ref = "javaDiffUtils" }

Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/api/AsmMetadataLoading.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package kotlinx.validation.api

import kotlinx.metadata.jvm.*
import kotlin.metadata.jvm.*
import org.objectweb.asm.*
import org.objectweb.asm.tree.*

Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/api/KotlinMetadataSignature.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package kotlinx.validation.api

import kotlinx.metadata.jvm.*
import kotlin.metadata.jvm.*
import kotlinx.validation.*
import org.objectweb.asm.*
import org.objectweb.asm.tree.*
Expand All @@ -30,7 +30,7 @@ public data class ClassBinarySignature internal constructor(
internal interface MemberBinarySignature {
val jvmMember: JvmMemberSignature
val name: String get() = jvmMember.name
val desc: String get() = jvmMember.desc
val desc: String get() = jvmMember.descriptor
val access: AccessFlags
val isPublishedApi: Boolean
val annotations: List<AnnotationNode>
Expand Down Expand Up @@ -172,14 +172,14 @@ internal fun FieldNode.isCompanionField(outerClassMetadata: KotlinClassMetadata?
val metadata = outerClassMetadata ?: return false
// Non-classes are not affected by the problem
if (metadata !is KotlinClassMetadata.Class) return false
return metadata.toKmClass().companionObject == name
return metadata.kmClass.companionObject == name
}

internal fun ClassNode.companionName(outerClassMetadata: KotlinClassMetadata?): String? {
if (outerClassMetadata !is KotlinClassMetadata.Class) {
// Happens when outerClassMetadata == KotlinClassMetadata$FileFacade for an example
return null
}
val outerKClass = outerClassMetadata.toKmClass()
val outerKClass = outerClassMetadata.kmClass
return name + "$" + outerKClass.companionObject
}
83 changes: 42 additions & 41 deletions src/main/kotlin/api/KotlinMetadataVisibilities.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,18 @@

package kotlinx.validation.api

import kotlinx.metadata.*
import kotlinx.metadata.jvm.*
import kotlinx.metadata.jvm.KotlinClassMetadata.Companion.COMPATIBLE_METADATA_VERSION
import kotlin.metadata.*
import kotlin.metadata.jvm.*
import org.objectweb.asm.tree.*

internal class ClassVisibility(
val name: String,
val flags: Flags?,
val visibility: Visibility?,
val classKind: ClassKind?,
val members: Map<JvmMemberSignature, MemberVisibility>,
val facadeClassName: String? = null
) {
val visibility get() = flags
val isCompanion: Boolean get() = flags != null && Flag.Class.IS_COMPANION_OBJECT(flags)

val isCompanion: Boolean get() = classKind == ClassKind.COMPANION_OBJECT
var companionVisibilities: ClassVisibility? = null
val partVisibilities = mutableListOf<ClassVisibility>()
}
Expand All @@ -29,7 +27,7 @@ internal fun ClassVisibility.findMember(signature: JvmMemberSignature): MemberVi

internal data class MemberVisibility(
val member: JvmMemberSignature,
val visibility: Flags?,
val visibility: Visibility?,
val isReified: Boolean,
/*
* This property includes both annotations on the member itself,
Expand All @@ -38,24 +36,25 @@ internal data class MemberVisibility(
val propertyAnnotation: PropertyAnnotationHolders? = null
)

private fun isPublic(visibility: Flags?, isPublishedApi: Boolean) =
private fun isPublic(visibility: Visibility?, isPublishedApi: Boolean) =
visibility == null
|| Flag.IS_PUBLIC(visibility)
|| Flag.IS_PROTECTED(visibility)
|| (isPublishedApi && Flag.IS_INTERNAL(visibility))
|| visibility == Visibility.PUBLIC
|| visibility == Visibility.PROTECTED
|| (isPublishedApi && visibility == Visibility.INTERNAL)

internal fun ClassVisibility.isPublic(isPublishedApi: Boolean) =
isPublic(visibility, isPublishedApi)

internal fun MemberVisibility.isPublic(isPublishedApi: Boolean) =
// Assuming isReified implies inline
!isReified && isPublic(visibility, isPublishedApi)
// Assuming isReified implies inline
!isReified && isPublic(visibility, isPublishedApi)

internal fun MemberVisibility.isInternal(): Boolean = visibility != null && Flag.IS_INTERNAL(visibility)
internal fun MemberVisibility.isInternal(): Boolean = visibility == Visibility.INTERNAL

internal val ClassNode.kotlinMetadata: KotlinClassMetadata?
get() {
val metadata = findAnnotation("kotlin/Metadata", false) ?: return null

@Suppress("UNCHECKED_CAST")
val header = with(metadata) {
Metadata(
Expand All @@ -68,15 +67,7 @@ internal val ClassNode.kotlinMetadata: KotlinClassMetadata?
extraInt = get("xi") as Int?
)
}
return KotlinClassMetadata.read(header)
?: error(
"""
Incompatible version of Kotlin metadata.
Maximal supported Kotlin metadata version: ${COMPATIBLE_METADATA_VERSION.joinToString(".")},
$name Kotlin metadata version: ${header.metadataVersion.joinToString(".")}.
As a workaround, it is possible to manually update 'kotlinx-metadata-jvm' version in your project.
""".trimIndent()
)
return KotlinClassMetadata.readStrict(header)
fzhinkin marked this conversation as resolved.
Show resolved Hide resolved
}


Expand All @@ -92,62 +83,72 @@ internal class PropertyAnnotationHolders(
)

internal fun KotlinClassMetadata.toClassVisibility(classNode: ClassNode): ClassVisibility {
var flags: Flags? = null
var visibility: Visibility? = null
var kind: ClassKind? = null
var _facadeClassName: String? = null
val members = mutableListOf<MemberVisibility>()

fun addMember(
signature: JvmMemberSignature?,
flags: Flags,
visibility: Visibility?,
isReified: Boolean,
propertyAnnotation: PropertyAnnotationHolders? = null
) {
if (signature != null) {
members.add(MemberVisibility(signature, flags, isReified, propertyAnnotation))
members.add(MemberVisibility(signature, visibility, isReified, propertyAnnotation))
}
}

val container: KmDeclarationContainer? = when (this) {
is KotlinClassMetadata.Class ->
toKmClass().also { klass ->
flags = klass.flags
kmClass.also { klass ->
visibility = klass.visibility
kind = klass.kind

for (constructor in klass.constructors) {
addMember(constructor.signature, constructor.flags, isReified = false)
addMember(constructor.signature, constructor.visibility, isReified = false)
}
}

is KotlinClassMetadata.FileFacade ->
toKmPackage()
kmPackage

is KotlinClassMetadata.MultiFileClassPart ->
toKmPackage().also { _facadeClassName = this.facadeClassName }
kmPackage.also { _facadeClassName = this.facadeClassName }

else -> null
}

if (container != null) {
fun List<KmTypeParameter>.containsReified() = any { Flag.TypeParameter.IS_REIFIED(it.flags) }
fun List<KmTypeParameter>.containsReified() = any { it.isReified }

for (function in container.functions) {
addMember(function.signature, function.flags, function.typeParameters.containsReified())
addMember(function.signature, function.visibility, function.typeParameters.containsReified())
}

for (property in container.properties) {
val isReified = property.typeParameters.containsReified()
val propertyAnnotations =
PropertyAnnotationHolders(property.fieldSignature, property.syntheticMethodForAnnotations)

addMember(property.getterSignature, property.getterFlags, isReified, propertyAnnotations)
addMember(property.setterSignature, property.setterFlags, isReified, propertyAnnotations)
addMember(property.getterSignature, property.getter.visibility, isReified, propertyAnnotations)
addMember(property.setterSignature, property.setter?.visibility, isReified, propertyAnnotations)

val fieldVisibility = when {
Flag.Property.IS_LATEINIT(property.flags) -> property.setterFlags
property.getterSignature == null && property.setterSignature == null -> property.flags // JvmField or const case
else -> flagsOf(Flag.IS_PRIVATE)
property.isLateinit -> property.setter!!.visibility
property.getterSignature == null && property.setterSignature == null -> property.visibility // JvmField or const case
else -> Visibility.PRIVATE
}
addMember(property.fieldSignature, fieldVisibility, isReified = false, propertyAnnotation = propertyAnnotations)
addMember(
property.fieldSignature,
fieldVisibility,
isReified = false,
propertyAnnotation = propertyAnnotations
)
}
}

return ClassVisibility(classNode.name, flags, members.associateBy { it.member }, _facadeClassName)
return ClassVisibility(classNode.name, visibility, kind, members.associateBy { it.member }, _facadeClassName)
}

internal fun ClassNode.toClassVisibility() = kotlinMetadata?.toClassVisibility(this)
Expand Down
18 changes: 8 additions & 10 deletions src/main/kotlin/api/KotlinSignaturesLoading.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@

package kotlinx.validation.api

import kotlinx.metadata.Flag
import kotlinx.metadata.KmProperty
import kotlinx.metadata.internal.metadata.jvm.deserialization.JvmFlags
import kotlinx.metadata.jvm.*
import kotlin.metadata.jvm.*
import kotlinx.validation.*
import org.objectweb.asm.*
import org.objectweb.asm.tree.*
import java.io.*
import java.util.*
import java.util.jar.*
import kotlin.metadata.KmProperty
import kotlin.metadata.visibility

@ExternalApi
@Suppress("unused")
Expand Down Expand Up @@ -132,10 +131,9 @@ private fun FieldNode.buildFieldSignature(
}

val property = companionClassCandidate?.kmProperty(name)

if (property != null && JvmFlag.Property.IS_MOVED_FROM_INTERFACE_COMPANION(property.flags)) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a bug, and JvmFlag.Property.IS_MOVED_FROM_INTERFACE_COMPANION worked here purely by coincidence.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks to the new stable API, it became obvious! ;)

if (property != null) {
/*
* The property was moved from the companion object. Take all the annotations from there
* The property was declared in the companion object. Take all the annotations from there
* to be able to filter out the non-public markers.
*
* See https://github.com/Kotlin/binary-compatibility-validator/issues/90
Expand Down Expand Up @@ -163,7 +161,7 @@ private fun ClassNode.kmProperty(name: String?): KmProperty? {
return null
}

return metadata.toKmClass().properties.firstOrNull {
return metadata.kmClass.properties.firstOrNull {
it.name == name
}
}
Expand Down Expand Up @@ -198,7 +196,7 @@ private fun MethodNode.buildMethodSignature(
private fun List<MethodNode>.annotationsFor(methodSignature: JvmMethodSignature?): List<AnnotationNode> {
if (methodSignature == null) return emptyList()

return firstOrNull { it.name == methodSignature.name && it.desc == methodSignature.desc }
return firstOrNull { it.name == methodSignature.name && it.desc == methodSignature.descriptor }
?.run {
visibleAnnotations.orEmpty() + invisibleAnnotations.orEmpty()
} ?: emptyList()
Expand All @@ -207,7 +205,7 @@ private fun List<MethodNode>.annotationsFor(methodSignature: JvmMethodSignature?
private fun List<FieldNode>.annotationsFor(fieldSignature: JvmFieldSignature?): List<AnnotationNode> {
if (fieldSignature == null) return emptyList()

return firstOrNull { it.name == fieldSignature.name && it.desc == fieldSignature.desc }
return firstOrNull { it.name == fieldSignature.name && it.desc == fieldSignature.descriptor }
?.run {
visibleAnnotations.orEmpty() + invisibleAnnotations.orEmpty()
} ?: emptyList()
Expand Down