[FIR] Remove old visitor generator and configure gradle task for tree generator

This commit is contained in:
Dmitriy Novozhilov
2019-10-10 10:59:53 +03:00
parent cb981919f9
commit a8192dba90
26 changed files with 7 additions and 679 deletions
+6 -14
View File
@@ -27,25 +27,17 @@ sourceSets {
val generatorClasspath by configurations.creating
dependencies {
generatorClasspath(project("visitors-generator"))
generatorClasspath(project("tree-generator"))
}
val generateVisitors by tasks.registering(NoDebugJavaExec::class) {
val generationRoot = "$projectDir/src/org/jetbrains/kotlin/fir/"
val output = "$projectDir/visitors"
val allSourceFiles = fileTree(generationRoot) {
include("**/*.kt")
}
inputs.files(allSourceFiles)
outputs.dirs(output)
val generateTree by tasks.registering(NoDebugJavaExec::class) {
val generationRoot = "$projectDir/src/"
args(generationRoot)
classpath = generatorClasspath
args(generationRoot, output)
main = "org.jetbrains.kotlin.fir.visitors.generator.VisitorsGeneratorKt"
main = "org.jetbrains.kotlin.fir.tree.generator.MainKt"
}
val compileKotlin by tasks
compileKotlin.dependsOn(generateVisitors)
compileKotlin.dependsOn(generateTree)
@@ -1,127 +0,0 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.visitors.generator
import org.jetbrains.kotlin.utils.Printer
abstract class AbstractVisitorGenerator(val referencesData: DataCollector.ReferencesData) {
fun Printer.generateFunction(
name: String,
parameters: Map<String, DataCollector.NameWithTypeParameters>,
returnType: String,
override: Boolean = false,
final: Boolean = false,
typeParametersWithBounds: List<String> = emptyList(),
body: (Printer.() -> Unit)?
) {
if (body == null) {
print("abstract ")
} else {
printIndent()
if (!final) {
printWithNoIndent("open ")
}
if (override) {
if (final) {
printWithNoIndent("final ")
}
printWithNoIndent("override ")
}
}
printWithNoIndent("fun ")
if (typeParametersWithBounds.isNotEmpty()) {
printWithNoIndent("<")
separatedOneLine(typeParametersWithBounds, ", ")
printWithNoIndent("> ")
}
printWithNoIndent(name, "(")
parameters
.flatMap { (parameterName, typeNameWithTypeParameters) ->
listOf(parameterName, ": ", typeNameWithTypeParameters.asStringWithoutBounds(), ", ")
}.dropLast(1)
.forEach {
printWithNoIndent(it)
}
printWithNoIndent(")")
if (returnType != "Unit") {
printWithNoIndent(": ", returnType)
}
if (body != null) {
printlnWithNoIndent(" {")
indented {
body()
}
println("}")
} else {
printlnWithNoIndent()
}
println()
}
protected inline fun Printer.indented(l: () -> Unit) {
pushIndent()
l()
popIndent()
}
protected fun Printer.generateCall(name: String, args: List<String>) {
printWithNoIndent(name, "(")
separatedOneLine(args, ", ")
printWithNoIndent(")")
}
private fun Printer.separatedOneLine(iterable: Iterable<Any>, separator: Any) {
var first = true
for (element in iterable) {
if (!first) {
printWithNoIndent(separator)
} else {
first = false
}
printWithNoIndent(element)
}
}
private fun Printer.generateDefaultImports() {
referencesData.usedPackages.forEach {
println("import ", it.asString(), ".*")
}
println()
println()
}
val String.safeName
get() = when (this) {
"class" -> "klass"
else -> this
}
fun generate(): String {
val builder = StringBuilder()
val printer = Printer(builder, " ")
printer.apply {
println(javaClass.getResource("/notice.txt").readText())
println("package $VISITOR_PACKAGE")
println()
generateDefaultImports()
println(WARNING_GENERATED_FILE)
printer.generateContent()
}
return builder.toString()
}
fun allElementTypes() =
referencesData.direct.let { map ->
map.keys + map.values.flatten()
}.distinct()
abstract fun Printer.generateContent()
}
@@ -1,160 +0,0 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.visitors.generator
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
class DataCollector {
class NameWithTypeParameters private constructor(
val name: String,
val typeParameters: List<String>,
private val typeParameterBounds: Map<String, String>
) : Comparable<NameWithTypeParameters> {
override fun compareTo(other: NameWithTypeParameters): Int = name.compareTo(other.name)
private constructor(name: String, typeParameters: Pair<List<String>, Map<String, String>>) :
this(name, typeParameters.first, typeParameters.second)
constructor(name: String, typeParameterString: String) :
this(name, typeParameterString.splitToParametersAndBounds())
constructor(name: String) : this(name, emptyList(), emptyMap())
fun typeParametersWithBounds(): List<String> =
typeParameters.map { parameter ->
parameter + (typeParameterBounds[parameter]?.let { " : $it" } ?: "")
}
fun asStringWithoutBounds(): String =
name + if (typeParameters.isEmpty()) "" else typeParameters.joinToString(prefix = "<", postfix = ">", separator = ", ")
override fun equals(other: Any?): Boolean =
other is NameWithTypeParameters && name == other.name
override fun hashCode(): Int = name.hashCode()
override fun toString(): String =
name + if (typeParameters.isEmpty()) "" else typeParameters.joinToString(prefix = "<", postfix = ">", separator = ", ") {
val bound = typeParameterBounds[it]
if (bound == null) it else "$it : $bound"
}
companion object {
private fun String.splitToParametersAndBounds(): Pair<List<String>, Map<String, String>> {
val typeParametersWithBounds = drop(1).dropLast(1).split(",").map { it.trim() }.filter { it.isNotBlank() }
val typeParameters = mutableListOf<String>()
val bounds = mutableMapOf<String, String>()
for (parameterWithBounds in typeParametersWithBounds) {
val parts = parameterWithBounds.split(":").map { it.trim() }
typeParameters += parts[0]
if (parts.size > 1) {
bounds[parts[0]] = parts[1]
}
}
return typeParameters to bounds
}
}
}
private val references = mutableMapOf<NameWithTypeParameters, List<NameWithTypeParameters>>()
private val packagePerClass = mutableMapOf<NameWithTypeParameters, FqName>()
private val baseTransformedTypes = mutableListOf<NameWithTypeParameters>()
private fun KtSuperTypeListEntry.toNameWithTypeParameters(): NameWithTypeParameters? {
val type = typeAsUserType ?: return null
val name = type.referencedName ?: return null
val typeArguments = type.typeArgumentList?.text ?: ""
return NameWithTypeParameters(name, typeArguments)
}
fun readFile(file: KtFile) {
file.acceptChildren(object : KtVisitorVoid() {
override fun visitKtElement(element: KtElement) {
super.visitKtElement(element)
element.acceptChildren(this)
}
override fun visitClass(klass: KtClass) {
val className = klass.name ?: run {
super.visitClass(klass)
return
}
val classNameWithParameters = NameWithTypeParameters(className, klass.typeParameterList?.text ?: "")
if (klass.isInterface() || (klass.hasModifier(KtTokens.ABSTRACT_KEYWORD) && "Abstract" !in (klass.name ?: ""))) {
packagePerClass[classNameWithParameters] = file.packageFqName
val isBaseTT = klass.annotationEntries.any {
it.shortName?.asString() == BASE_TRANSFORMED_TYPE_ANNOTATION_NAME
}
if (isBaseTT) {
baseTransformedTypes += classNameWithParameters
}
val manual = klass.superTypeListEntries.find {
it.typeReference?.findDescendantOfType<KtAnnotationEntry> { annotationEntry ->
annotationEntry.shortName?.asString() == VISITED_SUPERTYPE_ANNOTATION_NAME
} != null
}
if (manual != null) {
references[classNameWithParameters] = listOfNotNull(manual.toNameWithTypeParameters())
} else {
references[classNameWithParameters] =
klass.superTypeListEntries.mapNotNull {
it.toNameWithTypeParameters()
}
}
}
super.visitClass(klass)
}
})
}
private fun <K> Map<K, List<K>>.computeBackReferences(): Map<K, List<K>> {
val result = mutableMapOf<K, List<K>>()
this.forEach { (k, v) ->
v.forEach {
result.merge(it, listOf(k)) { a, b -> a + b }
}
}
return result
}
private fun <K : Comparable<K>> Map<K, List<K>>.sortedMap(): Map<K, List<K>> {
return this.toSortedMap().mapValues { (_, v) -> v.sorted() }
}
fun computeResult(): ReferencesData {
val back = references.computeBackReferences()
val keysToKeep =
generateSequence(listOf(FIR_ELEMENT_CLASS_NAME)) { firName ->
firName.flatMap { back[it].orEmpty() }.takeUnless { it.isEmpty() }
}.flatten().toSet()
val cleanBack = back.filterKeys { it in keysToKeep }
return ReferencesData(
cleanBack.computeBackReferences().sortedMap(),
cleanBack.sortedMap(),
packagePerClass.filterKeys { it in keysToKeep }.values.distinct().sortedBy { it.asString() },
baseTransformedTypes.sorted()
)
}
data class ReferencesData(
val direct: Map<NameWithTypeParameters, List<NameWithTypeParameters>>,
val back: Map<NameWithTypeParameters, List<NameWithTypeParameters>>,
val usedPackages: List<FqName>,
val baseTransformedTypes: List<NameWithTypeParameters>
)
}
@@ -1,114 +0,0 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.visitors.generator
import org.jetbrains.kotlin.fir.visitors.generator.DataCollector.NameWithTypeParameters
import org.jetbrains.kotlin.utils.Printer
class ParametricTransformerGenerator(data: DataCollector.ReferencesData) : AbstractVisitorGenerator(data) {
override fun Printer.generateContent() {
println("abstract class $PARAMETRIC_TRANSFORMER_NAME<in D> : $SIMPLE_VISITOR_NAME<$TRANSFORMER_RESULT_NAME<$FIR_ELEMENT_CLASS_NAME>, D>() {")
indented {
generateFunction(
"transformElement",
mapOf(
"element" to NameWithTypeParameters("E"),
"data" to NameWithTypeParameters("D")
),
constructCompositeBoxType("E"),
typeParametersWithBounds = listOf("E : $FIR_ELEMENT_CLASS_NAME"),
body = null
)
referencesData.walkHierarchyTopDown(FIR_ELEMENT_CLASS_NAME) { parent, className ->
generateTransformMethod(className, parent)
}
allElementTypes().forEach {
generateTrampolineVisit(it)
}
}
println("}")
}
fun constructCompositeBoxType(type: String): String {
return "$TRANSFORMER_RESULT_NAME<$type>"
}
val baseTypes = mutableMapOf<NameWithTypeParameters, NameWithTypeParameters>().also {
for (baseTransformedType in referencesData.baseTransformedTypes) {
it[baseTransformedType] = baseTransformedType
}
data.walkHierarchyTopDown(FIR_ELEMENT_CLASS_NAME) { parent, element ->
it[element] = it[parent] ?: return@walkHierarchyTopDown
}
}
fun Printer.generateTrampolineVisit(className: NameWithTypeParameters) {
val shortcutName = className.name.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
generateFunction(
name = "visit$shortcutName",
parameters = mapOf(
parameterName to className,
"data" to NameWithTypeParameters("D")
),
returnType = constructCompositeBoxType(FIR_ELEMENT_CLASS_NAME.name),
override = true,
final = true,
typeParametersWithBounds = className.typeParametersWithBounds()
) {
print("return ")
generateCall("transform$shortcutName", listOf(parameterName, "data"))
println()
}
}
fun Printer.generateTransformMethod(
className: NameWithTypeParameters,
parent: NameWithTypeParameters
) {
val baseType = baseTypes[className]
val shortcutName = className.name.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
if (baseType == null) {
generateFunction(
"transform$shortcutName",
mapOf(
parameterName to NameWithTypeParameters("E"),
"data" to NameWithTypeParameters("D")
),
constructCompositeBoxType("E"),
typeParametersWithBounds = listOf("E : $parent") + className.typeParametersWithBounds()
) {
print("return ")
generateCall("transformElement", listOf(parameterName, "data"))
printlnWithNoIndent()
}
} else {
generateFunction(
"transform$shortcutName",
mapOf(
parameterName to className,
"data" to NameWithTypeParameters("D")
),
constructCompositeBoxType(baseType.name),
typeParametersWithBounds = className.typeParametersWithBounds()
) {
print("return ")
generateCall("transformElement", listOf(parameterName, "data"))
printlnWithNoIndent()
}
}
}
}
@@ -1,49 +0,0 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.visitors.generator
import com.intellij.core.CoreApplicationEnvironment
import com.intellij.core.CoreProjectEnvironment
import com.intellij.lang.MetaLanguage
import com.intellij.openapi.Disposable
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
internal data class PsiSetup(
val applicationEnvironment: CoreApplicationEnvironment,
val projectEnvironment: CoreProjectEnvironment,
val project: Project,
val disposable: Disposable
)
internal fun setup(): PsiSetup {
val disposable = Disposer.newDisposable()
val applicationEnvironment = CoreApplicationEnvironment(disposable, false)
val projectEnvironment = CoreProjectEnvironment(disposable, applicationEnvironment)
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), MetaLanguage.EP_NAME, MetaLanguage::class.java)
applicationEnvironment.registerFileType(KotlinFileType.INSTANCE, "kt")
applicationEnvironment.registerParserDefinition(KotlinParserDefinition())
val project = projectEnvironment.project
return PsiSetup(applicationEnvironment, projectEnvironment, project, disposable)
}
internal inline fun <T> withPsiSetup(l: PsiSetup.() -> T): T {
val setup = setup()
val t = setup.l()
Disposer.dispose(setup.disposable)
return t
}
@@ -1,53 +0,0 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.visitors.generator
import org.jetbrains.kotlin.fir.visitors.generator.DataCollector.NameWithTypeParameters
import org.jetbrains.kotlin.utils.Printer
class SimpleVisitorGenerator(referencesData: DataCollector.ReferencesData) : AbstractVisitorGenerator(referencesData) {
override fun Printer.generateContent() {
println("abstract class $SIMPLE_VISITOR_NAME<out R, in D> {")
indented {
generateFunction(
"visitElement",
parameters = mapOf(
"element" to FIR_ELEMENT_CLASS_NAME,
"data" to NameWithTypeParameters("D")
),
returnType = "R",
body = null
)
referencesData.walkHierarchyTopDown(from = FIR_ELEMENT_CLASS_NAME) { parent, element ->
generateVisit(element, parent)
}
}
println("}")
}
private fun Printer.generateVisit(
className: NameWithTypeParameters,
parent: NameWithTypeParameters
) {
val shortcutName = className.name.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
generateFunction(
name = "visit$shortcutName",
parameters = mapOf(
parameterName to className,
"data" to NameWithTypeParameters("D")
),
returnType = "R",
typeParametersWithBounds = className.typeParametersWithBounds()
) {
print("return ")
generateCall("visitElement", listOf(parameterName, "data"))
println()
}
}
}
@@ -1,76 +0,0 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.visitors.generator
import org.jetbrains.kotlin.fir.visitors.generator.DataCollector.NameWithTypeParameters
import org.jetbrains.kotlin.utils.Printer
class UnitVisitorGenerator(referencesData: DataCollector.ReferencesData) : AbstractVisitorGenerator(referencesData) {
override fun Printer.generateContent() {
println("abstract class $UNIT_VISITOR_NAME : $SIMPLE_VISITOR_NAME<Unit, Nothing?>() {")
indented {
generateFunction(
"visitElement",
parameters = mapOf(
"element" to FIR_ELEMENT_CLASS_NAME
),
returnType = "Unit",
body = null
)
referencesData.walkHierarchyTopDown(from = FIR_ELEMENT_CLASS_NAME) { parent, element ->
generateVisit(element, parent)
}
allElementTypes().forEach {
generateTrampolineVisit(it)
}
}
println("}")
}
private fun Printer.generateVisit(
className: NameWithTypeParameters,
parent: NameWithTypeParameters
) {
val shortcutName = className.name.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
generateFunction(
name = "visit$shortcutName",
parameters = mapOf(
parameterName to className
),
returnType = "Unit",
typeParametersWithBounds = className.typeParametersWithBounds()
) {
printIndent()
generateCall("visitElement", listOf(parameterName, "null"))
println()
}
}
private fun Printer.generateTrampolineVisit(className: NameWithTypeParameters) {
val shortcutName = className.name.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
generateFunction(
name = "visit$shortcutName",
parameters = mapOf(
parameterName to className,
"data" to NameWithTypeParameters("Nothing?")
),
returnType = "Unit",
override = true,
final = true,
typeParametersWithBounds = className.typeParametersWithBounds()
) {
printIndent()
generateCall("visit$shortcutName", listOf(parameterName))
println()
}
}
}
@@ -1,85 +0,0 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.visitors.generator
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.local.CoreLocalFileSystem
import com.intellij.psi.PsiManager
import com.intellij.psi.SingleRootFileViewProvider
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
val FIR_ELEMENT_CLASS_NAME = DataCollector.NameWithTypeParameters("FirElement")
const val VISITOR_PACKAGE = "org.jetbrains.kotlin.fir.visitors"
const val SIMPLE_VISITOR_NAME = "FirVisitor"
const val UNIT_VISITOR_NAME = "FirVisitorVoid"
const val PARAMETRIC_TRANSFORMER_NAME = "FirTransformer"
const val TRANSFORMER_RESULT_NAME = "CompositeTransformResult"
const val BASE_TRANSFORMED_TYPE_ANNOTATION_NAME = "BaseTransformedType"
const val VISITED_SUPERTYPE_ANNOTATION_NAME = "VisitedSupertype"
const val WARNING_GENERATED_FILE =
"""/** This file generated by :compiler:fir:tree:generateVisitors. DO NOT MODIFY MANUALLY! */"""
fun main(args: Array<String>) {
// val rootPath = File(args[0])
// val output = File(args[1])
//
// val packageDirectory = output.resolve(VISITOR_PACKAGE.replace('.', '/'))
// packageDirectory.mkdirs()
//
// withPsiSetup {
// val psiManager = PsiManager.getInstance(project)
// val vfm = VirtualFileManager.getInstance()
//
//
// val dataCollector = DataCollector()
//
// rootPath.walkTopDown()
// .filter {
// it.extension == "kt"
// }.map {
// (vfm.getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem).findFileByIoFile(it)
// }.flatMap {
// SingleRootFileViewProvider(psiManager, it!!).allFiles.asSequence()
// }
// .filterIsInstance<KtFile>()
// .forEach(dataCollector::readFile)
//
//
// val data = dataCollector.computeResult()
//
//
// SimpleVisitorGenerator(data).runGenerator(packageDirectory.resolve("${SIMPLE_VISITOR_NAME}Generated.kt"))
// UnitVisitorGenerator(data).runGenerator(packageDirectory.resolve("${UNIT_VISITOR_NAME}Generated.kt"))
// ParametricTransformerGenerator(data).runGenerator(packageDirectory.resolve("${PARAMETRIC_TRANSFORMER_NAME}Generated.kt"))
// }
}
fun AbstractVisitorGenerator.runGenerator(file: File) {
file.writeText(StringUtil.convertLineSeparators(this.generate(), System.lineSeparator()))
}
val String.classNameWithoutFir get() = this.removePrefix("Fir")
fun DataCollector.ReferencesData.walkHierarchyTopDown(
from: DataCollector.NameWithTypeParameters,
l: (p: DataCollector.NameWithTypeParameters, e: DataCollector.NameWithTypeParameters) -> Unit
) {
val referents = back[from] ?: return
for (referent in referents) {
l(from, referent)
walkHierarchyTopDown(referent, l)
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ include ":kotlin-build-common",
":compiler:visualizer:render-psi",
":compiler:fir:cones",
":compiler:fir:tree",
":compiler:fir:tree:visitors-generator",
":compiler:fir:tree:tree-generator",
":compiler:fir:psi2fir",
":compiler:fir:lightTree",
":compiler:fir:fir2ir",