From 11b9a6eed267f22cc531c54af6f23c6d4e51a106 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 2 Dec 2021 10:53:52 +0300 Subject: [PATCH] [FIR] Implement simple serializer like declaration generator This generator is needed to test complex dependency between generated members and annotated classes in incremental compilation --- .../kotlin/fir/plugin/annotations.kt | 3 + .../plugin/FirAllOpenComponentRegistrar.kt | 1 + .../MembersOfSerializerGenerator.kt | 103 ++++++++++++++++++ .../fir/plugin/generators/generationUtils.kt | 9 ++ .../testData/box/serializer.fir.txt | 78 +++++++++++++ .../testData/box/serializer.kt | 40 +++++++ ...FirPluginBlackBoxCodegenTestGenerated.java | 6 + 7 files changed, 240 insertions(+) create mode 100644 plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/MembersOfSerializerGenerator.kt create mode 100644 plugins/fir-plugin-prototype/testData/box/serializer.fir.txt create mode 100644 plugins/fir-plugin-prototype/testData/box/serializer.kt diff --git a/plugins/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt b/plugins/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt index a7135d136e9..d30914a77ff 100644 --- a/plugins/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt +++ b/plugins/fir-plugin-prototype/plugin-annotations/src/org/jetbrains/kotlin/fir/plugin/annotations.kt @@ -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) diff --git a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt index 05c3b6af6ff..04841faa69a 100644 --- a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt +++ b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/FirAllOpenComponentRegistrar.kt @@ -22,5 +22,6 @@ class FirAllOpenComponentRegistrar : FirExtensionRegistrar() { +::ExternalClassGenerator +::AdditionalMembersGenerator +::CompanionGenerator + +::MembersOfSerializerGenerator } } diff --git a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/MembersOfSerializerGenerator.kt b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/MembersOfSerializerGenerator.kt new file mode 100644 index 00000000000..def34a583e6 --- /dev/null +++ b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/MembersOfSerializerGenerator.kt @@ -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() + } + private val serializableClassIds by lazy { + matchedSerializableClasses.map { it.classId } + } + + private val matchedCoreSerializerClasses by lazy { + predicateBasedProvider.getSymbolsByPredicate(CORE_SERIALIZER_PREDICATE).filterIsInstance() + } + + private val serializeMethodNames by lazy { + serializableClassIds.associateBy { Name.identifier("serialize${it.shortClassName.identifier}") } + } + + override fun generateFunctions(callableId: CallableId, owner: FirClassSymbol<*>?): List { + 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 { + return when (classSymbol) { + in matchedCoreSerializerClasses -> serializeMethodNames.keys + else -> emptySet() + } + } + + override fun FirDeclarationPredicateRegistrar.registerPredicates() { + register(SERIALIZABLE_PREDICATE, CORE_SERIALIZER_PREDICATE) + } + + object Key : FirPluginKey() +} diff --git a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/generationUtils.kt b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/generationUtils.kt index 5434fd131e6..ad1e6a3a2eb 100644 --- a/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/generationUtils.kt +++ b/plugins/fir-plugin-prototype/src/org/jetbrains/kotlin/fir/plugin/generators/generationUtils.kt @@ -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 + ) +} diff --git a/plugins/fir-plugin-prototype/testData/box/serializer.fir.txt b/plugins/fir-plugin-prototype/testData/box/serializer.fir.txt new file mode 100644 index 00000000000..a940167c1d4 --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/box/serializer.fir.txt @@ -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() + } + + 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() + } + + 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|org/jetbrains/kotlin/fir/plugin/MySerializable|() public final class B : R|kotlin/Any| { + public constructor(): R|B| { + super() + } + + } + @R|org/jetbrains/kotlin/fir/plugin/MySerializable|() public final class C : R|kotlin/Any| { + public constructor(): R|C| { + super() + } + + } + @R|org/jetbrains/kotlin/fir/plugin/MySerializable|() public final class D : R|kotlin/Any| { + public constructor(): R|D| { + super() + } + + } + 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) + } diff --git a/plugins/fir-plugin-prototype/testData/box/serializer.kt b/plugins/fir-plugin-prototype/testData/box/serializer.kt new file mode 100644 index 00000000000..ca5c4691b1f --- /dev/null +++ b/plugins/fir-plugin-prototype/testData/box/serializer.kt @@ -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" +} diff --git a/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginBlackBoxCodegenTestGenerated.java b/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginBlackBoxCodegenTestGenerated.java index f0aa200cea1..5f6ee58b1a7 100644 --- a/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginBlackBoxCodegenTestGenerated.java +++ b/plugins/fir-plugin-prototype/tests-gen/org/jetbrains/kotlin/fir/plugin/runners/FirPluginBlackBoxCodegenTestGenerated.java @@ -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 {