[FIR] Automatically generated abstract classes instead of interfaces if possible

This commit is contained in:
Dmitriy Novozhilov
2019-10-18 14:25:34 +03:00
parent 6b48112a2b
commit 615d3aafef
166 changed files with 1143 additions and 915 deletions
@@ -81,3 +81,9 @@ elementName.configure {
- `defaultNull(fieldName, [withGetter: Boolean])`
- If some fields should be `lateinit` you describe them in call `lateinit(vararg fields: String)`
- If you use some types that shoub be imported list them in method `useTypes(vararg types: Type/Element)`
# Notes
- There is algorithm that automatically makes as most abstract classes instead of interfaces as possible. If you want to some `Element` or `Implementation` should be always an interface you should:
- call `shouldBeAnInterface` when configuring a `Element` in `NodeConfigurator.kt`
- specify `kind = Interface` when configuring an `Implementation` in `ImplementationConfigurator.kt`
@@ -19,20 +19,13 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
}
private fun configure() = with(FirTreeBuilder) {
val callWithArgumentList = impl(call, "FirCallWithArgumentList") {
kind = Interface
}
val callWithArgumentList = impl(call, "FirCallWithArgumentList")
abstractAnnotatedElement = impl(annotationContainer, "FirAbstractAnnotatedElement") {
kind = Interface
}
abstractAnnotatedElement = impl(annotationContainer, "FirAbstractAnnotatedElement")
val modifiableTypeParametersOwner = impl(typeParametersOwner, "FirModifiableTypeParametersOwner") {
kind = Interface
}
val modifiableTypeParametersOwner = impl(typeParametersOwner, "FirModifiableTypeParametersOwner")
val modifiableConstructor = impl(constructor, "FirModifiableConstructor") {
kind = Interface
parents += modifiableTypeParametersOwner
}
@@ -57,14 +50,11 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
noImpl(resolvedDeclarationStatus)
noImpl(field)
val modifiableClass = impl(klass, "FirModifiableClass") {
kind = Interface
}
val modifiableClass = impl(klass, "FirModifiableClass")
val modifiableRegularClass = impl(regularClass, "FirModifiableRegularClass") {
parents += modifiableClass
parents += modifiableTypeParametersOwner
kind = Interface
}
val regularClassConfig: ImplementationContext.() -> Unit = {
@@ -153,7 +143,6 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
}
val modifiableQualifiedAccess = impl(qualifiedAccessWithoutCallee, "FirModifiableQualifiedAccess") {
kind = Interface
isMutable("safe")
}
@@ -196,9 +185,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
useTypes(simpleNamedReferenceType, nameType, noReceiverExpressionType)
}
val abstractLoop = impl(loop, "FirAbstractLoop") {
kind = Interface
}
val abstractLoop = impl(loop, "FirAbstractLoop")
impl(whileLoop) {
parents += abstractLoop
@@ -230,8 +217,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
useTypes(implicitBooleanTypeRefType)
}
impl(block) {
}
impl(block)
val emptyExpressionBlock = impl(block, "FirEmptyExpressionBlock") {
// TODO: make statements immutable
@@ -274,9 +260,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
}
}
val modifiableVariable = impl(variable, "FirModifiableVariable") {
kind = Interface
}
val modifiableVariable = impl(variable, "FirModifiableVariable")
impl(property) {
parents += modifiableVariable.withArg(property)
@@ -398,9 +382,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
}
}
val modifiableFunction = impl(function, "FirModifiableFunction") {
kind = Interface
}
val modifiableFunction = impl(function, "FirModifiableFunction")
impl(anonymousFunction) {
parents += modifiableFunction.withArg(anonymousFunction)
@@ -534,7 +516,6 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
}
val abstractLoopJump = impl(loopJump, "FirAbstractLoopJump") {
kind = Interface
lateinit("target")
}
@@ -0,0 +1,178 @@
/*
* Copyright 2010-2019 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.tree.generator
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import org.jetbrains.kotlin.fir.tree.generator.model.Element
import org.jetbrains.kotlin.fir.tree.generator.model.Implementation
import org.jetbrains.kotlin.fir.tree.generator.model.ImplementationWithArg
import org.jetbrains.kotlin.fir.tree.generator.model.KindOwner
fun configureInterfacesAndAbstractClasses(builder: AbstractFirTreeBuilder) {
val elements = collectElements(builder)
val elementMapping = ElementMapping(elements)
val solution = solve2sat(elements, elementMapping)
processRequirementsFromConfig(solution, elementMapping)
updateKinds(solution, elementMapping)
}
private class ElementMapping(elements: Collection<KindOwner>) {
private val varToElements: Map<Int, KindOwner> = elements.mapIndexed { index, element -> 2 * index to element.origin }.toMap() +
elements.mapIndexed { index, element -> 2 * index + 1 to element }.toMap()
private val elementsToVar: Map<KindOwner, Int> = elements.mapIndexed { index, element -> element.origin to index }.toMap()
private val hasInheritors = elements.map { it to false }.toMap(mutableMapOf()).also {
for (element in elements) {
for (parent in element.allParents) {
it[parent.origin] = true
}
}
}
operator fun get(element: KindOwner): Int = elementsToVar.getValue(element)
operator fun get(index: Int): KindOwner = varToElements.getValue(index)
fun hasInheritors(element: KindOwner): Boolean {
return hasInheritors[element]!!
}
val size: Int = elements.size
}
private fun collectElements(builder: AbstractFirTreeBuilder): List<KindOwner> {
return (builder.elements + builder.elements.flatMap { it.allImplementations }).map { it.origin }
}
private fun updateKinds(solution: List<Boolean>, elementMapping: ElementMapping) {
for (index in solution.indices) {
val isClass = solution[index]
val element = elementMapping[index * 2].origin
val existingKind = element.kind
if (isClass) {
when (existingKind) {
Implementation.Kind.Interface -> throw IllegalStateException(element.toString())
null -> element.kind = when (element) {
is Implementation -> {
if (elementMapping.hasInheritors(element))
Implementation.Kind.AbstractClass
else
Implementation.Kind.FinalClass
}
is Element -> Implementation.Kind.AbstractClass
else -> throw IllegalStateException()
}
}
} else {
element.kind = Implementation.Kind.Interface
}
}
}
private fun processRequirementsFromConfig(solution: MutableList<Boolean>, elementMapping: ElementMapping) {
fun processParents(element: KindOwner) {
val origin = element.origin
solution[elementMapping[origin]] = false
origin.allParents.forEach { processParents(it) }
}
for (index in solution.indices) {
val element = elementMapping[index * 2]
if (element.kind != Implementation.Kind.Interface) continue
if (!solution[index]) continue
processParents(element)
}
}
private fun solve2sat(elements: Collection<KindOwner>, elementsToVar: ElementMapping): MutableList<Boolean> {
val (g, gt) = buildGraphs(elements, elementsToVar)
val used = g.indices.mapTo(mutableListOf()) { false }
val order = mutableListOf<Int>()
val comp = g.indices.mapTo(mutableListOf()) { -1 }
val n = g.size
fun dfs1(v: Int) {
used[v] = true
for (to in g[v]) {
if (!used[to]) {
dfs1(to)
}
}
order += v
}
fun dfs2(v: Int, cl: Int) {
comp[v] = cl
for (to in gt[v]) {
if (comp[to] == -1) {
dfs2(to, cl)
}
}
}
for (i in g.indices) {
if (!used[i]) {
dfs1(i)
}
}
var j = 0
for (i in g.indices) {
val v = order[n - i - 1]
if (comp[v] == -1) {
dfs2(v, j++)
}
}
val res = (1..elements.size).mapTo(mutableListOf()) { false }
for (i in 0 until n step 2) {
if (comp[i] == comp[i + 1]) {
throw IllegalStateException("Somehow there is no solution. Please contact with @dmitriy.novozhilov")
}
res[i / 2] = comp[i] > comp[i + 1]
}
return res
}
private fun buildGraphs(elements: Collection<KindOwner>, elementMapping: ElementMapping): Pair<List<List<Int>>, List<List<Int>>> {
val g = (1..elementMapping.size * 2).map { mutableListOf<Int>() }
val gt = (1..elementMapping.size * 2).map { mutableListOf<Int>() }
fun Int.direct(): Int = this
fun Int.invert(): Int = this + 1
fun extractIndex(element: KindOwner) = elementMapping[element] * 2
for (element in elements) {
val elementVar = extractIndex(element)
for (parent in element.allParents) {
val parentVar = extractIndex(parent.origin)
// parent -> element
g[parentVar.direct()] += elementVar.direct()
g[elementVar.invert()] += parentVar.invert()
}
for (i in 0 until element.allParents.size) {
for (j in i + 1 until element.allParents.size) {
val firstParentVar = extractIndex(element.allParents[i].origin)
val secondParentVar = extractIndex(element.allParents[j].origin)
// firstParent -> !secondParent
g[firstParentVar.direct()] += secondParentVar.invert()
g[secondParentVar.direct()] += firstParentVar.invert()
}
}
}
for (from in g.indices) {
for (to in g[from]) {
gt[to] += from
}
}
return g to gt
}
private val KindOwner.origin: KindOwner get() = if (this is ImplementationWithArg) implementation else this
@@ -20,9 +20,11 @@ fun main(args: Array<String>) {
NodeConfigurator.configureFields()
detectBaseTransformerTypes(FirTreeBuilder)
ImplementationConfigurator.configureImplementations()
configureInterfacesAndAbstractClasses(FirTreeBuilder)
removePreviousGeneratedFiles(generationPath)
printElements(FirTreeBuilder, generationPath)
// printTable(FirTreeBuilder)
// printInterfaceClassGraph(FirTreeBuilder)
}
fun Element.traverseParents(block: (Element) -> Unit) {
@@ -30,6 +32,30 @@ fun Element.traverseParents(block: (Element) -> Unit) {
parents.forEach { it.traverseParents(block) }
}
private fun printInterfaceClassGraph(builder: AbstractFirTreeBuilder) {
fun Implementation.Kind.toColor(): String = when (this) {
Implementation.Kind.Interface -> "green"
else -> "red"
}
val elements = builder.elements + builder.elements.flatMap { it.allParents }
File("FirTree.dot").printWriter().use { printer ->
with(printer) {
println("digraph FirTree {")
elements.forEach {
println(" ${it.type} [color=${it.kind!!.toColor()}]")
}
println()
elements.forEach { element ->
element.allParents.forEach { parent ->
println(" ${parent.type} -> ${element.type}")
}
}
println("}")
}
}
}
private fun detectBaseTransformerTypes(builder: AbstractFirTreeBuilder) {
val usedAsFieldType = mutableMapOf<AbstractElement, Boolean>().withDefault { false }
for (element in builder.elements) {
@@ -294,6 +294,10 @@ object NodeConfigurator : AbstractFieldConfigurator() {
)
}
resolvedDeclarationStatus.configure {
shouldBeAnInterface()
}
constructor.configure {
parentArg(memberFunction, "F", constructor)
+symbol("FirConstructorSymbol")
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.tree.generator.model.*
import java.io.File
import java.io.PrintWriter
import java.util.*
import kotlin.math.abs
val COPYRIGHT = """
/*
@@ -167,10 +168,15 @@ fun Implementation.generateCode(generationPath: String) {
}
}
val KindOwner.needPureAbstractElement: Boolean get() = (kind != Implementation.Kind.Interface) && !allParents.any { it.kind == Implementation.Kind.AbstractClass }
fun Implementation.collectImports(): List<String> {
return element.collectImportsInternal(
listOf(element.fullQualifiedName) + usedTypes.mapNotNull { it.fullQualifiedName } + parents.mapNotNull { it.fullQualifiedName }
+ listOfNotNull(pureAbstractElementType.fullQualifiedName?.takeIf { kind != Implementation.Kind.Interface }),
listOf(
element.fullQualifiedName)
+ usedTypes.mapNotNull { it.fullQualifiedName } + parents.mapNotNull { it.fullQualifiedName }
+ listOfNotNull(pureAbstractElementType.fullQualifiedName?.takeIf { needPureAbstractElement }
),
isImpl = true
)
}
@@ -182,6 +188,9 @@ fun Element.collectImports(): List<String> {
if (isBaseFirElement) {
baseTypes += compositeTransformResultType.fullQualifiedName!!
}
if (needPureAbstractElement) {
baseTypes += pureAbstractElementType.fullQualifiedName!!
}
return collectImportsInternal(
baseTypes,
isImpl = false
@@ -257,34 +266,43 @@ fun PrintWriter.printImplementation(implementation: Implementation) {
}
with(implementation) {
print("${kind.title} $type")
print("${kind!!.title} $type")
print(element.typeParameters)
val fieldsWithoutDefault = allFields.filter { it.defaultValue == null && !it.isLateinit }
val fieldsWithDefault = allFields.filter { it.defaultValue != null || it.isLateinit }
val isInterface = kind == Implementation.Kind.Interface
val isAbstract = kind == Implementation.Kind.AbstractClass
if (!isInterface && fieldsWithoutDefault.isNotEmpty()) {
fun abstract() {
if (isAbstract) {
print("abstract ")
}
}
if (!isInterface && !isAbstract && fieldsWithoutDefault.isNotEmpty()) {
println("(")
fieldsWithoutDefault.forEachIndexed { i, field ->
val end = if (i == fieldsWithoutDefault.size - 1) "" else ","
printField(field, isImplementation = true, override = true, end = end)
printField(field, isImplementation = true, override = true, end = end, withIndent = true)
}
print(")")
}
print(" : ")
if (!isInterface) {
if (!isInterface && !allParents.any { it.kind == Implementation.Kind.AbstractClass }) {
print("${pureAbstractElementType.type}(), ")
}
print(element.typeWithArguments)
parents.forEach {
print(", ${it.typeWithArguments}")
}
// print(element.typeWithArguments)
print(allParents.joinToString { "${it.typeWithArguments}${it.kind.braces()}" })
println(" {")
if (isInterface) {
allFields.forEach { printField(it, isImplementation = true, override = true, end = "") }
if (isInterface || isAbstract) {
allFields.forEach {
indent()
abstract()
printField(it, isImplementation = true, override = true, end = "", withIndent = false)
}
} else {
fieldsWithDefault.forEach {
printFieldWithDefaultInImplementation(it)
@@ -295,7 +313,7 @@ fun PrintWriter.printImplementation(implementation: Implementation) {
}
element.allFields.filter { it.type.contains("Symbol") && it !is FieldList }
.takeIf { it.isNotEmpty() && !isInterface && !element.type.contains("Reference")}
.takeIf { it.isNotEmpty() && !isInterface && !isAbstract && !element.type.contains("Reference")}
?.let { symbolFields ->
indent(1)
println("init {")
@@ -309,7 +327,7 @@ fun PrintWriter.printImplementation(implementation: Implementation) {
}
fun Field.acceptString(): String = "${name}${call()}accept(visitor, data)"
if (!isInterface) {
if (!isInterface && !isAbstract) {
indent(1)
print("override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {")
@@ -382,8 +400,9 @@ fun PrintWriter.printImplementation(implementation: Implementation) {
}
indent()
abstract()
print("override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): $typeWithArguments")
if (!isInterface) {
if (!isInterface && !isAbstract) {
println(" {")
for (field in allFields) {
when {
@@ -456,8 +475,9 @@ fun PrintWriter.printImplementation(implementation: Implementation) {
if (!field.needsSeparateTransform) continue
println()
indent()
abstract()
print("override ${field.transformFunctionDeclaration(typeWithArguments)}")
if (isInterface) {
if (isInterface || isAbstract) {
println()
continue
}
@@ -486,8 +506,9 @@ fun PrintWriter.printImplementation(implementation: Implementation) {
if (element.needTransformOtherChildren) {
println()
indent()
abstract()
print("override fun <D> transformOtherChildren(transformer: FirTransformer<D>, data: D): $typeWithArguments")
if (isInterface) {
if (isInterface || isAbstract) {
println()
} else {
println(" {")
@@ -508,8 +529,9 @@ fun PrintWriter.printImplementation(implementation: Implementation) {
for (field in allFields.filter { it.withReplace }) {
println()
indent()
abstract()
print("override ${field.replaceFunctionDeclaration()}")
if (isInterface) {
if (isInterface || isAbstract) {
println()
continue
}
@@ -606,8 +628,10 @@ fun printTransformer(elements: List<Element>, generationPath: String) {
}
}
fun PrintWriter.printField(field: Field, isImplementation: Boolean, override: Boolean, end: String) {
indent()
fun PrintWriter.printField(field: Field, isImplementation: Boolean, override: Boolean, end: String, withIndent: Boolean) {
if (withIndent) {
indent()
}
if (override) {
print("override ")
}
@@ -621,10 +645,10 @@ fun PrintWriter.printField(field: Field, isImplementation: Boolean, override: Bo
}
val Field.mutableType: String get() = when (this) {
is FieldList -> if (isMutable) "Mutable$typeWithArguments" else typeWithArguments
is FieldWithDefault -> if (isMutable) origin.mutableType else typeWithArguments
else -> typeWithArguments
}
is FieldList -> if (isMutable) "Mutable$typeWithArguments" else typeWithArguments
is FieldWithDefault -> if (isMutable) origin.mutableType else typeWithArguments
else -> typeWithArguments
}
fun Field.call(): String = if (nullable) "?." else "."
@@ -637,44 +661,68 @@ fun Element.multipleUpperBoundsList(): String {
} ?: " "
}
fun PrintWriter.printElement(element: Element) {
fun Element.override() {
indent()
if (this != AbstractFirTreeBuilder.baseFirElement) {
print("override ")
}
}
fun Implementation.Kind?.braces(): String = when (this) {
Implementation.Kind.Interface -> ""
Implementation.Kind.OpenClass, Implementation.Kind.AbstractClass -> "()"
else -> throw IllegalStateException(this.toString())
}
fun PrintWriter.printElement(element: Element) {
with(element) {
print("interface $type")
val isInterface = kind == Implementation.Kind.Interface
fun abstract() {
indent()
if (!isInterface) {
print("abstract ")
}
}
fun override() {
if (this != AbstractFirTreeBuilder.baseFirElement) {
print("override ")
}
}
print("${kind!!.title} $type")
if (typeArguments.isNotEmpty()) {
print(typeArguments.joinToString(", ", "<", ">") { it.toString() })
}
if (parents.isNotEmpty()) {
val needPureAbstractElement = !isInterface && !allParents.any { it.kind == Implementation.Kind.AbstractClass }
if (parents.isNotEmpty() || needPureAbstractElement) {
print(" : ")
if (needPureAbstractElement) {
print("${pureAbstractElementType.type}()")
if (parents.isNotEmpty()) {
print(", ")
}
}
print(parents.joinToString(", ") {
var result = it.type
parentsArguments[it]?.let { arguments ->
result += arguments.values.joinToString(", ", "<", ">") { it.typeWithArguments }
}
result
result + it.kind.braces()
})
}
print(multipleUpperBoundsList())
println("{")
allFields.forEach {
printField(it, isImplementation = false, override = it.fromParent, end = "")
abstract()
printField(it, isImplementation = false, override = it.fromParent, end = "", withIndent = false)
}
if (allFields.isNotEmpty()) {
println()
}
indent()
override()
println("fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visit$name(this, data)")
fields.filter { it.withReplace }.forEach {
println()
indent()
abstract()
if (it.fromParent) print("override ")
println(it.replaceFunctionDeclaration())
}
@@ -682,7 +730,7 @@ fun PrintWriter.printElement(element: Element) {
for (field in allFields) {
if (!field.needsSeparateTransform) continue
println()
indent()
abstract()
if (field.fromParent) {
print("override ")
}
@@ -690,7 +738,7 @@ fun PrintWriter.printElement(element: Element) {
}
if (needTransformOtherChildren) {
println()
indent()
abstract()
if (element.parents.any { it.needTransformOtherChildren }) {
print("override ")
}
@@ -698,6 +746,7 @@ fun PrintWriter.printElement(element: Element) {
}
if (element == AbstractFirTreeBuilder.baseFirElement) {
require(isInterface)
println()
indent()
println("fun accept(visitor: FirVisitorVoid) = accept(visitor, null)")
@@ -72,6 +72,10 @@ abstract class AbstractFieldConfigurator {
fun needTransformOtherChildren() {
element._needTransformOtherChildren = true
}
fun shouldBeAnInterface() {
element.kind = Implementation.Kind.Interface
}
}
inline fun Element.configure(block: ConfigureContext.() -> Unit) {
@@ -180,7 +180,7 @@ abstract class AbstractFirTreeImplementationConfigurator {
}
}
var kind: Implementation.Kind
var kind: Implementation.Kind?
get() = implementation.kind
set(value) {
implementation.kind = value
@@ -8,12 +8,17 @@ package org.jetbrains.kotlin.fir.tree.generator.model
import org.jetbrains.kotlin.fir.tree.generator.BASE_PACKAGE
import org.jetbrains.kotlin.fir.tree.generator.typeWithArguments
interface KindOwner : Importable {
var kind: Implementation.Kind?
val allParents: List<KindOwner>
}
interface FieldContainer {
val allFields: List<Field>
operator fun get(fieldName: String): Field?
}
interface AbstractElement : FieldContainer, Importable {
interface AbstractElement : FieldContainer, KindOwner {
val fields: Set<Field>
val parents: List<AbstractElement>
val typeArguments: List<TypeArgument>
@@ -26,6 +31,8 @@ interface AbstractElement : FieldContainer, Importable {
val allFirFields: List<Field>
val defaultImplementation: Implementation?
val customImplementations: List<Implementation>
override val allParents: List<KindOwner> get() = parents
}
class Element(val name: String, kind: Kind) : AbstractElement {
@@ -38,6 +45,13 @@ class Element(val name: String, kind: Kind) : AbstractElement {
override val customImplementations = mutableListOf<Implementation>()
override val typeArguments = mutableListOf<TypeArgument>()
override val parentsArguments = mutableMapOf<AbstractElement, MutableMap<Importable, Importable>>()
override var kind: Implementation.Kind? = null
set(value) {
if (value != Implementation.Kind.Interface && value != Implementation.Kind.AbstractClass) {
throw IllegalArgumentException(value.toString())
}
field = value
}
var _needTransformOtherChildren: Boolean = false
override var baseTransformerType: Element? = null
@@ -8,20 +8,21 @@ package org.jetbrains.kotlin.fir.tree.generator.model
class ImplementationWithArg(
val implementation: Implementation,
val argument: Importable?
) : Importable by implementation, FieldContainer by implementation {
) : FieldContainer by implementation, KindOwner by implementation {
val element: Element get() = implementation.element
}
class Implementation(val element: Element, val name: String?) : Importable, FieldContainer {
class Implementation(val element: Element, val name: String?) : FieldContainer, KindOwner {
private val _parents = mutableListOf<ImplementationWithArg>()
val parents: List<ImplementationWithArg> get() = _parents
override val allParents: List<KindOwner> get() = listOf(element) + parents
val isDefault = name == null
override val type = name ?: element.type + "Impl"
override val allFields = element.allFields.toMutableList().mapTo(mutableListOf()) {
FieldWithDefault(it)
}
var kind: Kind = Kind.FinalClass
override var kind: Kind? = null
override val packageName = element.packageName + ".impl"
val usedTypes = mutableListOf<Importable>()