[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
|
||||
|
||||
import org.jetbrains.kotlin.generators.tree.addPureAbstractElement
|
||||
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil
|
||||
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil.collectPreviouslyGeneratedFiles
|
||||
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 java.io.File
|
||||
|
||||
@@ -20,8 +21,13 @@ fun main(args: Array<String>) {
|
||||
val generationPath = args.firstOrNull()?.let { File(it) }
|
||||
?: File("compiler/ir/ir.tree/gen").canonicalFile
|
||||
|
||||
val config = IrTree.build()
|
||||
val model = config2model(config)
|
||||
val model = IrTree.build()
|
||||
|
||||
configureInterfacesAndAbstractClasses(model.elements)
|
||||
addPureAbstractElement(model.elements, elementBaseType)
|
||||
markLeaves(model.elements)
|
||||
processFieldOverrides(model.elements)
|
||||
addWalkableChildren(model.elements)
|
||||
|
||||
val previouslyGeneratedFiles = collectPreviouslyGeneratedFiles(generationPath)
|
||||
val generatedFiles = sequence {
|
||||
|
||||
+60
-28
@@ -5,30 +5,47 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.generator.config
|
||||
|
||||
import org.jetbrains.kotlin.generators.tree.TypeRef
|
||||
import org.jetbrains.kotlin.generators.tree.TypeRefWithNullability
|
||||
import org.jetbrains.kotlin.generators.tree.TypeVariable
|
||||
import org.jetbrains.kotlin.generators.tree.type
|
||||
import org.jetbrains.kotlin.generators.tree.*
|
||||
import org.jetbrains.kotlin.ir.generator.model.*
|
||||
import org.jetbrains.kotlin.ir.generator.model.ElementRef
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import kotlin.properties.PropertyDelegateProvider
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
import org.jetbrains.kotlin.generators.tree.ElementOrRef as GenericElementOrRef
|
||||
|
||||
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 {
|
||||
val del = ElementConfigDel(category, name)
|
||||
protected fun Field.skipInIrFactory() {
|
||||
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 {
|
||||
del.element!!.apply { initializer() }
|
||||
del.element!!.apply {
|
||||
initializer()
|
||||
if (elementParents.isEmpty() && this != rootElement) {
|
||||
elementParents.add(ElementRef(rootElement))
|
||||
}
|
||||
}
|
||||
}
|
||||
return del
|
||||
}
|
||||
|
||||
protected fun ElementConfig.parent(type: TypeRef) {
|
||||
parents.add(type)
|
||||
protected fun Element.parent(type: ClassRef<*>) {
|
||||
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 {
|
||||
@@ -41,33 +58,48 @@ abstract class AbstractTreeBuilder {
|
||||
nullable: Boolean = false,
|
||||
mutable: Boolean = true,
|
||||
isChild: Boolean = false,
|
||||
initializer: SimpleFieldConfig.() -> Unit = {}
|
||||
): SimpleFieldConfig {
|
||||
initializer: SingleField.() -> Unit = {}
|
||||
): SingleField {
|
||||
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(
|
||||
name: String,
|
||||
elementType: TypeRef?,
|
||||
nullable: Boolean = false,
|
||||
mutability: ListFieldConfig.Mutability = ListFieldConfig.Mutability.Immutable,
|
||||
mutability: ListField.Mutability = ListField.Mutability.Immutable,
|
||||
isChild: Boolean = false,
|
||||
initializer: ListFieldConfig.() -> Unit = {}
|
||||
): ListFieldConfig {
|
||||
initializer: ListField.() -> Unit = {}
|
||||
): ListField {
|
||||
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) {
|
||||
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() }
|
||||
return Config(elements, rootElement)
|
||||
return Model(elements, rootElement)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -77,20 +109,20 @@ abstract class AbstractTreeBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
class ElementConfigDel(
|
||||
private val category: ElementConfig.Category,
|
||||
class ElementDelegate(
|
||||
private val category: Element.Category,
|
||||
private val name: String?
|
||||
) : ReadOnlyProperty<AbstractTreeBuilder, ElementConfig>, PropertyDelegateProvider<AbstractTreeBuilder, ElementConfigDel> {
|
||||
var element: ElementConfig? = null
|
||||
) : ReadOnlyProperty<AbstractTreeBuilder, Element>, PropertyDelegateProvider<AbstractTreeBuilder, ElementDelegate> {
|
||||
var element: Element? = null
|
||||
private set
|
||||
|
||||
override fun getValue(thisRef: AbstractTreeBuilder, property: KProperty<*>): ElementConfig {
|
||||
override fun getValue(thisRef: AbstractTreeBuilder, property: KProperty<*>): 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
|
||||
element = ElementConfig(path, name ?: property.name, category)
|
||||
element = Element(name ?: property.name, path, category)
|
||||
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
|
||||
|
||||
import org.jetbrains.kotlin.generators.tree.*
|
||||
import org.jetbrains.kotlin.ir.generator.config.ElementConfig
|
||||
import org.jetbrains.kotlin.ir.generator.config.FieldConfig
|
||||
import org.jetbrains.kotlin.ir.generator.BASE_PACKAGE
|
||||
import org.jetbrains.kotlin.utils.SmartPrinter
|
||||
import org.jetbrains.kotlin.utils.topologicalSort
|
||||
import org.jetbrains.kotlin.generators.tree.ElementOrRef as GenericElementOrRef
|
||||
import org.jetbrains.kotlin.generators.tree.ElementRef as GenericElementRef
|
||||
|
||||
class Element(
|
||||
config: ElementConfig,
|
||||
override val name: String,
|
||||
override val packageName: String,
|
||||
override val params: List<TypeVariable>,
|
||||
override val fields: MutableSet<Field>,
|
||||
val additionalFactoryMethodParameters: MutableList<Field>,
|
||||
override val propertyName: String,
|
||||
category: Category,
|
||||
) : 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 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>
|
||||
get() = fields.toList()
|
||||
|
||||
@@ -38,26 +55,46 @@ class Element(
|
||||
override var parentInVisitor: Element? = null
|
||||
var transformerReturnType: Element? = null
|
||||
|
||||
override var kind: ImplementationKind? = when (config.typeKind) {
|
||||
TypeKind.Class -> ImplementationKind.AbstractClass
|
||||
TypeKind.Interface -> ImplementationKind.Interface
|
||||
null -> null
|
||||
}
|
||||
var typeKind: TypeKind? = null
|
||||
set(value) {
|
||||
kind = when (value) {
|
||||
TypeKind.Class -> ImplementationKind.AbstractClass
|
||||
TypeKind.Interface -> ImplementationKind.Interface
|
||||
null -> null
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
override var kind: ImplementationKind? = null
|
||||
|
||||
override val typeName
|
||||
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 val transformableChildren get() = walkableChildren.filter { it.transformable }
|
||||
|
||||
override val visitFunctionName = "visit" + (config.visitorName ?: name).replaceFirstChar(Char::uppercaseChar)
|
||||
override val visitorParameterName = config.visitorParam ?: config.category.defaultVisitorParam
|
||||
var visitorName: String? = null
|
||||
|
||||
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
|
||||
get() = ownsChildren && (isRootElement || walkableChildren.isNotEmpty())
|
||||
@@ -65,17 +102,14 @@ class Element(
|
||||
override val hasTransformChildrenMethod: Boolean
|
||||
get() = ownsChildren && (isRootElement || transformableChildren.isNotEmpty())
|
||||
|
||||
val transformByChildren = config.transformByChildren
|
||||
val ownsChildren = config.ownsChildren
|
||||
val generateIrFactoryMethod = config.generateIrFactoryMethod
|
||||
val fieldsToSkipInIrFactoryMethod = config.fieldsToSkipInIrFactoryMethod
|
||||
var transformByChildren = false
|
||||
var ownsChildren = true // If false, acceptChildren/transformChildren will NOT be generated.
|
||||
|
||||
val generationCallback = config.generationCallback
|
||||
override val propertyName = config.propertyName
|
||||
var generationCallback: (context(ImportCollector) SmartPrinter.() -> Unit)? = null
|
||||
|
||||
override val kDoc = config.kDoc
|
||||
override var kDoc: String? = null
|
||||
|
||||
val usedTypes: List<Importable> = config.usedTypes
|
||||
val usedTypes = mutableListOf<Importable>()
|
||||
|
||||
override fun toString() = name
|
||||
|
||||
@@ -105,38 +139,46 @@ class Element(
|
||||
.distinctBy { it.name }
|
||||
.asReversed()
|
||||
}
|
||||
|
||||
operator fun TypeVariable.unaryPlus() = apply {
|
||||
params.add(this)
|
||||
}
|
||||
|
||||
operator fun Field.unaryPlus() = apply {
|
||||
fields.add(this)
|
||||
}
|
||||
}
|
||||
|
||||
typealias ElementRef = GenericElementRef<Element, Field>
|
||||
typealias ElementOrRef = GenericElementOrRef<Element, Field>
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
sealed class Field(
|
||||
config: FieldConfig,
|
||||
override val name: String,
|
||||
override var isMutable: Boolean,
|
||||
val isChild: Boolean,
|
||||
) : AbstractField() {
|
||||
abstract val baseDefaultValue: String?
|
||||
abstract val baseGetter: String?
|
||||
var baseDefaultValue: String? = null
|
||||
var baseGetter: String? = null
|
||||
|
||||
abstract val transformable: Boolean
|
||||
|
||||
val useInIrFactoryStrategy = config.useFieldInIrFactoryStrategy
|
||||
sealed class UseFieldAsParameterInIrFactoryStrategy {
|
||||
|
||||
init {
|
||||
kDoc = config.kDoc
|
||||
optInAnnotation = config.optInAnnotation
|
||||
deprecation = config.deprecation
|
||||
visibility = config.visibility
|
||||
data object No : UseFieldAsParameterInIrFactoryStrategy()
|
||||
|
||||
data class Yes(val defaultValue: String?) : UseFieldAsParameterInIrFactoryStrategy()
|
||||
}
|
||||
|
||||
var useInIrFactoryStrategy =
|
||||
if (isChild) UseFieldAsParameterInIrFactoryStrategy.No else UseFieldAsParameterInIrFactoryStrategy.Yes(null)
|
||||
|
||||
override val withGetter: Boolean
|
||||
get() = baseGetter != null
|
||||
|
||||
override val defaultValueInImplementation: String?
|
||||
get() = baseGetter ?: baseDefaultValue
|
||||
|
||||
override val customSetter: String? = config.customSetter
|
||||
override var customSetter: String? = null
|
||||
|
||||
override fun toString() = "$name: $typeRef"
|
||||
|
||||
@@ -154,29 +196,32 @@ sealed class Field(
|
||||
}
|
||||
|
||||
class SingleField(
|
||||
config: FieldConfig,
|
||||
name: String,
|
||||
override var typeRef: TypeRefWithNullability,
|
||||
mutable: Boolean,
|
||||
isChild: Boolean,
|
||||
override val baseDefaultValue: String?,
|
||||
override val baseGetter: String?,
|
||||
) : Field(config, name, mutable, isChild) {
|
||||
) : Field(name, mutable, isChild) {
|
||||
override val transformable: Boolean
|
||||
get() = isMutable
|
||||
}
|
||||
|
||||
class ListField(
|
||||
config: FieldConfig,
|
||||
name: String,
|
||||
var elementType: TypeRef,
|
||||
private val isNullable: Boolean,
|
||||
private val listType: ClassRef<PositionTypeParameterRef>,
|
||||
mutable: Boolean,
|
||||
isChild: Boolean,
|
||||
override val transformable: Boolean,
|
||||
override val baseDefaultValue: String?,
|
||||
override val baseGetter: String?,
|
||||
) : Field(config, name, mutable, isChild) {
|
||||
) : Field(name, mutable, isChild) {
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
override fun renderTo(appendable: Appendable) {
|
||||
@@ -30,117 +24,7 @@ private object InferredOverriddenType : TypeRefWithNullability {
|
||||
|
||||
data class Model(val elements: List<Element>, val rootElement: Element)
|
||||
|
||||
fun config2model(config: Config): Model {
|
||||
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>) {
|
||||
internal fun markLeaves(elements: List<Element>) {
|
||||
val leaves = elements.toMutableSet()
|
||||
|
||||
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 (field in element.fields) {
|
||||
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) {
|
||||
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.ir.generator.IrTree
|
||||
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.model.Element
|
||||
import org.jetbrains.kotlin.ir.generator.model.Field
|
||||
import org.jetbrains.kotlin.ir.generator.model.Model
|
||||
import org.jetbrains.kotlin.ir.generator.stageControllerType
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
||||
@@ -255,10 +255,10 @@ private class FactoryMethod(val element: Element) {
|
||||
|
||||
val name = "create" + element.name.capitalizeAsciiOnly()
|
||||
|
||||
val parameters = (element.allFieldsRecursively() + element.additionalFactoryMethodParameters)
|
||||
val parameters = (element.allFieldsRecursively() + element.additionalIrFactoryMethodParameters)
|
||||
.filterNot { it.name in element.fieldsToSkipInIrFactoryMethod }
|
||||
.mapNotNull { field ->
|
||||
(field.useInIrFactoryStrategy as? UseFieldAsParameterInIrFactoryStrategy.Yes)?.let {
|
||||
(field.useInIrFactoryStrategy as? Field.UseFieldAsParameterInIrFactoryStrategy.Yes)?.let {
|
||||
FunctionParameter(field.name, field.typeRef, it.defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user