Refactor data structures
This commit is contained in:
+17
-21
@@ -19,13 +19,6 @@ fun main(args: Array<String>) {
|
|||||||
fun JarFile.classEntries() = entries().asSequence().filter { !it.isDirectory && it.name.endsWith(".class") }
|
fun JarFile.classEntries() = entries().asSequence().filter { !it.isDirectory && it.name.endsWith(".class") }
|
||||||
|
|
||||||
|
|
||||||
data class ClassBinarySignature(val name: String, val superName: String, val outerName: String?, val modifiers: String, val supertypes: List<String>, val memberSignatures: List<MemberBinarySignature>, val isPublic: Boolean)
|
|
||||||
data class MemberBinarySignature(val name: String, val signature: String, val isStatic: Boolean)
|
|
||||||
|
|
||||||
val ClassBinarySignature.signature: String
|
|
||||||
get() = "$modifiers class $name" + if (supertypes.isEmpty()) "" else ": ${supertypes.joinToString()}"
|
|
||||||
|
|
||||||
|
|
||||||
fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>): List<ClassBinarySignature> =
|
fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>): List<ClassBinarySignature> =
|
||||||
getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityMap)
|
getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityMap)
|
||||||
|
|
||||||
@@ -36,26 +29,29 @@ fun getBinaryAPI(classStreams: Sequence<InputStream>, visibilityMap: Map<String,
|
|||||||
classNode
|
classNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.asIterable()
|
.map { with(it) {
|
||||||
.sortedBy { it.name }
|
|
||||||
.mapNotNull { with(it) {
|
|
||||||
val classVisibility = visibilityMap[name]
|
val classVisibility = visibilityMap[name]
|
||||||
val isPublic = it.isEffectivelyPublic(classVisibility)
|
val classAccess = AccessFlags(effectiveAccess and Opcodes.ACC_STATIC.inv())
|
||||||
|
|
||||||
val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted()
|
val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted()
|
||||||
|
|
||||||
val modifiers = getModifierString(effectiveAccess and Opcodes.ACC_STATIC.inv())
|
val memberSignatures = (
|
||||||
|
fields
|
||||||
val memberSignatures =
|
|
||||||
fields.filter { it.isEffectivelyPublic(classVisibility) }
|
|
||||||
.sortedBy { it.name }
|
.sortedBy { it.name }
|
||||||
.map { with(it) { MemberBinarySignature(name, "${getModifierString(access)} field $name $desc", isStatic(access) ) } } +
|
.map { with(it) { FieldBinarySignature(name, desc, isInlineExposed(), AccessFlags(access)) } } +
|
||||||
methods.filter { it.isEffectivelyPublic(classVisibility) }
|
methods
|
||||||
.sortedWith(compareBy({ it.name }, { it.desc }))
|
.sortedWith(compareBy({ it.name }, { it.desc }))
|
||||||
.map { with(it) { MemberBinarySignature(name, "${getModifierString(access)} fun $name $desc", isStatic(access) ) } }
|
.map { with(it) { MethodBinarySignature(name, desc, isInlineExposed(), AccessFlags(access)) } }
|
||||||
|
).filter {
|
||||||
|
it.isEffectivelyPublic(classAccess, classVisibility)
|
||||||
|
}
|
||||||
|
val isEffectivelyPublic = it.isEffectivelyPublic(classVisibility)
|
||||||
|
&& !(isFileOrMultipartFacade() && memberSignatures.isEmpty())
|
||||||
|
|
||||||
ClassBinarySignature(name, superName, outerClassName, modifiers, supertypes, memberSignatures, isPublic)
|
ClassBinarySignature(name, superName, outerClassName, supertypes, memberSignatures, classAccess, isEffectivelyPublic)
|
||||||
}}
|
}}
|
||||||
|
.asIterable()
|
||||||
|
.sortedBy { it.name }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -63,7 +59,7 @@ fun List<ClassBinarySignature>.filterOutNonPublic(): List<ClassBinarySignature>
|
|||||||
val classByName = associateBy { it.name }
|
val classByName = associateBy { it.name }
|
||||||
|
|
||||||
fun ClassBinarySignature.isPublicAndAccessible(): Boolean =
|
fun ClassBinarySignature.isPublicAndAccessible(): Boolean =
|
||||||
isPublic && (outerName == null || classByName[outerName]?.isPublicAndAccessible() ?: true)
|
isEffectivelyPublic && (outerName == null || classByName[outerName]?.isPublicAndAccessible() ?: true)
|
||||||
|
|
||||||
fun supertypes(superName: String) = generateSequence({ classByName[superName] }, { classByName[it.superName] })
|
fun supertypes(superName: String) = generateSequence({ classByName[superName] }, { classByName[it.superName] })
|
||||||
|
|
||||||
@@ -73,7 +69,7 @@ fun List<ClassBinarySignature>.filterOutNonPublic(): List<ClassBinarySignature>
|
|||||||
if (nonPublicSupertypes.isEmpty())
|
if (nonPublicSupertypes.isEmpty())
|
||||||
return this
|
return this
|
||||||
|
|
||||||
val inheritedStaticSignatures = nonPublicSupertypes.flatMap { it.memberSignatures.filter { it.isStatic }}
|
val inheritedStaticSignatures = nonPublicSupertypes.flatMap { it.memberSignatures.filter { it.access.isStatic }}
|
||||||
|
|
||||||
// not covered the case when there is public superclass after chain of private superclasses
|
// not covered the case when there is public superclass after chain of private superclasses
|
||||||
return this.copy(memberSignatures = memberSignatures + inheritedStaticSignatures, supertypes = supertypes - superName)
|
return this.copy(memberSignatures = memberSignatures + inheritedStaticSignatures, supertypes = supertypes - superName)
|
||||||
|
|||||||
+78
-35
@@ -3,7 +3,6 @@ package org.jetbrains.kotlin.tools
|
|||||||
import org.objectweb.asm.Opcodes
|
import org.objectweb.asm.Opcodes
|
||||||
import org.objectweb.asm.tree.*
|
import org.objectweb.asm.tree.*
|
||||||
|
|
||||||
|
|
||||||
val ACCESS_NAMES = mapOf(
|
val ACCESS_NAMES = mapOf(
|
||||||
Opcodes.ACC_PUBLIC to "public",
|
Opcodes.ACC_PUBLIC to "public",
|
||||||
Opcodes.ACC_PROTECTED to "protected",
|
Opcodes.ACC_PROTECTED to "protected",
|
||||||
@@ -15,49 +14,97 @@ val ACCESS_NAMES = mapOf(
|
|||||||
Opcodes.ACC_INTERFACE to "interface",
|
Opcodes.ACC_INTERFACE to "interface",
|
||||||
Opcodes.ACC_ANNOTATION to "annotation")
|
Opcodes.ACC_ANNOTATION to "annotation")
|
||||||
|
|
||||||
|
data class ClassBinarySignature(
|
||||||
|
val name: String,
|
||||||
|
val superName: String,
|
||||||
|
val outerName: String?,
|
||||||
|
val supertypes: List<String>,
|
||||||
|
val memberSignatures: List<MemberBinarySignature>,
|
||||||
|
val access: AccessFlags,
|
||||||
|
val isEffectivelyPublic: Boolean) {
|
||||||
|
|
||||||
public fun isPublic(access: Int) = access and Opcodes.ACC_PUBLIC != 0 || access and Opcodes.ACC_PROTECTED != 0
|
val signature: String
|
||||||
public fun isStatic(access: Int) = access and Opcodes.ACC_STATIC != 0
|
get() = "${access.getModifierString()} class $name" + if (supertypes.isEmpty()) "" else ": ${supertypes.joinToString()}"
|
||||||
fun getModifiers(access: Int): List<String> = ACCESS_NAMES.entries.mapNotNull { if (access and it.key != 0) it.value else null }
|
|
||||||
fun getModifierString(access: Int): String = getModifiers(access).joinToString(" ")
|
|
||||||
|
|
||||||
fun ClassNode.isSynthetic() = access and Opcodes.ACC_SYNTHETIC != 0
|
}
|
||||||
fun MethodNode.isSynthetic() = access and Opcodes.ACC_SYNTHETIC != 0
|
|
||||||
fun ClassNode.isPublic() = isPublic(access)
|
|
||||||
fun MethodNode.isPublic() = isPublic(access)
|
interface MemberBinarySignature {
|
||||||
fun FieldNode.isPublic() = isPublic(access)
|
val name: String
|
||||||
|
val desc: String
|
||||||
|
val access: AccessFlags
|
||||||
|
val isInlineExposed: Boolean
|
||||||
|
|
||||||
|
fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?)
|
||||||
|
= access.isPublic // && !(access.isProtected && classAccess.isFinal)
|
||||||
|
&& (classVisibility?.members?.get(MemberSignature(name, desc))?.isPublic(isInlineExposed) ?: true)
|
||||||
|
|
||||||
|
val signature: String
|
||||||
|
}
|
||||||
|
|
||||||
|
data class MethodBinarySignature(
|
||||||
|
override val name: String,
|
||||||
|
override val desc: String,
|
||||||
|
override val isInlineExposed: Boolean,
|
||||||
|
override val access: AccessFlags) : MemberBinarySignature {
|
||||||
|
override val signature: String
|
||||||
|
get() = "${access.getModifierString()} fun $name $desc"
|
||||||
|
|
||||||
|
override fun isEffectivelyPublic(classAccess: AccessFlags, classVisibility: ClassVisibility?)
|
||||||
|
= super.isEffectivelyPublic(classAccess, classVisibility)
|
||||||
|
&& !isAccessMethod()
|
||||||
|
|
||||||
|
private fun isAccessMethod() = access.isSynthetic && name.startsWith("access\$")
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FieldBinarySignature(
|
||||||
|
override val name: String,
|
||||||
|
override val desc: String,
|
||||||
|
override val isInlineExposed: Boolean,
|
||||||
|
override val access: AccessFlags) : MemberBinarySignature {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
data class AccessFlags(val access: Int) {
|
||||||
|
val isPublic: Boolean get() = isPublic(access)
|
||||||
|
val isProtected: Boolean get() = isProtected(access)
|
||||||
|
val isStatic: Boolean get() = isStatic(access)
|
||||||
|
val isFinal: Boolean get() = isFinal(access)
|
||||||
|
val isSynthetic: Boolean get() = isSynthetic(access)
|
||||||
|
|
||||||
|
fun getModifiers(): List<String> = ACCESS_NAMES.entries.mapNotNull { if (access and it.key != 0) it.value else null }
|
||||||
|
fun getModifierString(): String = getModifiers().joinToString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isPublic(access: Int) = access and Opcodes.ACC_PUBLIC != 0 || access and Opcodes.ACC_PROTECTED != 0
|
||||||
|
fun isProtected(access: Int) = access and Opcodes.ACC_PROTECTED != 0
|
||||||
|
fun isStatic(access: Int) = access and Opcodes.ACC_STATIC != 0
|
||||||
|
fun isFinal(access: Int) = access and Opcodes.ACC_FINAL != 0
|
||||||
|
fun isSynthetic(access: Int) = access and Opcodes.ACC_SYNTHETIC != 0
|
||||||
|
|
||||||
|
|
||||||
fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
|
fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
|
||||||
isPublic()
|
isPublic(access)
|
||||||
&& !isLocal()
|
&& !isLocal()
|
||||||
&& !isWhenMappings()
|
&& !isWhenMappings()
|
||||||
&& (classVisibility?.isPublic(isInlineExposed()) ?: true)
|
&& (classVisibility?.isPublic(isInlineExposed()) ?: true)
|
||||||
&& !isNonPublicFileOrFacade(classVisibility)
|
|
||||||
|
|
||||||
fun ClassNode.isNonPublicFileOrFacade(classVisibility: ClassVisibility?) =
|
|
||||||
isFileOrMultipartFacade()
|
|
||||||
&& methods.none { it.isEffectivelyPublic(classVisibility) }
|
|
||||||
&& fields.none { it.isEffectivelyPublic(classVisibility) }
|
|
||||||
|
|
||||||
|
|
||||||
fun MethodNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
|
val ClassNode.innerClassNode: InnerClassNode? get() = innerClasses.singleOrNull { it.name == name }
|
||||||
isPublic()
|
fun ClassNode.isLocal() = innerClassNode?.run { innerName == null && outerName == null} ?: false
|
||||||
&& (classVisibility?.members?.get(MemberSignature(name, desc))?.isPublic(isInlineExposed()) ?: true)
|
fun ClassNode.isWhenMappings() = isSynthetic(access) && name.endsWith("\$WhenMappings")
|
||||||
&& !isAccessMethod()
|
|
||||||
|
|
||||||
fun FieldNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
|
val ClassNode.effectiveAccess: Int get() = innerClassNode?.access ?: access
|
||||||
isPublic()
|
val ClassNode.outerClassName: String? get() = innerClassNode?.outerName
|
||||||
&& (classVisibility?.members?.get(MemberSignature(name, desc))?.isPublic(isInlineExposed()) ?: true)
|
|
||||||
// TODO: lateinit exposed field
|
|
||||||
|
|
||||||
|
|
||||||
fun ClassNode.isLocal() = innerClasses.filter { it.name == name && it.innerName == null && it.outerName == null }.count() == 1
|
|
||||||
fun ClassNode.isWhenMappings() = isSynthetic() && name.endsWith("\$WhenMappings")
|
|
||||||
fun MethodNode.isAccessMethod() = isSynthetic() && name.startsWith("access\$")
|
|
||||||
|
|
||||||
val ClassNode.effectiveAccess: Int get() = innerClasses.singleOrNull { it.name == name }?.access ?: access
|
|
||||||
|
|
||||||
const val inlineExposedAnnotationName = "kotlin/internal/InlineExposed"
|
const val inlineExposedAnnotationName = "kotlin/internal/InlineExposed"
|
||||||
fun ClassNode.isInlineExposed() = hasAnnotation(inlineExposedAnnotationName, includeInvisible = true)
|
fun ClassNode.isInlineExposed() = hasAnnotation(inlineExposedAnnotationName, includeInvisible = true)
|
||||||
fun MethodNode.isInlineExposed() = hasAnnotation(inlineExposedAnnotationName, includeInvisible = true)
|
fun MethodNode.isInlineExposed() = hasAnnotation(inlineExposedAnnotationName, includeInvisible = true)
|
||||||
@@ -96,7 +143,3 @@ private fun List<Any>.annotationValue(key: String): Any? {
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val ClassNode.outerClassName: String?
|
|
||||||
get() = innerClasses.singleOrNull { it.name == name }?.outerName
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user