1
1
package com.github.codeql
2
2
3
- import com.github.codeql.utils.isExternalDeclaration
4
3
import com.github.codeql.utils.isExternalFileClassMember
5
4
import com.semmle.extractor.java.OdasaOutput
6
5
import com.semmle.util.data.StringDigestor
7
6
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
7
+ import org.jetbrains.kotlin.ir.IrElement
8
8
import org.jetbrains.kotlin.ir.declarations.*
9
- import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
10
9
import org.jetbrains.kotlin.ir.util.isFileClass
11
10
import org.jetbrains.kotlin.ir.util.packageFqName
12
- import org.jetbrains.kotlin.ir.util.parentClassOrNull
13
- import org.jetbrains.kotlin.name.FqName
11
+ import java.io.BufferedWriter
14
12
import java.io.File
15
13
import java.util.ArrayList
16
14
import java.util.HashSet
@@ -25,87 +23,103 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
25
23
val propertySignature = " ;property"
26
24
val fieldSignature = " ;field"
27
25
26
+ val output = OdasaOutput (false , logger).also {
27
+ it.setCurrentSourceFile(File (sourceFilePath))
28
+ }
29
+
28
30
fun extractLater (d : IrDeclarationWithName , signature : String ): Boolean {
29
31
if (d !is IrClass && ! isExternalFileClassMember(d)) {
30
32
logger.errorElement(" External declaration is neither a class, nor a top-level declaration" , d)
31
33
return false
32
34
}
33
- val declBinaryName = declBinaryNames.getOrPut(d) { getIrDeclBinaryName (d) }
35
+ val declBinaryName = declBinaryNames.getOrPut(d) { getIrElementBinaryName (d) }
34
36
val ret = externalDeclsDone.add(Pair (declBinaryName, signature))
35
37
if (ret) externalDeclWorkList.add(Pair (d, signature))
36
38
return ret
37
39
}
38
40
fun extractLater (c : IrClass ) = extractLater(c, " " )
39
41
42
+ fun writeStubTrapFile (e : IrElement , signature : String = "") {
43
+ extractElement(e, signature, true ) { trapFileBW, _, _ ->
44
+ trapFileBW.write(" // Trap file stubbed because this declaration was extracted from source in $sourceFilePath \n " )
45
+ trapFileBW.write(" // Part of invocation $invocationTrapFile \n " )
46
+ }
47
+ }
48
+
49
+ private fun extractElement (element : IrElement , possiblyLongSignature : String , fromSource : Boolean , extractorFn : (BufferedWriter , String , OdasaOutput .TrapFileManager ) -> Unit ) {
50
+ // In order to avoid excessively long signatures which can lead to trap file names longer than the filesystem
51
+ // limit, we truncate and add a hash to preserve uniqueness if necessary.
52
+ val signature = if (possiblyLongSignature.length > 100 ) {
53
+ possiblyLongSignature.substring(0 , 92 ) + " #" + StringDigestor .digest(possiblyLongSignature).substring(0 , 8 )
54
+ } else { possiblyLongSignature }
55
+ output.getTrapLockerForDecl(element, signature, fromSource).useAC { locker ->
56
+ locker.trapFileManager.useAC { manager ->
57
+ val shortName = when (element) {
58
+ is IrDeclarationWithName -> element.name.asString()
59
+ is IrFile -> element.name
60
+ else -> " (unknown name)"
61
+ }
62
+ if (manager == null ) {
63
+ logger.info(" Skipping extracting external decl $shortName " )
64
+ } else {
65
+ val trapFile = manager.file
66
+ val trapTmpFile = File .createTempFile(" ${trapFile.nameWithoutExtension} ." , " .${trapFile.extension} .tmp" , trapFile.parentFile)
67
+ try {
68
+ GZIPOutputStream (trapTmpFile.outputStream()).bufferedWriter().use {
69
+ extractorFn(it, signature, manager)
70
+ }
71
+
72
+ if (! trapTmpFile.renameTo(trapFile)) {
73
+ logger.error(" Failed to rename $trapTmpFile to $trapFile " )
74
+ }
75
+ } catch (e: Exception ) {
76
+ manager.setHasError()
77
+ logger.error(" Failed to extract '$shortName '. Partial TRAP file location is $trapTmpFile " , e)
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+
40
84
fun extractExternalClasses () {
41
- val output = OdasaOutput (false , logger)
42
- output.setCurrentSourceFile(File (sourceFilePath))
43
85
do {
44
86
val nextBatch = ArrayList (externalDeclWorkList)
45
87
externalDeclWorkList.clear()
46
88
nextBatch.forEach { workPair ->
47
89
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 )
90
+ extractElement(irDecl, possiblyLongSignature, false ) { trapFileBW, signature, manager ->
91
+ val containingClass = getContainingClassOrSelf(irDecl)
92
+ if (containingClass == null ) {
93
+ logger.errorElement(" Unable to get containing class" , irDecl)
94
+ } else {
95
+ val binaryPath = getIrClassBinaryPath(containingClass)
84
96
85
- val fileExtractor = KotlinFileExtractor (logger, ftw, null , binaryPath, manager, this , primitiveTypeMapping, pluginContext, KotlinFileExtractor .DeclarationStack (), globalExtensionState)
97
+ // We want our comments to be the first thing in the file,
98
+ // so start off with a mere TrapWriter
99
+ val tw = TrapWriter (logger.loggerBase, TrapLabelManager (), trapFileBW, diagnosticTrapWriter)
100
+ tw.writeComment(" Generated by the CodeQL Kotlin extractor for external dependencies" )
101
+ tw.writeComment(" Part of invocation $invocationTrapFile " )
102
+ if (signature != possiblyLongSignature) {
103
+ tw.writeComment(" Function signature abbreviated; full signature is: $possiblyLongSignature " )
104
+ }
105
+ // Now elevate to a SourceFileTrapWriter, and populate the
106
+ // file information if needed:
107
+ val ftw = tw.makeFileTrapWriter(binaryPath, true )
86
108
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)
109
+ val fileExtractor = KotlinFileExtractor (logger, ftw, null , binaryPath, manager, this , primitiveTypeMapping, pluginContext, KotlinFileExtractor .DeclarationStack (), globalExtensionState)
95
110
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
- }
111
+ if (irDecl is IrClass ) {
112
+ // Populate a location and compilation-unit package for the file. This is similar to
113
+ // the beginning of `KotlinFileExtractor.extractFileContents` but without an `IrFile`
114
+ // to start from.
115
+ val pkg = irDecl.packageFqName?.asString() ? : " "
116
+ val pkgId = fileExtractor.extractPackage(pkg)
117
+ ftw.writeHasLocation(ftw.fileId, ftw.getWholeFileLocation())
118
+ ftw.writeCupackage(ftw.fileId, pkgId)
101
119
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
- }
120
+ fileExtractor.extractClassSource(irDecl, extractDeclarations = ! irDecl.isFileClass, extractStaticInitializer = false , extractPrivateMembers = false , extractFunctionBodies = false )
121
+ } else {
122
+ fileExtractor.extractDeclaration(irDecl, extractPrivateMembers = false , extractFunctionBodies = false )
109
123
}
110
124
}
111
125
}
0 commit comments