Skip to content

Commit

Permalink
Fix Declarative DSL runtime setting properties in Kotlin types (#28569)
Browse files Browse the repository at this point in the history
  • Loading branch information
h0tk3y committed Mar 26, 2024
2 parents e46ec93 + 28e7367 commit 0f3cf9d
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 142 deletions.
Expand Up @@ -168,8 +168,8 @@ class DeclarativeReflectionToObjectConverter(
?: error("tried to access a property ${dataProperty.name} on a null receiver")
val receiverKClass = receiverInstance::class
return when (val property = propertyResolver.resolvePropertyRead(receiverKClass, dataProperty.name)) {
is RuntimePropertyResolver.Resolution.Resolved -> property.property.getValue(receiverInstance)
else -> error("cannot get property ${dataProperty.name} from the receiver class $receiverKClass")
is RuntimePropertyResolver.ReadResolution.ResolvedRead -> property.getter.getValue(receiverInstance)
RuntimePropertyResolver.ReadResolution.UnresolvedRead -> error("cannot get property ${dataProperty.name} from the receiver class $receiverKClass")
}
}

Expand All @@ -179,8 +179,8 @@ class DeclarativeReflectionToObjectConverter(
?: error("tried to access a property ${dataProperty.name} on a null receiver")
val receiverKClass = receiverInstance::class
when (val property = propertyResolver.resolvePropertyWrite(receiverKClass, dataProperty.name)) {
is RuntimePropertyResolver.Resolution.Resolved -> property.property.setValue(receiverInstance, value)
RuntimePropertyResolver.Resolution.Unresolved -> error("cannot set property ${dataProperty.name} in the receiver class $receiverKClass")
is RuntimePropertyResolver.WriteResolution.ResolvedWrite -> property.setter.setValue(receiverInstance, value)
RuntimePropertyResolver.WriteResolution.UnresolvedWrite -> error("cannot set property ${dataProperty.name} in the receiver class $receiverKClass")
}
}
}
Expand Up @@ -17,7 +17,11 @@
package org.gradle.internal.declarativedsl.mappingToJvm


