Hello everyone, I’m a novice at kotlin compiler. I...
# compiler
c
Hello everyone, I’m a novice at kotlin compiler. I’m trying to write a compiler plugin. I want to change the
val
of property to
var
in
IrElementTransformerVoid.visitPropertyNew
, but I found that
isVar
is a immutable value. What should I do?
I tried to solve it with the following code, but failed.
Copy code
override fun visitPropertyNew(declaration: IrProperty): IrStatement {
  var newDeclaration =
  // Some logic...
  return super.visitPropertyNew(newDeclaration.copy(isVar = false))
}

fun IrProperty.copy(
  startOffset: Int = this.startOffset,
  endOffset: Int = this.endOffset,
  origin: IrDeclarationOrigin = this.origin,
  symbol: IrPropertySymbol = IrPropertyPublicSymbolImpl(this.symbol.signature!!, this.symbol.descriptor),
  name: Name = this.name,
  visibility: DescriptorVisibility = this.visibility,
  modality: Modality = this.modality,
  isVar: Boolean = this.isVar,
  isConst: Boolean = this.isConst,
  isLateinit: Boolean = this.isLateinit,
  isDelegated: Boolean = this.isDelegated,
  isExternal: Boolean = this.isExternal,
  isExpect: Boolean = this.isExpect,
  isFakeOverride: Boolean = this.isFakeOverride,
  containerSource: DeserializedContainerSource? = this.containerSource,
  parent: IrDeclarationParent = this.parent,
  annotations: List<IrConstructorCall> = this.annotations,
  backingField: IrField? = this.backingField,
  getter: IrSimpleFunction? = this.getter,
  setter: IrSimpleFunction? = this.setter,
  overriddenSymbols: List<IrPropertySymbol> = this.overriddenSymbols,
  metadata: MetadataSource? = this.metadata,
  attributeOwnerId: IrAttributeContainer = this.attributeOwnerId,
) = IrPropertyImpl(
  startOffset,
  endOffset,
  origin,
  symbol,
  name,
  visibility,
  modality,
  isVar,
  isConst,
  isLateinit,
  isDelegated,
  isExternal,
  isExpect,
  isFakeOverride,
  containerSource
).also {
  it.parent = parent
  it.annotations = annotations
  it.backingField = backingField
  it.getter = getter
  it.setter = setter
  it.overriddenSymbols = overriddenSymbols
  it.metadata = metadata
  it.attributeOwnerId = attributeOwnerId
}
Exception:
Copy code
Caused by: java.lang.IllegalStateException: Parent of this declaration is not a class: PROPERTY name:lazy visibility:public modality:FINAL [var]
	at org.jetbrains.kotlin.ir.util.IrUtilsKt.getParentAsClass(IrUtils.kt:268)
	at org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper.mapFunctionName(MethodSignatureMapper.kt:86)
	at org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper.mapFunctionName$default(MethodSignatureMapper.kt:70)
	at org.jetbrains.kotlin.backend.jvm.lower.JvmPropertiesLowering.computeSyntheticMethodName(JvmPropertiesLowering.kt:221)
	at org.jetbrains.kotlin.backend.jvm.lower.JvmPropertiesLowering.createSyntheticMethodForAnnotations(JvmPropertiesLowering.kt:164)
	at org.jetbrains.kotlin.backend.jvm.lower.JvmPropertiesLowering.lowerProperty(JvmPropertiesLowering.kt:154)
	at org.jetbrains.kotlin.backend.jvm.lower.JvmPropertiesLowering.access$lowerProperty(JvmPropertiesLowering.kt:37)
	at org.jetbrains.kotlin.backend.jvm.lower.JvmPropertiesLowering$visitClassNew$1.invoke(JvmPropertiesLowering.kt:44)
	at org.jetbrains.kotlin.backend.jvm.lower.JvmPropertiesLowering$visitClassNew$1.invoke(JvmPropertiesLowering.kt:44)
	at org.jetbrains.kotlin.ir.util.TransformKt.transformDeclarationsFlat(transform.kt:94)
s
I think you are on the right track, but you also want to replace property itself with all of its references, so that the calls to all getters and setters are referring to the correct property you created and not the old one
l
There is something like 'deepCopy' which also remaps symbols. You can check its usage in the Compose compiler plugin.
Things going under hood: 1. replace that property 2. replace all the symbols referencing old property with 1. To do 2., you need to deep copy all the IR trees. (as references to symbols are mostly immutable) To copy all the IR trees, you need to update all the symbols EVERYWHERE. There was
IrElement.deepCopyIrWithSymbol
or something which I cannot remember the exact name..
c
Yes, there is such a function, but I don’t know how to start...
l
AFAIK, you need to 1. create a SymbolRemapper (let it be
r
) 2. Let root element
e
,
e.accept(r)
(as r itself is IrElementVisitor) 3. Create your class extending DeepCopyIrTreeWithSymbols. When you need a symbol, do not use the original symbol as-is, instead use symbol from
symbolRemapper.getDeclared***
(from declaration) or
symbolRemapper.getReferenced***
(from other). Start with copy-pasting corresponding functions from https://github.com/JetBrains/kotlin/blob/master/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt. 4. Transform ir element with 3. Note that all the metadatas will be lost. You need to copy it by your hands if needed. (don't know a lot about this)
I do not remember the exact method however...
c
Thank you for your inspiration, I found another way!
Copy code
super.visitPropertyNew(declaration.nonFinal())
Copy code
fun IrProperty.nonFinal() = deepCopyWithSymbols(parent) { symbolRemapper, typeRemapper ->
  object : DeepCopyIrTreeWithSymbols(symbolRemapper, typeRemapper) {
    override fun visitField(declaration: IrField): IrField = declaration.factory.createField(
        declaration.startOffset, declaration.endOffset,
        mapDeclarationOrigin(declaration.origin),
        symbolRemapper.getDeclaredField(declaration.symbol),
        declaration.symbol.owner.name,
        declaration.type.remapType(),
        declaration.visibility,
        isFinal = false,
        isExternal = declaration.isExternal,
        isStatic = declaration.isStatic,
      ).apply {
        transformAnnotations(declaration)
        annotations = declaration.annotations.transform()
        initializer = declaration.initializer?.transform()
      }
  }
}
l
Yes I guess deepCopyWithSymbols did the same thing under hood.
c
I have to complain. It’s very troublesome mind blown
m
hello!I tried your method but it doesn't work. Is this all the code to complete this work?
m
Also, why not just make `isVar`mutable?
p
I'm using reflection to change IrField::isVar and it works. It is not good solution i know, but I feel more safe than using copyWithSymbols. Also wonder why not it simpler to make it settable.