Skip to content

Commit

Permalink
Added object mappers for multiple input parameters, up to 5
Browse files Browse the repository at this point in the history
  • Loading branch information
stefankoppier committed Jul 4, 2024
1 parent e72deca commit 2ad8072
Show file tree
Hide file tree
Showing 26 changed files with 998 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@ import tech.mappie.util.*
import tech.mappie.validation.MappingValidation
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.ir.IrFileEntry
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrFail
import org.jetbrains.kotlin.ir.types.typeOrFail
import org.jetbrains.kotlin.ir.util.*
import tech.mappie.MappieIrRegistrar
import tech.mappie.resolving.enums.ExplicitEnumMappingTarget
import tech.mappie.resolving.enums.ResolvedEnumMappingTarget
import tech.mappie.resolving.enums.ThrowingEnumMappingTarget
import java.beans.Expression

class MappieIrTransformer(private val symbols: List<MappieDefinition>) : IrElementTransformerVoidWithContext() {

Expand Down Expand Up @@ -60,8 +69,9 @@ class MappieIrTransformer(private val symbols: List<MappieDefinition>) : IrEleme
context.blockBody(scope) {
+irReturn(irCallConstructor(mapping.symbol, emptyList()).apply {
mapping.mappings.map { (target, source) ->
val file = declaration.fileEntry
val index = mapping.symbol.owner.valueParameters.indexOf(target)
putValueArgument(index, source.single().toIr(this@blockBody))
putValueArgument(index, generateValueArgument(file, source.single(), declaration.valueParameters))
}
})
}
Expand Down Expand Up @@ -104,41 +114,53 @@ class MappieIrTransformer(private val symbols: List<MappieDefinition>) : IrEleme
}
}

fun ObjectMappingSource.toIr(builder: IrBuilderWithScope): IrExpression =
when (this) {
is ResolvedSource -> toIr(builder)
is PropertySource -> toIr(builder)
is ExpressionSource -> toIr(builder)
is ValueSource -> value
}

