From eac9a9fc79a9427dd6a55ebe92e640e7f97b56dc Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Tue, 19 Oct 2021 16:46:42 +0300 Subject: [PATCH] [FIR] Implement FIR version of parcelize plugin This commit includes only generation of declarations and ir backend part, checkers are added in separate commit --- .../kotlin/generators/tests/GenerateTests.kt | 9 +- .../parcelize-compiler/build.gradle.kts | 20 + .../ParcelizeIrGeneratorExtension.kt | 8 + .../fir/FirParcelizeDeclarationGenerator.kt | 191 +++++++ .../fir/FirParcelizeExtensionRegistrar.kt | 14 + .../parcelize/fir/FirParcelizePluginKey.kt | 14 + .../parcelize/ir/ParcelizeFirIrTransformer.kt | 82 +++ .../parcelize/ir/ParcelizeIrTransformer.kt | 192 +------ .../ir/ParcelizeIrTransformerBase.kt | 197 ++++++++ .../testData/box/customParcelerScoping.kt | 2 + .../testData/box/customSerializerBoxing.kt | 4 +- .../testData/box/customSerializerSimple.kt | 4 +- .../testData/box/customSerializerWriteWith.kt | 2 + .../testData/box/kt46567.kt | 2 + .../runners/ParcelizeFirBoxTestGenerated.java | 477 ++++++++++++++++++ .../test/runners/AbstractParcelizeBoxTest.kt | 42 +- .../AbstractParcelizeBytecodeListingTest.kt | 3 +- .../ParcelizeEnvironmentConfigurator.kt | 13 +- 18 files changed, 1098 insertions(+), 178 deletions(-) create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeDeclarationGenerator.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizePluginKey.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeFirIrTransformer.kt create mode 100644 plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformerBase.kt create mode 100644 plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeFirBoxTestGenerated.java diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 169a3b06da0..14604060c0c 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -31,10 +31,7 @@ import org.jetbrains.kotlin.kapt3.test.AbstractIrKotlinKaptContextTest import org.jetbrains.kotlin.kapt3.test.AbstractKotlinKaptContextTest import org.jetbrains.kotlin.lombok.AbstractLombokCompileTest import org.jetbrains.kotlin.noarg.* -import org.jetbrains.kotlin.parcelize.test.runners.AbstractParcelizeBoxTest -import org.jetbrains.kotlin.parcelize.test.runners.AbstractParcelizeBytecodeListingTest -import org.jetbrains.kotlin.parcelize.test.runners.AbstractParcelizeIrBoxTest -import org.jetbrains.kotlin.parcelize.test.runners.AbstractParcelizeIrBytecodeListingTest +import org.jetbrains.kotlin.parcelize.test.runners.* import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest import org.jetbrains.kotlin.test.TargetBackend @@ -362,6 +359,10 @@ fun main(args: Array) { model("box") } + testClass { + model("box") + } + testClass { model("codegen") } diff --git a/plugins/parcelize/parcelize-compiler/build.gradle.kts b/plugins/parcelize/parcelize-compiler/build.gradle.kts index 332671e8293..70d6896415d 100644 --- a/plugins/parcelize/parcelize-compiler/build.gradle.kts +++ b/plugins/parcelize/parcelize-compiler/build.gradle.kts @@ -24,6 +24,17 @@ dependencies { compileOnly(intellijCoreDep()) { includeJars("intellij-core") } compileOnly(intellijDep()) { includeJars("asm-all", rootProject = rootProject) } + // FIR dependencies + compileOnly(project(":compiler:fir:cones")) + compileOnly(project(":compiler:fir:tree")) + compileOnly(project(":compiler:fir:resolve")) + compileOnly(project(":compiler:fir:checkers")) + compileOnly(project(":compiler:fir:fir2ir")) + compileOnly(project(":compiler:ir.backend.common")) + compileOnly(project(":compiler:ir.tree.impl")) + compileOnly(project(":compiler:fir:entrypoint")) + compileOnly(project(":kotlin-reflect-api")) + testApiJUnit5() testApi(project(":compiler:util")) @@ -39,6 +50,15 @@ dependencies { testApi(projectTests(":compiler:test-infrastructure")) testApi(projectTests(":compiler:test-infrastructure-utils")) + // FIR dependencies + testApi(project(":compiler:fir:checkers")) + testApi(project(":compiler:fir:checkers:checkers.jvm")) + testRuntimeOnly(project(":compiler:fir:fir-serialization")) + + testCompileOnly(project(":kotlin-reflect-api")) + testRuntimeOnly(project(":kotlin-reflect")) + testRuntimeOnly(project(":core:descriptors.runtime")) + testApi(commonDep("junit:junit")) testRuntimeOnly(intellijPluginDep("junit")) diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt index 2b44150a760..e1a2a388f8e 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.parcelize.ir.AndroidSymbols +import org.jetbrains.kotlin.parcelize.ir.ParcelizeFirIrTransformer import org.jetbrains.kotlin.parcelize.ir.ParcelizeIrTransformer class ParcelizeIrGeneratorExtension : IrGenerationExtension { @@ -17,3 +18,10 @@ class ParcelizeIrGeneratorExtension : IrGenerationExtension { ParcelizeIrTransformer(pluginContext, androidSymbols).transform(moduleFragment) } } + +class ParcelizeFirIrGeneratorExtension : IrGenerationExtension { + override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { + val androidSymbols = AndroidSymbols(pluginContext.irBuiltIns, moduleFragment) + ParcelizeFirIrTransformer(pluginContext, androidSymbols).transform(moduleFragment) + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeDeclarationGenerator.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeDeclarationGenerator.kt new file mode 100644 index 00000000000..6b24086745e --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeDeclarationGenerator.kt @@ -0,0 +1,191 @@ +/* + * 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.parcelize.fir + +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.analysis.checkers.toRegularClassSymbol +import org.jetbrains.kotlin.fir.declarations.FirPluginKey +import org.jetbrains.kotlin.fir.declarations.builder.FirSimpleFunctionBuilder +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.declarations.utils.modality +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.resolve.defaultType +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType +import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes +import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.classId +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.fir.types.isInt +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind +import org.jetbrains.kotlin.utils.addToStdlib.runIf + +class FirParcelizeDeclarationGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) { + companion object { + private val PARCELIZE_ID = ClassId(FqName("kotlinx.parcelize"), Name.identifier("Parcelize")) + private val OLD_PARCELIZE_ID = ClassId(FqName("kotlinx.android.parcel"), Name.identifier("Parcelize")) + private val PREDICATE = has(PARCELIZE_ID.asSingleFqName(), OLD_PARCELIZE_ID.asSingleFqName()) + + private val DESCRIBE_CONTENTS_NAME = Name.identifier(ComponentKind.DESCRIBE_CONTENTS.methodName) + private val WRITE_TO_PARCEL_NAME = Name.identifier(ComponentKind.WRITE_TO_PARCEL.methodName) + private val DEST_NAME = Name.identifier("dest") + + private val FLAGS_NAME = Name.identifier("flags") + + private val PARCEL_ID = ClassId(FqName("android.os"), Name.identifier("Parcel")) + private val parcelizeMethodsNames = setOf(DESCRIBE_CONTENTS_NAME, WRITE_TO_PARCEL_NAME) + } + + private val matchedClasses by lazy { + session.predicateBasedProvider.getSymbolsByPredicate(PREDICATE) + .filterIsInstance() + } + + override fun generateFunctions(callableId: CallableId, owner: FirClassSymbol<*>?): List { + if (owner == null) return emptyList() + require(owner is FirRegularClassSymbol) + val functionSymbol = when (callableId.callableName) { + DESCRIBE_CONTENTS_NAME -> { + val hasDescribeContentImplementation = owner.hasDescribeContentsImplementation() || + lookupSuperTypes(owner, lookupInterfaces = false, deep = true, session).any { + it.fullyExpandedType(session).toRegularClassSymbol(session)?.hasDescribeContentsImplementation() ?: false + } + runIf(!hasDescribeContentImplementation) { + generateDescribeContents(owner, callableId) + } + } + WRITE_TO_PARCEL_NAME -> { + val declaredFunctions = owner.declarationSymbols.filterIsInstance() + runIf(declaredFunctions.none { it.isWriteToParcel() }) { generateWriteToParcel(owner, callableId) } + } + else -> null + } + return listOfNotNull(functionSymbol) + } + + private fun FirRegularClassSymbol.hasDescribeContentsImplementation(): Boolean { + return declarationSymbols.filterIsInstance().any { it.isDescribeContentsImplementation() } + } + + private fun FirNamedFunctionSymbol.isDescribeContentsImplementation(): Boolean { + if (name != DESCRIBE_CONTENTS_NAME) return false + return valueParameterSymbols.isEmpty() + } + + private fun FirNamedFunctionSymbol.isWriteToParcel(): Boolean { + if (name != WRITE_TO_PARCEL_NAME) return false + val parameterSymbols = valueParameterSymbols + if (parameterSymbols.size != 2) return false + val (destSymbol, flagsSymbol) = parameterSymbols + if (destSymbol.resolvedReturnTypeRef.coneType.classId != PARCEL_ID) return false + if (!flagsSymbol.resolvedReturnTypeRef.type.isInt) return false + return true + } + + private fun generateDescribeContents(owner: FirRegularClassSymbol, callableId: CallableId): FirNamedFunctionSymbol { + return createFunction(owner, callableId) { + returnTypeRef = session.builtinTypes.intType + } + } + + private fun generateWriteToParcel(owner: FirRegularClassSymbol, callableId: CallableId): FirNamedFunctionSymbol { + return createFunction(owner, callableId) { + returnTypeRef = session.builtinTypes.unitType + + valueParameters += buildValueParameter { + moduleData = session.moduleData + origin = key.origin + name = DEST_NAME + returnTypeRef = buildResolvedTypeRef { + type = ConeClassLikeTypeImpl( + ConeClassLikeLookupTagImpl(PARCEL_ID), + emptyArray(), + isNullable = false + ) + } + symbol = FirValueParameterSymbol(name) + isCrossinline = false + isNoinline = false + isVararg = false + } + + valueParameters += buildValueParameter { + moduleData = session.moduleData + origin = key.origin + name = FLAGS_NAME + returnTypeRef = session.builtinTypes.intType + symbol = FirValueParameterSymbol(name) + isCrossinline = false + isNoinline = false + isVararg = false + } + } + } + + private inline fun createFunction( + owner: FirRegularClassSymbol, + callableId: CallableId, + init: FirSimpleFunctionBuilder.() -> Unit + ): FirNamedFunctionSymbol { + val function = buildSimpleFunction { + moduleData = session.moduleData + origin = key.origin + status = FirResolvedDeclarationStatusImpl( + Visibilities.Public, + if (owner.modality == Modality.FINAL) Modality.FINAL else Modality.OPEN, + EffectiveVisibility.Public + ).apply { + isOverride = true + } + name = callableId.callableName + symbol = FirNamedFunctionSymbol(callableId) + dispatchReceiverType = owner.defaultType() + init() + } + return function.symbol + } + + @OptIn(SymbolInternals::class) + override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set { + return when { + classSymbol.fir.modality == Modality.ABSTRACT -> emptySet() + classSymbol in matchedClasses && classSymbol.fir.modality != Modality.SEALED -> parcelizeMethodsNames + else -> { + val hasAnnotatedSealedSuperType = classSymbol.resolvedSuperTypeRefs.any { + val superSymbol = it.type.fullyExpandedType(session).toRegularClassSymbol(session) ?: return@any false + superSymbol.fir.modality == Modality.SEALED && superSymbol in matchedClasses + } + if (hasAnnotatedSealedSuperType) { + parcelizeMethodsNames + } else { + emptySet() + } + } + } + } + + override val key: FirPluginKey + get() = FirParcelizePluginKey + + override fun FirDeclarationPredicateRegistrar.registerPredicates() { + register(PREDICATE) + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt new file mode 100644 index 00000000000..58cff107abc --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizeExtensionRegistrar.kt @@ -0,0 +1,14 @@ +/* + * 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.parcelize.fir + +import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar + +class FirParcelizeExtensionRegistrar : FirExtensionRegistrar() { + override fun ExtensionRegistrarContext.configurePlugin() { + +::FirParcelizeDeclarationGenerator + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizePluginKey.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizePluginKey.kt new file mode 100644 index 00000000000..f7e023b2ffc --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/fir/FirParcelizePluginKey.kt @@ -0,0 +1,14 @@ +/* + * 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.parcelize.fir + +import org.jetbrains.kotlin.fir.declarations.FirPluginKey + +object FirParcelizePluginKey : FirPluginKey() { + override fun toString(): String { + return "FirParcelize" + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeFirIrTransformer.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeFirIrTransformer.kt new file mode 100644 index 00000000000..23777b5d2fe --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeFirIrTransformer.kt @@ -0,0 +1,82 @@ +/* + * 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.kotlin.parcelize.ir + +import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.fir.backend.IrPluginDeclarationOrigin +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.util.companionObject +import org.jetbrains.kotlin.ir.util.functions +import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.parcelize.PARCELER_FQNAME +import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent +import org.jetbrains.kotlin.parcelize.fir.FirParcelizePluginKey +import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase + +class ParcelizeFirIrTransformer( + context: IrPluginContext, + androidSymbols: AndroidSymbols +) : ParcelizeIrTransformerBase(context, androidSymbols) { + + fun transform(moduleFragment: IrModuleFragment) { + moduleFragment.accept(this, null) + deferredOperations.forEach { it() } + } + + override fun visitElement(element: IrElement) = element.acceptChildren(this, null) + + override fun visitClass(declaration: IrClass) { + declaration.acceptChildren(this, null) + + // Sealed classes can be annotated with `@Parcelize`, but that only implies that we + // should process their immediate subclasses. + if (!declaration.isParcelize || declaration.modality == Modality.SEALED) + return + + val parcelableProperties = declaration.parcelableProperties + + // If the companion extends Parceler, it can override parts of the generated implementation. + val parcelerObject = declaration.companionObject()?.takeIf { + it.isSubclassOfFqName(PARCELER_FQNAME.asString()) + } + + for (function in declaration.functions) { + val origin = function.origin + if (origin !is IrPluginDeclarationOrigin || origin.pluginKey != FirParcelizePluginKey) continue + when (function.name.identifier) { + ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS.methodName -> { + function.generateDescribeContentsBody(parcelableProperties) + } + ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL.methodName -> { + function.apply { + val receiverParameter = dispatchReceiverParameter!! + val (parcelParameter, flagsParameter) = function.valueParameters + + // We need to defer the construction of the writer, since it may refer to the [writeToParcel] methods in other + // @Parcelize classes in the current module, which might not be constructed yet at this point. + defer { + generateWriteToParcelBody( + declaration, + parcelerObject, + parcelableProperties, + receiverParameter, + parcelParameter, + flagsParameter + ) + } + } + } + else -> error("Generated declaration with unknown name: ${function.render()}") + } + } + + generateCreator(declaration, parcelerObject, parcelableProperties) + } +} diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformer.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformer.kt index ce4046fac56..e88adc6e5a6 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformer.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformer.kt @@ -6,54 +6,42 @@ package org.jetbrains.kotlin.parcelize.ir import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext -import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor -import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder -import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI -import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.builders.declarations.* -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl +import org.jetbrains.kotlin.ir.builders.declarations.addFunction +import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter +import org.jetbrains.kotlin.ir.declarations.DescriptorMetadataSource +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionReference import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.util.companionObject +import org.jetbrains.kotlin.ir.util.copyTypeAndValueArgumentsFrom +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.ir.util.functions import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.parcelize.ANDROID_PARCELABLE_CLASS_FQNAME import org.jetbrains.kotlin.parcelize.PARCELER_FQNAME import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent -import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase import org.jetbrains.kotlin.utils.addToStdlib.safeAs @OptIn(ObsoleteDescriptorBasedAPI::class) -class ParcelizeIrTransformer(private val context: IrPluginContext, private val androidSymbols: AndroidSymbols) : - ParcelizeExtensionBase, IrElementVisitorVoid { - private val serializerFactory = IrParcelSerializerFactory(androidSymbols) - - private val deferredOperations = mutableListOf<() -> Unit>() - private fun defer(block: () -> Unit) = deferredOperations.add(block) - - private fun IrPluginContext.createIrBuilder(symbol: IrSymbol) = - DeclarationIrBuilder(this, symbol, symbol.owner.startOffset, symbol.owner.endOffset) - +class ParcelizeIrTransformer( + context: IrPluginContext, + androidSymbols: AndroidSymbols +) : ParcelizeIrTransformerBase(context, androidSymbols) { private val symbolMap = mutableMapOf() - private val irFactory: IrFactory = IrFactoryImpl - fun transform(moduleFragment: IrModuleFragment) { moduleFragment.accept(this, null) deferredOperations.forEach { it() } @@ -142,10 +130,7 @@ class ParcelizeIrTransformer(private val context: IrPluginContext, private val a context.irBuiltIns.intType, modality = Modality.OPEN ).apply { - val flags = if (parcelableProperties.any { it.field.type.containsFileDescriptors }) 1 else 0 - body = context.createIrBuilder(symbol).run { - irExprBody(irInt(flags)) - } + generateDescribeContentsBody(parcelableProperties) metadata = DescriptorMetadataSource.Function( declaration.descriptor.findFunction(ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS)!! @@ -174,32 +159,14 @@ class ParcelizeIrTransformer(private val context: IrPluginContext, private val a // We need to defer the construction of the writer, since it may refer to the [writeToParcel] methods in other // @Parcelize classes in the current module, which might not be constructed yet at this point. defer { - body = androidSymbols.createBuilder(symbol).run { - irBlockBody { - when { - parcelerObject != null -> - +parcelerWrite(parcelerObject, parcelParameter, flagsParameter, irGet(receiverParameter)) - - parcelableProperties.isNotEmpty() -> - for (property in parcelableProperties) { - +writeParcelWith( - property.parceler, - parcelParameter, - flagsParameter, - irGetField(irGet(receiverParameter), property.field) - ) - } - - else -> - +writeParcelWith( - declaration.classParceler, - parcelParameter, - flagsParameter, - irGet(receiverParameter) - ) - } - } - } + generateWriteToParcelBody( + declaration, + parcelerObject, + parcelableProperties, + receiverParameter, + parcelParameter, + flagsParameter + ) } metadata = DescriptorMetadataSource.Function( @@ -215,85 +182,8 @@ class ParcelizeIrTransformer(private val context: IrPluginContext, private val a } } - // Since the `CREATOR` object cannot refer to the type parameters of the parcelable class we use a star projected type - val declarationType = declaration.symbol.starProjectedType - val creatorType = androidSymbols.androidOsParcelableCreator.typeWith(declarationType) - if (!declaration.descriptor.hasCreatorField()) { - declaration.addField { - name = ParcelizeExtensionBase.CREATOR_NAME - type = creatorType - isStatic = true - isFinal = true - }.apply { - val irField = this - val creatorClass = irFactory.buildClass { - name = Name.identifier("Creator") - visibility = DescriptorVisibilities.LOCAL - }.apply { - parent = irField - superTypes = listOf(creatorType) - createImplicitParameterDeclarationWithWrappedDescriptor() - - addConstructor { - isPrimary = true - }.apply { - body = context.createIrBuilder(symbol).irBlockBody { - +irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single()) - } - } - - val arrayType = context.irBuiltIns.arrayClass.typeWith(declarationType.makeNullable()) - addFunction("newArray", arrayType).apply { - overriddenSymbols = listOf(androidSymbols.androidOsParcelableCreator.getSimpleFunction(name.asString())!!) - val sizeParameter = addValueParameter("size", context.irBuiltIns.intType) - body = context.createIrBuilder(symbol).run { - irExprBody( - parcelerNewArray(parcelerObject, sizeParameter) - ?: irCall(context.irBuiltIns.arrayOfNulls, arrayType).apply { - putTypeArgument(0, arrayType) - putValueArgument(0, irGet(sizeParameter)) - } - ) - } - } - - addFunction("createFromParcel", declarationType).apply { - overriddenSymbols = listOf(androidSymbols.androidOsParcelableCreator.getSimpleFunction(name.asString())!!) - val parcelParameter = addValueParameter("parcel", androidSymbols.androidOsParcel.defaultType) - - // We need to defer the construction of the create method, since it may refer to the [Parcelable.Creator] - // instances in other @Parcelize classes in the current module, which may not exist yet. - defer { - body = androidSymbols.createBuilder(symbol).run { - irExprBody( - when { - parcelerObject != null -> - parcelerCreate(parcelerObject, parcelParameter) - - parcelableProperties.isNotEmpty() -> - irCall(declaration.primaryConstructor!!).apply { - for ((index, property) in parcelableProperties.withIndex()) { - putValueArgument(index, readParcelWith(property.parceler, parcelParameter)) - } - } - - else -> - readParcelWith(declaration.classParceler, parcelParameter) - } - ) - } - } - } - } - - initializer = context.createIrBuilder(symbol).run { - irExprBody(irBlock { - +creatorClass - +irCall(creatorClass.primaryConstructor!!) - }) - } - } + generateCreator(declaration, parcelerObject, parcelableProperties) } } @@ -311,38 +201,4 @@ class ParcelizeIrTransformer(private val context: IrPluginContext, private val a }.map { it.symbol }.toList() } } - - private class ParcelableProperty(val field: IrField, parcelerThunk: () -> IrParcelSerializer) { - val parceler by lazy(parcelerThunk) - } - - private val IrClass.classParceler: IrParcelSerializer - get() = if (kind == ClassKind.CLASS) { - IrNoParameterClassParcelSerializer(this) - } else { - serializerFactory.get(defaultType, parcelizeType = defaultType, strict = true, toplevel = true, scope = getParcelerScope()) - } - - private val IrClass.parcelableProperties: List - get() { - if (kind != ClassKind.CLASS) return emptyList() - - val constructor = primaryConstructor ?: return emptyList() - val topLevelScope = getParcelerScope() - - return constructor.valueParameters.map { parameter -> - val property = properties.first { it.name == parameter.name } - val localScope = property.getParcelerScope(topLevelScope) - ParcelableProperty(property.backingField!!) { - serializerFactory.get(parameter.type, parcelizeType = defaultType, scope = localScope) - } - } - } - - // *Heuristic* to determine if a Parcelable contains file descriptors. - private val IrType.containsFileDescriptors: Boolean - get() = erasedUpperBound.fqNameWhenAvailable == ParcelizeExtensionBase.FILE_DESCRIPTOR_FQNAME || - (this as? IrSimpleType)?.arguments?.any { argument -> - argument.typeOrNull?.containsFileDescriptors == true - } == true } diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformerBase.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformerBase.kt new file mode 100644 index 00000000000..8ca3b80caab --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformerBase.kt @@ -0,0 +1,197 @@ +/* + * 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.parcelize.ir + +import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext +import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor +import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder +import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.builders.declarations.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase + +abstract class ParcelizeIrTransformerBase( + protected val context: IrPluginContext, + protected val androidSymbols: AndroidSymbols +) : ParcelizeExtensionBase, IrElementVisitorVoid { + private val irFactory: IrFactory = IrFactoryImpl + + protected val deferredOperations = mutableListOf<() -> Unit>() + protected fun defer(block: () -> Unit) = deferredOperations.add(block) + + protected fun IrSimpleFunction.generateDescribeContentsBody(parcelableProperties: List) { + val flags = if (parcelableProperties.any { it.field.type.containsFileDescriptors }) 1 else 0 + body = context.createIrBuilder(symbol).run { + irExprBody(irInt(flags)) + } + } + + protected fun IrSimpleFunction.generateWriteToParcelBody( + irClass: IrClass, + parcelerObject: IrClass?, + parcelableProperties: List, + receiverParameter: IrValueParameter, + parcelParameter: IrValueParameter, + flagsParameter: IrValueParameter + ) { + body = androidSymbols.createBuilder(symbol).run { + irBlockBody { + when { + parcelerObject != null -> + +parcelerWrite(parcelerObject, parcelParameter, flagsParameter, irGet(receiverParameter)) + + parcelableProperties.isNotEmpty() -> + for (property in parcelableProperties) { + +writeParcelWith( + property.parceler, + parcelParameter, + flagsParameter, + irGetField(irGet(receiverParameter), property.field) + ) + } + + else -> + +writeParcelWith( + irClass.classParceler, + parcelParameter, + flagsParameter, + irGet(receiverParameter) + ) + } + } + } + } + + protected fun generateCreator(declaration: IrClass, parcelerObject: IrClass?, parcelableProperties: List) { + // Since the `CREATOR` object cannot refer to the type parameters of the parcelable class we use a star projected type + val declarationType = declaration.symbol.starProjectedType + val creatorType = androidSymbols.androidOsParcelableCreator.typeWith(declarationType) + + declaration.addField { + name = ParcelizeExtensionBase.CREATOR_NAME + type = creatorType + isStatic = true + isFinal = true + }.apply { + val irField = this + val creatorClass = irFactory.buildClass { + name = Name.identifier("Creator") + visibility = DescriptorVisibilities.LOCAL + }.apply { + parent = irField + superTypes = listOf(creatorType) + createImplicitParameterDeclarationWithWrappedDescriptor() + + addConstructor { + isPrimary = true + }.apply { + body = context.createIrBuilder(symbol).irBlockBody { + +irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single()) + } + } + + val arrayType = context.irBuiltIns.arrayClass.typeWith(declarationType.makeNullable()) + addFunction("newArray", arrayType).apply { + overriddenSymbols = listOf(androidSymbols.androidOsParcelableCreator.getSimpleFunction(name.asString())!!) + val sizeParameter = addValueParameter("size", context.irBuiltIns.intType) + body = context.createIrBuilder(symbol).run { + irExprBody( + parcelerNewArray(parcelerObject, sizeParameter) + ?: irCall(context.irBuiltIns.arrayOfNulls, arrayType).apply { + putTypeArgument(0, arrayType) + putValueArgument(0, irGet(sizeParameter)) + } + ) + } + } + + addFunction("createFromParcel", declarationType).apply { + overriddenSymbols = listOf(androidSymbols.androidOsParcelableCreator.getSimpleFunction(name.asString())!!) + val parcelParameter = addValueParameter("parcel", androidSymbols.androidOsParcel.defaultType) + + // We need to defer the construction of the create method, since it may refer to the [Parcelable.Creator] + // instances in other @Parcelize classes in the current module, which may not exist yet. + defer { + body = androidSymbols.createBuilder(symbol).run { + irExprBody( + when { + parcelerObject != null -> + parcelerCreate(parcelerObject, parcelParameter) + + parcelableProperties.isNotEmpty() -> + irCall(declaration.primaryConstructor!!).apply { + for ((index, property) in parcelableProperties.withIndex()) { + putValueArgument(index, readParcelWith(property.parceler, parcelParameter)) + } + } + + else -> + readParcelWith(declaration.classParceler, parcelParameter) + } + ) + } + } + } + } + + initializer = context.createIrBuilder(symbol).run { + irExprBody(irBlock { + +creatorClass + +irCall(creatorClass.primaryConstructor!!) + }) + } + } + } + + private val IrClass.classParceler: IrParcelSerializer + get() = if (kind == ClassKind.CLASS) { + IrNoParameterClassParcelSerializer(this) + } else { + serializerFactory.get(defaultType, parcelizeType = defaultType, strict = true, toplevel = true, scope = getParcelerScope()) + } + + protected class ParcelableProperty(val field: IrField, parcelerThunk: () -> IrParcelSerializer) { + val parceler by lazy(parcelerThunk) + } + + private val serializerFactory = IrParcelSerializerFactory(androidSymbols) + + protected val IrClass.parcelableProperties: List + get() { + if (kind != ClassKind.CLASS) return emptyList() + + val constructor = primaryConstructor ?: return emptyList() + val topLevelScope = getParcelerScope() + + return constructor.valueParameters.map { parameter -> + val property = properties.first { it.name == parameter.name } + val localScope = property.getParcelerScope(topLevelScope) + ParcelableProperty(property.backingField!!) { + serializerFactory.get(parameter.type, parcelizeType = defaultType, scope = localScope) + } + } + } + + // *Heuristic* to determine if a Parcelable contains file descriptors. + private val IrType.containsFileDescriptors: Boolean + get() = erasedUpperBound.fqNameWhenAvailable == ParcelizeExtensionBase.FILE_DESCRIPTOR_FQNAME || + (this as? IrSimpleType)?.arguments?.any { argument -> + argument.typeOrNull?.containsFileDescriptors == true + } == true + + private fun IrPluginContext.createIrBuilder(symbol: IrSymbol): DeclarationIrBuilder { + return DeclarationIrBuilder(this, symbol, symbol.owner.startOffset, symbol.owner.endOffset) + } +} diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customParcelerScoping.kt b/plugins/parcelize/parcelize-compiler/testData/box/customParcelerScoping.kt index 81df3500cde..a7513aa8e9f 100644 --- a/plugins/parcelize/parcelize-compiler/testData/box/customParcelerScoping.kt +++ b/plugins/parcelize/parcelize-compiler/testData/box/customParcelerScoping.kt @@ -1,4 +1,6 @@ // WITH_STDLIB +// fir doesn't support annotations with type arguments +// IGNORE_BACKEND_FIR: JVM_IR @file:JvmName("TestKt") package test diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerBoxing.kt b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerBoxing.kt index 5e360f2df35..28304dfdda3 100644 --- a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerBoxing.kt +++ b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerBoxing.kt @@ -1,4 +1,6 @@ // WITH_STDLIB +// fir doesn't support annotations with type arguments +// IGNORE_BACKEND_FIR: JVM_IR @file:JvmName("TestKt") package test @@ -53,4 +55,4 @@ fun box() = parcelTest { parcel -> with (test2) { assert(a == 5 && b == -5 && c == 3L && d == listOf(-1, -2, -3) && Arrays.equals(e, longArrayOf(3, 3, 3))) } -} \ No newline at end of file +} diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt index fe37fef4329..2bffb874ed4 100644 --- a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt +++ b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt @@ -1,4 +1,6 @@ // WITH_STDLIB +// fir doesn't support annotations with type arguments +// IGNORE_BACKEND_FIR: JVM_IR @file:JvmName("TestKt") package test @@ -46,4 +48,4 @@ fun box() = parcelTest { parcel -> assert(test.a == "Abc" && test.b == "Abc" && test.c == "Abc" && test.d == "Abc") assert(test2.a == "3" && test2.b == "3" && test2.c == "Abc" && test2.d == "ABC") -} \ No newline at end of file +} diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerWriteWith.kt b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerWriteWith.kt index 365f7636e48..2a7565c39b5 100644 --- a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerWriteWith.kt +++ b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerWriteWith.kt @@ -2,6 +2,8 @@ // See KT-38107 // The JVM backend is missing support for custom parcelers in List // WITH_STDLIB +// fir doesn't support annotations with type arguments +// IGNORE_BACKEND_FIR: JVM_IR @file:JvmName("TestKt") package test diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt46567.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt46567.kt index 1800221f2e6..49796863122 100644 --- a/plugins/parcelize/parcelize-compiler/testData/box/kt46567.kt +++ b/plugins/parcelize/parcelize-compiler/testData/box/kt46567.kt @@ -1,4 +1,6 @@ // WITH_STDLIB +// fir doesn't support annotations with type arguments +// IGNORE_BACKEND_FIR: JVM_IR @file:JvmName("TestKt") package test diff --git a/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeFirBoxTestGenerated.java b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeFirBoxTestGenerated.java new file mode 100644 index 00000000000..1a316416127 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/tests-gen/org/jetbrains/kotlin/parcelize/test/runners/ParcelizeFirBoxTestGenerated.java @@ -0,0 +1,477 @@ +/* + * 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.parcelize.test.runners; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/parcelize/parcelize-compiler/testData/box") +@TestDataPath("$PROJECT_ROOT") +public class ParcelizeFirBoxTestGenerated extends AbstractParcelizeFirBoxTest { + @Test + public void testAllFilesPresentInBox() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/parcelize/parcelize-compiler/testData/box"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("allPrimitiveTypes.kt") + public void testAllPrimitiveTypes() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/allPrimitiveTypes.kt"); + } + + @Test + @TestMetadata("allUnsignedTypes.kt") + public void testAllUnsignedTypes() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/allUnsignedTypes.kt"); + } + + @Test + @TestMetadata("arraySimple.kt") + public void testArraySimple() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/arraySimple.kt"); + } + + @Test + @TestMetadata("arrays.kt") + public void testArrays() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/arrays.kt"); + } + + @Test + @TestMetadata("binder.kt") + public void testBinder() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/binder.kt"); + } + + @Test + @TestMetadata("boxedTypes.kt") + public void testBoxedTypes() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/boxedTypes.kt"); + } + + @Test + @TestMetadata("bundle.kt") + public void testBundle() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/bundle.kt"); + } + + @Test + @TestMetadata("charSequence.kt") + public void testCharSequence() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/charSequence.kt"); + } + + @Test + @TestMetadata("customNewArray.kt") + public void testCustomNewArray() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/customNewArray.kt"); + } + + @Test + @TestMetadata("customParcelable.kt") + public void testCustomParcelable() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/customParcelable.kt"); + } + + @Test + @TestMetadata("customParcelerScoping.kt") + public void testCustomParcelerScoping() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/customParcelerScoping.kt"); + } + + @Test + @TestMetadata("customSerializerBoxing.kt") + public void testCustomSerializerBoxing() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/customSerializerBoxing.kt"); + } + + @Test + @TestMetadata("customSerializerSimple.kt") + public void testCustomSerializerSimple() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt"); + } + + @Test + @TestMetadata("customSerializerWriteWith.kt") + public void testCustomSerializerWriteWith() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/customSerializerWriteWith.kt"); + } + + @Test + @TestMetadata("customSimple.kt") + public void testCustomSimple() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/customSimple.kt"); + } + + @Test + @TestMetadata("enumObject.kt") + public void testEnumObject() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/enumObject.kt"); + } + + @Test + @TestMetadata("enums.kt") + public void testEnums() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/enums.kt"); + } + + @Test + @TestMetadata("exceptions.kt") + public void testExceptions() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/exceptions.kt"); + } + + @Test + @TestMetadata("functions.kt") + public void testFunctions() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/functions.kt"); + } + + @Test + @TestMetadata("genericParcelable.kt") + public void testGenericParcelable() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/genericParcelable.kt"); + } + + @Test + @TestMetadata("generics.kt") + public void testGenerics() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/generics.kt"); + } + + @Test + @TestMetadata("intArray.kt") + public void testIntArray() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/intArray.kt"); + } + + @Test + @TestMetadata("javaInterop.kt") + public void testJavaInterop() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/javaInterop.kt"); + } + + @Test + @TestMetadata("kt19747.kt") + public void testKt19747() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt19747.kt"); + } + + @Test + @TestMetadata("kt19747Deprecated.kt") + public void testKt19747Deprecated() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt19747Deprecated.kt"); + } + + @Test + @TestMetadata("kt19747_2.kt") + public void testKt19747_2() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt19747_2.kt"); + } + + @Test + @TestMetadata("kt19749.kt") + public void testKt19749() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt19749.kt"); + } + + @Test + @TestMetadata("kt19853.kt") + public void testKt19853() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt19853.kt"); + } + + @Test + @TestMetadata("kt20002.kt") + public void testKt20002() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt20002.kt"); + } + + @Test + @TestMetadata("kt20021.kt") + public void testKt20021() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt20021.kt"); + } + + @Test + @TestMetadata("kt20717.kt") + public void testKt20717() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt20717.kt"); + } + + @Test + @TestMetadata("kt25839.kt") + public void testKt25839() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt25839.kt"); + } + + @Test + @TestMetadata("kt26221.kt") + public void testKt26221() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt26221.kt"); + } + + @Test + @TestMetadata("kt36658.kt") + public void testKt36658() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt36658.kt"); + } + + @Test + @TestMetadata("kt39981.kt") + public void testKt39981() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt39981.kt"); + } + + @Test + @TestMetadata("kt41553.kt") + public void testKt41553() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt41553.kt"); + } + + @Test + @TestMetadata("kt41553_2.kt") + public void testKt41553_2() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt41553_2.kt"); + } + + @Test + @TestMetadata("kt46567.kt") + public void testKt46567() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/kt46567.kt"); + } + + @Test + @TestMetadata("listKinds.kt") + public void testListKinds() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/listKinds.kt"); + } + + @Test + @TestMetadata("listSimple.kt") + public void testListSimple() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/listSimple.kt"); + } + + @Test + @TestMetadata("lists.kt") + public void testLists() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/lists.kt"); + } + + @Test + @TestMetadata("mapKinds.kt") + public void testMapKinds() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/mapKinds.kt"); + } + + @Test + @TestMetadata("mapSimple.kt") + public void testMapSimple() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/mapSimple.kt"); + } + + @Test + @TestMetadata("maps.kt") + public void testMaps() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/maps.kt"); + } + + @Test + @TestMetadata("nestedArrays.kt") + public void testNestedArrays() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/nestedArrays.kt"); + } + + @Test + @TestMetadata("nestedLists.kt") + public void testNestedLists() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/nestedLists.kt"); + } + + @Test + @TestMetadata("nestedMaps.kt") + public void testNestedMaps() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/nestedMaps.kt"); + } + + @Test + @TestMetadata("nestedParcelable.kt") + public void testNestedParcelable() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/nestedParcelable.kt"); + } + + @Test + @TestMetadata("nestedSparseArrays.kt") + public void testNestedSparseArrays() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/nestedSparseArrays.kt"); + } + + @Test + @TestMetadata("nestedSparseArrays2.kt") + public void testNestedSparseArrays2() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/nestedSparseArrays2.kt"); + } + + @Test + @TestMetadata("newArray.kt") + public void testNewArray() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/newArray.kt"); + } + + @Test + @TestMetadata("newArrayParceler.kt") + public void testNewArrayParceler() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/newArrayParceler.kt"); + } + + @Test + @TestMetadata("nullableTypes.kt") + public void testNullableTypes() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/nullableTypes.kt"); + } + + @Test + @TestMetadata("nullableTypesSimple.kt") + public void testNullableTypesSimple() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/nullableTypesSimple.kt"); + } + + @Test + @TestMetadata("objectWriteParcelable.kt") + public void testObjectWriteParcelable() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/objectWriteParcelable.kt"); + } + + @Test + @TestMetadata("objects.kt") + public void testObjects() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/objects.kt"); + } + + @Test + @TestMetadata("openParcelize.kt") + public void testOpenParcelize() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/openParcelize.kt"); + } + + @Test + @TestMetadata("parcelableValueClass.kt") + public void testParcelableValueClass() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/parcelableValueClass.kt"); + } + + @Test + @TestMetadata("parcelizeCustomValueClass.kt") + public void testParcelizeCustomValueClass() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/parcelizeCustomValueClass.kt"); + } + + @Test + @TestMetadata("persistableBundle.kt") + public void testPersistableBundle() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/persistableBundle.kt"); + } + + @Test + @TestMetadata("primitiveTypes.kt") + public void testPrimitiveTypes() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/primitiveTypes.kt"); + } + + @Test + @TestMetadata("privateConstructor.kt") + public void testPrivateConstructor() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/privateConstructor.kt"); + } + + @Test + @TestMetadata("recursiveGenerics.kt") + public void testRecursiveGenerics() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/recursiveGenerics.kt"); + } + + @Test + @TestMetadata("sealedClass.kt") + public void testSealedClass() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/sealedClass.kt"); + } + + @Test + @TestMetadata("sealedClass2.kt") + public void testSealedClass2() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/sealedClass2.kt"); + } + + @Test + @TestMetadata("sealedInterface.kt") + public void testSealedInterface() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/sealedInterface.kt"); + } + + @Test + @TestMetadata("shortArray.kt") + public void testShortArray() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/shortArray.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/simple.kt"); + } + + @Test + @TestMetadata("simpleDeprecated.kt") + public void testSimpleDeprecated() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/simpleDeprecated.kt"); + } + + @Test + @TestMetadata("sparseArrays.kt") + public void testSparseArrays() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/sparseArrays.kt"); + } + + @Test + @TestMetadata("sparseBooleanArray.kt") + public void testSparseBooleanArray() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/sparseBooleanArray.kt"); + } + + @Test + @TestMetadata("typeParameters.kt") + public void testTypeParameters() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/typeParameters.kt"); + } + + @Test + @TestMetadata("unsignedArrays.kt") + public void testUnsignedArrays() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/unsignedArrays.kt"); + } + + @Test + @TestMetadata("valueClassWrapper.kt") + public void testValueClassWrapper() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/valueClassWrapper.kt"); + } + + @Test + @TestMetadata("valueClasses.kt") + public void testValueClasses() throws Exception { + runTest("plugins/parcelize/parcelize-compiler/testData/box/valueClasses.kt"); + } +} diff --git a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBoxTest.kt b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBoxTest.kt index ebba8c3bbe3..f9dce526ec9 100644 --- a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBoxTest.kt +++ b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBoxTest.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.parcelize.test.runners +import org.jetbrains.kotlin.parcelize.fir.FirParcelizeExtensionRegistrar import org.jetbrains.kotlin.parcelize.test.services.ParcelizeEnvironmentConfigurator import org.jetbrains.kotlin.parcelize.test.services.ParcelizeMainClassProvider import org.jetbrains.kotlin.parcelize.test.services.ParcelizeRuntimeClasspathProvider @@ -14,19 +15,26 @@ import org.jetbrains.kotlin.test.TargetBackend import org.jetbrains.kotlin.test.backend.BlackBoxCodegenSuppressor import org.jetbrains.kotlin.test.backend.classic.ClassicBackendInput import org.jetbrains.kotlin.test.backend.classic.ClassicJvmBackendFacade +import org.jetbrains.kotlin.test.backend.handlers.IrTextDumpHandler import org.jetbrains.kotlin.test.backend.ir.IrBackendInput import org.jetbrains.kotlin.test.backend.ir.JvmIrBackendFacade +import org.jetbrains.kotlin.test.bind +import org.jetbrains.kotlin.test.builders.* import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder import org.jetbrains.kotlin.test.builders.configureClassicFrontendHandlersStep import org.jetbrains.kotlin.test.builders.configureFirHandlersStep import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.REQUIRES_SEPARATE_PROCESS import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.DIAGNOSTICS import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.REPORT_ONLY_EXPLICITLY_DEFINED_DEBUG_INFO +import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.ENABLE_PLUGIN_PHASES import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontend2ClassicBackendConverter import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontend2IrConverter import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendFacade import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact import org.jetbrains.kotlin.test.frontend.classic.handlers.ClassicDiagnosticsHandler +import org.jetbrains.kotlin.test.frontend.fir.Fir2IrResultsConverter +import org.jetbrains.kotlin.test.frontend.fir.FirFrontendFacade +import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact import org.jetbrains.kotlin.test.frontend.fir.handlers.FirDiagnosticsHandler import org.jetbrains.kotlin.test.model.* import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerWithTargetBackendTest @@ -63,10 +71,16 @@ abstract class AbstractParcelizeBoxTestBase(::ParcelizeMainClassProvider)) @@ -104,3 +118,29 @@ open class AbstractParcelizeIrBoxTest : AbstractParcelizeBoxTestBase> get() = ::JvmIrBackendFacade } + +open class AbstractParcelizeFirBoxTest : AbstractParcelizeBoxTestBase( + FrontendKinds.FIR, + TargetBackend.JVM_IR +) { + override val frontendFacade: Constructor> + get() = { testServices -> + FirFrontendFacade(testServices) { + it.registerExtensions(FirParcelizeExtensionRegistrar().configure()) + } + } + + override val frontendToBackendConverter: Constructor> + get() = ::Fir2IrResultsConverter + override val backendFacade: Constructor> + get() = ::JvmIrBackendFacade + + override fun configure(builder: TestConfigurationBuilder) { + super.configure(builder) + with(builder) { + defaultDirectives { + +ENABLE_PLUGIN_PHASES + } + } + } +} diff --git a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBytecodeListingTest.kt b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBytecodeListingTest.kt index b643405209f..a457882a74b 100644 --- a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBytecodeListingTest.kt +++ b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/runners/AbstractParcelizeBytecodeListingTest.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.parcelize.test.runners import org.jetbrains.kotlin.parcelize.test.services.ParcelizeEnvironmentConfigurator +import org.jetbrains.kotlin.test.bind import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder import org.jetbrains.kotlin.test.directives.AsmLikeInstructionListingDirectives.IR_DIFFERENCE import org.jetbrains.kotlin.test.runners.codegen.AbstractAsmLikeInstructionListingTest @@ -29,5 +30,5 @@ private fun TestConfigurationBuilder.configureParcelizeSpecific() { defaultDirectives { +IR_DIFFERENCE } - useConfigurators(::ParcelizeEnvironmentConfigurator) + useConfigurators(::ParcelizeEnvironmentConfigurator.bind(false)) } diff --git a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/ParcelizeEnvironmentConfigurator.kt b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/ParcelizeEnvironmentConfigurator.kt index 54b1fd1426d..9ebf2bdfaa4 100644 --- a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/ParcelizeEnvironmentConfigurator.kt +++ b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/services/ParcelizeEnvironmentConfigurator.kt @@ -6,9 +6,11 @@ package org.jetbrains.kotlin.parcelize.test.services import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.parcelize.ParcelizeComponentRegistrar +import org.jetbrains.kotlin.parcelize.ParcelizeFirIrGeneratorExtension import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.services.EnvironmentConfigurator import org.jetbrains.kotlin.test.services.TestServices @@ -16,7 +18,10 @@ import org.jetbrains.kotlin.test.util.KtTestUtil import org.jetbrains.kotlin.utils.PathUtil import java.io.File -class ParcelizeEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) { +class ParcelizeEnvironmentConfigurator( + testServices: TestServices, + private val useFirExtension: Boolean +) : EnvironmentConfigurator(testServices) { override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule) { val runtimeLibrary = File(PathUtil.kotlinPathsForCompiler.libPath, PathUtil.PARCELIZE_RUNTIME_PLUGIN_JAR_NAME) val androidExtensionsRuntimeLibrary = File(PathUtil.kotlinPathsForCompiler.libPath, PathUtil.ANDROID_EXTENSIONS_RUNTIME_PLUGIN_JAR_NAME) @@ -25,6 +30,10 @@ class ParcelizeEnvironmentConfigurator(testServices: TestServices) : Environment } override fun registerCompilerExtensions(project: Project) { - ParcelizeComponentRegistrar.registerParcelizeComponents(project) + if (useFirExtension) { + IrGenerationExtension.registerExtension(project, ParcelizeFirIrGeneratorExtension()) + } else { + ParcelizeComponentRegistrar.registerParcelizeComponents(project) + } } }