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) + } + } +} +