Parcelize: Add the Parcelize compiler plugin with sources extracted from Android Extensions plugin (KT-40030)

This commit is contained in:
Yan Zhulanow
2020-07-07 19:30:23 +09:00
parent c9bca165bc
commit b7796d63d8
152 changed files with 11872 additions and 0 deletions
+3
View File
@@ -10,7 +10,10 @@
<w>kapt</w>
<w>kotlinc</w>
<w>mutators</w>
<w>parcelable</w>
<w>parceler</w>
<w>parcelers</w>
<w>parcelize</w>
<w>repl</w>
<w>testdata</w>
<w>uast</w>
+1
View File
@@ -57,6 +57,7 @@ dependencies {
testCompile(projectTests(":plugins:jvm-abi-gen"))
testCompile(projectTests(":plugins:android-extensions-compiler"))
testCompile(projectTests(":plugins:android-extensions-ide"))
testCompile(projectTests(":plugins:parcelize:parcelize-compiler"))
testCompile(projectTests(":kotlin-annotation-processing"))
testCompile(projectTests(":kotlin-annotation-processing-cli"))
testCompile(projectTests(":kotlin-allopen-compiler-plugin"))
@@ -174,6 +174,10 @@ import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenc
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBoxTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBytecodeListingTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBoxTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBytecodeListingTest
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest
@@ -1508,6 +1512,24 @@ fun main(args: Array<String>) {
}
}
testGroup("plugins/parcelize/parcelize-compiler/tests", "plugins/parcelize/parcelize-compiler/testData") {
testClass<AbstractParcelizeBoxTest> {
model("box", targetBackend = TargetBackend.JVM)
}
testClass<AbstractParcelizeIrBoxTest> {
model("box", targetBackend = TargetBackend.JVM_IR)
}
testClass<AbstractParcelizeBytecodeListingTest> {
model("codegen", targetBackend = TargetBackend.JVM)
}
testClass<AbstractParcelizeIrBytecodeListingTest> {
model("codegen", targetBackend = TargetBackend.JVM_IR)
}
}
testGroup("plugins/jvm-abi-gen/test", "plugins/jvm-abi-gen/testData") {
testClass<AbstractCompareJvmAbiTest> {
model("compare", recursive = false, extension = null)
@@ -148,6 +148,10 @@ import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenc
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBoxTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBytecodeListingTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBoxTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBytecodeListingTest
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest
@@ -1127,6 +1131,24 @@ fun main(args: Array<String>) {
}
}
testGroup("plugins/parcelize/parcelize-compiler/tests", "plugins/parcelize/parcelize-compiler/testData") {
testClass<AbstractParcelizeBoxTest> {
model("box", targetBackend = TargetBackend.JVM)
}
testClass<AbstractParcelizeIrBoxTest> {
model("box", targetBackend = TargetBackend.JVM_IR)
}
testClass<AbstractParcelizeBytecodeListingTest> {
model("codegen", targetBackend = TargetBackend.JVM)
}
testClass<AbstractParcelizeIrBytecodeListingTest> {
model("codegen", targetBackend = TargetBackend.JVM_IR)
}
}
testGroup("plugins/kapt3/kapt3-compiler/test", "plugins/kapt3/kapt3-compiler/testData") {
testClass<AbstractClassFileToSourceStubConverterTest> {
model("converter")
@@ -148,6 +148,10 @@ import org.jetbrains.kotlin.nj2k.inference.mutability.AbstractMutabilityInferenc
import org.jetbrains.kotlin.nj2k.inference.nullability.AbstractNullabilityInferenceTest
import org.jetbrains.kotlin.noarg.AbstractBlackBoxCodegenTestForNoArg
import org.jetbrains.kotlin.noarg.AbstractBytecodeListingTestForNoArg
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBoxTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeBytecodeListingTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBoxTest
import org.jetbrains.kotlin.parcelize.test.AbstractParcelizeIrBytecodeListingTest
import org.jetbrains.kotlin.psi.patternMatching.AbstractPsiUnifierTest
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverScriptTest
import org.jetbrains.kotlin.samWithReceiver.AbstractSamWithReceiverTest
@@ -1127,6 +1131,24 @@ fun main(args: Array<String>) {
}
}
testGroup("plugins/parcelize/parcelize-compiler/tests", "plugins/parcelize/parcelize-compiler/testData") {
testClass<AbstractParcelizeBoxTest> {
model("box", targetBackend = TargetBackend.JVM)
}
testClass<AbstractParcelizeIrBoxTest> {
model("box", targetBackend = TargetBackend.JVM_IR)
}
testClass<AbstractParcelizeBytecodeListingTest> {
model("codegen", targetBackend = TargetBackend.JVM)
}
testClass<AbstractParcelizeIrBytecodeListingTest> {
model("codegen", targetBackend = TargetBackend.JVM_IR)
}
}
testGroup("plugins/kapt3/kapt3-compiler/test", "plugins/kapt3/kapt3-compiler/testData") {
testClass<AbstractClassFileToSourceStubConverterTest> {
model("converter")
@@ -0,0 +1,71 @@
description = "Parcelize compiler plugin"
plugins {
kotlin("jvm")
id("jps-compatible")
}
val robolectricClasspath by configurations.creating
val parcelizeRuntimeForTests by configurations.creating
dependencies {
testCompile(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(project(":compiler:util"))
compileOnly(project(":compiler:plugin-api"))
compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:frontend.java"))
compileOnly(project(":compiler:backend"))
compileOnly(project(":compiler:ir.backend.common"))
compileOnly(project(":compiler:backend.jvm"))
compileOnly(project(":compiler:ir.tree.impl"))
compileOnly(project(":plugins:parcelize:parcelize-runtime"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeJars("asm-all", rootProject = rootProject) }
testCompile(project(":compiler:util"))
testCompile(project(":compiler:backend"))
testCompile(project(":compiler:ir.backend.common"))
testCompile(project(":compiler:backend.jvm"))
testCompile(project(":compiler:cli"))
testCompile(project(":plugins:parcelize:parcelize-runtime"))
testCompile(projectTests(":compiler:tests-common"))
testCompile(project(":kotlin-test:kotlin-test-jvm"))
testCompile(commonDep("junit:junit"))
testRuntime(intellijPluginDep("junit"))
robolectricClasspath(commonDep("org.robolectric", "robolectric"))
robolectricClasspath("org.robolectric:android-all:4.4_r1-robolectric-1")
robolectricClasspath(project(":plugins:parcelize:parcelize-runtime")) { isTransitive = false }
embedded(project(":plugins:parcelize:parcelize-runtime")) { isTransitive = false }
parcelizeRuntimeForTests(project(":plugins:parcelize:parcelize-runtime")) { isTransitive = false }
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
runtimeJar()
javadocJar()
sourcesJar()
testsJar()
projectTest {
dependsOn(parcelizeRuntimeForTests)
dependsOn(":dist")
workingDir = rootDir
useAndroidJar()
doFirst {
systemProperty("parcelizeRuntime.classpath", parcelizeRuntimeForTests.asPath)
val androidPluginPath = File(intellijRootDir(), "plugins/android/lib").canonicalPath
systemProperty("ideaSdk.androidPlugin.path", androidPluginPath)
systemProperty("robolectric.classpath", robolectricClasspath.asPath)
}
}
apply(from = "$rootDir/gradle/kotlinPluginPublication.gradle.kts")
@@ -0,0 +1 @@
org.jetbrains.kotlin.parcelize.ParcelizeComponentRegistrar
@@ -0,0 +1,174 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.parcelize
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.TypeParceler
import kotlinx.parcelize.WriteWith
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.parcelize.diagnostic.ErrorsParcelize
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations
import org.jetbrains.kotlin.types.typeUtil.supertypes
class ParcelizeAnnotationChecker : CallChecker {
companion object {
val TYPE_PARCELER_FQNAME = FqName(TypeParceler::class.java.name)
val WRITE_WITH_FQNAME = FqName(WriteWith::class.java.name)
val IGNORED_ON_PARCEL_FQNAME = FqName(IgnoredOnParcel::class.java.name)
}
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val constructorDescriptor = resolvedCall.resultingDescriptor as? ClassConstructorDescriptor ?: return
val annotationClass = constructorDescriptor.constructedClass.takeIf { it.kind == ClassKind.ANNOTATION_CLASS } ?: return
val annotationEntry = resolvedCall.call.callElement.getNonStrictParentOfType<KtAnnotationEntry>() ?: return
val annotationOwner = annotationEntry.getStrictParentOfType<KtModifierListOwner>() ?: return
if (annotationClass.fqNameSafe == TYPE_PARCELER_FQNAME) {
checkTypeParcelerUsage(resolvedCall, annotationEntry, context, annotationOwner)
}
if (annotationClass.fqNameSafe == WRITE_WITH_FQNAME) {
checkWriteWithUsage(resolvedCall, annotationEntry, context, annotationOwner)
}
if (annotationClass.fqNameSafe == IGNORED_ON_PARCEL_FQNAME) {
checkIgnoredOnParcelUsage(annotationEntry, context, annotationOwner)
}
}
private fun checkIgnoredOnParcelUsage(annotationEntry: KtAnnotationEntry, context: CallCheckerContext, element: KtModifierListOwner) {
if (element is KtParameter && PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java) is KtPrimaryConstructor) {
context.trace.report(ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY.on(annotationEntry))
} else if (element !is KtProperty || PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java) !is KtClass) {
context.trace.report(ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL.on(annotationEntry))
}
}
private fun checkTypeParcelerUsage(
resolvedCall: ResolvedCall<*>,
annotationEntry: KtAnnotationEntry,
context: CallCheckerContext,
element: KtModifierListOwner
) {
val descriptor = context.trace[BindingContext.DECLARATION_TO_DESCRIPTOR, element] ?: return
val thisMappedType = resolvedCall.typeArguments.values.takeIf { it.size == 2 }?.first() ?: return
val duplicatingAnnotationCount = descriptor.annotations
.filter { it.fqName == TYPE_PARCELER_FQNAME }
.mapNotNull { it.type.arguments.takeIf { args -> args.size == 2 }?.first()?.type }
.count { it == thisMappedType }
if (duplicatingAnnotationCount > 1) {
val reportElement = annotationEntry.typeArguments.firstOrNull() ?: annotationEntry
context.trace.report(ErrorsParcelize.DUPLICATING_TYPE_PARCELERS.on(reportElement))
return
}
val containingClass = when (element) {
is KtClassOrObject -> element
is KtParameter -> element.containingClassOrObject
else -> null
}
checkIfTheContainingClassIsParcelize(containingClass, annotationEntry, context)
if (element is KtParameter && element.getStrictParentOfType<KtDeclaration>() is KtPrimaryConstructor) {
val containingClassDescriptor = context.trace[BindingContext.CLASS, containingClass]
val thisAnnotationDescriptor = context.trace[BindingContext.ANNOTATION, annotationEntry]
if (containingClass != null && containingClassDescriptor != null && thisAnnotationDescriptor != null) {
// We can ignore value arguments here cause @TypeParceler is a zero-parameter annotation
if (containingClassDescriptor.annotations.any { it.type == thisAnnotationDescriptor.type }) {
val reportElement = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: annotationEntry
context.trace.report(ErrorsParcelize.REDUNDANT_TYPE_PARCELER.on(reportElement, containingClass))
}
}
}
}
private fun checkWriteWithUsage(
resolvedCall: ResolvedCall<*>,
annotationEntry: KtAnnotationEntry,
context: CallCheckerContext,
element: KtModifierListOwner
) {
if (element !is KtTypeReference) {
return
}
val actualType = context.trace[BindingContext.TYPE, element]?.replaceAnnotations(Annotations.EMPTY) ?: return
val parcelerType = resolvedCall.typeArguments.values.singleOrNull() ?: return
val parcelerClass = parcelerType.constructor.declarationDescriptor as? ClassDescriptor ?: return
val containingClass = element.getStrictParentOfType<KtClassOrObject>()
checkIfTheContainingClassIsParcelize(containingClass, annotationEntry, context)
fun reportElement() = annotationEntry.typeArguments.singleOrNull() ?: annotationEntry
if (parcelerClass.kind != ClassKind.OBJECT) {
context.trace.report(ErrorsParcelize.PARCELER_SHOULD_BE_OBJECT.on(reportElement()))
return
}
fun KotlinType.fqName() = constructor.declarationDescriptor?.fqNameSafe
val parcelerSuperType = parcelerClass.defaultType.supertypes().firstOrNull { it.fqName() == PARCELER_FQNAME } ?: return
val expectedType = parcelerSuperType.arguments.singleOrNull()?.type ?: return
if (!actualType.isSubtypeOf(expectedType)) {
context.trace.report(ErrorsParcelize.PARCELER_TYPE_INCOMPATIBLE.on(reportElement(), expectedType, actualType))
}
}
private fun checkIfTheContainingClassIsParcelize(
containingClass: KtClassOrObject?,
annotationEntry: KtAnnotationEntry,
context: CallCheckerContext
) {
if (containingClass != null) {
val containingClassDescriptor = context.trace[BindingContext.CLASS, containingClass]
if (containingClassDescriptor != null && !containingClassDescriptor.isParcelize) {
val reportElement = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: annotationEntry
context.trace.report(ErrorsParcelize.CLASS_SHOULD_BE_PARCELIZE.on(reportElement, containingClass))
}
}
}
}
internal inline fun <reified T : PsiElement> PsiElement.getStrictParentOfType(): T? {
return PsiTreeUtil.getParentOfType(this, T::class.java, true)
}
internal inline fun <reified T : PsiElement> PsiElement.getNonStrictParentOfType(): T? {
return PsiTreeUtil.getParentOfType(this, T::class.java, false)
}
@@ -0,0 +1,149 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.parcelize
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
import org.jetbrains.kotlin.codegen.DelegatingClassBuilder
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class ParcelizeClinitClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
override fun interceptClassBuilderFactory(
interceptedFactory: ClassBuilderFactory,
bindingContext: BindingContext,
diagnostics: DiagnosticSink
): ClassBuilderFactory {
return ParcelableClinitClassBuilderFactory(interceptedFactory, bindingContext)
}
private inner class ParcelableClinitClassBuilderFactory(
private val delegateFactory: ClassBuilderFactory,
val bindingContext: BindingContext
) : ClassBuilderFactory {
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder {
return AndroidOnDestroyCollectorClassBuilder(origin, delegateFactory.newClassBuilder(origin), bindingContext)
}
override fun getClassBuilderMode() = delegateFactory.classBuilderMode
override fun asText(builder: ClassBuilder?): String? {
return delegateFactory.asText((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
}
override fun asBytes(builder: ClassBuilder?): ByteArray? {
return delegateFactory.asBytes((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
}
override fun close() {
delegateFactory.close()
}
}
private class AndroidOnDestroyCollectorClassBuilder(
val declarationOrigin: JvmDeclarationOrigin,
val delegateClassBuilder: ClassBuilder,
val bindingContext: BindingContext
) : DelegatingClassBuilder() {
private var currentClass: KtClassOrObject? = null
private var currentClassName: String? = null
private var isClinitGenerated = false
override fun getDelegate() = delegateClassBuilder
override fun defineClass(
origin: PsiElement?,
version: Int,
access: Int,
name: String,
signature: String?,
superName: String,
interfaces: Array<out String>
) {
currentClass = origin as? KtClassOrObject
currentClassName = name
isClinitGenerated = false
super.defineClass(origin, version, access, name, signature, superName, interfaces)
}
override fun done() {
if (!isClinitGenerated && currentClass != null && currentClassName != null) {
val descriptor = bindingContext[BindingContext.CLASS, currentClass]
if (descriptor != null && declarationOrigin.descriptor == descriptor && descriptor.isParcelize) {
val baseVisitor = super.newMethod(JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_STATIC, "<clinit>", "()V", null, null)
val visitor = ClinitAwareMethodVisitor(currentClassName!!, baseVisitor)
visitor.visitCode()
visitor.visitInsn(Opcodes.RETURN)
visitor.visitEnd()
}
}
super.done()
}
override fun newMethod(
origin: JvmDeclarationOrigin,
access: Int,
name: String,
desc: String,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
if (name == "<clinit>" && currentClass != null && currentClassName != null) {
isClinitGenerated = true
val descriptor = bindingContext[BindingContext.CLASS, currentClass]
if (descriptor != null && declarationOrigin.descriptor == descriptor && descriptor.isParcelize) {
return ClinitAwareMethodVisitor(
currentClassName!!,
super.newMethod(origin, access, name, desc, signature, exceptions)
)
}
}
return super.newMethod(origin, access, name, desc, signature, exceptions)
}
}
private class ClinitAwareMethodVisitor(val parcelableName: String, mv: MethodVisitor) : MethodVisitor(Opcodes.API_VERSION, mv) {
override fun visitInsn(opcode: Int) {
if (opcode == Opcodes.RETURN) {
val iv = InstructionAdapter(this)
val creatorName = "$parcelableName\$Creator"
val creatorType = Type.getObjectType(creatorName)
iv.anew(creatorType)
iv.dup()
iv.invokespecial(creatorName, "<init>", "()V", false)
iv.putstatic(parcelableName, "CREATOR", "Landroid/os/Parcelable\$Creator;")
}
super.visitInsn(opcode)
}
}
}
@@ -0,0 +1,439 @@
/*
* Copyright 2010-2019 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 com.intellij.psi.PsiElement
import kotlinx.parcelize.TypeParceler
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy.CodegenBased
import org.jetbrains.kotlin.codegen.OwnerKind
import org.jetbrains.kotlin.codegen.context.ClassContext
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.parcelize.ParcelizeResolveExtension.Companion.createMethod
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.*
import org.jetbrains.kotlin.parcelize.serializers.*
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase.Companion.FILE_DESCRIPTOR_FQNAME
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.org.objectweb.asm.Type
open class ParcelizeCodegenExtension : ParcelizeExtensionBase, ExpressionCodegenExtension {
open fun isAvailable(element: PsiElement): Boolean {
return true
}
override val shouldGenerateClassSyntheticPartsInLightClassesMode: Boolean
get() = true
override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) {
val declaration = codegen.myClass as? PsiElement ?: return
if (!isAvailable(declaration)) {
return
}
val parcelableClass = codegen.descriptor
if (!parcelableClass.isParcelizeClassDescriptor) {
return
}
val propertiesToSerialize = getPropertiesToSerialize(codegen, parcelableClass)
val parcelerObject = parcelableClass.companionObjectDescriptor?.takeIf {
TypeUtils.getAllSupertypes(it.defaultType).any { supertype -> supertype.isParceler }
}
with(parcelableClass) {
if (hasSyntheticDescribeContents()) {
writeDescribeContentsFunction(codegen, propertiesToSerialize)
}
if (hasSyntheticWriteToParcel()) {
writeWriteToParcel(codegen, propertiesToSerialize, PARCEL_TYPE, parcelerObject)
}
if (!hasCreatorField()) {
writeCreatorAccessField(codegen, parcelableClass)
}
}
if (codegen.state.classBuilderMode != ClassBuilderMode.LIGHT_CLASSES) {
val parcelClassType = ParcelizeResolveExtension.resolveParcelClassType(parcelableClass.module)
?: error("Can't resolve 'android.os.Parcel' class")
val parcelableCreatorClassType = ParcelizeResolveExtension.resolveParcelableCreatorClassType(parcelableClass.module)
?: error("Can't resolve 'android.os.Parcelable.Creator' class")
writeCreatorClass(
codegen,
parcelableClass, parcelClassType, parcelableCreatorClassType,
PARCEL_TYPE, parcelerObject, propertiesToSerialize
)
}
}
private fun getCompanionClassType(containerAsmType: Type, parcelerObject: ClassDescriptor): Pair<Type, String> {
val shortName = parcelerObject.name
return Pair(Type.getObjectType(containerAsmType.internalName + "\$$shortName"), shortName.asString())
}
private fun ClassDescriptor.writeWriteToParcel(
codegen: ImplementationBodyCodegen,
properties: List<PropertyToSerialize>,
parcelAsmType: Type,
parcelerObject: ClassDescriptor?
): Unit? {
val containerAsmType = codegen.typeMapper.mapType(this.defaultType)
return findFunction(WRITE_TO_PARCEL)?.write(codegen) {
if (parcelerObject != null) {
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
v.load(0, containerAsmType)
v.load(1, PARCEL_TYPE)
v.load(2, Type.INT_TYPE)
v.invokevirtual(
companionAsmType.internalName, "write",
"(${containerAsmType.descriptor}${PARCEL_TYPE.descriptor}I)V", false
)
} else {
val frameMap = FrameMap().apply {
enterTemp(containerAsmType)
enterTemp(PARCEL_TYPE)
enterTemp(Type.INT_TYPE)
}
val globalContext = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType, emptyList(), frameMap)
if (properties.isEmpty()) {
val entityType = this@writeWriteToParcel.defaultType
val asmType = codegen.state.typeMapper.mapType(entityType)
val serializer = if (this@writeWriteToParcel.kind == ClassKind.CLASS) {
NullAwareParcelSerializerWrapper(ZeroParameterClassSerializer(asmType, entityType))
} else {
ParcelSerializer.get(entityType, asmType, globalContext, strict = true)
}
v.load(1, parcelAsmType)
v.load(0, containerAsmType)
serializer.writeValue(v)
} else {
for ((fieldName, type, parcelers) in properties) {
val asmType = codegen.typeMapper.mapType(type)
v.load(1, parcelAsmType)
v.load(0, containerAsmType)
v.getfield(containerAsmType.internalName, fieldName, asmType.descriptor)
val serializer = ParcelSerializer.get(type, asmType, globalContext.copy(typeParcelers = parcelers))
serializer.writeValue(v)
}
}
}
v.areturn(Type.VOID_TYPE)
}
}
private fun ClassDescriptor.writeDescribeContentsFunction(
codegen: ImplementationBodyCodegen,
propertiesToSerialize: List<PropertyToSerialize>
): Unit? {
val hasFileDescriptorAnywhere = propertiesToSerialize.any { it.type.containsFileDescriptor() }
return findFunction(DESCRIBE_CONTENTS)?.write(codegen) {
v.aconst(if (hasFileDescriptorAnywhere) 1 /* CONTENTS_FILE_DESCRIPTOR */ else 0)
v.areturn(Type.INT_TYPE)
}
}
private fun KotlinType.containsFileDescriptor(): Boolean {
val declarationDescriptor = this.constructor.declarationDescriptor
if (declarationDescriptor != null) {
if (declarationDescriptor.fqNameSafe == FILE_DESCRIPTOR_FQNAME) {
return true
}
}
return this.arguments.any { it.type.containsFileDescriptor() }
}
data class PropertyToSerialize(val name: String, val type: KotlinType, val parcelers: List<TypeParcelerMapping>)
private fun getPropertiesToSerialize(
codegen: ImplementationBodyCodegen,
parcelableClass: ClassDescriptor
): List<PropertyToSerialize> {
if (parcelableClass.kind != ClassKind.CLASS) {
return emptyList()
}
val constructor = parcelableClass.constructors.firstOrNull { it.isPrimary } ?: return emptyList()
val propertiesToSerialize = constructor.valueParameters.mapNotNull { param ->
codegen.bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, param]
}
val classParcelers = getTypeParcelers(parcelableClass.annotations)
return propertiesToSerialize.map {
PropertyToSerialize(it.name.asString(), it.type, classParcelers + getTypeParcelers(it.annotations))
}
}
private fun writeCreateFromParcel(
codegen: ImplementationBodyCodegen,
parcelableClass: ClassDescriptor,
parcelableCreatorClassType: KotlinType,
creatorClass: ClassDescriptorImpl,
parcelClassType: KotlinType,
parcelAsmType: Type,
parcelerObject: ClassDescriptor?,
properties: List<PropertyToSerialize>
) {
val containerAsmType = codegen.typeMapper.mapType(parcelableClass)
val creatorAsmType = codegen.typeMapper.mapType(creatorClass)
val overriddenFunction = parcelableCreatorClassType.findFunction(CREATE_FROM_PARCEL)
?: error("Can't resolve 'android.os.Parcelable.Creator.${CREATE_FROM_PARCEL.methodName}' method")
createMethod(
creatorClass, CREATE_FROM_PARCEL, Modality.FINAL,
parcelableClass.defaultType, "in" to parcelClassType
).write(codegen, overriddenFunction) {
if (parcelerObject != null) {
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
v.load(1, PARCEL_TYPE)
v.invokevirtual(companionAsmType.internalName, "create", "(${PARCEL_TYPE.descriptor})$containerAsmType", false)
} else {
v.anew(containerAsmType)
v.dup()
val asmConstructorParameters = StringBuilder()
val frameMap = FrameMap().apply {
enterTemp(creatorAsmType)
enterTemp(PARCEL_TYPE)
}
val globalContext = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType, emptyList(), frameMap)
if (properties.isEmpty()) {
val entityType = parcelableClass.defaultType
val asmType = codegen.state.typeMapper.mapType(entityType)
val serializer = if (parcelableClass.kind == ClassKind.CLASS) {
NullAwareParcelSerializerWrapper(ZeroParameterClassSerializer(asmType, entityType))
} else {
ParcelSerializer.get(entityType, asmType, globalContext, strict = true)
}
v.load(1, parcelAsmType)
serializer.readValue(v)
} else {
for ((_, type, parcelers) in properties) {
val asmType = codegen.typeMapper.mapType(type)
asmConstructorParameters.append(asmType.descriptor)
val serializer = ParcelSerializer.get(type, asmType, globalContext.copy(typeParcelers = parcelers))
v.load(1, parcelAsmType)
serializer.readValue(v)
}
v.invokespecial(containerAsmType.internalName, "<init>", "($asmConstructorParameters)V", false)
}
}
v.areturn(containerAsmType)
}
}
private fun writeCreatorAccessField(codegen: ImplementationBodyCodegen, parcelableClass: ClassDescriptor) {
val creatorType = Type.getObjectType("android/os/Parcelable\$Creator")
val parcelableType = codegen.typeMapper.mapType(parcelableClass)
val fieldSignature = "L${creatorType.internalName}<${parcelableType.descriptor}>;"
codegen.v.newField(
JvmDeclarationOrigin.NO_ORIGIN,
ACC_STATIC or ACC_PUBLIC or ACC_FINAL,
"CREATOR",
creatorType.descriptor,
fieldSignature,
null
)
}
private fun writeCreatorClass(
codegen: ImplementationBodyCodegen,
parcelableClass: ClassDescriptor,
parcelClassType: KotlinType,
parcelableCreatorClassType: KotlinType,
parcelAsmType: Type,
parcelerObject: ClassDescriptor?,
properties: List<PropertyToSerialize>
) {
val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
val creatorAsmType = Type.getObjectType(containerAsmType.internalName + "\$Creator")
val creatorClass = ClassDescriptorImpl(
parcelableClass, Name.identifier("Creator"), Modality.FINAL, ClassKind.CLASS, listOf(parcelableCreatorClassType),
parcelableClass.source, false, LockBasedStorageManager.NO_LOCKS
)
creatorClass.initialize(
MemberScope.Empty, emptySet(),
DescriptorFactory.createPrimaryConstructorForObject(creatorClass, creatorClass.source)
)
val classBuilderForCreator = codegen.state.factory.newVisitor(
JvmDeclarationOrigin(JvmDeclarationOriginKind.OTHER, null, creatorClass),
Type.getObjectType(creatorAsmType.internalName),
codegen.myClass.containingKtFile
)
val classContextForCreator = ClassContext(
codegen.typeMapper, creatorClass, OwnerKind.IMPLEMENTATION, codegen.context.parentContext, null
)
val codegenForCreator = ImplementationBodyCodegen(
codegen.myClass, classContextForCreator, classBuilderForCreator, codegen.state, codegen.parentCodegen, false
)
val classSignature = "Ljava/lang/Object;Landroid/os/Parcelable\$Creator<${containerAsmType.descriptor}>;"
classBuilderForCreator.defineClass(
null, V1_6, ACC_PUBLIC or ACC_FINAL or ACC_SUPER,
creatorAsmType.internalName, classSignature, "java/lang/Object",
arrayOf("android/os/Parcelable\$Creator")
)
codegen.v.visitInnerClass(creatorAsmType.internalName, containerAsmType.internalName, "Creator", ACC_PUBLIC or ACC_STATIC)
codegenForCreator.v.visitInnerClass(creatorAsmType.internalName, containerAsmType.internalName, "Creator", ACC_PUBLIC or ACC_STATIC)
writeSyntheticClassMetadata(classBuilderForCreator, codegen.state)
writeCreatorConstructor(codegenForCreator, creatorClass, creatorAsmType)
writeNewArrayMethod(codegenForCreator, parcelableClass, parcelableCreatorClassType, creatorClass, parcelerObject)
writeCreateFromParcel(
codegenForCreator,
parcelableClass, parcelableCreatorClassType, creatorClass, parcelClassType,
parcelAsmType, parcelerObject, properties
)
classBuilderForCreator.done()
}
private fun writeCreatorConstructor(codegen: ImplementationBodyCodegen, creatorClass: ClassDescriptor, creatorAsmType: Type) {
DescriptorFactory.createPrimaryConstructorForObject(creatorClass, creatorClass.source)
.apply {
returnType = creatorClass.defaultType
}.write(codegen) {
v.load(0, creatorAsmType)
v.invokespecial("java/lang/Object", "<init>", "()V", false)
v.areturn(Type.VOID_TYPE)
}
}
private fun writeNewArrayMethod(
codegen: ImplementationBodyCodegen,
parcelableClass: ClassDescriptor,
parcelableCreatorClassType: KotlinType,
creatorClass: ClassDescriptorImpl,
parcelerObject: ClassDescriptor?
) {
val builtIns = parcelableClass.builtIns
val parcelableAsmType = codegen.typeMapper.mapType(parcelableClass)
val overriddenFunction = parcelableCreatorClassType.findFunction(NEW_ARRAY)
?: error("Can't resolve 'android.os.Parcelable.Creator.${NEW_ARRAY.methodName}' method")
createMethod(
creatorClass, NEW_ARRAY, Modality.FINAL,
builtIns.getArrayType(Variance.INVARIANT, parcelableClass.defaultType),
"size" to builtIns.intType
).write(codegen, overriddenFunction) {
if (parcelerObject != null) {
val newArrayMethod = parcelerObject.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("newArray"), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS)
.firstOrNull {
it.typeParameters.isEmpty()
&& it.kind == CallableMemberDescriptor.Kind.DECLARATION
&& (it.valueParameters.size == 1 && KotlinBuiltIns.isInt(it.valueParameters[0].type))
&& !((it.containingDeclaration as? ClassDescriptor)?.defaultType?.isParceler ?: true)
}
if (newArrayMethod != null) {
val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
v.load(1, Type.INT_TYPE)
v.invokevirtual(companionAsmType.internalName, "newArray", "(I)[$parcelableAsmType", false)
v.areturn(Type.getType("[$parcelableAsmType"))
return@write
}
}
v.load(1, Type.INT_TYPE)
v.newarray(parcelableAsmType)
v.areturn(Type.getType("[$parcelableAsmType"))
}
}
private fun FunctionDescriptor.write(
codegen: ImplementationBodyCodegen,
overriddenDescriptor: FunctionDescriptor? = null,
code: ExpressionCodegen.() -> Unit
) {
val declarationOrigin = JvmDeclarationOrigin(JvmDeclarationOriginKind.OTHER, null, this)
if (overriddenDescriptor != null) {
this.overriddenDescriptors = listOf(overriddenDescriptor)
}
codegen.functionCodegen.generateMethod(declarationOrigin, this, object : CodegenBased(codegen.state) {
override fun doGenerateBody(e: ExpressionCodegen, signature: JvmMethodSignature) = with(e) {
e.code()
}
})
}
private fun KotlinType.findFunction(componentKind: ParcelizeSyntheticComponent.ComponentKind): SimpleFunctionDescriptor? {
return memberScope
.getContributedFunctions(Name.identifier(componentKind.methodName), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS)
.firstOrNull()
}
}
internal fun getTypeParcelers(annotations: Annotations): List<TypeParcelerMapping> {
val typeParcelerFqName = FqName(TypeParceler::class.java.name)
val serializers = mutableListOf<TypeParcelerMapping>()
for (annotation in annotations.filter { it.fqName == typeParcelerFqName }) {
val (mappedType, parcelerType) = annotation.type.arguments.takeIf { it.size == 2 } ?: continue
serializers += TypeParcelerMapping(mappedType.type, parcelerType.type)
}
return serializers
}
@@ -0,0 +1,50 @@
/*
* 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
import com.intellij.mock.MockProject
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
class ParcelizeComponentRegistrar : ComponentRegistrar {
companion object {
fun registerParcelizeComponents(project: Project) {
ExpressionCodegenExtension.registerExtension(project, ParcelizeCodegenExtension())
IrGenerationExtension.registerExtension(project, ParcelizeIrGeneratorExtension())
SyntheticResolveExtension.registerExtension(project, ParcelizeResolveExtension())
ClassBuilderInterceptorExtension.registerExtension(project, ParcelizeClinitClassBuilderInterceptorExtension())
StorageComponentContainerContributor.registerExtension(project, ParcelizeDeclarationCheckerComponentContainerContributor())
}
}
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
registerParcelizeComponents(project)
}
}
class ParcelizeDeclarationCheckerComponentContainerContributor : StorageComponentContainerContributor {
override fun registerModuleComponents(
container: StorageComponentContainer,
platform: TargetPlatform,
moduleDescriptor: ModuleDescriptor,
) {
if (platform.isJvm()) {
container.useInstance(ParcelizeDeclarationChecker())
container.useInstance(ParcelizeAnnotationChecker())
}
}
}
@@ -0,0 +1,238 @@
/*
* Copyright 2010-2018 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 kotlinx.parcelize.IgnoredOnParcel
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.FrameMap
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.parcelize.diagnostic.ErrorsParcelize
import org.jetbrains.kotlin.parcelize.serializers.ParcelSerializer
import org.jetbrains.kotlin.parcelize.serializers.isParcelable
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmFieldAnnotation
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.isError
val ANDROID_PARCELABLE_CLASS_FQNAME = FqName("android.os.Parcelable")
val ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME = FqName("android.os.Parcelable.Creator")
val ANDROID_PARCEL_CLASS_FQNAME = FqName("android.os.Parcel")
class ParcelizeDeclarationChecker : DeclarationChecker {
private companion object {
private val IGNORED_ON_PARCEL_FQNAME = FqName(IgnoredOnParcel::class.java.canonicalName)
}
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
val trace = context.trace
when (descriptor) {
is ClassDescriptor -> {
checkParcelableClass(descriptor, declaration, trace, trace.bindingContext, context.languageVersionSettings)
}
is SimpleFunctionDescriptor -> {
val containingClass = descriptor.containingDeclaration as? ClassDescriptor
val ktFunction = declaration as? KtFunction
if (containingClass != null && ktFunction != null) {
checkParcelableClassMethod(descriptor, containingClass, ktFunction, trace)
}
}
is PropertyDescriptor -> {
val containingClass = descriptor.containingDeclaration as? ClassDescriptor
val ktProperty = declaration as? KtProperty
if (containingClass != null && ktProperty != null) {
checkParcelableClassProperty(descriptor, containingClass, ktProperty, trace, trace.bindingContext)
}
}
}
}
private fun checkParcelableClassMethod(
method: SimpleFunctionDescriptor,
containingClass: ClassDescriptor,
declaration: KtFunction,
diagnosticHolder: DiagnosticSink
) {
if (!containingClass.isParcelize) {
return
}
if (method.isWriteToParcel() && declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
val reportElement = declaration.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD)
?: declaration.nameIdentifier
?: declaration
diagnosticHolder.report(ErrorsParcelize.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED.on(reportElement))
}
}
private fun checkParcelableClassProperty(
property: PropertyDescriptor,
containingClass: ClassDescriptor,
declaration: KtProperty,
diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext
) {
fun hasIgnoredOnParcel(): Boolean {
fun Annotations.hasIgnoredOnParcel() = any { it.fqName == IGNORED_ON_PARCEL_FQNAME }
return property.annotations.hasIgnoredOnParcel() || (property.getter?.annotations?.hasIgnoredOnParcel() ?: false)
}
if (containingClass.isParcelize
&& (declaration.hasDelegate() || bindingContext[BindingContext.BACKING_FIELD_REQUIRED, property] == true)
&& !hasIgnoredOnParcel()
) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsParcelize.PROPERTY_WONT_BE_SERIALIZED.on(reportElement))
}
// @JvmName is not applicable to property so we can check just the descriptor name
if (property.name.asString() == "CREATOR" && property.findJvmFieldAnnotation() != null && containingClass.isCompanionObject) {
val outerClass = containingClass.containingDeclaration as? ClassDescriptor
if (outerClass != null && outerClass.isParcelize) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement))
}
}
}
private fun checkParcelableClass(
descriptor: ClassDescriptor,
declaration: KtDeclaration,
diagnosticHolder: DiagnosticSink,
bindingContext: BindingContext,
languageVersionSettings: LanguageVersionSettings
) {
if (!descriptor.isParcelize) {
return
}
if (declaration !is KtClassOrObject) {
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS.on(declaration))
return
}
if (declaration is KtClass && (declaration.isAnnotation() || declaration.isInterface())) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS.on(reportElement))
return
}
for (companion in declaration.companionObjects) {
if (companion.name == "CREATOR") {
val reportElement = companion.nameIdentifier ?: companion
diagnosticHolder.report(ErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement))
}
}
val sealedOrAbstract =
declaration.modifierList?.let { it.getModifier(KtTokens.ABSTRACT_KEYWORD) ?: it.getModifier(KtTokens.SEALED_KEYWORD) }
if (sealedOrAbstract != null) {
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_BE_INSTANTIABLE.on(sealedOrAbstract))
}
if (declaration is KtClass && declaration.isInner()) {
val reportElement = declaration.modifierList?.getModifier(KtTokens.INNER_KEYWORD) ?: declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_CANT_BE_INNER_CLASS.on(reportElement))
}
if (declaration.isLocal) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_CANT_BE_LOCAL_CLASS.on(reportElement))
}
val superTypes = TypeUtils.getAllSupertypes(descriptor.defaultType)
if (superTypes.none { it.constructor.declarationDescriptor?.fqNameSafe == ANDROID_PARCELABLE_CLASS_FQNAME }) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsParcelize.NO_PARCELABLE_SUPERTYPE.on(reportElement))
}
for (supertypeEntry in declaration.superTypeListEntries) {
supertypeEntry as? KtDelegatedSuperTypeEntry ?: continue
val delegateExpression = supertypeEntry.delegateExpression ?: continue
val type = bindingContext[BindingContext.TYPE, supertypeEntry.typeReference] ?: continue
if (type.isParcelable()) {
val reportElement = supertypeEntry.byKeywordNode?.psi ?: delegateExpression
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_DELEGATE_IS_NOT_ALLOWED.on(reportElement))
}
}
val primaryConstructor = declaration.primaryConstructor
if (primaryConstructor == null && declaration.secondaryConstructors.isNotEmpty()) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR.on(reportElement))
} else if (primaryConstructor != null && primaryConstructor.valueParameters.isEmpty()) {
val reportElement = declaration.nameIdentifier ?: declaration
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY.on(reportElement))
}
val typeMapper = KotlinTypeMapper(
bindingContext,
ClassBuilderMode.FULL,
descriptor.module.name.asString(),
languageVersionSettings
)
for (parameter in primaryConstructor?.valueParameters.orEmpty<KtParameter>()) {
checkParcelableClassProperty(parameter, descriptor, diagnosticHolder, typeMapper)
}
}
private fun checkParcelableClassProperty(
parameter: KtParameter,
containerClass: ClassDescriptor,
diagnosticHolder: DiagnosticSink,
typeMapper: KotlinTypeMapper
) {
if (!parameter.hasValOrVar()) {
val reportElement = parameter.nameIdentifier ?: parameter
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR.on(reportElement))
}
val descriptor = typeMapper.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter] ?: return
val type = descriptor.type
if (!type.isError && !containerClass.hasCustomParceler()) {
val asmType = typeMapper.mapType(type)
try {
val parcelers = getTypeParcelers(descriptor.annotations) + getTypeParcelers(containerClass.annotations)
val context = ParcelSerializer.ParcelSerializerContext(
typeMapper,
typeMapper.mapType(containerClass.defaultType),
parcelers,
FrameMap()
)
ParcelSerializer.get(type, asmType, context, strict = true)
} catch (e: IllegalArgumentException) {
// get() throws IllegalArgumentException on unknown types
val reportElement = parameter.typeReference ?: parameter.nameIdentifier ?: parameter
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_TYPE_NOT_SUPPORTED.on(reportElement))
}
}
}
private fun ClassDescriptor.hasCustomParceler(): Boolean {
val companionObjectSuperTypes = companionObjectDescriptor?.let { TypeUtils.getAllSupertypes(it.defaultType) } ?: return false
return companionObjectSuperTypes.any { it.isParceler }
}
}
@@ -0,0 +1,21 @@
/*
* 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
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.ParcelizeIrTransformer
class ParcelizeIrGeneratorExtension : IrGenerationExtension {
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
val arrayOfNulls = pluginContext.symbols.arrayOfNulls
val charSequence = pluginContext.symbols.charSequence
val androidSymbols = AndroidSymbols(pluginContext.irBuiltIns, arrayOfNulls, charSequence, moduleFragment)
ParcelizeIrTransformer(pluginContext, androidSymbols).transform(moduleFragment)
}
}
@@ -0,0 +1,179 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.parcelize
import com.intellij.psi.PsiElement
import kotlinx.parcelize.Parceler
import kotlinx.parcelize.Parcelize
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
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.DESCRIBE_CONTENTS
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.SimpleType
open class ParcelizeResolveExtension : SyntheticResolveExtension {
companion object {
fun resolveParcelClassType(module: ModuleDescriptor): SimpleType? {
return module.findClassAcrossModuleDependencies(ClassId.topLevel(FqName("android.os.Parcel")))?.defaultType
}
fun resolveParcelableCreatorClassType(module: ModuleDescriptor): SimpleType? {
val creatorClassId = ClassId(FqName("android.os"), FqName("Parcelable.Creator"), false)
return module.findClassAcrossModuleDependencies(creatorClassId)?.defaultType
}
fun createMethod(
classDescriptor: ClassDescriptor,
componentKind: ParcelizeSyntheticComponent.ComponentKind,
modality: Modality,
returnType: KotlinType,
vararg parameters: Pair<String, KotlinType>
): SimpleFunctionDescriptor {
val functionDescriptor = object : ParcelizeSyntheticComponent, SimpleFunctionDescriptorImpl(
classDescriptor,
null,
Annotations.EMPTY,
Name.identifier(componentKind.methodName),
CallableMemberDescriptor.Kind.SYNTHESIZED,
classDescriptor.source
) {
override val componentKind = componentKind
}
val valueParameters = parameters.mapIndexed { index, (name, type) -> functionDescriptor.makeValueParameter(name, type, index) }
functionDescriptor.initialize(
null, classDescriptor.thisAsReceiverParameter, emptyList(), valueParameters,
returnType, modality, DescriptorVisibilities.PUBLIC
)
return functionDescriptor
}
private fun FunctionDescriptor.makeValueParameter(name: String, type: KotlinType, index: Int): ValueParameterDescriptor {
return ValueParameterDescriptorImpl(
containingDeclaration = this,
original = null,
index = index,
annotations = Annotations.EMPTY,
name = Name.identifier(name),
outType = type,
declaresDefaultValue = false,
isCrossinline = false,
isNoinline = false,
varargElementType = null,
source = this.source
)
}
private val parcelizeMethodNames: List<Name> =
listOf(Name.identifier(DESCRIBE_CONTENTS.methodName), Name.identifier(WRITE_TO_PARCEL.methodName))
}
open fun isAvailable(element: PsiElement): Boolean {
return true
}
override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor): Name? = null
override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List<Name> {
return if (thisDescriptor.isParcelize) parcelizeMethodNames else emptyList()
}
override fun generateSyntheticMethods(
thisDescriptor: ClassDescriptor,
name: Name,
bindingContext: BindingContext,
fromSupertypes: List<SimpleFunctionDescriptor>,
result: MutableCollection<SimpleFunctionDescriptor>
) {
fun isParcelizePluginEnabled(): Boolean {
val sourceElement = (thisDescriptor.source as? PsiSourceElement)?.psi ?: return false
return isAvailable(sourceElement)
}
if (name.asString() == DESCRIBE_CONTENTS.methodName
&& thisDescriptor.isParcelize
&& isParcelizePluginEnabled()
&& result.none { it.isDescribeContents() }
&& fromSupertypes.none { it.isDescribeContents() }
) {
result += createMethod(thisDescriptor, DESCRIBE_CONTENTS, Modality.OPEN, thisDescriptor.builtIns.intType)
} else if (name.asString() == WRITE_TO_PARCEL.methodName
&& thisDescriptor.isParcelize
&& isParcelizePluginEnabled()
&& result.none { it.isWriteToParcel() }
) {
val builtIns = thisDescriptor.builtIns
val parcelClassType = resolveParcelClassType(thisDescriptor.module) ?: ErrorUtils.createErrorType("Unresolved 'Parcel' type")
result += createMethod(
thisDescriptor, WRITE_TO_PARCEL, Modality.OPEN,
builtIns.unitType, "parcel" to parcelClassType, "flags" to builtIns.intType
)
}
}
private fun SimpleFunctionDescriptor.isDescribeContents(): Boolean {
return this.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE
&& modality != Modality.ABSTRACT
&& typeParameters.isEmpty()
&& valueParameters.isEmpty()
// Unfortunately, we can't check the return type as it's unresolved in IDE light classes
}
}
internal fun SimpleFunctionDescriptor.isWriteToParcel(): Boolean {
return typeParameters.isEmpty()
&& valueParameters.size == 2
// Unfortunately, we can't check the first parameter type as it's unresolved in IDE light classes
&& KotlinBuiltIns.isInt(valueParameters[1].type)
&& returnType?.let { KotlinBuiltIns.isUnit(it) } == true
}
interface ParcelizeSyntheticComponent {
val componentKind: ComponentKind
enum class ComponentKind(val methodName: String) {
WRITE_TO_PARCEL("writeToParcel"),
DESCRIBE_CONTENTS("describeContents"),
NEW_ARRAY("newArray"),
CREATE_FROM_PARCEL("createFromParcel")
}
}
val PARCELIZE_CLASS_FQNAME: FqName = FqName(Parcelize::class.java.canonicalName)
internal val PARCELER_FQNAME: FqName = FqName(Parceler::class.java.canonicalName)
val ClassDescriptor.isParcelize: Boolean
get() = this.annotations.hasAnnotation(PARCELIZE_CLASS_FQNAME)
val KotlinType.isParceler
get() = constructor.declarationDescriptor?.fqNameSafe == PARCELER_FQNAME
@@ -0,0 +1,138 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.parcelize.diagnostic
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_CLASS_OR_OBJECT
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_TYPE_WITH_ANNOTATIONS
object DefaultErrorMessagesParcelize : DefaultErrorMessages.Extension {
private val MAP = DiagnosticFactoryToRendererMap("Parcelize")
override fun getMap() = MAP
init {
MAP.put(
ErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS,
"'Parcelable' should be a class"
)
MAP.put(
ErrorsParcelize.PARCELABLE_DELEGATE_IS_NOT_ALLOWED,
"Delegating 'Parcelable' is not allowed"
)
MAP.put(
ErrorsParcelize.PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS,
"'Parcelable' should not be a 'enum class'"
)
MAP.put(
ErrorsParcelize.PARCELABLE_SHOULD_BE_INSTANTIABLE,
"'Parcelable' should not be a 'sealed' or 'abstract' class"
)
MAP.put(
ErrorsParcelize.PARCELABLE_CANT_BE_INNER_CLASS,
"'Parcelable' can't be an inner class"
)
MAP.put(
ErrorsParcelize.PARCELABLE_CANT_BE_LOCAL_CLASS,
"'Parcelable' can't be a local class"
)
MAP.put(
ErrorsParcelize.NO_PARCELABLE_SUPERTYPE,
"No 'Parcelable' supertype"
)
MAP.put(
ErrorsParcelize.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR,
"'Parcelable' should have a primary constructor"
)
MAP.put(
ErrorsParcelize.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY,
"The primary constructor is empty, no data will be serialized to 'Parcel'"
)
MAP.put(
ErrorsParcelize.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR,
"'Parcelable' constructor parameter should be 'val' or 'var'"
)
MAP.put(
ErrorsParcelize.PROPERTY_WONT_BE_SERIALIZED,
"Property would not be serialized into a 'Parcel'. Add '@IgnoredOnParcel' annotation to remove the warning"
)
MAP.put(
ErrorsParcelize.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED,
"Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead"
)
MAP.put(
ErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED,
"'CREATOR' definition is not allowed. Use 'Parceler' companion object instead"
)
MAP.put(
ErrorsParcelize.PARCELABLE_TYPE_NOT_SUPPORTED,
"Type is not directly supported by 'Parcelize'. " +
"Annotate the parameter type with '@RawValue' if you want it to be serialized using 'writeValue()'"
)
MAP.put(
ErrorsParcelize.PARCELER_SHOULD_BE_OBJECT,
"Parceler should be an object"
)
MAP.put(
ErrorsParcelize.PARCELER_TYPE_INCOMPATIBLE,
"Parceler type {0} is incompatible with {1}",
RENDER_TYPE_WITH_ANNOTATIONS, RENDER_TYPE_WITH_ANNOTATIONS
)
MAP.put(
ErrorsParcelize.DUPLICATING_TYPE_PARCELERS,
"Duplicating ''TypeParceler'' annotations"
)
MAP.put(
ErrorsParcelize.REDUNDANT_TYPE_PARCELER,
"This ''TypeParceler'' is already provided for {0}",
RENDER_CLASS_OR_OBJECT
)
MAP.put(
ErrorsParcelize.CLASS_SHOULD_BE_PARCELIZE,
"{0} should be annotated with ''@Parcelize''",
RENDER_CLASS_OR_OBJECT
)
MAP.put(
ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL,
"'@IgnoredOnParcel' is only applicable to class properties"
)
MAP.put(
ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY,
"'@IgnoredOnParcel' is inapplicable to properties declared in the primary constructor"
)
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.parcelize.diagnostic;
import com.intellij.psi.PsiElement;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.types.KotlinType;
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
public interface ErrorsParcelize {
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_BE_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PARCELABLE_DELEGATE_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_BE_INSTANTIABLE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PARCELABLE_CANT_BE_INNER_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PARCELABLE_CANT_BE_LOCAL_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> NO_PARCELABLE_SUPERTYPE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PROPERTY_WONT_BE_SERIALIZED = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> CREATOR_DEFINITION_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PARCELABLE_TYPE_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PARCELER_SHOULD_BE_OBJECT = DiagnosticFactory0.create(ERROR);
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> PARCELER_TYPE_INCOMPATIBLE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory0<PsiElement> DUPLICATING_TYPE_PARCELERS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, KtClassOrObject> REDUNDANT_TYPE_PARCELER = DiagnosticFactory1.create(WARNING);
DiagnosticFactory1<PsiElement, KtClassOrObject> CLASS_SHOULD_BE_PARCELIZE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> INAPPLICABLE_IGNORED_ON_PARCEL = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY = DiagnosticFactory0.create(WARNING);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
{
Errors.Initializer.initializeFactoryNamesAndDefaultErrorMessages(ErrorsParcelize.class, DefaultErrorMessagesParcelize.INSTANCE);
}
};
}
@@ -0,0 +1,90 @@
/*
* 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.ir.builders.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrSymbol
// An IR builder with access to AndroidSymbols and convenience methods to build calls to some of these methods.
class AndroidIrBuilder internal constructor(
val androidSymbols: AndroidSymbols,
symbol: IrSymbol,
startOffset: Int,
endOffset: Int
) : IrBuilderWithScope(IrGeneratorContextBase(androidSymbols.irBuiltIns), Scope(symbol), startOffset, endOffset) {
fun parcelReadInt(receiver: IrExpression): IrExpression {
return irCall(androidSymbols.parcelReadInt).apply {
dispatchReceiver = receiver
}
}
fun parcelReadParcelable(receiver: IrExpression, loader: IrExpression): IrExpression {
return irCall(androidSymbols.parcelReadParcelable).apply {
dispatchReceiver = receiver
putValueArgument(0, loader)
}
}
fun parcelReadString(receiver: IrExpression): IrExpression {
return irCall(androidSymbols.parcelReadString).apply {
dispatchReceiver = receiver
}
}
fun parcelReadValue(receiver: IrExpression, loader: IrExpression): IrExpression {
return irCall(androidSymbols.parcelReadValue).apply {
dispatchReceiver = receiver
putValueArgument(0, loader)
}
}
fun parcelWriteInt(receiver: IrExpression, value: IrExpression): IrExpression {
return irCall(androidSymbols.parcelWriteInt).apply {
dispatchReceiver = receiver
putValueArgument(0, value)
}
}
fun parcelWriteParcelable(receiver: IrExpression, p: IrExpression, parcelableFlags: IrExpression): IrExpression {
return irCall(androidSymbols.parcelWriteParcelable).apply {
dispatchReceiver = receiver
putValueArgument(0, p)
putValueArgument(1, parcelableFlags)
}
}
fun parcelWriteString(receiver: IrExpression, value: IrExpression): IrExpression {
return irCall(androidSymbols.parcelWriteString).apply {
dispatchReceiver = receiver
putValueArgument(0, value)
}
}
fun parcelWriteValue(receiver: IrExpression, v: IrExpression): IrExpression {
return irCall(androidSymbols.parcelWriteValue).apply {
dispatchReceiver = receiver
putValueArgument(0, v)
}
}
fun textUtilsWriteToParcel(cs: IrExpression, p: IrExpression, parcelableFlags: IrExpression): IrExpression {
return irCall(androidSymbols.textUtilsWriteToParcel).apply {
putValueArgument(0, cs)
putValueArgument(1, p)
putValueArgument(2, parcelableFlags)
}
}
fun classGetClassLoader(receiver: IrExpression): IrExpression {
return irCall(androidSymbols.classGetClassLoader).apply {
dispatchReceiver = receiver
}
}
fun getTextUtilsCharSequenceCreator(): IrExpression {
return irGetField(null, androidSymbols.textUtilsCharSequenceCreator.owner)
}
}
@@ -0,0 +1,467 @@
/*
* 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.
*/
// This file was autogenerated based on android.jar, do not edit it directly.
package org.jetbrains.kotlin.parcelize.ir
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.types.starProjectedType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
// All of the IR declarations needed by the parcelize plugin. Note that the declarations are generated based on JVM descriptors and
// hence contain just enough information to produce correct JVM bytecode for *calls*. In particular, we omit generic types and
// supertypes, which are not needed to produce correct bytecode.
class AndroidSymbols(
val irBuiltIns: IrBuiltIns,
val arrayOfNulls: IrSimpleFunctionSymbol,
private val charSequence: IrClassSymbol,
private val moduleFragment: IrModuleFragment
) {
private val irFactory: IrFactory = IrFactoryImpl
private val javaIo: IrPackageFragment = createPackage("java.io")
private val javaLang: IrPackageFragment = createPackage("java.lang")
private val javaUtil: IrPackageFragment = createPackage("java.util")
private val kotlinJvm: IrPackageFragment = createPackage("kotlin.jvm")
private val androidOs: IrPackageFragment = createPackage("android.os")
private val androidUtil: IrPackageFragment = createPackage("android.util")
private val androidText: IrPackageFragment = createPackage("android.text")
private val androidOsBundle: IrClassSymbol =
createClass(androidOs, "Bundle", ClassKind.CLASS, Modality.FINAL)
private val androidOsIBinder: IrClassSymbol =
createClass(androidOs, "IBinder", ClassKind.INTERFACE, Modality.ABSTRACT)
val androidOsParcel: IrClassSymbol =
createClass(androidOs, "Parcel", ClassKind.CLASS, Modality.FINAL)
private val androidOsParcelFileDescriptor: IrClassSymbol =
createClass(androidOs, "ParcelFileDescriptor", ClassKind.CLASS, Modality.OPEN)
private val androidOsParcelable: IrClassSymbol =
createClass(androidOs, "Parcelable", ClassKind.INTERFACE, Modality.ABSTRACT)
private val androidOsPersistableBundle: IrClassSymbol =
createClass(androidOs, "PersistableBundle", ClassKind.CLASS, Modality.FINAL)
private val androidTextTextUtils: IrClassSymbol =
createClass(androidText, "TextUtils", ClassKind.CLASS, Modality.OPEN)
private val androidUtilSize: IrClassSymbol =
createClass(androidUtil, "Size", ClassKind.CLASS, Modality.FINAL)
private val androidUtilSizeF: IrClassSymbol =
createClass(androidUtil, "SizeF", ClassKind.CLASS, Modality.FINAL)
private val androidUtilSparseBooleanArray: IrClassSymbol =
createClass(androidUtil, "SparseBooleanArray", ClassKind.CLASS, Modality.OPEN)
private val javaIoFileDescriptor: IrClassSymbol =
createClass(javaIo, "FileDescriptor", ClassKind.CLASS, Modality.FINAL)
private val javaIoSerializable: IrClassSymbol =
createClass(javaIo, "Serializable", ClassKind.INTERFACE, Modality.ABSTRACT)
val javaLangClass: IrClassSymbol =
createClass(javaLang, "Class", ClassKind.CLASS, Modality.FINAL)
private val javaLangClassLoader: IrClassSymbol =
createClass(javaLang, "ClassLoader", ClassKind.CLASS, Modality.ABSTRACT)
private val javaUtilArrayList: IrClassSymbol =
createClass(javaUtil, "ArrayList", ClassKind.CLASS, Modality.OPEN)
private val javaUtilLinkedHashMap: IrClassSymbol =
createClass(javaUtil, "LinkedHashMap", ClassKind.CLASS, Modality.OPEN)
private val javaUtilLinkedHashSet: IrClassSymbol =
createClass(javaUtil, "LinkedHashSet", ClassKind.CLASS, Modality.OPEN)
private val javaUtilList: IrClassSymbol =
createClass(javaUtil, "List", ClassKind.INTERFACE, Modality.ABSTRACT)
private val javaUtilTreeMap: IrClassSymbol =
createClass(javaUtil, "TreeMap", ClassKind.CLASS, Modality.OPEN)
private val javaUtilTreeSet: IrClassSymbol =
createClass(javaUtil, "TreeSet", ClassKind.CLASS, Modality.OPEN)
val androidOsParcelableCreator: IrClassSymbol = irFactory.buildClass {
name = Name.identifier("Creator")
kind = ClassKind.INTERFACE
modality = Modality.ABSTRACT
}.apply {
createImplicitParameterDeclarationWithWrappedDescriptor()
val t = addTypeParameter("T", irBuiltIns.anyNType)
parent = androidOsParcelable.owner
addFunction("createFromParcel", t.defaultType, Modality.ABSTRACT).apply {
addValueParameter("source", androidOsParcel.defaultType)
}
addFunction(
"newArray", irBuiltIns.arrayClass.typeWith(t.defaultType.makeNullable()),
Modality.ABSTRACT
).apply {
addValueParameter("size", irBuiltIns.intType)
}
}.symbol
val kotlinKClassJava: IrPropertySymbol = irFactory.buildProperty {
name = Name.identifier("java")
}.apply {
parent = kotlinJvm
addGetter().apply {
addExtensionReceiver(irBuiltIns.kClassClass.starProjectedType)
returnType = javaLangClass.defaultType
}
}.symbol
val parcelCreateBinderArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("createBinderArray", irBuiltIns.arrayClass.typeWith(androidOsIBinder.defaultType)).symbol
val parcelCreateBinderArrayList: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("createBinderArrayList", javaUtilArrayList.defaultType).symbol
val parcelCreateBooleanArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction(
"createBooleanArray",
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.booleanType).defaultType
).symbol
val parcelCreateByteArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction(
"createByteArray",
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.byteType).defaultType
).symbol
val parcelCreateCharArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction(
"createCharArray",
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.charType).defaultType
).symbol
val parcelCreateDoubleArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction(
"createDoubleArray",
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.doubleType).defaultType
).symbol
val parcelCreateFloatArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction(
"createFloatArray",
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.floatType).defaultType
).symbol
val parcelCreateIntArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction(
"createIntArray",
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.intType).defaultType
).symbol
val parcelCreateLongArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction(
"createLongArray",
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.longType).defaultType
).symbol
val parcelCreateStringArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("createStringArray", irBuiltIns.arrayClass.typeWith(irBuiltIns.stringType)).symbol
val parcelCreateStringArrayList: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("createStringArrayList", javaUtilArrayList.defaultType).symbol
val parcelReadBundle: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readBundle", androidOsBundle.defaultType).symbol
val parcelReadByte: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readByte", irBuiltIns.byteType).symbol
val parcelReadDouble: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readDouble", irBuiltIns.doubleType).symbol
val parcelReadFileDescriptor: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readFileDescriptor", androidOsParcelFileDescriptor.defaultType).symbol
val parcelReadFloat: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readFloat", irBuiltIns.floatType).symbol
val parcelReadInt: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readInt", irBuiltIns.intType).symbol
val parcelReadLong: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readLong", irBuiltIns.longType).symbol
val parcelReadParcelable: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readParcelable", androidOsParcelable.defaultType).apply {
addValueParameter("loader", javaLangClassLoader.defaultType)
}.symbol
val parcelReadPersistableBundle: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readPersistableBundle", androidOsPersistableBundle.defaultType).symbol
val parcelReadSerializable: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readSerializable", javaIoSerializable.defaultType).symbol
val parcelReadSize: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readSize", androidUtilSize.defaultType).symbol
val parcelReadSizeF: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readSizeF", androidUtilSizeF.defaultType).symbol
val parcelReadSparseBooleanArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readSparseBooleanArray", androidUtilSparseBooleanArray.defaultType).symbol
val parcelReadString: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readString", irBuiltIns.stringType).symbol
val parcelReadStrongBinder: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readStrongBinder", androidOsIBinder.defaultType).symbol
val parcelReadValue: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readValue", irBuiltIns.anyNType).apply {
addValueParameter("loader", javaLangClassLoader.defaultType)
}.symbol
val parcelWriteBinderArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeBinderArray", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.arrayClass.typeWith(androidOsIBinder.defaultType))
}.symbol
val parcelWriteBinderList: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeBinderList", irBuiltIns.unitType).apply {
addValueParameter("val", javaUtilList.defaultType)
}.symbol
val parcelWriteBooleanArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeBooleanArray", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.booleanType).defaultType)
}.symbol
val parcelWriteBundle: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeBundle", irBuiltIns.unitType).apply {
addValueParameter("val", androidOsBundle.defaultType)
}.symbol
val parcelWriteByte: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeByte", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.byteType)
}.symbol
val parcelWriteByteArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeByteArray", irBuiltIns.unitType).apply {
addValueParameter("b", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.byteType).defaultType)
}.symbol
val parcelWriteCharArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeCharArray", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.charType).defaultType)
}.symbol
val parcelWriteDouble: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeDouble", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.doubleType)
}.symbol
val parcelWriteDoubleArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeDoubleArray", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.doubleType).defaultType)
}.symbol
val parcelWriteFileDescriptor: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeFileDescriptor", irBuiltIns.unitType).apply {
addValueParameter("val", javaIoFileDescriptor.defaultType)
}.symbol
val parcelWriteFloat: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeFloat", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.floatType)
}.symbol
val parcelWriteFloatArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeFloatArray", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.floatType).defaultType)
}.symbol
val parcelWriteInt: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeInt", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.intType)
}.symbol
val parcelWriteIntArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeIntArray", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.intType).defaultType)
}.symbol
val parcelWriteLong: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeLong", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.longType)
}.symbol
val parcelWriteLongArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeLongArray", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.longType).defaultType)
}.symbol
val parcelWriteParcelable: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeParcelable", irBuiltIns.unitType).apply {
addValueParameter("p", androidOsParcelable.defaultType)
addValueParameter("parcelableFlags", irBuiltIns.intType)
}.symbol
val parcelWritePersistableBundle: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writePersistableBundle", irBuiltIns.unitType).apply {
addValueParameter("val", androidOsPersistableBundle.defaultType)
}.symbol
val parcelWriteSerializable: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeSerializable", irBuiltIns.unitType).apply {
addValueParameter("s", javaIoSerializable.defaultType)
}.symbol
val parcelWriteSize: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeSize", irBuiltIns.unitType).apply {
addValueParameter("val", androidUtilSize.defaultType)
}.symbol
val parcelWriteSizeF: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeSizeF", irBuiltIns.unitType).apply {
addValueParameter("val", androidUtilSizeF.defaultType)
}.symbol
val parcelWriteSparseBooleanArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeSparseBooleanArray", irBuiltIns.unitType).apply {
addValueParameter("val", androidUtilSparseBooleanArray.defaultType)
}.symbol
val parcelWriteString: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeString", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.stringType)
}.symbol
val parcelWriteStringArray: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeStringArray", irBuiltIns.unitType).apply {
addValueParameter("val", irBuiltIns.arrayClass.typeWith(irBuiltIns.stringType))
}.symbol
val parcelWriteStringList: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeStringList", irBuiltIns.unitType).apply {
addValueParameter("val", javaUtilList.defaultType)
}.symbol
val parcelWriteStrongBinder: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeStrongBinder", irBuiltIns.unitType).apply {
addValueParameter("val", androidOsIBinder.defaultType)
}.symbol
val parcelWriteValue: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("writeValue", irBuiltIns.unitType).apply {
addValueParameter("v", irBuiltIns.anyNType)
}.symbol
val textUtilsWriteToParcel: IrSimpleFunctionSymbol =
androidTextTextUtils.owner.addFunction("writeToParcel", irBuiltIns.unitType, isStatic = true).apply {
addValueParameter("cs", charSequence.defaultType)
addValueParameter("p", androidOsParcel.defaultType)
addValueParameter("parcelableFlags", irBuiltIns.intType)
}.symbol
val classGetClassLoader: IrSimpleFunctionSymbol =
javaLangClass.owner.addFunction("getClassLoader", javaLangClassLoader.defaultType).symbol
val arrayListConstructor: IrConstructorSymbol = javaUtilArrayList.owner.addConstructor().apply {
addValueParameter("p_0", irBuiltIns.intType)
}.symbol
val arrayListAdd: IrSimpleFunctionSymbol =
javaUtilArrayList.owner.addFunction("add", irBuiltIns.booleanType).apply {
addValueParameter("p_0", irBuiltIns.anyNType)
}.symbol
val linkedHashMapConstructor: IrConstructorSymbol =
javaUtilLinkedHashMap.owner.addConstructor().apply {
addValueParameter("p_0", irBuiltIns.intType)
}.symbol
val linkedHashMapPut: IrSimpleFunctionSymbol =
javaUtilLinkedHashMap.owner.addFunction("put", irBuiltIns.anyNType).apply {
addValueParameter("p_0", irBuiltIns.anyNType)
addValueParameter("p_1", irBuiltIns.anyNType)
}.symbol
val linkedHashSetConstructor: IrConstructorSymbol =
javaUtilLinkedHashSet.owner.addConstructor().apply {
addValueParameter("p_0", irBuiltIns.intType)
}.symbol
val linkedHashSetAdd: IrSimpleFunctionSymbol =
javaUtilLinkedHashSet.owner.addFunction("add", irBuiltIns.booleanType).apply {
addValueParameter("p_0", irBuiltIns.anyNType)
}.symbol
val treeMapConstructor: IrConstructorSymbol = javaUtilTreeMap.owner.addConstructor().symbol
val treeMapPut: IrSimpleFunctionSymbol =
javaUtilTreeMap.owner.addFunction("put", irBuiltIns.anyNType).apply {
addValueParameter("p_0", irBuiltIns.anyNType)
addValueParameter("p_1", irBuiltIns.anyNType)
}.symbol
val treeSetConstructor: IrConstructorSymbol = javaUtilTreeSet.owner.addConstructor().symbol
val treeSetAdd: IrSimpleFunctionSymbol =
javaUtilTreeSet.owner.addFunction("add", irBuiltIns.booleanType).apply {
addValueParameter("p_0", irBuiltIns.anyNType)
}.symbol
val textUtilsCharSequenceCreator: IrFieldSymbol = androidTextTextUtils.owner.addField {
name = Name.identifier("CHAR_SEQUENCE_CREATOR")
type = androidOsParcelableCreator.defaultType
isStatic = true
}.symbol
private fun createPackage(packageName: String): IrPackageFragment =
IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(
moduleFragment.descriptor,
FqName(packageName)
)
private fun createClass(
irPackage: IrPackageFragment,
shortName: String,
classKind: ClassKind,
classModality: Modality
): IrClassSymbol = irFactory.buildClass {
name = Name.identifier(shortName)
kind = classKind
modality = classModality
}.apply {
parent = irPackage
createImplicitParameterDeclarationWithWrappedDescriptor()
}.symbol
fun createBuilder(
symbol: IrSymbol,
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET
) = AndroidIrBuilder(this, symbol, startOffset, endOffset)
}
@@ -0,0 +1,288 @@
/*
* 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.jvm.codegen.psiElement
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.parcelize.serializers.RAW_VALUE_ANNOTATION_FQNAME
class IrParcelSerializerFactory(symbols: AndroidSymbols) {
/**
* Resolve the given [irType] to a corresponding [IrParcelSerializer]. This depends on the TypeParcelers which
* are currently in [scope], as well as the type of the enclosing Parceleable class [parcelizeType], which is needed
* to get a class loader for reflection based serialization. Beyond this, we need to know whether to allow
* using read/writeValue for serialization (if [strict] is false). Beyond this, we need to know whether we are
* producing parcelers for properties of a Parcelable (if [toplevel] is true), or for a complete Parcelable.
*/
fun get(
irType: IrType,
scope: IrParcelerScope?,
parcelizeType: IrType,
strict: Boolean = false,
toplevel: Boolean = false
): IrParcelSerializer {
fun strict() = strict && !irType.hasAnnotation(RAW_VALUE_ANNOTATION_FQNAME)
scope.getCustomSerializer(irType)?.let { parceler ->
return IrCustomParcelSerializer(parceler)
}
// TODO inline classes
val classifier = irType.erasedUpperBound
val classifierFqName = classifier.fqNameWhenAvailable?.asString()
when (classifierFqName) {
// Built-in parcel serializers
"kotlin.String", "java.lang.String" ->
return stringSerializer
"kotlin.CharSequence", "java.lang.CharSequence" ->
return charSequenceSerializer
"android.os.Bundle" ->
return bundleSerializer
"android.os.PersistableBundle" ->
return persistableBundleSerializer
// Non-nullable built-in serializers
"kotlin.Byte", "java.lang.Byte" ->
return wrapNullableSerializerIfNeeded(irType, byteSerializer)
"kotlin.Boolean", "java.lang.Boolean" ->
return wrapNullableSerializerIfNeeded(irType, booleanSerializer)
"kotlin.Char", "java.lang.Character" ->
return wrapNullableSerializerIfNeeded(irType, charSerializer)
"kotlin.Short", "java.lang.Short" ->
return wrapNullableSerializerIfNeeded(irType, shortSerializer)
"kotlin.Int", "java.lang.Integer" ->
return wrapNullableSerializerIfNeeded(irType, intSerializer)
"kotlin.Long", "java.lang.Long" ->
return wrapNullableSerializerIfNeeded(irType, longSerializer)
"kotlin.Float", "java.lang.Float" ->
return wrapNullableSerializerIfNeeded(irType, floatSerializer)
"kotlin.Double", "java.lang.Double" ->
return wrapNullableSerializerIfNeeded(irType, doubleSerializer)
"java.io.FileDescriptor" ->
return wrapNullableSerializerIfNeeded(irType, fileDescriptorSerializer)
"android.util.Size" ->
return wrapNullableSerializerIfNeeded(irType, sizeSerializer)
"android.util.SizeF" ->
return wrapNullableSerializerIfNeeded(irType, sizeFSerializer)
// Built-in non-parameterized container types.
"kotlin.IntArray" ->
if (!scope.hasCustomSerializer(irBuiltIns.intType))
return intArraySerializer
"kotlin.BooleanArray" ->
if (!scope.hasCustomSerializer(irBuiltIns.booleanType))
return booleanArraySerializer
"kotlin.ByteArray" ->
if (!scope.hasCustomSerializer(irBuiltIns.byteType))
return byteArraySerializer
"kotlin.CharArray" ->
if (!scope.hasCustomSerializer(irBuiltIns.charType))
return charArraySerializer
"kotlin.FloatArray" ->
if (!scope.hasCustomSerializer(irBuiltIns.floatType))
return floatArraySerializer
"kotlin.DoubleArray" ->
if (!scope.hasCustomSerializer(irBuiltIns.doubleType))
return doubleArraySerializer
"kotlin.LongArray" ->
if (!scope.hasCustomSerializer(irBuiltIns.longType))
return longArraySerializer
"android.util.SparseBooleanArray" ->
if (!scope.hasCustomSerializer(irBuiltIns.booleanType))
return sparseBooleanArraySerializer
}
// Generic container types
when (classifierFqName) {
// Apart from kotlin.Array and kotlin.ShortArray, these will only be hit if we have a
// special parceler for the element type.
"kotlin.Array", "kotlin.ShortArray", "kotlin.IntArray",
"kotlin.BooleanArray", "kotlin.ByteArray", "kotlin.CharArray",
"kotlin.FloatArray", "kotlin.DoubleArray", "kotlin.LongArray" -> {
val elementType = irType.getArrayElementType(irBuiltIns)
if (!scope.hasCustomSerializer(elementType)) {
when (elementType.erasedUpperBound.fqNameWhenAvailable?.asString()) {
"java.lang.String", "kotlin.String" ->
return stringArraySerializer
"android.os.IBinder" ->
return iBinderArraySerializer
}
}
val arrayType =
if (classifier.defaultType.isPrimitiveArray()) classifier.defaultType else irBuiltIns.arrayClass.typeWith(elementType)
return IrArrayParcelSerializer(arrayType, elementType, get(elementType, scope, parcelizeType, strict()))
}
// This will only be hit if we have a custom serializer for booleans
"android.util.SparseBooleanArray" ->
return IrSparseArrayParcelSerializer(
classifier,
irBuiltIns.booleanType,
get(irBuiltIns.booleanType, scope, parcelizeType, strict())
)
"android.util.SparseIntArray" ->
return IrSparseArrayParcelSerializer(
classifier,
irBuiltIns.intType,
get(irBuiltIns.intType, scope, parcelizeType, strict())
)
"android.util.SparseLongArray" ->
return IrSparseArrayParcelSerializer(
classifier,
irBuiltIns.longType,
get(irBuiltIns.longType, scope, parcelizeType, strict())
)
"android.util.SparseArray" -> {
val elementType = (irType as IrSimpleType).arguments.single().upperBound(irBuiltIns)
return IrSparseArrayParcelSerializer(classifier, elementType, get(elementType, scope, parcelizeType, strict()))
}
// TODO: More java collections?
// TODO: Add tests for all of these types, not just some common ones...
// FIXME: Is the support for ArrayDeque missing in the old BE?
"kotlin.collections.MutableList", "kotlin.collections.List", "java.util.List",
"kotlin.collections.ArrayList", "java.util.ArrayList",
"kotlin.collections.ArrayDeque", "java.util.ArrayDeque",
"kotlin.collections.MutableSet", "kotlin.collections.Set", "java.util.Set",
"kotlin.collections.HashSet", "java.util.HashSet",
"kotlin.collections.LinkedHashSet", "java.util.LinkedHashSet",
"java.util.NavigableSet", "java.util.SortedSet" -> {
val elementType = (irType as IrSimpleType).arguments.single().upperBound(irBuiltIns)
if (!scope.hasCustomSerializer(elementType) && classifierFqName in setOf(
"kotlin.collections.List", "kotlin.collections.MutableList", "kotlin.collections.ArrayList",
"java.util.List", "java.util.ArrayList"
)
) {
when (elementType.erasedUpperBound.fqNameWhenAvailable?.asString()) {
"android.os.IBinder" ->
return iBinderListSerializer
"kotlin.String", "java.lang.String" ->
return stringListSerializer
}
}
return wrapNullableSerializerIfNeeded(
irType,
IrListParcelSerializer(classifier, elementType, get(elementType, scope, parcelizeType, strict()))
)
}
"kotlin.collections.MutableMap", "kotlin.collections.Map", "java.util.Map",
"kotlin.collections.HashMap", "java.util.HashMap",
"kotlin.collections.LinkedHashMap", "java.util.LinkedHashMap",
"java.util.SortedMap", "java.util.NavigableMap", "java.util.TreeMap",
"java.util.concurrent.ConcurrentHashMap" -> {
val keyType = (irType as IrSimpleType).arguments[0].upperBound(irBuiltIns)
val valueType = irType.arguments[1].upperBound(irBuiltIns)
val parceler =
IrMapParcelSerializer(
classifier,
keyType,
valueType,
get(keyType, scope, parcelizeType, strict()),
get(valueType, scope, parcelizeType, strict())
)
return wrapNullableSerializerIfNeeded(irType, parceler)
}
}
// Generic parcelable types
when {
classifier.isSubclassOfFqName("android.os.Parcelable")
// Avoid infinite loops when deriving parcelers for enum or object classes.
&& !(toplevel && (classifier.isObject || classifier.isEnumClass)) -> {
// We try to use writeToParcel/createFromParcel directly whenever possible, but there are some caveats.
//
// According to the JLS, changing a class from final to non-final is a binary compatible change, hence we
// cannot use the writeToParcel/createFromParcel methods directly when serializing classes from external
// dependencies. This issue was originally reported in KT-20029.
//
// Conversely, we can and should apply this optimization to all classes in the current module which
// implement Parcelable (KT-20030). There are two cases to consider. If the class is annotated with
// @Parcelize, we will create the corresponding methods/fields ourselves, before we generate the code
// for writeToParcel/createFromParcel. For Java classes (or compiled Kotlin classes annotated with
// @Parcelize), we'll have a field in the class itself. Finally, with Parcelable instances which were
// manually implemented in Kotlin, we'll instead have an @JvmField property getter in the companion object.
return if (classifier.modality == Modality.FINAL && classifier.psiElement != null
&& (classifier.isParcelize || classifier.hasCreatorField)
) {
wrapNullableSerializerIfNeeded(irType, IrEfficientParcelableParcelSerializer(classifier))
} else {
// In all other cases, we have to use the generic methods in Parcel, which use reflection internally.
IrGenericParcelableParcelSerializer(parcelizeType)
}
}
classifier.isSubclassOfFqName("android.os.IBinder") ->
return iBinderSerializer
classifier.isObject ->
return IrObjectParcelSerializer(classifier)
classifier.isEnumClass ->
return wrapNullableSerializerIfNeeded(irType, IrEnumParcelSerializer(classifier))
classifier.isSubclassOfFqName("java.io.Serializable")
// Functions and Continuations are always serializable.
|| irType.isFunctionTypeOrSubtype() || irType.isSuspendFunctionTypeOrSubtype() ->
return serializableSerializer
strict() ->
throw IllegalArgumentException("Illegal type, could not find a specific serializer for ${irType.render()}")
else ->
return IrGenericValueParcelSerializer(parcelizeType)
}
}
private fun wrapNullableSerializerIfNeeded(irType: IrType, serializer: IrParcelSerializer) =
if (irType.isNullable()) IrNullAwareParcelSerializer(serializer) else serializer
private val irBuiltIns: IrBuiltIns = symbols.irBuiltIns
private val stringArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateStringArray, symbols.parcelWriteStringArray)
private val stringListSerializer = IrSimpleParcelSerializer(symbols.parcelCreateStringArrayList, symbols.parcelWriteStringList)
private val iBinderSerializer = IrSimpleParcelSerializer(symbols.parcelReadStrongBinder, symbols.parcelWriteStrongBinder)
private val iBinderArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateBinderArray, symbols.parcelWriteBinderArray)
private val iBinderListSerializer = IrSimpleParcelSerializer(symbols.parcelCreateBinderArrayList, symbols.parcelWriteBinderList)
private val serializableSerializer = IrSimpleParcelSerializer(symbols.parcelReadSerializable, symbols.parcelWriteSerializable)
private val stringSerializer = IrSimpleParcelSerializer(symbols.parcelReadString, symbols.parcelWriteString)
private val byteSerializer = IrSimpleParcelSerializer(symbols.parcelReadByte, symbols.parcelWriteByte)
private val intSerializer = IrSimpleParcelSerializer(symbols.parcelReadInt, symbols.parcelWriteInt)
private val longSerializer = IrSimpleParcelSerializer(symbols.parcelReadLong, symbols.parcelWriteLong)
private val floatSerializer = IrSimpleParcelSerializer(symbols.parcelReadFloat, symbols.parcelWriteFloat)
private val doubleSerializer = IrSimpleParcelSerializer(symbols.parcelReadDouble, symbols.parcelWriteDouble)
private val intArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateIntArray, symbols.parcelWriteIntArray)
private val booleanArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateBooleanArray, symbols.parcelWriteBooleanArray)
private val byteArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateByteArray, symbols.parcelWriteByteArray)
private val charArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateCharArray, symbols.parcelWriteCharArray)
private val doubleArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateDoubleArray, symbols.parcelWriteDoubleArray)
private val floatArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateFloatArray, symbols.parcelWriteFloatArray)
private val longArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateLongArray, symbols.parcelWriteLongArray)
// Primitive types without dedicated read/write methods need an additional cast.
private val booleanSerializer = IrWrappedIntParcelSerializer(irBuiltIns.booleanType)
private val shortSerializer = IrWrappedIntParcelSerializer(irBuiltIns.shortType)
private val charSerializer = IrWrappedIntParcelSerializer(irBuiltIns.charType)
private val charSequenceSerializer = IrCharSequenceParcelSerializer()
// TODO The old backend uses the hidden "read/writeRawFileDescriptor" methods.
private val fileDescriptorSerializer = IrSimpleParcelSerializer(symbols.parcelReadFileDescriptor, symbols.parcelWriteFileDescriptor)
private val sizeSerializer = IrSimpleParcelSerializer(symbols.parcelReadSize, symbols.parcelWriteSize)
private val sizeFSerializer = IrSimpleParcelSerializer(symbols.parcelReadSizeF, symbols.parcelWriteSizeF)
private val bundleSerializer = IrSimpleParcelSerializer(symbols.parcelReadBundle, symbols.parcelWriteBundle)
private val persistableBundleSerializer =
IrSimpleParcelSerializer(symbols.parcelReadPersistableBundle, symbols.parcelWritePersistableBundle)
private val sparseBooleanArraySerializer =
IrSimpleParcelSerializer(symbols.parcelReadSparseBooleanArray, symbols.parcelWriteSparseBooleanArray)
}
@@ -0,0 +1,536 @@
/*
* 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.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase.Companion.CREATOR_NAME
interface IrParcelSerializer {
fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression
fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression
}
fun AndroidIrBuilder.readParcelWith(serializer: IrParcelSerializer, parcel: IrValueDeclaration): IrExpression {
return with(serializer) { readParcel(parcel) }
}
fun AndroidIrBuilder.writeParcelWith(
serializer: IrParcelSerializer,
parcel: IrValueDeclaration,
flags: IrValueDeclaration,
value: IrExpression
): IrExpression {
return with(serializer) { writeParcel(parcel, flags, value) }
}
// Creates a serializer from a pair of parcel methods of the form reader()T and writer(T)V.
class IrSimpleParcelSerializer(private val reader: IrSimpleFunctionSymbol, private val writer: IrSimpleFunctionSymbol) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return irCall(reader).apply { dispatchReceiver = irGet(parcel) }
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return irCall(writer).apply {
dispatchReceiver = irGet(parcel)
putValueArgument(0, value)
}
}
}
// Serialize a value of the primitive [parcelType] by coercion to int.
class IrWrappedIntParcelSerializer(private val parcelType: IrType) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return if (parcelType.isBoolean()) {
irNotEquals(parcelReadInt(irGet(parcel)), irInt(0))
} else {
val conversion = context.irBuiltIns.intClass.functions.first { function ->
function.owner.name.asString() == "to${parcelType.getClass()!!.name}"
}
irCall(conversion).apply {
dispatchReceiver = parcelReadInt(irGet(parcel))
}
}
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
parcelWriteInt(
irGet(parcel),
if (parcelType.isBoolean()) {
irIfThenElse(context.irBuiltIns.intType, value, irInt(1), irInt(0))
} else {
val conversion = parcelType.classOrNull!!.functions.first { function ->
function.owner.name.asString() == "toInt"
}
irCall(conversion).apply { dispatchReceiver = value }
}
)
}
// Wraps a non-null aware parceler to handle nullable types.
class IrNullAwareParcelSerializer(private val serializer: IrParcelSerializer) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
val nonNullResult = readParcelWith(serializer, parcel)
return irIfThenElse(
nonNullResult.type.makeNullable(),
irEquals(parcelReadInt(irGet(parcel)), irInt(0)),
irNull(),
nonNullResult
)
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return irLetS(value) { irValueSymbol ->
irIfNull(
context.irBuiltIns.unitType,
irGet(irValueSymbol.owner),
parcelWriteInt(irGet(parcel), irInt(0)),
irBlock {
+parcelWriteInt(irGet(parcel), irInt(1))
+writeParcelWith(serializer, parcel, flags, irGet(irValueSymbol.owner))
}
)
}
}
}
// Parcel serializer for object classes. We avoid empty parcels by writing a dummy value. Not null-safe.
class IrObjectParcelSerializer(private val objectClass: IrClass) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression
// Avoid empty parcels
{
return irBlock {
+parcelReadInt(irGet(parcel))
+irGetObject(objectClass.symbol)
}
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return parcelWriteInt(irGet(parcel), irInt(1))
}
}
// Parcel serializer for classes with a default constructor. We avoid empty parcels by writing a dummy value. Not null-safe.
class IrNoParameterClassParcelSerializer(private val irClass: IrClass) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
val defaultConstructor = irClass.primaryConstructor!!
return irBlock {
+parcelReadInt(irGet(parcel))
+irCall(defaultConstructor)
}
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return parcelWriteInt(irGet(parcel), irInt(1))
}
}
// Parcel serializer for enum classes. Not null-safe.
class IrEnumParcelSerializer(enumClass: IrClass) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return irCall(enumValueOf).apply {
putValueArgument(0, parcelReadString(irGet(parcel)))
}
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return parcelWriteString(irGet(parcel), irCall(enumName).apply {
dispatchReceiver = value
})
}
private val enumValueOf: IrFunctionSymbol =
enumClass.functions.single { function ->
function.name.asString() == "valueOf" && function.dispatchReceiverParameter == null
&& function.extensionReceiverParameter == null && function.valueParameters.size == 1
&& function.valueParameters.single().type.isString()
}.symbol
private val enumName: IrFunctionSymbol = enumClass.getPropertyGetter("name")!!
}
// Parcel serializer for the java CharSequence interface.
class IrCharSequenceParcelSerializer : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return parcelableCreatorCreateFromParcel(getTextUtilsCharSequenceCreator(), irGet(parcel))
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return textUtilsWriteToParcel(value, irGet(parcel), irGet(flags))
}
}
// Parcel serializer for Parcelables in the same module, which accesses the writeToParcel/createFromParcel methods without reflection.
class IrEfficientParcelableParcelSerializer(private val irClass: IrClass) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
val creator: IrExpression = irClass.fields.find { it.name == CREATOR_NAME }?.let { creatorField ->
irGetField(null, creatorField)
} ?: irCall(irClass.creatorGetter!!).apply {
dispatchReceiver = irGetObject(irClass.companionObject()!!.symbol)
}
return parcelableCreatorCreateFromParcel(creator, irGet(parcel))
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return parcelableWriteToParcel(irClass, value, irGet(parcel), irGet(flags))
}
}
// Parcel serializer for Parcelables using reflection.
// This needs a reference to the parcelize type itself in order to find the correct class loader to use, see KT-20027.
class IrGenericParcelableParcelSerializer(private val parcelizeType: IrType) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return parcelReadParcelable(irGet(parcel), classGetClassLoader(javaClassReference(parcelizeType)))
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return parcelWriteParcelable(irGet(parcel), value, irGet(flags))
}
}
// Fallback parcel serializer for unknown types using Parcel.readValue/writeValue.
// This needs a reference to the parcelize type itself in order to find the correct class loader to use, see KT-20027.
class IrGenericValueParcelSerializer(private val parcelizeType: IrType) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return parcelReadValue(irGet(parcel), classGetClassLoader(javaClassReference(parcelizeType)))
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return parcelWriteValue(irGet(parcel), value)
}
}
// Parcel serializer using a custom Parceler object.
class IrCustomParcelSerializer(private val parcelerObject: IrClass) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return parcelerCreate(parcelerObject, parcel)
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return parcelerWrite(parcelerObject, parcel, flags, value)
}
}
// Parcel serializer for array types. This handles both primitive array types (for ShortArray and for primitive arrays using custom element
// parcelers) as well as boxed arrays.
// TODO: Unsigned array types
class IrArrayParcelSerializer(
private val arrayType: IrType,
private val elementType: IrType,
private val elementSerializer: IrParcelSerializer
) : IrParcelSerializer {
private fun AndroidIrBuilder.newArray(size: IrExpression): IrExpression {
val arrayConstructor: IrFunctionSymbol = if (arrayType.isBoxedArray) {
androidSymbols.arrayOfNulls
} else {
arrayType.classOrNull!!.constructors.single { it.owner.valueParameters.size == 1 }
}
return irCall(arrayConstructor, arrayType).apply {
if (typeArgumentsCount != 0)
putTypeArgument(0, elementType)
putValueArgument(0, size)
}
}
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return irBlock {
val arraySize = irTemporary(parcelReadInt(irGet(parcel)))
val arrayTemporary = irTemporary(newArray(irGet(arraySize)))
forUntil(irGet(arraySize)) { index ->
val setter = arrayType.classOrNull!!.getSimpleFunction("set")!!
+irCall(setter).apply {
dispatchReceiver = irGet(arrayTemporary)
putValueArgument(0, irGet(index))
putValueArgument(1, readParcelWith(elementSerializer, parcel))
}
}
+irGet(arrayTemporary)
}
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return irBlock {
val arrayTemporary = irTemporary(value)
val arraySizeSymbol = arrayType.classOrNull!!.getPropertyGetter("size")!!
val arraySize = irTemporary(irCall(arraySizeSymbol).apply {
dispatchReceiver = irGet(arrayTemporary)
})
+parcelWriteInt(irGet(parcel), irGet(arraySize))
forUntil(irGet(arraySize)) { index ->
val getter = context.irBuiltIns.arrayClass.getSimpleFunction("get")!!
val element = irCall(getter, elementType).apply {
dispatchReceiver = irGet(arrayTemporary)
putValueArgument(0, irGet(index))
}
+writeParcelWith(elementSerializer, parcel, flags, element)
}
}
}
}
// Parcel serializer for android SparseArrays. Note that this also needs to handle BooleanSparseArray, in case of a custom element parceler.
class IrSparseArrayParcelSerializer(
private val sparseArrayClass: IrClass,
private val elementType: IrType,
private val elementSerializer: IrParcelSerializer
) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return irBlock {
val remainingSizeTemporary = irTemporaryVar(parcelReadInt(irGet(parcel)))
val sparseArrayConstructor = sparseArrayClass.constructors.first { irConstructor ->
irConstructor.valueParameters.size == 1 && irConstructor.valueParameters.single().type.isInt()
}
val constructorCall = if (sparseArrayClass.typeParameters.isEmpty())
irCall(sparseArrayConstructor)
else
irCallConstructor(sparseArrayConstructor.symbol, listOf(elementType))
val arrayTemporary = irTemporary(constructorCall.apply {
putValueArgument(0, irGet(remainingSizeTemporary))
})
+irWhile().apply {
condition = irNotEquals(irGet(remainingSizeTemporary), irInt(0))
body = irBlock {
val sparseArrayPut = sparseArrayClass.functions.first { function ->
function.name.asString() == "put" && function.valueParameters.size == 2
}
+irCall(sparseArrayPut).apply {
dispatchReceiver = irGet(arrayTemporary)
putValueArgument(0, parcelReadInt(irGet(parcel)))
putValueArgument(1, readParcelWith(elementSerializer, parcel))
}
val dec = context.irBuiltIns.intClass.getSimpleFunction("dec")!!
+irSetVar(remainingSizeTemporary.symbol, irCall(dec).apply {
dispatchReceiver = irGet(remainingSizeTemporary)
})
}
}
+irGet(arrayTemporary)
}
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return irBlock {
val sizeFunction = sparseArrayClass.functions.first { function ->
function.name.asString() == "size" && function.valueParameters.isEmpty()
}
val keyAtFunction = sparseArrayClass.functions.first { function ->
function.name.asString() == "keyAt" && function.valueParameters.size == 1
}
val valueAtFunction = sparseArrayClass.functions.first { function ->
function.name.asString() == "valueAt" && function.valueParameters.size == 1
}
val arrayTemporary = irTemporary(value)
val sizeTemporary = irTemporary(irCall(sizeFunction).apply {
dispatchReceiver = irGet(arrayTemporary)
})
+parcelWriteInt(irGet(parcel), irGet(sizeTemporary))
forUntil(irGet(sizeTemporary)) { index ->
+parcelWriteInt(irGet(parcel), irCall(keyAtFunction).apply {
dispatchReceiver = irGet(arrayTemporary)
putValueArgument(0, irGet(index))
})
+writeParcelWith(elementSerializer, parcel, flags, irCall(valueAtFunction.symbol, elementType).apply {
dispatchReceiver = irGet(arrayTemporary)
putValueArgument(0, irGet(index))
})
}
}
}
}
// Parcel serializer for all lists supported by Parcelize. List interfaces use hard-coded default implementations for deserialization.
// List maps to ArrayList, Set maps to LinkedHashSet, NavigableSet and SortedSet map to TreeSet.
class IrListParcelSerializer(
private val irClass: IrClass,
private val elementType: IrType,
private val elementSerializer: IrParcelSerializer
) : IrParcelSerializer {
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
val sizeFunction = irClass.getPropertyGetter("size")!!
val iteratorFunction = irClass.getMethodWithoutArguments("iterator")
val iteratorClass = iteratorFunction.returnType.erasedUpperBound
val iteratorHasNext = iteratorClass.getMethodWithoutArguments("hasNext")
val iteratorNext = iteratorClass.getMethodWithoutArguments("next")
return irBlock {
val list = irTemporary(value)
+parcelWriteInt(irGet(parcel), irCall(sizeFunction).apply {
dispatchReceiver = irGet(list)
})
val iterator = irTemporary(irCall(iteratorFunction).apply {
dispatchReceiver = irGet(list)
})
+irWhile().apply {
condition = irCall(iteratorHasNext).apply { dispatchReceiver = irGet(iterator) }
body = writeParcelWith(elementSerializer, parcel, flags, irCall(iteratorNext.symbol, elementType).apply {
dispatchReceiver = irGet(iterator)
})
}
}
}
private fun listSymbols(symbols: AndroidSymbols): Pair<IrConstructorSymbol, IrSimpleFunctionSymbol> {
// If the IrClass refers to a concrete type, try to find a constructor with capacity or fall back
// the the default constructor if none exist.
if (!irClass.isJvmInterface) {
val constructor = irClass.constructors.find { constructor ->
constructor.valueParameters.size == 1 && constructor.valueParameters.single().type.isInt()
} ?: irClass.constructors.first { constructor -> constructor.valueParameters.isEmpty() }
val add = irClass.functions.first { function ->
function.name.asString() == "add" && function.valueParameters.size == 1
}
return constructor.symbol to add.symbol
}
return when (irClass.fqNameWhenAvailable?.asString()) {
"kotlin.collections.MutableList", "kotlin.collections.List", "java.util.List" ->
symbols.arrayListConstructor to symbols.arrayListAdd
"kotlin.collections.MutableSet", "kotlin.collections.Set", "java.util.Set" ->
symbols.linkedHashSetConstructor to symbols.linkedHashSetAdd
"java.util.NavigableSet", "java.util.SortedSet" ->
symbols.treeSetConstructor to symbols.treeSetAdd
else -> error("Unknown list interface type: ${irClass.render()}")
}
}
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return irBlock {
val (constructorSymbol, addSymbol) = listSymbols(androidSymbols)
val sizeTemporary = irTemporary(parcelReadInt(irGet(parcel)))
val list = irTemporary(irCall(constructorSymbol).apply {
if (constructorSymbol.owner.valueParameters.isNotEmpty())
putValueArgument(0, irGet(sizeTemporary))
})
forUntil(irGet(sizeTemporary)) {
+irCall(addSymbol).apply {
dispatchReceiver = irGet(list)
putValueArgument(0, readParcelWith(elementSerializer, parcel))
}
}
+irGet(list)
}
}
}
// Parcel serializer for all maps supported by Parcelize. Map interfaces use hard-coded default implementations for deserialization.
// Map uses LinkedHashMap, while NavigableMap and SortedMap use to TreeMap.
class IrMapParcelSerializer(
private val irClass: IrClass,
private val keyType: IrType,
private val valueType: IrType,
private val keySerializer: IrParcelSerializer,
private val valueSerializer: IrParcelSerializer
) : IrParcelSerializer {
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
val sizeFunction = irClass.getPropertyGetter("size")!!
val entriesFunction = irClass.getPropertyGetter("entries")!!
val entrySetClass = entriesFunction.owner.returnType.erasedUpperBound
val iteratorFunction = entrySetClass.getMethodWithoutArguments("iterator")
val iteratorClass = iteratorFunction.returnType.erasedUpperBound
val iteratorHasNext = iteratorClass.getMethodWithoutArguments("hasNext")
val iteratorNext = iteratorClass.getMethodWithoutArguments("next")
val elementClass =
(entriesFunction.owner.returnType as IrSimpleType).arguments.single().upperBound(context.irBuiltIns).erasedUpperBound
val elementKey = elementClass.getPropertyGetter("key")!!
val elementValue = elementClass.getPropertyGetter("value")!!
return irBlock {
val list = irTemporary(value)
+parcelWriteInt(irGet(parcel), irCall(sizeFunction).apply {
dispatchReceiver = irGet(list)
})
val iterator = irTemporary(irCall(iteratorFunction).apply {
dispatchReceiver = irCall(entriesFunction).apply {
dispatchReceiver = irGet(list)
}
})
+irWhile().apply {
condition = irCall(iteratorHasNext).apply { dispatchReceiver = irGet(iterator) }
body = irBlock {
val element = irTemporary(irCall(iteratorNext).apply {
dispatchReceiver = irGet(iterator)
})
+writeParcelWith(keySerializer, parcel, flags, irCall(elementKey, keyType).apply {
dispatchReceiver = irGet(element)
})
+writeParcelWith(valueSerializer, parcel, flags, irCall(elementValue, valueType).apply {
dispatchReceiver = irGet(element)
})
}
}
}
}
private fun mapSymbols(symbols: AndroidSymbols): Pair<IrConstructorSymbol, IrSimpleFunctionSymbol> {
// If the IrClass refers to a concrete type, try to find a constructor with capacity or fall back
// the the default constructor if none exist.
if (!irClass.isJvmInterface) {
val constructor = irClass.constructors.find { constructor ->
constructor.valueParameters.size == 1 && constructor.valueParameters.single().type.isInt()
} ?: irClass.constructors.find { constructor ->
constructor.valueParameters.isEmpty()
}!!
val put = irClass.functions.first { function ->
function.name.asString() == "put" && function.valueParameters.size == 2
}
return constructor.symbol to put.symbol
}
return when (irClass.fqNameWhenAvailable?.asString()) {
"kotlin.collections.MutableMap", "kotlin.collections.Map", "java.util.Map" ->
symbols.linkedHashMapConstructor to symbols.linkedHashMapPut
"java.util.SortedMap", "java.util.NavigableMap" ->
symbols.treeMapConstructor to symbols.treeMapPut
else -> error("Unknown map interface type: ${irClass.render()}")
}
}
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return irBlock {
val (constructorSymbol, putSymbol) = mapSymbols(androidSymbols)
val sizeTemporary = irTemporary(parcelReadInt(irGet(parcel)))
val map = irTemporary(irCall(constructorSymbol).apply {
if (constructorSymbol.owner.valueParameters.isNotEmpty())
putValueArgument(0, irGet(sizeTemporary))
})
forUntil(irGet(sizeTemporary)) {
+irCall(putSymbol).apply {
dispatchReceiver = irGet(map)
putValueArgument(0, readParcelWith(keySerializer, parcel))
putValueArgument(1, readParcelWith(valueSerializer, parcel))
}
}
+irGet(map)
}
}
}
@@ -0,0 +1,58 @@
/*
* 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.ir.declarations.IrAnnotationContainer
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.getAnnotation
import org.jetbrains.kotlin.parcelize.ParcelizeAnnotationChecker
// Keep track of all custom parcelers which are currently in scope.
// Note that custom parcelers are resolved in *reverse* lexical order.
class IrParcelerScope(private val parent: IrParcelerScope? = null) {
private val typeParcelers = mutableMapOf<IrType, IrClass>()
fun add(type: IrType, parceler: IrClass) {
typeParcelers.putIfAbsent(type, parceler)
}
fun get(type: IrType): IrClass? =
parent?.get(type) ?: typeParcelers[type]
}
fun IrParcelerScope?.getCustomSerializer(irType: IrType): IrClass? {
return irType.getAnnotation(ParcelizeAnnotationChecker.WRITE_WITH_FQNAME)?.let { writeWith ->
(writeWith.type as IrSimpleType).arguments.single().typeOrNull!!.getClass()!!
} ?: this?.get(irType)
}
fun IrParcelerScope?.hasCustomSerializer(irType: IrType): Boolean {
return getCustomSerializer(irType) != null
}
fun IrAnnotationContainer.getParcelerScope(parent: IrParcelerScope? = null): IrParcelerScope? {
val typeParcelerAnnotations = annotations.filterTo(mutableListOf()) {
it.symbol.owner.constructedClass.fqNameWhenAvailable == ParcelizeAnnotationChecker.TYPE_PARCELER_FQNAME
}
if (typeParcelerAnnotations.isEmpty())
return parent
val scope = IrParcelerScope(parent)
for (annotation in typeParcelerAnnotations) {
val (mappedType, parcelerType) = (annotation.type as IrSimpleType).arguments.map { it.typeOrNull!! }
scope.add(mappedType, parcelerType.getClass()!!)
}
return scope
}
@@ -0,0 +1,322 @@
/*
* 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.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.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.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)
private val symbolMap = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunctionSymbol>()
private val irFactory: IrFactory = IrFactoryImpl
fun transform(moduleFragment: IrModuleFragment) {
moduleFragment.accept(this, null)
deferredOperations.forEach { it() }
// Remap broken stubs, which psi2ir generates for the synthetic descriptors coming from the ParcelizeResolveExtension.
moduleFragment.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
val remappedSymbol = symbolMap[expression.symbol]
?: return super.visitCall(expression)
return IrCallImpl(
expression.startOffset, expression.endOffset, expression.type, remappedSymbol,
expression.typeArgumentsCount, expression.valueArgumentsCount, expression.origin,
expression.superQualifierSymbol
).apply {
copyTypeAndValueArgumentsFrom(expression)
}
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
val remappedSymbol = symbolMap[expression.symbol]
val remappedReflectionTarget = expression.reflectionTarget?.let { symbolMap[it] }
if (remappedSymbol == null && remappedReflectionTarget == null)
return super.visitFunctionReference(expression)
return IrFunctionReferenceImpl(
expression.startOffset, expression.endOffset, expression.type, remappedSymbol ?: expression.symbol,
expression.typeArgumentsCount, expression.valueArgumentsCount, remappedReflectionTarget,
expression.origin
).apply {
copyTypeAndValueArgumentsFrom(expression)
}
}
override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
// Remap overridden symbols, otherwise the code might break in BridgeLowering
declaration.overriddenSymbols = declaration.overriddenSymbols.map { symbol ->
symbolMap[symbol] ?: symbol
}
return super.visitSimpleFunction(declaration)
}
})
}
override fun visitElement(element: IrElement) = element.acceptChildren(this, null)
override fun visitClass(declaration: IrClass) {
declaration.acceptChildren(this, null)
if (!declaration.isParcelize)
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())
}
if (declaration.descriptor.hasSyntheticDescribeContents()) {
val describeContents = declaration.addOverride(
ANDROID_PARCELABLE_CLASS_FQNAME,
"describeContents",
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))
}
metadata = DescriptorMetadataSource.Function(
declaration.descriptor.findFunction(ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS)!!
)
}
declaration.functions.find {
it.descriptor.safeAs<ParcelizeSyntheticComponent>()?.componentKind == ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS
}?.let { stub ->
symbolMap[stub.symbol] = describeContents.symbol
declaration.declarations.remove(stub)
}
}
if (declaration.descriptor.hasSyntheticWriteToParcel()) {
val writeToParcel = declaration.addOverride(
ANDROID_PARCELABLE_CLASS_FQNAME,
"writeToParcel",
context.irBuiltIns.unitType,
modality = Modality.OPEN
).apply {
val receiverParameter = dispatchReceiverParameter!!
val parcelParameter = addValueParameter("out", androidSymbols.androidOsParcel.defaultType)
val flagsParameter = addValueParameter("flags", context.irBuiltIns.intType)
// 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)
)
}
}
}
}
metadata = DescriptorMetadataSource.Function(
declaration.descriptor.findFunction(ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL)!!
)
}
declaration.functions.find {
it.descriptor.safeAs<ParcelizeSyntheticComponent>()?.componentKind == ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL
}?.let { stub ->
symbolMap[stub.symbol] = writeToParcel.symbol
declaration.declarations.remove(stub)
}
}
val creatorType = androidSymbols.androidOsParcelableCreator.typeWith(declaration.defaultType)
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(declaration.defaultType.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(androidSymbols.arrayOfNulls, arrayType).apply {
putTypeArgument(0, arrayType)
putValueArgument(0, irGet(sizeParameter))
}
)
}
}
addFunction("createFromParcel", declaration.defaultType).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 fun IrClass.addOverride(
baseFqName: FqName,
name: String,
returnType: IrType,
modality: Modality = Modality.FINAL
): IrSimpleFunction = addFunction(name, returnType, modality).apply {
overriddenSymbols = superTypes.mapNotNull { superType ->
superType.classOrNull?.owner?.takeIf { superClass -> superClass.isSubclassOfFqName(baseFqName.asString()) }
}.flatMap { superClass ->
superClass.functions.filter { function ->
function.name.asString() == name && function.overridesFunctionIn(baseFqName)
}.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,177 @@
/*
* 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.ir.allOverridden
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.parcelize.ANDROID_PARCELABLE_CLASS_FQNAME
import org.jetbrains.kotlin.parcelize.ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME
import org.jetbrains.kotlin.parcelize.PARCELER_FQNAME
import org.jetbrains.kotlin.parcelize.PARCELIZE_CLASS_FQNAME
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase.Companion.CREATOR_NAME
import org.jetbrains.kotlin.types.Variance
// true if the class should be processed by the parcelize plugin
val IrClass.isParcelize: Boolean
get() = kind in ParcelizeExtensionBase.ALLOWED_CLASS_KINDS && hasAnnotation(PARCELIZE_CLASS_FQNAME)
// Finds the getter for a pre-existing CREATOR field on the class companion, which is used for manual Parcelable implementations in Kotlin.
val IrClass.creatorGetter: IrSimpleFunctionSymbol?
get() = companionObject()?.getPropertyGetter(CREATOR_NAME.asString())?.takeIf {
it.owner.correspondingPropertySymbol?.owner?.backingField?.hasAnnotation(FqName("kotlin.jvm.JvmField")) == true
}
// true if the class has a static CREATOR field
val IrClass.hasCreatorField: Boolean
get() = fields.any { field -> field.name == CREATOR_NAME } || creatorGetter != null
// object P : Parceler<T> { fun T.write(parcel: Parcel, flags: Int) ...}
fun IrBuilderWithScope.parcelerWrite(
parceler: IrClass, parcel: IrValueDeclaration,
flags: IrValueDeclaration, value: IrExpression
): IrCall {
return irCall(parceler.parcelerSymbolByName("write")!!).apply {
dispatchReceiver = irGetObject(parceler.symbol)
extensionReceiver = value
putValueArgument(0, irGet(parcel))
putValueArgument(1, irGet(flags))
}
}
// object P : Parceler<T> { fun create(parcel: Parcel): T }
fun IrBuilderWithScope.parcelerCreate(parceler: IrClass, parcel: IrValueDeclaration): IrExpression {
return irCall(parceler.parcelerSymbolByName("create")!!).apply {
dispatchReceiver = irGetObject(parceler.symbol)
putValueArgument(0, irGet(parcel))
}
}
// object P: Parceler<T> { fun newArray(size: Int): Array<T> }
fun IrBuilderWithScope.parcelerNewArray(parceler: IrClass?, size: IrValueDeclaration): IrExpression? {
return parceler?.parcelerSymbolByName("newArray")?.let { newArraySymbol ->
irCall(newArraySymbol).apply {
dispatchReceiver = irGetObject(parceler.symbol)
putValueArgument(0, irGet(size))
}
}
}
// class Parcelable { fun writeToParcel(parcel: Parcel, flags: Int) ...}
fun IrBuilderWithScope.parcelableWriteToParcel(
parcelableClass: IrClass,
parcelable: IrExpression,
parcel: IrExpression,
flags: IrExpression
): IrExpression {
val writeToParcel = parcelableClass.functions.first { function ->
function.name.asString() == "writeToParcel" && function.overridesFunctionIn(ANDROID_PARCELABLE_CLASS_FQNAME)
}
return irCall(writeToParcel).apply {
dispatchReceiver = parcelable
putValueArgument(0, parcel)
putValueArgument(1, flags)
}
}
// class C : Parcelable.Creator<T> { fun createFromParcel(parcel: Parcel): T ...}
fun IrBuilderWithScope.parcelableCreatorCreateFromParcel(creator: IrExpression, parcel: IrExpression): IrExpression {
val createFromParcel = creator.type.getClass()!!.functions.first { function ->
function.name.asString() == "createFromParcel" && function.overridesFunctionIn(ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME)
}
return irCall(createFromParcel).apply {
dispatchReceiver = creator
putValueArgument(0, parcel)
}
}
// Find a named function declaration which overrides the corresponding function in [Parceler].
// This is more reliable than trying to match the functions signature ourselves, since the frontend
// has already done the work.
private fun IrClass.parcelerSymbolByName(name: String): IrSimpleFunctionSymbol? {
return functions.firstOrNull { function ->
!function.isFakeOverride && function.name.asString() == name && function.overridesFunctionIn(PARCELER_FQNAME)
}?.symbol
}
fun IrSimpleFunction.overridesFunctionIn(fqName: FqName): Boolean {
return parentClassOrNull?.fqNameWhenAvailable == fqName || allOverridden().any { it.parentClassOrNull?.fqNameWhenAvailable == fqName }
}
private fun IrBuilderWithScope.kClassReference(classType: IrType): IrClassReferenceImpl {
return IrClassReferenceImpl(
startOffset, endOffset, context.irBuiltIns.kClassClass.starProjectedType, context.irBuiltIns.kClassClass, classType
)
}
private fun AndroidIrBuilder.kClassToJavaClass(kClassReference: IrExpression): IrCall {
return irGet(androidSymbols.javaLangClass.starProjectedType, null, androidSymbols.kotlinKClassJava.owner.getter!!.symbol).apply {
extensionReceiver = kClassReference
}
}
// Produce a static reference to the java class of the given type.
fun AndroidIrBuilder.javaClassReference(classType: IrType): IrCall = kClassToJavaClass(kClassReference(classType))
fun IrClass.isSubclassOfFqName(fqName: String): Boolean {
return fqNameWhenAvailable?.asString() == fqName || superTypes.any { it.erasedUpperBound.isSubclassOfFqName(fqName) }
}
inline fun IrBlockBuilder.forUntil(upperBound: IrExpression, loopBody: IrBlockBuilder.(IrValueDeclaration) -> Unit) {
val indexTemporary = irTemporaryVar(irInt(0))
+irWhile().apply {
condition = irNotEquals(irGet(indexTemporary), upperBound)
body = irBlock {
loopBody(indexTemporary)
val inc = context.irBuiltIns.intClass.getSimpleFunction("inc")!!
+irSetVar(indexTemporary.symbol, irCall(inc).apply {
dispatchReceiver = irGet(indexTemporary)
})
}
}
}
fun IrTypeArgument.upperBound(builtIns: IrBuiltIns): IrType =
when (this) {
is IrStarProjection -> builtIns.anyNType
is IrTypeProjection -> {
if (variance == Variance.OUT_VARIANCE || variance == Variance.INVARIANT)
type
else
builtIns.anyNType
}
else -> error("Unknown type argument: ${render()}")
}
private fun IrClass.getSimpleFunction(name: String): IrSimpleFunctionSymbol? =
findDeclaration<IrSimpleFunction> { it.name.asString() == name }?.symbol
// This is a version of getPropertyGetter which does not throw when applied to broken lazy classes, such as java.util.HashMap,
// which contains two "size" properties with different visibilities.
fun IrClass.getPropertyGetter(name: String): IrSimpleFunctionSymbol? =
declarations.filterIsInstance<IrProperty>().firstOrNull { it.name.asString() == name && it.getter != null }?.getter?.symbol
?: getSimpleFunction("<get-$name>")
fun IrClass.getMethodWithoutArguments(name: String): IrSimpleFunction =
functions.first { function ->
function.name.asString() == name && function.dispatchReceiverParameter != null
&& function.extensionReceiverParameter == null && function.valueParameters.isEmpty()
}
@@ -0,0 +1,449 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.parcelize.serializers
import kotlinx.parcelize.WriteWith
import org.jetbrains.kotlin.codegen.FrameMap
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.parcelize.isParcelize
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.synthetic.isVisibleOutside
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import java.util.*
import java.util.concurrent.ConcurrentHashMap
val RAW_VALUE_ANNOTATION_FQNAME = FqName("kotlinx.parcelize.RawValue")
internal typealias TypeParcelerMapping = Pair<KotlinType, KotlinType>
interface ParcelSerializer {
val asmType: Type
fun writeValue(v: InstructionAdapter)
fun readValue(v: InstructionAdapter)
data class ParcelSerializerContext(
val typeMapper: KotlinTypeMapper,
val containerClassType: Type,
val typeParcelers: List<TypeParcelerMapping>,
val frameMap: FrameMap
) {
fun findParcelerClass(type: KotlinType): KotlinType? {
return typeParcelers.firstOrNull { it.first == type }?.second
}
}
companion object {
private val WRITE_WITH_FQNAME = FqName(WriteWith::class.java.name)
private fun KotlinTypeMapper.mapTypeSafe(type: KotlinType, forceBoxed: Boolean) = when {
type.isError -> Type.getObjectType("java/lang/Object")
else -> mapType(type, null, if (forceBoxed) TypeMappingMode.GENERIC_ARGUMENT else TypeMappingMode.DEFAULT)
}
fun get(
type: KotlinType,
asmType: Type,
context: ParcelSerializerContext,
forceBoxed: Boolean = false,
strict: Boolean = false
): ParcelSerializer {
val typeMapper = context.typeMapper
val className = asmType.className
fun strict() = strict && !type.annotations.hasAnnotation(RAW_VALUE_ANNOTATION_FQNAME)
fun findCustomParcelerType(type: KotlinType): KotlinType? {
type.annotations.findAnnotation(WRITE_WITH_FQNAME)?.let { writeWith ->
val parceler = writeWith.type.arguments.singleOrNull()?.type
if (parceler != null && !parceler.isError) {
return parceler
}
}
return context.findParcelerClass(type)?.takeIf { !it.isError }
}
findCustomParcelerType(type)?.let { return TypeParcelerParcelSerializer(asmType, it, context.typeMapper) }
return when {
asmType.descriptor == "[I"
|| asmType.descriptor == "[Z"
|| asmType.descriptor == "[B"
|| asmType.descriptor == "[C"
|| asmType.descriptor == "[S"
|| asmType.descriptor == "[D"
|| asmType.descriptor == "[F"
|| asmType.descriptor == "[J"
-> {
val customElementParcelerType = findCustomParcelerType(type.builtIns.getArrayElementType(type))
if (customElementParcelerType != null) {
val elementType = asmType.elementType
val elementParceler = TypeParcelerParcelSerializer(elementType, customElementParcelerType, context.typeMapper)
ArrayParcelSerializer(asmType, elementParceler)
} else {
PrimitiveArrayParcelSerializer(asmType)
}
}
asmType.descriptor == "[Landroid/os/IBinder;" -> {
NullCompliantObjectParcelSerializer(
asmType,
Method("writeBinderArray"), Method("createBinderArray")
)
}
asmType.descriptor == "[Ljava/lang/String;" -> {
NullCompliantObjectParcelSerializer(
asmType,
Method("writeStringArray"), Method("createStringArray")
)
}
asmType.sort == Type.ARRAY -> {
val elementType = type.builtIns.getArrayElementType(type)
val elementSerializer =
get(elementType, typeMapper.mapTypeSafe(elementType, forceBoxed = true), context, strict = strict())
wrapToNullAwareIfNeeded(type, ArrayParcelSerializer(asmType, elementSerializer))
}
asmType.isPrimitive() -> {
if (forceBoxed || type.isMarkedNullable)
wrapToNullAwareIfNeeded(type, BoxedPrimitiveTypeParcelSerializer.forUnboxedType(asmType))
else
PrimitiveTypeParcelSerializer.getInstance(asmType)
}
asmType.isString() -> {
NullCompliantObjectParcelSerializer(
asmType,
Method("writeString"),
Method("readString")
)
}
className == List::class.java.canonicalName
|| className == ArrayList::class.java.canonicalName
|| className == LinkedList::class.java.canonicalName
|| className == Set::class.java.canonicalName
|| className == SortedSet::class.java.canonicalName
|| className == NavigableSet::class.java.canonicalName
|| className == HashSet::class.java.canonicalName
|| className == LinkedHashSet::class.java.canonicalName
|| className == TreeSet::class.java.canonicalName
-> {
val elementType = type.arguments.single().type
val elementAsmType = typeMapper.mapTypeSafe(elementType, forceBoxed = true)
if (className == List::class.java.canonicalName) {
// Don't care if the element type is nullable cause both writeStrongBinder() and writeString() support null values
if (elementAsmType.descriptor == "Landroid/os/IBinder;") {
return NullCompliantObjectParcelSerializer(
asmType,
Method("writeBinderList"), Method("createBinderArrayList", "()Ljava/util/ArrayList;")
)
} else if (elementAsmType.descriptor == "Ljava/lang/String;") {
return NullCompliantObjectParcelSerializer(
asmType,
Method("writeStringList"), Method("createStringArrayList", "()Ljava/util/ArrayList;")
)
}
}
val elementSerializer = get(elementType, elementAsmType, context, forceBoxed = true, strict = strict())
wrapToNullAwareIfNeeded(type, ListSetParcelSerializer(asmType, elementSerializer, context.frameMap))
}
className == Map::class.java.canonicalName
|| className == SortedMap::class.java.canonicalName
|| className == NavigableMap::class.java.canonicalName
|| className == HashMap::class.java.canonicalName
|| className == LinkedHashMap::class.java.canonicalName
|| className == TreeMap::class.java.canonicalName
|| className == ConcurrentHashMap::class.java.canonicalName
-> {
val (keyType, valueType) = type.arguments.apply { assert(this.size == 2) }
val keySerializer = get(
keyType.type, typeMapper.mapTypeSafe(keyType.type, forceBoxed = true), context, forceBoxed = true, strict = strict()
)
val valueSerializer = get(
valueType.type,
typeMapper.mapTypeSafe(valueType.type, forceBoxed = true),
context,
forceBoxed = true,
strict = strict()
)
wrapToNullAwareIfNeeded(type, MapParcelSerializer(asmType, keySerializer, valueSerializer, context.frameMap))
}
asmType.isBoxedPrimitive() -> {
wrapToNullAwareIfNeeded(type, BoxedPrimitiveTypeParcelSerializer.forBoxedType(asmType))
}
asmType.isBlob() -> {
NullCompliantObjectParcelSerializer(
asmType,
Method("writeBlob"),
Method("readBlob")
)
}
asmType.isSize() -> {
wrapToNullAwareIfNeeded(
type, NullCompliantObjectParcelSerializer(
asmType,
Method("writeSize"),
Method("readSize")
)
)
}
asmType.isSizeF() -> wrapToNullAwareIfNeeded(
type, NullCompliantObjectParcelSerializer(
asmType,
Method("writeSizeF"),
Method("readSizeF")
)
)
asmType.isBundle() -> {
NullCompliantObjectParcelSerializer(
asmType,
Method("writeBundle"),
Method("readBundle")
)
}
type.isIBinder() -> {
NullCompliantObjectParcelSerializer(
asmType,
Method("writeStrongBinder", "(Landroid/os/IBinder;)V"),
Method("readStrongBinder", "()Landroid/os/IBinder;")
)
}
type.isIInterface() -> {
NullCompliantObjectParcelSerializer(
asmType,
Method("writeStrongInterface", "(Landroid/os/IInterface;)V"),
Method("readStrongInterface", "()Landroid/os/IInterface;")
)
}
asmType.isPersistableBundle() -> {
NullCompliantObjectParcelSerializer(
asmType,
Method("writeBundle"),
Method("readBundle")
)
}
asmType.isSparseBooleanArray() -> {
NullCompliantObjectParcelSerializer(
asmType,
Method("writeSparseBooleanArray"),
Method("readSparseBooleanArray")
)
}
asmType.isSparseIntArray() -> {
wrapToNullAwareIfNeeded(
type, SparseArrayParcelSerializer(
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.INT_TYPE), context.frameMap
)
)
}
asmType.isSparseLongArray() -> {
wrapToNullAwareIfNeeded(
type, SparseArrayParcelSerializer(
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.LONG_TYPE), context.frameMap
)
)
}
asmType.isSparseArray() -> {
val elementType = type.arguments.single().type
val elementSerializer = get(
elementType, typeMapper.mapTypeSafe(elementType, forceBoxed = true), context, forceBoxed = true, strict = strict()
)
wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(asmType, elementSerializer, context.frameMap))
}
type.isCharSequence() -> {
CharSequenceParcelSerializer(asmType)
}
type.isException() -> {
wrapToNullAwareIfNeeded(
type, NullCompliantObjectParcelSerializer(
asmType,
Method("writeException"),
Method("readException")
)
)
}
asmType.isFileDescriptor() -> {
wrapToNullAwareIfNeeded(
type, NullCompliantObjectParcelSerializer(
asmType,
Method("writeRawFileDescriptor"),
Method("readRawFileDescriptor")
)
)
}
// Write at least a nullability byte.
// We don't want parcel to be empty in case if all constructor parameters are objects
type.isNamedObject() -> {
NullAwareParcelSerializerWrapper(ObjectParcelSerializer(asmType, type, typeMapper))
}
type.isEnum() -> {
wrapToNullAwareIfNeeded(type, EnumParcelSerializer(asmType))
}
type.isParcelable() -> {
val clazz = type.constructor.declarationDescriptor as? ClassDescriptor
if (clazz != null && clazz.modality == Modality.FINAL && clazz.source is PsiSourceElement) {
fun MemberScope.findCreatorField() = getContributedVariables(
Name.identifier("CREATOR"), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS
).firstOrNull()
val creatorVar = when (clazz) {
is JavaClassDescriptor -> clazz.staticScope.findCreatorField()
else -> clazz.companionObjectDescriptor?.unsubstitutedMemberScope?.findCreatorField()
?.takeIf { it.annotations.hasAnnotation(FqName(JvmField::class.java.name)) }
}
val creatorAsmType = when {
creatorVar != null -> typeMapper.mapTypeSafe(creatorVar.type, forceBoxed = true)
clazz.isParcelize -> Type.getObjectType(asmType.internalName + "\$Creator")
else -> null
}
creatorAsmType?.let { wrapToNullAwareIfNeeded(type, EfficientParcelableParcelSerializer(asmType)) }
?: GenericParcelableParcelSerializer(asmType, context.containerClassType)
} else {
GenericParcelableParcelSerializer(asmType, context.containerClassType)
}
}
type.isSerializable() -> {
NullCompliantObjectParcelSerializer(
asmType,
Method("writeSerializable", "(Ljava/io/Serializable;)V"),
Method("readSerializable", "()Ljava/io/Serializable;")
)
}
else -> {
if (strict && !type.annotations.hasAnnotation(RAW_VALUE_ANNOTATION_FQNAME))
throw IllegalArgumentException("Illegal type")
else
GenericParcelSerializer(asmType)
}
}
}
private fun wrapToNullAwareIfNeeded(type: KotlinType, serializer: ParcelSerializer): ParcelSerializer {
return when {
type.isMarkedNullable -> NullAwareParcelSerializerWrapper(serializer)
else -> serializer
}
}
private fun Type.isBlob() = this.sort == Type.ARRAY && this.elementType == Type.BYTE_TYPE
private fun Type.isString() = this.descriptor == "Ljava/lang/String;"
private fun Type.isSize() = this.descriptor == "Landroid/util/Size;"
private fun Type.isSizeF() = this.descriptor == "Landroid/util/SizeF;"
private fun Type.isFileDescriptor() = this.descriptor == "Ljava/io/FileDescriptor;"
private fun Type.isBundle() = this.descriptor == "Landroid/os/Bundle;"
private fun Type.isPersistableBundle() = this.descriptor == "Landroid/os/PersistableBundle;"
private fun Type.isSparseBooleanArray() = this.descriptor == "Landroid/util/SparseBooleanArray;"
private fun Type.isSparseIntArray() = this.descriptor == "Landroid/util/SparseIntArray;"
private fun Type.isSparseLongArray() = this.descriptor == "Landroid/util/SparseLongArray;"
private fun Type.isSparseArray() = this.descriptor == "Landroid/util/SparseArray;"
private fun KotlinType.isException() = matchesFqNameWithSupertypes("java.lang.Exception")
private fun KotlinType.isIBinder() = matchesFqNameWithSupertypes("android.os.IBinder")
private fun KotlinType.isIInterface() = matchesFqNameWithSupertypes("android.os.IInterface")
private fun KotlinType.isCharSequence() = matchesFqName("kotlin.CharSequence") || matchesFqName("java.lang.CharSequence")
private fun KotlinType.isSerializable() = matchesFqNameWithSupertypes("java.io.Serializable")
|| matchesFqNameWithSupertypes("kotlin.Function")
private fun KotlinType.isNamedObject(): Boolean {
val classDescriptor = constructor.declarationDescriptor as? ClassDescriptor ?: return false
if (!classDescriptor.visibility.isVisibleOutside()) return false
if (DescriptorUtils.isAnonymousObject(classDescriptor)) return false
return classDescriptor.kind == ClassKind.OBJECT
}
private fun KotlinType.isEnum() = (constructor.declarationDescriptor as? ClassDescriptor)?.kind == ClassKind.ENUM_CLASS
private fun Type.isPrimitive(): Boolean = when (this.sort) {
Type.BOOLEAN, Type.CHAR, Type.BYTE, Type.SHORT, Type.INT, Type.FLOAT, Type.LONG, Type.DOUBLE -> true
else -> false
}
private fun Type.isBoxedPrimitive(): Boolean = when (this.descriptor) {
"Ljava/lang/Boolean;",
"Ljava/lang/Character;",
"Ljava/lang/Byte;",
"Ljava/lang/Short;",
"Ljava/lang/Integer;",
"Ljava/lang/Float;",
"Ljava/lang/Long;",
"Ljava/lang/Double;"
-> true
else -> false
}
}
}
internal fun KotlinType.isParcelable() = matchesFqNameWithSupertypes("android.os.Parcelable")
private fun KotlinType.matchesFqName(fqName: String): Boolean {
return this.constructor.declarationDescriptor?.fqNameSafe?.asString() == fqName
}
private fun KotlinType.matchesFqNameWithSupertypes(fqName: String): Boolean {
if (this.matchesFqName(fqName)) {
return true
}
return TypeUtils.getAllSupertypes(this).any { it.matchesFqName(fqName) }
}
@@ -0,0 +1,834 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.parcelize.serializers
import kotlinx.parcelize.Parceler
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.FrameMap
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.codegen.useTmpVar
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.parcelize.serializers.BoxedPrimitiveTypeParcelSerializer.Companion.BOXED_VALUE_METHOD_NAMES
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
internal val PARCEL_TYPE = Type.getObjectType("android/os/Parcel")
internal val PARCELER_TYPE = Type.getObjectType(Parceler::class.java.name.replace(".", "/"))
internal class GenericParcelSerializer(override val asmType: Type) : ParcelSerializer {
override fun writeValue(v: InstructionAdapter) {
v.invokevirtual(PARCEL_TYPE.internalName, "writeValue", "(Ljava/lang/Object;)V", false)
}
override fun readValue(v: InstructionAdapter) {
v.aconst(asmType) // -> parcel, type
v.invokevirtual("java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false) // -> parcel, classloader
v.invokevirtual(PARCEL_TYPE.internalName, "readValue", "(Ljava/lang/ClassLoader;)Ljava/lang/Object;", false)
v.castIfNeeded(asmType)
}
}
internal class TypeParcelerParcelSerializer(
override val asmType: Type,
private val parcelerType: KotlinType,
private val typeMapper: KotlinTypeMapper
) : ParcelSerializer {
private val parcelerAsmType = typeMapper.mapType(parcelerType)
override fun writeValue(v: InstructionAdapter) {
// -> parcel, value(?)
boxTypeIfNeeded(v) // -> parcel, (boxed)value
v.swap() // -> value, parcel
putObjectOrClassInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> value, parcel, parceler
v.dupX2() // -> parceler, value, parcel, parceler
v.pop() // -> parceler, value, parcel
v.load(2, Type.INT_TYPE) // -> parceler, value, parcel, flags
v.invokeinterface(PARCELER_TYPE.internalName, "write", "(Ljava/lang/Object;Landroid/os/Parcel;I)V")
}
override fun readValue(v: InstructionAdapter) {
// -> parcel
putObjectOrClassInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> parcel, parceler
v.swap() // -> parceler, parcel
v.invokeinterface(PARCELER_TYPE.internalName, "create", "(Landroid/os/Parcel;)Ljava/lang/Object;") // -> obj
unboxTypeIfNeeded(v)
v.castIfNeeded(asmType)
}
private fun handleSpecialBoxingCases(v: InstructionAdapter): Type? {
assert(asmType.sort != Type.METHOD)
if (asmType.sort == Type.OBJECT || asmType.sort == Type.ARRAY) {
return null
}
if (asmType == Type.VOID_TYPE) {
v.pop()
v.aconst(null)
return null
}
return AsmUtil.boxType(asmType)
}
private fun boxTypeIfNeeded(v: InstructionAdapter) {
val boxedType = handleSpecialBoxingCases(v) ?: return
v.invokestatic(boxedType.internalName, "valueOf", "(${asmType.descriptor})${boxedType.descriptor}", false)
}
private fun unboxTypeIfNeeded(v: InstructionAdapter) {
val boxedType = handleSpecialBoxingCases(v) ?: return
val getValueMethodName = BOXED_VALUE_METHOD_NAMES.getValue(boxedType.internalName)
v.castIfNeeded(boxedType)
v.invokevirtual(boxedType.internalName, getValueMethodName, "()${asmType.descriptor}", false)
}
}
internal class ArrayParcelSerializer(override val asmType: Type, private val elementSerializer: ParcelSerializer) : ParcelSerializer {
override fun writeValue(v: InstructionAdapter) {
v.dupX1() // -> arr, parcel, arr
v.arraylength() // -> arr, parcel, length
v.dupX2() // -> length, arr, parcel, length
// Write array size
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // -> length, arr
v.swap() // -> arr, length
v.aconst(0) // -> arr, length, <index>
val nextLoopIteration = Label()
val loopIsOver = Label()
v.visitLabel(nextLoopIteration)
// Loop
v.dup2() // -> arr, length, index, length, index
v.ificmple(loopIsOver) // -> arr, length, index
v.swap() // -> arr, index, length
v.dupX2() // -> length, arr, index, length
v.pop() // -> length, arr, index
v.dup2() // -> length, arr, index, arr, index
v.load(1, PARCEL_TYPE) // -> length, arr, index, arr, index, parcel
v.dupX2() // -> length, arr, index, parcel, arr, index, parcel
v.pop() // -> length, arr, index, parcel, arr, index
v.aload(elementSerializer.asmType) // -> length, arr, index, parcel, obj
v.castIfNeeded(elementSerializer.asmType)
elementSerializer.writeValue(v) // -> length, arr, index
v.aconst(1) // -> length, arr, index, (1)
v.add(Type.INT_TYPE) // -> length, arr, (index + 1)
v.swap() // -> length, (index + 1), arr
v.dupX2() // -> arr, length, (index + 1), arr
v.pop() // -> arr, length, (index + 1)
v.goTo(nextLoopIteration)
v.visitLabel(loopIsOver)
v.pop2() // -> arr
v.pop()
}
override fun readValue(v: InstructionAdapter) {
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> length
v.dup() // -> length, length
v.newarray(elementSerializer.asmType) // -> length, arr
v.swap() // -> arr, length
v.aconst(0) // -> arr, length, index
val nextLoopIteration = Label()
val loopIsOver = Label()
v.visitLabel(nextLoopIteration)
v.dup2() // -> arr, length, index, length, index
v.ificmple(loopIsOver) // -> arr, length, index
v.swap() // -> arr, index, length
v.dupX2() // -> length, arr, index, length
v.pop() // -> length, arr, index
v.dup2() // -> length, arr, index, arr, index
v.load(1, PARCEL_TYPE) // -> length, arr, index, arr, index, parcel
elementSerializer.readValue(v) // -> length, arr, index, arr, index, obj
v.castIfNeeded(elementSerializer.asmType)
v.astore(elementSerializer.asmType) // -> length, arr, index
v.aconst(1) // -> length, arr, index, (1)
v.add(Type.INT_TYPE) // -> length, arr, (index + 1)
v.swap() // -> length, (index + 1), arr
v.dupX2() // -> arr, length, (index + 1), arr
v.pop() // -> arr, length, (index + 1)
v.goTo(nextLoopIteration)
v.visitLabel(loopIsOver)
v.pop2() // -> arr
}
}
internal fun InstructionAdapter.castIfNeeded(targetType: Type) {
if (targetType.sort != Type.OBJECT && targetType.sort != Type.ARRAY) return
if (targetType.descriptor == "Ljava/lang/Object;") return
checkcast(targetType)
}
internal class ListSetParcelSerializer(
asmType: Type,
elementSerializer: ParcelSerializer,
frameMap: FrameMap
) : AbstractCollectionParcelSerializer(asmType, elementSerializer, frameMap) {
override fun getSize(v: InstructionAdapter) {
v.invokeinterface("java/util/Collection", "size", "()I")
}
override fun getIterator(v: InstructionAdapter) {
v.invokeinterface("java/util/Collection", "iterator", "()Ljava/util/Iterator;")
}
override fun doWriteValue(v: InstructionAdapter) {
// -> parcel, obj
v.castIfNeeded(elementSerializer.asmType)
elementSerializer.writeValue(v)
}
override fun doReadValue(v: InstructionAdapter) {
// -> collection, parcel
elementSerializer.readValue(v) // -> collection, element
v.castIfNeeded(elementSerializer.asmType)
v.invokevirtual(collectionType.internalName, "add", "(Ljava/lang/Object;)Z", false) // -> bool
v.pop()
}
}
internal class MapParcelSerializer(
asmType: Type,
private val keySerializer: ParcelSerializer,
elementSerializer: ParcelSerializer,
frameMap: FrameMap
) : AbstractCollectionParcelSerializer(asmType, elementSerializer, frameMap) {
override fun getSize(v: InstructionAdapter) {
v.invokeinterface("java/util/Map", "size", "()I")
}
override fun getIterator(v: InstructionAdapter) {
v.invokeinterface("java/util/Map", "entrySet", "()Ljava/util/Set;")
v.invokeinterface("java/util/Set", "iterator", "()Ljava/util/Iterator;")
}
override fun doWriteValue(v: InstructionAdapter) {
// -> parcel, obj
v.dup2() // -> parcel, obj, parcel, obj
v.invokeinterface("java/util/Map\$Entry", "getKey", "()Ljava/lang/Object;") // -> parcel, obj, parcel, key
v.castIfNeeded(keySerializer.asmType)
keySerializer.writeValue(v) // -> parcel, obj
v.invokeinterface("java/util/Map\$Entry", "getValue", "()Ljava/lang/Object;") // -> parcel, value
v.castIfNeeded(elementSerializer.asmType)
elementSerializer.writeValue(v)
}
override fun doReadValue(v: InstructionAdapter) {
// -> map, parcel
v.dup() // -> map, parcel, parcel
keySerializer.readValue(v) // -> map, parcel, key
v.castIfNeeded(keySerializer.asmType)
v.swap() // -> map, key, parcel
elementSerializer.readValue(v) // -> map, key, value
v.castIfNeeded(elementSerializer.asmType)
v.invokeinterface("java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") // -> obj
v.pop()
}
}
internal abstract class AbstractCollectionParcelSerializer(
final override val asmType: Type,
protected val elementSerializer: ParcelSerializer,
private val frameMap: FrameMap
) : ParcelSerializer {
protected val collectionType: Type = Type.getObjectType(
when (asmType.internalName) {
"java/util/List" -> "java/util/ArrayList"
"java/util/Set" -> "java/util/LinkedHashSet"
"java/util/SortedSet" -> "java/util/TreeSet"
"java/util/NavigableSet" -> "java/util/TreeSet"
"java/util/Map" -> "java/util/LinkedHashMap"
"java/util/SortedMap" -> "java/util/TreeMap"
"java/util/NavigableMap" -> "java/util/TreeMap"
else -> asmType.internalName
}
)
private var hasConstructorWithCapacity = when (collectionType.internalName) {
"java/util/LinkedList", "java/util/TreeSet", "java/util/TreeMap" -> false
else -> true
}
/**
* Stack before: collection
* Stack after: size
*/
protected abstract fun getSize(v: InstructionAdapter)
/**
* Stack before: collection
* Stack after: iterator
*/
protected abstract fun getIterator(v: InstructionAdapter)
/**
* Stack before: parcel, obj
* Stack after: <empty>
*/
protected abstract fun doWriteValue(v: InstructionAdapter)
/**
* Stack before: collection, parcel
* Stack after: <empty>
*/
protected abstract fun doReadValue(v: InstructionAdapter)
override fun writeValue(v: InstructionAdapter) {
val labelIteratorLoop = Label()
val labelReturn = Label()
v.dupX1() // -> collection, parcel, collection
getSize(v) // -> collection, parcel, size
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // collection
getIterator(v) // -> iterator
v.visitLabel(labelIteratorLoop)
v.dup() // -> iterator, iterator
v.invokeinterface("java/util/Iterator", "hasNext", "()Z") // -> iterator, hasNext
v.ifeq(labelReturn) // -> iterator
v.dup() // -> iterator, iterator
v.load(1, PARCEL_TYPE) // iterator, iterator, parcel
v.swap() // iterator, parcel, iterator
v.invokeinterface("java/util/Iterator", "next", "()Ljava/lang/Object;") // -> iterator, parcel, obj
doWriteValue(v) // -> iterator
v.goTo(labelIteratorLoop)
v.visitLabel(labelReturn)
v.pop()
}
override fun readValue(v: InstructionAdapter) {
frameMap.useTmpVar(Type.INT_TYPE) { sizeVarIndex ->
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> size
v.store(sizeVarIndex, Type.INT_TYPE)
v.anew(collectionType) // -> list
v.dup() // -> list, list
if (hasConstructorWithCapacity) {
v.load(sizeVarIndex, Type.INT_TYPE)
v.invokespecial(collectionType.internalName, "<init>", "(I)V", false) // -> list
} else {
v.invokespecial(collectionType.internalName, "<init>", "()V", false) // -> list
}
v.load(sizeVarIndex, Type.INT_TYPE) // -> list, size
}
val nextLoopIteration = Label()
val loopIsOver = Label()
v.visitLabel(nextLoopIteration)
v.dupX1() // -> size, list, size
v.ifeq(loopIsOver) // -> size, list
v.dup() // -> size, list, list
v.load(1, PARCEL_TYPE) // -> size, list, list, parcel
doReadValue(v) // -> size, list
v.swap() // -> list, size
v.aconst(-1) // -> list, size, (-1)
v.add(Type.INT_TYPE) // -> list, (size - 1)
v.goTo(nextLoopIteration)
v.visitLabel(loopIsOver)
v.swap() // -> list, size
v.pop()
}
}
internal class SparseArrayParcelSerializer(
override val asmType: Type,
private val valueSerializer: ParcelSerializer,
private val frameMap: FrameMap
) : ParcelSerializer {
private val valueType = (valueSerializer as? PrimitiveTypeParcelSerializer)?.asmType ?: Type.getObjectType("java/lang/Object")
override fun writeValue(v: InstructionAdapter) {
v.dup() // -> parcel, arr, arr
v.invokevirtual(asmType.internalName, "size", "()I", false) // -> parcel, arr, size
v.dup2X1() // -> arr, size, parcel, arr, size
v.swap() // -> arr, size, parcel, size, arr
v.pop() // -> arr, size, parcel, size
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // -> arr, size
v.aconst(0) // -> arr, size, <index>
val nextLoopIteration = Label()
val loopIsOver = Label()
v.visitLabel(nextLoopIteration)
v.dup2() // -> arr, size, index, size, index
v.ificmple(loopIsOver) // -> arr, size, index
v.swap() // -> arr, index, size
v.dupX2() // -> size, arr, index, size
v.pop() // -> size, arr, index
v.dup2() // -> size, arr, index, arr, index
v.invokevirtual(asmType.internalName, "keyAt", "(I)I", false) // -> size, arr, index, key
v.load(1, PARCEL_TYPE) // size, arr, index, key, parcel
v.dupX2() // -> size, arr, parcel, index, key, parcel
v.swap() // -> size, arr, parcel, index, parcel, key
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // -> size, arr, parcel, index
v.swap() // -> size, arr, index, parcel
v.dupX2() // -> size, parcel, arr, index, parcel
v.pop() // -> size, parcel, arr, index
v.dup2X1() // -> size, arr, index, parcel, arr, index
v.invokevirtual(asmType.internalName, "valueAt", "(I)${valueType.descriptor}", false) // -> size, arr, index, parcel, value
valueSerializer.writeValue(v) // -> size, arr, index
v.aconst(1) // -> size, arr, index, (1)
v.add(Type.INT_TYPE) // -> size, arr, (index + 1)
v.dup2X1() // -> arr, (index + 1), size, arr, (index + 1)
v.pop2() // -> arr, (index + 1), size
v.swap() // -> arr, size, (index + 1)
v.goTo(nextLoopIteration)
v.visitLabel(loopIsOver)
v.pop2()
v.pop()
}
override fun readValue(v: InstructionAdapter) {
frameMap.useTmpVar(Type.INT_TYPE) { sizeVarIndex ->
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> size
v.store(sizeVarIndex, Type.INT_TYPE) // -> (empty)
v.anew(asmType) // -> arr
v.dup() // -> arr, arr
v.load(sizeVarIndex, Type.INT_TYPE) // -> arr, arr, size
v.invokespecial(asmType.internalName, "<init>", "(I)V", false) // -> arr
v.load(sizeVarIndex, Type.INT_TYPE) // -> arr, size
}
val nextLoopIteration = Label()
val loopIsOver = Label()
v.visitLabel(nextLoopIteration)
v.dup() // -> arr, size, size
v.ifle(loopIsOver) // -> arr, size
v.swap() // -> size, arr
v.dupX1() // -> arr, size, arr
v.load(1, PARCEL_TYPE) // -> arr, size, arr, parcel
v.dup() // -> arr, size, arr, parcel, parcel
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> arr, size, arr, parcel, key
v.swap() // -> arr, size, arr, key, parcel
valueSerializer.readValue(v) // -> arr, size, arr, key, value
v.invokevirtual(asmType.internalName, "put", "(I${valueType.descriptor})V", false) // -> arr, size
v.aconst(-1) // -> arr, size, (-1)
v.add(Type.INT_TYPE) // -> arr, (size - 1)
v.goTo(nextLoopIteration)
v.visitLabel(loopIsOver)
v.pop() // -> arr
}
}
internal class ObjectParcelSerializer(
override val asmType: Type,
private val type: KotlinType,
private val typeMapper: KotlinTypeMapper
) : ParcelSerializer {
override fun writeValue(v: InstructionAdapter) {
v.pop2()
}
override fun readValue(v: InstructionAdapter) {
v.pop()
putObjectOrClassInstanceOnStack(type, asmType, typeMapper, v)
}
}
internal class ZeroParameterClassSerializer(
override val asmType: Type,
type: KotlinType
) : ParcelSerializer {
private val clazz = type.constructor.declarationDescriptor as ClassDescriptor
init {
assert(clazz.kind == ClassKind.CLASS)
}
override fun writeValue(v: InstructionAdapter) {
v.pop2()
}
override fun readValue(v: InstructionAdapter) {
v.pop()
val constructor = clazz.unsubstitutedPrimaryConstructor
assert(constructor == null || constructor.valueParameters.isEmpty())
v.anew(asmType)
v.dup()
v.invokespecial(asmType.internalName, "<init>", "()V", false)
}
}
private fun putObjectOrClassInstanceOnStack(type: KotlinType, asmType: Type, typeMapper: KotlinTypeMapper, v: InstructionAdapter) {
val clazz = type.constructor.declarationDescriptor as? ClassDescriptor
if (clazz != null) {
if (clazz.isCompanionObject) {
val outerClass = clazz.containingDeclaration as? ClassDescriptor
if (outerClass != null) {
v.getstatic(typeMapper.mapType(outerClass.defaultType).internalName, clazz.name.asString(), asmType.descriptor)
return
}
}
}
v.getstatic(asmType.internalName, "INSTANCE", asmType.descriptor)
}
internal class EnumParcelSerializer(override val asmType: Type) : ParcelSerializer {
override fun writeValue(v: InstructionAdapter) {
v.invokevirtual("java/lang/Enum", "name", "()Ljava/lang/String;", false)
v.invokevirtual(PARCEL_TYPE.internalName, "writeString", "(Ljava/lang/String;)V", false)
}
override fun readValue(v: InstructionAdapter) {
v.invokevirtual(PARCEL_TYPE.internalName, "readString", "()Ljava/lang/String;", false)
v.aconst(asmType)
v.swap()
v.invokestatic("java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;", false)
v.castIfNeeded(asmType)
}
}
internal class CharSequenceParcelSerializer(override val asmType: Type) : ParcelSerializer {
override fun writeValue(v: InstructionAdapter) {
// -> parcel, seq
v.swap() // -> seq, parcel
v.aconst(0) // -> seq, parcel, flags
v.invokestatic("android/text/TextUtils", "writeToParcel", "(Ljava/lang/CharSequence;Landroid/os/Parcel;I)V", false)
}
override fun readValue(v: InstructionAdapter) {
// -> parcel
v.getstatic("android/text/TextUtils", "CHAR_SEQUENCE_CREATOR", "Landroid/os/Parcelable\$Creator;") // -> parcel, creator
v.swap() // -> creator, parcel
v.invokeinterface("android/os/Parcelable\$Creator", "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;")
v.castIfNeeded(asmType)
}
}
internal class EfficientParcelableParcelSerializer(override val asmType: Type) : ParcelSerializer {
override fun writeValue(v: InstructionAdapter) {
// -> parcel, parcelable
v.swap() // -> parcelable, parcel
v.aconst(0) // -> parcelable, parcel, flags
v.invokeinterface("android/os/Parcelable", "writeToParcel", "(Landroid/os/Parcel;I)V")
}
override fun readValue(v: InstructionAdapter) {
// -> parcel
v.getstatic(asmType.internalName, "CREATOR", "Landroid/os/Parcelable\$Creator;") // -> parcel, creator
v.swap() // -> creator, parcel
v.invokeinterface("android/os/Parcelable\$Creator", "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;")
v.castIfNeeded(asmType)
}
}
internal class GenericParcelableParcelSerializer(override val asmType: Type, private val containerClassType: Type) : ParcelSerializer {
override fun writeValue(v: InstructionAdapter) {
// -> parcel, parcelable
v.load(2, Type.INT_TYPE) // -> parcel, parcelable, flags
v.invokevirtual(PARCEL_TYPE.internalName, "writeParcelable", "(Landroid/os/Parcelable;I)V", false)
}
override fun readValue(v: InstructionAdapter) {
// -> parcel
v.aconst(containerClassType) // -> parcel, type
v.invokevirtual("java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false) // -> parcel, classloader
v.invokevirtual(PARCEL_TYPE.internalName, "readParcelable", "(Ljava/lang/ClassLoader;)Landroid/os/Parcelable;", false)
v.castIfNeeded(asmType)
}
}
internal class NullAwareParcelSerializerWrapper(private val delegate: ParcelSerializer) : ParcelSerializer {
override val asmType: Type
get() = delegate.asmType
override fun writeValue(v: InstructionAdapter) = writeValueNullAware(v) {
delegate.writeValue(v)
}
override fun readValue(v: InstructionAdapter) = readValueNullAware(v) {
delegate.readValue(v)
}
}
internal class PrimitiveArrayParcelSerializer(
override val asmType: Type
) : ParcelSerializer {
private val methodNameBase = when (asmType.elementType) {
Type.INT_TYPE -> "Int"
Type.BOOLEAN_TYPE -> "Boolean"
Type.BYTE_TYPE -> "Byte"
Type.CHAR_TYPE -> "Char"
Type.DOUBLE_TYPE -> "Double"
Type.FLOAT_TYPE -> "Float"
Type.LONG_TYPE -> "Long"
else -> error("Unsupported type ${asmType.elementType.descriptor}")
}
private val writeMethod = Method("write${methodNameBase}Array", "(${asmType.descriptor})V")
private val createArrayMethod = Method("create${methodNameBase}Array", "()${asmType.descriptor}")
override fun writeValue(v: InstructionAdapter) {
v.invokevirtual(PARCEL_TYPE.internalName, writeMethod.name, writeMethod.signature, false)
}
override fun readValue(v: InstructionAdapter) {
v.invokevirtual(PARCEL_TYPE.internalName, createArrayMethod.name, createArrayMethod.signature, false)
}
}
/** write...() and get...() methods in Android should support passing `null` values. */
internal class NullCompliantObjectParcelSerializer(
override val asmType: Type,
writeMethod: Method<String?>,
readMethod: Method<String?>
) : ParcelSerializer {
private val writeMethod = Method(writeMethod.name, writeMethod.signature ?: "(${asmType.descriptor})V")
private val readMethod = Method(readMethod.name, readMethod.signature ?: "()${asmType.descriptor}")
override fun writeValue(v: InstructionAdapter) {
v.invokevirtual(PARCEL_TYPE.internalName, writeMethod.name, writeMethod.signature, false)
}
override fun readValue(v: InstructionAdapter) {
v.invokevirtual(PARCEL_TYPE.internalName, readMethod.name, readMethod.signature, false)
v.castIfNeeded(asmType)
}
}
internal class BoxedPrimitiveTypeParcelSerializer private constructor(private val unboxedType: Type) : ParcelSerializer {
companion object {
private val BOXED_TO_UNBOXED_TYPE_MAPPINGS = mapOf(
"java/lang/Boolean" to Type.BOOLEAN_TYPE,
"java/lang/Character" to Type.CHAR_TYPE,
"java/lang/Byte" to Type.BYTE_TYPE,
"java/lang/Short" to Type.SHORT_TYPE,
"java/lang/Integer" to Type.INT_TYPE,
"java/lang/Float" to Type.FLOAT_TYPE,
"java/lang/Long" to Type.LONG_TYPE,
"java/lang/Double" to Type.DOUBLE_TYPE
)
private val UNBOXED_TO_BOXED_TYPE_MAPPINGS = BOXED_TO_UNBOXED_TYPE_MAPPINGS.map { it.value to it.key }.toMap()
internal val BOXED_VALUE_METHOD_NAMES = mapOf(
"java/lang/Boolean" to "booleanValue",
"java/lang/Character" to "charValue",
"java/lang/Byte" to "byteValue",
"java/lang/Short" to "shortValue",
"java/lang/Integer" to "intValue",
"java/lang/Float" to "floatValue",
"java/lang/Long" to "longValue",
"java/lang/Double" to "doubleValue"
)
private val INSTANCES = BOXED_TO_UNBOXED_TYPE_MAPPINGS.values.map { type ->
type to BoxedPrimitiveTypeParcelSerializer(type)
}.toMap()
fun forUnboxedType(type: Type) = INSTANCES[type] ?: error("Unsupported type $type")
fun forBoxedType(type: Type) = INSTANCES[BOXED_TO_UNBOXED_TYPE_MAPPINGS[type.internalName]] ?: error("Unsupported type $type")
}
override val asmType: Type = Type.getObjectType(UNBOXED_TO_BOXED_TYPE_MAPPINGS[unboxedType] ?: error("Unsupported type $unboxedType"))
private val unboxedSerializer = PrimitiveTypeParcelSerializer.getInstance(unboxedType)
private val typeValueMethodName = BOXED_VALUE_METHOD_NAMES[asmType.internalName] ?: error("Boxed method name not found for $asmType")
override fun writeValue(v: InstructionAdapter) {
v.invokevirtual(asmType.internalName, typeValueMethodName, "()${unboxedType.descriptor}", false)
unboxedSerializer.writeValue(v)
}
override fun readValue(v: InstructionAdapter) {
unboxedSerializer.readValue(v)
v.invokestatic(asmType.internalName, "valueOf", "(${unboxedType.descriptor})${asmType.descriptor}", false)
}
}
internal open class PrimitiveTypeParcelSerializer private constructor(final override val asmType: Type) : ParcelSerializer {
companion object {
private val WRITE_METHOD_NAMES = mapOf(
Type.BOOLEAN_TYPE to Method("writeInt", "(I)V"),
Type.CHAR_TYPE to Method("writeInt", "(I)V"),
Type.BYTE_TYPE to Method("writeByte", "(B)V"),
Type.SHORT_TYPE to Method("writeInt", "(I)V"),
Type.INT_TYPE to Method("writeInt", "(I)V"),
Type.FLOAT_TYPE to Method("writeFloat", "(F)V"),
Type.LONG_TYPE to Method("writeLong", "(J)V"),
Type.DOUBLE_TYPE to Method("writeDouble", "(D)V")
)
private val READ_METHOD_NAMES = mapOf(
Type.BOOLEAN_TYPE to Method("readInt", "()I"),
Type.CHAR_TYPE to Method("readInt", "()I"),
Type.BYTE_TYPE to Method("readByte", "()B"),
Type.SHORT_TYPE to Method("readInt", "()I"),
Type.INT_TYPE to Method("readInt", "()I"),
Type.FLOAT_TYPE to Method("readFloat", "()F"),
Type.LONG_TYPE to Method("readLong", "()J"),
Type.DOUBLE_TYPE to Method("readDouble", "()D")
)
private val INSTANCES = READ_METHOD_NAMES.keys.map {
it to when (it) {
Type.CHAR_TYPE -> CharParcelSerializer
Type.SHORT_TYPE -> ShortParcelSerializer
Type.BOOLEAN_TYPE -> BooleanParcelSerializer
else -> PrimitiveTypeParcelSerializer(it)
}
}.toMap()
fun getInstance(type: Type) = INSTANCES[type] ?: error("Unsupported type ${type.descriptor}")
}
object CharParcelSerializer : PrimitiveTypeParcelSerializer(Type.CHAR_TYPE) {
override fun writeValue(v: InstructionAdapter) {
v.cast(Type.CHAR_TYPE, Type.INT_TYPE)
super.writeValue(v)
}
override fun readValue(v: InstructionAdapter) {
super.readValue(v)
v.cast(Type.INT_TYPE, Type.CHAR_TYPE)
}
}
object ShortParcelSerializer : PrimitiveTypeParcelSerializer(Type.SHORT_TYPE) {
override fun writeValue(v: InstructionAdapter) {
v.cast(Type.SHORT_TYPE, Type.INT_TYPE)
super.writeValue(v)
}
override fun readValue(v: InstructionAdapter) {
super.readValue(v)
v.cast(Type.INT_TYPE, Type.SHORT_TYPE)
}
}
object BooleanParcelSerializer : PrimitiveTypeParcelSerializer(Type.BOOLEAN_TYPE) {
override fun readValue(v: InstructionAdapter) {
super.readValue(v)
val falseLabel = Label()
val conditionIsOver = Label()
v.ifeq(falseLabel)
v.iconst(1)
v.goTo(conditionIsOver)
v.visitLabel(falseLabel)
v.iconst(0)
v.visitLabel(conditionIsOver)
}
}
private val writeMethod = WRITE_METHOD_NAMES[asmType] ?: error("Write method not found for $asmType")
private val readMethod = READ_METHOD_NAMES[asmType] ?: error("Read method not found for $asmType")
override fun writeValue(v: InstructionAdapter) {
v.invokevirtual(PARCEL_TYPE.internalName, writeMethod.name, writeMethod.signature, false)
}
override fun readValue(v: InstructionAdapter) {
v.invokevirtual(PARCEL_TYPE.internalName, readMethod.name, readMethod.signature, false)
}
}
private fun readValueNullAware(v: InstructionAdapter, block: () -> Unit) {
val labelNull = Label()
val labelReturn = Label()
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false)
v.ifeq(labelNull)
v.load(1, PARCEL_TYPE)
block()
v.goTo(labelReturn)
// Just push null on stack if the value is null
v.visitLabel(labelNull)
v.aconst(null)
v.visitLabel(labelReturn)
}
private fun writeValueNullAware(v: InstructionAdapter, block: () -> Unit) {
val labelReturn = Label()
val labelNull = Label()
v.dup()
v.ifnull(labelNull)
// Write 1 if non-null, 0 if null
v.load(1, PARCEL_TYPE)
v.aconst(1)
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false)
block()
v.goTo(labelReturn)
v.visitLabel(labelNull)
v.pop()
v.aconst(0)
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false)
v.visitLabel(labelReturn)
}
internal class Method<out T : String?>(val name: String, val signature: T) {
companion object {
operator fun invoke(name: String) = Method(name, null)
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2019 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.serializers
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL
import org.jetbrains.kotlin.parcelize.isParcelize
import java.io.FileDescriptor
interface ParcelizeExtensionBase {
companion object {
val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName)
val CREATOR_NAME = Name.identifier("CREATOR")
val ALLOWED_CLASS_KINDS = listOf(ClassKind.CLASS, ClassKind.OBJECT, ClassKind.ENUM_CLASS)
}
fun ClassDescriptor.hasCreatorField(): Boolean {
val companionObject = companionObjectDescriptor ?: return false
if (companionObject.name == CREATOR_NAME) {
return true
}
return companionObject.unsubstitutedMemberScope
.getContributedVariables(CREATOR_NAME, NoLookupLocation.FROM_BACKEND)
.isNotEmpty()
}
val ClassDescriptor.isParcelizeClassDescriptor get() = kind in ALLOWED_CLASS_KINDS && isParcelize
fun ClassDescriptor.hasSyntheticDescribeContents() = hasParcelizeSyntheticMethod(DESCRIBE_CONTENTS)
fun ClassDescriptor.hasSyntheticWriteToParcel() = hasParcelizeSyntheticMethod(WRITE_TO_PARCEL)
fun ClassDescriptor.findFunction(componentKind: ParcelizeSyntheticComponent.ComponentKind): SimpleFunctionDescriptor? {
return unsubstitutedMemberScope
.getContributedFunctions(Name.identifier(componentKind.methodName), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS)
.firstOrNull { (it as? ParcelizeSyntheticComponent)?.componentKind == componentKind }
}
private fun ClassDescriptor.hasParcelizeSyntheticMethod(componentKind: ParcelizeSyntheticComponent.ComponentKind): Boolean {
val methodName = Name.identifier(componentKind.methodName)
val writeToParcelMethods = unsubstitutedMemberScope
.getContributedFunctions(methodName, NoLookupLocation.FROM_BACKEND)
.filter { it is ParcelizeSyntheticComponent && it.componentKind == componentKind }
return writeToParcelMethods.size == 1
}
}
@@ -0,0 +1,61 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class PrimitiveTypes(
val boo: Boolean, val c: Char, val byt: Byte, val s: Short,
val i: Int, val f: Float, val l: Long, val d: Double,
val nboo: Boolean?, val nc: Char?, val nbyt: Byte?, val ns: Short?,
val ni: Int?, val nf: Float?, val nl: Long?, val nd: Double?,
val jboo: java.lang.Boolean, val jc: java.lang.Character, val jbyt: java.lang.Byte, val js: java.lang.Short,
val ji: java.lang.Integer, val jf: java.lang.Float, val jl: java.lang.Long, val jd: java.lang.Double,
val njboo: java.lang.Boolean?, val njc: java.lang.Character?, val njbyt: java.lang.Byte?, val njs: java.lang.Short?,
val nji: java.lang.Integer?, val njf: java.lang.Float?, val njl: java.lang.Long?, val njd: java.lang.Double?
) : Parcelable
fun box() = parcelTest { parcel ->
val first = PrimitiveTypes(
true, '#', 3.toByte(), 10.toShort(), -300, -5.0f, Long.MAX_VALUE, 3.14,
true, '#', 3.toByte(), 10.toShort(), -300, -5.0f, Long.MAX_VALUE, 3.14,
true as java.lang.Boolean, '#' as java.lang.Character,
3.toByte() as java.lang.Byte, 10.toShort() as java.lang.Short,
-300 as java.lang.Integer, -5.0f as java.lang.Float,
10L as java.lang.Long, 3.14 as java.lang.Double,
true as java.lang.Boolean, '#' as java.lang.Character,
3.toByte() as java.lang.Byte, 10.toShort() as java.lang.Short,
-300 as java.lang.Integer, -5.0f as java.lang.Float,
10L as java.lang.Long, 3.14 as java.lang.Double
)
val second = PrimitiveTypes(
false, '\n', Byte.MIN_VALUE, Short.MIN_VALUE,
Int.MIN_VALUE, Float.POSITIVE_INFINITY, Long.MAX_VALUE, Double.NEGATIVE_INFINITY,
null, null, null, null, null, null, null, null,
false as java.lang.Boolean, '\n' as java.lang.Character,
Byte.MIN_VALUE as java.lang.Byte, Short.MIN_VALUE as java.lang.Short,
Int.MIN_VALUE as java.lang.Integer, Float.POSITIVE_INFINITY as java.lang.Float,
java.lang.Long(Long.MAX_VALUE), java.lang.Double(Double.NEGATIVE_INFINITY),
null, null, null, null, null, null, null, null
)
first.writeToParcel(parcel, 0)
second.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<PrimitiveTypes>(parcel)
val second2 = readFromParcel<PrimitiveTypes>(parcel)
assert(first == first2)
assert(second == second2)
}
@@ -0,0 +1,39 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.Arrays
@Parcelize
data class Test(val a: Array<String>) : Parcelable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as Test
if (!Arrays.equals(a, other.a)) return false
return true
}
override fun hashCode() = Arrays.hashCode(a)
}
fun box() = parcelTest { parcel ->
val first = Test(arrayOf("A", "B"))
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
assert(first == first2)
}
@@ -0,0 +1,77 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.Arrays
@Parcelize
data class Test(
val a: Array<String>,
val b: Array<String?>,
val c: IntArray,
val d: CharArray?,
val e: Array<IntArray>,
val f: Array<List<String>>,
val g: List<Array<String>>,
val h: Array<String>?
) : Parcelable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as Test
if (!Arrays.equals(a, other.a)) return false
if (!Arrays.equals(b, other.b)) return false
if (!Arrays.equals(c, other.c)) return false
if (!Arrays.equals(d, other.d)) return false
if (!Arrays.deepEquals(e, other.e)) return false
if (!Arrays.equals(f, other.f)) return false
if (g.size != other.g.size) return false
if (!g.zip(other.g).all { (f, s) -> Arrays.equals(f, s) }) return false
if (!Arrays.equals(h, other.h)) return false
return true
}
override fun hashCode(): Int {
var result = Arrays.hashCode(a)
result = 31 * result + Arrays.hashCode(b)
result = 31 * result + Arrays.hashCode(c)
result = 31 * result + (d?.let { Arrays.hashCode(it) } ?: 0)
result = 31 * result + Arrays.hashCode(e)
result = 31 * result + Arrays.hashCode(f)
result = 31 * result + g.hashCode()
result = 31 * result + (h?.let { Arrays.hashCode(it) } ?: 0)
return result
}
}
fun box() = parcelTest { parcel ->
val first = Test(
a = arrayOf("A", "B", "C"),
b = arrayOf("A", null, "B"),
c = intArrayOf(1, 2, 3),
d = null,
e = arrayOf(intArrayOf(2, 4, 1), intArrayOf(10, 20)),
f = arrayOf(listOf("A"), listOf("B", "C")),
g = listOf(arrayOf("Z", "X"), arrayOf()),
h = arrayOf("")
)
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
assert(first == first2)
}
@@ -0,0 +1,41 @@
// IGNORE_BACKEND: JVM
// See KT-38103
// There is no such thing as a readStrongInterface method to deserialize arbitrary IIinterface implementations
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Binder
import android.os.IBinder
import android.os.IInterface
import android.os.Parcel
import android.os.Parcelable
import java.io.Serializable
class MockBinder : Binder(), Serializable
@Parcelize
class MockIInterface : IInterface, Parcelable {
override fun asBinder(): IBinder = MockBinder()
}
@Parcelize
class ServiceContainer(
val binder: MockBinder,
val iinterface: MockIInterface,
val binderArray: Array<IBinder>,
val binderList: List<IBinder>
) : Parcelable
fun box() = parcelTest { parcel ->
val test = ServiceContainer(MockBinder(), MockIInterface(), arrayOf(MockBinder()), listOf(MockBinder()))
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<ServiceContainer>(parcel)
}
@@ -0,0 +1,42 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class BoxedTypes(
val boo: java.lang.Boolean,
val c: java.lang.Character,
val byt: java.lang.Byte,
val s: java.lang.Short,
val i: java.lang.Integer,
val f: java.lang.Float,
val l: java.lang.Long,
val d: java.lang.Double
) : Parcelable
fun box() = parcelTest { parcel ->
val first = BoxedTypes(
true as java.lang.Boolean,
'#' as java.lang.Character,
3.toByte() as java.lang.Byte,
10.toShort() as java.lang.Short,
-300 as java.lang.Integer,
-5.0f as java.lang.Float,
Long.MAX_VALUE as java.lang.Long,
3.14 as java.lang.Double)
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<BoxedTypes>(parcel)
assert(first == first2)
}
@@ -0,0 +1,35 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import android.os.Bundle
@Parcelize
data class User(val a: Bundle) : Parcelable
fun box() = parcelTest { parcel ->
val test = User(Bundle().apply { putChar("A", 'c'); putByte("B", 40.toByte()); putString("C", "ABC") })
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<User>(parcel)
assert(compareBundles(test.a, test2.a))
}
private fun compareBundles(first: Bundle, second: Bundle): Boolean {
if (first.size() != second.size()) return false
for (key in first.keySet()) {
if (first.get(key) != second.get(key)) return false
}
return true
}
@@ -0,0 +1,26 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import android.text.SpannableString
@Parcelize
data class Test(val simple: CharSequence, val spanned: CharSequence) : Parcelable
fun box() = parcelTest { parcel ->
val test = Test("John", SpannableString("Smith"))
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<Test>(parcel)
assert(test.simple.toString() == test2.simple.toString())
assert(test.spanned.toString() == test2.spanned.toString())
}
@@ -0,0 +1,40 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class User(val firstName: String, val secondName: String, val age: Int) : Parcelable {
companion object : Parceler<User> {
override fun User.write(parcel: Parcel, flags: Int) {
parcel.writeString(firstName)
parcel.writeString(secondName)
}
override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), 0)
override fun newArray(size: Int) = arrayOfNulls<User>(size) as Array<User>
}
}
fun box() = parcelTest { parcel ->
val user = User("John", "Smith", 20)
val user2 = User("Joe", "Bloggs", 30)
val array = arrayOf(user, user2)
parcel.writeTypedArray(array, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val creator = User::class.java.getDeclaredField("CREATOR").get(null) as Parcelable.Creator<User>
val result = parcel.createTypedArray(creator)
assert(result.size == 2)
assert(result[0].firstName == user.firstName)
assert(result[1].firstName == user2.firstName)
}
@@ -0,0 +1,38 @@
// IGNORE_BACKEND: JVM
// See KT-38105
// Throws IllegalAccessError, since the code tries to access the private companion field directly from the generated User$Creator class.
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
data class User(val name: String, val age: Int)
@Parcelize
data class UserParcelable(val user: User) : Parcelable {
private companion object : Parceler<UserParcelable> {
override fun UserParcelable.write(parcel: Parcel, flags: Int) {
parcel.writeString(user.name)
}
override fun create(parcel: Parcel) = UserParcelable(User(parcel.readString(), 0))
}
}
fun box() = parcelTest { parcel ->
val userParcelable = UserParcelable(User("John", 20))
userParcelable.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val userParcelable2 = readFromParcel<UserParcelable>(parcel)
assert(userParcelable.user.name == userParcelable2.user.name)
assert(userParcelable2.user.age == 0)
}
@@ -0,0 +1,44 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import android.util.SparseBooleanArray
object Parceler1 : Parceler<Int> {
override fun create(parcel: Parcel) = -parcel.readInt()
override fun Int.write(parcel: Parcel, flags: Int) {
parcel.writeInt(this)
}
}
object Parceler2 : Parceler<Int> {
override fun create(parcel: Parcel) = parcel.readString().length
override fun Int.write(parcel: Parcel, flags: Int) {
parcel.writeString("Abc")
}
}
@Parcelize
@TypeParceler<Int, Parceler1>
data class Ints(val a: Int, @TypeParceler<Int, Parceler2> val b: Int, val c: @WriteWith<Parceler2> Int) : Parcelable
fun box() = parcelTest { parcel ->
val test = Ints(1, 1, 1)
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<Ints>(parcel)
assert(test2.a == -test.a)
assert(test2.b == -test.b)
assert(test2.c == 3)
}
@@ -0,0 +1,56 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.Arrays
object Parceler1 : Parceler<Int> {
override fun create(parcel: Parcel) = -parcel.readInt()
override fun Int.write(parcel: Parcel, flags: Int) {
parcel.writeInt(this)
}
}
object Parceler2 : Parceler<Long> {
override fun create(parcel: Parcel) = parcel.readString().length.toLong()
override fun Long.write(parcel: Parcel, flags: Int) {
parcel.writeString("Abc")
}
}
@Parcelize
data class Test(
val a: Int,
@TypeParceler<Int, Parceler1> val b: Int,
@TypeParceler<Long, Parceler2> val c: Long,
@TypeParceler<Int, Parceler1> val d: List<Int>,
@TypeParceler<Long, Parceler2> val e: LongArray
) : Parcelable
fun box() = parcelTest { parcel ->
val test = Test(5, 5, 50L, listOf(1, 2, 3), longArrayOf(3, 2, 1))
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<Test>(parcel)
println(test.toString())
println(test2.toString())
with (test) {
assert(a == 5 && b == 5 && c == 50L && d == listOf(1, 2, 3) && Arrays.equals(e, longArrayOf(3, 2, 1)))
}
with (test2) {
assert(a == 5 && b == -5 && c == 3L && d == listOf(-1, -2, -3) && Arrays.equals(e, longArrayOf(3, 3, 3)))
}
}
@@ -0,0 +1,49 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
object Parceler1 : Parceler<String> {
override fun create(parcel: Parcel) = parcel.readInt().toString()
override fun String.write(parcel: Parcel, flags: Int) {
parcel.writeInt(length)
}
}
typealias Parceler2 = Parceler1
object Parceler3 : Parceler<String> {
override fun create(parcel: Parcel) = parcel.readString().toUpperCase()
override fun String.write(parcel: Parcel, flags: Int) {
parcel.writeString(this)
}
}
@Parcelize
@TypeParceler<String, Parceler2>
data class Test(
val a: String,
@TypeParceler<String, Parceler1> val b: String,
@TypeParceler<String, Parceler3> val c: CharSequence,
val d: @WriteWith<Parceler3> String
) : Parcelable
fun box() = parcelTest { parcel ->
val test = Test("Abc", "Abc", "Abc", "Abc")
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<Test>(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")
}
@@ -0,0 +1,55 @@
// IGNORE_BACKEND: JVM
// See KT-38107
// The JVM backend is missing support for custom parcelers in List<String>
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
object Parceler1 : Parceler<String> {
override fun create(parcel: Parcel) = parcel.readInt().toString()
override fun String.write(parcel: Parcel, flags: Int) {
parcel.writeInt(length)
}
}
object Parceler2 : Parceler<List<String>> {
override fun create(parcel: Parcel) = listOf(parcel.readString())
override fun List<String>.write(parcel: Parcel, flags: Int) {
parcel.writeString(this.joinToString(","))
}
}
@Parcelize
data class Test(
val a: String,
val b: @WriteWith<Parceler1> String,
val c: List<@WriteWith<Parceler1> String>,
val d: @WriteWith<Parceler2> List<String>,
val e: @WriteWith<Parceler2> List<@WriteWith<Parceler1> String>
) : Parcelable
fun box() = parcelTest { parcel ->
val test = Test("Abc", "Abc", listOf("A", "bc"), listOf("A", "bc"), listOf("A", "bc"))
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<Test>(parcel)
with (test) {
assert(a == "Abc" && b == "Abc" && c == listOf("A", "bc") && d == listOf("A", "bc") && e == listOf("A", "bc"))
}
with (test2) {
assert(a == "Abc" && b == "3" && c == listOf("1", "2") && d == listOf("A,bc") && e == listOf("A,bc"))
}
}
@@ -0,0 +1,38 @@
// IGNORE_BACKEND: JVM
// See KT-38105
// Throws IllegalAccessError, since the code tries to access the private companion field directly from the generated User$Creator class.
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class User(val firstName: String, val secondName: String, val age: Int) : Parcelable {
private companion object : Parceler<User> {
override fun User.write(parcel: Parcel, flags: Int) {
parcel.writeString(firstName)
parcel.writeString(secondName)
}
override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), 0)
}
}
fun box() = parcelTest { parcel ->
val user = User("John", "Smith", 20)
user.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val user2 = readFromParcel<User>(parcel)
assert(user.firstName == user2.firstName)
assert(user.secondName == user2.secondName)
assert(user2.age == 0)
}
@@ -0,0 +1,38 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
enum class Color : Parcelable { BLACK, WHITE }
@Parcelize
object Obj : Parcelable
fun box() = parcelTest { parcel ->
val black = Color.BLACK
val obj = Obj
black.writeToParcel(parcel, 0)
obj.writeToParcel(parcel, 0)
println(black)
println(obj)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val black2 = readFromParcel<Color>(parcel)
val obj2 = readFromParcel<Obj>(parcel)
println(black2)
println(obj2)
assert(black2 == black)
assert(obj2 != null)
}
@@ -0,0 +1,27 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
enum class Color {
BLACK, WHITE
}
@Parcelize
data class Test(val name: String, val color: Color) : Parcelable
fun box() = parcelTest { parcel ->
val test = Test("John", Color.WHITE)
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<Test>(parcel)
assert(test == test2)
}
@@ -0,0 +1,25 @@
// IGNORE_BACKEND: JVM
// Parcel.readException throws the exception it reads and only supports a small number of exception types.
// If we have to parcel an exception we should instead use read/writeSerializable (compare KT-31830)
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
class ExceptionContainer(val exn: Exception) : Parcelable
fun box() = parcelTest { parcel ->
val test = ExceptionContainer(java.lang.RuntimeException("Don't throw me."))
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<ExceptionContainer>(parcel)
}
@@ -0,0 +1,24 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class Test(val callback: () -> Int = { 0 }, val suspendCallback: suspend () -> Int = { 0 }) : Parcelable
fun box() = parcelTest { parcel ->
val test = Test({ 1 }, { 1 })
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<Test>(parcel)
assert(test.callback() == 1)
}
@@ -0,0 +1,39 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.Arrays
@Parcelize
data class Film(val genres: Array<Int>) : Parcelable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Film
if (!Arrays.equals(genres, other.genres)) return false
return true
}
override fun hashCode(): Int {
return Arrays.hashCode(genres)
}
}
fun box() = parcelTest { parcel ->
val film = Film(arrayOf(3, 5, 7))
film.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val film2 = readFromParcel<Film>(parcel)
assert(film == film2)
}
@@ -0,0 +1,33 @@
// This issue affects AIDL generated files, as reported in KT-25807
// WITH_RUNTIME
// FILE: J.java
import android.os.Parcel;
import test.K;
public class J {
public static K readParcel(Parcel parcel) {
return K.CREATOR.createFromParcel(parcel);
}
}
// FILE: test.kt
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class K(val x: Int) : Parcelable
fun box() = parcelTest { parcel ->
val first = K(0)
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val second = J.readParcel(parcel)
assert(first == second)
}
@@ -0,0 +1,35 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.io.Serializable
class JHelp(var j1: String) {
val j2 = 9
}
@Parcelize
class J(val j: @RawValue JHelp) : Parcelable
fun box() = parcelTest { parcel ->
val test = J(JHelp("A"))
var exceptionCaught = false
try {
test.writeToParcel(parcel, 0)
} catch (e: RuntimeException) {
if (e.message!!.contains("Parcel: unable to marshal value test.JHelp")) {
exceptionCaught = true
} else {
throw e
}
}
if (!exceptionCaught) {
error("Exception should be thrown")
}
}
@@ -0,0 +1,33 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.io.Serializable
interface IJHelp {
val j1: String
}
class JHelp(override var j1: String): IJHelp, Serializable {
val j2 = 9
}
@Parcelize
class J(val j: @RawValue JHelp) : Parcelable
fun box() = parcelTest { parcel ->
val test = J(JHelp("A"))
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<J>(parcel)
assert(test.j.j1 == test2.j.j1)
}
@@ -0,0 +1,29 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.io.Serializable
class MHelp(var m1: String): Serializable {
val m2 = 9
}
@Parcelize
class M(val m: @RawValue MHelp) : Parcelable
fun box() = parcelTest { parcel ->
val test = M(MHelp("A"))
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<M>(parcel)
assert(test.m.m1 == test2.m.m1)
}
@@ -0,0 +1,40 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.Arrays
@Parcelize
data class Test(val a: LongArray, val b: List<Long>) : Parcelable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as Test
if (!Arrays.equals(a, other.a)) return false
if (b != other.b) return false
return true
}
override fun hashCode() = Arrays.hashCode(a)
}
fun box() = parcelTest { parcel ->
val first = Test(longArrayOf(1, 2, 3, 4, 5), listOf(1, 2, 3, 4))
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
assert(first == first2)
}
@@ -0,0 +1,49 @@
// IGNORE_BACKEND: JVM
// See KT-38106
// This feature regressed with the fix for KT-22576
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.Arrays
enum class ParcelableEnum : Parcelable {
ONE, TWO, THREE;
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(ordinal)
}
override fun describeContents() = 0
companion object {
@JvmField
val CREATOR: Parcelable.Creator<ParcelableEnum> = object : Parcelable.Creator<ParcelableEnum> {
override fun createFromParcel(parcel: Parcel) = ParcelableEnum.ONE
override fun newArray(size: Int) = arrayOfNulls<ParcelableEnum>(size)
}
}
}
@Parcelize
class Test(val parcelableEnum: ParcelableEnum): Parcelable
fun box() = parcelTest { parcel ->
val first = Test(ParcelableEnum.THREE)
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
assert(first.parcelableEnum == ParcelableEnum.THREE)
assert(first2.parcelableEnum == ParcelableEnum.ONE)
assert(first != first2)
}
@@ -0,0 +1,30 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
fun box() = doTest { creator ->
assert(creator.newArray(5) != null)
}
fun doTest(work: (Parcelable.Creator<DummyParcelable>) -> Unit): String {
val dummy = DummyParcelable(42)
val clazz = dummy.javaClass
val field = clazz.getDeclaredField("CREATOR")
val creator = field.get(dummy) as Parcelable.Creator<DummyParcelable>
val parcel = Parcel.obtain()
dummy.writeToParcel(parcel, 0)
parcel.setDataPosition(0)
work(creator)
return "OK"
}
@Parcelize
data class DummyParcelable(val int: Int): Parcelable
@@ -0,0 +1,29 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
class User : Parcelable
@Parcelize
class User2() : Parcelable
fun box() = parcelTest { parcel ->
val user = User()
val user2 = User2()
user.writeToParcel(parcel, 0)
user2.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
readFromParcel<User>(parcel)
readFromParcel<User2>(parcel)
}
@@ -0,0 +1,39 @@
// IGNORE_BACKEND: JVM
// Fails with a VerifyError in Foo.writeToParcel
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import android.util.SparseArray
@Parcelize
data class PInt(val x: Int) : Parcelable
@Parcelize
data class Foo(val values: SparseArray<SparseArray<Parcelable>>) : Parcelable
fun box() = parcelTest { parcel ->
val pint = PInt(0)
val sarray = SparseArray<Parcelable>()
sarray.put(0, pint)
val sarray2 = SparseArray<SparseArray<Parcelable>>()
sarray2.put(1, sarray)
val foo = Foo(sarray2)
foo.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val foo2 = readFromParcel<Foo>(parcel)
assert(foo2.values.size() == 1)
assert(foo2.values.get(1) != null) // SparseArray.contains was only added in Android R
assert(foo2.values.get(1).size() == 1)
assert(foo2.values.get(1).get(0) != null)
assert(foo2.values.get(1).get(0) == pint)
}
@@ -0,0 +1,23 @@
// IGNORE_BACKEND: JVM
// StackOverflowError caused by infinite loop in MyObject.writeToParcel
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
private object MyObject : Parcelable
fun box() = parcelTest { parcel ->
MyObject.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
readFromParcel<MyObject>(parcel)
}
@@ -0,0 +1,21 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize class TestParcel : Parcelable {
companion object : Parceler<TestParcel> {
override fun create(parcel: Parcel): TestParcel {
return TestParcel()
}
override fun TestParcel.write(parcel: Parcel, flags: Int) {}
}
}
fun box() = parcelTest { parcel ->
TestParcel()
}
@@ -0,0 +1,55 @@
// WITH_RUNTIME
// FULL_JDK
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.*
@Parcelize
data class Test(
val a: List<String>,
val b: MutableList<String>,
val c: ArrayList<String>,
val d: LinkedList<String>,
val e: Set<String>,
val f: MutableSet<String>,
val g: TreeSet<String>,
val h: HashSet<String>,
val i: LinkedHashSet<String>,
val j: NavigableSet<String>,
val k: SortedSet<String>
) : Parcelable
fun box() = parcelTest { parcel ->
val first = Test(
a = listOf("A"),
b = mutableListOf("B"),
c = ArrayList<String>().apply { this += "C" },
d = LinkedList<String>().apply { this += "D" },
e = setOf("E"),
f = mutableSetOf("F"),
g = TreeSet<String>().apply { this += "G" },
h = HashSet<String>().apply { this += "H" },
i = LinkedHashSet<String>().apply { this += "I" },
j = TreeSet<String>().apply { this += "J" },
k = TreeSet<String>().apply { this += "K" }
)
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
assert(first == first2)
assert((first.d as LinkedList<*>).size == 1)
assert((first2.h as HashSet<*>).size == 1)
assert(first2.j is NavigableSet<*>)
assert(first2.k is SortedSet<*>)
}
@@ -0,0 +1,25 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class Test(val a: List<String>) : Parcelable
fun box() = parcelTest { parcel ->
val first = Test(listOf("A", "B"))
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
assert(first == first2)
}
@@ -0,0 +1,42 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class Test(
val a: List<String>,
val b: List<String?>,
val c: List<Int>,
val d: List<Int?>,
val e: List<List<String>?>,
val f: List<List<List<Int>>>
) : Parcelable
fun box() = parcelTest { parcel ->
val first = Test(
a = listOf("A", "B"),
b = listOf("A", null, "C"),
c = listOf(1, 2, 3),
d = listOf(1, null, 5),
e = listOf(listOf("A", "B"), listOf(), null),
f = listOf(listOf(listOf(1, 2), listOf(3)), listOf(listOf(5, 3)))
)
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
assert(first == first2)
assert(first2.a == listOf("A", "B"))
assert(first2.b.size == 3)
}
@@ -0,0 +1,47 @@
// WITH_RUNTIME
// FULL_JDK
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.*
@Parcelize
data class Test(
val a: Map<String, String>,
val b: MutableMap<String, String>,
val c: HashMap<String, String>,
val d: LinkedHashMap<String, String>,
val e: TreeMap<String, String>,
val f: SortedMap<String, String>,
val g: NavigableMap<String, String>
) : Parcelable
fun box() = parcelTest { parcel ->
val first = Test(
a = mapOf("A" to "B"),
b = mutableMapOf("A" to "B"),
c = HashMap<String, String>().apply { put("A", "B") },
d = LinkedHashMap<String, String>().apply { put("A", "B") },
e = TreeMap<String, String>().apply { put("A", "B") },
f = TreeMap<String, String>().apply { put("A", "B") },
g = TreeMap<String, String>().apply { put("A", "B") }
)
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
assert(first == first2)
assert((first.c as HashMap<*, *>).size == 1)
assert((first2.e as TreeMap<*, *>).size == 1)
assert(first2.f is SortedMap<*, *>)
assert(first2.g is NavigableMap<*, *>)
}
@@ -0,0 +1,25 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class Test(val a: Map<String, String>) : Parcelable
fun box() = parcelTest { parcel ->
val first = Test(mapOf("A" to "B", "C" to "D"))
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
assert(first == first2)
}
@@ -0,0 +1,41 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class Test(
val a: Map<String, String>,
val b: Map<String?, String>,
val c: Map<String, String?>,
val d: Map<String, Map<Int, String>>,
val e: Map<Int?, List<String>>,
val f: Map<Boolean, Boolean>,
val g: Map<String, Map<String, Map<String, String>>>
) : Parcelable
fun box() = parcelTest { parcel ->
val first = Test(
a = mapOf("A" to "B", "C" to "D"),
b = mapOf("A" to "B", null to "D", "E" to "F"),
c = mapOf("A" to null, "C" to "D"),
d = mapOf("A" to mapOf(1 to "", 2 to "x")),
e = mapOf(1 to listOf("", ""), null to listOf()),
f = mapOf(true to false, false to true),
g = mapOf("A" to mapOf("B" to mapOf("C" to "D", "E" to "F")))
)
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
assert(first == first2)
}
@@ -0,0 +1,26 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.Arrays
@Parcelize
class Data(val data: Array<Array<Int>>) : Parcelable
fun box() = parcelTest { parcel ->
val first = Data(arrayOf(arrayOf(0, 1)))
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val second = readFromParcel<Data>(parcel)
assert(second.data.size == 1)
assert(Arrays.equals(second.data[0], arrayOf(0, 1)))
}
@@ -0,0 +1,26 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.Arrays
@Parcelize
class Data(val data: List<Array<Int>>) : Parcelable
fun box() = parcelTest { parcel ->
val first = Data(listOf(arrayOf(0, 1)))
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val second = readFromParcel<Data>(parcel)
assert(second.data.size == 1)
assert(Arrays.equals(second.data[0], arrayOf(0, 1)))
}
@@ -0,0 +1,28 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.Arrays
@Parcelize
class Data(val data: Map<Array<Int>, Array<Int>>) : Parcelable
fun box() = parcelTest { parcel ->
val first = Data(mapOf(arrayOf(0) to arrayOf(1)))
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val second = readFromParcel<Data>(parcel)
assert(second.data.size == 1)
val entry = second.data.entries.single()
assert(Arrays.equals(entry.key, arrayOf(0)))
assert(Arrays.equals(entry.value, arrayOf(1)))
}
@@ -0,0 +1,34 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
// Starts with A, should be loaded before other classes
abstract class AParcelable : Parcelable
@Parcelize
data class P1(val a: String) : AParcelable()
sealed class Sealed : AParcelable()
@Parcelize
data class Sealed1(val a: Int) : Sealed()
@Parcelize
data class Test(val a: P1, val b: AParcelable, val c: Sealed, val d: Sealed1) : Parcelable
fun box() = parcelTest { parcel ->
val test = Test(P1(""), P1("My"), Sealed1(1), Sealed1(5))
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<Test>(parcel)
assert(test == test2)
}
@@ -0,0 +1,30 @@
// WITH_RUNTIME
// IGNORE_BACKEND: JVM
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import android.util.SparseArray
import java.util.Arrays
@Parcelize
class Data(val data: SparseArray<Array<Int>>) : Parcelable
fun box() = parcelTest { parcel ->
var array = SparseArray<Array<Int>>()
array.append(0, arrayOf(0, 1))
val first = Data(array)
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val second = readFromParcel<Data>(parcel)
assert(second.data.size() == 1)
assert(Arrays.equals(second.data.get(0), arrayOf(0, 1)))
}
@@ -0,0 +1,29 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class User(val firstName: String, val secondName: String, val age: Int) : Parcelable
fun box() = parcelTest { parcel ->
val user = User("John", "Smith", 20)
val user2 = User("Joe", "Bloggs", 30)
val array = arrayOf(user, user2)
parcel.writeTypedArray(array, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val creator = User::class.java.getDeclaredField("CREATOR").get(null) as Parcelable.Creator<User>
val result = parcel.createTypedArray(creator)
assert(result.size == 2)
assert(result[0].firstName == user.firstName)
assert(result[1].firstName == user2.firstName)
}
@@ -0,0 +1,38 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class Foo(val a: String) : Parcelable
@Parcelize
data class Test(
val str1: String,
val str2: String?,
val int1: Int,
val int2: Int?,
val foo: Foo?
) : Parcelable
fun box() = parcelTest { parcel ->
val first = Test("John", "Smith", 20, 30, Foo("a"))
val second = Test("A", null, 20, null, null)
first.writeToParcel(parcel, 0)
second.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
val second2 = readFromParcel<Test>(parcel)
assert(first == first2)
assert(second == second2)
}
@@ -0,0 +1,29 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class Test(val a: String?) : Parcelable
fun box() = parcelTest { parcel ->
val first = Test("John")
val second = Test(null)
first.writeToParcel(parcel, 0)
second.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<Test>(parcel)
val second2 = readFromParcel<Test>(parcel)
assert(first == first2)
assert(second == second2)
}
@@ -0,0 +1,31 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
object Obj1 {
object Obj2
}
@Parcelize
data class Test(val o1: Obj1, val o2: Obj1.Obj2, val com: Com) : Parcelable {
companion object Com {
}
}
fun box() = parcelTest { parcel ->
val test = Test(Obj1, Obj1.Obj2, Test.Com)
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<Test>(parcel)
assert(test == test2)
}
@@ -0,0 +1,19 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
open class Base(val a: String) : Parcelable
@Parcelize
class Inh(var b: Int) : Base(""), Parcelable
fun box(): String {
Inh(0)
return "OK"
}
@@ -0,0 +1,41 @@
// IGNORE_BACKEND: JVM
// See KT-38104
// The support for PersistableBundles is broken on JVM.
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import android.os.BaseBundle
import android.os.PersistableBundle
@Parcelize
data class User(val a: PersistableBundle) : Parcelable
fun box() = parcelTest { parcel ->
val test = User(
PersistableBundle().apply { putLong("A", 1L) }
)
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<User>(parcel)
assert(compareBundles(test.a, test2.a))
}
private fun compareBundles(first: BaseBundle, second: BaseBundle): Boolean {
if (first.size() != second.size()) return false
for (key in first.keySet()) {
if (first.get(key) != second.get(key)) return false
}
return true
}
@@ -0,0 +1,39 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
data class PrimitiveTypes(
val boo: Boolean,
val c: Char,
val byt: Byte,
val s: Short,
val i: Int,
val f: Float,
val l: Long,
val d: Double
) : Parcelable
fun box() = parcelTest { parcel ->
val first = PrimitiveTypes(true, '#', 3.toByte(), 10.toShort(), -300, -5.0f, Long.MAX_VALUE, 3.14)
val second = PrimitiveTypes(false, '\n', Byte.MIN_VALUE, Short.MIN_VALUE, Int.MIN_VALUE, Float.POSITIVE_INFINITY,
Long.MAX_VALUE, Double.NEGATIVE_INFINITY)
first.writeToParcel(parcel, 0)
second.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = readFromParcel<PrimitiveTypes>(parcel)
val second2 = readFromParcel<PrimitiveTypes>(parcel)
assert(first == first2)
assert(second == second2)
}
@@ -0,0 +1,33 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
sealed class Foo : Parcelable {
@Parcelize
data class A(val x: Int) : Foo()
@Parcelize
data class B (val x: String) : Foo()
}
@Parcelize
data class Bar(val a: Foo) : Parcelable
fun box() = parcelTest { parcel ->
val first = Bar(Foo.B("OK"))
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val second = readFromParcel<Bar>(parcel)
assert(first == second)
}
@@ -0,0 +1,25 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
annotation class SerializableLike
@Parcelize @SerializableLike
data class User(val firstName: String, val secondName: String, val age: Int) : Parcelable
fun box() = parcelTest { parcel ->
val user = User("John", "Smith", 20)
user.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val user2 = readFromParcel<User>(parcel)
assert(user == user2)
}
@@ -0,0 +1,71 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import android.util.*
@Parcelize
data class Data(val a: String, val b: String) : Parcelable
@Parcelize
data class User(val a: SparseIntArray, val b: SparseLongArray, val c: SparseArray<Data>) : Parcelable
fun box() = parcelTest { parcel ->
val user = User(
a = SparseIntArray().apply { put(1, 5); put(100, -1); put(1000, 0) },
b = SparseLongArray().apply { put(3, 2); put(2, 3); put(10, 10) },
c = SparseArray<Data>().apply { put(1, Data("A", "B")); put(10, Data("C", "D")); put(105, Data("E", "")) }
)
user.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val user2 = readFromParcel<User>(parcel)
assert(compareSparseIntArrays(user.a, user2.a))
assert(compareSparseLongArrays(user.b, user2.b))
assert(compareSparseArrays(user.c, user2.c))
}
private fun compareSparseIntArrays(first: SparseIntArray, second: SparseIntArray): Boolean {
if (first === second) return true
if (first.size() != second.size()) return false
for (i in 0 until first.size()) {
if (first.keyAt(i) != second.keyAt(i)) return false
if (first.valueAt(i) != second.valueAt(i)) return false
}
return true
}
private fun compareSparseLongArrays(first: SparseLongArray, second: SparseLongArray): Boolean {
if (first === second) return true
if (first.size() != second.size()) return false
for (i in 0 until first.size()) {
if (first.keyAt(i) != second.keyAt(i)) return false
if (first.valueAt(i) != second.valueAt(i)) return false
}
return true
}
private fun compareSparseArrays(first: SparseArray<*>, second: SparseArray<*>): Boolean {
if (first === second) return true
if (first.size() != second.size()) return false
for (i in 0 until first.size()) {
if (first.keyAt(i) != second.keyAt(i)) return false
if (first.valueAt(i) != second.valueAt(i)) return false
}
return true
}
@@ -0,0 +1,37 @@
// WITH_RUNTIME
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import android.util.SparseBooleanArray
@Parcelize
data class User(val a: SparseBooleanArray) : Parcelable
fun box() = parcelTest { parcel ->
val test = User(SparseBooleanArray().apply { put(1, false); put(5, true); put(1000, false) })
test.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val test2 = readFromParcel<User>(parcel)
assert(compareSparseBooleanArrays(test.a, test2.a))
}
private fun compareSparseBooleanArrays(first: SparseBooleanArray, second: SparseBooleanArray): Boolean {
if (first === second) return true
if (first.size() != second.size()) return false
for (i in 0 until first.size()) {
if (first.keyAt(i) != second.keyAt(i)) return false
if (first.valueAt(i) != second.valueAt(i)) return false
}
return true
}
+18
View File
@@ -0,0 +1,18 @@
package test
import android.os.Parcel
fun parcelTest(block: (Parcel) -> Unit): String {
val parcel = Parcel.obtain()
try {
block(parcel)
return "OK"
} finally {
parcel.recycle()
}
}
inline fun <reified T> readFromParcel(parcel: Parcel): T {
val creator = T::class.java.getDeclaredField("CREATOR").get(null)
return creator::class.java.getDeclaredMethod("createFromParcel", Parcel::class.java).invoke(creator, parcel) as T
}
@@ -0,0 +1,89 @@
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final User createFromParcel(android.os.Parcel parcel) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
NEW
DUP
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readStrongBinder, ()Landroid/os/IBinder;)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createBinderArray, ()[Landroid/os/IBinder;)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createBinderArrayList, ()Ljava/util/ArrayList;)
CHECKCAST
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createBinderArrayList, ()Ljava/util/ArrayList;)
INVOKESPECIAL (User, <init>, (Landroid/os/IBinder;[Landroid/os/IBinder;Ljava/util/List;Ljava/util/ArrayList;)V)
ARETURN
LABEL (L1)
}
public java.lang.Object createFromParcel(android.os.Parcel source) {
LABEL (L0)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (User$Creator, createFromParcel, (Landroid/os/Parcel;)LUser;)
ARETURN
LABEL (L1)
}
public final User[] newArray(int size)
public java.lang.Object[] newArray(int size)
}
public final class User : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final android.os.IBinder binder
private final android.os.IBinder[] binderArray
private final java.util.ArrayList binderArrayList
private final java.util.List binderList
static void <clinit>()
public void <init>(android.os.IBinder binder, android.os.IBinder[] binderArray, java.util.List binderList, java.util.ArrayList binderArrayList)
public int describeContents()
public final android.os.IBinder getBinder()
public final android.os.IBinder[] getBinderArray()
public final java.util.ArrayList getBinderArrayList()
public final java.util.List getBinderList()
public void writeToParcel(android.os.Parcel out, int flags) {
LABEL (L0)
ALOAD (1)
LDC (out)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (binder, Landroid/os/IBinder;)
INVOKEVIRTUAL (android/os/Parcel, writeStrongBinder, (Landroid/os/IBinder;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (binderArray, [Landroid/os/IBinder;)
INVOKEVIRTUAL (android/os/Parcel, writeBinderArray, ([Landroid/os/IBinder;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (binderList, Ljava/util/List;)
INVOKEVIRTUAL (android/os/Parcel, writeBinderList, (Ljava/util/List;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (binderArrayList, Ljava/util/ArrayList;)
CHECKCAST
INVOKEVIRTUAL (android/os/Parcel, writeBinderList, (Ljava/util/List;)V)
RETURN
LABEL (L1)
}
}
@@ -0,0 +1,17 @@
// CURIOUS_ABOUT writeToParcel, createFromParcel
// WITH_RUNTIME
import kotlinx.parcelize.*
import android.os.Parcelable
import android.os.IBinder
import android.os.IInterface
@Parcelize
class User(
val binder: IBinder,
val binderArray: Array<IBinder>,
val binderList: List<IBinder>,
val binderArrayList: ArrayList<IBinder> // should be serialized using our strategy, not using Parcel.writeBinderList()
// There is no readStrongInterface method in Parcel.
// val intf: IInterface?
) : Parcelable
@@ -0,0 +1,126 @@
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final User createFromParcel(android.os.Parcel in) {
LABEL (L0)
ALOAD (1)
LDC (in)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
NEW
DUP
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readStrongBinder, ()Landroid/os/IBinder;)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createBinderArray, ()[Landroid/os/IBinder;)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createBinderArrayList, ()Ljava/util/ArrayList;)
CHECKCAST
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
ISTORE (2)
NEW
DUP
ILOAD (2)
INVOKESPECIAL (java/util/ArrayList, <init>, (I)V)
ILOAD (2)
LABEL (L2)
DUP_X1
IFEQ (L3)
DUP
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readStrongBinder, ()Landroid/os/IBinder;)
INVOKEVIRTUAL (java/util/ArrayList, add, (Ljava/lang/Object;)Z)
POP
SWAP
LDC (-1)
IADD
GOTO (L2)
LABEL (L3)
SWAP
POP
INVOKESPECIAL (User, <init>, (Landroid/os/IBinder;[Landroid/os/IBinder;Ljava/util/List;Ljava/util/ArrayList;)V)
ARETURN
LABEL (L4)
}
public java.lang.Object createFromParcel(android.os.Parcel p0) {
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (User$Creator, createFromParcel, (Landroid/os/Parcel;)LUser;)
ARETURN
}
public final User[] newArray(int size)
public java.lang.Object[] newArray(int p0)
}
public final class User : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final android.os.IBinder binder
private final android.os.IBinder[] binderArray
private final java.util.ArrayList binderArrayList
private final java.util.List binderList
static void <clinit>()
public void <init>(android.os.IBinder binder, android.os.IBinder[] binderArray, java.util.List binderList, java.util.ArrayList binderArrayList)
public int describeContents()
public final android.os.IBinder getBinder()
public final android.os.IBinder[] getBinderArray()
public final java.util.ArrayList getBinderArrayList()
public final java.util.List getBinderList()
public void writeToParcel(android.os.Parcel parcel, int flags) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (binder, Landroid/os/IBinder;)
INVOKEVIRTUAL (android/os/Parcel, writeStrongBinder, (Landroid/os/IBinder;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (binderArray, [Landroid/os/IBinder;)
INVOKEVIRTUAL (android/os/Parcel, writeBinderArray, ([Landroid/os/IBinder;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (binderList, Ljava/util/List;)
INVOKEVIRTUAL (android/os/Parcel, writeBinderList, (Ljava/util/List;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (binderArrayList, Ljava/util/ArrayList;)
DUP_X1
INVOKEINTERFACE (java/util/Collection, size, ()I)
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
INVOKEINTERFACE (java/util/Collection, iterator, ()Ljava/util/Iterator;)
LABEL (L1)
DUP
INVOKEINTERFACE (java/util/Iterator, hasNext, ()Z)
IFEQ (L2)
DUP
ALOAD (1)
SWAP
INVOKEINTERFACE (java/util/Iterator, next, ()Ljava/lang/Object;)
CHECKCAST
INVOKEVIRTUAL (android/os/Parcel, writeStrongBinder, (Landroid/os/IBinder;)V)
GOTO (L1)
LABEL (L2)
POP
RETURN
LABEL (L3)
}
}
@@ -0,0 +1,45 @@
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final User createFromParcel(android.os.Parcel parcel)
public java.lang.Object createFromParcel(android.os.Parcel source)
public final User[] newArray(int size)
public java.lang.Object[] newArray(int size)
}
public final class User : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final int age
private final java.lang.String firstName
private final boolean isProUser
private final java.lang.String lastName
static void <clinit>()
public void <init>(java.lang.String firstName, java.lang.String lastName, int age, boolean isProUser)
public int describeContents() {
LABEL (L0)
LINENUMBER (9)
BIPUSH (100)
IRETURN
LABEL (L1)
}
public final int getAge()
public final java.lang.String getFirstName()
public final java.lang.String getLastName()
public final boolean isProUser()
public void writeToParcel(android.os.Parcel out, int flags)
}
@@ -0,0 +1,10 @@
// CURIOUS_ABOUT describeContents
// WITH_RUNTIME
import kotlinx.parcelize.*
import android.os.Parcelable
@Parcelize
class User(val firstName: String, val lastName: String, val age: Int, val isProUser: Boolean) : Parcelable {
override fun describeContents() = 100
}
@@ -0,0 +1,45 @@
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final User createFromParcel(android.os.Parcel in)
public java.lang.Object createFromParcel(android.os.Parcel p0)
public final User[] newArray(int size)
public java.lang.Object[] newArray(int p0)
}
public final class User : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final int age
private final java.lang.String firstName
private final boolean isProUser
private final java.lang.String lastName
static void <clinit>()
public void <init>(java.lang.String firstName, java.lang.String lastName, int age, boolean isProUser)
public int describeContents() {
LABEL (L0)
LINENUMBER (9)
BIPUSH (100)
IRETURN
LABEL (L1)
}
public final int getAge()
public final java.lang.String getFirstName()
public final java.lang.String getLastName()
public final boolean isProUser()
public void writeToParcel(android.os.Parcel parcel, int flags)
}
@@ -0,0 +1,69 @@
public final class test/Foo$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final test.Foo createFromParcel(android.os.Parcel parcel) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
NEW
DUP
ALOAD (1)
LDC (Ltest/Foo;)
INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;)
INVOKEVIRTUAL (android/os/Parcel, readParcelable, (Ljava/lang/ClassLoader;)Landroid/os/Parcelable;)
CHECKCAST
INVOKESPECIAL (test/Foo, <init>, (Landroid/accounts/Account;)V)
ARETURN
LABEL (L1)
}
public java.lang.Object createFromParcel(android.os.Parcel source) {
LABEL (L0)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (test/Foo$Creator, createFromParcel, (Landroid/os/Parcel;)Ltest/Foo;)
ARETURN
LABEL (L1)
}
public final test.Foo[] newArray(int size)
public java.lang.Object[] newArray(int size)
}
public final class test/Foo : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final android.accounts.Account kp
static void <clinit>() {
NEW
DUP
INVOKESPECIAL (test/Foo$Creator, <init>, ()V)
CHECKCAST
PUTSTATIC (CREATOR, Landroid/os/Parcelable$Creator;)
RETURN
}
public void <init>(android.accounts.Account kp)
public int describeContents()
public final android.accounts.Account getKp()
public void writeToParcel(android.os.Parcel out, int flags) {
LABEL (L0)
ALOAD (1)
LDC (out)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (kp, Landroid/accounts/Account;)
CHECKCAST
ILOAD (2)
INVOKEVIRTUAL (android/os/Parcel, writeParcelable, (Landroid/os/Parcelable;I)V)
RETURN
LABEL (L1)
}
}
@@ -0,0 +1,11 @@
// CURIOUS_ABOUT writeToParcel, createFromParcel, <clinit>
// WITH_RUNTIME
package test
import kotlinx.parcelize.*
import android.os.*
import android.accounts.Account
@Parcelize
class Foo(val kp: Account): Parcelable
@@ -0,0 +1,67 @@
public final class test/Foo$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final test.Foo createFromParcel(android.os.Parcel in) {
LABEL (L0)
ALOAD (1)
LDC (in)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
NEW
DUP
ALOAD (1)
LDC (Ltest/Foo;)
INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;)
INVOKEVIRTUAL (android/os/Parcel, readParcelable, (Ljava/lang/ClassLoader;)Landroid/os/Parcelable;)
CHECKCAST
INVOKESPECIAL (test/Foo, <init>, (Landroid/accounts/Account;)V)
ARETURN
LABEL (L1)
}
public java.lang.Object createFromParcel(android.os.Parcel p0) {
LABEL (L0)
LINENUMBER (11)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (test/Foo$Creator, createFromParcel, (Landroid/os/Parcel;)Ltest/Foo;)
ARETURN
}
public final test.Foo[] newArray(int size)
public java.lang.Object[] newArray(int p0)
}
public final class test/Foo : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final android.accounts.Account kp
static void <clinit>() {
NEW
DUP
INVOKESPECIAL (test/Foo$Creator, <init>, ()V)
PUTSTATIC (CREATOR, Landroid/os/Parcelable$Creator;)
RETURN
}
public void <init>(android.accounts.Account kp)
public int describeContents()
public final android.accounts.Account getKp()
public void writeToParcel(android.os.Parcel parcel, int flags) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (kp, Landroid/accounts/Account;)
ILOAD (2)
INVOKEVIRTUAL (android/os/Parcel, writeParcelable, (Landroid/os/Parcelable;I)V)
RETURN
LABEL (L1)
}
}
@@ -0,0 +1,169 @@
public final class k/KotlinParcelable$Companion : java/lang/Object {
private void <init>()
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
}
public final class k/KotlinParcelable$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public k.KotlinParcelable createFromParcel(android.os.Parcel source) {
LABEL (L0)
ALOAD (1)
LDC (source)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (23)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
ISTORE (2)
LABEL (L2)
LINENUMBER (24)
NEW
DUP
ILOAD (2)
INVOKESPECIAL (k/KotlinParcelable, <init>, (I)V)
ARETURN
LABEL (L3)
}
public java.lang.Object createFromParcel(android.os.Parcel p0) {
LABEL (L0)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (k/KotlinParcelable$Creator, createFromParcel, (Landroid/os/Parcel;)Lk/KotlinParcelable;)
ARETURN
LABEL (L1)
}
public k.KotlinParcelable[] newArray(int size)
public java.lang.Object[] newArray(int p0)
}
public final class k/KotlinParcelable : java/lang/Object, android/os/Parcelable {
public final static k.KotlinParcelable$Creator CREATOR
public final static k.KotlinParcelable$Companion Companion
private int data
static void <clinit>() {
NEW
DUP
ACONST_NULL
INVOKESPECIAL (k/KotlinParcelable$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Companion, Lk/KotlinParcelable$Companion;)
LABEL (L0)
LINENUMBER (18)
NEW
DUP
INVOKESPECIAL (k/KotlinParcelable$Creator, <init>, ()V)
PUTSTATIC (CREATOR, Lk/KotlinParcelable$Creator;)
RETURN
}
public void <init>(int data)
public final int component1()
public final k.KotlinParcelable copy(int data)
public static k.KotlinParcelable copy$default(k.KotlinParcelable p0, int p1, int p2, java.lang.Object p3)
public int describeContents()
public boolean equals(java.lang.Object other)
public final int getData()
public int hashCode()
public final void setData(int <set-?>)
public java.lang.String toString()
public void writeToParcel(android.os.Parcel dest, int flags) {
LABEL (L0)
ALOAD (1)
LDC (dest)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (13)
ALOAD (1)
ALOAD (0)
GETFIELD (data, I)
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
LABEL (L2)
LINENUMBER (14)
RETURN
LABEL (L3)
}
}
public final class test/Foo$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final test.Foo createFromParcel(android.os.Parcel parcel) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
NEW
DUP
GETSTATIC (CREATOR, Lk/KotlinParcelable$Creator;)
ALOAD (1)
INVOKEVIRTUAL (k/KotlinParcelable$Creator, createFromParcel, (Landroid/os/Parcel;)Lk/KotlinParcelable;)
INVOKESPECIAL (test/Foo, <init>, (Lk/KotlinParcelable;)V)
ARETURN
LABEL (L1)
}
public java.lang.Object createFromParcel(android.os.Parcel source) {
LABEL (L0)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (test/Foo$Creator, createFromParcel, (Landroid/os/Parcel;)Ltest/Foo;)
ARETURN
LABEL (L1)
}
public final test.Foo[] newArray(int size)
public java.lang.Object[] newArray(int size)
}
public final class test/Foo : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final k.KotlinParcelable kp
static void <clinit>() {
NEW
DUP
INVOKESPECIAL (test/Foo$Creator, <init>, ()V)
CHECKCAST
PUTSTATIC (CREATOR, Landroid/os/Parcelable$Creator;)
RETURN
}
public void <init>(k.KotlinParcelable kp)
public int describeContents()
public final k.KotlinParcelable getKp()
public void writeToParcel(android.os.Parcel out, int flags) {
LABEL (L0)
ALOAD (1)
LDC (out)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (0)
GETFIELD (kp, Lk/KotlinParcelable;)
ALOAD (1)
ILOAD (2)
INVOKEVIRTUAL (k/KotlinParcelable, writeToParcel, (Landroid/os/Parcel;I)V)
RETURN
LABEL (L1)
}
}
@@ -0,0 +1,40 @@
// CURIOUS_ABOUT writeToParcel, createFromParcel, <clinit>
// WITH_RUNTIME
// FILE: KotlinParcelable.kt
package k
import android.os.*
data class KotlinParcelable(var data: Int): Parcelable {
override fun describeContents() = 1
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeInt(data)
}
companion object {
@JvmField
val CREATOR = Creator()
}
class Creator : Parcelable.Creator<KotlinParcelable> {
override fun createFromParcel(source: Parcel): KotlinParcelable {
val data = source.readInt()
return KotlinParcelable(data)
}
override fun newArray(size: Int) = arrayOfNulls<KotlinParcelable>(size)
}
}
// FILE: test.kt
package test
import kotlinx.parcelize.*
import android.os.*
import k.*
@Parcelize
class Foo(val kp: KotlinParcelable): Parcelable
@@ -0,0 +1,170 @@
public final class k/KotlinParcelable$Companion : java/lang/Object {
private void <init>()
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
}
public final class k/KotlinParcelable$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public k.KotlinParcelable createFromParcel(android.os.Parcel source) {
LABEL (L0)
ALOAD (1)
LDC (source)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (23)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
ISTORE (2)
LABEL (L2)
LINENUMBER (24)
NEW
DUP
ILOAD (2)
INVOKESPECIAL (k/KotlinParcelable, <init>, (I)V)
ARETURN
LABEL (L3)
}
public java.lang.Object createFromParcel(android.os.Parcel p0) {
LABEL (L0)
LINENUMBER (21)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (k/KotlinParcelable$Creator, createFromParcel, (Landroid/os/Parcel;)Lk/KotlinParcelable;)
ARETURN
}
public k.KotlinParcelable[] newArray(int size)
public java.lang.Object[] newArray(int p0)
}
public final class k/KotlinParcelable : java/lang/Object, android/os/Parcelable {
public final static k.KotlinParcelable$Creator CREATOR
public final static k.KotlinParcelable$Companion Companion
private int data
static void <clinit>() {
NEW
DUP
ACONST_NULL
INVOKESPECIAL (k/KotlinParcelable$Companion, <init>, (Lkotlin/jvm/internal/DefaultConstructorMarker;)V)
PUTSTATIC (Companion, Lk/KotlinParcelable$Companion;)
LABEL (L0)
LINENUMBER (18)
NEW
DUP
INVOKESPECIAL (k/KotlinParcelable$Creator, <init>, ()V)
PUTSTATIC (CREATOR, Lk/KotlinParcelable$Creator;)
RETURN
}
public void <init>(int data)
public final int component1()
public final k.KotlinParcelable copy(int data)
public static k.KotlinParcelable copy$default(k.KotlinParcelable p0, int p1, int p2, java.lang.Object p3)
public int describeContents()
public boolean equals(java.lang.Object p0)
public final int getData()
public int hashCode()
public final void setData(int <set-?>)
public java.lang.String toString()
public void writeToParcel(android.os.Parcel dest, int flags) {
LABEL (L0)
ALOAD (1)
LDC (dest)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
LABEL (L1)
LINENUMBER (13)
ALOAD (1)
ALOAD (0)
GETFIELD (data, I)
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
LABEL (L2)
LINENUMBER (14)
RETURN
LABEL (L3)
}
}
public final class test/Foo$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final test.Foo createFromParcel(android.os.Parcel in) {
LABEL (L0)
ALOAD (1)
LDC (in)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
NEW
DUP
ALOAD (1)
LDC (Ltest/Foo;)
INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;)
INVOKEVIRTUAL (android/os/Parcel, readParcelable, (Ljava/lang/ClassLoader;)Landroid/os/Parcelable;)
CHECKCAST
INVOKESPECIAL (test/Foo, <init>, (Lk/KotlinParcelable;)V)
ARETURN
LABEL (L1)
}
public java.lang.Object createFromParcel(android.os.Parcel p0) {
LABEL (L0)
LINENUMBER (9)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (test/Foo$Creator, createFromParcel, (Landroid/os/Parcel;)Ltest/Foo;)
ARETURN
}
public final test.Foo[] newArray(int size)
public java.lang.Object[] newArray(int p0)
}
public final class test/Foo : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final k.KotlinParcelable kp
static void <clinit>() {
NEW
DUP
INVOKESPECIAL (test/Foo$Creator, <init>, ()V)
PUTSTATIC (CREATOR, Landroid/os/Parcelable$Creator;)
RETURN
}
public void <init>(k.KotlinParcelable kp)
public int describeContents()
public final k.KotlinParcelable getKp()
public void writeToParcel(android.os.Parcel parcel, int flags) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (kp, Lk/KotlinParcelable;)
ILOAD (2)
INVOKEVIRTUAL (android/os/Parcel, writeParcelable, (Landroid/os/Parcelable;I)V)
RETURN
LABEL (L1)
}
}
@@ -0,0 +1,115 @@
final class User$Companion : java/lang/Object, kotlinx/parcelize/Parceler {
private void <init>()
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
public User create(android.os.Parcel parcel)
public java.lang.Object create(android.os.Parcel parcel)
public User[] newArray(int size) {
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
ILOAD (1)
INVOKESTATIC (kotlinx/parcelize/Parceler$DefaultImpls, newArray, (Lkotlinx/parcelize/Parceler;I)[Ljava/lang/Object;)
CHECKCAST
ARETURN
LABEL (L1)
}
public java.lang.Object[] newArray(int size) {
LABEL (L0)
ALOAD (0)
ILOAD (1)
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
ARETURN
LABEL (L1)
}
public void write(User $this$write, android.os.Parcel parcel, int flags)
public void write(java.lang.Object $this$write, android.os.Parcel parcel, int flags)
}
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final User createFromParcel(android.os.Parcel parcel) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
INVOKESTATIC (User, access$getCompanion$p$s2645995, ()LUser$Companion;)
ALOAD (1)
INVOKEVIRTUAL (User$Companion, create, (Landroid/os/Parcel;)LUser;)
ARETURN
LABEL (L1)
}
public java.lang.Object createFromParcel(android.os.Parcel source) {
LABEL (L0)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (User$Creator, createFromParcel, (Landroid/os/Parcel;)LUser;)
ARETURN
LABEL (L1)
}
public final User[] newArray(int size) {
LABEL (L0)
ILOAD (1)
ANEWARRAY
ARETURN
LABEL (L1)
}
public java.lang.Object[] newArray(int size) {
LABEL (L0)
ALOAD (0)
ILOAD (1)
INVOKEVIRTUAL (User$Creator, newArray, (I)[LUser;)
ARETURN
LABEL (L1)
}
}
public final class User : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final static User$Companion Companion
private final int age
private final java.lang.String firstName
private final java.lang.String lastName
static void <clinit>()
public void <init>(java.lang.String firstName, java.lang.String lastName, int age)
public final static User$Companion access$getCompanion$p$s2645995()
public int describeContents()
public final int getAge()
public final java.lang.String getFirstName()
public final java.lang.String getLastName()
public void writeToParcel(android.os.Parcel out, int flags) {
LABEL (L0)
ALOAD (1)
LDC (out)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (Companion, LUser$Companion;)
ALOAD (0)
ALOAD (1)
ILOAD (2)
INVOKEVIRTUAL (User$Companion, write, (LUser;Landroid/os/Parcel;I)V)
RETURN
LABEL (L1)
}
}
@@ -0,0 +1,19 @@
// CURIOUS_ABOUT writeToParcel, createFromParcel, newArray
// WITH_RUNTIME
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
class User(val firstName: String, val lastName: String, val age: Int) : Parcelable {
private companion object : Parceler<User> {
override fun User.write(parcel: Parcel, flags: Int) {
parcel.writeString(firstName)
parcel.writeString(lastName)
parcel.writeInt(age)
}
override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), parcel.readInt())
}
}
@@ -0,0 +1,113 @@
final class User$Companion : java/lang/Object, kotlinx/parcelize/Parceler {
private void <init>()
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
public User create(android.os.Parcel parcel)
public java.lang.Object create(android.os.Parcel p0)
public User[] newArray(int size) {
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
ILOAD (1)
INVOKESTATIC (kotlinx/parcelize/Parceler$DefaultImpls, newArray, (Lkotlinx/parcelize/Parceler;I)[Ljava/lang/Object;)
CHECKCAST
ARETURN
LABEL (L1)
}
public java.lang.Object[] newArray(int p0) {
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
ILOAD (1)
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
ARETURN
}
public void write(User $this$write, android.os.Parcel parcel, int flags)
public void write(java.lang.Object p0, android.os.Parcel p1, int p2)
}
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final User createFromParcel(android.os.Parcel in) {
LABEL (L0)
ALOAD (1)
LDC (in)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (Companion, LUser$Companion;)
ALOAD (1)
INVOKEVIRTUAL (User$Companion, create, (Landroid/os/Parcel;)LUser;)
ARETURN
LABEL (L1)
}
public java.lang.Object createFromParcel(android.os.Parcel p0) {
LABEL (L0)
LINENUMBER (9)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (User$Creator, createFromParcel, (Landroid/os/Parcel;)LUser;)
ARETURN
}
public final User[] newArray(int size) {
LABEL (L0)
ILOAD (1)
ANEWARRAY
ARETURN
LABEL (L1)
}
public java.lang.Object[] newArray(int p0) {
LABEL (L0)
LINENUMBER (9)
ALOAD (0)
ILOAD (1)
INVOKEVIRTUAL (User$Creator, newArray, (I)[LUser;)
ARETURN
}
}
public final class User : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final static User$Companion Companion
private final int age
private final java.lang.String firstName
private final java.lang.String lastName
static void <clinit>()
public void <init>(java.lang.String firstName, java.lang.String lastName, int age)
public int describeContents()
public final int getAge()
public final java.lang.String getFirstName()
public final java.lang.String getLastName()
public void writeToParcel(android.os.Parcel parcel, int flags) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
GETSTATIC (Companion, LUser$Companion;)
ALOAD (0)
ALOAD (1)
ILOAD (2)
INVOKEVIRTUAL (User$Companion, write, (LUser;Landroid/os/Parcel;I)V)
RETURN
LABEL (L1)
}
}
@@ -0,0 +1,85 @@
final class User$Companion : java/lang/Object, kotlinx/parcelize/Parceler {
private void <init>()
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
public User create(android.os.Parcel parcel)
public java.lang.Object create(android.os.Parcel parcel)
public User[] newArray(int size) {
LABEL (L0)
LINENUMBER (19)
ILOAD (1)
ANEWARRAY
ARETURN
LABEL (L1)
}
public java.lang.Object[] newArray(int size) {
LABEL (L0)
ALOAD (0)
ILOAD (1)
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
ARETURN
LABEL (L1)
}
public void write(User $this$write, android.os.Parcel parcel, int flags)
public void write(java.lang.Object $this$write, android.os.Parcel parcel, int flags)
}
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final User createFromParcel(android.os.Parcel parcel)
public java.lang.Object createFromParcel(android.os.Parcel source)
public final User[] newArray(int size) {
LABEL (L0)
INVOKESTATIC (User, access$getCompanion$p$s2645995, ()LUser$Companion;)
ILOAD (1)
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
ARETURN
LABEL (L1)
}
public java.lang.Object[] newArray(int size) {
LABEL (L0)
ALOAD (0)
ILOAD (1)
INVOKEVIRTUAL (User$Creator, newArray, (I)[LUser;)
ARETURN
LABEL (L1)
}
}
public final class User : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final static User$Companion Companion
private final int age
private final java.lang.String firstName
private final java.lang.String lastName
static void <clinit>()
public void <init>(java.lang.String firstName, java.lang.String lastName, int age)
public final static User$Companion access$getCompanion$p$s2645995()
public int describeContents()
public final int getAge()
public final java.lang.String getFirstName()
public final java.lang.String getLastName()
public void writeToParcel(android.os.Parcel out, int flags)
}
@@ -0,0 +1,21 @@
// CURIOUS_ABOUT newArray
// WITH_RUNTIME
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
@Parcelize
class User(val firstName: String, val lastName: String, val age: Int) : Parcelable {
private companion object : Parceler<User> {
override fun User.write(parcel: Parcel, flags: Int) {
parcel.writeString(firstName)
parcel.writeString(lastName)
parcel.writeInt(age)
}
override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), parcel.readInt())
override fun newArray(size: Int) = arrayOfNulls<User>(size) as Array<User>
}
}
@@ -0,0 +1,83 @@
final class User$Companion : java/lang/Object, kotlinx/parcelize/Parceler {
private void <init>()
public void <init>(kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
public User create(android.os.Parcel parcel)
public java.lang.Object create(android.os.Parcel p0)
public User[] newArray(int size) {
LABEL (L0)
LINENUMBER (19)
ILOAD (1)
ANEWARRAY
ARETURN
LABEL (L1)
}
public java.lang.Object[] newArray(int p0) {
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
ILOAD (1)
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
ARETURN
}
public void write(User $this$write, android.os.Parcel parcel, int flags)
public void write(java.lang.Object p0, android.os.Parcel p1, int p2)
}
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final User createFromParcel(android.os.Parcel in)
public java.lang.Object createFromParcel(android.os.Parcel p0)
public final User[] newArray(int size) {
LABEL (L0)
GETSTATIC (Companion, LUser$Companion;)
ILOAD (1)
INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;)
ARETURN
LABEL (L1)
}
public java.lang.Object[] newArray(int p0) {
LABEL (L0)
LINENUMBER (9)
ALOAD (0)
ILOAD (1)
INVOKEVIRTUAL (User$Creator, newArray, (I)[LUser;)
ARETURN
}
}
public final class User : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final static User$Companion Companion
private final int age
private final java.lang.String firstName
private final java.lang.String lastName
static void <clinit>()
public void <init>(java.lang.String firstName, java.lang.String lastName, int age)
public int describeContents()
public final int getAge()
public final java.lang.String getFirstName()
public final java.lang.String getLastName()
public void writeToParcel(android.os.Parcel parcel, int flags)
}
@@ -0,0 +1,49 @@
public abstract class AbstractUser : java/lang/Object, android/os/Parcelable {
public void <init>()
public int describeContents() {
LABEL (L0)
LINENUMBER (8)
BIPUSH (100)
IRETURN
LABEL (L1)
}
}
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final User createFromParcel(android.os.Parcel parcel)
public java.lang.Object createFromParcel(android.os.Parcel source)
public final User[] newArray(int size)
public java.lang.Object[] newArray(int size)
}
public final class User : AbstractUser {
public final static android.os.Parcelable$Creator CREATOR
private final int age
private final java.lang.String firstName
private final boolean isProUser
private final java.lang.String lastName
static void <clinit>()
public void <init>(java.lang.String firstName, java.lang.String lastName, int age, boolean isProUser)
public final int getAge()
public final java.lang.String getFirstName()
public final java.lang.String getLastName()
public final boolean isProUser()
public void writeToParcel(android.os.Parcel out, int flags)
}
@@ -0,0 +1,12 @@
// CURIOUS_ABOUT describeContents
// WITH_RUNTIME
import kotlinx.parcelize.*
import android.os.Parcelable
abstract class AbstractUser : Parcelable {
override fun describeContents() = 100
}
@Parcelize
class User(val firstName: String, val lastName: String, val age: Int, val isProUser: Boolean) : AbstractUser()
@@ -0,0 +1,49 @@
public abstract class AbstractUser : java/lang/Object, android/os/Parcelable {
public void <init>()
public int describeContents() {
LABEL (L0)
LINENUMBER (8)
BIPUSH (100)
IRETURN
LABEL (L1)
}
}
public final class User$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final User createFromParcel(android.os.Parcel in)
public java.lang.Object createFromParcel(android.os.Parcel p0)
public final User[] newArray(int size)
public java.lang.Object[] newArray(int p0)
}
public final class User : AbstractUser {
public final static android.os.Parcelable$Creator CREATOR
private final int age
private final java.lang.String firstName
private final boolean isProUser
private final java.lang.String lastName
static void <clinit>()
public void <init>(java.lang.String firstName, java.lang.String lastName, int age, boolean isProUser)
public final int getAge()
public final java.lang.String getFirstName()
public final java.lang.String getLastName()
public final boolean isProUser()
public void writeToParcel(android.os.Parcel parcel, int flags)
}

Some files were not shown because too many files have changed in this diff Show More