Kapt: Fix remaining issues with Java 9 compatibility in converter, add Java 9 tests
This commit is contained in:
+25
-16
@@ -16,8 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.kapt3
|
package org.jetbrains.kotlin.kapt3
|
||||||
|
|
||||||
import com.sun.tools.javac.comp.CompileStates
|
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.JavaCompiler
|
||||||
import com.sun.tools.javac.main.Option
|
import com.sun.tools.javac.main.Option
|
||||||
import com.sun.tools.javac.processing.AnnotationProcessingError
|
import com.sun.tools.javac.processing.AnnotationProcessingError
|
||||||
@@ -29,7 +28,6 @@ import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
|
|||||||
import org.jetbrains.kotlin.kapt3.util.putJavacOption
|
import org.jetbrains.kotlin.kapt3.util.putJavacOption
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.annotation.processing.Processor
|
import javax.annotation.processing.Processor
|
||||||
import javax.tools.JavaFileManager
|
|
||||||
import javax.tools.JavaFileObject
|
import javax.tools.JavaFileObject
|
||||||
import com.sun.tools.javac.util.List as JavacList
|
import com.sun.tools.javac.util.List as JavacList
|
||||||
|
|
||||||
@@ -71,23 +69,12 @@ fun KaptContext<*>.doAnnotationProcessing(
|
|||||||
compiler.initProcessAnnotations(processors)
|
compiler.initProcessAnnotations(processors)
|
||||||
}
|
}
|
||||||
|
|
||||||
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(javaSourceFiles)
|
val parsedJavaFiles = parseJavaFiles(javaSourceFiles)
|
||||||
var parsedJavaFiles = compiler.stopIfErrorOccurred(
|
|
||||||
CompileStates.CompileState.PARSE, compiler.parseFiles(javaFileObjects))
|
|
||||||
|
|
||||||
if (isJava9OrLater) {
|
|
||||||
val initModulesMethod = compiler.javaClass.getMethod("initModules", JavacList::class.java)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
parsedJavaFiles = compiler.stopIfErrorOccurred(
|
|
||||||
CompileStates.CompileState.PARSE,
|
|
||||||
initModulesMethod.invoke(compiler, parsedJavaFiles) as JavacList<JCTree.JCCompilationUnit>)
|
|
||||||
}
|
|
||||||
|
|
||||||
compilerAfterAP = try {
|
compilerAfterAP = try {
|
||||||
javaLog.interceptorData.files = parsedJavaFiles.map { it.sourceFile to it }.toMap()
|
javaLog.interceptorData.files = parsedJavaFiles.map { it.sourceFile to it }.toMap()
|
||||||
val analyzedFiles = compiler.stopIfErrorOccurred(
|
val analyzedFiles = compiler.stopIfErrorOccurred(
|
||||||
CompileStates.CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources))
|
CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources))
|
||||||
|
|
||||||
if (isJava9OrLater) {
|
if (isJava9OrLater) {
|
||||||
val processAnnotationsMethod = compiler.javaClass.getMethod("processAnnotations", JavacList::class.java)
|
val processAnnotationsMethod = compiler.javaClass.getMethod("processAnnotations", JavacList::class.java)
|
||||||
@@ -119,4 +106,26 @@ fun KaptContext<*>.doAnnotationProcessing(
|
|||||||
processingEnvironment.close()
|
processingEnvironment.close()
|
||||||
this@doAnnotationProcessing.close()
|
this@doAnnotationProcessing.close()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.kapt3.KaptContext
|
|||||||
import org.jetbrains.kotlin.kapt3.stubs.KaptStubLineInformation
|
import org.jetbrains.kotlin.kapt3.stubs.KaptStubLineInformation
|
||||||
import org.jetbrains.kotlin.kapt3.stubs.KotlinPosition
|
import org.jetbrains.kotlin.kapt3.stubs.KotlinPosition
|
||||||
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedWriter
|
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedWriter
|
||||||
|
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import javax.tools.JavaFileObject
|
import javax.tools.JavaFileObject
|
||||||
@@ -74,6 +75,8 @@ class KaptJavaLog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun report(diagnostic: JCDiagnostic) {
|
override fun report(diagnostic: JCDiagnostic) {
|
||||||
|
System.err.println("Diagnostic code: ${diagnostic.code}")
|
||||||
|
|
||||||
if (diagnostic.type == JCDiagnostic.DiagnosticType.ERROR && diagnostic.code in IGNORED_DIAGNOSTICS) {
|
if (diagnostic.type == JCDiagnostic.DiagnosticType.ERROR && diagnostic.code in IGNORED_DIAGNOSTICS) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -102,7 +105,7 @@ class KaptJavaLog(
|
|||||||
val kotlinFile = kotlinPosition?.let { getKotlinSourceFile(it) }
|
val kotlinFile = kotlinPosition?.let { getKotlinSourceFile(it) }
|
||||||
if (kotlinPosition != null && kotlinFile != null) {
|
if (kotlinPosition != null && kotlinFile != null) {
|
||||||
val locationMessage = "$KOTLIN_LOCATION_PREFIX${kotlinFile.absolutePath}: (${kotlinPosition.line}, ${kotlinPosition.column})"
|
val locationMessage = "$KOTLIN_LOCATION_PREFIX${kotlinFile.absolutePath}: (${kotlinPosition.line}, ${kotlinPosition.column})"
|
||||||
val locationDiagnostic = diagnosticFactory.note("proc.messager", locationMessage)
|
val locationDiagnostic = diagnosticFactory.note(null as DiagnosticSource?, null, "proc.messager", locationMessage)
|
||||||
val wrappedDiagnostic = JCDiagnostic.MultilineDiagnostic(diagnostic, JavacList.of(locationDiagnostic))
|
val wrappedDiagnostic = JCDiagnostic.MultilineDiagnostic(diagnostic, JavacList.of(locationDiagnostic))
|
||||||
|
|
||||||
super.report(wrappedDiagnostic)
|
super.report(wrappedDiagnostic)
|
||||||
@@ -126,9 +129,9 @@ class KaptJavaLog(
|
|||||||
|
|
||||||
val writer = when (diagnostic.type) {
|
val writer = when (diagnostic.type) {
|
||||||
DiagnosticType.FRAGMENT, null -> kotlin.error("Invalid root diagnostic type: ${diagnostic.type}")
|
DiagnosticType.FRAGMENT, null -> kotlin.error("Invalid root diagnostic type: ${diagnostic.type}")
|
||||||
DiagnosticType.NOTE -> super.noticeWriter
|
DiagnosticType.NOTE -> super.getWriter(WriterKind.NOTICE)
|
||||||
DiagnosticType.WARNING -> super.warnWriter
|
DiagnosticType.WARNING -> super.getWriter(WriterKind.WARNING)
|
||||||
DiagnosticType.ERROR -> super.errWriter
|
DiagnosticType.ERROR -> super.getWriter(WriterKind.ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
val formattedMessage = diagnosticFormatter.format(diagnostic, javacMessages.currentLocale)
|
val formattedMessage = diagnosticFormatter.format(diagnostic, javacMessages.currentLocale)
|
||||||
@@ -211,10 +214,32 @@ class KaptJavaLog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun KaptContext<*>.kaptError(text: String): JCDiagnostic {
|
fun KaptContext<*>.kaptError(text: String): JCDiagnostic {
|
||||||
return JCDiagnostic.Factory.instance(context).error(null, null, "proc.messager", text)
|
return JCDiagnostic.Factory.instance(context).errorJava9Aware(null, null, "proc.messager", text)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KaptContext<*>.kaptError(text: String, target: PsiElement): JCDiagnostic {
|
fun KaptContext<*>.kaptError(text: String, target: PsiElement): JCDiagnostic {
|
||||||
//TODO provide source binding
|
//TODO provide source binding
|
||||||
return JCDiagnostic.Factory.instance(context).error(null, null, "proc.messager", text)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.kapt3.stubs
|
|||||||
import com.sun.tools.javac.tree.JCTree
|
import com.sun.tools.javac.tree.JCTree
|
||||||
import com.sun.tools.javac.tree.TreeScanner
|
import com.sun.tools.javac.tree.TreeScanner
|
||||||
import org.jetbrains.kotlin.kapt3.stubs.KaptLineMappingCollector.Companion.KAPT_SIGNATURE_ANNOTATION_FQNAME
|
import org.jetbrains.kotlin.kapt3.stubs.KaptLineMappingCollector.Companion.KAPT_SIGNATURE_ANNOTATION_FQNAME
|
||||||
|
import org.jetbrains.kotlin.kapt3.util.getPackageNameJava9Aware
|
||||||
|
|
||||||
class KaptStubLineInformation {
|
class KaptStubLineInformation {
|
||||||
private val offsets = mutableMapOf<JCTree.JCCompilationUnit, KaptLineMappingCollector.LineInfo>()
|
private val offsets = mutableMapOf<JCTree.JCCompilationUnit, KaptLineMappingCollector.LineInfo>()
|
||||||
@@ -84,7 +85,7 @@ class KaptStubLineInformation {
|
|||||||
// Unfortunately, we have to do this the hard way, as symbols may be not available yet
|
// 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()")
|
// (for instance, if this code is called inside the "enterTrees()")
|
||||||
val simpleDescriptor = getFqName(declaration, file, "")
|
val simpleDescriptor = getFqName(declaration, file, "")
|
||||||
val packageName = file.packageName?.toString()?.replace('.', '/')
|
val packageName = file.getPackageNameJava9Aware()?.toString()?.replace('.', '/')
|
||||||
return if (packageName == null) simpleDescriptor else "$packageName/$simpleDescriptor"
|
return if (packageName == null) simpleDescriptor else "$packageName/$simpleDescriptor"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+64
-4
@@ -34,8 +34,10 @@ import org.jetbrains.kotlin.kapt3.KaptContext
|
|||||||
import org.jetbrains.kotlin.kapt3.doAnnotationProcessing
|
import org.jetbrains.kotlin.kapt3.doAnnotationProcessing
|
||||||
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
|
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
|
||||||
import org.jetbrains.kotlin.kapt3.javac.KaptJavaLog
|
import org.jetbrains.kotlin.kapt3.javac.KaptJavaLog
|
||||||
|
import org.jetbrains.kotlin.kapt3.parseJavaFiles
|
||||||
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
|
import org.jetbrains.kotlin.kapt3.stubs.ClassFileToSourceStubConverter
|
||||||
import org.jetbrains.kotlin.kapt3.util.KaptLogger
|
import org.jetbrains.kotlin.kapt3.util.KaptLogger
|
||||||
|
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
|
||||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
|
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
|
||||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||||
@@ -44,8 +46,11 @@ import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndAddNewlineAtEOF
|
|||||||
import org.jetbrains.kotlin.utils.PathUtil
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
import java.net.URLClassLoader
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import com.sun.tools.javac.util.List as JavacList
|
import com.sun.tools.javac.util.List as JavacList
|
||||||
|
|
||||||
abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
|
abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
|
||||||
@@ -88,6 +93,8 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
check(kaptContext, javaFiles, txtFile, wholeFile)
|
check(kaptContext, javaFiles, txtFile, wholeFile)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
throw RuntimeException(e)
|
||||||
} finally {
|
} finally {
|
||||||
javaFiles.forEach { it.delete() }
|
javaFiles.forEach { it.delete() }
|
||||||
tempFiles.forEach { it.delete() }
|
tempFiles.forEach { it.delete() }
|
||||||
@@ -115,7 +122,8 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
|
|||||||
kaptContext.compiler.log.nerrors = 0
|
kaptContext.compiler.log.nerrors = 0
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val parsedJavaFiles = kaptContext.compiler.parseFiles(kaptContext.fileManager.getJavaFileObjectsFromFiles(allJavaFiles))
|
val parsedJavaFiles = kaptContext.parseJavaFiles(allJavaFiles)
|
||||||
|
|
||||||
for (file in parsedJavaFiles) {
|
for (file in parsedJavaFiles) {
|
||||||
// By default, JavaFileObject.getName() returns the absolute path to the file.
|
// By default, JavaFileObject.getName() returns the absolute path to the file.
|
||||||
// In our test, such a path will be temporary, so the comparision against it will lead to flaky tests.
|
// In our test, such a path will be temporary, so the comparision against it will lead to flaky tests.
|
||||||
@@ -135,18 +143,70 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
|
|||||||
wholeFile: File)
|
wholeFile: File)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test() {
|
open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test() {
|
||||||
internal companion object {
|
companion object {
|
||||||
private val KOTLIN_METADATA_GROUP = "[a-z0-9]+ = (\\{.+?\\}|[0-9]+)"
|
private val KOTLIN_METADATA_GROUP = "[a-z0-9]+ = (\\{.+?\\}|[0-9]+)"
|
||||||
private val KOTLIN_METADATA_REGEX = "@kotlin\\.Metadata\\(($KOTLIN_METADATA_GROUP)(, $KOTLIN_METADATA_GROUP)*\\)".toRegex()
|
private val KOTLIN_METADATA_REGEX = "@kotlin\\.Metadata\\(($KOTLIN_METADATA_GROUP)(, $KOTLIN_METADATA_GROUP)*\\)".toRegex()
|
||||||
private val KAPT_METADATA_REGEX = "@kapt\\.internal\\.KaptMetadata\\((value = )?\"[^(].*?\"\\)".toRegex()
|
private val KAPT_METADATA_REGEX = "@kapt\\.internal\\.KaptMetadata\\((value = )?\"[^(].*?\"\\)".toRegex()
|
||||||
|
|
||||||
private val EXPECTED_ERROR = "EXPECTED_ERROR"
|
private val EXPECTED_ERROR = "EXPECTED_ERROR"
|
||||||
|
|
||||||
fun removeMetadataAnnotationContents(s: String): String {
|
internal fun removeMetadataAnnotationContents(s: String): String {
|
||||||
return s.replace(KOTLIN_METADATA_REGEX, "@kotlin.Metadata()")
|
return s.replace(KOTLIN_METADATA_REGEX, "@kotlin.Metadata()")
|
||||||
.replace(KAPT_METADATA_REGEX, "@kapt.internal.KaptMetadata()")
|
.replace(KAPT_METADATA_REGEX, "@kapt.internal.KaptMetadata()")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
if (args.isEmpty()) error("1 argument expected, 0 passed")
|
||||||
|
AbstractClassFileToSourceStubConverterTest().doTest(args[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is to suppress "AssertionFailedError: No tests found"
|
||||||
|
fun testSuppressWarning() {}
|
||||||
|
|
||||||
|
override fun doTest(filePath: String) {
|
||||||
|
super.doTest(filePath)
|
||||||
|
|
||||||
|
if (!isJava9OrLater) {
|
||||||
|
doTestWithJdk9(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTestWithJdk9(filePath: String) {
|
||||||
|
val jdk9Home = KotlinTestUtils.getJdk9HomeIfPossible() ?: run {
|
||||||
|
println("JDK9 not found, the test was skipped")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val javaExe = File(jdk9Home, "bin/java.exe").takeIf { it.exists() } ?: File(jdk9Home, "bin/java")
|
||||||
|
assert(javaExe.exists()) { "Can't find 'java' executable in $jdk9Home" }
|
||||||
|
|
||||||
|
val currentJavaHome = System.getProperty("java.home")
|
||||||
|
val classpath = collectClasspath(AbstractClassFileToSourceStubConverterTest::class.java.classLoader)
|
||||||
|
.filter { !it.path.startsWith(currentJavaHome) }
|
||||||
|
|
||||||
|
val process = ProcessBuilder(
|
||||||
|
javaExe.absolutePath,
|
||||||
|
"--illegal-access=warn",
|
||||||
|
"-ea",
|
||||||
|
"-classpath",
|
||||||
|
classpath.joinToString(File.pathSeparator),
|
||||||
|
AbstractClassFileToSourceStubConverterTest::class.java.name,
|
||||||
|
filePath
|
||||||
|
).inheritIO().start()
|
||||||
|
|
||||||
|
process.waitFor(3, TimeUnit.MINUTES)
|
||||||
|
if (process.exitValue() != 0) {
|
||||||
|
throw AssertionError("Java 9 test process exited with exit code ${process.exitValue()} \n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectClasspath(classLoader: ClassLoader?): List<URL> = when (classLoader) {
|
||||||
|
is URLClassLoader -> classLoader.urLs.asList() + collectClasspath(classLoader.parent)
|
||||||
|
is ClassLoader -> collectClasspath(classLoader.parent)
|
||||||
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun check(kaptContext: KaptContext<GenerationState>, javaFiles: List<File>, txtFile: File, wholeFile: File) {
|
override fun check(kaptContext: KaptContext<GenerationState>, javaFiles: List<File>, txtFile: File, wholeFile: File) {
|
||||||
|
|||||||
Reference in New Issue
Block a user