Remove source annotations removing (not needed with KAPT3)

This commit is contained in:
Alexey Tsvetkov
2016-12-13 13:46:30 +03:00
parent 190c038ad8
commit 4fdca24db4
21 changed files with 14 additions and 398 deletions
@@ -1,68 +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.annotation
import org.jetbrains.kotlin.incremental.components.SourceRetentionAnnotationHandler
import java.io.*
import java.util.*
internal class SourceAnnotationsRegistry(private val file: File) : SourceRetentionAnnotationHandler {
private val mutableAnnotations: MutableSet<String> by lazy { readAnnotations() }
val annotations: Set<String>
get() = mutableAnnotations
override fun register(internalName: String) {
mutableAnnotations.add(internalName)
}
fun clear() {
mutableAnnotations.clear()
file.delete()
}
fun flush() {
if (mutableAnnotations.isEmpty()) {
file.delete()
return
}
if (!file.exists()) {
file.parentFile.mkdirs()
file.createNewFile()
}
ObjectOutputStream(BufferedOutputStream(file.outputStream())).use { out ->
out.writeInt(mutableAnnotations.size)
mutableAnnotations.forEach { out.writeUTF(it) }
}
}
private fun readAnnotations(): MutableSet<String> {
val result = HashSet<String>()
if (!file.exists()) return result
ObjectInputStream(BufferedInputStream(file.inputStream())).use { input ->
val size = input.readInt()
repeat(size) {
result.add(input.readUTF())
}
}
return result
}
}
@@ -1,78 +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.bytecode
import org.jetbrains.org.objectweb.asm.*
import java.io.File
import java.util.*
internal class AnnotationsRemover(annotations: Iterable<String>) {
private val annotations = annotations.mapTo(HashSet()) { "L$it;" }
fun transformClassFile(inputFile: File, outputFile: File) {
assert(inputFile.extension.toLowerCase() == "class") { "Expected class file: $inputFile" }
val bytes = inputFile.readBytes()
val reader = ClassReader(bytes)
val classWriter = ClassWriter(0)
val visitor = ClassAnnotationRemover(classWriter)
reader.accept(visitor, 0)
outputFile.writeBytes(classWriter.toByteArray())
}
inner class ClassAnnotationRemover(classVisitor: ClassVisitor) : ClassVisitor(Opcodes.ASM5, classVisitor) {
override fun visitAnnotation(desc: String?, visible: Boolean): AnnotationVisitor? =
checkAnnotation(desc) { super.visitAnnotation(desc, visible) }
override fun visitTypeAnnotation(typeRef: Int, typePath: TypePath?, desc: String?, visible: Boolean): AnnotationVisitor? =
checkAnnotation(desc) { super.visitTypeAnnotation(typeRef, typePath, desc, visible) }
override fun visitMethod(access: Int, name: String?, desc: String?, signature: String?, exceptions: Array<out String>?): MethodVisitor {
val methodVisitor = super.visitMethod(access, name, desc, signature, exceptions)
return MethodAnnotationRemover(methodVisitor)
}
override fun visitField(access: Int, name: String?, desc: String?, signature: String?, value: Any?): FieldVisitor {
val fieldVisitor = super.visitField(access, name, desc, signature, value)
return FieldAnnotationRemover(fieldVisitor)
}
}
inner class MethodAnnotationRemover(methodVisitor: MethodVisitor) : MethodVisitor(Opcodes.ASM5, methodVisitor) {
override fun visitAnnotation(desc: String?, visible: Boolean): AnnotationVisitor? =
checkAnnotation(desc) { super.visitAnnotation(desc, visible) }
override fun visitTypeAnnotation(typeRef: Int, typePath: TypePath?, desc: String?, visible: Boolean): AnnotationVisitor? =
checkAnnotation(desc) { super.visitTypeAnnotation(typeRef, typePath, desc, visible) }
override fun visitParameterAnnotation(parameter: Int, desc: String?, visible: Boolean): AnnotationVisitor? =
checkAnnotation(desc) { super.visitParameterAnnotation(parameter, desc, visible) }
}
inner class FieldAnnotationRemover(fieldVisitor: FieldVisitor) : FieldVisitor(Opcodes.ASM5, fieldVisitor) {
override fun visitAnnotation(desc: String?, visible: Boolean): AnnotationVisitor? =
checkAnnotation(desc) { super.visitAnnotation(desc, visible) }
override fun visitTypeAnnotation(typeRef: Int, typePath: TypePath?, desc: String?, visible: Boolean): AnnotationVisitor? =
checkAnnotation(desc) { super.visitTypeAnnotation(typeRef, typePath, desc, visible) }
}
private inline fun checkAnnotation(desc: String?, default: () -> AnnotationVisitor?): AnnotationVisitor? {
if (desc in annotations) return null
return default()
}
}
@@ -62,7 +62,7 @@ class Kapt3KotlinGradleSubplugin : KotlinGradleSubplugin<KotlinCompile> {
}
private val kotlinToKaptTasksMap = mutableMapOf<KotlinCompile, KaptTask>()
override fun isApplicable(project: Project, task: KotlinCompile) = Kapt3GradleSubplugin.isEnabled(project)
fun getKaptGeneratedDir(project: Project, sourceSetName: String): File {
@@ -23,7 +23,6 @@ import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import org.gradle.api.tasks.incremental.InputFileDetails
import org.jetbrains.kotlin.bytecode.AnnotationsRemover
import org.jetbrains.kotlin.gradle.plugin.kotlinDebug
import java.io.File
import java.io.ObjectInputStream
@@ -58,12 +57,6 @@ internal open class SyncOutputTask : DefaultTask() {
var kotlinOutputDir: File by Delegates.notNull()
var javaOutputDir: File by Delegates.notNull()
var kotlinTask: KotlinCompile by Delegates.notNull()
private val sourceAnnotations: Set<String> by lazy {
kotlinTask.sourceAnnotationsRegistry?.annotations ?: emptySet()
}
private val annotationsRemover by lazy {
AnnotationsRemover(sourceAnnotations)
}
// OutputDirectory needed for task to be incremental
@get:OutputDirectory
@@ -144,13 +137,7 @@ internal open class SyncOutputTask : DefaultTask() {
if (!fileInKotlinDir.isFile) return
fileInJavaDir.parentFile.mkdirs()
if (sourceAnnotations.isNotEmpty() && fileInKotlinDir.extension.toLowerCase() == "class") {
logger.kotlinDebug { "Removing source annotations from class: $fileInKotlinDir" }
annotationsRemover.transformClassFile(fileInKotlinDir, fileInJavaDir)
}
else {
fileInKotlinDir.copyTo(fileInJavaDir, overwrite = true)
}
fileInKotlinDir.copyTo(fileInJavaDir, overwrite = true)
timestamps[fileInJavaDir] = fileInJavaDir.lastModified()
@@ -27,7 +27,6 @@ import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import org.jetbrains.kotlin.annotation.AnnotationFileUpdater
import org.jetbrains.kotlin.annotation.SourceAnnotationsRegistry
import org.jetbrains.kotlin.cli.common.CLICompiler
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
@@ -165,8 +164,6 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
internal val pluginOptions = CompilerPluginOptions()
internal var artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider? = null
internal var artifactFile: File? = null
// created only if kapt2 is active
internal var sourceAnnotationsRegistry: SourceAnnotationsRegistry? = null
override fun findKotlinCompilerJar(project: Project): File? =
findKotlinJvmCompilerJar(project)
@@ -227,7 +224,6 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
cacheVersions,
reporter,
kaptAnnotationsFileUpdater,
sourceAnnotationsRegistry,
artifactDifferenceRegistryProvider,
artifactFile
)
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.incremental
import org.jetbrains.kotlin.annotation.AnnotationFileUpdater
import org.jetbrains.kotlin.annotation.SourceAnnotationsRegistry
import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.build.GeneratedJvmClass
import org.jetbrains.kotlin.cli.common.ExitCode
@@ -97,7 +96,6 @@ internal class IncrementalJvmCompilerRunner(
private val cacheVersions: List<CacheVersion>,
private val reporter: IncReporter,
private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
private val sourceAnnotationsRegistry: SourceAnnotationsRegistry? = null,
private val artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider? = null,
private val artifactFile: File? = null
) {
@@ -394,7 +392,6 @@ internal class IncrementalJvmCompilerRunner(
}
if (exitCode == ExitCode.OK) {
sourceAnnotationsRegistry?.flush()
cacheVersions.forEach { it.saveIfNeeded() }
}
@@ -462,7 +459,6 @@ internal class IncrementalJvmCompilerRunner(
val outputItemCollector = OutputItemsCollectorImpl()
@Suppress("NAME_SHADOWING")
val messageCollector = MessageCollectorWrapper(messageCollector, outputItemCollector)
sourceAnnotationsRegistry?.clear()
try {
val incrementalCaches = makeIncrementalCachesMap(targets, { listOf<TargetId>() }, getIncrementalCache, { this })
@@ -473,7 +469,7 @@ internal class IncrementalJvmCompilerRunner(
reporter.report { "compiling with args: ${ArgumentUtils.convertArgumentsToStringList(args)}" }
reporter.report { "compiling with classpath: ${classpath.toList().sorted().joinToString()}" }
val compileServices = makeCompileServices(incrementalCaches, lookupTracker, compilationCanceledStatus, sourceAnnotationsRegistry)
val compileServices = makeCompileServices(incrementalCaches, lookupTracker, compilationCanceledStatus)
val exitCode = compiler.exec(messageCollector, compileServices, args)
val generatedFiles = outputItemCollector.generatedFiles(targets, targets.first(), {sourcesToCompile}, {outputDir})
reporter.reportCompileIteration(sourcesToCompile, exitCode)
@@ -1,108 +0,0 @@
package org.jetbrains.kotlin.bytecode
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.com.intellij.openapi.util.io.FileUtil.createTempDirectory
import org.jetbrains.kotlin.gradle.util.checkBytecodeContains
import org.jetbrains.kotlin.gradle.util.checkBytecodeNotContains
import org.junit.After
import org.junit.Test
import org.junit.Assert.*
import org.junit.Before
import java.io.*
import kotlin.properties.Delegates
class AnnotationsRemoverTest {
private var workingDir: File by Delegates.notNull()
@Before
fun setUp() {
workingDir = createTempDirectory(AnnotationsRemoverTest::class.java.simpleName, null)
}
@After
fun tearDown() {
workingDir.deleteRecursively()
}
@Test
fun testRemoveAnnotations() {
// initial build
val sourceDir = File(workingDir, "src").apply { mkdirs() }
val annotationsKt = File(sourceDir, "annotations.kt").apply {
writeText("""
package foo
annotation class Ann1
annotation class Ann2
annotation class Ann3
annotation class Ann4
annotation class Ann5
annotation class NotRemovableAnn1
annotation class NotRemovableAnn2
annotation class NotRemovableAnn3
annotation class NotRemovableAnn4
annotation class NotRemovableAnn5
""".trimIndent())
}
File(sourceDir, "A.kt").apply {
writeText("""
import foo.*
@Ann1
@NotRemovableAnn1
class A {
@get:Ann2
@field:Ann3
@get:NotRemovableAnn2
@field:NotRemovableAnn3
val i = 10
@Ann4
@NotRemovableAnn4
fun m(@Ann5 @NotRemovableAnn5 x: Int) {}
}
""".trimIndent())
}
val annClassRegex = "annotation class (Ann\\d)".toRegex()
val annotationsToRemove = annClassRegex.findAll(annotationsKt.readText()).toList().map { "foo/${it.groupValues[1]}" }
assertEquals(5, annotationsToRemove.size)
val notRemovableAnnClassRegex = "annotation class (NotRemovableAnn\\d)".toRegex()
val notRemovableAnns = notRemovableAnnClassRegex.findAll(annotationsKt.readText()).toList().map { "foo/${it.groupValues[1]}" }
assertEquals(5, notRemovableAnns.size)
val outDir = File(workingDir, "out").apply { mkdirs() }
compileAll(sourceDir, outDir)
val aClass = File(outDir, "A.class")
assert(aClass.exists()) { "$aClass does not exist" }
checkBytecodeContains(aClass, annotationsToRemove)
// remove annotations
val transformedOut = File(workingDir, "transformed").apply { mkdirs() }
val aTransformedClass = File(transformedOut, "A.class")
val remover = AnnotationsRemover(annotationsToRemove)
remover.transformClassFile(aClass, aTransformedClass)
checkBytecodeNotContains(aTransformedClass, annotationsToRemove)
checkBytecodeContains(aTransformedClass, notRemovableAnns)
}
private fun compileAll(inputDir: File, outputDir: File) {
val ktFiles = inputDir.walk()
.filter { it.isFile && it.extension.toLowerCase() == "kt" }
.map { it.absolutePath }
.toList().toTypedArray()
val byteOut = ByteArrayOutputStream()
val exitCode = PrintStream(byteOut).use { err ->
K2JVMCompiler().exec(err, *ktFiles, "-d", outputDir.absolutePath, "-Xadd-compiler-builtins")
}
if (exitCode != ExitCode.OK) {
System.err.print(byteOut.toString())
}
assertEquals(ExitCode.OK, exitCode)
}
}