Skip to content

Commit

Permalink
Merge pull request #137 from kitakkun/refactor/compiler
Browse files Browse the repository at this point in the history
Refactor compiler implementation
  • Loading branch information
kitakkun authored Dec 27, 2024
2 parents 12fd929 + 8455011 commit 1d8842b
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@ import com.kitakkun.backintime.compiler.backend.transformer.capture.BackInTimeDe
import com.kitakkun.backintime.compiler.backend.transformer.capture.BackInTimeDebuggableCapturePropertyChangesTransformer
import com.kitakkun.backintime.compiler.backend.transformer.capture.BackInTimeDebuggableConstructorTransformer
import com.kitakkun.backintime.compiler.backend.transformer.implement.BackInTimeDebuggableImplementTransformer
import com.kitakkun.backintime.compiler.common.BackInTimeCompilerConfiguration
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid

class BackInTimeIrGenerationExtension(
private val config: BackInTimeCompilerConfiguration,
) : IrGenerationExtension {
class BackInTimeIrGenerationExtension : IrGenerationExtension {
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
VersionSpecificAPI.INSTANCE = VersionSpecificAPIImpl
val context = BackInTimePluginContext(baseContext = pluginContext, config = config, moduleFragment = moduleFragment)
val context = BackInTimePluginContext(baseContext = pluginContext, moduleFragment = moduleFragment)
with(context) {
moduleFragment.transformChildrenVoid(BackInTimeDebuggableConstructorTransformer())
moduleFragment.transformChildrenVoid(BackInTimeDebuggableCapturePropertyChangesTransformer())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ import com.kitakkun.backintime.compiler.backend.analyzer.UserDefinedValueContain
import com.kitakkun.backintime.compiler.backend.api.VersionSpecificAPI
import com.kitakkun.backintime.compiler.backend.valuecontainer.ValueContainerBuiltIns
import com.kitakkun.backintime.compiler.backend.valuecontainer.resolved.ResolvedValueContainer
import com.kitakkun.backintime.compiler.common.BackInTimeCompilerConfiguration
import com.kitakkun.backintime.compiler.common.BackInTimeConsts
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.isVararg
import org.jetbrains.kotlin.javac.resolve.classId
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name

class BackInTimePluginContext(
baseContext: IrPluginContext,
config: BackInTimeCompilerConfiguration,
moduleFragment: IrModuleFragment,
) : IrPluginContext by baseContext {
val pluginContext: IrPluginContext = baseContext
Expand All @@ -28,39 +29,63 @@ class BackInTimePluginContext(
resolvedValueContainer
} + UserDefinedValueContainerAnalyzer.analyzeAdditionalValueContainerClassInfo(moduleFragment)

private val internalCompilerApiPackageFqName = FqName("com.kitakkun.backintime.core.runtime.internal")

// event report functions
val reportInstanceRegistrationFunctionSymbol = referenceFunctions(CallableId(internalCompilerApiPackageFqName, Name.identifier("reportInstanceRegistration"))).first()
val reportMethodInvocationFunctionSymbol = referenceFunctions(CallableId(internalCompilerApiPackageFqName, Name.identifier("reportMethodInvocation"))).first()
val reportPropertyValueChangeFunctionSymbol = referenceFunctions(CallableId(internalCompilerApiPackageFqName, Name.identifier("reportPropertyValueChange"))).first()
val reportNewRelationshipFunctionSymbol = referenceFunctions(CallableId(internalCompilerApiPackageFqName, Name.identifier("reportNewRelationship"))).first()
val reportInstanceRegistrationFunctionSymbol by lazy { backintimeNamedFunction(name = "reportInstanceRegistration", subpackage = "core.runtime.internal") }
val reportMethodInvocationFunctionSymbol by lazy { backintimeNamedFunction(name = "reportMethodInvocation", subpackage = "core.runtime.internal") }
val reportPropertyValueChangeFunctionSymbol by lazy { backintimeNamedFunction(name = "reportPropertyValueChange", subpackage = "core.runtime.internal") }
val reportNewRelationshipFunctionSymbol by lazy { backintimeNamedFunction(name = "reportNewRelationship", subpackage = "core.runtime.internal") }

// error generation functions
val throwTypeMismatchExceptionFunctionSymbol = referenceFunctions(CallableId(internalCompilerApiPackageFqName, Name.identifier("throwTypeMismatchException"))).first()
val throwNoSuchPropertyExceptionFunctionSymbol = referenceFunctions(CallableId(internalCompilerApiPackageFqName, Name.identifier("throwNoSuchPropertyException"))).first()
val throwTypeMismatchExceptionFunctionSymbol by lazy { backintimeNamedFunction(name = "throwTypeMismatchException", subpackage = "core.runtime.internal") }
val throwNoSuchPropertyExceptionFunctionSymbol by lazy { backintimeNamedFunction(name = "throwNoSuchPropertyException", subpackage = "core.runtime.internal") }

// capture utils
val captureThenReturnValueFunctionSymbol = referenceFunctions(CallableId(internalCompilerApiPackageFqName, Name.identifier("captureThenReturnValue"))).first()
val captureThenReturnValueFunctionSymbol by lazy { backintimeNamedFunction(name = "captureThenReturnValue", subpackage = "core.runtime.internal") }

/**
* Used in [com.kitakkun.backintime.compiler.backend.transformer.BackInTimeDebuggableConstructorTransformer]
*/
val propertyInfoClass = referenceClass(BackInTimeConsts.propertyInfoClassId)!!
val propertyInfoClass by lazy { backintimeIrClassSymbol(name = "PropertyInfo", subpackage = "core.websocket.event.model") }
val propertyInfoClassConstructor = propertyInfoClass.constructors.first { it.owner.isPrimary }
val listOfFunction = referenceFunctions(BackInTimeConsts.listOfFunctionId).first { it.owner.valueParameters.size == 1 && it.owner.valueParameters.first().isVararg }
val listOfFunction by lazy { namedFunction("kotlin.collections", "listOf") { it.owner.valueParameters.size == 1 && it.owner.valueParameters.first().isVararg } }

// kotlinx-serialization
val backInTimeJsonGetter = referenceProperties(BackInTimeConsts.backInTimeJsonCallableId).single().owner.getter!!
val encodeToStringFunction = referenceFunctions(BackInTimeConsts.kotlinxSerializationEncodeToStringCallableId).firstOrNull {
VersionSpecificAPI.INSTANCE.isReifiable(it.owner) && it.owner.typeParameters.size == 1 && it.owner.valueParameters.size == 1
} ?: error("${BackInTimeConsts.kotlinxSerializationEncodeToStringCallableId} is not found. Make sure you have kotlinx-serialization runtime dependency.")
val decodeFromStringFunction = referenceFunctions(BackInTimeConsts.kotlinxSerializationDecodeFromStringCallableId).firstOrNull {
VersionSpecificAPI.INSTANCE.isReifiable(it.owner) && it.owner.typeParameters.size == 1 && it.owner.valueParameters.size == 1
} ?: error("${BackInTimeConsts.kotlinxSerializationDecodeFromStringCallableId} is not found. Make sure you have kotlinx-serialization runtime dependency.")
val encodeToStringFunction by lazy {
namedFunction("kotlinx.serialization", "encodeToString") {
VersionSpecificAPI.INSTANCE.isReifiable(it.owner) && it.owner.typeParameters.size == 1 && it.owner.valueParameters.size == 1
}
}
val decodeFromStringFunction by lazy {
namedFunction("kotlinx.serialization", "decodeFromString") {
VersionSpecificAPI.INSTANCE.isReifiable(it.owner) && it.owner.typeParameters.size == 1 && it.owner.valueParameters.size == 1
}
}

// uuid
val uuidFunctionSymbol = referenceFunctions(CallableId(internalCompilerApiPackageFqName, Name.identifier("uuid"))).single()
val uuidFunctionSymbol by lazy { backintimeNamedFunction(name = "uuid", subpackage = "core.runtime.internal") }

val mutableMapOfFunction by lazy { namedFunction("kotlin.collections", "mutableMapOf") }

private fun namedFunction(
packageName: String,
name: String,
filter: (IrSimpleFunctionSymbol) -> Boolean = { true },
): IrSimpleFunctionSymbol {
val callableId = CallableId(FqName(packageName), Name.identifier(name))
return pluginContext.referenceFunctions(callableId).first(filter)
}

private fun backintimeNamedFunction(name: String, subpackage: String? = null): IrSimpleFunctionSymbol {
val suffix = subpackage?.let { ".$subpackage" } ?: ""
return namedFunction("com.kitakkun.backintime$suffix", name)
}

private fun backintimeIrClassSymbol(name: String, subpackage: String? = null): IrClassSymbol {
val suffix = subpackage?.let { ".$subpackage" } ?: ""
return getIrClassSymbol("com.kitakkun.backintime$suffix", name)
}

val mutableMapOfFunction = referenceFunctions(CallableId(FqName("kotlin.collections"), Name.identifier("mutableMapOf"))).first { it.owner.isInline }
private fun getIrClassSymbol(packageName: String, name: String): IrClassSymbol = pluginContext.referenceClass(classId(packageName, name))
?: error("Unable to find symbol. Package: $packageName, Name: $name")
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,17 @@ class BackInTimeDebuggableConstructorTransformer : IrElementTransformerVoid() {
if (!parentClass.isBackInTimeDebuggable) return declaration

val irBuilder = irBuiltIns.createIrBuilder(declaration.symbol)
val registerCall = irBuilder.generateRegisterCall(parentClass)

val registerCall = with(irBuilder) {
/** see [com.kitakkun.backintime.core.runtime.event.BackInTimeDebuggableInstanceEvent.RegisterTarget] */
irCall(reportInstanceRegistrationFunctionSymbol).apply {
putValueArgument(0, irGet(parentClass.thisReceiver!!))
putValueArgument(1, irString(parentClass.fqNameWhenAvailable?.asString() ?: "unknown"))
putValueArgument(2, irString(parentClass.superClass?.fqNameWhenAvailable?.asString() ?: "unknown"))
putValueArgument(3, generatePropertiesInfo(parentClass.properties))
}
}

val propertyRelationshipResolveCalls = parentClass.properties
.filter { it.isBackInTimeDebuggable && !it.isDelegated && !it.isVar }
.mapNotNull { property ->
Expand All @@ -62,14 +72,6 @@ class BackInTimeDebuggableConstructorTransformer : IrElementTransformerVoid() {
return declaration
}

/** see [com.kitakkun.backintime.core.runtime.event.BackInTimeDebuggableInstanceEvent.RegisterTarget] */
private fun IrBuilderWithScope.generateRegisterCall(parentClass: IrClass) = irCall(reportInstanceRegistrationFunctionSymbol).apply {
putValueArgument(0, irGet(parentClass.thisReceiver!!))
putValueArgument(1, irString(parentClass.fqNameWhenAvailable?.asString() ?: "unknown"))
putValueArgument(2, irString(parentClass.superClass?.fqNameWhenAvailable?.asString() ?: "unknown"))
putValueArgument(3, generatePropertiesInfo(parentClass.properties))
}

private fun IrBuilderWithScope.generatePropertiesInfo(
properties: Sequence<IrProperty>,
) = irCall(listOfFunction).apply {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ class BackInTimeCompilerRegistrar : CompilerPluginRegistrar() {

MessageCollectorHolder.messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
FirExtensionRegistrarAdapter.registerExtension(BackInTimeFirExtensionRegistrar())
IrGenerationExtension.registerExtension(BackInTimeIrGenerationExtension(config))
IrGenerationExtension.registerExtension(BackInTimeIrGenerationExtension())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,13 @@ import org.jetbrains.kotlin.name.Name

object BackInTimeConsts {
val backInTimeDebuggableInterfaceClassId = classId("com.kitakkun.backintime.core.runtime", "BackInTimeDebuggable")
val backInTimeDebuggableInterfaceClassFqName = backInTimeDebuggableInterfaceClassId.asSingleFqName()

val serializeMethodName = Name.identifier("serializeValue")
val deserializeMethodName = Name.identifier("deserializeValue")
val forceSetValueMethodName = Name.identifier("forceSetValue")
val backInTimeInstanceUUIDName = Name.identifier("backInTimeInstanceUUID")
val backInTimeInitializedPropertyMapName = Name.identifier("backInTimeInitializedPropertyMap")

val propertyInfoClassId = classId("com.kitakkun.backintime.core.websocket.event.model", "PropertyInfo")
val listOfFunctionId = CallableId(FqName("kotlin.collections"), Name.identifier("listOf"))
val UUIDClassId = classId("java.util", "UUID")
const val RANDOM_UUID_FUNCTION_NAME = "randomUUID"

// kotlinx.serialization
val backInTimeJsonCallableId = CallableId(FqName("com.kitakkun.backintime.core.runtime"), Name.identifier("backInTimeJson"))
val kotlinxSerializationEncodeToStringCallableId = CallableId(FqName("kotlinx.serialization"), Name.identifier("encodeToString"))
val kotlinxSerializationDecodeFromStringCallableId = CallableId(FqName("kotlinx.serialization"), Name.identifier("decodeFromString"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import com.kitakkun.backintime.compiler.k2.api.VersionSpecificAPIImpl
import com.kitakkun.backintime.compiler.k2.checkers.BackInTimeFirAdditionalCheckersExtension
import com.kitakkun.backintime.compiler.k2.extension.BackInTimeFirDeclarationGenerationExtension
import com.kitakkun.backintime.compiler.k2.extension.BackInTimeFirSupertypeGenerationExtension
import com.kitakkun.backintime.compiler.k2.matcher.DebuggableStateHolderPredicateMatcher
import com.kitakkun.backintime.compiler.k2.matcher.ValueContainerPredicateMatcher
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar

class BackInTimeFirExtensionRegistrar : FirExtensionRegistrar() {
Expand All @@ -16,9 +14,6 @@ class BackInTimeFirExtensionRegistrar : FirExtensionRegistrar() {
+::BackInTimeFirSupertypeGenerationExtension
+::BackInTimeFirDeclarationGenerationExtension

+::DebuggableStateHolderPredicateMatcher
+::ValueContainerPredicateMatcher

+::BackInTimeFirAdditionalCheckersExtension
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package com.kitakkun.backintime.compiler.k2.checkers

import com.kitakkun.backintime.compiler.k2.predicate.ValueContainerPredicate
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirRegularClassChecker
import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar

class BackInTimeFirAdditionalCheckersExtension(session: FirSession) : FirAdditionalCheckersExtension(session) {
override val declarationCheckers = object : DeclarationCheckers() {
override val regularClassCheckers: Set<FirRegularClassChecker> = setOf(ValueContainerDefinitionChecker, DebuggableStateHolderPropertyChecker)
}

override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(ValueContainerPredicate)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.kitakkun.backintime.compiler.k2.checkers

import com.kitakkun.backintime.compiler.common.BackInTimeAnnotations
import com.kitakkun.backintime.compiler.k2.api.VersionSpecificAPI
import com.kitakkun.backintime.compiler.k2.matcher.debuggableStateHolderPredicateMatcher
import com.kitakkun.backintime.compiler.k2.matcher.valueContainerPredicateMatcher
import com.kitakkun.backintime.compiler.k2.predicate.BackInTimePredicate
import com.kitakkun.backintime.compiler.k2.predicate.ValueContainerPredicate
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
Expand All @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirRegularClassChe
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneType
Expand Down Expand Up @@ -138,13 +139,17 @@ object DebuggableStateHolderPropertyChecker : FirRegularClassChecker(MppCheckerK
context(CheckerContext)
private fun ConeKotlinType.isValueContainer(): Boolean {
// val predefinedInGradle = session.backInTimeCompilerConfigurationProvider.config.valueContainers.any { it.classId == this.classId }
val configuredViaAnnotation = VersionSpecificAPI.INSTANCE.resolveToRegularClassSymbol(this, session)?.let { session.valueContainerPredicateMatcher.isAnnotated(it) } ?: false
val configuredViaAnnotation = VersionSpecificAPI.INSTANCE.resolveToRegularClassSymbol(this, session)?.let {
session.predicateBasedProvider.matches(ValueContainerPredicate, it)
} ?: false
// return predefinedInGradle || configuredViaAnnotation
return configuredViaAnnotation
}

context(CheckerContext)
private fun ConeKotlinType.isDebuggableStateHolder(): Boolean {
return VersionSpecificAPI.INSTANCE.resolveToRegularClassSymbol(this, session)?.let { session.debuggableStateHolderPredicateMatcher.isAnnotated(it) } ?: false
return VersionSpecificAPI.INSTANCE.resolveToRegularClassSymbol(this, session)?.let {
session.predicateBasedProvider.matches(BackInTimePredicate, it)
} ?: false
}
}

This file was deleted.

This file was deleted.

Loading

0 comments on commit 1d8842b

Please sign in to comment.