mirror of
https://github.com/github/codeql.git
synced 2025-12-18 18:10:39 +01:00
Kotlin: stub trap .class files when extracting a class from Kotlin source
This commit is contained in:
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.ir.util.isFileClass
|
||||
import org.jetbrains.kotlin.ir.util.packageFqName
|
||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.util.ArrayList
|
||||
import java.util.HashSet
|
||||
@@ -25,6 +26,10 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
|
||||
val propertySignature = ";property"
|
||||
val fieldSignature = ";field"
|
||||
|
||||
val output = OdasaOutput(false, logger).also {
|
||||
it.setCurrentSourceFile(File(sourceFilePath))
|
||||
}
|
||||
|
||||
fun extractLater(d: IrDeclarationWithName, signature: String): Boolean {
|
||||
if (d !is IrClass && !isExternalFileClassMember(d)) {
|
||||
logger.errorElement("External declaration is neither a class, nor a top-level declaration", d)
|
||||
@@ -37,76 +42,88 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
|
||||
}
|
||||
fun extractLater(c: IrClass) = extractLater(c, "")
|
||||
|
||||
fun noteClassSourceExtractedTo(c: IrClass, sourceFile: String) {
|
||||
extractDecl(c, "") { trapFileBW, _, _, _ ->
|
||||
val tw = TrapWriter(logger.loggerBase, TrapLabelManager(), trapFileBW, diagnosticTrapWriter)
|
||||
tw.writeComment(".class trap file stubbed as source is extracted to $sourceFile")
|
||||
tw.writeComment("Part of invocation $invocationTrapFile")
|
||||
}
|
||||
}
|
||||
|
||||
fun extractDecl(irDecl: IrDeclaration, possiblyLongSignature: String, extractorFn: (BufferedWriter, String, String, OdasaOutput.TrapFileManager) -> Unit) {
|
||||
// In order to avoid excessively long signatures which can lead to trap file names longer than the filesystem
|
||||
// limit, we truncate and add a hash to preserve uniqueness if necessary.
|
||||
val signature = if (possiblyLongSignature.length > 100) {
|
||||
possiblyLongSignature.substring(0, 92) + "#" + StringDigestor.digest(possiblyLongSignature).substring(0, 8)
|
||||
} else { possiblyLongSignature }
|
||||
output.getTrapLockerForDecl(irDecl, signature).useAC { locker ->
|
||||
locker.trapFileManager.useAC { manager ->
|
||||
val shortName = when(irDecl) {
|
||||
is IrDeclarationWithName -> irDecl.name.asString()
|
||||
else -> "(unknown name)"
|
||||
}
|
||||
if(manager == null) {
|
||||
logger.info("Skipping extracting external decl $shortName")
|
||||
} else {
|
||||
val trapFile = manager.file
|
||||
val trapTmpFile = File.createTempFile("${trapFile.nameWithoutExtension}.", ".${trapFile.extension}.tmp", trapFile.parentFile)
|
||||
|
||||
val containingClass = getContainingClassOrSelf(irDecl)
|
||||
if (containingClass == null) {
|
||||
logger.errorElement("Unable to get containing class", irDecl)
|
||||
return
|
||||
}
|
||||
val binaryPath = getIrClassBinaryPath(containingClass)
|
||||
try {
|
||||
GZIPOutputStream(trapTmpFile.outputStream()).bufferedWriter().use {
|
||||
extractorFn(it, binaryPath, signature, manager)
|
||||
}
|
||||
|
||||
if (!trapTmpFile.renameTo(trapFile)) {
|
||||
logger.error("Failed to rename $trapTmpFile to $trapFile")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
manager.setHasError()
|
||||
logger.error("Failed to extract '$shortName'. Partial TRAP file location is $trapTmpFile", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun extractExternalClasses() {
|
||||
val output = OdasaOutput(false, logger)
|
||||
output.setCurrentSourceFile(File(sourceFilePath))
|
||||
do {
|
||||
val nextBatch = ArrayList(externalDeclWorkList)
|
||||
externalDeclWorkList.clear()
|
||||
nextBatch.forEach { workPair ->
|
||||
val (irDecl, possiblyLongSignature) = workPair
|
||||
// In order to avoid excessively long signatures which can lead to trap file names longer than the filesystem
|
||||
// limit, we truncate and add a hash to preserve uniqueness if necessary.
|
||||
val signature = if (possiblyLongSignature.length > 100) {
|
||||
possiblyLongSignature.substring(0, 92) + "#" + StringDigestor.digest(possiblyLongSignature).substring(0, 8)
|
||||
} else { possiblyLongSignature }
|
||||
output.getTrapLockerForDecl(irDecl, signature).useAC { locker ->
|
||||
locker.trapFileManager.useAC { manager ->
|
||||
val shortName = when(irDecl) {
|
||||
is IrDeclarationWithName -> irDecl.name.asString()
|
||||
else -> "(unknown name)"
|
||||
}
|
||||
if(manager == null) {
|
||||
logger.info("Skipping extracting external decl $shortName")
|
||||
} else {
|
||||
val trapFile = manager.file
|
||||
val trapTmpFile = File.createTempFile("${trapFile.nameWithoutExtension}.", ".${trapFile.extension}.tmp", trapFile.parentFile)
|
||||
extractDecl(irDecl, possiblyLongSignature) { trapFileBW, binaryPath, signature, manager ->
|
||||
// We want our comments to be the first thing in the file,
|
||||
// so start off with a mere TrapWriter
|
||||
val tw = TrapWriter(logger.loggerBase, TrapLabelManager(), trapFileBW, diagnosticTrapWriter)
|
||||
tw.writeComment("Generated by the CodeQL Kotlin extractor for external dependencies")
|
||||
tw.writeComment("Part of invocation $invocationTrapFile")
|
||||
if (signature != possiblyLongSignature) {
|
||||
tw.writeComment("Function signature abbreviated; full signature is: $possiblyLongSignature")
|
||||
}
|
||||
// Now elevate to a SourceFileTrapWriter, and populate the
|
||||
// file information if needed:
|
||||
val ftw = tw.makeFileTrapWriter(binaryPath, true)
|
||||
|
||||
val containingClass = getContainingClassOrSelf(irDecl)
|
||||
if (containingClass == null) {
|
||||
logger.errorElement("Unable to get containing class", irDecl)
|
||||
return
|
||||
}
|
||||
val binaryPath = getIrClassBinaryPath(containingClass)
|
||||
try {
|
||||
GZIPOutputStream(trapTmpFile.outputStream()).bufferedWriter().use { trapFileBW ->
|
||||
// We want our comments to be the first thing in the file,
|
||||
// so start off with a mere TrapWriter
|
||||
val tw = TrapWriter(logger.loggerBase, TrapLabelManager(), trapFileBW, diagnosticTrapWriter)
|
||||
tw.writeComment("Generated by the CodeQL Kotlin extractor for external dependencies")
|
||||
tw.writeComment("Part of invocation $invocationTrapFile")
|
||||
if (signature != possiblyLongSignature) {
|
||||
tw.writeComment("Function signature abbreviated; full signature is: $possiblyLongSignature")
|
||||
}
|
||||
// Now elevate to a SourceFileTrapWriter, and populate the
|
||||
// file information if needed:
|
||||
val ftw = tw.makeFileTrapWriter(binaryPath, true)
|
||||
val fileExtractor = KotlinFileExtractor(logger, ftw, null, binaryPath, manager, this, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
|
||||
|
||||
val fileExtractor = KotlinFileExtractor(logger, ftw, null, binaryPath, manager, this, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
|
||||
if (irDecl is IrClass) {
|
||||
// Populate a location and compilation-unit package for the file. This is similar to
|
||||
// the beginning of `KotlinFileExtractor.extractFileContents` but without an `IrFile`
|
||||
// to start from.
|
||||
val pkg = irDecl.packageFqName?.asString() ?: ""
|
||||
val pkgId = fileExtractor.extractPackage(pkg)
|
||||
ftw.writeHasLocation(ftw.fileId, ftw.getWholeFileLocation())
|
||||
ftw.writeCupackage(ftw.fileId, pkgId)
|
||||
|
||||
if (irDecl is IrClass) {
|
||||
// Populate a location and compilation-unit package for the file. This is similar to
|
||||
// the beginning of `KotlinFileExtractor.extractFileContents` but without an `IrFile`
|
||||
// to start from.
|
||||
val pkg = irDecl.packageFqName?.asString() ?: ""
|
||||
val pkgId = fileExtractor.extractPackage(pkg)
|
||||
ftw.writeHasLocation(ftw.fileId, ftw.getWholeFileLocation())
|
||||
ftw.writeCupackage(ftw.fileId, pkgId)
|
||||
|
||||
fileExtractor.extractClassSource(irDecl, extractDeclarations = !irDecl.isFileClass, extractStaticInitializer = false, extractPrivateMembers = false, extractFunctionBodies = false)
|
||||
} else {
|
||||
fileExtractor.extractDeclaration(irDecl, extractPrivateMembers = false, extractFunctionBodies = false)
|
||||
}
|
||||
}
|
||||
|
||||
if (!trapTmpFile.renameTo(trapFile)) {
|
||||
logger.error("Failed to rename $trapTmpFile to $trapFile")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
manager.setHasError()
|
||||
logger.error("Failed to extract '$shortName'. Partial TRAP file location is $trapTmpFile", e)
|
||||
}
|
||||
}
|
||||
fileExtractor.extractClassSource(irDecl, extractDeclarations = !irDecl.isFileClass, extractStaticInitializer = false, extractPrivateMembers = false, extractFunctionBodies = false)
|
||||
} else {
|
||||
fileExtractor.extractDeclaration(irDecl, extractPrivateMembers = false, extractFunctionBodies = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,6 +516,9 @@ open class KotlinFileExtractor(
|
||||
|
||||
linesOfCode?.linesOfCodeInDeclaration(c, id)
|
||||
|
||||
if (extractFunctionBodies)
|
||||
externalClassExtractor.noteClassSourceExtractedTo(c, tw.filePath)
|
||||
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user