Kapt: Implement Annotation Processing plugin in Kotlin (KT-13499)

(cherry picked from commit 32c461a)
This commit is contained in:
Yan Zhulanow
2016-06-21 21:08:37 +03:00
committed by Yan Zhulanow
parent cc12a6c228
commit 0181dd000d
12 changed files with 943 additions and 0 deletions
@@ -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)
@@ -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<String>
) : AnalysisCompletedHandlerExtension {
private var annotationProcessingComplete = false
override fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
bindingContext: BindingContext,
files: Collection<KtFile>
): AnalysisResult? {
if (annotationProcessingComplete) {
return null
}
val processors = loadAnnotationProcessors(annotationProcessingClasspath)
if (processors.isEmpty()) return null
val analysisContext = AnalysisContext(hashMapOf())
analysisContext.analyzeFiles(files)
val options = emptyMap<String, String>()
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<String>): List<Processor> {
val classLoader = URLClassLoader(classpath.map { File(it).toURI().toURL() }.toTypedArray())
return ServiceLoader.load(Processor::class.java, classLoader).toList()
}
}
internal class AnalysisContext(val annotationsMap: MutableMap<String, MutableList<PsiModifierListOwner>>)
private fun AnalysisContext.analyzeFiles(files: Collection<KtFile>) {
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) }
}
}
@@ -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<String> =
CompilerConfigurationKey.create<String>("generated files output directory")
val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create<List<String>>("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", "<path>", "Output path for generated files", required = false)
val ANNOTATION_PROCESSOR_CLASSPATH_OPTION: CliOption =
CliOption("apclasspath", "<classpath>", "Annotation processor classpath",
required = false, allowMultipleOccurrences = true)
}
override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID
override val pluginOptions: Collection<CliOption> =
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)
}
}
@@ -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<out ExecutableElement, AnnotationValue> {
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<AnnotationMirror> {
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 <= '~'
}
@@ -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"))
}
}
@@ -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
}
@@ -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)
}
}
@@ -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<String, String>,
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
}
@@ -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<Element>()
override fun processingOver() = isProcessingOver
private fun getElementsAnnotatedWith(fqName: String): Set<Element> {
val declarations = context.annotationsMap[fqName] ?: return emptySet()
return hashSetOf<Element>().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<out Annotation>) = getElementsAnnotatedWith(a.canonicalName)
override fun errorRaised() = isError
}
@@ -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<TypeMirror> {
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()
}
}