Minor: refactoring

This commit is contained in:
Ilya Gorbunov
2016-03-22 20:31:05 +03:00
parent 88a038a0df
commit da032e371a
3 changed files with 148 additions and 124 deletions
@@ -1,10 +1,9 @@
package org.jetbrains.kotlin.tools
import com.google.gson.internal.Streams
import com.google.gson.stream.JsonReader
import org.objectweb.asm.*
import org.objectweb.asm.tree.*
import java.io.File
import java.io.InputStream
import java.util.jar.JarFile
import kotlin.comparisons.compareBy
@@ -17,27 +16,20 @@ fun main(args: Array<String>) {
}
fun JarFile.classEntries() = entries().asSequence().filter { !it.isDirectory && it.name.endsWith(".class") }
val ACCESS_NAMES = mapOf(
Opcodes.ACC_PUBLIC to "public",
Opcodes.ACC_PROTECTED to "protected",
Opcodes.ACC_PRIVATE to "private",
Opcodes.ACC_STATIC to "static",
Opcodes.ACC_FINAL to "final",
Opcodes.ACC_ABSTRACT to "abstract",
Opcodes.ACC_SYNTHETIC to "synthetic",
Opcodes.ACC_INTERFACE to "interface",
Opcodes.ACC_ANNOTATION to "annotation")
fun JarFile.classEntries() = entries().asSequence().filter {
!it.isDirectory && it.name.endsWith(".class")
}
data class ClassBinarySignature(val name: String, val outerName: String?, val modifiers: String, val supertypes: List<String>, val memberSignatures: List<String>, val isPublic: Boolean)
data class ClassBinarySignature(val name: String, val outerName: String?, val signature: String, val memberSignatures: List<String>, val isPublic: 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> = jar.classEntries()
.map { entry ->
jar.getInputStream(entry).use { stream ->
fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>): List<ClassBinarySignature> =
getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityMap)
fun getBinaryAPI(classStreams: Sequence<InputStream>, visibilityMap: Map<String, ClassVisibility>): List<ClassBinarySignature> =
classStreams.map { it.use { stream ->
val classNode = ClassNode()
ClassReader(stream).accept(classNode, ClassReader.SKIP_CODE)
classNode
@@ -51,8 +43,7 @@ fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>): Lis
val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted()
val classSignature = "${getModifierString(access)} class $name" +
if (supertypes.isEmpty()) "" else ": ${supertypes.joinToString()}"
val modifiers = getModifierString(access)
val memberSignatures =
fields.filter { it.isPublic() }
@@ -62,7 +53,7 @@ fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>): Lis
.sortedWith(compareBy({ it.name }, { it.desc }))
.map { with(it) { "${getModifierString(access)} fun $name $desc" } }
ClassBinarySignature(name, outerClassName, classSignature, memberSignatures, isPublic)
ClassBinarySignature(name, outerClassName, modifiers, supertypes, memberSignatures, isPublic)
}}
@@ -73,109 +64,14 @@ fun List<ClassBinarySignature>.filterOutNonPublic(): List<ClassBinarySignature>
fun ClassBinarySignature.isPublicAndAccessible(): Boolean =
isPublic && (outerName == null || classByName[outerName]?.isPublicAndAccessible() ?: true)
return filter { it.isPublicAndAccessible() }
return filter { it -> it.isPublicAndAccessible() }
}
fun List<ClassBinarySignature>.dump() = forEach {
println(it.signature)
it.memberSignatures.forEach { println(it) }
println("------------------\n")
}
fun List<ClassBinarySignature>.dump() = dump(to = System.out)
fun <T: Appendable> List<ClassBinarySignature>.dump(to: T): T = to.apply { this@dump.forEach {
appendln(it.signature)
it.memberSignatures.forEach { appendln(it) }
appendln("------------------\n")
}}
fun isPublic(access: Int) = access and Opcodes.ACC_PUBLIC != 0 || access and Opcodes.ACC_PROTECTED != 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(" ")
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)
fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
isPublic()
&& !isLocal()
&& !isWhenMappings()
&& (classVisibility?.isPublic() ?: true)
&& !isNonPublicFileOrFacade(classVisibility)
fun ClassNode.isNonPublicFileOrFacade(classVisibility: ClassVisibility?) =
isFileOrMultipartFacade()
&& methods.none { it.isEffectivelyPublic(classVisibility) }
&& fields.none { it.isPublic() }
fun MethodNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
isPublic()
&& (classVisibility?.members?.get(MemberSignature(name, desc))?.isPublic() ?: true)
&& !isAccessMethod()
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\$")
private val FILE_OR_MULTIPART_FACADE_KINDS = listOf(2, 4)
fun ClassNode.isFileOrMultipartFacade() = kotlinClassKind.let { it != null && it in FILE_OR_MULTIPART_FACADE_KINDS }
val ClassNode.kotlinClassKind: Int?
get() = visibleAnnotations
?.filter { it.desc == "Lkotlin/Metadata;" }
?.map { (it.values.annotationValue("k") as? Int) }
?.firstOrNull()
private fun List<Any>.annotationValue(key: String): Any? {
for (index in (0 .. size / 2 - 1)) {
if (this[index*2] == key)
return this[index*2 + 1]
}
return null
}
val ClassNode.outerClassName: String?
get() = innerClasses.singleOrNull { it.name == name }?.outerName
fun readKotlinVisibilities(declarationFile: File): Map<String, ClassVisibility> {
val result = mutableListOf<ClassVisibility>()
declarationFile.bufferedReader().use { reader ->
val jsonReader = JsonReader(reader)
jsonReader.beginArray()
while (jsonReader.hasNext()) {
val classObject = Streams.parse(jsonReader).asJsonObject
result += with (classObject) {
val name = getAsJsonPrimitive("class").asString
val visibility = getAsJsonPrimitive("visibility")?.asString
val members = getAsJsonArray("members").map { it -> with(it.asJsonObject) {
val name = getAsJsonPrimitive("name").asString
val desc = getAsJsonPrimitive("desc").asString
val visibility = getAsJsonPrimitive("visibility")?.asString
MemberVisibility(MemberSignature(name, desc), visibility)
}}
ClassVisibility(name, visibility, members.associateByTo(hashMapOf()) { it.member })
}
}
jsonReader.endArray()
}
return result.associateByTo(hashMapOf()) { it.name }
}
data class ClassVisibility(val name: String, val visibility: String?, val members: Map<MemberSignature, MemberVisibility>)
fun ClassVisibility.isPublic() = visibility == "public"
data class MemberVisibility(val member: MemberSignature, val visibility: String?)
data class MemberSignature(val name: String, val desc: String)
fun MemberVisibility.isPublic() = visibility == "public" || visibility == "protected"
@@ -0,0 +1,85 @@
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",
Opcodes.ACC_PRIVATE to "private",
Opcodes.ACC_STATIC to "static",
Opcodes.ACC_FINAL to "final",
Opcodes.ACC_ABSTRACT to "abstract",
Opcodes.ACC_SYNTHETIC to "synthetic",
Opcodes.ACC_INTERFACE to "interface",
Opcodes.ACC_ANNOTATION to "annotation")
private fun isPublic(access: Int) = access and Opcodes.ACC_PUBLIC != 0 || access and Opcodes.ACC_PROTECTED != 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(" ")
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)
fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
isPublic()
&& !isLocal()
&& !isWhenMappings()
&& (classVisibility?.isPublic() ?: true)
&& !isNonPublicFileOrFacade(classVisibility)
fun ClassNode.isNonPublicFileOrFacade(classVisibility: ClassVisibility?) =
isFileOrMultipartFacade()
&& methods.none { it.isEffectivelyPublic(classVisibility) }
&& fields.none { it.isPublic() }
fun MethodNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
isPublic()
&& (classVisibility?.members?.get(MemberSignature(name, desc))?.isPublic() ?: true)
&& !isAccessMethod()
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\$")
private object KotlinClassKind {
const val FILE = 2
const val MULTIPART_FACADE = 4
val FILE_OR_MULTIPART_FACADE_KINDS = listOf(FILE, MULTIPART_FACADE)
}
fun ClassNode.isFileOrMultipartFacade() = kotlinClassKind.let { it != null && it in KotlinClassKind.FILE_OR_MULTIPART_FACADE_KINDS }
val ClassNode.kotlinClassKind: Int?
get() = visibleAnnotations
?.filter { it.desc == "Lkotlin/Metadata;" }
?.map { (it.values.annotationValue("k") as? Int) }
?.firstOrNull()
fun ClassNode.hasAnnotation(annotationName: String)
= "L$annotationName;".let { desc -> visibleAnnotations?.any { it.desc == desc } ?: false }
private fun List<Any>.annotationValue(key: String): Any? {
for (index in (0 .. size / 2 - 1)) {
if (this[index*2] == key)
return this[index*2 + 1]
}
return null
}
val ClassNode.outerClassName: String?
get() = innerClasses.singleOrNull { it.name == name }?.outerName
@@ -0,0 +1,43 @@
package org.jetbrains.kotlin.tools
import com.google.gson.internal.Streams
import com.google.gson.stream.JsonReader
import java.io.File
data class ClassVisibility(val name: String, val visibility: String?, val members: Map<MemberSignature, MemberVisibility>)
data class MemberVisibility(val member: MemberSignature, val visibility: String?)
data class MemberSignature(val name: String, val desc: String)
private fun isPublic(visibility: String?) = visibility == null || visibility == "public" || visibility == "protected"
fun ClassVisibility.isPublic() = isPublic(visibility)
fun MemberVisibility.isPublic() = isPublic(visibility)
fun readKotlinVisibilities(declarationFile: File): Map<String, ClassVisibility> {
val result = mutableListOf<ClassVisibility>()
declarationFile.bufferedReader().use { reader ->
val jsonReader = JsonReader(reader)
jsonReader.beginArray()
while (jsonReader.hasNext()) {
val classObject = Streams.parse(jsonReader).asJsonObject
result += with (classObject) {
val name = getAsJsonPrimitive("class").asString
val visibility = getAsJsonPrimitive("visibility")?.asString
val members = getAsJsonArray("members").map { it ->
with(it.asJsonObject) {
val name = getAsJsonPrimitive("name").asString
val desc = getAsJsonPrimitive("desc").asString
val visibility = getAsJsonPrimitive("visibility")?.asString
MemberVisibility(MemberSignature(name, desc), visibility)
}
}
ClassVisibility(name, visibility, members.associateByTo(hashMapOf()) { it.member })
}
}
jsonReader.endArray()
}
return result.associateByTo(hashMapOf()) { it.name }
}