KT-4107 Data objects

This commit is contained in:
Pavel Mikhailovskii
2022-06-28 09:51:57 +02:00
parent 6c31dc90e4
commit c3c09aa95a
75 changed files with 1183 additions and 134 deletions
@@ -14,17 +14,13 @@ import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.isArray
import org.jetbrains.kotlin.ir.util.DataClassMembersGenerator
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.isPrimitiveArray
import org.jetbrains.kotlin.ir.util.*
class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irClass: IrClass, val origin: IrDeclarationOrigin) {
private fun IrClass.addSyntheticFunction(name: String, returnType: IrType) =
@@ -74,6 +70,7 @@ open class LoweringDataClassMemberGenerator(
IrGeneratorContextBase(backendContext.irBuiltIns),
backendContext.ir.symbols.externalSymbolTable,
irClass,
irClass.kotlinFqName,
origin,
forbidDirectFieldAccess
) {
@@ -353,6 +353,7 @@ private val jvmFilePhases = listOf(
enumClassPhase,
objectClassPhase,
readResolveForDataObjectsPhase,
staticInitializersPhase,
initializersPhase,
initializersCleanupPhase,
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2022 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.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.ir.addDispatchReceiver
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.builders.irGetField
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.fields
import org.jetbrains.kotlin.ir.util.getAllSuperclasses
import org.jetbrains.kotlin.ir.util.hasEqualFqName
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
internal val readResolveForDataObjectsPhase = makeIrFilePhase(
::ReadResolveForDataObjectsLowering,
name = "ReadResolveForDataObjectsLowering",
description = "Generate readResolve for serializable data objects"
)
private class ReadResolveForDataObjectsLowering(val context: JvmBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) {
if (!context.state.languageVersionSettings.supportsFeature(LanguageFeature.DataObjects)) return
if (!irClass.isData || irClass.kind != ClassKind.OBJECT || !irClass.isSerializable()) return
context.irFactory.buildFun {
name = Name.identifier("readResolve")
modality = Modality.FINAL
origin = IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER
returnType = context.irBuiltIns.anyType
visibility = DescriptorVisibilities.PRIVATE
}.apply {
addDispatchReceiver { type = irClass.defaultType }
parent = irClass
body = context.createJvmIrBuilder(symbol).run {
val instanceField = irClass.fields.single { it.name.asString() == JvmAbi.INSTANCE_FIELD }
irExprBody(irGetField(null, instanceField))
}
irClass.declarations.add(this)
}
}
}
private val SERIALIZABLE_FQ_NAME = FqName("java.io.Serializable")
private fun IrClass.isSerializable(): Boolean =
getAllSuperclasses().any { it.hasEqualFqName(SERIALIZABLE_FQ_NAME) }
@@ -74,8 +74,9 @@ class DataClassMembersGenerator(
val origin: IrDeclarationOrigin
) : DataClassMethodGenerator(ktClassOrObject, declarationGenerator.context.bindingContext) {
private val irDataClassMembersGenerator =
object : DataClassMembersGenerator(context, context.symbolTable, irClass, origin, generateBodies = generateBodies) {
private val irDataClassMembersGenerator = object : DataClassMembersGenerator(
context, context.symbolTable, irClass, ktClassOrObject.fqName, origin, generateBodies = generateBodies
) {
override fun declareSimpleFunction(startOffset: Int, endOffset: Int, functionDescriptor: FunctionDescriptor): IrFunction =
declareSimpleFunction(startOffset, endOffset, origin, functionDescriptor)
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
/**
@@ -32,6 +33,7 @@ abstract class DataClassMembersGenerator(
val context: IrGeneratorContext,
val symbolTable: ReferenceSymbolTable,
val irClass: IrClass,
val fqName: FqName?,
val origin: IrDeclarationOrigin,
val forbidDirectFieldAccess: Boolean = false,
val generateBodies: Boolean = false
@@ -139,9 +141,9 @@ abstract class DataClassMembersGenerator(
+irReturnTrue()
}
fun generateHashCodeMethodBody(properties: List<IrProperty>) {
fun generateHashCodeMethodBody(properties: List<IrProperty>, constHashCode: Int) {
if (properties.isEmpty()) {
+irReturn(irInt(0))
+irReturn(irInt(constHashCode))
return
} else if (properties.size == 1) {
+irReturn(getHashCodeOfProperty(properties[0]))
@@ -186,6 +188,10 @@ abstract class DataClassMembersGenerator(
}
fun generateToStringMethodBody(properties: List<IrProperty>) {
if (properties.isEmpty() && irClass.kind == ClassKind.OBJECT) {
+irReturn(irString(irClass.name.asString()))
return
}
val irConcat = irConcat()
irConcat.addArgument(irString(irClass.classNameForToString() + "("))
var first = true
@@ -259,6 +265,7 @@ abstract class DataClassMembersGenerator(
) {
MemberFunctionBuilder(startOffset, endOffset, declareSimpleFunction(startOffset, endOffset, function)).addToClass { irFunction ->
irFunction.buildWithScope {
irFunction.parent = irClass
generateSyntheticFunctionParameterDeclarations(irFunction)
body(irFunction)
}
@@ -344,14 +351,20 @@ abstract class DataClassMembersGenerator(
// Entry for psi2ir
fun generateHashCodeMethod(function: FunctionDescriptor, properties: List<PropertyDescriptor>) {
buildMember(function) {
generateHashCodeMethodBody(properties.map { getIrProperty(it) })
generateHashCodeMethodBody(
properties.map { getIrProperty(it) },
if (irClass.kind == ClassKind.OBJECT && irClass.isData) fqName.hashCode() else 0
)
}
}
// Entry for fir2ir
fun generateHashCodeMethod(irFunction: IrFunction, properties: List<IrProperty>) {
buildMember(irFunction) {
generateHashCodeMethodBody(properties)
generateHashCodeMethodBody(
properties,
if (irClass.kind == ClassKind.OBJECT && irClass.isData) fqName.hashCode() else 0
)
}
}