[FIR] Implement simple serializer like declaration generator

This generator is needed to test complex dependency between generated
  members and annotated classes in incremental compilation
This commit is contained in:
Dmitriy Novozhilov
2021-12-02 10:53:52 +03:00
committed by TeamCityServer
parent 8c11ea266a
commit 11b9a6eed2
7 changed files with 240 additions and 0 deletions
@@ -13,6 +13,9 @@ annotation class NestedClassAndMaterializeMember
annotation class MyInterfaceSupertype
annotation class CompanionWithFoo
annotation class MySerializable
annotation class CoreSerializer
annotation class AllPublic(val visibility: Visibility)
@Target(AnnotationTarget.TYPE)
@@ -22,5 +22,6 @@ class FirAllOpenComponentRegistrar : FirExtensionRegistrar() {
+::ExternalClassGenerator
+::AdditionalMembersGenerator
+::CompanionGenerator
+::MembersOfSerializerGenerator
}
}
@@ -0,0 +1,103 @@
/*
* Copyright 2010-2021 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.fir.plugin.generators
import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirPluginKey
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
import org.jetbrains.kotlin.fir.expressions.builder.buildBlock
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.predicate.has
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.plugin.fqn
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
/*
* For each class with @Serializable annotation generates method serializeClassName(x: ClassName)
* in each class annotated with @CoreSerializer
*/
class MembersOfSerializerGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {
companion object {
private val SERIALIZABLE_PREDICATE = has("MySerializable".fqn())
private val CORE_SERIALIZER_PREDICATE = has("CoreSerializer".fqn())
private val X_NAME = Name.identifier("x")
}
private val predicateBasedProvider = session.predicateBasedProvider
private val matchedSerializableClasses by lazy {
predicateBasedProvider.getSymbolsByPredicate(SERIALIZABLE_PREDICATE).filterIsInstance<FirRegularClassSymbol>()
}
private val serializableClassIds by lazy {
matchedSerializableClasses.map { it.classId }
}
private val matchedCoreSerializerClasses by lazy {
predicateBasedProvider.getSymbolsByPredicate(CORE_SERIALIZER_PREDICATE).filterIsInstance<FirRegularClassSymbol>()
}
private val serializeMethodNames by lazy {
serializableClassIds.associateBy { Name.identifier("serialize${it.shortClassName.identifier}") }
}
override fun generateFunctions(callableId: CallableId, owner: FirClassSymbol<*>?): List<FirNamedFunctionSymbol> {
val argumentClassId = serializeMethodNames[callableId.callableName] ?: return emptyList()
val dispatchReceiverClassId = callableId.classId ?: return emptyList()
val function = buildSimpleFunction {
moduleData = session.moduleData
resolvePhase = FirResolvePhase.BODY_RESOLVE
origin = Key.origin
status = FirResolvedDeclarationStatusImpl(Visibilities.Public, Modality.FINAL, EffectiveVisibility.Public)
returnTypeRef = session.builtinTypes.unitType
dispatchReceiverType = dispatchReceiverClassId.toSimpleConeType()
valueParameters += buildValueParameter {
moduleData = session.moduleData
resolvePhase = FirResolvePhase.BODY_RESOLVE
origin = Key.origin
returnTypeRef = buildResolvedTypeRef {
type = argumentClassId.toSimpleConeType()
}
name = X_NAME
symbol = FirValueParameterSymbol(name)
isCrossinline = false
isNoinline = false
isVararg = false
}
body = buildBlock {}
.apply { replaceTypeRef(session.builtinTypes.unitType) }
name = callableId.callableName
symbol = FirNamedFunctionSymbol(callableId)
}
return listOf(function.symbol)
}
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
return when (classSymbol) {
in matchedCoreSerializerClasses -> serializeMethodNames.keys
else -> emptySet()
}
}
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(SERIALIZABLE_PREDICATE, CORE_SERIALIZER_PREDICATE)
}
object Key : FirPluginKey()
}
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.name.CallableId
@@ -89,3 +90,11 @@ fun FirDeclarationGenerationExtension.buildConstructor(classId: ClassId, isInner
it.containingClassForStaticMemberAttr = lookupTag
}
}
fun ClassId.toSimpleConeType(): ConeClassLikeType {
return ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(this),
emptyArray(),
isNullable = false
)
}
@@ -0,0 +1,78 @@
FILE: serializer.kt
@R|org/jetbrains/kotlin/fir/plugin/CoreSerializer|() public final object FirstSerializer : R|kotlin/Any| {
private constructor(): R|FirstSerializer| {
super<R|kotlin/Any|>()
}
public final fun serializeA(x: R|A|): R|kotlin/Unit| {
}
public final fun serializeB(x: R|B|): R|kotlin/Unit| {
}
public final fun serializeC(x: R|C|): R|kotlin/Unit| {
}
public final fun serializeD(x: R|D|): R|kotlin/Unit| {
}
}
@R|org/jetbrains/kotlin/fir/plugin/CoreSerializer|() public final object SecondSerializer : R|kotlin/Any| {
private constructor(): R|SecondSerializer| {
super<R|kotlin/Any|>()
}
public final fun serializeA(x: R|A|): R|kotlin/Unit| {
}
public final fun serializeB(x: R|B|): R|kotlin/Unit| {
}
public final fun serializeC(x: R|C|): R|kotlin/Unit| {
}
public final fun serializeD(x: R|D|): R|kotlin/Unit| {
}
}
@R|org/jetbrains/kotlin/fir/plugin/MySerializable|() public final class A : R|kotlin/Any| {
public constructor(): R|A| {
super<R|kotlin/Any|>()
}
}
@R|org/jetbrains/kotlin/fir/plugin/MySerializable|() public final class B : R|kotlin/Any| {
public constructor(): R|B| {
super<R|kotlin/Any|>()
}
}
@R|org/jetbrains/kotlin/fir/plugin/MySerializable|() public final class C : R|kotlin/Any| {
public constructor(): R|C| {
super<R|kotlin/Any|>()
}
}
@R|org/jetbrains/kotlin/fir/plugin/MySerializable|() public final class D : R|kotlin/Any| {
public constructor(): R|D| {
super<R|kotlin/Any|>()
}
}
public final fun testFirstSerializer(): R|kotlin/Unit| {
Q|FirstSerializer|.R|/FirstSerializer.serializeA|(R|/A.A|())
Q|FirstSerializer|.R|/FirstSerializer.serializeB|(R|/B.B|())
Q|FirstSerializer|.R|/FirstSerializer.serializeC|(R|/C.C|())
Q|FirstSerializer|.R|/FirstSerializer.serializeD|(R|/D.D|())
}
public final fun testSecondSerializer(): R|kotlin/Unit| {
Q|SecondSerializer|.R|/SecondSerializer.serializeA|(R|/A.A|())
Q|SecondSerializer|.R|/SecondSerializer.serializeB|(R|/B.B|())
Q|SecondSerializer|.R|/SecondSerializer.serializeC|(R|/C.C|())
Q|SecondSerializer|.R|/SecondSerializer.serializeD|(R|/D.D|())
}
public final fun box(): R|kotlin/String| {
R|/testFirstSerializer|()
R|/testSecondSerializer|()
^box String(OK)
}
+40
View File
@@ -0,0 +1,40 @@
import org.jetbrains.kotlin.fir.plugin.CoreSerializer
import org.jetbrains.kotlin.fir.plugin.MySerializable
@CoreSerializer
object FirstSerializer
@CoreSerializer
object SecondSerializer
@MySerializable
class A
@MySerializable
class B
@MySerializable
class C
@MySerializable
class D
fun testFirstSerializer() {
FirstSerializer.serializeA(A())
FirstSerializer.serializeB(B())
FirstSerializer.serializeC(C())
FirstSerializer.serializeD(D())
}
fun testSecondSerializer() {
SecondSerializer.serializeA(A())
SecondSerializer.serializeB(B())
SecondSerializer.serializeC(C())
SecondSerializer.serializeD(D())
}
fun box(): String {
testFirstSerializer()
testSecondSerializer()
return "OK"
}
@@ -49,6 +49,12 @@ public class FirPluginBlackBoxCodegenTestGenerated extends AbstractFirPluginBlac
runTest("plugins/fir-plugin-prototype/testData/box/newSupertype.kt");
}
@Test
@TestMetadata("serializer.kt")
public void testSerializer() throws Exception {
runTest("plugins/fir-plugin-prototype/testData/box/serializer.kt");
}
@Test
@TestMetadata("topLevelCallables.kt")
public void testTopLevelCallables() throws Exception {