diff --git a/.idea/modules.xml b/.idea/modules.xml index 6d8f9759829..e424833dd6f 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -10,6 +10,7 @@ + @@ -52,6 +53,7 @@ + diff --git a/plugins/annotation-processing/annotation-processing.iml b/plugins/annotation-processing/annotation-processing.iml new file mode 100644 index 00000000000..81a78835658 --- /dev/null +++ b/plugins/annotation-processing/annotation-processing.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingConfigurationService.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingConfigurationService.kt new file mode 100644 index 00000000000..88bb8a0d63a --- /dev/null +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingConfigurationService.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2016 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.processing + +class AnnotationProcessingConfigurationService(val aptClassLoader: ClassLoader) \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt new file mode 100644 index 00000000000..b08b1c953c3 --- /dev/null +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingExtension.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2010-2016 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.openapi.project.Project +import com.intellij.psi.* +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analyzer.AnalysisResult +import org.jetbrains.kotlin.annotation.processing.impl.* +import org.jetbrains.kotlin.asJava.findFacadeClass +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.java.model.elements.JeTypeElement +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension +import java.io.File +import java.net.URLClassLoader +import java.util.* +import javax.annotation.processing.Processor + +class AnnotationProcessingExtension( + val generatedOutputDir: File, + val annotationProcessingClasspath: List +) : AnalysisCompletedHandlerExtension { + private var annotationProcessingComplete = false + + override fun analysisCompleted( + project: Project, + module: ModuleDescriptor, + bindingContext: BindingContext, + files: Collection + ): AnalysisResult? { + if (annotationProcessingComplete) { + return null + } + + val processors = loadAnnotationProcessors(annotationProcessingClasspath) + if (processors.isEmpty()) return null + + val analysisContext = AnalysisContext(hashMapOf()) + analysisContext.analyzeFiles(files) + + val options = emptyMap() + + val javaPsiFacade = JavaPsiFacade.getInstance(project) + val projectScope = GlobalSearchScope.projectScope(project) + + val filer = KotlinFiler(generatedOutputDir) + val messages = KotlinMessager() + val types = KotlinTypes(javaPsiFacade, projectScope) + val elements = KotlinElements(javaPsiFacade, projectScope) + + val processingEnvironment = KotlinProcessingEnvironment(elements, types, messages, options, filer) + processors.forEach { it.init(processingEnvironment) } + + + // Round 1 + val round1Environment = KotlinRoundEnvironment(analysisContext) + for (processor in processors) { + val supportedAnnotationNames = processor.supportedAnnotationTypes.filter { it in analysisContext.annotationsMap } + if (supportedAnnotationNames.isEmpty()) continue + + val supportedAnnotations = supportedAnnotationNames + .map { javaPsiFacade.findClass(it, projectScope)?.let { JeTypeElement(it) } } + .filterNotNullTo(hashSetOf()) + + processor.process(supportedAnnotations, round1Environment) + } + + // Round 2 + val round2Environment = KotlinRoundEnvironment(AnalysisContext(hashMapOf()), isProcessingOver = true) + for (processor in processors) { + processor.process(emptySet(), round2Environment) + } + + annotationProcessingComplete = true + return AnalysisResult.RetryWithAdditionalJavaRoots(bindingContext, module, listOf(generatedOutputDir)) + } + + private fun loadAnnotationProcessors(classpath: List): List { + val classLoader = URLClassLoader(classpath.map { File(it).toURI().toURL() }.toTypedArray()) + return ServiceLoader.load(Processor::class.java, classLoader).toList() + } +} + +internal class AnalysisContext(val annotationsMap: MutableMap>) + +private fun AnalysisContext.analyzeFiles(files: Collection) { + for (file in files) { + analyzeFile(file) + } +} + +private fun AnalysisContext.analyzeFile(file: KtFile) { + val lightClass = file.findFacadeClass() + + if (lightClass != null) { + analyzeDeclaration(lightClass) + } + + for (declaration in file.declarations) { + if (declaration !is KtClassOrObject) continue + val clazz = declaration.toLightClass() ?: continue + analyzeDeclaration(clazz) + } +} + +private fun AnalysisContext.analyzeDeclaration(declaration: PsiElement) { + if (declaration !is PsiModifierListOwner) return + + val annotations = declaration.modifierList?.annotations + if (annotations != null) { + for (annotation in annotations) { + val fqName = annotation.qualifiedName ?: continue + annotationsMap.getOrPut(fqName, { mutableListOf() }).add(declaration) + } + } + + if (declaration is PsiClass) { + declaration.methods.forEach { analyzeDeclaration(it) } + declaration.fields.forEach { analyzeDeclaration(it) } + declaration.innerClasses.forEach { analyzeDeclaration(it) } + } + + when (declaration) { + is KtClassOrObject -> declaration.declarations.forEach { analyzeDeclaration(it) } + + } +} \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt new file mode 100644 index 00000000000..1e25d5486bc --- /dev/null +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/AnnotationProcessingPlugin.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2016 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.processing + +import com.intellij.mock.MockProject +import org.jetbrains.kotlin.annotation.AnnotationProcessingExtension +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.resolve.jvm.extensions.AnalysisCompletedHandlerExtension +import java.io.File + +object AnnotationProcessingConfigurationKeys { + val GENERATED_OUTPUT_DIR: CompilerConfigurationKey = + CompilerConfigurationKey.create("generated files output directory") + + val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey> = + CompilerConfigurationKey.create>("annotation processor classpath") +} + +class AnnotationProcessingCommandLineProcessor : CommandLineProcessor { + companion object { + val ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.kapt2" + + val GENERATED_OUTPUT_DIR_OPTION: CliOption = + CliOption("generated", "", "Output path for generated files", required = false) + + val ANNOTATION_PROCESSOR_CLASSPATH_OPTION: CliOption = + CliOption("apclasspath", "", "Annotation processor classpath", + required = false, allowMultipleOccurrences = true) + } + + override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID + + override val pluginOptions: Collection = + listOf(GENERATED_OUTPUT_DIR_OPTION, ANNOTATION_PROCESSOR_CLASSPATH_OPTION) + + override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) { + when (option) { + ANNOTATION_PROCESSOR_CLASSPATH_OPTION -> { + val paths = configuration.getList(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH).toMutableList() + paths.add(value) + configuration.put(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH, paths) + } + GENERATED_OUTPUT_DIR_OPTION -> configuration.put(AnnotationProcessingConfigurationKeys.GENERATED_OUTPUT_DIR, value) + else -> throw CliOptionProcessingException("Unknown option: ${option.name}") + } + } +} + +class AnnotationProcessingComponentRegistrar : ComponentRegistrar { + override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { + val generatedOutputDir = configuration.get(AnnotationProcessingConfigurationKeys.GENERATED_OUTPUT_DIR) ?: return + val classpath = configuration.get(AnnotationProcessingConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH) ?: return + + val generatedOutputDirFile = File(generatedOutputDir) + generatedOutputDirFile.mkdirs() + + val annotationProcessingExtension = AnnotationProcessingExtension(generatedOutputDirFile, classpath) + AnalysisCompletedHandlerExtension.registerExtension(project, annotationProcessingExtension) + } +} \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinElements.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinElements.kt new file mode 100644 index 00000000000..665ec769536 --- /dev/null +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinElements.kt @@ -0,0 +1,202 @@ +/* + * Copyright 2010-2016 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.processing.impl + +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiClass +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.util.PsiSuperMethodUtil +import com.intellij.psi.util.PsiTypesUtil +import org.jetbrains.kotlin.java.model.JeAnnotationOwner +import org.jetbrains.kotlin.java.model.JeElement +import org.jetbrains.kotlin.java.model.JeName +import org.jetbrains.kotlin.java.model.elements.JeAnnotationMirror +import org.jetbrains.kotlin.java.model.elements.JeMethodExecutableElement +import org.jetbrains.kotlin.java.model.elements.JePackageElement +import org.jetbrains.kotlin.java.model.elements.JeTypeElement +import java.io.PrintWriter +import java.io.Writer +import javax.lang.model.element.* +import javax.lang.model.util.Elements + +class KotlinElements(val javaPsiFacade: JavaPsiFacade, val scope: GlobalSearchScope) : Elements { + override fun hides(hider: Element, hidden: Element): Boolean { + val hiderMethod = (hider as? JeMethodExecutableElement)?.psi ?: return false + val hiddenMethod = (hidden as? JeMethodExecutableElement)?.psi ?: return false + + if (hiderMethod.name != hiddenMethod.name) return false + if (hiderMethod.parameterList.parametersCount != hiddenMethod.parameterList.parametersCount) return false + + val hiderMethodClass = hiderMethod.containingClass ?: return false + val hiddenMethodClass = hiddenMethod.containingClass ?: return false + + if (PsiTypesUtil.getClassType(hiddenMethodClass) !in hiderMethodClass.superTypes) return false + + if (hiderMethod.returnType != hiddenMethod.returnType) return false + for (i in 0..hiderMethod.parameterList.parametersCount - 1) { + if (hiderMethod.parameterList.parameters[i].type != hiddenMethod.parameterList.parameters[i].type) return false + } + + return true + } + + override fun overrides(overrider: ExecutableElement, overridden: ExecutableElement, type: TypeElement): Boolean { + overrider as? JeMethodExecutableElement ?: return false + overridden as? JeMethodExecutableElement ?: return false + + return PsiSuperMethodUtil.isSuperMethod(overrider.psi, overridden.psi) + } + + override fun getName(cs: CharSequence?) = JeName(cs?.toString()) + + override fun getElementValuesWithDefaults(a: AnnotationMirror): Map { + a as? JeAnnotationMirror ?: return emptyMap() + return a.getAllElementValues() + } + + override fun getBinaryName(type: TypeElement) = JeName((type as JeTypeElement).psi.qualifiedName) + + override fun getDocComment(e: Element?) = "" + + override fun isDeprecated(e: Element?): Boolean { + return (e as? JeAnnotationOwner)?.annotationOwner?.findAnnotation("java.lang.Deprecated") != null + } + + override fun getAllMembers(type: TypeElement) = (type as? JeTypeElement)?.getAllMembers() ?: emptyList() + + override fun printElements(w: Writer, vararg elements: Element) { + val printWriter = PrintWriter(w) + for (element in elements) { + printWriter.println(element.simpleName.toString() + " (" + element.javaClass.name + ")") + } + } + + override fun getPackageElement(name: CharSequence): PackageElement? { + val psiPackage = javaPsiFacade.findPackage(name.toString()) ?: return null + return JePackageElement(psiPackage) + } + + override fun getTypeElement(name: CharSequence): TypeElement? { + val psiClass = javaPsiFacade.findClass(name.toString(), scope) ?: return null + return JeTypeElement(psiClass) + } + + override fun getConstantExpression(value: Any?) = Constants.format(value) + + override tailrec fun getPackageOf(element: Element): PackageElement? { + if (element is PackageElement) return element + val parent = element.enclosingElement ?: return null + return getPackageOf(parent) + } + + override fun getAllAnnotationMirrors(e: Element): List { + val annotations = (e as? JeElement)?.annotationMirrors?.toMutableList() ?: return emptyList() + + if (e is JeTypeElement) { + var parent = e.psi.superClass + while (parent != null) { + val parentAnnotations = parent.modifierList?.annotations + if (parentAnnotations == null) { + parent = parent.superClass + continue + } + + for (parentAnnotation in parentAnnotations) { + val annotationClass = parentAnnotation.nameReferenceElement?.resolve() as? PsiClass ?: continue + annotationClass.modifierList?.findAnnotation("java.lang.annotation.Inherited") ?: continue + annotations += JeAnnotationMirror(parentAnnotation) + } + + parent = parent.superClass + } + } + + return annotations + } + + override fun isFunctionalInterface(type: TypeElement): Boolean { + val jeTypeElement = type as? JeTypeElement ?: return false + if (!jeTypeElement.psi.isInterface) return false + if (jeTypeElement.psi.allMethods.size != 1) return false + return true + } +} + +object Constants { + fun format(value: Any?): String { + return when (value) { + is Byte -> formatByte((value as Byte?)!!) + is Short -> formatShort((value as Short?)!!) + is Long -> formatLong((value as Long?)!!) + is Float -> formatFloat((value as Float?)!!) + is Double -> formatDouble((value as Double?)!!) + is Char -> formatChar(value) + is String -> formatString(value) + is Int, is Boolean -> value.toString() + else -> throw IllegalArgumentException( + "Argument is not a primitive type or a string; it " + + (if (value == null) "is a null value." else "has class " + value.javaClass.name) + ".") + } + } + + private fun formatByte(b: Byte) = String.format("(byte)0x%02x", b) + private fun formatShort(s: Short) = String.format("(short)%d", s) + private fun formatLong(lng: Long) = "${lng}L" + private fun formatChar(c: Char) = '\'' + quote(c) + '\'' + private fun formatString(s: String) = '"' + quote(s) + '"' + + private fun formatFloat(f: Float): String { + if (java.lang.Float.isNaN(f)) + return "0.0f/0.0f" + else if (java.lang.Float.isInfinite(f)) + return if (f < 0) "-1.0f/0.0f" else "1.0f/0.0f" + else + return "${f}f" + } + + private fun formatDouble(d: Double): String { + if (java.lang.Double.isNaN(d)) + return "0.0/0.0" + else if (java.lang.Double.isInfinite(d)) + return if (d < 0) "-1.0/0.0" else "1.0/0.0" + else + return d.toString() + } + + fun quote(ch: Char): String { + return when (ch) { + '\b' -> "\\b" + '\n' -> "\\n" + '\r' -> "\\r" + '\t' -> "\\t" + '\'' -> "\\'" + '\"' -> "\\\"" + '\\' -> "\\\\" + else -> if (isPrintableAscii(ch)) ch.toString() else String.format("\\u%04x", ch.toInt()) + } + } + + fun quote(s: String): String { + val buf = StringBuilder() + for (i in 0..s.length - 1) { + buf.append(quote(s[i])) + } + return buf.toString() + } + + private fun isPrintableAscii(ch: Char) = ch >= ' ' && ch <= '~' +} \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinFiler.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinFiler.kt new file mode 100644 index 00000000000..8398785203a --- /dev/null +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinFiler.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2016 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.processing.impl + +import java.io.File +import javax.annotation.processing.Filer +import javax.lang.model.element.Element +import javax.tools.FileObject +import javax.tools.JavaFileManager +import javax.tools.JavaFileObject + +class KotlinFiler(val generatedDir: File) : Filer { + private companion object { + val PACKAGE_INFO_SUFFIX = ".packageInfo" + } + + private var dotGeneratedGenerated = false + + private fun createDotGeneratedIfNeeded() { + if (!dotGeneratedGenerated) { + dotGeneratedGenerated = true + File(generatedDir, ".generated").writeText("Generated by kapt2") + } + } + + private fun getGeneratedFile(nameCharSequence: CharSequence, extension: String): File { + createDotGeneratedIfNeeded() + + val name = nameCharSequence.toString() + val isPackageInfo = name.endsWith(PACKAGE_INFO_SUFFIX) + val fqName = if (isPackageInfo) name.substring(0, PACKAGE_INFO_SUFFIX.length) else name + + val packageName = fqName.substringBeforeLast('.') + + val packageDir = File(generatedDir, packageName.replace('.', '/')) + packageDir.mkdirs() + + val fileName = fqName.substringAfterLast('.') + (if (isPackageInfo) PACKAGE_INFO_SUFFIX else extension) + return File(packageDir, fileName) + } + + override fun createSourceFile(name: CharSequence, vararg originatingElements: Element?): JavaFileObject { + return KotlinJavaFileObject(getGeneratedFile(name, ".java")) + } + + override fun getResource(location: JavaFileManager.Location, pkg: CharSequence, relativeName: CharSequence): FileObject? { + throw UnsupportedOperationException() + } + + override fun createResource( + location: JavaFileManager.Location?, + pkg: CharSequence, + relativeName: CharSequence, + vararg originatingElements: Element? + ): FileObject? { + throw UnsupportedOperationException() + } + + override fun createClassFile(name: CharSequence, vararg originatingElements: Element?): JavaFileObject { + return KotlinJavaFileObject(getGeneratedFile(name, ".class")) + } +} \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinJavaFileObject.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinJavaFileObject.kt new file mode 100644 index 00000000000..962522223d7 --- /dev/null +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinJavaFileObject.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2016 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.processing.impl + +import java.io.File +import javax.tools.JavaFileObject + +class KotlinJavaFileObject(val file: File) : JavaFileObject { + override fun openOutputStream() = file.outputStream() + + override fun getName() = file.name + + override fun openWriter() = file.writer() + + override fun openInputStream() = file.inputStream() + + override fun getCharContent(ignoreEncodingErrors: Boolean): CharSequence? { + TODO() + } + + override fun getLastModified() = file.lastModified() + + override fun toUri() = file.toURI() + + override fun openReader(ignoreEncodingErrors: Boolean) = file.reader() + + override fun delete() = file.delete() + + //TODO + override fun isNameCompatible(simpleName: String, kind: JavaFileObject.Kind) = true + + override fun getKind() = when (file.extension) { + "class" -> JavaFileObject.Kind.CLASS + "java" -> JavaFileObject.Kind.SOURCE + "html" -> JavaFileObject.Kind.HTML + else -> JavaFileObject.Kind.OTHER + } + + //TODO + override fun getAccessLevel() = null + + //TODO + override fun getNestingKind() = null +} \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinMessager.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinMessager.kt new file mode 100644 index 00000000000..ca1f2f9629f --- /dev/null +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinMessager.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2016 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.processing.impl + +import javax.annotation.processing.Messager +import javax.lang.model.element.AnnotationMirror +import javax.lang.model.element.AnnotationValue +import javax.lang.model.element.Element +import javax.tools.Diagnostic + +class KotlinMessager : Messager { + override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence) = printMessage(kind, msg, null) + + override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence, e: Element?) = printMessage(kind, msg, e, null) + + override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence, e: Element?, a: AnnotationMirror?) { + printMessage(kind, msg, e, a, null) + } + + override fun printMessage(kind: Diagnostic.Kind, msg: CharSequence, e: Element?, a: AnnotationMirror?, v: AnnotationValue?) { + println(msg) + } +} \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinProcessingEnvironment.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinProcessingEnvironment.kt new file mode 100644 index 00000000000..c72898a80b7 --- /dev/null +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinProcessingEnvironment.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2016 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.processing.impl + +import java.util.* +import javax.annotation.processing.ProcessingEnvironment +import javax.lang.model.SourceVersion + +class KotlinProcessingEnvironment( + private val elements: KotlinElements, + private val types: KotlinTypes, + private val messager: KotlinMessager, + options: Map, + private val filer: KotlinFiler +) : ProcessingEnvironment { + private val options = Collections.unmodifiableMap(options) + + override fun getElementUtils() = elements + override fun getTypeUtils() = types + override fun getMessager() = messager + override fun getLocale() = Locale.getDefault() + override fun getSourceVersion() = SourceVersion.RELEASE_6 + override fun getOptions() = options + override fun getFiler() = filer +} \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinRoundEnvironment.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinRoundEnvironment.kt new file mode 100644 index 00000000000..c8f66a62b5e --- /dev/null +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinRoundEnvironment.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2016 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.processing.impl + +import org.jetbrains.kotlin.annotation.AnalysisContext +import org.jetbrains.kotlin.java.model.JeConverter +import javax.annotation.processing.RoundEnvironment +import javax.lang.model.element.Element +import javax.lang.model.element.TypeElement + +internal class KotlinRoundEnvironment( + private val context: AnalysisContext, + private val isProcessingOver: Boolean = false) : RoundEnvironment { + private var isError = false + + override fun getRootElements() = emptySet() + + override fun processingOver() = isProcessingOver + + private fun getElementsAnnotatedWith(fqName: String): Set { + val declarations = context.annotationsMap[fqName] ?: return emptySet() + return hashSetOf().apply { + for (declaration in declarations) { + JeConverter.convert(declaration)?.let { add(it) } + } + } + } + + override fun getElementsAnnotatedWith(a: TypeElement) = getElementsAnnotatedWith(a.qualifiedName.toString()) + override fun getElementsAnnotatedWith(a: Class) = getElementsAnnotatedWith(a.canonicalName) + + override fun errorRaised() = isError +} \ No newline at end of file diff --git a/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinTypes.kt b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinTypes.kt new file mode 100644 index 00000000000..e372315e387 --- /dev/null +++ b/plugins/annotation-processing/src/org/jetbrains/kotlin/annotation/processing/impl/KotlinTypes.kt @@ -0,0 +1,221 @@ +/* + * Copyright 2010-2016 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.processing.impl + +import com.intellij.psi.* +import com.intellij.psi.impl.PsiSubstitutorImpl +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.util.PsiTypesUtil +import com.intellij.psi.util.TypeConversionUtil +import org.jetbrains.kotlin.java.model.elements.JeClassInitializerExecutableElement +import org.jetbrains.kotlin.java.model.elements.JeMethodExecutableElement +import org.jetbrains.kotlin.java.model.elements.JeTypeElement +import org.jetbrains.kotlin.java.model.elements.JeVariableElement +import org.jetbrains.kotlin.java.model.types.* +import javax.lang.model.element.Element +import javax.lang.model.element.TypeElement +import javax.lang.model.type.* +import javax.lang.model.util.Types + +class KotlinTypes(val javaPsiFacade: JavaPsiFacade, val scope: GlobalSearchScope) : Types { + override fun contains(t1: TypeMirror, t2: TypeMirror): Boolean { + t1 as? JeAbstractType ?: return false + t2 as? JeAbstractType ?: return false + + val classType = t1.psiType as? PsiClassType ?: return false + return t2.psiType in classType.parameters + } + + override fun getArrayType(componentType: TypeMirror): ArrayType { + val psiType = (componentType as? JeAbstractType)?.psiType + ?: throw IllegalArgumentException("Invalid component type: $componentType") + return JeArrayType(PsiArrayType(psiType)) + } + + override fun isAssignable(t1: TypeMirror, t2: TypeMirror): Boolean { + fun error(t: TypeMirror?): Nothing = throw IllegalArgumentException("Invalid type: $t") + if (t1 is ExecutableType || t1 is NoType) error(t1) + if (t2 is ExecutableType || t2 is NoType) error(t2) + + t1 as? JeAbstractType ?: return false + t2 as? JeAbstractType ?: return false + return t1.psiType.isAssignableFrom(t2.psiType) + } + + override fun getNullType() = JeNullType + + override fun getWildcardType(extendsBound: TypeMirror, superBound: TypeMirror): JeWildcardTypeWithBounds { + return JeWildcardTypeWithBounds(extendsBound, superBound) + } + + override fun unboxedType(t: TypeMirror): PrimitiveType? { + fun error(): Nothing = throw IllegalArgumentException("This type could not be unboxed: $t") + t as? JeAbstractType ?: error() + val unboxedType = PsiPrimitiveType.getUnboxedType(t.psiType) ?: error() + return unboxedType.toJePrimitiveType() + } + + override fun getPrimitiveType(kind: TypeKind) = kind.toJePrimitiveType() + + override fun erasure(t: TypeMirror): TypeMirror { + if (t is NoType) throw IllegalArgumentException("Invalid type: $t") + t as? JeAbstractType ?: return t + return TypeConversionUtil.erasure(t.psiType).toJeType() + } + + override fun directSupertypes(t: TypeMirror): List { + if (t is NoType) throw IllegalArgumentException("Invalid type: $t") + val psiType = (t as? JeAbstractType)?.psiType as? PsiClassType ?: return emptyList() + return psiType.superTypes.map { it.toJeType() } + } + + override fun boxedClass(p: PrimitiveType): TypeElement? { + p as? JePrimitiveType ?: throw IllegalArgumentException("Unknown type: $p") + val boxedTypeName = p.psiType.boxedTypeName + val boxedClass = javaPsiFacade.findClass(boxedTypeName, scope) + ?: throw IllegalStateException("Can't find boxed class $boxedTypeName") + return JeTypeElement(boxedClass) + } + + override fun asElement(t: TypeMirror): Element? { + if (t is JeDeclaredType) { + return t.asElement() + } else if (t is JeTypeVariableType) { + return t.asElement() + } + + return null + } + + override fun isSubtype(t1: TypeMirror, t2: TypeMirror): Boolean { + val psiType1 = (t1 as? JeAbstractType)?.psiType ?: return false + val psiType2 = (t2 as? JeAbstractType)?.psiType ?: return false + + return TypeConversionUtil.isAssignable(psiType2, psiType1, false) + } + + override fun isSameType(t1: TypeMirror, t2: TypeMirror) = t1 == t2 + + override fun getNoType(kind: TypeKind) = CustomJeNoneType(kind) + + override fun getDeclaredType(typeElem: TypeElement, vararg typeArgMirrors: TypeMirror): DeclaredType { + val psiClass = (typeElem as JeTypeElement).psi + if (!psiClass.hasTypeParameters() && typeArgMirrors.size > 0) { + throw IllegalArgumentException("$typeElem has not type parameters") + } + + // Raw type + if (psiClass.hasTypeParameters() && typeArgMirrors.isEmpty()) { + return JeDeclaredType(PsiTypesUtil.getClassType(psiClass).rawType(), psiClass) + } + + val typeParameters = psiClass.typeParameters + if (typeArgMirrors.size != typeParameters.size) { + throw IllegalArgumentException("$typeElem type parameters count: " + + "${typeParameters.size}, received ${typeArgMirrors.size} args") + } + + val typeArgs = typeArgMirrors.mapIndexed { + i, t -> (t as? JeAbstractType)?.psiType ?: throw IllegalArgumentException("Invalid type argument #$i: $t") + } + + return JeCompoundDeclaredType(psiClass, typeElem, null, typeArgs, typeArgMirrors.toList()) + } + + override fun getDeclaredType( + containing: DeclaredType?, + typeElem: TypeElement, + vararg typeArgMirrors: TypeMirror + ): DeclaredType? { + if (containing == null) { + return getDeclaredType(typeElem, *typeArgMirrors) + } + + val psiClass = (typeElem as JeTypeElement).psi + if (psiClass.containingClass == null) { + throw IllegalArgumentException("$typeElem is a top-level class element") + } + + val containingType = (containing as? JeAbstractType)?.psiType as? PsiClassType + ?: throw IllegalArgumentException("Illegal containing type: $containing") + val containingClass = containingType.resolve() + ?: throw IllegalArgumentException("Class type can't be resolved: $containing") + + if (psiClass.containingClass != containingClass) { + throw IllegalArgumentException("$containing is not an enclosing element for $typeElem") + } + + val typeArgs = typeArgMirrors.mapIndexed { + i, t -> (t as? JeAbstractType)?.psiType ?: throw IllegalArgumentException("Invalid type argument #$i: $t") + } + + return JeCompoundDeclaredType(psiClass, typeElem, containing, typeArgs, typeArgMirrors.toList()) + } + + override fun asMemberOf(containing: DeclaredType, element: Element): TypeMirror { + val substitutor = when (containing) { + is JeDeclaredType -> { + val result = containing.psiType.resolveGenerics() + if (result.isValidResult) result.substitutor else PsiSubstitutor.EMPTY + } + is JeCompoundDeclaredType -> { + val mapping = containing.psiClass.typeParameters.zip(containing.typeArgs).toMap() + PsiSubstitutorImpl.createSubstitutor(mapping) + } + else -> throw IllegalArgumentException("Invalid containing type: $containing") + } + + return when (element) { + is JeMethodExecutableElement -> { + val method = element.psi + if (method.hasModifierProperty(PsiModifier.STATIC) || !method.hasTypeParameters()) { + JeMethodExecutableTypeMirror(method) + } else { + val signature = method.getSignature(substitutor) + val returnType = substitutor.substitute(element.psi.returnType) + JeMethodExecutableTypeMirror(method, signature, returnType) + } + } + is JeClassInitializerExecutableElement -> element.asType() + is JeVariableElement -> substitutor.substitute(element.psi.type).toJeType() + else -> throw IllegalArgumentException("Invalid element type: $element") + } + } + + override fun isSubsignature(t1: ExecutableType, t2: ExecutableType): Boolean { + val m1 = (t1 as JeClassInitializerExecutableTypeMirror).initializer + val m2 = (t2 as JeClassInitializerExecutableTypeMirror).initializer + + if (m1 !is PsiMethod || m2 !is PsiMethod) return false + if (m1.parameterList.parametersCount != m2.parameterList.parametersCount) return false + + for (i in 0..(m1.parameterList.parametersCount - 1)) { + val p1 = m1.parameterList.parameters[i] + val p2 = m2.parameterList.parameters[i] + + if (!TypeConversionUtil.isAssignable(p2.type, p1.type, false)) { + return false + } + } + + return true + } + + override fun capture(t: TypeMirror): TypeMirror? { + TODO() + } +} \ No newline at end of file