11package com.github.codeql
22
3- import com.github.codeql.utils.isExternalDeclaration
43import com.github.codeql.utils.isExternalFileClassMember
54import com.semmle.extractor.java.OdasaOutput
65import com.semmle.util.data.StringDigestor
76import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
7+ import org.jetbrains.kotlin.ir.IrElement
88import org.jetbrains.kotlin.ir.declarations.*
9- import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
109import org.jetbrains.kotlin.ir.util.isFileClass
1110import 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
1412import java.io.File
1513import java.util.ArrayList
1614import java.util.HashSet
@@ -25,87 +23,103 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
2523 val propertySignature = " ;property"
2624 val fieldSignature = " ;field"
2725
26+ val output = OdasaOutput (false , logger).also {
27+ it.setCurrentSourceFile(File (sourceFilePath))
28+ }
29+
2830 fun extractLater (d : IrDeclarationWithName , signature : String ): Boolean {
2931 if (d !is IrClass && ! isExternalFileClassMember(d)) {
3032 logger.errorElement(" External declaration is neither a class, nor a top-level declaration" , d)
3133 return false
3234 }
33- val declBinaryName = declBinaryNames.getOrPut(d) { getIrDeclBinaryName (d) }
35+ val declBinaryName = declBinaryNames.getOrPut(d) { getIrElementBinaryName (d) }
3436 val ret = externalDeclsDone.add(Pair (declBinaryName, signature))
3537 if (ret) externalDeclWorkList.add(Pair (d, signature))
3638 return ret
3739 }
3840 fun extractLater (c : IrClass ) = extractLater(c, " " )
3941
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+
4084 fun extractExternalClasses () {
41- val output = OdasaOutput (false , logger)
42- output.setCurrentSourceFile(File (sourceFilePath))
4385 do {
4486 val nextBatch = ArrayList (externalDeclWorkList)
4587 externalDeclWorkList.clear()
4688 nextBatch.forEach { workPair ->
4789 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)
8496
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 )
86108
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)
95110
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)
101119
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 )
109123 }
110124 }
111125 }
0 commit comments