[Parcelize] Allow parcelize to work on common code in multiplatform.

Since parcelize is Android specific, it should only be enabled for
Android compilation. In order to allow parcelize to generate code
for declarations in common code, we make checkers platform checkers
and we allow the registration of an additional annotations to
trigger parcelize processing.

That way, code such as:

```
package my.package

annotation class Parcelize

expect interface MyParcelable

@Parcelize
data class User(name: String): MyParcelable
```

Will work with an Android platform actual of the form:

```
actual typealias MyParcelable = android.os.Parcelable
```

And telling the plugin to trigger parcelize processing with the
additional annotation `my.package.Parcelize`.

Fixes https://issuetracker.google.com/315775835.
This commit is contained in:
Mads Ager
2024-02-26 14:29:55 +01:00
committed by Space Cloud
parent 5f971d6d9d
commit fa0d456850
26 changed files with 177 additions and 87 deletions
@@ -0,0 +1 @@
org.jetbrains.kotlin.parcelize.ParcelizeCommandLineProcessor
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2024 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
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.config.CompilerConfiguration
class ParcelizeCommandLineProcessor : CommandLineProcessor {
companion object {
const val COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.parcelize"
val ADDITIONAL_ANNOTATION_OPTION: CliOption =
CliOption(
"additionalAnnotation",
"<fully qualified name>",
"Additional annotation to trigger parcelize processing.",
required = false,
allowMultipleOccurrences = true
)
}
override val pluginId: String
get() = COMPILER_PLUGIN_ID
override val pluginOptions: Collection<AbstractCliOption>
get() = listOf(ADDITIONAL_ANNOTATION_OPTION)
override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) {
when (option) {
ADDITIONAL_ANNOTATION_OPTION -> configuration.appendList(ParcelizeConfigurationKeys.ADDITIONAL_ANNOTATION, value)
else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
}
}
}
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.parcelize.fir.FirParcelizeExtensionRegistrar
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.isJvm
@@ -21,35 +22,44 @@ import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
class ParcelizeComponentRegistrar : CompilerPluginRegistrar() {
companion object {
fun registerParcelizeComponents(extensionStorage: ExtensionStorage, useFir: Boolean) = with(extensionStorage) {
fun registerParcelizeComponents(
extensionStorage: ExtensionStorage,
additionalAnnotation: List<String>,
useFir: Boolean
) = with(extensionStorage) {
val parcelizeAnnotations = ParcelizeNames.PARCELIZE_CLASS_FQ_NAMES.toMutableList()
additionalAnnotation.mapTo(parcelizeAnnotations) { FqName(it) }
if (useFir) {
IrGenerationExtension.registerExtension(ParcelizeFirIrGeneratorExtension())
IrGenerationExtension.registerExtension(ParcelizeFirIrGeneratorExtension(parcelizeAnnotations))
} else {
IrGenerationExtension.registerExtension(ParcelizeIrGeneratorExtension())
IrGenerationExtension.registerExtension(ParcelizeIrGeneratorExtension(parcelizeAnnotations))
}
SyntheticResolveExtension.registerExtension(ParcelizeResolveExtension())
StorageComponentContainerContributor.registerExtension(ParcelizeDeclarationCheckerComponentContainerContributor())
FirExtensionRegistrarAdapter.registerExtension(FirParcelizeExtensionRegistrar())
SyntheticResolveExtension.registerExtension(ParcelizeResolveExtension(parcelizeAnnotations))
StorageComponentContainerContributor.registerExtension(ParcelizeDeclarationCheckerComponentContainerContributor(parcelizeAnnotations))
FirExtensionRegistrarAdapter.registerExtension(FirParcelizeExtensionRegistrar(parcelizeAnnotations))
}
}
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
registerParcelizeComponents(this, configuration.getBoolean(CommonConfigurationKeys.USE_FIR))
val additionalAnnotation = configuration.get(ParcelizeConfigurationKeys.ADDITIONAL_ANNOTATION) ?: emptyList()
registerParcelizeComponents(this, additionalAnnotation, configuration.getBoolean(CommonConfigurationKeys.USE_FIR))
}
override val supportsK2: Boolean
get() = true
}
class ParcelizeDeclarationCheckerComponentContainerContributor : StorageComponentContainerContributor {
class ParcelizeDeclarationCheckerComponentContainerContributor(
private val parcelizeAnnotations: List<FqName>
) : StorageComponentContainerContributor {
override fun registerModuleComponents(
container: StorageComponentContainer,
platform: TargetPlatform,
moduleDescriptor: ModuleDescriptor,
) {
if (platform.isJvm()) {
container.useInstance(ParcelizeDeclarationChecker())
container.useInstance(ParcelizeAnnotationChecker())
container.useInstance(ParcelizeDeclarationChecker(parcelizeAnnotations))
container.useInstance(ParcelizeAnnotationChecker(parcelizeAnnotations))
}
}
}
@@ -0,0 +1,13 @@
/*
* Copyright 2010-2024 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
import org.jetbrains.kotlin.config.CompilerConfigurationKey
object ParcelizeConfigurationKeys {
val ADDITIONAL_ANNOTATION: CompilerConfigurationKey<List<String>> =
CompilerConfigurationKey.create(ParcelizeCommandLineProcessor.ADDITIONAL_ANNOTATION_OPTION.description)
}