From 0d12d60e8afe398f4a1d11b48e9fcfd6ff83cf98 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Wed, 23 Mar 2016 23:00:40 +0300 Subject: [PATCH] Infer lateinit-exposed field effective visibility from its setter visibility. --- .../org.jetbrains.kotlin.tools/asmUtils.kt | 17 +++++++++++++---- .../kotlinVisibilities.kt | 16 ++++++++++++++-- .../src/test/resources/output/cases/special.txt | 1 - 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/asmUtils.kt b/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/asmUtils.kt index a06f5ee81fd..91c00d8bdde 100644 --- a/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/asmUtils.kt +++ b/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/asmUtils.kt @@ -37,7 +37,10 @@ interface MemberBinarySignature { fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?) = access.isPublic && !(access.isProtected && classAccess.isFinal) - && (classVisibility?.members?.get(MemberSignature(name, desc))?.isPublic(isInlineExposed) ?: true) + && (findMemberVisibility(classVisibility)?.isPublic(isInlineExposed) ?: true) + + fun findMemberVisibility(classVisibility: ClassVisibility?) + = classVisibility?.members?.get(MemberSignature(name, desc)) val signature: String } @@ -65,9 +68,15 @@ data class FieldBinarySignature( override val signature: String get() = "${access.getModifierString()} field $name $desc" - override fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?) - = super.isEffectivelyPublic(classAccess, classVisibility) - // TODO: lateinit exposed field + override fun findMemberVisibility(classVisibility: ClassVisibility?): MemberVisibility? { + val fieldVisibility = super.findMemberVisibility(classVisibility) ?: return null + + // good case for 'satisfying': fieldVisibility.satisfying { it.isLateInit() }?.let { classVisibility?.findSetterForProperty(it) } + if (fieldVisibility.isLateInit()) { + classVisibility?.findSetterForProperty(fieldVisibility)?.let { return it } + } + return fieldVisibility + } } diff --git a/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/kotlinVisibilities.kt b/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/kotlinVisibilities.kt index 1f0b2dc2b60..bb757f2a46c 100644 --- a/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/kotlinVisibilities.kt +++ b/libraries/tools/binary-compatibility-validator/src/main/kotlin/org.jetbrains.kotlin.tools/kotlinVisibilities.kt @@ -5,13 +5,24 @@ import com.google.gson.stream.JsonReader import java.io.File data class ClassVisibility(val name: String, val visibility: String?, val members: Map) -data class MemberVisibility(val member: MemberSignature, val visibility: String?) +data class MemberVisibility(val member: MemberSignature, val declaration: String?, val visibility: String?) data class MemberSignature(val name: String, val desc: String) private fun isPublic(visibility: String?, isInlineExposed: Boolean) = visibility == null || visibility == "public" || visibility == "protected" || (isInlineExposed && visibility == "internal") fun ClassVisibility.isPublic(isInlineExposed: Boolean) = isPublic(visibility, isInlineExposed) fun MemberVisibility.isPublic(isInlineExposed: Boolean) = isPublic(visibility, isInlineExposed) +fun MemberVisibility.isLateInit() = declaration != null && "lateinit var " in declaration + +private val varValPrefix = Regex("va[lr]\\s+") +fun ClassVisibility.findSetterForProperty(property: MemberVisibility): MemberVisibility? { + // ad-hoc solution: + val declaration = property.declaration ?: return null + val match = varValPrefix.find(declaration) ?: return null + val name = declaration.substring(match.range.endInclusive + 1).substringBefore(':') + val setterName = "" + return members.values.find { it.declaration?.contains(setterName) ?: false } +} fun readKotlinVisibilities(declarationFile: File): Map { val result = mutableListOf() @@ -27,8 +38,9 @@ fun readKotlinVisibilities(declarationFile: File): Map with(it.asJsonObject) { val name = getAsJsonPrimitive("name").asString val desc = getAsJsonPrimitive("desc").asString + val declaration = getAsJsonPrimitive("declaration")?.asString val visibility = getAsJsonPrimitive("visibility")?.asString - MemberVisibility(MemberSignature(name, desc), visibility) + MemberVisibility(MemberSignature(name, desc), declaration, visibility) } } ClassVisibility(name, visibility, members.associateByTo(hashMapOf()) { it.member }) diff --git a/libraries/tools/binary-compatibility-validator/src/test/resources/output/cases/special.txt b/libraries/tools/binary-compatibility-validator/src/test/resources/output/cases/special.txt index 3284b4c65a3..5c6fd2cd6b6 100644 --- a/libraries/tools/binary-compatibility-validator/src/test/resources/output/cases/special.txt +++ b/libraries/tools/binary-compatibility-validator/src/test/resources/output/cases/special.txt @@ -1,5 +1,4 @@ public final class cases/special/ClassWithLateInitMembers -public field publicLateInitWithInternalSet Ljava/lang/String; public final fun getPublicLateInitWithInternalSet ()Ljava/lang/String; ------------------