Skip to content

Commit

Permalink
First setup for multiple constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
stefankoppier committed Jun 15, 2024
1 parent 12a5116 commit c3a4f24
Show file tree
Hide file tree
Showing 16 changed files with 163 additions and 88 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package io.github.mappie

import io.github.mappie.MappieIrRegistrar.Companion.context
import io.github.mappie.util.error
import io.github.mappie.util.location
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrFileEntry
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor

interface BaseVisitor<R, D> : IrElementVisitor<R, D> {
abstract class BaseVisitor<R, D>(protected var file: IrFileEntry? = null) : IrElementVisitor<R, D> {

override fun visitElement(element: IrElement, data: D): R {
context.messageCollector.error("Unexpected element", file?.let { location(it, element) })
error("${javaClass.simpleName} Not implemented for ${element::class} :: ${element.dump()}")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,18 @@ class IrTransformer : IrElementTransformerVoidWithContext() {
"Expected return type of map to be non-null."
}

val mapping = declaration.accept(MappingResolver(), Unit)
val (valids, invalids) = declaration.accept(MappingResolver(), Unit)
.map { it to MappingValidation.of(declaration.fileEntry, it) }
.partition { it.second.isValid() }

val validation = MappingValidation.of(declaration.fileEntry, mapping)
if (validation.isValid()) {
if (valids.isNotEmpty()) {
declaration.body = with(createScope(declaration)) {
when (mapping) {
when (val mapping = valids.single().first) {
is ConstructorCallMapping -> {
context.blockBody(this.scope) {
val primaryConstructor = requireNotNull(targetClass.primaryConstructor) {
"The target type must have a primary constructor."
}
+irReturn(irCallConstructor(primaryConstructor.symbol, emptyList()).apply {
+irReturn(irCallConstructor(mapping.symbol, emptyList()).apply {
mapping.mappings.map { (target, source) ->
val index = primaryConstructor.valueParameters.indexOf(target)
val index = mapping.symbol.owner.valueParameters.indexOf(target)
putValueArgument(index, source.single().toIr(this@blockBody))
}
})
Expand All @@ -73,7 +71,7 @@ class IrTransformer : IrElementTransformerVoidWithContext() {
}
}
} else {
validation.problems().forEach { problem ->
invalids.first().second.problems().forEach { problem ->
context.messageCollector.error(problem.description, problem.location ?: location(declaration))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrReturn

class ShouldTransformCollector : BaseVisitor<Boolean, Unit> {
class ShouldTransformCollector : BaseVisitor<Boolean, Unit>() {
override fun visitClass(declaration: IrClass, data: Unit): Boolean {
return declaration.isSubclassOfFqName("io.github.mappie.api.Mapper")
&& declaration.declarations.filterIsInstance<IrSimpleFunction>().any { it.accept(data) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isPrimitiveType
Expand All @@ -20,6 +22,7 @@ sealed interface Mapping
data class ConstructorCallMapping(
val targetType: IrType,
val sourceType: IrType,
val symbol: IrConstructorSymbol,
val mappings: Map<IrValueParameter, List<MappingSource>>,
) : Mapping

Expand All @@ -34,16 +37,15 @@ data class SingleValueMapping(
val value: IrExpression,
) : Mapping

class MappingResolver
: BaseVisitor<Mapping, Unit> {
class MappingResolver : BaseVisitor<List<Mapping>, Unit>() {

override fun visitFunction(declaration: IrFunction, data: Unit): Mapping {
override fun visitFunction(declaration: IrFunction, data: Unit): List<Mapping> {
val type = declaration.returnType
val clazz = type.getClass()!!
return when {
clazz.isEnumClass -> declaration.accept(EnumMappingResolver(), Unit)
clazz.isEnumClass -> listOf(declaration.accept(EnumMappingResolver(), Unit))
clazz.isData -> declaration.accept(ClassMappingResolver(), Unit)
type.isPrimitiveType() || type.isString() -> declaration.accept(PrimitiveMappingResolver(), Unit)
type.isPrimitiveType() || type.isString() -> listOf(declaration.accept(PrimitiveMappingResolver(), Unit))
else -> error("Only mapping of data- and enum classes are supported yet.")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,54 @@
package io.github.mappie.resolving.classes

import io.github.mappie.BaseVisitor
import io.github.mappie.MappieIrRegistrar.Companion.context
import io.github.mappie.resolving.*
import io.github.mappie.util.irGet
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.getPropertyGetter
import org.jetbrains.kotlin.ir.util.properties
import org.jetbrains.kotlin.ir.util.callableId
import org.jetbrains.kotlin.ir.util.fileEntry
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name

class ClassMappingResolver : BaseVisitor<Mapping, Unit> {
class ClassMappingResolver : BaseVisitor<List<Mapping>, Unit>() {

override fun visitFunction(declaration: IrFunction, data: Unit): Mapping {
override fun visitFunction(declaration: IrFunction, data: Unit): List<Mapping> {
check(declaration.returnType.getClass()!!.isData)

val sourceParameter = requireNotNull(declaration.valueParameters.firstOrNull())
val targets = declaration.accept(ValueParametersCollector(), Unit)
val dispatchReceiverSymbol = declaration.valueParameters.first().symbol
val concreteSources = declaration.body?.accept(
ObjectSourcesCollector(dispatchReceiverSymbol),
Unit
) ?: emptyList()
val possibilities = declaration.accept(ConstructorsCollector(), Unit)
val sourceParameter = declaration.valueParameters.first()
val getters = sourceParameter.accept(GettersCollector(), Unit)
val dispatchReceiverSymbol = sourceParameter.symbol
val concreteSources = declaration.body?.accept(ObjectSourcesCollector(declaration.fileEntry, dispatchReceiverSymbol), Unit) ?: emptyList()

val mappings = targets.associateWith { target ->
val concreteSource = concreteSources.firstOrNull { it.first == target.name }
return possibilities.map { constructor ->
val targets = constructor.valueParameters
val mappings = targets.associateWith { target ->
val concreteSource = concreteSources.firstOrNull { it.first == target.name }

if (concreteSource != null) {
listOf(concreteSource.second)
} else {
val sourceClass = requireNotNull(sourceParameter.type.getClass()) {
"Expected type of source argument to be non-null."
}
val source = sourceClass.properties.firstOrNull { source -> source.name == target.name }
if (source != null) {
val getter = sourceClass.getPropertyGetter(source.name.asString())
if (concreteSource != null) {
listOf(concreteSource.second)
} else {
val getter = getters.firstOrNull { getter ->
getter.name == Name.special("<get-${target.name.asString()}>")
}
if (getter != null) {
listOf(PropertySource(getter, target.type, sourceParameter.symbol))
listOf(PropertySource(getter.symbol, target.type, sourceParameter.symbol))
} else {
emptyList()
}
} else {
emptyList()
}
}
}

return ConstructorCallMapping(
targetType = declaration.returnType,
sourceType = sourceParameter.type,
mappings = mappings
)
ConstructorCallMapping(
targetType = declaration.returnType,
sourceType = sourceParameter.type,
symbol = constructor.symbol,
mappings = mappings
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.github.mappie.resolving.classes

import io.github.mappie.BaseVisitor
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.constructors

class ConstructorsCollector : BaseVisitor<List<IrConstructor>, Unit>() {

override fun visitFunction(declaration: IrFunction, data: Unit): List<IrConstructor> {
return declaration.returnType.getClass()!!.constructors.toList()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.mappie.resolving.classes

import io.github.mappie.BaseVisitor
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.properties

class GettersCollector : BaseVisitor<List<IrSimpleFunction>, Unit>() {

override fun visitValueParameter(declaration: IrValueParameter, data: Unit): List<IrSimpleFunction> {
return declaration.type.getClass()!!.properties.flatMap { it.accept(Unit) }.toList()
}

override fun visitProperty(declaration: IrProperty, data: Unit): List<IrSimpleFunction> {
return if (declaration.getter != null) declaration.getter?.let { listOf(it) } ?: emptyList() else emptyList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package io.github.mappie.resolving.classes
import io.github.mappie.BaseVisitor
import io.github.mappie.MappieIrRegistrar.Companion.context
import io.github.mappie.resolving.*
import io.github.mappie.util.error
import io.github.mappie.util.irGet
import io.github.mappie.util.location
import org.jetbrains.kotlin.ir.IrFileEntry
import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
Expand All @@ -17,13 +20,15 @@ import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import kotlin.math.exp

sealed interface MappingSource {
fun resolveType(): IrType
}

data class PropertySource(
val property: IrSimpleFunctionSymbol,
// val property: IrField,
val type: IrType,
val dispatchReceiverSymbol: IrValueSymbol,
val transformation: IrFunctionExpression? = null,
Expand All @@ -48,8 +53,9 @@ data class ConstantSource<T>(
}

class ObjectSourcesCollector(
private val dispatchReceiverSymbol: IrValueSymbol
) : BaseVisitor<List<Pair<Name, MappingSource>>, Unit> {
file: IrFileEntry,
private val dispatchReceiverSymbol: IrValueSymbol,
) : BaseVisitor<List<Pair<Name, MappingSource>>, Unit>(file) {

override fun visitBlockBody(body: IrBlockBody, data: Unit): List<Pair<Name, MappingSource>> {
return body.statements.single().accept(data)
Expand All @@ -71,15 +77,16 @@ class ObjectSourcesCollector(
}

override fun visitFunctionExpression(expression: IrFunctionExpression, data: Unit): List<Pair<Name, MappingSource>> {
return expression.function.body!!.statements.map { it.accept(ObjectSourceCollector(dispatchReceiverSymbol), Unit) }
return expression.function.body!!.statements.map { it.accept(ObjectSourceCollector(file, dispatchReceiverSymbol), Unit) }.filterNotNull()
}
}

private class ObjectSourceCollector(
file: IrFileEntry?,
private val dispatchReceiverSymbol: IrValueSymbol,
) : BaseVisitor<Pair<Name, MappingSource>, Unit> {
) : BaseVisitor<Pair<Name, MappingSource>?, Unit>(file) {

override fun visitCall(expression: IrCall, data: Unit): Pair<Name, MappingSource> {
override fun visitCall(expression: IrCall, data: Unit): Pair<Name, MappingSource>? {
return when (expression.symbol.owner.name) {
IDENTIFIER_MAPPED_FROM_PROPERTY, IDENTIFIER_MAPPED_FROM_CONSTANT -> {
val target = expression.extensionReceiver!!.accept(TargetValueCollector(), Unit)
Expand All @@ -91,12 +98,12 @@ private class ObjectSourceCollector(
TODO("Implement")
}
IDENTIFIER_TRANFORM -> {
val mapping = expression.dispatchReceiver!!.accept(data)
val mapping = expression.dispatchReceiver!!.accept(data)!!
val transformation = expression.valueArguments.first()!! as IrFunctionExpression
mapping.first to (mapping.second as PropertySource).copy(transformation = transformation)
}
IDENTIFIER_VIA -> {
val mapping = expression.dispatchReceiver!!.accept(data)
val mapping = expression.dispatchReceiver!!.accept(data)!!
val transformation = expression.valueArguments.first()!!.accept(MapperReferenceCollector(), Unit)
val type = transformation.type
mapping.first to (mapping.second as PropertySource).copy(
Expand All @@ -105,20 +112,29 @@ private class ObjectSourceCollector(
)
}
else -> {
TODO("$javaClass :: visitCall Not implemented for ${expression::class} :: ${expression.dump()}")
context.messageCollector.error("Unexpected method call", file?.let { location(it, expression) })
return null
}
}
}

override fun visitTypeOperator(expression: IrTypeOperatorCall, data: Unit): Pair<Name, MappingSource> {
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: Unit): Pair<Name, MappingSource>? {
return when (expression.operator.name) {
"IMPLICIT_COERCION_TO_UNIT" -> expression.argument.accept(data)
else -> error(expression.operator.name)
}
}

override fun visitReturn(expression: IrReturn, data: Unit): Pair<Name, MappingSource>? {
return expression.value.accept(data)
}

override fun visitGetObjectValue(expression: IrGetObjectValue, data: Unit): Pair<Name, MappingSource>? {
return null
}
}

private class MapperReferenceCollector : BaseVisitor<IrFunctionExpression, Unit> {
private class MapperReferenceCollector : BaseVisitor<IrFunctionExpression, Unit>() {

override fun visitGetObjectValue(expression: IrGetObjectValue, data: Unit): IrFunctionExpression {
return context.referenceClass(expression.symbol.owner.classId!!)!!
Expand Down Expand Up @@ -187,7 +203,7 @@ private class MapperReferenceCollector : BaseVisitor<IrFunctionExpression, Unit>

private class SourceValueCollector(
private val dispatchReceiverSymbol: IrValueSymbol,
) : BaseVisitor<MappingSource, Unit> {
) : BaseVisitor<MappingSource, Unit>() {

override fun visitPropertyReference(expression: IrPropertyReference, data: Unit): MappingSource {
return PropertySource(
Expand All @@ -204,7 +220,7 @@ private class SourceValueCollector(
}
}

private class TargetValueCollector : BaseVisitor<Name, Unit> {
private class TargetValueCollector : BaseVisitor<Name, Unit>() {

override fun visitPropertyReference(expression: IrPropertyReference, data: Unit): Name {
return expression.symbol.owner.name
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import io.github.mappie.BaseVisitor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry

class EnumEntriesCollector : BaseVisitor<List<IrEnumEntry>, Unit> {
class EnumEntriesCollector : BaseVisitor<List<IrEnumEntry>, Unit>() {

override fun visitClass(declaration: IrClass, data: Unit): List<IrEnumEntry> {
return declaration.declarations
Expand Down
Loading

0 comments on commit c3a4f24

Please sign in to comment.