Skip to content

Commit

Permalink
イケてるDSLで設定できるようにする
Browse files Browse the repository at this point in the history
  • Loading branch information
kitakkun committed Dec 31, 2023
1 parent a82f9a3 commit 129d497
Show file tree
Hide file tree
Showing 21 changed files with 167 additions and 153 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.github.kitakkun.backintime.annotations

@Target(AnnotationTarget.CLASS)
annotation class ValueContainer

@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
annotation class Capture

Expand Down
2 changes: 2 additions & 0 deletions gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
kotlin("jvm")
kotlin("kapt")
kotlin("plugin.serialization")
`java-gradle-plugin`
`maven-publish`
}
Expand All @@ -21,6 +22,7 @@ dependencies {

compileOnly("com.google.auto.service:auto-service:1.1.1")
kapt("com.google.auto.service:auto-service:1.1.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}

publishing {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package com.github.kitakkun.backintime

import com.github.kitakkun.backintime.extension.BackInTimeExtension
import com.github.kitakkun.backintime.plugin.BackInTimeCompilerOptionKey
import com.github.kitakkun.backintime.plugin.BackInTimePluginConsts
import com.google.auto.service.AutoService
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
import java.util.Base64

@Suppress("unused")
@AutoService(KotlinCompilerPluginSupportPlugin::class)
Expand All @@ -29,13 +33,14 @@ class BackInTimePlugin : KotlinCompilerPluginSupportPlugin {
return kotlinCompilation.target.project.provider {
listOf(
SubpluginOption(key = BackInTimeCompilerOptionKey.ENABLED, value = extension.enabled.toString()),
) + extension.capturedCalls.map {
SubpluginOption(key = BackInTimeCompilerOptionKey.CAPTURED_CALLS, value = it)
} + extension.valueGetters.map {
SubpluginOption(key = BackInTimeCompilerOptionKey.VALUE_GETTERS, value = it)
} + extension.valueSetters.map {
SubpluginOption(key = BackInTimeCompilerOptionKey.VALUE_SETTERS, value = it)
}
).plus(
extension.valueContainers
.map { Json.encodeToString(it) }
.map { Base64.getEncoder().encodeToString(it.toByteArray(Charsets.UTF_8)) }
.map {
SubpluginOption(key = BackInTimeCompilerOptionKey.VALUE_CONTAINER, value = it)
}
)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.github.kitakkun.backintime.extension

import com.github.kitakkun.backintime.plugin.extension.ValueContainerConfig
import kotlinx.serialization.Serializable

@Serializable
open class BackInTimeExtension(
var enabled: Boolean = true,
val valueContainers: MutableList<ValueContainerConfig> = mutableListOf(),
) {
fun valueContainers(configuration: ValueContainersScope.() -> Unit) {
val scope = ValueContainersScope().apply(configuration)
valueContainers.addAll(scope.containers)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.github.kitakkun.backintime.extension

import com.github.kitakkun.backintime.plugin.extension.ValueContainerConfig

class ValueContainersScope {
var containers: MutableList<ValueContainerConfig> = mutableListOf()
private set

fun container(configuration: ValueContainerConfig.() -> Unit) {
val config = ValueContainerConfig().apply(configuration)
this.containers.add(config)
}

fun androidValueContainers() {
container {
className = "androidx.lifecycle.MutableLiveData"
captures = listOf("postValue", "setValue")
getter = "getValue"
setter = "postValue"
}
container {
className = "androidx.compose.runtime.MutableState"
captures = listOf("<set-value>")
getter = "<get-value>"
setter = "<set-value>"
}
container {
className = "kotlinx.coroutines.flow.MutableStateFlow"
captures = listOf("<set-value>", "update", "updateAndGet", "getAndUpdate", "emit", "tryEmit")
getter = "<get-value>"
setter = "<set-value>"
}
}
}

2 changes: 2 additions & 0 deletions kotlin-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
kotlin("jvm")
kotlin("kapt")
kotlin("plugin.serialization")
`maven-publish`
}

Expand All @@ -18,6 +19,7 @@ dependencies {
kapt("com.google.auto.service:auto-service:1.1.1")

testImplementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}

publishing {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ object BackInTimeAnnotations {
val debuggableStateHolderAnnotationClassId = ClassId.topLevel(debuggableStateHolderAnnotationFqName)
val debuggableStateHolderManipulatorAnnotationClassId = ClassId.topLevel(debuggableStateHolderManipulatorFqName)

val valueContainerAnnotationFqName = FqName("com.github.kitakkun.backintime.annotations.ValueContainer")
val captureAnnotationFqName = FqName("com.github.kitakkun.backintime.annotations.Capture")
val getterAnnotationFqName = FqName("com.github.kitakkun.backintime.annotations.Getter")
val setterAnnotationFqName = FqName("com.github.kitakkun.backintime.annotations.Setter")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package com.github.kitakkun.backintime.compiler

import com.github.kitakkun.backintime.plugin.BackInTimeCompilerOptionKey
import com.github.kitakkun.backintime.plugin.BackInTimePluginConsts
import com.github.kitakkun.backintime.plugin.extension.ValueContainerConfig
import com.google.auto.service.AutoService
import kotlinx.serialization.json.Json
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.jetbrains.kotlin.config.CompilerConfiguration
import java.util.Base64

@Suppress("UNUSED")
@OptIn(ExperimentalCompilerApi::class)
Expand All @@ -22,33 +25,22 @@ class BackInTimeCommandLineProcessor : CommandLineProcessor {
required = false,
),
CliOption(
optionName = BackInTimeCompilerOptionKey.CAPTURED_CALLS,
valueDescription = "className1:functionName1,className2:functionName2,...(ex: androidx.lifecycle.MutableLiveData:<set-value>, androidx.compose.runtime.MutableState:<set-value>)",
description = "Functions to be captured.",
allowMultipleOccurrences = true,
required = false,
),
CliOption(
optionName = BackInTimeCompilerOptionKey.VALUE_GETTERS,
valueDescription = "className1:functionName1,className2:functionName2,...(ex: androidx.lifecycle.MutableLiveData:<get-value>, androidx.compose.runtime.MutableState:<get-value>)",
description = "Value getters to be used.",
allowMultipleOccurrences = true,
required = false,
),
CliOption(
optionName = BackInTimeCompilerOptionKey.VALUE_SETTERS,
valueDescription = "className1:functionName1,className2:functionName2,...(ex: androidx.lifecycle.MutableLiveData:<set-value>, androidx.compose.runtime.MutableState:<set-value>)",
description = "Value setters to be used.",
optionName = BackInTimeCompilerOptionKey.VALUE_CONTAINER,
valueDescription = "configurable via container dsl",
description = "predefined debuggable value-container class",
allowMultipleOccurrences = true,
required = false,
)
)

override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) = when (option.optionName) {
BackInTimeCompilerOptionKey.ENABLED -> configuration.put(BackInTimeCompilerConfigurationKey.ENABLED, value.toBoolean())
BackInTimeCompilerOptionKey.CAPTURED_CALLS -> configuration.appendList(BackInTimeCompilerConfigurationKey.CAPTURED_CALLS, value)
BackInTimeCompilerOptionKey.VALUE_GETTERS -> configuration.appendList(BackInTimeCompilerConfigurationKey.VALUE_GETTERS, value)
BackInTimeCompilerOptionKey.VALUE_SETTERS -> configuration.appendList(BackInTimeCompilerConfigurationKey.VALUE_SETTERS, value)
BackInTimeCompilerOptionKey.VALUE_CONTAINER -> {
val decodedValue = String(Base64.getDecoder().decode(value), Charsets.UTF_8)
val config = Json.decodeFromString<ValueContainerConfig>(decodedValue)
configuration.appendList(BackInTimeCompilerConfigurationKey.VALUE_CONTAINER, config)
}

else -> error("Unexpected config option ${option.optionName}")
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.github.kitakkun.backintime.compiler

import org.jetbrains.kotlin.name.CallableId
import com.github.kitakkun.backintime.compiler.backend.ValueContainerClassInfo

data class BackInTimeCompilerConfiguration(
val enabled: Boolean,
val capturedCallableIds: Set<CallableId>,
val valueGetterCallableIds: Set<CallableId>,
val valueSetterCallableIds: Set<CallableId>,
val valueContainers: List<ValueContainerClassInfo>,
)
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.github.kitakkun.backintime.compiler

import com.github.kitakkun.backintime.plugin.BackInTimeCompilerOptionKey
import com.github.kitakkun.backintime.plugin.extension.ValueContainerConfig
import org.jetbrains.kotlin.config.CompilerConfigurationKey

object BackInTimeCompilerConfigurationKey {
val ENABLED = CompilerConfigurationKey.create<Boolean>(BackInTimeCompilerOptionKey.ENABLED)
val CAPTURED_CALLS = CompilerConfigurationKey.create<List<String>>(BackInTimeCompilerOptionKey.CAPTURED_CALLS)
val VALUE_GETTERS = CompilerConfigurationKey.create<List<String>>(BackInTimeCompilerOptionKey.VALUE_GETTERS)
val VALUE_SETTERS = CompilerConfigurationKey.create<List<String>>(BackInTimeCompilerOptionKey.VALUE_SETTERS)
val VALUE_CONTAINER = CompilerConfigurationKey.create<List<ValueContainerConfig>>(BackInTimeCompilerOptionKey.VALUE_CONTAINER)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.kitakkun.backintime.compiler

import com.github.kitakkun.backintime.compiler.backend.ValueContainerClassInfo
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
Expand All @@ -8,17 +9,16 @@ import org.jetbrains.kotlin.name.Name
class BackInTimeCompilerConfigurationProcessor {
fun process(configuration: CompilerConfiguration) = BackInTimeCompilerConfiguration(
enabled = configuration[BackInTimeCompilerConfigurationKey.ENABLED] ?: false,
capturedCallableIds = configuration[BackInTimeCompilerConfigurationKey.CAPTURED_CALLS].orEmpty().map {
val (className, functionName) = it.replace(".", "/").split(":")
CallableId(ClassId.fromString(className), Name.guessByFirstCharacter(functionName))
}.toSet(),
valueGetterCallableIds = configuration[BackInTimeCompilerConfigurationKey.VALUE_GETTERS].orEmpty().map {
val (className, functionName) = it.replace(".", "/").split(":")
CallableId(ClassId.fromString(className), Name.guessByFirstCharacter(functionName))
}.toSet(),
valueSetterCallableIds = configuration[BackInTimeCompilerConfigurationKey.VALUE_SETTERS].orEmpty().map {
val (className, functionName) = it.replace(".", "/").split(":")
CallableId(ClassId.fromString(className), Name.guessByFirstCharacter(functionName))
}.toSet(),
valueContainers = configuration[BackInTimeCompilerConfigurationKey.VALUE_CONTAINER].orEmpty()
.map { config ->
val classId = ClassId.fromString(config.className.replace(".", "/"))

ValueContainerClassInfo(
classId = classId,
capturedCallableIds = config.captures.map { CallableId(classId, Name.guessByFirstCharacter(it)) },
valueGetter = CallableId(classId, Name.guessByFirstCharacter(config.getter)),
valueSetter = CallableId(classId, Name.guessByFirstCharacter(config.setter)),
)
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class BackInTimePluginContext(
moduleFragment: IrModuleFragment,
) : IrPluginContext by baseContext {
val pluginContext: IrPluginContext = baseContext
val valueContainerClassInfoList: List<ValueContainerClassInfo> = UserDefinedValueContainerAnalyzer.analyzeAdditionalValueContainerClassInfo(config, moduleFragment)
val valueContainerClassInfoList: List<ValueContainerClassInfo> = config.valueContainers + UserDefinedValueContainerAnalyzer.analyzeAdditionalValueContainerClassInfo(moduleFragment)

// BackInTimeService
val backInTimeServiceClassSymbol = referenceClass(BackInTimeConsts.backInTimeDebugServiceClassId)!!
Expand Down
Loading

0 comments on commit 129d497

Please sign in to comment.