[IR generator] Get rid of the config model
In the IR generator, we had a so-called `ConfigModel` with classes like `ElementConfig` and `FieldConfig` that we deal with during tree configuration in `IrTree.kt`, and another kind of model called just `Model` that included the `Element` and `Field` classes correspondingly. Those were used for actual code generation. After configuration, `ConfigModel` was transformed to `Model` and then the code generation was performed using `Model`. This architecture is overly complicated and results in massive code duplication. Most `ElementConfig` and `FieldConfig` properties had exactly the same counterparts in `Element` in `Field` classes. So, if you wanted to add a new feature to the tree generator, you had to add a property to both `ConfigModel` and `Model`. Turns out we can do just fine with only one kind of model.
This commit is contained in:
committed by
Space Team
parent
37417f7919
commit
6ac4cd5973
+227
-229
File diff suppressed because it is too large
Load Diff
@@ -5,10 +5,11 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.generator
|
package org.jetbrains.kotlin.ir.generator
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.generators.tree.addPureAbstractElement
|
||||||
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil
|
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil
|
||||||
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil.collectPreviouslyGeneratedFiles
|
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil.collectPreviouslyGeneratedFiles
|
||||||
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil.removeExtraFilesFromPreviousGeneration
|
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil.removeExtraFilesFromPreviousGeneration
|
||||||
import org.jetbrains.kotlin.ir.generator.model.config2model
|
import org.jetbrains.kotlin.ir.generator.model.*
|
||||||
import org.jetbrains.kotlin.ir.generator.print.*
|
import org.jetbrains.kotlin.ir.generator.print.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -20,8 +21,13 @@ fun main(args: Array<String>) {
|
|||||||
val generationPath = args.firstOrNull()?.let { File(it) }
|
val generationPath = args.firstOrNull()?.let { File(it) }
|
||||||
?: File("compiler/ir/ir.tree/gen").canonicalFile
|
?: File("compiler/ir/ir.tree/gen").canonicalFile
|
||||||
|
|
||||||
val config = IrTree.build()
|
val model = IrTree.build()
|
||||||
val model = config2model(config)
|
|
||||||
|
configureInterfacesAndAbstractClasses(model.elements)
|
||||||
|
addPureAbstractElement(model.elements, elementBaseType)
|
||||||
|
markLeaves(model.elements)
|
||||||
|
processFieldOverrides(model.elements)
|
||||||
|
addWalkableChildren(model.elements)
|
||||||
|
|
||||||
val previouslyGeneratedFiles = collectPreviouslyGeneratedFiles(generationPath)
|
val previouslyGeneratedFiles = collectPreviouslyGeneratedFiles(generationPath)
|
||||||
val generatedFiles = sequence {
|
val generatedFiles = sequence {
|
||||||
|
|||||||
+60
-28
@@ -5,30 +5,47 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.generator.config
|
package org.jetbrains.kotlin.ir.generator.config
|
||||||
|
|
||||||
import org.jetbrains.kotlin.generators.tree.TypeRef
|
import org.jetbrains.kotlin.generators.tree.*
|
||||||
import org.jetbrains.kotlin.generators.tree.TypeRefWithNullability
|
import org.jetbrains.kotlin.ir.generator.model.*
|
||||||
import org.jetbrains.kotlin.generators.tree.TypeVariable
|
import org.jetbrains.kotlin.ir.generator.model.ElementRef
|
||||||
import org.jetbrains.kotlin.generators.tree.type
|
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import kotlin.properties.PropertyDelegateProvider
|
import kotlin.properties.PropertyDelegateProvider
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
import org.jetbrains.kotlin.generators.tree.ElementOrRef as GenericElementOrRef
|
||||||
|
|
||||||
abstract class AbstractTreeBuilder {
|
abstract class AbstractTreeBuilder {
|
||||||
private val configurationCallbacks = mutableListOf<() -> ElementConfig>()
|
private val configurationCallbacks = mutableListOf<() -> Element>()
|
||||||
|
|
||||||
abstract val rootElement: ElementConfig
|
abstract val rootElement: Element
|
||||||
|
|
||||||
fun element(category: ElementConfig.Category, name: String? = null, initializer: ElementConfig.() -> Unit = {}): ElementConfigDel {
|
protected fun Field.skipInIrFactory() {
|
||||||
val del = ElementConfigDel(category, name)
|
useInIrFactoryStrategy = Field.UseFieldAsParameterInIrFactoryStrategy.No
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun Field.useFieldInIrFactory(defaultValue: String? = null) {
|
||||||
|
useInIrFactoryStrategy = Field.UseFieldAsParameterInIrFactoryStrategy.Yes(defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun element(category: Element.Category, name: String? = null, initializer: Element.() -> Unit = {}): ElementDelegate {
|
||||||
|
val del = ElementDelegate(category, name)
|
||||||
configurationCallbacks.add {
|
configurationCallbacks.add {
|
||||||
del.element!!.apply { initializer() }
|
del.element!!.apply {
|
||||||
|
initializer()
|
||||||
|
if (elementParents.isEmpty() && this != rootElement) {
|
||||||
|
elementParents.add(ElementRef(rootElement))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return del
|
return del
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun ElementConfig.parent(type: TypeRef) {
|
protected fun Element.parent(type: ClassRef<*>) {
|
||||||
parents.add(type)
|
otherParents.add(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun Element.parent(type: ElementOrRef) {
|
||||||
|
elementParents.add(ElementRef(type.element, type.args, type.nullable))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun param(name: String, vararg bounds: TypeRef, variance: Variance = Variance.INVARIANT): TypeVariable {
|
protected fun param(name: String, vararg bounds: TypeRef, variance: Variance = Variance.INVARIANT): TypeVariable {
|
||||||
@@ -41,33 +58,48 @@ abstract class AbstractTreeBuilder {
|
|||||||
nullable: Boolean = false,
|
nullable: Boolean = false,
|
||||||
mutable: Boolean = true,
|
mutable: Boolean = true,
|
||||||
isChild: Boolean = false,
|
isChild: Boolean = false,
|
||||||
initializer: SimpleFieldConfig.() -> Unit = {}
|
initializer: SingleField.() -> Unit = {}
|
||||||
): SimpleFieldConfig {
|
): SingleField {
|
||||||
checkChildType(isChild, type, name)
|
checkChildType(isChild, type, name)
|
||||||
return SimpleFieldConfig(name, type, nullable, mutable, isChild).apply(initializer)
|
return SingleField(name, type?.copy(nullable) ?: InferredOverriddenType, mutable, isChild).apply(initializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun listField(
|
protected fun listField(
|
||||||
name: String,
|
name: String,
|
||||||
elementType: TypeRef?,
|
elementType: TypeRef?,
|
||||||
nullable: Boolean = false,
|
nullable: Boolean = false,
|
||||||
mutability: ListFieldConfig.Mutability = ListFieldConfig.Mutability.Immutable,
|
mutability: ListField.Mutability = ListField.Mutability.Immutable,
|
||||||
isChild: Boolean = false,
|
isChild: Boolean = false,
|
||||||
initializer: ListFieldConfig.() -> Unit = {}
|
initializer: ListField.() -> Unit = {}
|
||||||
): ListFieldConfig {
|
): ListField {
|
||||||
checkChildType(isChild, elementType, name)
|
checkChildType(isChild, elementType, name)
|
||||||
return ListFieldConfig(name, elementType, nullable, mutability, isChild).apply(initializer)
|
val listType = when (mutability) {
|
||||||
|
ListField.Mutability.List -> StandardTypes.mutableList
|
||||||
|
ListField.Mutability.Array -> StandardTypes.array
|
||||||
|
else -> StandardTypes.list
|
||||||
|
}
|
||||||
|
return ListField(
|
||||||
|
name = name,
|
||||||
|
elementType = elementType ?: InferredOverriddenType,
|
||||||
|
listType = listType,
|
||||||
|
isNullable = nullable,
|
||||||
|
mutable = mutability == ListField.Mutability.Var,
|
||||||
|
isChild = isChild,
|
||||||
|
transformable = mutability != ListField.Mutability.Immutable,
|
||||||
|
).apply(initializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkChildType(isChild: Boolean, type: TypeRef?, name: String) {
|
private fun checkChildType(isChild: Boolean, type: TypeRef?, name: String) {
|
||||||
if (isChild) {
|
if (isChild) {
|
||||||
require(type == null || type is ElementConfigOrRef) { "Field $name is a child field but has non-element type $type" }
|
require(type == null || type is GenericElementOrRef<*, *>) {
|
||||||
|
"Field $name is a child field but has non-element type $type"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(): Config {
|
fun build(): Model {
|
||||||
val elements = configurationCallbacks.map { it() }
|
val elements = configurationCallbacks.map { it() }
|
||||||
return Config(elements, rootElement)
|
return Model(elements, rootElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -77,20 +109,20 @@ abstract class AbstractTreeBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ElementConfigDel(
|
class ElementDelegate(
|
||||||
private val category: ElementConfig.Category,
|
private val category: Element.Category,
|
||||||
private val name: String?
|
private val name: String?
|
||||||
) : ReadOnlyProperty<AbstractTreeBuilder, ElementConfig>, PropertyDelegateProvider<AbstractTreeBuilder, ElementConfigDel> {
|
) : ReadOnlyProperty<AbstractTreeBuilder, Element>, PropertyDelegateProvider<AbstractTreeBuilder, ElementDelegate> {
|
||||||
var element: ElementConfig? = null
|
var element: Element? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
override fun getValue(thisRef: AbstractTreeBuilder, property: KProperty<*>): ElementConfig {
|
override fun getValue(thisRef: AbstractTreeBuilder, property: KProperty<*>): Element {
|
||||||
return element!!
|
return element!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun provideDelegate(thisRef: AbstractTreeBuilder, property: KProperty<*>): ElementConfigDel {
|
override fun provideDelegate(thisRef: AbstractTreeBuilder, property: KProperty<*>): ElementDelegate {
|
||||||
val path = thisRef.javaClass.name + "." + property.name
|
val path = thisRef.javaClass.name + "." + property.name
|
||||||
element = ElementConfig(path, name ?: property.name, category)
|
element = Element(name ?: property.name, path, category)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-185
@@ -1,185 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2021 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.ir.generator.config
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.generators.tree.*
|
|
||||||
import org.jetbrains.kotlin.ir.generator.BASE_PACKAGE
|
|
||||||
import org.jetbrains.kotlin.ir.generator.model.Element
|
|
||||||
import org.jetbrains.kotlin.utils.SmartPrinter
|
|
||||||
|
|
||||||
class Config(
|
|
||||||
val elements: List<ElementConfig>,
|
|
||||||
val rootElement: ElementConfig,
|
|
||||||
)
|
|
||||||
|
|
||||||
class ElementConfig(
|
|
||||||
val propertyName: String,
|
|
||||||
val name: String,
|
|
||||||
val category: Category
|
|
||||||
) : ElementConfigOrRef {
|
|
||||||
val params = mutableListOf<TypeVariable>()
|
|
||||||
val parents = mutableListOf<TypeRef>()
|
|
||||||
val fields = mutableListOf<FieldConfig>()
|
|
||||||
val usedTypes = mutableListOf<Importable>()
|
|
||||||
|
|
||||||
var visitorName: String? = null
|
|
||||||
var visitorParent: ElementConfig? = null
|
|
||||||
var visitorParam: String? = null
|
|
||||||
var accept = false // By default, accept is generated only for leaves.
|
|
||||||
var transform = false
|
|
||||||
var transformByChildren = false
|
|
||||||
var transformerReturnType: ElementConfig? = null
|
|
||||||
var childrenOrderOverride: List<String>? = null
|
|
||||||
|
|
||||||
var ownsChildren = true // If false, acceptChildren/transformChildren will NOT be generated.
|
|
||||||
|
|
||||||
var generateIrFactoryMethod = category == Category.Declaration
|
|
||||||
val additionalIrFactoryMethodParameters = mutableListOf<FieldConfig>()
|
|
||||||
val fieldsToSkipInIrFactoryMethod = hashSetOf<String>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set this to `true` if the element should be a leaf semantically, but technically it's not.
|
|
||||||
*
|
|
||||||
* For example, we only generate [IrFactory] methods for leaf elements. If we want to generate a method for this element, but it has
|
|
||||||
* subclasses, it can be done by setting this property to `true`.
|
|
||||||
*/
|
|
||||||
var isForcedLeaf = false
|
|
||||||
|
|
||||||
var typeKind: TypeKind? = null
|
|
||||||
|
|
||||||
var generationCallback: (context(ImportCollector) SmartPrinter.() -> Unit)? = null
|
|
||||||
var kDoc: String? = null
|
|
||||||
|
|
||||||
override val element get() = this
|
|
||||||
override val args get() = emptyMap<NamedTypeParameterRef, TypeRef>()
|
|
||||||
override val nullable get() = false
|
|
||||||
override fun copy(args: Map<NamedTypeParameterRef, TypeRef>) = ElementConfigRef(this, args, false)
|
|
||||||
override fun copy(nullable: Boolean) = ElementConfigRef(this, args, nullable)
|
|
||||||
|
|
||||||
override fun substitute(map: TypeParameterSubstitutionMap): ElementConfig = this
|
|
||||||
|
|
||||||
operator fun TypeVariable.unaryPlus() = apply {
|
|
||||||
params.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun FieldConfig.unaryPlus() = apply {
|
|
||||||
fields.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString() = element.name
|
|
||||||
|
|
||||||
override val packageName: String
|
|
||||||
get() = category.packageName
|
|
||||||
|
|
||||||
override val typeName: String
|
|
||||||
get() = Element.elementName2typeName(name)
|
|
||||||
|
|
||||||
context(ImportCollector)
|
|
||||||
override fun renderTo(appendable: Appendable) {
|
|
||||||
addImport(this)
|
|
||||||
appendable.append(typeName)
|
|
||||||
if (params.isNotEmpty()) {
|
|
||||||
params.joinTo(appendable, prefix = "<", postfix = ">") { it.name }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Category(private val packageDir: String, val defaultVisitorParam: String) {
|
|
||||||
Expression("expressions", "expression"),
|
|
||||||
Declaration("declarations", "declaration"),
|
|
||||||
Other("", "element");
|
|
||||||
|
|
||||||
val packageName: String get() = BASE_PACKAGE + if (packageDir.isNotEmpty()) ".$packageDir" else ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface ElementConfigOrRef : ParametrizedTypeRef<ElementConfigOrRef, NamedTypeParameterRef>, TypeRefWithNullability, Importable {
|
|
||||||
val element: ElementConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
class ElementConfigRef(
|
|
||||||
override val element: ElementConfig,
|
|
||||||
override val args: Map<NamedTypeParameterRef, TypeRef>,
|
|
||||||
override val nullable: Boolean
|
|
||||||
) : ElementConfigOrRef {
|
|
||||||
|
|
||||||
override val packageName: String
|
|
||||||
get() = element.packageName
|
|
||||||
|
|
||||||
override val typeName: String
|
|
||||||
get() = element.typeName
|
|
||||||
|
|
||||||
override fun copy(args: Map<NamedTypeParameterRef, TypeRef>) = ElementConfigRef(element, args, nullable)
|
|
||||||
override fun copy(nullable: Boolean) = ElementConfigRef(element, args, nullable)
|
|
||||||
|
|
||||||
override fun toString() = element.name
|
|
||||||
|
|
||||||
context(ImportCollector)
|
|
||||||
override fun renderTo(appendable: Appendable) {
|
|
||||||
addImport(element)
|
|
||||||
appendable.append(element.typeName)
|
|
||||||
renderArgsTo(appendable)
|
|
||||||
renderNullabilityTo(appendable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class UseFieldAsParameterInIrFactoryStrategy {
|
|
||||||
|
|
||||||
data object No : UseFieldAsParameterInIrFactoryStrategy()
|
|
||||||
|
|
||||||
data class Yes(val defaultValue: String?) : UseFieldAsParameterInIrFactoryStrategy()
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class FieldConfig(
|
|
||||||
val name: String,
|
|
||||||
val isChild: Boolean,
|
|
||||||
) {
|
|
||||||
var baseDefaultValue: String? = null
|
|
||||||
var baseGetter: String? = null
|
|
||||||
var customSetter: String? = null
|
|
||||||
var optInAnnotation: ClassRef<*>? = null
|
|
||||||
|
|
||||||
var deprecation: Deprecated? = null
|
|
||||||
|
|
||||||
var visibility = Visibility.PUBLIC
|
|
||||||
|
|
||||||
internal var useFieldInIrFactoryStrategy: UseFieldAsParameterInIrFactoryStrategy =
|
|
||||||
if (isChild) UseFieldAsParameterInIrFactoryStrategy.No else UseFieldAsParameterInIrFactoryStrategy.Yes(null)
|
|
||||||
|
|
||||||
fun useFieldInIrFactory(defaultValue: String? = null) {
|
|
||||||
useFieldInIrFactoryStrategy = UseFieldAsParameterInIrFactoryStrategy.Yes(defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun skipInIrFactory() {
|
|
||||||
useFieldInIrFactoryStrategy = UseFieldAsParameterInIrFactoryStrategy.No
|
|
||||||
}
|
|
||||||
|
|
||||||
var kDoc: String? = null
|
|
||||||
|
|
||||||
override fun toString() = name
|
|
||||||
}
|
|
||||||
|
|
||||||
class SimpleFieldConfig(
|
|
||||||
name: String,
|
|
||||||
val type: TypeRefWithNullability?,
|
|
||||||
val nullable: Boolean,
|
|
||||||
val mutable: Boolean,
|
|
||||||
isChildElement: Boolean,
|
|
||||||
) : FieldConfig(name, isChildElement)
|
|
||||||
|
|
||||||
class ListFieldConfig(
|
|
||||||
name: String,
|
|
||||||
val elementType: TypeRef?,
|
|
||||||
val nullable: Boolean,
|
|
||||||
val mutability: Mutability,
|
|
||||||
isChildElement: Boolean,
|
|
||||||
) : FieldConfig(name, isChildElement) {
|
|
||||||
enum class Mutability {
|
|
||||||
Immutable,
|
|
||||||
Var,
|
|
||||||
List,
|
|
||||||
Array
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+92
-47
@@ -6,23 +6,40 @@
|
|||||||
package org.jetbrains.kotlin.ir.generator.model
|
package org.jetbrains.kotlin.ir.generator.model
|
||||||
|
|
||||||
import org.jetbrains.kotlin.generators.tree.*
|
import org.jetbrains.kotlin.generators.tree.*
|
||||||
import org.jetbrains.kotlin.ir.generator.config.ElementConfig
|
import org.jetbrains.kotlin.ir.generator.BASE_PACKAGE
|
||||||
import org.jetbrains.kotlin.ir.generator.config.FieldConfig
|
import org.jetbrains.kotlin.utils.SmartPrinter
|
||||||
import org.jetbrains.kotlin.utils.topologicalSort
|
import org.jetbrains.kotlin.utils.topologicalSort
|
||||||
import org.jetbrains.kotlin.generators.tree.ElementOrRef as GenericElementOrRef
|
import org.jetbrains.kotlin.generators.tree.ElementOrRef as GenericElementOrRef
|
||||||
import org.jetbrains.kotlin.generators.tree.ElementRef as GenericElementRef
|
import org.jetbrains.kotlin.generators.tree.ElementRef as GenericElementRef
|
||||||
|
|
||||||
class Element(
|
class Element(
|
||||||
config: ElementConfig,
|
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val packageName: String,
|
override val propertyName: String,
|
||||||
override val params: List<TypeVariable>,
|
category: Category,
|
||||||
override val fields: MutableSet<Field>,
|
|
||||||
val additionalFactoryMethodParameters: MutableList<Field>,
|
|
||||||
) : AbstractElement<Element, Field>() {
|
) : AbstractElement<Element, Field>() {
|
||||||
override var elementParents: List<ElementRef> = emptyList()
|
|
||||||
|
enum class Category(private val packageDir: String, val defaultVisitorParam: String) {
|
||||||
|
Expression("expressions", "expression"),
|
||||||
|
Declaration("declarations", "declaration"),
|
||||||
|
Other("", "element");
|
||||||
|
|
||||||
|
val packageName: String get() = BASE_PACKAGE + if (packageDir.isNotEmpty()) ".$packageDir" else ""
|
||||||
|
}
|
||||||
|
|
||||||
|
override val packageName: String = category.packageName
|
||||||
|
|
||||||
|
override val elementParents = mutableListOf<ElementRef>()
|
||||||
override var otherParents: MutableList<ClassRef<*>> = mutableListOf()
|
override var otherParents: MutableList<ClassRef<*>> = mutableListOf()
|
||||||
|
|
||||||
|
override val params = mutableListOf<TypeVariable>()
|
||||||
|
|
||||||
|
override val fields = mutableSetOf<Field>()
|
||||||
|
|
||||||
|
val additionalIrFactoryMethodParameters = mutableListOf<Field>()
|
||||||
|
var generateIrFactoryMethod = category == Category.Declaration
|
||||||
|
val fieldsToSkipInIrFactoryMethod = hashSetOf<String>()
|
||||||
|
|
||||||
|
|
||||||
override val allFields: List<Field>
|
override val allFields: List<Field>
|
||||||
get() = fields.toList()
|
get() = fields.toList()
|
||||||
|
|
||||||
@@ -38,26 +55,46 @@ class Element(
|
|||||||
override var parentInVisitor: Element? = null
|
override var parentInVisitor: Element? = null
|
||||||
var transformerReturnType: Element? = null
|
var transformerReturnType: Element? = null
|
||||||
|
|
||||||
override var kind: ImplementationKind? = when (config.typeKind) {
|
var typeKind: TypeKind? = null
|
||||||
TypeKind.Class -> ImplementationKind.AbstractClass
|
set(value) {
|
||||||
TypeKind.Interface -> ImplementationKind.Interface
|
kind = when (value) {
|
||||||
null -> null
|
TypeKind.Class -> ImplementationKind.AbstractClass
|
||||||
}
|
TypeKind.Interface -> ImplementationKind.Interface
|
||||||
|
null -> null
|
||||||
|
}
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override var kind: ImplementationKind? = null
|
||||||
|
|
||||||
override val typeName
|
override val typeName
|
||||||
get() = elementName2typeName(name)
|
get() = elementName2typeName(name)
|
||||||
|
|
||||||
var isLeaf = config.isForcedLeaf
|
/**
|
||||||
val childrenOrderOverride: List<String>? = config.childrenOrderOverride
|
* Whether this element is semantically a leaf element in the hierarchy.
|
||||||
|
*
|
||||||
|
* This is set automatically by the [markLeaves] function for all true leaves, but can also be set manually to `true` if
|
||||||
|
* this element should be considered a leaf semantically.
|
||||||
|
*
|
||||||
|
* For example, we only generate [org.jetbrains.kotlin.ir.declarations.IrFactory] methods for leaf elements.
|
||||||
|
* If we want to generate a method for this element, but it has subclasses, it can be done by manually setting this property to `true`.
|
||||||
|
*/
|
||||||
|
var isLeaf = false
|
||||||
|
|
||||||
|
var childrenOrderOverride: List<String>? = null
|
||||||
override var walkableChildren: List<Field> = emptyList()
|
override var walkableChildren: List<Field> = emptyList()
|
||||||
override val transformableChildren get() = walkableChildren.filter { it.transformable }
|
override val transformableChildren get() = walkableChildren.filter { it.transformable }
|
||||||
|
|
||||||
override val visitFunctionName = "visit" + (config.visitorName ?: name).replaceFirstChar(Char::uppercaseChar)
|
var visitorName: String? = null
|
||||||
override val visitorParameterName = config.visitorParam ?: config.category.defaultVisitorParam
|
|
||||||
|
|
||||||
override var hasAcceptMethod = config.accept
|
override val visitFunctionName: String
|
||||||
|
get() = "visit" + (visitorName ?: name).replaceFirstChar(Char::uppercaseChar)
|
||||||
|
|
||||||
override val hasTransformMethod = config.transform
|
override var visitorParameterName = category.defaultVisitorParam
|
||||||
|
|
||||||
|
override var hasAcceptMethod = false // By default, accept is generated only for leaves.
|
||||||
|
|
||||||
|
override var hasTransformMethod = false
|
||||||
|
|
||||||
override val hasAcceptChildrenMethod: Boolean
|
override val hasAcceptChildrenMethod: Boolean
|
||||||
get() = ownsChildren && (isRootElement || walkableChildren.isNotEmpty())
|
get() = ownsChildren && (isRootElement || walkableChildren.isNotEmpty())
|
||||||
@@ -65,17 +102,14 @@ class Element(
|
|||||||
override val hasTransformChildrenMethod: Boolean
|
override val hasTransformChildrenMethod: Boolean
|
||||||
get() = ownsChildren && (isRootElement || transformableChildren.isNotEmpty())
|
get() = ownsChildren && (isRootElement || transformableChildren.isNotEmpty())
|
||||||
|
|
||||||
val transformByChildren = config.transformByChildren
|
var transformByChildren = false
|
||||||
val ownsChildren = config.ownsChildren
|
var ownsChildren = true // If false, acceptChildren/transformChildren will NOT be generated.
|
||||||
val generateIrFactoryMethod = config.generateIrFactoryMethod
|
|
||||||
val fieldsToSkipInIrFactoryMethod = config.fieldsToSkipInIrFactoryMethod
|
|
||||||
|
|
||||||
val generationCallback = config.generationCallback
|
var generationCallback: (context(ImportCollector) SmartPrinter.() -> Unit)? = null
|
||||||
override val propertyName = config.propertyName
|
|
||||||
|
|
||||||
override val kDoc = config.kDoc
|
override var kDoc: String? = null
|
||||||
|
|
||||||
val usedTypes: List<Importable> = config.usedTypes
|
val usedTypes = mutableListOf<Importable>()
|
||||||
|
|
||||||
override fun toString() = name
|
override fun toString() = name
|
||||||
|
|
||||||
@@ -105,38 +139,46 @@ class Element(
|
|||||||
.distinctBy { it.name }
|
.distinctBy { it.name }
|
||||||
.asReversed()
|
.asReversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator fun TypeVariable.unaryPlus() = apply {
|
||||||
|
params.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Field.unaryPlus() = apply {
|
||||||
|
fields.add(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias ElementRef = GenericElementRef<Element, Field>
|
typealias ElementRef = GenericElementRef<Element, Field>
|
||||||
typealias ElementOrRef = GenericElementOrRef<Element, Field>
|
typealias ElementOrRef = GenericElementOrRef<Element, Field>
|
||||||
|
|
||||||
@Suppress("LeakingThis")
|
|
||||||
sealed class Field(
|
sealed class Field(
|
||||||
config: FieldConfig,
|
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override var isMutable: Boolean,
|
override var isMutable: Boolean,
|
||||||
val isChild: Boolean,
|
val isChild: Boolean,
|
||||||
) : AbstractField() {
|
) : AbstractField() {
|
||||||
abstract val baseDefaultValue: String?
|
var baseDefaultValue: String? = null
|
||||||
abstract val baseGetter: String?
|
var baseGetter: String? = null
|
||||||
|
|
||||||
abstract val transformable: Boolean
|
abstract val transformable: Boolean
|
||||||
|
|
||||||
val useInIrFactoryStrategy = config.useFieldInIrFactoryStrategy
|
sealed class UseFieldAsParameterInIrFactoryStrategy {
|
||||||
|
|
||||||
init {
|
data object No : UseFieldAsParameterInIrFactoryStrategy()
|
||||||
kDoc = config.kDoc
|
|
||||||
optInAnnotation = config.optInAnnotation
|
data class Yes(val defaultValue: String?) : UseFieldAsParameterInIrFactoryStrategy()
|
||||||
deprecation = config.deprecation
|
|
||||||
visibility = config.visibility
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var useInIrFactoryStrategy =
|
||||||
|
if (isChild) UseFieldAsParameterInIrFactoryStrategy.No else UseFieldAsParameterInIrFactoryStrategy.Yes(null)
|
||||||
|
|
||||||
override val withGetter: Boolean
|
override val withGetter: Boolean
|
||||||
get() = baseGetter != null
|
get() = baseGetter != null
|
||||||
|
|
||||||
override val defaultValueInImplementation: String?
|
override val defaultValueInImplementation: String?
|
||||||
get() = baseGetter ?: baseDefaultValue
|
get() = baseGetter ?: baseDefaultValue
|
||||||
|
|
||||||
override val customSetter: String? = config.customSetter
|
override var customSetter: String? = null
|
||||||
|
|
||||||
override fun toString() = "$name: $typeRef"
|
override fun toString() = "$name: $typeRef"
|
||||||
|
|
||||||
@@ -154,29 +196,32 @@ sealed class Field(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SingleField(
|
class SingleField(
|
||||||
config: FieldConfig,
|
|
||||||
name: String,
|
name: String,
|
||||||
override var typeRef: TypeRefWithNullability,
|
override var typeRef: TypeRefWithNullability,
|
||||||
mutable: Boolean,
|
mutable: Boolean,
|
||||||
isChild: Boolean,
|
isChild: Boolean,
|
||||||
override val baseDefaultValue: String?,
|
) : Field(name, mutable, isChild) {
|
||||||
override val baseGetter: String?,
|
|
||||||
) : Field(config, name, mutable, isChild) {
|
|
||||||
override val transformable: Boolean
|
override val transformable: Boolean
|
||||||
get() = isMutable
|
get() = isMutable
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListField(
|
class ListField(
|
||||||
config: FieldConfig,
|
|
||||||
name: String,
|
name: String,
|
||||||
var elementType: TypeRef,
|
var elementType: TypeRef,
|
||||||
|
private val isNullable: Boolean,
|
||||||
private val listType: ClassRef<PositionTypeParameterRef>,
|
private val listType: ClassRef<PositionTypeParameterRef>,
|
||||||
mutable: Boolean,
|
mutable: Boolean,
|
||||||
isChild: Boolean,
|
isChild: Boolean,
|
||||||
override val transformable: Boolean,
|
override val transformable: Boolean,
|
||||||
override val baseDefaultValue: String?,
|
) : Field(name, mutable, isChild) {
|
||||||
override val baseGetter: String?,
|
|
||||||
) : Field(config, name, mutable, isChild) {
|
|
||||||
override val typeRef: TypeRefWithNullability
|
override val typeRef: TypeRefWithNullability
|
||||||
get() = listType.withArgs(elementType)
|
get() = listType.withArgs(elementType).copy(isNullable)
|
||||||
|
|
||||||
|
enum class Mutability {
|
||||||
|
Immutable,
|
||||||
|
Var,
|
||||||
|
List,
|
||||||
|
Array
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-120
@@ -6,14 +6,8 @@
|
|||||||
package org.jetbrains.kotlin.ir.generator.model
|
package org.jetbrains.kotlin.ir.generator.model
|
||||||
|
|
||||||
import org.jetbrains.kotlin.generators.tree.*
|
import org.jetbrains.kotlin.generators.tree.*
|
||||||
import org.jetbrains.kotlin.ir.generator.config.*
|
|
||||||
import org.jetbrains.kotlin.ir.generator.elementBaseType
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.castAll
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.partitionIsInstance
|
|
||||||
import org.jetbrains.kotlin.generators.tree.ElementRef as GenericElementRef
|
|
||||||
|
|
||||||
private object InferredOverriddenType : TypeRefWithNullability {
|
internal object InferredOverriddenType : TypeRefWithNullability {
|
||||||
|
|
||||||
context(ImportCollector)
|
context(ImportCollector)
|
||||||
override fun renderTo(appendable: Appendable) {
|
override fun renderTo(appendable: Appendable) {
|
||||||
@@ -30,117 +24,7 @@ private object InferredOverriddenType : TypeRefWithNullability {
|
|||||||
|
|
||||||
data class Model(val elements: List<Element>, val rootElement: Element)
|
data class Model(val elements: List<Element>, val rootElement: Element)
|
||||||
|
|
||||||
fun config2model(config: Config): Model {
|
internal fun markLeaves(elements: List<Element>) {
|
||||||
val ec2el = mutableMapOf<ElementConfig, Element>()
|
|
||||||
|
|
||||||
val elements = config.elements.map { ec ->
|
|
||||||
Element(
|
|
||||||
config = ec,
|
|
||||||
name = ec.name,
|
|
||||||
packageName = ec.category.packageName,
|
|
||||||
params = ec.params,
|
|
||||||
fields = ec.fields.mapTo(mutableSetOf(), ::transformFieldConfig),
|
|
||||||
additionalFactoryMethodParameters = ec.additionalIrFactoryMethodParameters.mapTo(mutableListOf(), ::transformFieldConfig)
|
|
||||||
).also {
|
|
||||||
ec2el[ec.element] = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val rootElement = replaceElementRefs(config, ec2el)
|
|
||||||
configureInterfacesAndAbstractClasses(elements)
|
|
||||||
addPureAbstractElement(elements, elementBaseType)
|
|
||||||
markLeaves(elements)
|
|
||||||
processFieldOverrides(elements)
|
|
||||||
addWalkableChildren(elements)
|
|
||||||
|
|
||||||
return Model(elements, rootElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun transformFieldConfig(fc: FieldConfig): Field = when (fc) {
|
|
||||||
is SimpleFieldConfig -> SingleField(
|
|
||||||
fc,
|
|
||||||
fc.name,
|
|
||||||
fc.type?.copy(fc.nullable) ?: InferredOverriddenType,
|
|
||||||
fc.mutable,
|
|
||||||
fc.isChild,
|
|
||||||
fc.baseDefaultValue,
|
|
||||||
fc.baseGetter,
|
|
||||||
)
|
|
||||||
is ListFieldConfig -> {
|
|
||||||
val listType = when (fc.mutability) {
|
|
||||||
ListFieldConfig.Mutability.List -> StandardTypes.mutableList
|
|
||||||
ListFieldConfig.Mutability.Array -> StandardTypes.array
|
|
||||||
else -> StandardTypes.list
|
|
||||||
}
|
|
||||||
ListField(
|
|
||||||
fc,
|
|
||||||
fc.name,
|
|
||||||
fc.elementType ?: InferredOverriddenType,
|
|
||||||
listType.copy(fc.nullable),
|
|
||||||
fc.mutability == ListFieldConfig.Mutability.Var,
|
|
||||||
fc.isChild,
|
|
||||||
fc.mutability != ListFieldConfig.Mutability.Immutable,
|
|
||||||
fc.baseDefaultValue,
|
|
||||||
fc.baseGetter,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(UnsafeCastFunction::class)
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
private fun replaceElementRefs(config: Config, mapping: Map<ElementConfig, Element>): Element {
|
|
||||||
val visited = mutableMapOf<TypeRef, TypeRef>()
|
|
||||||
|
|
||||||
fun transform(type: TypeRef): TypeRef {
|
|
||||||
visited[type]?.let {
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
return when (type) {
|
|
||||||
is ElementConfigOrRef -> {
|
|
||||||
val args = type.args.mapValues { transform(it.value) }
|
|
||||||
val el = mapping.getValue(type.element)
|
|
||||||
ElementRef(el, args, type.nullable)
|
|
||||||
}
|
|
||||||
is ClassRef<*> -> {
|
|
||||||
@Suppress("UNCHECKED_CAST") // this is the upper bound, compiler could know that, right?
|
|
||||||
type as ClassRef<TypeParameterRef>
|
|
||||||
|
|
||||||
val args = type.args.mapValues { transform(it.value) }
|
|
||||||
type.copy(args = args)
|
|
||||||
}
|
|
||||||
else -> type
|
|
||||||
}.also { visited[type] = it }
|
|
||||||
}
|
|
||||||
|
|
||||||
val rootEl = transform(config.rootElement) as GenericElementRef<Element, Field>
|
|
||||||
|
|
||||||
for (ec in config.elements) {
|
|
||||||
val el = mapping[ec.element]!!
|
|
||||||
val (elParents, otherParents) = ec.parents
|
|
||||||
.map { transform(it) }
|
|
||||||
.partitionIsInstance<TypeRef, ElementRef>()
|
|
||||||
el.elementParents = elParents.takeIf { it.isNotEmpty() || el == rootEl.element } ?: listOf(rootEl)
|
|
||||||
el.otherParents = otherParents.castAll<ClassRef<*>>().toMutableList()
|
|
||||||
el.parentInVisitor = (ec.visitorParent?.let(::transform) as GenericElementRef<Element, Field>?)?.element
|
|
||||||
el.transformerReturnType = (ec.transformerReturnType?.let(::transform) as GenericElementRef<Element, Field>?)?.element
|
|
||||||
|
|
||||||
for (field in el.fields) {
|
|
||||||
when (field) {
|
|
||||||
is SingleField -> {
|
|
||||||
field.typeRef = transform(field.typeRef) as TypeRefWithNullability
|
|
||||||
}
|
|
||||||
is ListField -> {
|
|
||||||
field.elementType = transform(field.elementType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rootEl.element
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun markLeaves(elements: List<Element>) {
|
|
||||||
val leaves = elements.toMutableSet()
|
val leaves = elements.toMutableSet()
|
||||||
|
|
||||||
for (el in elements) {
|
for (el in elements) {
|
||||||
@@ -159,7 +43,7 @@ private fun markLeaves(elements: List<Element>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processFieldOverrides(elements: List<Element>) {
|
internal fun processFieldOverrides(elements: List<Element>) {
|
||||||
for (element in iterateElementsParentFirst(elements)) {
|
for (element in iterateElementsParentFirst(elements)) {
|
||||||
for (field in element.fields) {
|
for (field in element.fields) {
|
||||||
fun visitParents(visited: Element) {
|
fun visitParents(visited: Element) {
|
||||||
@@ -193,7 +77,7 @@ private fun processFieldOverrides(elements: List<Element>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addWalkableChildren(elements: List<Element>) {
|
internal fun addWalkableChildren(elements: List<Element>) {
|
||||||
for (element in elements) {
|
for (element in elements) {
|
||||||
val walkableChildren = mutableMapOf<String, Field>()
|
val walkableChildren = mutableMapOf<String, Field>()
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -12,9 +12,9 @@ import org.jetbrains.kotlin.generators.tree.printer.printFunctionDeclaration
|
|||||||
import org.jetbrains.kotlin.generators.tree.printer.printGeneratedType
|
import org.jetbrains.kotlin.generators.tree.printer.printGeneratedType
|
||||||
import org.jetbrains.kotlin.ir.generator.IrTree
|
import org.jetbrains.kotlin.ir.generator.IrTree
|
||||||
import org.jetbrains.kotlin.ir.generator.TREE_GENERATOR_README
|
import org.jetbrains.kotlin.ir.generator.TREE_GENERATOR_README
|
||||||
import org.jetbrains.kotlin.ir.generator.config.UseFieldAsParameterInIrFactoryStrategy
|
|
||||||
import org.jetbrains.kotlin.ir.generator.irFactoryType
|
import org.jetbrains.kotlin.ir.generator.irFactoryType
|
||||||
import org.jetbrains.kotlin.ir.generator.model.Element
|
import org.jetbrains.kotlin.ir.generator.model.Element
|
||||||
|
import org.jetbrains.kotlin.ir.generator.model.Field
|
||||||
import org.jetbrains.kotlin.ir.generator.model.Model
|
import org.jetbrains.kotlin.ir.generator.model.Model
|
||||||
import org.jetbrains.kotlin.ir.generator.stageControllerType
|
import org.jetbrains.kotlin.ir.generator.stageControllerType
|
||||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
||||||
@@ -255,10 +255,10 @@ private class FactoryMethod(val element: Element) {
|
|||||||
|
|
||||||
val name = "create" + element.name.capitalizeAsciiOnly()
|
val name = "create" + element.name.capitalizeAsciiOnly()
|
||||||
|
|
||||||
val parameters = (element.allFieldsRecursively() + element.additionalFactoryMethodParameters)
|
val parameters = (element.allFieldsRecursively() + element.additionalIrFactoryMethodParameters)
|
||||||
.filterNot { it.name in element.fieldsToSkipInIrFactoryMethod }
|
.filterNot { it.name in element.fieldsToSkipInIrFactoryMethod }
|
||||||
.mapNotNull { field ->
|
.mapNotNull { field ->
|
||||||
(field.useInIrFactoryStrategy as? UseFieldAsParameterInIrFactoryStrategy.Yes)?.let {
|
(field.useInIrFactoryStrategy as? Field.UseFieldAsParameterInIrFactoryStrategy.Yes)?.let {
|
||||||
FunctionParameter(field.name, field.typeRef, it.defaultValue)
|
FunctionParameter(field.name, field.typeRef, it.defaultValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user