Annotation collector compiler plugin
This commit is contained in:
Generated
+1
@@ -42,6 +42,7 @@
|
||||
<element id="module-output" name="serialization" />
|
||||
<element id="module-output" name="idea-completion" />
|
||||
<element id="module-output" name="idea-core" />
|
||||
<element id="module-output" name="annotation-collector" />
|
||||
</element>
|
||||
<element id="library" level="project" name="javax.inject" />
|
||||
<element id="directory" name="jps">
|
||||
|
||||
Generated
+1
@@ -8,6 +8,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/android-jps-plugin/android-jps-plugin.iml" filepath="$PROJECT_DIR$/plugins/android-jps-plugin/android-jps-plugin.iml" group="plugins" />
|
||||
<module fileurl="file://$PROJECT_DIR$/android-studio/android-studio.iml" filepath="$PROJECT_DIR$/android-studio/android-studio.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/android-tests/android-tests.iml" filepath="$PROJECT_DIR$/compiler/android-tests/android-tests.iml" group="compiler" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/annotation-collector/annotation-collector.iml" filepath="$PROJECT_DIR$/plugins/annotation-collector/annotation-collector.iml" group="plugins" />
|
||||
<module fileurl="file://$PROJECT_DIR$/ant/ant.iml" filepath="$PROJECT_DIR$/ant/ant.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/backend/backend.iml" filepath="$PROJECT_DIR$/compiler/backend/backend.iml" group="compiler/java" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/backend-common/backend-common.iml" filepath="$PROJECT_DIR$/compiler/backend-common/backend-common.iml" group="compiler" />
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
<include name="compiler/frontend.java/src"/>
|
||||
<include name="compiler/light-classes/src"/>
|
||||
<include name="compiler/plugin-api/src"/>
|
||||
<include name="plugins/annotation-collector/src"/>
|
||||
<include name="compiler/serialization/src"/>
|
||||
<include name="compiler/util/src"/>
|
||||
<include name="js/js.dart-ast/src"/>
|
||||
@@ -116,6 +117,7 @@
|
||||
<include name="util.runtime/**"/>
|
||||
<include name="light-classes/**"/>
|
||||
<include name="plugin-api/**"/>
|
||||
<include name="annotation-collector/**"/>
|
||||
<include name="builtins-serializer/**"/>
|
||||
<include name="js.dart-ast/**"/>
|
||||
<include name="js.translator/**"/>
|
||||
@@ -200,6 +202,7 @@
|
||||
<fileset dir="compiler/frontend.java/src"/>
|
||||
<fileset dir="compiler/light-classes/src"/>
|
||||
<fileset dir="compiler/plugin-api/src"/>
|
||||
<fileset dir="plugins/annotation-collector/src"/>
|
||||
<fileset dir="compiler/serialization/src"/>
|
||||
<fileset dir="compiler/util/src"/>
|
||||
<fileset dir="js/js.dart-ast/src"/>
|
||||
|
||||
@@ -49,6 +49,11 @@ public class ClassBuilderFactories {
|
||||
public byte[] asBytes(ClassBuilder builder) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
@@ -79,6 +84,11 @@ public class ClassBuilderFactories {
|
||||
public byte[] asBytes(ClassBuilder builder) {
|
||||
return ((TraceBuilder) builder).binary.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
@@ -105,6 +115,11 @@ public class ClassBuilderFactories {
|
||||
ClassWriter visitor = (ClassWriter) builder.getVisitor();
|
||||
return visitor.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
private ClassBuilderFactories() {
|
||||
|
||||
@@ -29,4 +29,6 @@ public interface ClassBuilderFactory {
|
||||
String asText(ClassBuilder builder);
|
||||
|
||||
byte[] asBytes(ClassBuilder builder);
|
||||
|
||||
void close();
|
||||
}
|
||||
|
||||
+4
@@ -47,6 +47,10 @@ public abstract class SignatureCollectingClassBuilderFactory(
|
||||
return delegate.asText((builder as SignatureCollectingClassBuilder)._delegate)
|
||||
}
|
||||
|
||||
public override fun close() {
|
||||
delegate.close()
|
||||
}
|
||||
|
||||
private inner class SignatureCollectingClassBuilder(
|
||||
private val classCreatedFor: JvmDeclarationOrigin,
|
||||
internal val _delegate: ClassBuilder
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.extensions
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
|
||||
public trait ClassBuilderInterceptorExtension {
|
||||
companion object : ProjectExtensionDescriptor<ClassBuilderInterceptorExtension>(
|
||||
"org.jetbrains.kotlin.classBuilderFactoryInterceptorExtension", javaClass<ClassBuilderInterceptorExtension>())
|
||||
|
||||
public fun interceptClassBuilderFactory(
|
||||
interceptedFactory: ClassBuilderFactory,
|
||||
bindingContext: BindingContext,
|
||||
diagnostics: DiagnosticSink
|
||||
): ClassBuilderFactory
|
||||
|
||||
}
|
||||
+5
@@ -50,4 +50,9 @@ public class OptimizationClassBuilderFactory implements ClassBuilderFactory {
|
||||
public byte[] asBytes(ClassBuilder builder) {
|
||||
return delegate.asBytes(((OptimizationClassBuilder) builder).getDelegate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
delegate.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.*;
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
|
||||
import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory;
|
||||
import org.jetbrains.kotlin.codegen.when.MappingsClassesForWhenByEnum;
|
||||
@@ -138,6 +139,9 @@ public class GenerationState {
|
||||
@Nullable
|
||||
private final File outDirectory; // TODO: temporary hack, see JetTypeMapperWithOutDirectory state for details
|
||||
|
||||
@NotNull
|
||||
private final ClassBuilderFactory interceptedBuilderFactory;
|
||||
|
||||
public GenerationState(
|
||||
@NotNull Project project,
|
||||
@NotNull ClassBuilderFactory builderFactory,
|
||||
@@ -187,9 +191,20 @@ public class GenerationState {
|
||||
builderFactory = new OptimizationClassBuilderFactory(builderFactory);
|
||||
}
|
||||
|
||||
ClassBuilderFactory interceptedBuilderFactory = new BuilderFactoryForDuplicateSignatureDiagnostics(
|
||||
builderFactory, this.bindingContext, diagnostics);
|
||||
|
||||
Collection<ClassBuilderInterceptorExtension> interceptExtensions =
|
||||
ClassBuilderInterceptorExtension.Companion.getInstances(project);
|
||||
|
||||
for (ClassBuilderInterceptorExtension extension : interceptExtensions) {
|
||||
interceptedBuilderFactory = extension.interceptClassBuilderFactory(interceptedBuilderFactory, bindingContext, diagnostics);
|
||||
}
|
||||
|
||||
this.interceptedBuilderFactory = interceptedBuilderFactory;
|
||||
|
||||
this.diagnostics = diagnostics;
|
||||
this.classFileFactory = new ClassFileFactory(this, new BuilderFactoryForDuplicateSignatureDiagnostics(
|
||||
builderFactory, this.bindingContext, diagnostics));
|
||||
this.classFileFactory = new ClassFileFactory(this, interceptedBuilderFactory);
|
||||
|
||||
this.disableCallAssertions = disableCallAssertions;
|
||||
this.disableParamAssertions = disableParamAssertions;
|
||||
@@ -306,6 +321,7 @@ public class GenerationState {
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
interceptedBuilderFactory.close();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -19,5 +19,6 @@
|
||||
<orderEntry type="module" module-name="serialization" />
|
||||
<orderEntry type="module" module-name="js.serializer" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="module" module-name="annotation-collector" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -21,6 +21,8 @@ import java.util.jar.JarFile
|
||||
import java.util.jar.Attributes
|
||||
import java.util.regex.Pattern
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.annotation.AnnotationCollectorCommandLineProcessor
|
||||
import org.jetbrains.kotlin.annotation.AnnotationCollectorComponentRegistrar
|
||||
import java.net.URLClassLoader
|
||||
import java.net.URL
|
||||
import java.io.File
|
||||
@@ -45,10 +47,9 @@ public object PluginCliParser {
|
||||
javaClass.getClassLoader()
|
||||
)
|
||||
|
||||
configuration.addAll(
|
||||
ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS,
|
||||
ServiceLoader.load(javaClass<ComponentRegistrar>(), classLoader).toList()
|
||||
)
|
||||
val componentRegistrars = ServiceLoader.load(javaClass<ComponentRegistrar>(), classLoader).toArrayList()
|
||||
componentRegistrars.add(AnnotationCollectorComponentRegistrar())
|
||||
configuration.addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, componentRegistrars)
|
||||
|
||||
processPluginOptions(arguments, configuration, classLoader)
|
||||
}
|
||||
@@ -63,7 +64,8 @@ public object PluginCliParser {
|
||||
it.pluginId
|
||||
} ?: mapOf()
|
||||
|
||||
val commandLineProcessors = ServiceLoader.load(javaClass<CommandLineProcessor>(), classLoader).toList()
|
||||
val commandLineProcessors = ServiceLoader.load(javaClass<CommandLineProcessor>(), classLoader).toArrayList()
|
||||
commandLineProcessors.add(AnnotationCollectorCommandLineProcessor())
|
||||
|
||||
for (processor in commandLineProcessors) {
|
||||
val declaredOptions = processor.pluginOptions.valuesToMap { it.name }
|
||||
|
||||
@@ -63,6 +63,7 @@ import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
@@ -139,6 +140,7 @@ public class KotlinCoreEnvironment private(
|
||||
|
||||
ExternalDeclarationsProvider.registerExtensionPoint(project)
|
||||
ExpressionCodegenExtension.registerExtensionPoint(project)
|
||||
ClassBuilderInterceptorExtension.registerExtensionPoint(project)
|
||||
|
||||
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
|
||||
registrar.registerProjectComponents(project, configuration)
|
||||
|
||||
+5
@@ -52,4 +52,9 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||
public byte[] asBytes(ClassBuilder builder) {
|
||||
throw new UnsupportedOperationException("asBytes is not implemented"); // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,5 +20,8 @@
|
||||
<extensionPoint name="simpleNameReferenceExtension"
|
||||
interface="org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension"
|
||||
area="IDEA_PROJECT"/>
|
||||
<extensionPoint name="classBuilderInterceptorExtension"
|
||||
interface="org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension"
|
||||
area="IDEA_PROJECT"/>
|
||||
</extensionPoints>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -5,5 +5,7 @@
|
||||
<defaultErrorMessages implementation="org.jetbrains.kotlin.resolve.jvm.diagnostics.DefaultErrorMessagesJvm"/>
|
||||
|
||||
<suppressStringProvider implementation="org.jetbrains.kotlin.load.kotlin.nativeDeclarations.SuppressNoBodyErrorsForNativeDeclarations"/>
|
||||
|
||||
<classBuilderInterceptorExtension implementation="org.jetbrains.kotlin.annotation.AnnotationCollectorExtension"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="plugin-api" />
|
||||
<orderEntry type="module" module-name="backend" />
|
||||
<orderEntry type="module" module-name="frontend" />
|
||||
<orderEntry type="library" name="kotlin-runtime" level="project" />
|
||||
<orderEntry type="module" module-name="frontend.java" />
|
||||
</component>
|
||||
</module>
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
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.StringWriter
|
||||
import java.io.Writer
|
||||
import java.util.regex.Pattern
|
||||
import java.util.regex.PatternSyntaxException
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
public abstract class AnnotationCollectorExtensionBase() : 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"
|
||||
}
|
||||
|
||||
protected abstract val annotationFilterList: List<String>?
|
||||
|
||||
private val classBuilderFactories = arrayListOf<AnnotationCollectorClassBuilderFactory>()
|
||||
|
||||
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 {
|
||||
val factory = AnnotationCollectorClassBuilderFactory(interceptedFactory, getWriter(diagnostics), diagnostics)
|
||||
classBuilderFactories.add(factory)
|
||||
return factory
|
||||
}
|
||||
|
||||
protected abstract fun getWriter(diagnostic: DiagnosticSink): Writer
|
||||
|
||||
private inner class AnnotationCollectorClassBuilderFactory(
|
||||
private val delegateFactory: ClassBuilderFactory,
|
||||
val writer: Writer,
|
||||
val diagnostics: DiagnosticSink
|
||||
) : ClassBuilderFactory {
|
||||
|
||||
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder {
|
||||
return AnnotationCollectorClassBuilder(delegateFactory.newClassBuilder(origin), writer, diagnostics)
|
||||
}
|
||||
|
||||
override fun getClassBuilderMode() = delegateFactory.getClassBuilderMode()
|
||||
|
||||
override fun asText(builder: ClassBuilder?): String? {
|
||||
return delegateFactory.asText((builder as AnnotationCollectorClassBuilder).delegateClassBuilder)
|
||||
}
|
||||
|
||||
override fun asBytes(builder: ClassBuilder?): ByteArray? {
|
||||
return delegateFactory.asBytes((builder as AnnotationCollectorClassBuilder).delegateClassBuilder)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
for (factory in classBuilderFactories) {
|
||||
factory.writer.close()
|
||||
}
|
||||
classBuilderFactories.clear()
|
||||
|
||||
delegateFactory.close()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class AnnotationCollectorClassBuilder(
|
||||
internal val delegateClassBuilder: ClassBuilder,
|
||||
val writer: Writer,
|
||||
val diagnostics: DiagnosticSink
|
||||
) : DelegatingClassBuilder() {
|
||||
private val annotationFilterEnabled: Boolean
|
||||
private val annotationFilters: List<Pattern>
|
||||
|
||||
init {
|
||||
val nullableAnnotations = annotationFilterList?.map { it.compilePatternOpt() } ?: listOf()
|
||||
annotationFilterEnabled = nullableAnnotations.isNotEmpty()
|
||||
annotationFilters = nullableAnnotations.filterNotNull()
|
||||
}
|
||||
|
||||
private val classVisitor: ClassVisitor by Delegates.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 var currentClassSimpleName: String? = null
|
||||
|
||||
private var currentPackageName: String? = null
|
||||
|
||||
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<out String>
|
||||
) {
|
||||
currentClassSimpleName = name.substringAfterLast('/')
|
||||
currentPackageName = name.substringBeforeLast('/', "").replace('/', '.')
|
||||
|
||||
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<out String>?
|
||||
): 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.startsWith("kotlin.jvm.internal.") //apply to all
|
||||
}
|
||||
|
||||
private fun recordAnnotation(name: String?, type: String, annotationDesc: String) {
|
||||
val annotationFqName = Type.getType(annotationDesc).getClassName()
|
||||
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 = if (packageNameId == null) className else "$packageNameId/$className"
|
||||
val elementName = if (name != null) " $name" else ""
|
||||
|
||||
writer.write("$type $annotationId $outputClassName$elementName\n")
|
||||
}
|
||||
catch (e: IOException) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.compilePatternOpt(): Pattern? {
|
||||
return try {
|
||||
Pattern.compile(this)
|
||||
}
|
||||
catch (e: PatternSyntaxException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ShortenedNameCache(val type: String) {
|
||||
private val internalCache = hashMapOf<String, String>()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AnnotationCollectorExtension(
|
||||
override val annotationFilterList: List<String>? = null,
|
||||
val outputFilename: String? = null
|
||||
) : AnnotationCollectorExtensionBase() {
|
||||
|
||||
override fun getWriter(diagnostic: DiagnosticSink): Writer {
|
||||
try {
|
||||
return with (File(outputFilename)) {
|
||||
val parent = getParentFile()
|
||||
if (!parent.exists()) parent.mkdirs()
|
||||
bufferedWriter()
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.annotation
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.CliOption
|
||||
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
|
||||
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.MemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters1
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.JetCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.JetDeclaration
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.Writer
|
||||
import java.util.regex.Pattern
|
||||
import java.util.regex.PatternSyntaxException
|
||||
|
||||
public object AnnotationCollectorConfigurationKeys {
|
||||
public val ANNOTATION_FILTER_LIST: CompilerConfigurationKey<List<String>> =
|
||||
CompilerConfigurationKey.create<List<String>>("annotation filter regular expressions")
|
||||
public val OUTPUT_FILENAME: CompilerConfigurationKey<String> =
|
||||
CompilerConfigurationKey.create<String>("output file")
|
||||
}
|
||||
|
||||
public class AnnotationCollectorCommandLineProcessor : CommandLineProcessor {
|
||||
companion object {
|
||||
public val ANNOTATION_COLLECTOR_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.kapt"
|
||||
|
||||
public val ANNOTATION_FILTER_LIST_OPTION: CliOption =
|
||||
CliOption("annotations", "<path>", "Annotation filter regular expressions, separated by commas", required = false)
|
||||
|
||||
public val OUTPUT_FILENAME_OPTION: CliOption =
|
||||
CliOption("output", "<path>", "File in which annotated declarations will be placed", required = false)
|
||||
}
|
||||
|
||||
override val pluginId: String = ANNOTATION_COLLECTOR_COMPILER_PLUGIN_ID
|
||||
|
||||
override val pluginOptions: Collection<CliOption> = listOf(ANNOTATION_FILTER_LIST_OPTION, OUTPUT_FILENAME_OPTION)
|
||||
|
||||
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
|
||||
when (option) {
|
||||
ANNOTATION_FILTER_LIST_OPTION -> {
|
||||
val annotations = value.split(',').filter { !it.isEmpty() }.toList()
|
||||
configuration.put(AnnotationCollectorConfigurationKeys.ANNOTATION_FILTER_LIST, annotations)
|
||||
}
|
||||
OUTPUT_FILENAME_OPTION -> configuration.put(AnnotationCollectorConfigurationKeys.OUTPUT_FILENAME, value)
|
||||
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AnnotationCollectorComponentRegistrar : ComponentRegistrar {
|
||||
public override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||
val annotationFilterList = configuration.get(AnnotationCollectorConfigurationKeys.ANNOTATION_FILTER_LIST)
|
||||
val outputFilename = configuration.get(AnnotationCollectorConfigurationKeys.OUTPUT_FILENAME)
|
||||
|
||||
if (outputFilename != null) {
|
||||
val collectorExtension = AnnotationCollectorExtension(annotationFilterList, outputFilename)
|
||||
ClassBuilderInterceptorExtension.registerExtension(project, collectorExtension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user