Kapt: Extract annotation processing running logic from the compiler plugin

This commit is contained in:
Yan Zhulanow
2018-05-14 16:37:05 +03:00
parent 3ecf355e7a
commit 2bc45e0484
43 changed files with 843 additions and 577 deletions
@@ -17,16 +17,20 @@ dependencies {
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:plugin-api"))
compileOnly(project(":kotlin-annotation-processing-base"))
compileOnly(project(":kotlin-annotation-processing-runtime"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeJars("asm-all") }
testCompile(project(":compiler:tests-common"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(project(":kotlin-annotation-processing-base"))
testCompile(projectTests(":kotlin-annotation-processing-base"))
testCompile(commonDep("junit:junit"))
testCompile(project(":kotlin-annotation-processing-runtime"))
embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false }
embeddedComponents(project(":kotlin-annotation-processing-base")) { isTransitive = false }
}
sourceSets {
@@ -17,16 +17,20 @@ dependencies {
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:plugin-api"))
compileOnly(project(":kotlin-annotation-processing-base"))
compileOnly(project(":kotlin-annotation-processing-runtime"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeJars("asm-all") }
testCompile(project(":compiler:tests-common"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(project(":kotlin-annotation-processing-base"))
testCompile(projectTests(":kotlin-annotation-processing-base"))
testCompile(commonDep("junit:junit"))
testCompile(project(":kotlin-annotation-processing-runtime"))
embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false }
embeddedComponents(project(":kotlin-annotation-processing-base")) { isTransitive = false }
}
sourceSets {
@@ -17,16 +17,20 @@ dependencies {
compile(project(":compiler:frontend"))
compile(project(":compiler:frontend.java"))
compile(project(":compiler:plugin-api"))
compileOnly(project(":kotlin-annotation-processing-base"))
compileOnly(project(":kotlin-annotation-processing-runtime"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeJars("asm-all") }
testCompile(project(":compiler:tests-common"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(project(":kotlin-annotation-processing-base"))
testCompile(projectTests(":kotlin-annotation-processing-base"))
testCompile(commonDep("junit:junit"))
testCompile(project(":kotlin-annotation-processing-runtime"))
embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false }
embeddedComponents(project(":kotlin-annotation-processing-base")) { isTransitive = false }
}
sourceSets {
@@ -16,14 +16,12 @@
package org.jetbrains.kotlin.kapt3
import com.intellij.ide.ClassUtilCore
import com.intellij.openapi.project.Project
import com.sun.tools.javac.code.Flags
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.Pretty
import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.Convert
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.backend.common.output.OutputFile
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.OUTPUT
@@ -39,39 +37,29 @@ import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.kapt3.AptMode.*
import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.base.ProcessorLoader
import org.jetbrains.kotlin.kapt3.base.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.base.util.KaptBaseError
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter.KaptStub
import org.jetbrains.kotlin.kapt3.stubs.KaptLineMappingCollector.Companion.KAPT_METADATA_EXTENSION
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.kapt3.util.getPackageNameJava9Aware
import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation.Companion.KAPT_METADATA_EXTENSION
import org.jetbrains.kotlin.kapt3.base.util.getPackageNameJava9Aware
import org.jetbrains.kotlin.kapt3.base.util.info
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger
import org.jetbrains.kotlin.modules.TargetId
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.PartialAnalysisHandlerExtension
import java.io.File
import java.io.IOException
import java.io.StringWriter
import java.io.Writer
import java.net.URLClassLoader
import java.util.*
import javax.annotation.processing.Processor
import com.sun.tools.javac.util.List as JavacList
class KaptPaths(
compileClasspath: List<File>,
annotationProcessingClasspath: List<File>,
val javaSourceRoots: List<File>,
val sourcesOutputDir: File,
val classFilesOutputDir: File,
val stubsOutputDir: File,
val incrementalDataOutputDir: File?
) {
val compileClasspath = compileClasspath.distinct()
val annotationProcessingClasspath = annotationProcessingClasspath.distinct()
}
class ClasspathBasedKapt3Extension(
paths: KaptPaths,
options: Map<String, String>,
@@ -82,14 +70,18 @@ class ClasspathBasedKapt3Extension(
correctErrorTypes: Boolean,
mapDiagnosticLocations: Boolean,
pluginInitializedTime: Long,
logger: KaptLogger,
logger: MessageCollectorBackedKaptLogger,
compilerConfiguration: CompilerConfiguration
) : AbstractKapt3Extension(paths, options, javacOptions, annotationProcessorFqNames,
aptMode, pluginInitializedTime, logger, correctErrorTypes, mapDiagnosticLocations, compilerConfiguration) {
override val analyzePartially: Boolean
get() = useLightAnalysis
private var annotationProcessingClassLoader: URLClassLoader? = null
private var processorLoader: ProcessorLoader? = null
override fun loadProcessors(): List<Processor> {
return ProcessorLoader(paths, annotationProcessorFqNames, logger).also { this.processorLoader = it }.loadProcessors()
}
override fun analysisCompleted(
project: Project,
@@ -100,54 +92,7 @@ class ClasspathBasedKapt3Extension(
try {
return super.analysisCompleted(project, module, bindingTrace, files)
} finally {
annotationProcessingClassLoader?.close()
ClassUtilCore.clearJarURLCache()
}
}
override fun loadProcessors(): List<Processor> {
ClassUtilCore.clearJarURLCache()
val classpath = (paths.annotationProcessingClasspath + paths.compileClasspath).distinct()
val classLoader = URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray())
this.annotationProcessingClassLoader = classLoader
val processors = if (annotationProcessorFqNames.isNotEmpty()) {
logger.info("Annotation processor class names are set, skip AP discovery")
annotationProcessorFqNames.mapNotNull { tryLoadProcessor(it, classLoader) }
} else {
logger.info("Need to discovery annotation processors in the AP classpath")
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::class.java.canonicalName } }
}
return processors
}
private fun tryLoadProcessor(fqName: String, classLoader: ClassLoader): Processor? {
val annotationProcessorClass = try {
Class.forName(fqName, true, classLoader)
} catch (e: Throwable) {
logger.warn("Can't find annotation processor class $fqName: ${e.message}")
return null
}
try {
val annotationProcessorInstance = annotationProcessorClass.newInstance()
if (annotationProcessorInstance !is Processor) {
logger.warn("$fqName is not an instance of 'Processor'")
return null
}
return annotationProcessorInstance
} catch (e: Throwable) {
logger.warn("Can't load annotation processor class $fqName: ${e.message}")
return null
processorLoader?.close()
}
}
}
@@ -159,7 +104,7 @@ abstract class AbstractKapt3Extension(
val annotationProcessorFqNames: List<String>,
val aptMode: AptMode,
val pluginInitializedTime: Long,
val logger: KaptLogger,
val logger: MessageCollectorBackedKaptLogger,
val correctErrorTypes: Boolean,
val mapDiagnosticLocations: Boolean,
val compilerConfiguration: CompilerConfiguration
@@ -205,9 +150,7 @@ abstract class AbstractKapt3Extension(
val kaptContext = generateStubs(project, module, bindingTrace.bindingContext, files)
try {
runAnnotationProcessing(kaptContext, processors)
} catch (error: KaptError) {
fun handleKaptError(error: KaptError): AnalysisResult {
val cause = error.cause
if (cause != null) {
@@ -215,6 +158,20 @@ abstract class AbstractKapt3Extension(
}
return AnalysisResult.compilationError(bindingTrace.bindingContext)
}
try {
runAnnotationProcessing(kaptContext, processors)
} catch (error: KaptBaseError) {
val kind = when (error.kind) {
KaptBaseError.Kind.EXCEPTION -> KaptError.Kind.EXCEPTION
KaptBaseError.Kind.ERROR_RAISED -> KaptError.Kind.ERROR_RAISED
}
val cause = error.cause
return handleKaptError(if (cause != null) KaptError(kind, cause) else KaptError(kind))
} catch (error: KaptError) {
return handleKaptError(error)
} catch (thr: Throwable) {
return AnalysisResult.internalError(bindingTrace.bindingContext, thr)
} finally {
@@ -232,10 +189,14 @@ abstract class AbstractKapt3Extension(
}
}
private fun generateStubs(project: Project, module: ModuleDescriptor, context: BindingContext, files: Collection<KtFile>): KaptContext<*> {
private fun generateStubs(
project: Project,
module: ModuleDescriptor,
context: BindingContext,
files: Collection<KtFile>
): KaptContext {
if (!aptMode.generateStubs) {
return KaptContext(paths, false, aptMode, logger, project, BindingContext.EMPTY, emptyList(), emptyMap(), null,
mapDiagnosticLocations, options, javacOptions)
return KaptContext(paths, false, logger, mapDiagnosticLocations, options, javacOptions)
}
logger.info { "Kotlin files to compile: " + files.map { it.virtualFile?.name ?: "<in memory ${it.hashCode()}>" } }
@@ -245,10 +206,11 @@ abstract class AbstractKapt3Extension(
}
}
private fun runAnnotationProcessing(kaptContext: KaptContext<*>, processors: List<Processor>) {
private fun runAnnotationProcessing(kaptContext: KaptContext, processors: List<Processor>) {
if (!aptMode.runAnnotationProcessing) return
val javaSourceFiles = collectJavaSourceFiles()
val javaSourceFiles = paths.collectJavaSourceFiles()
logger.info { "Java source files: " + javaSourceFiles.joinToString { it.canonicalPath } }
val (annotationProcessingTime) = measureTimeMillis {
kaptContext.doAnnotationProcessing(javaSourceFiles, processors)
@@ -262,7 +224,7 @@ abstract class AbstractKapt3Extension(
module: ModuleDescriptor,
bindingContext: BindingContext,
files: List<KtFile>
): KaptContext<GenerationState> {
): KaptContextForStubGeneration {
val builderFactory = Kapt3BuilderFactory()
val targetId = TargetId(
@@ -288,11 +250,13 @@ abstract class AbstractKapt3Extension(
logger.info { "Stubs compilation took $classFilesCompilationTime ms" }
logger.info { "Compiled classes: " + compiledClasses.joinToString { it.name } }
return KaptContext(paths, false, aptMode, logger, project, bindingContext, compiledClasses, origins, generationState,
mapDiagnosticLocations, options, javacOptions)
return KaptContextForStubGeneration(
paths, false, logger, project, bindingContext, compiledClasses, origins, generationState,
mapDiagnosticLocations, options, javacOptions
)
}
private fun generateKotlinSourceStubs(kaptContext: KaptContext<GenerationState>) {
private fun generateKotlinSourceStubs(kaptContext: KaptContextForStubGeneration) {
val converter = ClassFileToSourceStubConverter(kaptContext, generateNonExistentClass = true, correctErrorTypes = correctErrorTypes)
val (stubGenerationTime, kaptStubs) = measureTimeMillis {
@@ -306,16 +270,7 @@ abstract class AbstractKapt3Extension(
saveIncrementalData(kaptContext, logger.messageCollector, converter)
}
private fun collectJavaSourceFiles(): List<File> {
val javaFilesFromJavaSourceRoots = (paths.javaSourceRoots + paths.stubsOutputDir).flatMap {
root -> root.walk().filter { it.isFile && it.extension == "java" }.toList()
}
logger.info { "Java source files: " + javaFilesFromJavaSourceRoots.joinToString { it.canonicalPath } }
return javaFilesFromJavaSourceRoots
}
protected open fun saveStubs(kaptContext: KaptContext<*>, stubs: List<KaptStub>) {
protected open fun saveStubs(kaptContext: KaptContext, stubs: List<KaptStub>) {
for (kaptStub in stubs) {
val stub = kaptStub.file
val className = (stub.defs.first { it is JCTree.JCClassDecl } as JCTree.JCClassDecl).simpleName.toString()
@@ -332,7 +287,7 @@ abstract class AbstractKapt3Extension(
}
protected open fun saveIncrementalData(
kaptContext: KaptContext<GenerationState>,
kaptContext: KaptContextForStubGeneration,
messageCollector: MessageCollector,
converter: ClassFileToSourceStubConverter) {
val incrementalDataOutputDir = paths.incrementalDataOutputDir ?: return
@@ -52,7 +52,10 @@ import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.SOURCE_OUT
import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.STUBS_OUTPUT_DIR_OPTION
import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.USE_LIGHT_ANALYSIS_OPTION
import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.VERBOSE_MODE_OPTION
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.kapt3.base.Kapt
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.base.log
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
@@ -195,10 +198,6 @@ class Kapt3CommandLineProcessor : CommandLineProcessor {
}
class Kapt3ComponentRegistrar : ComponentRegistrar {
private companion object {
private const val JAVAC_CONTEXT_CLASS = "com.sun.tools.javac.util.Context"
}
private fun decodeList(options: String): Map<String, String> {
val map = LinkedHashMap<String, String>()
@@ -224,14 +223,11 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
val isVerbose = configuration.get(Kapt3ConfigurationKeys.VERBOSE_MODE) == "true"
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
?: PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, isVerbose)
val logger = KaptLogger(isVerbose, messageCollector)
val logger = MessageCollectorBackedKaptLogger(isVerbose, messageCollector)
fun abortAnalysis() = AnalysisHandlerExtension.registerExtension(project, AbortAnalysisHandlerExtension())
try {
Class.forName(JAVAC_CONTEXT_CLASS)
} catch (e: ClassNotFoundException) {
logger.error("'$JAVAC_CONTEXT_CLASS' class can't be found ('tools.jar' is absent in the plugin classpath). Kapt won't work.")
if (!Kapt.checkJavacComponentsAccess(logger)) {
abortAnalysis()
return
}
@@ -284,28 +280,24 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
val correctErrorTypes = configuration.get(Kapt3ConfigurationKeys.CORRECT_ERROR_TYPES) == "true"
val mapDiagnosticLocations = configuration.get(Kapt3ConfigurationKeys.MAP_DIAGNOSTIC_LOCATIONS) == "true"
val paths = KaptPaths(
project.basePath?.let(::File),
compileClasspath, apClasspath, javaSourceRoots, sourcesOutputDir, classFilesOutputDir,
stubsOutputDir, incrementalDataOutputDir
)
if (isVerbose) {
logger.info("Kapt3 is enabled.")
logger.info("Annotation processing mode: $aptMode")
logger.info("Use light analysis: $useLightAnalysis")
logger.info("Correct error types: $correctErrorTypes")
logger.info("Map diagnostic locations: $mapDiagnosticLocations")
logger.info("Source output directory: $sourcesOutputDir")
logger.info("Classes output directory: $classFilesOutputDir")
logger.info("Stubs output directory: $stubsOutputDir")
logger.info("Incremental data output directory: $incrementalDataOutputDir")
logger.info("Compile classpath: " + compileClasspath.joinToString())
logger.info("Annotation processing classpath: " + apClasspath.joinToString())
paths.log(logger)
logger.info("Annotation processors: " + annotationProcessors.joinToString())
logger.info("Java source roots: " + javaSourceRoots.joinToString())
logger.info("Options: $apOptions")
logger.info("Javac options: $apOptions")
logger.info("AP options: $apOptions")
}
val paths = KaptPaths(
compileClasspath, apClasspath, javaSourceRoots, sourcesOutputDir, classFilesOutputDir,
stubsOutputDir, incrementalDataOutputDir
)
val kapt3AnalysisCompletedHandlerExtension = ClasspathBasedKapt3Extension(
paths, apOptions, javacCliOptions, annotationProcessors,
aptMode, useLightAnalysis, correctErrorTypes, mapDiagnosticLocations, System.currentTimeMillis(), logger, configuration
@@ -1,132 +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.jvm.ClassReader
import com.sun.tools.javac.main.JavaCompiler
import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.main.Option
import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.Options
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.kapt3.javac.KaptJavaCompiler
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileManager
import org.jetbrains.kotlin.kapt3.javac.KaptJavaLog
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
import org.jetbrains.kotlin.kapt3.util.putJavacOption
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import java.io.File
import javax.tools.JavaFileManager
class KaptContext<out GState : GenerationState?>(
val paths: KaptPaths,
private val withJdk: Boolean,
aptMode: AptMode,
val logger: KaptLogger,
val project: Project,
val bindingContext: BindingContext,
val compiledClasses: List<ClassNode>,
val origins: Map<Any, JvmDeclarationOrigin>,
val generationState: GState,
mapDiagnosticLocations: Boolean,
processorOptions: Map<String, String>,
javacOptions: Map<String, String> = emptyMap()
) : AutoCloseable {
val context = Context()
val compiler: KaptJavaCompiler
val fileManager: KaptJavaFileManager
val options: Options
val javaLog: KaptJavaLog
private val treeMaker: TreeMaker
init {
KaptJavaLog.preRegister(this, logger.messageCollector, mapDiagnosticLocations)
KaptJavaFileManager.preRegister(context)
if (aptMode != AptMode.APT_ONLY) {
KaptTreeMaker.preRegister(context, this)
}
KaptJavaCompiler.preRegister(context)
options = Options.instance(context).apply {
for ((key, value) in processorOptions) {
val option = if (value.isEmpty()) "-A$key" else "-A$key=$value"
put(option, option) // key == value: it's intentional
}
for ((key, value) in javacOptions) {
if (value.isNotEmpty()) {
put(key, value)
} else {
put(key, key)
}
}
put(Option.PROC, "only") // Only process annotations
if (!withJdk) {
putJavacOption("BOOTCLASSPATH", "BOOT_CLASS_PATH", "") // No boot classpath
}
if (isJava9OrLater) {
put("accessInternalAPI", "true")
}
putJavacOption("CLASSPATH", "CLASS_PATH",
paths.compileClasspath.joinToString(File.pathSeparator) { it.canonicalPath })
putJavacOption("PROCESSORPATH", "PROCESSOR_PATH",
paths.annotationProcessingClasspath.joinToString(File.pathSeparator) { it.canonicalPath })
put(Option.S, paths.sourcesOutputDir.canonicalPath)
put(Option.D, paths.classFilesOutputDir.canonicalPath)
put(Option.ENCODING, "UTF-8")
}
if (logger.isVerbose) {
logger.info("Javac options: " + options.keySet().keysToMap { key -> options[key] ?: "" })
}
fileManager = context.get(JavaFileManager::class.java) as KaptJavaFileManager
if (isJava9OrLater) {
for (option in Option.getJavacFileManagerOptions()) {
val value = options.get(option) ?: continue
fileManager.handleOptionJavac9(option, value)
}
}
compiler = JavaCompiler.instance(context) as KaptJavaCompiler
compiler.keepComments = true
ClassReader.instance(context).saveParameterNames = true
javaLog = compiler.log as KaptJavaLog
treeMaker = TreeMaker.instance(context)
}
override fun close() {
(treeMaker as? KaptTreeMaker)?.dispose()
compiler.close()
fileManager.close()
generationState?.destroy()
}
}
@@ -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.intellij.openapi.project.Project
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.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.tree.ClassNode
class KaptContextForStubGeneration(
paths: KaptPaths,
withJdk: Boolean,
logger: KaptLogger,
val project: Project,
val bindingContext: BindingContext,
val compiledClasses: List<ClassNode>,
val origins: Map<Any, JvmDeclarationOrigin>,
val generationState: GenerationState,
mapDiagnosticLocations: Boolean,
processorOptions: Map<String, String>,
javacOptions: Map<String, String> = emptyMap()
) : KaptContext(paths, withJdk, logger, mapDiagnosticLocations, processorOptions, javacOptions) {
private val treeMaker = TreeMaker.instance(context)
override fun preregisterTreeMaker(context: Context) {
KaptTreeMaker.preRegister(context, this)
}
override fun close() {
(treeMaker as? KaptTreeMaker)?.dispose()
generationState.destroy()
super.close()
}
}
@@ -1,140 +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.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 org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
import org.jetbrains.kotlin.kapt3.util.putJavacOption
import org.jetbrains.kotlin.utils.addToStdlib.measureTimeMillisWithResult
import java.io.File
import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.TypeElement
import javax.tools.JavaFileObject
import com.sun.tools.javac.util.List as JavacList
fun KaptContext<*>.doAnnotationProcessing(
javaSourceFiles: List<File>,
processors: List<Processor>,
additionalSources: JavacList<JCTree.JCCompilationUnit> = JavacList.nil()
) {
val processingEnvironment = JavacProcessingEnvironment.instance(context)
val wrappedProcessors = processors.map { ProcessorWrapper(it) }
val compilerAfterAP: JavaCompiler
try {
if (isJava9OrLater) {
val initProcessAnnotationsMethod = JavaCompiler::class.java.declaredMethods.single { it.name == "initProcessAnnotations" }
initProcessAnnotationsMethod.invoke(compiler, wrappedProcessors, emptyList<JavaFileObject>(), emptyList<String>())
}
else {
compiler.initProcessAnnotations(wrappedProcessors)
}
val parsedJavaFiles = parseJavaFiles(javaSourceFiles)
compilerAfterAP = try {
javaLog.interceptorData.files = parsedJavaFiles.map { it.sourceFile to it }.toMap()
val analyzedFiles = compiler.stopIfErrorOccurred(
CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources))
if (isJava9OrLater) {
val processAnnotationsMethod = compiler.javaClass.getMethod("processAnnotations", JavacList::class.java)
processAnnotationsMethod.invoke(compiler, analyzedFiles)
compiler
}
else {
compiler.processAnnotations(analyzedFiles)
}
} catch (e: AnnotationProcessingError) {
throw KaptError(KaptError.Kind.EXCEPTION, e.cause ?: e)
}
val log = compilerAfterAP.log
val filer = processingEnvironment.filer as JavacFiler
val errorCount = log.nerrors
val warningCount = log.nwarnings
if (logger.isVerbose) {
logger.info("Annotation processing complete, errors: $errorCount, warnings: $warningCount")
logger.info("Annotation processor stats:")
wrappedProcessors.forEach { processor ->
val rounds = processor.rounds
val roundMs = rounds.joinToString { "$it ms" }
val totalMs = rounds.sum()
logger.info("${processor.name}: ${rounds.size} rounds ($roundMs), $totalMs ms in total")
}
filer.displayState()
}
if (log.nerrors > 0) {
throw KaptError(KaptError.Kind.ERROR_RAISED)
}
} finally {
processingEnvironment.close()
this@doAnnotationProcessing.close()
}
}
private class ProcessorWrapper(private val delegate: Processor) : Processor by delegate {
val rounds = mutableListOf<Long>()
val name: String
get() = delegate.javaClass.simpleName
override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment?): Boolean {
val (time, result) = measureTimeMillisWithResult {
delegate.process(annotations, roundEnv)
}
rounds += time
return result
}
}
internal fun KaptContext<*>.parseJavaFiles(javaSourceFiles: List<File>): JavacList<JCTree.JCCompilationUnit> {
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
return compiler.stopIfErrorOccurred(CompileState.PARSE,
initModulesIfNeeded(
compiler.stopIfErrorOccurred(CompileState.PARSE,
compiler.parseFiles(javaFileObjects))))
}
private fun KaptContext<*>.initModulesIfNeeded(files: JavacList<JCTree.JCCompilationUnit>): JavacList<JCTree.JCCompilationUnit> {
if (isJava9OrLater) {
val initModulesMethod = compiler.javaClass.getMethod("initModules", JavacList::class.java)
@Suppress("UNCHECKED_CAST")
return compiler.stopIfErrorOccurred(
CompileState.PARSE,
initModulesMethod.invoke(compiler, files) as JavacList<JCTree.JCCompilationUnit>)
}
return files
}
@@ -1,36 +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.javac
import com.sun.tools.javac.comp.CompileStates
import com.sun.tools.javac.main.JavaCompiler
import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.List as JavacList
class KaptJavaCompiler(context: Context) : JavaCompiler(context) {
public override fun shouldStop(cs: CompileStates.CompileState) = super.shouldStop(cs)
fun <T> stopIfErrorOccurred(cs: CompileStates.CompileState, list: JavacList<T>): JavacList<T> {
return if (shouldStop(cs)) JavacList.nil<T>() else list
}
companion object {
internal fun preRegister(context: Context) {
context.put(compilerKey, Context.Factory<JavaCompiler>(::KaptJavaCompiler))
}
}
}
@@ -1,21 +0,0 @@
package org.jetbrains.kotlin.kapt3.javac
import com.sun.tools.javac.file.JavacFileManager
import com.sun.tools.javac.main.Option
import com.sun.tools.javac.util.Context
import javax.tools.JavaFileManager
class KaptJavaFileManager(context: Context) : JavacFileManager(context, true, null) {
fun handleOptionJavac9(option: Option, value: String) {
val handleOptionMethod = JavacFileManager::class.java
.getMethod("handleOption", Option::class.java, String::class.java)
handleOptionMethod.invoke(this, option, value)
}
companion object {
internal fun preRegister(context: Context) {
context.put(JavaFileManager::class.java, Context.Factory<JavaFileManager> { KaptJavaFileManager(it) })
}
}
}
@@ -17,10 +17,9 @@
package org.jetbrains.kotlin.kapt3.javac
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.kapt3.util.getPackageNameJava9Aware
import org.jetbrains.kotlin.kapt3.base.util.getPackageNameJava9Aware
import java.io.File
import java.net.URI
import java.net.URL
import javax.lang.model.element.Modifier
import javax.lang.model.element.NestingKind
import javax.tools.JavaFileObject
@@ -1,291 +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.javac
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.util.*
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.kapt3.KaptContext
import org.jetbrains.kotlin.kapt3.stubs.KaptStubLineInformation
import org.jetbrains.kotlin.kapt3.stubs.KotlinPosition
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedWriter
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
import java.io.*
import javax.tools.Diagnostic
import javax.tools.JavaFileObject
import javax.tools.JavaFileObject.Kind
import javax.tools.SimpleJavaFileObject
import com.sun.tools.javac.util.List as JavacList
class KaptJavaLog(
private val projectBasePath: String?,
context: Context,
errWriter: PrintWriter,
warnWriter: PrintWriter,
noticeWriter: PrintWriter,
val interceptorData: DiagnosticInterceptorData,
val mapDiagnosticLocations: Boolean
) : Log(context, errWriter, warnWriter, noticeWriter) {
private val stubLineInfo = KaptStubLineInformation()
private val javacMessages = JavacMessages.instance(context)
init {
context.put(Log.outKey, noticeWriter)
}
val reportedDiagnostics: List<JCDiagnostic>
get() = _reportedDiagnostics
private val _reportedDiagnostics = mutableListOf<JCDiagnostic>()
override fun flush(kind: WriterKind?) {
super.flush(kind)
val diagnosticKind = when (kind) {
WriterKind.ERROR -> JCDiagnostic.DiagnosticType.ERROR
WriterKind.WARNING -> JCDiagnostic.DiagnosticType.WARNING
WriterKind.NOTICE -> JCDiagnostic.DiagnosticType.NOTE
else -> return
}
_reportedDiagnostics.removeAll { it.type == diagnosticKind }
}
override fun flush() {
super.flush()
_reportedDiagnostics.clear()
}
override fun report(diagnostic: JCDiagnostic) {
if (diagnostic.type == JCDiagnostic.DiagnosticType.ERROR && diagnostic.code in IGNORED_DIAGNOSTICS) {
return
}
if (diagnostic.type == JCDiagnostic.DiagnosticType.WARNING
&& diagnostic.code == "compiler.warn.proc.unmatched.processor.options"
&& diagnostic.args.singleOrNull() == "[kapt.kotlin.generated]"
) {
// Do not report the warning about "kapt.kotlin.generated" option being ignored if it's the only ignored option
return
}
val targetElement = diagnostic.diagnosticPosition
val sourceFile = interceptorData.files[diagnostic.source]
if (diagnostic.code.contains("err.cant.resolve") && targetElement != null) {
if (sourceFile != null) {
val insideImports = targetElement.tree in sourceFile.imports
// Ignore resolve errors in import statements
if (insideImports) return
}
}
if (mapDiagnosticLocations && sourceFile != null && targetElement.tree != null) {
val kotlinPosition = stubLineInfo.getPositionInKotlinFile(sourceFile, targetElement.tree)
val kotlinFile = kotlinPosition?.let { getKotlinSourceFile(it) }
if (kotlinPosition != null && kotlinFile != null) {
val flags = JCDiagnostic.DiagnosticFlag.values().filterTo(mutableSetOf(), diagnostic::isFlagSet)
val kotlinDiagnostic = diags.create(
diagnostic.type,
diagnostic.lintCategory,
flags,
DiagnosticSource(KotlinFileObject(kotlinFile), this),
JCDiagnostic.SimpleDiagnosticPosition(kotlinPosition.pos),
diagnostic.code.stripCompilerKeyPrefix(),
*diagnostic.args
)
reportDiagnostic(kotlinDiagnostic)
// Avoid reporting the diagnostic twice
return
}
}
reportDiagnostic(diagnostic)
}
private fun String.stripCompilerKeyPrefix(): String {
for (kind in listOf("err", "warn", "misc", "note")) {
val prefix = "compiler.$kind."
if (startsWith(prefix)) {
return drop(prefix.length)
}
}
return this
}
private fun reportDiagnostic(diagnostic: JCDiagnostic) {
if (diagnostic.kind == Diagnostic.Kind.ERROR) {
val oldErrors = nerrors
super.report(diagnostic)
if (nerrors > oldErrors) {
_reportedDiagnostics += diagnostic
}
}
else if (diagnostic.kind == Diagnostic.Kind.WARNING) {
val oldWarnings = nwarnings
super.report(diagnostic)
if (nwarnings > oldWarnings) {
_reportedDiagnostics += diagnostic
}
}
else {
super.report(diagnostic)
}
}
override fun writeDiagnostic(diagnostic: JCDiagnostic) {
if (hasDiagnosticListener()) {
diagListener.report(diagnostic)
return
}
val writer = when (diagnostic.type) {
DiagnosticType.FRAGMENT, null -> kotlin.error("Invalid root diagnostic type: ${diagnostic.type}")
DiagnosticType.NOTE -> super.getWriter(WriterKind.NOTICE)
DiagnosticType.WARNING -> super.getWriter(WriterKind.WARNING)
DiagnosticType.ERROR -> super.getWriter(WriterKind.ERROR)
}
val formattedMessage = diagnosticFormatter.format(diagnostic, javacMessages.currentLocale)
.lines()
.joinToString(LINE_SEPARATOR) { original ->
// Kotlin location is put as a sub-diagnostic, so the formatter indents it with four additional spaces (6 in total).
// It looks weird, especially in the build log inside IntelliJ, so let's make things a bit better.
val trimmed = original.trimStart()
// Typically, javac places additional details about the diagnostics indented by two spaces
if (trimmed.startsWith(KOTLIN_LOCATION_PREFIX)) " " + trimmed else original
}
writer.print(formattedMessage)
writer.flush()
}
private fun getKotlinSourceFile(pos: KotlinPosition): File? {
return if (pos.isRelativePath) {
val basePath = this.projectBasePath
if (basePath != null) File(basePath, pos.path) else null
}
else {
File(pos.path)
}
}
private operator fun <T : JCTree> Iterable<T>.contains(element: JCTree?): Boolean {
if (element == null) {
return false
}
var found = false
val visitor = object : JCTree.Visitor() {
override fun visitImport(that: JCTree.JCImport) {
super.visitImport(that)
if (!found) that.qualid.accept(this)
}
override fun visitSelect(that: JCTree.JCFieldAccess) {
super.visitSelect(that)
if (!found) that.selected.accept(this)
}
override fun visitTree(that: JCTree) {
if (!found && element == that) found = true
}
}
this.forEach { if (!found) it.accept(visitor) }
return found
}
companion object {
private val LINE_SEPARATOR: String = System.getProperty("line.separator")
private val KOTLIN_LOCATION_PREFIX = "Kotlin location: "
private val IGNORED_DIAGNOSTICS = setOf(
"compiler.err.name.clash.same.erasure",
"compiler.err.name.clash.same.erasure.no.override",
"compiler.err.name.clash.same.erasure.no.override.1",
"compiler.err.name.clash.same.erasure.no.hide",
"compiler.err.already.defined",
"compiler.err.annotation.type.not.applicable",
"compiler.err.doesnt.exist",
"compiler.err.duplicate.annotation.missing.container",
"compiler.err.not.def.access.package.cant.access",
"compiler.err.package.not.visible"
)
internal fun preRegister(kaptContext: KaptContext<*>, messageCollector: MessageCollector, mapDiagnosticLocations: Boolean) {
val interceptorData = DiagnosticInterceptorData()
kaptContext.context.put(Log.logKey, Context.Factory<Log> { newContext ->
fun makeWriter(severity: CompilerMessageSeverity) = PrintWriter(MessageCollectorBackedWriter(messageCollector, severity))
val errWriter = makeWriter(ERROR)
val warnWriter = makeWriter(STRONG_WARNING)
val noticeWriter = makeWriter(WARNING)
KaptJavaLog(
kaptContext.project.basePath, newContext, errWriter, warnWriter, noticeWriter,
interceptorData, mapDiagnosticLocations)
})
}
}
class DiagnosticInterceptorData {
var files: Map<JavaFileObject, JCTree.JCCompilationUnit> = emptyMap()
}
}
fun KaptContext<*>.kaptError(text: String): JCDiagnostic {
return JCDiagnostic.Factory.instance(context).errorJava9Aware(null, null, "proc.messager", text)
}
private fun JCDiagnostic.Factory.errorJava9Aware(
source: DiagnosticSource?,
pos: JCDiagnostic.DiagnosticPosition?,
key: String,
vararg args: String
): JCDiagnostic {
return if (isJava9OrLater) {
val errorMethod = this::class.java.getDeclaredMethod(
"error",
JCDiagnostic.DiagnosticFlag::class.java,
DiagnosticSource::class.java,
JCDiagnostic.DiagnosticPosition::class.java,
String::class.java,
Array<Any>::class.java)
errorMethod.invoke(this, JCDiagnostic.DiagnosticFlag.MANDATORY, source, pos, key, args) as JCDiagnostic
}
else {
this.error(source, pos, key, *args)
}
}
private data class KotlinFileObject(val file: File) : SimpleJavaFileObject(file.toURI(), Kind.SOURCE) {
override fun openOutputStream() = file.outputStream()
override fun openWriter() = file.writer()
override fun openInputStream() = file.inputStream()
override fun getCharContent(ignoreEncodingErrors: Boolean) = file.readText()
override fun getLastModified() = file.lastModified()
override fun openReader(ignoreEncodingErrors: Boolean) = file.reader()
override fun delete() = file.delete()
}
@@ -27,13 +27,13 @@ import com.sun.tools.javac.util.Context
import com.sun.tools.javac.util.Name
import com.sun.tools.javac.util.Names
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.kapt3.KaptContext
import org.jetbrains.kotlin.kapt3.KaptContextForStubGeneration
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.Type.*
import org.jetbrains.org.objectweb.asm.tree.ClassNode
class KaptTreeMaker(context: Context, kaptContext: KaptContext<*>) : TreeMaker(context), Disposable {
class KaptTreeMaker(context: Context, kaptContext: KaptContextForStubGeneration) : TreeMaker(context), Disposable {
private var kaptContext = DisposableReference(kaptContext)
val nameTable: Name.Table = Names.instance(context).table
@@ -171,7 +171,7 @@ class KaptTreeMaker(context: Context, kaptContext: KaptContext<*>) : TreeMaker(c
}
companion object {
internal fun preRegister(context: Context, kaptContext: KaptContext<*>) {
internal fun preRegister(context: Context, kaptContext: KaptContextForStubGeneration) {
context.put(treeMakerKey, Context.Factory<TreeMaker> { KaptTreeMaker(it, kaptContext) })
}
}
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.kapt3.mapJList
import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
@@ -24,14 +24,19 @@ import com.sun.tools.javac.tree.JCTree.*
import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.tree.TreeScanner
import kotlinx.kapt.KaptIgnored
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.javac.kaptError
import org.jetbrains.kotlin.kapt3.base.plus
import org.jetbrains.kotlin.kapt3.base.javac.kaptError
import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.kapt3.base.mapJListIndexed
import org.jetbrains.kotlin.kapt3.base.pairedListToMap
import org.jetbrains.kotlin.kapt3.base.util.TopLevelJava9Aware
import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation
import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.*
import org.jetbrains.kotlin.kapt3.util.*
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
@@ -55,7 +60,7 @@ import javax.lang.model.element.ElementKind
import com.sun.tools.javac.util.List as JavacList
class ClassFileToSourceStubConverter(
val kaptContext: KaptContext<GenerationState>,
val kaptContext: KaptContextForStubGeneration,
val generateNonExistentClass: Boolean,
val correctErrorTypes: Boolean
) {
@@ -146,7 +151,7 @@ class ClassFileToSourceStubConverter(
val metadataFile = File(
forSource.parentFile,
forSource.nameWithoutExtension + KaptLineMappingCollector.KAPT_METADATA_EXTENSION
forSource.nameWithoutExtension + KaptStubLineInformation.KAPT_METADATA_EXTENSION
)
metadataFile.writeBytes(kaptMetadata)
@@ -22,9 +22,9 @@ import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.codegen.state.updateArgumentModeFromAnnotations
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.kapt3.javac.kaptError
import org.jetbrains.kotlin.kapt3.mapJList
import org.jetbrains.kotlin.kapt3.mapJListIndexed
import org.jetbrains.kotlin.kapt3.base.javac.kaptError
import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.kapt3.base.mapJListIndexed
import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.METHOD_PARAMETER_TYPE
import org.jetbrains.kotlin.kapt3.stubs.ErrorTypeCorrector.TypeKind.RETURN_TYPE
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
@@ -26,7 +26,7 @@ import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.TreeScanner
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
import org.jetbrains.kotlin.kapt3.KaptContext
import org.jetbrains.kotlin.kapt3.KaptContextForStubGeneration
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.psi.*
@@ -35,7 +35,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.FieldNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
class KDocCommentKeeper(private val kaptContext: KaptContext<*>) {
class KDocCommentKeeper(private val kaptContext: KaptContextForStubGeneration) {
private val docCommentTable = KaptDocCommentTable()
fun getDocTable(file: JCTree.JCCompilationUnit): DocCommentTable {
@@ -19,75 +19,17 @@ package org.jetbrains.kotlin.kapt3.stubs
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.kapt3.KaptContext
import org.jetbrains.kotlin.kapt3.KaptContextForStubGeneration
import org.jetbrains.kotlin.kapt3.base.stubs.KaptStubLineInformation
import org.jetbrains.kotlin.kapt3.base.stubs.KotlinPosition
import org.jetbrains.kotlin.kapt3.base.stubs.LineInfoMap
import org.jetbrains.kotlin.kapt3.base.stubs.getJavacSignature
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.*
data class KotlinPosition(val path: String, val isRelativePath: Boolean, val pos: Int)
private typealias LineInfoMap = MutableMap<String, KotlinPosition>
class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
companion object {
const val KAPT_METADATA_EXTENSION = ".kapt_metadata"
private const val METADATA_VERSION = 1
fun parseFileInfo(file: JCTree.JCCompilationUnit): FileInfo {
val sourceUri = file.sourcefile
?.toUri()
?.takeIf { it.isAbsolute && !it.isOpaque && it.path != null && it.scheme?.toLowerCase() == "file" } ?: return FileInfo.EMPTY
val sourceFile = File(sourceUri).takeIf { it.exists() } ?: return FileInfo.EMPTY
val kaptMetadataFile = File(sourceFile.parentFile, sourceFile.nameWithoutExtension + KAPT_METADATA_EXTENSION)
if (!kaptMetadataFile.isFile) {
return FileInfo.EMPTY
}
return deserialize(kaptMetadataFile.readBytes())
}
private fun deserialize(data: ByteArray): FileInfo {
val lineInfo: LineInfoMap = mutableMapOf()
val signatureInfo = mutableMapOf<String, String>()
val ois = ObjectInputStream(ByteArrayInputStream(data))
val version = ois.readInt()
if (version != METADATA_VERSION) {
return FileInfo.EMPTY
}
val lineInfoCount = ois.readInt()
repeat(lineInfoCount) {
val fqName = ois.readUTF()
val path = ois.readUTF()
val isRelative = ois.readBoolean()
val pos = ois.readInt()
lineInfo[fqName] = KotlinPosition(path, isRelative, pos)
}
val signatureCount = ois.readInt()
repeat(signatureCount) {
val javacSignature = ois.readUTF()
val methodDesc = ois.readUTF()
signatureInfo[javacSignature] = methodDesc
}
return FileInfo(lineInfo, signatureInfo)
}
private fun getJavacSignature(decl: JCTree.JCMethodDecl): String {
val name = decl.name.toString()
val params = decl.parameters.joinToString { it.getType().toString() }
return "$name($params)"
}
}
class KaptLineMappingCollector(private val kaptContext: KaptContextForStubGeneration) {
private val lineInfo: LineInfoMap = mutableMapOf()
private val signatureInfo = mutableMapOf<String, String>()
@@ -106,7 +48,7 @@ class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
}
fun registerSignature(decl: JCTree.JCMethodDecl, method: MethodNode) {
signatureInfo[getJavacSignature(decl)] = method.name + method.desc
signatureInfo[decl.getJavacSignature()] = method.name + method.desc
}
private fun register(asmNode: Any, fqName: String) {
@@ -142,7 +84,7 @@ class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
val os = ByteArrayOutputStream()
val oos = ObjectOutputStream(os)
oos.writeInt(METADATA_VERSION)
oos.writeInt(KaptStubLineInformation.METADATA_VERSION)
oos.writeInt(lineInfo.size)
for ((fqName, kotlinPosition) in lineInfo) {
@@ -161,13 +103,4 @@ class KaptLineMappingCollector(private val kaptContext: KaptContext<*>) {
oos.flush()
return os.toByteArray()
}
class FileInfo(private val lineInfo: LineInfoMap, private val signatureInfo: Map<String, String>) {
companion object {
val EMPTY = FileInfo(mutableMapOf(), emptyMap())
}
fun getPositionFor(fqName: String) = lineInfo[fqName]
fun getMethodDescriptor(decl: JCTree.JCMethodDecl) = signatureInfo[getJavacSignature(decl)]
}
}
@@ -1,142 +0,0 @@
/*
* Copyright 2010-2017 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 com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.TreeScanner
import org.jetbrains.kotlin.kapt3.stubs.KaptLineMappingCollector.FileInfo
import org.jetbrains.kotlin.kapt3.util.getPackageNameJava9Aware
class KaptStubLineInformation {
private val offsets = mutableMapOf<JCTree.JCCompilationUnit, FileInfo>()
private val declarations = mutableMapOf<JCTree.JCCompilationUnit, List<JCTree>>()
fun getPositionInKotlinFile(file: JCTree.JCCompilationUnit, element: JCTree): KotlinPosition? {
val declaration = findDeclarationFor(element, file) ?: return null
val fileInfo = offsets.getOrPut(file) { KaptLineMappingCollector.parseFileInfo(file) }
val elementDescriptor = getKaptDescriptor(declaration, file, fileInfo) ?: return null
return fileInfo.getPositionFor(elementDescriptor)
}
private fun findDeclarationFor(element: JCTree, file: JCTree.JCCompilationUnit): JCTree? {
val fileDeclarations = declarations.getOrPut(file) { collectDeclarations(file) }
return fileDeclarations.firstOrNull { element.isLocatedInside(it) }
}
private fun getKaptDescriptor(declaration: JCTree, file: JCTree.JCCompilationUnit, fileInfo: FileInfo): String? {
fun getFqName(declaration: JCTree, parent: JCTree, currentName: String): String? {
return when (parent) {
is JCTree.JCCompilationUnit -> {
for (definition in parent.defs) {
// There could be only class definitions on the top level
definition as? JCTree.JCClassDecl ?: continue
getFqName(declaration, definition, "")?.let { return it }
}
return null
}
is JCTree.JCClassDecl -> {
val className = parent.simpleName.toString()
val newName = if (currentName.isEmpty()) className else currentName + "#" + className
if (declaration === parent) {
return newName
}
for (definition in parent.defs) {
getFqName(declaration, definition, className)?.let { return it }
}
return null
}
is JCTree.JCVariableDecl -> {
if (declaration === parent) {
return currentName + "#" + parent.name.toString()
}
return null
}
is JCTree.JCMethodDecl -> {
// We don't need to process local declarations here as kapt does not support locals entirely.
if (declaration === parent) {
val nameAndSignature = fileInfo.getMethodDescriptor(parent) ?: return null
return currentName + "#" + nameAndSignature
}
return null
}
else -> null
}
}
// Unfortunately, we have to do this the hard way, as symbols may be not available yet
// (for instance, if this code is called inside the "enterTrees()")
val simpleDescriptor = getFqName(declaration, file, "")
val packageName = file.getPackageNameJava9Aware()?.toString()?.replace('.', '/')
return if (packageName == null) simpleDescriptor else "$packageName/$simpleDescriptor"
}
private fun collectDeclarations(file: JCTree.JCCompilationUnit): List<JCTree> {
val declarations = mutableListOf<JCTree>()
// Note that super.visit...() is above the declarations saving.
// This allows us to get the deepest declarations in the beginning of the list.
file.accept(object : TreeScanner() {
override fun visitClassDef(tree: JCTree.JCClassDecl) {
super.visitClassDef(tree)
declarations += tree
}
override fun visitVarDef(tree: JCTree.JCVariableDecl) {
// Do not visit variable contents, there can be nothing but local declarations which we don't support
declarations += tree
}
override fun visitMethodDef(tree: JCTree.JCMethodDecl) {
// Do not visit methods contents, there can be nothing but local declarations which we don't support
declarations += tree
}
override fun visitTree(tree: JCTree?) {}
})
return declarations
}
private fun JCTree.isLocatedInside(declaration: JCTree): Boolean {
var found = false
declaration.accept(object : TreeScanner() {
override fun scan(tree: JCTree?) {
if (!found && tree === this@isLocatedInside) {
found = true
}
if (found) return
super.scan(tree)
}
override fun scan(trees: com.sun.tools.javac.util.List<out JCTree>?) {
// We don't need to repeat the logic above here as scan(List) calls scan(JCTree)
if (found) return
super.scan(trees)
}
})
return found
}
}
@@ -24,8 +24,8 @@ import org.jetbrains.org.objectweb.asm.signature.SignatureVisitor
import java.util.*
import org.jetbrains.kotlin.kapt3.stubs.ElementKind.*
import org.jetbrains.kotlin.kapt3.javac.KaptTreeMaker
import org.jetbrains.kotlin.kapt3.mapJList
import org.jetbrains.kotlin.kapt3.mapJListIndexed
import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.kapt3.base.mapJListIndexed
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.org.objectweb.asm.signature.SignatureReader
import com.sun.tools.javac.util.List as JavacList
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3.util
@@ -20,38 +9,37 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
import java.io.PrintWriter
import java.io.StringWriter
class KaptLogger(
val isVerbose: Boolean,
val messageCollector: MessageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, isVerbose)
) {
class MessageCollectorBackedKaptLogger(
override val isVerbose: Boolean,
val messageCollector: MessageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, isVerbose)
) : KaptLogger {
private companion object {
val PREFIX = "[kapt] "
}
fun info(message: String) {
override val errorWriter = makeWriter(CompilerMessageSeverity.ERROR)
override val warnWriter = makeWriter(CompilerMessageSeverity.STRONG_WARNING)
override val infoWriter = makeWriter(CompilerMessageSeverity.WARNING)
override fun info(message: String) {
if (isVerbose) {
messageCollector.report(CompilerMessageSeverity.INFO, PREFIX + message)
}
}
inline fun info(message: () -> String) {
if (isVerbose) {
info(message())
}
}
fun warn(message: String) {
override fun warn(message: String) {
messageCollector.report(CompilerMessageSeverity.WARNING, PREFIX + message)
}
fun error(message: String) {
override fun error(message: String) {
messageCollector.report(CompilerMessageSeverity.ERROR, PREFIX + message)
}
fun exception(e: Throwable) {
override fun exception(e: Throwable) {
val stacktrace = run {
val writer = StringWriter()
e.printStackTrace(PrintWriter(writer))
@@ -59,4 +47,8 @@ class KaptLogger(
}
messageCollector.report(CompilerMessageSeverity.ERROR, PREFIX + "An exception occurred: " + stacktrace)
}
private fun makeWriter(severity: CompilerMessageSeverity): PrintWriter {
return PrintWriter(MessageCollectorBackedWriter(messageCollector, severity))
}
}
@@ -1,63 +0,0 @@
/*
* Copyright 2010-2017 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 com.intellij.openapi.util.SystemInfo
import com.sun.tools.javac.main.Option
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.TreeMaker
import com.sun.tools.javac.util.Options
import com.sun.tools.javac.util.List as JavacList
import org.jetbrains.kotlin.kapt3.plus
internal val isJava9OrLater: Boolean
get() = SystemInfo.isJavaVersionAtLeast("9")
internal fun Options.putJavacOption(jdk8Name: String, jdk9Name: String, value: String) {
val option = if (isJava9OrLater) {
Option.valueOf(jdk9Name)
}
else {
Option.valueOf(jdk8Name)
}
put(option, value)
}
internal fun TreeMaker.TopLevelJava9Aware(packageClause: JCTree.JCExpression?, declarations: JavacList<JCTree>): JCTree.JCCompilationUnit {
return if (isJava9OrLater) {
val topLevelMethod = TreeMaker::class.java.declaredMethods.single { it.name == "TopLevel" }
val packageDecl: JCTree? = packageClause?.let {
val packageDeclMethod = TreeMaker::class.java.methods.single { it.name == "PackageDecl" }
packageDeclMethod.invoke(this, JavacList.nil<JCTree>(), packageClause) as JCTree
}
val allDeclarations = if (packageDecl != null) JavacList.of(packageDecl) + declarations else declarations
topLevelMethod.invoke(this, allDeclarations) as JCTree.JCCompilationUnit
}
else {
TopLevel(JavacList.nil(), packageClause, declarations)
}
}
internal fun JCTree.JCCompilationUnit.getPackageNameJava9Aware(): JCTree? {
return if (isJava9OrLater) {
JCTree.JCCompilationUnit::class.java.getDeclaredMethod("getPackageName").invoke(this) as JCTree?
}
else {
this.packageName
}
}
@@ -1,69 +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.util.List as JavacList
internal inline fun <T, R> mapJList(values: Iterable<T>?, f: (T) -> R?): JavacList<R> {
if (values == null) return JavacList.nil()
var result = JavacList.nil<R>()
for (item in values) {
f(item)?.let { result = result.append(it) }
}
return result
}
internal inline fun <T, R> mapJListIndexed(values: Iterable<T>?, f: (Int, T) -> R?): JavacList<R> {
if (values == null) return JavacList.nil()
var result = JavacList.nil<R>()
values.forEachIndexed { index, item ->
f(index, item)?.let { result = result.append(it) }
}
return result
}
internal inline fun <T> mapPairedValuesJList(valuePairs: List<Any>?, f: (String, Any) -> T?): JavacList<T> {
if (valuePairs == null || valuePairs.isEmpty()) return JavacList.nil()
val size = valuePairs.size
var result = JavacList.nil<T>()
assert(size % 2 == 0)
var index = 0
while (index < size) {
val key = valuePairs[index] as String
val value = valuePairs[index + 1]
f(key, value)?.let { result = result.prepend(it) }
index += 2
}
return result.reverse()
}
internal fun pairedListToMap(valuePairs: List<Any>?): Map<String, Any?> {
val map = mutableMapOf<String, Any?>()
mapPairedValuesJList(valuePairs) { key, value ->
map.put(key, value)
}
return map
}
internal operator fun <T : Any> JavacList<T>.plus(other: JavacList<T>): JavacList<T> {
return this.appendList(other)
}
@@ -16,19 +16,20 @@
package org.jetbrains.kotlin.kapt3.test
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.codegen.CodegenTestCase
import org.jetbrains.kotlin.codegen.GenerationUtils
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.AptMode.STUBS_AND_APT
import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter.KaptStub
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
@@ -130,10 +131,11 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, *javaSources)
val kapt3Extension = Kapt3ExtensionForTests(processors, javaSources.toList(), sourceOutputDir, this.options,
val project = myEnvironment.project
val kapt3Extension = Kapt3ExtensionForTests(project, processors, javaSources.toList(), sourceOutputDir, this.options,
stubsOutputDir = stubsDir, incrementalDataOutputDir = incrementalDataDir)
AnalysisHandlerExtension.registerExtension(myEnvironment.project, kapt3Extension)
AnalysisHandlerExtension.registerExtension(project, kapt3Extension)
try {
loadMultiFiles(files)
@@ -153,6 +155,7 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
}
protected class Kapt3ExtensionForTests(
project: Project,
private val processors: List<Processor>,
javaSourceRoots: List<File>,
outputDir: File,
@@ -161,10 +164,11 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
incrementalDataOutputDir: File
) : AbstractKapt3Extension(
KaptPaths(
project.basePath?.let(::File),
PathUtil.getJdkClassesRootsFromCurrentJre() + PathUtil.kotlinPathsForIdeaPlugin.stdlibPath,
emptyList(), javaSourceRoots, outputDir, outputDir, stubsOutputDir, incrementalDataOutputDir
), options, emptyMap(), emptyList(), STUBS_AND_APT, System.currentTimeMillis(),
KaptLogger(true), correctErrorTypes = true, mapDiagnosticLocations = true,
MessageCollectorBackedKaptLogger(true), correctErrorTypes = true, mapDiagnosticLocations = true,
compilerConfiguration = CompilerConfiguration.EMPTY
) {
internal var savedStubs: String? = null
@@ -172,7 +176,7 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
override fun loadProcessors() = processors
override fun saveStubs(kaptContext: KaptContext<*>, stubs: List<KaptStub>) {
override fun saveStubs(kaptContext: KaptContext, stubs: List<KaptStub>) {
if (this.savedStubs != null) {
error("Stubs are already saved")
}
@@ -186,7 +190,7 @@ abstract class AbstractKotlinKapt3IntegrationTest : CodegenTestCase() {
}
override fun saveIncrementalData(
kaptContext: KaptContext<GenerationState>,
kaptContext: KaptContextForStubGeneration,
messageCollector: MessageCollector,
converter: ClassFileToSourceStubConverter
) {
@@ -32,12 +32,15 @@ import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.codegen.CodegenTestCase
import org.jetbrains.kotlin.codegen.CodegenTestFiles
import org.jetbrains.kotlin.codegen.GenerationUtils
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.base.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.base.javac.KaptJavaLog
import org.jetbrains.kotlin.kapt3.base.parseJavaFiles
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt3.javac.KaptJavaLog
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
@@ -112,7 +115,7 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
val classBuilderFactory = Kapt3BuilderFactory()
val generationState = GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory)
val logger = KaptLogger(isVerbose = true, messageCollector = messageCollector)
val logger = MessageCollectorBackedKaptLogger(isVerbose = true, messageCollector = messageCollector)
val javacOptions = wholeFile.getOptionValues("JAVAC_OPTION")
.map { opt ->
@@ -121,22 +124,25 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
}.toMap()
var javaFiles: List<File>? = null
var kaptContext: KaptContext<*>? = null
var kaptContext: KaptContext? = null
try {
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile()
val paths = KaptPaths(
generationState.project.basePath?.let(::File),
compileClasspath = PathUtil.getJdkClassesRootsFromCurrentJre() + PathUtil.kotlinPathsForIdeaPlugin.stdlibPath,
annotationProcessingClasspath = emptyList(), javaSourceRoots = emptyList(),
sourcesOutputDir = sourceOutputDir, classFilesOutputDir = sourceOutputDir,
stubsOutputDir = sourceOutputDir, incrementalDataOutputDir = sourceOutputDir
)
kaptContext = KaptContext(paths, true, AptMode.STUBS_AND_APT,
logger, generationState.project, generationState.bindingContext, classBuilderFactory.compiledClasses,
classBuilderFactory.origins, generationState, mapDiagnosticLocations = true,
processorOptions = emptyMap(), javacOptions = javacOptions)
kaptContext = KaptContextForStubGeneration(
paths, true,
logger, generationState.project, generationState.bindingContext, classBuilderFactory.compiledClasses,
classBuilderFactory.origins, generationState, mapDiagnosticLocations = true,
processorOptions = emptyMap(), javacOptions = javacOptions
)
javaFiles = files
.filter { it.name.toLowerCase().endsWith(".java") }
@@ -154,7 +160,7 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
}
protected fun convert(
kaptContext: KaptContext<GenerationState>,
kaptContext: KaptContextForStubGeneration,
javaFiles: List<File>,
generateNonExistentClass: Boolean,
correctErrorTypes: Boolean
@@ -201,7 +207,7 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
protected fun File.getOptionValues(name: String) = getRawOptionValues(name).map { it.drop("// ".length + name.length).trim() }
protected abstract fun check(
kaptContext: KaptContext<GenerationState>,
kaptContext: KaptContextForStubGeneration,
javaFiles: List<File>,
txtFile: File,
wholeFile: File)
@@ -239,7 +245,7 @@ open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test(
doTestWithJdk9(AbstractClassFileToSourceStubConverterTest::class.java, filePath)
}
override fun check(kaptContext: KaptContext<GenerationState>, javaFiles: List<File>, txtFile: File, wholeFile: File) {
override fun check(kaptContext: KaptContextForStubGeneration, javaFiles: List<File>, txtFile: File, wholeFile: File) {
val generateNonExistentClass = wholeFile.isOptionSet("NON_EXISTENT_CLASS")
val correctErrorTypes = wholeFile.isOptionSet("CORRECT_ERROR_TYPES")
val validate = !wholeFile.isOptionSet("NO_VALIDATION")
@@ -298,7 +304,7 @@ open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test(
}
abstract class AbstractKotlinKaptContextTest : AbstractKotlinKapt3Test() {
override fun check(kaptContext: KaptContext<GenerationState>, javaFiles: List<File>, txtFile: File, wholeFile: File) {
override fun check(kaptContext: KaptContextForStubGeneration, javaFiles: List<File>, txtFile: File, wholeFile: File) {
val compilationUnits = convert(kaptContext, javaFiles, generateNonExistentClass = false, correctErrorTypes = true)
kaptContext.doAnnotationProcessing(
@@ -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.test
import com.intellij.openapi.command.impl.DummyProject
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.kapt3.AptMode
import org.jetbrains.kotlin.kapt3.KaptContext
import org.jetbrains.kotlin.kapt3.KaptPaths
import org.jetbrains.kotlin.kapt3.diagnostic.KaptError
import org.jetbrains.kotlin.kapt3.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.util.KaptLogger
import org.jetbrains.kotlin.resolve.BindingContext
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
/*
* 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.
*/
class JavaKaptContextTest {
companion object {
private val TEST_DATA_DIR = File("plugins/kapt3/kapt3-compiler/testData/runner")
val messageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false)
fun simpleProcessor() = object : AbstractProcessor() {
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
for (annotation in annotations) {
val annotationName = annotation.simpleName.toString()
val annotatedElements = roundEnv.getElementsAnnotatedWith(annotation)
for (annotatedElement in annotatedElements) {
val generatedClassName = annotatedElement.simpleName.toString().capitalize() + annotationName.capitalize()
val file = processingEnv.filer.createSourceFile("generated." + generatedClassName)
file.openWriter().use {
it.write("""
package generated;
class $generatedClassName {}
""".trimIndent())
}
}
}
return true
}
override fun getSupportedAnnotationTypes() = setOf("test.MyAnnotation")
}
}
private fun doAnnotationProcessing(javaSourceFile: File, processor: Processor, outputDir: File) {
KaptContext(
KaptPaths(
compileClasspath = emptyList(),
annotationProcessingClasspath = emptyList(),
javaSourceRoots = emptyList(),
sourcesOutputDir = outputDir,
classFilesOutputDir = outputDir,
stubsOutputDir = outputDir,
incrementalDataOutputDir = outputDir
),
withJdk = true,
aptMode = AptMode.STUBS_AND_APT,
logger = KaptLogger(isVerbose = true, messageCollector = messageCollector),
project = DummyProject.getInstance(),
bindingContext = BindingContext.EMPTY,
compiledClasses = emptyList(),
origins = emptyMap(),
generationState = null,
mapDiagnosticLocations = true,
processorOptions = emptyMap()
).doAnnotationProcessing(listOf(javaSourceFile), listOf(processor))
}
@Test
fun testSimple() {
val sourceOutputDir = Files.createTempDirectory("kaptRunner").toFile()
try {
doAnnotationProcessing(File(TEST_DATA_DIR, "Simple.java"), simpleProcessor(), sourceOutputDir)
val myMethodFile = File(sourceOutputDir, "generated/MyMethodMyAnnotation.java")
assertTrue(myMethodFile.exists())
} finally {
sourceOutputDir.deleteRecursively()
}
}
@Test(expected = KaptError::class)
fun testException() {
val exceptionMessage = "Here we are!"
val processor = object : AbstractProcessor() {
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
throw RuntimeException(exceptionMessage)
}
override fun getSupportedAnnotationTypes() = setOf("test.MyAnnotation")
}
try {
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)
throw e
}
}
@Test(expected = KaptError::class)
fun testParsingError() {
try {
doAnnotationProcessing(File(TEST_DATA_DIR, "ParseError.java"), simpleProcessor(), TEST_DATA_DIR)
} catch (e: KaptError) {
assertEquals(KaptError.Kind.ERROR_RAISED, e.kind)
throw e
}
}
}
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.kapt3.test
import com.intellij.openapi.util.SystemInfoRt
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
import org.jetbrains.kotlin.kapt3.base.util.isJava9OrLater
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
import java.net.URL
@@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit
interface Java9TestLauncher {
fun doTestWithJdk9(mainClass: Class<*>, arg: String) {
// Already under Java 9
if (isJava9OrLater) return
if (isJava9OrLater()) return
//TODO unmute after investigation (tests are failing on TeamCity)
if (SystemInfoRt.isWindows) return
@@ -1 +0,0 @@
BLAHBLAH
@@ -1,39 +0,0 @@
package test;
/**
* KDoc comment.
*/
class Simple {
@MyAnnotation
void myMethod() {
// do nothing
}
}
@interface MyAnnotation {
}
enum EnumClass {
BLACK, WHITE
}
enum EnumClass2 {
WHITE("A"), RED("B");
private final String blah;
EnumClass2(String blah) {
this.blah = blah;
}
}
enum EnumClass3 {
A {
@Override
void a() {}
};
abstract void a();
}