Kapt3: Minor: Refactor kapt3 module (move/rename)

This commit is contained in:
Yan Zhulanow
2016-10-30 02:34:40 +03:00
committed by Yan Zhulanow
parent e61a7c7f2c
commit 86aa82da6c
21 changed files with 661 additions and 538 deletions
@@ -136,7 +136,7 @@ import org.jetbrains.kotlin.js.test.semantics.*
import org.jetbrains.kotlin.jvm.compiler.*
import org.jetbrains.kotlin.jvm.runtime.AbstractJvm8RuntimeDescriptorLoaderTest
import org.jetbrains.kotlin.jvm.runtime.AbstractJvmRuntimeDescriptorLoaderTest
import org.jetbrains.kotlin.kapt3.test.AbstractJCTreeConverterTest
import org.jetbrains.kotlin.kapt3.test.AbstractClassFileToSourceStubConverterTest
import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptRunnerTest
import org.jetbrains.kotlin.kdoc.AbstractKDocLexerTest
import org.jetbrains.kotlin.lang.resolve.android.test.AbstractAndroidBoxTest
@@ -1140,7 +1140,7 @@ fun main(args: Array<String>) {
}
testGroup("plugins/kapt3/test", "plugins/kapt3/testData") {
testClass<AbstractJCTreeConverterTest> {
testClass<AbstractClassFileToSourceStubConverterTest> {
model("converter")
}
@@ -1,220 +0,0 @@
/*
* 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.kapt3
import com.intellij.openapi.project.Project
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.kapt3.diagnostic.ErrorsKapt3
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
import org.jetbrains.org.objectweb.asm.FieldVisitor
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import org.jetbrains.org.objectweb.asm.tree.FieldNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.io.File
import java.net.URLClassLoader
import java.util.*
import javax.annotation.processing.Processor
import com.sun.tools.javac.util.List as JavacList
class Kapt3AnalysisCompletedHandlerExtension(
val annotationProcessingClasspath: List<File>,
val javaSourceRoots: List<File>,
val sourcesOutputDir: File,
val classFilesOutputDir: File,
val stubsOutputDir: File?,
val options: Map<String, String>, //TODO
val aptOnly: Boolean,
val pluginInitializedTime: Long,
val logger: Logger
) : AnalysisCompletedHandlerExtension {
private var annotationProcessingComplete = false
override fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
bindingTrace: BindingTrace,
files: Collection<KtFile>
): AnalysisResult? {
if (annotationProcessingComplete) {
return null
}
annotationProcessingComplete = true
fun doNotGenerateCode() = AnalysisResult.Companion.success(BindingContext.EMPTY, module, shouldGenerateCode = false)
if (files.isEmpty()) {
logger.info("No Kotlin source files, aborting")
return if (aptOnly) doNotGenerateCode() else null
}
logger.info { "Initial analysis took ${System.currentTimeMillis() - pluginInitializedTime} ms" }
logger.info { "Kotlin files: " + files.map { it.virtualFile?.name ?: "<in memory ${it.hashCode()}>" } }
val processors = loadProcessors(annotationProcessingClasspath)
if (processors.isEmpty()) {
logger.info("No annotation processors available, aborting")
return if (aptOnly) doNotGenerateCode() else null
}
logger.info { "Annotation processors: " + processors.joinToString { it.javaClass.canonicalName } }
val builderFactory = Kapt3BuilderFactory()
val generationState = GenerationState(
project,
builderFactory,
module,
bindingTrace.bindingContext,
files.toList(),
disableCallAssertions = false,
disableParamAssertions = false)
try {
val (stubCompilationTime) = measureTimeMillis {
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
}
val compiledClasses = builderFactory.compiledClasses
val origins = builderFactory.origins
logger.info { "Compiled classes: " + compiledClasses.joinToString { it.name } }
val javaFilesFromJavaSourceRoots = javaSourceRoots.flatMap {
root -> root.walk().filter { it.isFile && it.extension == "java" }.toList()
}
logger.info { "Java source files: " + javaFilesFromJavaSourceRoots.joinToString { it.canonicalPath } }
val kaptRunner = KaptRunner(logger)
val (jcStubGenerationTime, jcCompilationUnitsForKotlinClasses) = measureTimeMillis {
val treeConverter = JCTreeConverter(kaptRunner.context, generationState.typeMapper, compiledClasses, origins)
treeConverter.convert()
}
logger.info { "Stubs for Kotlin classes: " + jcCompilationUnitsForKotlinClasses.joinToString { it.sourcefile.name } }
logger.info { "Stubs compilation took $stubCompilationTime ms" }
logger.info { "Java stub generation took $jcStubGenerationTime ms" }
if (stubsOutputDir != null) {
saveStubs(stubsOutputDir, jcCompilationUnitsForKotlinClasses)
}
val (annotationProcessingTime) = measureTimeMillis {
kaptRunner.doAnnotationProcessing(
javaFilesFromJavaSourceRoots, processors,
annotationProcessingClasspath, sourcesOutputDir, classFilesOutputDir, jcCompilationUnitsForKotlinClasses)
}
logger.info { "Annotation processing took $annotationProcessingTime ms" }
} catch (thr: Throwable) {
if (thr !is KaptError || thr.kind != KaptError.Kind.ERROR_RAISED) {
logger.exception(thr)
}
bindingTrace.report(ErrorsKapt3.KAPT3_PROCESSING_ERROR.on(files.first()))
return null // Compilation will be aborted anyway because of the error above
} finally {
generationState.destroy()
}
return if (aptOnly) {
doNotGenerateCode()
} else {
AnalysisResult.RetryWithAdditionalJavaRoots(
bindingTrace.bindingContext,
module,
listOf(sourcesOutputDir),
addToEnvironment = true)
}
}
private inline fun <T> measureTimeMillis(block: () -> T) : Pair<Long, T> {
val start = System.currentTimeMillis()
val result = block()
return Pair(System.currentTimeMillis() - start, result)
}
private fun saveStubs(outputDir: File, stubs: JavacList<JCTree.JCCompilationUnit>) {
for (stub in stubs) {
val className = (stub.defs.first { it is JCTree.JCClassDecl } as JCTree.JCClassDecl).simpleName.toString()
val packageName = stub.packageName?.toString() ?: ""
val packageDir = if (packageName.isEmpty()) outputDir else File(outputDir, packageName.replace('.', '/'))
packageDir.mkdirs()
File(packageDir, className + ".java").writeText(stub.toString())
}
}
private fun loadProcessors(classpath: List<File>): List<Processor> {
val classLoader = URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray())
return ServiceLoader.load(Processor::class.java, classLoader).toList()
}
}
class Kapt3BuilderFactory : ClassBuilderFactory {
val compiledClasses = mutableListOf<ClassNode>()
val origins = mutableMapOf<Any, JvmDeclarationOrigin>()
override fun getClassBuilderMode(): ClassBuilderMode = ClassBuilderMode.KAPT3
override fun newClassBuilder(origin: JvmDeclarationOrigin): AbstractClassBuilder.Concrete {
val classNode = ClassNode()
compiledClasses += classNode
origins.put(classNode, origin)
return object : AbstractClassBuilder.Concrete(classNode) {
override fun newField(
origin: JvmDeclarationOrigin,
access: Int,
name: String,
desc: String,
signature: String?,
value: Any?
): FieldVisitor {
val fieldNode = super.newField(origin, access, name, desc, signature, value) as FieldNode
origins.put(fieldNode, origin)
return fieldNode
}
override fun newMethod(
origin: JvmDeclarationOrigin,
access: Int,
name: String,
desc: String,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
val methodNode = super.newMethod(origin, access, name, desc, signature, exceptions) as MethodNode
origins.put(methodNode, origin)
return methodNode
}
}
}
override fun asText(builder: ClassBuilder) = throw UnsupportedOperationException()
override fun asBytes(builder: ClassBuilder) = throw UnsupportedOperationException()
override fun close() {}
}
@@ -0,0 +1,73 @@
/*
* 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.kapt3
import org.jetbrains.kotlin.codegen.AbstractClassBuilder
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.FieldVisitor
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import org.jetbrains.org.objectweb.asm.tree.FieldNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
class Kapt3BuilderFactory : ClassBuilderFactory {
val compiledClasses = mutableListOf<ClassNode>()
val origins = mutableMapOf<Any, JvmDeclarationOrigin>()
override fun getClassBuilderMode(): ClassBuilderMode = ClassBuilderMode.KAPT3
override fun newClassBuilder(origin: JvmDeclarationOrigin): AbstractClassBuilder.Concrete {
val classNode = ClassNode()
compiledClasses += classNode
origins.put(classNode, origin)
return object : AbstractClassBuilder.Concrete(classNode) {
override fun newField(
origin: JvmDeclarationOrigin,
access: Int,
name: String,
desc: String,
signature: String?,
value: Any?
): FieldVisitor {
val fieldNode = super.newField(origin, access, name, desc, signature, value) as FieldNode
origins.put(fieldNode, origin)
return fieldNode
}
override fun newMethod(
origin: JvmDeclarationOrigin,
access: Int,
name: String,
desc: String,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
val methodNode = super.newMethod(origin, access, name, desc, signature, exceptions) as MethodNode
origins.put(methodNode, origin)
return methodNode
}
}
}
override fun asText(builder: ClassBuilder) = throw UnsupportedOperationException()
override fun asBytes(builder: ClassBuilder) = throw UnsupportedOperationException()
override fun close() {}
}
@@ -0,0 +1,200 @@
/*
* 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.kapt3
import com.intellij.openapi.project.Project
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.kapt3.diagnostic.ErrorsKapt3
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
import java.io.File
import java.net.URLClassLoader
import java.util.*
import javax.annotation.processing.Processor
import com.sun.tools.javac.util.List as JavacList
class Kapt3Extension(
val annotationProcessingClasspath: List<File>,
val javaSourceRoots: List<File>,
val sourcesOutputDir: File,
val classFilesOutputDir: File,
val stubsOutputDir: File?,
val options: Map<String, String>, //TODO
val aptOnly: Boolean,
val pluginInitializedTime: Long,
val logger: KaptLogger
) : AnalysisCompletedHandlerExtension {
private var annotationProcessingComplete = false
private fun setAnnotationProcessingComplete(): Boolean {
if (annotationProcessingComplete) return true
annotationProcessingComplete = true
return false
}
override fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
bindingTrace: BindingTrace,
files: Collection<KtFile>
): AnalysisResult? {
if (setAnnotationProcessingComplete()) return null
fun doNotGenerateCode() = AnalysisResult.Companion.success(BindingContext.EMPTY, module, shouldGenerateCode = false)
if (files.isEmpty()) {
logger.info("No Kotlin source files, aborting")
return if (aptOnly) doNotGenerateCode() else null
}
logger.info { "Initial analysis took ${System.currentTimeMillis() - pluginInitializedTime} ms" }
logger.info { "Kotlin files to compile: " + files.map { it.virtualFile?.name ?: "<in memory ${it.hashCode()}>" } }
val processors = loadProcessors(annotationProcessingClasspath)
if (processors.isEmpty()) return if (aptOnly) doNotGenerateCode() else null
val (kaptContext, generationState) = compileStubs(project, module, bindingTrace.bindingContext, files.toList())
try {
val javaSourceFiles = collectJavaSourceFiles()
val kotlinSourceStubs = generateKotlinSourceStubs(kaptContext, generationState.typeMapper)
val (annotationProcessingTime) = measureTimeMillis {
kaptContext.doAnnotationProcessing(
javaSourceFiles, processors,
annotationProcessingClasspath, sourcesOutputDir, classFilesOutputDir, kotlinSourceStubs)
}
logger.info { "Annotation processing took $annotationProcessingTime ms" }
} catch (thr: Throwable) {
if (thr !is KaptError || thr.kind != KaptError.Kind.ERROR_RAISED) {
logger.exception(thr)
}
bindingTrace.report(ErrorsKapt3.KAPT3_PROCESSING_ERROR.on(files.first()))
return null // Compilation will be aborted anyway because of the error above
} finally {
generationState.destroy()
kaptContext.close()
}
return if (aptOnly) {
doNotGenerateCode()
} else {
AnalysisResult.RetryWithAdditionalJavaRoots(
bindingTrace.bindingContext,
module,
listOf(sourcesOutputDir),
addToEnvironment = true)
}
}
private fun compileStubs(
project: Project,
module: ModuleDescriptor,
bindingContext: BindingContext,
files: List<KtFile>
): Pair<KaptContext, GenerationState> {
val builderFactory = Kapt3BuilderFactory()
val generationState = GenerationState(
project,
builderFactory,
module,
bindingContext,
files,
CompilerConfiguration.EMPTY)
val (classFilesCompilationTime) = measureTimeMillis {
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
}
val compiledClasses = builderFactory.compiledClasses
val origins = builderFactory.origins
logger.info { "Stubs compilation took $classFilesCompilationTime ms" }
logger.info { "Compiled classes: " + compiledClasses.joinToString { it.name } }
return Pair(KaptContext(logger, compiledClasses, origins), generationState)
}
private fun generateKotlinSourceStubs(kaptContext: KaptContext, typeMapper: KotlinTypeMapper): JavacList<JCCompilationUnit> {
val (stubGenerationTime, kotlinSourceStubs) = measureTimeMillis {
ClassFileToSourceStubConverter(kaptContext, typeMapper).convert()
}
logger.info { "Java stub generation took $stubGenerationTime ms" }
logger.info { "Stubs for Kotlin classes: " + kotlinSourceStubs.joinToString { it.sourcefile.name } }
if (stubsOutputDir != null) {
saveStubs(stubsOutputDir, kotlinSourceStubs)
}
return kotlinSourceStubs
}
private fun saveStubs(outputDir: File, stubs: JavacList<JCTree.JCCompilationUnit>) {
for (stub in stubs) {
val className = (stub.defs.first { it is JCTree.JCClassDecl } as JCTree.JCClassDecl).simpleName.toString()
val packageName = stub.packageName?.toString() ?: ""
val packageDir = if (packageName.isEmpty()) outputDir else File(outputDir, packageName.replace('.', '/'))
packageDir.mkdirs()
File(packageDir, className + ".java").writeText(stub.toString())
}
}
private fun collectJavaSourceFiles(): List<File> {
val javaFilesFromJavaSourceRoots = javaSourceRoots.flatMap {
root -> root.walk().filter { it.isFile && it.extension == "java" }.toList()
}
logger.info { "Java source files: " + javaFilesFromJavaSourceRoots.joinToString { it.canonicalPath } }
return javaFilesFromJavaSourceRoots
}
private fun loadProcessors(classpath: List<File>): List<Processor> {
val classLoader = URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray())
val processors = ServiceLoader.load(Processor::class.java, classLoader).toList()
if (processors.isEmpty()) {
logger.info("No annotation processors available, aborting")
} else {
logger.info { "Annotation processors: " + processors.joinToString { it.javaClass.canonicalName } }
}
return processors
}
}
private inline fun <T> measureTimeMillis(block: () -> T) : Pair<Long, T> {
val start = System.currentTimeMillis()
val result = block()
return Pair(System.currentTimeMillis() - start, result)
}
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.kapt3.diagnostic.DefaultErrorMessagesKapt3
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExtension
import java.io.File
@@ -139,7 +140,7 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesKapt3())
val logger = Logger(isVerbose)
val logger = KaptLogger(isVerbose)
if (isVerbose) {
logger.info("Kapt3 is enabled.")
logger.info("Do annotation processing only: $isAptOnly")
@@ -151,7 +152,7 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
logger.info("Options: $apOptions")
}
val kapt3AnalysisCompletedHandlerExtension = Kapt3AnalysisCompletedHandlerExtension(
val kapt3AnalysisCompletedHandlerExtension = Kapt3Extension(
classpath, javaSourceRoots, sourcesOutputDir, classFilesOutputDir, stubsOutputDir, apOptions,
isAptOnly, System.currentTimeMillis(), logger)
AnalysisCompletedHandlerExtension.registerExtension(project, kapt3AnalysisCompletedHandlerExtension)
@@ -0,0 +1,55 @@
/*
* 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.kapt3
import com.sun.tools.javac.file.JavacFileManager
import com.sun.tools.javac.main.JavaCompiler
import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.Options
import org.jetbrains.kotlin.kapt3.javac.KaptJavaCompiler
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import javax.tools.JavaFileManager
class KaptContext(
val logger: KaptLogger,
val compiledClasses: List<ClassNode>,
val origins: Map<Any, JvmDeclarationOrigin>
) {
val context = Context()
val compiler: KaptJavaCompiler
val fileManager: JavacFileManager
val options: Options
init {
JavacFileManager.preRegister(context)
KaptTreeMaker.preRegister(context)
KaptJavaCompiler.preRegister(context)
fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
compiler = JavaCompiler.instance(context) as KaptJavaCompiler
options = Options.instance(context)
}
fun close() {
compiler.close()
fileManager.close()
}
}
@@ -1,150 +0,0 @@
/*
* 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.kapt3
import com.sun.tools.javac.comp.CompileStates
import com.sun.tools.javac.file.JavacFileManager
import com.sun.tools.javac.main.JavaCompiler
import com.sun.tools.javac.main.Option
import com.sun.tools.javac.processing.AnnotationProcessingError
import com.sun.tools.javac.processing.JavacFiler
import com.sun.tools.javac.processing.JavacMessager
import com.sun.tools.javac.processing.JavacProcessingEnvironment
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.Log
import com.sun.tools.javac.util.Options
import java.io.File
import javax.annotation.processing.Processor
import javax.tools.JavaFileManager
import com.sun.tools.javac.util.List as JavacList
class KaptError : RuntimeException {
val kind: Kind
enum class Kind(val message: String) {
JAVA_FILE_PARSING_ERROR("Java file parsing error"),
EXCEPTION("Exception while annotation processing"),
ERROR_RAISED("Error while annotation processing"),
UNKNOWN("Unknown error while annotation processing")
}
constructor(kind: Kind) : super(kind.message) {
this.kind = kind
}
constructor(kind: Kind, cause: Throwable) : super(kind.message, cause) {
this.kind = kind
}
}
class KaptRunner(val logger: Logger) {
val context = Context()
val compiler: KaptJavaCompiler
val fileManager: JavacFileManager
private val options: Options
init {
JavacFileManager.preRegister(context)
KaptTreeMaker.preRegister(context)
KaptJavaCompiler.preRegister(context)
fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
compiler = JavaCompiler.instance(context) as KaptJavaCompiler
options = Options.instance(context)
}
fun close() {
compiler.close()
fileManager.close()
}
fun parseJavaFiles(
javaSourceFiles: List<File>,
classpath: List<File>
): JavacList<JCTree.JCCompilationUnit> {
classpath.forEach { options.put(Option.CLASSPATH, it.canonicalPath) }
val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
try {
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
return compiler.parseFiles(javaFileObjects)
} finally {
fileManager.close()
compiler.close()
}
}
fun doAnnotationProcessing(
javaSourceFiles: List<File>,
processors: List<Processor>,
classpath: List<File>,
sourcesOutputDir: File,
classesOutputDir: File,
additionalSources: JavacList<JCTree.JCCompilationUnit> = JavacList.nil()
) {
options.put(Option.PROC, "only") // Only process annotations
options.put(Option.CLASSPATH, classpath.joinToString(File.pathSeparator) { it.canonicalPath })
options.put(Option.S, sourcesOutputDir.canonicalPath)
options.put(Option.D, classesOutputDir.canonicalPath)
val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
val processingEnvironment = JavacProcessingEnvironment.instance(context)
try {
compiler.initProcessAnnotations(processors)
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
val parsedJavaFiles = compiler.parseFiles(javaFileObjects)
if (compiler.shouldStop(CompileStates.CompileState.PARSE)) {
throw KaptError(KaptError.Kind.JAVA_FILE_PARSING_ERROR)
}
val log = Log.instance(context)
val errorCountBeforeAp = log.nerrors
val warningsBeforeAp = log.nwarnings
val compilerAfterAnnotationProcessing: JavaCompiler? = null
try {
compiler.processAnnotations(compiler.enterTrees(parsedJavaFiles + additionalSources))
} catch (e: AnnotationProcessingError) {
throw KaptError(KaptError.Kind.EXCEPTION, e.cause ?: e)
}
val filer = processingEnvironment.filer as JavacFiler
val errorCount = Math.max(log.nerrors, errorCountBeforeAp)
val warningCount = log.nwarnings - warningsBeforeAp
logger.info { "Annotation processing complete, errors: $errorCount, warnings: $warningCount" }
if (logger.isVerbose) {
filer.displayState()
}
if (log.nerrors > 0) {
throw KaptError(KaptError.Kind.ERROR_RAISED)
}
compilerAfterAnnotationProcessing?.close()
} finally {
processingEnvironment.close()
compiler.close()
fileManager.close()
}
}
}
@@ -0,0 +1,90 @@
/*
* 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.kapt3
import com.sun.tools.javac.comp.CompileStates
import com.sun.tools.javac.file.JavacFileManager
import com.sun.tools.javac.main.JavaCompiler
import com.sun.tools.javac.main.Option
import com.sun.tools.javac.processing.AnnotationProcessingError
import com.sun.tools.javac.processing.JavacFiler
import com.sun.tools.javac.processing.JavacProcessingEnvironment
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.util.Log
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import java.io.File
import javax.annotation.processing.Processor
import javax.tools.JavaFileManager
import com.sun.tools.javac.util.List as JavacList
fun KaptContext.doAnnotationProcessing(
javaSourceFiles: List<File>,
processors: List<Processor>,
classpath: List<File>,
sourcesOutputDir: File,
classesOutputDir: File,
additionalSources: JavacList<JCTree.JCCompilationUnit> = JavacList.nil()
) {
with (options) {
put(Option.PROC, "only") // Only process annotations
put(Option.CLASSPATH, classpath.joinToString(File.pathSeparator) { it.canonicalPath })
put(Option.S, sourcesOutputDir.canonicalPath)
put(Option.D, classesOutputDir.canonicalPath)
}
val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
val processingEnvironment = JavacProcessingEnvironment.instance(context)
try {
compiler.initProcessAnnotations(processors)
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
val parsedJavaFiles = compiler.parseFiles(javaFileObjects)
if (compiler.shouldStop(CompileStates.CompileState.PARSE)) {
throw KaptError(KaptError.Kind.JAVA_FILE_PARSING_ERROR)
}
val log = Log.instance(context)
val errorCountBeforeAp = log.nerrors
val warningsBeforeAp = log.nwarnings
val compilerAfterAnnotationProcessing: JavaCompiler? = null
try {
compiler.processAnnotations(compiler.enterTrees(parsedJavaFiles + additionalSources))
} catch (e: AnnotationProcessingError) {
throw KaptError(KaptError.Kind.EXCEPTION, e.cause ?: e)
}
val filer = processingEnvironment.filer as JavacFiler
val errorCount = Math.max(log.nerrors, errorCountBeforeAp)
val warningCount = log.nwarnings - warningsBeforeAp
logger.info { "Annotation processing complete, errors: $errorCount, warnings: $warningCount" }
if (logger.isVerbose) {
filer.displayState()
}
if (log.nerrors > 0) {
throw KaptError(KaptError.Kind.ERROR_RAISED)
}
compilerAfterAnnotationProcessing?.close()
} finally {
processingEnvironment.close()
this@doAnnotationProcessing.close()
}
}
@@ -0,0 +1,36 @@
/*
* 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.kapt3.diagnostic
class KaptError : RuntimeException {
val kind: Kind
enum class Kind(val message: String) {
JAVA_FILE_PARSING_ERROR("Java file parsing error"),
EXCEPTION("Exception while annotation processing"),
ERROR_RAISED("Error while annotation processing"),
UNKNOWN("Unknown error while annotation processing")
}
constructor(kind: Kind) : super(kind.message) {
this.kind = kind
}
constructor(kind: Kind, cause: Throwable) : super(kind.message, cause) {
this.kind = kind
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.kapt3
package org.jetbrains.kotlin.kapt3.javac
import com.sun.tools.javac.comp.CompileStates
import com.sun.tools.javac.main.JavaCompiler
@@ -24,7 +24,7 @@ class KaptJavaCompiler(context: Context) : JavaCompiler(context) {
public override fun shouldStop(cs: CompileStates.CompileState) = super.shouldStop(cs)
companion object {
fun preRegister(context: Context) {
internal fun preRegister(context: Context) {
context.put(compilerKey, Context.Factory<JavaCompiler>(::KaptJavaCompiler))
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.kapt3
package org.jetbrains.kotlin.kapt3.javac
import com.sun.tools.javac.file.BaseFileObject
import com.sun.tools.javac.file.JavacFileManager
@@ -26,7 +26,7 @@ import javax.lang.model.element.Modifier
import javax.lang.model.element.NestingKind
import javax.tools.JavaFileObject
class SyntheticJavaFileObject(
class KaptJavaFileObject(
val compilationUnit: JCTree.JCCompilationUnit,
val clazz: JCTree.JCClassDecl,
val timestamp: Long,
@@ -81,7 +81,7 @@ class SyntheticJavaFileObject(
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as SyntheticJavaFileObject
other as KaptJavaFileObject
if (compilationUnit != other.compilationUnit) return false
if (clazz != other.clazz) return false
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.kapt3
package org.jetbrains.kotlin.kapt3.javac
import com.sun.tools.javac.code.TypeTag
import com.sun.tools.javac.tree.JCTree
@@ -24,23 +24,23 @@ import com.sun.tools.javac.util.Names
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.Type.*
class KaptTreeMaker(context: Context?) : TreeMaker(context) {
class KaptTreeMaker(context: Context) : TreeMaker(context) {
private val nameTable = Names.instance(context).table
fun convertType(type: Type): JCTree.JCExpression {
fun Type(type: Type): JCTree.JCExpression {
convertBuiltinType(type)?.let { return it }
if (type.sort == ARRAY) {
return TypeArray(convertType(type.elementType))
return TypeArray(Type(type.elementType))
}
return convertFqName(type.className)
return FqName(type.className)
}
fun convertFqName(internalOrFqName: String): JCTree.JCExpression {
fun FqName(internalOrFqName: String): JCTree.JCExpression {
val path = internalOrFqName.replace('/', '.').split('.')
assert(path.isNotEmpty())
if (path.size == 1) return convertSimpleName(path.single())
if (path.size == 1) return SimpleName(path.single())
var expr = Select(convertSimpleName(path[0]), name(path[1]))
var expr = Select(SimpleName(path[0]), name(path[1]))
for (index in 2..path.lastIndex) {
expr = Select(expr, name(path[index]))
}
@@ -63,12 +63,12 @@ class KaptTreeMaker(context: Context?) : TreeMaker(context) {
return TypeIdent(typeTag)
}
fun convertSimpleName(name: String): JCTree.JCExpression = Ident(name(name))
fun SimpleName(name: String): JCTree.JCExpression = Ident(name(name))
fun name(name: String) = nameTable.fromString(name)
companion object {
fun preRegister(context: Context) {
internal fun preRegister(context: Context) {
context.put(treeMakerKey, Context.Factory<TreeMaker>(::KaptTreeMaker))
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.kapt3
package org.jetbrains.kotlin.kapt3.stubs
import com.sun.tools.javac.code.Flags
import com.sun.tools.javac.code.TypeTag
@@ -22,28 +22,25 @@ import com.sun.tools.javac.file.JavacFileManager
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.JCTree.*
import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.util.Context
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.util.*
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.*
import java.util.*
import javax.lang.model.element.ElementKind
import javax.tools.JavaFileManager
import com.sun.tools.javac.util.List as JavacList
class JCTreeConverter(
context: Context,
val typeMapper: KotlinTypeMapper,
val classes: List<ClassNode>,
val origins: Map<Any, JvmDeclarationOrigin>
) {
class ClassFileToSourceStubConverter(val kaptContext: KaptContext, val typeMapper: KotlinTypeMapper) {
private companion object {
private val VISIBILITY_MODIFIERS = (Opcodes.ACC_PUBLIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED).toLong()
private val MODALITY_MODIFIERS = (Opcodes.ACC_FINAL or Opcodes.ACC_ABSTRACT).toLong()
@@ -68,20 +65,20 @@ class JCTreeConverter(
)
}
private val fileManager = context.get(JavaFileManager::class.java) as JavacFileManager
private val treeMaker = TreeMaker.instance(context) as KaptTreeMaker
private val fileManager = kaptContext.context.get(JavaFileManager::class.java) as JavacFileManager
private val treeMaker = TreeMaker.instance(kaptContext.context) as KaptTreeMaker
private val signatureParser = SignatureParser(treeMaker)
private var done = false
fun convert(): JavacList<JCCompilationUnit> {
if (done) error(JCTreeConverter::class.java.simpleName + " can convert classes only once")
if (done) error(ClassFileToSourceStubConverter::class.java.simpleName + " can convert classes only once")
done = true
return mapValues(classes) { convertTopLevelClass(it) }
return mapValues(kaptContext.compiledClasses) { convertTopLevelClass(it) }
}
private fun convertTopLevelClass(clazz: ClassNode): JCCompilationUnit? {
val origin = origins[clazz] ?: return null
val origin = kaptContext.origins[clazz] ?: return null
val ktFile = origin.element?.containingFile as? KtFile ?: return null
val descriptor = origin.descriptor as? ClassDescriptor ?: return null
@@ -92,13 +89,13 @@ class JCTreeConverter(
val packageAnnotations = JavacList.nil<JCAnnotation>()
val packageName = ktFile.packageFqName.asString()
val packageClause = if (packageName.isEmpty()) null else treeMaker.convertFqName(packageName)
val packageClause = if (packageName.isEmpty()) null else treeMaker.FqName(packageName)
val imports = JavacList.nil<JCTree>()
val classes = JavacList.of<JCTree>(classDeclaration)
val topLevel = treeMaker.TopLevel(packageAnnotations, packageClause, imports + classes)
val javaFileObject = SyntheticJavaFileObject(topLevel, classDeclaration, System.currentTimeMillis(), fileManager)
val javaFileObject = KaptJavaFileObject(topLevel, classDeclaration, System.currentTimeMillis(), fileManager)
topLevel.sourcefile = javaFileObject
return topLevel
}
@@ -109,7 +106,7 @@ class JCTreeConverter(
private fun convertClass(clazz: ClassNode, isTopLevel: Boolean): JCClassDecl? {
if (isSynthetic(clazz.access)) return null
val descriptor = origins[clazz]?.descriptor as? ClassDescriptor ?: return null
val descriptor = kaptContext.origins[clazz]?.descriptor as? ClassDescriptor ?: return null
val modifiers = convertModifiers(
if (!descriptor.isInner && descriptor.isNested) (clazz.access or Opcodes.ACC_STATIC) else clazz.access,
ElementKind.CLASS, clazz.visibleAnnotations, clazz.invisibleAnnotations)
@@ -130,10 +127,10 @@ class JCTreeConverter(
val interfaces = mapValues(clazz.interfaces) {
if (isAnnotation && it == "java/lang/annotation/Annotation") return@mapValues null
treeMaker.convertFqName(it)
treeMaker.FqName(it)
}
val superClass = treeMaker.convertFqName(clazz.superName)
val superClass = treeMaker.FqName(clazz.superName)
val hasSuperClass = clazz.superName != "java/lang/Object" && !isEnum
val genericType = signatureParser.parseClassSignature(clazz.signature, superClass, interfaces)
@@ -149,7 +146,7 @@ class JCTreeConverter(
}
val nestedClasses = mapValues<InnerClassNode, JCTree>(clazz.innerClasses) { innerClass ->
if (innerClass.outerName != clazz.name) return@mapValues null
val innerClassNode = classes.firstOrNull { it.name == innerClass.name } ?: return@mapValues null
val innerClassNode = kaptContext.compiledClasses.firstOrNull { it.name == innerClass.name } ?: return@mapValues null
convertClass(innerClassNode, false)
}
@@ -171,9 +168,9 @@ class JCTreeConverter(
// Enum type must be an identifier (Javac requirement)
val typeExpression = if (isEnum(field.access)) {
treeMaker.convertSimpleName(type.className.substringAfterLast('.'))
treeMaker.SimpleName(type.className.substringAfterLast('.'))
} else {
signatureParser.parseFieldSignature(field.signature, treeMaker.convertType(type))
signatureParser.parseFieldSignature(field.signature, treeMaker.Type(type))
}
val value = field.value
@@ -189,7 +186,7 @@ class JCTreeConverter(
private fun convertMethod(method: MethodNode, containingClass: ClassNode): JCMethodDecl? {
if (isSynthetic(method.access)) return null
val descriptor = origins[method]?.descriptor as? CallableDescriptor ?: return null
val descriptor = kaptContext.origins[method]?.descriptor as? CallableDescriptor ?: return null
val isOverridden = descriptor.overriddenDescriptors.isNotEmpty()
val visibleAnnotations = if (isOverridden) {
@@ -206,7 +203,7 @@ class JCTreeConverter(
val name = treeMaker.name(method.name)
val returnType = Type.getReturnType(method.desc)
val jcReturnType = if (isConstructor) null else treeMaker.convertType(returnType)
val jcReturnType = if (isConstructor) null else treeMaker.Type(returnType)
val parametersInfo = method.getParametersInfo(containingClass)
@Suppress("NAME_SHADOWING")
@@ -219,11 +216,11 @@ class JCTreeConverter(
info.flags or varargs or Flags.PARAMETER,
ElementKind.PARAMETER, info.visibleAnnotations, info.invisibleAnnotations)
val name = treeMaker.name(info.name)
val type = treeMaker.convertType(info.type)
val type = treeMaker.Type(info.type)
treeMaker.VarDef(modifiers, name, type, null)
}
val exceptionTypes = mapValues(method.exceptions) { treeMaker.convertFqName(it) }
val exceptionTypes = mapValues(method.exceptions) { treeMaker.FqName(it) }
val genericType = signatureParser.parseMethodSignature(method.signature, parameters, exceptionTypes, jcReturnType)
@@ -237,7 +234,7 @@ class JCTreeConverter(
treeMaker.Block(0, JavacList.nil())
} else if (isConstructor) {
// We already checked it in convertClass()
val declaration = origins[containingClass]?.descriptor as ClassDescriptor
val declaration = kaptContext.origins[containingClass]?.descriptor as ClassDescriptor
val superClass = declaration.getSuperClassOrAny()
val superClassConstructor = superClass.constructors.firstOrNull { it.visibility.isVisible(null, it, declaration) }
@@ -245,7 +242,7 @@ class JCTreeConverter(
val args = mapValues(superClassConstructor.valueParameters) { param ->
convertLiteralExpression(getDefaultValue(typeMapper.mapType(param.type)))
}
val call = treeMaker.Apply(JavacList.nil(), treeMaker.convertSimpleName("super"), args)
val call = treeMaker.Apply(JavacList.nil(), treeMaker.SimpleName("super"), args)
JavacList.of<JCStatement>(treeMaker.Exec(call))
} else {
JavacList.nil<JCStatement>()
@@ -301,9 +298,9 @@ class JCTreeConverter(
val fqName = annotationType.className
if (BLACKLISTED_ANNOTATATIONS.any { fqName.startsWith(it) }) return null
val name = treeMaker.convertType(annotationType)
val name = treeMaker.Type(annotationType)
val values = mapPairedValues<JCExpression>(annotation.values) { key, value ->
treeMaker.Assign(treeMaker.convertSimpleName(key), convertLiteralExpression(value))
treeMaker.Assign(treeMaker.SimpleName(key), convertLiteralExpression(value))
}
return treeMaker.Annotation(name, values)
}
@@ -326,11 +323,11 @@ class JCTreeConverter(
assert(value.size == 2)
val enumType = Type.getType(value[0] as String)
val valueName = value[1] as String
treeMaker.Select(treeMaker.convertType(enumType), treeMaker.name(valueName))
treeMaker.Select(treeMaker.Type(enumType), treeMaker.name(valueName))
}
is List<*> -> treeMaker.NewArray(null, JavacList.nil(), mapValues(value) { convertLiteralExpression(it) })
is List<*> -> treeMaker.NewArray(null, JavacList.nil(), org.jetbrains.kotlin.kapt3.mapValues(value) { convertLiteralExpression(it) })
is Type -> treeMaker.Select(treeMaker.convertType(value), treeMaker.name("class"))
is Type -> treeMaker.Select(treeMaker.Type(value), treeMaker.name("class"))
is AnnotationNode -> convertAnnotation(value) ?: error("Annotation is filtered out")
else -> throw IllegalArgumentException("Illegal literal expression value: $value (${value.javaClass.canonicalName})")
}
@@ -349,54 +346,5 @@ class JCTreeConverter(
}
}
private class ParameterInfo(
val flags: Long,
val name: String,
val type: Type,
val visibleAnnotations: List<AnnotationNode>?,
val invisibleAnnotations: List<AnnotationNode>?)
private fun MethodNode.getParametersInfo(containingClass: ClassNode): List<ParameterInfo> {
val localVariables = this.localVariables ?: emptyList()
val parameters = this.parameters ?: emptyList()
val isStatic = isStatic(access)
// First and second parameters in enum constructors are synthetic, we should ignore them
val isEnumConstructor = (name == "<init>") && containingClass.isEnum()
val startParameterIndex = if (isEnumConstructor) 2 else 0
val parameterTypes = Type.getArgumentTypes(desc)
val parameterInfos = ArrayList<ParameterInfo>(parameterTypes.size - startParameterIndex)
for (index in startParameterIndex..parameterTypes.lastIndex) {
val type = parameterTypes[index]
var name = parameters.getOrNull(index - startParameterIndex)?.name
?: localVariables.getOrNull(index + (if (isStatic) 0 else 1))?.name
?: "p${index - startParameterIndex}"
// Property setters has bad parameter names
if (name.startsWith("<") && name.endsWith(">")) {
name = "p${index - startParameterIndex}"
}
val visibleAnnotations = visibleParameterAnnotations?.get(index)
val invisibleAnnotations = invisibleParameterAnnotations?.get(index)
parameterInfos += ParameterInfo(0, name, type, visibleAnnotations, invisibleAnnotations)
}
return parameterInfos
}
private val ClassDescriptor.isNested: Boolean
get() = containingDeclaration is ClassDescriptor
private fun isEnum(access: Int) = (access and Opcodes.ACC_ENUM) > 0
private fun isPublic(access: Int) = (access and Opcodes.ACC_PUBLIC) > 0
private fun isSynthetic(access: Int) = (access and Opcodes.ACC_SYNTHETIC) > 0
private fun isFinal(access: Int) = (access and Opcodes.ACC_FINAL) > 0
private fun isStatic(access: Int) = (access and Opcodes.ACC_STATIC) > 0
private fun isAbstract(access: Int) = (access and Opcodes.ACC_ABSTRACT) > 0
private fun ClassNode.isEnum() = (access and Opcodes.ACC_ENUM) > 0
private fun ClassNode.isAnnotation() = (access and Opcodes.ACC_ANNOTATION) > 0
private fun MethodNode.isVarargs() = (access and Opcodes.ACC_VARARGS) > 0
private fun <T> List<T>?.isNullOrEmpty() = this == null || this.isEmpty()
get() = containingDeclaration is ClassDescriptor
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.kapt3
package org.jetbrains.kotlin.kapt3.stubs
import com.sun.tools.javac.code.BoundKind
import com.sun.tools.javac.code.TypeTag
@@ -22,7 +22,10 @@ import com.sun.tools.javac.tree.JCTree.*
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.signature.SignatureVisitor
import java.util.*
import org.jetbrains.kotlin.kapt3.ElementKind.*
import org.jetbrains.kotlin.kapt3.stubs.ElementKind.*
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.mapValues
import org.jetbrains.kotlin.kapt3.mapValuesIndexed
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.org.objectweb.asm.signature.SignatureReader
import com.sun.tools.javac.util.List as JavacList
@@ -76,35 +79,35 @@ import com.sun.tools.javac.util.List as JavacList
+ ClassType
*/
enum class ElementKind {
internal enum class ElementKind {
Root, TypeParameter, ClassBound, InterfaceBound, SuperClass, Interface, TypeArgument, ParameterType, ReturnType, ExceptionType,
ClassType, TypeVariable, PrimitiveType, ArrayType
}
class SignatureNode(val kind: ElementKind, val name: String? = null) {
private class SignatureNode(val kind: ElementKind, val name: String? = null) {
val children: MutableList<SignatureNode> = SmartList<SignatureNode>()
}
class SignatureParser(val treeMaker: KaptTreeMaker) {
class ClassGenericSignature(
val typeParameters: JavacList<JCTypeParameter>,
val typeParameters: com.sun.tools.javac.util.List<JCTypeParameter>,
val superClass: JCExpression,
val interfaces: JavacList<JCExpression>)
val interfaces: com.sun.tools.javac.util.List<JCExpression>)
class MethodGenericSignature(
val typeParameters: JavacList<JCTypeParameter>,
val parameterTypes: JavacList<JCVariableDecl>,
val exceptionTypes: JavacList<JCExpression>,
val typeParameters: com.sun.tools.javac.util.List<JCTypeParameter>,
val parameterTypes: com.sun.tools.javac.util.List<JCVariableDecl>,
val exceptionTypes: com.sun.tools.javac.util.List<JCExpression>,
val returnType: JCExpression?
)
fun parseClassSignature(
signature: String?,
rawSuperClass: JCExpression,
rawInterfaces: JavacList<JCExpression>
rawInterfaces: com.sun.tools.javac.util.List<JCExpression>
): ClassGenericSignature {
if (signature == null) {
return ClassGenericSignature(JavacList.nil(), rawSuperClass, rawInterfaces)
return ClassGenericSignature(com.sun.tools.javac.util.List.nil(), rawSuperClass, rawInterfaces)
}
val root = parse(signature)
@@ -121,12 +124,12 @@ class SignatureParser(val treeMaker: KaptTreeMaker) {
fun parseMethodSignature(
signature: String?,
rawParameters: JavacList<JCVariableDecl>,
rawExceptionTypes: JavacList<JCExpression>,
rawParameters: com.sun.tools.javac.util.List<JCVariableDecl>,
rawExceptionTypes: com.sun.tools.javac.util.List<JCExpression>,
rawReturnType: JCExpression?
): MethodGenericSignature {
if (signature == null) {
return MethodGenericSignature(JavacList.nil(), rawParameters, rawExceptionTypes, rawReturnType)
return MethodGenericSignature(com.sun.tools.javac.util.List.nil(), rawParameters, rawExceptionTypes, rawReturnType)
}
val root = parse(signature)
@@ -186,7 +189,7 @@ class SignatureParser(val treeMaker: KaptTreeMaker) {
ClassType -> {
val classFqName = node.name!!.replace('/', '.')
val args = node.children
val fqNameExpression = treeMaker.convertFqName(classFqName)
val fqNameExpression = treeMaker.FqName(classFqName)
if (args.isEmpty()) return fqNameExpression
treeMaker.TypeApply(fqNameExpression, mapValues(args) { arg ->
@@ -202,7 +205,7 @@ class SignatureParser(val treeMaker: KaptTreeMaker) {
}
})
}
TypeVariable -> treeMaker.convertSimpleName(node.name!!)
TypeVariable -> treeMaker.SimpleName(node.name!!)
ArrayType -> treeMaker.TypeArray(parseType(node.children.single()))
PrimitiveType -> {
val typeTag = when (node.name!!.single()) {
@@ -0,0 +1,62 @@
/*
* 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.kapt3.stubs
import org.jetbrains.kotlin.kapt3.util.isEnum
import org.jetbrains.kotlin.kapt3.util.isStatic
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.AnnotationNode
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import java.util.*
internal class ParameterInfo(
val flags: Long,
val name: String,
val type: Type,
val visibleAnnotations: List<AnnotationNode>?,
val invisibleAnnotations: List<AnnotationNode>?)
internal fun MethodNode.getParametersInfo(containingClass: ClassNode): List<ParameterInfo> {
val localVariables = this.localVariables ?: emptyList()
val parameters = this.parameters ?: emptyList()
val isStatic = isStatic(access)
// First and second parameters in enum constructors are synthetic, we should ignore them
val isEnumConstructor = (name == "<init>") && containingClass.isEnum()
val startParameterIndex = if (isEnumConstructor) 2 else 0
val parameterTypes = Type.getArgumentTypes(desc)
val parameterInfos = ArrayList<ParameterInfo>(parameterTypes.size - startParameterIndex)
for (index in startParameterIndex..parameterTypes.lastIndex) {
val type = parameterTypes[index]
var name = parameters.getOrNull(index - startParameterIndex)?.name
?: localVariables.getOrNull(index + (if (isStatic) 0 else 1))?.name
?: "p${index - startParameterIndex}"
// Property setters has bad parameter names
if (name.startsWith("<") && name.endsWith(">")) {
name = "p${index - startParameterIndex}"
}
val visibleAnnotations = visibleParameterAnnotations?.get(index)
val invisibleAnnotations = invisibleParameterAnnotations?.get(index)
parameterInfos += ParameterInfo(0, name, type, visibleAnnotations, invisibleAnnotations)
}
return parameterInfos
}
@@ -14,9 +14,9 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.kapt3
package org.jetbrains.kotlin.kapt3.util
class Logger(val isVerbose: Boolean) {
class KaptLogger(val isVerbose: Boolean) {
private companion object {
val PREFIX = "[kapt] "
}
@@ -0,0 +1,33 @@
/*
* 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.kapt3.util
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
internal fun isEnum(access: Int) = (access and Opcodes.ACC_ENUM) > 0
internal fun isPublic(access: Int) = (access and Opcodes.ACC_PUBLIC) > 0
internal fun isSynthetic(access: Int) = (access and Opcodes.ACC_SYNTHETIC) > 0
internal fun isFinal(access: Int) = (access and Opcodes.ACC_FINAL) > 0
internal fun isStatic(access: Int) = (access and Opcodes.ACC_STATIC) > 0
internal fun isAbstract(access: Int) = (access and Opcodes.ACC_ABSTRACT) > 0
internal fun ClassNode.isEnum() = (access and Opcodes.ACC_ENUM) > 0
internal fun ClassNode.isAnnotation() = (access and Opcodes.ACC_ANNOTATION) > 0
internal fun MethodNode.isVarargs() = (access and Opcodes.ACC_VARARGS) > 0
internal fun <T> List<T>?.isNullOrEmpty() = this == null || this.isEmpty()
@@ -15,6 +15,7 @@
*/
package org.jetbrains.kotlin.kapt3
import com.sun.tools.javac.util.List as JavacList
internal inline fun <T, R> mapValues(values: Iterable<T>?, f: (T) -> R?): JavacList<R> {
@@ -18,19 +18,16 @@ package org.jetbrains.kotlin.kapt3.test
import com.intellij.openapi.util.text.StringUtil
import com.sun.tools.javac.comp.CompileStates
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit
import org.jetbrains.kotlin.codegen.CodegenTestCase
import org.jetbrains.kotlin.codegen.CodegenTestUtil
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.kapt3.JCTreeConverter
import org.jetbrains.kotlin.kapt3.Kapt3BuilderFactory
import org.jetbrains.kotlin.kapt3.KaptRunner
import org.jetbrains.kotlin.kapt3.Logger
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndAddNewlineAtEOF
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import com.sun.tools.javac.util.List as JavacList
import java.io.File
import java.nio.file.Files
@@ -51,35 +48,29 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
val factory = CodegenTestUtil.generateFiles(myEnvironment, myFiles, classBuilderFactory)
val typeMapper = factory.generationState.typeMapper
val logger = Logger(isVerbose = true)
val kaptRunner = KaptRunner(logger)
val logger = KaptLogger(isVerbose = true)
val kaptContext = KaptContext(logger, classBuilderFactory.compiledClasses, classBuilderFactory.origins)
try {
check(kaptRunner, typeMapper, classBuilderFactory, txtFile)
check(kaptContext, typeMapper, txtFile)
} finally {
kaptRunner.close()
kaptContext.close()
}
}
protected fun convert(
kaptRunner: KaptRunner,
typeMapper: KotlinTypeMapper,
compiledClasses: List<ClassNode>,
origins: Map<Any, JvmDeclarationOrigin>
): JavacList<JCTree.JCCompilationUnit> {
val converter = JCTreeConverter(kaptRunner.context, typeMapper, compiledClasses, origins)
protected fun convert(kaptRunner: KaptContext, typeMapper: KotlinTypeMapper): JavacList<JCCompilationUnit> {
val converter = ClassFileToSourceStubConverter(kaptRunner, typeMapper)
return converter.convert()
}
protected abstract fun check(
kaptRunner: KaptRunner,
kaptRunner: KaptContext,
typeMapper: KotlinTypeMapper,
classBuilderFactory: Kapt3BuilderFactory,
txtFile: File)
}
abstract class AbstractJCTreeConverterTest : AbstractKotlinKapt3Test() {
override fun check(kaptRunner: KaptRunner, typeMapper: KotlinTypeMapper, classBuilderFactory: Kapt3BuilderFactory, txtFile: File) {
val javaFiles = convert(kaptRunner, typeMapper, classBuilderFactory.compiledClasses, classBuilderFactory.origins)
abstract class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test() {
override fun check(kaptRunner: KaptContext, typeMapper: KotlinTypeMapper, txtFile: File) {
val javaFiles = convert(kaptRunner, typeMapper)
kaptRunner.compiler.enterTrees(javaFiles)
val actualRaw = javaFiles.joinToString (FILE_SEPARATOR)
@@ -93,8 +84,8 @@ abstract class AbstractJCTreeConverterTest : AbstractKotlinKapt3Test() {
}
abstract class AbstractKotlinKaptRunnerTest : AbstractKotlinKapt3Test() {
override fun check(kaptRunner: KaptRunner, typeMapper: KotlinTypeMapper, classBuilderFactory: Kapt3BuilderFactory, txtFile: File) {
val compilationUnits = convert(kaptRunner, typeMapper, classBuilderFactory.compiledClasses, classBuilderFactory.origins)
override fun check(kaptRunner: KaptContext, typeMapper: KotlinTypeMapper, txtFile: File) {
val compilationUnits = convert(kaptRunner, typeMapper)
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile()
try {
kaptRunner.doAnnotationProcessing(emptyList(), listOf(KaptRunnerTest.simpleProcessor()),
@@ -30,7 +30,7 @@ import java.util.regex.Pattern;
@TestMetadata("plugins/kapt3/testData/converter")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class JCTreeConverterTestGenerated extends AbstractJCTreeConverterTest {
public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFileToSourceStubConverterTest {
public void testAllFilesPresentInConverter() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/kapt3/testData/converter"), Pattern.compile("^(.+)\\.kt$"), true);
}
@@ -16,14 +16,16 @@
package org.jetbrains.kotlin.kapt3.test
import org.jetbrains.kotlin.kapt3.KaptError
import org.jetbrains.kotlin.kapt3.KaptRunner
import org.jetbrains.kotlin.kapt3.Logger
import org.jetbrains.kotlin.kapt3.KaptContext
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import org.jetbrains.kotlin.kapt3.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.junit.Assert.*
import org.junit.Test
import java.io.File
import java.nio.file.Files
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.TypeElement
@@ -72,16 +74,20 @@ class KaptRunnerTest {
}
}
private fun doAnnotationProcessing(javaSourceFile: File, processor: Processor, outputDir: File) {
KaptContext(KaptLogger(isVerbose = true), emptyList(), emptyMap()).doAnnotationProcessing(
listOf(javaSourceFile),
listOf(processor),
emptyList(), // classpath
outputDir,
outputDir)
}
@Test
fun testSimple() {
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile()
try {
KaptRunner(Logger(isVerbose = true)).doAnnotationProcessing(
listOf(File(TEST_DATA_DIR, "Simple.java")),
listOf(simpleProcessor()),
emptyList(), // classpath
sourceOutputDir,
sourceOutputDir)
doAnnotationProcessing(File(TEST_DATA_DIR, "Simple.java"), simpleProcessor(), sourceOutputDir)
val myMethodFile = File(sourceOutputDir, "generated/MyMethodMyAnnotation.java")
assertTrue(myMethodFile.exists())
} finally {
@@ -102,12 +108,7 @@ class KaptRunnerTest {
}
try {
KaptRunner(Logger(isVerbose = true)).doAnnotationProcessing(
listOf(File(TEST_DATA_DIR, "Simple.java")),
listOf(processor),
emptyList(),
TEST_DATA_DIR,
TEST_DATA_DIR)
doAnnotationProcessing(File(TEST_DATA_DIR, "Simple.java"), processor, TEST_DATA_DIR)
} catch (e: KaptError) {
assertEquals(KaptError.Kind.EXCEPTION, e.kind)
assertEquals("Here we are!", e.cause!!.message)
@@ -116,15 +117,14 @@ class KaptRunnerTest {
@Test
fun testParsingError() {
var catched = false
try {
KaptRunner(Logger(isVerbose = true)).doAnnotationProcessing(
listOf(File(TEST_DATA_DIR, "ParseError.java")),
listOf(simpleProcessor()),
emptyList(),
TEST_DATA_DIR,
TEST_DATA_DIR)
doAnnotationProcessing(File(TEST_DATA_DIR, "ParseError.java"), simpleProcessor(), TEST_DATA_DIR)
} catch (e: KaptError) {
assertEquals(KaptError.Kind.JAVA_FILE_PARSING_ERROR, e.kind)
catched = true
}
assertTrue("Exception was not catched", catched)
}
}