kitakkun / back-in-time-plugin

Kotlin Compiler Plugin to make your program back-in-time debuggable.
Apache License 2.0
33 stars 1 forks source link

Private properties of the superclass cannot be set via subclasses’ forceSetValue methods #88

Closed kitakkun closed 3 days ago

kitakkun commented 3 days ago

Example:

@BackInTime
open class SuperClass {
    private var privateSuperProperty = "private-super"
    fun getPrivateSuperProperty() = privateSuperProperty
}

@BackInTime
class SubClass : SuperClass()

val subClass = SubClass()
subClass.forceSetValue("privateSuperProperty", "private-super(modified)") // will throw NoSuchPropertyException
kitakkun commented 3 days ago

Throwing the error makes it difficult to fall back to superclass methods.

The inside of the generated method (current implementation):

@BackInTime
class SubClass : SuperClass(), BackInTimeDebuggable {
    override fun forceSetValue(propertyName: String, value: Any?) {
        when (propertyName) {
            "prop1" -> { /* set value to prop1 */ }
            "prop2" -> { /* set value to prop2 */ }
            "prop3" -> { /* set value to prop3 */ }
            else -> error()
        }
    }
}

This should be...

@BackInTime
class SubClass : SuperClass(), BackInTimeDebuggable {
    override fun forceSetValue(propertyName: String, value: Any?) {
        when (propertyName) {
            "prop1" -> { /* set value to prop1 */ }
            "prop2" -> { /* set value to prop2 */ }
            "prop3" -> { /* set value to prop3 */ }
            else -> super.forceSetValue(propertyName, value) // Should call super method because SuperClass is also BackInTimeDebuggable.
        }
    }
}
kitakkun commented 3 days ago

BTW, I don't know how to call super declarations. I couldn't find anything like irSuper though there's irThis. I must investigate how super is represented at the IR level.

kitakkun commented 3 days ago

I found the function named getLastOverridden...! This might be useful for this use case.

fun IrFunction.getLastOverridden(): IrFunction {
    if (this !is IrSimpleFunction) return this

    return generateSequence(listOf(this)) { it.firstOrNull()?.overriddenSymbols?.map { it.owner } }.flatten().last()
}
kitakkun commented 3 days ago

Oops, getLastOverriden seems to return the function at the most base class. I mean, it always returns the declaration inside BackInTimeDebuggable interface. What we want is the nearest overridden symbol.

kitakkun commented 3 days ago

Maybe we can get the implementation inside the superclass like this:

        val superDeclaration = declaration.overriddenSymbols.firstOrNull { it.owner.modality == Modality.OPEN }
kitakkun commented 3 days ago

The other functions like serializeValue and deserializeValue have the same problem.