mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Kotlin: Add kotlin-extractor
This commit is contained in:
23
java/kotlin-extractor/build.gradle
Normal file
23
java/kotlin-extractor/build.gradle
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm' version "${kotlinVersion}"
|
||||||
|
id 'org.jetbrains.dokka' version '1.4.32'
|
||||||
|
id "com.vanniktech.maven.publish" version '0.15.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'com.github.codeql'
|
||||||
|
version '0.0.1'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib"
|
||||||
|
compileOnly("org.jetbrains.kotlin:kotlin-compiler")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
7
java/kotlin-extractor/gradle.properties
Normal file
7
java/kotlin-extractor/gradle.properties
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kotlin.code.style=official
|
||||||
|
kotlinVersion=1.5.21
|
||||||
|
|
||||||
|
GROUP=com.github.codeql
|
||||||
|
VERSION_NAME=0.0.1
|
||||||
|
POM_DESCRIPTION=CodeQL Kotlin extractor
|
||||||
|
|
||||||
8
java/kotlin-extractor/settings.gradle
Normal file
8
java/kotlin-extractor/settings.gradle
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = 'codeql-kotlin-extractor'
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.github.codeql
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.CliOption
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||||
|
|
||||||
|
class KotlinExtractorCommandLineProcessor : CommandLineProcessor {
|
||||||
|
override val pluginId = "kotlin-extractor"
|
||||||
|
|
||||||
|
override val pluginOptions = listOf(
|
||||||
|
CliOption(
|
||||||
|
optionName = "testOption",
|
||||||
|
valueDescription = "A test option",
|
||||||
|
description = "For testing options",
|
||||||
|
required = false,
|
||||||
|
allowMultipleOccurrences = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun processOption(
|
||||||
|
option: AbstractCliOption,
|
||||||
|
value: String,
|
||||||
|
configuration: CompilerConfiguration
|
||||||
|
) = when (option.optionName) {
|
||||||
|
"testOption" -> configuration.appendList(KEY_TEST, value)
|
||||||
|
else -> error("kotlin extractor: Bad option: ${option.optionName}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val KEY_TEST = CompilerConfigurationKey<List<String>>("kotlin extractor test")
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.github.codeql
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||||
|
import com.intellij.mock.MockProject
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
|
||||||
|
class KotlinExtractorComponentRegistrar : ComponentRegistrar {
|
||||||
|
override fun registerProjectComponents(
|
||||||
|
project: MockProject,
|
||||||
|
configuration: CompilerConfiguration
|
||||||
|
) {
|
||||||
|
val tests = configuration[KEY_TEST] ?: emptyList()
|
||||||
|
IrGenerationExtension.registerExtension(project, KotlinExtractorExtension(tests))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package com.github.codeql
|
||||||
|
|
||||||
|
import java.io.BufferedWriter
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||||
|
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.path
|
||||||
|
import org.jetbrains.kotlin.ir.util.dump
|
||||||
|
import org.jetbrains.kotlin.ir.util.packageFqName
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
import org.jetbrains.kotlin.ir.IrFileEntry
|
||||||
|
|
||||||
|
class KotlinExtractorExtension(private val tests: List<String>) : IrGenerationExtension {
|
||||||
|
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||||
|
val trapDir = File(System.getenv("CODEQL_EXTRACTOR_KOTLIN_TRAP_DIR").takeUnless { it.isNullOrEmpty() } ?: "kotlin-extractor/trap")
|
||||||
|
trapDir.mkdirs()
|
||||||
|
val srcDir = File(System.getenv("CODEQL_EXTRACTOR_KOTLIN_SOURCE_ARCHIVE_DIR").takeUnless { it.isNullOrEmpty() } ?: "kotlin-extractor/src")
|
||||||
|
srcDir.mkdirs()
|
||||||
|
moduleFragment.accept(KotlinExtractorVisitor(trapDir, srcDir), RootTrapWriter())
|
||||||
|
// We don't want the compiler to continue and generate class
|
||||||
|
// files etc, so we just exit when we are finished extracting.
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extractorBug(msg: String) {
|
||||||
|
println(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TrapWriter {
|
||||||
|
fun writeTrap(trap: String)
|
||||||
|
fun getLocation(startOffset: Int, endOffset: Int): Int
|
||||||
|
fun getIdFor(label: String): Int
|
||||||
|
fun getFreshId(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class RootTrapWriter: TrapWriter {
|
||||||
|
override fun writeTrap(trap: String) {
|
||||||
|
extractorBug("Tried to write TRAP outside a file: $trap")
|
||||||
|
}
|
||||||
|
override fun getLocation(startOffset: Int, endOffset: Int): Int {
|
||||||
|
extractorBug("Asked for location, but not in a file")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
override fun getIdFor(label: String): Int {
|
||||||
|
extractorBug("Asked for ID for '$label' outside a file")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
override fun getFreshId(): Int {
|
||||||
|
extractorBug("Asked for fresh ID outside a file")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileTrapWriter(
|
||||||
|
val fileLabel: String,
|
||||||
|
val file: BufferedWriter,
|
||||||
|
val fileEntry: IrFileEntry
|
||||||
|
): TrapWriter {
|
||||||
|
var nextId: Int = 100
|
||||||
|
override fun writeTrap(trap: String) {
|
||||||
|
file.write(trap)
|
||||||
|
}
|
||||||
|
override fun getLocation(startOffset: Int, endOffset: Int): Int {
|
||||||
|
val startLine = fileEntry.getLineNumber(startOffset) + 1
|
||||||
|
val startColumn = fileEntry.getColumnNumber(startOffset) + 1
|
||||||
|
val endLine = fileEntry.getLineNumber(endOffset) + 1
|
||||||
|
val endColumn = fileEntry.getColumnNumber(endOffset)
|
||||||
|
val id = getFreshId()
|
||||||
|
val fileId = getIdFor(fileLabel)
|
||||||
|
writeTrap("#$id = @\"loc,{#$fileId},$startLine,$startColumn,$endLine,$endColumn\"\n")
|
||||||
|
writeTrap("locations_default(#$id, #$fileId, $startLine, $startColumn, $endLine, $endColumn)\n")
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
val labelMapping: MutableMap<String, Int> = mutableMapOf<String, Int>()
|
||||||
|
override fun getIdFor(label: String): Int {
|
||||||
|
val maybeId = labelMapping.get(label)
|
||||||
|
if(maybeId == null) {
|
||||||
|
val id = getFreshId()
|
||||||
|
labelMapping.put(label, id)
|
||||||
|
return id
|
||||||
|
} else {
|
||||||
|
return maybeId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun getFreshId(): Int {
|
||||||
|
return nextId++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KotlinExtractorVisitor(val trapDir: File, val srcDir: File) : IrElementVisitor<Unit, TrapWriter> {
|
||||||
|
override fun visitElement(element: IrElement, data: TrapWriter) {
|
||||||
|
extractorBug("Unrecognised IrElement: " + element.javaClass)
|
||||||
|
if(data is RootTrapWriter) {
|
||||||
|
extractorBug("... and outside any file!")
|
||||||
|
}
|
||||||
|
element.acceptChildren(this, data)
|
||||||
|
}
|
||||||
|
override fun visitClass(declaration: IrClass, data: TrapWriter) {
|
||||||
|
val id = data.getFreshId()
|
||||||
|
val locId = data.getLocation(declaration.startOffset, declaration.endOffset)
|
||||||
|
val pkg = declaration.packageFqName?.asString() ?: ""
|
||||||
|
val cls = declaration.name.asString()
|
||||||
|
data.writeTrap("#$id = @\"class;$pkg.$cls\"\n")
|
||||||
|
data.writeTrap("classes(#$id, \"$cls\")\n")
|
||||||
|
data.writeTrap("hasLocation(#$id, #$locId)\n")
|
||||||
|
declaration.acceptChildren(this, data)
|
||||||
|
}
|
||||||
|
override fun visitFile(declaration: IrFile, data: TrapWriter) {
|
||||||
|
val filePath = declaration.path
|
||||||
|
val file = File(filePath)
|
||||||
|
val fileLabel = "@\"$filePath;sourcefile\""
|
||||||
|
val basename = file.nameWithoutExtension
|
||||||
|
val extension = file.extension
|
||||||
|
val dest = Paths.get("$srcDir/${declaration.path}")
|
||||||
|
val destDir = dest.getParent()
|
||||||
|
Files.createDirectories(destDir)
|
||||||
|
Files.copy(Paths.get(declaration.path), dest)
|
||||||
|
|
||||||
|
val trapFile = File("$trapDir/$filePath.trap")
|
||||||
|
val trapFileDir = trapFile.getParentFile()
|
||||||
|
trapFileDir.mkdirs()
|
||||||
|
trapFile.bufferedWriter().use { trapFileBW ->
|
||||||
|
val tw = FileTrapWriter(fileLabel, trapFileBW, declaration.fileEntry)
|
||||||
|
val id = tw.getIdFor(fileLabel)
|
||||||
|
tw.writeTrap("#$id = $fileLabel\n")
|
||||||
|
tw.writeTrap("files(#$id, \"$filePath\", \"$basename\", \"$extension\", 0)\n")
|
||||||
|
declaration.acceptChildren(this, tw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
com.github.codeql.KotlinExtractorCommandLineProcessor
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
com.github.codeql.KotlinExtractorComponentRegistrar
|
||||||
Reference in New Issue
Block a user