From 1d589e3f56b2370de3b42f7b74712299084a53f9 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Mon, 1 Jun 2020 15:03:53 +0300 Subject: [PATCH] Rework access to properties in IR plugin use accessors instead of fields whenever possible --- .../compiler/backend/ir/GeneratorHelpers.kt | 34 +++++++++++++++++++ .../backend/ir/SerializableIrGenerator.kt | 13 +++---- .../backend/ir/SerializerIrGenerator.kt | 30 ++++++++++++---- 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt index 806e0295802..e08bc587575 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -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().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 + } + } \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt index eb4de352671..84a3ba79baf 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt @@ -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)) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index 3102ebe4d7a..678da55896e 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -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 {