Refactor data structures

This commit is contained in:
Ilya Gorbunov
2016-03-23 21:59:57 +03:00
parent 10792de4c1
commit 4e250e4dd1
2 changed files with 95 additions and 56 deletions
@@ -19,13 +19,6 @@ fun main(args: Array<String>) {
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> =
getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityMap)
@@ -36,26 +29,29 @@ fun getBinaryAPI(classStreams: Sequence<InputStream>, visibilityMap: Map<String,
classNode
}
}
.asIterable()
.sortedBy { it.name }
.mapNotNull { with(it) {
.map { with(it) {
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 modifiers = getModifierString(effectiveAccess and Opcodes.ACC_STATIC.inv())
val memberSignatures =
fields.filter { it.isEffectivelyPublic(classVisibility) }
val memberSignatures = (
fields
.sortedBy { it.name }
.map { with(it) { MemberBinarySignature(name, "${getModifierString(access)} field $name $desc", isStatic(access) ) } } +
methods.filter { it.isEffectivelyPublic(classVisibility) }
.map { with(it) { FieldBinarySignature(name, desc, isInlineExposed(), AccessFlags(access)) } } +
methods
.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 }
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] })
@@ -73,7 +69,7 @@ fun List<ClassBinarySignature>.filterOutNonPublic(): List<ClassBinarySignature>
if (nonPublicSupertypes.isEmpty())
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
return this.copy(memberSignatures = memberSignatures + inheritedStaticSignatures, supertypes = supertypes - superName)
@@ -3,7 +3,6 @@ package org.jetbrains.kotlin.tools
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.*
val ACCESS_NAMES = mapOf(
Opcodes.ACC_PUBLIC to "public",
Opcodes.ACC_PROTECTED to "protected",
@@ -15,49 +14,97 @@ val ACCESS_NAMES = mapOf(
Opcodes.ACC_INTERFACE to "interface",
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
public fun isStatic(access: Int) = access and Opcodes.ACC_STATIC != 0
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(" ")
val signature: String
get() = "${access.getModifierString()} class $name" + if (supertypes.isEmpty()) "" else ": ${supertypes.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)
fun FieldNode.isPublic() = isPublic(access)
}
interface MemberBinarySignature {
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?) =
isPublic()
isPublic(access)
&& !isLocal()
&& !isWhenMappings()
&& (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?) =
isPublic()
&& (classVisibility?.members?.get(MemberSignature(name, desc))?.isPublic(isInlineExposed()) ?: true)
&& !isAccessMethod()
val ClassNode.innerClassNode: InnerClassNode? get() = innerClasses.singleOrNull { it.name == name }
fun ClassNode.isLocal() = innerClassNode?.run { innerName == null && outerName == null} ?: false
fun ClassNode.isWhenMappings() = isSynthetic(access) && name.endsWith("\$WhenMappings")
fun FieldNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
isPublic()
&& (classVisibility?.members?.get(MemberSignature(name, desc))?.isPublic(isInlineExposed()) ?: true)
// TODO: lateinit exposed field
val ClassNode.effectiveAccess: Int get() = innerClassNode?.access ?: access
val ClassNode.outerClassName: String? get() = innerClassNode?.outerName
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"
fun ClassNode.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
}
val ClassNode.outerClassName: String?
get() = innerClasses.singleOrNull { it.name == name }?.outerName