fun ExpressionSource.toIr(builder: IrBuilderWithScope): IrExpression {
return builder.irCall(context.referenceLetFunction()).apply {
extensionReceiver = builder.irGet(type, extensionReceiverSymbol)
putValueArgument(0, expression)
fun IrBuilderWithScope.generateValueArgument(file: IrFileEntry, source: ObjectMappingSource, parameters: List<IrValueParameter>): IrExpression {
return when (source) {
is ResolvedSource -> generateResolvedValueArgument(source)
is PropertySource -> generatePropertyValueArgument(file, source, parameters)
is ExpressionSource -> generateExpressionValueArgument(source, parameters)
is ValueSource -> source.value
}
}

fun ResolvedSource.toIr(builder: IrBuilderWithScope): IrExpression {
val getter = builder.irCall(property.function).apply {
dispatchReceiver = this@toIr.dispatchReceiver
fun IrBuilderWithScope.generateResolvedValueArgument(source: ResolvedSource): IrFunctionAccessExpression {
val getter = irCall(source.property.function).apply {
dispatchReceiver = irGet(source.property.holder)
}
return via?.let {
builder.irCall(via).apply {
dispatchReceiver = viaDispatchReceiver
return source.via?.let {
irCall(source.via).apply {
dispatchReceiver = source.viaDispatchReceiver
putValueArgument(0, getter)
}
} ?: getter
}

fun PropertySource.toIr(builder: IrBuilderWithScope): IrExpression {
val getter = builder.irCall(property).apply {
dispatchReceiver = this@toIr.dispatchReceiver
fun IrPropertyReference.classType(): IrType {
return when (type.classOrFail) {
context.irBuiltIns.kProperty0Class -> dispatchReceiver!!.type
context.irBuiltIns.kProperty1Class -> (this.type as IrSimpleType).arguments.first().typeOrFail
else -> error("Unknown KProperty ${dumpKotlinLike()}")
}
}

fun IrBuilderWithScope.generatePropertyValueArgument(file: IrFileEntry, source: PropertySource, parameters: List<IrValueParameter>): IrFunctionAccessExpression {
val getter = irCall(source.getter).apply {
dispatchReceiver = source.property.dispatchReceiver
?: irGet(parameters.singleOrNull { it.type == source.property.classType() } ?: run {
logError("Could not determine value parameters for property reference. Please use a property reference of an object instead of the class", location(file, source.property))
error("Mappie resolving failed")
})
}
return transformation?.let {
builder.irCall(context.referenceLetFunction()).apply {
return source.transformation?.let {
irCall(MappieIrRegistrar.context.referenceLetFunction()).apply {
extensionReceiver = getter
putValueArgument(0, transformation)
putValueArgument(0, source.transformation)
} } ?: getter
}

fun IrBuilderWithScope.generateExpressionValueArgument(source: ExpressionSource, parameters: List<IrValueParameter>): IrFunctionAccessExpression {
return irCall(MappieIrRegistrar.context.referenceLetFunction()).apply {
extensionReceiver = irGet(parameters.single())
putValueArgument(0, source.expression)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ sealed interface Mapping

data class ConstructorCallMapping(
val targetType: IrType,
val sourceType: IrType,
val sourceTypes: List<IrType>,
val symbol: IrConstructorSymbol,
val mappings: Map<IrValueParameter, List<ObjectMappingSource>>,
val unknowns: Map<Name, List<ObjectMappingSource>>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import org.jetbrains.kotlin.ir.util.isClass

class ClassResolver(private val declaration: IrFunction, private val symbols: List<MappieDefinition>) {

private val sourceParameter = declaration.valueParameters.first()
private val sourceParameters = declaration.valueParameters

init {
require(declaration.returnType.getClass()!!.isClass)
}

fun resolve(): List<ConstructorCallMapping> {
val constructor = ObjectMappingsConstructor.of(declaration.returnType, sourceParameter)
.apply { getters.addAll(sourceParameter.accept(GettersCollector(), Unit)) }
val constructor = ObjectMappingsConstructor.of(declaration.returnType, sourceParameters)
.apply { getters.addAll(sourceParameters.flatMap { it.accept(GettersCollector(), it) }) }

declaration.body?.accept(ObjectMappingBodyCollector(declaration.fileEntry, sourceParameter.symbol), constructor)
declaration.body?.accept(ObjectMappingBodyCollector(declaration.fileEntry, sourceParameters), constructor)

return declaration.accept(ConstructorsCollector(), Unit).map {
ObjectMappingsConstructor.of(constructor).apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,23 @@ 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<MappieGetter>, Unit>() {
class GettersCollector : BaseVisitor<List<MappieGetter>, IrValueParameter>() {

override fun visitValueParameter(declaration: IrValueParameter, data: Unit): List<MappieGetter> {
return declaration.type.getClass()!!.properties.flatMap { it.accept(data) }.toList() +
declaration.type.getClass()!!.declarations.filterIsInstance<IrSimpleFunction>().flatMap { it.accept(data) }
override fun visitValueParameter(declaration: IrValueParameter, data: IrValueParameter): List<MappieGetter> {
val clazz = declaration.type.getClass()!!
val properties = clazz.properties.flatMap { it.accept(data) }.toList()
val methods = clazz.declarations.filterIsInstance<IrSimpleFunction>().flatMap { it.accept(data) }
return properties + methods
}

override fun visitFunction(declaration: IrFunction, data: Unit): List<MappieGetter> {
override fun visitFunction(declaration: IrFunction, data: IrValueParameter): List<MappieGetter> {
if (declaration.name.asString().startsWith("get") && declaration.symbol.owner.valueParameters.isEmpty()) {
return listOf(MappieFunctionGetter(declaration))
return listOf(MappieFunctionGetter(declaration, data))
}
return emptyList()
}

override fun visitProperty(declaration: IrProperty, data: Unit): List<MappieGetter> {
return declaration.getter?.let { listOf(MappiePropertyGetter(it)) } ?: emptyList()
override fun visitProperty(declaration: IrProperty, data: IrValueParameter): List<MappieGetter> {
return declaration.getter?.let { listOf(MappiePropertyGetter(it, data)) } ?: emptyList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tech.mappie.resolving.classes

import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.name.Name
import tech.mappie.util.dumpKotlinLike
Expand All @@ -11,18 +12,19 @@ sealed interface MappieGetter {
val name: Name
val type: IrType
val function: IrFunction
val holder: IrValueParameter

fun dumpKotlinLike(): String
}

data class MappiePropertyGetter(override val function: IrSimpleFunction) : MappieGetter {
data class MappiePropertyGetter(override val function: IrSimpleFunction, override val holder: IrValueParameter) : MappieGetter {
override val name = function.name
override val type = function.returnType

override fun dumpKotlinLike(): String = function.symbol.dumpKotlinLike()
}

data class MappieFunctionGetter(override val function: IrFunction) : MappieGetter {
data class MappieFunctionGetter(override val function: IrFunction, override val holder: IrValueParameter) : MappieGetter {
override val name = getterName(function.name.asString().removePrefix("get").replaceFirstChar { it.lowercaseChar() })
override val type = function.returnType

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.declarations.createExpressionBody
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
Expand All @@ -25,7 +26,7 @@ import org.jetbrains.kotlin.name.Name

class ObjectMappingBodyCollector(
file: IrFileEntry,
private val dispatchReceiverSymbol: IrValueSymbol,
private val dispatchReceiverSymbols: List<IrValueParameter>,
) : BaseVisitor<ObjectMappingsConstructor, ObjectMappingsConstructor>(file) {

override fun visitBlockBody(body: IrBlockBody, data: ObjectMappingsConstructor): ObjectMappingsConstructor {
Expand All @@ -49,21 +50,20 @@ class ObjectMappingBodyCollector(

override fun visitFunctionExpression(expression: IrFunctionExpression, data: ObjectMappingsConstructor): ObjectMappingsConstructor {
return expression.function.body?.statements?.fold(data) { acc, current ->
acc.let { current.accept(ObjectBodyStatementCollector(file!!, dispatchReceiverSymbol), Unit)?.let { acc.explicit(it) } ?: it }
acc.let { current.accept(ObjectBodyStatementCollector(file!!), Unit)?.let { acc.explicit(it) } ?: it }
} ?: data
}
}

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

override fun visitCall(expression: IrCall, data: Unit): Pair<Name, ObjectMappingSource>? {
return when (expression.symbol.owner.name) {
IDENTIFIER_FROM_PROPERTY, IDENTIFIER_FROM_CONSTANT -> {
val target = expression.extensionReceiver!!.accept(TargetValueCollector(file!!), data)
val source = expression.valueArguments.first()!!.accept(SourceValueCollector(dispatchReceiverSymbol), Unit)
val source = expression.valueArguments.first()!!.accept(SourceValueCollector(), Unit)

target to source
}
Expand All @@ -78,7 +78,6 @@ private class ObjectBodyStatementCollector(
val source = expression.valueArguments.first() as IrFunctionExpression

target to ExpressionSource(
dispatchReceiverSymbol,
source,
expression,
)
Expand Down Expand Up @@ -195,15 +194,11 @@ private class MapperReferenceCollector : BaseVisitor<IrFunctionExpression, Unit>
)
}

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

override fun visitPropertyReference(expression: IrPropertyReference, data: Unit): ObjectMappingSource {
val dispatchReceiver = expression.dispatchReceiver ?: irGet(expression.type, dispatchReceiverSymbol)
return PropertySource(
property = expression.getter!!,
dispatchReceiver = dispatchReceiver,
property = expression,
transformation = null,
origin = expression,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package tech.mappie.resolving.classes

import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
Expand All @@ -16,7 +19,6 @@ sealed interface ObjectMappingSource {

data class ResolvedSource(
val property: MappieGetter,
val dispatchReceiver: IrExpression,
val via: IrSimpleFunction? = null,
val viaDispatchReceiver: IrExpression? = null,
) : ObjectMappingSource {
Expand All @@ -25,15 +27,16 @@ data class ResolvedSource(
}

data class PropertySource(
val property: IrSimpleFunctionSymbol,
val dispatchReceiver: IrExpression,
val property: IrPropertyReference,
val transformation: IrFunctionExpression? = null,
val origin: IrExpression,
) : ObjectMappingSource {

val getter = property.getter!!

override val type: IrType
get() = if (transformation == null) {
property.owner.returnType
getter.owner.returnType
} else if (transformation.type.isFunction()) {
(transformation.type as IrSimpleType).arguments[1].typeOrFail
} else {
Expand All @@ -42,7 +45,6 @@ data class PropertySource(
}

data class ExpressionSource(
val extensionReceiverSymbol: IrValueSymbol,
val expression: IrFunctionExpression,
val origin: IrExpression,
) : ObjectMappingSource {
Expand All @@ -54,4 +56,4 @@ data class ValueSource(
val origin: IrExpression?,
) : ObjectMappingSource {
override val type = value.type
}
}
Loading

0 comments on commit 2ad8072

Please sign in to comment.