Skip to content

Commit e34d72a

Browse files
committed
Kotlin: stub trap .class files when extracting a class from Kotlin source
1 parent 2c50014 commit e34d72a

File tree

2 files changed

+81
-61
lines changed

2 files changed

+81
-61
lines changed

java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt

Lines changed: 78 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.ir.util.isFileClass
1111
import org.jetbrains.kotlin.ir.util.packageFqName
1212
import org.jetbrains.kotlin.ir.util.parentClassOrNull
1313
import org.jetbrains.kotlin.name.FqName
14+
import java.io.BufferedWriter
1415
import java.io.File
1516
import java.util.ArrayList
1617
import java.util.HashSet
@@ -25,6 +26,10 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
2526
val propertySignature = ";property"
2627
val fieldSignature = ";field"
2728

29+
val output = OdasaOutput(false, logger).also {
30+
it.setCurrentSourceFile(File(sourceFilePath))
31+
}
32+
2833
fun extractLater(d: IrDeclarationWithName, signature: String): Boolean {
2934
if (d !is IrClass && !isExternalFileClassMember(d)) {
3035
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
3742
}
3843
fun extractLater(c: IrClass) = extractLater(c, "")
3944

45+
fun noteClassSourceExtractedTo(c: IrClass, sourceFile: String) {
46+
extractDecl(c, "") { trapFileBW, _, _, _ ->
47+
val tw = TrapWriter(logger.loggerBase, TrapLabelManager(), trapFileBW, diagnosticTrapWriter)
48+
tw.writeComment(".class trap file stubbed as source is extracted to $sourceFile")
49+
tw.writeComment("Part of invocation $invocationTrapFile")
50+
}
51+
}
52+
53+
fun extractDecl(irDecl: IrDeclaration, possiblyLongSignature: String, extractorFn: (BufferedWriter, String, String, OdasaOutput.TrapFileManager) -> Unit) {
54+
// In order to avoid excessively long signatures which can lead to trap file names longer than the filesystem
55+
// limit, we truncate and add a hash to preserve uniqueness if necessary.
56+
val signature = if (possiblyLongSignature.length > 100) {
57+
possiblyLongSignature.substring(0, 92) + "#" + StringDigestor.digest(possiblyLongSignature).substring(0, 8)
58+
} else { possiblyLongSignature }
59+
output.getTrapLockerForDecl(irDecl, signature).useAC { locker ->
60+
locker.trapFileManager.useAC { manager ->
61+
val shortName = when(irDecl) {
62+
is IrDeclarationWithName -> irDecl.name.asString()
63+
else -> "(unknown name)"
64+
}
65+
if(manager == null) {
66+
logger.info("Skipping extracting external decl $shortName")
67+
} else {
68+
val trapFile = manager.file
69+
val trapTmpFile = File.createTempFile("${trapFile.nameWithoutExtension}.", ".${trapFile.extension}.tmp", trapFile.parentFile)
70+
71+
val containingClass = getContainingClassOrSelf(irDecl)
72+
if (containingClass == null) {
73+
logger.errorElement("Unable to get containing class", irDecl)
74+
return
75+
}
76+
val binaryPath = getIrClassBinaryPath(containingClass)
77+
try {
78+
GZIPOutputStream(trapTmpFile.outputStream()).bufferedWriter().use {
79+
extractorFn(it, binaryPath, signature, manager)
80+
}
81+
82+
if (!trapTmpFile.renameTo(trapFile)) {
83+
logger.error("Failed to rename $trapTmpFile to $trapFile")
84+
}
85+
} catch (e: Exception) {
86+
manager.setHasError()
87+
logger.error("Failed to extract '$shortName'. Partial TRAP file location is $trapTmpFile", e)
88+
}
89+
}
90+
}
91+
}
92+
}
93+
4094
fun extractExternalClasses() {
41-
val output = OdasaOutput(false, logger)
42-
output.setCurrentSourceFile(File(sourceFilePath))
4395
do {
4496
val nextBatch = ArrayList(externalDeclWorkList)
4597
externalDeclWorkList.clear()
4698
nextBatch.forEach { workPair ->
4799
val (irDecl, possiblyLongSignature) = workPair
48-
// In order to avoid excessively long signatures which can lead to trap file names longer than the filesystem
49-
// limit, we truncate and add a hash to preserve uniqueness if necessary.
50-
val signature = if (possiblyLongSignature.length > 100) {
51-
possiblyLongSignature.substring(0, 92) + "#" + StringDigestor.digest(possiblyLongSignature).substring(0, 8)
52-
} else { possiblyLongSignature }
53-
output.getTrapLockerForDecl(irDecl, signature).useAC { locker ->
54-
locker.trapFileManager.useAC { manager ->
55-
val shortName = when(irDecl) {
56-
is IrDeclarationWithName -> irDecl.name.asString()
57-
else -> "(unknown name)"
58-
}
59-
if(manager == null) {
60-
logger.info("Skipping extracting external decl $shortName")
61-
} else {
62-
val trapFile = manager.file
63-
val trapTmpFile = File.createTempFile("${trapFile.nameWithoutExtension}.", ".${trapFile.extension}.tmp", trapFile.parentFile)
64-
65-
val containingClass = getContainingClassOrSelf(irDecl)
66-
if (containingClass == null) {
67-
logger.errorElement("Unable to get containing class", irDecl)
68-
return
69-
}
70-
val binaryPath = getIrClassBinaryPath(containingClass)
71-
try {
72-
GZIPOutputStream(trapTmpFile.outputStream()).bufferedWriter().use { trapFileBW ->
73-
// We want our comments to be the first thing in the file,
74-
// so start off with a mere TrapWriter
75-
val tw = TrapWriter(logger.loggerBase, TrapLabelManager(), trapFileBW, diagnosticTrapWriter)
76-
tw.writeComment("Generated by the CodeQL Kotlin extractor for external dependencies")
77-
tw.writeComment("Part of invocation $invocationTrapFile")
78-
if (signature != possiblyLongSignature) {
79-
tw.writeComment("Function signature abbreviated; full signature is: $possiblyLongSignature")
80-
}
81-
// Now elevate to a SourceFileTrapWriter, and populate the
82-
// file information if needed:
83-
val ftw = tw.makeFileTrapWriter(binaryPath, true)
84-
85-
val fileExtractor = KotlinFileExtractor(logger, ftw, null, binaryPath, manager, this, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
100+
extractDecl(irDecl, possiblyLongSignature) { trapFileBW, binaryPath, signature, manager ->
101+
// We want our comments to be the first thing in the file,
102+
// so start off with a mere TrapWriter
103+
val tw = TrapWriter(logger.loggerBase, TrapLabelManager(), trapFileBW, diagnosticTrapWriter)
104+
tw.writeComment("Generated by the CodeQL Kotlin extractor for external dependencies")
105+
tw.writeComment("Part of invocation $invocationTrapFile")
106+
if (signature != possiblyLongSignature) {
107+
tw.writeComment("Function signature abbreviated; full signature is: $possiblyLongSignature")
108+
}
109+
// Now elevate to a SourceFileTrapWriter, and populate the
110+
// file information if needed:
111+
val ftw = tw.makeFileTrapWriter(binaryPath, true)
86112

87-
if (irDecl is IrClass) {
88-
// Populate a location and compilation-unit package for the file. This is similar to
89-
// the beginning of `KotlinFileExtractor.extractFileContents` but without an `IrFile`
90-
// to start from.
91-
val pkg = irDecl.packageFqName?.asString() ?: ""
92-
val pkgId = fileExtractor.extractPackage(pkg)
93-
ftw.writeHasLocation(ftw.fileId, ftw.getWholeFileLocation())
94-
ftw.writeCupackage(ftw.fileId, pkgId)
113+
val fileExtractor = KotlinFileExtractor(logger, ftw, null, binaryPath, manager, this, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
95114

96-
fileExtractor.extractClassSource(irDecl, extractDeclarations = !irDecl.isFileClass, extractStaticInitializer = false, extractPrivateMembers = false, extractFunctionBodies = false)
97-
} else {
98-
fileExtractor.extractDeclaration(irDecl, extractPrivateMembers = false, extractFunctionBodies = false)
99-
}
100-
}
115+
if (irDecl is IrClass) {
116+
// Populate a location and compilation-unit package for the file. This is similar to
117+
// the beginning of `KotlinFileExtractor.extractFileContents` but without an `IrFile`
118+
// to start from.
119+
val pkg = irDecl.packageFqName?.asString() ?: ""
120+
val pkgId = fileExtractor.extractPackage(pkg)
121+
ftw.writeHasLocation(ftw.fileId, ftw.getWholeFileLocation())
122+
ftw.writeCupackage(ftw.fileId, pkgId)
101123

102-
if (!trapTmpFile.renameTo(trapFile)) {
103-
logger.error("Failed to rename $trapTmpFile to $trapFile")
104-
}
105-
} catch (e: Exception) {
106-
manager.setHasError()
107-
logger.error("Failed to extract '$shortName'. Partial TRAP file location is $trapTmpFile", e)
108-
}
109-
}
124+
fileExtractor.extractClassSource(irDecl, extractDeclarations = !irDecl.isFileClass, extractStaticInitializer = false, extractPrivateMembers = false, extractFunctionBodies = false)
125+
} else {
126+
fileExtractor.extractDeclaration(irDecl, extractPrivateMembers = false, extractFunctionBodies = false)
110127
}
111128
}
112129
}

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,9 @@ open class KotlinFileExtractor(
516516

517517
linesOfCode?.linesOfCodeInDeclaration(c, id)
518518

519+
if (extractFunctionBodies)
520+
externalClassExtractor.noteClassSourceExtractedTo(c, tw.filePath)
521+
519522
return id
520523
}
521524
}

0 commit comments

Comments
 (0)