Rework access to properties in IR plugin

use accessors instead of fields whenever possible
This commit is contained in:
Leonid Startsev
2020-06-01 15:03:53 +03:00
parent 02bd8cbd6b
commit 1d589e3f56
3 changed files with 64 additions and 13 deletions
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
@@ -175,8 +176,35 @@ interface IrBuilderExtension {
fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this)
// note: this method should be used only for properties from current module. Fields from other modules are private and inaccessible.
val SerializableProperty.irField: IrField get() = compilerContext.symbolTable.referenceField(this.descriptor).owner
val SerializableProperty.irProp: IrProperty
get() {
val desc = this.descriptor
// this API is used to reference both current module descriptors and external ones (because serializable class can be in any of them),
// so we use descriptor api for current module because it is not possible to obtain FQname for e.g. local classes.
return if (desc.module == compilerContext.moduleDescriptor) {
compilerContext.symbolTable.referenceProperty(desc).owner
} else {
compilerContext.referenceProperties(this.descriptor.fqNameSafe).single().owner
}
}
fun IrBuilderWithScope.getProperty(receiver: IrExpression, property: IrProperty): IrExpression {
return if (property.getter != null)
irGet(property.getter!!.returnType, receiver, property.getter!!.symbol)
else
irGetField(receiver, property.backingField!!)
}
fun IrBuilderWithScope.setProperty(receiver: IrExpression, property: IrProperty, value: IrExpression): IrExpression {
return if (property.setter != null)
irSet(property.setter!!.returnType, receiver, property.setter!!.symbol, value)
else
irSetField(receiver, property.backingField!!, value)
}
/*
The rest of the file is mainly copied from FunctionGenerator.
However, I can't use it's directly because all generateSomething methods require KtProperty (psi element)
@@ -683,4 +711,10 @@ interface IrBuilderExtension {
return forClass.declarations.filterIsInstance<IrConstructor>().single { it.isSerializationCtor() }.symbol
}
fun IrClass.getSuperClassOrAny(): IrClass {
val superClasses = superTypes.mapNotNull { it.classOrNull }.map { it.owner }
return superClasses.singleOrNull { it.kind == ClassKind.CLASS } ?: compilerContext.irBuiltIns.anyClass.owner
}
}
@@ -1,3 +1,8 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
@@ -30,11 +35,7 @@ class SerializableIrGenerator(
bindingContext: BindingContext
) : SerializableCodegen(irClass.descriptor, bindingContext), IrBuilderExtension {
private fun IrClass.getSuperClassOrAny(): IrClass {
val superClasses = superTypes.mapNotNull { it.classOrNull }.map { it.owner }
return superClasses.singleOrNull { it.kind == ClassKind.CLASS } ?: compilerContext.irBuiltIns.anyClass.owner
}
private fun IrClass.hasSerializableAnnotationWithoutArgs(): Boolean {
val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return false
@@ -76,12 +77,12 @@ class SerializableIrGenerator(
val prop = serializableProperties[index]
val paramRef = ctor.valueParameters[index + seenVarsOffset]
// assign this.a = a in else branch
val assignParamExpr = irSetField(irGet(thiz), prop.irField, irGet(paramRef))
val assignParamExpr = setProperty(irGet(thiz), prop.irProp, irGet(paramRef))
val ifNotSeenExpr: IrExpression = if (prop.optional) {
val initializerBody =
requireNotNull(transformFieldInitializer(prop.irField)) { "Optional value without an initializer" } // todo: filter abstract here
irSetField(irGet(thiz), prop.irField, initializerBody)
setProperty(irGet(thiz), prop.irProp, initializerBody)
} else {
irThrow(irInvoke(null, exceptionCtorRef, irString(prop.name), typeHint = exceptionType))
}
@@ -13,16 +13,20 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrAnonymousInitializerImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.mapValueParametersIndexed
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.util.withReferenceScope
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.KotlinType
@@ -137,8 +141,9 @@ open class SerializerIrGenerator(val irClass: IrClass, final override val compil
if (classProp.transient) continue
+addFieldCall(classProp)
// add property annotations
val property = classProp.irProp
copySerialInfoAnnotationsToDescriptor(
classProp.irField.correspondingPropertySymbol?.owner?.annotations.orEmpty(),
property.annotations,
localDescriptor,
serialDescImplClass.referenceMethod(CallingConventions.addAnnotation)
)
@@ -245,16 +250,27 @@ open class SerializerIrGenerator(val irClass: IrClass, final override val compil
val objectToSerialize = saveFunc.valueParameters[1]
val localOutput = irTemporary(call, "output")
fun SerializableProperty.irGet(): IrGetField {
fun SerializableProperty.irGet(): IrExpression {
val ownerType = objectToSerialize.symbol.owner.type
return irGetField(
return getProperty(
irGet(
type = ownerType,
variable = objectToSerialize.symbol
), irField
), irProp
)
}
// Ignore comparing to default values of properties from superclass,
// because we do not have access to their fields (and initializers), if superclass is in another module.
// In future, IR analogue of JVM's write$Self should be implemented.
val superClass = irClass.getSuperClassOrAny().descriptor
val ignoreIndexTo = if (superClass.isInternalSerializable) {
bindingContext.serializablePropertiesFor(superClass).size
} else {
-1
}
// internal serialization via virtual calls?
for ((index, property) in serializableProperties.filter { !it.transient }.withIndex()) {
// output.writeXxxElementValue(classDesc, index, value)
@@ -287,7 +303,7 @@ open class SerializerIrGenerator(val irClass: IrClass, final override val compil
val elementCall = irInvoke(irGet(localOutput), writeFunc, typeArguments = typeArgs, valueArguments = args)
// check for call to .shouldEncodeElementDefault
if (!property.optional) {
if (!property.optional || index < ignoreIndexTo) {
// emit call right away
+elementCall
} else {