Kapt: Fix remaining issues with Java 9 compatibility in converter, add Java 9 tests

This commit is contained in:
Yan Zhulanow
2017-11-14 00:00:34 +09:00
parent dde87ae1f9
commit af57ab8119
4 changed files with 122 additions and 27 deletions
@@ -16,8 +16,7 @@
package org.jetbrains.kotlin.kapt3
import com.sun.tools.javac.comp.CompileStates
import com.sun.tools.javac.file.JavacFileManager
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
@@ -29,7 +28,6 @@ import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
import org.jetbrains.kotlin.kapt3.util.putJavacOption
import java.io.File
import javax.annotation.processing.Processor
import javax.tools.JavaFileManager
import javax.tools.JavaFileObject
import com.sun.tools.javac.util.List as JavacList
@@ -71,23 +69,12 @@ fun KaptContext<*>.doAnnotationProcessing(
compiler.initProcessAnnotations(processors)
}
val javaFileObjects = fileManager.getJavaFileObjectsFromFiles(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>)
}
val parsedJavaFiles = parseJavaFiles(javaSourceFiles)
compilerAfterAP = try {
javaLog.interceptorData.files = parsedJavaFiles.map { it.sourceFile to it }.toMap()
val analyzedFiles = compiler.stopIfErrorOccurred(
CompileStates.CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources))
CompileState.PARSE, compiler.enterTrees(parsedJavaFiles + additionalSources))
if (isJava9OrLater) {
val processAnnotationsMethod = compiler.javaClass.getMethod("processAnnotations", JavacList::class.java)
@@ -119,4 +106,26 @@ fun KaptContext<*>.doAnnotationProcessing(
processingEnvironment.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.KotlinPosition
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedWriter
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
import java.io.File
import java.io.PrintWriter
import javax.tools.JavaFileObject
@@ -74,6 +75,8 @@ class KaptJavaLog(
}
override fun report(diagnostic: JCDiagnostic) {
System.err.println("Diagnostic code: ${diagnostic.code}")
if (diagnostic.type == JCDiagnostic.DiagnosticType.ERROR && diagnostic.code in IGNORED_DIAGNOSTICS) {
return
}
@@ -102,7 +105,7 @@ class KaptJavaLog(
val kotlinFile = kotlinPosition?.let { getKotlinSourceFile(it) }
if (kotlinPosition != null && kotlinFile != null) {
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))
super.report(wrappedDiagnostic)
@@ -126,9 +129,9 @@ class KaptJavaLog(
val writer = when (diagnostic.type) {
DiagnosticType.FRAGMENT, null -> kotlin.error("Invalid root diagnostic type: ${diagnostic.type}")
DiagnosticType.NOTE -> super.noticeWriter
DiagnosticType.WARNING -> super.warnWriter
DiagnosticType.ERROR -> super.errWriter
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)
@@ -211,10 +214,32 @@ class KaptJavaLog(
}
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 {
//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)
}
}
@@ -19,6 +19,7 @@ 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.Companion.KAPT_SIGNATURE_ANNOTATION_FQNAME
import org.jetbrains.kotlin.kapt3.util.getPackageNameJava9Aware
class KaptStubLineInformation {
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
// (for instance, if this code is called inside the "enterTrees()")
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"
}
@@ -34,8 +34,10 @@ import org.jetbrains.kotlin.kapt3.KaptContext
import org.jetbrains.kotlin.kapt3.doAnnotationProcessing
import org.jetbrains.kotlin.kapt3.javac.KaptJavaFileObject
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.util.KaptLogger
import org.jetbrains.kotlin.kapt3.util.isJava9OrLater
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
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.addToStdlib.firstIsInstance
import java.io.File
import java.net.URL
import java.net.URLClassLoader
import java.nio.file.Files
import java.util.*
import java.util.concurrent.TimeUnit
import com.sun.tools.javac.util.List as JavacList
abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
@@ -88,6 +93,8 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
try {
check(kaptContext, javaFiles, txtFile, wholeFile)
} catch (e: Throwable) {
throw RuntimeException(e)
} finally {
javaFiles.forEach { it.delete() }
tempFiles.forEach { it.delete() }
@@ -115,7 +122,8 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
kaptContext.compiler.log.nerrors = 0
try {
val parsedJavaFiles = kaptContext.compiler.parseFiles(kaptContext.fileManager.getJavaFileObjectsFromFiles(allJavaFiles))
val parsedJavaFiles = kaptContext.parseJavaFiles(allJavaFiles)
for (file in parsedJavaFiles) {
// 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.
@@ -135,18 +143,70 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
wholeFile: File)
}
abstract class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test() {
internal companion object {
open class AbstractClassFileToSourceStubConverterTest : AbstractKotlinKapt3Test() {
companion object {
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 KAPT_METADATA_REGEX = "@kapt\\.internal\\.KaptMetadata\\((value = )?\"[^(].*?\"\\)".toRegex()
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()")
.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) {