[FIR] Implement FIR version of parcelize plugin

This commit includes only generation of declarations and
  ir backend part, checkers are added in separate commit
This commit is contained in:
Dmitriy Novozhilov
2021-10-19 16:46:42 +03:00
committed by teamcityserver
parent cb0705ec03
commit eac9a9fc79
18 changed files with 1098 additions and 178 deletions
@@ -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<String>) {
model("box")
}
testClass<AbstractParcelizeFirBoxTest> {
model("box")
}
testClass<AbstractParcelizeBytecodeListingTest> {
model("codegen")
}
@@ -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"))
@@ -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)
}
}
@@ -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<FirRegularClassSymbol>()
}
override fun generateFunctions(callableId: CallableId, owner: FirClassSymbol<*>?): List<FirNamedFunctionSymbol> {
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<FirNamedFunctionSymbol>()
runIf(declaredFunctions.none { it.isWriteToParcel() }) { generateWriteToParcel(owner, callableId) }
}
else -> null
}
return listOfNotNull(functionSymbol)
}
private fun FirRegularClassSymbol.hasDescribeContentsImplementation(): Boolean {
return declarationSymbols.filterIsInstance<FirNamedFunctionSymbol>().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<Name> {
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)
}
}
@@ -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
}
}
@@ -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"
}
}
@@ -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)
}
}
@@ -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<IrSimpleFunctionSymbol, IrSimpleFunctionSymbol>()
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<ParcelableProperty>
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
}
@@ -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<ParcelableProperty>) {
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<ParcelableProperty>,
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<ParcelableProperty>) {
// 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<ParcelableProperty>
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)
}
}
@@ -1,4 +1,6 @@
// WITH_STDLIB
// fir doesn't support annotations with type arguments
// IGNORE_BACKEND_FIR: JVM_IR
@file:JvmName("TestKt")
package test
@@ -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)))
}
}
}
@@ -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")
}
}
@@ -2,6 +2,8 @@
// See KT-38107
// The JVM backend is missing support for custom parcelers in List<String>
// WITH_STDLIB
// fir doesn't support annotations with type arguments
// IGNORE_BACKEND_FIR: JVM_IR
@file:JvmName("TestKt")
package test
@@ -1,4 +1,6 @@
// WITH_STDLIB
// fir doesn't support annotations with type arguments
// IGNORE_BACKEND_FIR: JVM_IR
@file:JvmName("TestKt")
package test
@@ -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");
}
}
@@ -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<R : ResultingArtifact.FrontendOutput
)
}
configureIrHandlersStep {
useHandlers(::IrTextDumpHandler)
}
configureCommonHandlersForBoxTest()
useCustomRuntimeClasspathProviders(::ParcelizeRuntimeClasspathProvider)
useConfigurators(::ParcelizeEnvironmentConfigurator)
useConfigurators(::ParcelizeEnvironmentConfigurator.bind(targetFrontend == FrontendKinds.FIR))
useAdditionalSourceProviders(::ParcelizeUtilSourcesProvider)
useAdditionalServices(service<JvmBoxMainClassProvider>(::ParcelizeMainClassProvider))
@@ -104,3 +118,29 @@ open class AbstractParcelizeIrBoxTest : AbstractParcelizeBoxTestBase<ClassicFron
override val backendFacade: Constructor<BackendFacade<IrBackendInput, BinaryArtifacts.Jvm>>
get() = ::JvmIrBackendFacade
}
open class AbstractParcelizeFirBoxTest : AbstractParcelizeBoxTestBase<FirOutputArtifact, IrBackendInput>(
FrontendKinds.FIR,
TargetBackend.JVM_IR
) {
override val frontendFacade: Constructor<FrontendFacade<FirOutputArtifact>>
get() = { testServices ->
FirFrontendFacade(testServices) {
it.registerExtensions(FirParcelizeExtensionRegistrar().configure())
}
}
override val frontendToBackendConverter: Constructor<Frontend2BackendConverter<FirOutputArtifact, IrBackendInput>>
get() = ::Fir2IrResultsConverter
override val backendFacade: Constructor<BackendFacade<IrBackendInput, BinaryArtifacts.Jvm>>
get() = ::JvmIrBackendFacade
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
with(builder) {
defaultDirectives {
+ENABLE_PLUGIN_PHASES
}
}
}
}
@@ -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))
}
@@ -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)
}
}
}