1
1
package kotlinx.benchmark.integration
2
2
3
+ @OptIn(ExperimentalStdlibApi ::class )
3
4
class AnnotationsSpecifier {
4
- private var isMeasurementSpecified: Boolean = false
5
- private var iterations: Int? = null
6
- private var time: Int? = null
7
- private var timeUnit: String? = null
5
+ private val classAnnotations = mutableListOf<Annotation >()
6
+ private val propertyAnnotations = mutableListOf<AnnotatedMember >()
7
+ private val functionAnnotations = mutableListOf<AnnotatedMember >()
8
8
9
9
fun measurement (iterations : Int , time : Int , timeUnit : String ) {
10
- isMeasurementSpecified = true
11
- this .iterations = iterations
12
- this .time = time
13
- this .timeUnit = timeUnit
10
+ classAnnotations.add(
11
+ Annotation (" @Measurement" , listOf (iterations, time, timeUnit))
12
+ )
14
13
}
15
14
16
- fun replacementForLine (line : String ): String {
15
+ fun outputTimeUnit (timeUnit : String ) {
16
+ classAnnotations.add(
17
+ Annotation (" @OutputTimeUnit" , listOf (timeUnit))
18
+ )
19
+ }
20
+
21
+ fun benchmarkMode (mode : String ) {
22
+ classAnnotations.add(
23
+ Annotation (" @BenchmarkMode" , listOf (mode))
24
+ )
25
+ }
26
+
27
+ fun benchmark (functionName : String ) {
28
+ functionAnnotations.add(
29
+ AnnotatedMember (functionName, Annotation (" @Benchmark" ))
30
+ )
31
+ }
32
+
33
+ fun setup (functionName : String ) {
34
+ functionAnnotations.add(
35
+ AnnotatedMember (functionName, Annotation (" @Setup" ))
36
+ )
37
+ }
38
+
39
+ fun teardown (functionName : String ) {
40
+ functionAnnotations.add(
41
+ AnnotatedMember (functionName, Annotation (" @TearDown" ))
42
+ )
43
+ }
44
+
45
+ fun param (propertyName : String , vararg values : String ) {
46
+ require(values.all { ' \" ' !in it }) { " TODO: Support param values that contain '\" '." }
47
+
48
+ propertyAnnotations.add(
49
+ AnnotatedMember (propertyName, Annotation (" @Param" , values.map { " \" $it \" " }))
50
+ )
51
+ }
52
+
53
+ fun annotationsForProperty (line : String ): List <String > {
54
+ val annotations = mutableListOf<String >()
55
+ for ((propertyName, annotation) in propertyAnnotations) {
56
+ val regex = Regex (" \\ s*(public|private|protected|internal)?\\ s*(final|open)?\\ s*(val|var)\\ s+${Regex .escape(propertyName)} " )
57
+ if (regex.matchesAt(line, 0 )) {
58
+ check(! annotation.isUsed)
59
+ annotation.isUsed = true
60
+ annotations.add(annotation.toCode())
61
+ }
62
+ }
63
+ return annotations
64
+ }
65
+
66
+ fun annotationsForFunction (line : String ): List <String > {
67
+ val annotations = mutableListOf<String >()
68
+ for ((functionName, annotation) in functionAnnotations) {
69
+ val regex = Regex (" \\ s*(public|private|protected|internal)?\\ s*(final|open)?\\ s*fun\\ s+${Regex .escape(functionName)} \\ (" )
70
+ if (regex.matchesAt(line, 0 )) {
71
+ check(! annotation.isUsed)
72
+ annotation.isUsed = true
73
+ annotations.add(annotation.toCode())
74
+ }
75
+ }
76
+ return annotations
77
+ }
78
+
79
+ fun replaceClassAnnotation (line : String ): String {
17
80
val trimmedLine = line.trimStart()
18
81
val prefix = line.substring(0 , line.length - trimmedLine.length)
19
- return when {
20
- isMeasurementSpecified && trimmedLine.startsWith(" @Measurement" ) ->
21
- " $prefix @Measurement($iterations , $time , $timeUnit )"
22
- else ->
23
- line
82
+ for (annotation in classAnnotations) {
83
+ if (trimmedLine.startsWith(annotation.name)) {
84
+ check(! annotation.isUsed)
85
+ annotation.isUsed = true
86
+ return prefix + annotation.toCode()
87
+ }
24
88
}
89
+ return line
90
+ }
91
+
92
+ fun checkAllAnnotationsAreUsed () {
93
+ classAnnotations.forEach { check(it.isUsed) { " Unused class annotation: $it " } }
94
+ propertyAnnotations.forEach { check(it.annotation.isUsed) { " Unused property annotation: $it " } }
95
+ functionAnnotations.forEach { check(it.annotation.isUsed) { " Unused function annotation: $it " } }
25
96
}
97
+ }
98
+
99
+ private data class AnnotatedMember (
100
+ val memberName : String ,
101
+ val annotation : Annotation
102
+ )
103
+
104
+ private data class Annotation (
105
+ val name : String ,
106
+ val arguments : List <Any ?> = emptyList(),
107
+ var isUsed : Boolean = false
108
+ ) {
109
+ fun toCode (): String =
110
+ " $name${if (arguments.isEmpty()) " " else arguments.joinToString(" , " , " (" , " )" )} "
26
111
}
0 commit comments