interface DeclarativeRuntimeProperty {
fun interface DeclarativeRuntimePropertyGetter {
fun getValue(receiver: Any): Any?
}


fun interface DeclarativeRuntimePropertySetter {
fun setValue(receiver: Any, value: Any?)
}
Expand Up @@ -16,55 +16,86 @@

package org.gradle.internal.declarativedsl.mappingToJvm

import org.gradle.internal.declarativedsl.mappingToJvm.RuntimePropertyResolver.Resolution.Resolved
import org.gradle.internal.declarativedsl.mappingToJvm.RuntimePropertyResolver.Resolution.Unresolved
import org.gradle.internal.declarativedsl.mappingToJvm.RuntimePropertyResolver.ReadResolution
import org.gradle.internal.declarativedsl.mappingToJvm.RuntimePropertyResolver.ReadResolution.ResolvedRead
import org.gradle.internal.declarativedsl.mappingToJvm.RuntimePropertyResolver.ReadResolution.UnresolvedRead
import org.gradle.internal.declarativedsl.mappingToJvm.RuntimePropertyResolver.WriteResolution
import org.gradle.internal.declarativedsl.mappingToJvm.RuntimePropertyResolver.WriteResolution.ResolvedWrite
import org.gradle.internal.declarativedsl.mappingToJvm.RuntimePropertyResolver.WriteResolution.UnresolvedWrite
import java.lang.reflect.Modifier
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty
import kotlin.reflect.KProperty
import kotlin.reflect.KVisibility
import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.memberProperties


interface RuntimePropertyResolver {
fun resolvePropertyRead(receiverClass: KClass<*>, name: String): Resolution
fun resolvePropertyWrite(receiverClass: KClass<*>, name: String): Resolution
fun resolvePropertyRead(receiverClass: KClass<*>, name: String): ReadResolution
fun resolvePropertyWrite(receiverClass: KClass<*>, name: String): WriteResolution

sealed interface Resolution {
data class Resolved(val property: DeclarativeRuntimeProperty) : Resolution
data object Unresolved : Resolution
sealed interface ReadResolution {
data class ResolvedRead(val getter: DeclarativeRuntimePropertyGetter) : ReadResolution
data object UnresolvedRead : ReadResolution
}

sealed interface WriteResolution {
data class ResolvedWrite(val setter: DeclarativeRuntimePropertySetter) : WriteResolution
data object UnresolvedWrite : WriteResolution
}
}


object ReflectionRuntimePropertyResolver : RuntimePropertyResolver {
override fun resolvePropertyRead(receiverClass: KClass<*>, name: String): RuntimePropertyResolver.Resolution {
val callable = receiverClass.memberProperties.find { it.name == name && it.visibility == KVisibility.PUBLIC }
?: receiverClass.memberFunctions.find { it.name == getterName(name) && it.parameters.size == 1 && it.visibility == KVisibility.PUBLIC }

return when (callable) {
null -> Unresolved
else -> Resolved(object : DeclarativeRuntimeProperty {
override fun getValue(receiver: Any): Any? = callable.call(receiver)
override fun setValue(receiver: Any, value: Any?) = throw UnsupportedOperationException()
})
}
override fun resolvePropertyRead(receiverClass: KClass<*>, name: String): ReadResolution {
val getter = findKotlinProperty(receiverClass, name)?.let(::kotlinPropertyGetter)
?: findKotlinFunctionGetter(receiverClass, name)
?: findJavaGetter(receiverClass, name)

return getter?.let(::ResolvedRead) ?: UnresolvedRead
}

override fun resolvePropertyWrite(receiverClass: KClass<*>, name: String): RuntimePropertyResolver.Resolution {
val setter = (receiverClass.memberProperties.find { it.name == name && it.visibility == KVisibility.PUBLIC } as? KMutableProperty<*>)?.setter
?: receiverClass.memberFunctions.find { it.name == setterName(name) && it.visibility == KVisibility.PUBLIC }

return when (setter) {
null -> Unresolved
else -> Resolved(object : DeclarativeRuntimeProperty {
override fun getValue(receiver: Any): Any = throw UnsupportedOperationException()
override fun setValue(receiver: Any, value: Any?) {
setter.call(receiver, value)
}
})
}
override fun resolvePropertyWrite(receiverClass: KClass<*>, name: String): WriteResolution {
val setter = (findKotlinProperty(receiverClass, name) as? KMutableProperty<*>)?.let(::kotlinPropertySetter)
?: findKotlinFunctionSetter(receiverClass, name)
?: findJavaSetter(receiverClass, name)

return setter?.let(::ResolvedWrite) ?: UnresolvedWrite
}

private
fun findKotlinProperty(receiverClass: KClass<*>, name: String) =
receiverClass.memberProperties.find { it.name == name && it.visibility == KVisibility.PUBLIC }

private
fun kotlinPropertyGetter(property: KProperty<*>) =
DeclarativeRuntimePropertyGetter { property.call(it) }

private
fun kotlinPropertySetter(property: KMutableProperty<*>) =
DeclarativeRuntimePropertySetter { receiver, value -> property.setter.call(receiver, value) }

private
fun findKotlinFunctionGetter(receiverClass: KClass<*>, name: String) =
receiverClass.memberFunctions.find { it.name == getterName(name) && it.parameters.size == 1 && it.visibility == KVisibility.PUBLIC }
?.let { property -> DeclarativeRuntimePropertyGetter { property.call(it) } }

private
fun findKotlinFunctionSetter(receiverClass: KClass<*>, name: String) =
receiverClass.memberFunctions.find { it.name == setterName(name) && it.visibility == KVisibility.PUBLIC }
?.let { function -> DeclarativeRuntimePropertySetter { receiver: Any, value: Any? -> function.call(receiver, value) } }

private
fun findJavaGetter(receiverClass: KClass<*>, name: String) =
receiverClass.java.methods.find { it.name == getterName(name) && it.parameters.isEmpty() && it.modifiers.and(Modifier.PUBLIC) != 0 }
?.let { method -> DeclarativeRuntimePropertyGetter { method.invoke(it) } }

private
fun findJavaSetter(receiverClass: KClass<*>, name: String) =
receiverClass.java.methods.find { it.name == setterName(name) && it.parameters.size == 1 && it.modifiers.and(Modifier.PUBLIC) != 0 }
?.let { method -> DeclarativeRuntimePropertySetter { receiver: Any, value: Any? -> method.invoke(receiver, value) } }

private
fun getterName(propertyName: String) = "get" + capitalize(propertyName)

Expand All @@ -79,21 +110,21 @@ object ReflectionRuntimePropertyResolver : RuntimePropertyResolver {


class CompositePropertyResolver(private val resolvers: List<RuntimePropertyResolver>) : RuntimePropertyResolver {
override fun resolvePropertyRead(receiverClass: KClass<*>, name: String): RuntimePropertyResolver.Resolution {
override fun resolvePropertyRead(receiverClass: KClass<*>, name: String): ReadResolution {
resolvers.forEach {
val resolution = it.resolvePropertyRead(receiverClass, name)
if (resolution is Resolved)
if (resolution !is UnresolvedRead)
return resolution
}
return Unresolved
return UnresolvedRead
}

override fun resolvePropertyWrite(receiverClass: KClass<*>, name: String): RuntimePropertyResolver.Resolution {
override fun resolvePropertyWrite(receiverClass: KClass<*>, name: String): WriteResolution {
resolvers.forEach {
val resolution = it.resolvePropertyWrite(receiverClass, name)
if (resolution is Resolved)
if (resolution !is UnresolvedWrite)
return resolution
}
return Unresolved
return UnresolvedWrite
}
}

0 comments on commit 0f3cf9d

Please sign in to comment.