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="serialization" />
|
||||||
<element id="module-output" name="idea-completion" />
|
<element id="module-output" name="idea-completion" />
|
||||||
<element id="module-output" name="idea-core" />
|
<element id="module-output" name="idea-core" />
|
||||||
|
<element id="module-output" name="annotation-collector" />
|
||||||
</element>
|
</element>
|
||||||
<element id="library" level="project" name="javax.inject" />
|
<element id="library" level="project" name="javax.inject" />
|
||||||
<element id="directory" name="jps">
|
<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$/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$/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$/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$/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/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" />
|
<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/frontend.java/src"/>
|
||||||
<include name="compiler/light-classes/src"/>
|
<include name="compiler/light-classes/src"/>
|
||||||
<include name="compiler/plugin-api/src"/>
|
<include name="compiler/plugin-api/src"/>
|
||||||
|
<include name="plugins/annotation-collector/src"/>
|
||||||
<include name="compiler/serialization/src"/>
|
<include name="compiler/serialization/src"/>
|
||||||
<include name="compiler/util/src"/>
|
<include name="compiler/util/src"/>
|
||||||
<include name="js/js.dart-ast/src"/>
|
<include name="js/js.dart-ast/src"/>
|
||||||
@@ -116,6 +117,7 @@
|
|||||||
<include name="util.runtime/**"/>
|
<include name="util.runtime/**"/>
|
||||||
<include name="light-classes/**"/>
|
<include name="light-classes/**"/>
|
||||||
<include name="plugin-api/**"/>
|
<include name="plugin-api/**"/>
|
||||||
|
<include name="annotation-collector/**"/>
|
||||||
<include name="builtins-serializer/**"/>
|
<include name="builtins-serializer/**"/>
|
||||||
<include name="js.dart-ast/**"/>
|
<include name="js.dart-ast/**"/>
|
||||||
<include name="js.translator/**"/>
|
<include name="js.translator/**"/>
|
||||||
@@ -200,6 +202,7 @@
|
|||||||
<fileset dir="compiler/frontend.java/src"/>
|
<fileset dir="compiler/frontend.java/src"/>
|
||||||
<fileset dir="compiler/light-classes/src"/>
|
<fileset dir="compiler/light-classes/src"/>
|
||||||
<fileset dir="compiler/plugin-api/src"/>
|
<fileset dir="compiler/plugin-api/src"/>
|
||||||
|
<fileset dir="plugins/annotation-collector/src"/>
|
||||||
<fileset dir="compiler/serialization/src"/>
|
<fileset dir="compiler/serialization/src"/>
|
||||||
<fileset dir="compiler/util/src"/>
|
<fileset dir="compiler/util/src"/>
|
||||||
<fileset dir="js/js.dart-ast/src"/>
|
<fileset dir="js/js.dart-ast/src"/>
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ public class ClassBuilderFactories {
|
|||||||
public byte[] asBytes(ClassBuilder builder) {
|
public byte[] asBytes(ClassBuilder builder) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -79,6 +84,11 @@ public class ClassBuilderFactories {
|
|||||||
public byte[] asBytes(ClassBuilder builder) {
|
public byte[] asBytes(ClassBuilder builder) {
|
||||||
return ((TraceBuilder) builder).binary.toByteArray();
|
return ((TraceBuilder) builder).binary.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -105,6 +115,11 @@ public class ClassBuilderFactories {
|
|||||||
ClassWriter visitor = (ClassWriter) builder.getVisitor();
|
ClassWriter visitor = (ClassWriter) builder.getVisitor();
|
||||||
return visitor.toByteArray();
|
return visitor.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private ClassBuilderFactories() {
|
private ClassBuilderFactories() {
|
||||||
|
|||||||
@@ -29,4 +29,6 @@ public interface ClassBuilderFactory {
|
|||||||
String asText(ClassBuilder builder);
|
String asText(ClassBuilder builder);
|
||||||
|
|
||||||
byte[] asBytes(ClassBuilder builder);
|
byte[] asBytes(ClassBuilder builder);
|
||||||
|
|
||||||
|
void close();
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -47,6 +47,10 @@ public abstract class SignatureCollectingClassBuilderFactory(
|
|||||||
return delegate.asText((builder as SignatureCollectingClassBuilder)._delegate)
|
return delegate.asText((builder as SignatureCollectingClassBuilder)._delegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override fun close() {
|
||||||
|
delegate.close()
|
||||||
|
}
|
||||||
|
|
||||||
private inner class SignatureCollectingClassBuilder(
|
private inner class SignatureCollectingClassBuilder(
|
||||||
private val classCreatedFor: JvmDeclarationOrigin,
|
private val classCreatedFor: JvmDeclarationOrigin,
|
||||||
internal val _delegate: ClassBuilder
|
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) {
|
public byte[] asBytes(ClassBuilder builder) {
|
||||||
return delegate.asBytes(((OptimizationClassBuilder) builder).getDelegate());
|
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.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.codegen.*;
|
import org.jetbrains.kotlin.codegen.*;
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
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.intrinsics.IntrinsicMethods;
|
||||||
import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory;
|
import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory;
|
||||||
import org.jetbrains.kotlin.codegen.when.MappingsClassesForWhenByEnum;
|
import org.jetbrains.kotlin.codegen.when.MappingsClassesForWhenByEnum;
|
||||||
@@ -138,6 +139,9 @@ public class GenerationState {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private final File outDirectory; // TODO: temporary hack, see JetTypeMapperWithOutDirectory state for details
|
private final File outDirectory; // TODO: temporary hack, see JetTypeMapperWithOutDirectory state for details
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final ClassBuilderFactory interceptedBuilderFactory;
|
||||||
|
|
||||||
public GenerationState(
|
public GenerationState(
|
||||||
@NotNull Project project,
|
@NotNull Project project,
|
||||||
@NotNull ClassBuilderFactory builderFactory,
|
@NotNull ClassBuilderFactory builderFactory,
|
||||||
@@ -187,9 +191,20 @@ public class GenerationState {
|
|||||||
builderFactory = new OptimizationClassBuilderFactory(builderFactory);
|
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.diagnostics = diagnostics;
|
||||||
this.classFileFactory = new ClassFileFactory(this, new BuilderFactoryForDuplicateSignatureDiagnostics(
|
this.classFileFactory = new ClassFileFactory(this, interceptedBuilderFactory);
|
||||||
builderFactory, this.bindingContext, diagnostics));
|
|
||||||
|
|
||||||
this.disableCallAssertions = disableCallAssertions;
|
this.disableCallAssertions = disableCallAssertions;
|
||||||
this.disableParamAssertions = disableParamAssertions;
|
this.disableParamAssertions = disableParamAssertions;
|
||||||
@@ -306,6 +321,7 @@ public class GenerationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
|
interceptedBuilderFactory.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
@@ -19,5 +19,6 @@
|
|||||||
<orderEntry type="module" module-name="serialization" />
|
<orderEntry type="module" module-name="serialization" />
|
||||||
<orderEntry type="module" module-name="js.serializer" />
|
<orderEntry type="module" module-name="js.serializer" />
|
||||||
<orderEntry type="module" module-name="util" />
|
<orderEntry type="module" module-name="util" />
|
||||||
|
<orderEntry type="module" module-name="annotation-collector" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
@@ -21,6 +21,8 @@ import java.util.jar.JarFile
|
|||||||
import java.util.jar.Attributes
|
import java.util.jar.Attributes
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import com.intellij.util.containers.MultiMap
|
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.URLClassLoader
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -45,10 +47,9 @@ public object PluginCliParser {
|
|||||||
javaClass.getClassLoader()
|
javaClass.getClassLoader()
|
||||||
)
|
)
|
||||||
|
|
||||||
configuration.addAll(
|
val componentRegistrars = ServiceLoader.load(javaClass<ComponentRegistrar>(), classLoader).toArrayList()
|
||||||
ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS,
|
componentRegistrars.add(AnnotationCollectorComponentRegistrar())
|
||||||
ServiceLoader.load(javaClass<ComponentRegistrar>(), classLoader).toList()
|
configuration.addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, componentRegistrars)
|
||||||
)
|
|
||||||
|
|
||||||
processPluginOptions(arguments, configuration, classLoader)
|
processPluginOptions(arguments, configuration, classLoader)
|
||||||
}
|
}
|
||||||
@@ -63,7 +64,8 @@ public object PluginCliParser {
|
|||||||
it.pluginId
|
it.pluginId
|
||||||
} ?: mapOf()
|
} ?: mapOf()
|
||||||
|
|
||||||
val commandLineProcessors = ServiceLoader.load(javaClass<CommandLineProcessor>(), classLoader).toList()
|
val commandLineProcessors = ServiceLoader.load(javaClass<CommandLineProcessor>(), classLoader).toArrayList()
|
||||||
|
commandLineProcessors.add(AnnotationCollectorCommandLineProcessor())
|
||||||
|
|
||||||
for (processor in commandLineProcessors) {
|
for (processor in commandLineProcessors) {
|
||||||
val declaredOptions = processor.pluginOptions.valuesToMap { it.name }
|
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.JavaSourceRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmContentRoot
|
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.codegen.extensions.ExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||||
@@ -139,6 +140,7 @@ public class KotlinCoreEnvironment private(
|
|||||||
|
|
||||||
ExternalDeclarationsProvider.registerExtensionPoint(project)
|
ExternalDeclarationsProvider.registerExtensionPoint(project)
|
||||||
ExpressionCodegenExtension.registerExtensionPoint(project)
|
ExpressionCodegenExtension.registerExtensionPoint(project)
|
||||||
|
ClassBuilderInterceptorExtension.registerExtensionPoint(project)
|
||||||
|
|
||||||
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
|
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
|
||||||
registrar.registerProjectComponents(project, configuration)
|
registrar.registerProjectComponents(project, configuration)
|
||||||
|
|||||||
+5
@@ -52,4 +52,9 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
|||||||
public byte[] asBytes(ClassBuilder builder) {
|
public byte[] asBytes(ClassBuilder builder) {
|
||||||
throw new UnsupportedOperationException("asBytes is not implemented"); // TODO
|
throw new UnsupportedOperationException("asBytes is not implemented"); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,5 +20,8 @@
|
|||||||
<extensionPoint name="simpleNameReferenceExtension"
|
<extensionPoint name="simpleNameReferenceExtension"
|
||||||
interface="org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension"
|
interface="org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension"
|
||||||
area="IDEA_PROJECT"/>
|
area="IDEA_PROJECT"/>
|
||||||
|
<extensionPoint name="classBuilderInterceptorExtension"
|
||||||
|
interface="org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension"
|
||||||
|
area="IDEA_PROJECT"/>
|
||||||
</extensionPoints>
|
</extensionPoints>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
|||||||
@@ -5,5 +5,7 @@
|
|||||||
<defaultErrorMessages implementation="org.jetbrains.kotlin.resolve.jvm.diagnostics.DefaultErrorMessagesJvm"/>
|
<defaultErrorMessages implementation="org.jetbrains.kotlin.resolve.jvm.diagnostics.DefaultErrorMessagesJvm"/>
|
||||||
|
|
||||||
<suppressStringProvider implementation="org.jetbrains.kotlin.load.kotlin.nativeDeclarations.SuppressNoBodyErrorsForNativeDeclarations"/>
|
<suppressStringProvider implementation="org.jetbrains.kotlin.load.kotlin.nativeDeclarations.SuppressNoBodyErrorsForNativeDeclarations"/>
|
||||||
|
|
||||||
|
<classBuilderInterceptorExtension implementation="org.jetbrains.kotlin.annotation.AnnotationCollectorExtension"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</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