Skip to content

Commit

Permalink
Add custom logger features (#35)
Browse files Browse the repository at this point in the history
* Add custom logger features

* Remove @ComposeInvestigatorCompilerApi

* Fix package

* Clarify the name of the LogType class in ComposeInvestigate
  • Loading branch information
jisungbin committed Nov 19, 2023
1 parent 5bcf463 commit 4e44dc2
Show file tree
Hide file tree
Showing 13 changed files with 298 additions and 96 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ root = true
charset = utf-8
indent_size = 2
indent_style = space
max_line_length = 150
max_line_length = 170
insert_final_newline = true
trim_trailing_whitespace = true
ktlint_standard_filename = disabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,25 @@
package land.sungbin.composeinvestigator.compiler.test.source

import androidx.compose.runtime.Composable
import land.sungbin.composeinvestigator.runtime.ComposeInvestigateLogType
import land.sungbin.composeinvestigator.runtime.ComposeInvestigateLogger

@ComposeInvestigateLogger
fun composeInvestigateLogger(composableName: String, logType: ComposeInvestigateLogType) {
when (logType) {
is ComposeInvestigateLogType.InvalidationProcessed -> {
println("<$composableName> InvalidationProcessed. DiffParams: ${logType.diffParams}")
}
is ComposeInvestigateLogType.InvalidationSkipped -> {
println("<$composableName> InvalidationSkipped")
}
}
}

@Composable
fun entry(one: Int, two: String) {
println(one)
println(two)
println(ComposeInvestigateLogType.InvalidationSkipped)
println(ComposeInvestigateLogType.InvalidationProcessed(null))
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
package land.sungbin.composeinvestigator.compiler

import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
import land.sungbin.composeinvestigator.compiler.internal.logger.InvestigateLogger
import land.sungbin.composeinvestigator.compiler.internal.logger.InvestigateLoggerVisitor
import land.sungbin.composeinvestigator.compiler.internal.tracker.InvalidationTrackableTransformer
import land.sungbin.composeinvestigator.compiler.internal.tracker.key.DurableFunctionKeyTransformer
import land.sungbin.composeinvestigator.compiler.util.VerboseLogger
Expand All @@ -22,8 +24,14 @@ internal class InvalidationTrackExtension(private val logger: VerboseLogger) : I
val stabilityInferencer = StabilityInferencer(moduleFragment.descriptor, emptySet())

DurableFunctionKeyTransformer(pluginContext).lower(moduleFragment)
moduleFragment.transformChildrenVoid(InvestigateLoggerVisitor(pluginContext, logger))

if (!InvestigateLogger.checkLoggerIsInstalled()) {
InvestigateLogger.useDefaultLogger(pluginContext)
}

moduleFragment.transformChildrenVoid(
transformer = InvalidationTrackableTransformer(
InvalidationTrackableTransformer(
context = pluginContext,
logger = logger,
stabilityInferencer = stabilityInferencer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,6 @@ internal abstract class AbstractInvalidationTrackingLower(context: IrPluginConte
symbol = value.symbol,
)

protected fun irPrintln(value: IrExpression): IrCall =
IrCallImpl.fromSymbolOwner(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
symbol = printlnSymbol,
).apply {
putValueArgument(0, value)
}

protected fun irHashCode(value: IrExpression): IrCall =
IrCallImpl.fromSymbolOwner(
startOffset = UNDEFINED_OFFSET,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Designed and developed by Ji Sungbin 2023.
*
* Licensed under the MIT.
* Please see full license: https://github.com/jisungbin/ComposeInvestigator/blob/main/LICENSE
*/

package land.sungbin.composeinvestigator.compiler.internal.logger

import land.sungbin.composeinvestigator.compiler.internal.COMPOSE_INVESTIGATE_LOG_TYPE_FQN
import land.sungbin.composeinvestigator.compiler.internal.COMPOSE_INVESTIGATE_LOG_TYPE_INVALIDATION_PROCESSED_FQN
import land.sungbin.composeinvestigator.compiler.internal.COMPOSE_INVESTIGATE_LOG_TYPE_INVALIDATION_SKIPPED_FQN
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrDeclarationReference
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.isNullableAny
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name

internal object InvestigateLogger {
private var loggerSymbol: IrSimpleFunctionSymbol? = null
private var loggerType: LoggerType? = null

private var logTypeSymbol: IrClassSymbol? = null
private var logTypeInvalidationProcessedSymbol: IrClassSymbol? = null
private var logTypeInvalidationSkippedSymbol: IrClassSymbol? = null

internal fun checkLoggerIsInstalled(): Boolean = loggerSymbol != null && loggerType != null

internal fun useDefaultLogger(context: IrPluginContext) {
val printlnSymbol: IrSimpleFunctionSymbol =
context
.referenceFunctions(
CallableId(
packageName = FqName("kotlin.io"),
callableName = Name.identifier("println"),
),
)
.single { symbol ->
symbol.owner.valueParameters.size == 1 &&
symbol.owner.valueParameters.single().type.isNullableAny()
}

loggerSymbol = printlnSymbol
loggerType = LoggerType.Println
}

internal fun useCustomLogger(symbol: IrSimpleFunctionSymbol) {
loggerSymbol = symbol
loggerType = LoggerType.Custom
}

internal fun makeIrCall(
composableName: IrConst<String>,
logType: IrDeclarationReference,
originalMessage: IrConst<String>,
): IrCall =
IrCallImpl.fromSymbolOwner(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
symbol = loggerSymbol!!,
).apply {
when (loggerType!!) {
LoggerType.Println -> {
putValueArgument(0, originalMessage)
}
LoggerType.Custom -> {
putValueArgument(0, composableName)
putValueArgument(1, logType)
}
}
}

internal fun obtainLogTypeSymbol(context: IrPluginContext): IrClassSymbol {
if (logTypeSymbol == null) {
logTypeSymbol = context.referenceClass(ClassId.topLevel(COMPOSE_INVESTIGATE_LOG_TYPE_FQN))
}
return logTypeSymbol!!
}

internal fun obtainLogTypeInvalidationProcessedSymbol(context: IrPluginContext): IrClassSymbol {
if (logTypeInvalidationProcessedSymbol == null) {
logTypeInvalidationProcessedSymbol =
context.referenceClass(ClassId.topLevel(COMPOSE_INVESTIGATE_LOG_TYPE_INVALIDATION_PROCESSED_FQN))
}
return logTypeInvalidationProcessedSymbol!!
}

internal fun obtainLogTypeInvalidationSkippedSymbol(context: IrPluginContext): IrClassSymbol {
if (logTypeInvalidationSkippedSymbol == null) {
logTypeInvalidationSkippedSymbol =
context.referenceClass(ClassId.topLevel(COMPOSE_INVESTIGATE_LOG_TYPE_INVALIDATION_SKIPPED_FQN))
}
return logTypeInvalidationSkippedSymbol!!
}

private enum class LoggerType {
Println,
Custom,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Designed and developed by Ji Sungbin 2023.
*
* Licensed under the MIT.
* Please see full license: https://github.com/jisungbin/ComposeInvestigator/blob/main/LICENSE
*/

package land.sungbin.composeinvestigator.compiler.internal.logger

import land.sungbin.composeinvestigator.compiler.internal.COMPOSABLE_FQN
import land.sungbin.composeinvestigator.compiler.internal.COMPOSE_INVESTIGATE_LOGGER_FQN
import land.sungbin.composeinvestigator.compiler.internal.COMPOSE_INVESTIGATE_LOG_TYPE_FQN
import land.sungbin.composeinvestigator.compiler.internal.origin.InvestigateLoggerUsedFuntionOrigin
import land.sungbin.composeinvestigator.compiler.util.VerboseLogger
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.types.classFqName
import org.jetbrains.kotlin.ir.types.isString
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.util.isTopLevel
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid

internal class InvestigateLoggerVisitor(
private val context: IrPluginContext,
private val logger: VerboseLogger,
) : IrElementTransformerVoid(), IrPluginContext by context {
override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
if (declaration.isValidComposeInvestigateLoggerFunction()) {
if (declaration.origin == InvestigateLoggerUsedFuntionOrigin) return super.visitFunction(declaration)
if (InvestigateLogger.checkLoggerIsInstalled()) {
error(
"More than one ComposeInvestigateLogger function was found. " +
"Only one ComposeInvestigateLogger function is supported.",
)
}
logger("Found ComposeInvestigateLogger function: ${declaration.dump()}")
declaration.origin = InvestigateLoggerUsedFuntionOrigin
InvestigateLogger.useCustomLogger(declaration.symbol)
}
return super.visitFunction(declaration)
}

// 1. has @ComposeInvestigateLogger and no @Composable
// 2. top-level declaration
// 3. public
// 4. no extension receiver and no context receiver
// 5. not suspend
// 6. unit type
// 7. has only two parameters: <String, LogType>
private fun IrFunction.isValidComposeInvestigateLoggerFunction(): Boolean =
hasAnnotation(COMPOSE_INVESTIGATE_LOGGER_FQN) &&
!hasAnnotation(COMPOSABLE_FQN) &&
isTopLevel &&
visibility.isPublicAPI &&
extensionReceiverParameter == null &&
contextReceiverParametersCount == 0 &&
!isSuspend &&
returnType.isUnit() &&
valueParameters.size == 2 &&
valueParameters[0].type.isString() &&
valueParameters[1].type.classFqName == COMPOSE_INVESTIGATE_LOG_TYPE_FQN
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ internal val COMPOSABLE_FQN = FqName("$AndroidxComposeRuntime.Composable")
internal val COMPOSER_FQN = FqName("$AndroidxComposeRuntime.Composer")
internal val COMPOSER_KT_FQN = FqName("$AndroidxComposeRuntime.ComposerKt")

internal val COMPOSE_INVESTIGATE_LOGGER_FQN = FqName("$ComposeInvestigatorRuntime.ComposeInvestigateLogger")
internal val COMPOSE_INVESTIGATE_LOG_TYPE_FQN = FqName("$ComposeInvestigatorRuntime.ComposeInvestigateLogType")
internal val COMPOSE_INVESTIGATE_LOG_TYPE_INVALIDATION_PROCESSED_FQN = COMPOSE_INVESTIGATE_LOG_TYPE_FQN.child(Name.identifier("InvalidationProcessed"))
internal val COMPOSE_INVESTIGATE_LOG_TYPE_INVALIDATION_SKIPPED_FQN = COMPOSE_INVESTIGATE_LOG_TYPE_FQN.child(Name.identifier("InvalidationSkipped"))

internal val PARAMETER_INFO_FQN = FqName("$ComposeInvestigatorRuntime.ParameterInfo")
internal val COMPOSABLE_INVALIDATION_TRACK_TABLE_FQN =
FqName("$ComposeInvestigatorRuntime.ComposableInvalidationTrackTable")
internal val COMPOSABLE_INVALIDATION_TRACK_TABLE_FQN = FqName("$ComposeInvestigatorRuntime.ComposableInvalidationTrackTable")

internal val DECLARATION_STABILITY_FQN = FqName("$ComposeInvestigatorRuntime.DeclarationStability")
internal val DECLARATION_STABILITY_CERTAIN_FQN = DECLARATION_STABILITY_FQN.child(Name.identifier("Certain"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Designed and developed by Ji Sungbin 2023.
*
* Licensed under the MIT.
* Please see full license: https://github.com/jisungbin/ComposeInvestigator/blob/main/LICENSE
*/

package land.sungbin.composeinvestigator.compiler.internal.origin

import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl

public data object InvestigateLoggerUsedFuntionOrigin : IrDeclarationOriginImpl("USED_BY_INVESTIGATE_LOGGER")
Loading

0 comments on commit 4e44dc2

Please sign in to comment.