Rework access to properties in IR plugin
use accessors instead of fields whenever possible
This commit is contained in:
+34
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
@@ -175,8 +176,35 @@ interface IrBuilderExtension {
|
|||||||
|
|
||||||
fun KotlinType.toIrType() = compilerContext.typeTranslator.translateType(this)
|
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.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.
|
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)
|
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
|
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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+7
-6
@@ -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
|
package org.jetbrains.kotlinx.serialization.compiler.backend.ir
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
|
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
|
||||||
@@ -30,11 +35,7 @@ class SerializableIrGenerator(
|
|||||||
bindingContext: BindingContext
|
bindingContext: BindingContext
|
||||||
) : SerializableCodegen(irClass.descriptor, bindingContext), IrBuilderExtension {
|
) : 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 {
|
private fun IrClass.hasSerializableAnnotationWithoutArgs(): Boolean {
|
||||||
val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return false
|
val annot = getAnnotation(SerializationAnnotations.serializableAnnotationFqName) ?: return false
|
||||||
@@ -76,12 +77,12 @@ class SerializableIrGenerator(
|
|||||||
val prop = serializableProperties[index]
|
val prop = serializableProperties[index]
|
||||||
val paramRef = ctor.valueParameters[index + seenVarsOffset]
|
val paramRef = ctor.valueParameters[index + seenVarsOffset]
|
||||||
// assign this.a = a in else branch
|
// 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 ifNotSeenExpr: IrExpression = if (prop.optional) {
|
||||||
val initializerBody =
|
val initializerBody =
|
||||||
requireNotNull(transformFieldInitializer(prop.irField)) { "Optional value without an initializer" } // todo: filter abstract here
|
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 {
|
} else {
|
||||||
irThrow(irInvoke(null, exceptionCtorRef, irString(prop.name), typeHint = exceptionType))
|
irThrow(irInvoke(null, exceptionCtorRef, irString(prop.name), typeHint = exceptionType))
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-7
@@ -13,16 +13,20 @@ import org.jetbrains.kotlin.descriptors.*
|
|||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrAnonymousInitializerImpl
|
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.IrBranchImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
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.IrConstructorSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
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.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
@@ -137,8 +141,9 @@ open class SerializerIrGenerator(val irClass: IrClass, final override val compil
|
|||||||
if (classProp.transient) continue
|
if (classProp.transient) continue
|
||||||
+addFieldCall(classProp)
|
+addFieldCall(classProp)
|
||||||
// add property annotations
|
// add property annotations
|
||||||
|
val property = classProp.irProp
|
||||||
copySerialInfoAnnotationsToDescriptor(
|
copySerialInfoAnnotationsToDescriptor(
|
||||||
classProp.irField.correspondingPropertySymbol?.owner?.annotations.orEmpty(),
|
property.annotations,
|
||||||
localDescriptor,
|
localDescriptor,
|
||||||
serialDescImplClass.referenceMethod(CallingConventions.addAnnotation)
|
serialDescImplClass.referenceMethod(CallingConventions.addAnnotation)
|
||||||
)
|
)
|
||||||
@@ -245,16 +250,27 @@ open class SerializerIrGenerator(val irClass: IrClass, final override val compil
|
|||||||
val objectToSerialize = saveFunc.valueParameters[1]
|
val objectToSerialize = saveFunc.valueParameters[1]
|
||||||
val localOutput = irTemporary(call, "output")
|
val localOutput = irTemporary(call, "output")
|
||||||
|
|
||||||
fun SerializableProperty.irGet(): IrGetField {
|
fun SerializableProperty.irGet(): IrExpression {
|
||||||
val ownerType = objectToSerialize.symbol.owner.type
|
val ownerType = objectToSerialize.symbol.owner.type
|
||||||
return irGetField(
|
return getProperty(
|
||||||
irGet(
|
irGet(
|
||||||
type = ownerType,
|
type = ownerType,
|
||||||
variable = objectToSerialize.symbol
|
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?
|
// internal serialization via virtual calls?
|
||||||
for ((index, property) in serializableProperties.filter { !it.transient }.withIndex()) {
|
for ((index, property) in serializableProperties.filter { !it.transient }.withIndex()) {
|
||||||
// output.writeXxxElementValue(classDesc, index, value)
|
// 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)
|
val elementCall = irInvoke(irGet(localOutput), writeFunc, typeArguments = typeArgs, valueArguments = args)
|
||||||
|
|
||||||
// check for call to .shouldEncodeElementDefault
|
// check for call to .shouldEncodeElementDefault
|
||||||
if (!property.optional) {
|
if (!property.optional || index < ignoreIndexTo) {
|
||||||
// emit call right away
|
// emit call right away
|
||||||
+elementCall
|
+elementCall
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user