Skip to content

Commit 353428e

Browse files
committed
Use 'ClassAnnotationsDescriptor' to store annotation data
1 parent fdda59b commit 353428e

File tree

2 files changed

+118
-65
lines changed

2 files changed

+118
-65
lines changed

plugin/main/src/kotlinx/benchmark/gradle/AndroidTasks.kt

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import kotlinx.benchmark.gradle.internal.KotlinxBenchmarkPluginInternalApi
44
import org.gradle.api.*
55
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
66
import java.io.File
7+
import java.util.jar.JarFile
78

89
@KotlinxBenchmarkPluginInternalApi
910
fun Project.unpackAndProcessAar(target: KotlinJvmAndroidCompilation) {
@@ -12,7 +13,6 @@ fun Project.unpackAndProcessAar(target: KotlinJvmAndroidCompilation) {
1213
if (aarFile.exists()) {
1314
val unpackedDir = File("${project.projectDir}/build/outputs/unpacked-aar/${target.name}")
1415
val classesJar = File(unpackedDir, "classes.jar")
15-
val unzipDir = File("${project.projectDir}/build/outputs/unzipped-classes/${target.name}")
1616

1717
// Unpack AAR file
1818
project.copy {
@@ -21,46 +21,51 @@ fun Project.unpackAndProcessAar(target: KotlinJvmAndroidCompilation) {
2121
}
2222

2323
if (classesJar.exists()) {
24-
project.copy {
25-
it.from(project.zipTree(classesJar))
26-
it.into(unzipDir)
27-
}
28-
println("Unzipped classes.jar to: $unzipDir")
24+
println("Processing classes.jar for ${target.name}")
25+
val jar = JarFile(classesJar)
26+
val entries = jar.entries()
2927

30-
// Process the .class files to retrieve annotation data
3128
val annotationProcessor = AnnotationProcessor()
32-
unzipDir.walk().forEach { file ->
33-
if (file.extension == "class") {
34-
println("Processing class file: $file")
35-
annotationProcessor.processClassFile(file)
29+
30+
while (entries.hasMoreElements()) {
31+
val entry = entries.nextElement()
32+
if (entry.name.endsWith(".class")) {
33+
val inputStream = jar.getInputStream(entry)
34+
val classBytes = inputStream.readBytes()
35+
annotationProcessor.processClassBytes(classBytes)
3636
}
3737
}
38+
jar.close()
3839

39-
val annotations = annotationProcessor.getClassAnnotations()
40-
annotations.forEach { (className, classAnnotations) ->
41-
println("Annotation for class: $className")
40+
val classAnnotationsDescriptors = annotationProcessor.getClassDescriptors()
41+
println("Class annotations for ${target.name}:")
4242

43-
classAnnotations.classAnnotations.forEach { (annotationDesc, annotationData) ->
44-
println("Class annotation: $annotationDesc")
45-
annotationData.parameters.forEach { (name, value) ->
46-
println(" - $name: $value")
43+
classAnnotationsDescriptors.forEach { descriptor ->
44+
println("Class: ${descriptor.name}")
45+
println(" Visibility: ${descriptor.visibility}")
46+
descriptor.annotations.forEach { annotation ->
47+
println(" Annotation: ${annotation.name}")
48+
annotation.parameters.forEach { (key, value) ->
49+
println(" $key: $value")
4750
}
4851
}
49-
50-
classAnnotations.methodAnnotations.forEach { (methodName, methodAnnotationMap) ->
51-
methodAnnotationMap.forEach { (annotationDesc, annotationData) ->
52-
println("Method annotation in $methodName: $annotationDesc")
53-
annotationData.parameters.forEach { (name, value) ->
54-
println(" - $name: $value")
52+
descriptor.methods.forEach { method ->
53+
println(" Method: ${method.name}")
54+
println(" Visibility: ${method.visibility}")
55+
method.annotations.forEach { annotation ->
56+
println(" Annotation: ${annotation.name}")
57+
annotation.parameters.forEach { (key, value) ->
58+
println(" $key: $value")
5559
}
5660
}
5761
}
58-
59-
classAnnotations.fieldAnnotations.forEach { (fieldName, fieldAnnotationMap) ->
60-
fieldAnnotationMap.forEach { (annotationDesc, annotationData) ->
61-
println("Field annotation in $fieldName: $annotationDesc")
62-
annotationData.parameters.forEach { (name, value) ->
63-
println(" - $name: $value")
62+
descriptor.fields.forEach { field ->
63+
println(" Field: ${field.name}")
64+
println(" Visibility: ${field.visibility}")
65+
field.annotations.forEach { annotation ->
66+
println(" Annotation: ${annotation.name}")
67+
annotation.parameters.forEach { (key, value) ->
68+
println(" $key: $value")
6469
}
6570
}
6671
}

plugin/main/src/kotlinx/benchmark/gradle/AnnotationProcessor.kt

Lines changed: 83 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,74 @@ package kotlinx.benchmark.gradle
22

33
import org.jetbrains.org.objectweb.asm.*
44
import org.jetbrains.org.objectweb.asm.tree.*
5-
import java.io.File
65

76
data class AnnotationData(
7+
val name: String,
88
val parameters: Map<String, Any?>
99
)
1010

11-
data class ClassAnnotations(
12-
val classAnnotations: Map<String, AnnotationData>,
13-
val methodAnnotations: Map<String, Map<String, AnnotationData>>,
14-
val fieldAnnotations: Map<String, Map<String, AnnotationData>>
11+
data class ClassAnnotationsDescriptor(
12+
val packageName: String,
13+
val name: String,
14+
val visibility: String,
15+
val annotations: List<AnnotationData>,
16+
val methods: List<MethodAnnotationsDescriptor>,
17+
val fields: List<FieldAnnotationsDescriptor>
18+
)
19+
20+
data class MethodAnnotationsDescriptor(
21+
val name: String,
22+
val visibility: String,
23+
val annotations: List<AnnotationData>
24+
)
25+
26+
data class FieldAnnotationsDescriptor(
27+
val name: String,
28+
val visibility: String,
29+
val annotations: List<AnnotationData>
1530
)
1631

1732
class AnnotationProcessor {
1833

19-
private val classAnnotationsMap = mutableMapOf<String, ClassAnnotations>()
34+
private val classAnnotationsDescriptors = mutableListOf<ClassAnnotationsDescriptor>()
2035

21-
fun processClassFile(classFile: File) {
22-
val classReader = ClassReader(classFile.readBytes())
36+
fun processClassBytes(classBytes: ByteArray) {
37+
val classReader = ClassReader(classBytes)
2338
val classNode = ClassNode()
2439
classReader.accept(classNode, 0)
2540

26-
val classAnnotations = mutableMapOf<String, AnnotationData>()
27-
val methodAnnotations = mutableMapOf<String, MutableMap<String, AnnotationData>>()
28-
val fieldAnnotations = mutableMapOf<String, MutableMap<String, AnnotationData>>()
41+
val classAnnotations = classNode.visibleAnnotations
42+
?.filter { it.desc != "Lkotlin/Metadata;" }
43+
?.map { parseAnnotation(it) }
44+
?: emptyList()
2945

30-
classNode.visibleAnnotations?.forEach { annotationNode ->
31-
if (annotationNode.desc != "Lkotlin/Metadata;") {
32-
val annotationData = parseAnnotation(annotationNode)
33-
classAnnotations[annotationNode.desc] = annotationData
34-
}
46+
val methodDescriptors = classNode.methods.map { methodNode ->
47+
val methodAnnotations = methodNode.visibleAnnotations
48+
?.filter { it.desc != "Lkotlin/Metadata;" }
49+
?.map { parseAnnotation(it) }
50+
?: emptyList()
51+
MethodAnnotationsDescriptor(methodNode.name, getVisibility(methodNode.access), methodAnnotations)
3552
}
3653

37-
classNode.methods?.forEach { methodNode ->
38-
methodNode.visibleAnnotations?.forEach { annotationNode ->
39-
if (annotationNode.desc != "Lkotlin/Metadata;") {
40-
val annotationData = parseAnnotation(annotationNode)
41-
methodAnnotations.getOrPut(methodNode.name) { mutableMapOf() }[annotationNode.desc] = annotationData
42-
}
43-
}
54+
val fieldDescriptors = classNode.fields.map { fieldNode ->
55+
val fieldAnnotations = fieldNode.visibleAnnotations
56+
?.filter { it.desc != "Lkotlin/Metadata;" }
57+
?.map { parseAnnotation(it) }
58+
?: emptyList()
59+
FieldAnnotationsDescriptor(fieldNode.name, getFieldVisibility(classNode, fieldNode), fieldAnnotations)
4460
}
4561

46-
classNode.fields?.forEach { fieldNode ->
47-
fieldNode.visibleAnnotations?.forEach { annotationNode ->
48-
if (annotationNode.desc != "Lkotlin/Metadata;") {
49-
val annotationData = parseAnnotation(annotationNode)
50-
fieldAnnotations.getOrPut(fieldNode.name) { mutableMapOf() }[annotationNode.desc] = annotationData
51-
}
52-
}
53-
}
62+
val packageName = classNode.name.substringBeforeLast('/', "").replace('/', '.')
63+
val classDescriptor = ClassAnnotationsDescriptor(
64+
packageName,
65+
classNode.name.replace('/', '.').substringAfterLast('/'),
66+
getVisibility(classNode.access),
67+
classAnnotations,
68+
methodDescriptors,
69+
fieldDescriptors
70+
)
5471

55-
classAnnotationsMap[classNode.name] = ClassAnnotations(classAnnotations, methodAnnotations, fieldAnnotations)
72+
classAnnotationsDescriptors.add(classDescriptor)
5673
}
5774

5875
private fun parseAnnotation(annotationNode: AnnotationNode): AnnotationData {
@@ -64,7 +81,7 @@ class AnnotationProcessor {
6481
parameters[name] = formatAnnotationValue(value)
6582
}
6683
}
67-
return AnnotationData(parameters)
84+
return AnnotationData(annotationNode.desc.removePrefix("L").removeSuffix(";").replace('/', '.'), parameters)
6885
}
6986

7087
private fun formatAnnotationValue(value: Any?): Any? {
@@ -103,7 +120,38 @@ class AnnotationProcessor {
103120
return sb.toString()
104121
}
105122

106-
fun getClassAnnotations(): Map<String, ClassAnnotations> {
107-
return classAnnotationsMap
123+
private fun getVisibility(access: Int): String {
124+
return when {
125+
(access and Opcodes.ACC_PUBLIC) != 0 -> "public"
126+
(access and Opcodes.ACC_PROTECTED) != 0 -> "protected"
127+
(access and Opcodes.ACC_PRIVATE) != 0 -> "private"
128+
else -> "package-private"
129+
}
130+
}
131+
132+
private fun getFieldVisibility(classNode: ClassNode, fieldNode: FieldNode): String {
133+
val getterName = "get${fieldNode.name.capitalize()}"
134+
val setterName = "set${fieldNode.name.capitalize()}"
135+
136+
val getterMethod = classNode.methods.find { it.name == getterName }
137+
val setterMethod = classNode.methods.find { it.name == setterName }
138+
139+
return if (getterMethod != null && setterMethod != null) {
140+
val getterVisibility = getVisibility(getterMethod.access)
141+
val setterVisibility = getVisibility(setterMethod.access)
142+
if (getterVisibility == setterVisibility) getterVisibility else "package-private"
143+
} else {
144+
getVisibility(fieldNode.access)
145+
}
146+
}
147+
148+
fun getClassDescriptors(): List<ClassAnnotationsDescriptor> {
149+
return classAnnotationsDescriptors
150+
}
151+
152+
fun getClassAnnotations(packageName: String, className: String): List<AnnotationData> {
153+
return classAnnotationsDescriptors
154+
.filter { it.packageName == packageName && it.name == className }
155+
.flatMap { it.annotations }
108156
}
109157
}

0 commit comments

Comments
 (0)