Minor: refactoring
This commit is contained in:
+20
-124
@@ -1,10 +1,9 @@
|
|||||||
package org.jetbrains.kotlin.tools
|
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.*
|
||||||
import org.objectweb.asm.tree.*
|
import org.objectweb.asm.tree.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
import java.util.jar.JarFile
|
import java.util.jar.JarFile
|
||||||
import kotlin.comparisons.compareBy
|
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 {
|
data class ClassBinarySignature(val name: String, val outerName: String?, val modifiers: String, val supertypes: List<String>, val memberSignatures: List<String>, val isPublic: Boolean)
|
||||||
!it.isDirectory && it.name.endsWith(".class")
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ->
|
fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>): List<ClassBinarySignature> =
|
||||||
jar.getInputStream(entry).use { stream ->
|
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()
|
val classNode = ClassNode()
|
||||||
ClassReader(stream).accept(classNode, ClassReader.SKIP_CODE)
|
ClassReader(stream).accept(classNode, ClassReader.SKIP_CODE)
|
||||||
classNode
|
classNode
|
||||||
@@ -51,8 +43,7 @@ fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>): Lis
|
|||||||
|
|
||||||
val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted()
|
val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted()
|
||||||
|
|
||||||
val classSignature = "${getModifierString(access)} class $name" +
|
val modifiers = getModifierString(access)
|
||||||
if (supertypes.isEmpty()) "" else ": ${supertypes.joinToString()}"
|
|
||||||
|
|
||||||
val memberSignatures =
|
val memberSignatures =
|
||||||
fields.filter { it.isPublic() }
|
fields.filter { it.isPublic() }
|
||||||
@@ -62,7 +53,7 @@ fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>): Lis
|
|||||||
.sortedWith(compareBy({ it.name }, { it.desc }))
|
.sortedWith(compareBy({ it.name }, { it.desc }))
|
||||||
.map { with(it) { "${getModifierString(access)} fun $name $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 =
|
fun ClassBinarySignature.isPublicAndAccessible(): Boolean =
|
||||||
isPublic && (outerName == null || classByName[outerName]?.isPublicAndAccessible() ?: true)
|
isPublic && (outerName == null || classByName[outerName]?.isPublicAndAccessible() ?: true)
|
||||||
|
|
||||||
return filter { it.isPublicAndAccessible() }
|
return filter { it -> it.isPublicAndAccessible() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun List<ClassBinarySignature>.dump() = forEach {
|
fun List<ClassBinarySignature>.dump() = dump(to = System.out)
|
||||||
println(it.signature)
|
|
||||||
it.memberSignatures.forEach { println(it) }
|
|
||||||
println("------------------\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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"
|
|
||||||
|
|||||||
+85
@@ -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
|
||||||
|
|
||||||
+43
@@ -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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user