diff --git a/.idea/dictionaries/yan.xml b/.idea/dictionaries/yan.xml
index 2b004b50aa3..52bb6ae1c1e 100644
--- a/.idea/dictionaries/yan.xml
+++ b/.idea/dictionaries/yan.xml
@@ -10,7 +10,10 @@
kapt
kotlinc
mutators
+ parcelable
parceler
+ parcelers
+ parcelize
repl
testdata
uast
diff --git a/generators/build.gradle.kts b/generators/build.gradle.kts
index b895ec0757c..95dbee9fdc4 100644
--- a/generators/build.gradle.kts
+++ b/generators/build.gradle.kts
@@ -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"))
diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
index cbfaf1e0ba1..d967d435a21 100644
--- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
+++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
@@ -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) {
}
}
+ testGroup("plugins/parcelize/parcelize-compiler/tests", "plugins/parcelize/parcelize-compiler/testData") {
+ testClass {
+ model("box", targetBackend = TargetBackend.JVM)
+ }
+
+ testClass {
+ model("box", targetBackend = TargetBackend.JVM_IR)
+ }
+
+ testClass {
+ model("codegen", targetBackend = TargetBackend.JVM)
+ }
+
+ testClass {
+ model("codegen", targetBackend = TargetBackend.JVM_IR)
+ }
+ }
+
testGroup("plugins/jvm-abi-gen/test", "plugins/jvm-abi-gen/testData") {
testClass {
model("compare", recursive = false, extension = null)
diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as40 b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as40
index be49ff0303d..84a7ec4885c 100644
--- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as40
+++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as40
@@ -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) {
}
}
+ testGroup("plugins/parcelize/parcelize-compiler/tests", "plugins/parcelize/parcelize-compiler/testData") {
+ testClass {
+ model("box", targetBackend = TargetBackend.JVM)
+ }
+
+ testClass {
+ model("box", targetBackend = TargetBackend.JVM_IR)
+ }
+
+ testClass {
+ model("codegen", targetBackend = TargetBackend.JVM)
+ }
+
+ testClass {
+ model("codegen", targetBackend = TargetBackend.JVM_IR)
+ }
+ }
+
testGroup("plugins/kapt3/kapt3-compiler/test", "plugins/kapt3/kapt3-compiler/testData") {
testClass {
model("converter")
diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as41 b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as41
index be49ff0303d..84a7ec4885c 100644
--- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as41
+++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as41
@@ -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) {
}
}
+ testGroup("plugins/parcelize/parcelize-compiler/tests", "plugins/parcelize/parcelize-compiler/testData") {
+ testClass {
+ model("box", targetBackend = TargetBackend.JVM)
+ }
+
+ testClass {
+ model("box", targetBackend = TargetBackend.JVM_IR)
+ }
+
+ testClass {
+ model("codegen", targetBackend = TargetBackend.JVM)
+ }
+
+ testClass {
+ model("codegen", targetBackend = TargetBackend.JVM_IR)
+ }
+ }
+
testGroup("plugins/kapt3/kapt3-compiler/test", "plugins/kapt3/kapt3-compiler/testData") {
testClass {
model("converter")
diff --git a/plugins/parcelize/parcelize-compiler/build.gradle.kts b/plugins/parcelize/parcelize-compiler/build.gradle.kts
new file mode 100644
index 00000000000..94e04195844
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/build.gradle.kts
@@ -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")
diff --git a/plugins/parcelize/parcelize-compiler/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar b/plugins/parcelize/parcelize-compiler/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
new file mode 100644
index 00000000000..da4c2aa3e36
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
@@ -0,0 +1 @@
+org.jetbrains.kotlin.parcelize.ParcelizeComponentRegistrar
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt
new file mode 100644
index 00000000000..a48a9ce8152
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeAnnotationChecker.kt
@@ -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() ?: return
+ val annotationOwner = annotationEntry.getStrictParentOfType() ?: 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() 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()
+ 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 PsiElement.getStrictParentOfType(): T? {
+ return PsiTreeUtil.getParentOfType(this, T::class.java, true)
+}
+
+internal inline fun PsiElement.getNonStrictParentOfType(): T? {
+ return PsiTreeUtil.getParentOfType(this, T::class.java, false)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeClinitClassBuilderInterceptorExtension.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeClinitClassBuilderInterceptorExtension.kt
new file mode 100644
index 00000000000..da91f94c9c6
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeClinitClassBuilderInterceptorExtension.kt
@@ -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
+ ) {
+ 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, "", "()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?
+ ): MethodVisitor {
+ if (name == "" && 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, "", "()V", false)
+ iv.putstatic(parcelableName, "CREATOR", "Landroid/os/Parcelable\$Creator;")
+ }
+
+ super.visitInsn(opcode)
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeCodegenExtension.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeCodegenExtension.kt
new file mode 100644
index 00000000000..0176567e4a5
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeCodegenExtension.kt
@@ -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 {
+ val shortName = parcelerObject.name
+ return Pair(Type.getObjectType(containerAsmType.internalName + "\$$shortName"), shortName.asString())
+ }
+
+ private fun ClassDescriptor.writeWriteToParcel(
+ codegen: ImplementationBodyCodegen,
+ properties: List,
+ 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
+ ): 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)
+
+ private fun getPropertiesToSerialize(
+ codegen: ImplementationBodyCodegen,
+ parcelableClass: ClassDescriptor
+ ): List {
+ 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
+ ) {
+ 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, "", "($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
+ ) {
+ 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", "", "()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 {
+ val typeParcelerFqName = FqName(TypeParceler::class.java.name)
+ val serializers = mutableListOf()
+
+ 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
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeComponentRegistrar.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeComponentRegistrar.kt
new file mode 100644
index 00000000000..7a0a8eb018c
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeComponentRegistrar.kt
@@ -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())
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt
new file mode 100644
index 00000000000..3189f197468
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeDeclarationChecker.kt
@@ -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()) {
+ 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 }
+ }
+}
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt
new file mode 100644
index 00000000000..2fc74f6306f
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeIrGeneratorExtension.kt
@@ -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)
+ }
+}
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt
new file mode 100644
index 00000000000..d46f4d05511
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ParcelizeResolveExtension.kt
@@ -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
+ ): 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 =
+ 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 {
+ return if (thisDescriptor.isParcelize) parcelizeMethodNames else emptyList()
+ }
+
+ override fun generateSyntheticMethods(
+ thisDescriptor: ClassDescriptor,
+ name: Name,
+ bindingContext: BindingContext,
+ fromSupertypes: List,
+ result: MutableCollection
+ ) {
+ 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
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/diagnostic/DefaultErrorMessagesParcelize.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/diagnostic/DefaultErrorMessagesParcelize.kt
new file mode 100644
index 00000000000..3ec3054a203
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/diagnostic/DefaultErrorMessagesParcelize.kt
@@ -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"
+ )
+ }
+}
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/diagnostic/ErrorsParcelize.java b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/diagnostic/ErrorsParcelize.java
new file mode 100644
index 00000000000..87c178223d7
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/diagnostic/ErrorsParcelize.java
@@ -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 PARCELABLE_SHOULD_BE_CLASS = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PARCELABLE_DELEGATE_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PARCELABLE_SHOULD_BE_INSTANTIABLE = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PARCELABLE_CANT_BE_INNER_CLASS = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PARCELABLE_CANT_BE_LOCAL_CLASS = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 NO_PARCELABLE_SUPERTYPE = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY = DiagnosticFactory0.create(WARNING);
+ DiagnosticFactory0 PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PROPERTY_WONT_BE_SERIALIZED = DiagnosticFactory0.create(WARNING);
+ DiagnosticFactory0 OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 CREATOR_DEFINITION_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PARCELABLE_TYPE_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory0 PARCELER_SHOULD_BE_OBJECT = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory2 PARCELER_TYPE_INCOMPATIBLE = DiagnosticFactory2.create(ERROR);
+ DiagnosticFactory0 DUPLICATING_TYPE_PARCELERS = DiagnosticFactory0.create(ERROR);
+ DiagnosticFactory1 REDUNDANT_TYPE_PARCELER = DiagnosticFactory1.create(WARNING);
+ DiagnosticFactory1 CLASS_SHOULD_BE_PARCELIZE = DiagnosticFactory1.create(ERROR);
+
+ DiagnosticFactory0 INAPPLICABLE_IGNORED_ON_PARCEL = DiagnosticFactory0.create(WARNING);
+ DiagnosticFactory0 INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY = DiagnosticFactory0.create(WARNING);
+
+ @SuppressWarnings("UnusedDeclaration")
+ Object _initializer = new Object() {
+ {
+ Errors.Initializer.initializeFactoryNamesAndDefaultErrorMessages(ErrorsParcelize.class, DefaultErrorMessagesParcelize.INSTANCE);
+ }
+ };
+
+}
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidIrBuilder.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidIrBuilder.kt
new file mode 100644
index 00000000000..8fb0177c7ac
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidIrBuilder.kt
@@ -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)
+ }
+}
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidSymbols.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidSymbols.kt
new file mode 100644
index 00000000000..878192c6e58
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidSymbols.kt
@@ -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)
+}
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializerFactory.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializerFactory.kt
new file mode 100644
index 00000000000..027f835ef12
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializerFactory.kt
@@ -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)
+}
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializers.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializers.kt
new file mode 100644
index 00000000000..472628c0584
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializers.kt
@@ -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 {
+ // 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 {
+ // 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)
+ }
+ }
+}
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelerScope.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelerScope.kt
new file mode 100644
index 00000000000..6574f1cd3a6
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelerScope.kt
@@ -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()
+
+ 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
+}
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformer.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformer.kt
new file mode 100644
index 00000000000..6d04529add1
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/ParcelizeIrTransformer.kt
@@ -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()
+
+ 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()?.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()?.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
+ get() {
+ if (kind != ClassKind.CLASS) return emptyList()
+
+ val constructor = primaryConstructor ?: return emptyList()
+ val topLevelScope = getParcelerScope()
+
+ return constructor.valueParameters.map { parameter ->
+ val property = properties.first { it.name == parameter.name }
+ val localScope = property.getParcelerScope(topLevelScope)
+ ParcelableProperty(property.backingField!!) {
+ serializerFactory.get(parameter.type, parcelizeType = defaultType, scope = localScope)
+ }
+ }
+ }
+
+ // *Heuristic* to determine if a Parcelable contains file descriptors.
+ private val IrType.containsFileDescriptors: Boolean
+ get() = erasedUpperBound.fqNameWhenAvailable == ParcelizeExtensionBase.FILE_DESCRIPTOR_FQNAME ||
+ (this as? IrSimpleType)?.arguments?.any { argument ->
+ argument.typeOrNull?.containsFileDescriptors == true
+ } == true
+}
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/irUtils.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/irUtils.kt
new file mode 100644
index 00000000000..50403ecbcb2
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/irUtils.kt
@@ -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 { 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 { 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 { fun newArray(size: Int): Array }
+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 { 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 { 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().firstOrNull { it.name.asString() == name && it.getter != null }?.getter?.symbol
+ ?: getSimpleFunction("")
+
+fun IrClass.getMethodWithoutArguments(name: String): IrSimpleFunction =
+ functions.first { function ->
+ function.name.asString() == name && function.dispatchReceiverParameter != null
+ && function.extensionReceiverParameter == null && function.valueParameters.isEmpty()
+ }
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/serializers/ParcelSerializer.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/serializers/ParcelSerializer.kt
new file mode 100644
index 00000000000..3960d14d68f
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/serializers/ParcelSerializer.kt
@@ -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
+
+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,
+ 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) }
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/serializers/ParcelSerializers.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/serializers/ParcelSerializers.kt
new file mode 100644
index 00000000000..082b9f7e57f
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/serializers/ParcelSerializers.kt
@@ -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,
+
+ 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:
+ */
+ protected abstract fun doWriteValue(v: InstructionAdapter)
+
+ /**
+ * Stack before: collection, parcel
+ * Stack after:
+ */
+ 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, "", "(I)V", false) // -> list
+ } else {
+ v.invokespecial(collectionType.internalName, "", "()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,
+
+ 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, "", "(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, "", "()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,
+ readMethod: Method
+) : 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(val name: String, val signature: T) {
+ companion object {
+ operator fun invoke(name: String) = Method(name, null)
+ }
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/serializers/ParcelizeExtensionBase.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/serializers/ParcelizeExtensionBase.kt
new file mode 100644
index 00000000000..8407fd2b19b
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/serializers/ParcelizeExtensionBase.kt
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/allPrimitiveTypes.kt b/plugins/parcelize/parcelize-compiler/testData/box/allPrimitiveTypes.kt
new file mode 100644
index 00000000000..01faab7f0ec
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/allPrimitiveTypes.kt
@@ -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(parcel)
+ val second2 = readFromParcel(parcel)
+
+ assert(first == first2)
+ assert(second == second2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/arraySimple.kt b/plugins/parcelize/parcelize-compiler/testData/box/arraySimple.kt
new file mode 100644
index 00000000000..819c9b48e9c
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/arraySimple.kt
@@ -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) : 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(parcel)
+
+ assert(first == first2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/arrays.kt b/plugins/parcelize/parcelize-compiler/testData/box/arrays.kt
new file mode 100644
index 00000000000..d0a52705cae
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/arrays.kt
@@ -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,
+ val b: Array,
+ val c: IntArray,
+ val d: CharArray?,
+ val e: Array,
+ val f: Array>,
+ val g: List>,
+ val h: Array?
+) : 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(parcel)
+
+ assert(first == first2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/binder.kt b/plugins/parcelize/parcelize-compiler/testData/box/binder.kt
new file mode 100644
index 00000000000..880f18603e4
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/binder.kt
@@ -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,
+ val binderList: List
+) : 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(parcel)
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/boxedTypes.kt b/plugins/parcelize/parcelize-compiler/testData/box/boxedTypes.kt
new file mode 100644
index 00000000000..359d93f5835
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/boxedTypes.kt
@@ -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(parcel)
+
+ assert(first == first2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/bundle.kt b/plugins/parcelize/parcelize-compiler/testData/box/bundle.kt
new file mode 100644
index 00000000000..c1b83d9f8e5
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/bundle.kt
@@ -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(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
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/charSequence.kt b/plugins/parcelize/parcelize-compiler/testData/box/charSequence.kt
new file mode 100644
index 00000000000..79456cbc108
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/charSequence.kt
@@ -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(parcel)
+
+ assert(test.simple.toString() == test2.simple.toString())
+ assert(test.spanned.toString() == test2.spanned.toString())
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customNewArray.kt b/plugins/parcelize/parcelize-compiler/testData/box/customNewArray.kt
new file mode 100644
index 00000000000..a7639a1b7c5
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/customNewArray.kt
@@ -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 {
+ 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(size) as Array
+ }
+}
+
+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
+ val result = parcel.createTypedArray(creator)
+
+ assert(result.size == 2)
+ assert(result[0].firstName == user.firstName)
+ assert(result[1].firstName == user2.firstName)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customParcelable.kt b/plugins/parcelize/parcelize-compiler/testData/box/customParcelable.kt
new file mode 100644
index 00000000000..b73a8609681
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/customParcelable.kt
@@ -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 {
+ 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(parcel)
+
+ assert(userParcelable.user.name == userParcelable2.user.name)
+ assert(userParcelable2.user.age == 0)
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customParcelerScoping.kt b/plugins/parcelize/parcelize-compiler/testData/box/customParcelerScoping.kt
new file mode 100644
index 00000000000..7fb8fe35caf
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/customParcelerScoping.kt
@@ -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 {
+ override fun create(parcel: Parcel) = -parcel.readInt()
+
+ override fun Int.write(parcel: Parcel, flags: Int) {
+ parcel.writeInt(this)
+ }
+}
+
+object Parceler2 : Parceler {
+ override fun create(parcel: Parcel) = parcel.readString().length
+
+ override fun Int.write(parcel: Parcel, flags: Int) {
+ parcel.writeString("Abc")
+ }
+}
+
+@Parcelize
+@TypeParceler
+data class Ints(val a: Int, @TypeParceler val b: Int, val c: @WriteWith 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(parcel)
+
+ assert(test2.a == -test.a)
+ assert(test2.b == -test.b)
+ assert(test2.c == 3)
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerBoxing.kt b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerBoxing.kt
new file mode 100644
index 00000000000..c690d172450
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerBoxing.kt
@@ -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 {
+ override fun create(parcel: Parcel) = -parcel.readInt()
+
+ override fun Int.write(parcel: Parcel, flags: Int) {
+ parcel.writeInt(this)
+ }
+}
+
+object Parceler2 : Parceler {
+ 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 val b: Int,
+ @TypeParceler val c: Long,
+ @TypeParceler val d: List,
+ @TypeParceler 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(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)))
+ }
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt
new file mode 100644
index 00000000000..0935cc90112
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerSimple.kt
@@ -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 {
+ 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 {
+ override fun create(parcel: Parcel) = parcel.readString().toUpperCase()
+
+ override fun String.write(parcel: Parcel, flags: Int) {
+ parcel.writeString(this)
+ }
+}
+
+@Parcelize
+@TypeParceler
+data class Test(
+ val a: String,
+ @TypeParceler val b: String,
+ @TypeParceler val c: CharSequence,
+ val d: @WriteWith 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(parcel)
+
+ assert(test.a == "Abc" && test.b == "Abc" && test.c == "Abc" && test.d == "Abc")
+ assert(test2.a == "3" && test2.b == "3" && test2.c == "Abc" && test2.d == "ABC")
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customSerializerWriteWith.kt b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerWriteWith.kt
new file mode 100644
index 00000000000..bc8f95b7a3b
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/customSerializerWriteWith.kt
@@ -0,0 +1,55 @@
+// IGNORE_BACKEND: JVM
+// See KT-38107
+// The JVM backend is missing support for custom parcelers in List
+// WITH_RUNTIME
+
+@file:JvmName("TestKt")
+package test
+
+import kotlinx.parcelize.*
+import android.os.Parcel
+import android.os.Parcelable
+
+object Parceler1 : Parceler {
+ override fun create(parcel: Parcel) = parcel.readInt().toString()
+
+ override fun String.write(parcel: Parcel, flags: Int) {
+ parcel.writeInt(length)
+ }
+}
+
+object Parceler2 : Parceler> {
+ override fun create(parcel: Parcel) = listOf(parcel.readString())
+
+ override fun List.write(parcel: Parcel, flags: Int) {
+ parcel.writeString(this.joinToString(","))
+ }
+}
+
+@Parcelize
+data class Test(
+ val a: String,
+ val b: @WriteWith String,
+ val c: List<@WriteWith String>,
+ val d: @WriteWith List,
+ val e: @WriteWith List<@WriteWith 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(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"))
+ }
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/customSimple.kt b/plugins/parcelize/parcelize-compiler/testData/box/customSimple.kt
new file mode 100644
index 00000000000..2ba8456562b
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/customSimple.kt
@@ -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 {
+ 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(parcel)
+
+ assert(user.firstName == user2.firstName)
+ assert(user.secondName == user2.secondName)
+ assert(user2.age == 0)
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/enumObject.kt b/plugins/parcelize/parcelize-compiler/testData/box/enumObject.kt
new file mode 100644
index 00000000000..b9d74fcd1b3
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/enumObject.kt
@@ -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(parcel)
+ val obj2 = readFromParcel(parcel)
+
+ println(black2)
+ println(obj2)
+
+ assert(black2 == black)
+ assert(obj2 != null)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/enums.kt b/plugins/parcelize/parcelize-compiler/testData/box/enums.kt
new file mode 100644
index 00000000000..41217b43a3b
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/enums.kt
@@ -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(parcel)
+ assert(test == test2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/exceptions.kt b/plugins/parcelize/parcelize-compiler/testData/box/exceptions.kt
new file mode 100644
index 00000000000..8571e465627
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/exceptions.kt
@@ -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(parcel)
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/functions.kt b/plugins/parcelize/parcelize-compiler/testData/box/functions.kt
new file mode 100644
index 00000000000..e99bce084a5
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/functions.kt
@@ -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(parcel)
+
+ assert(test.callback() == 1)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/intArray.kt b/plugins/parcelize/parcelize-compiler/testData/box/intArray.kt
new file mode 100644
index 00000000000..7d0e0d1a9d8
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/intArray.kt
@@ -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) : 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(parcel)
+ assert(film == film2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/javaInterop.kt b/plugins/parcelize/parcelize-compiler/testData/box/javaInterop.kt
new file mode 100644
index 00000000000..2f8f4da2add
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/javaInterop.kt
@@ -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)
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt19747.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt19747.kt
new file mode 100644
index 00000000000..62896cd193a
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/kt19747.kt
@@ -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")
+ }
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt19747_2.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt19747_2.kt
new file mode 100644
index 00000000000..448443dffe9
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/kt19747_2.kt
@@ -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(parcel)
+
+ assert(test.j.j1 == test2.j.j1)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt19749.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt19749.kt
new file mode 100644
index 00000000000..7cbcfd92ad4
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/kt19749.kt
@@ -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(parcel)
+
+ assert(test.m.m1 == test2.m.m1)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt20002.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt20002.kt
new file mode 100644
index 00000000000..037ae43a490
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/kt20002.kt
@@ -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) : 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(parcel)
+
+ assert(first == first2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt20021.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt20021.kt
new file mode 100644
index 00000000000..b24e291ac5d
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/kt20021.kt
@@ -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 = object : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel) = ParcelableEnum.ONE
+ override fun newArray(size: Int) = arrayOfNulls(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(parcel)
+
+ assert(first.parcelableEnum == ParcelableEnum.THREE)
+ assert(first2.parcelableEnum == ParcelableEnum.ONE)
+ assert(first != first2)
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt20717.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt20717.kt
new file mode 100644
index 00000000000..13f583c220c
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/kt20717.kt
@@ -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) -> Unit): String {
+ val dummy = DummyParcelable(42)
+
+ val clazz = dummy.javaClass
+ val field = clazz.getDeclaredField("CREATOR")
+ val creator = field.get(dummy) as Parcelable.Creator
+
+ val parcel = Parcel.obtain()
+ dummy.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+
+ work(creator)
+ return "OK"
+}
+
+@Parcelize
+data class DummyParcelable(val int: Int): Parcelable
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt25839.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt25839.kt
new file mode 100644
index 00000000000..bd55fd5ea5c
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/kt25839.kt
@@ -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(parcel)
+ readFromParcel(parcel)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt26221.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt26221.kt
new file mode 100644
index 00000000000..8852244e767
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/kt26221.kt
@@ -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>) : Parcelable
+
+fun box() = parcelTest { parcel ->
+ val pint = PInt(0)
+ val sarray = SparseArray()
+ sarray.put(0, pint)
+ val sarray2 = SparseArray>()
+ 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(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)
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt36658.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt36658.kt
new file mode 100644
index 00000000000..0113db9fc81
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/kt36658.kt
@@ -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(parcel)
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/kt39981.kt b/plugins/parcelize/parcelize-compiler/testData/box/kt39981.kt
new file mode 100644
index 00000000000..801a049cf44
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/kt39981.kt
@@ -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 {
+ override fun create(parcel: Parcel): TestParcel {
+ return TestParcel()
+ }
+ override fun TestParcel.write(parcel: Parcel, flags: Int) {}
+ }
+}
+
+fun box() = parcelTest { parcel ->
+ TestParcel()
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/listKinds.kt b/plugins/parcelize/parcelize-compiler/testData/box/listKinds.kt
new file mode 100644
index 00000000000..8883420c0f0
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/listKinds.kt
@@ -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,
+ val b: MutableList,
+ val c: ArrayList,
+ val d: LinkedList,
+ val e: Set,
+ val f: MutableSet,
+ val g: TreeSet,
+ val h: HashSet,
+ val i: LinkedHashSet,
+ val j: NavigableSet,
+ val k: SortedSet
+) : Parcelable
+
+fun box() = parcelTest { parcel ->
+ val first = Test(
+ a = listOf("A"),
+ b = mutableListOf("B"),
+ c = ArrayList().apply { this += "C" },
+ d = LinkedList().apply { this += "D" },
+ e = setOf("E"),
+ f = mutableSetOf("F"),
+ g = TreeSet().apply { this += "G" },
+ h = HashSet().apply { this += "H" },
+ i = LinkedHashSet().apply { this += "I" },
+ j = TreeSet().apply { this += "J" },
+ k = TreeSet().apply { this += "K" }
+ )
+
+ first.writeToParcel(parcel, 0)
+
+ val bytes = parcel.marshall()
+ parcel.unmarshall(bytes, 0, bytes.size)
+ parcel.setDataPosition(0)
+
+ val first2 = readFromParcel(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<*>)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/listSimple.kt b/plugins/parcelize/parcelize-compiler/testData/box/listSimple.kt
new file mode 100644
index 00000000000..5029aa8ec54
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/listSimple.kt
@@ -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) : 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(parcel)
+
+ assert(first == first2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/lists.kt b/plugins/parcelize/parcelize-compiler/testData/box/lists.kt
new file mode 100644
index 00000000000..3ddbd31982a
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/lists.kt
@@ -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,
+ val b: List,
+ val c: List,
+ val d: List,
+ val e: List?>,
+ val f: List>>
+) : 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(parcel)
+
+ assert(first == first2)
+
+ assert(first2.a == listOf("A", "B"))
+ assert(first2.b.size == 3)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/mapKinds.kt b/plugins/parcelize/parcelize-compiler/testData/box/mapKinds.kt
new file mode 100644
index 00000000000..d714822ef81
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/mapKinds.kt
@@ -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,
+ val b: MutableMap,
+ val c: HashMap,
+ val d: LinkedHashMap,
+ val e: TreeMap,
+ val f: SortedMap,
+ val g: NavigableMap
+) : Parcelable
+
+fun box() = parcelTest { parcel ->
+ val first = Test(
+ a = mapOf("A" to "B"),
+ b = mutableMapOf("A" to "B"),
+ c = HashMap().apply { put("A", "B") },
+ d = LinkedHashMap().apply { put("A", "B") },
+ e = TreeMap().apply { put("A", "B") },
+ f = TreeMap().apply { put("A", "B") },
+ g = TreeMap().apply { put("A", "B") }
+ )
+
+ first.writeToParcel(parcel, 0)
+
+ val bytes = parcel.marshall()
+ parcel.unmarshall(bytes, 0, bytes.size)
+ parcel.setDataPosition(0)
+
+ val first2 = readFromParcel(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<*, *>)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/mapSimple.kt b/plugins/parcelize/parcelize-compiler/testData/box/mapSimple.kt
new file mode 100644
index 00000000000..a6ca24bb230
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/mapSimple.kt
@@ -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) : 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(parcel)
+
+ assert(first == first2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/maps.kt b/plugins/parcelize/parcelize-compiler/testData/box/maps.kt
new file mode 100644
index 00000000000..b758d112ab4
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/maps.kt
@@ -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,
+ val b: Map,
+ val c: Map,
+ val d: Map>,
+ val e: Map>,
+ val f: Map,
+ val g: Map>>
+) : 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(parcel)
+
+ assert(first == first2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/nestedArrays.kt b/plugins/parcelize/parcelize-compiler/testData/box/nestedArrays.kt
new file mode 100644
index 00000000000..8add9ddd3c5
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/nestedArrays.kt
@@ -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>) : 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(parcel)
+ assert(second.data.size == 1)
+ assert(Arrays.equals(second.data[0], arrayOf(0, 1)))
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/nestedLists.kt b/plugins/parcelize/parcelize-compiler/testData/box/nestedLists.kt
new file mode 100644
index 00000000000..def43a46c90
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/nestedLists.kt
@@ -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>) : 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(parcel)
+ assert(second.data.size == 1)
+ assert(Arrays.equals(second.data[0], arrayOf(0, 1)))
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/nestedMaps.kt b/plugins/parcelize/parcelize-compiler/testData/box/nestedMaps.kt
new file mode 100644
index 00000000000..fa2bfe03d93
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/nestedMaps.kt
@@ -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>) : 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(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)))
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/nestedParcelable.kt b/plugins/parcelize/parcelize-compiler/testData/box/nestedParcelable.kt
new file mode 100644
index 00000000000..75e476f46a0
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/nestedParcelable.kt
@@ -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(parcel)
+ assert(test == test2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/nestedSparseArrays.kt b/plugins/parcelize/parcelize-compiler/testData/box/nestedSparseArrays.kt
new file mode 100644
index 00000000000..c6bb3c71b7e
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/nestedSparseArrays.kt
@@ -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>) : Parcelable
+
+fun box() = parcelTest { parcel ->
+ var array = SparseArray>()
+ 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(parcel)
+ assert(second.data.size() == 1)
+ assert(Arrays.equals(second.data.get(0), arrayOf(0, 1)))
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/newArray.kt b/plugins/parcelize/parcelize-compiler/testData/box/newArray.kt
new file mode 100644
index 00000000000..4ed9cc868ff
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/newArray.kt
@@ -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
+ val result = parcel.createTypedArray(creator)
+
+ assert(result.size == 2)
+ assert(result[0].firstName == user.firstName)
+ assert(result[1].firstName == user2.firstName)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/nullableTypes.kt b/plugins/parcelize/parcelize-compiler/testData/box/nullableTypes.kt
new file mode 100644
index 00000000000..e165b3f8ab2
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/nullableTypes.kt
@@ -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(parcel)
+ val second2 = readFromParcel(parcel)
+
+ assert(first == first2)
+ assert(second == second2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/nullableTypesSimple.kt b/plugins/parcelize/parcelize-compiler/testData/box/nullableTypesSimple.kt
new file mode 100644
index 00000000000..7b777e7ccda
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/nullableTypesSimple.kt
@@ -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(parcel)
+ val second2 = readFromParcel(parcel)
+
+ assert(first == first2)
+ assert(second == second2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/objects.kt b/plugins/parcelize/parcelize-compiler/testData/box/objects.kt
new file mode 100644
index 00000000000..9cd0643c5f5
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/objects.kt
@@ -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(parcel)
+ assert(test == test2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/openParcelize.kt b/plugins/parcelize/parcelize-compiler/testData/box/openParcelize.kt
new file mode 100644
index 00000000000..e07e26245c2
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/openParcelize.kt
@@ -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"
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/persistableBundle.kt b/plugins/parcelize/parcelize-compiler/testData/box/persistableBundle.kt
new file mode 100644
index 00000000000..a7bba12cb56
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/persistableBundle.kt
@@ -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(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
+}
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/primitiveTypes.kt b/plugins/parcelize/parcelize-compiler/testData/box/primitiveTypes.kt
new file mode 100644
index 00000000000..9bcc086d4e7
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/primitiveTypes.kt
@@ -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(parcel)
+ val second2 = readFromParcel(parcel)
+
+ assert(first == first2)
+ assert(second == second2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/sealedClass.kt b/plugins/parcelize/parcelize-compiler/testData/box/sealedClass.kt
new file mode 100644
index 00000000000..eae381fa5c9
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/sealedClass.kt
@@ -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(parcel)
+
+ assert(first == second)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/simple.kt b/plugins/parcelize/parcelize-compiler/testData/box/simple.kt
new file mode 100644
index 00000000000..4462e898ad0
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/simple.kt
@@ -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(parcel)
+ assert(user == user2)
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/sparseArrays.kt b/plugins/parcelize/parcelize-compiler/testData/box/sparseArrays.kt
new file mode 100644
index 00000000000..4b2673129a1
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/sparseArrays.kt
@@ -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) : 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().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(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
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/box/sparseBooleanArray.kt b/plugins/parcelize/parcelize-compiler/testData/box/sparseBooleanArray.kt
new file mode 100644
index 00000000000..c97e04aa8dc
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/box/sparseBooleanArray.kt
@@ -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(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
+}
\ No newline at end of file
diff --git a/plugins/parcelize/parcelize-compiler/testData/boxLib.kt b/plugins/parcelize/parcelize-compiler/testData/boxLib.kt
new file mode 100644
index 00000000000..b24f2513a9b
--- /dev/null
+++ b/plugins/parcelize/parcelize-compiler/testData/boxLib.kt
@@ -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