Skip to content

add compile time loggers, optional logging for runtime #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ android {
}

dependencies {
// todo: for debug
// implementation(project(":compiler-plugin"))
// kotlinCompilerPluginClasspath(project(":compiler-plugin"))

implementation(libs.stslex.compilerPlugin)
kotlinCompilerPluginClasspath(libs.stslex.compilerPlugin)

Expand Down
4 changes: 2 additions & 2 deletions app/src/main/kotlin/com/stslex/compiler_app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ class MainActivity : ComponentActivity() {
setSecondName(user.secondName)
}

@DistinctUntilChangeFun
@DistinctUntilChangeFun(false)
private fun setName(name: String) {
logger.log(Level.INFO, "setName: $name")
findViewById<TextView>(R.id.usernameFieldTextView).text = name
}

@DistinctUntilChangeFun
@DistinctUntilChangeFun(true)
private fun setSecondName(name: String) {
logger.log(Level.INFO, "setSecondName: $name")
findViewById<TextView>(R.id.secondNameFieldTextView).text = name
Expand Down
10 changes: 2 additions & 8 deletions compiler-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group = "io.github.stslex"
version = "0.0.1"
version = libs.versions.stslexCompilerPlugin.get()

java {
sourceCompatibility = JavaVersion.VERSION_21
Expand Down Expand Up @@ -43,7 +43,7 @@ publishing {

groupId = "io.github.stslex"
artifactId = "compiler-plugin"
version = "0.0.1"
version = libs.versions.stslexCompilerPlugin.get()

artifact(tasks["javadocJar"])
artifact(tasks["sourcesJar"])
Expand Down Expand Up @@ -94,7 +94,6 @@ tasks.named("publishToMavenLocal") {

val generateChecksums by tasks.register("generateChecksums") {
mustRunAfter("publishToMavenLocal")
finalizedBy("packageArtifacts")

group = "publishing"
description = "Generate MD5 и SHA1 for all artifacts in local repository"
Expand Down Expand Up @@ -127,15 +126,10 @@ val generateChecksums by tasks.register("generateChecksums") {
}

tasks.register<Zip>("packageArtifacts") {
mustRunAfter("generateChecksums")
dependsOn("generateChecksums")

group = "publishing"
description = "Create ZIP-archive with artifacts for Central Publisher Portal"


val localRepo = file(localRepoPath)
from(localRepo)

println("📦 Package artifacts...")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
package io.github.stslex.compiler_plugin

import java.util.logging.Level
import java.util.logging.Logger
import io.github.stslex.compiler_plugin.utils.RuntimeLogger

internal object DistinctChangeCache {

private const val TAG = "KotlinCompilerDistinct"
private val logger = Logger.getLogger(TAG)
private val cache = mutableMapOf<String, Pair<List<Any?>, Any?>>()
private val logger = RuntimeLogger.tag("DistinctChangeLogger")

@JvmStatic
@Suppress("UNCHECKED_CAST")
fun <R> invoke(key: String, args: List<Any?>, body: () -> R): R {
fun <R> invoke(
key: String,
args: List<Any?>,
body: () -> R,
config: DistinctChangeConfig
): R {
val entry = cache[key]
logger.log(Level.INFO, "memorize key: $key, entry: $entry, args: $args")

if (config.logging) {
logger.i("key: $key, config:$config, entry: $entry, args: $args")
}

if (entry != null && entry.first == args) {
return entry.second as R
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.github.stslex.compiler_plugin

internal data class DistinctChangeConfig(
val logging: Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@ package io.github.stslex.compiler_plugin

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
public annotation class DistinctUntilChangeFun
public annotation class DistinctUntilChangeFun(
val logging: Boolean = LOGGING_DEFAULT
) {

public companion object {

internal const val LOGGING_DEFAULT = false

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ internal class WorkshopCompilerPlugin(
) : CompilerPluginRegistrar() {

override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
IrGenerationExtension.registerExtension(DistinctChangeIrGenerationExtension())
IrGenerationExtension.registerExtension(DistinctChangeIrGenerationExtension(configuration))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,24 @@ package io.github.stslex.compiler_plugin.extensions
import io.github.stslex.compiler_plugin.transformers.IrFunctionTransformer
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid

internal class DistinctChangeIrGenerationExtension : IrGenerationExtension {
internal class DistinctChangeIrGenerationExtension(
private val configuration: CompilerConfiguration
) : IrGenerationExtension {

override fun generate(
moduleFragment: IrModuleFragment,
pluginContext: IrPluginContext
pluginContext: IrPluginContext,
) {
moduleFragment.transformChildrenVoid(IrFunctionTransformer(pluginContext))
val messageCollector = configuration.get(
CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY,
MessageCollector.NONE
)
moduleFragment.transformChildrenVoid(IrFunctionTransformer(pluginContext, messageCollector))
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
package io.github.stslex.compiler_plugin.transformers

import buildArgsListExpression
import io.github.stslex.compiler_plugin.DistinctUntilChangeFun
import io.github.stslex.compiler_plugin.utils.CompileLogger.Companion.toCompilerLogger
import io.github.stslex.compiler_plugin.utils.buildLambdaForBody
import io.github.stslex.compiler_plugin.utils.buildSaveInCacheCall
import io.github.stslex.compiler_plugin.utils.fullyQualifiedName
import io.github.stslex.compiler_plugin.utils.readQualifier
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.createExpressionBody
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.name.FqName

internal class IrFunctionTransformer(
private val pluginContext: IrPluginContext
private val pluginContext: IrPluginContext,
private val messageCollector: MessageCollector
) : IrElementTransformerVoidWithContext() {

private val IrSimpleFunction.isAnnotationValid: Boolean
get() = DistinctUntilChangeFun::class.qualifiedName
?.let { qualifier -> hasAnnotation(FqName(qualifier)) }
?: false
private val logger by lazy { messageCollector.toCompilerLogger() }

override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
if (declaration.isAnnotationValid.not()) {
return super.visitSimpleFunction(declaration)
}
val originalBody = declaration.body ?: return super.visitSimpleFunction(declaration)
val qualifierArgs = pluginContext.readQualifier(declaration, logger)
?: return super.visitSimpleFunction(declaration)

val originalBody = declaration.body ?: return super.visitSimpleFunction(declaration)

logger.i("fullyQualifiedName: ${declaration.fullyQualifiedName}")
val keyLiteral = IrConstImpl.string(
startOffset = declaration.startOffset,
endOffset = declaration.endOffset,
Expand All @@ -44,11 +42,14 @@ internal class IrFunctionTransformer(
argsListExpr = argsListExpr,
lambdaExpr = lambdaExpr,
function = declaration,
qualifierArgs = qualifierArgs,
logger = logger
)

declaration.body = pluginContext.irFactory.createExpressionBody(memoizeCall)

return super.visitSimpleFunction(declaration)
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.github.stslex.compiler_plugin.utils

import org.jetbrains.kotlin.backend.common.toLogger
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector

internal class CompileLogger(
private val messageCollector: MessageCollector
) {

fun i(msg: String) {
messageCollector.toLogger()
messageCollector.report(CompilerMessageSeverity.INFO, msg)
}

companion object {

fun MessageCollector.toCompilerLogger(): CompileLogger = CompileLogger(this)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package io.github.stslex.compiler_plugin.utils

import io.github.stslex.compiler_plugin.DistinctChangeCache
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
Expand All @@ -16,13 +18,26 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import kotlin.reflect.KClass

internal fun IrPluginContext.createIrBuilder(
declaration: IrDeclaration
): DeclarationIrBuilder = DeclarationIrBuilder(
generatorContext = this,
symbol = declaration.symbol
)

internal inline fun <reified T : Any> KClass<T>.toClassId(): ClassId = ClassId(
FqName(java.`package`.name),
Name.identifier(java.simpleName)
)

internal fun IrPluginContext.buildLambdaForBody(
originalBody: IrBody,
Expand Down Expand Up @@ -78,13 +93,14 @@ internal fun IrPluginContext.buildSaveInCacheCall(
argsListExpr: IrExpression,
lambdaExpr: IrExpression,
function: IrSimpleFunction,
qualifierArgs: IrExpression,
logger: CompileLogger
): IrExpression {
val cacheNameClass = checkNotNull(DistinctChangeCache::class.simpleName) {
"Cannot get simpleName"
}
logger.i("buildSaveInCacheCall for ${function.name}, args: ${argsListExpr.dump()} with config: ${qualifierArgs.dump()}")

val memoizeFunction = referenceFunctions(
CallableId(
classId = ClassId(FqName(CompilerConsts.PATH), Name.identifier(cacheNameClass)),
classId = DistinctChangeCache::class.toClassId(),
callableName = Name.identifier("invoke")
)
)
Expand All @@ -97,13 +113,14 @@ internal fun IrPluginContext.buildSaveInCacheCall(
type = function.returnType,
symbol = memoizeFunction,
typeArgumentsCount = 1,
valueArgumentsCount = 3
valueArgumentsCount = 4
)
.also { call -> call.patchDeclarationParents(function) }
.apply {
putTypeArgument(0, function.returnType)
putValueArgument(0, keyLiteral)
putValueArgument(1, argsListExpr)
putValueArgument(2, lambdaExpr)
putValueArgument(3, qualifierArgs)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.github.stslex.compiler_plugin.utils

import io.github.stslex.compiler_plugin.DistinctChangeConfig
import io.github.stslex.compiler_plugin.DistinctUntilChangeFun
import io.github.stslex.compiler_plugin.DistinctUntilChangeFun.Companion.LOGGING_DEFAULT
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.ir.builders.irBoolean
import org.jetbrains.kotlin.ir.builders.irCallConstructor
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.getAnnotation
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.name.FqName

@OptIn(UnsafeDuringIrConstructionAPI::class)
internal fun IrPluginContext.readQualifier(
function: IrSimpleFunction,
logger: CompileLogger
): IrExpression? {
val qualifiedName = DistinctUntilChangeFun::class.qualifiedName ?: return null
val annotation = function.getAnnotation(FqName(qualifiedName)) ?: return null

logger.i("readQualifier: annotation is found for ${function.name}")

val irBuilder = createIrBuilder(function)

val currentValue = annotation.getValueArgument(0)
val logging = currentValue ?: irBuilder.irBoolean(LOGGING_DEFAULT)

val constructorSymbol = referenceClass(DistinctChangeConfig::class.toClassId())
?.constructors
?.firstOrNull()
?: error("CheckChangesConfig not found in IR")

return irBuilder
.irCallConstructor(
callee = constructorSymbol,
typeArguments = emptyList()
)
.also { it.patchDeclarationParents(function) }
.apply {
putValueArgument(0, logging)
}
}
Loading