Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.annotation.AnnotationCollectorExtension.kt Maven / Gradle / Ivy
package org.jetbrains.kotlin.annotation
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
import org.jetbrains.kotlin.codegen.DelegatingClassBuilder
import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.*
import java.io.File
import java.io.IOException
import java.io.Writer
import java.util.regex.Pattern
import java.util.regex.PatternSyntaxException
abstract class AnnotationCollectorExtensionBase (val supportInheritedAnnotations: Boolean ) : ClassBuilderInterceptorExtension {
private object RecordTypes {
val ANNOTATED_CLASS = "c"
val ANNOTATED_METHOD = "m"
val ANNOTATED_FIELD = "f"
val SHORTENED_ANNOTATION = "a"
val SHORTENED_PACKAGE_NAME = "p"
val CLASS_DECLARATION = "d"
}
protected abstract val annotationFilterList: List ?
private val shortenedAnnotationCache = ShortenedNameCache (RecordTypes .SHORTENED_ANNOTATION )
private val shortenedPackageNameCache = ShortenedNameCache (RecordTypes .SHORTENED_PACKAGE_NAME )
override fun interceptClassBuilderFactory(
interceptedFactory: ClassBuilderFactory ,
bindingContext: BindingContext ,
diagnostics: DiagnosticSink
): ClassBuilderFactory {
return AnnotationCollectorClassBuilderFactory (interceptedFactory, getWriter(diagnostics), diagnostics)
}
protected abstract fun getWriter(diagnostic: DiagnosticSink ): Writer
protected abstract fun closeWriter()
private inner class AnnotationCollectorClassBuilderFactory (
delegateFactory: ClassBuilderFactory ,
val writer: Writer ,
val diagnostics: DiagnosticSink
) : DelegatingClassBuilderFactory (delegateFactory) {
override fun newClassBuilder(origin: JvmDeclarationOrigin ): DelegatingClassBuilder {
return AnnotationCollectorClassBuilder (delegate.newClassBuilder(origin), writer, diagnostics)
}
override fun close() {
closeWriter()
delegate.close()
}
}
private inner class AnnotationCollectorClassBuilder (
internal val delegateClassBuilder: ClassBuilder ,
val writer: Writer ,
val diagnostics: DiagnosticSink
) : DelegatingClassBuilder () {
private val annotationFilterEnabled: Boolean
private val annotationFilters: List
init {
val nullableAnnotations = annotationFilterList?.map { it.compilePatternOpt() } ?: listOf()
annotationFilterEnabled = nullableAnnotations.isNotEmpty()
annotationFilters = nullableAnnotations.filterNotNull()
}
private val classVisitor: ClassVisitor by lazy {
object : ClassVisitor (Opcodes .ASM5 , super .getVisitor()) {
override fun visitAnnotation(desc: String , visible: Boolean ): AnnotationVisitor ? {
recordAnnotation(null , RecordTypes .ANNOTATED_CLASS , desc)
return super .visitAnnotation(desc, visible)
}
}
}
private lateinit var currentClassSimpleName: String
private lateinit var currentPackageName: String
override fun getVisitor() = classVisitor
override fun getDelegate() = delegateClassBuilder
override fun defineClass(
origin: PsiElement ?,
version: Int ,
access: Int ,
name: String ,
signature: String ?,
superName: String ,
interfaces: Array
) {
val currentClassSimpleName = name.substringAfterLast('/')
val currentPackageName = name.substringBeforeLast('/', "" ).replace('/', '.')
this .currentClassSimpleName = currentClassSimpleName
this .currentPackageName = currentPackageName
if (supportInheritedAnnotations) {
recordClass(currentPackageName, currentClassSimpleName)
}
super .defineClass(origin, version, access, name, signature, superName, interfaces)
}
override fun newField(
origin: JvmDeclarationOrigin ,
access: Int ,
name: String ,
desc: String ,
signature: String ?,
value: Any ?
): FieldVisitor {
return object : FieldVisitor (Opcodes .ASM5 , super .newField(origin, access, name, desc, signature, value)) {
override fun visitAnnotation(desc: String , visible: Boolean ): AnnotationVisitor ? {
recordAnnotation(name, RecordTypes .ANNOTATED_FIELD , desc)
return super .visitAnnotation(desc, visible)
}
}
}
override fun newMethod(
origin: JvmDeclarationOrigin ,
access: Int ,
name: String ,
desc: String ,
signature: String ?,
exceptions: Array ?
): MethodVisitor {
return object : MethodVisitor (Opcodes .ASM5 , super .newMethod(origin, access, name, desc, signature, exceptions)) {
override fun visitAnnotation(desc: String , visible: Boolean ): AnnotationVisitor ? {
recordAnnotation(name, RecordTypes .ANNOTATED_METHOD , desc)
return super .visitAnnotation(desc, visible)
}
}
}
private fun isAnnotationHandled(annotationFqName: String ): Boolean {
return if (annotationFilterEnabled)
annotationFilters.any { it.matcher(annotationFqName).matches() }
else annotationFqName != JvmAnnotationNames .METADATA_FQ_NAME .asString()
}
private fun recordClass(packageName: String , className: String ) {
val packageNameId = if (!packageName.isEmpty())
shortenedPackageNameCache.save(packageName, writer)
else null
val outputClassName = getOutputClassName(packageNameId, className)
writer.write("${RecordTypes.CLASS_DECLARATION} $outputClassName\n" )
}
private fun recordAnnotation(name: String ?, type : String , annotationDesc: String ) {
val annotationFqName = Type .getType(annotationDesc).className
if (!isAnnotationHandled(annotationFqName)) return
try {
val annotationId = shortenedAnnotationCache.save(annotationFqName, writer)
val packageName = this .currentPackageName
val packageNameId = if (!packageName.isEmpty())
shortenedPackageNameCache.save(packageName, writer)
else null
val className = this .currentClassSimpleName
val outputClassName = getOutputClassName(packageNameId, className)
val elementName = if (name != null ) " $name" else ""
writer.write("$type $annotationId $outputClassName$elementName\n" )
}
catch (e: IOException ) {
throw e
}
}
private fun getOutputClassName(packageNameId: String ?, className: String ): String {
return if (packageNameId == null ) className else "$packageNameId/$className"
}
private fun String .compilePatternOpt(): Pattern ? {
return try {
Pattern .compile(this )
}
catch (e: PatternSyntaxException ) {
null
}
}
}
private class ShortenedNameCache (val type: String ) {
private val internalCache = hashMapOf()
private var counter: Int = 0
fun save(name: String , writer: Writer ): String {
return internalCache.getOrPut(name) {
val resultId = counter.toString()
writer.write("$type $name $resultId\n" )
counter += 1
resultId
}
}
}
}
class AnnotationCollectorExtension (
override val annotationFilterList: List ? = null,
val outputFilename: String ? = null,
supportInheritedAnnotations: Boolean
) : AnnotationCollectorExtensionBase (supportInheritedAnnotations) {
private var writerInternal: Writer ? = null
override fun closeWriter() {
writerInternal?.close()
}
override fun getWriter(diagnostic: DiagnosticSink ): Writer {
return writerInternal ?: try {
with (File (outputFilename)) {
val parent = parentFile
if (!parent.exists()) parent.mkdirs()
writerInternal = bufferedWriter()
writerInternal!!
}
}
catch (e: IOException ) {
throw e
}
}
}