mirror of
https://github.com/github/codeql.git
synced 2025-12-21 11:16:30 +01:00
Kotlin: Fix extraction when 2 invocations produce the same TRAP file
The second invocation was failing with a "file already exists" error. I've also added a checkTrapIdentical flag, which is enabled for now. This means that if 2 invocations write the same TRAP file, we will awrn if they are not identical. It may be that this produces false positives, but we can look at that if it happens.
This commit is contained in:
@@ -16,6 +16,13 @@ class KotlinExtractorCommandLineProcessor : CommandLineProcessor {
|
||||
description = "Extractor will append invocation-related TRAP to this file",
|
||||
required = true,
|
||||
allowMultipleOccurrences = false
|
||||
),
|
||||
CliOption(
|
||||
optionName = OPTION_CHECK_TRAP_IDENTICAL,
|
||||
valueDescription = "Check whether different invocations produce identical TRAP",
|
||||
description = "Check whether different invocations produce identical TRAP",
|
||||
required = false,
|
||||
allowMultipleOccurrences = false
|
||||
)
|
||||
)
|
||||
|
||||
@@ -25,9 +32,17 @@ class KotlinExtractorCommandLineProcessor : CommandLineProcessor {
|
||||
configuration: CompilerConfiguration
|
||||
) = when (option.optionName) {
|
||||
"invocationTrapFile" -> configuration.put(KEY_INVOCATION_TRAP_FILE, value)
|
||||
"checkTrapIdentical" ->
|
||||
when (value) {
|
||||
"true" -> configuration.put(KEY_CHECK_TRAP_IDENTICAL, true)
|
||||
"fale" -> configuration.put(KEY_CHECK_TRAP_IDENTICAL, false)
|
||||
else -> error("kotlin extractor: Bad argument $value for checkTrapIdentical")
|
||||
}
|
||||
else -> error("kotlin extractor: Bad option: ${option.optionName}")
|
||||
}
|
||||
}
|
||||
|
||||
private val OPTION_INVOCATION_TRAP_FILE = "invocationTrapFile"
|
||||
val KEY_INVOCATION_TRAP_FILE = CompilerConfigurationKey<String>(OPTION_INVOCATION_TRAP_FILE)
|
||||
private val OPTION_CHECK_TRAP_IDENTICAL = "checkTrapIdentical"
|
||||
val KEY_CHECK_TRAP_IDENTICAL= CompilerConfigurationKey<Boolean>(OPTION_CHECK_TRAP_IDENTICAL)
|
||||
|
||||
@@ -14,6 +14,7 @@ class KotlinExtractorComponentRegistrar : ComponentRegistrar {
|
||||
if(invocationTrapFile == null) {
|
||||
throw Exception("Required argument for TRAP invocation file not given")
|
||||
}
|
||||
IrGenerationExtension.registerExtension(project, KotlinExtractorExtension(invocationTrapFile))
|
||||
val checkTrapIdentical = configuration[KEY_CHECK_TRAP_IDENTICAL]
|
||||
IrGenerationExtension.registerExtension(project, KotlinExtractorExtension(invocationTrapFile, checkTrapIdentical ?: false))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.io.FileOutputStream
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
@@ -28,7 +29,7 @@ import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
|
||||
class KotlinExtractorExtension(private val invocationTrapFile: String) : IrGenerationExtension {
|
||||
class KotlinExtractorExtension(private val invocationTrapFile: String, private val checkTrapIdentical: Boolean) : IrGenerationExtension {
|
||||
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||
// This default should be kept in sync with language-packs/java/tools/kotlin-extractor
|
||||
val trapDir = File(System.getenv("CODEQL_EXTRACTOR_JAVA_TRAP_DIR").takeUnless { it.isNullOrEmpty() } ?: "kotlin-extractor/trap")
|
||||
@@ -40,7 +41,7 @@ class KotlinExtractorExtension(private val invocationTrapFile: String) : IrGener
|
||||
logger.flush()
|
||||
val srcDir = File(System.getenv("CODEQL_EXTRACTOR_JAVA_SOURCE_ARCHIVE_DIR").takeUnless { it.isNullOrEmpty() } ?: "kotlin-extractor/src")
|
||||
srcDir.mkdirs()
|
||||
moduleFragment.files.map { doFile(logger, trapDir, srcDir, it) }
|
||||
moduleFragment.files.map { doFile(checkTrapIdentical, logger, trapDir, srcDir, it) }
|
||||
logger.printLimitedWarningCounts()
|
||||
// We don't want the compiler to continue and generate class
|
||||
// files etc, so we just exit when we are finished extracting.
|
||||
@@ -194,7 +195,23 @@ class TrapWriter (
|
||||
}
|
||||
}
|
||||
|
||||
fun doFile(logger: Logger, trapDir: File, srcDir: File, declaration: IrFile) {
|
||||
private fun identical(f1: File, f2: File): Boolean {
|
||||
f1.bufferedReader().use { bw1 ->
|
||||
f2.bufferedReader().use { bw2 ->
|
||||
while(true) {
|
||||
val l1 = bw1.readLine()
|
||||
val l2 = bw2.readLine()
|
||||
if (l1 != l2) {
|
||||
return false
|
||||
} else if (l1 == null) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun doFile(checkTrapIdentical: Boolean, logger: Logger, trapDir: File, srcDir: File, declaration: IrFile) {
|
||||
val filePath = declaration.path
|
||||
logger.info("Extracting file $filePath")
|
||||
logger.flush()
|
||||
@@ -210,12 +227,33 @@ fun doFile(logger: Logger, trapDir: File, srcDir: File, declaration: IrFile) {
|
||||
val trapFile = File("$trapDir/$filePath.trap")
|
||||
val trapFileDir = trapFile.getParentFile()
|
||||
trapFileDir.mkdirs()
|
||||
trapFile.bufferedWriter().use { trapFileBW ->
|
||||
val tw = TrapWriter(fileLabel, trapFileBW, declaration)
|
||||
val id: Label<DbFile> = tw.getLabelFor(fileLabel)
|
||||
tw.writeFiles(id, filePath, basename, extension, 0)
|
||||
val fileExtractor = KotlinFileExtractor(logger, tw, declaration)
|
||||
fileExtractor.extractFile(id)
|
||||
if (checkTrapIdentical || !trapFile.exists()) {
|
||||
val trapTmpFile = File.createTempFile("$filePath.", ".trap.tmp", trapDir)
|
||||
trapTmpFile.bufferedWriter().use { trapFileBW ->
|
||||
val tw = TrapWriter(fileLabel, trapFileBW, declaration)
|
||||
val id: Label<DbFile> = tw.getLabelFor(fileLabel)
|
||||
tw.writeFiles(id, filePath, basename, extension, 0)
|
||||
val fileExtractor = KotlinFileExtractor(logger, tw, declaration)
|
||||
fileExtractor.extractFile(id)
|
||||
}
|
||||
if (checkTrapIdentical && trapFile.exists()) {
|
||||
if(identical(trapTmpFile, trapFile)) {
|
||||
if(!trapTmpFile.delete()) {
|
||||
logger.warn("Failed to delete $trapTmpFile")
|
||||
}
|
||||
} else {
|
||||
val trapDifferentFile = File.createTempFile("$filePath.", ".trap.different", trapDir)
|
||||
if(trapTmpFile.renameTo(trapDifferentFile)) {
|
||||
logger.warn("TRAP difference: $trapFile vs $trapDifferentFile")
|
||||
} else {
|
||||
logger.warn("Failed to rename $trapTmpFile to $trapFile")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(!trapTmpFile.renameTo(trapFile)) {
|
||||
logger.warn("Failed to rename $trapTmpFile to $trapFile")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user