diff --git a/.idea/artifacts/KotlinPlugin.xml b/.idea/artifacts/KotlinPlugin.xml
index 7ca9d29691a..7b638fed790 100644
--- a/.idea/artifacts/KotlinPlugin.xml
+++ b/.idea/artifacts/KotlinPlugin.xml
@@ -42,6 +42,7 @@
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
index fcc620ddae4..3a722f7d750 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -8,6 +8,7 @@
+
diff --git a/build.xml b/build.xml
index eefafafe456..b6692ab9baa 100644
--- a/build.xml
+++ b/build.xml
@@ -90,6 +90,7 @@
+
@@ -116,6 +117,7 @@
+
@@ -200,6 +202,7 @@
+
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderFactories.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderFactories.java
index b9dc1f6d137..aa6f1fad6ba 100644
--- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderFactories.java
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderFactories.java
@@ -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() {
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderFactory.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderFactory.java
index 99902c82b16..3e16f14c4ff 100644
--- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderFactory.java
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassBuilderFactory.java
@@ -29,4 +29,6 @@ public interface ClassBuilderFactory {
String asText(ClassBuilder builder);
byte[] asBytes(ClassBuilder builder);
+
+ void close();
}
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt
index 5309406b7e7..b7264bdd071 100644
--- a/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt
@@ -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
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ClassBuilderInterceptorExtension.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ClassBuilderInterceptorExtension.kt
new file mode 100644
index 00000000000..0ff930d3178
--- /dev/null
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/extensions/ClassBuilderInterceptorExtension.kt
@@ -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(
+ "org.jetbrains.kotlin.classBuilderFactoryInterceptorExtension", javaClass())
+
+ public fun interceptClassBuilderFactory(
+ interceptedFactory: ClassBuilderFactory,
+ bindingContext: BindingContext,
+ diagnostics: DiagnosticSink
+ ): ClassBuilderFactory
+
+}
\ No newline at end of file
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationClassBuilderFactory.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationClassBuilderFactory.java
index fe1cb1219d8..05072f92b9b 100644
--- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationClassBuilderFactory.java
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationClassBuilderFactory.java
@@ -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();
+ }
}
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.java
index 9bd224f3f91..75e5594fa32 100644
--- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.java
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.java
@@ -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 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
diff --git a/compiler/cli/cli.iml b/compiler/cli/cli.iml
index 6df025f6ea5..f52d3e1cf9a 100644
--- a/compiler/cli/cli.iml
+++ b/compiler/cli/cli.iml
@@ -19,5 +19,6 @@
+
\ No newline at end of file
diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/PluginCliParser.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/PluginCliParser.kt
index fe71d1d26ab..31d5bf77fba 100644
--- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/PluginCliParser.kt
+++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/PluginCliParser.kt
@@ -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(), classLoader).toList()
- )
+ val componentRegistrars = ServiceLoader.load(javaClass(), 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(), classLoader).toList()
+ val commandLineProcessors = ServiceLoader.load(javaClass(), classLoader).toArrayList()
+ commandLineProcessors.add(AnnotationCollectorCommandLineProcessor())
for (processor in commandLineProcessors) {
val declaredOptions = processor.pluginOptions.valuesToMap { it.name }
diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt
index eabaa77f1af..a51fecde864 100644
--- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt
+++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt
@@ -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)
diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassBuilderFactory.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassBuilderFactory.java
index acf345e112a..040fafbf598 100644
--- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassBuilderFactory.java
+++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassBuilderFactory.java
@@ -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() {
+
+ }
}
diff --git a/idea/src/META-INF/extensions/common.xml b/idea/src/META-INF/extensions/common.xml
index b9046b26f3f..13707352502 100644
--- a/idea/src/META-INF/extensions/common.xml
+++ b/idea/src/META-INF/extensions/common.xml
@@ -20,5 +20,8 @@
+
diff --git a/idea/src/META-INF/extensions/kotlin2jvm.xml b/idea/src/META-INF/extensions/kotlin2jvm.xml
index 075d8e5faf0..b99dcd7e61a 100644
--- a/idea/src/META-INF/extensions/kotlin2jvm.xml
+++ b/idea/src/META-INF/extensions/kotlin2jvm.xml
@@ -5,5 +5,7 @@
+
+
diff --git a/plugins/annotation-collector/annotation-collector.iml b/plugins/annotation-collector/annotation-collector.iml
new file mode 100644
index 00000000000..8838ea8bb49
--- /dev/null
+++ b/plugins/annotation-collector/annotation-collector.iml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/annotation-collector/src/org/jetbrains/kotlin/annotation/AnnotationCollectorExtension.kt b/plugins/annotation-collector/src/org/jetbrains/kotlin/annotation/AnnotationCollectorExtension.kt
new file mode 100644
index 00000000000..997f8f47b28
--- /dev/null
+++ b/plugins/annotation-collector/src/org/jetbrains/kotlin/annotation/AnnotationCollectorExtension.kt
@@ -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?
+
+ private val classBuilderFactories = arrayListOf()
+
+ 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
+
+ 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
+ ) {
+ 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?
+ ): 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()
+ 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? = 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
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/annotation-collector/src/org/jetbrains/kotlin/annotation/AnnotationCollectorPlugin.kt b/plugins/annotation-collector/src/org/jetbrains/kotlin/annotation/AnnotationCollectorPlugin.kt
new file mode 100644
index 00000000000..482dc6bd936
--- /dev/null
+++ b/plugins/annotation-collector/src/org/jetbrains/kotlin/annotation/AnnotationCollectorPlugin.kt
@@ -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> =
+ CompilerConfigurationKey.create>("annotation filter regular expressions")
+ public val OUTPUT_FILENAME: CompilerConfigurationKey =
+ CompilerConfigurationKey.create("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", "", "Annotation filter regular expressions, separated by commas", required = false)
+
+ public val OUTPUT_FILENAME_OPTION: CliOption =
+ CliOption("output", "", "File in which annotated declarations will be placed", required = false)
+ }
+
+ override val pluginId: String = ANNOTATION_COLLECTOR_COMPILER_PLUGIN_ID
+
+ override val pluginOptions: Collection = 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)
+ }
+ }
+}
+